activerecord 4.2.0 → 6.0.5.1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +5 -5
- data/CHANGELOG.md +852 -801
- data/MIT-LICENSE +4 -2
- data/README.rdoc +14 -13
- data/examples/performance.rb +33 -32
- data/examples/simple.rb +5 -4
- data/lib/active_record/advisory_lock_base.rb +18 -0
- data/lib/active_record/aggregations.rb +267 -249
- data/lib/active_record/association_relation.rb +26 -6
- data/lib/active_record/associations/alias_tracker.rb +29 -36
- data/lib/active_record/associations/association.rb +137 -55
- data/lib/active_record/associations/association_scope.rb +110 -132
- data/lib/active_record/associations/belongs_to_association.rb +67 -54
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -12
- data/lib/active_record/associations/builder/association.rb +27 -40
- data/lib/active_record/associations/builder/belongs_to.rb +69 -55
- data/lib/active_record/associations/builder/collection_association.rb +10 -29
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +58 -70
- data/lib/active_record/associations/builder/has_many.rb +8 -4
- data/lib/active_record/associations/builder/has_one.rb +46 -5
- data/lib/active_record/associations/builder/singular_association.rb +16 -10
- data/lib/active_record/associations/collection_association.rb +150 -275
- data/lib/active_record/associations/collection_proxy.rb +253 -152
- data/lib/active_record/associations/foreign_association.rb +20 -0
- data/lib/active_record/associations/has_many_association.rb +35 -84
- data/lib/active_record/associations/has_many_through_association.rb +62 -80
- data/lib/active_record/associations/has_one_association.rb +62 -49
- data/lib/active_record/associations/has_one_through_association.rb +20 -11
- data/lib/active_record/associations/join_dependency/join_association.rb +43 -78
- data/lib/active_record/associations/join_dependency/join_base.rb +10 -9
- data/lib/active_record/associations/join_dependency/join_part.rb +14 -14
- data/lib/active_record/associations/join_dependency.rb +159 -162
- data/lib/active_record/associations/preloader/association.rb +102 -113
- data/lib/active_record/associations/preloader/through_association.rb +85 -65
- data/lib/active_record/associations/preloader.rb +96 -95
- data/lib/active_record/associations/singular_association.rb +18 -45
- data/lib/active_record/associations/through_association.rb +49 -24
- data/lib/active_record/associations.rb +1737 -1596
- data/lib/active_record/attribute_assignment.rb +57 -185
- data/lib/active_record/attribute_decorators.rb +39 -17
- data/lib/active_record/attribute_methods/before_type_cast.rb +14 -5
- data/lib/active_record/attribute_methods/dirty.rb +174 -134
- data/lib/active_record/attribute_methods/primary_key.rb +90 -84
- data/lib/active_record/attribute_methods/query.rb +6 -5
- data/lib/active_record/attribute_methods/read.rb +20 -77
- data/lib/active_record/attribute_methods/serialization.rb +40 -21
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +61 -37
- data/lib/active_record/attribute_methods/write.rb +33 -56
- data/lib/active_record/attribute_methods.rb +124 -143
- data/lib/active_record/attributes.rb +213 -74
- data/lib/active_record/autosave_association.rb +125 -54
- data/lib/active_record/base.rb +60 -49
- data/lib/active_record/callbacks.rb +101 -76
- data/lib/active_record/coders/json.rb +3 -1
- data/lib/active_record/coders/yaml_column.rb +36 -13
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +810 -291
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +26 -8
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +253 -108
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +83 -24
- data/lib/active_record/connection_adapters/abstract/quoting.rb +171 -53
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +6 -4
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +74 -47
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +383 -239
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +79 -36
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +736 -235
- data/lib/active_record/connection_adapters/abstract/transaction.rb +190 -87
- data/lib/active_record/connection_adapters/abstract_adapter.rb +487 -192
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +536 -600
- data/lib/active_record/connection_adapters/column.rb +56 -43
- data/lib/active_record/connection_adapters/connection_specification.rb +174 -153
- 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 +196 -0
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +71 -0
- data/lib/active_record/connection_adapters/mysql/quoting.rb +81 -0
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +71 -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 +268 -0
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +31 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +59 -196
- data/lib/active_record/connection_adapters/postgresql/column.rb +21 -11
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +71 -115
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +49 -57
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +9 -8
- data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +5 -2
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +5 -1
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +13 -1
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +17 -13
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +6 -3
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +31 -20
- data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -11
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +7 -9
- data/lib/active_record/connection_adapters/postgresql/oid/{infinity.rb → oid.rb} +5 -3
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +32 -11
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +70 -34
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +4 -1
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +67 -51
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +9 -5
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid.rb +23 -25
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +144 -47
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +27 -14
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +178 -108
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +49 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +465 -291
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +36 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +11 -8
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +565 -363
- data/lib/active_record/connection_adapters/schema_cache.rb +72 -25
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +37 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +119 -0
- data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +102 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +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 +299 -364
- data/lib/active_record/connection_adapters/statement_pool.rb +33 -13
- data/lib/active_record/connection_handling.rb +167 -41
- data/lib/active_record/core.rb +277 -233
- data/lib/active_record/counter_cache.rb +71 -50
- 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 +78 -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 +87 -106
- data/lib/active_record/enum.rb +172 -89
- data/lib/active_record/errors.rb +189 -53
- data/lib/active_record/explain.rb +22 -11
- data/lib/active_record/explain_registry.rb +4 -2
- data/lib/active_record/explain_subscriber.rb +11 -6
- data/lib/active_record/fixture_set/file.rb +35 -9
- data/lib/active_record/fixture_set/model_metadata.rb +33 -0
- data/lib/active_record/fixture_set/render_context.rb +17 -0
- data/lib/active_record/fixture_set/table_row.rb +152 -0
- data/lib/active_record/fixture_set/table_rows.rb +46 -0
- data/lib/active_record/fixtures.rb +225 -497
- data/lib/active_record/gem_version.rb +6 -4
- data/lib/active_record/inheritance.rb +158 -115
- data/lib/active_record/insert_all.rb +179 -0
- data/lib/active_record/integration.rb +123 -29
- data/lib/active_record/internal_metadata.rb +53 -0
- data/lib/active_record/legacy_yaml_adapter.rb +48 -0
- data/lib/active_record/locale/en.yml +3 -2
- data/lib/active_record/locking/optimistic.rb +99 -98
- data/lib/active_record/locking/pessimistic.rb +18 -6
- data/lib/active_record/log_subscriber.rb +76 -33
- data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +87 -0
- data/lib/active_record/middleware/database_selector.rb +74 -0
- data/lib/active_record/migration/command_recorder.rb +166 -91
- data/lib/active_record/migration/compatibility.rb +244 -0
- data/lib/active_record/migration/join_table.rb +8 -7
- data/lib/active_record/migration.rb +636 -290
- data/lib/active_record/model_schema.rb +344 -112
- data/lib/active_record/nested_attributes.rb +265 -215
- data/lib/active_record/no_touching.rb +15 -2
- data/lib/active_record/null_relation.rb +24 -38
- data/lib/active_record/persistence.rb +559 -125
- data/lib/active_record/query_cache.rb +19 -23
- data/lib/active_record/querying.rb +44 -30
- data/lib/active_record/railtie.rb +166 -47
- data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
- data/lib/active_record/railties/console_sandbox.rb +2 -0
- data/lib/active_record/railties/controller_runtime.rb +34 -33
- data/lib/active_record/railties/databases.rake +341 -202
- data/lib/active_record/readonly_attributes.rb +5 -4
- data/lib/active_record/reflection.rb +461 -302
- data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
- data/lib/active_record/relation/batches.rb +206 -55
- data/lib/active_record/relation/calculations.rb +270 -249
- data/lib/active_record/relation/delegation.rb +76 -84
- data/lib/active_record/relation/finder_methods.rb +287 -255
- data/lib/active_record/relation/from_clause.rb +30 -0
- data/lib/active_record/relation/merger.rb +86 -68
- data/lib/active_record/relation/predicate_builder/array_handler.rb +27 -25
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +43 -0
- data/lib/active_record/relation/predicate_builder/base_handler.rb +18 -0
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +19 -0
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +53 -0
- data/lib/active_record/relation/predicate_builder/range_handler.rb +22 -0
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +7 -1
- data/lib/active_record/relation/predicate_builder.rb +112 -92
- data/lib/active_record/relation/query_attribute.rb +50 -0
- data/lib/active_record/relation/query_methods.rb +612 -392
- data/lib/active_record/relation/record_fetch_warning.rb +51 -0
- data/lib/active_record/relation/spawn_methods.rb +18 -17
- data/lib/active_record/relation/where_clause.rb +189 -0
- data/lib/active_record/relation/where_clause_factory.rb +33 -0
- data/lib/active_record/relation.rb +533 -340
- data/lib/active_record/result.rb +79 -43
- data/lib/active_record/runtime_registry.rb +6 -4
- data/lib/active_record/sanitization.rb +144 -121
- data/lib/active_record/schema.rb +21 -24
- data/lib/active_record/schema_dumper.rb +112 -93
- data/lib/active_record/schema_migration.rb +24 -20
- data/lib/active_record/scoping/default.rb +98 -82
- data/lib/active_record/scoping/named.rb +91 -33
- data/lib/active_record/scoping.rb +45 -27
- data/lib/active_record/secure_token.rb +40 -0
- data/lib/active_record/serialization.rb +5 -5
- data/lib/active_record/statement_cache.rb +73 -36
- data/lib/active_record/store.rb +127 -42
- data/lib/active_record/suppressor.rb +61 -0
- data/lib/active_record/table_metadata.rb +90 -0
- data/lib/active_record/tasks/database_tasks.rb +309 -99
- data/lib/active_record/tasks/mysql_database_tasks.rb +58 -89
- data/lib/active_record/tasks/postgresql_database_tasks.rb +81 -31
- data/lib/active_record/tasks/sqlite_database_tasks.rb +37 -16
- data/lib/active_record/test_databases.rb +23 -0
- data/lib/active_record/test_fixtures.rb +243 -0
- data/lib/active_record/timestamp.rb +86 -41
- data/lib/active_record/touch_later.rb +65 -0
- data/lib/active_record/transactions.rb +222 -146
- data/lib/active_record/translation.rb +3 -1
- data/lib/active_record/type/adapter_specific_registry.rb +126 -0
- data/lib/active_record/type/date.rb +4 -41
- data/lib/active_record/type/date_time.rb +4 -38
- data/lib/active_record/type/decimal_without_scale.rb +6 -2
- data/lib/active_record/type/hash_lookup_type_map.rb +12 -5
- 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 +29 -15
- data/lib/active_record/type/text.rb +2 -2
- data/lib/active_record/type/time.rb +21 -16
- data/lib/active_record/type/type_map.rb +16 -19
- data/lib/active_record/type/unsigned_integer.rb +9 -8
- data/lib/active_record/type.rb +77 -23
- 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 +12 -4
- data/lib/active_record/validations/length.rb +26 -0
- data/lib/active_record/validations/presence.rb +14 -13
- data/lib/active_record/validations/uniqueness.rb +43 -46
- data/lib/active_record/validations.rb +38 -35
- data/lib/active_record/version.rb +3 -1
- data/lib/active_record.rb +44 -21
- 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 +256 -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 +203 -0
- data/lib/arel/visitors/dot.rb +296 -0
- data/lib/arel/visitors/ibm_db.rb +34 -0
- data/lib/arel/visitors/informix.rb +62 -0
- data/lib/arel/visitors/mssql.rb +156 -0
- data/lib/arel/visitors/mysql.rb +83 -0
- data/lib/arel/visitors/oracle.rb +158 -0
- data/lib/arel/visitors/oracle12.rb +65 -0
- data/lib/arel/visitors/postgresql.rb +109 -0
- data/lib/arel/visitors/sqlite.rb +38 -0
- data/lib/arel/visitors/to_sql.rb +888 -0
- data/lib/arel/visitors/visitor.rb +45 -0
- data/lib/arel/visitors/where_sql.rb +22 -0
- data/lib/arel/visitors.rb +20 -0
- data/lib/arel/window_predications.rb +9 -0
- data/lib/arel.rb +62 -0
- data/lib/rails/generators/active_record/application_record/application_record_generator.rb +26 -0
- data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +42 -37
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +24 -0
- data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +11 -8
- data/lib/rails/generators/active_record/migration.rb +30 -1
- data/lib/rails/generators/active_record/model/model_generator.rb +18 -22
- data/lib/rails/generators/active_record/model/templates/model.rb.tt +22 -0
- data/lib/rails/generators/active_record.rb +7 -5
- metadata +174 -63
- data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
- data/lib/active_record/associations/preloader/collection_association.rb +0 -24
- data/lib/active_record/associations/preloader/has_many.rb +0 -17
- data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
- data/lib/active_record/associations/preloader/has_one.rb +0 -23
- data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
- data/lib/active_record/associations/preloader/singular_association.rb +0 -21
- data/lib/active_record/attribute.rb +0 -149
- data/lib/active_record/attribute_set/builder.rb +0 -86
- data/lib/active_record/attribute_set.rb +0 -77
- data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -491
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
- data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
- data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +0 -11
- data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -35
- data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
- data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
- data/lib/active_record/serializers/xml_serializer.rb +0 -193
- data/lib/active_record/type/big_integer.rb +0 -13
- data/lib/active_record/type/binary.rb +0 -50
- data/lib/active_record/type/boolean.rb +0 -30
- data/lib/active_record/type/decimal.rb +0 -40
- data/lib/active_record/type/decorator.rb +0 -14
- data/lib/active_record/type/float.rb +0 -19
- data/lib/active_record/type/integer.rb +0 -55
- data/lib/active_record/type/mutable.rb +0 -16
- data/lib/active_record/type/numeric.rb +0 -36
- data/lib/active_record/type/string.rb +0 -36
- data/lib/active_record/type/time_value.rb +0 -38
- data/lib/active_record/type/value.rb +0 -101
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +0 -22
- data/lib/rails/generators/active_record/model/templates/model.rb +0 -10
- /data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
@@ -1,140 +1,121 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActiveRecord
|
2
4
|
module DynamicMatchers #:nodoc:
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
5
|
+
private
|
6
|
+
def respond_to_missing?(name, _)
|
7
|
+
if self == Base
|
8
|
+
super
|
9
|
+
else
|
10
|
+
match = Method.match(self, name)
|
11
|
+
match && match.valid? || super
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def method_missing(name, *arguments, &block)
|
12
16
|
match = Method.match(self, name)
|
13
|
-
|
17
|
+
|
18
|
+
if match && match.valid?
|
19
|
+
match.define
|
20
|
+
send(name, *arguments, &block)
|
21
|
+
else
|
22
|
+
super
|
23
|
+
end
|
14
24
|
end
|
15
|
-
end
|
16
25
|
|
17
|
-
|
26
|
+
class Method
|
27
|
+
@matchers = []
|
18
28
|
|
19
|
-
|
20
|
-
|
29
|
+
class << self
|
30
|
+
attr_reader :matchers
|
21
31
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
super
|
27
|
-
end
|
28
|
-
end
|
32
|
+
def match(model, name)
|
33
|
+
klass = matchers.find { |k| k.pattern.match?(name) }
|
34
|
+
klass.new(model, name) if klass
|
35
|
+
end
|
29
36
|
|
30
|
-
|
31
|
-
|
37
|
+
def pattern
|
38
|
+
@pattern ||= /\A#{prefix}_([_a-zA-Z]\w*)#{suffix}\Z/
|
39
|
+
end
|
32
40
|
|
33
|
-
|
34
|
-
|
41
|
+
def prefix
|
42
|
+
raise NotImplementedError
|
43
|
+
end
|
35
44
|
|
36
|
-
|
37
|
-
|
38
|
-
|
45
|
+
def suffix
|
46
|
+
""
|
47
|
+
end
|
39
48
|
end
|
40
49
|
|
41
|
-
|
42
|
-
@pattern ||= /\A#{prefix}_([_a-zA-Z]\w*)#{suffix}\Z/
|
43
|
-
end
|
50
|
+
attr_reader :model, :name, :attribute_names
|
44
51
|
|
45
|
-
def
|
46
|
-
|
52
|
+
def initialize(model, method_name)
|
53
|
+
@model = model
|
54
|
+
@name = method_name.to_s
|
55
|
+
@attribute_names = @name.match(self.class.pattern)[1].split("_and_")
|
56
|
+
@attribute_names.map! { |name| @model.attribute_aliases[name] || name }
|
47
57
|
end
|
48
58
|
|
49
|
-
def
|
50
|
-
|
59
|
+
def valid?
|
60
|
+
attribute_names.all? { |name| model.columns_hash[name] || model.reflect_on_aggregation(name.to_sym) }
|
51
61
|
end
|
52
|
-
end
|
53
62
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
end
|
62
|
-
|
63
|
-
def valid?
|
64
|
-
attribute_names.all? { |name| model.columns_hash[name] || model.reflect_on_aggregation(name.to_sym) }
|
65
|
-
end
|
63
|
+
def define
|
64
|
+
model.class_eval <<-CODE, __FILE__, __LINE__ + 1
|
65
|
+
def self.#{name}(#{signature})
|
66
|
+
#{body}
|
67
|
+
end
|
68
|
+
CODE
|
69
|
+
end
|
66
70
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
#{body}
|
71
|
+
private
|
72
|
+
def body
|
73
|
+
"#{finder}(#{attributes_hash})"
|
71
74
|
end
|
72
|
-
CODE
|
73
|
-
end
|
74
|
-
|
75
|
-
def body
|
76
|
-
raise NotImplementedError
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
|
-
module Finder
|
81
|
-
# Extended in activerecord-deprecated_finders
|
82
|
-
def body
|
83
|
-
result
|
84
|
-
end
|
85
75
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
# The parameters in the signature may have reserved Ruby words, in order
|
92
|
-
# to prevent errors, we start each param name with `_`.
|
93
|
-
#
|
94
|
-
# Extended in activerecord-deprecated_finders
|
95
|
-
def signature
|
96
|
-
attribute_names.map { |name| "_#{name}" }.join(', ')
|
97
|
-
end
|
76
|
+
# The parameters in the signature may have reserved Ruby words, in order
|
77
|
+
# to prevent errors, we start each param name with `_`.
|
78
|
+
def signature
|
79
|
+
attribute_names.map { |name| "_#{name}" }.join(", ")
|
80
|
+
end
|
98
81
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
82
|
+
# Given that the parameters starts with `_`, the finder needs to use the
|
83
|
+
# same parameter name.
|
84
|
+
def attributes_hash
|
85
|
+
"{" + attribute_names.map { |name| ":#{name} => _#{name}" }.join(",") + "}"
|
86
|
+
end
|
104
87
|
|
105
|
-
|
106
|
-
|
88
|
+
def finder
|
89
|
+
raise NotImplementedError
|
90
|
+
end
|
107
91
|
end
|
108
|
-
end
|
109
92
|
|
110
|
-
|
111
|
-
|
112
|
-
include Finder
|
93
|
+
class FindBy < Method
|
94
|
+
Method.matchers << self
|
113
95
|
|
114
|
-
|
115
|
-
|
116
|
-
|
96
|
+
def self.prefix
|
97
|
+
"find_by"
|
98
|
+
end
|
117
99
|
|
118
|
-
|
119
|
-
|
100
|
+
def finder
|
101
|
+
"find_by"
|
102
|
+
end
|
120
103
|
end
|
121
|
-
end
|
122
104
|
|
123
|
-
|
124
|
-
|
125
|
-
include Finder
|
105
|
+
class FindByBang < Method
|
106
|
+
Method.matchers << self
|
126
107
|
|
127
|
-
|
128
|
-
|
129
|
-
|
108
|
+
def self.prefix
|
109
|
+
"find_by"
|
110
|
+
end
|
130
111
|
|
131
|
-
|
132
|
-
|
133
|
-
|
112
|
+
def self.suffix
|
113
|
+
"!"
|
114
|
+
end
|
134
115
|
|
135
|
-
|
136
|
-
|
116
|
+
def finder
|
117
|
+
"find_by!"
|
118
|
+
end
|
137
119
|
end
|
138
|
-
end
|
139
120
|
end
|
140
121
|
end
|
data/lib/active_record/enum.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/core_ext/object/deep_dup"
|
2
4
|
|
3
5
|
module ActiveRecord
|
4
6
|
# Declare an enum attribute where the values map to integers in the database,
|
@@ -18,10 +20,9 @@ module ActiveRecord
|
|
18
20
|
# conversation.archived? # => true
|
19
21
|
# conversation.status # => "archived"
|
20
22
|
#
|
21
|
-
# # conversation.
|
23
|
+
# # conversation.status = 1
|
22
24
|
# conversation.status = "archived"
|
23
25
|
#
|
24
|
-
# # conversation.update! status: nil
|
25
26
|
# conversation.status = nil
|
26
27
|
# conversation.status.nil? # => true
|
27
28
|
# conversation.status # => nil
|
@@ -30,7 +31,15 @@ module ActiveRecord
|
|
30
31
|
# as well. With the above example:
|
31
32
|
#
|
32
33
|
# Conversation.active
|
34
|
+
# Conversation.not_active
|
33
35
|
# Conversation.archived
|
36
|
+
# Conversation.not_archived
|
37
|
+
#
|
38
|
+
# Of course, you can also query them directly if the scopes don't fit your
|
39
|
+
# needs:
|
40
|
+
#
|
41
|
+
# Conversation.where(status: [:active, :archived])
|
42
|
+
# Conversation.where.not(status: :active)
|
34
43
|
#
|
35
44
|
# You can set the default value from the database declaration, like:
|
36
45
|
#
|
@@ -41,13 +50,13 @@ module ActiveRecord
|
|
41
50
|
# Good practice is to let the first declared status be the default.
|
42
51
|
#
|
43
52
|
# Finally, it's also possible to explicitly map the relation between attribute and
|
44
|
-
# database integer with a
|
53
|
+
# database integer with a hash:
|
45
54
|
#
|
46
55
|
# class Conversation < ActiveRecord::Base
|
47
56
|
# enum status: { active: 0, archived: 1 }
|
48
57
|
# end
|
49
58
|
#
|
50
|
-
# Note that when an
|
59
|
+
# Note that when an array is used, the implicit mapping from the values to database
|
51
60
|
# integers is derived from the order the values appear in the array. In the example,
|
52
61
|
# <tt>:active</tt> is mapped to +0+ as it's the first element, and <tt>:archived</tt>
|
53
62
|
# is mapped to +1+. In general, the +i+-th element is mapped to <tt>i-1</tt> in the
|
@@ -55,23 +64,42 @@ module ActiveRecord
|
|
55
64
|
#
|
56
65
|
# Therefore, once a value is added to the enum array, its position in the array must
|
57
66
|
# be maintained, and new values should only be added to the end of the array. To
|
58
|
-
# remove unused values, the explicit
|
67
|
+
# remove unused values, the explicit hash syntax should be used.
|
59
68
|
#
|
60
69
|
# In rare circumstances you might need to access the mapping directly.
|
61
70
|
# The mappings are exposed through a class method with the pluralized attribute
|
62
|
-
# name
|
71
|
+
# name, which return the mapping in a +HashWithIndifferentAccess+:
|
63
72
|
#
|
64
|
-
# Conversation.statuses
|
73
|
+
# Conversation.statuses[:active] # => 0
|
74
|
+
# Conversation.statuses["archived"] # => 1
|
65
75
|
#
|
66
|
-
# Use that class method when you need to know the ordinal value of an enum
|
76
|
+
# Use that class method when you need to know the ordinal value of an enum.
|
77
|
+
# For example, you can use that when manually building SQL strings:
|
67
78
|
#
|
68
79
|
# Conversation.where("status <> ?", Conversation.statuses[:archived])
|
69
80
|
#
|
70
|
-
#
|
81
|
+
# You can use the +:_prefix+ or +:_suffix+ options when you need to define
|
82
|
+
# multiple enums with same values. If the passed value is +true+, the methods
|
83
|
+
# are prefixed/suffixed with the name of the enum. It is also possible to
|
84
|
+
# supply a custom value:
|
85
|
+
#
|
86
|
+
# class Conversation < ActiveRecord::Base
|
87
|
+
# enum status: [:active, :archived], _suffix: true
|
88
|
+
# enum comments_status: [:active, :inactive], _prefix: :comments
|
89
|
+
# end
|
90
|
+
#
|
91
|
+
# With the above example, the bang and predicate methods along with the
|
92
|
+
# associated scopes are now prefixed and/or suffixed accordingly:
|
93
|
+
#
|
94
|
+
# conversation.active_status!
|
95
|
+
# conversation.archived_status? # => false
|
96
|
+
#
|
97
|
+
# conversation.comments_inactive!
|
98
|
+
# conversation.comments_active? # => false
|
99
|
+
|
71
100
|
module Enum
|
72
101
|
def self.extended(base) # :nodoc:
|
73
|
-
base.class_attribute(:defined_enums)
|
74
|
-
base.defined_enums = {}
|
102
|
+
base.class_attribute(:defined_enums, instance_writer: false, default: {})
|
75
103
|
end
|
76
104
|
|
77
105
|
def inherited(base) # :nodoc:
|
@@ -79,119 +107,174 @@ module ActiveRecord
|
|
79
107
|
super
|
80
108
|
end
|
81
109
|
|
110
|
+
class EnumType < Type::Value # :nodoc:
|
111
|
+
delegate :type, to: :subtype
|
112
|
+
|
113
|
+
def initialize(name, mapping, subtype)
|
114
|
+
@name = name
|
115
|
+
@mapping = mapping
|
116
|
+
@subtype = subtype
|
117
|
+
end
|
118
|
+
|
119
|
+
def cast(value)
|
120
|
+
return if value.blank?
|
121
|
+
|
122
|
+
if mapping.has_key?(value)
|
123
|
+
value.to_s
|
124
|
+
elsif mapping.has_value?(value)
|
125
|
+
mapping.key(value)
|
126
|
+
else
|
127
|
+
assert_valid_value(value)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def deserialize(value)
|
132
|
+
return if value.nil?
|
133
|
+
mapping.key(subtype.deserialize(value))
|
134
|
+
end
|
135
|
+
|
136
|
+
def serialize(value)
|
137
|
+
mapping.fetch(value, value)
|
138
|
+
end
|
139
|
+
|
140
|
+
def assert_valid_value(value)
|
141
|
+
unless value.blank? || mapping.has_key?(value) || mapping.has_value?(value)
|
142
|
+
raise ArgumentError, "'#{value}' is not a valid #{name}"
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
private
|
147
|
+
attr_reader :name, :mapping, :subtype
|
148
|
+
end
|
149
|
+
|
82
150
|
def enum(definitions)
|
83
151
|
klass = self
|
152
|
+
enum_prefix = definitions.delete(:_prefix)
|
153
|
+
enum_suffix = definitions.delete(:_suffix)
|
154
|
+
enum_scopes = definitions.delete(:_scopes)
|
84
155
|
definitions.each do |name, values|
|
156
|
+
assert_valid_enum_definition_values(values)
|
85
157
|
# statuses = { }
|
86
158
|
enum_values = ActiveSupport::HashWithIndifferentAccess.new
|
87
|
-
name
|
159
|
+
name = name.to_s
|
88
160
|
|
89
|
-
# def self.statuses statuses end
|
90
|
-
detect_enum_conflict!(name, name.
|
91
|
-
|
161
|
+
# def self.statuses() statuses end
|
162
|
+
detect_enum_conflict!(name, name.pluralize, true)
|
163
|
+
singleton_class.define_method(name.pluralize) { enum_values }
|
164
|
+
defined_enums[name] = enum_values
|
165
|
+
|
166
|
+
detect_enum_conflict!(name, name)
|
167
|
+
detect_enum_conflict!(name, "#{name}=")
|
168
|
+
|
169
|
+
attr = attribute_alias?(name) ? attribute_alias(name) : name
|
170
|
+
decorate_attribute_type(attr, :enum) do |subtype|
|
171
|
+
EnumType.new(attr, enum_values, subtype)
|
172
|
+
end
|
92
173
|
|
93
174
|
_enum_methods_module.module_eval do
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
if
|
98
|
-
|
99
|
-
elsif
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
175
|
+
pairs = values.respond_to?(:each_pair) ? values.each_pair : values.each_with_index
|
176
|
+
value_method_names = []
|
177
|
+
pairs.each do |label, value|
|
178
|
+
if enum_prefix == true
|
179
|
+
prefix = "#{name}_"
|
180
|
+
elsif enum_prefix
|
181
|
+
prefix = "#{enum_prefix}_"
|
182
|
+
end
|
183
|
+
if enum_suffix == true
|
184
|
+
suffix = "_#{name}"
|
185
|
+
elsif enum_suffix
|
186
|
+
suffix = "_#{enum_suffix}"
|
106
187
|
end
|
107
|
-
}
|
108
|
-
|
109
|
-
# def status() statuses.key self[:status] end
|
110
|
-
klass.send(:detect_enum_conflict!, name, name)
|
111
|
-
define_method(name) { enum_values.key self[name] }
|
112
188
|
|
113
|
-
|
114
|
-
|
115
|
-
|
189
|
+
value_method_name = "#{prefix}#{label}#{suffix}"
|
190
|
+
value_method_names << value_method_name
|
191
|
+
enum_values[label] = value
|
192
|
+
label = label.to_s
|
116
193
|
|
117
|
-
|
118
|
-
|
119
|
-
|
194
|
+
# def active?() status == "active" end
|
195
|
+
klass.send(:detect_enum_conflict!, name, "#{value_method_name}?")
|
196
|
+
define_method("#{value_method_name}?") { self[attr] == label }
|
120
197
|
|
121
|
-
# def active
|
122
|
-
klass.send(:detect_enum_conflict!, name, "#{
|
123
|
-
define_method("#{
|
198
|
+
# def active!() update!(status: 0) end
|
199
|
+
klass.send(:detect_enum_conflict!, name, "#{value_method_name}!")
|
200
|
+
define_method("#{value_method_name}!") { update!(attr => value) }
|
124
201
|
|
125
|
-
#
|
126
|
-
|
127
|
-
|
202
|
+
# scope :active, -> { where(status: 0) }
|
203
|
+
# scope :not_active, -> { where.not(status: 0) }
|
204
|
+
if enum_scopes != false
|
205
|
+
klass.send(:detect_enum_conflict!, name, value_method_name, true)
|
206
|
+
klass.scope value_method_name, -> { where(attr => value) }
|
128
207
|
|
129
|
-
|
130
|
-
|
131
|
-
|
208
|
+
klass.send(:detect_enum_conflict!, name, "not_#{value_method_name}", true)
|
209
|
+
klass.scope "not_#{value_method_name}", -> { where.not(attr => value) }
|
210
|
+
end
|
132
211
|
end
|
212
|
+
klass.send(:detect_negative_enum_conditions!, value_method_names) if enum_scopes != false
|
133
213
|
end
|
134
|
-
|
214
|
+
enum_values.freeze
|
135
215
|
end
|
136
216
|
end
|
137
217
|
|
138
218
|
private
|
139
219
|
def _enum_methods_module
|
140
220
|
@_enum_methods_module ||= begin
|
141
|
-
mod = Module.new
|
142
|
-
private
|
143
|
-
def save_changed_attribute(attr_name, old)
|
144
|
-
if (mapping = self.class.defined_enums[attr_name.to_s])
|
145
|
-
value = _read_attribute(attr_name)
|
146
|
-
if attribute_changed?(attr_name)
|
147
|
-
if mapping[old] == value
|
148
|
-
clear_attribute_changes([attr_name])
|
149
|
-
end
|
150
|
-
else
|
151
|
-
if old != value
|
152
|
-
set_attribute_was(attr_name, mapping.key(old))
|
153
|
-
end
|
154
|
-
end
|
155
|
-
else
|
156
|
-
super
|
157
|
-
end
|
158
|
-
end
|
159
|
-
end
|
221
|
+
mod = Module.new
|
160
222
|
include mod
|
161
223
|
mod
|
162
224
|
end
|
163
225
|
end
|
164
226
|
|
227
|
+
def assert_valid_enum_definition_values(values)
|
228
|
+
unless values.is_a?(Hash) || values.all? { |v| v.is_a?(Symbol) } || values.all? { |v| v.is_a?(String) }
|
229
|
+
error_message = <<~MSG
|
230
|
+
Enum values #{values} must be either a hash, an array of symbols, or an array of strings.
|
231
|
+
MSG
|
232
|
+
raise ArgumentError, error_message
|
233
|
+
end
|
234
|
+
|
235
|
+
if values.is_a?(Hash) && values.keys.any?(&:blank?) || values.is_a?(Array) && values.any?(&:blank?)
|
236
|
+
raise ArgumentError, "Enum label name must not be blank."
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
165
240
|
ENUM_CONFLICT_MESSAGE = \
|
166
241
|
"You tried to define an enum named \"%{enum}\" on the model \"%{klass}\", but " \
|
167
242
|
"this will generate a %{type} method \"%{method}\", which is already defined " \
|
168
243
|
"by %{source}."
|
244
|
+
private_constant :ENUM_CONFLICT_MESSAGE
|
169
245
|
|
170
246
|
def detect_enum_conflict!(enum_name, method_name, klass_method = false)
|
171
247
|
if klass_method && dangerous_class_method?(method_name)
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
type: 'class',
|
176
|
-
method: method_name,
|
177
|
-
source: 'Active Record'
|
178
|
-
}
|
248
|
+
raise_conflict_error(enum_name, method_name, type: "class")
|
249
|
+
elsif klass_method && method_defined_within?(method_name, Relation)
|
250
|
+
raise_conflict_error(enum_name, method_name, type: "class", source: Relation.name)
|
179
251
|
elsif !klass_method && dangerous_attribute_method?(method_name)
|
180
|
-
|
181
|
-
enum: enum_name,
|
182
|
-
klass: self.name,
|
183
|
-
type: 'instance',
|
184
|
-
method: method_name,
|
185
|
-
source: 'Active Record'
|
186
|
-
}
|
252
|
+
raise_conflict_error(enum_name, method_name)
|
187
253
|
elsif !klass_method && method_defined_within?(method_name, _enum_methods_module, Module)
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
254
|
+
raise_conflict_error(enum_name, method_name, source: "another enum")
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
def raise_conflict_error(enum_name, method_name, type: "instance", source: "Active Record")
|
259
|
+
raise ArgumentError, ENUM_CONFLICT_MESSAGE % {
|
260
|
+
enum: enum_name,
|
261
|
+
klass: name,
|
262
|
+
type: type,
|
263
|
+
method: method_name,
|
264
|
+
source: source
|
265
|
+
}
|
266
|
+
end
|
267
|
+
|
268
|
+
def detect_negative_enum_conditions!(method_names)
|
269
|
+
return unless logger
|
270
|
+
|
271
|
+
method_names.select { |m| m.start_with?("not_") }.each do |potential_not|
|
272
|
+
inverted_form = potential_not.sub("not_", "")
|
273
|
+
if method_names.include?(inverted_form)
|
274
|
+
logger.warn "Enum element '#{potential_not}' in #{self.name} uses the prefix 'not_'." \
|
275
|
+
" This has caused a conflict with auto generated negative scopes." \
|
276
|
+
" Avoid using enum elements starting with 'not' where the positive form is also an element."
|
277
|
+
end
|
195
278
|
end
|
196
279
|
end
|
197
280
|
end
|