aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord')
-rw-r--r--activerecord/CHANGELOG.md486
-rw-r--r--activerecord/MIT-LICENSE2
-rw-r--r--activerecord/Rakefile90
-rw-r--r--activerecord/activerecord.gemspec30
-rwxr-xr-xactiverecord/bin/test8
-rw-r--r--activerecord/examples/performance.rb56
-rw-r--r--activerecord/examples/simple.rb6
-rw-r--r--activerecord/lib/active_record.rb42
-rw-r--r--activerecord/lib/active_record/aggregations.rb486
-rw-r--r--activerecord/lib/active_record/association_relation.rb9
-rw-r--r--activerecord/lib/active_record/associations.rb3113
-rw-r--r--activerecord/lib/active_record/associations/alias_tracker.rb2
-rw-r--r--activerecord/lib/active_record/associations/association.rb25
-rw-r--r--activerecord/lib/active_record/associations/association_scope.rb160
-rw-r--r--activerecord/lib/active_record/associations/belongs_to_association.rb1
-rw-r--r--activerecord/lib/active_record/associations/builder/belongs_to.rb30
-rw-r--r--activerecord/lib/active_record/associations/builder/collection_association.rb3
-rw-r--r--activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb56
-rw-r--r--activerecord/lib/active_record/associations/builder/singular_association.rb11
-rw-r--r--activerecord/lib/active_record/associations/collection_association.rb271
-rw-r--r--activerecord/lib/active_record/associations/collection_proxy.rb193
-rw-r--r--activerecord/lib/active_record/associations/has_many_association.rb17
-rw-r--r--activerecord/lib/active_record/associations/has_many_through_association.rb19
-rw-r--r--activerecord/lib/active_record/associations/has_one_association.rb50
-rw-r--r--activerecord/lib/active_record/associations/has_one_through_association.rb2
-rw-r--r--activerecord/lib/active_record/associations/join_dependency.rb234
-rw-r--r--activerecord/lib/active_record/associations/join_dependency/join_association.rb6
-rw-r--r--activerecord/lib/active_record/associations/join_dependency/join_base.rb2
-rw-r--r--activerecord/lib/active_record/associations/join_dependency/join_part.rb6
-rw-r--r--activerecord/lib/active_record/associations/preloader.rb188
-rw-r--r--activerecord/lib/active_record/associations/preloader/association.rb160
-rw-r--r--activerecord/lib/active_record/associations/preloader/belongs_to.rb2
-rw-r--r--activerecord/lib/active_record/associations/preloader/collection_association.rb13
-rw-r--r--activerecord/lib/active_record/associations/preloader/has_many.rb2
-rw-r--r--activerecord/lib/active_record/associations/preloader/singular_association.rb15
-rw-r--r--activerecord/lib/active_record/associations/preloader/through_association.rb69
-rw-r--r--activerecord/lib/active_record/associations/singular_association.rb41
-rw-r--r--activerecord/lib/active_record/associations/through_association.rb7
-rw-r--r--activerecord/lib/active_record/attribute.rb151
-rw-r--r--activerecord/lib/active_record/attribute/user_provided_default.rb6
-rw-r--r--activerecord/lib/active_record/attribute_assignment.rb122
-rw-r--r--activerecord/lib/active_record/attribute_decorators.rb48
-rw-r--r--activerecord/lib/active_record/attribute_methods.rb108
-rw-r--r--activerecord/lib/active_record/attribute_methods/before_type_cast.rb14
-rw-r--r--activerecord/lib/active_record/attribute_methods/dirty.rb240
-rw-r--r--activerecord/lib/active_record/attribute_methods/primary_key.rb144
-rw-r--r--activerecord/lib/active_record/attribute_methods/read.rb76
-rw-r--r--activerecord/lib/active_record/attribute_methods/serialization.rb14
-rw-r--r--activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb93
-rw-r--r--activerecord/lib/active_record/attribute_methods/write.rb66
-rw-r--r--activerecord/lib/active_record/attribute_mutation_tracker.rb58
-rw-r--r--activerecord/lib/active_record/attribute_set.rb16
-rw-r--r--activerecord/lib/active_record/attribute_set/builder.rb64
-rw-r--r--activerecord/lib/active_record/attribute_set/yaml_encoder.rb12
-rw-r--r--activerecord/lib/active_record/attributes.rb42
-rw-r--r--activerecord/lib/active_record/autosave_association.rb48
-rw-r--r--activerecord/lib/active_record/base.rb48
-rw-r--r--activerecord/lib/active_record/callbacks.rb17
-rw-r--r--activerecord/lib/active_record/coders/yaml_column.rb17
-rw-r--r--activerecord/lib/active_record/collection_cache_key.rb1
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb538
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/database_limits.rb4
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb48
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb71
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/quoting.rb100
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_creation.rb8
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb105
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb77
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb213
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/transaction.rb6
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_adapter.rb248
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb776
-rw-r--r--activerecord/lib/active_record/connection_adapters/column.rb30
-rw-r--r--activerecord/lib/active_record/connection_adapters/connection_specification.rb219
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql/column.rb30
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql/database_statements.rb86
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb44
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql/quoting.rb20
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql/schema_creation.rb84
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql/schema_definitions.rb11
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql/schema_dumper.rb48
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql/type_metadata.rb11
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb56
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb55
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb6
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid.rb42
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid/array.rb30
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid/bit.rb12
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid/cidr.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid/date_time.rb4
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid/enum.rb6
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid/hstore.rb34
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb43
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid/money.rb4
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid/point.rb36
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb52
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid/range.rb58
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb102
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb93
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/schema_definitions.rb36
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/schema_dumper.rb30
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb283
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/type_metadata.rb8
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/utils.rb12
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb254
-rw-r--r--activerecord/lib/active_record/connection_adapters/schema_cache.rb23
-rw-r--r--activerecord/lib/active_record/connection_adapters/sql_type_metadata.rb6
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite3/quoting.rb34
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb23
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb13
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb264
-rw-r--r--activerecord/lib/active_record/connection_adapters/statement_pool.rb14
-rw-r--r--activerecord/lib/active_record/connection_handling.rb6
-rw-r--r--activerecord/lib/active_record/core.rb183
-rw-r--r--activerecord/lib/active_record/counter_cache.rb74
-rw-r--r--activerecord/lib/active_record/define_callbacks.rb20
-rw-r--r--activerecord/lib/active_record/dynamic_matchers.rb159
-rw-r--r--activerecord/lib/active_record/enum.rb25
-rw-r--r--activerecord/lib/active_record/errors.rb67
-rw-r--r--activerecord/lib/active_record/explain.rb32
-rw-r--r--activerecord/lib/active_record/explain_registry.rb2
-rw-r--r--activerecord/lib/active_record/explain_subscriber.rb11
-rw-r--r--activerecord/lib/active_record/fixture_set/file.rb19
-rw-r--r--activerecord/lib/active_record/fixtures.rb122
-rw-r--r--activerecord/lib/active_record/inheritance.rb169
-rw-r--r--activerecord/lib/active_record/integration.rb37
-rw-r--r--activerecord/lib/active_record/internal_metadata.rb6
-rw-r--r--activerecord/lib/active_record/locking/optimistic.rb117
-rw-r--r--activerecord/lib/active_record/locking/pessimistic.rb2
-rw-r--r--activerecord/lib/active_record/log_subscriber.rb72
-rw-r--r--activerecord/lib/active_record/migration.rb296
-rw-r--r--activerecord/lib/active_record/migration/command_recorder.rb188
-rw-r--r--activerecord/lib/active_record/migration/compatibility.rb75
-rw-r--r--activerecord/lib/active_record/migration/join_table.rb12
-rw-r--r--activerecord/lib/active_record/model_schema.rb321
-rw-r--r--activerecord/lib/active_record/nested_attributes.rb399
-rw-r--r--activerecord/lib/active_record/no_touching.rb4
-rw-r--r--activerecord/lib/active_record/null_relation.rb43
-rw-r--r--activerecord/lib/active_record/persistence.rb43
-rw-r--r--activerecord/lib/active_record/query_cache.rb24
-rw-r--r--activerecord/lib/active_record/querying.rb11
-rw-r--r--activerecord/lib/active_record/railtie.rb29
-rw-r--r--activerecord/lib/active_record/railties/controller_runtime.rb10
-rw-r--r--activerecord/lib/active_record/railties/databases.rake237
-rw-r--r--activerecord/lib/active_record/railties/jdbcmysql_error.rb2
-rw-r--r--activerecord/lib/active_record/readonly_attributes.rb2
-rw-r--r--activerecord/lib/active_record/reflection.rb78
-rw-r--r--activerecord/lib/active_record/relation.rb176
-rw-r--r--activerecord/lib/active_record/relation/batches.rb131
-rw-r--r--activerecord/lib/active_record/relation/batches/batch_enumerator.rb2
-rw-r--r--activerecord/lib/active_record/relation/calculations.rb312
-rw-r--r--activerecord/lib/active_record/relation/delegation.rb59
-rw-r--r--activerecord/lib/active_record/relation/finder_methods.rb423
-rw-r--r--activerecord/lib/active_record/relation/merger.rb120
-rw-r--r--activerecord/lib/active_record/relation/predicate_builder.rb175
-rw-r--r--activerecord/lib/active_record/relation/predicate_builder/array_handler.rb12
-rw-r--r--activerecord/lib/active_record/relation/predicate_builder/association_query_handler.rb41
-rw-r--r--activerecord/lib/active_record/relation/predicate_builder/base_handler.rb4
-rw-r--r--activerecord/lib/active_record/relation/predicate_builder/class_handler.rb27
-rw-r--r--activerecord/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb22
-rw-r--r--activerecord/lib/active_record/relation/query_attribute.rb2
-rw-r--r--activerecord/lib/active_record/relation/query_methods.rb533
-rw-r--r--activerecord/lib/active_record/relation/record_fetch_warning.rb6
-rw-r--r--activerecord/lib/active_record/relation/spawn_methods.rb9
-rw-r--r--activerecord/lib/active_record/relation/where_clause.rb124
-rw-r--r--activerecord/lib/active_record/relation/where_clause_factory.rb9
-rw-r--r--activerecord/lib/active_record/result.rb60
-rw-r--r--activerecord/lib/active_record/runtime_registry.rb2
-rw-r--r--activerecord/lib/active_record/sanitization.rb371
-rw-r--r--activerecord/lib/active_record/schema.rb4
-rw-r--r--activerecord/lib/active_record/schema_dumper.rb76
-rw-r--r--activerecord/lib/active_record/schema_migration.rb6
-rw-r--r--activerecord/lib/active_record/scoping.rb12
-rw-r--r--activerecord/lib/active_record/scoping/default.rb174
-rw-r--r--activerecord/lib/active_record/scoping/named.rb22
-rw-r--r--activerecord/lib/active_record/secure_token.rb4
-rw-r--r--activerecord/lib/active_record/statement_cache.rb32
-rw-r--r--activerecord/lib/active_record/store.rb61
-rw-r--r--activerecord/lib/active_record/table_metadata.rb20
-rw-r--r--activerecord/lib/active_record/tasks/database_tasks.rb112
-rw-r--r--activerecord/lib/active_record/tasks/mysql_database_tasks.rb146
-rw-r--r--activerecord/lib/active_record/tasks/postgresql_database_tasks.rb102
-rw-r--r--activerecord/lib/active_record/tasks/sqlite_database_tasks.rb34
-rw-r--r--activerecord/lib/active_record/timestamp.rb64
-rw-r--r--activerecord/lib/active_record/touch_later.rb3
-rw-r--r--activerecord/lib/active_record/transactions.rb201
-rw-r--r--activerecord/lib/active_record/type.rb30
-rw-r--r--activerecord/lib/active_record/type/adapter_specific_registry.rb88
-rw-r--r--activerecord/lib/active_record/type/decimal_without_scale.rb9
-rw-r--r--activerecord/lib/active_record/type/hash_lookup_type_map.rb6
-rw-r--r--activerecord/lib/active_record/type/internal/abstract_json.rb6
-rw-r--r--activerecord/lib/active_record/type/serialized.rb14
-rw-r--r--activerecord/lib/active_record/type/text.rb9
-rw-r--r--activerecord/lib/active_record/type/time.rb1
-rw-r--r--activerecord/lib/active_record/type/type_map.rb26
-rw-r--r--activerecord/lib/active_record/type/unsigned_integer.rb15
-rw-r--r--activerecord/lib/active_record/type_caster.rb4
-rw-r--r--activerecord/lib/active_record/type_caster/connection.rb14
-rw-r--r--activerecord/lib/active_record/type_caster/map.rb4
-rw-r--r--activerecord/lib/active_record/validations.rb8
-rw-r--r--activerecord/lib/active_record/validations/associated.rb2
-rw-r--r--activerecord/lib/active_record/validations/presence.rb2
-rw-r--r--activerecord/lib/active_record/validations/uniqueness.rb38
-rw-r--r--activerecord/lib/active_record/version.rb2
-rw-r--r--activerecord/lib/rails/generators/active_record.rb8
-rw-r--r--activerecord/lib/rails/generators/active_record/migration.rb2
-rw-r--r--activerecord/lib/rails/generators/active_record/migration/migration_generator.rb71
-rw-r--r--activerecord/lib/rails/generators/active_record/model/model_generator.rb16
-rw-r--r--activerecord/test/active_record/connection_adapters/fake_adapter.rb2
-rw-r--r--activerecord/test/assets/schema_dump_5_1.yml345
-rw-r--r--activerecord/test/cases/adapter_test.rb150
-rw-r--r--activerecord/test/cases/adapters/mysql2/active_schema_test.rb49
-rw-r--r--activerecord/test/cases/adapters/mysql2/bind_parameter_test.rb6
-rw-r--r--activerecord/test/cases/adapters/mysql2/boolean_test.rb4
-rw-r--r--activerecord/test/cases/adapters/mysql2/case_sensitivity_test.rb38
-rw-r--r--activerecord/test/cases/adapters/mysql2/charset_collation_test.rb28
-rw-r--r--activerecord/test/cases/adapters/mysql2/connection_test.rb69
-rw-r--r--activerecord/test/cases/adapters/mysql2/datetime_precision_quoting_test.rb16
-rw-r--r--activerecord/test/cases/adapters/mysql2/enum_test.rb11
-rw-r--r--activerecord/test/cases/adapters/mysql2/explain_test.rb4
-rw-r--r--activerecord/test/cases/adapters/mysql2/json_test.rb290
-rw-r--r--activerecord/test/cases/adapters/mysql2/legacy_migration_test.rb60
-rw-r--r--activerecord/test/cases/adapters/mysql2/mysql2_adapter_test.rb29
-rw-r--r--activerecord/test/cases/adapters/mysql2/reserved_word_test.rb63
-rw-r--r--activerecord/test/cases/adapters/mysql2/schema_migrations_test.rb36
-rw-r--r--activerecord/test/cases/adapters/mysql2/schema_test.rb38
-rw-r--r--activerecord/test/cases/adapters/mysql2/sp_test.rb14
-rw-r--r--activerecord/test/cases/adapters/mysql2/sql_types_test.rb8
-rw-r--r--activerecord/test/cases/adapters/mysql2/table_options_test.rb2
-rw-r--r--activerecord/test/cases/adapters/mysql2/transaction_test.rb55
-rw-r--r--activerecord/test/cases/adapters/mysql2/unsigned_type_test.rb7
-rw-r--r--activerecord/test/cases/adapters/postgresql/active_schema_test.rb26
-rw-r--r--activerecord/test/cases/adapters/postgresql/array_test.rb139
-rw-r--r--activerecord/test/cases/adapters/postgresql/bit_string_test.rb17
-rw-r--r--activerecord/test/cases/adapters/postgresql/bytea_test.rb32
-rw-r--r--activerecord/test/cases/adapters/postgresql/case_insensitive_test.rb26
-rw-r--r--activerecord/test/cases/adapters/postgresql/change_schema_test.rb8
-rw-r--r--activerecord/test/cases/adapters/postgresql/citext_test.rb36
-rw-r--r--activerecord/test/cases/adapters/postgresql/collation_test.rb26
-rw-r--r--activerecord/test/cases/adapters/postgresql/composite_test.rb22
-rw-r--r--activerecord/test/cases/adapters/postgresql/connection_test.rb98
-rw-r--r--activerecord/test/cases/adapters/postgresql/datatype_test.rb21
-rw-r--r--activerecord/test/cases/adapters/postgresql/domain_test.rb8
-rw-r--r--activerecord/test/cases/adapters/postgresql/enum_test.rb12
-rw-r--r--activerecord/test/cases/adapters/postgresql/explain_test.rb8
-rw-r--r--activerecord/test/cases/adapters/postgresql/full_text_test.rb8
-rw-r--r--activerecord/test/cases/adapters/postgresql/geometric_test.rb100
-rw-r--r--activerecord/test/cases/adapters/postgresql/hstore_test.rb190
-rw-r--r--activerecord/test/cases/adapters/postgresql/infinity_test.rb8
-rw-r--r--activerecord/test/cases/adapters/postgresql/json_test.rb74
-rw-r--r--activerecord/test/cases/adapters/postgresql/legacy_migration_test.rb54
-rw-r--r--activerecord/test/cases/adapters/postgresql/ltree_test.rb20
-rw-r--r--activerecord/test/cases/adapters/postgresql/money_test.rb14
-rw-r--r--activerecord/test/cases/adapters/postgresql/network_test.rb44
-rw-r--r--activerecord/test/cases/adapters/postgresql/numbers_test.rb8
-rw-r--r--activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb164
-rw-r--r--activerecord/test/cases/adapters/postgresql/prepared_statements_disabled_test.rb25
-rw-r--r--activerecord/test/cases/adapters/postgresql/prepared_statements_test.rb22
-rw-r--r--activerecord/test/cases/adapters/postgresql/quoting_test.rb12
-rw-r--r--activerecord/test/cases/adapters/postgresql/range_test.rb42
-rw-r--r--activerecord/test/cases/adapters/postgresql/referential_integrity_test.rb22
-rw-r--r--activerecord/test/cases/adapters/postgresql/rename_table_test.rb14
-rw-r--r--activerecord/test/cases/adapters/postgresql/schema_authorization_test.rb24
-rw-r--r--activerecord/test/cases/adapters/postgresql/schema_test.rb110
-rw-r--r--activerecord/test/cases/adapters/postgresql/serial_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/statement_pool_test.rb12
-rw-r--r--activerecord/test/cases/adapters/postgresql/timestamp_test.rb26
-rw-r--r--activerecord/test/cases/adapters/postgresql/transaction_test.rb96
-rw-r--r--activerecord/test/cases/adapters/postgresql/type_lookup_test.rb8
-rw-r--r--activerecord/test/cases/adapters/postgresql/utils_test.rb18
-rw-r--r--activerecord/test/cases/adapters/postgresql/uuid_test.rb151
-rw-r--r--activerecord/test/cases/adapters/postgresql/xml_test.rb16
-rw-r--r--activerecord/test/cases/adapters/sqlite3/collation_test.rb26
-rw-r--r--activerecord/test/cases/adapters/sqlite3/copy_table_test.rb54
-rw-r--r--activerecord/test/cases/adapters/sqlite3/explain_test.rb8
-rw-r--r--activerecord/test/cases/adapters/sqlite3/legacy_migration_test.rb59
-rw-r--r--activerecord/test/cases/adapters/sqlite3/quoting_test.rb18
-rw-r--r--activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb194
-rw-r--r--activerecord/test/cases/adapters/sqlite3/sqlite3_create_folder_test.rb12
-rw-r--r--activerecord/test/cases/adapters/sqlite3/statement_pool_test.rb10
-rw-r--r--activerecord/test/cases/aggregations_test.rb27
-rw-r--r--activerecord/test/cases/ar_schema_test.rb40
-rw-r--r--activerecord/test/cases/associations/association_scope_test.rb8
-rw-r--r--activerecord/test/cases/associations/belongs_to_associations_test.rb238
-rw-r--r--activerecord/test/cases/associations/bidirectional_destroy_dependencies_test.rb4
-rw-r--r--activerecord/test/cases/associations/callbacks_test.rb53
-rw-r--r--activerecord/test/cases/associations/cascaded_eager_loading_test.rb80
-rw-r--r--activerecord/test/cases/associations/eager_load_includes_full_sti_class_test.rb19
-rw-r--r--activerecord/test/cases/associations/eager_load_nested_include_test.rb62
-rw-r--r--activerecord/test/cases/associations/eager_singularization_test.rb247
-rw-r--r--activerecord/test/cases/associations/eager_test.rb542
-rw-r--r--activerecord/test/cases/associations/extension_test.rb22
-rw-r--r--activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb298
-rw-r--r--activerecord/test/cases/associations/has_many_associations_test.rb708
-rw-r--r--activerecord/test/cases/associations/has_many_through_associations_test.rb428
-rw-r--r--activerecord/test/cases/associations/has_one_associations_test.rb193
-rw-r--r--activerecord/test/cases/associations/has_one_through_associations_test.rb136
-rw-r--r--activerecord/test/cases/associations/inner_join_association_test.rb54
-rw-r--r--activerecord/test/cases/associations/inverse_associations_test.rb238
-rw-r--r--activerecord/test/cases/associations/join_model_test.rb233
-rw-r--r--activerecord/test/cases/associations/left_outer_join_association_test.rb55
-rw-r--r--activerecord/test/cases/associations/nested_through_associations_test.rb120
-rw-r--r--activerecord/test/cases/associations/required_test.rb16
-rw-r--r--activerecord/test/cases/associations_test.rb162
-rw-r--r--activerecord/test/cases/attribute_decorators_test.rb50
-rw-r--r--activerecord/test/cases/attribute_methods/read_test.rb6
-rw-r--r--activerecord/test/cases/attribute_methods_test.rb500
-rw-r--r--activerecord/test/cases/attribute_set_test.rb44
-rw-r--r--activerecord/test/cases/attribute_test.rb60
-rw-r--r--activerecord/test/cases/attributes_test.rb86
-rw-r--r--activerecord/test/cases/autosave_association_test.rb473
-rw-r--r--activerecord/test/cases/base_test.rb393
-rw-r--r--activerecord/test/cases/batches_test.rb218
-rw-r--r--activerecord/test/cases/binary_test.rb18
-rw-r--r--activerecord/test/cases/bind_parameter_test.rb37
-rw-r--r--activerecord/test/cases/calculations_test.rb233
-rw-r--r--activerecord/test/cases/callbacks_test.rb58
-rw-r--r--activerecord/test/cases/clone_test.rb18
-rw-r--r--activerecord/test/cases/coders/yaml_column_test.rb4
-rw-r--r--activerecord/test/cases/collection_cache_key_test.rb6
-rw-r--r--activerecord/test/cases/column_alias_test.rb14
-rw-r--r--activerecord/test/cases/column_definition_test.rb15
-rw-r--r--activerecord/test/cases/comment_test.rb215
-rw-r--r--activerecord/test/cases/connection_adapters/adapter_leasing_test.rb14
-rw-r--r--activerecord/test/cases/connection_adapters/connection_handler_test.rb14
-rw-r--r--activerecord/test/cases/connection_adapters/connection_specification_test.rb2
-rw-r--r--activerecord/test/cases/connection_adapters/merge_and_resolve_default_url_config_test.rb110
-rw-r--r--activerecord/test/cases/connection_adapters/mysql_type_lookup_test.rb100
-rw-r--r--activerecord/test/cases/connection_adapters/quoting_test.rb13
-rw-r--r--activerecord/test/cases/connection_adapters/schema_cache_test.rb82
-rw-r--r--activerecord/test/cases/connection_adapters/type_lookup_test.rb172
-rw-r--r--activerecord/test/cases/connection_management_test.rb10
-rw-r--r--activerecord/test/cases/connection_pool_test.rb160
-rw-r--r--activerecord/test/cases/connection_specification/resolver_test.rb54
-rw-r--r--activerecord/test/cases/core_test.rb34
-rw-r--r--activerecord/test/cases/counter_cache_test.rb240
-rw-r--r--activerecord/test/cases/custom_locking_test.rb6
-rw-r--r--activerecord/test/cases/database_statements_test.rb31
-rw-r--r--activerecord/test/cases/date_test.rb8
-rw-r--r--activerecord/test/cases/date_time_precision_test.rb127
-rw-r--r--activerecord/test/cases/date_time_test.rb14
-rw-r--r--activerecord/test/cases/defaults_test.rb100
-rw-r--r--activerecord/test/cases/dirty_test.rb303
-rw-r--r--activerecord/test/cases/dup_test.rb36
-rw-r--r--activerecord/test/cases/enum_test.rb90
-rw-r--r--activerecord/test/cases/errors_test.rb2
-rw-r--r--activerecord/test/cases/explain_subscriber_test.rb18
-rw-r--r--activerecord/test/cases/explain_test.rb28
-rw-r--r--activerecord/test/cases/finder_respond_to_test.rb11
-rw-r--r--activerecord/test/cases/finder_test.rb380
-rw-r--r--activerecord/test/cases/fixture_set/file_test.rb66
-rw-r--r--activerecord/test/cases/fixtures_test.rb274
-rw-r--r--activerecord/test/cases/forbidden_attributes_protection_test.rb63
-rw-r--r--activerecord/test/cases/habtm_destroy_order_test.rb20
-rw-r--r--activerecord/test/cases/helper.rb72
-rw-r--r--activerecord/test/cases/hot_compatibility_test.rb60
-rw-r--r--activerecord/test/cases/i18n_test.rb33
-rw-r--r--activerecord/test/cases/inheritance_test.rb167
-rw-r--r--activerecord/test/cases/integration_test.rb74
-rw-r--r--activerecord/test/cases/invalid_connection_test.rb30
-rw-r--r--activerecord/test/cases/invertible_migration_test.rb75
-rw-r--r--activerecord/test/cases/json_serialization_test.rb102
-rw-r--r--activerecord/test/cases/locking_test.rb306
-rw-r--r--activerecord/test/cases/log_subscriber_test.rb20
-rw-r--r--activerecord/test/cases/migration/change_schema_test.rb134
-rw-r--r--activerecord/test/cases/migration/change_table_test.rb29
-rw-r--r--activerecord/test/cases/migration/column_attributes_test.rb63
-rw-r--r--activerecord/test/cases/migration/column_positioning_test.rb12
-rw-r--r--activerecord/test/cases/migration/columns_test.rb129
-rw-r--r--activerecord/test/cases/migration/command_recorder_test.rb44
-rw-r--r--activerecord/test/cases/migration/compatibility_test.rb34
-rw-r--r--activerecord/test/cases/migration/create_join_table_test.rb44
-rw-r--r--activerecord/test/cases/migration/foreign_key_test.rb469
-rw-r--r--activerecord/test/cases/migration/helper.rb2
-rw-r--r--activerecord/test/cases/migration/index_test.rb92
-rw-r--r--activerecord/test/cases/migration/logger_test.rb4
-rw-r--r--activerecord/test/cases/migration/pending_migrations_test.rb2
-rw-r--r--activerecord/test/cases/migration/references_foreign_key_test.rb316
-rw-r--r--activerecord/test/cases/migration/references_index_test.rb34
-rw-r--r--activerecord/test/cases/migration/references_statements_test.rb24
-rw-r--r--activerecord/test/cases/migration/rename_table_test.rb26
-rw-r--r--activerecord/test/cases/migration_test.rb218
-rw-r--r--activerecord/test/cases/migrator_test.rb102
-rw-r--r--activerecord/test/cases/mixin_test.rb1
-rw-r--r--activerecord/test/cases/modules_test.rb60
-rw-r--r--activerecord/test/cases/multiparameter_attributes_test.rb33
-rw-r--r--activerecord/test/cases/multiple_db_test.rb8
-rw-r--r--activerecord/test/cases/nested_attributes_test.rb537
-rw-r--r--activerecord/test/cases/nested_attributes_with_callbacks_test.rb32
-rw-r--r--activerecord/test/cases/persistence_test.rb235
-rw-r--r--activerecord/test/cases/pooled_connections_test.rb12
-rw-r--r--activerecord/test/cases/primary_keys_test.rb189
-rw-r--r--activerecord/test/cases/query_cache_test.rb227
-rw-r--r--activerecord/test/cases/quoting_test.rb62
-rw-r--r--activerecord/test/cases/readonly_test.rb37
-rw-r--r--activerecord/test/cases/reaper_test.rb2
-rw-r--r--activerecord/test/cases/reflection_test.rb192
-rw-r--r--activerecord/test/cases/relation/delegation_test.rb8
-rw-r--r--activerecord/test/cases/relation/merging_test.rb40
-rw-r--r--activerecord/test/cases/relation/mutation_test.rb97
-rw-r--r--activerecord/test/cases/relation/or_test.rb52
-rw-r--r--activerecord/test/cases/relation/predicate_builder_test.rb4
-rw-r--r--activerecord/test/cases/relation/record_fetch_warning_test.rb6
-rw-r--r--activerecord/test/cases/relation/where_chain_test.rb42
-rw-r--r--activerecord/test/cases/relation/where_clause_test.rb20
-rw-r--r--activerecord/test/cases/relation/where_test.rb58
-rw-r--r--activerecord/test/cases/relation_test.rb101
-rw-r--r--activerecord/test/cases/relations_test.rb676
-rw-r--r--activerecord/test/cases/reload_models_test.rb14
-rw-r--r--activerecord/test/cases/result_test.rb22
-rw-r--r--activerecord/test/cases/sanitize_test.rb104
-rw-r--r--activerecord/test/cases/schema_dumper_test.rb83
-rw-r--r--activerecord/test/cases/schema_loading_test.rb2
-rw-r--r--activerecord/test/cases/scoping/default_scoping_test.rb178
-rw-r--r--activerecord/test/cases/scoping/named_scoping_test.rb88
-rw-r--r--activerecord/test/cases/scoping/relation_scoping_test.rb131
-rw-r--r--activerecord/test/cases/secure_token_test.rb6
-rw-r--r--activerecord/test/cases/serialization_test.rb44
-rw-r--r--activerecord/test/cases/serialized_attribute_test.rb63
-rw-r--r--activerecord/test/cases/statement_cache_test.rb21
-rw-r--r--activerecord/test/cases/store_test.rb100
-rw-r--r--activerecord/test/cases/suppressor_test.rb26
-rw-r--r--activerecord/test/cases/tasks/database_tasks_test.rb166
-rw-r--r--activerecord/test/cases/tasks/mysql_rake_test.rb558
-rw-r--r--activerecord/test/cases/tasks/postgresql_rake_test.rb491
-rw-r--r--activerecord/test/cases/tasks/sqlite_rake_test.rb336
-rw-r--r--activerecord/test/cases/test_case.rb39
-rw-r--r--activerecord/test/cases/test_fixtures_test.rb22
-rw-r--r--activerecord/test/cases/time_precision_test.rb123
-rw-r--r--activerecord/test/cases/timestamp_test.rb109
-rw-r--r--activerecord/test/cases/touch_later_test.rb21
-rw-r--r--activerecord/test/cases/transaction_callbacks_test.rb135
-rw-r--r--activerecord/test/cases/transaction_isolation_test.rb20
-rw-r--r--activerecord/test/cases/transactions_test.rb153
-rw-r--r--activerecord/test/cases/type/date_time_test.rb2
-rw-r--r--activerecord/test/cases/type/integer_test.rb4
-rw-r--r--activerecord/test/cases/type/string_test.rb10
-rw-r--r--activerecord/test/cases/type/type_map_test.rb49
-rw-r--r--activerecord/test/cases/type/unsigned_integer_test.rb17
-rw-r--r--activerecord/test/cases/types_test.rb2
-rw-r--r--activerecord/test/cases/validations/absence_validation_test.rb14
-rw-r--r--activerecord/test/cases/validations/association_validation_test.rb22
-rw-r--r--activerecord/test/cases/validations/i18n_generate_message_validation_test.rb28
-rw-r--r--activerecord/test/cases/validations/i18n_validation_test.rb37
-rw-r--r--activerecord/test/cases/validations/length_validation_test.rb29
-rw-r--r--activerecord/test/cases/validations/presence_validation_test.rb16
-rw-r--r--activerecord/test/cases/validations/uniqueness_validation_test.rb110
-rw-r--r--activerecord/test/cases/validations_test.rb34
-rw-r--r--activerecord/test/cases/view_test.rb240
-rw-r--r--activerecord/test/cases/yaml_serialization_test.rb40
-rw-r--r--activerecord/test/config.example.yml3
-rw-r--r--activerecord/test/fixtures/all/namespaced/accounts.yml2
-rw-r--r--activerecord/test/fixtures/books.yml1
-rw-r--r--activerecord/test/fixtures/naked/yml/courses_with_invalid_key.yml3
-rw-r--r--activerecord/test/fixtures/other_dogs.yml2
-rw-r--r--activerecord/test/migrations/decimal/1_give_me_big_numbers.rb8
-rw-r--r--activerecord/test/migrations/magic/1_currencies_have_symbols.rb4
-rw-r--r--activerecord/test/migrations/missing/4_innocent_jointable.rb2
-rw-r--r--activerecord/test/migrations/valid/3_innocent_jointable.rb2
-rw-r--r--activerecord/test/migrations/valid_with_subdirectories/sub1/3_innocent_jointable.rb2
-rw-r--r--activerecord/test/migrations/valid_with_timestamps/20100301010101_valid_with_timestamps_innocent_jointable.rb2
-rw-r--r--activerecord/test/models/admin.rb2
-rw-r--r--activerecord/test/models/admin/user.rb16
-rw-r--r--activerecord/test/models/aircraft.rb2
-rw-r--r--activerecord/test/models/author.rb199
-rw-r--r--activerecord/test/models/bird.rb2
-rw-r--r--activerecord/test/models/book.rb7
-rw-r--r--activerecord/test/models/boolean.rb3
-rw-r--r--activerecord/test/models/bulb.rb12
-rw-r--r--activerecord/test/models/car.rb18
-rw-r--r--activerecord/test/models/categorization.rb12
-rw-r--r--activerecord/test/models/category.rb32
-rw-r--r--activerecord/test/models/citation.rb2
-rw-r--r--activerecord/test/models/club.rb14
-rw-r--r--activerecord/test/models/college.rb4
-rw-r--r--activerecord/test/models/comment.rb16
-rw-r--r--activerecord/test/models/company.rb148
-rw-r--r--activerecord/test/models/company_in_module.rb50
-rw-r--r--activerecord/test/models/computer.rb2
-rw-r--r--activerecord/test/models/contact.rb8
-rw-r--r--activerecord/test/models/content.rb6
-rw-r--r--activerecord/test/models/contract.rb2
-rw-r--r--activerecord/test/models/country.rb2
-rw-r--r--activerecord/test/models/course.rb2
-rw-r--r--activerecord/test/models/customer.rb13
-rw-r--r--activerecord/test/models/developer.rb147
-rw-r--r--activerecord/test/models/doubloon.rb2
-rw-r--r--activerecord/test/models/edge.rb4
-rw-r--r--activerecord/test/models/engine.rb3
-rw-r--r--activerecord/test/models/essay.rb6
-rw-r--r--activerecord/test/models/eye.rb6
-rw-r--r--activerecord/test/models/face.rb10
-rw-r--r--activerecord/test/models/friendship.rb6
-rw-r--r--activerecord/test/models/hotel.rb6
-rw-r--r--activerecord/test/models/interest.rb6
-rw-r--r--activerecord/test/models/invoice.rb4
-rw-r--r--activerecord/test/models/item.rb2
-rw-r--r--activerecord/test/models/job.rb6
-rw-r--r--activerecord/test/models/joke.rb4
-rw-r--r--activerecord/test/models/keyboard.rb2
-rw-r--r--activerecord/test/models/line_item.rb2
-rw-r--r--activerecord/test/models/man.rb14
-rw-r--r--activerecord/test/models/matey.rb2
-rw-r--r--activerecord/test/models/member.rb40
-rw-r--r--activerecord/test/models/membership.rb2
-rw-r--r--activerecord/test/models/mentor.rb2
-rw-r--r--activerecord/test/models/minivan.rb3
-rw-r--r--activerecord/test/models/node.rb4
-rw-r--r--activerecord/test/models/order.rb4
-rw-r--r--activerecord/test/models/organization.rb14
-rw-r--r--activerecord/test/models/other_dog.rb5
-rw-r--r--activerecord/test/models/owner.rb8
-rw-r--r--activerecord/test/models/parrot.rb11
-rw-r--r--activerecord/test/models/person.rb107
-rw-r--r--activerecord/test/models/personal_legacy_thing.rb2
-rw-r--r--activerecord/test/models/pet.rb4
-rw-r--r--activerecord/test/models/pirate.rb80
-rw-r--r--activerecord/test/models/possession.rb2
-rw-r--r--activerecord/test/models/post.rb171
-rw-r--r--activerecord/test/models/price_estimate.rb2
-rw-r--r--activerecord/test/models/professor.rb2
-rw-r--r--activerecord/test/models/project.rb24
-rw-r--r--activerecord/test/models/rating.rb2
-rw-r--r--activerecord/test/models/reader.rb10
-rw-r--r--activerecord/test/models/reference.rb6
-rw-r--r--activerecord/test/models/reply.rb24
-rw-r--r--activerecord/test/models/ship.rb14
-rw-r--r--activerecord/test/models/ship_part.rb4
-rw-r--r--activerecord/test/models/shop.rb4
-rw-r--r--activerecord/test/models/sponsor.rb10
-rw-r--r--activerecord/test/models/subject.rb8
-rw-r--r--activerecord/test/models/subscriber.rb4
-rw-r--r--activerecord/test/models/subscription.rb2
-rw-r--r--activerecord/test/models/tag.rb6
-rw-r--r--activerecord/test/models/tagging.rb12
-rw-r--r--activerecord/test/models/topic.rb34
-rw-r--r--activerecord/test/models/treasure.rb6
-rw-r--r--activerecord/test/models/treaty.rb2
-rw-r--r--activerecord/test/models/user.rb6
-rw-r--r--activerecord/test/models/vegetables.rb5
-rw-r--r--activerecord/test/models/vehicle.rb2
-rw-r--r--activerecord/test/models/vertex.rb8
-rw-r--r--activerecord/test/models/wheel.rb2
-rw-r--r--activerecord/test/models/without_table.rb2
-rw-r--r--activerecord/test/models/zine.rb2
-rw-r--r--activerecord/test/schema/mysql2_specific_schema.rb16
-rw-r--r--activerecord/test/schema/postgresql_specific_schema.rb33
-rw-r--r--activerecord/test/schema/schema.rb57
-rw-r--r--activerecord/test/support/config.rb25
-rw-r--r--activerecord/test/support/connection.rb13
551 files changed, 22601 insertions, 19377 deletions
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index 4637847987..e614458375 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,3 +1,478 @@
+* Respect precision option for arrays of timestamps.
+
+ Fixes #27514.
+
+ *Sean Griffin*
+
+* Optimize slow model instantiation when using STI and `store_full_sti_class = false` option.
+
+ *Konstantin Lazarev*
+
+* Add `touch` option to counter cache modifying methods.
+
+ Works when updating, resetting, incrementing and decrementing counters:
+
+ # Touches `updated_at`/`updated_on`.
+ Topic.increment_counter(:messages_count, 1, touch: true)
+ Topic.decrement_counter(:messages_count, 1, touch: true)
+
+ # Touches `last_discussed_at`.
+ Topic.reset_counters(18, :messages, touch: :last_discussed_at)
+
+ # Touches `updated_at` and `last_discussed_at`.
+ Topic.update_counters(18, messages_count: 5, touch: %i( updated_at last_discussed_at ))
+
+ Fixes #26724.
+
+ *Jarred Trost*
+
+* Remove deprecated `#uniq`, `#uniq!`, and `#uniq_value`.
+
+ *Ryuta Kamizono*
+
+* Remove deprecated `#insert_sql`, `#update_sql`, and `#delete_sql`.
+
+ *Ryuta Kamizono*
+
+* Remove deprecated `#use_transactional_fixtures` configuration.
+
+ *Rafael Mendonça França*
+
+* Remove deprecated `#raise_in_transactional_callbacks` configuration.
+
+ *Rafael Mendonça França*
+
+* Remove deprecated `#load_schema_for`.
+
+ *Rafael Mendonça França*
+
+* Remove deprecated conditions parameter from `#destroy_all` and `#delete_all`.
+
+ *Rafael Mendonça França*
+
+* Remove deprecated support to passing arguments to `#select` when a block is provided.
+
+ *Rafael Mendonça França*
+
+* Remove deprecated support to query using commas on LIMIT.
+
+ *Rafael Mendonça França*
+
+* Remove deprecated support to passing a class as a value in a query.
+
+ *Rafael Mendonça França*
+
+* Raise `ActiveRecord::IrreversibleOrderError` when using `last` with an irreversible
+ order.
+
+ *Rafael Mendonça França*
+
+* Raise when a `has_many :through` association has an ambiguous reflection name.
+
+ *Rafael Mendonça França*
+
+* Raise when `ActiveRecord::Migration` is inherited from directly.
+
+ *Rafael Mendonça França*
+
+* Remove deprecated `original_exception` argument in `ActiveRecord::StatementInvalid#initialize`
+ and `ActiveRecord::StatementInvalid#original_exception`.
+
+ *Rafael Mendonça França*
+
+* `#tables` and `#table_exists?` return only tables and not views.
+
+ All the deprecations on those methods were removed.
+
+ *Rafael Mendonça França*
+
+* Remove deprecated `name` argument from `#tables`.
+
+ *Rafael Mendonça França*
+
+* Remove deprecated support to passing a column to `#quote`.
+
+ *Rafael Mendonça França*
+
+* Set `:time` as a timezone aware type and remove deprecation when
+ `config.active_record.time_zone_aware_types` is not explictly set.
+
+ *Rafael Mendonça França*
+
+* Remove deprecated force reload argument in singular and collection association readers.
+
+ *Rafael Mendonça França*
+
+* Remove deprecated `activerecord.errors.messages.restrict_dependent_destroy.one` and
+ `activerecord.errors.messages.restrict_dependent_destroy.many` i18n scopes.
+
+ *Rafael Mendonça França*
+
+* Allow passing extra flags to `db:structure:load` and `db:structure:dump`
+
+ Introduces `ActiveRecord::Tasks::DatabaseTasks.structure_(load|dump)_flags` to customize the
+ eventual commands run against the database, e.g. mysqldump/pg_dump.
+
+ *Kir Shatrov*
+
+* Notifications see frozen SQL string.
+
+ Fixes #23774.
+
+ *Richard Monette*
+
+* RuntimeErrors are no longer translated to `ActiveRecord::StatementInvalid`.
+
+ *Richard Monette*
+
+* Change the schema cache format to use YAML instead of Marshal.
+
+ *Kir Shatrov*
+
+* Support index length and order options using both string and symbol
+ column names.
+
+ Fixes #27243.
+
+ *Ryuta Kamizono*
+
+* Raise `ActiveRecord::RangeError` when values that executed are out of range.
+
+ *Ryuta Kamizono*
+
+* Raise `ActiveRecord::NotNullViolation` when a record cannot be inserted
+ or updated because it would violate a not null constraint.
+
+ *Ryuta Kamizono*
+
+* Emulate db trigger behaviour for after_commit :destroy, :update.
+
+ Race conditions can occur when an ActiveRecord is destroyed
+ twice or destroyed and updated. The callbacks should only be
+ triggered once, similar to a SQL database trigger.
+
+ *Stefan Budeanu*
+
+* Moved `DecimalWithoutScale`, `Text`, and `UnsignedInteger` from Active Model to Active Record.
+
+ *Iain Beeston*
+
+* Fix `write_attribute` method to check whether an attribute is aliased or not, and
+ use the aliased attribute name if needed.
+
+ *Prathamesh Sonpatki*
+
+* Fix `read_attribute` method to check whether an attribute is aliased or not, and
+ use the aliased attribute name if needed.
+
+ Fixes #26417.
+
+ *Prathamesh Sonpatki*
+
+* PostgreSQL & MySQL: Use big integer as primary key type for new tables.
+
+ *Jon McCartie*, *Pavel Pravosud*
+
+* Change the type argument of `ActiveRecord::Base#attribute` to be optional.
+ The default is now `ActiveRecord::Type::Value.new`, which provides no type
+ casting behavior.
+
+ *Sean Griffin*
+
+* Fix that unsigned with zerofill is treated as signed.
+
+ Fixes #27125.
+
+ *Ryuta Kamizono*
+
+* Fix the uniqueness validation scope with a polymorphic association.
+
+ *Sergey Alekseev*
+
+* Raise `ActiveRecord::RecordNotFound` from collection `*_ids` setters
+ for unknown IDs with a better error message.
+
+ Changes the collection `*_ids` setters to cast provided IDs the data
+ type of the primary key set in the association, not the model
+ primary key.
+
+ *Dominic Cleal*
+
+* For PostgreSQL >= 9.4 use `pgcrypto`'s `gen_random_uuid()` instead of
+ `uuid-ossp`'s UUID generation function.
+
+ *Yuji Yaginuma*, *Yaw Boakye*
+
+* Introduce `Model#reload_<association>` to bring back the behavior
+ of `Article.category(true)` where `category` is a singular
+ association.
+
+ The force reloading of the association reader was deprecated
+ in #20888. Unfortunately the suggested alternative of
+ `article.reload.category` does not expose the same behavior.
+
+ This patch adds a reader method with the prefix `reload_` for
+ singular associations. This method has the same semantics as
+ passing true to the association reader used to have.
+
+ *Yves Senn*
+
+* Make sure eager loading `ActiveRecord::Associations` also loads
+ constants defined in `ActiveRecord::Associations::Preloader`.
+
+ *Yves Senn*
+
+* Allow `ActionController::Parameters`-like objects to be passed as
+ values for Postgres HStore columns.
+
+ Fixes #26904.
+
+ *Jon Moss*
+
+* Added `stat` method to `ActiveRecord::ConnectionAdapters::ConnectionPool`.
+
+ Example:
+
+ ActiveRecord::Base.connection_pool.stat # =>
+ { size: 15, connections: 1, busy: 1, dead: 0, idle: 0, waiting: 0, checkout_timeout: 5 }
+
+ *Pavel Evstigneev*
+
+* Avoid `unscope(:order)` when `limit_value` is presented for `count`
+ and `exists?`.
+
+ If `limit_value` is presented, records fetching order is very important
+ for performance. We should not unscope the order in the case.
+
+ *Ryuta Kamizono*
+
+* Fix an Active Record `DateTime` field `NoMethodError` caused by incomplete
+ datetime.
+
+ Fixes #24195.
+
+ *Sen Zhang*
+
+* Allow `slice` to take an array of methods(without the need for splatting).
+
+ *Cohen Carlisle*
+
+* Improved partial writes with HABTM and has many through associations
+ to fire database query only if relation has been changed.
+
+ Fixes #19663.
+
+ *Mehmet Emin İNAÇ*
+
+* Deprecate passing arguments and block at the same time to
+ `ActiveRecord::QueryMethods#select`.
+
+ *Prathamesh Sonpatki*
+
+* Optimistic locking: Added ability to update `locking_column` value.
+ Ignore optimistic locking if trying to update with new `locking_column` value.
+
+ *bogdanvlviv*
+
+* Fixed: Optimistic locking does not work well with `null` in the database.
+
+ Fixes #26024.
+
+ *bogdanvlviv*
+
+* Fixed support for case insensitive comparisons of `text` columns in
+ PostgreSQL.
+
+ *Edho Arief*
+
+* Serialize JSON attribute value `nil` as SQL `NULL`, not JSON `null`.
+
+ *Trung Duc Tran*
+
+* Return `true` from `update_attribute` when the value of the attribute
+ to be updated is unchanged.
+
+ Fixes #26593.
+
+ *Prathamesh Sonpatki*
+
+* Always store errors details information with symbols.
+
+ When the association is autosaved we were storing the details with
+ string keys. This was creating inconsistency with other details that are
+ added using the `Errors#add` method. It was also inconsistent with the
+ `Errors#messages` storage.
+
+ To fix this inconsistency we are always storing with symbols. This will
+ cause a small breaking change because in those cases the details could
+ be accessed as strings keys but now it can not.
+
+ Fix #26499.
+
+ *Rafael Mendonça França*, *Marcus Vieira*
+
+* Calling `touch` on a model using optimistic locking will now leave the model
+ in a non-dirty state with no attribute changes.
+
+ Fixes #26496.
+
+ *Jakob Skjerning*
+
+* Using a mysql2 connection after it fails to reconnect will now have an error message
+ saying the connection is closed rather than an undefined method error message.
+
+ *Dylan Thacker-Smith*
+
+* PostgreSQL array columns will now respect the encoding of strings contained
+ in the array.
+
+ Fixes #26326.
+
+ *Sean Griffin*
+
+* Inverse association instances will now be set before `after_find` or
+ `after_initialize` callbacks are run.
+
+ Fixes #26320.
+
+ *Sean Griffin*
+
+* Remove unnecessarily association load when a `belongs_to` association has already been
+ loaded then the foreign key is changed directly and the record saved.
+
+ *James Coleman*
+
+* Remove standardized column types/arguments spaces in schema dump.
+
+ *Tim Petricola*
+
+* Avoid loading records from database when they are already loaded using
+ the `pluck` method on a collection.
+
+ Fixes #25921.
+
+ *Ryuta Kamizono*
+
+* Remove text default treated as an empty string in non-strict mode for
+ consistency with other types.
+
+ Strict mode controls how MySQL handles invalid or missing values in
+ data-change statements such as INSERT or UPDATE. If strict mode is not
+ in effect, MySQL inserts adjusted values for invalid or missing values
+ and produces warnings.
+
+ def test_mysql_not_null_defaults_non_strict
+ using_strict(false) do
+ with_mysql_not_null_table do |klass|
+ record = klass.new
+ assert_nil record.non_null_integer
+ assert_nil record.non_null_string
+ assert_nil record.non_null_text
+ assert_nil record.non_null_blob
+
+ record.save!
+ record.reload
+
+ assert_equal 0, record.non_null_integer
+ assert_equal "", record.non_null_string
+ assert_equal "", record.non_null_text
+ assert_equal "", record.non_null_blob
+ end
+ end
+ end
+
+ https://dev.mysql.com/doc/refman/5.7/en/sql-mode.html#sql-mode-strict
+
+ *Ryuta Kamizono*
+
+* Sqlite3 migrations to add a column to an existing table can now be
+ successfully rolled back when the column was given and invalid column
+ type.
+
+ Fixes #26087.
+
+ *Travis O'Neill*
+
+* Deprecate `sanitize_conditions`. Use `sanitize_sql` instead.
+
+ *Ryuta Kamizono*
+
+* Doing count on relations that contain LEFT OUTER JOIN Arel node no longer
+ force a DISTINCT. This solves issues when using count after a left_joins.
+
+ *Maxime Handfield Lapointe*
+
+* RecordNotFound raised by association.find exposes `id`, `primary_key` and
+ `model` methods to be consistent with RecordNotFound raised by Record.find.
+
+ *Michel Pigassou*
+
+* Hashes can once again be passed to setters of `composed_of`, if all of the
+ mapping methods are methods implemented on `Hash`.
+
+ Fixes #25978.
+
+ *Sean Griffin*
+
+* Fix the SELECT statement in `#table_comment` for MySQL.
+
+ *Takeshi Akima*
+
+* Virtual attributes will no longer raise when read on models loaded from the
+ database.
+
+ *Sean Griffin*
+
+* Support calling the method `merge` in `scope`'s lambda.
+
+ *Yasuhiro Sugino*
+
+* Fixes multi-parameter attributes conversion with invalid params.
+
+ *Hiroyuki Ishii*
+
+* Add newline between each migration in `structure.sql`.
+
+ Keeps schema migration inserts as a single commit, but allows for easier
+ git diffing.
+
+ Fixes #25504.
+
+ *Grey Baker*, *Norberto Lopes*
+
+* The flag `error_on_ignored_order_or_limit` has been deprecated in favor of
+ the current `error_on_ignored_order`.
+
+ *Xavier Noria*
+
+* Batch processing methods support `limit`:
+
+ Post.limit(10_000).find_each do |post|
+ # ...
+ end
+
+ It also works in `find_in_batches` and `in_batches`.
+
+ *Xavier Noria*
+
+* Using `group` with an attribute that has a custom type will properly cast
+ the hash keys after calling a calculation method like `count`.
+
+ Fixes #25595.
+
+ *Sean Griffin*
+
+* Fix the generated `#to_param` method to use `omission: ''` so that
+ the resulting output is actually up to 20 characters, not
+ effectively 17 to leave room for the default "...".
+ Also call `#parameterize` before `#truncate` and make the
+ `separator: /-/` to maximize the information included in the
+ output.
+
+ Fixes #23635.
+
+ *Rob Biedenharn*
+
* Ensure concurrent invocations of the connection reaper cannot allocate the
same connection to two threads.
@@ -12,11 +487,12 @@
*Kevin McPhillips*
* Removed the unused methods `ActiveRecord::Base.connection_id` and
- `ActiveRecord::Base.connection_id=`
+ `ActiveRecord::Base.connection_id=`.
*Sean Griffin*
* Ensure hashes can be assigned to attributes created using `composed_of`.
+
Fixes #25210.
*Sean Griffin*
@@ -31,15 +507,15 @@
*Johannes Opper*
-* Introduce ActiveRecord::TransactionSerializationError for catching
+* Introduce `ActiveRecord::TransactionSerializationError` for catching
transaction serialization failures or deadlocks.
*Erol Fornoles*
-* PostgreSQL: Fix db:structure:load silent failure on SQL error
+* PostgreSQL: Fix `db:structure:load` silent failure on SQL error.
- The command line flag "-v ON_ERROR_STOP=1" should be used
- when invoking psql to make sure errors are not suppressed.
+ The command line flag `-v ON_ERROR_STOP=1` should be used
+ when invoking `psql` to make sure errors are not suppressed.
Example:
diff --git a/activerecord/MIT-LICENSE b/activerecord/MIT-LICENSE
index 1f496cf280..f9e4444f07 100644
--- a/activerecord/MIT-LICENSE
+++ b/activerecord/MIT-LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2004-2016 David Heinemeier Hansson
+Copyright (c) 2004-2017 David Heinemeier Hansson
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
diff --git a/activerecord/Rakefile b/activerecord/Rakefile
index 012db35765..ec28df8fea 100644
--- a/activerecord/Rakefile
+++ b/activerecord/Rakefile
@@ -1,4 +1,4 @@
-require 'rake/testtask'
+require "rake/testtask"
require File.expand_path(File.dirname(__FILE__)) + "/test/config"
require File.expand_path(File.dirname(__FILE__)) + "/test/support/config"
@@ -17,12 +17,12 @@ def run_without_aborting(*tasks)
abort "Errors running #{errors.join(', ')}" if errors.any?
end
-desc 'Run mysql2, sqlite, and postgresql tests by default'
-task :default => :test
+desc "Run mysql2, sqlite, and postgresql tests by default"
+task default: :test
task :package
-desc 'Run mysql2, sqlite, and postgresql tests'
+desc "Run mysql2, sqlite, and postgresql tests"
task :test do
tasks = defined?(JRUBY_VERSION) ?
%w(test_jdbcmysql test_jdbcsqlite3 test_jdbcpostgresql) :
@@ -39,19 +39,19 @@ namespace :test do
end
end
-desc 'Build MySQL and PostgreSQL test databases'
+desc "Build MySQL and PostgreSQL test databases"
namespace :db do
- task :create => ['db:mysql:build', 'db:postgresql:build']
- task :drop => ['db:mysql:drop', 'db:postgresql:drop']
+ task create: ["db:mysql:build", "db:postgresql:build"]
+ task drop: ["db:mysql:drop", "db:postgresql:drop"]
end
%w( mysql2 postgresql sqlite3 sqlite3_mem db2 oracle jdbcmysql jdbcpostgresql jdbcsqlite3 jdbcderby jdbch2 jdbchsqldb ).each do |adapter|
namespace :test do
Rake::TestTask.new(adapter => "#{adapter}:env") { |t|
- adapter_short = adapter == 'db2' ? adapter : adapter[/^[a-z0-9]+/]
- t.libs << 'test'
- t.test_files = (Dir.glob( "test/cases/**/*_test.rb" ).reject {
- |x| x =~ /\/adapters\//
+ adapter_short = adapter == "db2" ? adapter : adapter[/^[a-z0-9]+/]
+ t.libs << "test"
+ t.test_files = (Dir.glob("test/cases/**/*_test.rb").reject {
+ |x| x.include?("/adapters/")
} + Dir.glob("test/cases/adapters/#{adapter_short}/**/*_test.rb"))
t.warning = true
@@ -61,23 +61,23 @@ end
namespace :isolated do
task adapter => "#{adapter}:env" do
- adapter_short = adapter == 'db2' ? adapter : adapter[/^[a-z0-9]+/]
+ adapter_short = adapter == "db2" ? adapter : adapter[/^[a-z0-9]+/]
puts [adapter, adapter_short].inspect
(Dir["test/cases/**/*_test.rb"].reject {
- |x| x =~ /\/adapters\//
+ |x| x.include?("/adapters/")
} + Dir["test/cases/adapters/#{adapter_short}/**/*_test.rb"]).all? do |file|
- sh(Gem.ruby, '-w' ,"-Itest", file)
- end or raise "Failures"
+ sh(Gem.ruby, "-w" , "-Itest", file)
+ end || raise("Failures")
end
end
end
namespace adapter do
- task :test => "test_#{adapter}"
- task :isolated_test => "isolated_test_#{adapter}"
+ task test: "test_#{adapter}"
+ task isolated_test: "isolated_test_#{adapter}"
# Set the connection environment for the adapter
- task(:env) { ENV['ARCONN'] = adapter }
+ task(:env) { ENV["ARCONN"] = adapter }
end
# Make sure the adapter test evaluates the env setting task
@@ -87,30 +87,30 @@ end
namespace :db do
namespace :mysql do
- desc 'Build the MySQL test databases'
+ desc "Build the MySQL test databases"
task :build do
- config = ARTest.config['connections']['mysql2']
- %x( mysql --user=#{config['arunit']['username']} --password=#{config['arunit']['password']} -e "create DATABASE #{config['arunit']['database']} DEFAULT CHARACTER SET utf8 DEFAULT COLLATE utf8_unicode_ci ")
- %x( mysql --user=#{config['arunit2']['username']} --password=#{config['arunit2']['password']} -e "create DATABASE #{config['arunit2']['database']} DEFAULT CHARACTER SET utf8 DEFAULT COLLATE utf8_unicode_ci ")
+ config = ARTest.config["connections"]["mysql2"]
+ %x( mysql --user=#{config["arunit"]["username"]} --password=#{config["arunit"]["password"]} -e "create DATABASE #{config["arunit"]["database"]} DEFAULT CHARACTER SET utf8 DEFAULT COLLATE utf8_unicode_ci ")
+ %x( mysql --user=#{config["arunit2"]["username"]} --password=#{config["arunit2"]["password"]} -e "create DATABASE #{config["arunit2"]["database"]} DEFAULT CHARACTER SET utf8 DEFAULT COLLATE utf8_unicode_ci ")
end
- desc 'Drop the MySQL test databases'
+ desc "Drop the MySQL test databases"
task :drop do
- config = ARTest.config['connections']['mysql2']
- %x( mysqladmin --user=#{config['arunit']['username']} --password=#{config['arunit']['password']} -f drop #{config['arunit']['database']} )
- %x( mysqladmin --user=#{config['arunit2']['username']} --password=#{config['arunit2']['password']} -f drop #{config['arunit2']['database']} )
+ config = ARTest.config["connections"]["mysql2"]
+ %x( mysqladmin --user=#{config["arunit"]["username"]} --password=#{config["arunit"]["password"]} -f drop #{config["arunit"]["database"]} )
+ %x( mysqladmin --user=#{config["arunit2"]["username"]} --password=#{config["arunit2"]["password"]} -f drop #{config["arunit2"]["database"]} )
end
- desc 'Rebuild the MySQL test databases'
- task :rebuild => [:drop, :build]
+ desc "Rebuild the MySQL test databases"
+ task rebuild: [:drop, :build]
end
namespace :postgresql do
- desc 'Build the PostgreSQL test databases'
+ desc "Build the PostgreSQL test databases"
task :build do
- config = ARTest.config['connections']['postgresql']
- %x( createdb -E UTF8 -T template0 #{config['arunit']['database']} )
- %x( createdb -E UTF8 -T template0 #{config['arunit2']['database']} )
+ config = ARTest.config["connections"]["postgresql"]
+ %x( createdb -E UTF8 -T template0 #{config["arunit"]["database"]} )
+ %x( createdb -E UTF8 -T template0 #{config["arunit2"]["database"]} )
# prepare hstore
if %x( createdb --version ).strip.gsub(/(.*)(\d\.\d\.\d)$/, "\\2") < "9.1.0"
@@ -118,28 +118,28 @@ namespace :db do
end
end
- desc 'Drop the PostgreSQL test databases'
+ desc "Drop the PostgreSQL test databases"
task :drop do
- config = ARTest.config['connections']['postgresql']
- %x( dropdb #{config['arunit']['database']} )
- %x( dropdb #{config['arunit2']['database']} )
+ config = ARTest.config["connections"]["postgresql"]
+ %x( dropdb #{config["arunit"]["database"]} )
+ %x( dropdb #{config["arunit2"]["database"]} )
end
- desc 'Rebuild the PostgreSQL test databases'
- task :rebuild => [:drop, :build]
+ desc "Rebuild the PostgreSQL test databases"
+ task rebuild: [:drop, :build]
end
end
-task :build_mysql_databases => 'db:mysql:build'
-task :drop_mysql_databases => 'db:mysql:drop'
-task :rebuild_mysql_databases => 'db:mysql:rebuild'
+task build_mysql_databases: "db:mysql:build"
+task drop_mysql_databases: "db:mysql:drop"
+task rebuild_mysql_databases: "db:mysql:rebuild"
-task :build_postgresql_databases => 'db:postgresql:build'
-task :drop_postgresql_databases => 'db:postgresql:drop'
-task :rebuild_postgresql_databases => 'db:postgresql:rebuild'
+task build_postgresql_databases: "db:postgresql:build"
+task drop_postgresql_databases: "db:postgresql:drop"
+task rebuild_postgresql_databases: "db:postgresql:rebuild"
task :lines do
- load File.expand_path('..', File.dirname(__FILE__)) + '/tools/line_statistics'
+ load File.expand_path("..", File.dirname(__FILE__)) + "/tools/line_statistics"
files = FileList["lib/active_record/**/*.rb"]
CodeTools::LineStatistics.new(files).print_loc
end
diff --git a/activerecord/activerecord.gemspec b/activerecord/activerecord.gemspec
index 881dee13e4..0b37e9076c 100644
--- a/activerecord/activerecord.gemspec
+++ b/activerecord/activerecord.gemspec
@@ -1,28 +1,28 @@
-version = File.read(File.expand_path('../../RAILS_VERSION', __FILE__)).strip
+version = File.read(File.expand_path("../../RAILS_VERSION", __FILE__)).strip
Gem::Specification.new do |s|
s.platform = Gem::Platform::RUBY
- s.name = 'activerecord'
+ s.name = "activerecord"
s.version = version
- s.summary = 'Object-relational mapper framework (part of Rails).'
- s.description = 'Databases on Rails. Build a persistent domain model by mapping database tables to Ruby classes. Strong conventions for associations, validations, aggregations, migrations, and testing come baked-in.'
+ s.summary = "Object-relational mapper framework (part of Rails)."
+ s.description = "Databases on Rails. Build a persistent domain model by mapping database tables to Ruby classes. Strong conventions for associations, validations, aggregations, migrations, and testing come baked-in."
- s.required_ruby_version = '>= 2.2.2'
+ s.required_ruby_version = ">= 2.2.2"
- s.license = 'MIT'
+ s.license = "MIT"
- s.author = 'David Heinemeier Hansson'
- s.email = 'david@loudthinking.com'
- s.homepage = 'http://rubyonrails.org'
+ s.author = "David Heinemeier Hansson"
+ s.email = "david@loudthinking.com"
+ s.homepage = "http://rubyonrails.org"
- s.files = Dir['CHANGELOG.md', 'MIT-LICENSE', 'README.rdoc', 'examples/**/*', 'lib/**/*']
- s.require_path = 'lib'
+ s.files = Dir["CHANGELOG.md", "MIT-LICENSE", "README.rdoc", "examples/**/*", "lib/**/*"]
+ s.require_path = "lib"
s.extra_rdoc_files = %w(README.rdoc)
- s.rdoc_options.concat ['--main', 'README.rdoc']
+ s.rdoc_options.concat ["--main", "README.rdoc"]
- s.add_dependency 'activesupport', version
- s.add_dependency 'activemodel', version
+ s.add_dependency "activesupport", version
+ s.add_dependency "activemodel", version
- s.add_dependency 'arel', '~> 7.0'
+ s.add_dependency "arel", "~> 8.0"
end
diff --git a/activerecord/bin/test b/activerecord/bin/test
index 7417b068bf..3a9547e5c1 100755
--- a/activerecord/bin/test
+++ b/activerecord/bin/test
@@ -1,6 +1,8 @@
#!/usr/bin/env ruby
-COMPONENT_ROOT = File.expand_path("../../", __FILE__)
+
+COMPONENT_ROOT = File.expand_path("..", __dir__)
require File.expand_path("../tools/test", COMPONENT_ROOT)
+
module Minitest
def self.plugin_active_record_options(opts, options)
opts.separator ""
@@ -14,6 +16,4 @@ module Minitest
end
end
-Minitest.extensions.unshift 'active_record'
-
-exit Minitest.run(ARGV)
+Minitest.extensions.unshift "active_record"
diff --git a/activerecord/examples/performance.rb b/activerecord/examples/performance.rb
index c3c46c19c5..3257dd4ad7 100644
--- a/activerecord/examples/performance.rb
+++ b/activerecord/examples/performance.rb
@@ -1,10 +1,10 @@
require "active_record"
-require 'benchmark/ips'
+require "benchmark/ips"
-TIME = (ENV['BENCHMARK_TIME'] || 20).to_i
-RECORDS = (ENV['BENCHMARK_RECORDS'] || TIME*1000).to_i
+TIME = (ENV["BENCHMARK_TIME"] || 20).to_i
+RECORDS = (ENV["BENCHMARK_RECORDS"] || TIME * 1000).to_i
-conn = { adapter: 'sqlite3', database: ':memory:' }
+conn = { adapter: "sqlite3", database: ":memory:" }
ActiveRecord::Base.establish_connection(conn)
@@ -42,26 +42,26 @@ class Exhibit < ActiveRecord::Base
def self.feel(exhibits) exhibits.each(&:feel) end
end
-def progress_bar(int); print "." if (int%100).zero? ; end
+def progress_bar(int); print "." if (int % 100).zero? ; end
-puts 'Generating data...'
+puts "Generating data..."
module ActiveRecord
class Faker
- LOREM = %Q{Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse non aliquet diam. Curabitur vel urna metus, quis malesuada elit.
+ LOREM = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse non aliquet diam. Curabitur vel urna metus, quis malesuada elit.
Integer consequat tincidunt felis. Etiam non erat dolor. Vivamus imperdiet nibh sit amet diam eleifend id posuere diam malesuada. Mauris at accumsan sem.
Donec id lorem neque. Fusce erat lorem, ornare eu congue vitae, malesuada quis neque. Maecenas vel urna a velit pretium fermentum. Donec tortor enim,
tempor venenatis egestas a, tempor sed ipsum. Ut arcu justo, faucibus non imperdiet ac, interdum at diam. Pellentesque ipsum enim, venenatis ut iaculis vitae,
varius vitae sem. Sed rutrum quam ac elit euismod bibendum. Donec ultricies ultricies magna, at lacinia libero mollis aliquam. Sed ac arcu in tortor elementum
tincidunt vel interdum sem. Curabitur eget erat arcu. Praesent eget eros leo. Nam magna enim, sollicitudin vehicula scelerisque in, vulputate ut libero.
- Praesent varius tincidunt commodo}.split
+ Praesent varius tincidunt commodo".split
def self.name
- LOREM.grep(/^\w*$/).sort_by { rand }.first(2).join ' '
+ LOREM.grep(/^\w*$/).sort_by { rand }.first(2).join " "
end
def self.email
- LOREM.grep(/^\w*$/).sort_by { rand }.first(2).join('@') + ".com"
+ LOREM.grep(/^\w*$/).sort_by { rand }.first(2).join("@") + ".com"
end
end
end
@@ -72,7 +72,7 @@ end
# Using the same paragraph for all exhibits because it is very slow
# to generate unique paragraphs for all exhibits.
-notes = ActiveRecord::Faker::LOREM.join ' '
+notes = ActiveRecord::Faker::LOREM.join " "
today = Date.today
puts "Inserting #{RECORDS} users and exhibits..."
@@ -95,9 +95,9 @@ puts "Done!\n"
Benchmark.ips(TIME) do |x|
ar_obj = Exhibit.find(1)
- attrs = { name: 'sam' }
- attrs_first = { name: 'sam' }
- attrs_second = { name: 'tom' }
+ attrs = { name: "sam" }
+ attrs_first = { name: "sam" }
+ attrs_second = { name: "tom" }
exhibit = {
name: ActiveRecord::Faker.name,
notes: notes,
@@ -108,22 +108,22 @@ Benchmark.ips(TIME) do |x|
ar_obj.id
end
- x.report 'Model.new (instantiation)' do
+ x.report "Model.new (instantiation)" do
Exhibit.new
end
- x.report 'Model.new (setting attributes)' do
+ x.report "Model.new (setting attributes)" do
Exhibit.new(attrs)
end
- x.report 'Model.first' do
+ x.report "Model.first" do
Exhibit.first.look
end
- x.report 'Model.take' do
+ x.report "Model.take" do
Exhibit.take
end
-
+
x.report("Model.all limit(100)") do
Exhibit.look Exhibit.limit(100)
end
@@ -140,36 +140,36 @@ Benchmark.ips(TIME) do |x|
Exhibit.look Exhibit.limit(10000)
end
- x.report 'Model.named_scope' do
+ x.report "Model.named_scope" do
Exhibit.limit(10).with_name.with_notes
end
- x.report 'Model.create' do
+ x.report "Model.create" do
Exhibit.create(exhibit)
end
- x.report 'Resource#attributes=' do
+ x.report "Resource#attributes=" do
e = Exhibit.new(attrs_first)
e.attributes = attrs_second
end
- x.report 'Resource#update' do
- Exhibit.first.update(name: 'bob')
+ x.report "Resource#update" do
+ Exhibit.first.update(name: "bob")
end
- x.report 'Resource#destroy' do
+ x.report "Resource#destroy" do
Exhibit.first.destroy
end
- x.report 'Model.transaction' do
+ x.report "Model.transaction" do
Exhibit.transaction { Exhibit.new }
end
- x.report 'Model.find(id)' do
+ x.report "Model.find(id)" do
User.find(1)
end
- x.report 'Model.find_by_sql' do
+ x.report "Model.find_by_sql" do
Exhibit.find_by_sql("SELECT * FROM exhibits WHERE id = #{(rand * 1000 + 1).to_i}").first
end
diff --git a/activerecord/examples/simple.rb b/activerecord/examples/simple.rb
index 5a041e8417..c3648fee48 100644
--- a/activerecord/examples/simple.rb
+++ b/activerecord/examples/simple.rb
@@ -1,13 +1,13 @@
-require 'active_record'
+require "active_record"
class Person < ActiveRecord::Base
- establish_connection adapter: 'sqlite3', database: 'foobar.db'
+ establish_connection adapter: "sqlite3", database: "foobar.db"
connection.create_table table_name, force: true do |t|
t.string :name
end
end
-bob = Person.create!(name: 'bob')
+bob = Person.create!(name: "bob")
puts Person.all.inspect
bob.destroy
puts Person.all.inspect
diff --git a/activerecord/lib/active_record.rb b/activerecord/lib/active_record.rb
index baa497dc98..250f48fad9 100644
--- a/activerecord/lib/active_record.rb
+++ b/activerecord/lib/active_record.rb
@@ -1,5 +1,5 @@
#--
-# Copyright (c) 2004-2016 David Heinemeier Hansson
+# Copyright (c) 2004-2017 David Heinemeier Hansson
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -21,13 +21,13 @@
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#++
-require 'active_support'
-require 'active_support/rails'
-require 'active_model'
-require 'arel'
+require "active_support"
+require "active_support/rails"
+require "active_model"
+require "arel"
-require 'active_record/version'
-require 'active_record/attribute_set'
+require "active_record/version"
+require "active_record/attribute_set"
module ActiveRecord
extend ActiveSupport::Autoload
@@ -46,7 +46,7 @@ module ActiveRecord
autoload :Integration
autoload :LegacyYamlAdapter
autoload :Migration
- autoload :Migrator, 'active_record/migration'
+ autoload :Migrator, "active_record/migration"
autoload :ModelSchema
autoload :NestedAttributes
autoload :NoTouching
@@ -56,7 +56,7 @@ module ActiveRecord
autoload :Querying
autoload :CollectionCacheKey
autoload :ReadonlyAttributes
- autoload :RecordInvalid, 'active_record/validations'
+ autoload :RecordInvalid, "active_record/validations"
autoload :Reflection
autoload :RuntimeRegistry
autoload :Sanitization
@@ -68,7 +68,6 @@ module ActiveRecord
autoload :StatementCache
autoload :Store
autoload :Suppressor
- autoload :TableMetadata
autoload :Timestamp
autoload :Transactions
autoload :Translation
@@ -76,9 +75,9 @@ module ActiveRecord
autoload :SecureToken
eager_autoload do
- autoload :ActiveRecordError, 'active_record/errors'
- autoload :ConnectionNotEstablished, 'active_record/errors'
- autoload :ConnectionAdapters, 'active_record/connection_adapters/abstract_adapter'
+ autoload :ActiveRecordError, "active_record/errors"
+ autoload :ConnectionNotEstablished, "active_record/errors"
+ autoload :ConnectionAdapters, "active_record/connection_adapters/abstract_adapter"
autoload :Aggregations
autoload :Associations
@@ -90,7 +89,7 @@ module ActiveRecord
autoload :AssociationRelation
autoload :NullRelation
- autoload_under 'relation' do
+ autoload_under "relation" do
autoload :QueryMethods
autoload :FinderMethods
autoload :Calculations
@@ -101,11 +100,12 @@ module ActiveRecord
end
autoload :Result
+ autoload :TableMetadata
end
module Coders
- autoload :YAMLColumn, 'active_record/coders/yaml_column'
- autoload :JSON, 'active_record/coders/json'
+ autoload :YAMLColumn, "active_record/coders/yaml_column"
+ autoload :JSON, "active_record/coders/json"
end
module AttributeMethods
@@ -153,13 +153,13 @@ module ActiveRecord
extend ActiveSupport::Autoload
autoload :DatabaseTasks
- autoload :SQLiteDatabaseTasks, 'active_record/tasks/sqlite_database_tasks'
- autoload :MySQLDatabaseTasks, 'active_record/tasks/mysql_database_tasks'
+ autoload :SQLiteDatabaseTasks, "active_record/tasks/sqlite_database_tasks"
+ autoload :MySQLDatabaseTasks, "active_record/tasks/mysql_database_tasks"
autoload :PostgreSQLDatabaseTasks,
- 'active_record/tasks/postgresql_database_tasks'
+ "active_record/tasks/postgresql_database_tasks"
end
- autoload :TestFixtures, 'active_record/fixtures'
+ autoload :TestFixtures, "active_record/fixtures"
def self.eager_load!
super
@@ -176,5 +176,5 @@ ActiveSupport.on_load(:active_record) do
end
ActiveSupport.on_load(:i18n) do
- I18n.load_path << File.dirname(__FILE__) + '/active_record/locale/en.yml'
+ I18n.load_path << File.dirname(__FILE__) + "/active_record/locale/en.yml"
end
diff --git a/activerecord/lib/active_record/aggregations.rb b/activerecord/lib/active_record/aggregations.rb
index 8bed5bca28..10cbd5429c 100644
--- a/activerecord/lib/active_record/aggregations.rb
+++ b/activerecord/lib/active_record/aggregations.rb
@@ -15,266 +15,268 @@ module ActiveRecord
private
- def clear_aggregation_cache # :nodoc:
+ def clear_aggregation_cache
@aggregation_cache.clear if persisted?
end
- def init_internals # :nodoc:
+ def init_internals
@aggregation_cache = {}
super
end
- # Active Record implements aggregation through a macro-like class method called #composed_of
- # for representing attributes as value objects. It expresses relationships like "Account [is]
- # composed of Money [among other things]" or "Person [is] composed of [an] address". Each call
- # to the macro adds a description of how the value objects are created from the attributes of
- # the entity object (when the entity is initialized either as a new object or from finding an
- # existing object) and how it can be turned back into attributes (when the entity is saved to
- # the database).
- #
- # class Customer < ActiveRecord::Base
- # composed_of :balance, class_name: "Money", mapping: %w(amount currency)
- # composed_of :address, mapping: [ %w(address_street street), %w(address_city city) ]
- # end
- #
- # The customer class now has the following methods to manipulate the value objects:
- # * <tt>Customer#balance, Customer#balance=(money)</tt>
- # * <tt>Customer#address, Customer#address=(address)</tt>
- #
- # These methods will operate with value objects like the ones described below:
- #
- # class Money
- # include Comparable
- # attr_reader :amount, :currency
- # EXCHANGE_RATES = { "USD_TO_DKK" => 6 }
- #
- # def initialize(amount, currency = "USD")
- # @amount, @currency = amount, currency
- # end
- #
- # def exchange_to(other_currency)
- # exchanged_amount = (amount * EXCHANGE_RATES["#{currency}_TO_#{other_currency}"]).floor
- # Money.new(exchanged_amount, other_currency)
- # end
- #
- # def ==(other_money)
- # amount == other_money.amount && currency == other_money.currency
- # end
- #
- # def <=>(other_money)
- # if currency == other_money.currency
- # amount <=> other_money.amount
- # else
- # amount <=> other_money.exchange_to(currency).amount
- # end
- # end
- # end
- #
- # class Address
- # attr_reader :street, :city
- # def initialize(street, city)
- # @street, @city = street, city
- # end
- #
- # def close_to?(other_address)
- # city == other_address.city
- # end
- #
- # def ==(other_address)
- # city == other_address.city && street == other_address.street
- # end
- # end
- #
- # Now it's possible to access attributes from the database through the value objects instead. If
- # you choose to name the composition the same as the attribute's name, it will be the only way to
- # access that attribute. That's the case with our +balance+ attribute. You interact with the value
- # objects just like you would with any other attribute:
- #
- # customer.balance = Money.new(20) # sets the Money value object and the attribute
- # customer.balance # => Money value object
- # customer.balance.exchange_to("DKK") # => Money.new(120, "DKK")
- # customer.balance > Money.new(10) # => true
- # customer.balance == Money.new(20) # => true
- # customer.balance < Money.new(5) # => false
- #
- # Value objects can also be composed of multiple attributes, such as the case of Address. The order
- # of the mappings will determine the order of the parameters.
- #
- # customer.address_street = "Hyancintvej"
- # customer.address_city = "Copenhagen"
- # customer.address # => Address.new("Hyancintvej", "Copenhagen")
- #
- # customer.address = Address.new("May Street", "Chicago")
- # customer.address_street # => "May Street"
- # customer.address_city # => "Chicago"
- #
- # == Writing value objects
- #
- # Value objects are immutable and interchangeable objects that represent a given value, such as
- # a Money object representing $5. Two Money objects both representing $5 should be equal (through
- # methods such as <tt>==</tt> and <tt><=></tt> from Comparable if ranking makes sense). This is
- # unlike entity objects where equality is determined by identity. An entity class such as Customer can
- # easily have two different objects that both have an address on Hyancintvej. Entity identity is
- # determined by object or relational unique identifiers (such as primary keys). Normal
- # ActiveRecord::Base classes are entity objects.
- #
- # It's also important to treat the value objects as immutable. Don't allow the Money object to have
- # its amount changed after creation. Create a new Money object with the new value instead. The
- # <tt>Money#exchange_to</tt> method is an example of this. It returns a new value object instead of changing
- # its own values. Active Record won't persist value objects that have been changed through means
- # other than the writer method.
- #
- # The immutable requirement is enforced by Active Record by freezing any object assigned as a value
- # object. Attempting to change it afterwards will result in a +RuntimeError+.
- #
- # Read more about value objects on http://c2.com/cgi/wiki?ValueObject and on the dangers of not
- # keeping value objects immutable on http://c2.com/cgi/wiki?ValueObjectsShouldBeImmutable
- #
- # == Custom constructors and converters
- #
- # By default value objects are initialized by calling the <tt>new</tt> constructor of the value
- # class passing each of the mapped attributes, in the order specified by the <tt>:mapping</tt>
- # option, as arguments. If the value class doesn't support this convention then #composed_of allows
- # a custom constructor to be specified.
- #
- # When a new value is assigned to the value object, the default assumption is that the new value
- # is an instance of the value class. Specifying a custom converter allows the new value to be automatically
- # converted to an instance of value class if necessary.
- #
- # For example, the +NetworkResource+ model has +network_address+ and +cidr_range+ attributes that should be
- # aggregated using the +NetAddr::CIDR+ value class (http://www.rubydoc.info/gems/netaddr/1.5.0/NetAddr/CIDR).
- # The constructor for the value class is called +create+ and it expects a CIDR address string as a parameter.
- # New values can be assigned to the value object using either another +NetAddr::CIDR+ object, a string
- # or an array. The <tt>:constructor</tt> and <tt>:converter</tt> options can be used to meet
- # these requirements:
- #
- # class NetworkResource < ActiveRecord::Base
- # composed_of :cidr,
- # class_name: 'NetAddr::CIDR',
- # mapping: [ %w(network_address network), %w(cidr_range bits) ],
- # allow_nil: true,
- # constructor: Proc.new { |network_address, cidr_range| NetAddr::CIDR.create("#{network_address}/#{cidr_range}") },
- # converter: Proc.new { |value| NetAddr::CIDR.create(value.is_a?(Array) ? value.join('/') : value) }
- # end
- #
- # # This calls the :constructor
- # network_resource = NetworkResource.new(network_address: '192.168.0.1', cidr_range: 24)
- #
- # # These assignments will both use the :converter
- # network_resource.cidr = [ '192.168.2.1', 8 ]
- # network_resource.cidr = '192.168.0.1/24'
- #
- # # This assignment won't use the :converter as the value is already an instance of the value class
- # network_resource.cidr = NetAddr::CIDR.create('192.168.2.1/8')
- #
- # # Saving and then reloading will use the :constructor on reload
- # network_resource.save
- # network_resource.reload
- #
- # == Finding records by a value object
- #
- # Once a #composed_of relationship is specified for a model, records can be loaded from the database
- # by specifying an instance of the value object in the conditions hash. The following example
- # finds all customers with +balance_amount+ equal to 20 and +balance_currency+ equal to "USD":
- #
- # Customer.where(balance: Money.new(20, "USD"))
- #
- module ClassMethods
- # Adds reader and writer methods for manipulating a value object:
- # <tt>composed_of :address</tt> adds <tt>address</tt> and <tt>address=(new_address)</tt> methods.
- #
- # Options are:
- # * <tt>:class_name</tt> - Specifies the class name of the association. Use it only if that name
- # can't be inferred from the part id. So <tt>composed_of :address</tt> will by default be linked
- # to the Address class, but if the real class name is +CompanyAddress+, you'll have to specify it
- # with this option.
- # * <tt>:mapping</tt> - Specifies the mapping of entity attributes to attributes of the value
- # object. Each mapping is represented as an array where the first item is the name of the
- # entity attribute and the second item is the name of the attribute in the value object. The
- # order in which mappings are defined determines the order in which attributes are sent to the
- # value class constructor.
- # * <tt>:allow_nil</tt> - Specifies that the value object will not be instantiated when all mapped
- # attributes are +nil+. Setting the value object to +nil+ has the effect of writing +nil+ to all
- # mapped attributes.
- # This defaults to +false+.
- # * <tt>:constructor</tt> - A symbol specifying the name of the constructor method or a Proc that
- # is called to initialize the value object. The constructor is passed all of the mapped attributes,
- # in the order that they are defined in the <tt>:mapping option</tt>, as arguments and uses them
- # to instantiate a <tt>:class_name</tt> object.
- # The default is <tt>:new</tt>.
- # * <tt>:converter</tt> - A symbol specifying the name of a class method of <tt>:class_name</tt>
- # or a Proc that is called when a new value is assigned to the value object. The converter is
- # passed the single value that is used in the assignment and is only called if the new value is
- # not an instance of <tt>:class_name</tt>. If <tt>:allow_nil</tt> is set to true, the converter
- # can return nil to skip the assignment.
- #
- # Option examples:
- # composed_of :temperature, mapping: %w(reading celsius)
- # composed_of :balance, class_name: "Money", mapping: %w(balance amount),
- # converter: Proc.new { |balance| balance.to_money }
- # composed_of :address, mapping: [ %w(address_street street), %w(address_city city) ]
- # composed_of :gps_location
- # composed_of :gps_location, allow_nil: true
- # composed_of :ip_address,
- # class_name: 'IPAddr',
- # mapping: %w(ip to_i),
- # constructor: Proc.new { |ip| IPAddr.new(ip, Socket::AF_INET) },
- # converter: Proc.new { |ip| ip.is_a?(Integer) ? IPAddr.new(ip, Socket::AF_INET) : IPAddr.new(ip.to_s) }
- #
- def composed_of(part_id, options = {})
- options.assert_valid_keys(:class_name, :mapping, :allow_nil, :constructor, :converter)
+ # Active Record implements aggregation through a macro-like class method called #composed_of
+ # for representing attributes as value objects. It expresses relationships like "Account [is]
+ # composed of Money [among other things]" or "Person [is] composed of [an] address". Each call
+ # to the macro adds a description of how the value objects are created from the attributes of
+ # the entity object (when the entity is initialized either as a new object or from finding an
+ # existing object) and how it can be turned back into attributes (when the entity is saved to
+ # the database).
+ #
+ # class Customer < ActiveRecord::Base
+ # composed_of :balance, class_name: "Money", mapping: %w(amount currency)
+ # composed_of :address, mapping: [ %w(address_street street), %w(address_city city) ]
+ # end
+ #
+ # The customer class now has the following methods to manipulate the value objects:
+ # * <tt>Customer#balance, Customer#balance=(money)</tt>
+ # * <tt>Customer#address, Customer#address=(address)</tt>
+ #
+ # These methods will operate with value objects like the ones described below:
+ #
+ # class Money
+ # include Comparable
+ # attr_reader :amount, :currency
+ # EXCHANGE_RATES = { "USD_TO_DKK" => 6 }
+ #
+ # def initialize(amount, currency = "USD")
+ # @amount, @currency = amount, currency
+ # end
+ #
+ # def exchange_to(other_currency)
+ # exchanged_amount = (amount * EXCHANGE_RATES["#{currency}_TO_#{other_currency}"]).floor
+ # Money.new(exchanged_amount, other_currency)
+ # end
+ #
+ # def ==(other_money)
+ # amount == other_money.amount && currency == other_money.currency
+ # end
+ #
+ # def <=>(other_money)
+ # if currency == other_money.currency
+ # amount <=> other_money.amount
+ # else
+ # amount <=> other_money.exchange_to(currency).amount
+ # end
+ # end
+ # end
+ #
+ # class Address
+ # attr_reader :street, :city
+ # def initialize(street, city)
+ # @street, @city = street, city
+ # end
+ #
+ # def close_to?(other_address)
+ # city == other_address.city
+ # end
+ #
+ # def ==(other_address)
+ # city == other_address.city && street == other_address.street
+ # end
+ # end
+ #
+ # Now it's possible to access attributes from the database through the value objects instead. If
+ # you choose to name the composition the same as the attribute's name, it will be the only way to
+ # access that attribute. That's the case with our +balance+ attribute. You interact with the value
+ # objects just like you would with any other attribute:
+ #
+ # customer.balance = Money.new(20) # sets the Money value object and the attribute
+ # customer.balance # => Money value object
+ # customer.balance.exchange_to("DKK") # => Money.new(120, "DKK")
+ # customer.balance > Money.new(10) # => true
+ # customer.balance == Money.new(20) # => true
+ # customer.balance < Money.new(5) # => false
+ #
+ # Value objects can also be composed of multiple attributes, such as the case of Address. The order
+ # of the mappings will determine the order of the parameters.
+ #
+ # customer.address_street = "Hyancintvej"
+ # customer.address_city = "Copenhagen"
+ # customer.address # => Address.new("Hyancintvej", "Copenhagen")
+ #
+ # customer.address = Address.new("May Street", "Chicago")
+ # customer.address_street # => "May Street"
+ # customer.address_city # => "Chicago"
+ #
+ # == Writing value objects
+ #
+ # Value objects are immutable and interchangeable objects that represent a given value, such as
+ # a Money object representing $5. Two Money objects both representing $5 should be equal (through
+ # methods such as <tt>==</tt> and <tt><=></tt> from Comparable if ranking makes sense). This is
+ # unlike entity objects where equality is determined by identity. An entity class such as Customer can
+ # easily have two different objects that both have an address on Hyancintvej. Entity identity is
+ # determined by object or relational unique identifiers (such as primary keys). Normal
+ # ActiveRecord::Base classes are entity objects.
+ #
+ # It's also important to treat the value objects as immutable. Don't allow the Money object to have
+ # its amount changed after creation. Create a new Money object with the new value instead. The
+ # <tt>Money#exchange_to</tt> method is an example of this. It returns a new value object instead of changing
+ # its own values. Active Record won't persist value objects that have been changed through means
+ # other than the writer method.
+ #
+ # The immutable requirement is enforced by Active Record by freezing any object assigned as a value
+ # object. Attempting to change it afterwards will result in a +RuntimeError+.
+ #
+ # Read more about value objects on http://c2.com/cgi/wiki?ValueObject and on the dangers of not
+ # keeping value objects immutable on http://c2.com/cgi/wiki?ValueObjectsShouldBeImmutable
+ #
+ # == Custom constructors and converters
+ #
+ # By default value objects are initialized by calling the <tt>new</tt> constructor of the value
+ # class passing each of the mapped attributes, in the order specified by the <tt>:mapping</tt>
+ # option, as arguments. If the value class doesn't support this convention then #composed_of allows
+ # a custom constructor to be specified.
+ #
+ # When a new value is assigned to the value object, the default assumption is that the new value
+ # is an instance of the value class. Specifying a custom converter allows the new value to be automatically
+ # converted to an instance of value class if necessary.
+ #
+ # For example, the +NetworkResource+ model has +network_address+ and +cidr_range+ attributes that should be
+ # aggregated using the +NetAddr::CIDR+ value class (http://www.rubydoc.info/gems/netaddr/1.5.0/NetAddr/CIDR).
+ # The constructor for the value class is called +create+ and it expects a CIDR address string as a parameter.
+ # New values can be assigned to the value object using either another +NetAddr::CIDR+ object, a string
+ # or an array. The <tt>:constructor</tt> and <tt>:converter</tt> options can be used to meet
+ # these requirements:
+ #
+ # class NetworkResource < ActiveRecord::Base
+ # composed_of :cidr,
+ # class_name: 'NetAddr::CIDR',
+ # mapping: [ %w(network_address network), %w(cidr_range bits) ],
+ # allow_nil: true,
+ # constructor: Proc.new { |network_address, cidr_range| NetAddr::CIDR.create("#{network_address}/#{cidr_range}") },
+ # converter: Proc.new { |value| NetAddr::CIDR.create(value.is_a?(Array) ? value.join('/') : value) }
+ # end
+ #
+ # # This calls the :constructor
+ # network_resource = NetworkResource.new(network_address: '192.168.0.1', cidr_range: 24)
+ #
+ # # These assignments will both use the :converter
+ # network_resource.cidr = [ '192.168.2.1', 8 ]
+ # network_resource.cidr = '192.168.0.1/24'
+ #
+ # # This assignment won't use the :converter as the value is already an instance of the value class
+ # network_resource.cidr = NetAddr::CIDR.create('192.168.2.1/8')
+ #
+ # # Saving and then reloading will use the :constructor on reload
+ # network_resource.save
+ # network_resource.reload
+ #
+ # == Finding records by a value object
+ #
+ # Once a #composed_of relationship is specified for a model, records can be loaded from the database
+ # by specifying an instance of the value object in the conditions hash. The following example
+ # finds all customers with +balance_amount+ equal to 20 and +balance_currency+ equal to "USD":
+ #
+ # Customer.where(balance: Money.new(20, "USD"))
+ #
+ module ClassMethods
+ # Adds reader and writer methods for manipulating a value object:
+ # <tt>composed_of :address</tt> adds <tt>address</tt> and <tt>address=(new_address)</tt> methods.
+ #
+ # Options are:
+ # * <tt>:class_name</tt> - Specifies the class name of the association. Use it only if that name
+ # can't be inferred from the part id. So <tt>composed_of :address</tt> will by default be linked
+ # to the Address class, but if the real class name is +CompanyAddress+, you'll have to specify it
+ # with this option.
+ # * <tt>:mapping</tt> - Specifies the mapping of entity attributes to attributes of the value
+ # object. Each mapping is represented as an array where the first item is the name of the
+ # entity attribute and the second item is the name of the attribute in the value object. The
+ # order in which mappings are defined determines the order in which attributes are sent to the
+ # value class constructor.
+ # * <tt>:allow_nil</tt> - Specifies that the value object will not be instantiated when all mapped
+ # attributes are +nil+. Setting the value object to +nil+ has the effect of writing +nil+ to all
+ # mapped attributes.
+ # This defaults to +false+.
+ # * <tt>:constructor</tt> - A symbol specifying the name of the constructor method or a Proc that
+ # is called to initialize the value object. The constructor is passed all of the mapped attributes,
+ # in the order that they are defined in the <tt>:mapping option</tt>, as arguments and uses them
+ # to instantiate a <tt>:class_name</tt> object.
+ # The default is <tt>:new</tt>.
+ # * <tt>:converter</tt> - A symbol specifying the name of a class method of <tt>:class_name</tt>
+ # or a Proc that is called when a new value is assigned to the value object. The converter is
+ # passed the single value that is used in the assignment and is only called if the new value is
+ # not an instance of <tt>:class_name</tt>. If <tt>:allow_nil</tt> is set to true, the converter
+ # can return +nil+ to skip the assignment.
+ #
+ # Option examples:
+ # composed_of :temperature, mapping: %w(reading celsius)
+ # composed_of :balance, class_name: "Money", mapping: %w(balance amount),
+ # converter: Proc.new { |balance| balance.to_money }
+ # composed_of :address, mapping: [ %w(address_street street), %w(address_city city) ]
+ # composed_of :gps_location
+ # composed_of :gps_location, allow_nil: true
+ # composed_of :ip_address,
+ # class_name: 'IPAddr',
+ # mapping: %w(ip to_i),
+ # constructor: Proc.new { |ip| IPAddr.new(ip, Socket::AF_INET) },
+ # converter: Proc.new { |ip| ip.is_a?(Integer) ? IPAddr.new(ip, Socket::AF_INET) : IPAddr.new(ip.to_s) }
+ #
+ def composed_of(part_id, options = {})
+ options.assert_valid_keys(:class_name, :mapping, :allow_nil, :constructor, :converter)
- name = part_id.id2name
- class_name = options[:class_name] || name.camelize
- mapping = options[:mapping] || [ name, name ]
- mapping = [ mapping ] unless mapping.first.is_a?(Array)
- allow_nil = options[:allow_nil] || false
- constructor = options[:constructor] || :new
- converter = options[:converter]
+ name = part_id.id2name
+ class_name = options[:class_name] || name.camelize
+ mapping = options[:mapping] || [ name, name ]
+ mapping = [ mapping ] unless mapping.first.is_a?(Array)
+ allow_nil = options[:allow_nil] || false
+ constructor = options[:constructor] || :new
+ converter = options[:converter]
- reader_method(name, class_name, mapping, allow_nil, constructor)
- writer_method(name, class_name, mapping, allow_nil, converter)
+ reader_method(name, class_name, mapping, allow_nil, constructor)
+ writer_method(name, class_name, mapping, allow_nil, converter)
- reflection = ActiveRecord::Reflection.create(:composed_of, part_id, nil, options, self)
- Reflection.add_aggregate_reflection self, part_id, reflection
- end
+ reflection = ActiveRecord::Reflection.create(:composed_of, part_id, nil, options, self)
+ Reflection.add_aggregate_reflection self, part_id, reflection
+ end
- private
- def reader_method(name, class_name, mapping, allow_nil, constructor)
- define_method(name) do
- if @aggregation_cache[name].nil? && (!allow_nil || mapping.any? {|key, _| !_read_attribute(key).nil? })
- attrs = mapping.collect {|key, _| _read_attribute(key)}
- object = constructor.respond_to?(:call) ?
- constructor.call(*attrs) :
- class_name.constantize.send(constructor, *attrs)
- @aggregation_cache[name] = object
+ private
+ def reader_method(name, class_name, mapping, allow_nil, constructor)
+ define_method(name) do
+ if @aggregation_cache[name].nil? && (!allow_nil || mapping.any? { |key, _| !_read_attribute(key).nil? })
+ attrs = mapping.collect { |key, _| _read_attribute(key) }
+ object = constructor.respond_to?(:call) ?
+ constructor.call(*attrs) :
+ class_name.constantize.send(constructor, *attrs)
+ @aggregation_cache[name] = object
+ end
+ @aggregation_cache[name]
end
- @aggregation_cache[name]
end
- end
- def writer_method(name, class_name, mapping, allow_nil, converter)
- define_method("#{name}=") do |part|
- klass = class_name.constantize
+ def writer_method(name, class_name, mapping, allow_nil, converter)
+ define_method("#{name}=") do |part|
+ klass = class_name.constantize
- unless part.is_a?(klass) || converter.nil? || part.nil?
- part = converter.respond_to?(:call) ? converter.call(part) : klass.send(converter, part)
- end
+ unless part.is_a?(klass) || converter.nil? || part.nil?
+ part = converter.respond_to?(:call) ? converter.call(part) : klass.send(converter, part)
+ end
- if part.is_a?(Hash)
- raise ArgumentError unless part.size == part.keys.max
- part = klass.new(*part.sort.map(&:last))
- end
+ hash_from_multiparameter_assignment = part.is_a?(Hash) &&
+ part.each_key.all? { |k| k.is_a?(Integer) }
+ if hash_from_multiparameter_assignment
+ raise ArgumentError unless part.size == part.each_key.max
+ part = klass.new(*part.sort.map(&:last))
+ end
- if part.nil? && allow_nil
- mapping.each { |key, _| self[key] = nil }
- @aggregation_cache[name] = nil
- else
- mapping.each { |key, value| self[key] = part.send(value) }
- @aggregation_cache[name] = part.freeze
+ if part.nil? && allow_nil
+ mapping.each { |key, _| self[key] = nil }
+ @aggregation_cache[name] = nil
+ else
+ mapping.each { |key, value| self[key] = part.send(value) }
+ @aggregation_cache[name] = part.freeze
+ end
end
end
- end
- end
+ end
end
end
diff --git a/activerecord/lib/active_record/association_relation.rb b/activerecord/lib/active_record/association_relation.rb
index c18e88e4cf..de2d03cd0b 100644
--- a/activerecord/lib/active_record/association_relation.rb
+++ b/activerecord/lib/active_record/association_relation.rb
@@ -28,8 +28,11 @@ module ActiveRecord
private
- def exec_queries
- super.each { |r| @association.set_inverse_instance r }
- end
+ def exec_queries
+ super do |r|
+ @association.set_inverse_instance r
+ yield r if block_given?
+ end
+ end
end
end
diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb
index 3729e22e64..1db5fc0dd1 100644
--- a/activerecord/lib/active_record/associations.rb
+++ b/activerecord/lib/active_record/associations.rb
@@ -1,7 +1,7 @@
-require 'active_support/core_ext/enumerable'
-require 'active_support/core_ext/string/conversions'
-require 'active_support/core_ext/module/remove_method'
-require 'active_record/errors'
+require "active_support/core_ext/enumerable"
+require "active_support/core_ext/string/conversions"
+require "active_support/core_ext/module/remove_method"
+require "active_record/errors"
module ActiveRecord
class AssociationNotFoundError < ConfigurationError #:nodoc:
@@ -90,7 +90,7 @@ module ActiveRecord
through_reflection = reflection.through_reflection
source_reflection_names = reflection.source_reflection_names
source_associations = reflection.through_reflection.klass._reflections.keys
- super("Could not find the source association(s) #{source_reflection_names.collect(&:inspect).to_sentence(:two_words_connector => ' or ', :last_word_connector => ', or ', :locale => :en)} in model #{through_reflection.klass}. Try 'has_many #{reflection.name.inspect}, :through => #{through_reflection.name.inspect}, :source => <name>'. Is it one of #{source_associations.to_sentence(:two_words_connector => ' or ', :last_word_connector => ', or ', :locale => :en)}?")
+ super("Could not find the source association(s) #{source_reflection_names.collect(&:inspect).to_sentence(two_words_connector: ' or ', last_word_connector: ', or ', locale: :en)} in model #{through_reflection.klass}. Try 'has_many #{reflection.name.inspect}, :through => #{through_reflection.name.inspect}, :source => <name>'. Is it one of #{source_associations.to_sentence(two_words_connector: ' or ', last_word_connector: ', or ', locale: :en)}?")
else
super("Could not find the source association(s).")
end
@@ -107,6 +107,21 @@ module ActiveRecord
end
end
+ class AmbiguousSourceReflectionForThroughAssociation < ActiveRecordError # :nodoc:
+ def initialize(klass, macro, association_name, options, possible_sources)
+ example_options = options.dup
+ example_options[:source] = possible_sources.first
+
+ super("Ambiguous source reflection for through association. Please " \
+ "specify a :source directive on your declaration like:\n" \
+ "\n" \
+ " class #{klass} < ActiveRecord::Base\n" \
+ " #{macro} :#{association_name}, #{example_options}\n" \
+ " end"
+ )
+ end
+ end
+
class HasManyThroughCantAssociateThroughHasOneOrManyReflection < ThroughCantAssociateThroughHasOneOrManyReflection #:nodoc:
end
@@ -207,14 +222,14 @@ module ActiveRecord
autoload :ThroughAssociation
module Builder #:nodoc:
- autoload :Association, 'active_record/associations/builder/association'
- autoload :SingularAssociation, 'active_record/associations/builder/singular_association'
- autoload :CollectionAssociation, 'active_record/associations/builder/collection_association'
+ autoload :Association, "active_record/associations/builder/association"
+ autoload :SingularAssociation, "active_record/associations/builder/singular_association"
+ autoload :CollectionAssociation, "active_record/associations/builder/collection_association"
- autoload :BelongsTo, 'active_record/associations/builder/belongs_to'
- autoload :HasOne, 'active_record/associations/builder/has_one'
- autoload :HasMany, 'active_record/associations/builder/has_many'
- autoload :HasAndBelongsToMany, 'active_record/associations/builder/has_and_belongs_to_many'
+ autoload :BelongsTo, "active_record/associations/builder/belongs_to"
+ autoload :HasOne, "active_record/associations/builder/has_one"
+ autoload :HasMany, "active_record/associations/builder/has_many"
+ autoload :HasAndBelongsToMany, "active_record/associations/builder/has_and_belongs_to_many"
end
eager_autoload do
@@ -224,6 +239,11 @@ module ActiveRecord
autoload :AliasTracker
end
+ def self.eager_load!
+ super
+ Preloader.eager_load!
+ end
+
# Returns the association instance for the given name, instantiating it if it doesn't already exist
def association(name) #:nodoc:
association = association_instance_get(name)
@@ -255,16 +275,16 @@ module ActiveRecord
private
# Clears out the association cache.
- def clear_association_cache # :nodoc:
+ def clear_association_cache
@association_cache.clear if persisted?
end
- def init_internals # :nodoc:
+ def init_internals
@association_cache = {}
super
end
- # Returns the specified association instance if it exists, nil otherwise.
+ # Returns the specified association instance if it exists, +nil+ otherwise.
def association_instance_get(name)
@association_cache[name]
end
@@ -274,1560 +294,1561 @@ module ActiveRecord
@association_cache[name] = association
end
- # \Associations are a set of macro-like class methods for tying objects together through
- # foreign keys. They express relationships like "Project has one Project Manager"
- # or "Project belongs to a Portfolio". Each macro adds a number of methods to the
- # class which are specialized according to the collection or association symbol and the
- # options hash. It works much the same way as Ruby's own <tt>attr*</tt>
- # methods.
- #
- # class Project < ActiveRecord::Base
- # belongs_to :portfolio
- # has_one :project_manager
- # has_many :milestones
- # has_and_belongs_to_many :categories
- # end
- #
- # The project class now has the following methods (and more) to ease the traversal and
- # manipulation of its relationships:
- # * <tt>Project#portfolio, Project#portfolio=(portfolio), Project#portfolio.nil?</tt>
- # * <tt>Project#project_manager, Project#project_manager=(project_manager), Project#project_manager.nil?,</tt>
- # * <tt>Project#milestones.empty?, Project#milestones.size, Project#milestones, Project#milestones<<(milestone),</tt>
- # <tt>Project#milestones.delete(milestone), Project#milestones.destroy(milestone), Project#milestones.find(milestone_id),</tt>
- # <tt>Project#milestones.build, Project#milestones.create</tt>
- # * <tt>Project#categories.empty?, Project#categories.size, Project#categories, Project#categories<<(category1),</tt>
- # <tt>Project#categories.delete(category1), Project#categories.destroy(category1)</tt>
- #
- # === A word of warning
- #
- # Don't create associations that have the same name as {instance methods}[rdoc-ref:ActiveRecord::Core] of
- # <tt>ActiveRecord::Base</tt>. Since the association adds a method with that name to
- # its model, using an association with the same name as one provided by <tt>ActiveRecord::Base</tt> will override the method inherited through <tt>ActiveRecord::Base</tt> and will break things.
- # For instance, +attributes+ and +connection+ would be bad choices for association names, because those names already exist in the list of <tt>ActiveRecord::Base</tt> instance methods.
- #
- # == Auto-generated methods
- # See also Instance Public methods below for more details.
- #
- # === Singular associations (one-to-one)
- # | | belongs_to |
- # generated methods | belongs_to | :polymorphic | has_one
- # ----------------------------------+------------+--------------+---------
- # other(force_reload=false) | X | X | X
- # other=(other) | X | X | X
- # build_other(attributes={}) | X | | X
- # create_other(attributes={}) | X | | X
- # create_other!(attributes={}) | X | | X
- #
- # === Collection associations (one-to-many / many-to-many)
- # | | | has_many
- # generated methods | habtm | has_many | :through
- # ----------------------------------+-------+----------+----------
- # others(force_reload=false) | X | X | X
- # others=(other,other,...) | X | X | X
- # other_ids | X | X | X
- # other_ids=(id,id,...) | X | X | X
- # others<< | X | X | X
- # others.push | X | X | X
- # others.concat | X | X | X
- # others.build(attributes={}) | X | X | X
- # others.create(attributes={}) | X | X | X
- # others.create!(attributes={}) | X | X | X
- # others.size | X | X | X
- # others.length | X | X | X
- # others.count | X | X | X
- # others.sum(*args) | X | X | X
- # others.empty? | X | X | X
- # others.clear | X | X | X
- # others.delete(other,other,...) | X | X | X
- # others.delete_all | X | X | X
- # others.destroy(other,other,...) | X | X | X
- # others.destroy_all | X | X | X
- # others.find(*args) | X | X | X
- # others.exists? | X | X | X
- # others.distinct | X | X | X
- # others.reset | X | X | X
- #
- # === Overriding generated methods
- #
- # Association methods are generated in a module that is included into the model class,
- # which allows you to easily override with your own methods and call the original
- # generated method with +super+. For example:
- #
- # class Car < ActiveRecord::Base
- # belongs_to :owner
- # belongs_to :old_owner
- # def owner=(new_owner)
- # self.old_owner = self.owner
- # super
- # end
- # end
- #
- # If your model class is <tt>Project</tt>, the module is
- # named <tt>Project::GeneratedAssociationMethods</tt>. The +GeneratedAssociationMethods+ module is
- # included in the model class immediately after the (anonymous) generated attributes methods
- # module, meaning an association will override the methods for an attribute with the same name.
- #
- # == Cardinality and associations
- #
- # Active Record associations can be used to describe one-to-one, one-to-many and many-to-many
- # relationships between models. Each model uses an association to describe its role in
- # the relation. The #belongs_to association is always used in the model that has
- # the foreign key.
- #
- # === One-to-one
- #
- # Use #has_one in the base, and #belongs_to in the associated model.
- #
- # class Employee < ActiveRecord::Base
- # has_one :office
- # end
- # class Office < ActiveRecord::Base
- # belongs_to :employee # foreign key - employee_id
- # end
- #
- # === One-to-many
- #
- # Use #has_many in the base, and #belongs_to in the associated model.
- #
- # class Manager < ActiveRecord::Base
- # has_many :employees
- # end
- # class Employee < ActiveRecord::Base
- # belongs_to :manager # foreign key - manager_id
- # end
- #
- # === Many-to-many
- #
- # There are two ways to build a many-to-many relationship.
- #
- # The first way uses a #has_many association with the <tt>:through</tt> option and a join model, so
- # there are two stages of associations.
- #
- # class Assignment < ActiveRecord::Base
- # belongs_to :programmer # foreign key - programmer_id
- # belongs_to :project # foreign key - project_id
- # end
- # class Programmer < ActiveRecord::Base
- # has_many :assignments
- # has_many :projects, through: :assignments
- # end
- # class Project < ActiveRecord::Base
- # has_many :assignments
- # has_many :programmers, through: :assignments
- # end
- #
- # For the second way, use #has_and_belongs_to_many in both models. This requires a join table
- # that has no corresponding model or primary key.
- #
- # class Programmer < ActiveRecord::Base
- # has_and_belongs_to_many :projects # foreign keys in the join table
- # end
- # class Project < ActiveRecord::Base
- # has_and_belongs_to_many :programmers # foreign keys in the join table
- # end
- #
- # Choosing which way to build a many-to-many relationship is not always simple.
- # If you need to work with the relationship model as its own entity,
- # use #has_many <tt>:through</tt>. Use #has_and_belongs_to_many when working with legacy schemas or when
- # you never work directly with the relationship itself.
- #
- # == Is it a #belongs_to or #has_one association?
- #
- # Both express a 1-1 relationship. The difference is mostly where to place the foreign
- # key, which goes on the table for the class declaring the #belongs_to relationship.
- #
- # class User < ActiveRecord::Base
- # # I reference an account.
- # belongs_to :account
- # end
- #
- # class Account < ActiveRecord::Base
- # # One user references me.
- # has_one :user
- # end
- #
- # The tables for these classes could look something like:
- #
- # CREATE TABLE users (
- # id int NOT NULL auto_increment,
- # account_id int default NULL,
- # name varchar default NULL,
- # PRIMARY KEY (id)
- # )
- #
- # CREATE TABLE accounts (
- # id int NOT NULL auto_increment,
- # name varchar default NULL,
- # PRIMARY KEY (id)
- # )
- #
- # == Unsaved objects and associations
- #
- # You can manipulate objects and associations before they are saved to the database, but
- # there is some special behavior you should be aware of, mostly involving the saving of
- # associated objects.
- #
- # You can set the <tt>:autosave</tt> option on a #has_one, #belongs_to,
- # #has_many, or #has_and_belongs_to_many association. Setting it
- # to +true+ will _always_ save the members, whereas setting it to +false+ will
- # _never_ save the members. More details about <tt>:autosave</tt> option is available at
- # AutosaveAssociation.
- #
- # === One-to-one associations
- #
- # * Assigning an object to a #has_one association automatically saves that object and
- # the object being replaced (if there is one), in order to update their foreign
- # keys - except if the parent object is unsaved (<tt>new_record? == true</tt>).
- # * If either of these saves fail (due to one of the objects being invalid), an
- # ActiveRecord::RecordNotSaved exception is raised and the assignment is
- # cancelled.
- # * If you wish to assign an object to a #has_one association without saving it,
- # use the <tt>#build_association</tt> method (documented below). The object being
- # replaced will still be saved to update its foreign key.
- # * Assigning an object to a #belongs_to association does not save the object, since
- # the foreign key field belongs on the parent. It does not save the parent either.
- #
- # === Collections
- #
- # * Adding an object to a collection (#has_many or #has_and_belongs_to_many) automatically
- # saves that object, except if the parent object (the owner of the collection) is not yet
- # stored in the database.
- # * If saving any of the objects being added to a collection (via <tt>push</tt> or similar)
- # fails, then <tt>push</tt> returns +false+.
- # * If saving fails while replacing the collection (via <tt>association=</tt>), an
- # ActiveRecord::RecordNotSaved exception is raised and the assignment is
- # cancelled.
- # * You can add an object to a collection without automatically saving it by using the
- # <tt>collection.build</tt> method (documented below).
- # * All unsaved (<tt>new_record? == true</tt>) members of the collection are automatically
- # saved when the parent is saved.
- #
- # == Customizing the query
- #
- # \Associations are built from <tt>Relation</tt> objects, and you can use the Relation syntax
- # to customize them. For example, to add a condition:
- #
- # class Blog < ActiveRecord::Base
- # has_many :published_posts, -> { where(published: true) }, class_name: 'Post'
- # end
- #
- # Inside the <tt>-> { ... }</tt> block you can use all of the usual Relation methods.
- #
- # === Accessing the owner object
- #
- # Sometimes it is useful to have access to the owner object when building the query. The owner
- # is passed as a parameter to the block. For example, the following association would find all
- # events that occur on the user's birthday:
- #
- # class User < ActiveRecord::Base
- # has_many :birthday_events, ->(user) { where(starts_on: user.birthday) }, class_name: 'Event'
- # end
- #
- # Note: Joining, eager loading and preloading of these associations is not fully possible.
- # These operations happen before instance creation and the scope will be called with a +nil+ argument.
- # This can lead to unexpected behavior and is deprecated.
- #
- # == Association callbacks
- #
- # Similar to the normal callbacks that hook into the life cycle of an Active Record object,
- # you can also define callbacks that get triggered when you add an object to or remove an
- # object from an association collection.
- #
- # class Project
- # has_and_belongs_to_many :developers, after_add: :evaluate_velocity
- #
- # def evaluate_velocity(developer)
- # ...
- # end
- # end
- #
- # It's possible to stack callbacks by passing them as an array. Example:
- #
- # class Project
- # has_and_belongs_to_many :developers,
- # after_add: [:evaluate_velocity, Proc.new { |p, d| p.shipping_date = Time.now}]
- # end
- #
- # Possible callbacks are: +before_add+, +after_add+, +before_remove+ and +after_remove+.
- #
- # If any of the +before_add+ callbacks throw an exception, the object will not be
- # added to the collection.
- #
- # Similarly, if any of the +before_remove+ callbacks throw an exception, the object
- # will not be removed from the collection.
- #
- # == Association extensions
- #
- # The proxy objects that control the access to associations can be extended through anonymous
- # modules. This is especially beneficial for adding new finders, creators, and other
- # factory-type methods that are only used as part of this association.
- #
- # class Account < ActiveRecord::Base
- # has_many :people do
- # def find_or_create_by_name(name)
- # first_name, last_name = name.split(" ", 2)
- # find_or_create_by(first_name: first_name, last_name: last_name)
- # end
- # end
- # end
- #
- # person = Account.first.people.find_or_create_by_name("David Heinemeier Hansson")
- # person.first_name # => "David"
- # person.last_name # => "Heinemeier Hansson"
- #
- # If you need to share the same extensions between many associations, you can use a named
- # extension module.
- #
- # module FindOrCreateByNameExtension
- # def find_or_create_by_name(name)
- # first_name, last_name = name.split(" ", 2)
- # find_or_create_by(first_name: first_name, last_name: last_name)
- # end
- # end
- #
- # class Account < ActiveRecord::Base
- # has_many :people, -> { extending FindOrCreateByNameExtension }
- # end
- #
- # class Company < ActiveRecord::Base
- # has_many :people, -> { extending FindOrCreateByNameExtension }
- # end
- #
- # Some extensions can only be made to work with knowledge of the association's internals.
- # Extensions can access relevant state using the following methods (where +items+ is the
- # name of the association):
- #
- # * <tt>record.association(:items).owner</tt> - Returns the object the association is part of.
- # * <tt>record.association(:items).reflection</tt> - Returns the reflection object that describes the association.
- # * <tt>record.association(:items).target</tt> - Returns the associated object for #belongs_to and #has_one, or
- # the collection of associated objects for #has_many and #has_and_belongs_to_many.
- #
- # However, inside the actual extension code, you will not have access to the <tt>record</tt> as
- # above. In this case, you can access <tt>proxy_association</tt>. For example,
- # <tt>record.association(:items)</tt> and <tt>record.items.proxy_association</tt> will return
- # the same object, allowing you to make calls like <tt>proxy_association.owner</tt> inside
- # association extensions.
- #
- # == Association Join Models
- #
- # Has Many associations can be configured with the <tt>:through</tt> option to use an
- # explicit join model to retrieve the data. This operates similarly to a
- # #has_and_belongs_to_many association. The advantage is that you're able to add validations,
- # callbacks, and extra attributes on the join model. Consider the following schema:
- #
- # class Author < ActiveRecord::Base
- # has_many :authorships
- # has_many :books, through: :authorships
- # end
- #
- # class Authorship < ActiveRecord::Base
- # belongs_to :author
- # belongs_to :book
- # end
- #
- # @author = Author.first
- # @author.authorships.collect { |a| a.book } # selects all books that the author's authorships belong to
- # @author.books # selects all books by using the Authorship join model
- #
- # You can also go through a #has_many association on the join model:
- #
- # class Firm < ActiveRecord::Base
- # has_many :clients
- # has_many :invoices, through: :clients
- # end
- #
- # class Client < ActiveRecord::Base
- # belongs_to :firm
- # has_many :invoices
- # end
- #
- # class Invoice < ActiveRecord::Base
- # belongs_to :client
- # end
- #
- # @firm = Firm.first
- # @firm.clients.flat_map { |c| c.invoices } # select all invoices for all clients of the firm
- # @firm.invoices # selects all invoices by going through the Client join model
- #
- # Similarly you can go through a #has_one association on the join model:
- #
- # class Group < ActiveRecord::Base
- # has_many :users
- # has_many :avatars, through: :users
- # end
- #
- # class User < ActiveRecord::Base
- # belongs_to :group
- # has_one :avatar
- # end
- #
- # class Avatar < ActiveRecord::Base
- # belongs_to :user
- # end
- #
- # @group = Group.first
- # @group.users.collect { |u| u.avatar }.compact # select all avatars for all users in the group
- # @group.avatars # selects all avatars by going through the User join model.
- #
- # An important caveat with going through #has_one or #has_many associations on the
- # join model is that these associations are *read-only*. For example, the following
- # would not work following the previous example:
- #
- # @group.avatars << Avatar.new # this would work if User belonged_to Avatar rather than the other way around
- # @group.avatars.delete(@group.avatars.last) # so would this
- #
- # == Setting Inverses
- #
- # If you are using a #belongs_to on the join model, it is a good idea to set the
- # <tt>:inverse_of</tt> option on the #belongs_to, which will mean that the following example
- # works correctly (where <tt>tags</tt> is a #has_many <tt>:through</tt> association):
- #
- # @post = Post.first
- # @tag = @post.tags.build name: "ruby"
- # @tag.save
- #
- # The last line ought to save the through record (a <tt>Tagging</tt>). This will only work if the
- # <tt>:inverse_of</tt> is set:
- #
- # class Tagging < ActiveRecord::Base
- # belongs_to :post
- # belongs_to :tag, inverse_of: :taggings
- # end
- #
- # If you do not set the <tt>:inverse_of</tt> record, the association will
- # do its best to match itself up with the correct inverse. Automatic
- # inverse detection only works on #has_many, #has_one, and
- # #belongs_to associations.
- #
- # Extra options on the associations, as defined in the
- # <tt>AssociationReflection::INVALID_AUTOMATIC_INVERSE_OPTIONS</tt> constant, will
- # also prevent the association's inverse from being found automatically.
- #
- # The automatic guessing of the inverse association uses a heuristic based
- # on the name of the class, so it may not work for all associations,
- # especially the ones with non-standard names.
- #
- # You can turn off the automatic detection of inverse associations by setting
- # the <tt>:inverse_of</tt> option to <tt>false</tt> like so:
- #
- # class Tagging < ActiveRecord::Base
- # belongs_to :tag, inverse_of: false
- # end
- #
- # == Nested \Associations
- #
- # You can actually specify *any* association with the <tt>:through</tt> option, including an
- # association which has a <tt>:through</tt> option itself. For example:
- #
- # class Author < ActiveRecord::Base
- # has_many :posts
- # has_many :comments, through: :posts
- # has_many :commenters, through: :comments
- # end
- #
- # class Post < ActiveRecord::Base
- # has_many :comments
- # end
- #
- # class Comment < ActiveRecord::Base
- # belongs_to :commenter
- # end
- #
- # @author = Author.first
- # @author.commenters # => People who commented on posts written by the author
- #
- # An equivalent way of setting up this association this would be:
- #
- # class Author < ActiveRecord::Base
- # has_many :posts
- # has_many :commenters, through: :posts
- # end
- #
- # class Post < ActiveRecord::Base
- # has_many :comments
- # has_many :commenters, through: :comments
- # end
- #
- # class Comment < ActiveRecord::Base
- # belongs_to :commenter
- # end
- #
- # When using a nested association, you will not be able to modify the association because there
- # is not enough information to know what modification to make. For example, if you tried to
- # add a <tt>Commenter</tt> in the example above, there would be no way to tell how to set up the
- # intermediate <tt>Post</tt> and <tt>Comment</tt> objects.
- #
- # == Polymorphic \Associations
- #
- # Polymorphic associations on models are not restricted on what types of models they
- # can be associated with. Rather, they specify an interface that a #has_many association
- # must adhere to.
- #
- # class Asset < ActiveRecord::Base
- # belongs_to :attachable, polymorphic: true
- # end
- #
- # class Post < ActiveRecord::Base
- # has_many :assets, as: :attachable # The :as option specifies the polymorphic interface to use.
- # end
- #
- # @asset.attachable = @post
- #
- # This works by using a type column in addition to a foreign key to specify the associated
- # record. In the Asset example, you'd need an +attachable_id+ integer column and an
- # +attachable_type+ string column.
- #
- # Using polymorphic associations in combination with single table inheritance (STI) is
- # a little tricky. In order for the associations to work as expected, ensure that you
- # store the base model for the STI models in the type column of the polymorphic
- # association. To continue with the asset example above, suppose there are guest posts
- # and member posts that use the posts table for STI. In this case, there must be a +type+
- # column in the posts table.
- #
- # Note: The <tt>attachable_type=</tt> method is being called when assigning an +attachable+.
- # The +class_name+ of the +attachable+ is passed as a String.
- #
- # class Asset < ActiveRecord::Base
- # belongs_to :attachable, polymorphic: true
- #
- # def attachable_type=(class_name)
- # super(class_name.constantize.base_class.to_s)
- # end
- # end
- #
- # class Post < ActiveRecord::Base
- # # because we store "Post" in attachable_type now dependent: :destroy will work
- # has_many :assets, as: :attachable, dependent: :destroy
- # end
- #
- # class GuestPost < Post
- # end
- #
- # class MemberPost < Post
- # end
- #
- # == Caching
- #
- # All of the methods are built on a simple caching principle that will keep the result
- # of the last query around unless specifically instructed not to. The cache is even
- # shared across methods to make it even cheaper to use the macro-added methods without
- # worrying too much about performance at the first go.
- #
- # project.milestones # fetches milestones from the database
- # project.milestones.size # uses the milestone cache
- # project.milestones.empty? # uses the milestone cache
- # project.milestones(true).size # fetches milestones from the database
- # project.milestones # uses the milestone cache
- #
- # == Eager loading of associations
- #
- # Eager loading is a way to find objects of a certain class and a number of named associations.
- # It is one of the easiest ways to prevent the dreaded N+1 problem in which fetching 100
- # posts that each need to display their author triggers 101 database queries. Through the
- # use of eager loading, the number of queries will be reduced from 101 to 2.
- #
- # class Post < ActiveRecord::Base
- # belongs_to :author
- # has_many :comments
- # end
- #
- # Consider the following loop using the class above:
- #
- # Post.all.each do |post|
- # puts "Post: " + post.title
- # puts "Written by: " + post.author.name
- # puts "Last comment on: " + post.comments.first.created_on
- # end
- #
- # To iterate over these one hundred posts, we'll generate 201 database queries. Let's
- # first just optimize it for retrieving the author:
- #
- # Post.includes(:author).each do |post|
- #
- # This references the name of the #belongs_to association that also used the <tt>:author</tt>
- # symbol. After loading the posts, find will collect the +author_id+ from each one and load
- # all the referenced authors with one query. Doing so will cut down the number of queries
- # from 201 to 102.
- #
- # We can improve upon the situation further by referencing both associations in the finder with:
- #
- # Post.includes(:author, :comments).each do |post|
- #
- # This will load all comments with a single query. This reduces the total number of queries
- # to 3. In general, the number of queries will be 1 plus the number of associations
- # named (except if some of the associations are polymorphic #belongs_to - see below).
- #
- # To include a deep hierarchy of associations, use a hash:
- #
- # Post.includes(:author, { comments: { author: :gravatar } }).each do |post|
- #
- # The above code will load all the comments and all of their associated
- # authors and gravatars. You can mix and match any combination of symbols,
- # arrays, and hashes to retrieve the associations you want to load.
- #
- # All of this power shouldn't fool you into thinking that you can pull out huge amounts
- # of data with no performance penalty just because you've reduced the number of queries.
- # The database still needs to send all the data to Active Record and it still needs to
- # be processed. So it's no catch-all for performance problems, but it's a great way to
- # cut down on the number of queries in a situation as the one described above.
- #
- # Since only one table is loaded at a time, conditions or orders cannot reference tables
- # other than the main one. If this is the case, Active Record falls back to the previously
- # used LEFT OUTER JOIN based strategy. For example:
- #
- # Post.includes([:author, :comments]).where(['comments.approved = ?', true])
- #
- # This will result in a single SQL query with joins along the lines of:
- # <tt>LEFT OUTER JOIN comments ON comments.post_id = posts.id</tt> and
- # <tt>LEFT OUTER JOIN authors ON authors.id = posts.author_id</tt>. Note that using conditions
- # like this can have unintended consequences.
- # In the above example posts with no approved comments are not returned at all, because
- # the conditions apply to the SQL statement as a whole and not just to the association.
- #
- # You must disambiguate column references for this fallback to happen, for example
- # <tt>order: "author.name DESC"</tt> will work but <tt>order: "name DESC"</tt> will not.
- #
- # If you want to load all posts (including posts with no approved comments) then write
- # your own LEFT OUTER JOIN query using ON
- #
- # Post.joins("LEFT OUTER JOIN comments ON comments.post_id = posts.id AND comments.approved = '1'")
- #
- # In this case it is usually more natural to include an association which has conditions defined on it:
- #
- # class Post < ActiveRecord::Base
- # has_many :approved_comments, -> { where(approved: true) }, class_name: 'Comment'
- # end
- #
- # Post.includes(:approved_comments)
- #
- # This will load posts and eager load the +approved_comments+ association, which contains
- # only those comments that have been approved.
- #
- # If you eager load an association with a specified <tt>:limit</tt> option, it will be ignored,
- # returning all the associated objects:
- #
- # class Picture < ActiveRecord::Base
- # has_many :most_recent_comments, -> { order('id DESC').limit(10) }, class_name: 'Comment'
- # end
- #
- # Picture.includes(:most_recent_comments).first.most_recent_comments # => returns all associated comments.
- #
- # Eager loading is supported with polymorphic associations.
- #
- # class Address < ActiveRecord::Base
- # belongs_to :addressable, polymorphic: true
- # end
- #
- # A call that tries to eager load the addressable model
- #
- # Address.includes(:addressable)
- #
- # This will execute one query to load the addresses and load the addressables with one
- # query per addressable type.
- # For example if all the addressables are either of class Person or Company then a total
- # of 3 queries will be executed. The list of addressable types to load is determined on
- # the back of the addresses loaded. This is not supported if Active Record has to fallback
- # to the previous implementation of eager loading and will raise ActiveRecord::EagerLoadPolymorphicError.
- # The reason is that the parent model's type is a column value so its corresponding table
- # name cannot be put in the +FROM+/+JOIN+ clauses of that query.
- #
- # == Table Aliasing
- #
- # Active Record uses table aliasing in the case that a table is referenced multiple times
- # in a join. If a table is referenced only once, the standard table name is used. The
- # second time, the table is aliased as <tt>#{reflection_name}_#{parent_table_name}</tt>.
- # Indexes are appended for any more successive uses of the table name.
- #
- # Post.joins(:comments)
- # # => SELECT ... FROM posts INNER JOIN comments ON ...
- # Post.joins(:special_comments) # STI
- # # => SELECT ... FROM posts INNER JOIN comments ON ... AND comments.type = 'SpecialComment'
- # Post.joins(:comments, :special_comments) # special_comments is the reflection name, posts is the parent table name
- # # => SELECT ... FROM posts INNER JOIN comments ON ... INNER JOIN comments special_comments_posts
- #
- # Acts as tree example:
- #
- # TreeMixin.joins(:children)
- # # => SELECT ... FROM mixins INNER JOIN mixins childrens_mixins ...
- # TreeMixin.joins(children: :parent)
- # # => SELECT ... FROM mixins INNER JOIN mixins childrens_mixins ...
- # INNER JOIN parents_mixins ...
- # TreeMixin.joins(children: {parent: :children})
- # # => SELECT ... FROM mixins INNER JOIN mixins childrens_mixins ...
- # INNER JOIN parents_mixins ...
- # INNER JOIN mixins childrens_mixins_2
- #
- # Has and Belongs to Many join tables use the same idea, but add a <tt>_join</tt> suffix:
- #
- # Post.joins(:categories)
- # # => SELECT ... FROM posts INNER JOIN categories_posts ... INNER JOIN categories ...
- # Post.joins(categories: :posts)
- # # => SELECT ... FROM posts INNER JOIN categories_posts ... INNER JOIN categories ...
- # INNER JOIN categories_posts posts_categories_join INNER JOIN posts posts_categories
- # Post.joins(categories: {posts: :categories})
- # # => SELECT ... FROM posts INNER JOIN categories_posts ... INNER JOIN categories ...
- # INNER JOIN categories_posts posts_categories_join INNER JOIN posts posts_categories
- # INNER JOIN categories_posts categories_posts_join INNER JOIN categories categories_posts_2
- #
- # If you wish to specify your own custom joins using ActiveRecord::QueryMethods#joins method, those table
- # names will take precedence over the eager associations:
- #
- # Post.joins(:comments).joins("inner join comments ...")
- # # => SELECT ... FROM posts INNER JOIN comments_posts ON ... INNER JOIN comments ...
- # Post.joins(:comments, :special_comments).joins("inner join comments ...")
- # # => SELECT ... FROM posts INNER JOIN comments comments_posts ON ...
- # INNER JOIN comments special_comments_posts ...
- # INNER JOIN comments ...
- #
- # Table aliases are automatically truncated according to the maximum length of table identifiers
- # according to the specific database.
- #
- # == Modules
- #
- # By default, associations will look for objects within the current module scope. Consider:
- #
- # module MyApplication
- # module Business
- # class Firm < ActiveRecord::Base
- # has_many :clients
- # end
- #
- # class Client < ActiveRecord::Base; end
- # end
- # end
- #
- # When <tt>Firm#clients</tt> is called, it will in turn call
- # <tt>MyApplication::Business::Client.find_all_by_firm_id(firm.id)</tt>.
- # If you want to associate with a class in another module scope, this can be done by
- # specifying the complete class name.
- #
- # module MyApplication
- # module Business
- # class Firm < ActiveRecord::Base; end
- # end
- #
- # module Billing
- # class Account < ActiveRecord::Base
- # belongs_to :firm, class_name: "MyApplication::Business::Firm"
- # end
- # end
- # end
- #
- # == Bi-directional associations
- #
- # When you specify an association there is usually an association on the associated model
- # that specifies the same relationship in reverse. For example, with the following models:
- #
- # class Dungeon < ActiveRecord::Base
- # has_many :traps
- # has_one :evil_wizard
- # end
- #
- # class Trap < ActiveRecord::Base
- # belongs_to :dungeon
- # end
- #
- # class EvilWizard < ActiveRecord::Base
- # belongs_to :dungeon
- # end
- #
- # The +traps+ association on +Dungeon+ and the +dungeon+ association on +Trap+ are
- # the inverse of each other and the inverse of the +dungeon+ association on +EvilWizard+
- # is the +evil_wizard+ association on +Dungeon+ (and vice-versa). By default,
- # Active Record can guess the inverse of the association based on the name
- # of the class. The result is the following:
- #
- # d = Dungeon.first
- # t = d.traps.first
- # d.object_id == t.dungeon.object_id # => true
- #
- # The +Dungeon+ instances +d+ and <tt>t.dungeon</tt> in the above example refer to
- # the same in-memory instance since the association matches the name of the class.
- # The result would be the same if we added +:inverse_of+ to our model definitions:
- #
- # class Dungeon < ActiveRecord::Base
- # has_many :traps, inverse_of: :dungeon
- # has_one :evil_wizard, inverse_of: :dungeon
- # end
- #
- # class Trap < ActiveRecord::Base
- # belongs_to :dungeon, inverse_of: :traps
- # end
- #
- # class EvilWizard < ActiveRecord::Base
- # belongs_to :dungeon, inverse_of: :evil_wizard
- # end
- #
- # There are limitations to <tt>:inverse_of</tt> support:
- #
- # * does not work with <tt>:through</tt> associations.
- # * does not work with <tt>:polymorphic</tt> associations.
- # * for #belongs_to associations #has_many inverse associations are ignored.
- #
- # For more information, see the documentation for the +:inverse_of+ option.
- #
- # == Deleting from associations
- #
- # === Dependent associations
- #
- # #has_many, #has_one and #belongs_to associations support the <tt>:dependent</tt> option.
- # This allows you to specify that associated records should be deleted when the owner is
- # deleted.
- #
- # For example:
- #
- # class Author
- # has_many :posts, dependent: :destroy
- # end
- # Author.find(1).destroy # => Will destroy all of the author's posts, too
- #
- # The <tt>:dependent</tt> option can have different values which specify how the deletion
- # is done. For more information, see the documentation for this option on the different
- # specific association types. When no option is given, the behavior is to do nothing
- # with the associated records when destroying a record.
- #
- # Note that <tt>:dependent</tt> is implemented using Rails' callback
- # system, which works by processing callbacks in order. Therefore, other
- # callbacks declared either before or after the <tt>:dependent</tt> option
- # can affect what it does.
- #
- # Note that <tt>:dependent</tt> option is ignored for #has_one <tt>:through</tt> associations.
- #
- # === Delete or destroy?
- #
- # #has_many and #has_and_belongs_to_many associations have the methods <tt>destroy</tt>,
- # <tt>delete</tt>, <tt>destroy_all</tt> and <tt>delete_all</tt>.
- #
- # For #has_and_belongs_to_many, <tt>delete</tt> and <tt>destroy</tt> are the same: they
- # cause the records in the join table to be removed.
- #
- # For #has_many, <tt>destroy</tt> and <tt>destroy_all</tt> will always call the <tt>destroy</tt> method of the
- # record(s) being removed so that callbacks are run. However <tt>delete</tt> and <tt>delete_all</tt> will either
- # do the deletion according to the strategy specified by the <tt>:dependent</tt> option, or
- # if no <tt>:dependent</tt> option is given, then it will follow the default strategy.
- # The default strategy is to do nothing (leave the foreign keys with the parent ids set), except for
- # #has_many <tt>:through</tt>, where the default strategy is <tt>delete_all</tt> (delete
- # the join records, without running their callbacks).
- #
- # There is also a <tt>clear</tt> method which is the same as <tt>delete_all</tt>, except that
- # it returns the association rather than the records which have been deleted.
- #
- # === What gets deleted?
- #
- # There is a potential pitfall here: #has_and_belongs_to_many and #has_many <tt>:through</tt>
- # associations have records in join tables, as well as the associated records. So when we
- # call one of these deletion methods, what exactly should be deleted?
- #
- # The answer is that it is assumed that deletion on an association is about removing the
- # <i>link</i> between the owner and the associated object(s), rather than necessarily the
- # associated objects themselves. So with #has_and_belongs_to_many and #has_many
- # <tt>:through</tt>, the join records will be deleted, but the associated records won't.
- #
- # This makes sense if you think about it: if you were to call <tt>post.tags.delete(Tag.find_by(name: 'food'))</tt>
- # you would want the 'food' tag to be unlinked from the post, rather than for the tag itself
- # to be removed from the database.
- #
- # However, there are examples where this strategy doesn't make sense. For example, suppose
- # a person has many projects, and each project has many tasks. If we deleted one of a person's
- # tasks, we would probably not want the project to be deleted. In this scenario, the delete method
- # won't actually work: it can only be used if the association on the join model is a
- # #belongs_to. In other situations you are expected to perform operations directly on
- # either the associated records or the <tt>:through</tt> association.
- #
- # With a regular #has_many there is no distinction between the "associated records"
- # and the "link", so there is only one choice for what gets deleted.
- #
- # With #has_and_belongs_to_many and #has_many <tt>:through</tt>, if you want to delete the
- # associated records themselves, you can always do something along the lines of
- # <tt>person.tasks.each(&:destroy)</tt>.
- #
- # == Type safety with ActiveRecord::AssociationTypeMismatch
- #
- # If you attempt to assign an object to an association that doesn't match the inferred
- # or specified <tt>:class_name</tt>, you'll get an ActiveRecord::AssociationTypeMismatch.
- #
- # == Options
- #
- # All of the association macros can be specialized through options. This makes cases
- # more complex than the simple and guessable ones possible.
- module ClassMethods
- # Specifies a one-to-many association. The following methods for retrieval and query of
- # collections of associated objects will be added:
- #
- # +collection+ is a placeholder for the symbol passed as the +name+ argument, so
- # <tt>has_many :clients</tt> would add among others <tt>clients.empty?</tt>.
- #
- # [collection(force_reload = false)]
- # Returns an array of all the associated objects.
- # An empty array is returned if none are found.
- # [collection<<(object, ...)]
- # Adds one or more objects to the collection by setting their foreign keys to the collection's primary key.
- # Note that this operation instantly fires update SQL without waiting for the save or update call on the
- # parent object, unless the parent object is a new record.
- # This will also run validations and callbacks of associated object(s).
- # [collection.delete(object, ...)]
- # Removes one or more objects from the collection by setting their foreign keys to +NULL+.
- # Objects will be in addition destroyed if they're associated with <tt>dependent: :destroy</tt>,
- # and deleted if they're associated with <tt>dependent: :delete_all</tt>.
- #
- # If the <tt>:through</tt> option is used, then the join records are deleted (rather than
- # nullified) by default, but you can specify <tt>dependent: :destroy</tt> or
- # <tt>dependent: :nullify</tt> to override this.
- # [collection.destroy(object, ...)]
- # Removes one or more objects from the collection by running <tt>destroy</tt> on
- # each record, regardless of any dependent option, ensuring callbacks are run.
- #
- # If the <tt>:through</tt> option is used, then the join records are destroyed
- # instead, not the objects themselves.
- # [collection=objects]
- # Replaces the collections content by deleting and adding objects as appropriate. If the <tt>:through</tt>
- # option is true callbacks in the join models are triggered except destroy callbacks, since deletion is
- # direct by default. You can specify <tt>dependent: :destroy</tt> or
- # <tt>dependent: :nullify</tt> to override this.
- # [collection_singular_ids]
- # Returns an array of the associated objects' ids
- # [collection_singular_ids=ids]
- # Replace the collection with the objects identified by the primary keys in +ids+. This
- # method loads the models and calls <tt>collection=</tt>. See above.
- # [collection.clear]
- # Removes every object from the collection. This destroys the associated objects if they
- # are associated with <tt>dependent: :destroy</tt>, deletes them directly from the
- # database if <tt>dependent: :delete_all</tt>, otherwise sets their foreign keys to +NULL+.
- # If the <tt>:through</tt> option is true no destroy callbacks are invoked on the join models.
- # Join models are directly deleted.
- # [collection.empty?]
- # Returns +true+ if there are no associated objects.
- # [collection.size]
- # Returns the number of associated objects.
- # [collection.find(...)]
- # Finds an associated object according to the same rules as ActiveRecord::FinderMethods#find.
- # [collection.exists?(...)]
- # Checks whether an associated object with the given conditions exists.
- # Uses the same rules as ActiveRecord::FinderMethods#exists?.
- # [collection.build(attributes = {}, ...)]
- # Returns one or more new objects of the collection type that have been instantiated
- # with +attributes+ and linked to this object through a foreign key, but have not yet
- # been saved.
- # [collection.create(attributes = {})]
- # Returns a new object of the collection type that has been instantiated
- # with +attributes+, linked to this object through a foreign key, and that has already
- # been saved (if it passed the validation). *Note*: This only works if the base model
- # already exists in the DB, not if it is a new (unsaved) record!
- # [collection.create!(attributes = {})]
- # Does the same as <tt>collection.create</tt>, but raises ActiveRecord::RecordInvalid
- # if the record is invalid.
- #
- # === Example
- #
- # A <tt>Firm</tt> class declares <tt>has_many :clients</tt>, which will add:
- # * <tt>Firm#clients</tt> (similar to <tt>Client.where(firm_id: id)</tt>)
- # * <tt>Firm#clients<<</tt>
- # * <tt>Firm#clients.delete</tt>
- # * <tt>Firm#clients.destroy</tt>
- # * <tt>Firm#clients=</tt>
- # * <tt>Firm#client_ids</tt>
- # * <tt>Firm#client_ids=</tt>
- # * <tt>Firm#clients.clear</tt>
- # * <tt>Firm#clients.empty?</tt> (similar to <tt>firm.clients.size == 0</tt>)
- # * <tt>Firm#clients.size</tt> (similar to <tt>Client.count "firm_id = #{id}"</tt>)
- # * <tt>Firm#clients.find</tt> (similar to <tt>Client.where(firm_id: id).find(id)</tt>)
- # * <tt>Firm#clients.exists?(name: 'ACME')</tt> (similar to <tt>Client.exists?(name: 'ACME', firm_id: firm.id)</tt>)
- # * <tt>Firm#clients.build</tt> (similar to <tt>Client.new("firm_id" => id)</tt>)
- # * <tt>Firm#clients.create</tt> (similar to <tt>c = Client.new("firm_id" => id); c.save; c</tt>)
- # * <tt>Firm#clients.create!</tt> (similar to <tt>c = Client.new("firm_id" => id); c.save!</tt>)
- # The declaration can also include an +options+ hash to specialize the behavior of the association.
- #
- # === Scopes
- #
- # You can pass a second argument +scope+ as a callable (i.e. proc or
- # lambda) to retrieve a specific set of records or customize the generated
- # query when you access the associated collection.
- #
- # Scope examples:
- # has_many :comments, -> { where(author_id: 1) }
- # has_many :employees, -> { joins(:address) }
- # has_many :posts, ->(post) { where("max_post_length > ?", post.length) }
- #
- # === Extensions
- #
- # The +extension+ argument allows you to pass a block into a has_many
- # association. This is useful for adding new finders, creators and other
- # factory-type methods to be used as part of the association.
- #
- # Extension examples:
- # has_many :employees do
- # def find_or_create_by_name(name)
- # first_name, last_name = name.split(" ", 2)
- # find_or_create_by(first_name: first_name, last_name: last_name)
+ # \Associations are a set of macro-like class methods for tying objects together through
+ # foreign keys. They express relationships like "Project has one Project Manager"
+ # or "Project belongs to a Portfolio". Each macro adds a number of methods to the
+ # class which are specialized according to the collection or association symbol and the
+ # options hash. It works much the same way as Ruby's own <tt>attr*</tt>
+ # methods.
+ #
+ # class Project < ActiveRecord::Base
+ # belongs_to :portfolio
+ # has_one :project_manager
+ # has_many :milestones
+ # has_and_belongs_to_many :categories
+ # end
+ #
+ # The project class now has the following methods (and more) to ease the traversal and
+ # manipulation of its relationships:
+ # * <tt>Project#portfolio, Project#portfolio=(portfolio), Project#portfolio.nil?</tt>
+ # * <tt>Project#project_manager, Project#project_manager=(project_manager), Project#project_manager.nil?,</tt>
+ # * <tt>Project#milestones.empty?, Project#milestones.size, Project#milestones, Project#milestones<<(milestone),</tt>
+ # <tt>Project#milestones.delete(milestone), Project#milestones.destroy(milestone), Project#milestones.find(milestone_id),</tt>
+ # <tt>Project#milestones.build, Project#milestones.create</tt>
+ # * <tt>Project#categories.empty?, Project#categories.size, Project#categories, Project#categories<<(category1),</tt>
+ # <tt>Project#categories.delete(category1), Project#categories.destroy(category1)</tt>
+ #
+ # === A word of warning
+ #
+ # Don't create associations that have the same name as {instance methods}[rdoc-ref:ActiveRecord::Core] of
+ # <tt>ActiveRecord::Base</tt>. Since the association adds a method with that name to
+ # its model, using an association with the same name as one provided by <tt>ActiveRecord::Base</tt> will override the method inherited through <tt>ActiveRecord::Base</tt> and will break things.
+ # For instance, +attributes+ and +connection+ would be bad choices for association names, because those names already exist in the list of <tt>ActiveRecord::Base</tt> instance methods.
+ #
+ # == Auto-generated methods
+ # See also Instance Public methods below for more details.
+ #
+ # === Singular associations (one-to-one)
+ # | | belongs_to |
+ # generated methods | belongs_to | :polymorphic | has_one
+ # ----------------------------------+------------+--------------+---------
+ # other(force_reload=false) | X | X | X
+ # other=(other) | X | X | X
+ # build_other(attributes={}) | X | | X
+ # create_other(attributes={}) | X | | X
+ # create_other!(attributes={}) | X | | X
+ #
+ # === Collection associations (one-to-many / many-to-many)
+ # | | | has_many
+ # generated methods | habtm | has_many | :through
+ # ----------------------------------+-------+----------+----------
+ # others(force_reload=false) | X | X | X
+ # others=(other,other,...) | X | X | X
+ # other_ids | X | X | X
+ # other_ids=(id,id,...) | X | X | X
+ # others<< | X | X | X
+ # others.push | X | X | X
+ # others.concat | X | X | X
+ # others.build(attributes={}) | X | X | X
+ # others.create(attributes={}) | X | X | X
+ # others.create!(attributes={}) | X | X | X
+ # others.size | X | X | X
+ # others.length | X | X | X
+ # others.count | X | X | X
+ # others.sum(*args) | X | X | X
+ # others.empty? | X | X | X
+ # others.clear | X | X | X
+ # others.delete(other,other,...) | X | X | X
+ # others.delete_all | X | X | X
+ # others.destroy(other,other,...) | X | X | X
+ # others.destroy_all | X | X | X
+ # others.find(*args) | X | X | X
+ # others.exists? | X | X | X
+ # others.distinct | X | X | X
+ # others.reset | X | X | X
+ #
+ # === Overriding generated methods
+ #
+ # Association methods are generated in a module included into the model
+ # class, making overrides easy. The original generated method can thus be
+ # called with +super+:
+ #
+ # class Car < ActiveRecord::Base
+ # belongs_to :owner
+ # belongs_to :old_owner
+ #
+ # def owner=(new_owner)
+ # self.old_owner = self.owner
+ # super
# end
# end
#
- # === Options
- # [:class_name]
- # Specify the class name of the association. Use it only if that name can't be inferred
- # from the association name. So <tt>has_many :products</tt> will by default be linked
- # to the +Product+ class, but if the real class name is +SpecialProduct+, you'll have to
- # specify it with this option.
- # [:foreign_key]
- # Specify the foreign key used for the association. By default this is guessed to be the name
- # of this class in lower-case and "_id" suffixed. So a Person class that makes a #has_many
- # association will use "person_id" as the default <tt>:foreign_key</tt>.
- # [:foreign_type]
- # Specify the column used to store the associated object's type, if this is a polymorphic
- # association. By default this is guessed to be the name of the polymorphic association
- # specified on "as" option with a "_type" suffix. So a class that defines a
- # <tt>has_many :tags, as: :taggable</tt> association will use "taggable_type" as the
- # default <tt>:foreign_type</tt>.
- # [:primary_key]
- # Specify the name of the column to use as the primary key for the association. By default this is +id+.
- # [:dependent]
- # Controls what happens to the associated objects when
- # their owner is destroyed. Note that these are implemented as
- # callbacks, and Rails executes callbacks in order. Therefore, other
- # similar callbacks may affect the <tt>:dependent</tt> behavior, and the
- # <tt>:dependent</tt> behavior may affect other callbacks.
- #
- # * <tt>:destroy</tt> causes all the associated objects to also be destroyed.
- # * <tt>:delete_all</tt> causes all the associated objects to be deleted directly from the database (so callbacks will not be executed).
- # * <tt>:nullify</tt> causes the foreign keys to be set to +NULL+. Callbacks are not executed.
- # * <tt>:restrict_with_exception</tt> causes an exception to be raised if there are any associated records.
- # * <tt>:restrict_with_error</tt> causes an error to be added to the owner if there are any associated objects.
- #
- # If using with the <tt>:through</tt> option, the association on the join model must be
- # a #belongs_to, and the records which get deleted are the join records, rather than
- # the associated records.
- # [:counter_cache]
- # This option can be used to configure a custom named <tt>:counter_cache.</tt> You only need this option,
- # when you customized the name of your <tt>:counter_cache</tt> on the #belongs_to association.
- # [:as]
- # Specifies a polymorphic interface (See #belongs_to).
- # [:through]
- # Specifies an association through which to perform the query. This can be any other type
- # of association, including other <tt>:through</tt> associations. Options for <tt>:class_name</tt>,
- # <tt>:primary_key</tt> and <tt>:foreign_key</tt> are ignored, as the association uses the
- # source reflection.
- #
- # If the association on the join model is a #belongs_to, the collection can be modified
- # and the records on the <tt>:through</tt> model will be automatically created and removed
- # as appropriate. Otherwise, the collection is read-only, so you should manipulate the
- # <tt>:through</tt> association directly.
- #
- # If you are going to modify the association (rather than just read from it), then it is
- # a good idea to set the <tt>:inverse_of</tt> option on the source association on the
- # join model. This allows associated records to be built which will automatically create
- # the appropriate join model records when they are saved. (See the 'Association Join Models'
- # section above.)
- # [:source]
- # Specifies the source association name used by #has_many <tt>:through</tt> queries.
- # Only use it if the name cannot be inferred from the association.
- # <tt>has_many :subscribers, through: :subscriptions</tt> will look for either <tt>:subscribers</tt> or
- # <tt>:subscriber</tt> on Subscription, unless a <tt>:source</tt> is given.
- # [:source_type]
- # Specifies type of the source association used by #has_many <tt>:through</tt> queries where the source
- # association is a polymorphic #belongs_to.
- # [:validate]
- # When set to +true+, validates new objects added to association when saving the parent object. +true+ by default.
- # If you want to ensure associated objects are revalidated on every update, use +validates_associated+.
- # [:autosave]
- # If true, always save the associated objects or destroy them if marked for destruction,
- # when saving the parent object. If false, never save or destroy the associated objects.
- # By default, only save associated objects that are new records. This option is implemented as a
- # +before_save+ callback. Because callbacks are run in the order they are defined, associated objects
- # may need to be explicitly saved in any user-defined +before_save+ callbacks.
- #
- # Note that NestedAttributes::ClassMethods#accepts_nested_attributes_for sets
- # <tt>:autosave</tt> to <tt>true</tt>.
- # [:inverse_of]
- # Specifies the name of the #belongs_to association on the associated object
- # that is the inverse of this #has_many association. Does not work in combination
- # with <tt>:through</tt> or <tt>:as</tt> options.
- # See ActiveRecord::Associations::ClassMethods's overview on Bi-directional associations for more detail.
- # [:extend]
- # Specifies a module or array of modules that will be extended into the association object returned.
- # Useful for defining methods on associations, especially when they should be shared between multiple
- # association objects.
- #
- # Option examples:
- # has_many :comments, -> { order("posted_on") }
- # has_many :comments, -> { includes(:author) }
- # has_many :people, -> { where(deleted: false).order("name") }, class_name: "Person"
- # has_many :tracks, -> { order("position") }, dependent: :destroy
- # has_many :comments, dependent: :nullify
- # has_many :tags, as: :taggable
- # has_many :reports, -> { readonly }
- # has_many :subscribers, through: :subscriptions, source: :user
- def has_many(name, scope = nil, options = {}, &extension)
- reflection = Builder::HasMany.build(self, name, scope, options, &extension)
- Reflection.add_reflection self, name, reflection
- end
-
- # Specifies a one-to-one association with another class. This method should only be used
- # if the other class contains the foreign key. If the current class contains the foreign key,
- # then you should use #belongs_to instead. See also ActiveRecord::Associations::ClassMethods's overview
- # on when to use #has_one and when to use #belongs_to.
- #
- # The following methods for retrieval and query of a single associated object will be added:
- #
- # +association+ is a placeholder for the symbol passed as the +name+ argument, so
- # <tt>has_one :manager</tt> would add among others <tt>manager.nil?</tt>.
- #
- # [association(force_reload = false)]
- # Returns the associated object. +nil+ is returned if none is found.
- # [association=(associate)]
- # Assigns the associate object, extracts the primary key, sets it as the foreign key,
- # and saves the associate object. To avoid database inconsistencies, permanently deletes an existing
- # associated object when assigning a new one, even if the new one isn't saved to database.
- # [build_association(attributes = {})]
- # Returns a new object of the associated type that has been instantiated
- # with +attributes+ and linked to this object through a foreign key, but has not
- # yet been saved.
- # [create_association(attributes = {})]
- # Returns a new object of the associated type that has been instantiated
- # with +attributes+, linked to this object through a foreign key, and that
- # has already been saved (if it passed the validation).
- # [create_association!(attributes = {})]
- # Does the same as <tt>create_association</tt>, but raises ActiveRecord::RecordInvalid
- # if the record is invalid.
- #
- # === Example
- #
- # An Account class declares <tt>has_one :beneficiary</tt>, which will add:
- # * <tt>Account#beneficiary</tt> (similar to <tt>Beneficiary.where(account_id: id).first</tt>)
- # * <tt>Account#beneficiary=(beneficiary)</tt> (similar to <tt>beneficiary.account_id = account.id; beneficiary.save</tt>)
- # * <tt>Account#build_beneficiary</tt> (similar to <tt>Beneficiary.new("account_id" => id)</tt>)
- # * <tt>Account#create_beneficiary</tt> (similar to <tt>b = Beneficiary.new("account_id" => id); b.save; b</tt>)
- # * <tt>Account#create_beneficiary!</tt> (similar to <tt>b = Beneficiary.new("account_id" => id); b.save!; b</tt>)
- #
- # === Scopes
- #
- # You can pass a second argument +scope+ as a callable (i.e. proc or
- # lambda) to retrieve a specific record or customize the generated query
- # when you access the associated object.
- #
- # Scope examples:
- # has_one :author, -> { where(comment_id: 1) }
- # has_one :employer, -> { joins(:company) }
- # has_one :dob, ->(dob) { where("Date.new(2000, 01, 01) > ?", dob) }
- #
- # === Options
- #
- # The declaration can also include an +options+ hash to specialize the behavior of the association.
- #
- # Options are:
- # [:class_name]
- # Specify the class name of the association. Use it only if that name can't be inferred
- # from the association name. So <tt>has_one :manager</tt> will by default be linked to the Manager class, but
- # if the real class name is Person, you'll have to specify it with this option.
- # [:dependent]
- # Controls what happens to the associated object when
- # its owner is destroyed:
- #
- # * <tt>:destroy</tt> causes the associated object to also be destroyed
- # * <tt>:delete</tt> causes the associated object to be deleted directly from the database (so callbacks will not execute)
- # * <tt>:nullify</tt> causes the foreign key to be set to +NULL+. Callbacks are not executed.
- # * <tt>:restrict_with_exception</tt> causes an exception to be raised if there is an associated record
- # * <tt>:restrict_with_error</tt> causes an error to be added to the owner if there is an associated object
- #
- # Note that <tt>:dependent</tt> option is ignored when using <tt>:through</tt> option.
- # [:foreign_key]
- # Specify the foreign key used for the association. By default this is guessed to be the name
- # of this class in lower-case and "_id" suffixed. So a Person class that makes a #has_one association
- # will use "person_id" as the default <tt>:foreign_key</tt>.
- # [:foreign_type]
- # Specify the column used to store the associated object's type, if this is a polymorphic
- # association. By default this is guessed to be the name of the polymorphic association
- # specified on "as" option with a "_type" suffix. So a class that defines a
- # <tt>has_one :tag, as: :taggable</tt> association will use "taggable_type" as the
- # default <tt>:foreign_type</tt>.
- # [:primary_key]
- # Specify the method that returns the primary key used for the association. By default this is +id+.
- # [:as]
- # Specifies a polymorphic interface (See #belongs_to).
- # [:through]
- # Specifies a Join Model through which to perform the query. Options for <tt>:class_name</tt>,
- # <tt>:primary_key</tt>, and <tt>:foreign_key</tt> are ignored, as the association uses the
- # source reflection. You can only use a <tt>:through</tt> query through a #has_one
- # or #belongs_to association on the join model.
- # [:source]
- # Specifies the source association name used by #has_one <tt>:through</tt> queries.
- # Only use it if the name cannot be inferred from the association.
- # <tt>has_one :favorite, through: :favorites</tt> will look for a
- # <tt>:favorite</tt> on Favorite, unless a <tt>:source</tt> is given.
- # [:source_type]
- # Specifies type of the source association used by #has_one <tt>:through</tt> queries where the source
- # association is a polymorphic #belongs_to.
- # [:validate]
- # When set to +true+, validates new objects added to association when saving the parent object. +false+ by default.
- # If you want to ensure associated objects are revalidated on every update, use +validates_associated+.
- # [:autosave]
- # If true, always save the associated object or destroy it if marked for destruction,
- # when saving the parent object. If false, never save or destroy the associated object.
- # By default, only save the associated object if it's a new record.
- #
- # Note that NestedAttributes::ClassMethods#accepts_nested_attributes_for sets
- # <tt>:autosave</tt> to <tt>true</tt>.
- # [:inverse_of]
- # Specifies the name of the #belongs_to association on the associated object
- # that is the inverse of this #has_one association. Does not work in combination
- # with <tt>:through</tt> or <tt>:as</tt> options.
- # See ActiveRecord::Associations::ClassMethods's overview on Bi-directional associations for more detail.
- # [:required]
- # When set to +true+, the association will also have its presence validated.
- # This will validate the association itself, not the id. You can use
- # +:inverse_of+ to avoid an extra query during validation.
- #
- # Option examples:
- # has_one :credit_card, dependent: :destroy # destroys the associated credit card
- # has_one :credit_card, dependent: :nullify # updates the associated records foreign
- # # key value to NULL rather than destroying it
- # has_one :last_comment, -> { order('posted_on') }, class_name: "Comment"
- # has_one :project_manager, -> { where(role: 'project_manager') }, class_name: "Person"
- # has_one :attachment, as: :attachable
- # has_one :boss, -> { readonly }
- # has_one :club, through: :membership
- # has_one :primary_address, -> { where(primary: true) }, through: :addressables, source: :addressable
- # has_one :credit_card, required: true
- def has_one(name, scope = nil, options = {})
- reflection = Builder::HasOne.build(self, name, scope, options)
- Reflection.add_reflection self, name, reflection
- end
-
- # Specifies a one-to-one association with another class. This method should only be used
- # if this class contains the foreign key. If the other class contains the foreign key,
- # then you should use #has_one instead. See also ActiveRecord::Associations::ClassMethods's overview
- # on when to use #has_one and when to use #belongs_to.
- #
- # Methods will be added for retrieval and query for a single associated object, for which
- # this object holds an id:
- #
- # +association+ is a placeholder for the symbol passed as the +name+ argument, so
- # <tt>belongs_to :author</tt> would add among others <tt>author.nil?</tt>.
- #
- # [association(force_reload = false)]
- # Returns the associated object. +nil+ is returned if none is found.
- # [association=(associate)]
- # Assigns the associate object, extracts the primary key, and sets it as the foreign key.
- # [build_association(attributes = {})]
- # Returns a new object of the associated type that has been instantiated
- # with +attributes+ and linked to this object through a foreign key, but has not yet been saved.
- # [create_association(attributes = {})]
- # Returns a new object of the associated type that has been instantiated
- # with +attributes+, linked to this object through a foreign key, and that
- # has already been saved (if it passed the validation).
- # [create_association!(attributes = {})]
- # Does the same as <tt>create_association</tt>, but raises ActiveRecord::RecordInvalid
- # if the record is invalid.
- #
- # === Example
- #
- # A Post class declares <tt>belongs_to :author</tt>, which will add:
- # * <tt>Post#author</tt> (similar to <tt>Author.find(author_id)</tt>)
- # * <tt>Post#author=(author)</tt> (similar to <tt>post.author_id = author.id</tt>)
- # * <tt>Post#build_author</tt> (similar to <tt>post.author = Author.new</tt>)
- # * <tt>Post#create_author</tt> (similar to <tt>post.author = Author.new; post.author.save; post.author</tt>)
- # * <tt>Post#create_author!</tt> (similar to <tt>post.author = Author.new; post.author.save!; post.author</tt>)
- # The declaration can also include an +options+ hash to specialize the behavior of the association.
- #
- # === Scopes
- #
- # You can pass a second argument +scope+ as a callable (i.e. proc or
- # lambda) to retrieve a specific record or customize the generated query
- # when you access the associated object.
- #
- # Scope examples:
- # belongs_to :firm, -> { where(id: 2) }
- # belongs_to :user, -> { joins(:friends) }
- # belongs_to :level, ->(level) { where("game_level > ?", level.current) }
- #
- # === Options
- #
- # [:class_name]
- # Specify the class name of the association. Use it only if that name can't be inferred
- # from the association name. So <tt>belongs_to :author</tt> will by default be linked to the Author class, but
- # if the real class name is Person, you'll have to specify it with this option.
- # [:foreign_key]
- # Specify the foreign key used for the association. By default this is guessed to be the name
- # of the association with an "_id" suffix. So a class that defines a <tt>belongs_to :person</tt>
- # association will use "person_id" as the default <tt>:foreign_key</tt>. Similarly,
- # <tt>belongs_to :favorite_person, class_name: "Person"</tt> will use a foreign key
- # of "favorite_person_id".
- # [:foreign_type]
- # Specify the column used to store the associated object's type, if this is a polymorphic
- # association. By default this is guessed to be the name of the association with a "_type"
- # suffix. So a class that defines a <tt>belongs_to :taggable, polymorphic: true</tt>
- # association will use "taggable_type" as the default <tt>:foreign_type</tt>.
- # [:primary_key]
- # Specify the method that returns the primary key of associated object used for the association.
- # By default this is id.
- # [:dependent]
- # If set to <tt>:destroy</tt>, the associated object is destroyed when this object is. If set to
- # <tt>:delete</tt>, the associated object is deleted *without* calling its destroy method.
- # This option should not be specified when #belongs_to is used in conjunction with
- # a #has_many relationship on another class because of the potential to leave
- # orphaned records behind.
- # [:counter_cache]
- # Caches the number of belonging objects on the associate class through the use of CounterCache::ClassMethods#increment_counter
- # and CounterCache::ClassMethods#decrement_counter. The counter cache is incremented when an object of this
- # class is created and decremented when it's destroyed. This requires that a column
- # named <tt>#{table_name}_count</tt> (such as +comments_count+ for a belonging Comment class)
- # is used on the associate class (such as a Post class) - that is the migration for
- # <tt>#{table_name}_count</tt> is created on the associate class (such that <tt>Post.comments_count</tt> will
- # return the count cached, see note below). You can also specify a custom counter
- # cache column by providing a column name instead of a +true+/+false+ value to this
- # option (e.g., <tt>counter_cache: :my_custom_counter</tt>.)
- # Note: Specifying a counter cache will add it to that model's list of readonly attributes
- # using +attr_readonly+.
- # [:polymorphic]
- # Specify this association is a polymorphic association by passing +true+.
- # Note: If you've enabled the counter cache, then you may want to add the counter cache attribute
- # to the +attr_readonly+ list in the associated classes (e.g. <tt>class Post; attr_readonly :comments_count; end</tt>).
- # [:validate]
- # When set to +true+, validates new objects added to association when saving the parent object. +false+ by default.
- # If you want to ensure associated objects are revalidated on every update, use +validates_associated+.
- # [:autosave]
- # If true, always save the associated object or destroy it if marked for destruction, when
- # saving the parent object.
- # If false, never save or destroy the associated object.
- # By default, only save the associated object if it's a new record.
- #
- # Note that NestedAttributes::ClassMethods#accepts_nested_attributes_for
- # sets <tt>:autosave</tt> to <tt>true</tt>.
- # [:touch]
- # If true, the associated object will be touched (the updated_at/on attributes set to current time)
- # when this record is either saved or destroyed. If you specify a symbol, that attribute
- # will be updated with the current time in addition to the updated_at/on attribute.
- # Please note that with touching no validation is performed and only the +after_touch+,
- # +after_commit+ and +after_rollback+ callbacks are executed.
- # [:inverse_of]
- # Specifies the name of the #has_one or #has_many association on the associated
- # object that is the inverse of this #belongs_to association. Does not work in
- # combination with the <tt>:polymorphic</tt> options.
- # See ActiveRecord::Associations::ClassMethods's overview on Bi-directional associations for more detail.
- # [:optional]
- # When set to +true+, the association will not have its presence validated.
- # [:required]
- # When set to +true+, the association will also have its presence validated.
- # This will validate the association itself, not the id. You can use
- # +:inverse_of+ to avoid an extra query during validation.
- # NOTE: <tt>required</tt> is set to <tt>true</tt> by default and is deprecated. If
- # you don't want to have association presence validated, use <tt>optional: true</tt>.
- #
- # Option examples:
- # belongs_to :firm, foreign_key: "client_of"
- # belongs_to :person, primary_key: "name", foreign_key: "person_name"
- # belongs_to :author, class_name: "Person", foreign_key: "author_id"
- # belongs_to :valid_coupon, ->(o) { where "discounts > ?", o.payments_count },
- # class_name: "Coupon", foreign_key: "coupon_id"
- # belongs_to :attachable, polymorphic: true
- # belongs_to :project, -> { readonly }
- # belongs_to :post, counter_cache: true
- # belongs_to :comment, touch: true
- # belongs_to :company, touch: :employees_last_updated_at
- # belongs_to :user, optional: true
- def belongs_to(name, scope = nil, options = {})
- reflection = Builder::BelongsTo.build(self, name, scope, options)
- Reflection.add_reflection self, name, reflection
- end
-
- # Specifies a many-to-many relationship with another class. This associates two classes via an
- # intermediate join table. Unless the join table is explicitly specified as an option, it is
- # guessed using the lexical order of the class names. So a join between Developer and Project
- # will give the default join table name of "developers_projects" because "D" precedes "P" alphabetically.
- # Note that this precedence is calculated using the <tt><</tt> operator for String. This
- # means that if the strings are of different lengths, and the strings are equal when compared
- # up to the shortest length, then the longer string is considered of higher
- # lexical precedence than the shorter one. For example, one would expect the tables "paper_boxes" and "papers"
- # to generate a join table name of "papers_paper_boxes" because of the length of the name "paper_boxes",
- # but it in fact generates a join table name of "paper_boxes_papers". Be aware of this caveat, and use the
- # custom <tt>:join_table</tt> option if you need to.
- # If your tables share a common prefix, it will only appear once at the beginning. For example,
- # the tables "catalog_categories" and "catalog_products" generate a join table name of "catalog_categories_products".
- #
- # The join table should not have a primary key or a model associated with it. You must manually generate the
- # join table with a migration such as this:
- #
- # class CreateDevelopersProjectsJoinTable < ActiveRecord::Migration[5.0]
- # def change
- # create_join_table :developers, :projects
+ # The association methods module is included immediately after the
+ # generated attributes methods module, meaning an association will
+ # override the methods for an attribute with the same name.
+ #
+ # == Cardinality and associations
+ #
+ # Active Record associations can be used to describe one-to-one, one-to-many and many-to-many
+ # relationships between models. Each model uses an association to describe its role in
+ # the relation. The #belongs_to association is always used in the model that has
+ # the foreign key.
+ #
+ # === One-to-one
+ #
+ # Use #has_one in the base, and #belongs_to in the associated model.
+ #
+ # class Employee < ActiveRecord::Base
+ # has_one :office
+ # end
+ # class Office < ActiveRecord::Base
+ # belongs_to :employee # foreign key - employee_id
+ # end
+ #
+ # === One-to-many
+ #
+ # Use #has_many in the base, and #belongs_to in the associated model.
+ #
+ # class Manager < ActiveRecord::Base
+ # has_many :employees
+ # end
+ # class Employee < ActiveRecord::Base
+ # belongs_to :manager # foreign key - manager_id
+ # end
+ #
+ # === Many-to-many
+ #
+ # There are two ways to build a many-to-many relationship.
+ #
+ # The first way uses a #has_many association with the <tt>:through</tt> option and a join model, so
+ # there are two stages of associations.
+ #
+ # class Assignment < ActiveRecord::Base
+ # belongs_to :programmer # foreign key - programmer_id
+ # belongs_to :project # foreign key - project_id
+ # end
+ # class Programmer < ActiveRecord::Base
+ # has_many :assignments
+ # has_many :projects, through: :assignments
+ # end
+ # class Project < ActiveRecord::Base
+ # has_many :assignments
+ # has_many :programmers, through: :assignments
+ # end
+ #
+ # For the second way, use #has_and_belongs_to_many in both models. This requires a join table
+ # that has no corresponding model or primary key.
+ #
+ # class Programmer < ActiveRecord::Base
+ # has_and_belongs_to_many :projects # foreign keys in the join table
+ # end
+ # class Project < ActiveRecord::Base
+ # has_and_belongs_to_many :programmers # foreign keys in the join table
+ # end
+ #
+ # Choosing which way to build a many-to-many relationship is not always simple.
+ # If you need to work with the relationship model as its own entity,
+ # use #has_many <tt>:through</tt>. Use #has_and_belongs_to_many when working with legacy schemas or when
+ # you never work directly with the relationship itself.
+ #
+ # == Is it a #belongs_to or #has_one association?
+ #
+ # Both express a 1-1 relationship. The difference is mostly where to place the foreign
+ # key, which goes on the table for the class declaring the #belongs_to relationship.
+ #
+ # class User < ActiveRecord::Base
+ # # I reference an account.
+ # belongs_to :account
+ # end
+ #
+ # class Account < ActiveRecord::Base
+ # # One user references me.
+ # has_one :user
+ # end
+ #
+ # The tables for these classes could look something like:
+ #
+ # CREATE TABLE users (
+ # id int NOT NULL auto_increment,
+ # account_id int default NULL,
+ # name varchar default NULL,
+ # PRIMARY KEY (id)
+ # )
+ #
+ # CREATE TABLE accounts (
+ # id int NOT NULL auto_increment,
+ # name varchar default NULL,
+ # PRIMARY KEY (id)
+ # )
+ #
+ # == Unsaved objects and associations
+ #
+ # You can manipulate objects and associations before they are saved to the database, but
+ # there is some special behavior you should be aware of, mostly involving the saving of
+ # associated objects.
+ #
+ # You can set the <tt>:autosave</tt> option on a #has_one, #belongs_to,
+ # #has_many, or #has_and_belongs_to_many association. Setting it
+ # to +true+ will _always_ save the members, whereas setting it to +false+ will
+ # _never_ save the members. More details about <tt>:autosave</tt> option is available at
+ # AutosaveAssociation.
+ #
+ # === One-to-one associations
+ #
+ # * Assigning an object to a #has_one association automatically saves that object and
+ # the object being replaced (if there is one), in order to update their foreign
+ # keys - except if the parent object is unsaved (<tt>new_record? == true</tt>).
+ # * If either of these saves fail (due to one of the objects being invalid), an
+ # ActiveRecord::RecordNotSaved exception is raised and the assignment is
+ # cancelled.
+ # * If you wish to assign an object to a #has_one association without saving it,
+ # use the <tt>#build_association</tt> method (documented below). The object being
+ # replaced will still be saved to update its foreign key.
+ # * Assigning an object to a #belongs_to association does not save the object, since
+ # the foreign key field belongs on the parent. It does not save the parent either.
+ #
+ # === Collections
+ #
+ # * Adding an object to a collection (#has_many or #has_and_belongs_to_many) automatically
+ # saves that object, except if the parent object (the owner of the collection) is not yet
+ # stored in the database.
+ # * If saving any of the objects being added to a collection (via <tt>push</tt> or similar)
+ # fails, then <tt>push</tt> returns +false+.
+ # * If saving fails while replacing the collection (via <tt>association=</tt>), an
+ # ActiveRecord::RecordNotSaved exception is raised and the assignment is
+ # cancelled.
+ # * You can add an object to a collection without automatically saving it by using the
+ # <tt>collection.build</tt> method (documented below).
+ # * All unsaved (<tt>new_record? == true</tt>) members of the collection are automatically
+ # saved when the parent is saved.
+ #
+ # == Customizing the query
+ #
+ # \Associations are built from <tt>Relation</tt> objects, and you can use the Relation syntax
+ # to customize them. For example, to add a condition:
+ #
+ # class Blog < ActiveRecord::Base
+ # has_many :published_posts, -> { where(published: true) }, class_name: 'Post'
+ # end
+ #
+ # Inside the <tt>-> { ... }</tt> block you can use all of the usual Relation methods.
+ #
+ # === Accessing the owner object
+ #
+ # Sometimes it is useful to have access to the owner object when building the query. The owner
+ # is passed as a parameter to the block. For example, the following association would find all
+ # events that occur on the user's birthday:
+ #
+ # class User < ActiveRecord::Base
+ # has_many :birthday_events, ->(user) { where(starts_on: user.birthday) }, class_name: 'Event'
+ # end
+ #
+ # Note: Joining, eager loading and preloading of these associations is not fully possible.
+ # These operations happen before instance creation and the scope will be called with a +nil+ argument.
+ # This can lead to unexpected behavior and is deprecated.
+ #
+ # == Association callbacks
+ #
+ # Similar to the normal callbacks that hook into the life cycle of an Active Record object,
+ # you can also define callbacks that get triggered when you add an object to or remove an
+ # object from an association collection.
+ #
+ # class Project
+ # has_and_belongs_to_many :developers, after_add: :evaluate_velocity
+ #
+ # def evaluate_velocity(developer)
+ # ...
# end
# end
#
- # It's also a good idea to add indexes to each of those columns to speed up the joins process.
- # However, in MySQL it is advised to add a compound index for both of the columns as MySQL only
- # uses one index per table during the lookup.
- #
- # Adds the following methods for retrieval and query:
- #
- # +collection+ is a placeholder for the symbol passed as the +name+ argument, so
- # <tt>has_and_belongs_to_many :categories</tt> would add among others <tt>categories.empty?</tt>.
- #
- # [collection(force_reload = false)]
- # Returns an array of all the associated objects.
- # An empty array is returned if none are found.
- # [collection<<(object, ...)]
- # Adds one or more objects to the collection by creating associations in the join table
- # (<tt>collection.push</tt> and <tt>collection.concat</tt> are aliases to this method).
- # Note that this operation instantly fires update SQL without waiting for the save or update call on the
- # parent object, unless the parent object is a new record.
- # [collection.delete(object, ...)]
- # Removes one or more objects from the collection by removing their associations from the join table.
- # This does not destroy the objects.
- # [collection.destroy(object, ...)]
- # Removes one or more objects from the collection by running destroy on each association in the join table, overriding any dependent option.
- # This does not destroy the objects.
- # [collection=objects]
- # Replaces the collection's content by deleting and adding objects as appropriate.
- # [collection_singular_ids]
- # Returns an array of the associated objects' ids.
- # [collection_singular_ids=ids]
- # Replace the collection by the objects identified by the primary keys in +ids+.
- # [collection.clear]
- # Removes every object from the collection. This does not destroy the objects.
- # [collection.empty?]
- # Returns +true+ if there are no associated objects.
- # [collection.size]
- # Returns the number of associated objects.
- # [collection.find(id)]
- # Finds an associated object responding to the +id+ and that
- # meets the condition that it has to be associated with this object.
- # Uses the same rules as ActiveRecord::FinderMethods#find.
- # [collection.exists?(...)]
- # Checks whether an associated object with the given conditions exists.
- # Uses the same rules as ActiveRecord::FinderMethods#exists?.
- # [collection.build(attributes = {})]
- # Returns a new object of the collection type that has been instantiated
- # with +attributes+ and linked to this object through the join table, but has not yet been saved.
- # [collection.create(attributes = {})]
- # Returns a new object of the collection type that has been instantiated
- # with +attributes+, linked to this object through the join table, and that has already been
- # saved (if it passed the validation).
- #
- # === Example
- #
- # A Developer class declares <tt>has_and_belongs_to_many :projects</tt>, which will add:
- # * <tt>Developer#projects</tt>
- # * <tt>Developer#projects<<</tt>
- # * <tt>Developer#projects.delete</tt>
- # * <tt>Developer#projects.destroy</tt>
- # * <tt>Developer#projects=</tt>
- # * <tt>Developer#project_ids</tt>
- # * <tt>Developer#project_ids=</tt>
- # * <tt>Developer#projects.clear</tt>
- # * <tt>Developer#projects.empty?</tt>
- # * <tt>Developer#projects.size</tt>
- # * <tt>Developer#projects.find(id)</tt>
- # * <tt>Developer#projects.exists?(...)</tt>
- # * <tt>Developer#projects.build</tt> (similar to <tt>Project.new("developer_id" => id)</tt>)
- # * <tt>Developer#projects.create</tt> (similar to <tt>c = Project.new("developer_id" => id); c.save; c</tt>)
- # The declaration may include an +options+ hash to specialize the behavior of the association.
- #
- # === Scopes
- #
- # You can pass a second argument +scope+ as a callable (i.e. proc or
- # lambda) to retrieve a specific set of records or customize the generated
- # query when you access the associated collection.
- #
- # Scope examples:
- # has_and_belongs_to_many :projects, -> { includes(:milestones, :manager) }
- # has_and_belongs_to_many :categories, ->(category) {
- # where("default_category = ?", category.name)
- # }
- #
- # === Extensions
- #
- # The +extension+ argument allows you to pass a block into a
- # has_and_belongs_to_many association. This is useful for adding new
- # finders, creators and other factory-type methods to be used as part of
- # the association.
- #
- # Extension examples:
- # has_and_belongs_to_many :contractors do
+ # It's possible to stack callbacks by passing them as an array. Example:
+ #
+ # class Project
+ # has_and_belongs_to_many :developers,
+ # after_add: [:evaluate_velocity, Proc.new { |p, d| p.shipping_date = Time.now}]
+ # end
+ #
+ # Possible callbacks are: +before_add+, +after_add+, +before_remove+ and +after_remove+.
+ #
+ # If any of the +before_add+ callbacks throw an exception, the object will not be
+ # added to the collection.
+ #
+ # Similarly, if any of the +before_remove+ callbacks throw an exception, the object
+ # will not be removed from the collection.
+ #
+ # == Association extensions
+ #
+ # The proxy objects that control the access to associations can be extended through anonymous
+ # modules. This is especially beneficial for adding new finders, creators, and other
+ # factory-type methods that are only used as part of this association.
+ #
+ # class Account < ActiveRecord::Base
+ # has_many :people do
+ # def find_or_create_by_name(name)
+ # first_name, last_name = name.split(" ", 2)
+ # find_or_create_by(first_name: first_name, last_name: last_name)
+ # end
+ # end
+ # end
+ #
+ # person = Account.first.people.find_or_create_by_name("David Heinemeier Hansson")
+ # person.first_name # => "David"
+ # person.last_name # => "Heinemeier Hansson"
+ #
+ # If you need to share the same extensions between many associations, you can use a named
+ # extension module.
+ #
+ # module FindOrCreateByNameExtension
# def find_or_create_by_name(name)
# first_name, last_name = name.split(" ", 2)
# find_or_create_by(first_name: first_name, last_name: last_name)
# end
# end
#
- # === Options
- #
- # [:class_name]
- # Specify the class name of the association. Use it only if that name can't be inferred
- # from the association name. So <tt>has_and_belongs_to_many :projects</tt> will by default be linked to the
- # Project class, but if the real class name is SuperProject, you'll have to specify it with this option.
- # [:join_table]
- # Specify the name of the join table if the default based on lexical order isn't what you want.
- # <b>WARNING:</b> If you're overwriting the table name of either class, the +table_name+ method
- # MUST be declared underneath any #has_and_belongs_to_many declaration in order to work.
- # [:foreign_key]
- # Specify the foreign key used for the association. By default this is guessed to be the name
- # of this class in lower-case and "_id" suffixed. So a Person class that makes
- # a #has_and_belongs_to_many association to Project will use "person_id" as the
- # default <tt>:foreign_key</tt>.
- # [:association_foreign_key]
- # Specify the foreign key used for the association on the receiving side of the association.
- # By default this is guessed to be the name of the associated class in lower-case and "_id" suffixed.
- # So if a Person class makes a #has_and_belongs_to_many association to Project,
- # the association will use "project_id" as the default <tt>:association_foreign_key</tt>.
- # [:validate]
- # When set to +true+, validates new objects added to association when saving the parent object. +true+ by default.
- # If you want to ensure associated objects are revalidated on every update, use +validates_associated+.
- # [:autosave]
- # If true, always save the associated objects or destroy them if marked for destruction, when
- # saving the parent object.
- # If false, never save or destroy the associated objects.
- # By default, only save associated objects that are new records.
- #
- # Note that NestedAttributes::ClassMethods#accepts_nested_attributes_for sets
- # <tt>:autosave</tt> to <tt>true</tt>.
- #
- # Option examples:
- # has_and_belongs_to_many :projects
- # has_and_belongs_to_many :projects, -> { includes(:milestones, :manager) }
- # has_and_belongs_to_many :nations, class_name: "Country"
- # has_and_belongs_to_many :categories, join_table: "prods_cats"
- # has_and_belongs_to_many :categories, -> { readonly }
- def has_and_belongs_to_many(name, scope = nil, options = {}, &extension)
- if scope.is_a?(Hash)
- options = scope
- scope = nil
+ # class Account < ActiveRecord::Base
+ # has_many :people, -> { extending FindOrCreateByNameExtension }
+ # end
+ #
+ # class Company < ActiveRecord::Base
+ # has_many :people, -> { extending FindOrCreateByNameExtension }
+ # end
+ #
+ # Some extensions can only be made to work with knowledge of the association's internals.
+ # Extensions can access relevant state using the following methods (where +items+ is the
+ # name of the association):
+ #
+ # * <tt>record.association(:items).owner</tt> - Returns the object the association is part of.
+ # * <tt>record.association(:items).reflection</tt> - Returns the reflection object that describes the association.
+ # * <tt>record.association(:items).target</tt> - Returns the associated object for #belongs_to and #has_one, or
+ # the collection of associated objects for #has_many and #has_and_belongs_to_many.
+ #
+ # However, inside the actual extension code, you will not have access to the <tt>record</tt> as
+ # above. In this case, you can access <tt>proxy_association</tt>. For example,
+ # <tt>record.association(:items)</tt> and <tt>record.items.proxy_association</tt> will return
+ # the same object, allowing you to make calls like <tt>proxy_association.owner</tt> inside
+ # association extensions.
+ #
+ # == Association Join Models
+ #
+ # Has Many associations can be configured with the <tt>:through</tt> option to use an
+ # explicit join model to retrieve the data. This operates similarly to a
+ # #has_and_belongs_to_many association. The advantage is that you're able to add validations,
+ # callbacks, and extra attributes on the join model. Consider the following schema:
+ #
+ # class Author < ActiveRecord::Base
+ # has_many :authorships
+ # has_many :books, through: :authorships
+ # end
+ #
+ # class Authorship < ActiveRecord::Base
+ # belongs_to :author
+ # belongs_to :book
+ # end
+ #
+ # @author = Author.first
+ # @author.authorships.collect { |a| a.book } # selects all books that the author's authorships belong to
+ # @author.books # selects all books by using the Authorship join model
+ #
+ # You can also go through a #has_many association on the join model:
+ #
+ # class Firm < ActiveRecord::Base
+ # has_many :clients
+ # has_many :invoices, through: :clients
+ # end
+ #
+ # class Client < ActiveRecord::Base
+ # belongs_to :firm
+ # has_many :invoices
+ # end
+ #
+ # class Invoice < ActiveRecord::Base
+ # belongs_to :client
+ # end
+ #
+ # @firm = Firm.first
+ # @firm.clients.flat_map { |c| c.invoices } # select all invoices for all clients of the firm
+ # @firm.invoices # selects all invoices by going through the Client join model
+ #
+ # Similarly you can go through a #has_one association on the join model:
+ #
+ # class Group < ActiveRecord::Base
+ # has_many :users
+ # has_many :avatars, through: :users
+ # end
+ #
+ # class User < ActiveRecord::Base
+ # belongs_to :group
+ # has_one :avatar
+ # end
+ #
+ # class Avatar < ActiveRecord::Base
+ # belongs_to :user
+ # end
+ #
+ # @group = Group.first
+ # @group.users.collect { |u| u.avatar }.compact # select all avatars for all users in the group
+ # @group.avatars # selects all avatars by going through the User join model.
+ #
+ # An important caveat with going through #has_one or #has_many associations on the
+ # join model is that these associations are *read-only*. For example, the following
+ # would not work following the previous example:
+ #
+ # @group.avatars << Avatar.new # this would work if User belonged_to Avatar rather than the other way around
+ # @group.avatars.delete(@group.avatars.last) # so would this
+ #
+ # == Setting Inverses
+ #
+ # If you are using a #belongs_to on the join model, it is a good idea to set the
+ # <tt>:inverse_of</tt> option on the #belongs_to, which will mean that the following example
+ # works correctly (where <tt>tags</tt> is a #has_many <tt>:through</tt> association):
+ #
+ # @post = Post.first
+ # @tag = @post.tags.build name: "ruby"
+ # @tag.save
+ #
+ # The last line ought to save the through record (a <tt>Tagging</tt>). This will only work if the
+ # <tt>:inverse_of</tt> is set:
+ #
+ # class Tagging < ActiveRecord::Base
+ # belongs_to :post
+ # belongs_to :tag, inverse_of: :taggings
+ # end
+ #
+ # If you do not set the <tt>:inverse_of</tt> record, the association will
+ # do its best to match itself up with the correct inverse. Automatic
+ # inverse detection only works on #has_many, #has_one, and
+ # #belongs_to associations.
+ #
+ # Extra options on the associations, as defined in the
+ # <tt>AssociationReflection::INVALID_AUTOMATIC_INVERSE_OPTIONS</tt> constant, will
+ # also prevent the association's inverse from being found automatically.
+ #
+ # The automatic guessing of the inverse association uses a heuristic based
+ # on the name of the class, so it may not work for all associations,
+ # especially the ones with non-standard names.
+ #
+ # You can turn off the automatic detection of inverse associations by setting
+ # the <tt>:inverse_of</tt> option to <tt>false</tt> like so:
+ #
+ # class Tagging < ActiveRecord::Base
+ # belongs_to :tag, inverse_of: false
+ # end
+ #
+ # == Nested \Associations
+ #
+ # You can actually specify *any* association with the <tt>:through</tt> option, including an
+ # association which has a <tt>:through</tt> option itself. For example:
+ #
+ # class Author < ActiveRecord::Base
+ # has_many :posts
+ # has_many :comments, through: :posts
+ # has_many :commenters, through: :comments
+ # end
+ #
+ # class Post < ActiveRecord::Base
+ # has_many :comments
+ # end
+ #
+ # class Comment < ActiveRecord::Base
+ # belongs_to :commenter
+ # end
+ #
+ # @author = Author.first
+ # @author.commenters # => People who commented on posts written by the author
+ #
+ # An equivalent way of setting up this association this would be:
+ #
+ # class Author < ActiveRecord::Base
+ # has_many :posts
+ # has_many :commenters, through: :posts
+ # end
+ #
+ # class Post < ActiveRecord::Base
+ # has_many :comments
+ # has_many :commenters, through: :comments
+ # end
+ #
+ # class Comment < ActiveRecord::Base
+ # belongs_to :commenter
+ # end
+ #
+ # When using a nested association, you will not be able to modify the association because there
+ # is not enough information to know what modification to make. For example, if you tried to
+ # add a <tt>Commenter</tt> in the example above, there would be no way to tell how to set up the
+ # intermediate <tt>Post</tt> and <tt>Comment</tt> objects.
+ #
+ # == Polymorphic \Associations
+ #
+ # Polymorphic associations on models are not restricted on what types of models they
+ # can be associated with. Rather, they specify an interface that a #has_many association
+ # must adhere to.
+ #
+ # class Asset < ActiveRecord::Base
+ # belongs_to :attachable, polymorphic: true
+ # end
+ #
+ # class Post < ActiveRecord::Base
+ # has_many :assets, as: :attachable # The :as option specifies the polymorphic interface to use.
+ # end
+ #
+ # @asset.attachable = @post
+ #
+ # This works by using a type column in addition to a foreign key to specify the associated
+ # record. In the Asset example, you'd need an +attachable_id+ integer column and an
+ # +attachable_type+ string column.
+ #
+ # Using polymorphic associations in combination with single table inheritance (STI) is
+ # a little tricky. In order for the associations to work as expected, ensure that you
+ # store the base model for the STI models in the type column of the polymorphic
+ # association. To continue with the asset example above, suppose there are guest posts
+ # and member posts that use the posts table for STI. In this case, there must be a +type+
+ # column in the posts table.
+ #
+ # Note: The <tt>attachable_type=</tt> method is being called when assigning an +attachable+.
+ # The +class_name+ of the +attachable+ is passed as a String.
+ #
+ # class Asset < ActiveRecord::Base
+ # belongs_to :attachable, polymorphic: true
+ #
+ # def attachable_type=(class_name)
+ # super(class_name.constantize.base_class.to_s)
+ # end
+ # end
+ #
+ # class Post < ActiveRecord::Base
+ # # because we store "Post" in attachable_type now dependent: :destroy will work
+ # has_many :assets, as: :attachable, dependent: :destroy
+ # end
+ #
+ # class GuestPost < Post
+ # end
+ #
+ # class MemberPost < Post
+ # end
+ #
+ # == Caching
+ #
+ # All of the methods are built on a simple caching principle that will keep the result
+ # of the last query around unless specifically instructed not to. The cache is even
+ # shared across methods to make it even cheaper to use the macro-added methods without
+ # worrying too much about performance at the first go.
+ #
+ # project.milestones # fetches milestones from the database
+ # project.milestones.size # uses the milestone cache
+ # project.milestones.empty? # uses the milestone cache
+ # project.milestones(true).size # fetches milestones from the database
+ # project.milestones # uses the milestone cache
+ #
+ # == Eager loading of associations
+ #
+ # Eager loading is a way to find objects of a certain class and a number of named associations.
+ # It is one of the easiest ways to prevent the dreaded N+1 problem in which fetching 100
+ # posts that each need to display their author triggers 101 database queries. Through the
+ # use of eager loading, the number of queries will be reduced from 101 to 2.
+ #
+ # class Post < ActiveRecord::Base
+ # belongs_to :author
+ # has_many :comments
+ # end
+ #
+ # Consider the following loop using the class above:
+ #
+ # Post.all.each do |post|
+ # puts "Post: " + post.title
+ # puts "Written by: " + post.author.name
+ # puts "Last comment on: " + post.comments.first.created_on
+ # end
+ #
+ # To iterate over these one hundred posts, we'll generate 201 database queries. Let's
+ # first just optimize it for retrieving the author:
+ #
+ # Post.includes(:author).each do |post|
+ #
+ # This references the name of the #belongs_to association that also used the <tt>:author</tt>
+ # symbol. After loading the posts, +find+ will collect the +author_id+ from each one and load
+ # all of the referenced authors with one query. Doing so will cut down the number of queries
+ # from 201 to 102.
+ #
+ # We can improve upon the situation further by referencing both associations in the finder with:
+ #
+ # Post.includes(:author, :comments).each do |post|
+ #
+ # This will load all comments with a single query. This reduces the total number of queries
+ # to 3. In general, the number of queries will be 1 plus the number of associations
+ # named (except if some of the associations are polymorphic #belongs_to - see below).
+ #
+ # To include a deep hierarchy of associations, use a hash:
+ #
+ # Post.includes(:author, { comments: { author: :gravatar } }).each do |post|
+ #
+ # The above code will load all the comments and all of their associated
+ # authors and gravatars. You can mix and match any combination of symbols,
+ # arrays, and hashes to retrieve the associations you want to load.
+ #
+ # All of this power shouldn't fool you into thinking that you can pull out huge amounts
+ # of data with no performance penalty just because you've reduced the number of queries.
+ # The database still needs to send all the data to Active Record and it still needs to
+ # be processed. So it's no catch-all for performance problems, but it's a great way to
+ # cut down on the number of queries in a situation as the one described above.
+ #
+ # Since only one table is loaded at a time, conditions or orders cannot reference tables
+ # other than the main one. If this is the case, Active Record falls back to the previously
+ # used <tt>LEFT OUTER JOIN</tt> based strategy. For example:
+ #
+ # Post.includes([:author, :comments]).where(['comments.approved = ?', true])
+ #
+ # This will result in a single SQL query with joins along the lines of:
+ # <tt>LEFT OUTER JOIN comments ON comments.post_id = posts.id</tt> and
+ # <tt>LEFT OUTER JOIN authors ON authors.id = posts.author_id</tt>. Note that using conditions
+ # like this can have unintended consequences.
+ # In the above example, posts with no approved comments are not returned at all because
+ # the conditions apply to the SQL statement as a whole and not just to the association.
+ #
+ # You must disambiguate column references for this fallback to happen, for example
+ # <tt>order: "author.name DESC"</tt> will work but <tt>order: "name DESC"</tt> will not.
+ #
+ # If you want to load all posts (including posts with no approved comments), then write
+ # your own <tt>LEFT OUTER JOIN</tt> query using <tt>ON</tt>:
+ #
+ # Post.joins("LEFT OUTER JOIN comments ON comments.post_id = posts.id AND comments.approved = '1'")
+ #
+ # In this case, it is usually more natural to include an association which has conditions defined on it:
+ #
+ # class Post < ActiveRecord::Base
+ # has_many :approved_comments, -> { where(approved: true) }, class_name: 'Comment'
+ # end
+ #
+ # Post.includes(:approved_comments)
+ #
+ # This will load posts and eager load the +approved_comments+ association, which contains
+ # only those comments that have been approved.
+ #
+ # If you eager load an association with a specified <tt>:limit</tt> option, it will be ignored,
+ # returning all the associated objects:
+ #
+ # class Picture < ActiveRecord::Base
+ # has_many :most_recent_comments, -> { order('id DESC').limit(10) }, class_name: 'Comment'
+ # end
+ #
+ # Picture.includes(:most_recent_comments).first.most_recent_comments # => returns all associated comments.
+ #
+ # Eager loading is supported with polymorphic associations.
+ #
+ # class Address < ActiveRecord::Base
+ # belongs_to :addressable, polymorphic: true
+ # end
+ #
+ # A call that tries to eager load the addressable model
+ #
+ # Address.includes(:addressable)
+ #
+ # This will execute one query to load the addresses and load the addressables with one
+ # query per addressable type.
+ # For example, if all the addressables are either of class Person or Company, then a total
+ # of 3 queries will be executed. The list of addressable types to load is determined on
+ # the back of the addresses loaded. This is not supported if Active Record has to fallback
+ # to the previous implementation of eager loading and will raise ActiveRecord::EagerLoadPolymorphicError.
+ # The reason is that the parent model's type is a column value so its corresponding table
+ # name cannot be put in the +FROM+/+JOIN+ clauses of that query.
+ #
+ # == Table Aliasing
+ #
+ # Active Record uses table aliasing in the case that a table is referenced multiple times
+ # in a join. If a table is referenced only once, the standard table name is used. The
+ # second time, the table is aliased as <tt>#{reflection_name}_#{parent_table_name}</tt>.
+ # Indexes are appended for any more successive uses of the table name.
+ #
+ # Post.joins(:comments)
+ # # => SELECT ... FROM posts INNER JOIN comments ON ...
+ # Post.joins(:special_comments) # STI
+ # # => SELECT ... FROM posts INNER JOIN comments ON ... AND comments.type = 'SpecialComment'
+ # Post.joins(:comments, :special_comments) # special_comments is the reflection name, posts is the parent table name
+ # # => SELECT ... FROM posts INNER JOIN comments ON ... INNER JOIN comments special_comments_posts
+ #
+ # Acts as tree example:
+ #
+ # TreeMixin.joins(:children)
+ # # => SELECT ... FROM mixins INNER JOIN mixins childrens_mixins ...
+ # TreeMixin.joins(children: :parent)
+ # # => SELECT ... FROM mixins INNER JOIN mixins childrens_mixins ...
+ # INNER JOIN parents_mixins ...
+ # TreeMixin.joins(children: {parent: :children})
+ # # => SELECT ... FROM mixins INNER JOIN mixins childrens_mixins ...
+ # INNER JOIN parents_mixins ...
+ # INNER JOIN mixins childrens_mixins_2
+ #
+ # Has and Belongs to Many join tables use the same idea, but add a <tt>_join</tt> suffix:
+ #
+ # Post.joins(:categories)
+ # # => SELECT ... FROM posts INNER JOIN categories_posts ... INNER JOIN categories ...
+ # Post.joins(categories: :posts)
+ # # => SELECT ... FROM posts INNER JOIN categories_posts ... INNER JOIN categories ...
+ # INNER JOIN categories_posts posts_categories_join INNER JOIN posts posts_categories
+ # Post.joins(categories: {posts: :categories})
+ # # => SELECT ... FROM posts INNER JOIN categories_posts ... INNER JOIN categories ...
+ # INNER JOIN categories_posts posts_categories_join INNER JOIN posts posts_categories
+ # INNER JOIN categories_posts categories_posts_join INNER JOIN categories categories_posts_2
+ #
+ # If you wish to specify your own custom joins using ActiveRecord::QueryMethods#joins method, those table
+ # names will take precedence over the eager associations:
+ #
+ # Post.joins(:comments).joins("inner join comments ...")
+ # # => SELECT ... FROM posts INNER JOIN comments_posts ON ... INNER JOIN comments ...
+ # Post.joins(:comments, :special_comments).joins("inner join comments ...")
+ # # => SELECT ... FROM posts INNER JOIN comments comments_posts ON ...
+ # INNER JOIN comments special_comments_posts ...
+ # INNER JOIN comments ...
+ #
+ # Table aliases are automatically truncated according to the maximum length of table identifiers
+ # according to the specific database.
+ #
+ # == Modules
+ #
+ # By default, associations will look for objects within the current module scope. Consider:
+ #
+ # module MyApplication
+ # module Business
+ # class Firm < ActiveRecord::Base
+ # has_many :clients
+ # end
+ #
+ # class Client < ActiveRecord::Base; end
+ # end
+ # end
+ #
+ # When <tt>Firm#clients</tt> is called, it will in turn call
+ # <tt>MyApplication::Business::Client.find_all_by_firm_id(firm.id)</tt>.
+ # If you want to associate with a class in another module scope, this can be done by
+ # specifying the complete class name.
+ #
+ # module MyApplication
+ # module Business
+ # class Firm < ActiveRecord::Base; end
+ # end
+ #
+ # module Billing
+ # class Account < ActiveRecord::Base
+ # belongs_to :firm, class_name: "MyApplication::Business::Firm"
+ # end
+ # end
+ # end
+ #
+ # == Bi-directional associations
+ #
+ # When you specify an association, there is usually an association on the associated model
+ # that specifies the same relationship in reverse. For example, with the following models:
+ #
+ # class Dungeon < ActiveRecord::Base
+ # has_many :traps
+ # has_one :evil_wizard
+ # end
+ #
+ # class Trap < ActiveRecord::Base
+ # belongs_to :dungeon
+ # end
+ #
+ # class EvilWizard < ActiveRecord::Base
+ # belongs_to :dungeon
+ # end
+ #
+ # The +traps+ association on +Dungeon+ and the +dungeon+ association on +Trap+ are
+ # the inverse of each other, and the inverse of the +dungeon+ association on +EvilWizard+
+ # is the +evil_wizard+ association on +Dungeon+ (and vice-versa). By default,
+ # Active Record can guess the inverse of the association based on the name
+ # of the class. The result is the following:
+ #
+ # d = Dungeon.first
+ # t = d.traps.first
+ # d.object_id == t.dungeon.object_id # => true
+ #
+ # The +Dungeon+ instances +d+ and <tt>t.dungeon</tt> in the above example refer to
+ # the same in-memory instance since the association matches the name of the class.
+ # The result would be the same if we added +:inverse_of+ to our model definitions:
+ #
+ # class Dungeon < ActiveRecord::Base
+ # has_many :traps, inverse_of: :dungeon
+ # has_one :evil_wizard, inverse_of: :dungeon
+ # end
+ #
+ # class Trap < ActiveRecord::Base
+ # belongs_to :dungeon, inverse_of: :traps
+ # end
+ #
+ # class EvilWizard < ActiveRecord::Base
+ # belongs_to :dungeon, inverse_of: :evil_wizard
+ # end
+ #
+ # There are limitations to <tt>:inverse_of</tt> support:
+ #
+ # * does not work with <tt>:through</tt> associations.
+ # * does not work with <tt>:polymorphic</tt> associations.
+ # * inverse associations for #belongs_to associations #has_many are ignored.
+ #
+ # For more information, see the documentation for the +:inverse_of+ option.
+ #
+ # == Deleting from associations
+ #
+ # === Dependent associations
+ #
+ # #has_many, #has_one, and #belongs_to associations support the <tt>:dependent</tt> option.
+ # This allows you to specify that associated records should be deleted when the owner is
+ # deleted.
+ #
+ # For example:
+ #
+ # class Author
+ # has_many :posts, dependent: :destroy
+ # end
+ # Author.find(1).destroy # => Will destroy all of the author's posts, too
+ #
+ # The <tt>:dependent</tt> option can have different values which specify how the deletion
+ # is done. For more information, see the documentation for this option on the different
+ # specific association types. When no option is given, the behavior is to do nothing
+ # with the associated records when destroying a record.
+ #
+ # Note that <tt>:dependent</tt> is implemented using Rails' callback
+ # system, which works by processing callbacks in order. Therefore, other
+ # callbacks declared either before or after the <tt>:dependent</tt> option
+ # can affect what it does.
+ #
+ # Note that <tt>:dependent</tt> option is ignored for #has_one <tt>:through</tt> associations.
+ #
+ # === Delete or destroy?
+ #
+ # #has_many and #has_and_belongs_to_many associations have the methods <tt>destroy</tt>,
+ # <tt>delete</tt>, <tt>destroy_all</tt> and <tt>delete_all</tt>.
+ #
+ # For #has_and_belongs_to_many, <tt>delete</tt> and <tt>destroy</tt> are the same: they
+ # cause the records in the join table to be removed.
+ #
+ # For #has_many, <tt>destroy</tt> and <tt>destroy_all</tt> will always call the <tt>destroy</tt> method of the
+ # record(s) being removed so that callbacks are run. However <tt>delete</tt> and <tt>delete_all</tt> will either
+ # do the deletion according to the strategy specified by the <tt>:dependent</tt> option, or
+ # if no <tt>:dependent</tt> option is given, then it will follow the default strategy.
+ # The default strategy is to do nothing (leave the foreign keys with the parent ids set), except for
+ # #has_many <tt>:through</tt>, where the default strategy is <tt>delete_all</tt> (delete
+ # the join records, without running their callbacks).
+ #
+ # There is also a <tt>clear</tt> method which is the same as <tt>delete_all</tt>, except that
+ # it returns the association rather than the records which have been deleted.
+ #
+ # === What gets deleted?
+ #
+ # There is a potential pitfall here: #has_and_belongs_to_many and #has_many <tt>:through</tt>
+ # associations have records in join tables, as well as the associated records. So when we
+ # call one of these deletion methods, what exactly should be deleted?
+ #
+ # The answer is that it is assumed that deletion on an association is about removing the
+ # <i>link</i> between the owner and the associated object(s), rather than necessarily the
+ # associated objects themselves. So with #has_and_belongs_to_many and #has_many
+ # <tt>:through</tt>, the join records will be deleted, but the associated records won't.
+ #
+ # This makes sense if you think about it: if you were to call <tt>post.tags.delete(Tag.find_by(name: 'food'))</tt>
+ # you would want the 'food' tag to be unlinked from the post, rather than for the tag itself
+ # to be removed from the database.
+ #
+ # However, there are examples where this strategy doesn't make sense. For example, suppose
+ # a person has many projects, and each project has many tasks. If we deleted one of a person's
+ # tasks, we would probably not want the project to be deleted. In this scenario, the delete method
+ # won't actually work: it can only be used if the association on the join model is a
+ # #belongs_to. In other situations you are expected to perform operations directly on
+ # either the associated records or the <tt>:through</tt> association.
+ #
+ # With a regular #has_many there is no distinction between the "associated records"
+ # and the "link", so there is only one choice for what gets deleted.
+ #
+ # With #has_and_belongs_to_many and #has_many <tt>:through</tt>, if you want to delete the
+ # associated records themselves, you can always do something along the lines of
+ # <tt>person.tasks.each(&:destroy)</tt>.
+ #
+ # == Type safety with ActiveRecord::AssociationTypeMismatch
+ #
+ # If you attempt to assign an object to an association that doesn't match the inferred
+ # or specified <tt>:class_name</tt>, you'll get an ActiveRecord::AssociationTypeMismatch.
+ #
+ # == Options
+ #
+ # All of the association macros can be specialized through options. This makes cases
+ # more complex than the simple and guessable ones possible.
+ module ClassMethods
+ # Specifies a one-to-many association. The following methods for retrieval and query of
+ # collections of associated objects will be added:
+ #
+ # +collection+ is a placeholder for the symbol passed as the +name+ argument, so
+ # <tt>has_many :clients</tt> would add among others <tt>clients.empty?</tt>.
+ #
+ # [collection(force_reload = false)]
+ # Returns an array of all the associated objects.
+ # An empty array is returned if none are found.
+ # [collection<<(object, ...)]
+ # Adds one or more objects to the collection by setting their foreign keys to the collection's primary key.
+ # Note that this operation instantly fires update SQL without waiting for the save or update call on the
+ # parent object, unless the parent object is a new record.
+ # This will also run validations and callbacks of associated object(s).
+ # [collection.delete(object, ...)]
+ # Removes one or more objects from the collection by setting their foreign keys to +NULL+.
+ # Objects will be in addition destroyed if they're associated with <tt>dependent: :destroy</tt>,
+ # and deleted if they're associated with <tt>dependent: :delete_all</tt>.
+ #
+ # If the <tt>:through</tt> option is used, then the join records are deleted (rather than
+ # nullified) by default, but you can specify <tt>dependent: :destroy</tt> or
+ # <tt>dependent: :nullify</tt> to override this.
+ # [collection.destroy(object, ...)]
+ # Removes one or more objects from the collection by running <tt>destroy</tt> on
+ # each record, regardless of any dependent option, ensuring callbacks are run.
+ #
+ # If the <tt>:through</tt> option is used, then the join records are destroyed
+ # instead, not the objects themselves.
+ # [collection=objects]
+ # Replaces the collections content by deleting and adding objects as appropriate. If the <tt>:through</tt>
+ # option is true callbacks in the join models are triggered except destroy callbacks, since deletion is
+ # direct by default. You can specify <tt>dependent: :destroy</tt> or
+ # <tt>dependent: :nullify</tt> to override this.
+ # [collection_singular_ids]
+ # Returns an array of the associated objects' ids
+ # [collection_singular_ids=ids]
+ # Replace the collection with the objects identified by the primary keys in +ids+. This
+ # method loads the models and calls <tt>collection=</tt>. See above.
+ # [collection.clear]
+ # Removes every object from the collection. This destroys the associated objects if they
+ # are associated with <tt>dependent: :destroy</tt>, deletes them directly from the
+ # database if <tt>dependent: :delete_all</tt>, otherwise sets their foreign keys to +NULL+.
+ # If the <tt>:through</tt> option is true no destroy callbacks are invoked on the join models.
+ # Join models are directly deleted.
+ # [collection.empty?]
+ # Returns +true+ if there are no associated objects.
+ # [collection.size]
+ # Returns the number of associated objects.
+ # [collection.find(...)]
+ # Finds an associated object according to the same rules as ActiveRecord::FinderMethods#find.
+ # [collection.exists?(...)]
+ # Checks whether an associated object with the given conditions exists.
+ # Uses the same rules as ActiveRecord::FinderMethods#exists?.
+ # [collection.build(attributes = {}, ...)]
+ # Returns one or more new objects of the collection type that have been instantiated
+ # with +attributes+ and linked to this object through a foreign key, but have not yet
+ # been saved.
+ # [collection.create(attributes = {})]
+ # Returns a new object of the collection type that has been instantiated
+ # with +attributes+, linked to this object through a foreign key, and that has already
+ # been saved (if it passed the validation). *Note*: This only works if the base model
+ # already exists in the DB, not if it is a new (unsaved) record!
+ # [collection.create!(attributes = {})]
+ # Does the same as <tt>collection.create</tt>, but raises ActiveRecord::RecordInvalid
+ # if the record is invalid.
+ #
+ # === Example
+ #
+ # A <tt>Firm</tt> class declares <tt>has_many :clients</tt>, which will add:
+ # * <tt>Firm#clients</tt> (similar to <tt>Client.where(firm_id: id)</tt>)
+ # * <tt>Firm#clients<<</tt>
+ # * <tt>Firm#clients.delete</tt>
+ # * <tt>Firm#clients.destroy</tt>
+ # * <tt>Firm#clients=</tt>
+ # * <tt>Firm#client_ids</tt>
+ # * <tt>Firm#client_ids=</tt>
+ # * <tt>Firm#clients.clear</tt>
+ # * <tt>Firm#clients.empty?</tt> (similar to <tt>firm.clients.size == 0</tt>)
+ # * <tt>Firm#clients.size</tt> (similar to <tt>Client.count "firm_id = #{id}"</tt>)
+ # * <tt>Firm#clients.find</tt> (similar to <tt>Client.where(firm_id: id).find(id)</tt>)
+ # * <tt>Firm#clients.exists?(name: 'ACME')</tt> (similar to <tt>Client.exists?(name: 'ACME', firm_id: firm.id)</tt>)
+ # * <tt>Firm#clients.build</tt> (similar to <tt>Client.new("firm_id" => id)</tt>)
+ # * <tt>Firm#clients.create</tt> (similar to <tt>c = Client.new("firm_id" => id); c.save; c</tt>)
+ # * <tt>Firm#clients.create!</tt> (similar to <tt>c = Client.new("firm_id" => id); c.save!</tt>)
+ # The declaration can also include an +options+ hash to specialize the behavior of the association.
+ #
+ # === Scopes
+ #
+ # You can pass a second argument +scope+ as a callable (i.e. proc or
+ # lambda) to retrieve a specific set of records or customize the generated
+ # query when you access the associated collection.
+ #
+ # Scope examples:
+ # has_many :comments, -> { where(author_id: 1) }
+ # has_many :employees, -> { joins(:address) }
+ # has_many :posts, ->(post) { where("max_post_length > ?", post.length) }
+ #
+ # === Extensions
+ #
+ # The +extension+ argument allows you to pass a block into a has_many
+ # association. This is useful for adding new finders, creators and other
+ # factory-type methods to be used as part of the association.
+ #
+ # Extension examples:
+ # has_many :employees do
+ # def find_or_create_by_name(name)
+ # first_name, last_name = name.split(" ", 2)
+ # find_or_create_by(first_name: first_name, last_name: last_name)
+ # end
+ # end
+ #
+ # === Options
+ # [:class_name]
+ # Specify the class name of the association. Use it only if that name can't be inferred
+ # from the association name. So <tt>has_many :products</tt> will by default be linked
+ # to the +Product+ class, but if the real class name is +SpecialProduct+, you'll have to
+ # specify it with this option.
+ # [:foreign_key]
+ # Specify the foreign key used for the association. By default this is guessed to be the name
+ # of this class in lower-case and "_id" suffixed. So a Person class that makes a #has_many
+ # association will use "person_id" as the default <tt>:foreign_key</tt>.
+ # [:foreign_type]
+ # Specify the column used to store the associated object's type, if this is a polymorphic
+ # association. By default this is guessed to be the name of the polymorphic association
+ # specified on "as" option with a "_type" suffix. So a class that defines a
+ # <tt>has_many :tags, as: :taggable</tt> association will use "taggable_type" as the
+ # default <tt>:foreign_type</tt>.
+ # [:primary_key]
+ # Specify the name of the column to use as the primary key for the association. By default this is +id+.
+ # [:dependent]
+ # Controls what happens to the associated objects when
+ # their owner is destroyed. Note that these are implemented as
+ # callbacks, and Rails executes callbacks in order. Therefore, other
+ # similar callbacks may affect the <tt>:dependent</tt> behavior, and the
+ # <tt>:dependent</tt> behavior may affect other callbacks.
+ #
+ # * <tt>:destroy</tt> causes all the associated objects to also be destroyed.
+ # * <tt>:delete_all</tt> causes all the associated objects to be deleted directly from the database (so callbacks will not be executed).
+ # * <tt>:nullify</tt> causes the foreign keys to be set to +NULL+. Callbacks are not executed.
+ # * <tt>:restrict_with_exception</tt> causes an exception to be raised if there are any associated records.
+ # * <tt>:restrict_with_error</tt> causes an error to be added to the owner if there are any associated objects.
+ #
+ # If using with the <tt>:through</tt> option, the association on the join model must be
+ # a #belongs_to, and the records which get deleted are the join records, rather than
+ # the associated records.
+ #
+ # If using <tt>dependent: :destroy</tt> on a scoped association, only the scoped objects are destroyed.
+ # For example, if a Post model defines
+ # <tt>has_many :comments, -> { where published: true }, dependent: :destroy</tt> and <tt>destroy</tt> is
+ # called on a post, only published comments are destroyed. This means that any unpublished comments in the
+ # database would still contain a foreign key pointing to the now deleted post.
+ # [:counter_cache]
+ # This option can be used to configure a custom named <tt>:counter_cache.</tt> You only need this option,
+ # when you customized the name of your <tt>:counter_cache</tt> on the #belongs_to association.
+ # [:as]
+ # Specifies a polymorphic interface (See #belongs_to).
+ # [:through]
+ # Specifies an association through which to perform the query. This can be any other type
+ # of association, including other <tt>:through</tt> associations. Options for <tt>:class_name</tt>,
+ # <tt>:primary_key</tt> and <tt>:foreign_key</tt> are ignored, as the association uses the
+ # source reflection.
+ #
+ # If the association on the join model is a #belongs_to, the collection can be modified
+ # and the records on the <tt>:through</tt> model will be automatically created and removed
+ # as appropriate. Otherwise, the collection is read-only, so you should manipulate the
+ # <tt>:through</tt> association directly.
+ #
+ # If you are going to modify the association (rather than just read from it), then it is
+ # a good idea to set the <tt>:inverse_of</tt> option on the source association on the
+ # join model. This allows associated records to be built which will automatically create
+ # the appropriate join model records when they are saved. (See the 'Association Join Models'
+ # section above.)
+ # [:source]
+ # Specifies the source association name used by #has_many <tt>:through</tt> queries.
+ # Only use it if the name cannot be inferred from the association.
+ # <tt>has_many :subscribers, through: :subscriptions</tt> will look for either <tt>:subscribers</tt> or
+ # <tt>:subscriber</tt> on Subscription, unless a <tt>:source</tt> is given.
+ # [:source_type]
+ # Specifies type of the source association used by #has_many <tt>:through</tt> queries where the source
+ # association is a polymorphic #belongs_to.
+ # [:validate]
+ # When set to +true+, validates new objects added to association when saving the parent object. +true+ by default.
+ # If you want to ensure associated objects are revalidated on every update, use +validates_associated+.
+ # [:autosave]
+ # If true, always save the associated objects or destroy them if marked for destruction,
+ # when saving the parent object. If false, never save or destroy the associated objects.
+ # By default, only save associated objects that are new records. This option is implemented as a
+ # +before_save+ callback. Because callbacks are run in the order they are defined, associated objects
+ # may need to be explicitly saved in any user-defined +before_save+ callbacks.
+ #
+ # Note that NestedAttributes::ClassMethods#accepts_nested_attributes_for sets
+ # <tt>:autosave</tt> to <tt>true</tt>.
+ # [:inverse_of]
+ # Specifies the name of the #belongs_to association on the associated object
+ # that is the inverse of this #has_many association. Does not work in combination
+ # with <tt>:through</tt> or <tt>:as</tt> options.
+ # See ActiveRecord::Associations::ClassMethods's overview on Bi-directional associations for more detail.
+ # [:extend]
+ # Specifies a module or array of modules that will be extended into the association object returned.
+ # Useful for defining methods on associations, especially when they should be shared between multiple
+ # association objects.
+ #
+ # Option examples:
+ # has_many :comments, -> { order("posted_on") }
+ # has_many :comments, -> { includes(:author) }
+ # has_many :people, -> { where(deleted: false).order("name") }, class_name: "Person"
+ # has_many :tracks, -> { order("position") }, dependent: :destroy
+ # has_many :comments, dependent: :nullify
+ # has_many :tags, as: :taggable
+ # has_many :reports, -> { readonly }
+ # has_many :subscribers, through: :subscriptions, source: :user
+ def has_many(name, scope = nil, options = {}, &extension)
+ reflection = Builder::HasMany.build(self, name, scope, options, &extension)
+ Reflection.add_reflection self, name, reflection
+ end
+
+ # Specifies a one-to-one association with another class. This method should only be used
+ # if the other class contains the foreign key. If the current class contains the foreign key,
+ # then you should use #belongs_to instead. See also ActiveRecord::Associations::ClassMethods's overview
+ # on when to use #has_one and when to use #belongs_to.
+ #
+ # The following methods for retrieval and query of a single associated object will be added:
+ #
+ # +association+ is a placeholder for the symbol passed as the +name+ argument, so
+ # <tt>has_one :manager</tt> would add among others <tt>manager.nil?</tt>.
+ #
+ # [association(force_reload = false)]
+ # Returns the associated object. +nil+ is returned if none is found.
+ # [association=(associate)]
+ # Assigns the associate object, extracts the primary key, sets it as the foreign key,
+ # and saves the associate object. To avoid database inconsistencies, permanently deletes an existing
+ # associated object when assigning a new one, even if the new one isn't saved to database.
+ # [build_association(attributes = {})]
+ # Returns a new object of the associated type that has been instantiated
+ # with +attributes+ and linked to this object through a foreign key, but has not
+ # yet been saved.
+ # [create_association(attributes = {})]
+ # Returns a new object of the associated type that has been instantiated
+ # with +attributes+, linked to this object through a foreign key, and that
+ # has already been saved (if it passed the validation).
+ # [create_association!(attributes = {})]
+ # Does the same as <tt>create_association</tt>, but raises ActiveRecord::RecordInvalid
+ # if the record is invalid.
+ #
+ # === Example
+ #
+ # An Account class declares <tt>has_one :beneficiary</tt>, which will add:
+ # * <tt>Account#beneficiary</tt> (similar to <tt>Beneficiary.where(account_id: id).first</tt>)
+ # * <tt>Account#beneficiary=(beneficiary)</tt> (similar to <tt>beneficiary.account_id = account.id; beneficiary.save</tt>)
+ # * <tt>Account#build_beneficiary</tt> (similar to <tt>Beneficiary.new("account_id" => id)</tt>)
+ # * <tt>Account#create_beneficiary</tt> (similar to <tt>b = Beneficiary.new("account_id" => id); b.save; b</tt>)
+ # * <tt>Account#create_beneficiary!</tt> (similar to <tt>b = Beneficiary.new("account_id" => id); b.save!; b</tt>)
+ #
+ # === Scopes
+ #
+ # You can pass a second argument +scope+ as a callable (i.e. proc or
+ # lambda) to retrieve a specific record or customize the generated query
+ # when you access the associated object.
+ #
+ # Scope examples:
+ # has_one :author, -> { where(comment_id: 1) }
+ # has_one :employer, -> { joins(:company) }
+ # has_one :dob, ->(dob) { where("Date.new(2000, 01, 01) > ?", dob) }
+ #
+ # === Options
+ #
+ # The declaration can also include an +options+ hash to specialize the behavior of the association.
+ #
+ # Options are:
+ # [:class_name]
+ # Specify the class name of the association. Use it only if that name can't be inferred
+ # from the association name. So <tt>has_one :manager</tt> will by default be linked to the Manager class, but
+ # if the real class name is Person, you'll have to specify it with this option.
+ # [:dependent]
+ # Controls what happens to the associated object when
+ # its owner is destroyed:
+ #
+ # * <tt>:destroy</tt> causes the associated object to also be destroyed
+ # * <tt>:delete</tt> causes the associated object to be deleted directly from the database (so callbacks will not execute)
+ # * <tt>:nullify</tt> causes the foreign key to be set to +NULL+. Callbacks are not executed.
+ # * <tt>:restrict_with_exception</tt> causes an exception to be raised if there is an associated record
+ # * <tt>:restrict_with_error</tt> causes an error to be added to the owner if there is an associated object
+ #
+ # Note that <tt>:dependent</tt> option is ignored when using <tt>:through</tt> option.
+ # [:foreign_key]
+ # Specify the foreign key used for the association. By default this is guessed to be the name
+ # of this class in lower-case and "_id" suffixed. So a Person class that makes a #has_one association
+ # will use "person_id" as the default <tt>:foreign_key</tt>.
+ # [:foreign_type]
+ # Specify the column used to store the associated object's type, if this is a polymorphic
+ # association. By default this is guessed to be the name of the polymorphic association
+ # specified on "as" option with a "_type" suffix. So a class that defines a
+ # <tt>has_one :tag, as: :taggable</tt> association will use "taggable_type" as the
+ # default <tt>:foreign_type</tt>.
+ # [:primary_key]
+ # Specify the method that returns the primary key used for the association. By default this is +id+.
+ # [:as]
+ # Specifies a polymorphic interface (See #belongs_to).
+ # [:through]
+ # Specifies a Join Model through which to perform the query. Options for <tt>:class_name</tt>,
+ # <tt>:primary_key</tt>, and <tt>:foreign_key</tt> are ignored, as the association uses the
+ # source reflection. You can only use a <tt>:through</tt> query through a #has_one
+ # or #belongs_to association on the join model.
+ # [:source]
+ # Specifies the source association name used by #has_one <tt>:through</tt> queries.
+ # Only use it if the name cannot be inferred from the association.
+ # <tt>has_one :favorite, through: :favorites</tt> will look for a
+ # <tt>:favorite</tt> on Favorite, unless a <tt>:source</tt> is given.
+ # [:source_type]
+ # Specifies type of the source association used by #has_one <tt>:through</tt> queries where the source
+ # association is a polymorphic #belongs_to.
+ # [:validate]
+ # When set to +true+, validates new objects added to association when saving the parent object. +false+ by default.
+ # If you want to ensure associated objects are revalidated on every update, use +validates_associated+.
+ # [:autosave]
+ # If true, always save the associated object or destroy it if marked for destruction,
+ # when saving the parent object. If false, never save or destroy the associated object.
+ # By default, only save the associated object if it's a new record.
+ #
+ # Note that NestedAttributes::ClassMethods#accepts_nested_attributes_for sets
+ # <tt>:autosave</tt> to <tt>true</tt>.
+ # [:inverse_of]
+ # Specifies the name of the #belongs_to association on the associated object
+ # that is the inverse of this #has_one association. Does not work in combination
+ # with <tt>:through</tt> or <tt>:as</tt> options.
+ # See ActiveRecord::Associations::ClassMethods's overview on Bi-directional associations for more detail.
+ # [:required]
+ # When set to +true+, the association will also have its presence validated.
+ # This will validate the association itself, not the id. You can use
+ # +:inverse_of+ to avoid an extra query during validation.
+ #
+ # Option examples:
+ # has_one :credit_card, dependent: :destroy # destroys the associated credit card
+ # has_one :credit_card, dependent: :nullify # updates the associated records foreign
+ # # key value to NULL rather than destroying it
+ # has_one :last_comment, -> { order('posted_on') }, class_name: "Comment"
+ # has_one :project_manager, -> { where(role: 'project_manager') }, class_name: "Person"
+ # has_one :attachment, as: :attachable
+ # has_one :boss, -> { readonly }
+ # has_one :club, through: :membership
+ # has_one :primary_address, -> { where(primary: true) }, through: :addressables, source: :addressable
+ # has_one :credit_card, required: true
+ def has_one(name, scope = nil, options = {})
+ reflection = Builder::HasOne.build(self, name, scope, options)
+ Reflection.add_reflection self, name, reflection
end
- habtm_reflection = ActiveRecord::Reflection::HasAndBelongsToManyReflection.new(name, scope, options, self)
+ # Specifies a one-to-one association with another class. This method should only be used
+ # if this class contains the foreign key. If the other class contains the foreign key,
+ # then you should use #has_one instead. See also ActiveRecord::Associations::ClassMethods's overview
+ # on when to use #has_one and when to use #belongs_to.
+ #
+ # Methods will be added for retrieval and query for a single associated object, for which
+ # this object holds an id:
+ #
+ # +association+ is a placeholder for the symbol passed as the +name+ argument, so
+ # <tt>belongs_to :author</tt> would add among others <tt>author.nil?</tt>.
+ #
+ # [association(force_reload = false)]
+ # Returns the associated object. +nil+ is returned if none is found.
+ # [association=(associate)]
+ # Assigns the associate object, extracts the primary key, and sets it as the foreign key.
+ # [build_association(attributes = {})]
+ # Returns a new object of the associated type that has been instantiated
+ # with +attributes+ and linked to this object through a foreign key, but has not yet been saved.
+ # [create_association(attributes = {})]
+ # Returns a new object of the associated type that has been instantiated
+ # with +attributes+, linked to this object through a foreign key, and that
+ # has already been saved (if it passed the validation).
+ # [create_association!(attributes = {})]
+ # Does the same as <tt>create_association</tt>, but raises ActiveRecord::RecordInvalid
+ # if the record is invalid.
+ #
+ # === Example
+ #
+ # A Post class declares <tt>belongs_to :author</tt>, which will add:
+ # * <tt>Post#author</tt> (similar to <tt>Author.find(author_id)</tt>)
+ # * <tt>Post#author=(author)</tt> (similar to <tt>post.author_id = author.id</tt>)
+ # * <tt>Post#build_author</tt> (similar to <tt>post.author = Author.new</tt>)
+ # * <tt>Post#create_author</tt> (similar to <tt>post.author = Author.new; post.author.save; post.author</tt>)
+ # * <tt>Post#create_author!</tt> (similar to <tt>post.author = Author.new; post.author.save!; post.author</tt>)
+ # The declaration can also include an +options+ hash to specialize the behavior of the association.
+ #
+ # === Scopes
+ #
+ # You can pass a second argument +scope+ as a callable (i.e. proc or
+ # lambda) to retrieve a specific record or customize the generated query
+ # when you access the associated object.
+ #
+ # Scope examples:
+ # belongs_to :firm, -> { where(id: 2) }
+ # belongs_to :user, -> { joins(:friends) }
+ # belongs_to :level, ->(level) { where("game_level > ?", level.current) }
+ #
+ # === Options
+ #
+ # [:class_name]
+ # Specify the class name of the association. Use it only if that name can't be inferred
+ # from the association name. So <tt>belongs_to :author</tt> will by default be linked to the Author class, but
+ # if the real class name is Person, you'll have to specify it with this option.
+ # [:foreign_key]
+ # Specify the foreign key used for the association. By default this is guessed to be the name
+ # of the association with an "_id" suffix. So a class that defines a <tt>belongs_to :person</tt>
+ # association will use "person_id" as the default <tt>:foreign_key</tt>. Similarly,
+ # <tt>belongs_to :favorite_person, class_name: "Person"</tt> will use a foreign key
+ # of "favorite_person_id".
+ # [:foreign_type]
+ # Specify the column used to store the associated object's type, if this is a polymorphic
+ # association. By default this is guessed to be the name of the association with a "_type"
+ # suffix. So a class that defines a <tt>belongs_to :taggable, polymorphic: true</tt>
+ # association will use "taggable_type" as the default <tt>:foreign_type</tt>.
+ # [:primary_key]
+ # Specify the method that returns the primary key of associated object used for the association.
+ # By default this is id.
+ # [:dependent]
+ # If set to <tt>:destroy</tt>, the associated object is destroyed when this object is. If set to
+ # <tt>:delete</tt>, the associated object is deleted *without* calling its destroy method.
+ # This option should not be specified when #belongs_to is used in conjunction with
+ # a #has_many relationship on another class because of the potential to leave
+ # orphaned records behind.
+ # [:counter_cache]
+ # Caches the number of belonging objects on the associate class through the use of CounterCache::ClassMethods#increment_counter
+ # and CounterCache::ClassMethods#decrement_counter. The counter cache is incremented when an object of this
+ # class is created and decremented when it's destroyed. This requires that a column
+ # named <tt>#{table_name}_count</tt> (such as +comments_count+ for a belonging Comment class)
+ # is used on the associate class (such as a Post class) - that is the migration for
+ # <tt>#{table_name}_count</tt> is created on the associate class (such that <tt>Post.comments_count</tt> will
+ # return the count cached, see note below). You can also specify a custom counter
+ # cache column by providing a column name instead of a +true+/+false+ value to this
+ # option (e.g., <tt>counter_cache: :my_custom_counter</tt>.)
+ # Note: Specifying a counter cache will add it to that model's list of readonly attributes
+ # using +attr_readonly+.
+ # [:polymorphic]
+ # Specify this association is a polymorphic association by passing +true+.
+ # Note: If you've enabled the counter cache, then you may want to add the counter cache attribute
+ # to the +attr_readonly+ list in the associated classes (e.g. <tt>class Post; attr_readonly :comments_count; end</tt>).
+ # [:validate]
+ # When set to +true+, validates new objects added to association when saving the parent object. +false+ by default.
+ # If you want to ensure associated objects are revalidated on every update, use +validates_associated+.
+ # [:autosave]
+ # If true, always save the associated object or destroy it if marked for destruction, when
+ # saving the parent object.
+ # If false, never save or destroy the associated object.
+ # By default, only save the associated object if it's a new record.
+ #
+ # Note that NestedAttributes::ClassMethods#accepts_nested_attributes_for
+ # sets <tt>:autosave</tt> to <tt>true</tt>.
+ # [:touch]
+ # If true, the associated object will be touched (the updated_at/on attributes set to current time)
+ # when this record is either saved or destroyed. If you specify a symbol, that attribute
+ # will be updated with the current time in addition to the updated_at/on attribute.
+ # Please note that with touching no validation is performed and only the +after_touch+,
+ # +after_commit+ and +after_rollback+ callbacks are executed.
+ # [:inverse_of]
+ # Specifies the name of the #has_one or #has_many association on the associated
+ # object that is the inverse of this #belongs_to association. Does not work in
+ # combination with the <tt>:polymorphic</tt> options.
+ # See ActiveRecord::Associations::ClassMethods's overview on Bi-directional associations for more detail.
+ # [:optional]
+ # When set to +true+, the association will not have its presence validated.
+ # [:required]
+ # When set to +true+, the association will also have its presence validated.
+ # This will validate the association itself, not the id. You can use
+ # +:inverse_of+ to avoid an extra query during validation.
+ # NOTE: <tt>required</tt> is set to <tt>true</tt> by default and is deprecated. If
+ # you don't want to have association presence validated, use <tt>optional: true</tt>.
+ #
+ # Option examples:
+ # belongs_to :firm, foreign_key: "client_of"
+ # belongs_to :person, primary_key: "name", foreign_key: "person_name"
+ # belongs_to :author, class_name: "Person", foreign_key: "author_id"
+ # belongs_to :valid_coupon, ->(o) { where "discounts > ?", o.payments_count },
+ # class_name: "Coupon", foreign_key: "coupon_id"
+ # belongs_to :attachable, polymorphic: true
+ # belongs_to :project, -> { readonly }
+ # belongs_to :post, counter_cache: true
+ # belongs_to :comment, touch: true
+ # belongs_to :company, touch: :employees_last_updated_at
+ # belongs_to :user, optional: true
+ def belongs_to(name, scope = nil, options = {})
+ reflection = Builder::BelongsTo.build(self, name, scope, options)
+ Reflection.add_reflection self, name, reflection
+ end
- builder = Builder::HasAndBelongsToMany.new name, self, options
+ # Specifies a many-to-many relationship with another class. This associates two classes via an
+ # intermediate join table. Unless the join table is explicitly specified as an option, it is
+ # guessed using the lexical order of the class names. So a join between Developer and Project
+ # will give the default join table name of "developers_projects" because "D" precedes "P" alphabetically.
+ # Note that this precedence is calculated using the <tt><</tt> operator for String. This
+ # means that if the strings are of different lengths, and the strings are equal when compared
+ # up to the shortest length, then the longer string is considered of higher
+ # lexical precedence than the shorter one. For example, one would expect the tables "paper_boxes" and "papers"
+ # to generate a join table name of "papers_paper_boxes" because of the length of the name "paper_boxes",
+ # but it in fact generates a join table name of "paper_boxes_papers". Be aware of this caveat, and use the
+ # custom <tt>:join_table</tt> option if you need to.
+ # If your tables share a common prefix, it will only appear once at the beginning. For example,
+ # the tables "catalog_categories" and "catalog_products" generate a join table name of "catalog_categories_products".
+ #
+ # The join table should not have a primary key or a model associated with it. You must manually generate the
+ # join table with a migration such as this:
+ #
+ # class CreateDevelopersProjectsJoinTable < ActiveRecord::Migration[5.0]
+ # def change
+ # create_join_table :developers, :projects
+ # end
+ # end
+ #
+ # It's also a good idea to add indexes to each of those columns to speed up the joins process.
+ # However, in MySQL it is advised to add a compound index for both of the columns as MySQL only
+ # uses one index per table during the lookup.
+ #
+ # Adds the following methods for retrieval and query:
+ #
+ # +collection+ is a placeholder for the symbol passed as the +name+ argument, so
+ # <tt>has_and_belongs_to_many :categories</tt> would add among others <tt>categories.empty?</tt>.
+ #
+ # [collection(force_reload = false)]
+ # Returns an array of all the associated objects.
+ # An empty array is returned if none are found.
+ # [collection<<(object, ...)]
+ # Adds one or more objects to the collection by creating associations in the join table
+ # (<tt>collection.push</tt> and <tt>collection.concat</tt> are aliases to this method).
+ # Note that this operation instantly fires update SQL without waiting for the save or update call on the
+ # parent object, unless the parent object is a new record.
+ # [collection.delete(object, ...)]
+ # Removes one or more objects from the collection by removing their associations from the join table.
+ # This does not destroy the objects.
+ # [collection.destroy(object, ...)]
+ # Removes one or more objects from the collection by running destroy on each association in the join table, overriding any dependent option.
+ # This does not destroy the objects.
+ # [collection=objects]
+ # Replaces the collection's content by deleting and adding objects as appropriate.
+ # [collection_singular_ids]
+ # Returns an array of the associated objects' ids.
+ # [collection_singular_ids=ids]
+ # Replace the collection by the objects identified by the primary keys in +ids+.
+ # [collection.clear]
+ # Removes every object from the collection. This does not destroy the objects.
+ # [collection.empty?]
+ # Returns +true+ if there are no associated objects.
+ # [collection.size]
+ # Returns the number of associated objects.
+ # [collection.find(id)]
+ # Finds an associated object responding to the +id+ and that
+ # meets the condition that it has to be associated with this object.
+ # Uses the same rules as ActiveRecord::FinderMethods#find.
+ # [collection.exists?(...)]
+ # Checks whether an associated object with the given conditions exists.
+ # Uses the same rules as ActiveRecord::FinderMethods#exists?.
+ # [collection.build(attributes = {})]
+ # Returns a new object of the collection type that has been instantiated
+ # with +attributes+ and linked to this object through the join table, but has not yet been saved.
+ # [collection.create(attributes = {})]
+ # Returns a new object of the collection type that has been instantiated
+ # with +attributes+, linked to this object through the join table, and that has already been
+ # saved (if it passed the validation).
+ #
+ # === Example
+ #
+ # A Developer class declares <tt>has_and_belongs_to_many :projects</tt>, which will add:
+ # * <tt>Developer#projects</tt>
+ # * <tt>Developer#projects<<</tt>
+ # * <tt>Developer#projects.delete</tt>
+ # * <tt>Developer#projects.destroy</tt>
+ # * <tt>Developer#projects=</tt>
+ # * <tt>Developer#project_ids</tt>
+ # * <tt>Developer#project_ids=</tt>
+ # * <tt>Developer#projects.clear</tt>
+ # * <tt>Developer#projects.empty?</tt>
+ # * <tt>Developer#projects.size</tt>
+ # * <tt>Developer#projects.find(id)</tt>
+ # * <tt>Developer#projects.exists?(...)</tt>
+ # * <tt>Developer#projects.build</tt> (similar to <tt>Project.new("developer_id" => id)</tt>)
+ # * <tt>Developer#projects.create</tt> (similar to <tt>c = Project.new("developer_id" => id); c.save; c</tt>)
+ # The declaration may include an +options+ hash to specialize the behavior of the association.
+ #
+ # === Scopes
+ #
+ # You can pass a second argument +scope+ as a callable (i.e. proc or
+ # lambda) to retrieve a specific set of records or customize the generated
+ # query when you access the associated collection.
+ #
+ # Scope examples:
+ # has_and_belongs_to_many :projects, -> { includes(:milestones, :manager) }
+ # has_and_belongs_to_many :categories, ->(category) {
+ # where("default_category = ?", category.name)
+ # }
+ #
+ # === Extensions
+ #
+ # The +extension+ argument allows you to pass a block into a
+ # has_and_belongs_to_many association. This is useful for adding new
+ # finders, creators and other factory-type methods to be used as part of
+ # the association.
+ #
+ # Extension examples:
+ # has_and_belongs_to_many :contractors do
+ # def find_or_create_by_name(name)
+ # first_name, last_name = name.split(" ", 2)
+ # find_or_create_by(first_name: first_name, last_name: last_name)
+ # end
+ # end
+ #
+ # === Options
+ #
+ # [:class_name]
+ # Specify the class name of the association. Use it only if that name can't be inferred
+ # from the association name. So <tt>has_and_belongs_to_many :projects</tt> will by default be linked to the
+ # Project class, but if the real class name is SuperProject, you'll have to specify it with this option.
+ # [:join_table]
+ # Specify the name of the join table if the default based on lexical order isn't what you want.
+ # <b>WARNING:</b> If you're overwriting the table name of either class, the +table_name+ method
+ # MUST be declared underneath any #has_and_belongs_to_many declaration in order to work.
+ # [:foreign_key]
+ # Specify the foreign key used for the association. By default this is guessed to be the name
+ # of this class in lower-case and "_id" suffixed. So a Person class that makes
+ # a #has_and_belongs_to_many association to Project will use "person_id" as the
+ # default <tt>:foreign_key</tt>.
+ # [:association_foreign_key]
+ # Specify the foreign key used for the association on the receiving side of the association.
+ # By default this is guessed to be the name of the associated class in lower-case and "_id" suffixed.
+ # So if a Person class makes a #has_and_belongs_to_many association to Project,
+ # the association will use "project_id" as the default <tt>:association_foreign_key</tt>.
+ # [:validate]
+ # When set to +true+, validates new objects added to association when saving the parent object. +true+ by default.
+ # If you want to ensure associated objects are revalidated on every update, use +validates_associated+.
+ # [:autosave]
+ # If true, always save the associated objects or destroy them if marked for destruction, when
+ # saving the parent object.
+ # If false, never save or destroy the associated objects.
+ # By default, only save associated objects that are new records.
+ #
+ # Note that NestedAttributes::ClassMethods#accepts_nested_attributes_for sets
+ # <tt>:autosave</tt> to <tt>true</tt>.
+ #
+ # Option examples:
+ # has_and_belongs_to_many :projects
+ # has_and_belongs_to_many :projects, -> { includes(:milestones, :manager) }
+ # has_and_belongs_to_many :nations, class_name: "Country"
+ # has_and_belongs_to_many :categories, join_table: "prods_cats"
+ # has_and_belongs_to_many :categories, -> { readonly }
+ def has_and_belongs_to_many(name, scope = nil, **options, &extension)
+ habtm_reflection = ActiveRecord::Reflection::HasAndBelongsToManyReflection.new(name, scope, options, self)
- join_model = builder.through_model
+ builder = Builder::HasAndBelongsToMany.new name, self, options
- const_set join_model.name, join_model
- private_constant join_model.name
+ join_model = builder.through_model
- middle_reflection = builder.middle_reflection join_model
+ const_set join_model.name, join_model
+ private_constant join_model.name
- Builder::HasMany.define_callbacks self, middle_reflection
- Reflection.add_reflection self, middle_reflection.name, middle_reflection
- middle_reflection.parent_reflection = habtm_reflection
+ middle_reflection = builder.middle_reflection join_model
- include Module.new {
- class_eval <<-RUBY, __FILE__, __LINE__ + 1
- def destroy_associations
- association(:#{middle_reflection.name}).delete_all(:delete_all)
- association(:#{name}).reset
- super
- end
- RUBY
- }
+ Builder::HasMany.define_callbacks self, middle_reflection
+ Reflection.add_reflection self, middle_reflection.name, middle_reflection
+ middle_reflection.parent_reflection = habtm_reflection
- hm_options = {}
- hm_options[:through] = middle_reflection.name
- hm_options[:source] = join_model.right_reflection.name
+ include Module.new {
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
+ def destroy_associations
+ association(:#{middle_reflection.name}).delete_all(:delete_all)
+ association(:#{name}).reset
+ super
+ end
+ RUBY
+ }
- [:before_add, :after_add, :before_remove, :after_remove, :autosave, :validate, :join_table, :class_name, :extend].each do |k|
- hm_options[k] = options[k] if options.key? k
- end
+ hm_options = {}
+ hm_options[:through] = middle_reflection.name
+ hm_options[:source] = join_model.right_reflection.name
- has_many name, scope, hm_options, &extension
- self._reflections[name.to_s].parent_reflection = habtm_reflection
+ [:before_add, :after_add, :before_remove, :after_remove, :autosave, :validate, :join_table, :class_name, :extend].each do |k|
+ hm_options[k] = options[k] if options.key? k
+ end
+
+ has_many name, scope, hm_options, &extension
+ self._reflections[name.to_s].parent_reflection = habtm_reflection
+ end
end
- end
end
end
diff --git a/activerecord/lib/active_record/associations/alias_tracker.rb b/activerecord/lib/active_record/associations/alias_tracker.rb
index 021bc32237..3963008a76 100644
--- a/activerecord/lib/active_record/associations/alias_tracker.rb
+++ b/activerecord/lib/active_record/associations/alias_tracker.rb
@@ -1,4 +1,4 @@
-require 'active_support/core_ext/string/conversions'
+require "active_support/core_ext/string/conversions"
module ActiveRecord
module Associations
diff --git a/activerecord/lib/active_record/associations/association.rb b/activerecord/lib/active_record/associations/association.rb
index 62e867a353..84d0493a60 100644
--- a/activerecord/lib/active_record/associations/association.rb
+++ b/activerecord/lib/active_record/associations/association.rb
@@ -1,4 +1,4 @@
-require 'active_support/core_ext/array/wrap'
+require "active_support/core_ext/array/wrap"
module ActiveRecord
module Associations
@@ -19,7 +19,7 @@ module ActiveRecord
attr_reader :owner, :target, :reflection
attr_accessor :inversed
- delegate :options, :to => :reflection
+ delegate :options, to: :reflection
def initialize(owner, reflection)
reflection.check_validity!
@@ -112,6 +112,15 @@ module ActiveRecord
record
end
+ # Remove the inverse association, if possible
+ def remove_inverse_instance(record)
+ if invertible_for?(record)
+ inverse = record.association(inverse_reflection_for(record).name)
+ inverse.target = nil
+ inverse.inversed = false
+ end
+ end
+
# Returns the class of the target. belongs_to polymorphic overrides this to look at the
# polymorphic_type field on the owner.
def klass
@@ -166,13 +175,21 @@ module ActiveRecord
def initialize_attributes(record, except_from_scope_attributes = nil) #:nodoc:
except_from_scope_attributes ||= {}
skip_assign = [reflection.foreign_key, reflection.type].compact
- assigned_keys = record.changed
+ assigned_keys = record.changed_attribute_names_to_save
assigned_keys += except_from_scope_attributes.keys.map(&:to_s)
attributes = create_scope.except(*(assigned_keys - skip_assign))
record.assign_attributes(attributes)
set_inverse_instance(record)
end
+ def create(attributes = {}, &block)
+ _create_record(attributes, &block)
+ end
+
+ def create!(attributes = {}, &block)
+ _create_record(attributes, true, &block)
+ end
+
private
def find_target?
@@ -246,7 +263,7 @@ module ActiveRecord
# so that when stale_state is different from the value stored on the last find_target,
# the target is stale.
#
- # This is only relevant to certain associations, which is why it returns nil by default.
+ # This is only relevant to certain associations, which is why it returns +nil+ by default.
def stale_state
end
diff --git a/activerecord/lib/active_record/associations/association_scope.rb b/activerecord/lib/active_record/associations/association_scope.rb
index 15844de0bc..c6d204d3c2 100644
--- a/activerecord/lib/active_record/associations/association_scope.rb
+++ b/activerecord/lib/active_record/associations/association_scope.rb
@@ -49,117 +49,119 @@ module ActiveRecord
binds
end
+ # TODO Change this to private once we've dropped Ruby 2.2 support.
+ # Workaround for Ruby 2.2 "private attribute?" warning.
protected
- attr_reader :value_transformation
+ attr_reader :value_transformation
private
- def join(table, constraint)
- table.create_join(table, table.create_on(constraint), join_type)
- end
+ def join(table, constraint)
+ table.create_join(table, table.create_on(constraint), join_type)
+ end
+
+ def last_chain_scope(scope, table, reflection, owner, association_klass)
+ join_keys = reflection.join_keys(association_klass)
+ key = join_keys.key
+ foreign_key = join_keys.foreign_key
- def last_chain_scope(scope, table, reflection, owner, association_klass)
- join_keys = reflection.join_keys(association_klass)
- key = join_keys.key
- foreign_key = join_keys.foreign_key
+ value = transform_value(owner[foreign_key])
+ scope = scope.where(table.name => { key => value })
- value = transform_value(owner[foreign_key])
- scope = scope.where(table.name => { key => value })
+ if reflection.type
+ polymorphic_type = transform_value(owner.class.base_class.name)
+ scope = scope.where(table.name => { reflection.type => polymorphic_type })
+ end
- if reflection.type
- polymorphic_type = transform_value(owner.class.base_class.name)
- scope = scope.where(table.name => { reflection.type => polymorphic_type })
+ scope
end
- scope
- end
+ def transform_value(value)
+ value_transformation.call(value)
+ end
- def transform_value(value)
- value_transformation.call(value)
- end
+ def next_chain_scope(scope, table, reflection, association_klass, foreign_table, next_reflection)
+ join_keys = reflection.join_keys(association_klass)
+ key = join_keys.key
+ foreign_key = join_keys.foreign_key
- def next_chain_scope(scope, table, reflection, association_klass, foreign_table, next_reflection)
- join_keys = reflection.join_keys(association_klass)
- key = join_keys.key
- foreign_key = join_keys.foreign_key
+ constraint = table[key].eq(foreign_table[foreign_key])
- constraint = table[key].eq(foreign_table[foreign_key])
+ if reflection.type
+ value = transform_value(next_reflection.klass.base_class.name)
+ scope = scope.where(table.name => { reflection.type => value })
+ end
- if reflection.type
- value = transform_value(next_reflection.klass.base_class.name)
- scope = scope.where(table.name => { reflection.type => value })
+ scope = scope.joins(join(foreign_table, constraint))
end
- scope = scope.joins(join(foreign_table, constraint))
- end
+ class ReflectionProxy < SimpleDelegator # :nodoc:
+ attr_accessor :next
+ attr_reader :alias_name
- class ReflectionProxy < SimpleDelegator # :nodoc:
- attr_accessor :next
- attr_reader :alias_name
+ def initialize(reflection, alias_name)
+ super(reflection)
+ @alias_name = alias_name
+ end
- def initialize(reflection, alias_name)
- super(reflection)
- @alias_name = alias_name
+ def all_includes; nil; end
end
- def all_includes; nil; end
- end
-
- def get_chain(reflection, association, tracker)
- name = reflection.name
- runtime_reflection = Reflection::RuntimeReflection.new(reflection, association)
- previous_reflection = runtime_reflection
- reflection.chain.drop(1).each do |refl|
- alias_name = tracker.aliased_table_for(refl.table_name, refl.alias_candidate(name))
- proxy = ReflectionProxy.new(refl, alias_name)
- previous_reflection.next = proxy
- previous_reflection = proxy
+ def get_chain(reflection, association, tracker)
+ name = reflection.name
+ runtime_reflection = Reflection::RuntimeReflection.new(reflection, association)
+ previous_reflection = runtime_reflection
+ reflection.chain.drop(1).each do |refl|
+ alias_name = tracker.aliased_table_for(refl.table_name, refl.alias_candidate(name))
+ proxy = ReflectionProxy.new(refl, alias_name)
+ previous_reflection.next = proxy
+ previous_reflection = proxy
+ end
+ [runtime_reflection, previous_reflection]
end
- [runtime_reflection, previous_reflection]
- end
- def add_constraints(scope, owner, association_klass, refl, chain_head, chain_tail)
- owner_reflection = chain_tail
- table = owner_reflection.alias_name
- scope = last_chain_scope(scope, table, owner_reflection, owner, association_klass)
+ def add_constraints(scope, owner, association_klass, refl, chain_head, chain_tail)
+ owner_reflection = chain_tail
+ table = owner_reflection.alias_name
+ scope = last_chain_scope(scope, table, owner_reflection, owner, association_klass)
- reflection = chain_head
- while reflection
- table = reflection.alias_name
+ reflection = chain_head
+ while reflection
+ table = reflection.alias_name
- unless reflection == chain_tail
- next_reflection = reflection.next
- foreign_table = next_reflection.alias_name
- scope = next_chain_scope(scope, table, reflection, association_klass, foreign_table, next_reflection)
- end
+ unless reflection == chain_tail
+ next_reflection = reflection.next
+ foreign_table = next_reflection.alias_name
+ scope = next_chain_scope(scope, table, reflection, association_klass, foreign_table, next_reflection)
+ end
- # Exclude the scope of the association itself, because that
- # was already merged in the #scope method.
- reflection.constraints.each do |scope_chain_item|
- item = eval_scope(reflection.klass, scope_chain_item, owner)
+ # Exclude the scope of the association itself, because that
+ # was already merged in the #scope method.
+ reflection.constraints.each do |scope_chain_item|
+ item = eval_scope(reflection.klass, scope_chain_item, owner)
- if scope_chain_item == refl.scope
- scope.merge! item.except(:where, :includes)
- end
+ if scope_chain_item == refl.scope
+ scope.merge! item.except(:where, :includes)
+ end
+
+ reflection.all_includes do
+ scope.includes! item.includes_values
+ end
- reflection.all_includes do
- scope.includes! item.includes_values
+ scope.unscope!(*item.unscope_values)
+ scope.where_clause += item.where_clause
+ scope.order_values |= item.order_values
end
- scope.unscope!(*item.unscope_values)
- scope.where_clause += item.where_clause
- scope.order_values |= item.order_values
+ reflection = reflection.next
end
- reflection = reflection.next
+ scope
end
- scope
- end
-
- def eval_scope(klass, scope, owner)
- klass.unscoped.instance_exec(owner, &scope)
- end
+ def eval_scope(klass, scope, owner)
+ klass.unscoped.instance_exec(owner, &scope)
+ end
end
end
end
diff --git a/activerecord/lib/active_record/associations/belongs_to_association.rb b/activerecord/lib/active_record/associations/belongs_to_association.rb
index 24997370b2..64b2311911 100644
--- a/activerecord/lib/active_record/associations/belongs_to_association.rb
+++ b/activerecord/lib/active_record/associations/belongs_to_association.rb
@@ -2,7 +2,6 @@ module ActiveRecord
# = Active Record Belongs To Association
module Associations
class BelongsToAssociation < SingularAssociation #:nodoc:
-
def handle_dependency
target.send(options[:dependent]) if load_target
end
diff --git a/activerecord/lib/active_record/associations/builder/belongs_to.rb b/activerecord/lib/active_record/associations/builder/belongs_to.rb
index 3121e70a04..a1609ab0fb 100644
--- a/activerecord/lib/active_record/associations/builder/belongs_to.rb
+++ b/activerecord/lib/active_record/associations/builder/belongs_to.rb
@@ -35,17 +35,17 @@ module ActiveRecord::Associations::Builder # :nodoc:
@_after_create_counter_called = false
elsif (@_after_replace_counter_called ||= false)
@_after_replace_counter_called = false
- elsif attribute_changed?(foreign_key) && !new_record?
+ elsif saved_change_to_attribute?(foreign_key) && !new_record?
if reflection.polymorphic?
- model = attribute(reflection.foreign_type).try(:constantize)
- model_was = attribute_was(reflection.foreign_type).try(:constantize)
+ model = attribute_in_database(reflection.foreign_type).try(:constantize)
+ model_was = attribute_before_last_save(reflection.foreign_type).try(:constantize)
else
model = reflection.klass
model_was = reflection.klass
end
- foreign_key_was = attribute_was foreign_key
- foreign_key = attribute foreign_key
+ foreign_key_was = attribute_before_last_save foreign_key
+ foreign_key = attribute_in_database foreign_key
if foreign_key && model.respond_to?(:increment_counter)
model.increment_counter(cache_column, foreign_key)
@@ -70,14 +70,16 @@ module ActiveRecord::Associations::Builder # :nodoc:
klass.attr_readonly cache_column if klass && klass.respond_to?(:attr_readonly)
end
- def self.touch_record(o, foreign_key, name, touch, touch_method) # :nodoc:
- old_foreign_id = o.changed_attributes[foreign_key]
+ def self.touch_record(o, changes, foreign_key, name, touch, touch_method) # :nodoc:
+ old_foreign_id = changes[foreign_key] && changes[foreign_key].first
if old_foreign_id
association = o.association(name)
reflection = association.reflection
if reflection.polymorphic?
- klass = o.public_send("#{reflection.foreign_type}_was").constantize
+ foreign_type = reflection.foreign_type
+ klass = changes[foreign_type] && changes[foreign_type].first || o.public_send(foreign_type)
+ klass = klass.constantize
else
klass = association.klass
end
@@ -107,13 +109,13 @@ module ActiveRecord::Associations::Builder # :nodoc:
n = reflection.name
touch = reflection.options[:touch]
- callback = lambda { |record|
- BelongsTo.touch_record(record, foreign_key, n, touch, belongs_to_touch_method)
- }
+ callback = lambda { |changes_method| lambda { |record|
+ BelongsTo.touch_record(record, record.send(changes_method), foreign_key, n, touch, belongs_to_touch_method)
+ }}
- model.after_save callback, if: :changed?
- model.after_touch callback
- model.after_destroy callback
+ model.after_save callback.(:saved_changes), if: :saved_changes?
+ model.after_touch callback.(:changes_to_save)
+ model.after_destroy callback.(:changes_to_save)
end
def self.add_destroy_callbacks(model, reflection)
diff --git a/activerecord/lib/active_record/associations/builder/collection_association.rb b/activerecord/lib/active_record/associations/builder/collection_association.rb
index f25bd7ca9f..edeb6491bd 100644
--- a/activerecord/lib/active_record/associations/builder/collection_association.rb
+++ b/activerecord/lib/active_record/associations/builder/collection_association.rb
@@ -1,10 +1,9 @@
# This class is inherited by the has_many and has_many_and_belongs_to_many association classes
-require 'active_record/associations'
+require "active_record/associations"
module ActiveRecord::Associations::Builder # :nodoc:
class CollectionAssociation < Association #:nodoc:
-
CALLBACKS = [:before_add, :after_add, :before_remove, :after_remove]
def self.valid_options(options)
diff --git a/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb b/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb
index 5fbd79d118..6b71826431 100644
--- a/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb
+++ b/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb
@@ -16,9 +16,9 @@ module ActiveRecord::Associations::Builder # :nodoc:
private
- def klass
- @lhs_class.send(:compute_type, @rhs_class_name)
- end
+ def klass
+ @lhs_class.send(:compute_type, @rhs_class_name)
+ end
end
def self.build(lhs_class, name, options)
@@ -28,7 +28,7 @@ module ActiveRecord::Associations::Builder # :nodoc:
class_name = options.fetch(:class_name) {
name.to_s.camelize.singularize
}
- KnownClass.new lhs_class, class_name
+ KnownClass.new lhs_class, class_name.to_s
end
end
end
@@ -76,9 +76,11 @@ module ActiveRecord::Associations::Builder # :nodoc:
left_model.retrieve_connection
end
- def self.primary_key
- false
- end
+ private
+
+ def self.suppress_composite_primary_key(pk)
+ pk unless pk.is_a?(Array)
+ end
}
join_model.name = "HABTM_#{association_name.to_s.camelize}"
@@ -92,7 +94,7 @@ module ActiveRecord::Associations::Builder # :nodoc:
def middle_reflection(join_model)
middle_name = [lhs_model.name.downcase.pluralize,
- association_name].join('_'.freeze).gsub('::'.freeze, '_'.freeze).to_sym
+ association_name].join("_".freeze).gsub("::".freeze, "_".freeze).to_sym
middle_options = middle_options join_model
HasMany.create_reflection(lhs_model,
@@ -103,29 +105,29 @@ module ActiveRecord::Associations::Builder # :nodoc:
private
- def middle_options(join_model)
- middle_options = {}
- middle_options[:class_name] = "#{lhs_model.name}::#{join_model.name}"
- middle_options[:source] = join_model.left_reflection.name
- if options.key? :foreign_key
- middle_options[:foreign_key] = options[:foreign_key]
+ def middle_options(join_model)
+ middle_options = {}
+ middle_options[:class_name] = "#{lhs_model.name}::#{join_model.name}"
+ middle_options[:source] = join_model.left_reflection.name
+ if options.key? :foreign_key
+ middle_options[:foreign_key] = options[:foreign_key]
+ end
+ middle_options
end
- middle_options
- end
- def belongs_to_options(options)
- rhs_options = {}
+ def belongs_to_options(options)
+ rhs_options = {}
- if options.key? :class_name
- rhs_options[:foreign_key] = options[:class_name].to_s.foreign_key
- rhs_options[:class_name] = options[:class_name]
- end
+ if options.key? :class_name
+ rhs_options[:foreign_key] = options[:class_name].to_s.foreign_key
+ rhs_options[:class_name] = options[:class_name]
+ end
- if options.key? :association_foreign_key
- rhs_options[:foreign_key] = options[:association_foreign_key]
- end
+ if options.key? :association_foreign_key
+ rhs_options[:foreign_key] = options[:association_foreign_key]
+ end
- rhs_options
- end
+ rhs_options
+ end
end
end
diff --git a/activerecord/lib/active_record/associations/builder/singular_association.rb b/activerecord/lib/active_record/associations/builder/singular_association.rb
index bb96202a22..7732b63af6 100644
--- a/activerecord/lib/active_record/associations/builder/singular_association.rb
+++ b/activerecord/lib/active_record/associations/builder/singular_association.rb
@@ -8,7 +8,16 @@ module ActiveRecord::Associations::Builder # :nodoc:
def self.define_accessors(model, reflection)
super
- define_constructors(model.generated_association_methods, reflection.name) if reflection.constructable?
+ mixin = model.generated_association_methods
+ name = reflection.name
+
+ define_constructors(mixin, name) if reflection.constructable?
+
+ mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
+ def reload_#{name}
+ association(:#{name}).force_reload_reader
+ end
+ CODE
end
# Defines the (build|create)_association methods for belongs_to or has_one association
diff --git a/activerecord/lib/active_record/associations/collection_association.rb b/activerecord/lib/active_record/associations/collection_association.rb
index 0eaa0a4f36..9fa8b5d469 100644
--- a/activerecord/lib/active_record/associations/collection_association.rb
+++ b/activerecord/lib/active_record/associations/collection_association.rb
@@ -24,18 +24,9 @@ module ActiveRecord
# If you need to work on all current children, new and existing records,
# +load_target+ and the +loaded+ flag are your friends.
class CollectionAssociation < Association #:nodoc:
-
# Implements the reader method, e.g. foo.items for Foo.has_many :items
- def reader(force_reload = false)
- if force_reload
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
- Passing an argument to force an association to reload is now
- deprecated and will be removed in Rails 5.1. Please call `reload`
- on the result collection proxy instead.
- MSG
-
- klass.uncached { reload }
- elsif stale_target?
+ def reader
+ if stale_target?
reload
end
@@ -56,9 +47,7 @@ module ActiveRecord
# Implements the ids reader method, e.g. foo.item_ids for Foo.has_many :items
def ids_reader
if loaded?
- load_target.map do |record|
- record.send(reflection.association_primary_key)
- end
+ target.pluck(reflection.association_primary_key)
else
@association_ids ||= (
column = "#{reflection.quoted_table_name}.#{reflection.association_primary_key}"
@@ -69,13 +58,17 @@ module ActiveRecord
# Implements the ids writer method, e.g. foo.item_ids= for Foo.has_many :items
def ids_writer(ids)
- pk_type = reflection.primary_key_type
+ pk_type = reflection.association_primary_key_type
ids = Array(ids).reject(&:blank?)
ids.map! { |i| pk_type.cast(i) }
records = klass.where(reflection.association_primary_key => ids).index_by do |r|
r.send(reflection.association_primary_key)
- end.values_at(*ids)
- replace(records)
+ end.values_at(*ids).compact
+ if records.size != ids.size
+ klass.all.raise_record_not_found_exception!(ids, records.size, ids.size, reflection.association_primary_key)
+ else
+ replace(records)
+ end
end
def reset
@@ -83,14 +76,6 @@ module ActiveRecord
@target = []
end
- def select(*fields)
- if block_given?
- load_target.select.each { |e| yield e }
- else
- scope.select(*fields)
- end
- end
-
def find(*args)
if block_given?
load_target.find(*args) { |*block_args| yield(*block_args) }
@@ -112,52 +97,6 @@ module ActiveRecord
end
end
- def first(*args)
- first_nth_or_last(:first, *args)
- end
-
- def second(*args)
- first_nth_or_last(:second, *args)
- end
-
- def third(*args)
- first_nth_or_last(:third, *args)
- end
-
- def fourth(*args)
- first_nth_or_last(:fourth, *args)
- end
-
- def fifth(*args)
- first_nth_or_last(:fifth, *args)
- end
-
- def forty_two(*args)
- first_nth_or_last(:forty_two, *args)
- end
-
- def third_to_last(*args)
- first_nth_or_last(:third_to_last, *args)
- end
-
- def second_to_last(*args)
- first_nth_or_last(:second_to_last, *args)
- end
-
- def last(*args)
- first_nth_or_last(:last, *args)
- end
-
- def take(n = nil)
- if loaded?
- n ? target.take(n) : target.first
- else
- scope.take(n).tap do |record|
- set_inverse_instance record if record.is_a? ActiveRecord::Base
- end
- end
- end
-
def build(attributes = {}, &block)
if attributes.is_a?(Array)
attributes.collect { |attr| build(attr, &block) }
@@ -168,14 +107,6 @@ module ActiveRecord
end
end
- def create(attributes = {}, &block)
- _create_record(attributes, &block)
- end
-
- def create!(attributes = {}, &block)
- _create_record(attributes, true, &block)
- end
-
# Add +records+ to this association. Returns +self+ so method calls may
# be chained. Since << flattens its argument list and inserts each record,
# +push+ and +concat+ behave identically.
@@ -223,12 +154,12 @@ module ActiveRecord
end
dependent = if dependent
- dependent
- elsif options[:dependent] == :destroy
- :delete_all
- else
- options[:dependent]
- end
+ dependent
+ elsif options[:dependent] == :destroy
+ :delete_all
+ else
+ options[:dependent]
+ end
delete_or_nullify_all_records(dependent).tap do
reset
@@ -246,31 +177,6 @@ module ActiveRecord
end
end
- # Returns the number of records. If no arguments are given, it counts all
- # columns using SQL. If one argument is given, it counts only the passed
- # column using SQL. If a block is given, it counts the number of records
- # yielding a true value.
- def count(column_name = nil)
- return super if block_given?
- relation = scope
- if association_scope.distinct_value
- # This is needed because 'SELECT count(DISTINCT *)..' is not valid SQL.
- column_name ||= reflection.klass.primary_key
- relation = relation.distinct
- end
-
- value = relation.count(column_name)
-
- limit = options[:limit]
- offset = options[:offset]
-
- if limit || offset
- [ [value - offset.to_i, 0].max, limit.to_i ].min
- else
- value
- end
- end
-
# Removes +records+ from this association calling +before_remove+ and
# +after_remove+ callbacks.
#
@@ -280,11 +186,8 @@ module ActiveRecord
# +delete_records+. They are in any case removed from the collection.
def delete(*records)
return if records.empty?
- _options = records.extract_options!
- dependent = _options[:dependent] || options[:dependent]
-
records = find(records) if records.any? { |record| record.kind_of?(Integer) || record.kind_of?(String) }
- delete_or_destroy(records, dependent)
+ delete_or_destroy(records, options[:dependent])
end
# Deletes the +records+ and removes them from this association calling
@@ -310,14 +213,10 @@ module ActiveRecord
# +count_records+, which is a method descendants have to provide.
def size
if !find_target? || loaded?
- if association_scope.distinct_value
- target.uniq.size
- else
- target.size
- end
- elsif !loaded? && !association_scope.group_values.empty?
+ target.size
+ elsif !association_scope.group_values.empty?
load_target.size
- elsif !loaded? && !association_scope.distinct_value && target.is_a?(Array)
+ elsif !association_scope.distinct_value && target.is_a?(Array)
unsaved_records = target.select(&:new_record?)
unsaved_records.size + count_records
else
@@ -325,15 +224,6 @@ module ActiveRecord
end
end
- # Returns the size of the collection calling +size+ on the target.
- #
- # If the collection has been already loaded +length+ and +size+ are
- # equivalent. If not and you are going to need the records anyway this
- # method will take one less query. Otherwise +size+ is more efficient.
- def length
- load_target.size
- end
-
# Returns true if the collection is empty.
#
# If the collection has been loaded
@@ -350,36 +240,6 @@ module ActiveRecord
end
end
- # Returns true if the collections is not empty.
- # If block given, loads all records and checks for one or more matches.
- # Otherwise, equivalent to +!collection.empty?+.
- def any?
- if block_given?
- load_target.any? { |*block_args| yield(*block_args) }
- else
- !empty?
- end
- end
-
- # Returns true if the collection has more than 1 record.
- # If block given, loads all records and checks for two or more matches.
- # Otherwise, equivalent to +collection.size > 1+.
- def many?
- if block_given?
- load_target.many? { |*block_args| yield(*block_args) }
- else
- size > 1
- end
- end
-
- def distinct
- seen = {}
- load_target.find_all do |record|
- seen[record.id] = true unless seen.key?(record.id)
- end
- end
- alias uniq distinct
-
# Replace this collection with +other_array+. This will perform a diff
# and delete/add only records that have changed.
def replace(other_array)
@@ -429,15 +289,23 @@ module ActiveRecord
def replace_on_target(record, index, skip_callbacks)
callback(:before_add, record) unless skip_callbacks
- was_loaded = loaded?
- yield(record) if block_given?
+ begin
+ if index
+ record_was = target[index]
+ target[index] = record
+ else
+ target << record
+ end
- unless !was_loaded && loaded?
+ yield(record) if block_given?
+ rescue
if index
- @target[index] = record
+ target[index] = record_was
else
- @target << record
+ target.delete(record)
end
+
+ raise
end
callback(:after_add, record) unless skip_callbacks
@@ -456,26 +324,29 @@ module ActiveRecord
owner.new_record? && !foreign_key_present?
end
- private
- def get_records
- return scope.to_a if skip_statement_cache?
-
- conn = klass.connection
- sc = reflection.association_scope_cache(conn, owner) do
- StatementCache.create(conn) { |params|
- as = AssociationScope.create { params.bind }
- target_scope.merge as.scope(self, conn)
- }
- end
-
- binds = AssociationScope.get_bind_values(owner, reflection.chain)
- sc.execute binds, klass, klass.connection
+ def find_from_target?
+ loaded? ||
+ owner.new_record? ||
+ target.any? { |record| record.new_record? || record.changed? }
end
+ private
+
def find_target
- records = get_records
- records.each { |record| set_inverse_instance(record) }
- records
+ return scope.to_a if skip_statement_cache?
+
+ conn = klass.connection
+ sc = reflection.association_scope_cache(conn, owner) do
+ StatementCache.create(conn) { |params|
+ as = AssociationScope.create { params.bind }
+ target_scope.merge as.scope(self, conn)
+ }
+ end
+
+ binds = AssociationScope.get_bind_values(owner, reflection.chain)
+ sc.execute(binds, klass, conn) do |record|
+ set_inverse_instance(record)
+ end
end
# We have some records loaded from the database (persisted) and some that are
@@ -495,7 +366,7 @@ module ActiveRecord
persisted.map! do |record|
if mem_record = memory.delete(record)
- ((record.attribute_names & mem_record.attribute_names) - mem_record.changes.keys).each do |name|
+ ((record.attribute_names & mem_record.attribute_names) - mem_record.changed_attribute_names_to_save).each do |name|
mem_record[name] = record[name]
end
@@ -555,8 +426,9 @@ module ActiveRecord
records.each { |record| callback(:after_remove, record) }
end
- # Delete the given records from the association, using one of the methods :destroy,
- # :delete_all or :nullify (or nil, in which case a default is used).
+ # Delete the given records from the association,
+ # using one of the methods +:destroy+, +:delete_all+
+ # or +:nullify+ (or +nil+, in which case a default is used).
def delete_records(records, method)
raise NotImplementedError
end
@@ -605,25 +477,6 @@ module ActiveRecord
owner.class.send(full_callback_name)
end
- # Should we deal with assoc.first or assoc.last by issuing an independent query to
- # the database, or by getting the target, and then taking the first/last item from that?
- #
- # If the args is just a non-empty options hash, go to the database.
- #
- # Otherwise, go to the database only if none of the following are true:
- # * target already loaded
- # * owner is new record
- # * target contains new or changed record(s)
- def fetch_first_nth_or_last_using_find?(args)
- if args.first.is_a?(Hash)
- true
- else
- !(loaded? ||
- owner.new_record? ||
- target.any? { |record| record.new_record? || record.changed? })
- end
- end
-
def include_in_memory?(record)
if reflection.is_a?(ActiveRecord::Reflection::ThroughReflection)
assoc = owner.association(reflection.through_reflection.name)
@@ -650,16 +503,6 @@ module ActiveRecord
load_target.select { |r| ids.include?(r.id.to_s) }
end
end
-
- # Fetches the first/last using SQL if possible, otherwise from the target array.
- def first_nth_or_last(type, *args)
- args.shift if args.first.is_a?(Hash) && args.first.empty?
-
- collection = fetch_first_nth_or_last_using_find?(args) ? scope : load_target
- collection.send(type, *args).tap do |record|
- set_inverse_instance record if record.is_a? ActiveRecord::Base
- end
- end
end
end
end
diff --git a/activerecord/lib/active_record/associations/collection_proxy.rb b/activerecord/lib/active_record/associations/collection_proxy.rb
index 5d1e7ffb73..0d84805b4d 100644
--- a/activerecord/lib/active_record/associations/collection_proxy.rb
+++ b/activerecord/lib/active_record/associations/collection_proxy.rb
@@ -28,8 +28,7 @@ module ActiveRecord
# is computed directly through SQL and does not trigger by itself the
# instantiation of the actual post records.
class CollectionProxy < Relation
- delegate(*(ActiveRecord::Calculations.public_instance_methods - [:count]), to: :scope)
- delegate :find_nth, to: :scope
+ delegate :exists?, :update_all, :arel, to: :scope
def initialize(klass, association) #:nodoc:
@association = association
@@ -54,6 +53,12 @@ module ActiveRecord
@association.loaded?
end
+ ##
+ # :method: select
+ #
+ # :call-seq:
+ # select(*fields, &block)
+ #
# Works in two ways.
#
# *First:* Specify a subset of fields to be selected from the result set.
@@ -101,15 +106,6 @@ module ActiveRecord
# # #<Pet id: 2, name: "Spook", person_id: 1>,
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
# # ]
- #
- # person.pets.select(:name) { |pet| pet.name =~ /oo/ }
- # # => [
- # # #<Pet id: 2, name: "Spook">,
- # # #<Pet id: 3, name: "Choo-Choo">
- # # ]
- def select(*fields, &block)
- @association.select(*fields, &block)
- end
# Finds an object in the collection responding to the +id+. Uses the same
# rules as ActiveRecord::Base.find. Returns ActiveRecord::RecordNotFound
@@ -141,6 +137,12 @@ module ActiveRecord
@association.find(*args, &block)
end
+ ##
+ # :method: first
+ #
+ # :call-seq:
+ # first(limit = nil)
+ #
# Returns the first record, or the first +n+ records, from the collection.
# If the collection is empty, the first form returns +nil+, and the second
# form returns an empty array.
@@ -167,45 +169,63 @@ module ActiveRecord
# another_person_without.pets # => []
# another_person_without.pets.first # => nil
# another_person_without.pets.first(3) # => []
- def first(*args)
- @association.first(*args)
- end
+ ##
+ # :method: second
+ #
+ # :call-seq:
+ # second()
+ #
# Same as #first except returns only the second record.
- def second(*args)
- @association.second(*args)
- end
+ ##
+ # :method: third
+ #
+ # :call-seq:
+ # third()
+ #
# Same as #first except returns only the third record.
- def third(*args)
- @association.third(*args)
- end
+ ##
+ # :method: fourth
+ #
+ # :call-seq:
+ # fourth()
+ #
# Same as #first except returns only the fourth record.
- def fourth(*args)
- @association.fourth(*args)
- end
+ ##
+ # :method: fifth
+ #
+ # :call-seq:
+ # fifth()
+ #
# Same as #first except returns only the fifth record.
- def fifth(*args)
- @association.fifth(*args)
- end
+ ##
+ # :method: forty_two
+ #
+ # :call-seq:
+ # forty_two()
+ #
# Same as #first except returns only the forty second record.
# Also known as accessing "the reddit".
- def forty_two(*args)
- @association.forty_two(*args)
- end
+ ##
+ # :method: third_to_last
+ #
+ # :call-seq:
+ # third_to_last()
+ #
# Same as #first except returns only the third-to-last record.
- def third_to_last(*args)
- @association.third_to_last(*args)
- end
+ ##
+ # :method: second_to_last
+ #
+ # :call-seq:
+ # second_to_last()
+ #
# Same as #first except returns only the second-to-last record.
- def second_to_last(*args)
- @association.second_to_last(*args)
- end
# Returns the last record, or the last +n+ records, from the collection.
# If the collection is empty, the first form returns +nil+, and the second
@@ -233,8 +253,9 @@ module ActiveRecord
# another_person_without.pets # => []
# another_person_without.pets.last # => nil
# another_person_without.pets.last(3) # => []
- def last(*args)
- @association.last(*args)
+ def last(limit = nil)
+ load_target if find_from_target?
+ super
end
# Gives a record (or N records if a parameter is supplied) from the collection
@@ -262,8 +283,9 @@ module ActiveRecord
# another_person_without.pets # => []
# another_person_without.pets.take # => nil
# another_person_without.pets.take(2) # => []
- def take(n = nil)
- @association.take(n)
+ def take(limit = nil)
+ load_target if find_from_target?
+ super
end
# Returns a new object of the collection type that has been instantiated
@@ -696,6 +718,12 @@ module ActiveRecord
@association.destroy(*records)
end
+ ##
+ # :method: distinct
+ #
+ # :call-seq:
+ # distinct(value = true)
+ #
# Specifies whether the records should be unique or not.
#
# class Person < ActiveRecord::Base
@@ -710,11 +738,32 @@ module ActiveRecord
#
# person.pets.select(:name).distinct
# # => [#<Pet name: "Fancy-Fancy">]
- def distinct
- @association.distinct
+ #
+ # person.pets.select(:name).distinct.distinct(false)
+ # # => [
+ # # #<Pet name: "Fancy-Fancy">,
+ # # #<Pet name: "Fancy-Fancy">
+ # # ]
+
+ #--
+ def uniq
+ load_target.uniq
end
- alias uniq distinct
+ def calculate(operation, column_name)
+ null_scope? ? scope.calculate(operation, column_name) : super
+ end
+
+ def pluck(*column_names)
+ null_scope? ? scope.pluck(*column_names) : super
+ end
+
+ ##
+ # :method: count
+ #
+ # :call-seq:
+ # count(column_name = nil, &block)
+ #
# Count all records.
#
# class Person < ActiveRecord::Base
@@ -734,9 +783,6 @@ module ActiveRecord
# perform the count using Ruby.
#
# person.pets.count { |pet| pet.name.include?('-') } # => 2
- def count(column_name = nil, &block)
- @association.count(column_name, &block)
- end
# Returns the size of the collection. If the collection hasn't been loaded,
# it executes a <tt>SELECT COUNT(*)</tt> query. Else it calls <tt>collection.size</tt>.
@@ -766,6 +812,12 @@ module ActiveRecord
@association.size
end
+ ##
+ # :method: length
+ #
+ # :call-seq:
+ # length()
+ #
# Returns the size of the collection calling +size+ on the target.
# If the collection has been already loaded, +length+ and +size+ are
# equivalent. If not and you are going to need the records anyway this
@@ -786,14 +838,11 @@ module ActiveRecord
# # #<Pet id: 2, name: "Spook", person_id: 1>,
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
# # ]
- def length
- @association.length
- end
# Returns +true+ if the collection is empty. If the collection has been
# loaded it is equivalent
# to <tt>collection.size.zero?</tt>. If the collection has not been loaded,
- # it is equivalent to <tt>collection.exists?</tt>. If the collection has
+ # it is equivalent to <tt>!collection.exists?</tt>. If the collection has
# not already been loaded and you are going to fetch the records anyway it
# is better to check <tt>collection.length.zero?</tt>.
#
@@ -812,6 +861,12 @@ module ActiveRecord
@association.empty?
end
+ ##
+ # :method: any?
+ #
+ # :call-seq:
+ # any?()
+ #
# Returns +true+ if the collection is not empty.
#
# class Person < ActiveRecord::Base
@@ -841,10 +896,13 @@ module ActiveRecord
# pet.group == 'dogs'
# end
# # => true
- def any?(&block)
- @association.any?(&block)
- end
+ ##
+ # :method: many?
+ #
+ # :call-seq:
+ # many?()
+ #
# Returns true if the collection has more than one record.
# Equivalent to <tt>collection.size > 1</tt>.
#
@@ -879,9 +937,6 @@ module ActiveRecord
# pet.group == 'cats'
# end
# # => true
- def many?(&block)
- @association.many?(&block)
- end
# Returns +true+ if the given +record+ is present in the collection.
#
@@ -897,10 +952,6 @@ module ActiveRecord
!!@association.include?(record)
end
- def arel #:nodoc:
- scope.arel
- end
-
def proxy_association
@association
end
@@ -1074,6 +1125,30 @@ module ActiveRecord
proxy_association.reset_scope
self
end
+
+ private
+
+ def find_nth_with_limit(index, limit)
+ load_target if find_from_target?
+ super
+ end
+
+ def find_nth_from_last(index)
+ load_target if find_from_target?
+ super
+ end
+
+ def null_scope?
+ @association.null_scope?
+ end
+
+ def find_from_target?
+ @association.find_from_target?
+ end
+
+ def exec_queries
+ load_target
+ end
end
end
end
diff --git a/activerecord/lib/active_record/associations/has_many_association.rb b/activerecord/lib/active_record/associations/has_many_association.rb
index 4daafedcfb..c5a7d92a2b 100644
--- a/activerecord/lib/active_record/associations/has_many_association.rb
+++ b/activerecord/lib/active_record/associations/has_many_association.rb
@@ -16,14 +16,7 @@ module ActiveRecord
when :restrict_with_error
unless empty?
record = owner.class.human_attribute_name(reflection.name).downcase
- message = owner.errors.generate_message(:base, :'restrict_dependent_destroy.many', record: record, raise: true) rescue nil
- if message
- ActiveSupport::Deprecation.warn(<<-MESSAGE.squish)
- The error key `:'restrict_dependent_destroy.many'` has been deprecated and will be removed in Rails 5.1.
- Please use `:'restrict_dependent_destroy.has_many'` instead.
- MESSAGE
- end
- owner.errors.add(:base, message || :'restrict_dependent_destroy.has_many', record: record)
+ owner.errors.add(:base, :'restrict_dependent_destroy.has_many', record: record)
throw(:abort)
end
@@ -41,9 +34,9 @@ module ActiveRecord
set_inverse_instance(record)
if raise
- record.save!(:validate => validate)
+ record.save!(validate: validate)
else
- record.save(:validate => validate)
+ record.save(validate: validate)
end
end
@@ -72,7 +65,7 @@ module ActiveRecord
# the loaded flag is set to true as well.
def count_records
count = if reflection.has_cached_counter?
- owner._read_attribute reflection.counter_cache_column
+ owner._read_attribute(reflection.counter_cache_column).to_i
else
scope.count
end
@@ -80,7 +73,7 @@ module ActiveRecord
# If there's nothing in the database and @target has no new records
# we are certain the current target is an empty array. This is a
# documented side-effect of the method that may avoid an extra SELECT.
- @target ||= [] and loaded! if count == 0
+ (@target ||= []) && loaded! if count == 0
[association_scope.limit_value, count].compact.min
end
diff --git a/activerecord/lib/active_record/associations/has_many_through_association.rb b/activerecord/lib/active_record/associations/has_many_through_association.rb
index 36fc381343..c4a7fe4432 100644
--- a/activerecord/lib/active_record/associations/has_many_through_association.rb
+++ b/activerecord/lib/active_record/associations/has_many_through_association.rb
@@ -38,10 +38,12 @@ module ActiveRecord
def insert_record(record, validate = true, raise = false)
ensure_not_nested
- if raise
- record.save!(:validate => validate)
- else
- return unless record.save(:validate => validate)
+ if record.new_record? || record.has_changes_to_save?
+ if raise
+ record.save!(validate: validate)
+ else
+ return unless record.save(validate: validate)
+ end
end
save_through_record(record)
@@ -86,7 +88,10 @@ module ActiveRecord
end
def save_through_record(record)
- build_through_record(record).save!
+ association = build_through_record(record)
+ if association.changed?
+ association.save!
+ end
ensure
@through_records.delete(record.object_id)
end
@@ -146,7 +151,7 @@ module ActiveRecord
stmt.from scope.klass.arel_table
stmt.wheres = arel.constraints
- count = scope.klass.connection.delete(stmt, 'SQL', scope.bound_attributes)
+ count = scope.klass.connection.delete(stmt, "SQL", scope.bound_attributes)
end
when :nullify
count = scope.update_all(source_reflection.foreign_key => nil)
@@ -196,7 +201,7 @@ module ActiveRecord
def find_target
return [] unless target_reflection_has_associated_record?
- get_records
+ super
end
# NOTE - not sure that we can actually cope with inverses here
diff --git a/activerecord/lib/active_record/associations/has_one_association.rb b/activerecord/lib/active_record/associations/has_one_association.rb
index 0fe9b2e81b..b624154def 100644
--- a/activerecord/lib/active_record/associations/has_one_association.rb
+++ b/activerecord/lib/active_record/associations/has_one_association.rb
@@ -12,14 +12,7 @@ module ActiveRecord
when :restrict_with_error
if load_target
record = owner.class.human_attribute_name(reflection.name).downcase
- message = owner.errors.generate_message(:base, :'restrict_dependent_destroy.one', record: record, raise: true) rescue nil
- if message
- ActiveSupport::Deprecation.warn(<<-MESSAGE.squish)
- The error key `:'restrict_dependent_destroy.one'` has been deprecated and will be removed in Rails 5.1.
- Please use `:'restrict_dependent_destroy.has_one'` instead.
- MESSAGE
- end
- owner.errors.add(:base, message || :'restrict_dependent_destroy.has_one', record: record)
+ owner.errors.add(:base, :'restrict_dependent_destroy.has_one', record: record)
throw(:abort)
end
@@ -32,10 +25,10 @@ module ActiveRecord
raise_on_type_mismatch!(record) if record
load_target
- return self.target if !(target || record)
+ return target unless target || record
assigning_another_record = target != record
- if assigning_another_record || record.changed?
+ if assigning_another_record || record.has_changes_to_save?
save &&= owner.persisted?
transaction_if(save) do
@@ -60,12 +53,12 @@ module ActiveRecord
def delete(method = options[:dependent])
if load_target
case method
- when :delete
- target.delete
- when :destroy
- target.destroy
- when :nullify
- target.update_columns(reflection.foreign_key => nil) if target.persisted?
+ when :delete
+ target.delete
+ when :destroy
+ target.destroy
+ when :nullify
+ target.update_columns(reflection.foreign_key => nil) if target.persisted?
end
end
end
@@ -82,18 +75,19 @@ module ActiveRecord
def remove_target!(method)
case method
- when :delete
- target.delete
- when :destroy
- target.destroy
- else
- nullify_owner_attributes(target)
-
- if target.persisted? && owner.persisted? && !target.save
- set_owner_attributes(target)
- raise RecordNotSaved, "Failed to remove the existing associated #{reflection.name}. " +
- "The record failed to save after its foreign key was set to nil."
- end
+ when :delete
+ target.delete
+ when :destroy
+ target.destroy
+ else
+ nullify_owner_attributes(target)
+ remove_inverse_instance(target)
+
+ if target.persisted? && owner.persisted? && !target.save
+ set_owner_attributes(target)
+ raise RecordNotSaved, "Failed to remove the existing associated #{reflection.name}. " +
+ "The record failed to save after its foreign key was set to nil."
+ end
end
end
diff --git a/activerecord/lib/active_record/associations/has_one_through_association.rb b/activerecord/lib/active_record/associations/has_one_through_association.rb
index 08e0ec691f..604904abcc 100644
--- a/activerecord/lib/active_record/associations/has_one_through_association.rb
+++ b/activerecord/lib/active_record/associations/has_one_through_association.rb
@@ -15,7 +15,7 @@ module ActiveRecord
ensure_not_nested
through_proxy = owner.association(through_reflection.name)
- through_record = through_proxy.send(:load_target)
+ through_record = through_proxy.load_target
if through_record && !record
through_record.destroy
diff --git a/activerecord/lib/active_record/associations/join_dependency.rb b/activerecord/lib/active_record/associations/join_dependency.rb
index 491152bbcb..4cd1e64c3d 100644
--- a/activerecord/lib/active_record/associations/join_dependency.rb
+++ b/activerecord/lib/active_record/associations/join_dependency.rb
@@ -1,18 +1,18 @@
module ActiveRecord
module Associations
class JoinDependency # :nodoc:
- autoload :JoinBase, 'active_record/associations/join_dependency/join_base'
- autoload :JoinAssociation, 'active_record/associations/join_dependency/join_association'
+ autoload :JoinBase, "active_record/associations/join_dependency/join_base"
+ autoload :JoinAssociation, "active_record/associations/join_dependency/join_association"
class Aliases # :nodoc:
def initialize(tables)
@tables = tables
- @alias_cache = tables.each_with_object({}) { |table,h|
- h[table.node] = table.columns.each_with_object({}) { |column,i|
+ @alias_cache = tables.each_with_object({}) { |table, h|
+ h[table.node] = table.columns.each_with_object({}) { |column, i|
i[column.name] = column.alias
}
}
- @name_and_alias_cache = tables.each_with_object({}) { |table,h|
+ @name_and_alias_cache = tables.each_with_object({}) { |table, h|
h[table.node] = table.columns.map { |column|
[column.name, column.alias]
}
@@ -62,7 +62,7 @@ module ActiveRecord
walk_tree assoc, hash
end
when Hash
- associations.each do |k,v|
+ associations.each do |k, v|
cache = hash[k] ||= {}
walk_tree v, cache
end
@@ -126,8 +126,8 @@ module ActiveRecord
end
def aliases
- Aliases.new join_root.each_with_index.map { |join_part,i|
- columns = join_part.column_names.each_with_index.map { |column_name,j|
+ Aliases.new join_root.each_with_index.map { |join_part, i|
+ columns = join_part.column_names.each_with_index.map { |column_name, j|
Aliases::Column.new column_name, "t#{i}_r#{j}"
}
Aliases::Table.new(join_part, columns)
@@ -143,7 +143,7 @@ module ActiveRecord
}
}
- model_cache = Hash.new { |h,klass| h[klass] = {} }
+ model_cache = Hash.new { |h, klass| h[klass] = {} }
parents = model_cache[join_root]
column_aliases = aliases.column_aliases join_root
@@ -154,7 +154,7 @@ module ActiveRecord
class_name: join_root.base_klass.name
}
- message_bus.instrument('instantiation.active_record', payload) do
+ message_bus.instrument("instantiation.active_record", payload) do
result_set.each { |row_hash|
parent_key = primary_key ? row_hash[primary_key] : row_hash
parent = parents[parent_key] ||= join_root.instantiate(row_hash, column_aliases)
@@ -167,134 +167,140 @@ module ActiveRecord
private
- def make_constraints(parent, child, tables, join_type)
- chain = child.reflection.chain
- foreign_table = parent.table
- foreign_klass = parent.base_klass
- child.join_constraints(foreign_table, foreign_klass, child, join_type, tables, child.reflection.scope_chain, chain)
- end
+ def make_constraints(parent, child, tables, join_type)
+ chain = child.reflection.chain
+ foreign_table = parent.table
+ foreign_klass = parent.base_klass
+ child.join_constraints(foreign_table, foreign_klass, child, join_type, tables, child.reflection.scope_chain, chain)
+ end
- def make_outer_joins(parent, child)
- tables = table_aliases_for(parent, child)
- join_type = Arel::Nodes::OuterJoin
- info = make_constraints parent, child, tables, join_type
+ def make_outer_joins(parent, child)
+ tables = table_aliases_for(parent, child)
+ join_type = Arel::Nodes::OuterJoin
+ info = make_constraints parent, child, tables, join_type
- [info] + child.children.flat_map { |c| make_outer_joins(child, c) }
- end
+ [info] + child.children.flat_map { |c| make_outer_joins(child, c) }
+ end
- def make_left_outer_joins(parent, child)
- tables = child.tables
- join_type = Arel::Nodes::OuterJoin
- info = make_constraints parent, child, tables, join_type
+ def make_left_outer_joins(parent, child)
+ tables = child.tables
+ join_type = Arel::Nodes::OuterJoin
+ info = make_constraints parent, child, tables, join_type
- [info] + child.children.flat_map { |c| make_left_outer_joins(child, c) }
- end
-
- def make_inner_joins(parent, child)
- tables = child.tables
- join_type = Arel::Nodes::InnerJoin
- info = make_constraints parent, child, tables, join_type
+ [info] + child.children.flat_map { |c| make_left_outer_joins(child, c) }
+ end
- [info] + child.children.flat_map { |c| make_inner_joins(child, c) }
- end
+ def make_inner_joins(parent, child)
+ tables = child.tables
+ join_type = Arel::Nodes::InnerJoin
+ info = make_constraints parent, child, tables, join_type
- def table_aliases_for(parent, node)
- node.reflection.chain.map { |reflection|
- alias_tracker.aliased_table_for(
- reflection.table_name,
- table_alias_for(reflection, parent, reflection != node.reflection)
- )
- }
- end
+ [info] + child.children.flat_map { |c| make_inner_joins(child, c) }
+ end
- def construct_tables!(parent, node)
- node.tables = table_aliases_for(parent, node)
- node.children.each { |child| construct_tables! node, child }
- end
+ def table_aliases_for(parent, node)
+ node.reflection.chain.map { |reflection|
+ alias_tracker.aliased_table_for(
+ reflection.table_name,
+ table_alias_for(reflection, parent, reflection != node.reflection)
+ )
+ }
+ end
- def table_alias_for(reflection, parent, join)
- name = "#{reflection.plural_name}_#{parent.table_name}"
- name << "_join" if join
- name
- end
+ def construct_tables!(parent, node)
+ node.tables = table_aliases_for(parent, node)
+ node.children.each { |child| construct_tables! node, child }
+ end
- def walk(left, right)
- intersection, missing = right.children.map { |node1|
- [left.children.find { |node2| node1.match? node2 }, node1]
- }.partition(&:first)
+ def table_alias_for(reflection, parent, join)
+ name = "#{reflection.plural_name}_#{parent.table_name}"
+ name << "_join" if join
+ name
+ end
- ojs = missing.flat_map { |_,n| make_outer_joins left, n }
- intersection.flat_map { |l,r| walk l, r }.concat ojs
- end
+ def walk(left, right)
+ intersection, missing = right.children.map { |node1|
+ [left.children.find { |node2| node1.match? node2 }, node1]
+ }.partition(&:first)
- def find_reflection(klass, name)
- klass._reflect_on_association(name) or
- raise ConfigurationError, "Can't join '#{ klass.name }' to association named '#{ name }'; perhaps you misspelled it?"
- end
+ ojs = missing.flat_map { |_, n| make_outer_joins left, n }
+ intersection.flat_map { |l, r| walk l, r }.concat ojs
+ end
- def build(associations, base_klass)
- associations.map do |name, right|
- reflection = find_reflection base_klass, name
- reflection.check_validity!
- reflection.check_eager_loadable!
+ def find_reflection(klass, name)
+ klass._reflect_on_association(name) ||
+ raise(ConfigurationError, "Can't join '#{klass.name}' to association named '#{name}'; perhaps you misspelled it?")
+ end
- if reflection.polymorphic?
- next unless @eager_loading
- raise EagerLoadPolymorphicError.new(reflection)
- end
+ def build(associations, base_klass)
+ associations.map do |name, right|
+ reflection = find_reflection base_klass, name
+ reflection.check_validity!
+ reflection.check_eager_loadable!
- JoinAssociation.new reflection, build(right, reflection.klass)
- end.compact
- end
+ if reflection.polymorphic?
+ next unless @eager_loading
+ raise EagerLoadPolymorphicError.new(reflection)
+ end
- def construct(ar_parent, parent, row, rs, seen, model_cache, aliases)
- return if ar_parent.nil?
+ JoinAssociation.new reflection, build(right, reflection.klass)
+ end.compact
+ end
- parent.children.each do |node|
- if node.reflection.collection?
- other = ar_parent.association(node.reflection.name)
- other.loaded!
- elsif ar_parent.association_cached?(node.reflection.name)
- model = ar_parent.association(node.reflection.name).target
- construct(model, node, row, rs, seen, model_cache, aliases)
- next
+ def construct(ar_parent, parent, row, rs, seen, model_cache, aliases)
+ return if ar_parent.nil?
+
+ parent.children.each do |node|
+ if node.reflection.collection?
+ other = ar_parent.association(node.reflection.name)
+ other.loaded!
+ elsif ar_parent.association_cached?(node.reflection.name)
+ model = ar_parent.association(node.reflection.name).target
+ construct(model, node, row, rs, seen, model_cache, aliases)
+ next
+ end
+
+ key = aliases.column_alias(node, node.primary_key)
+ id = row[key]
+ if id.nil?
+ nil_association = ar_parent.association(node.reflection.name)
+ nil_association.loaded!
+ next
+ end
+
+ model = seen[ar_parent.object_id][node.base_klass][id]
+
+ if model
+ construct(model, node, row, rs, seen, model_cache, aliases)
+ else
+ model = construct_model(ar_parent, node, row, model_cache, id, aliases)
+
+ if node.reflection.scope_for(node.base_klass).readonly_value
+ model.readonly!
+ end
+
+ seen[ar_parent.object_id][node.base_klass][id] = model
+ construct(model, node, row, rs, seen, model_cache, aliases)
+ end
end
+ end
- key = aliases.column_alias(node, node.primary_key)
- id = row[key]
- if id.nil?
- nil_association = ar_parent.association(node.reflection.name)
- nil_association.loaded!
- next
- end
+ def construct_model(record, node, row, model_cache, id, aliases)
+ other = record.association(node.reflection.name)
- model = seen[ar_parent.object_id][node.base_klass][id]
+ model = model_cache[node][id] ||=
+ node.instantiate(row, aliases.column_aliases(node)) do |m|
+ other.set_inverse_instance(m)
+ end
- if model
- construct(model, node, row, rs, seen, model_cache, aliases)
+ if node.reflection.collection?
+ other.target.push(model)
else
- model = construct_model(ar_parent, node, row, model_cache, id, aliases)
- model.readonly!
- seen[ar_parent.object_id][node.base_klass][id] = model
- construct(model, node, row, rs, seen, model_cache, aliases)
+ other.target = model
end
- end
- end
- def construct_model(record, node, row, model_cache, id, aliases)
- model = model_cache[node][id] ||= node.instantiate(row,
- aliases.column_aliases(node))
- other = record.association(node.reflection.name)
-
- if node.reflection.collection?
- other.target.push(model)
- else
- other.target = model
+ model
end
-
- other.set_inverse_instance(model)
- model
- end
end
end
end
diff --git a/activerecord/lib/active_record/associations/join_dependency/join_association.rb b/activerecord/lib/active_record/associations/join_dependency/join_association.rb
index c5fbe0d1d1..a5705951f3 100644
--- a/activerecord/lib/active_record/associations/join_dependency/join_association.rb
+++ b/activerecord/lib/active_record/associations/join_dependency/join_association.rb
@@ -1,4 +1,4 @@
-require 'active_record/associations/join_dependency/join_part'
+require "active_record/associations/join_dependency/join_part"
module ActiveRecord
module Associations
@@ -56,7 +56,9 @@ module ActiveRecord
klass_scope =
if klass.current_scope
- klass.current_scope.clone
+ klass.current_scope.clone.tap { |scope|
+ scope.joins_values = []
+ }
else
relation = ActiveRecord::Relation.create(
klass,
diff --git a/activerecord/lib/active_record/associations/join_dependency/join_base.rb b/activerecord/lib/active_record/associations/join_dependency/join_base.rb
index 3a26c25737..fca20514d1 100644
--- a/activerecord/lib/active_record/associations/join_dependency/join_base.rb
+++ b/activerecord/lib/active_record/associations/join_dependency/join_base.rb
@@ -1,4 +1,4 @@
-require 'active_record/associations/join_dependency/join_part'
+require "active_record/associations/join_dependency/join_part"
module ActiveRecord
module Associations
diff --git a/activerecord/lib/active_record/associations/join_dependency/join_part.rb b/activerecord/lib/active_record/associations/join_dependency/join_part.rb
index 9c6573f913..61cec5403a 100644
--- a/activerecord/lib/active_record/associations/join_dependency/join_part.rb
+++ b/activerecord/lib/active_record/associations/join_dependency/join_part.rb
@@ -15,7 +15,7 @@ module ActiveRecord
# association.
attr_reader :base_klass, :children
- delegate :table_name, :column_names, :primary_key, :to => :base_klass
+ delegate :table_name, :column_names, :primary_key, to: :base_klass
def initialize(base_klass, children)
@base_klass = base_klass
@@ -62,8 +62,8 @@ module ActiveRecord
hash
end
- def instantiate(row, aliases)
- base_klass.instantiate(extract_record(row, aliases))
+ def instantiate(row, aliases, &block)
+ base_klass.instantiate(extract_record(row, aliases), &block)
end
end
end
diff --git a/activerecord/lib/active_record/associations/preloader.rb b/activerecord/lib/active_record/associations/preloader.rb
index e64af84e1a..9f77f38b35 100644
--- a/activerecord/lib/active_record/associations/preloader.rb
+++ b/activerecord/lib/active_record/associations/preloader.rb
@@ -42,16 +42,16 @@ module ActiveRecord
extend ActiveSupport::Autoload
eager_autoload do
- autoload :Association, 'active_record/associations/preloader/association'
- autoload :SingularAssociation, 'active_record/associations/preloader/singular_association'
- autoload :CollectionAssociation, 'active_record/associations/preloader/collection_association'
- autoload :ThroughAssociation, 'active_record/associations/preloader/through_association'
-
- autoload :HasMany, 'active_record/associations/preloader/has_many'
- autoload :HasManyThrough, 'active_record/associations/preloader/has_many_through'
- autoload :HasOne, 'active_record/associations/preloader/has_one'
- autoload :HasOneThrough, 'active_record/associations/preloader/has_one_through'
- autoload :BelongsTo, 'active_record/associations/preloader/belongs_to'
+ autoload :Association, "active_record/associations/preloader/association"
+ autoload :SingularAssociation, "active_record/associations/preloader/singular_association"
+ autoload :CollectionAssociation, "active_record/associations/preloader/collection_association"
+ autoload :ThroughAssociation, "active_record/associations/preloader/through_association"
+
+ autoload :HasMany, "active_record/associations/preloader/has_many"
+ autoload :HasManyThrough, "active_record/associations/preloader/has_many_through"
+ autoload :HasOne, "active_record/associations/preloader/has_one"
+ autoload :HasOneThrough, "active_record/associations/preloader/has_one_through"
+ autoload :BelongsTo, "active_record/associations/preloader/belongs_to"
end
NULL_RELATION = Struct.new(:values, :where_clause, :joins_values).new({}, Relation::WhereClause.empty, [])
@@ -106,108 +106,108 @@ module ActiveRecord
private
- # Loads all the given data into +records+ for the +association+.
- def preloaders_on(association, records, scope)
- case association
- when Hash
- preloaders_for_hash(association, records, scope)
- when Symbol
- preloaders_for_one(association, records, scope)
- when String
- preloaders_for_one(association.to_sym, records, scope)
- else
- raise ArgumentError, "#{association.inspect} was not recognized for preload"
+ # Loads all the given data into +records+ for the +association+.
+ def preloaders_on(association, records, scope)
+ case association
+ when Hash
+ preloaders_for_hash(association, records, scope)
+ when Symbol
+ preloaders_for_one(association, records, scope)
+ when String
+ preloaders_for_one(association.to_sym, records, scope)
+ else
+ raise ArgumentError, "#{association.inspect} was not recognized for preload"
+ end
end
- end
- def preloaders_for_hash(association, records, scope)
- association.flat_map { |parent, child|
- loaders = preloaders_for_one parent, records, scope
+ def preloaders_for_hash(association, records, scope)
+ association.flat_map { |parent, child|
+ loaders = preloaders_for_one parent, records, scope
- recs = loaders.flat_map(&:preloaded_records).uniq
- loaders.concat Array.wrap(child).flat_map { |assoc|
- preloaders_on assoc, recs, scope
+ recs = loaders.flat_map(&:preloaded_records).uniq
+ loaders.concat Array.wrap(child).flat_map { |assoc|
+ preloaders_on assoc, recs, scope
+ }
+ loaders
}
- loaders
- }
- end
+ end
- # Loads all the given data into +records+ for a singular +association+.
- #
- # Functions by instantiating a preloader class such as Preloader::HasManyThrough and
- # call the +run+ method for each passed in class in the +records+ argument.
- #
- # Not all records have the same class, so group then preload group on the reflection
- # itself so that if various subclass share the same association then we do not split
- # them unnecessarily
- #
- # Additionally, polymorphic belongs_to associations can have multiple associated
- # classes, depending on the polymorphic_type field. So we group by the classes as
- # well.
- def preloaders_for_one(association, records, scope)
- grouped_records(association, records).flat_map do |reflection, klasses|
- klasses.map do |rhs_klass, rs|
- loader = preloader_for(reflection, rs, rhs_klass).new(rhs_klass, rs, reflection, scope)
- loader.run self
- loader
+ # Loads all the given data into +records+ for a singular +association+.
+ #
+ # Functions by instantiating a preloader class such as Preloader::HasManyThrough and
+ # call the +run+ method for each passed in class in the +records+ argument.
+ #
+ # Not all records have the same class, so group then preload group on the reflection
+ # itself so that if various subclass share the same association then we do not split
+ # them unnecessarily
+ #
+ # Additionally, polymorphic belongs_to associations can have multiple associated
+ # classes, depending on the polymorphic_type field. So we group by the classes as
+ # well.
+ def preloaders_for_one(association, records, scope)
+ grouped_records(association, records).flat_map do |reflection, klasses|
+ klasses.map do |rhs_klass, rs|
+ loader = preloader_for(reflection, rs, rhs_klass).new(rhs_klass, rs, reflection, scope)
+ loader.run self
+ loader
+ end
end
end
- end
- def grouped_records(association, records)
- h = {}
- records.each do |record|
- next unless record
- assoc = record.association(association)
- klasses = h[assoc.reflection] ||= {}
- (klasses[assoc.klass] ||= []) << record
+ def grouped_records(association, records)
+ h = {}
+ records.each do |record|
+ next unless record
+ assoc = record.association(association)
+ klasses = h[assoc.reflection] ||= {}
+ (klasses[assoc.klass] ||= []) << record
+ end
+ h
end
- h
- end
- class AlreadyLoaded # :nodoc:
- attr_reader :owners, :reflection
+ class AlreadyLoaded # :nodoc:
+ attr_reader :owners, :reflection
- def initialize(klass, owners, reflection, preload_scope)
- @owners = owners
- @reflection = reflection
- end
+ def initialize(klass, owners, reflection, preload_scope)
+ @owners = owners
+ @reflection = reflection
+ end
- def run(preloader); end
+ def run(preloader); end
- def preloaded_records
- owners.flat_map { |owner| owner.association(reflection.name).target }
+ def preloaded_records
+ owners.flat_map { |owner| owner.association(reflection.name).target }
+ end
end
- end
- class NullPreloader # :nodoc:
- def self.new(klass, owners, reflection, preload_scope); self; end
- def self.run(preloader); end
- def self.preloaded_records; []; end
- def self.owners; []; end
- end
+ class NullPreloader # :nodoc:
+ def self.new(klass, owners, reflection, preload_scope); self; end
+ def self.run(preloader); end
+ def self.preloaded_records; []; end
+ def self.owners; []; end
+ end
- # Returns a class containing the logic needed to load preload the data
- # and attach it to a relation. For example +Preloader::Association+ or
- # +Preloader::HasManyThrough+. The class returned implements a `run` method
- # that accepts a preloader.
- def preloader_for(reflection, owners, rhs_klass)
- return NullPreloader unless rhs_klass
+ # Returns a class containing the logic needed to load preload the data
+ # and attach it to a relation. For example +Preloader::Association+ or
+ # +Preloader::HasManyThrough+. The class returned implements a `run` method
+ # that accepts a preloader.
+ def preloader_for(reflection, owners, rhs_klass)
+ return NullPreloader unless rhs_klass
- if owners.first.association(reflection.name).loaded?
- return AlreadyLoaded
- end
- reflection.check_preloadable!
-
- case reflection.macro
- when :has_many
- reflection.options[:through] ? HasManyThrough : HasMany
- when :has_one
- reflection.options[:through] ? HasOneThrough : HasOne
- when :belongs_to
- BelongsTo
+ if owners.first.association(reflection.name).loaded?
+ return AlreadyLoaded
+ end
+ reflection.check_preloadable!
+
+ case reflection.macro
+ when :has_many
+ reflection.options[:through] ? HasManyThrough : HasMany
+ when :has_one
+ reflection.options[:through] ? HasOneThrough : HasOne
+ when :belongs_to
+ BelongsTo
+ end
end
- end
end
end
end
diff --git a/activerecord/lib/active_record/associations/preloader/association.rb b/activerecord/lib/active_record/associations/preloader/association.rb
index 3032bc786e..4072d19380 100644
--- a/activerecord/lib/active_record/associations/preloader/association.rb
+++ b/activerecord/lib/active_record/associations/preloader/association.rb
@@ -28,10 +28,6 @@ module ActiveRecord
end
def records_for(ids)
- query_scope(ids)
- end
-
- def query_scope(ids)
scope.where(association_key_name => ids)
end
@@ -61,99 +57,113 @@ module ActiveRecord
private
- def associated_records_by_owner(preloader)
- records = load_records
- owners.each_with_object({}) do |owner, result|
- result[owner] = records[convert_key(owner[owner_key_name])] || []
+ def associated_records_by_owner(preloader)
+ records = load_records do |record|
+ owner = owners_by_key[convert_key(record[association_key_name])]
+ association = owner.association(reflection.name)
+ association.set_inverse_instance(record)
+ end
+
+ owners.each_with_object({}) do |owner, result|
+ result[owner] = records[convert_key(owner[owner_key_name])] || []
+ end
end
- end
- def owner_keys
- unless defined?(@owner_keys)
- @owner_keys = owners.map do |owner|
- owner[owner_key_name]
+ def owner_keys
+ unless defined?(@owner_keys)
+ @owner_keys = owners.map do |owner|
+ owner[owner_key_name]
+ end
+ @owner_keys.uniq!
+ @owner_keys.compact!
end
- @owner_keys.uniq!
- @owner_keys.compact!
+ @owner_keys
end
- @owner_keys
- end
- def key_conversion_required?
- @key_conversion_required ||= association_key_type != owner_key_type
- end
+ def owners_by_key
+ unless defined?(@owners_by_key)
+ @owners_by_key = owners.each_with_object({}) do |owner, h|
+ h[convert_key(owner[owner_key_name])] = owner
+ end
+ end
+ @owners_by_key
+ end
- def convert_key(key)
- if key_conversion_required?
- key.to_s
- else
- key
+ def key_conversion_required?
+ @key_conversion_required ||= association_key_type != owner_key_type
end
- end
- def association_key_type
- @klass.type_for_attribute(association_key_name.to_s).type
- end
+ def convert_key(key)
+ if key_conversion_required?
+ key.to_s
+ else
+ key
+ end
+ end
- def owner_key_type
- @model.type_for_attribute(owner_key_name.to_s).type
- end
+ def association_key_type
+ @klass.type_for_attribute(association_key_name.to_s).type
+ end
+
+ def owner_key_type
+ @model.type_for_attribute(owner_key_name.to_s).type
+ end
- def load_records
- return {} if owner_keys.empty?
- # Some databases impose a limit on the number of ids in a list (in Oracle it's 1000)
- # Make several smaller queries if necessary or make one query if the adapter supports it
- slices = owner_keys.each_slice(klass.connection.in_clause_length || owner_keys.size)
- @preloaded_records = slices.flat_map do |slice|
- records_for(slice)
+ def load_records(&block)
+ return {} if owner_keys.empty?
+ # Some databases impose a limit on the number of ids in a list (in Oracle it's 1000)
+ # Make several smaller queries if necessary or make one query if the adapter supports it
+ slices = owner_keys.each_slice(klass.connection.in_clause_length || owner_keys.size)
+ @preloaded_records = slices.flat_map do |slice|
+ records_for(slice).load(&block)
+ end
+ @preloaded_records.group_by do |record|
+ convert_key(record[association_key_name])
+ end
end
- @preloaded_records.group_by do |record|
- convert_key(record[association_key_name])
+
+ def reflection_scope
+ @reflection_scope ||= reflection.scope_for(klass)
end
- end
- def reflection_scope
- @reflection_scope ||= reflection.scope ? klass.unscoped.instance_exec(nil, &reflection.scope) : klass.unscoped
- end
+ def build_scope
+ scope = klass.unscoped
- def build_scope
- scope = klass.unscoped
+ values = reflection_scope.values
+ preload_values = preload_scope.values
- values = reflection_scope.values
- preload_values = preload_scope.values
+ scope.where_clause = reflection_scope.where_clause + preload_scope.where_clause
+ scope.references_values = Array(values[:references]) + Array(preload_values[:references])
- scope.where_clause = reflection_scope.where_clause + preload_scope.where_clause
- scope.references_values = Array(values[:references]) + Array(preload_values[:references])
+ if preload_values[:select] || values[:select]
+ scope._select!(preload_values[:select] || values[:select])
+ end
+ scope.includes! preload_values[:includes] || values[:includes]
+ if preload_scope.joins_values.any?
+ scope.joins!(preload_scope.joins_values)
+ else
+ scope.joins!(reflection_scope.joins_values)
+ end
- if preload_values[:select] || values[:select]
- scope._select!(preload_values[:select] || values[:select])
- end
- scope.includes! preload_values[:includes] || values[:includes]
- if preload_scope.joins_values.any?
- scope.joins!(preload_scope.joins_values)
- else
- scope.joins!(reflection_scope.joins_values)
- end
+ if order_values = preload_values[:order] || values[:order]
+ scope.order!(order_values)
+ end
- if order_values = preload_values[:order] || values[:order]
- scope.order!(order_values)
- end
+ if preload_values[:reordering] || values[:reordering]
+ scope.reordering_value = true
+ end
- if preload_values[:reordering] || values[:reordering]
- scope.reordering_value = true
- end
+ if preload_values[:readonly] || values[:readonly]
+ scope.readonly!
+ end
- if preload_values[:readonly] || values[:readonly]
- scope.readonly!
- end
+ if options[:as]
+ scope.where!(klass.table_name => { reflection.type => model.base_class.sti_name })
+ end
- if options[:as]
- scope.where!(klass.table_name => { reflection.type => model.base_class.sti_name })
+ scope.unscope_values = Array(values[:unscope]) + Array(preload_values[:unscope])
+ klass.default_scoped.merge(scope)
end
-
- scope.unscope_values = Array(values[:unscope]) + Array(preload_values[:unscope])
- klass.default_scoped.merge(scope)
- end
end
end
end
diff --git a/activerecord/lib/active_record/associations/preloader/belongs_to.rb b/activerecord/lib/active_record/associations/preloader/belongs_to.rb
index 5091d4717a..38e231826c 100644
--- a/activerecord/lib/active_record/associations/preloader/belongs_to.rb
+++ b/activerecord/lib/active_record/associations/preloader/belongs_to.rb
@@ -2,7 +2,6 @@ module ActiveRecord
module Associations
class Preloader
class BelongsTo < SingularAssociation #:nodoc:
-
def association_key_name
reflection.options[:primary_key] || klass && klass.primary_key
end
@@ -10,7 +9,6 @@ module ActiveRecord
def owner_key_name
reflection.foreign_key
end
-
end
end
end
diff --git a/activerecord/lib/active_record/associations/preloader/collection_association.rb b/activerecord/lib/active_record/associations/preloader/collection_association.rb
index 9939280fa4..26690bf16d 100644
--- a/activerecord/lib/active_record/associations/preloader/collection_association.rb
+++ b/activerecord/lib/active_record/associations/preloader/collection_association.rb
@@ -4,14 +4,13 @@ module ActiveRecord
class CollectionAssociation < Association #:nodoc:
private
- def preload(preloader)
- associated_records_by_owner(preloader).each do |owner, records|
- association = owner.association(reflection.name)
- association.loaded!
- association.target.concat(records)
- records.each { |record| association.set_inverse_instance(record) }
+ def preload(preloader)
+ associated_records_by_owner(preloader).each do |owner, records|
+ association = owner.association(reflection.name)
+ association.loaded!
+ association.target.concat(records)
+ end
end
- end
end
end
end
diff --git a/activerecord/lib/active_record/associations/preloader/has_many.rb b/activerecord/lib/active_record/associations/preloader/has_many.rb
index 3ea91a8c11..20df1cc19a 100644
--- a/activerecord/lib/active_record/associations/preloader/has_many.rb
+++ b/activerecord/lib/active_record/associations/preloader/has_many.rb
@@ -2,7 +2,6 @@ module ActiveRecord
module Associations
class Preloader
class HasMany < CollectionAssociation #:nodoc:
-
def association_key_name
reflection.foreign_key
end
@@ -10,7 +9,6 @@ module ActiveRecord
def owner_key_name
reflection.active_record_primary_key
end
-
end
end
end
diff --git a/activerecord/lib/active_record/associations/preloader/singular_association.rb b/activerecord/lib/active_record/associations/preloader/singular_association.rb
index f60647a81e..5c5828262e 100644
--- a/activerecord/lib/active_record/associations/preloader/singular_association.rb
+++ b/activerecord/lib/active_record/associations/preloader/singular_association.rb
@@ -2,19 +2,16 @@ module ActiveRecord
module Associations
class Preloader
class SingularAssociation < Association #:nodoc:
-
private
- def preload(preloader)
- associated_records_by_owner(preloader).each do |owner, associated_records|
- record = associated_records.first
+ def preload(preloader)
+ associated_records_by_owner(preloader).each do |owner, associated_records|
+ record = associated_records.first
- association = owner.association(reflection.name)
- association.target = record
- association.set_inverse_instance(record) if record
+ association = owner.association(reflection.name)
+ association.target = record
+ end
end
- end
-
end
end
end
diff --git a/activerecord/lib/active_record/associations/preloader/through_association.rb b/activerecord/lib/active_record/associations/preloader/through_association.rb
index b0203909ce..9d44a02021 100644
--- a/activerecord/lib/active_record/associations/preloader/through_association.rb
+++ b/activerecord/lib/active_record/associations/preloader/through_association.rb
@@ -24,7 +24,7 @@ module ActiveRecord
reset_association owners, through_reflection.name
- middle_records = through_records.flat_map { |(_,rec)| rec }
+ middle_records = through_records.flat_map { |(_, rec)| rec }
preloaders = preloader.preload(middle_records,
source_reflection.name,
@@ -32,13 +32,13 @@ module ActiveRecord
@preloaded_records = preloaders.flat_map(&:preloaded_records)
- middle_to_pl = preloaders.each_with_object({}) do |pl,h|
+ middle_to_pl = preloaders.each_with_object({}) do |pl, h|
pl.owners.each { |middle|
h[middle] = pl
}
end
- through_records.each_with_object({}) do |(lhs,center), records_by_owner|
+ through_records.each_with_object({}) do |(lhs, center), records_by_owner|
pl_to_middle = center.group_by { |record| middle_to_pl[record] }
records_by_owner[lhs] = pl_to_middle.flat_map do |pl, middles|
@@ -61,48 +61,47 @@ module ActiveRecord
private
- def id_to_index_map(ids)
- id_map = {}
- ids.each_with_index { |id, index| id_map[id] = index }
- id_map
- end
+ def id_to_index_map(ids)
+ id_map = {}
+ ids.each_with_index { |id, index| id_map[id] = index }
+ id_map
+ end
- def reset_association(owners, association_name)
- should_reset = (through_scope != through_reflection.klass.unscoped) ||
- (reflection.options[:source_type] && through_reflection.collection?)
+ def reset_association(owners, association_name)
+ should_reset = (through_scope != through_reflection.klass.unscoped) ||
+ (reflection.options[:source_type] && through_reflection.collection?)
- # Don't cache the association - we would only be caching a subset
- if should_reset
- owners.each { |owner|
- owner.association(association_name).reset
- }
+ # Don't cache the association - we would only be caching a subset
+ if should_reset
+ owners.each { |owner|
+ owner.association(association_name).reset
+ }
+ end
end
- end
+ def through_scope
+ scope = through_reflection.klass.unscoped
- def through_scope
- scope = through_reflection.klass.unscoped
+ if options[:source_type]
+ scope.where! reflection.foreign_type => options[:source_type]
+ else
+ unless reflection_scope.where_clause.empty?
+ scope.includes_values = Array(reflection_scope.values[:includes] || options[:source])
+ scope.where_clause = reflection_scope.where_clause
+ end
- if options[:source_type]
- scope.where! reflection.foreign_type => options[:source_type]
- else
- unless reflection_scope.where_clause.empty?
- scope.includes_values = Array(reflection_scope.values[:includes] || options[:source])
- scope.where_clause = reflection_scope.where_clause
+ scope.references! reflection_scope.values[:references]
+ if scope.eager_loading? && order_values = reflection_scope.values[:order]
+ scope = scope.order(order_values)
+ end
end
- scope.references! reflection_scope.values[:references]
- if scope.eager_loading? && order_values = reflection_scope.values[:order]
- scope = scope.order(order_values)
- end
+ scope
end
- scope
- end
-
- def target_records_from_association(association)
- association.loaded? ? association.target : association.reader
- end
+ def target_records_from_association(association)
+ association.loaded? ? association.target : association.reader
+ end
end
end
end
diff --git a/activerecord/lib/active_record/associations/singular_association.rb b/activerecord/lib/active_record/associations/singular_association.rb
index f913f0852a..91580a28d0 100644
--- a/activerecord/lib/active_record/associations/singular_association.rb
+++ b/activerecord/lib/active_record/associations/singular_association.rb
@@ -2,16 +2,8 @@ module ActiveRecord
module Associations
class SingularAssociation < Association #:nodoc:
# Implements the reader method, e.g. foo.bar for Foo.has_one :bar
- def reader(force_reload = false)
- if force_reload && klass
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
- Passing an argument to force an association to reload is now
- deprecated and will be removed in Rails 5.1. Please call `reload`
- on the parent object instead.
- MSG
-
- klass.uncached { reload }
- elsif !loaded? || stale_target?
+ def reader
+ if !loaded? || stale_target?
reload
end
@@ -23,14 +15,6 @@ module ActiveRecord
replace(record)
end
- def create(attributes = {}, &block)
- _create_record(attributes, &block)
- end
-
- def create!(attributes = {}, &block)
- _create_record(attributes, true, &block)
- end
-
def build(attributes = {})
record = build_record(attributes)
yield(record) if block_given?
@@ -38,14 +22,21 @@ module ActiveRecord
record
end
+ # Implements the reload reader method, e.g. foo.reload_bar for
+ # Foo.has_one :bar
+ def force_reload_reader
+ klass.uncached { reload }
+ target
+ end
+
private
def create_scope
scope.scope_for_create.stringify_keys.except(klass.primary_key)
end
- def get_records
- return scope.limit(1).records if skip_statement_cache?
+ def find_target
+ return scope.take if skip_statement_cache?
conn = klass.connection
sc = reflection.association_scope_cache(conn, owner) do
@@ -56,13 +47,11 @@ module ActiveRecord
end
binds = AssociationScope.get_bind_values(owner, reflection.chain)
- sc.execute binds, klass, klass.connection
- end
-
- def find_target
- if record = get_records.first
+ sc.execute(binds, klass, conn) do |record|
set_inverse_instance record
- end
+ end.first
+ rescue ::RangeError
+ nil
end
def replace(record)
diff --git a/activerecord/lib/active_record/associations/through_association.rb b/activerecord/lib/active_record/associations/through_association.rb
index d0ec3e8015..6b87993ba3 100644
--- a/activerecord/lib/active_record/associations/through_association.rb
+++ b/activerecord/lib/active_record/associations/through_association.rb
@@ -2,10 +2,9 @@ module ActiveRecord
# = Active Record Through Association
module Associations
module ThroughAssociation #:nodoc:
+ delegate :source_reflection, :through_reflection, to: :reflection
- delegate :source_reflection, :through_reflection, :to => :reflection
-
- protected
+ private
# We merge in these scopes for two reasons:
#
@@ -22,8 +21,6 @@ module ActiveRecord
scope
end
- private
-
# Construct attributes for :through pointing to owner and associate. This is used by the
# methods which create and delete records on the association.
#
diff --git a/activerecord/lib/active_record/attribute.rb b/activerecord/lib/active_record/attribute.rb
index 9530f134d0..38281158d8 100644
--- a/activerecord/lib/active_record/attribute.rb
+++ b/activerecord/lib/active_record/attribute.rb
@@ -65,7 +65,7 @@ module ActiveRecord
def with_value_from_user(value)
type.assert_valid_value(value)
- self.class.from_user(name, value, type, self)
+ self.class.from_user(name, value, type, original_attribute || self)
end
def with_value_from_database(value)
@@ -128,110 +128,113 @@ module ActiveRecord
coder["value"] = value if defined?(@value)
end
+ # TODO Change this to private once we've dropped Ruby 2.2 support.
+ # Workaround for Ruby 2.2 "private attribute?" warning.
protected
- attr_reader :original_attribute
- alias_method :assigned?, :original_attribute
+ attr_reader :original_attribute
+ alias_method :assigned?, :original_attribute
- def initialize_dup(other)
- if defined?(@value) && @value.duplicable?
- @value = @value.dup
+ def original_value_for_database
+ if assigned?
+ original_attribute.original_value_for_database
+ else
+ _original_value_for_database
+ end
end
- end
-
- def changed_from_assignment?
- assigned? && type.changed?(original_value, value, value_before_type_cast)
- end
- def original_value_for_database
- if assigned?
- original_attribute.original_value_for_database
- else
- _original_value_for_database
+ private
+ def initialize_dup(other)
+ if defined?(@value) && @value.duplicable?
+ @value = @value.dup
+ end
end
- end
- def _original_value_for_database
- value_for_database
- end
-
- class FromDatabase < Attribute # :nodoc:
- def type_cast(value)
- type.deserialize(value)
+ def changed_from_assignment?
+ assigned? && type.changed?(original_value, value, value_before_type_cast)
end
def _original_value_for_database
- value_before_type_cast
+ type.serialize(original_value)
end
- end
- class FromUser < Attribute # :nodoc:
- def type_cast(value)
- type.cast(value)
- end
+ class FromDatabase < Attribute # :nodoc:
+ def type_cast(value)
+ type.deserialize(value)
+ end
- def came_from_user?
- true
+ def _original_value_for_database
+ value_before_type_cast
+ end
end
- end
- class WithCastValue < Attribute # :nodoc:
- def type_cast(value)
- value
- end
+ class FromUser < Attribute # :nodoc:
+ def type_cast(value)
+ type.cast(value)
+ end
- def changed_in_place_from?(old_value)
- false
+ def came_from_user?
+ true
+ end
end
- end
- class Null < Attribute # :nodoc:
- def initialize(name)
- super(name, nil, Type::Value.new)
- end
+ class WithCastValue < Attribute # :nodoc:
+ def type_cast(value)
+ value
+ end
- def type_cast(*)
- nil
+ def changed_in_place?
+ false
+ end
end
- def with_type(type)
- self.class.with_cast_value(name, nil, type)
- end
+ class Null < Attribute # :nodoc:
+ def initialize(name)
+ super(name, nil, Type.default_value)
+ end
- def with_value_from_database(value)
- raise ActiveModel::MissingAttributeError, "can't write unknown attribute `#{name}`"
- end
- alias_method :with_value_from_user, :with_value_from_database
- end
+ def type_cast(*)
+ nil
+ end
- class Uninitialized < Attribute # :nodoc:
- UNINITIALIZED_ORIGINAL_VALUE = Object.new
+ def with_type(type)
+ self.class.with_cast_value(name, nil, type)
+ end
- def initialize(name, type)
- super(name, nil, type)
+ def with_value_from_database(value)
+ raise ActiveModel::MissingAttributeError, "can't write unknown attribute `#{name}`"
+ end
+ alias_method :with_value_from_user, :with_value_from_database
end
- def value
- if block_given?
- yield name
+ class Uninitialized < Attribute # :nodoc:
+ UNINITIALIZED_ORIGINAL_VALUE = Object.new
+
+ def initialize(name, type)
+ super(name, nil, type)
end
- end
- def original_value
- UNINITIALIZED_ORIGINAL_VALUE
- end
+ def value
+ if block_given?
+ yield name
+ end
+ end
- def value_for_database
- end
+ def original_value
+ UNINITIALIZED_ORIGINAL_VALUE
+ end
- def initialized?
- false
- end
+ def value_for_database
+ end
+
+ def initialized?
+ false
+ end
- def with_type(type)
- self.class.new(name, type)
+ def with_type(type)
+ self.class.new(name, type)
+ end
end
- end
- private_constant :FromDatabase, :FromUser, :Null, :Uninitialized, :WithCastValue
+ private_constant :FromDatabase, :FromUser, :Null, :Uninitialized, :WithCastValue
end
end
diff --git a/activerecord/lib/active_record/attribute/user_provided_default.rb b/activerecord/lib/active_record/attribute/user_provided_default.rb
index 4580813364..57f8bbed76 100644
--- a/activerecord/lib/active_record/attribute/user_provided_default.rb
+++ b/activerecord/lib/active_record/attribute/user_provided_default.rb
@@ -1,4 +1,4 @@
-require 'active_record/attribute'
+require "active_record/attribute"
module ActiveRecord
class Attribute # :nodoc:
@@ -20,9 +20,11 @@ module ActiveRecord
self.class.new(name, user_provided_value, type, original_attribute)
end
+ # TODO Change this to private once we've dropped Ruby 2.2 support.
+ # Workaround for Ruby 2.2 "private attribute?" warning.
protected
- attr_reader :user_provided_value
+ attr_reader :user_provided_value
end
end
end
diff --git a/activerecord/lib/active_record/attribute_assignment.rb b/activerecord/lib/active_record/attribute_assignment.rb
index b96d8e9352..d0dfca0cac 100644
--- a/activerecord/lib/active_record/attribute_assignment.rb
+++ b/activerecord/lib/active_record/attribute_assignment.rb
@@ -1,4 +1,4 @@
-require 'active_model/forbidden_attributes_protection'
+require "active_model/forbidden_attributes_protection"
module ActiveRecord
module AttributeAssignment
@@ -12,80 +12,80 @@ module ActiveRecord
private
- def _assign_attributes(attributes) # :nodoc:
- multi_parameter_attributes = {}
- nested_parameter_attributes = {}
+ def _assign_attributes(attributes)
+ multi_parameter_attributes = {}
+ nested_parameter_attributes = {}
- attributes.each do |k, v|
- if k.include?("(")
- multi_parameter_attributes[k] = attributes.delete(k)
- elsif v.is_a?(Hash)
- nested_parameter_attributes[k] = attributes.delete(k)
+ attributes.each do |k, v|
+ if k.include?("(")
+ multi_parameter_attributes[k] = attributes.delete(k)
+ elsif v.is_a?(Hash)
+ nested_parameter_attributes[k] = attributes.delete(k)
+ end
end
- end
- super(attributes)
+ super(attributes)
- assign_nested_parameter_attributes(nested_parameter_attributes) unless nested_parameter_attributes.empty?
- assign_multiparameter_attributes(multi_parameter_attributes) unless multi_parameter_attributes.empty?
- end
+ assign_nested_parameter_attributes(nested_parameter_attributes) unless nested_parameter_attributes.empty?
+ assign_multiparameter_attributes(multi_parameter_attributes) unless multi_parameter_attributes.empty?
+ end
- # Assign any deferred nested attributes after the base attributes have been set.
- def assign_nested_parameter_attributes(pairs)
- pairs.each { |k, v| _assign_attribute(k, v) }
- end
+ # Assign any deferred nested attributes after the base attributes have been set.
+ def assign_nested_parameter_attributes(pairs)
+ pairs.each { |k, v| _assign_attribute(k, v) }
+ end
- # Instantiates objects for all attribute classes that needs more than one constructor parameter. This is done
- # by calling new on the column type or aggregation type (through composed_of) object with these parameters.
- # So having the pairs written_on(1) = "2004", written_on(2) = "6", written_on(3) = "24", will instantiate
- # written_on (a date type) with Date.new("2004", "6", "24"). You can also specify a typecast character in the
- # parentheses to have the parameters typecasted before they're used in the constructor. Use i for Integer and
- # f for Float. If all the values for a given attribute are empty, the attribute will be set to +nil+.
- def assign_multiparameter_attributes(pairs)
- execute_callstack_for_multiparameter_attributes(
- extract_callstack_for_multiparameter_attributes(pairs)
- )
- end
+ # Instantiates objects for all attribute classes that needs more than one constructor parameter. This is done
+ # by calling new on the column type or aggregation type (through composed_of) object with these parameters.
+ # So having the pairs written_on(1) = "2004", written_on(2) = "6", written_on(3) = "24", will instantiate
+ # written_on (a date type) with Date.new("2004", "6", "24"). You can also specify a typecast character in the
+ # parentheses to have the parameters typecasted before they're used in the constructor. Use i for Integer and
+ # f for Float. If all the values for a given attribute are empty, the attribute will be set to +nil+.
+ def assign_multiparameter_attributes(pairs)
+ execute_callstack_for_multiparameter_attributes(
+ extract_callstack_for_multiparameter_attributes(pairs)
+ )
+ end
- def execute_callstack_for_multiparameter_attributes(callstack)
- errors = []
- callstack.each do |name, values_with_empty_parameters|
- begin
- if values_with_empty_parameters.each_value.all?(&:nil?)
- values = nil
- else
- values = values_with_empty_parameters
+ def execute_callstack_for_multiparameter_attributes(callstack)
+ errors = []
+ callstack.each do |name, values_with_empty_parameters|
+ begin
+ if values_with_empty_parameters.each_value.all?(&:nil?)
+ values = nil
+ else
+ values = values_with_empty_parameters
+ end
+ send("#{name}=", values)
+ rescue => ex
+ errors << AttributeAssignmentError.new("error on assignment #{values_with_empty_parameters.values.inspect} to #{name} (#{ex.message})", ex, name)
end
- send("#{name}=", values)
- rescue => ex
- errors << AttributeAssignmentError.new("error on assignment #{values_with_empty_parameters.values.inspect} to #{name} (#{ex.message})", ex, name)
+ end
+ unless errors.empty?
+ error_descriptions = errors.map(&:message).join(",")
+ raise MultiparameterAssignmentErrors.new(errors), "#{errors.size} error(s) on assignment of multiparameter attributes [#{error_descriptions}]"
end
end
- unless errors.empty?
- error_descriptions = errors.map(&:message).join(",")
- raise MultiparameterAssignmentErrors.new(errors), "#{errors.size} error(s) on assignment of multiparameter attributes [#{error_descriptions}]"
- end
- end
- def extract_callstack_for_multiparameter_attributes(pairs)
- attributes = {}
+ def extract_callstack_for_multiparameter_attributes(pairs)
+ attributes = {}
- pairs.each do |(multiparameter_name, value)|
- attribute_name = multiparameter_name.split("(").first
- attributes[attribute_name] ||= {}
+ pairs.each do |(multiparameter_name, value)|
+ attribute_name = multiparameter_name.split("(").first
+ attributes[attribute_name] ||= {}
- parameter_value = value.empty? ? nil : type_cast_attribute_value(multiparameter_name, value)
- attributes[attribute_name][find_parameter_position(multiparameter_name)] ||= parameter_value
- end
+ parameter_value = value.empty? ? nil : type_cast_attribute_value(multiparameter_name, value)
+ attributes[attribute_name][find_parameter_position(multiparameter_name)] ||= parameter_value
+ end
- attributes
- end
+ attributes
+ end
- def type_cast_attribute_value(multiparameter_name, value)
- multiparameter_name =~ /\([0-9]*([if])\)/ ? value.send("to_" + $1) : value
- end
+ def type_cast_attribute_value(multiparameter_name, value)
+ multiparameter_name =~ /\([0-9]*([if])\)/ ? value.send("to_" + $1) : value
+ end
- def find_parameter_position(multiparameter_name)
- multiparameter_name.scan(/\(([0-9]*).*\)/).first.first.to_i
- end
+ def find_parameter_position(multiparameter_name)
+ multiparameter_name.scan(/\(([0-9]*).*\)/).first.first.to_i
+ end
end
end
diff --git a/activerecord/lib/active_record/attribute_decorators.rb b/activerecord/lib/active_record/attribute_decorators.rb
index 7d0ae32411..c39e9ce4c5 100644
--- a/activerecord/lib/active_record/attribute_decorators.rb
+++ b/activerecord/lib/active_record/attribute_decorators.rb
@@ -8,12 +8,34 @@ module ActiveRecord
end
module ClassMethods # :nodoc:
+ # This method is an internal API used to create class macros such as
+ # +serialize+, and features like time zone aware attributes.
+ #
+ # Used to wrap the type of an attribute in a new type.
+ # When the schema for a model is loaded, attributes with the same name as
+ # +column_name+ will have their type yielded to the given block. The
+ # return value of that block will be used instead.
+ #
+ # Subsequent calls where +column_name+ and +decorator_name+ are the same
+ # will override the previous decorator, not decorate twice. This can be
+ # used to create idempotent class macros like +serialize+
def decorate_attribute_type(column_name, decorator_name, &block)
matcher = ->(name, _) { name == column_name.to_s }
key = "_#{column_name}_#{decorator_name}"
decorate_matching_attribute_types(matcher, key, &block)
end
+ # This method is an internal API used to create higher level features like
+ # time zone aware attributes.
+ #
+ # When the schema for a model is loaded, +matcher+ will be called for each
+ # attribute with its name and type. If the matcher returns a truthy value,
+ # the type will then be yielded to the given block, and the return value
+ # of that block will replace the type.
+ #
+ # Subsequent calls to this method with the same value for +decorator_name+
+ # will replace the previous decorator, not decorate twice. This can be
+ # used to ensure that class macros are idempotent.
def decorate_matching_attribute_types(matcher, decorator_name, &block)
reload_schema_from_cache
decorator_name = decorator_name.to_s
@@ -24,13 +46,13 @@ module ActiveRecord
private
- def load_schema!
- super
- attribute_types.each do |name, type|
- decorated_type = attribute_type_decorations.apply(name, type)
- define_attribute(name, decorated_type)
+ def load_schema!
+ super
+ attribute_types.each do |name, type|
+ decorated_type = attribute_type_decorations.apply(name, type)
+ define_attribute(name, decorated_type)
+ end
end
- end
end
class TypeDecorator # :nodoc:
@@ -53,15 +75,15 @@ module ActiveRecord
private
- def decorators_for(name, type)
- matching(name, type).map(&:last)
- end
+ def decorators_for(name, type)
+ matching(name, type).map(&:last)
+ end
- def matching(name, type)
- @decorations.values.select do |(matcher, _)|
- matcher.call(name, type)
+ def matching(name, type)
+ @decorations.values.select do |(matcher, _)|
+ matcher.call(name, type)
+ end
end
- end
end
end
end
diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb
index eadd73aab0..1ed1deec55 100644
--- a/activerecord/lib/active_record/attribute_methods.rb
+++ b/activerecord/lib/active_record/attribute_methods.rb
@@ -1,7 +1,7 @@
-require 'active_support/core_ext/enumerable'
-require 'active_support/core_ext/string/filters'
-require 'mutex_m'
-require 'concurrent/map'
+require "active_support/core_ext/enumerable"
+require "active_support/core_ext/string/filters"
+require "mutex_m"
+require "concurrent/map"
module ActiveRecord
# = Active Record Attribute Methods
@@ -148,7 +148,7 @@ module ActiveRecord
# Person.attribute_method?(:age=) # => true
# Person.attribute_method?(:nothing) # => false
def attribute_method?(attribute)
- super || (table_exists? && column_names.include?(attribute.to_s.sub(/=$/, '')))
+ super || (table_exists? && column_names.include?(attribute.to_s.sub(/=$/, "")))
end
# Returns an array of column names as strings if it's not an abstract class and
@@ -161,10 +161,10 @@ module ActiveRecord
# # => ["id", "created_at", "updated_at", "name", "age"]
def attribute_names
@attribute_names ||= if !abstract_class? && table_exists?
- attribute_types.keys
- else
- []
- end
+ attribute_types.keys
+ else
+ []
+ end
end
# Returns true if the given attribute exists, otherwise false.
@@ -394,65 +394,65 @@ module ActiveRecord
protected
- def clone_attribute_value(reader_method, attribute_name) # :nodoc:
- value = send(reader_method, attribute_name)
- value.duplicable? ? value.clone : value
- rescue TypeError, NoMethodError
- value
- end
+ def clone_attribute_value(reader_method, attribute_name) # :nodoc:
+ value = send(reader_method, attribute_name)
+ value.duplicable? ? value.clone : value
+ rescue TypeError, NoMethodError
+ value
+ end
- def arel_attributes_with_values_for_create(attribute_names) # :nodoc:
- arel_attributes_with_values(attributes_for_create(attribute_names))
- end
+ def arel_attributes_with_values_for_create(attribute_names) # :nodoc:
+ arel_attributes_with_values(attributes_for_create(attribute_names))
+ end
- def arel_attributes_with_values_for_update(attribute_names) # :nodoc:
- arel_attributes_with_values(attributes_for_update(attribute_names))
- end
+ def arel_attributes_with_values_for_update(attribute_names) # :nodoc:
+ arel_attributes_with_values(attributes_for_update(attribute_names))
+ end
- def attribute_method?(attr_name) # :nodoc:
- # We check defined? because Syck calls respond_to? before actually calling initialize.
- defined?(@attributes) && @attributes.key?(attr_name)
- end
+ def attribute_method?(attr_name) # :nodoc:
+ # We check defined? because Syck calls respond_to? before actually calling initialize.
+ defined?(@attributes) && @attributes.key?(attr_name)
+ end
private
- # Returns a Hash of the Arel::Attributes and attribute values that have been
- # typecasted for use in an Arel insert/update method.
- def arel_attributes_with_values(attribute_names)
- attrs = {}
- arel_table = self.class.arel_table
+ # Returns a Hash of the Arel::Attributes and attribute values that have been
+ # typecasted for use in an Arel insert/update method.
+ def arel_attributes_with_values(attribute_names)
+ attrs = {}
+ arel_table = self.class.arel_table
- attribute_names.each do |name|
- attrs[arel_table[name]] = typecasted_attribute_value(name)
+ attribute_names.each do |name|
+ attrs[arel_table[name]] = typecasted_attribute_value(name)
+ end
+ attrs
end
- attrs
- end
- # Filters the primary keys and readonly attributes from the attribute names.
- def attributes_for_update(attribute_names)
- attribute_names.reject do |name|
- readonly_attribute?(name)
+ # Filters the primary keys and readonly attributes from the attribute names.
+ def attributes_for_update(attribute_names)
+ attribute_names.reject do |name|
+ readonly_attribute?(name)
+ end
end
- end
- # Filters out the primary keys, from the attribute names, when the primary
- # key is to be generated (e.g. the id attribute has no value).
- def attributes_for_create(attribute_names)
- attribute_names.reject do |name|
- pk_attribute?(name) && id.nil?
+ # Filters out the primary keys, from the attribute names, when the primary
+ # key is to be generated (e.g. the id attribute has no value).
+ def attributes_for_create(attribute_names)
+ attribute_names.reject do |name|
+ pk_attribute?(name) && id.nil?
+ end
end
- end
- def readonly_attribute?(name)
- self.class.readonly_attributes.include?(name)
- end
+ def readonly_attribute?(name)
+ self.class.readonly_attributes.include?(name)
+ end
- def pk_attribute?(name)
- name == self.class.primary_key
- end
+ def pk_attribute?(name)
+ name == self.class.primary_key
+ end
- def typecasted_attribute_value(name)
- _read_attribute(name)
- end
+ def typecasted_attribute_value(name)
+ _read_attribute(name)
+ end
end
end
diff --git a/activerecord/lib/active_record/attribute_methods/before_type_cast.rb b/activerecord/lib/active_record/attribute_methods/before_type_cast.rb
index 1db6776688..115eb1ef3f 100644
--- a/activerecord/lib/active_record/attribute_methods/before_type_cast.rb
+++ b/activerecord/lib/active_record/attribute_methods/before_type_cast.rb
@@ -63,14 +63,14 @@ module ActiveRecord
private
- # Handle *_before_type_cast for method_missing.
- def attribute_before_type_cast(attribute_name)
- read_attribute_before_type_cast(attribute_name)
- end
+ # Handle *_before_type_cast for method_missing.
+ def attribute_before_type_cast(attribute_name)
+ read_attribute_before_type_cast(attribute_name)
+ end
- def attribute_came_from_user?(attribute_name)
- @attributes[attribute_name].came_from_user?
- end
+ def attribute_came_from_user?(attribute_name)
+ @attributes[attribute_name].came_from_user?
+ end
end
end
end
diff --git a/activerecord/lib/active_record/attribute_methods/dirty.rb b/activerecord/lib/active_record/attribute_methods/dirty.rb
index 0bcfa5f00d..b0e1391cb9 100644
--- a/activerecord/lib/active_record/attribute_methods/dirty.rb
+++ b/activerecord/lib/active_record/attribute_methods/dirty.rb
@@ -1,5 +1,6 @@
-require 'active_support/core_ext/module/attribute_accessors'
-require 'active_record/attribute_mutation_tracker'
+# frozen_string_literal: true
+require "active_support/core_ext/module/attribute_accessors"
+require "active_record/attribute_mutation_tracker"
module ActiveRecord
module AttributeMethods
@@ -15,6 +16,18 @@ module ActiveRecord
class_attribute :partial_writes, instance_writer: false
self.partial_writes = true
+
+ after_create { changes_internally_applied }
+ after_update { changes_internally_applied }
+
+ # Attribute methods for "changed in last call to save?"
+ attribute_method_affix(prefix: "saved_change_to_", suffix: "?")
+ attribute_method_prefix("saved_change_to_")
+ attribute_method_suffix("_before_last_save")
+
+ # Attribute methods for "will change if I call save?"
+ attribute_method_affix(prefix: "will_save_change_to_", suffix: "?")
+ attribute_method_suffix("_change_to_be_saved", "_in_database")
end
# Attempts to +save+ the record and clears changed attributes if successful.
@@ -35,8 +48,8 @@ module ActiveRecord
# <tt>reload</tt> the record and clears changed attributes.
def reload(*)
super.tap do
- @mutation_tracker = nil
@previous_mutation_tracker = nil
+ clear_mutation_trackers
@changed_attributes = HashWithIndifferentAccess.new
end
end
@@ -46,19 +59,26 @@ module ActiveRecord
@attributes = self.class._default_attributes.map do |attr|
attr.with_value_from_user(@attributes.fetch_value(attr.name))
end
- @mutation_tracker = nil
+ clear_mutation_trackers
+ end
+
+ def changes_internally_applied # :nodoc:
+ @mutations_before_last_save = mutation_tracker
+ forget_attribute_assignments
+ @mutations_from_database = AttributeMutationTracker.new(@attributes)
end
def changes_applied
@previous_mutation_tracker = mutation_tracker
@changed_attributes = HashWithIndifferentAccess.new
- store_original_attributes
+ clear_mutation_trackers
end
def clear_changes_information
@previous_mutation_tracker = nil
@changed_attributes = HashWithIndifferentAccess.new
- store_original_attributes
+ forget_attribute_assignments
+ clear_mutation_trackers
end
def raw_write_attribute(attr_name, *)
@@ -80,17 +100,27 @@ module ActiveRecord
if defined?(@cached_changed_attributes)
@cached_changed_attributes
else
+ emit_warning_if_needed("changed_attributes", "saved_changes.transform_values(&:first)")
super.reverse_merge(mutation_tracker.changed_values).freeze
end
end
def changes
cache_changed_attributes do
+ emit_warning_if_needed("changes", "saved_changes")
super
end
end
def previous_changes
+ unless previous_mutation_tracker.equal?(mutations_before_last_save)
+ ActiveSupport::Deprecation.warn(<<-EOW.strip_heredoc)
+ The behavior of `previous_changes` inside of after callbacks is
+ deprecated without replacement. In the next release of Rails,
+ this method inside of `after_save` will return the changes that
+ were just saved.
+ EOW
+ end
previous_mutation_tracker.changes
end
@@ -98,54 +128,196 @@ module ActiveRecord
mutation_tracker.changed_in_place?(attr_name)
end
- private
+ # Did this attribute change when we last saved? This method can be invoked
+ # as `saved_change_to_name?` instead of `saved_change_to_attribute?("name")`.
+ # Behaves similarly to +attribute_changed?+. This method is useful in
+ # after callbacks to determine if the call to save changed a certain
+ # attribute.
+ #
+ # ==== Options
+ #
+ # +from+ When passed, this method will return false unless the original
+ # value is equal to the given option
+ #
+ # +to+ When passed, this method will return false unless the value was
+ # changed to the given value
+ def saved_change_to_attribute?(attr_name, **options)
+ mutations_before_last_save.changed?(attr_name, **options)
+ end
- def mutation_tracker
- unless defined?(@mutation_tracker)
- @mutation_tracker = nil
- end
- @mutation_tracker ||= AttributeMutationTracker.new(@attributes)
+ # Returns the change to an attribute during the last save. If the
+ # attribute was changed, the result will be an array containing the
+ # original value and the saved value.
+ #
+ # Behaves similarly to +attribute_change+. This method is useful in after
+ # callbacks, to see the change in an attribute that just occurred
+ #
+ # This method can be invoked as `saved_change_to_name` in instead of
+ # `saved_change_to_attribute("name")`
+ def saved_change_to_attribute(attr_name)
+ mutations_before_last_save.change_to_attribute(attr_name)
+ end
+
+ # Returns the original value of an attribute before the last save.
+ # Behaves similarly to +attribute_was+. This method is useful in after
+ # callbacks to get the original value of an attribute before the save that
+ # just occurred
+ def attribute_before_last_save(attr_name)
+ mutations_before_last_save.original_value(attr_name)
+ end
+
+ # Did the last call to `save` have any changes to change?
+ def saved_changes?
+ mutations_before_last_save.any_changes?
end
- def changes_include?(attr_name)
- super || mutation_tracker.changed?(attr_name)
+ # Returns a hash containing all the changes that were just saved.
+ def saved_changes
+ mutations_before_last_save.changes
end
- def clear_attribute_change(attr_name)
- mutation_tracker.forget_change(attr_name)
+ # Alias for `attribute_changed?`
+ def will_save_change_to_attribute?(attr_name, **options)
+ mutations_from_database.changed?(attr_name, **options)
end
- def _update_record(*)
- partial_writes? ? super(keys_for_partial_write) : super
+ # Alias for `attribute_change`
+ def attribute_change_to_be_saved(attr_name)
+ mutations_from_database.change_to_attribute(attr_name)
end
- def _create_record(*)
- partial_writes? ? super(keys_for_partial_write) : super
+ # Alias for `attribute_was`
+ def attribute_in_database(attr_name)
+ mutations_from_database.original_value(attr_name)
end
- def keys_for_partial_write
- changed & self.class.column_names
+ # Alias for `changed?`
+ def has_changes_to_save?
+ mutations_from_database.any_changes?
end
- def store_original_attributes
- @attributes = @attributes.map(&:forgetting_assignment)
- @mutation_tracker = nil
+ # Alias for `changes`
+ def changes_to_save
+ mutations_from_database.changes
end
- def previous_mutation_tracker
- @previous_mutation_tracker ||= NullMutationTracker.instance
+ # Alias for `changed`
+ def changed_attribute_names_to_save
+ changes_to_save.keys
end
- def cache_changed_attributes
- @cached_changed_attributes = changed_attributes
- yield
- ensure
- clear_changed_attributes_cache
+ # Alias for `changed_attributes`
+ def attributes_in_database
+ changes_to_save.transform_values(&:first)
end
- def clear_changed_attributes_cache
- remove_instance_variable(:@cached_changed_attributes) if defined?(@cached_changed_attributes)
+ def attribute_was(*)
+ emit_warning_if_needed("attribute_was", "attribute_before_last_save")
+ super
end
+
+ def attribute_change(*)
+ emit_warning_if_needed("attribute_change", "saved_change_to_attribute")
+ super
+ end
+
+ def attribute_changed?(*)
+ emit_warning_if_needed("attribute_changed?", "saved_change_to_attribute?")
+ super
+ end
+
+ def changed?(*)
+ emit_warning_if_needed("changed?", "saved_changes?")
+ super
+ end
+
+ def changed(*)
+ emit_warning_if_needed("changed", "saved_changes.keys")
+ super
+ end
+
+ private
+
+ def mutation_tracker
+ unless defined?(@mutation_tracker)
+ @mutation_tracker = nil
+ end
+ @mutation_tracker ||= AttributeMutationTracker.new(@attributes)
+ end
+
+ def emit_warning_if_needed(method_name, new_method_name)
+ unless mutation_tracker.equal?(mutations_from_database)
+ ActiveSupport::Deprecation.warn(<<-EOW.squish)
+ The behavior of `#{method_name}` inside of after callbacks will
+ be changing in the next version of Rails. The new return value will reflect the
+ behavior of calling the method after `save` returned (e.g. the opposite of what
+ it returns now). To maintain the current behavior, use `#{new_method_name}`
+ instead.
+ EOW
+ end
+ end
+
+ def mutations_from_database
+ unless defined?(@mutations_from_database)
+ @mutations_from_database = nil
+ end
+ @mutations_from_database ||= mutation_tracker
+ end
+
+ def changes_include?(attr_name)
+ super || mutation_tracker.changed?(attr_name)
+ end
+
+ def clear_attribute_change(attr_name)
+ mutation_tracker.forget_change(attr_name)
+ mutations_from_database.forget_change(attr_name)
+ end
+
+ def attribute_will_change!(attr_name)
+ super
+ mutations_from_database.force_change(attr_name)
+ end
+
+ def _update_record(*)
+ partial_writes? ? super(keys_for_partial_write) : super
+ end
+
+ def _create_record(*)
+ partial_writes? ? super(keys_for_partial_write) : super
+ end
+
+ def keys_for_partial_write
+ changed_attribute_names_to_save & self.class.column_names
+ end
+
+ def forget_attribute_assignments
+ @attributes = @attributes.map(&:forgetting_assignment)
+ end
+
+ def clear_mutation_trackers
+ @mutation_tracker = nil
+ @mutations_from_database = nil
+ @mutations_before_last_save = nil
+ end
+
+ def previous_mutation_tracker
+ @previous_mutation_tracker ||= NullMutationTracker.instance
+ end
+
+ def mutations_before_last_save
+ @mutations_before_last_save ||= previous_mutation_tracker
+ end
+
+ def cache_changed_attributes
+ @cached_changed_attributes = changed_attributes
+ yield
+ ensure
+ clear_changed_attributes_cache
+ end
+
+ def clear_changed_attributes_cache
+ remove_instance_variable(:@cached_changed_attributes) if defined?(@cached_changed_attributes)
+ end
end
end
end
diff --git a/activerecord/lib/active_record/attribute_methods/primary_key.rb b/activerecord/lib/active_record/attribute_methods/primary_key.rb
index 0d5cb8b37c..8fcac82a0d 100644
--- a/activerecord/lib/active_record/attribute_methods/primary_key.rb
+++ b/activerecord/lib/active_record/attribute_methods/primary_key.rb
@@ -1,4 +1,4 @@
-require 'set'
+require "set"
module ActiveRecord
module AttributeMethods
@@ -45,84 +45,102 @@ module ActiveRecord
attribute_was(self.class.primary_key)
end
- protected
-
- def attribute_method?(attr_name)
- attr_name == 'id' || super
+ def id_in_database
+ sync_with_transaction_state
+ attribute_in_database(self.class.primary_key)
end
- module ClassMethods
- def define_method_attribute(attr_name)
- super
+ private
- if attr_name == primary_key && attr_name != 'id'
- generated_attribute_methods.send(:alias_method, :id, primary_key)
- end
+ def attribute_method?(attr_name)
+ attr_name == "id" || super
end
- ID_ATTRIBUTE_METHODS = %w(id id= id? id_before_type_cast id_was).to_set
+ module ClassMethods
+ def define_method_attribute(attr_name)
+ super
- def dangerous_attribute_method?(method_name)
- super && !ID_ATTRIBUTE_METHODS.include?(method_name)
- end
+ if attr_name == primary_key && attr_name != "id"
+ generated_attribute_methods.send(:alias_method, :id, primary_key)
+ end
+ end
- # Defines the primary key field -- can be overridden in subclasses.
- # Overwriting will negate any effect of the +primary_key_prefix_type+
- # setting, though.
- def primary_key
- @primary_key = reset_primary_key unless defined? @primary_key
- @primary_key
- end
+ ID_ATTRIBUTE_METHODS = %w(id id= id? id_before_type_cast id_was id_in_database).to_set
- # Returns a quoted version of the primary key name, used to construct
- # SQL statements.
- def quoted_primary_key
- @quoted_primary_key ||= connection.quote_column_name(primary_key)
- end
+ def dangerous_attribute_method?(method_name)
+ super && !ID_ATTRIBUTE_METHODS.include?(method_name)
+ end
- def reset_primary_key #:nodoc:
- if self == base_class
- self.primary_key = get_primary_key(base_class.name)
- else
- self.primary_key = base_class.primary_key
+ # Defines the primary key field -- can be overridden in subclasses.
+ # Overwriting will negate any effect of the +primary_key_prefix_type+
+ # setting, though.
+ def primary_key
+ @primary_key = reset_primary_key unless defined? @primary_key
+ @primary_key
+ end
+
+ # Returns a quoted version of the primary key name, used to construct
+ # SQL statements.
+ def quoted_primary_key
+ @quoted_primary_key ||= connection.quote_column_name(primary_key)
end
- end
- def get_primary_key(base_name) #:nodoc:
- if base_name && primary_key_prefix_type == :table_name
- base_name.foreign_key(false)
- elsif base_name && primary_key_prefix_type == :table_name_with_underscore
- base_name.foreign_key
- else
- if ActiveRecord::Base != self && table_exists?
- connection.schema_cache.primary_keys(table_name)
+ def reset_primary_key #:nodoc:
+ if self == base_class
+ self.primary_key = get_primary_key(base_class.name)
else
- 'id'
+ self.primary_key = base_class.primary_key
end
end
- end
- # Sets the name of the primary key column.
- #
- # class Project < ActiveRecord::Base
- # self.primary_key = 'sysid'
- # end
- #
- # You can also define the #primary_key method yourself:
- #
- # class Project < ActiveRecord::Base
- # def self.primary_key
- # 'foo_' + super
- # end
- # end
- #
- # Project.primary_key # => "foo_id"
- def primary_key=(value)
- @primary_key = value && value.to_s
- @quoted_primary_key = nil
- @attributes_builder = nil
+ def get_primary_key(base_name) #:nodoc:
+ if base_name && primary_key_prefix_type == :table_name
+ base_name.foreign_key(false)
+ elsif base_name && primary_key_prefix_type == :table_name_with_underscore
+ base_name.foreign_key
+ else
+ if ActiveRecord::Base != self && table_exists?
+ pk = connection.schema_cache.primary_keys(table_name)
+ suppress_composite_primary_key(pk)
+ else
+ "id"
+ end
+ end
+ end
+
+ # Sets the name of the primary key column.
+ #
+ # class Project < ActiveRecord::Base
+ # self.primary_key = 'sysid'
+ # end
+ #
+ # You can also define the #primary_key method yourself:
+ #
+ # class Project < ActiveRecord::Base
+ # def self.primary_key
+ # 'foo_' + super
+ # end
+ # end
+ #
+ # Project.primary_key # => "foo_id"
+ def primary_key=(value)
+ @primary_key = value && value.to_s
+ @quoted_primary_key = nil
+ @attributes_builder = nil
+ end
+
+ private
+
+ def suppress_composite_primary_key(pk)
+ return pk unless pk.is_a?(Array)
+
+ warn <<-WARNING.strip_heredoc
+ WARNING: Active Record does not support composite primary key.
+
+ #{table_name} has composite primary key. Composite primary key is ignored.
+ WARNING
+ end
end
- end
end
end
end
diff --git a/activerecord/lib/active_record/attribute_methods/read.rb b/activerecord/lib/active_record/attribute_methods/read.rb
index ab2ecaa7c5..369a6e35aa 100644
--- a/activerecord/lib/active_record/attribute_methods/read.rb
+++ b/activerecord/lib/active_record/attribute_methods/read.rb
@@ -4,52 +4,57 @@ module ActiveRecord
extend ActiveSupport::Concern
module ClassMethods
- protected
+ private
- # We want to generate the methods via module_eval rather than
- # define_method, because define_method is slower on dispatch.
- # Evaluating many similar methods may use more memory as the instruction
- # sequences are duplicated and cached (in MRI). define_method may
- # be slower on dispatch, but if you're careful about the closure
- # created, then define_method will consume much less memory.
- #
- # But sometimes the database might return columns with
- # characters that are not allowed in normal method names (like
- # 'my_column(omg)'. So to work around this we first define with
- # the __temp__ identifier, and then use alias method to rename
- # it to what we want.
- #
- # We are also defining a constant to hold the frozen string of
- # the attribute name. Using a constant means that we do not have
- # to allocate an object on each call to the attribute method.
- # Making it frozen means that it doesn't get duped when used to
- # key the @attributes in read_attribute.
- def define_method_attribute(name)
- safe_name = name.unpack('h*'.freeze).first
- temp_method = "__temp__#{safe_name}"
+ # We want to generate the methods via module_eval rather than
+ # define_method, because define_method is slower on dispatch.
+ # Evaluating many similar methods may use more memory as the instruction
+ # sequences are duplicated and cached (in MRI). define_method may
+ # be slower on dispatch, but if you're careful about the closure
+ # created, then define_method will consume much less memory.
+ #
+ # But sometimes the database might return columns with
+ # characters that are not allowed in normal method names (like
+ # 'my_column(omg)'. So to work around this we first define with
+ # the __temp__ identifier, and then use alias method to rename
+ # it to what we want.
+ #
+ # We are also defining a constant to hold the frozen string of
+ # the attribute name. Using a constant means that we do not have
+ # to allocate an object on each call to the attribute method.
+ # Making it frozen means that it doesn't get duped when used to
+ # key the @attributes in read_attribute.
+ def define_method_attribute(name)
+ safe_name = name.unpack("h*".freeze).first
+ temp_method = "__temp__#{safe_name}"
- ActiveRecord::AttributeMethods::AttrNames.set_name_cache safe_name, name
+ ActiveRecord::AttributeMethods::AttrNames.set_name_cache safe_name, name
- generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1
- def #{temp_method}
- name = ::ActiveRecord::AttributeMethods::AttrNames::ATTR_#{safe_name}
- _read_attribute(name) { |n| missing_attribute(n, caller) }
- end
- STR
+ generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1
+ def #{temp_method}
+ name = ::ActiveRecord::AttributeMethods::AttrNames::ATTR_#{safe_name}
+ _read_attribute(name) { |n| missing_attribute(n, caller) }
+ end
+ STR
- generated_attribute_methods.module_eval do
- alias_method name, temp_method
- undef_method temp_method
+ generated_attribute_methods.module_eval do
+ alias_method name, temp_method
+ undef_method temp_method
+ end
end
- end
end
# Returns the value of the attribute identified by <tt>attr_name</tt> after
# it has been typecast (for example, "2004-12-12" in a date column is cast
# to a date object, like Date.new(2004, 12, 12)).
def read_attribute(attr_name, &block)
- name = attr_name.to_s
- name = self.class.primary_key if name == 'id'.freeze
+ name = if self.class.attribute_alias?(attr_name)
+ self.class.attribute_alias(attr_name).to_s
+ else
+ attr_name.to_s
+ end
+
+ name = self.class.primary_key if name == "id".freeze
_read_attribute(name, &block)
end
@@ -69,7 +74,6 @@ module ActiveRecord
alias :attribute :_read_attribute
private :attribute
-
end
end
end
diff --git a/activerecord/lib/active_record/attribute_methods/serialization.rb b/activerecord/lib/active_record/attribute_methods/serialization.rb
index 65978aea2a..945192fe04 100644
--- a/activerecord/lib/active_record/attribute_methods/serialization.rb
+++ b/activerecord/lib/active_record/attribute_methods/serialization.rb
@@ -26,7 +26,7 @@ module ActiveRecord
# ==== Parameters
#
# * +attr_name+ - The field name that should be serialized.
- # * +class_name_or_coder+ - Optional, a coder object, which responds to `.load` / `.dump`
+ # * +class_name_or_coder+ - Optional, a coder object, which responds to +.load+ and +.dump+
# or a class name that the object type should be equal to.
#
# ==== Example
@@ -50,12 +50,12 @@ module ActiveRecord
# to ensure special objects (e.g. Active Record models) are dumped correctly
# using the #as_json hook.
coder = if class_name_or_coder == ::JSON
- Coders::JSON
- elsif [:load, :dump].all? { |x| class_name_or_coder.respond_to?(x) }
- class_name_or_coder
- else
- Coders::YAMLColumn.new(class_name_or_coder)
- end
+ Coders::JSON
+ elsif [:load, :dump].all? { |x| class_name_or_coder.respond_to?(x) }
+ class_name_or_coder
+ else
+ Coders::YAMLColumn.new(class_name_or_coder)
+ end
decorate_attribute_type(attr_name, :serialize) do |type|
Type::Serialized.new(type, coder)
diff --git a/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb b/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
index e160460286..df1231ad47 100644
--- a/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
+++ b/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
@@ -1,4 +1,4 @@
-require 'active_support/core_ext/string/strip'
+require "active_support/core_ext/string/strip"
module ActiveRecord
module AttributeMethods
@@ -26,31 +26,31 @@ module ActiveRecord
private
- def convert_time_to_time_zone(value)
- return if value.nil?
+ def convert_time_to_time_zone(value)
+ return if value.nil?
- if value.acts_like?(:time)
- value.in_time_zone
- elsif value.is_a?(::Float)
- value
- else
- map_avoiding_infinite_recursion(value) { |v| convert_time_to_time_zone(v) }
+ if value.acts_like?(:time)
+ value.in_time_zone
+ elsif value.is_a?(::Float)
+ value
+ else
+ map_avoiding_infinite_recursion(value) { |v| convert_time_to_time_zone(v) }
+ end
end
- end
- def set_time_zone_without_conversion(value)
- ::Time.zone.local_to_utc(value).in_time_zone
- end
+ def set_time_zone_without_conversion(value)
+ ::Time.zone.local_to_utc(value).try(:in_time_zone) if value
+ end
- def map_avoiding_infinite_recursion(value)
- map(value) do |v|
- if value.equal?(v)
- nil
- else
- yield(v)
+ def map_avoiding_infinite_recursion(value)
+ map(value) do |v|
+ if value.equal?(v)
+ nil
+ else
+ yield(v)
+ end
end
end
- end
end
extend ActiveSupport::Concern
@@ -63,53 +63,32 @@ module ActiveRecord
self.skip_time_zone_conversion_for_attributes = []
class_attribute :time_zone_aware_types, instance_writer: false
- self.time_zone_aware_types = [:datetime, :not_explicitly_configured]
+ self.time_zone_aware_types = [:datetime, :time]
end
module ClassMethods
private
- def inherited(subclass)
- # We need to apply this decorator here, rather than on module inclusion. The closure
- # created by the matcher would otherwise evaluate for `ActiveRecord::Base`, not the
- # sub class being decorated. As such, changes to `time_zone_aware_attributes`, or
- # `skip_time_zone_conversion_for_attributes` would not be picked up.
- subclass.class_eval do
- matcher = ->(name, type) { create_time_zone_conversion_attribute?(name, type) }
- decorate_matching_attribute_types(matcher, :_time_zone_conversion) do |type|
- TimeZoneConverter.new(type)
+ def inherited(subclass)
+ super
+ # We need to apply this decorator here, rather than on module inclusion. The closure
+ # created by the matcher would otherwise evaluate for `ActiveRecord::Base`, not the
+ # sub class being decorated. As such, changes to `time_zone_aware_attributes`, or
+ # `skip_time_zone_conversion_for_attributes` would not be picked up.
+ subclass.class_eval do
+ matcher = ->(name, type) { create_time_zone_conversion_attribute?(name, type) }
+ decorate_matching_attribute_types(matcher, :_time_zone_conversion) do |type|
+ TimeZoneConverter.new(type)
+ end
end
end
- super
- end
-
- def create_time_zone_conversion_attribute?(name, cast_type)
- enabled_for_column = time_zone_aware_attributes &&
- !self.skip_time_zone_conversion_for_attributes.include?(name.to_sym)
- result = enabled_for_column &&
- time_zone_aware_types.include?(cast_type.type)
-
- if enabled_for_column &&
- !result &&
- cast_type.type == :time &&
- time_zone_aware_types.include?(:not_explicitly_configured)
- ActiveSupport::Deprecation.warn(<<-MESSAGE.strip_heredoc)
- Time columns will become time zone aware in Rails 5.1. This
- still causes `String`s to be parsed as if they were in `Time.zone`,
- and `Time`s to be converted to `Time.zone`.
- To keep the old behavior, you must add the following to your initializer:
+ def create_time_zone_conversion_attribute?(name, cast_type)
+ enabled_for_column = time_zone_aware_attributes &&
+ !skip_time_zone_conversion_for_attributes.include?(name.to_sym)
- config.active_record.time_zone_aware_types = [:datetime]
-
- To silence this deprecation warning, add the following:
-
- config.active_record.time_zone_aware_types = [:datetime, :time]
- MESSAGE
+ enabled_for_column && time_zone_aware_types.include?(cast_type.type)
end
-
- result
- end
end
end
end
diff --git a/activerecord/lib/active_record/attribute_methods/write.rb b/activerecord/lib/active_record/attribute_methods/write.rb
index 70c2d2f25d..fe0e01db28 100644
--- a/activerecord/lib/active_record/attribute_methods/write.rb
+++ b/activerecord/lib/active_record/attribute_methods/write.rb
@@ -8,28 +8,34 @@ module ActiveRecord
end
module ClassMethods
- protected
-
- def define_method_attribute=(name)
- safe_name = name.unpack('h*'.freeze).first
- ActiveRecord::AttributeMethods::AttrNames.set_name_cache safe_name, name
-
- generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1
- def __temp__#{safe_name}=(value)
- name = ::ActiveRecord::AttributeMethods::AttrNames::ATTR_#{safe_name}
- write_attribute(name, value)
- end
- alias_method #{(name + '=').inspect}, :__temp__#{safe_name}=
- undef_method :__temp__#{safe_name}=
- STR
- end
+ private
+
+ def define_method_attribute=(name)
+ safe_name = name.unpack("h*".freeze).first
+ ActiveRecord::AttributeMethods::AttrNames.set_name_cache safe_name, name
+
+ generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1
+ def __temp__#{safe_name}=(value)
+ name = ::ActiveRecord::AttributeMethods::AttrNames::ATTR_#{safe_name}
+ write_attribute(name, value)
+ end
+ alias_method #{(name + '=').inspect}, :__temp__#{safe_name}=
+ undef_method :__temp__#{safe_name}=
+ STR
+ end
end
# Updates the attribute identified by <tt>attr_name</tt> with the
# specified +value+. Empty strings for Integer and Float columns are
# turned into +nil+.
def write_attribute(attr_name, value)
- write_attribute_with_type_cast(attr_name, value, true)
+ name = if self.class.attribute_alias?(attr_name)
+ self.class.attribute_alias(attr_name).to_s
+ else
+ attr_name.to_s
+ end
+
+ write_attribute_with_type_cast(name, value, true)
end
def raw_write_attribute(attr_name, value) # :nodoc:
@@ -37,23 +43,23 @@ module ActiveRecord
end
private
- # Handle *= for method_missing.
- def attribute=(attribute_name, value)
- write_attribute(attribute_name, value)
- end
+ # Handle *= for method_missing.
+ def attribute=(attribute_name, value)
+ write_attribute(attribute_name, value)
+ end
- def write_attribute_with_type_cast(attr_name, value, should_type_cast)
- attr_name = attr_name.to_s
- attr_name = self.class.primary_key if attr_name == 'id' && self.class.primary_key
+ def write_attribute_with_type_cast(attr_name, value, should_type_cast)
+ attr_name = attr_name.to_s
+ attr_name = self.class.primary_key if attr_name == "id" && self.class.primary_key
- if should_type_cast
- @attributes.write_from_user(attr_name, value)
- else
- @attributes.write_cast_value(attr_name, value)
- end
+ if should_type_cast
+ @attributes.write_from_user(attr_name, value)
+ else
+ @attributes.write_cast_value(attr_name, value)
+ end
- value
- end
+ value
+ end
end
end
end
diff --git a/activerecord/lib/active_record/attribute_mutation_tracker.rb b/activerecord/lib/active_record/attribute_mutation_tracker.rb
index 0133b4d0be..3417090830 100644
--- a/activerecord/lib/active_record/attribute_mutation_tracker.rb
+++ b/activerecord/lib/active_record/attribute_mutation_tracker.rb
@@ -1,7 +1,10 @@
module ActiveRecord
class AttributeMutationTracker # :nodoc:
+ OPTION_NOT_GIVEN = Object.new
+
def initialize(attributes)
@attributes = attributes
+ @forced_changes = Set.new
end
def changed_values
@@ -14,15 +17,29 @@ module ActiveRecord
def changes
attr_names.each_with_object({}.with_indifferent_access) do |attr_name, result|
- if changed?(attr_name)
- result[attr_name] = [attributes[attr_name].original_value, attributes.fetch_value(attr_name)]
+ change = change_to_attribute(attr_name)
+ if change
+ result[attr_name] = change
end
end
end
- def changed?(attr_name)
+ def change_to_attribute(attr_name)
+ if changed?(attr_name)
+ [attributes[attr_name].original_value, attributes.fetch_value(attr_name)]
+ end
+ end
+
+ def any_changes?
+ attr_names.any? { |attr| changed?(attr) }
+ end
+
+ def changed?(attr_name, from: OPTION_NOT_GIVEN, to: OPTION_NOT_GIVEN)
attr_name = attr_name.to_s
- attributes[attr_name].changed?
+ forced_changes.include?(attr_name) ||
+ attributes[attr_name].changed? &&
+ (OPTION_NOT_GIVEN == from || attributes[attr_name].original_value == from) &&
+ (OPTION_NOT_GIVEN == to || attributes[attr_name].value == to)
end
def changed_in_place?(attr_name)
@@ -32,30 +49,48 @@ module ActiveRecord
def forget_change(attr_name)
attr_name = attr_name.to_s
attributes[attr_name] = attributes[attr_name].forgetting_assignment
+ forced_changes.delete(attr_name)
+ end
+
+ def original_value(attr_name)
+ attributes[attr_name].original_value
+ end
+
+ def force_change(attr_name)
+ forced_changes << attr_name.to_s
end
+ # TODO Change this to private once we've dropped Ruby 2.2 support.
+ # Workaround for Ruby 2.2 "private attribute?" warning.
protected
- attr_reader :attributes
+ attr_reader :attributes, :forced_changes
private
- def attr_names
- attributes.keys
- end
+ def attr_names
+ attributes.keys
+ end
end
class NullMutationTracker # :nodoc:
include Singleton
- def changed_values
+ def changed_values(*)
{}
end
- def changes
+ def changes(*)
{}
end
+ def change_to_attribute(attr_name)
+ end
+
+ def any_changes?(*)
+ false
+ end
+
def changed?(*)
false
end
@@ -66,5 +101,8 @@ module ActiveRecord
def forget_change(*)
end
+
+ def original_value(*)
+ end
end
end
diff --git a/activerecord/lib/active_record/attribute_set.rb b/activerecord/lib/active_record/attribute_set.rb
index 720d5f8b7c..66b278219a 100644
--- a/activerecord/lib/active_record/attribute_set.rb
+++ b/activerecord/lib/active_record/attribute_set.rb
@@ -1,9 +1,9 @@
-require 'active_record/attribute_set/builder'
-require 'active_record/attribute_set/yaml_encoder'
+require "active_record/attribute_set/builder"
+require "active_record/attribute_set/yaml_encoder"
module ActiveRecord
class AttributeSet # :nodoc:
- delegate :each_value, to: :attributes
+ delegate :each_value, :fetch, to: :attributes
def initialize(attributes)
@attributes = attributes
@@ -98,14 +98,16 @@ module ActiveRecord
attributes == other.attributes
end
+ # TODO Change this to private once we've dropped Ruby 2.2 support.
+ # Workaround for Ruby 2.2 "private attribute?" warning.
protected
- attr_reader :attributes
+ attr_reader :attributes
private
- def initialized_attributes
- attributes.select { |_, attr| attr.initialized? }
- end
+ def initialized_attributes
+ attributes.select { |_, attr| attr.initialized? }
+ end
end
end
diff --git a/activerecord/lib/active_record/attribute_set/builder.rb b/activerecord/lib/active_record/attribute_set/builder.rb
index 24a255efc1..2f624d32af 100644
--- a/activerecord/lib/active_record/attribute_set/builder.rb
+++ b/activerecord/lib/active_record/attribute_set/builder.rb
@@ -1,13 +1,14 @@
-require 'active_record/attribute'
+require "active_record/attribute"
module ActiveRecord
class AttributeSet # :nodoc:
class Builder # :nodoc:
- attr_reader :types, :always_initialized
+ attr_reader :types, :always_initialized, :default
- def initialize(types, always_initialized = nil)
+ def initialize(types, always_initialized = nil, &default)
@types = types
@always_initialized = always_initialized
+ @default = default
end
def build_from_database(values = {}, additional_types = {})
@@ -15,21 +16,22 @@ module ActiveRecord
values[always_initialized] = nil
end
- attributes = LazyAttributeHash.new(types, values, additional_types)
+ attributes = LazyAttributeHash.new(types, values, additional_types, &default)
AttributeSet.new(attributes)
end
end
end
class LazyAttributeHash # :nodoc:
- delegate :transform_values, :each_key, :each_value, to: :materialize
+ delegate :transform_values, :each_key, :each_value, :fetch, to: :materialize
- def initialize(types, values, additional_types)
+ def initialize(types, values, additional_types, &default)
@types = types
@values = values
@additional_types = additional_types
@materialized = false
@delegate_hash = {}
+ @default = default || proc {}
end
def key?(key)
@@ -76,33 +78,47 @@ module ActiveRecord
end
end
+ def marshal_dump
+ materialize
+ end
+
+ def marshal_load(delegate_hash)
+ @delegate_hash = delegate_hash
+ @types = {}
+ @values = {}
+ @additional_types = {}
+ @materialized = true
+ end
+
+ # TODO Change this to private once we've dropped Ruby 2.2 support.
+ # Workaround for Ruby 2.2 "private attribute?" warning.
protected
- attr_reader :types, :values, :additional_types, :delegate_hash
+ attr_reader :types, :values, :additional_types, :delegate_hash, :default
- def materialize
- unless @materialized
- values.each_key { |key| self[key] }
- types.each_key { |key| self[key] }
- unless frozen?
- @materialized = true
+ def materialize
+ unless @materialized
+ values.each_key { |key| self[key] }
+ types.each_key { |key| self[key] }
+ unless frozen?
+ @materialized = true
+ end
end
+ delegate_hash
end
- delegate_hash
- end
private
- def assign_default_value(name)
- type = additional_types.fetch(name, types[name])
- value_present = true
- value = values.fetch(name) { value_present = false }
+ def assign_default_value(name)
+ type = additional_types.fetch(name, types[name])
+ value_present = true
+ value = values.fetch(name) { value_present = false }
- if value_present
- delegate_hash[name] = Attribute.from_database(name, value, type)
- elsif types.key?(name)
- delegate_hash[name] = Attribute.uninitialized(name, type)
+ if value_present
+ delegate_hash[name] = Attribute.from_database(name, value, type)
+ elsif types.key?(name)
+ delegate_hash[name] = default.call(name) || Attribute.uninitialized(name, type)
+ end
end
- end
end
end
diff --git a/activerecord/lib/active_record/attribute_set/yaml_encoder.rb b/activerecord/lib/active_record/attribute_set/yaml_encoder.rb
index f9d527a5a3..899de14792 100644
--- a/activerecord/lib/active_record/attribute_set/yaml_encoder.rb
+++ b/activerecord/lib/active_record/attribute_set/yaml_encoder.rb
@@ -8,7 +8,7 @@ module ActiveRecord
end
def encode(attribute_set, coder)
- coder['concise_attributes'] = attribute_set.each_value.map do |attr|
+ coder["concise_attributes"] = attribute_set.each_value.map do |attr|
if attr.type.equal?(default_types[attr.name])
attr.with_type(nil)
else
@@ -18,10 +18,10 @@ module ActiveRecord
end
def decode(coder)
- if coder['attributes']
- coder['attributes']
+ if coder["attributes"]
+ coder["attributes"]
else
- attributes_hash = Hash[coder['concise_attributes'].map do |attr|
+ attributes_hash = Hash[coder["concise_attributes"].map do |attr|
if attr.type.nil?
attr = attr.with_type(default_types[attr.name])
end
@@ -31,9 +31,11 @@ module ActiveRecord
end
end
+ # TODO Change this to private once we've dropped Ruby 2.2 support.
+ # Workaround for Ruby 2.2 "private attribute?" warning.
protected
- attr_reader :default_types
+ attr_reader :default_types
end
end
end
diff --git a/activerecord/lib/active_record/attributes.rb b/activerecord/lib/active_record/attributes.rb
index 3211b6eaeb..75f5ba3a96 100644
--- a/activerecord/lib/active_record/attributes.rb
+++ b/activerecord/lib/active_record/attributes.rb
@@ -1,4 +1,4 @@
-require 'active_record/attribute/user_provided_default'
+require "active_record/attribute/user_provided_default"
module ActiveRecord
# See ActiveRecord::Attributes::ClassMethods for documentation
@@ -116,7 +116,7 @@ module ActiveRecord
# Users may also define their own custom types, as long as they respond
# to the methods defined on the value type. The method +deserialize+ or
# +cast+ will be called on your type object, with raw input from the
- # database or from your controllers. See ActiveRecord::Type::Value for the
+ # database or from your controllers. See ActiveModel::Type::Value for the
# expected API. It is recommended that your type objects inherit from an
# existing type, or from ActiveRecord::Type::Value
#
@@ -143,7 +143,7 @@ module ActiveRecord
# store_listing.price_in_cents # => 1000
#
# For more details on creating custom types, see the documentation for
- # ActiveRecord::Type::Value. For more details on registering your types
+ # ActiveModel::Type::Value. For more details on registering your types
# to be referenced by a symbol, see ActiveRecord::Type.register. You can
# also pass a type object directly, in place of a symbol.
#
@@ -190,8 +190,8 @@ module ActiveRecord
# The type of an attribute is given the opportunity to change how dirty
# tracking is performed. The methods +changed?+ and +changed_in_place?+
# will be called from ActiveModel::Dirty. See the documentation for those
- # methods in ActiveRecord::Type::Value for more details.
- def attribute(name, cast_type, **options)
+ # methods in ActiveModel::Type::Value for more details.
+ def attribute(name, cast_type = Type::Value.new, **options)
name = name.to_s
reload_schema_from_cache
@@ -242,24 +242,24 @@ module ActiveRecord
private
- NO_DEFAULT_PROVIDED = Object.new # :nodoc:
- private_constant :NO_DEFAULT_PROVIDED
+ NO_DEFAULT_PROVIDED = Object.new # :nodoc:
+ private_constant :NO_DEFAULT_PROVIDED
- def define_default_attribute(name, value, type, from_user:)
- if value == NO_DEFAULT_PROVIDED
- default_attribute = _default_attributes[name].with_type(type)
- elsif from_user
- default_attribute = Attribute::UserProvidedDefault.new(
- name,
- value,
- type,
- _default_attributes[name],
- )
- else
- default_attribute = Attribute.from_database(name, value, type)
+ def define_default_attribute(name, value, type, from_user:)
+ if value == NO_DEFAULT_PROVIDED
+ default_attribute = _default_attributes[name].with_type(type)
+ elsif from_user
+ default_attribute = Attribute::UserProvidedDefault.new(
+ name,
+ value,
+ type,
+ _default_attributes.fetch(name.to_s) { nil },
+ )
+ else
+ default_attribute = Attribute.from_database(name, value, type)
+ end
+ _default_attributes[name] = default_attribute
end
- _default_attributes[name] = default_attribute
- end
end
end
end
diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb
index 06c7482bf9..9d0b501862 100644
--- a/activerecord/lib/active_record/autosave_association.rb
+++ b/activerecord/lib/active_record/autosave_association.rb
@@ -154,10 +154,10 @@ module ActiveRecord
# Loop prevention for validation of associations
unless @_already_called[name]
begin
- @_already_called[name]=true
+ @_already_called[name] = true
result = instance_eval(&block)
ensure
- @_already_called[name]=false
+ @_already_called[name] = false
end
end
@@ -267,7 +267,7 @@ module ActiveRecord
# Returns whether or not this record has been changed in any way (including whether
# any of its nested autosave associations are likewise changed)
def changed_for_autosave?
- new_record? || changed? || marked_for_destruction? || nested_records_changed_for_autosave?
+ new_record? || has_changes_to_save? || marked_for_destruction? || nested_records_changed_for_autosave?
end
private
@@ -325,30 +325,24 @@ module ActiveRecord
# Returns whether or not the association is valid and applies any errors to
# the parent, <tt>self</tt>, if it wasn't. Skips any <tt>:autosave</tt>
# enabled records if they're marked_for_destruction? or destroyed.
- def association_valid?(reflection, record, index=nil)
+ def association_valid?(reflection, record, index = nil)
return true if record.destroyed? || (reflection.options[:autosave] && record.marked_for_destruction?)
validation_context = self.validation_context unless [:create, :update].include?(self.validation_context)
+
unless valid = record.valid?(validation_context)
if reflection.options[:autosave]
indexed_attribute = !index.nil? && (reflection.options[:index_errors] || ActiveRecord::Base.index_nested_attribute_errors)
record.errors.each do |attribute, message|
- if indexed_attribute
- attribute = "#{reflection.name}[#{index}].#{attribute}"
- else
- attribute = "#{reflection.name}.#{attribute}"
- end
+ attribute = normalize_reflection_attribute(indexed_attribute, reflection, index, attribute)
errors[attribute] << message
errors[attribute].uniq!
end
record.errors.details.each_key do |attribute|
- if indexed_attribute
- reflection_attribute = "#{reflection.name}[#{index}].#{attribute}"
- else
- reflection_attribute = "#{reflection.name}.#{attribute}"
- end
+ reflection_attribute =
+ normalize_reflection_attribute(indexed_attribute, reflection, index, attribute).to_sym
record.errors.details[attribute].each do |error|
errors.details[reflection_attribute] << error
@@ -362,6 +356,14 @@ module ActiveRecord
valid
end
+ def normalize_reflection_attribute(indexed_attribute, reflection, index, attribute)
+ if indexed_attribute
+ "#{reflection.name}[#{index}].#{attribute}"
+ else
+ "#{reflection.name}.#{attribute}"
+ end
+ end
+
# Is used as a before_save callback to check while saving a collection
# association whether or not the parent was a new record before saving.
def before_save_collection_association
@@ -381,6 +383,9 @@ module ActiveRecord
if association = association_instance_get(reflection.name)
autosave = reflection.options[:autosave]
+ # reconstruct the scope now that we know the owner's id
+ association.reset_scope if association.respond_to?(:reset_scope)
+
if records = associated_records_to_validate_or_save(association, @new_record_before_save, autosave)
if autosave
records_to_destroy = records.select(&:marked_for_destruction?)
@@ -400,15 +405,12 @@ module ActiveRecord
association.insert_record(record) unless reflection.nested?
end
elsif autosave
- saved = record.save(:validate => false)
+ saved = record.save(validate: false)
end
raise ActiveRecord::Rollback unless saved
end
end
-
- # reconstruct the scope now that we know the owner's id
- association.reset_scope if association.respond_to?(:reset_scope)
end
end
@@ -437,7 +439,7 @@ module ActiveRecord
record[reflection.foreign_key] = key
end
- saved = record.save(:validate => !autosave)
+ saved = record.save(validate: !autosave)
raise ActiveRecord::Rollback if !saved && autosave
saved
end
@@ -449,7 +451,7 @@ module ActiveRecord
def record_changed?(reflection, record, key)
record.new_record? ||
(record.has_attribute?(reflection.foreign_key) && record[reflection.foreign_key] != key) ||
- record.attribute_changed?(reflection.foreign_key)
+ record.will_save_change_to_attribute?(reflection.foreign_key)
end
# Saves the associated record if it's new or <tt>:autosave</tt> is enabled.
@@ -457,7 +459,9 @@ module ActiveRecord
# In addition, it will destroy the association if it was marked for destruction.
def save_belongs_to_association(reflection)
association = association_instance_get(reflection.name)
- record = association && association.load_target
+ return unless association && association.loaded? && !association.stale_target?
+
+ record = association.load_target
if record && !record.destroyed?
autosave = reflection.options[:autosave]
@@ -465,7 +469,7 @@ module ActiveRecord
self[reflection.foreign_key] = nil
record.destroy
elsif autosave != false
- saved = record.save(:validate => !autosave) if record.new_record? || (autosave && record.changed_for_autosave?)
+ saved = record.save(validate: !autosave) if record.new_record? || (autosave && record.changed_for_autosave?)
if association.updated?
association_id = record.send(reflection.options[:primary_key] || :id)
diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb
index 6a1a27ce41..ac1aa2df45 100644
--- a/activerecord/lib/active_record/base.rb
+++ b/activerecord/lib/active_record/base.rb
@@ -1,25 +1,26 @@
-require 'yaml'
-require 'active_support/benchmarkable'
-require 'active_support/dependencies'
-require 'active_support/descendants_tracker'
-require 'active_support/time'
-require 'active_support/core_ext/module/attribute_accessors'
-require 'active_support/core_ext/array/extract_options'
-require 'active_support/core_ext/hash/deep_merge'
-require 'active_support/core_ext/hash/slice'
-require 'active_support/core_ext/hash/transform_values'
-require 'active_support/core_ext/string/behavior'
-require 'active_support/core_ext/kernel/singleton_class'
-require 'active_support/core_ext/module/introspection'
-require 'active_support/core_ext/object/duplicable'
-require 'active_support/core_ext/class/subclasses'
-require 'active_record/attribute_decorators'
-require 'active_record/errors'
-require 'active_record/log_subscriber'
-require 'active_record/explain_subscriber'
-require 'active_record/relation/delegation'
-require 'active_record/attributes'
-require 'active_record/type_caster'
+require "yaml"
+require "active_support/benchmarkable"
+require "active_support/dependencies"
+require "active_support/descendants_tracker"
+require "active_support/time"
+require "active_support/core_ext/module/attribute_accessors"
+require "active_support/core_ext/array/extract_options"
+require "active_support/core_ext/hash/deep_merge"
+require "active_support/core_ext/hash/slice"
+require "active_support/core_ext/hash/transform_values"
+require "active_support/core_ext/string/behavior"
+require "active_support/core_ext/kernel/singleton_class"
+require "active_support/core_ext/module/introspection"
+require "active_support/core_ext/object/duplicable"
+require "active_support/core_ext/class/subclasses"
+require "active_record/attribute_decorators"
+require "active_record/define_callbacks"
+require "active_record/errors"
+require "active_record/log_subscriber"
+require "active_record/explain_subscriber"
+require "active_record/relation/delegation"
+require "active_record/attributes"
+require "active_record/type_caster"
module ActiveRecord #:nodoc:
# = Active Record
@@ -303,6 +304,7 @@ module ActiveRecord #:nodoc:
include AttributeDecorators
include Locking::Optimistic
include Locking::Pessimistic
+ include DefineCallbacks
include AttributeMethods
include Callbacks
include Timestamp
@@ -312,8 +314,8 @@ module ActiveRecord #:nodoc:
include NestedAttributes
include Aggregations
include Transactions
- include NoTouching
include TouchLater
+ include NoTouching
include Reflection
include Serialization
include Store
diff --git a/activerecord/lib/active_record/callbacks.rb b/activerecord/lib/active_record/callbacks.rb
index 95de6937af..be6720ddf3 100644
--- a/activerecord/lib/active_record/callbacks.rb
+++ b/activerecord/lib/active_record/callbacks.rb
@@ -265,17 +265,6 @@ module ActiveRecord
:before_destroy, :around_destroy, :after_destroy, :after_commit, :after_rollback
]
- module ClassMethods # :nodoc:
- include ActiveModel::Callbacks
- end
-
- included do
- include ActiveModel::Validations::Callbacks
-
- define_model_callbacks :initialize, :find, :touch, :only => :after
- define_model_callbacks :save, :create, :update, :destroy
- end
-
def destroy #:nodoc:
@_destroy_callback_already_called ||= false
return if @_destroy_callback_already_called
@@ -294,15 +283,15 @@ module ActiveRecord
private
- def create_or_update(*) #:nodoc:
+ def create_or_update(*)
_run_save_callbacks { super }
end
- def _create_record #:nodoc:
+ def _create_record
_run_create_callbacks { super }
end
- def _update_record(*) #:nodoc:
+ def _update_record(*)
_run_update_callbacks { super }
end
end
diff --git a/activerecord/lib/active_record/coders/yaml_column.rb b/activerecord/lib/active_record/coders/yaml_column.rb
index 2456b8ad8c..3a04a10fc9 100644
--- a/activerecord/lib/active_record/coders/yaml_column.rb
+++ b/activerecord/lib/active_record/coders/yaml_column.rb
@@ -1,9 +1,8 @@
-require 'yaml'
+require "yaml"
module ActiveRecord
module Coders # :nodoc:
class YAMLColumn # :nodoc:
-
attr_accessor :object_class
def initialize(object_class = Object)
@@ -20,7 +19,7 @@ module ActiveRecord
def load(yaml)
return object_class.new if object_class != Object && yaml.nil?
- return yaml unless yaml.is_a?(String) && yaml =~ /^---/
+ return yaml unless yaml.is_a?(String) && /^---/.match?(yaml)
obj = YAML.load(yaml)
assert_valid_value(obj)
@@ -38,13 +37,13 @@ module ActiveRecord
private
- def check_arity_of_constructor
- begin
- load(nil)
- rescue ArgumentError
- raise ArgumentError, "Cannot serialize #{object_class}. Classes passed to `serialize` must have a 0 argument constructor."
+ def check_arity_of_constructor
+ begin
+ load(nil)
+ rescue ArgumentError
+ raise ArgumentError, "Cannot serialize #{object_class}. Classes passed to `serialize` must have a 0 argument constructor."
+ end
end
- end
end
end
end
diff --git a/activerecord/lib/active_record/collection_cache_key.rb b/activerecord/lib/active_record/collection_cache_key.rb
index 8d41c0d799..43784b70e3 100644
--- a/activerecord/lib/active_record/collection_cache_key.rb
+++ b/activerecord/lib/active_record/collection_cache_key.rb
@@ -1,6 +1,5 @@
module ActiveRecord
module CollectionCacheKey
-
def collection_cache_key(collection = all, timestamp_column = :updated_at) # :nodoc:
query_signature = Digest::MD5.hexdigest(collection.to_sql)
key = "#{collection.model_name.cache_key}/query-#{query_signature}"
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
index 9b74c3a10f..5ec2fc073e 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
@@ -1,6 +1,6 @@
-require 'thread'
-require 'concurrent/map'
-require 'monitor'
+require "thread"
+require "concurrent/map"
+require "monitor"
module ActiveRecord
# Raised when a connection could not be obtained within the connection
@@ -69,7 +69,7 @@ module ActiveRecord
# threads, which can occur if a programmer forgets to close a
# connection at the end of a thread or a thread dies unexpectedly.
# Regardless of this setting, the Reaper will be invoked before every
- # blocking wait. (Default nil, which means don't schedule the Reaper).
+ # blocking wait. (Default +nil+, which means don't schedule the Reaper).
#
#--
# Synchronization policy:
@@ -116,7 +116,7 @@ module ActiveRecord
end
end
- # If +element+ is in the queue, remove and return it, or nil.
+ # If +element+ is in the queue, remove and return it, or +nil+.
def delete(element)
synchronize do
@queue.delete(element)
@@ -135,7 +135,7 @@ module ActiveRecord
# If +timeout+ is not given, remove and return the head the
# queue if the number of available elements is strictly
# greater than the number of threads currently waiting (that
- # is, don't jump ahead in line). Otherwise, return nil.
+ # is, don't jump ahead in line). Otherwise, return +nil+.
#
# If +timeout+ is given, block if there is no element
# available, waiting up to +timeout+ seconds for an element to
@@ -150,61 +150,61 @@ module ActiveRecord
private
- def internal_poll(timeout)
- no_wait_poll || (timeout && wait_poll(timeout))
- end
+ def internal_poll(timeout)
+ no_wait_poll || (timeout && wait_poll(timeout))
+ end
- def synchronize(&block)
- @lock.synchronize(&block)
- end
+ def synchronize(&block)
+ @lock.synchronize(&block)
+ end
- # Test if the queue currently contains any elements.
- def any?
- !@queue.empty?
- end
+ # Test if the queue currently contains any elements.
+ def any?
+ !@queue.empty?
+ end
- # A thread can remove an element from the queue without
- # waiting if and only if the number of currently available
- # connections is strictly greater than the number of waiting
- # threads.
- def can_remove_no_wait?
- @queue.size > @num_waiting
- end
+ # A thread can remove an element from the queue without
+ # waiting if and only if the number of currently available
+ # connections is strictly greater than the number of waiting
+ # threads.
+ def can_remove_no_wait?
+ @queue.size > @num_waiting
+ end
- # Removes and returns the head of the queue if possible, or nil.
- def remove
- @queue.shift
- end
+ # Removes and returns the head of the queue if possible, or +nil+.
+ def remove
+ @queue.shift
+ end
- # Remove and return the head the queue if the number of
- # available elements is strictly greater than the number of
- # threads currently waiting. Otherwise, return nil.
- def no_wait_poll
- remove if can_remove_no_wait?
- end
+ # Remove and return the head the queue if the number of
+ # available elements is strictly greater than the number of
+ # threads currently waiting. Otherwise, return +nil+.
+ def no_wait_poll
+ remove if can_remove_no_wait?
+ end
- # Waits on the queue up to +timeout+ seconds, then removes and
- # returns the head of the queue.
- def wait_poll(timeout)
- @num_waiting += 1
+ # Waits on the queue up to +timeout+ seconds, then removes and
+ # returns the head of the queue.
+ def wait_poll(timeout)
+ @num_waiting += 1
- t0 = Time.now
- elapsed = 0
- loop do
- @cond.wait(timeout - elapsed)
+ t0 = Time.now
+ elapsed = 0
+ loop do
+ @cond.wait(timeout - elapsed)
- return remove if any?
+ return remove if any?
- elapsed = Time.now - t0
- if elapsed >= timeout
- msg = 'could not obtain a connection from the pool within %0.3f seconds (waited %0.3f seconds); all pooled connections were in use' %
- [timeout, elapsed]
- raise ConnectionTimeoutError, msg
+ elapsed = Time.now - t0
+ if elapsed >= timeout
+ msg = "could not obtain a connection from the pool within %0.3f seconds (waited %0.3f seconds); all pooled connections were in use" %
+ [timeout, elapsed]
+ raise ConnectionTimeoutError, msg
+ end
end
+ ensure
+ @num_waiting -= 1
end
- ensure
- @num_waiting -= 1
- end
end
# Adds the ability to turn a basic fair FIFO queue into one
@@ -274,15 +274,15 @@ module ActiveRecord
include BiasableQueue
private
- def internal_poll(timeout)
- conn = super
- conn.lease if conn
- conn
- end
+ def internal_poll(timeout)
+ conn = super
+ conn.lease if conn
+ conn
+ end
end
# Every +frequency+ seconds, the reaper will call +reap+ on +pool+.
- # A reaper instantiated with a nil frequency will never reap the
+ # A reaper instantiated with a +nil+ frequency will never reap the
# connection pool.
#
# Configure the frequency by setting "reaping_frequency" in your
@@ -307,6 +307,7 @@ module ActiveRecord
end
include MonitorMixin
+ include QueryCache::ConnectionPoolConfiguration
attr_accessor :automatic_reconnect, :checkout_timeout, :schema_cache
attr_reader :spec, :connections, :size, :reaper
@@ -339,7 +340,7 @@ module ActiveRecord
# that case +conn.owner+ attr should be consulted.
# Access and modification of +@thread_cached_conns+ does not require
# synchronization.
- @thread_cached_conns = Concurrent::Map.new(:initial_capacity => @size)
+ @thread_cached_conns = Concurrent::Map.new(initial_capacity: @size)
@connections = []
@automatic_reconnect = true
@@ -349,8 +350,7 @@ module ActiveRecord
# currently in the process of independently establishing connections to the DB.
@now_connecting = 0
- # A boolean toggle that allows/disallows new connections.
- @new_cons_enabled = true
+ @threads_blocking_new_connections = 0
@available = ConnectionLeasingQueue.new self
end
@@ -445,8 +445,6 @@ module ActiveRecord
# connections in the pool within a timeout interval (default duration is
# <tt>spec.config[:checkout_timeout] * 2</tt> seconds).
def clear_reloadable_connections(raise_on_acquisition_timeout = true)
- num_new_conns_required = 0
-
with_exclusively_acquired_all_connections(raise_on_acquisition_timeout) do
synchronize do
@connections.each do |conn|
@@ -457,24 +455,9 @@ module ActiveRecord
conn.disconnect! if conn.requires_reloading?
end
@connections.delete_if(&:requires_reloading?)
-
@available.clear
-
- if @connections.size < @size
- # because of the pruning done by this method, we might be running
- # low on connections, while threads stuck in queue are helpless
- # (not being able to establish new connections for themselves),
- # see also more detailed explanation in +remove+
- num_new_conns_required = num_waiting_in_queue - @connections.size
- end
-
- @connections.each do |conn|
- @available.add conn
- end
end
end
-
- bulk_make_new_connections(num_new_conns_required) if num_new_conns_required > 0
end
# Clears the cache which maps classes and re-connects connections that
@@ -581,206 +564,243 @@ module ActiveRecord
@available.num_waiting
end
- private
- #--
- # this is unfortunately not concurrent
- def bulk_make_new_connections(num_new_conns_needed)
- num_new_conns_needed.times do
- # try_to_checkout_new_connection will not exceed pool's @size limit
- if new_conn = try_to_checkout_new_connection
- # make the new_conn available to the starving threads stuck @available Queue
- checkin(new_conn)
- end
+ # Return connection pool's usage statistic
+ # Example:
+ #
+ # ActiveRecord::Base.connection_pool.stat # => { size: 15, connections: 1, busy: 1, dead: 0, idle: 0, waiting: 0, checkout_timeout: 5 }
+ def stat
+ synchronize do
+ {
+ size: size,
+ connections: @connections.size,
+ busy: @connections.count { |c| c.in_use? && c.owner.alive? },
+ dead: @connections.count { |c| c.in_use? && !c.owner.alive? },
+ idle: @connections.count { |c| !c.in_use? },
+ waiting: num_waiting_in_queue,
+ checkout_timeout: checkout_timeout
+ }
end
end
- #--
- # From the discussion on GitHub:
- # https://github.com/rails/rails/pull/14938#commitcomment-6601951
- # This hook-in method allows for easier monkey-patching fixes needed by
- # JRuby users that use Fibers.
- def connection_cache_key(thread)
- thread
- end
+ private
+ #--
+ # this is unfortunately not concurrent
+ def bulk_make_new_connections(num_new_conns_needed)
+ num_new_conns_needed.times do
+ # try_to_checkout_new_connection will not exceed pool's @size limit
+ if new_conn = try_to_checkout_new_connection
+ # make the new_conn available to the starving threads stuck @available Queue
+ checkin(new_conn)
+ end
+ end
+ end
- # Take control of all existing connections so a "group" action such as
- # reload/disconnect can be performed safely. It is no longer enough to
- # wrap it in +synchronize+ because some pool's actions are allowed
- # to be performed outside of the main +synchronize+ block.
- def with_exclusively_acquired_all_connections(raise_on_acquisition_timeout = true)
- with_new_connections_blocked do
- attempt_to_checkout_all_existing_connections(raise_on_acquisition_timeout)
- yield
+ #--
+ # From the discussion on GitHub:
+ # https://github.com/rails/rails/pull/14938#commitcomment-6601951
+ # This hook-in method allows for easier monkey-patching fixes needed by
+ # JRuby users that use Fibers.
+ def connection_cache_key(thread)
+ thread
end
- end
- def attempt_to_checkout_all_existing_connections(raise_on_acquisition_timeout = true)
- collected_conns = synchronize do
- # account for our own connections
- @connections.select {|conn| conn.owner == Thread.current}
+ # Take control of all existing connections so a "group" action such as
+ # reload/disconnect can be performed safely. It is no longer enough to
+ # wrap it in +synchronize+ because some pool's actions are allowed
+ # to be performed outside of the main +synchronize+ block.
+ def with_exclusively_acquired_all_connections(raise_on_acquisition_timeout = true)
+ with_new_connections_blocked do
+ attempt_to_checkout_all_existing_connections(raise_on_acquisition_timeout)
+ yield
+ end
end
- newly_checked_out = []
- timeout_time = Time.now + (@checkout_timeout * 2)
+ def attempt_to_checkout_all_existing_connections(raise_on_acquisition_timeout = true)
+ collected_conns = synchronize do
+ # account for our own connections
+ @connections.select { |conn| conn.owner == Thread.current }
+ end
+
+ newly_checked_out = []
+ timeout_time = Time.now + (@checkout_timeout * 2)
- @available.with_a_bias_for(Thread.current) do
- loop do
- synchronize do
- return if collected_conns.size == @connections.size && @now_connecting == 0
- remaining_timeout = timeout_time - Time.now
- remaining_timeout = 0 if remaining_timeout < 0
- conn = checkout_for_exclusive_access(remaining_timeout)
- collected_conns << conn
- newly_checked_out << conn
+ @available.with_a_bias_for(Thread.current) do
+ loop do
+ synchronize do
+ return if collected_conns.size == @connections.size && @now_connecting == 0
+ remaining_timeout = timeout_time - Time.now
+ remaining_timeout = 0 if remaining_timeout < 0
+ conn = checkout_for_exclusive_access(remaining_timeout)
+ collected_conns << conn
+ newly_checked_out << conn
+ end
end
end
- end
- rescue ExclusiveConnectionTimeoutError
- # <tt>raise_on_acquisition_timeout == false</tt> means we are directed to ignore any
- # timeouts and are expected to just give up: we've obtained as many connections
- # as possible, note that in a case like that we don't return any of the
- # +newly_checked_out+ connections.
-
- if raise_on_acquisition_timeout
+ rescue ExclusiveConnectionTimeoutError
+ # <tt>raise_on_acquisition_timeout == false</tt> means we are directed to ignore any
+ # timeouts and are expected to just give up: we've obtained as many connections
+ # as possible, note that in a case like that we don't return any of the
+ # +newly_checked_out+ connections.
+
+ if raise_on_acquisition_timeout
+ release_newly_checked_out = true
+ raise
+ end
+ rescue Exception # if something else went wrong
+ # this can't be a "naked" rescue, because we have should return conns
+ # even for non-StandardErrors
release_newly_checked_out = true
raise
+ ensure
+ if release_newly_checked_out && newly_checked_out
+ # releasing only those conns that were checked out in this method, conns
+ # checked outside this method (before it was called) are not for us to release
+ newly_checked_out.each { |conn| checkin(conn) }
+ end
end
- rescue Exception # if something else went wrong
- # this can't be a "naked" rescue, because we have should return conns
- # even for non-StandardErrors
- release_newly_checked_out = true
- raise
- ensure
- if release_newly_checked_out && newly_checked_out
- # releasing only those conns that were checked out in this method, conns
- # checked outside this method (before it was called) are not for us to release
- newly_checked_out.each {|conn| checkin(conn)}
- end
- end
-
- #--
- # Must be called in a synchronize block.
- def checkout_for_exclusive_access(checkout_timeout)
- checkout(checkout_timeout)
- rescue ConnectionTimeoutError
- # this block can't be easily moved into attempt_to_checkout_all_existing_connections's
- # rescue block, because doing so would put it outside of synchronize section, without
- # being in a critical section thread_report might become inaccurate
- msg = "could not obtain ownership of all database connections in #{checkout_timeout} seconds"
- thread_report = []
- @connections.each do |conn|
- unless conn.owner == Thread.current
- thread_report << "#{conn} is owned by #{conn.owner}"
+ #--
+ # Must be called in a synchronize block.
+ def checkout_for_exclusive_access(checkout_timeout)
+ checkout(checkout_timeout)
+ rescue ConnectionTimeoutError
+ # this block can't be easily moved into attempt_to_checkout_all_existing_connections's
+ # rescue block, because doing so would put it outside of synchronize section, without
+ # being in a critical section thread_report might become inaccurate
+ msg = "could not obtain ownership of all database connections in #{checkout_timeout} seconds"
+
+ thread_report = []
+ @connections.each do |conn|
+ unless conn.owner == Thread.current
+ thread_report << "#{conn} is owned by #{conn.owner}"
+ end
end
+
+ msg << " (#{thread_report.join(', ')})" if thread_report.any?
+
+ raise ExclusiveConnectionTimeoutError, msg
end
- msg << " (#{thread_report.join(', ')})" if thread_report.any?
+ def with_new_connections_blocked
+ synchronize do
+ @threads_blocking_new_connections += 1
+ end
- raise ExclusiveConnectionTimeoutError, msg
- end
+ yield
+ ensure
+ num_new_conns_required = 0
- def with_new_connections_blocked
- previous_value = nil
- synchronize do
- previous_value, @new_cons_enabled = @new_cons_enabled, false
+ synchronize do
+ @threads_blocking_new_connections -= 1
+
+ if @threads_blocking_new_connections.zero?
+ @available.clear
+
+ num_new_conns_required = num_waiting_in_queue
+
+ @connections.each do |conn|
+ next if conn.in_use?
+
+ @available.add conn
+ num_new_conns_required -= 1
+ end
+ end
+ end
+
+ bulk_make_new_connections(num_new_conns_required) if num_new_conns_required > 0
end
- yield
- ensure
- synchronize { @new_cons_enabled = previous_value }
- end
- # Acquire a connection by one of 1) immediately removing one
- # from the queue of available connections, 2) creating a new
- # connection if the pool is not at capacity, 3) waiting on the
- # queue for a connection to become available.
- #
- # Raises:
- # - ActiveRecord::ConnectionTimeoutError if a connection could not be acquired
- #
- #--
- # Implementation detail: the connection returned by +acquire_connection+
- # will already be "+connection.lease+ -ed" to the current thread.
- def acquire_connection(checkout_timeout)
- # NOTE: we rely on +@available.poll+ and +try_to_checkout_new_connection+ to
- # +conn.lease+ the returned connection (and to do this in a +synchronized+
- # section). This is not the cleanest implementation, as ideally we would
- # <tt>synchronize { conn.lease }</tt> in this method, but by leaving it to +@available.poll+
- # and +try_to_checkout_new_connection+ we can piggyback on +synchronize+ sections
- # of the said methods and avoid an additional +synchronize+ overhead.
- if conn = @available.poll || try_to_checkout_new_connection
- conn
- else
- reap
- @available.poll(checkout_timeout)
+ # Acquire a connection by one of 1) immediately removing one
+ # from the queue of available connections, 2) creating a new
+ # connection if the pool is not at capacity, 3) waiting on the
+ # queue for a connection to become available.
+ #
+ # Raises:
+ # - ActiveRecord::ConnectionTimeoutError if a connection could not be acquired
+ #
+ #--
+ # Implementation detail: the connection returned by +acquire_connection+
+ # will already be "+connection.lease+ -ed" to the current thread.
+ def acquire_connection(checkout_timeout)
+ # NOTE: we rely on +@available.poll+ and +try_to_checkout_new_connection+ to
+ # +conn.lease+ the returned connection (and to do this in a +synchronized+
+ # section). This is not the cleanest implementation, as ideally we would
+ # <tt>synchronize { conn.lease }</tt> in this method, but by leaving it to +@available.poll+
+ # and +try_to_checkout_new_connection+ we can piggyback on +synchronize+ sections
+ # of the said methods and avoid an additional +synchronize+ overhead.
+ if conn = @available.poll || try_to_checkout_new_connection
+ conn
+ else
+ reap
+ @available.poll(checkout_timeout)
+ end
end
- end
- #--
- # if owner_thread param is omitted, this must be called in synchronize block
- def remove_connection_from_thread_cache(conn, owner_thread = conn.owner)
- @thread_cached_conns.delete_pair(connection_cache_key(owner_thread), conn)
- end
- alias_method :release, :remove_connection_from_thread_cache
+ #--
+ # if owner_thread param is omitted, this must be called in synchronize block
+ def remove_connection_from_thread_cache(conn, owner_thread = conn.owner)
+ @thread_cached_conns.delete_pair(connection_cache_key(owner_thread), conn)
+ end
+ alias_method :release, :remove_connection_from_thread_cache
- def new_connection
- Base.send(spec.adapter_method, spec.config).tap do |conn|
- conn.schema_cache = schema_cache.dup if schema_cache
+ def new_connection
+ Base.send(spec.adapter_method, spec.config).tap do |conn|
+ conn.schema_cache = schema_cache.dup if schema_cache
+ end
end
- end
- # If the pool is not at a +@size+ limit, establish new connection. Connecting
- # to the DB is done outside main synchronized section.
- #--
- # Implementation constraint: a newly established connection returned by this
- # method must be in the +.leased+ state.
- def try_to_checkout_new_connection
- # first in synchronized section check if establishing new conns is allowed
- # and increment @now_connecting, to prevent overstepping this pool's @size
- # constraint
- do_checkout = synchronize do
- if @new_cons_enabled && (@connections.size + @now_connecting) < @size
- @now_connecting += 1
- end
- end
- if do_checkout
- begin
- # if successfully incremented @now_connecting establish new connection
- # outside of synchronized section
- conn = checkout_new_connection
- ensure
- synchronize do
- if conn
- adopt_connection(conn)
- # returned conn needs to be already leased
- conn.lease
+ # If the pool is not at a +@size+ limit, establish new connection. Connecting
+ # to the DB is done outside main synchronized section.
+ #--
+ # Implementation constraint: a newly established connection returned by this
+ # method must be in the +.leased+ state.
+ def try_to_checkout_new_connection
+ # first in synchronized section check if establishing new conns is allowed
+ # and increment @now_connecting, to prevent overstepping this pool's @size
+ # constraint
+ do_checkout = synchronize do
+ if @threads_blocking_new_connections.zero? && (@connections.size + @now_connecting) < @size
+ @now_connecting += 1
+ end
+ end
+ if do_checkout
+ begin
+ # if successfully incremented @now_connecting establish new connection
+ # outside of synchronized section
+ conn = checkout_new_connection
+ ensure
+ synchronize do
+ if conn
+ adopt_connection(conn)
+ # returned conn needs to be already leased
+ conn.lease
+ end
+ @now_connecting -= 1
end
- @now_connecting -= 1
end
end
end
- end
- def adopt_connection(conn)
- conn.pool = self
- @connections << conn
- end
+ def adopt_connection(conn)
+ conn.pool = self
+ @connections << conn
+ end
- def checkout_new_connection
- raise ConnectionNotEstablished unless @automatic_reconnect
- new_connection
- end
+ def checkout_new_connection
+ raise ConnectionNotEstablished unless @automatic_reconnect
+ new_connection
+ end
- def checkout_and_verify(c)
- c._run_checkout_callbacks do
- c.verify!
+ def checkout_and_verify(c)
+ c._run_checkout_callbacks do
+ c.verify!
+ end
+ c
+ rescue
+ remove c
+ c.disconnect!
+ raise
end
- c
- rescue
- remove c
- c.disconnect!
- raise
- end
end
# ConnectionHandler is a collection of ConnectionPool objects. It is used
@@ -833,8 +853,8 @@ module ActiveRecord
class ConnectionHandler
def initialize
# These caches are keyed by spec.name (ConnectionSpecification#name).
- @owner_to_pool = Concurrent::Map.new(:initial_capacity => 2) do |h,k|
- h[k] = Concurrent::Map.new(:initial_capacity => 2)
+ @owner_to_pool = Concurrent::Map.new(initial_capacity: 2) do |h, k|
+ h[k] = Concurrent::Map.new(initial_capacity: 2)
end
end
@@ -848,7 +868,21 @@ module ActiveRecord
spec = resolver.spec(config)
remove_connection(spec.name)
- owner_to_pool[spec.name] = ConnectionAdapters::ConnectionPool.new(spec)
+
+ message_bus = ActiveSupport::Notifications.instrumenter
+ payload = {
+ connection_id: object_id
+ }
+ if spec
+ payload[:spec_name] = spec.name
+ payload[:config] = spec.config
+ end
+
+ message_bus.instrument("!connection.active_record", payload) do
+ owner_to_pool[spec.name] = ConnectionAdapters::ConnectionPool.new(spec)
+ end
+
+ owner_to_pool[spec.name]
end
# Returns true if there are any active connections among the connection
@@ -881,7 +915,7 @@ module ActiveRecord
# for (not necessarily the current class).
def retrieve_connection(spec_name) #:nodoc:
pool = retrieve_connection_pool(spec_name)
- raise ConnectionNotEstablished, "No connection pool with id '#{spec_name}' found." unless pool
+ raise ConnectionNotEstablished, "No connection pool with '#{spec_name}' found." unless pool
conn = pool.connection
raise ConnectionNotEstablished, "No connection for '#{spec_name}' in connection pool" unless conn
conn
@@ -928,14 +962,14 @@ module ActiveRecord
private
- def owner_to_pool
- @owner_to_pool[Process.pid]
- end
+ def owner_to_pool
+ @owner_to_pool[Process.pid]
+ end
- def pool_from_any_process_for(spec_name)
- owner_to_pool = @owner_to_pool.values.find { |v| v[spec_name] }
- owner_to_pool && owner_to_pool[spec_name]
- end
+ def pool_from_any_process_for(spec_name)
+ owner_to_pool = @owner_to_pool.values.find { |v| v[spec_name] }
+ owner_to_pool && owner_to_pool[spec_name]
+ end
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/database_limits.rb b/activerecord/lib/active_record/connection_adapters/abstract/database_limits.rb
index 6711049588..407e019326 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/database_limits.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/database_limits.rb
@@ -1,7 +1,6 @@
module ActiveRecord
module ConnectionAdapters # :nodoc:
module DatabaseLimits
-
# Returns the maximum length of a table alias.
def table_alias_length
255
@@ -47,7 +46,7 @@ module ActiveRecord
end
# Returns the maximum number of elements in an IN (x,y,z) clause.
- # nil means no limit.
+ # +nil+ means no limit.
def in_clause_length
nil
end
@@ -61,7 +60,6 @@ module ActiveRecord
def joins_per_query
256
end
-
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
index 507a925d32..2c352819fb 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
@@ -10,19 +10,20 @@ module ActiveRecord
def to_sql(arel, binds = [])
if arel.respond_to?(:ast)
collected = visitor.accept(arel.ast, collector)
- collected.compile(binds.dup, self)
+ collected.compile(binds, self).freeze
else
- arel
+ arel.dup.freeze
end
end
# This is used in the StatementCache object. It returns an object that
# can be used to query the database repeatedly.
- def cacheable_query(arel) # :nodoc:
+ def cacheable_query(klass, arel) # :nodoc:
+ collected = visitor.accept(arel.ast, collector)
if prepared_statements
- ActiveRecord::StatementCache.query visitor, arel.ast
+ klass.query(collected.value)
else
- ActiveRecord::StatementCache.partial_query visitor, arel.ast, collector
+ klass.partial_query(collected.value)
end
end
@@ -81,21 +82,22 @@ module ActiveRecord
# Executes +sql+ statement in the context of this connection using
# +binds+ as the bind substitutes. +name+ is logged along with
# the executed +sql+ statement.
- def exec_query(sql, name = 'SQL', binds = [], prepare: false)
+ def exec_query(sql, name = "SQL", binds = [], prepare: false)
raise NotImplementedError
end
# Executes insert +sql+ statement in the context of this connection using
# +binds+ as the bind substitutes. +name+ is logged along with
# the executed +sql+ statement.
- def exec_insert(sql, name, binds, pk = nil, sequence_name = nil)
+ def exec_insert(sql, name = nil, binds = [], pk = nil, sequence_name = nil)
+ sql, binds = sql_for_insert(sql, pk, nil, sequence_name, binds)
exec_query(sql, name, binds)
end
# Executes delete +sql+ statement in the context of this connection using
# +binds+ as the bind substitutes. +name+ is logged along with
# the executed +sql+ statement.
- def exec_delete(sql, name, binds)
+ def exec_delete(sql, name = nil, binds = [])
exec_query(sql, name, binds)
end
@@ -107,40 +109,33 @@ module ActiveRecord
# Executes update +sql+ statement in the context of this connection using
# +binds+ as the bind substitutes. +name+ is logged along with
# the executed +sql+ statement.
- def exec_update(sql, name, binds)
+ def exec_update(sql, name = nil, binds = [])
exec_query(sql, name, binds)
end
# Executes an INSERT query and returns the new record's ID
#
- # +id_value+ will be returned unless the value is nil, in
+ # +id_value+ will be returned unless the value is +nil+, in
# which case the database will attempt to calculate the last inserted
# id and return that value.
#
# If the next id was calculated in advance (as in Oracle), it should be
# passed in as +id_value+.
def insert(arel, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = [])
- sql, binds, pk, sequence_name = sql_for_insert(to_sql(arel, binds), pk, id_value, sequence_name, binds)
- value = exec_insert(sql, name, binds, pk, sequence_name)
+ value = exec_insert(to_sql(arel, binds), name, binds, pk, sequence_name)
id_value || last_inserted_id(value)
end
alias create insert
- alias insert_sql insert
- deprecate insert_sql: :insert
# Executes the update statement and returns the number of rows affected.
def update(arel, name = nil, binds = [])
exec_update(to_sql(arel, binds), name, binds)
end
- alias update_sql update
- deprecate update_sql: :update
# Executes the delete statement and returns the number of rows affected.
def delete(arel, name = nil, binds = [])
exec_delete(to_sql(arel, binds), name, binds)
end
- alias delete_sql delete
- deprecate delete_sql: :delete
# Returns +true+ when the connection adapter supports prepared statement
# caching, otherwise returns +false+
@@ -244,7 +239,7 @@ module ActiveRecord
end
def reset_transaction #:nodoc:
- @transaction_manager = TransactionManager.new(self)
+ @transaction_manager = ConnectionAdapters::TransactionManager.new(self)
end
# Register a record with the current transaction so that its after_commit and after_rollback callbacks
@@ -315,7 +310,7 @@ module ActiveRecord
end
end
key_list = fixture.keys.map { |name| quote_column_name(name) }
- value_list = prepare_binds_for_database(binds).map do |value|
+ value_list = binds.map(&:value_for_database).map do |value|
begin
quote(value)
rescue TypeError
@@ -323,7 +318,7 @@ module ActiveRecord
end
end
- execute "INSERT INTO #{quote_table_name(table_name)} (#{key_list.join(', ')}) VALUES (#{value_list.join(', ')})", 'Fixture Insert'
+ execute "INSERT INTO #{quote_table_name(table_name)} (#{key_list.join(', ')}) VALUES (#{value_list.join(', ')})", "Fixture Insert"
end
def empty_insert_statement_value
@@ -333,17 +328,12 @@ module ActiveRecord
# Sanitizes the given LIMIT parameter in order to prevent SQL injection.
#
# The +limit+ may be anything that can evaluate to a string via #to_s. It
- # should look like an integer, or a comma-delimited list of integers, or
- # an Arel SQL literal.
+ # should look like an integer, or an Arel SQL literal.
#
# Returns Integer and Arel::Nodes::SqlLiteral limits as is.
- # Returns the sanitized limit parameter, either as an integer, or as a
- # string which contains a comma-delimited list of integers.
def sanitize_limit(limit)
if limit.is_a?(Integer) || limit.is_a?(Arel::Nodes::SqlLiteral)
limit
- elsif limit.to_s.include?(',')
- Arel.sql limit.to_s.split(',').map{ |i| Integer(i) }.join(',')
else
Integer(limit)
end
@@ -359,7 +349,7 @@ module ActiveRecord
end
alias join_to_delete join_to_update
- protected
+ private
# Returns a subquery for the given key using the join information.
def subquery_for(key, select)
@@ -378,7 +368,7 @@ module ActiveRecord
end
def sql_for_insert(sql, pk, id_value, sequence_name, binds)
- [sql, binds, pk, sequence_name]
+ [sql, binds]
end
def last_inserted_id(result)
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb b/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb
index 0bdfd4f900..7eab7de5d3 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb
@@ -4,6 +4,9 @@ module ActiveRecord
class << self
def included(base) #:nodoc:
dirties_query_cache base, :insert, :update, :delete, :rollback_to_savepoint, :rollback_db_transaction
+
+ base.set_callback :checkout, :after, :configure_query_cache!
+ base.set_callback :checkin, :after, :disable_query_cache!
end
def dirties_query_cache(base, *method_names)
@@ -18,11 +21,32 @@ module ActiveRecord
end
end
+ module ConnectionPoolConfiguration
+ def initialize(*)
+ super
+ @query_cache_enabled = Concurrent::Map.new { false }
+ end
+
+ def enable_query_cache!
+ @query_cache_enabled[connection_cache_key(Thread.current)] = true
+ connection.enable_query_cache! if active_connection?
+ end
+
+ def disable_query_cache!
+ @query_cache_enabled.delete connection_cache_key(Thread.current)
+ connection.disable_query_cache! if active_connection?
+ end
+
+ def query_cache_enabled
+ @query_cache_enabled[connection_cache_key(Thread.current)]
+ end
+ end
+
attr_reader :query_cache, :query_cache_enabled
def initialize(*)
super
- @query_cache = Hash.new { |h,sql| h[sql] = {} }
+ @query_cache = Hash.new { |h, sql| h[sql] = {} }
@query_cache_enabled = false
end
@@ -41,6 +65,7 @@ module ActiveRecord
def disable_query_cache!
@query_cache_enabled = false
+ clear_query_cache
end
# Disable the query cache within the block.
@@ -65,7 +90,7 @@ module ActiveRecord
if @query_cache_enabled && !locked?(arel)
arel, binds = binds_from_relation arel, binds
sql = to_sql(arel, binds)
- cache_sql(sql, binds) { super(sql, name, binds, preparable: preparable) }
+ cache_sql(sql, name, binds) { super(sql, name, binds, preparable: preparable) }
else
super
end
@@ -73,23 +98,33 @@ module ActiveRecord
private
- def cache_sql(sql, binds)
- result =
- if @query_cache[sql].key?(binds)
- ActiveSupport::Notifications.instrument("sql.active_record",
- :sql => sql, :binds => binds, :name => "CACHE", :connection_id => object_id)
- @query_cache[sql][binds]
- else
- @query_cache[sql][binds] = yield
- end
- result.dup
- end
+ def cache_sql(sql, name, binds)
+ result =
+ if @query_cache[sql].key?(binds)
+ ActiveSupport::Notifications.instrument(
+ "sql.active_record",
+ sql: sql,
+ binds: binds,
+ name: name,
+ connection_id: object_id,
+ cached: true,
+ )
+ @query_cache[sql][binds]
+ else
+ @query_cache[sql][binds] = yield
+ end
+ result.dup
+ end
- # If arel is locked this is a SELECT ... FOR UPDATE or somesuch. Such
- # queries should not be cached.
- def locked?(arel)
- arel.respond_to?(:locked) && arel.locked
- end
+ # If arel is locked this is a SELECT ... FOR UPDATE or somesuch. Such
+ # queries should not be cached.
+ def locked?(arel)
+ arel.respond_to?(:locked) && arel.locked
+ end
+
+ def configure_query_cache!
+ enable_query_cache! if pool.query_cache_enabled
+ end
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
index 860ef17dca..0c6bc16e6f 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
@@ -1,24 +1,14 @@
-require 'active_support/core_ext/big_decimal/conversions'
+require "active_support/core_ext/big_decimal/conversions"
module ActiveRecord
module ConnectionAdapters # :nodoc:
module Quoting
# Quotes the column value to help prevent
# {SQL injection attacks}[http://en.wikipedia.org/wiki/SQL_injection].
- def quote(value, column = nil)
+ def quote(value)
# records are quoted as their primary key
return value.quoted_id if value.respond_to?(:quoted_id)
- if column
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
- Passing a column to `quote` has been deprecated. It is only used
- for type casting, which should be handled elsewhere. See
- https://github.com/rails/arel/commit/6160bfbda1d1781c3b08a33ec4955f170e95be11
- for more information.
- MSG
- value = type_cast_from_column(column, value)
- end
-
_quote(value)
end
@@ -112,19 +102,19 @@ module ActiveRecord
end
def quoted_true
- "'t'"
+ "'t'".freeze
end
def unquoted_true
- 't'
+ "t".freeze
end
def quoted_false
- "'f'"
+ "'f'".freeze
end
def unquoted_false
- 'f'
+ "f".freeze
end
# Quote date/time values for use in SQL input. Includes microseconds
@@ -147,52 +137,52 @@ module ActiveRecord
end
def quoted_time(value) # :nodoc:
- quoted_date(value).sub(/\A2000-01-01 /, '')
- end
-
- def prepare_binds_for_database(binds) # :nodoc:
- binds.map(&:value_for_database)
+ quoted_date(value).sub(/\A2000-01-01 /, "")
end
private
- def types_which_need_no_typecasting
- [nil, Numeric, String]
- end
-
- def _quote(value)
- case value
- when String, ActiveSupport::Multibyte::Chars, Type::Binary::Data
- "'#{quote_string(value.to_s)}'"
- when true then quoted_true
- when false then quoted_false
- when nil then "NULL"
- # BigDecimals need to be put in a non-normalized form and quoted.
- when BigDecimal then value.to_s('F')
- when Numeric, ActiveSupport::Duration then value.to_s
- when Type::Time::Value then "'#{quoted_time(value)}'"
- when Date, Time then "'#{quoted_date(value)}'"
- when Symbol then "'#{quote_string(value.to_s)}'"
- when Class then "'#{value}'"
- else raise TypeError, "can't quote #{value.class.name}"
+ def type_casted_binds(binds)
+ binds.map { |attr| type_cast(attr.value_for_database) }
end
- end
- def _type_cast(value)
- case value
- when Symbol, ActiveSupport::Multibyte::Chars, Type::Binary::Data
- value.to_s
- when true then unquoted_true
- when false then unquoted_false
- # BigDecimals need to be put in a non-normalized form and quoted.
- when BigDecimal then value.to_s('F')
- when Type::Time::Value then quoted_time(value)
- when Date, Time then quoted_date(value)
- when *types_which_need_no_typecasting
- value
- else raise TypeError
+ def types_which_need_no_typecasting
+ [nil, Numeric, String]
+ end
+
+ def _quote(value)
+ case value
+ when String, ActiveSupport::Multibyte::Chars, Type::Binary::Data
+ "'#{quote_string(value.to_s)}'"
+ when true then quoted_true
+ when false then quoted_false
+ when nil then "NULL"
+ # BigDecimals need to be put in a non-normalized form and quoted.
+ when BigDecimal then value.to_s("F")
+ when Numeric, ActiveSupport::Duration then value.to_s
+ when Type::Time::Value then "'#{quoted_time(value)}'"
+ when Date, Time then "'#{quoted_date(value)}'"
+ when Symbol then "'#{quote_string(value.to_s)}'"
+ when Class then "'#{value}'"
+ else raise TypeError, "can't quote #{value.class.name}"
+ end
+ end
+
+ def _type_cast(value)
+ case value
+ when Symbol, ActiveSupport::Multibyte::Chars, Type::Binary::Data
+ value.to_s
+ when true then unquoted_true
+ when false then unquoted_false
+ # BigDecimals need to be put in a non-normalized form and quoted.
+ when BigDecimal then value.to_s("F")
+ when Type::Time::Value then quoted_time(value)
+ when Date, Time then quoted_date(value)
+ when *types_which_need_no_typecasting
+ value
+ else raise TypeError
+ end
end
- end
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_creation.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_creation.rb
index 6add697eeb..322684672f 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_creation.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_creation.rb
@@ -1,4 +1,4 @@
-require 'active_support/core_ext/string/strip'
+require "active_support/core_ext/string/strip"
module ActiveRecord
module ConnectionAdapters
@@ -23,9 +23,9 @@ module ActiveRecord
def visit_AlterTable(o)
sql = "ALTER TABLE #{quote_table_name(o.name)} "
- sql << o.adds.map { |col| accept col }.join(' ')
- sql << o.foreign_key_adds.map { |fk| visit_AddForeignKey fk }.join(' ')
- sql << o.foreign_key_drops.map { |fk| visit_DropForeignKey fk }.join(' ')
+ sql << o.adds.map { |col| accept col }.join(" ")
+ sql << o.foreign_key_adds.map { |fk| visit_AddForeignKey fk }.join(" ")
+ sql << o.foreign_key_drops.map { |fk| visit_DropForeignKey fk }.join(" ")
end
def visit_ColumnDefinition(o)
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
index 8dbafc5a4b..9b324c090b 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
@@ -11,7 +11,6 @@ module ActiveRecord
# +columns+ attribute of said TableDefinition object, in order to be used
# for generating a number of table creation or table changing SQL statements.
class ColumnDefinition < Struct.new(:name, :type, :limit, :precision, :scale, :default, :null, :first, :after, :auto_increment, :primary_key, :collation, :sql_type, :comment) #:nodoc:
-
def primary_key?
primary_key || type.to_sym == :primary_key
end
@@ -61,9 +60,9 @@ module ActiveRecord
end
private
- def default_primary_key
- "id"
- end
+ def default_primary_key
+ "id"
+ end
end
class ReferenceDefinition # :nodoc:
@@ -72,7 +71,7 @@ module ActiveRecord
polymorphic: false,
index: true,
foreign_key: false,
- type: :integer,
+ type: :bigint,
**options
)
@name = name
@@ -101,53 +100,55 @@ module ActiveRecord
end
end
+ # TODO Change this to private once we've dropped Ruby 2.2 support.
+ # Workaround for Ruby 2.2 "private attribute?" warning.
protected
- attr_reader :name, :polymorphic, :index, :foreign_key, :type, :options
+ attr_reader :name, :polymorphic, :index, :foreign_key, :type, :options
private
- def as_options(value, default = {})
- if value.is_a?(Hash)
- value
- else
- default
+ def as_options(value, default = {})
+ if value.is_a?(Hash)
+ value
+ else
+ default
+ end
end
- end
- def polymorphic_options
- as_options(polymorphic, options)
- end
+ def polymorphic_options
+ as_options(polymorphic, options)
+ end
- def index_options
- as_options(index)
- end
+ def index_options
+ as_options(index)
+ end
- def foreign_key_options
- as_options(foreign_key).merge(column: column_name)
- end
+ def foreign_key_options
+ as_options(foreign_key).merge(column: column_name)
+ end
- def columns
- result = [[column_name, type, options]]
- if polymorphic
- result.unshift(["#{name}_type", :string, polymorphic_options])
+ def columns
+ result = [[column_name, type, options]]
+ if polymorphic
+ result.unshift(["#{name}_type", :string, polymorphic_options])
+ end
+ result
end
- result
- end
- def column_name
- "#{name}_id"
- end
+ def column_name
+ "#{name}_id"
+ end
- def column_names
- columns.map(&:first)
- end
+ def column_names
+ columns.map(&:first)
+ end
- def foreign_table_name
- foreign_key_options.fetch(:to_table) do
- Base.pluralize_table_names ? name.to_s.pluralize : name
+ def foreign_table_name
+ foreign_key_options.fetch(:to_table) do
+ Base.pluralize_table_names ? name.to_s.pluralize : name
+ end
end
- end
end
module ColumnMethods
@@ -212,7 +213,7 @@ module ActiveRecord
def initialize(name, temporary = false, options = nil, as = nil, comment: nil)
@columns_hash = {}
- @indexes = {}
+ @indexes = []
@foreign_keys = []
@primary_keys = nil
@temporary = temporary
@@ -304,7 +305,7 @@ module ActiveRecord
# end
def column(name, type, options = {})
name = name.to_s
- type = type.to_sym
+ type = type.to_sym if type
options = options.dup
if @columns_hash[name] && @columns_hash[name].primary_key?
@@ -328,7 +329,7 @@ module ActiveRecord
#
# index(:account_id, name: 'index_projects_on_account_id')
def index(column_name, options = {})
- indexes[column_name] = options
+ indexes << [column_name, options]
end
def foreign_key(table_name, options = {}) # :nodoc:
@@ -342,9 +343,7 @@ module ActiveRecord
# <tt>:updated_at</tt> to the table. See {connection.add_timestamps}[rdoc-ref:SchemaStatements#add_timestamps]
#
# t.timestamps null: false
- def timestamps(*args)
- options = args.extract_options!
-
+ def timestamps(**options)
options[:null] = false if options[:null].nil?
column(:created_at, :datetime, options)
@@ -383,13 +382,13 @@ module ActiveRecord
end
private
- def create_column_definition(name, type)
- ColumnDefinition.new name, type
- end
+ def create_column_definition(name, type)
+ ColumnDefinition.new name, type
+ end
- def aliased_types(name, fallback)
- 'timestamp' == name ? :datetime : fallback
- end
+ def aliased_types(name, fallback)
+ "timestamp" == name ? :datetime : fallback
+ end
end
class AlterTable # :nodoc:
@@ -478,7 +477,7 @@ module ActiveRecord
# Checks to see if a column exists.
#
- # t.string(:name) unless t.column_exists?(:name, :string)
+ # t.string(:name) unless t.column_exists?(:name, :string)
#
# See {connection.column_exists?}[rdoc-ref:SchemaStatements#column_exists?]
def column_exists?(column_name, type = nil, options = {})
@@ -499,9 +498,9 @@ module ActiveRecord
# Checks to see if an index exists.
#
- # unless t.index_exists?(:branch_id)
- # t.index(:branch_id)
- # end
+ # unless t.index_exists?(:branch_id)
+ # t.index(:branch_id)
+ # end
#
# See {connection.index_exists?}[rdoc-ref:SchemaStatements#index_exists?]
def index_exists?(column_name, options = {})
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb
index 677a4c6bd0..b912d24626 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb
@@ -7,10 +7,7 @@ module ActiveRecord
# Adapter level by over-writing this code inside the database specific adapters
module ColumnDumper
def column_spec(column)
- spec = Hash[prepare_column_options(column).map { |k, v| [k, "#{k}: #{v}"] }]
- spec[:name] = column.name.inspect
- spec[:type] = schema_type(column).to_s
- spec
+ [schema_type(column), prepare_column_options(column)]
end
def column_spec_for_primary_key(column)
@@ -38,9 +35,9 @@ module ActiveRecord
end
default = schema_default(column) if column.has_default?
- spec[:default] = default unless default.nil?
+ spec[:default] = default unless default.nil?
- spec[:null] = 'false' unless column.null
+ spec[:null] = "false" unless column.null
if collation = schema_collation(column)
spec[:collation] = collation
@@ -53,53 +50,53 @@ module ActiveRecord
# Lists the valid migration options
def migration_keys
- [:name, :limit, :precision, :scale, :default, :null, :collation, :comment]
+ [:limit, :precision, :scale, :default, :null, :collation, :comment]
end
private
- def default_primary_key?(column)
- schema_type(column) == :integer
- end
+ def default_primary_key?(column)
+ schema_type(column) == :bigint
+ end
- def schema_type(column)
- if column.bigint?
- :bigint
- else
- column.type
+ def schema_type(column)
+ if column.bigint?
+ :bigint
+ else
+ column.type
+ end
end
- end
- def schema_limit(column)
- limit = column.limit unless column.bigint?
- limit.inspect if limit && limit != native_database_types[column.type][:limit]
- end
+ def schema_limit(column)
+ limit = column.limit unless column.bigint?
+ limit.inspect if limit && limit != native_database_types[column.type][:limit]
+ end
- def schema_precision(column)
- column.precision.inspect if column.precision
- end
+ def schema_precision(column)
+ column.precision.inspect if column.precision
+ end
- def schema_scale(column)
- column.scale.inspect if column.scale
- end
+ def schema_scale(column)
+ column.scale.inspect if column.scale
+ end
- def schema_default(column)
- type = lookup_cast_type_from_column(column)
- default = type.deserialize(column.default)
- if default.nil?
- schema_expression(column)
- else
- type.type_cast_for_schema(default)
+ def schema_default(column)
+ type = lookup_cast_type_from_column(column)
+ default = type.deserialize(column.default)
+ if default.nil?
+ schema_expression(column)
+ else
+ type.type_cast_for_schema(default)
+ end
end
- end
- def schema_expression(column)
- "-> { #{column.default_function.inspect} }" if column.default_function
- end
+ def schema_expression(column)
+ "-> { #{column.default_function.inspect} }" if column.default_function
+ end
- def schema_collation(column)
- column.collation.inspect if column.collation
- end
+ def schema_collation(column)
+ column.collation.inspect if column.collation
+ end
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
index eec0bc8518..e5c0e1690c 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
@@ -1,6 +1,6 @@
-require 'active_record/migration/join_table'
-require 'active_support/core_ext/string/access'
-require 'digest'
+require "active_record/migration/join_table"
+require "active_support/core_ext/string/access"
+require "digest"
module ActiveRecord
module ConnectionAdapters # :nodoc:
@@ -25,7 +25,7 @@ module ActiveRecord
# Truncates a table alias according to the limits of the current adapter.
def table_alias_for(table_name)
- table_name[0...table_alias_length].tr('.', '_')
+ table_name[0...table_alias_length].tr(".", "_")
end
# Returns the relation names useable to back Active Record models.
@@ -43,7 +43,7 @@ module ActiveRecord
end
# Returns an array of table names defined in the database.
- def tables(name = nil)
+ def tables
raise NotImplementedError, "#tables is not implemented"
end
@@ -120,7 +120,7 @@ module ActiveRecord
checks = []
checks << lambda { |c| c.name == column_name }
checks << lambda { |c| c.type == type } if type
- (migration_keys - [:name]).each do |attr|
+ migration_keys.each do |attr|
checks << lambda { |c| c.send(attr) == options[attr] } if options.key?(attr)
end
@@ -129,14 +129,9 @@ module ActiveRecord
# Returns just a table's primary key
def primary_key(table_name)
- pks = primary_keys(table_name)
- warn <<-WARNING.strip_heredoc if pks.count > 1
- WARNING: Rails does not support composite primary key.
-
- #{table_name} has composite primary key. Composite primary key is ignored.
- WARNING
-
- pks.first if pks.one?
+ pk = primary_keys(table_name)
+ pk = pk.first unless pk.size > 1
+ pk
end
# Creates a new table with the name +table_name+. +table_name+ may either
@@ -179,7 +174,7 @@ module ActiveRecord
# A Symbol can be used to specify the type of the generated primary key column.
# [<tt>:primary_key</tt>]
# The name of the primary key, if one is to be added automatically.
- # Defaults to +id+. If <tt>:id</tt> is false this option is ignored.
+ # Defaults to +id+. If <tt>:id</tt> is false, then this option is ignored.
#
# Note that Active Record models will automatically detect their
# primary key. This can be avoided by using
@@ -283,16 +278,16 @@ module ActiveRecord
result = execute schema_creation.accept td
unless supports_indexes_in_create?
- td.indexes.each_pair do |column_name, index_options|
+ td.indexes.each do |column_name, index_options|
add_index(table_name, column_name, index_options)
end
end
if supports_comments? && !supports_comments_in_create?
- change_table_comment(table_name, comment) if comment
+ change_table_comment(table_name, comment) if comment.present?
td.columns.each do |column|
- change_column_comment(table_name, column.name, column.comment) if column.comment
+ change_column_comment(table_name, column.name, column.comment) if column.comment.present?
end
end
@@ -305,9 +300,9 @@ module ActiveRecord
# # Creates a table called 'assemblies_parts' with no id.
# create_join_table(:assemblies, :parts)
#
- # You can pass a +options+ hash can include the following keys:
+ # You can pass an +options+ hash which can include the following keys:
# [<tt>:table_name</tt>]
- # Sets the table name overriding the default
+ # Sets the table name, overriding the default.
# [<tt>:column_options</tt>]
# Any extra options you want appended to the columns definition.
# [<tt>:options</tt>]
@@ -344,7 +339,7 @@ module ActiveRecord
column_options.reverse_merge!(null: false)
type = column_options.delete(:type) || :integer
- t1_column, t2_column = [table_1, table_2].map{ |t| t.to_s.singularize.foreign_key }
+ t1_column, t2_column = [table_1, table_2].map { |t| t.to_s.singularize.foreign_key }
create_table(join_table_name, options.merge!(id: false)) do |td|
td.send type, t1_column, column_options
@@ -433,7 +428,7 @@ module ActiveRecord
# t.remove_index :company_id
# end
#
- # See also Table for details on all of the various column transformation.
+ # See also Table for details on all of the various column transformations.
def change_table(table_name, options = {})
if supports_bulk_alter? && options[:bulk]
recorder = ActiveRecord::Migration::CommandRecorder.new(self)
@@ -483,10 +478,10 @@ module ActiveRecord
#
# Available options are (none of these exists by default):
# * <tt>:limit</tt> -
- # Requests a maximum column length. This is number of characters for a <tt>:string</tt> column
+ # Requests a maximum column length. This is the number of characters for a <tt>:string</tt> column
# and number of bytes for <tt>:text</tt>, <tt>:binary</tt> and <tt>:integer</tt> columns.
# * <tt>:default</tt> -
- # The column's default value. Use nil for NULL.
+ # The column's default value. Use +nil+ for +NULL+.
# * <tt>:null</tt> -
# Allows or disallows +NULL+ values in the column. This option could
# have been named <tt>:null_allowed</tt>.
@@ -495,7 +490,7 @@ module ActiveRecord
# * <tt>:scale</tt> -
# Specifies the scale for the <tt>:decimal</tt> and <tt>:numeric</tt> columns.
#
- # Note: The precision is the total number of significant digits
+ # Note: The precision is the total number of significant digits,
# and the scale is the number of digits that can be stored following
# the decimal point. For example, the number 123.45 has a precision of 5
# and a scale of 2. A decimal with a precision of 5 and a scale of 2 can
@@ -516,7 +511,7 @@ module ActiveRecord
# Default is (38,0).
# * DB2: <tt>:precision</tt> [1..63], <tt>:scale</tt> [0..62].
# Default unknown.
- # * SqlServer?: <tt>:precision</tt> [1..38], <tt>:scale</tt> [0..38].
+ # * SqlServer: <tt>:precision</tt> [1..38], <tt>:scale</tt> [0..38].
# Default (38,0).
#
# == Examples
@@ -538,6 +533,10 @@ module ActiveRecord
# add_column(:measurements, :huge_integer, :decimal, precision: 30)
# # ALTER TABLE "measurements" ADD "huge_integer" decimal(30)
#
+ # # Defines a column that stores an array of a type.
+ # add_column(:users, :skills, :text, array: true)
+ # # ALTER TABLE "users" ADD "skills" text[]
+ #
# # Defines a column with a database-specific type.
# add_column(:shapes, :triangle, 'polygon')
# # ALTER TABLE "shapes" ADD "triangle" polygon
@@ -564,7 +563,7 @@ module ActiveRecord
#
# The +type+ and +options+ parameters will be ignored if present. It can be helpful
# to provide these in a migration's +change+ method so it can be reverted.
- # In that case, +type+ and +options+ will be used by add_column.
+ # In that case, +type+ and +options+ will be used by #add_column.
def remove_column(table_name, column_name, type = nil, options = {})
execute "ALTER TABLE #{quote_table_name(table_name)} DROP #{quote_column_name(column_name)}"
end
@@ -767,7 +766,7 @@ module ActiveRecord
raise ArgumentError, "You must specify the index name"
end
else
- index_name(table_name, :column => options)
+ index_name(table_name, column: options)
end
end
@@ -790,7 +789,7 @@ module ActiveRecord
# [<tt>:type</tt>]
# The reference column type. Defaults to +:integer+.
# [<tt>:index</tt>]
- # Add an appropriate index. Defaults to false.
+ # Add an appropriate index. Defaults to true.
# See #add_index for usage of this option.
# [<tt>:foreign_key</tt>]
# Add an appropriate foreign key constraint. Defaults to false.
@@ -952,13 +951,13 @@ module ActiveRecord
# Checks to see if a foreign key exists on a table for a given foreign key definition.
#
- # # Check a foreign key exists
+ # # Checks to see if a foreign key exists.
# foreign_key_exists?(:accounts, :branches)
#
- # # Check a foreign key on a specified column exists
+ # # Checks to see if a foreign key on a specified column exists.
# foreign_key_exists?(:accounts, column: :owner_id)
#
- # # Check a foreign key with a custom name exists
+ # # Checks to see if a foreign key with a custom name exists.
# foreign_key_exists?(:accounts, name: "special_fk_name")
#
def foreign_key_exists?(from_table, options_or_to_table = {})
@@ -967,12 +966,12 @@ module ActiveRecord
def foreign_key_for(from_table, options_or_to_table = {}) # :nodoc:
return unless supports_foreign_keys?
- foreign_keys(from_table).detect {|fk| fk.defined_for? options_or_to_table }
+ foreign_keys(from_table).detect { |fk| fk.defined_for? options_or_to_table }
end
def foreign_key_for!(from_table, options_or_to_table = {}) # :nodoc:
- foreign_key_for(from_table, options_or_to_table) or \
- raise ArgumentError, "Table '#{from_table}' has no foreign key for #{options_or_to_table}"
+ foreign_key_for(from_table, options_or_to_table) || \
+ raise(ArgumentError, "Table '#{from_table}' has no foreign key for #{options_or_to_table}")
end
def foreign_key_column_for(table_name) # :nodoc:
@@ -990,22 +989,20 @@ module ActiveRecord
end
def dump_schema_information #:nodoc:
- versions = ActiveRecord::SchemaMigration.order('version').pluck(:version)
+ versions = ActiveRecord::SchemaMigration.order("version").pluck(:version)
insert_versions_sql(versions)
end
def insert_versions_sql(versions) # :nodoc:
- sm_table = ActiveRecord::Migrator.schema_migrations_table_name
+ sm_table = quote_table_name(ActiveRecord::Migrator.schema_migrations_table_name)
- if supports_multi_insert?
- sql = "INSERT INTO #{sm_table} (version) VALUES "
- sql << versions.map {|v| "('#{v}')" }.join(', ')
+ if versions.is_a?(Array)
+ sql = "INSERT INTO #{sm_table} (version) VALUES\n"
+ sql << versions.map { |v| "(#{quote(v)})" }.join(",\n")
sql << ";\n\n"
sql
else
- versions.map { |version|
- "INSERT INTO #{sm_table} (version) VALUES ('#{version}');"
- }.join "\n\n"
+ "INSERT INTO #{sm_table} (version) VALUES (#{quote(versions)});"
end
end
@@ -1029,26 +1026,33 @@ module ActiveRecord
sm_table = quote_table_name(ActiveRecord::Migrator.schema_migrations_table_name)
migrated = select_values("SELECT version FROM #{sm_table}").map(&:to_i)
- paths = migrations_paths.map {|p| "#{p}/[0-9]*_*.rb" }
+ paths = migrations_paths.map { |p| "#{p}/[0-9]*_*.rb" }
versions = Dir[*paths].map do |filename|
- filename.split('/').last.split('_').first.to_i
+ filename.split("/").last.split("_").first.to_i
end
unless migrated.include?(version)
- execute "INSERT INTO #{sm_table} (version) VALUES ('#{version}')"
+ execute "INSERT INTO #{sm_table} (version) VALUES (#{quote(version)})"
end
- inserting = (versions - migrated).select {|v| v < version}
+ inserting = (versions - migrated).select { |v| v < version }
if inserting.any?
- if (duplicate = inserting.detect {|v| inserting.count(v) > 1})
+ if (duplicate = inserting.detect { |v| inserting.count(v) > 1 })
raise "Duplicate migration #{duplicate}. Please renumber your migrations to resolve the conflict."
end
- execute insert_versions_sql(inserting)
+ if supports_multi_insert?
+ execute insert_versions_sql(inserting)
+ else
+ inserting.each do |v|
+ execute insert_versions_sql(v)
+ end
+ end
end
end
def type_to_sql(type, limit = nil, precision = nil, scale = nil) #:nodoc:
- if native = native_database_types[type.to_sym]
+ type = type.to_sym if type
+ if native = native_database_types[type]
column_type_sql = (native.is_a?(Hash) ? native[:name] : native).dup
if type == :decimal # ignore limit, use precision and scale
@@ -1081,7 +1085,7 @@ module ActiveRecord
end
# Given a set of columns and an ORDER BY clause, returns the columns for a SELECT DISTINCT.
- # PostgreSQL, MySQL, and Oracle overrides this for custom DISTINCT syntax - they
+ # PostgreSQL, MySQL, and Oracle override this for custom DISTINCT syntax - they
# require the order columns appear in the SELECT.
#
# columns_for_distinct("posts.id", ["posts.created_at desc"])
@@ -1116,7 +1120,7 @@ module ActiveRecord
end
def add_index_options(table_name, column_name, comment: nil, **options) # :nodoc:
- if column_name.is_a?(String) && /\W/ === column_name
+ if column_name.is_a?(String) && /\W/.match?(column_name)
column_names = column_name
else
column_names = Array(column_name)
@@ -1165,41 +1169,41 @@ module ActiveRecord
raise NotImplementedError, "#{self.class} does not support changing column comments"
end
- protected
- def add_index_sort_order(option_strings, column_names, options = {})
- if options.is_a?(Hash) && order = options[:order]
+ private
+
+ def add_index_sort_order(quoted_columns, **options)
+ if order = options[:order]
case order
when Hash
- column_names.each {|name| option_strings[name] += " #{order[name].upcase}" if order.has_key?(name)}
+ order = order.symbolize_keys
+ quoted_columns.each { |name, column| column << " #{order[name].upcase}" if order[name].present? }
when String
- column_names.each {|name| option_strings[name] += " #{order.upcase}"}
+ quoted_columns.each { |name, column| column << " #{order.upcase}" if order.present? }
end
end
- return option_strings
+ quoted_columns
end
# Overridden by the MySQL adapter for supporting index lengths
- def quoted_columns_for_index(column_names, options = {})
- return [column_names] if column_names.is_a?(String)
-
- option_strings = Hash[column_names.map {|name| [name, '']}]
-
- # add index sort order if supported
+ def add_options_for_index_columns(quoted_columns, **options)
if supports_index_sort_order?
- option_strings = add_index_sort_order(option_strings, column_names, options)
+ quoted_columns = add_index_sort_order(quoted_columns, options)
end
- column_names.map {|name| quote_column_name(name) + option_strings[name]}
+ quoted_columns
+ end
+
+ def quoted_columns_for_index(column_names, **options)
+ return [column_names] if column_names.is_a?(String)
+
+ quoted_columns = Hash[column_names.map { |name| [name.to_sym, quote_column_name(name).dup] }]
+ add_options_for_index_columns(quoted_columns, options).values
end
def index_name_for_remove(table_name, options = {})
return options[:name] if can_remove_index_by_name?(options)
- # if the adapter doesn't support the indexes call the best we can do
- # is return the default index name for the options provided
- return index_name(table_name, options) unless respond_to?(:indexes)
-
checks = []
if options.is_a?(Hash)
@@ -1210,10 +1214,10 @@ module ActiveRecord
end
if column_names.any?
- checks << lambda { |i| i.columns.join('_and_') == column_names.join('_and_') }
+ checks << lambda { |i| i.columns.join("_and_") == column_names.join("_and_") }
end
- raise ArgumentError "No name or columns specified" if checks.none?
+ raise ArgumentError, "No name or columns specified" if checks.none?
matching_indexes = indexes(table_name).select { |i| checks.all? { |check| check[i] } }
@@ -1249,50 +1253,49 @@ module ActiveRecord
end
end
- private
- def create_table_definition(*args)
- TableDefinition.new(*args)
- end
-
- def create_alter_table(name)
- AlterTable.new create_table_definition(name)
- end
+ def create_table_definition(*args)
+ TableDefinition.new(*args)
+ end
- def index_name_options(column_names) # :nodoc:
- if column_names.is_a?(String)
- column_names = column_names.scan(/\w+/).join('_')
+ def create_alter_table(name)
+ AlterTable.new create_table_definition(name)
end
- { column: column_names }
- end
+ def index_name_options(column_names)
+ if column_names.is_a?(String)
+ column_names = column_names.scan(/\w+/).join("_")
+ end
- def foreign_key_name(table_name, options) # :nodoc:
- identifier = "#{table_name}_#{options.fetch(:column)}_fk"
- hashed_identifier = Digest::SHA256.hexdigest(identifier).first(10)
- options.fetch(:name) do
- "fk_rails_#{hashed_identifier}"
+ { column: column_names }
+ end
+
+ def foreign_key_name(table_name, options)
+ identifier = "#{table_name}_#{options.fetch(:column)}_fk"
+ hashed_identifier = Digest::SHA256.hexdigest(identifier).first(10)
+ options.fetch(:name) do
+ "fk_rails_#{hashed_identifier}"
+ end
end
- end
- def validate_index_length!(table_name, new_name, internal = false) # :nodoc:
- max_index_length = internal ? index_name_length : allowed_index_name_length
+ def validate_index_length!(table_name, new_name, internal = false)
+ max_index_length = internal ? index_name_length : allowed_index_name_length
- if new_name.length > max_index_length
- raise ArgumentError, "Index name '#{new_name}' on table '#{table_name}' is too long; the limit is #{allowed_index_name_length} characters"
+ if new_name.length > max_index_length
+ raise ArgumentError, "Index name '#{new_name}' on table '#{table_name}' is too long; the limit is #{allowed_index_name_length} characters"
+ end
end
- end
- def extract_new_default_value(default_or_changes)
- if default_or_changes.is_a?(Hash) && default_or_changes.has_key?(:from) && default_or_changes.has_key?(:to)
- default_or_changes[:to]
- else
- default_or_changes
+ def extract_new_default_value(default_or_changes)
+ if default_or_changes.is_a?(Hash) && default_or_changes.has_key?(:from) && default_or_changes.has_key?(:to)
+ default_or_changes[:to]
+ else
+ default_or_changes
+ end
end
- end
- def can_remove_index_by_name?(options)
- options.is_a?(Hash) && options.key?(:name) && options.except(:name, :algorithm).empty?
- end
+ def can_remove_index_by_name?(options)
+ options.is_a?(Hash) && options.key?(:name) && options.except(:name, :algorithm).empty?
+ end
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb b/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb
index ca795cb1ad..6bb072dd73 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb
@@ -41,7 +41,6 @@ module ActiveRecord
end
class Transaction #:nodoc:
-
attr_reader :connection, :state, :records, :savepoint_name
attr_writer :joinable
@@ -101,7 +100,6 @@ module ActiveRecord
end
class SavepointTransaction < Transaction
-
def initialize(connection, savepoint_name, options, *args)
super(connection, options, *args)
if options[:isolation]
@@ -124,7 +122,6 @@ module ActiveRecord
end
class RealTransaction < Transaction
-
def initialize(connection, options, *args)
super
if options[:isolation]
@@ -195,7 +192,7 @@ module ActiveRecord
raise
ensure
unless error
- if Thread.current.status == 'aborting'
+ if Thread.current.status == "aborting"
rollback_transaction if transaction
else
begin
@@ -226,7 +223,6 @@ module ActiveRecord
return unless error.is_a?(ActiveRecord::PreparedStatementCacheExpired)
@connection.clear_cache!
end
-
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
index 5747e4d1ee..4046b3829d 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
@@ -1,11 +1,11 @@
-require 'active_record/type'
-require 'active_record/connection_adapters/determine_if_preparable_visitor'
-require 'active_record/connection_adapters/schema_cache'
-require 'active_record/connection_adapters/sql_type_metadata'
-require 'active_record/connection_adapters/abstract/schema_dumper'
-require 'active_record/connection_adapters/abstract/schema_creation'
-require 'arel/collectors/bind'
-require 'arel/collectors/sql_string'
+require "active_record/type"
+require "active_record/connection_adapters/determine_if_preparable_visitor"
+require "active_record/connection_adapters/schema_cache"
+require "active_record/connection_adapters/sql_type_metadata"
+require "active_record/connection_adapters/abstract/schema_dumper"
+require "active_record/connection_adapters/abstract/schema_creation"
+require "arel/collectors/bind"
+require "arel/collectors/sql_string"
module ActiveRecord
module ConnectionAdapters # :nodoc:
@@ -14,7 +14,7 @@ module ActiveRecord
autoload :Column
autoload :ConnectionSpecification
- autoload_at 'active_record/connection_adapters/abstract/schema_definitions' do
+ autoload_at "active_record/connection_adapters/abstract/schema_definitions" do
autoload :IndexDefinition
autoload :ColumnDefinition
autoload :ChangeColumnDefinition
@@ -25,11 +25,11 @@ module ActiveRecord
autoload :ReferenceDefinition
end
- autoload_at 'active_record/connection_adapters/abstract/connection_pool' do
+ autoload_at "active_record/connection_adapters/abstract/connection_pool" do
autoload :ConnectionHandler
end
- autoload_under 'abstract' do
+ autoload_under "abstract" do
autoload :SchemaStatements
autoload :DatabaseStatements
autoload :DatabaseLimits
@@ -39,7 +39,7 @@ module ActiveRecord
autoload :Savepoints
end
- autoload_at 'active_record/connection_adapters/abstract/transaction' do
+ autoload_at "active_record/connection_adapters/abstract/transaction" do
autoload :TransactionManager
autoload :NullTransaction
autoload :RealTransaction
@@ -61,18 +61,18 @@ module ActiveRecord
# Most of the methods in the adapter are useful during migrations. Most
# notably, the instance methods provided by SchemaStatements are very useful.
class AbstractAdapter
- ADAPTER_NAME = 'Abstract'.freeze
+ ADAPTER_NAME = "Abstract".freeze
+ include ActiveSupport::Callbacks
+ define_callbacks :checkout, :checkin
+
include Quoting, DatabaseStatements, SchemaStatements
include DatabaseLimits
include QueryCache
- include ActiveSupport::Callbacks
include ColumnDumper
include Savepoints
SIMPLE_INT = /\A\d+\z/
- define_callbacks :checkout, :checkin
-
attr_accessor :visitor, :pool
attr_reader :schema_cache, :owner, :logger
alias :in_use? :owner
@@ -106,7 +106,7 @@ module ActiveRecord
@pool = nil
@schema_cache = SchemaCache.new self
@quoted_column_names, @quoted_table_names = {}, {}
- @visitor = arel_visitor
+ @visitor = arel_visitor
if self.class.type_cast_config_to_boolean(config.fetch(:prepared_statements) { true })
@prepared_statements = true
@@ -120,17 +120,17 @@ module ActiveRecord
include Comparable
def initialize(version_string)
- @version = version_string.split('.').map(&:to_i)
+ @version = version_string.split(".").map(&:to_i)
end
def <=>(version_string)
- @version <=> version_string.split('.').map(&:to_i)
+ @version <=> version_string.split(".").map(&:to_i)
end
end
class BindCollector < Arel::Collectors::Bind
def compile(bvs, conn)
- casted_binds = conn.prepare_binds_for_database(bvs)
+ casted_binds = bvs.map(&:value_for_database)
super(casted_binds.map { |value| conn.quote(value) })
end
end
@@ -161,12 +161,20 @@ module ActiveRecord
SchemaCreation.new self
end
+ # Returns an array of +Column+ objects for the table specified by +table_name+.
+ def columns(table_name) # :nodoc:
+ table_name = table_name.to_s
+ column_definitions(table_name).map do |field|
+ new_column_from_field(table_name, field)
+ end
+ end
+
# this method must only be called while holding connection pool's mutex
def lease
if in_use?
- msg = 'Cannot lease connection, '
+ msg = "Cannot lease connection, "
if @owner == Thread.current
- msg << 'it is already leased by the current thread.'
+ msg << "it is already leased by the current thread."
else
msg << "it is already in use by a different thread: #{@owner}. " <<
"Current thread: #{Thread.current}."
@@ -193,7 +201,7 @@ module ActiveRecord
@owner = nil
else
- raise ActiveRecordError, 'Cannot expire connection, it is not currently leased.'
+ raise ActiveRecordError, "Cannot expire connection, it is not currently leased."
end
end
@@ -206,7 +214,7 @@ module ActiveRecord
@owner = Thread.current
end
else
- raise ActiveRecordError, 'Cannot steal connection, it is not currently leased.'
+ raise ActiveRecordError, "Cannot steal connection, it is not currently leased."
end
end
@@ -434,11 +442,7 @@ module ActiveRecord
end
def case_sensitive_comparison(table, attribute, column, value)
- if value.nil?
- table[attribute].eq(value)
- else
- table[attribute].eq(Arel::Nodes::BindParam.new)
- end
+ table[attribute].eq(Arel::Nodes::BindParam.new)
end
def case_insensitive_comparison(table, attribute, column, value)
@@ -495,116 +499,122 @@ module ActiveRecord
result
end
- protected
-
- def initialize_type_map(m) # :nodoc:
- register_class_with_limit m, %r(boolean)i, Type::Boolean
- register_class_with_limit m, %r(char)i, Type::String
- register_class_with_limit m, %r(binary)i, Type::Binary
- register_class_with_limit m, %r(text)i, Type::Text
- register_class_with_precision m, %r(date)i, Type::Date
- register_class_with_precision m, %r(time)i, Type::Time
- register_class_with_precision m, %r(datetime)i, Type::DateTime
- register_class_with_limit m, %r(float)i, Type::Float
- register_class_with_limit m, %r(int)i, Type::Integer
-
- m.alias_type %r(blob)i, 'binary'
- m.alias_type %r(clob)i, 'text'
- m.alias_type %r(timestamp)i, 'datetime'
- m.alias_type %r(numeric)i, 'decimal'
- m.alias_type %r(number)i, 'decimal'
- m.alias_type %r(double)i, 'float'
-
- m.register_type(%r(decimal)i) do |sql_type|
- scale = extract_scale(sql_type)
- precision = extract_precision(sql_type)
-
- if scale == 0
- # FIXME: Remove this class as well
- Type::DecimalWithoutScale.new(precision: precision)
- else
- Type::Decimal.new(precision: precision, scale: scale)
+ private
+
+ def initialize_type_map(m)
+ register_class_with_limit m, %r(boolean)i, Type::Boolean
+ register_class_with_limit m, %r(char)i, Type::String
+ register_class_with_limit m, %r(binary)i, Type::Binary
+ register_class_with_limit m, %r(text)i, Type::Text
+ register_class_with_precision m, %r(date)i, Type::Date
+ register_class_with_precision m, %r(time)i, Type::Time
+ register_class_with_precision m, %r(datetime)i, Type::DateTime
+ register_class_with_limit m, %r(float)i, Type::Float
+ register_class_with_limit m, %r(int)i, Type::Integer
+
+ m.alias_type %r(blob)i, "binary"
+ m.alias_type %r(clob)i, "text"
+ m.alias_type %r(timestamp)i, "datetime"
+ m.alias_type %r(numeric)i, "decimal"
+ m.alias_type %r(number)i, "decimal"
+ m.alias_type %r(double)i, "float"
+
+ m.register_type(%r(decimal)i) do |sql_type|
+ scale = extract_scale(sql_type)
+ precision = extract_precision(sql_type)
+
+ if scale == 0
+ # FIXME: Remove this class as well
+ Type::DecimalWithoutScale.new(precision: precision)
+ else
+ Type::Decimal.new(precision: precision, scale: scale)
+ end
end
end
- end
- def reload_type_map # :nodoc:
- type_map.clear
- initialize_type_map(type_map)
- end
+ def reload_type_map
+ type_map.clear
+ initialize_type_map(type_map)
+ end
- def register_class_with_limit(mapping, key, klass) # :nodoc:
- mapping.register_type(key) do |*args|
- limit = extract_limit(args.last)
- klass.new(limit: limit)
+ def register_class_with_limit(mapping, key, klass)
+ mapping.register_type(key) do |*args|
+ limit = extract_limit(args.last)
+ klass.new(limit: limit)
+ end
end
- end
- def register_class_with_precision(mapping, key, klass) # :nodoc:
- mapping.register_type(key) do |*args|
- precision = extract_precision(args.last)
- klass.new(precision: precision)
+ def register_class_with_precision(mapping, key, klass)
+ mapping.register_type(key) do |*args|
+ precision = extract_precision(args.last)
+ klass.new(precision: precision)
+ end
end
- end
- def extract_scale(sql_type) # :nodoc:
- case sql_type
+ def extract_scale(sql_type)
+ case sql_type
when /\((\d+)\)/ then 0
when /\((\d+)(,(\d+))\)/ then $3.to_i
+ end
end
- end
- def extract_precision(sql_type) # :nodoc:
- $1.to_i if sql_type =~ /\((\d+)(,\d+)?\)/
- end
-
- def extract_limit(sql_type) # :nodoc:
- case sql_type
- when /^bigint/i
- 8
- when /\((.*)\)/
- $1.to_i
+ def extract_precision(sql_type)
+ $1.to_i if sql_type =~ /\((\d+)(,\d+)?\)/
end
- end
- def translate_exception_class(e, sql)
- begin
- message = "#{e.class.name}: #{e.message}: #{sql}"
- rescue Encoding::CompatibilityError
- message = "#{e.class.name}: #{e.message.force_encoding sql.encoding}: #{sql}"
+ def extract_limit(sql_type)
+ case sql_type
+ when /^bigint/i
+ 8
+ when /\((.*)\)/
+ $1.to_i
+ end
end
- exception = translate_exception(e, message)
- exception.set_backtrace e.backtrace
- exception
- end
+ def translate_exception_class(e, sql)
+ begin
+ message = "#{e.class.name}: #{e.message}: #{sql}"
+ rescue Encoding::CompatibilityError
+ message = "#{e.class.name}: #{e.message.force_encoding sql.encoding}: #{sql}"
+ end
- def log(sql, name = "SQL", binds = [], statement_name = nil)
- @instrumenter.instrument(
- "sql.active_record",
- :sql => sql,
- :name => name,
- :connection_id => object_id,
- :statement_name => statement_name,
- :binds => binds) { yield }
- rescue => e
- raise translate_exception_class(e, sql)
- end
+ exception = translate_exception(e, message)
+ exception.set_backtrace e.backtrace
+ exception
+ end
- def translate_exception(exception, message)
- # override in derived class
- ActiveRecord::StatementInvalid.new(message)
- end
+ def log(sql, name = "SQL", binds = [], type_casted_binds = [], statement_name = nil) # :doc:
+ @instrumenter.instrument(
+ "sql.active_record",
+ sql: sql,
+ name: name,
+ binds: binds,
+ type_casted_binds: type_casted_binds,
+ statement_name: statement_name,
+ connection_id: object_id) { yield }
+ rescue => e
+ raise translate_exception_class(e, sql)
+ end
- def without_prepared_statement?(binds)
- !prepared_statements || binds.empty?
- end
+ def translate_exception(exception, message)
+ # override in derived class
+ case exception
+ when RuntimeError
+ exception
+ else
+ ActiveRecord::StatementInvalid.new(message)
+ end
+ end
- def column_for(table_name, column_name) # :nodoc:
- column_name = column_name.to_s
- columns(table_name).detect { |c| c.name == column_name } ||
- raise(ActiveRecordError, "No such column: #{table_name}.#{column_name}")
- end
+ def without_prepared_statement?(binds)
+ !prepared_statements || binds.empty?
+ end
+
+ def column_for(table_name, column_name)
+ column_name = column_name.to_s
+ columns(table_name).detect { |c| c.name == column_name } ||
+ raise(ActiveRecordError, "No such column: #{table_name}.#{column_name}")
+ end
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
index 3e77b92141..01cd1e5446 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
@@ -1,14 +1,14 @@
-require 'active_record/connection_adapters/abstract_adapter'
-require 'active_record/connection_adapters/statement_pool'
-require 'active_record/connection_adapters/mysql/column'
-require 'active_record/connection_adapters/mysql/explain_pretty_printer'
-require 'active_record/connection_adapters/mysql/quoting'
-require 'active_record/connection_adapters/mysql/schema_creation'
-require 'active_record/connection_adapters/mysql/schema_definitions'
-require 'active_record/connection_adapters/mysql/schema_dumper'
-require 'active_record/connection_adapters/mysql/type_metadata'
-
-require 'active_support/core_ext/string/strip'
+require "active_record/connection_adapters/abstract_adapter"
+require "active_record/connection_adapters/statement_pool"
+require "active_record/connection_adapters/mysql/column"
+require "active_record/connection_adapters/mysql/explain_pretty_printer"
+require "active_record/connection_adapters/mysql/quoting"
+require "active_record/connection_adapters/mysql/schema_creation"
+require "active_record/connection_adapters/mysql/schema_definitions"
+require "active_record/connection_adapters/mysql/schema_dumper"
+require "active_record/connection_adapters/mysql/type_metadata"
+
+require "active_support/core_ext/string/strip"
module ActiveRecord
module ConnectionAdapters
@@ -39,7 +39,7 @@ module ActiveRecord
self.emulate_booleans = true
NATIVE_DATABASE_TYPES = {
- primary_key: "int auto_increment PRIMARY KEY",
+ primary_key: "bigint auto_increment PRIMARY KEY",
string: { name: "varchar", limit: 255 },
text: { name: "text", limit: 65535 },
integer: { name: "int", limit: 4 },
@@ -67,16 +67,16 @@ module ActiveRecord
@statements = StatementPool.new(self.class.type_cast_config_to_integer(config[:statement_limit]))
- if version < '5.0.0'
- raise "Your version of MySQL (#{full_version.match(/^\d+\.\d+\.\d+/)[0]}) is too old. Active Record supports MySQL >= 5.0."
+ if version < "5.1.10"
+ raise "Your version of MySQL (#{full_version.match(/^\d+\.\d+\.\d+/)[0]}) is too old. Active Record supports MySQL >= 5.1.10."
end
end
- CHARSETS_OF_4BYTES_MAXLEN = ['utf8mb4', 'utf16', 'utf16le', 'utf32']
+ CHARSETS_OF_4BYTES_MAXLEN = ["utf8mb4", "utf16", "utf16le", "utf32"]
def internal_string_options_for_primary_key # :nodoc:
super.tap { |options|
- options[:collation] = collation.sub(/\A[^_]+/, 'utf8') if CHARSETS_OF_4BYTES_MAXLEN.include?(charset)
+ options[:collation] = collation.sub(/\A[^_]+/, "utf8") if CHARSETS_OF_4BYTES_MAXLEN.include?(charset)
}
end
@@ -85,7 +85,7 @@ module ActiveRecord
end
def mariadb? # :nodoc:
- full_version =~ /mariadb/i
+ /mariadb/i.match?(full_version)
end
# Returns true, since this connection adapter supports migrations.
@@ -135,9 +135,9 @@ module ActiveRecord
def supports_datetime_with_precision?
if mariadb?
- version >= '5.3.0'
+ version >= "5.3.0"
else
- version >= '5.6.4'
+ version >= "5.6.4"
end
end
@@ -158,7 +158,7 @@ module ActiveRecord
end
def index_algorithms
- { default: 'ALGORITHM = DEFAULT', copy: 'ALGORITHM = COPY', inplace: 'ALGORITHM = INPLACE' }
+ { default: "ALGORITHM = DEFAULT", copy: "ALGORITHM = COPY", inplace: "ALGORITHM = INPLACE" }
end
# HELPER METHODS ===========================================
@@ -207,7 +207,7 @@ module ActiveRecord
def explain(arel, binds = [])
sql = "EXPLAIN #{to_sql(arel, binds)}"
start = Time.now
- result = exec_query(sql, 'EXPLAIN', binds)
+ result = exec_query(sql, "EXPLAIN", binds)
elapsed = Time.now - start
MySQL::ExplainPrettyPrinter.new.pp(result, elapsed)
@@ -215,7 +215,11 @@ module ActiveRecord
# Executes the SQL statement in the context of this connection.
def execute(sql, name = nil)
- log(sql, name) { @connection.query(sql) }
+ log(sql, name) do
+ ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
+ @connection.query(sql)
+ end
+ end
end
# Mysql2Adapter doesn't have to free a result after using it, but we use this method
@@ -293,58 +297,49 @@ module ActiveRecord
end
def current_database
- select_value 'SELECT DATABASE() as db'
+ select_value "SELECT DATABASE() as db"
end
# Returns the database character set.
def charset
- show_variable 'character_set_database'
+ show_variable "character_set_database"
end
# Returns the database collation strategy.
def collation
- show_variable 'collation_database'
+ show_variable "collation_database"
end
- def tables(name = nil) # :nodoc:
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
- #tables currently returns both tables and views.
- This behavior is deprecated and will be changed with Rails 5.1 to only return tables.
- Use #data_sources instead.
- MSG
+ def tables # :nodoc:
+ sql = "SELECT table_name FROM information_schema.tables WHERE table_type = 'BASE TABLE'"
+ sql << " AND table_schema = #{quote(@config[:database])}"
- if name
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
- Passing arguments to #tables is deprecated without replacement.
- MSG
- end
+ select_values(sql, "SCHEMA")
+ end
- data_sources
+ def views # :nodoc:
+ select_values("SHOW FULL TABLES WHERE table_type = 'VIEW'", "SCHEMA")
end
- def data_sources
+ def data_sources # :nodoc:
sql = "SELECT table_name FROM information_schema.tables "
sql << "WHERE table_schema = #{quote(@config[:database])}"
- select_values(sql, 'SCHEMA')
+ select_values(sql, "SCHEMA")
end
- def truncate(table_name, name = nil)
- execute "TRUNCATE TABLE #{quote_table_name(table_name)}", name
- end
+ def table_exists?(table_name) # :nodoc:
+ return false unless table_name.present?
+
+ schema, name = extract_schema_qualified_name(table_name)
- def table_exists?(table_name)
- # Update lib/active_record/internal_metadata.rb when this gets removed
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
- #table_exists? currently checks both tables and views.
- This behavior is deprecated and will be changed with Rails 5.1 to only check tables.
- Use #data_source_exists? instead.
- MSG
+ sql = "SELECT table_name FROM information_schema.tables WHERE table_type = 'BASE TABLE'"
+ sql << " AND table_schema = #{quote(schema)} AND table_name = #{quote(name)}"
- data_source_exists?(table_name)
+ select_values(sql, "SCHEMA").any?
end
- def data_source_exists?(table_name)
+ def data_source_exists?(table_name) # :nodoc:
return false unless table_name.present?
schema, name = extract_schema_qualified_name(table_name)
@@ -352,11 +347,7 @@ module ActiveRecord
sql = "SELECT table_name FROM information_schema.tables "
sql << "WHERE table_schema = #{quote(schema)} AND table_name = #{quote(name)}"
- select_values(sql, 'SCHEMA').any?
- end
-
- def views # :nodoc:
- select_values("SHOW FULL TABLES WHERE table_type = 'VIEW'", 'SCHEMA')
+ select_values(sql, "SCHEMA").any?
end
def view_exists?(view_name) # :nodoc:
@@ -367,57 +358,60 @@ module ActiveRecord
sql = "SELECT table_name FROM information_schema.tables WHERE table_type = 'VIEW'"
sql << " AND table_schema = #{quote(schema)} AND table_name = #{quote(name)}"
- select_values(sql, 'SCHEMA').any?
+ select_values(sql, "SCHEMA").any?
+ end
+
+ def truncate(table_name, name = nil)
+ execute "TRUNCATE TABLE #{quote_table_name(table_name)}", name
end
# Returns an array of indexes for the given table.
def indexes(table_name, name = nil) #:nodoc:
indexes = []
current_index = nil
- execute_and_free("SHOW KEYS FROM #{quote_table_name(table_name)}", 'SCHEMA') do |result|
+ execute_and_free("SHOW KEYS FROM #{quote_table_name(table_name)}", "SCHEMA") do |result|
each_hash(result) do |row|
if current_index != row[:Key_name]
- next if row[:Key_name] == 'PRIMARY' # skip the primary key
+ next if row[:Key_name] == "PRIMARY" # skip the primary key
current_index = row[:Key_name]
mysql_index_type = row[:Index_type].downcase.to_sym
index_type = INDEX_TYPES.include?(mysql_index_type) ? mysql_index_type : nil
index_using = INDEX_USINGS.include?(mysql_index_type) ? mysql_index_type : nil
- indexes << IndexDefinition.new(row[:Table], row[:Key_name], row[:Non_unique].to_i == 0, [], [], nil, nil, index_type, index_using, row[:Index_comment].presence)
+ indexes << IndexDefinition.new(row[:Table], row[:Key_name], row[:Non_unique].to_i == 0, [], {}, nil, nil, index_type, index_using, row[:Index_comment].presence)
end
indexes.last.columns << row[:Column_name]
- indexes.last.lengths << row[:Sub_part]
+ indexes.last.lengths.merge!(row[:Column_name] => row[:Sub_part].to_i) if row[:Sub_part]
end
end
indexes
end
- # Returns an array of +Column+ objects for the table specified by +table_name+.
- def columns(table_name) # :nodoc:
- table_name = table_name.to_s
- column_definitions(table_name).map do |field|
- type_metadata = fetch_type_metadata(field[:Type], field[:Extra])
- if type_metadata.type == :datetime && field[:Default] == "CURRENT_TIMESTAMP"
- default, default_function = nil, field[:Default]
- else
- default, default_function = field[:Default], nil
- end
- new_column(field[:Field], default, type_metadata, field[:Null] == "YES", table_name, default_function, field[:Collation], comment: field[:Comment].presence)
+ def new_column_from_field(table_name, field) # :nodoc:
+ type_metadata = fetch_type_metadata(field[:Type], field[:Extra])
+ if type_metadata.type == :datetime && field[:Default] == "CURRENT_TIMESTAMP"
+ default, default_function = nil, field[:Default]
+ else
+ default, default_function = field[:Default], nil
end
+ new_column(field[:Field], default, type_metadata, field[:Null] == "YES", table_name, default_function, field[:Collation], comment: field[:Comment].presence)
end
def table_comment(table_name) # :nodoc:
- select_value(<<-SQL.strip_heredoc, 'SCHEMA')
+ schema, name = extract_schema_qualified_name(table_name)
+
+ select_value(<<-SQL.strip_heredoc, "SCHEMA")
SELECT table_comment
FROM information_schema.tables
- WHERE table_name=#{quote(table_name)}
+ WHERE table_schema = #{quote(schema)}
+ AND table_name = #{quote(name)}
SQL
end
def create_table(table_name, **options) #:nodoc:
- super(table_name, options: 'ENGINE=InnoDB', **options)
+ super(table_name, options: "ENGINE=InnoDB", **options)
end
def bulk_change_table(table_name, operations) #:nodoc:
@@ -476,7 +470,7 @@ module ActiveRecord
def change_column_default(table_name, column_name, default_or_changes) #:nodoc:
default = extract_new_default_value(default_or_changes)
column = column_for(table_name, column_name)
- change_column table_name, column_name, column.sql_type, :default => default
+ change_column table_name, column_name, column.sql_type, default: default
end
def change_column_null(table_name, column_name, null, default = nil) #:nodoc:
@@ -486,7 +480,7 @@ module ActiveRecord
execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
end
- change_column table_name, column_name, column.sql_type, :null => null
+ change_column table_name, column_name, column.sql_type, null: null
end
def change_column(table_name, column_name, type, options = {}) #:nodoc:
@@ -505,7 +499,7 @@ module ActiveRecord
end
def add_sql_comment!(sql, comment) # :nodoc:
- sql << " COMMENT #{quote(comment)}" if comment
+ sql << " COMMENT #{quote(comment)}" if comment.present?
sql
end
@@ -514,7 +508,7 @@ module ActiveRecord
schema, name = extract_schema_qualified_name(table_name)
- fk_info = select_all(<<-SQL.strip_heredoc, 'SCHEMA')
+ fk_info = select_all(<<-SQL.strip_heredoc, "SCHEMA")
SELECT fk.referenced_table_name AS 'to_table',
fk.referenced_column_name AS 'primary_key',
fk.column_name AS 'column',
@@ -531,59 +525,66 @@ module ActiveRecord
fk_info.map do |row|
options = {
- column: row['column'],
- name: row['name'],
- primary_key: row['primary_key']
+ column: row["column"],
+ name: row["name"],
+ primary_key: row["primary_key"]
}
- options[:on_update] = extract_foreign_key_action(row['on_update'])
- options[:on_delete] = extract_foreign_key_action(row['on_delete'])
+ options[:on_update] = extract_foreign_key_action(row["on_update"])
+ options[:on_delete] = extract_foreign_key_action(row["on_delete"])
- ForeignKeyDefinition.new(table_name, row['to_table'], options)
+ ForeignKeyDefinition.new(table_name, row["to_table"], options)
end
end
- def table_options(table_name)
+ def table_options(table_name) # :nodoc:
+ table_options = {}
+
create_table_info = create_table_info(table_name)
# strip create_definitions and partition_options
- raw_table_options = create_table_info.sub(/\A.*\n\) /m, '').sub(/\n\/\*!.*\*\/\n\z/m, '').strip
+ raw_table_options = create_table_info.sub(/\A.*\n\) /m, "").sub(/\n\/\*!.*\*\/\n\z/m, "").strip
# strip AUTO_INCREMENT
raw_table_options.sub!(/(ENGINE=\w+)(?: AUTO_INCREMENT=\d+)/, '\1')
+ table_options[:options] = raw_table_options
+
# strip COMMENT
- raw_table_options.sub!(/ COMMENT='.+'/, '')
+ if raw_table_options.sub!(/ COMMENT='.+'/, "")
+ table_options[:comment] = table_comment(table_name)
+ end
- raw_table_options
+ table_options
end
# Maps logical Rails types to MySQL-specific data types.
def type_to_sql(type, limit = nil, precision = nil, scale = nil, unsigned = nil)
- sql = case type.to_s
- when 'integer'
- integer_to_sql(limit)
- when 'text'
- text_to_sql(limit)
- when 'blob'
- binary_to_sql(limit)
- when 'binary'
- if (0..0xfff) === limit
- "varbinary(#{limit})"
- else
+ sql = \
+ case type.to_s
+ when "integer"
+ integer_to_sql(limit)
+ when "text"
+ text_to_sql(limit)
+ when "blob"
binary_to_sql(limit)
+ when "binary"
+ if (0..0xfff) === limit
+ "varbinary(#{limit})"
+ else
+ binary_to_sql(limit)
+ end
+ else
+ super(type, limit, precision, scale)
end
- else
- super(type, limit, precision, scale)
- end
- sql << ' unsigned' if unsigned && type != :primary_key
+ sql << " unsigned" if unsigned && type != :primary_key
sql
end
# SHOW VARIABLES LIKE 'name'
def show_variable(name)
- select_value("SELECT @@#{name}", 'SCHEMA')
+ select_value("SELECT @@#{name}", "SCHEMA")
rescue ActiveRecord::StatementInvalid
nil
end
@@ -593,7 +594,7 @@ module ActiveRecord
schema, name = extract_schema_qualified_name(table_name)
- select_values(<<-SQL.strip_heredoc, 'SCHEMA')
+ select_values(<<-SQL.strip_heredoc, "SCHEMA")
SELECT column_name
FROM information_schema.key_column_usage
WHERE constraint_name = 'PRIMARY'
@@ -604,7 +605,7 @@ module ActiveRecord
end
def case_sensitive_comparison(table, attribute, column, value)
- if !value.nil? && column.collation && !column.case_sensitive?
+ if column.collation && !column.case_sensitive?
table[attribute].eq(Arel::Nodes::Bin.new(Arel::Nodes::BindParam.new))
else
super
@@ -625,10 +626,10 @@ module ActiveRecord
# Convert Arel node to string
s = s.to_sql unless s.is_a?(String)
# Remove any ASC/DESC modifiers
- s.gsub(/\s+(?:ASC|DESC)\b/i, '')
+ s.gsub(/\s+(?:ASC|DESC)\b/i, "")
}.reject(&:blank?).map.with_index { |column, i| "#{column} AS alias_#{i}" }
- [super, *order_columns].join(', ')
+ [super, *order_columns].join(", ")
end
def strict_mode?
@@ -639,342 +640,367 @@ module ActiveRecord
!native_database_types[type].nil?
end
- protected
-
- def initialize_type_map(m) # :nodoc:
- super
+ private
- register_class_with_limit m, %r(char)i, MysqlString
+ def initialize_type_map(m)
+ super
- m.register_type %r(tinytext)i, Type::Text.new(limit: 2**8 - 1)
- m.register_type %r(tinyblob)i, Type::Binary.new(limit: 2**8 - 1)
- m.register_type %r(text)i, Type::Text.new(limit: 2**16 - 1)
- m.register_type %r(blob)i, Type::Binary.new(limit: 2**16 - 1)
- m.register_type %r(mediumtext)i, Type::Text.new(limit: 2**24 - 1)
- m.register_type %r(mediumblob)i, Type::Binary.new(limit: 2**24 - 1)
- m.register_type %r(longtext)i, Type::Text.new(limit: 2**32 - 1)
- m.register_type %r(longblob)i, Type::Binary.new(limit: 2**32 - 1)
- m.register_type %r(^float)i, Type::Float.new(limit: 24)
- m.register_type %r(^double)i, Type::Float.new(limit: 53)
- m.register_type %r(^json)i, MysqlJson.new
+ register_class_with_limit m, %r(char)i, MysqlString
+
+ m.register_type %r(tinytext)i, Type::Text.new(limit: 2**8 - 1)
+ m.register_type %r(tinyblob)i, Type::Binary.new(limit: 2**8 - 1)
+ m.register_type %r(text)i, Type::Text.new(limit: 2**16 - 1)
+ m.register_type %r(blob)i, Type::Binary.new(limit: 2**16 - 1)
+ m.register_type %r(mediumtext)i, Type::Text.new(limit: 2**24 - 1)
+ m.register_type %r(mediumblob)i, Type::Binary.new(limit: 2**24 - 1)
+ m.register_type %r(longtext)i, Type::Text.new(limit: 2**32 - 1)
+ m.register_type %r(longblob)i, Type::Binary.new(limit: 2**32 - 1)
+ m.register_type %r(^float)i, Type::Float.new(limit: 24)
+ m.register_type %r(^double)i, Type::Float.new(limit: 53)
+ m.register_type %r(^json)i, MysqlJson.new
+
+ register_integer_type m, %r(^bigint)i, limit: 8
+ register_integer_type m, %r(^int)i, limit: 4
+ register_integer_type m, %r(^mediumint)i, limit: 3
+ register_integer_type m, %r(^smallint)i, limit: 2
+ register_integer_type m, %r(^tinyint)i, limit: 1
+
+ m.register_type %r(^tinyint\(1\))i, Type::Boolean.new if emulate_booleans
+ m.alias_type %r(year)i, "integer"
+ m.alias_type %r(bit)i, "binary"
+
+ m.register_type(%r(enum)i) do |sql_type|
+ limit = sql_type[/^enum\((.+)\)/i, 1]
+ .split(",").map { |enum| enum.strip.length - 2 }.max
+ MysqlString.new(limit: limit)
+ end
- register_integer_type m, %r(^bigint)i, limit: 8
- register_integer_type m, %r(^int)i, limit: 4
- register_integer_type m, %r(^mediumint)i, limit: 3
- register_integer_type m, %r(^smallint)i, limit: 2
- register_integer_type m, %r(^tinyint)i, limit: 1
+ m.register_type(%r(^set)i) do |sql_type|
+ limit = sql_type[/^set\((.+)\)/i, 1]
+ .split(",").map { |set| set.strip.length - 1 }.sum - 1
+ MysqlString.new(limit: limit)
+ end
+ end
- m.register_type %r(^tinyint\(1\))i, Type::Boolean.new if emulate_booleans
- m.alias_type %r(year)i, 'integer'
- m.alias_type %r(bit)i, 'binary'
+ def register_integer_type(mapping, key, options)
+ mapping.register_type(key) do |sql_type|
+ if /\bunsigned\b/.match?(sql_type)
+ Type::UnsignedInteger.new(options)
+ else
+ Type::Integer.new(options)
+ end
+ end
+ end
- m.register_type(%r(enum)i) do |sql_type|
- limit = sql_type[/^enum\((.+)\)/i, 1]
- .split(',').map{|enum| enum.strip.length - 2}.max
- MysqlString.new(limit: limit)
+ def extract_precision(sql_type)
+ if /time/.match?(sql_type)
+ super || 0
+ else
+ super
+ end
end
- m.register_type(%r(^set)i) do |sql_type|
- limit = sql_type[/^set\((.+)\)/i, 1]
- .split(',').map{|set| set.strip.length - 1}.sum - 1
- MysqlString.new(limit: limit)
+ def fetch_type_metadata(sql_type, extra = "")
+ MySQL::TypeMetadata.new(super(sql_type), extra: extra)
end
- end
- def register_integer_type(mapping, key, options) # :nodoc:
- mapping.register_type(key) do |sql_type|
- if /\bunsigned\z/ === sql_type
- Type::UnsignedInteger.new(options)
- else
- Type::Integer.new(options)
+ def add_index_length(quoted_columns, **options)
+ if length = options[:length]
+ case length
+ when Hash
+ length = length.symbolize_keys
+ quoted_columns.each { |name, column| column << "(#{length[name]})" if length[name].present? }
+ when Integer
+ quoted_columns.each { |name, column| column << "(#{length})" }
+ end
end
+
+ quoted_columns
end
- end
- def extract_precision(sql_type)
- if /time/ === sql_type
- super || 0
- else
+ def add_options_for_index_columns(quoted_columns, **options)
+ quoted_columns = add_index_length(quoted_columns, options)
super
end
- end
- def fetch_type_metadata(sql_type, extra = "")
- MySQL::TypeMetadata.new(super(sql_type), extra: extra, strict: strict_mode?)
- end
-
- def add_index_length(option_strings, column_names, options = {})
- if options.is_a?(Hash) && length = options[:length]
- case length
- when Hash
- column_names.each {|name| option_strings[name] += "(#{length[name]})" if length.has_key?(name) && length[name].present?}
- when Integer
- column_names.each {|name| option_strings[name] += "(#{length})"}
+ # See https://dev.mysql.com/doc/refman/5.7/en/error-messages-server.html
+ ER_DUP_ENTRY = 1062
+ ER_NOT_NULL_VIOLATION = 1048
+ ER_DO_NOT_HAVE_DEFAULT = 1364
+ ER_NO_REFERENCED_ROW_2 = 1452
+ ER_DATA_TOO_LONG = 1406
+ ER_OUT_OF_RANGE = 1264
+ ER_LOCK_DEADLOCK = 1213
+ ER_CANNOT_ADD_FOREIGN = 1215
+ ER_CANNOT_CREATE_TABLE = 1005
+
+ def translate_exception(exception, message)
+ case error_number(exception)
+ when ER_DUP_ENTRY
+ RecordNotUnique.new(message)
+ when ER_NO_REFERENCED_ROW_2
+ InvalidForeignKey.new(message)
+ when ER_CANNOT_ADD_FOREIGN
+ mismatched_foreign_key(message)
+ when ER_CANNOT_CREATE_TABLE
+ if message.include?("errno: 150")
+ mismatched_foreign_key(message)
+ else
+ super
+ end
+ when ER_DATA_TOO_LONG
+ ValueTooLong.new(message)
+ when ER_OUT_OF_RANGE
+ RangeError.new(message)
+ when ER_NOT_NULL_VIOLATION, ER_DO_NOT_HAVE_DEFAULT
+ NotNullViolation.new(message)
+ when ER_LOCK_DEADLOCK
+ Deadlocked.new(message)
+ else
+ super
end
end
- return option_strings
- end
-
- def quoted_columns_for_index(column_names, options = {})
- option_strings = Hash[column_names.map {|name| [name, '']}]
+ def add_column_sql(table_name, column_name, type, options = {})
+ td = create_table_definition(table_name)
+ cd = td.new_column_definition(column_name, type, options)
+ schema_creation.accept(AddColumnDefinition.new(cd))
+ end
- # add index length
- option_strings = add_index_length(option_strings, column_names, options)
+ def change_column_sql(table_name, column_name, type, options = {})
+ column = column_for(table_name, column_name)
- # add index sort order
- option_strings = add_index_sort_order(option_strings, column_names, options)
+ unless options_include_default?(options)
+ options[:default] = column.default
+ end
- column_names.map {|name| quote_column_name(name) + option_strings[name]}
- end
+ unless options.has_key?(:null)
+ options[:null] = column.null
+ end
- # See https://dev.mysql.com/doc/refman/5.7/en/error-messages-server.html
- ER_DUP_ENTRY = 1062
- ER_NO_REFERENCED_ROW_2 = 1452
- ER_DATA_TOO_LONG = 1406
- ER_LOCK_DEADLOCK = 1213
+ unless options.key?(:comment)
+ options[:comment] = column.comment
+ end
- def translate_exception(exception, message)
- case error_number(exception)
- when ER_DUP_ENTRY
- RecordNotUnique.new(message)
- when ER_NO_REFERENCED_ROW_2
- InvalidForeignKey.new(message)
- when ER_DATA_TOO_LONG
- ValueTooLong.new(message)
- when ER_LOCK_DEADLOCK
- TransactionSerializationError.new(message)
- else
- super
+ td = create_table_definition(table_name)
+ cd = td.new_column_definition(column.name, type, options)
+ schema_creation.accept(ChangeColumnDefinition.new(cd, column.name))
end
- end
-
- def add_column_sql(table_name, column_name, type, options = {})
- td = create_table_definition(table_name)
- cd = td.new_column_definition(column_name, type, options)
- schema_creation.accept(AddColumnDefinition.new(cd))
- end
- def change_column_sql(table_name, column_name, type, options = {})
- column = column_for(table_name, column_name)
+ def rename_column_sql(table_name, column_name, new_column_name)
+ column = column_for(table_name, column_name)
+ options = {
+ default: column.default,
+ null: column.null,
+ auto_increment: column.auto_increment?
+ }
- unless options_include_default?(options)
- options[:default] = column.default
+ current_type = select_one("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE '#{column_name}'", "SCHEMA")["Type"]
+ td = create_table_definition(table_name)
+ cd = td.new_column_definition(new_column_name, current_type, options)
+ schema_creation.accept(ChangeColumnDefinition.new(cd, column.name))
end
- unless options.has_key?(:null)
- options[:null] = column.null
+ def remove_column_sql(table_name, column_name, type = nil, options = {})
+ "DROP #{quote_column_name(column_name)}"
end
- td = create_table_definition(table_name)
- cd = td.new_column_definition(column.name, type, options)
- schema_creation.accept(ChangeColumnDefinition.new(cd, column.name))
- end
-
- def rename_column_sql(table_name, column_name, new_column_name)
- column = column_for(table_name, column_name)
- options = {
- default: column.default,
- null: column.null,
- auto_increment: column.auto_increment?
- }
-
- current_type = select_one("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE '#{column_name}'", 'SCHEMA')["Type"]
- td = create_table_definition(table_name)
- cd = td.new_column_definition(new_column_name, current_type, options)
- schema_creation.accept(ChangeColumnDefinition.new(cd, column.name))
- end
-
- def remove_column_sql(table_name, column_name, type = nil, options = {})
- "DROP #{quote_column_name(column_name)}"
- end
+ def remove_columns_sql(table_name, *column_names)
+ column_names.map { |column_name| remove_column_sql(table_name, column_name) }
+ end
- def remove_columns_sql(table_name, *column_names)
- column_names.map {|column_name| remove_column_sql(table_name, column_name) }
- end
+ def add_index_sql(table_name, column_name, options = {})
+ index_name, index_type, index_columns, _, index_algorithm, index_using = add_index_options(table_name, column_name, options)
+ index_algorithm[0, 0] = ", " if index_algorithm.present?
+ "ADD #{index_type} INDEX #{quote_column_name(index_name)} #{index_using} (#{index_columns})#{index_algorithm}"
+ end
- def add_index_sql(table_name, column_name, options = {})
- index_name, index_type, index_columns, _, index_algorithm, index_using = add_index_options(table_name, column_name, options)
- index_algorithm[0, 0] = ", " if index_algorithm.present?
- "ADD #{index_type} INDEX #{quote_column_name(index_name)} #{index_using} (#{index_columns})#{index_algorithm}"
- end
+ def remove_index_sql(table_name, options = {})
+ index_name = index_name_for_remove(table_name, options)
+ "DROP INDEX #{index_name}"
+ end
- def remove_index_sql(table_name, options = {})
- index_name = index_name_for_remove(table_name, options)
- "DROP INDEX #{index_name}"
- end
+ def add_timestamps_sql(table_name, options = {})
+ [add_column_sql(table_name, :created_at, :datetime, options), add_column_sql(table_name, :updated_at, :datetime, options)]
+ end
- def add_timestamps_sql(table_name, options = {})
- [add_column_sql(table_name, :created_at, :datetime, options), add_column_sql(table_name, :updated_at, :datetime, options)]
- end
+ def remove_timestamps_sql(table_name, options = {})
+ [remove_column_sql(table_name, :updated_at), remove_column_sql(table_name, :created_at)]
+ end
- def remove_timestamps_sql(table_name, options = {})
- [remove_column_sql(table_name, :updated_at), remove_column_sql(table_name, :created_at)]
- end
+ # MySQL is too stupid to create a temporary table for use subquery, so we have
+ # to give it some prompting in the form of a subsubquery. Ugh!
+ def subquery_for(key, select)
+ subsubselect = select.clone
+ subsubselect.projections = [key]
- private
+ # Materialize subquery by adding distinct
+ # to work with MySQL 5.7.6 which sets optimizer_switch='derived_merge=on'
+ subsubselect.distinct unless select.limit || select.offset || select.orders.any?
- # MySQL is too stupid to create a temporary table for use subquery, so we have
- # to give it some prompting in the form of a subsubquery. Ugh!
- def subquery_for(key, select)
- subsubselect = select.clone
- subsubselect.projections = [key]
+ subselect = Arel::SelectManager.new(select.engine)
+ subselect.project Arel.sql(key.name)
+ subselect.from subsubselect.as("__active_record_temp")
+ end
- # Materialize subquery by adding distinct
- # to work with MySQL 5.7.6 which sets optimizer_switch='derived_merge=on'
- subsubselect.distinct unless select.limit || select.offset || select.orders.any?
+ def supports_rename_index?
+ mariadb? ? false : version >= "5.7.6"
+ end
- subselect = Arel::SelectManager.new(select.engine)
- subselect.project Arel.sql(key.name)
- subselect.from subsubselect.as('__active_record_temp')
- end
+ def configure_connection
+ variables = @config.fetch(:variables, {}).stringify_keys
- def supports_rename_index?
- mariadb? ? false : version >= '5.7.6'
- end
+ # By default, MySQL 'where id is null' selects the last inserted id; Turn this off.
+ variables["sql_auto_is_null"] = 0
- def configure_connection
- variables = @config.fetch(:variables, {}).stringify_keys
+ # Increase timeout so the server doesn't disconnect us.
+ wait_timeout = @config[:wait_timeout]
+ wait_timeout = 2147483 unless wait_timeout.is_a?(Integer)
+ variables["wait_timeout"] = self.class.type_cast_config_to_integer(wait_timeout)
- # By default, MySQL 'where id is null' selects the last inserted id; Turn this off.
- variables['sql_auto_is_null'] = 0
+ defaults = [":default", :default].to_set
- # Increase timeout so the server doesn't disconnect us.
- wait_timeout = @config[:wait_timeout]
- wait_timeout = 2147483 unless wait_timeout.is_a?(Integer)
- variables['wait_timeout'] = self.class.type_cast_config_to_integer(wait_timeout)
+ # Make MySQL reject illegal values rather than truncating or blanking them, see
+ # http://dev.mysql.com/doc/refman/5.7/en/sql-mode.html#sqlmode_strict_all_tables
+ # If the user has provided another value for sql_mode, don't replace it.
+ if sql_mode = variables.delete("sql_mode")
+ sql_mode = quote(sql_mode)
+ elsif !defaults.include?(strict_mode?)
+ if strict_mode?
+ sql_mode = "CONCAT(@@sql_mode, ',STRICT_ALL_TABLES')"
+ else
+ sql_mode = "REPLACE(@@sql_mode, 'STRICT_TRANS_TABLES', '')"
+ sql_mode = "REPLACE(#{sql_mode}, 'STRICT_ALL_TABLES', '')"
+ sql_mode = "REPLACE(#{sql_mode}, 'TRADITIONAL', '')"
+ end
+ sql_mode = "CONCAT(#{sql_mode}, ',NO_AUTO_VALUE_ON_ZERO')"
+ end
+ sql_mode_assignment = "@@SESSION.sql_mode = #{sql_mode}, " if sql_mode
+
+ # NAMES does not have an equals sign, see
+ # http://dev.mysql.com/doc/refman/5.7/en/set-statement.html#id944430
+ # (trailing comma because variable_assignments will always have content)
+ if @config[:encoding]
+ encoding = "NAMES #{@config[:encoding]}"
+ encoding << " COLLATE #{@config[:collation]}" if @config[:collation]
+ encoding << ", "
+ end
- defaults = [':default', :default].to_set
+ # Gather up all of the SET variables...
+ variable_assignments = variables.map do |k, v|
+ if defaults.include?(v)
+ "@@SESSION.#{k} = DEFAULT" # Sets the value to the global or compile default
+ elsif !v.nil?
+ "@@SESSION.#{k} = #{quote(v)}"
+ end
+ # or else nil; compact to clear nils out
+ end.compact.join(", ")
- # Make MySQL reject illegal values rather than truncating or blanking them, see
- # http://dev.mysql.com/doc/refman/5.7/en/sql-mode.html#sqlmode_strict_all_tables
- # If the user has provided another value for sql_mode, don't replace it.
- if sql_mode = variables.delete('sql_mode')
- sql_mode = quote(sql_mode)
- elsif !defaults.include?(strict_mode?)
- if strict_mode?
- sql_mode = "CONCAT(@@sql_mode, ',STRICT_ALL_TABLES')"
- else
- sql_mode = "REPLACE(@@sql_mode, 'STRICT_TRANS_TABLES', '')"
- sql_mode = "REPLACE(#{sql_mode}, 'STRICT_ALL_TABLES', '')"
- sql_mode = "REPLACE(#{sql_mode}, 'TRADITIONAL', '')"
- end
- sql_mode = "CONCAT(#{sql_mode}, ',NO_AUTO_VALUE_ON_ZERO')"
+ # ...and send them all in one query
+ execute "SET #{encoding} #{sql_mode_assignment} #{variable_assignments}"
end
- sql_mode_assignment = "@@SESSION.sql_mode = #{sql_mode}, " if sql_mode
- # NAMES does not have an equals sign, see
- # http://dev.mysql.com/doc/refman/5.7/en/set-statement.html#id944430
- # (trailing comma because variable_assignments will always have content)
- if @config[:encoding]
- encoding = "NAMES #{@config[:encoding]}"
- encoding << " COLLATE #{@config[:collation]}" if @config[:collation]
- encoding << ", "
+ def column_definitions(table_name) # :nodoc:
+ execute_and_free("SHOW FULL FIELDS FROM #{quote_table_name(table_name)}", "SCHEMA") do |result|
+ each_hash(result)
+ end
end
- # Gather up all of the SET variables...
- variable_assignments = variables.map do |k, v|
- if defaults.include?(v)
- "@@SESSION.#{k} = DEFAULT" # Sets the value to the global or compile default
- elsif !v.nil?
- "@@SESSION.#{k} = #{quote(v)}"
+ def extract_foreign_key_action(specifier) # :nodoc:
+ case specifier
+ when "CASCADE"; :cascade
+ when "SET NULL"; :nullify
end
- # or else nil; compact to clear nils out
- end.compact.join(', ')
-
- # ...and send them all in one query
- @connection.query "SET #{encoding} #{sql_mode_assignment} #{variable_assignments}"
- end
-
- def column_definitions(table_name) # :nodoc:
- execute_and_free("SHOW FULL FIELDS FROM #{quote_table_name(table_name)}", 'SCHEMA') do |result|
- each_hash(result)
end
- end
- def extract_foreign_key_action(specifier) # :nodoc:
- case specifier
- when 'CASCADE'; :cascade
- when 'SET NULL'; :nullify
+ def create_table_info(table_name) # :nodoc:
+ select_one("SHOW CREATE TABLE #{quote_table_name(table_name)}")["Create Table"]
end
- end
-
- def create_table_info(table_name) # :nodoc:
- select_one("SHOW CREATE TABLE #{quote_table_name(table_name)}")["Create Table"]
- end
-
- def create_table_definition(*args) # :nodoc:
- MySQL::TableDefinition.new(*args)
- end
- def extract_schema_qualified_name(string) # :nodoc:
- schema, name = string.to_s.scan(/[^`.\s]+|`[^`]*`/)
- schema, name = @config[:database], schema unless name
- [schema, name]
- end
+ def create_table_definition(*args) # :nodoc:
+ MySQL::TableDefinition.new(*args)
+ end
- def integer_to_sql(limit) # :nodoc:
- case limit
- when 1; 'tinyint'
- when 2; 'smallint'
- when 3; 'mediumint'
- when nil, 4; 'int'
- when 5..8; 'bigint'
- else raise(ActiveRecordError, "No integer type has byte size #{limit}")
+ def mismatched_foreign_key(message)
+ parts = message.scan(/`(\w+)`[ $)]/).flatten
+ MismatchedForeignKey.new(
+ self,
+ message: message,
+ table: parts[0],
+ foreign_key: parts[1],
+ target_table: parts[2],
+ primary_key: parts[3],
+ )
end
- end
- def text_to_sql(limit) # :nodoc:
- case limit
- when 0..0xff; 'tinytext'
- when nil, 0x100..0xffff; 'text'
- when 0x10000..0xffffff; 'mediumtext'
- when 0x1000000..0xffffffff; 'longtext'
- else raise(ActiveRecordError, "No text type has byte length #{limit}")
+ def extract_schema_qualified_name(string) # :nodoc:
+ schema, name = string.to_s.scan(/[^`.\s]+|`[^`]*`/)
+ schema, name = @config[:database], schema unless name
+ [schema, name]
end
- end
- def binary_to_sql(limit) # :nodoc:
- case limit
- when 0..0xff; 'tinyblob'
- when nil, 0x100..0xffff; 'blob'
- when 0x10000..0xffffff; 'mediumblob'
- when 0x1000000..0xffffffff; 'longblob'
- else raise(ActiveRecordError, "No binary type has byte length #{limit}")
+ def integer_to_sql(limit) # :nodoc:
+ case limit
+ when 1; "tinyint"
+ when 2; "smallint"
+ when 3; "mediumint"
+ when nil, 4; "int"
+ when 5..8; "bigint"
+ else raise(ActiveRecordError, "No integer type has byte size #{limit}. Use a decimal with scale 0 instead.")
+ end
end
- end
- class MysqlJson < Type::Internal::AbstractJson # :nodoc:
- def changed_in_place?(raw_old_value, new_value)
- # Normalization is required because MySQL JSON data format includes
- # the space between the elements.
- super(serialize(deserialize(raw_old_value)), new_value)
+ def text_to_sql(limit) # :nodoc:
+ case limit
+ when 0..0xff; "tinytext"
+ when nil, 0x100..0xffff; "text"
+ when 0x10000..0xffffff; "mediumtext"
+ when 0x1000000..0xffffffff; "longtext"
+ else raise(ActiveRecordError, "No text type has byte length #{limit}")
+ end
end
- end
- class MysqlString < Type::String # :nodoc:
- def serialize(value)
- case value
- when true then MySQL::Quoting::QUOTED_TRUE
- when false then MySQL::Quoting::QUOTED_FALSE
- else super
+ def binary_to_sql(limit) # :nodoc:
+ case limit
+ when 0..0xff; "tinyblob"
+ when nil, 0x100..0xffff; "blob"
+ when 0x10000..0xffffff; "mediumblob"
+ when 0x1000000..0xffffffff; "longblob"
+ else raise(ActiveRecordError, "No binary type has byte length #{limit}")
end
end
- private
+ class MysqlJson < Type::Internal::AbstractJson # :nodoc:
+ def changed_in_place?(raw_old_value, new_value)
+ # Normalization is required because MySQL JSON data format includes
+ # the space between the elements.
+ super(serialize(deserialize(raw_old_value)), new_value)
+ end
+ end
- def cast_value(value)
- case value
- when true then MySQL::Quoting::QUOTED_TRUE
- when false then MySQL::Quoting::QUOTED_FALSE
- else super
+ class MysqlString < Type::String # :nodoc:
+ def serialize(value)
+ case value
+ when true then MySQL::Quoting::QUOTED_TRUE
+ when false then MySQL::Quoting::QUOTED_FALSE
+ else super
+ end
end
+
+ private
+
+ def cast_value(value)
+ case value
+ when true then MySQL::Quoting::QUOTED_TRUE
+ when false then MySQL::Quoting::QUOTED_FALSE
+ else super
+ end
+ end
end
- end
- ActiveRecord::Type.register(:json, MysqlJson, adapter: :mysql2)
- ActiveRecord::Type.register(:string, MysqlString, adapter: :mysql2)
- ActiveRecord::Type.register(:unsigned_integer, Type::UnsignedInteger, adapter: :mysql2)
+ ActiveRecord::Type.register(:json, MysqlJson, adapter: :mysql2)
+ ActiveRecord::Type.register(:string, MysqlString, adapter: :mysql2)
+ ActiveRecord::Type.register(:unsigned_integer, Type::UnsignedInteger, adapter: :mysql2)
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/column.rb b/activerecord/lib/active_record/connection_adapters/column.rb
index 28f0c8686a..61cd7ae4cc 100644
--- a/activerecord/lib/active_record/connection_adapters/column.rb
+++ b/activerecord/lib/active_record/connection_adapters/column.rb
@@ -29,7 +29,7 @@ module ActiveRecord
end
def bigint?
- /\Abigint\b/ === sql_type
+ /\Abigint\b/.match?(sql_type)
end
# Returns the human name of the column name.
@@ -40,6 +40,28 @@ module ActiveRecord
Base.human_attribute_name(@name)
end
+ def init_with(coder)
+ @name = coder["name"]
+ @table_name = coder["table_name"]
+ @sql_type_metadata = coder["sql_type_metadata"]
+ @null = coder["null"]
+ @default = coder["default"]
+ @default_function = coder["default_function"]
+ @collation = coder["collation"]
+ @comment = coder["comment"]
+ end
+
+ def encode_with(coder)
+ coder["name"] = @name
+ coder["table_name"] = @table_name
+ coder["sql_type_metadata"] = @sql_type_metadata
+ coder["null"] = @null
+ coder["default"] = @default
+ coder["default_function"] = @default_function
+ coder["collation"] = @collation
+ coder["comment"] = @comment
+ end
+
def ==(other)
other.is_a?(Column) &&
attributes_for_hash == other.attributes_for_hash
@@ -52,9 +74,9 @@ module ActiveRecord
protected
- def attributes_for_hash
- [self.class, name, default, sql_type_metadata, null, table_name, default_function, collation]
- end
+ def attributes_for_hash
+ [self.class, name, default, sql_type_metadata, null, table_name, default_function, collation]
+ end
end
class NullColumn < Column
diff --git a/activerecord/lib/active_record/connection_adapters/connection_specification.rb b/activerecord/lib/active_record/connection_adapters/connection_specification.rb
index 346916337e..dcf56997db 100644
--- a/activerecord/lib/active_record/connection_adapters/connection_specification.rb
+++ b/activerecord/lib/active_record/connection_adapters/connection_specification.rb
@@ -1,4 +1,4 @@
-require 'uri'
+require "uri"
module ActiveRecord
module ConnectionAdapters
@@ -19,7 +19,6 @@ module ActiveRecord
# Expands a connection string into a hash.
class ConnectionUrlResolver # :nodoc:
-
# == Example
#
# url = "postgresql://foo:bar@localhost:9000/foo_test?pool=5&timeout=3000"
@@ -37,11 +36,11 @@ module ActiveRecord
def initialize(url)
raise "Database URL cannot be empty" if url.blank?
@uri = uri_parser.parse(url)
- @adapter = @uri.scheme && @uri.scheme.tr('-', '_')
+ @adapter = @uri.scheme && @uri.scheme.tr("-", "_")
@adapter = "postgresql" if @adapter == "postgres"
if @uri.opaque
- @uri.opaque, @query = @uri.opaque.split('?', 2)
+ @uri.opaque, @query = @uri.opaque.split("?", 2)
else
@query = @uri.query
end
@@ -49,65 +48,65 @@ module ActiveRecord
# Converts the given URL to a full connection hash.
def to_hash
- config = raw_config.reject { |_,value| value.blank? }
- config.map { |key,value| config[key] = uri_parser.unescape(value) if value.is_a? String }
+ config = raw_config.reject { |_, value| value.blank? }
+ config.map { |key, value| config[key] = uri_parser.unescape(value) if value.is_a? String }
config
end
private
- def uri
- @uri
- end
+ def uri
+ @uri
+ end
- def uri_parser
- @uri_parser ||= URI::Parser.new
- end
+ def uri_parser
+ @uri_parser ||= URI::Parser.new
+ end
- # Converts the query parameters of the URI into a hash.
- #
- # "localhost?pool=5&reaping_frequency=2"
- # # => { "pool" => "5", "reaping_frequency" => "2" }
- #
- # returns empty hash if no query present.
- #
- # "localhost"
- # # => {}
- def query_hash
- Hash[(@query || '').split("&").map { |pair| pair.split("=") }]
- end
+ # Converts the query parameters of the URI into a hash.
+ #
+ # "localhost?pool=5&reaping_frequency=2"
+ # # => { "pool" => "5", "reaping_frequency" => "2" }
+ #
+ # returns empty hash if no query present.
+ #
+ # "localhost"
+ # # => {}
+ def query_hash
+ Hash[(@query || "").split("&").map { |pair| pair.split("=") }]
+ end
- def raw_config
- if uri.opaque
- query_hash.merge({
- "adapter" => @adapter,
- "database" => uri.opaque })
- else
- query_hash.merge({
- "adapter" => @adapter,
- "username" => uri.user,
- "password" => uri.password,
- "port" => uri.port,
- "database" => database_from_path,
- "host" => uri.hostname })
+ def raw_config
+ if uri.opaque
+ query_hash.merge(
+ "adapter" => @adapter,
+ "database" => uri.opaque)
+ else
+ query_hash.merge(
+ "adapter" => @adapter,
+ "username" => uri.user,
+ "password" => uri.password,
+ "port" => uri.port,
+ "database" => database_from_path,
+ "host" => uri.hostname)
+ end
end
- end
- # Returns name of the database.
- def database_from_path
- if @adapter == 'sqlite3'
- # 'sqlite3:/foo' is absolute, because that makes sense. The
- # corresponding relative version, 'sqlite3:foo', is handled
- # elsewhere, as an "opaque".
+ # Returns name of the database.
+ def database_from_path
+ if @adapter == "sqlite3"
+ # 'sqlite3:/foo' is absolute, because that makes sense. The
+ # corresponding relative version, 'sqlite3:foo', is handled
+ # elsewhere, as an "opaque".
- uri.path
- else
- # Only SQLite uses a filename as the "database" name; for
- # anything else, a leading slash would be silly.
+ uri.path
+ else
+ # Only SQLite uses a filename as the "database" name; for
+ # anything else, a leading slash would be silly.
- uri.path.sub(%r{^/}, "")
+ uri.path.sub(%r{^/}, "")
+ end
end
- end
end
##
@@ -193,72 +192,72 @@ module ActiveRecord
private
- # Returns fully resolved connection, accepts hash, string or symbol.
- # Always returns a hash.
- #
- # == Examples
- #
- # Symbol representing current environment.
- #
- # Resolver.new("production" => {}).resolve_connection(:production)
- # # => {}
- #
- # One layer deep hash of connection values.
- #
- # Resolver.new({}).resolve_connection("adapter" => "sqlite3")
- # # => { "adapter" => "sqlite3" }
- #
- # Connection URL.
- #
- # Resolver.new({}).resolve_connection("postgresql://localhost/foo")
- # # => { "host" => "localhost", "database" => "foo", "adapter" => "postgresql" }
- #
- def resolve_connection(spec)
- case spec
- when Symbol
- resolve_symbol_connection spec
- when String
- resolve_url_connection spec
- when Hash
- resolve_hash_connection spec
+ # Returns fully resolved connection, accepts hash, string or symbol.
+ # Always returns a hash.
+ #
+ # == Examples
+ #
+ # Symbol representing current environment.
+ #
+ # Resolver.new("production" => {}).resolve_connection(:production)
+ # # => {}
+ #
+ # One layer deep hash of connection values.
+ #
+ # Resolver.new({}).resolve_connection("adapter" => "sqlite3")
+ # # => { "adapter" => "sqlite3" }
+ #
+ # Connection URL.
+ #
+ # Resolver.new({}).resolve_connection("postgresql://localhost/foo")
+ # # => { "host" => "localhost", "database" => "foo", "adapter" => "postgresql" }
+ #
+ def resolve_connection(spec)
+ case spec
+ when Symbol
+ resolve_symbol_connection spec
+ when String
+ resolve_url_connection spec
+ when Hash
+ resolve_hash_connection spec
+ end
end
- end
- # Takes the environment such as +:production+ or +:development+.
- # This requires that the @configurations was initialized with a key that
- # matches.
- #
- # Resolver.new("production" => {}).resolve_symbol_connection(:production)
- # # => {}
- #
- def resolve_symbol_connection(spec)
- if config = configurations[spec.to_s]
- resolve_connection(config).merge("name" => spec.to_s)
- else
- raise(AdapterNotSpecified, "'#{spec}' database is not configured. Available: #{configurations.keys.inspect}")
+ # Takes the environment such as +:production+ or +:development+.
+ # This requires that the @configurations was initialized with a key that
+ # matches.
+ #
+ # Resolver.new("production" => {}).resolve_symbol_connection(:production)
+ # # => {}
+ #
+ def resolve_symbol_connection(spec)
+ if config = configurations[spec.to_s]
+ resolve_connection(config).merge("name" => spec.to_s)
+ else
+ raise(AdapterNotSpecified, "'#{spec}' database is not configured. Available: #{configurations.keys.inspect}")
+ end
end
- end
- # Accepts a hash. Expands the "url" key that contains a
- # URL database connection to a full connection
- # hash and merges with the rest of the hash.
- # Connection details inside of the "url" key win any merge conflicts
- def resolve_hash_connection(spec)
- if spec["url"] && spec["url"] !~ /^jdbc:/
- connection_hash = resolve_url_connection(spec.delete("url"))
- spec.merge!(connection_hash)
+ # Accepts a hash. Expands the "url" key that contains a
+ # URL database connection to a full connection
+ # hash and merges with the rest of the hash.
+ # Connection details inside of the "url" key win any merge conflicts
+ def resolve_hash_connection(spec)
+ if spec["url"] && spec["url"] !~ /^jdbc:/
+ connection_hash = resolve_url_connection(spec.delete("url"))
+ spec.merge!(connection_hash)
+ end
+ spec
end
- spec
- end
- # Takes a connection URL.
- #
- # Resolver.new({}).resolve_url_connection("postgresql://localhost/foo")
- # # => { "host" => "localhost", "database" => "foo", "adapter" => "postgresql" }
- #
- def resolve_url_connection(url)
- ConnectionUrlResolver.new(url).to_hash
- end
+ # Takes a connection URL.
+ #
+ # Resolver.new({}).resolve_url_connection("postgresql://localhost/foo")
+ # # => { "host" => "localhost", "database" => "foo", "adapter" => "postgresql" }
+ #
+ def resolve_url_connection(url)
+ ConnectionUrlResolver.new(url).to_hash
+ end
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/mysql/column.rb b/activerecord/lib/active_record/connection_adapters/mysql/column.rb
index ea554b188c..1499c1681f 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql/column.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql/column.rb
@@ -2,40 +2,18 @@ module ActiveRecord
module ConnectionAdapters
module MySQL
class Column < ConnectionAdapters::Column # :nodoc:
- delegate :strict, :extra, to: :sql_type_metadata, allow_nil: true
-
- def initialize(*)
- super
- extract_default
- end
-
- def has_default?
- return false if blob_or_text_column? # MySQL forbids defaults on blob and text columns
- super
- end
-
- def blob_or_text_column?
- /\A(?:tiny|medium|long)?blob\b/ === sql_type || type == :text
- end
+ delegate :extra, to: :sql_type_metadata, allow_nil: true
def unsigned?
- /\bunsigned\z/ === sql_type
+ /\bunsigned(?: zerofill)?\z/.match?(sql_type)
end
def case_sensitive?
- collation && collation !~ /_ci\z/
+ collation && !/_ci\z/.match?(collation)
end
def auto_increment?
- extra == 'auto_increment'
- end
-
- private
-
- def extract_default
- if blob_or_text_column?
- @default = null || strict ? nil : ''
- end
+ extra == "auto_increment"
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/mysql/database_statements.rb b/activerecord/lib/active_record/connection_adapters/mysql/database_statements.rb
index 87f0ff7d85..78e7181266 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql/database_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql/database_statements.rb
@@ -24,16 +24,14 @@ module ActiveRecord
# Executes the SQL statement in the context of this connection.
def execute(sql, name = nil)
- if @connection
- # make sure we carry over any changes to ActiveRecord::Base.default_timezone that have been
- # made since we established the connection
- @connection.query_options[:database_timezone] = ActiveRecord::Base.default_timezone
- end
+ # make sure we carry over any changes to ActiveRecord::Base.default_timezone that have been
+ # made since we established the connection
+ @connection.query_options[:database_timezone] = ActiveRecord::Base.default_timezone
super
end
- def exec_query(sql, name = 'SQL', binds = [], prepare: false)
+ def exec_query(sql, name = "SQL", binds = [], prepare: false)
if without_prepared_statement?(binds)
execute_and_free(sql, name) do |result|
ActiveRecord::Result.new(result.fields, result.to_a) if result
@@ -45,7 +43,7 @@ module ActiveRecord
end
end
- def exec_delete(sql, name, binds)
+ def exec_delete(sql, name = nil, binds = [])
if without_prepared_statement?(binds)
execute_and_free(sql, name) { @connection.affected_rows }
else
@@ -54,58 +52,56 @@ module ActiveRecord
end
alias :exec_update :exec_delete
- protected
-
- def last_inserted_id(result)
- @connection.last_id
- end
-
private
- def select_result(sql, name = nil, binds = [])
- if without_prepared_statement?(binds)
- execute_and_free(sql, name) { |result| yield result }
- else
- exec_stmt_and_free(sql, name, binds, cache_stmt: true) { |_, result| yield result }
+ def last_inserted_id(result)
+ @connection.last_id
end
- end
- def exec_stmt_and_free(sql, name, binds, cache_stmt: false)
- if @connection
+ def select_result(sql, name = nil, binds = [])
+ if without_prepared_statement?(binds)
+ execute_and_free(sql, name) { |result| yield result }
+ else
+ exec_stmt_and_free(sql, name, binds, cache_stmt: true) { |_, result| yield result }
+ end
+ end
+
+ def exec_stmt_and_free(sql, name, binds, cache_stmt: false)
# make sure we carry over any changes to ActiveRecord::Base.default_timezone that have been
# made since we established the connection
@connection.query_options[:database_timezone] = ActiveRecord::Base.default_timezone
- end
- type_casted_binds = binds.map { |attr| type_cast(attr.value_for_database) }
-
- log(sql, name, binds) do
- if cache_stmt
- cache = @statements[sql] ||= {
- stmt: @connection.prepare(sql)
- }
- stmt = cache[:stmt]
- else
- stmt = @connection.prepare(sql)
- end
+ type_casted_binds = type_casted_binds(binds)
- begin
- result = stmt.execute(*type_casted_binds)
- rescue Mysql2::Error => e
+ log(sql, name, binds, type_casted_binds) do
if cache_stmt
- @statements.delete(sql)
+ cache = @statements[sql] ||= {
+ stmt: @connection.prepare(sql)
+ }
+ stmt = cache[:stmt]
else
- stmt.close
+ stmt = @connection.prepare(sql)
+ end
+
+ begin
+ result = ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
+ stmt.execute(*type_casted_binds)
+ end
+ rescue Mysql2::Error => e
+ if cache_stmt
+ @statements.delete(sql)
+ else
+ stmt.close
+ end
+ raise e
end
- raise e
- end
- ret = yield stmt, result
- result.free if result
- stmt.close unless cache_stmt
- ret
+ ret = yield stmt, result
+ result.free if result
+ stmt.close unless cache_stmt
+ ret
+ end
end
- end
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb b/activerecord/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb
index 1820853196..9691060cd3 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb
@@ -36,34 +36,34 @@ module ActiveRecord
private
- def compute_column_widths(result)
- [].tap do |widths|
- result.columns.each_with_index do |column, i|
- cells_in_column = [column] + result.rows.map {|r| r[i].nil? ? 'NULL' : r[i].to_s}
- widths << cells_in_column.map(&:length).max
+ def compute_column_widths(result)
+ [].tap do |widths|
+ result.columns.each_with_index do |column, i|
+ cells_in_column = [column] + result.rows.map { |r| r[i].nil? ? "NULL" : r[i].to_s }
+ widths << cells_in_column.map(&:length).max
+ end
end
end
- end
- def build_separator(widths)
- padding = 1
- '+' + widths.map {|w| '-' * (w + (padding*2))}.join('+') + '+'
- end
+ def build_separator(widths)
+ padding = 1
+ "+" + widths.map { |w| "-" * (w + (padding * 2)) }.join("+") + "+"
+ end
- def build_cells(items, widths)
- cells = []
- items.each_with_index do |item, i|
- item = 'NULL' if item.nil?
- justifier = item.is_a?(Numeric) ? 'rjust' : 'ljust'
- cells << item.to_s.send(justifier, widths[i])
+ def build_cells(items, widths)
+ cells = []
+ items.each_with_index do |item, i|
+ item = "NULL" if item.nil?
+ justifier = item.is_a?(Numeric) ? "rjust" : "ljust"
+ cells << item.to_s.send(justifier, widths[i])
+ end
+ "| " + cells.join(" | ") + " |"
end
- '| ' + cells.join(' | ') + ' |'
- end
- def build_footer(nrows, elapsed)
- rows_label = nrows == 1 ? 'row' : 'rows'
- "#{nrows} #{rows_label} in set (%.2f sec)" % elapsed
- end
+ def build_footer(nrows, elapsed)
+ rows_label = nrows == 1 ? "row" : "rows"
+ "#{nrows} #{rows_label} in set (%.2f sec)" % elapsed
+ end
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/mysql/quoting.rb b/activerecord/lib/active_record/connection_adapters/mysql/quoting.rb
index fbab654112..9d11ad28d4 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql/quoting.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql/quoting.rb
@@ -2,14 +2,14 @@ module ActiveRecord
module ConnectionAdapters
module MySQL
module Quoting # :nodoc:
- QUOTED_TRUE, QUOTED_FALSE = '1', '0'
+ QUOTED_TRUE, QUOTED_FALSE = "1".freeze, "0".freeze
def quote_column_name(name)
- @quoted_column_names[name] ||= "`#{super.gsub('`', '``')}`"
+ @quoted_column_names[name] ||= "`#{super.gsub('`', '``')}`".freeze
end
def quote_table_name(name)
- @quoted_table_names[name] ||= super.gsub('.', '`.`')
+ @quoted_table_names[name] ||= super.gsub(".", "`.`").freeze
end
def quoted_true
@@ -32,19 +32,19 @@ module ActiveRecord
if supports_datetime_with_precision?
super
else
- super.sub(/\.\d{6}\z/, '')
+ super.sub(/\.\d{6}\z/, "")
end
end
private
- def _quote(value)
- if value.is_a?(Type::Binary::Data)
- "x'#{value.hex}'"
- else
- super
+ def _quote(value)
+ if value.is_a?(Type::Binary::Data)
+ "x'#{value.hex}'"
+ else
+ super
+ end
end
- end
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/mysql/schema_creation.rb b/activerecord/lib/active_record/connection_adapters/mysql/schema_creation.rb
index fd2dc2aee8..d808b50332 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql/schema_creation.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql/schema_creation.rb
@@ -7,60 +7,60 @@ module ActiveRecord
private
- def visit_DropForeignKey(name)
- "DROP FOREIGN KEY #{name}"
- end
-
- def visit_ColumnDefinition(o)
- o.sql_type = type_to_sql(o.type, o.limit, o.precision, o.scale, o.unsigned)
- super
- end
-
- def visit_AddColumnDefinition(o)
- add_column_position!(super, column_options(o.column))
- end
+ def visit_DropForeignKey(name)
+ "DROP FOREIGN KEY #{name}"
+ end
- def visit_ChangeColumnDefinition(o)
- change_column_sql = "CHANGE #{quote_column_name(o.name)} #{accept(o.column)}"
- add_column_position!(change_column_sql, column_options(o.column))
- end
+ def visit_ColumnDefinition(o)
+ o.sql_type = type_to_sql(o.type, o.limit, o.precision, o.scale, o.unsigned)
+ super
+ end
- def add_table_options!(create_sql, options)
- add_sql_comment!(super, options[:comment])
- end
+ def visit_AddColumnDefinition(o)
+ add_column_position!(super, column_options(o.column))
+ end
- def column_options(o)
- column_options = super
- column_options[:charset] = o.charset
- column_options
- end
+ def visit_ChangeColumnDefinition(o)
+ change_column_sql = "CHANGE #{quote_column_name(o.name)} #{accept(o.column)}"
+ add_column_position!(change_column_sql, column_options(o.column))
+ end
- def add_column_options!(sql, options)
- if charset = options[:charset]
- sql << " CHARACTER SET #{charset}"
+ def add_table_options!(create_sql, options)
+ add_sql_comment!(super, options[:comment])
end
- if collation = options[:collation]
- sql << " COLLATE #{collation}"
+ def column_options(o)
+ column_options = super
+ column_options[:charset] = o.charset
+ column_options
end
- add_sql_comment!(super, options[:comment])
- end
+ def add_column_options!(sql, options)
+ if charset = options[:charset]
+ sql << " CHARACTER SET #{charset}"
+ end
+
+ if collation = options[:collation]
+ sql << " COLLATE #{collation}"
+ end
- def add_column_position!(sql, options)
- if options[:first]
- sql << " FIRST"
- elsif options[:after]
- sql << " AFTER #{quote_column_name(options[:after])}"
+ add_sql_comment!(super, options[:comment])
end
- sql
- end
+ def add_column_position!(sql, options)
+ if options[:first]
+ sql << " FIRST"
+ elsif options[:after]
+ sql << " AFTER #{quote_column_name(options[:after])}"
+ end
- def index_in_create(table_name, column_name, options)
- index_name, index_type, index_columns, _, _, index_using, comment = @conn.add_index_options(table_name, column_name, options)
- add_sql_comment!("#{index_type} INDEX #{quote_column_name(index_name)} #{index_using} (#{index_columns})", comment)
- end
+ sql
+ end
+
+ def index_in_create(table_name, column_name, options)
+ index_name, index_type, index_columns, _, _, index_using, comment = @conn.add_index_options(table_name, column_name, options)
+ add_sql_comment!("#{index_type} INDEX #{quote_column_name(index_name)} #{index_using} (#{index_columns})", comment)
+ end
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/mysql/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/mysql/schema_definitions.rb
index 157e75dbf7..0cf40de70f 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql/schema_definitions.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql/schema_definitions.rb
@@ -3,7 +3,10 @@ module ActiveRecord
module MySQL
module ColumnMethods
def primary_key(name, type = :primary_key, **options)
- options[:auto_increment] = true if type == :bigint && !options.key?(:default)
+ if type == :primary_key && !options.key?(:default)
+ options[:auto_increment] = true
+ options[:limit] = 8
+ end
super
end
@@ -80,9 +83,9 @@ module ActiveRecord
private
- def create_column_definition(name, type)
- MySQL::ColumnDefinition.new(name, type)
- end
+ def create_column_definition(name, type)
+ MySQL::ColumnDefinition.new(name, type)
+ end
end
class Table < ActiveRecord::ConnectionAdapters::Table
diff --git a/activerecord/lib/active_record/connection_adapters/mysql/schema_dumper.rb b/activerecord/lib/active_record/connection_adapters/mysql/schema_dumper.rb
index 2ba9657f24..2065816501 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql/schema_dumper.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql/schema_dumper.rb
@@ -3,19 +3,17 @@ module ActiveRecord
module MySQL
module ColumnDumper
def column_spec_for_primary_key(column)
- if column.bigint?
- spec = { id: :bigint.inspect }
- spec[:default] = schema_default(column) || 'nil' unless column.auto_increment?
- else
- spec = super
+ spec = super
+ if column.type == :integer && !column.auto_increment?
+ spec[:default] = schema_default(column) || "nil"
end
- spec[:unsigned] = 'true' if column.unsigned?
+ spec[:unsigned] = "true" if column.unsigned?
spec
end
def prepare_column_options(column)
spec = super
- spec[:unsigned] = 'true' if column.unsigned?
+ spec[:unsigned] = "true" if column.unsigned?
spec
end
@@ -25,29 +23,29 @@ module ActiveRecord
private
- def default_primary_key?(column)
- super && column.auto_increment?
- end
+ def default_primary_key?(column)
+ super && column.auto_increment?
+ end
- def schema_type(column)
- if column.sql_type == 'tinyblob'
- :blob
- else
- super
+ def schema_type(column)
+ if column.sql_type == "tinyblob"
+ :blob
+ else
+ super
+ end
end
- end
- def schema_precision(column)
- super unless /time/ === column.sql_type && column.precision == 0
- end
+ def schema_precision(column)
+ super unless /time/.match?(column.sql_type) && column.precision == 0
+ end
- def schema_collation(column)
- if column.collation && table_name = column.table_name
- @table_collation_cache ||= {}
- @table_collation_cache[table_name] ||= select_one("SHOW TABLE STATUS LIKE '#{table_name}'")["Collation"]
- column.collation.inspect if column.collation != @table_collation_cache[table_name]
+ def schema_collation(column)
+ if column.collation && table_name = column.table_name
+ @table_collation_cache ||= {}
+ @table_collation_cache[table_name] ||= select_one("SHOW TABLE STATUS LIKE '#{table_name}'")["Collation"]
+ column.collation.inspect if column.collation != @table_collation_cache[table_name]
+ end
end
- end
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/mysql/type_metadata.rb b/activerecord/lib/active_record/connection_adapters/mysql/type_metadata.rb
index e1e3f7b472..24dcf852e1 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql/type_metadata.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql/type_metadata.rb
@@ -2,13 +2,12 @@ module ActiveRecord
module ConnectionAdapters
module MySQL
class TypeMetadata < DelegateClass(SqlTypeMetadata) # :nodoc:
- attr_reader :extra, :strict
+ attr_reader :extra
- def initialize(type_metadata, extra: "", strict: false)
+ def initialize(type_metadata, extra: "")
super(type_metadata)
@type_metadata = type_metadata
@extra = extra
- @strict = strict
end
def ==(other)
@@ -23,9 +22,9 @@ module ActiveRecord
protected
- def attributes_for_hash
- [self.class, @type_metadata, extra, strict]
- end
+ def attributes_for_hash
+ [self.class, @type_metadata, extra]
+ end
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb
index 22d35f1db5..45e400b75b 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb
@@ -1,9 +1,9 @@
-require 'active_record/connection_adapters/abstract_mysql_adapter'
-require 'active_record/connection_adapters/mysql/database_statements'
+require "active_record/connection_adapters/abstract_mysql_adapter"
+require "active_record/connection_adapters/mysql/database_statements"
-gem 'mysql2', '>= 0.3.18', '< 0.5'
-require 'mysql2'
-raise 'mysql2 0.4.3 is not supported. Please upgrade to 0.4.4+' if Mysql2::VERSION == '0.4.3'
+gem "mysql2", ">= 0.3.18", "< 0.5"
+require "mysql2"
+raise "mysql2 0.4.3 is not supported. Please upgrade to 0.4.4+" if Mysql2::VERSION == "0.4.3"
module ActiveRecord
module ConnectionHandling # :nodoc:
@@ -11,15 +11,13 @@ module ActiveRecord
def mysql2_connection(config)
config = config.symbolize_keys
- config[:username] = 'root' if config[:username].nil?
+ config[:username] = "root" if config[:username].nil?
config[:flags] ||= 0
- if Mysql2::Client.const_defined? :FOUND_ROWS
- if config[:flags].kind_of? Array
- config[:flags].push "FOUND_ROWS".freeze
- else
- config[:flags] |= Mysql2::Client::FOUND_ROWS
- end
+ if config[:flags].kind_of? Array
+ config[:flags].push "FOUND_ROWS".freeze
+ else
+ config[:flags] |= Mysql2::Client::FOUND_ROWS
end
client = Mysql2::Client.new(config)
@@ -35,7 +33,7 @@ module ActiveRecord
module ConnectionAdapters
class Mysql2Adapter < AbstractMysqlAdapter
- ADAPTER_NAME = 'Mysql2'.freeze
+ ADAPTER_NAME = "Mysql2".freeze
include MySQL::DatabaseStatements
@@ -46,7 +44,7 @@ module ActiveRecord
end
def supports_json?
- !mariadb? && version >= '5.7.8'
+ !mariadb? && version >= "5.7.8"
end
def supports_comments?
@@ -65,7 +63,7 @@ module ActiveRecord
def each_hash(result) # :nodoc:
if block_given?
- result.each(:as => :hash, :symbolize_keys => true) do |row|
+ result.each(as: :hash, symbolize_keys: true) do |row|
yield row
end
else
@@ -90,7 +88,6 @@ module ActiveRecord
#++
def active?
- return false unless @connection
@connection.ping
end
@@ -105,27 +102,24 @@ module ActiveRecord
# Otherwise, this method does nothing.
def disconnect!
super
- unless @connection.nil?
- @connection.close
- @connection = nil
- end
+ @connection.close
end
private
- def connect
- @connection = Mysql2::Client.new(@config)
- configure_connection
- end
+ def connect
+ @connection = Mysql2::Client.new(@config)
+ configure_connection
+ end
- def configure_connection
- @connection.query_options.merge!(:as => :array)
- super
- end
+ def configure_connection
+ @connection.query_options.merge!(as: :array)
+ super
+ end
- def full_version
- @full_version ||= @connection.server_info[:version]
- end
+ def full_version
+ @full_version ||= @connection.server_info[:version]
+ end
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb b/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb
index 6f2e03b370..9a2017b443 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb
@@ -4,7 +4,7 @@ module ActiveRecord
module DatabaseStatements
def explain(arel, binds = [])
sql = "EXPLAIN #{to_sql(arel, binds)}"
- PostgreSQL::ExplainPrettyPrinter.new.pp(exec_query(sql, 'EXPLAIN', binds))
+ PostgreSQL::ExplainPrettyPrinter.new.pp(exec_query(sql, "EXPLAIN", binds))
end
def select_value(arel, name = nil, binds = [])
@@ -74,9 +74,9 @@ module ActiveRecord
# (2) $12.345.678,12
case data
when /^-?\D+[\d,]+\.\d{2}$/ # (1)
- data.gsub!(/[^-\d.]/, '')
+ data.gsub!(/[^-\d.]/, "")
when /^-?\D+[\d.]+,\d{2}$/ # (2)
- data.gsub!(/[^-\d,]/, '').sub!(/,/, '.')
+ data.gsub!(/[^-\d,]/, "").sub!(/,/, ".")
end
end
end
@@ -85,21 +85,25 @@ module ActiveRecord
# Queries the database and returns the results in an Array-like object
def query(sql, name = nil) #:nodoc:
log(sql, name) do
- result_as_array @connection.async_exec(sql)
+ ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
+ result_as_array @connection.async_exec(sql)
+ end
end
end
- # Executes an SQL statement, returning a PGresult object on success
- # or raising a PGError exception otherwise.
- # Note: the PGresult object is manually memory managed; if you don't
- # need it specifically, you many want consider the exec_query wrapper.
+ # Executes an SQL statement, returning a PG::Result object on success
+ # or raising a PG::Error exception otherwise.
+ # Note: the PG::Result object is manually memory managed; if you don't
+ # need it specifically, you may want consider the <tt>exec_query</tt> wrapper.
def execute(sql, name = nil)
log(sql, name) do
- @connection.async_exec(sql)
+ ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
+ @connection.async_exec(sql)
+ end
end
end
- def exec_query(sql, name = 'SQL', binds = [], prepare: false)
+ def exec_query(sql, name = "SQL", binds = [], prepare: false)
execute_and_clear(sql, name, binds, prepare: prepare) do |result|
types = {}
fields = result.fields
@@ -112,8 +116,8 @@ module ActiveRecord
end
end
- def exec_delete(sql, name = 'SQL', binds = [])
- execute_and_clear(sql, name, binds) {|result| result.cmd_tuples }
+ def exec_delete(sql, name = nil, binds = [])
+ execute_and_clear(sql, name, binds) { |result| result.cmd_tuples }
end
alias :exec_update :exec_delete
@@ -124,24 +128,29 @@ module ActiveRecord
pk = primary_key(table_ref) if table_ref
end
- if pk && use_insert_returning?
+ if pk = suppress_composite_primary_key(pk)
sql = "#{sql} RETURNING #{quote_column_name(pk)}"
end
super
end
+ private :sql_for_insert
- def exec_insert(sql, name, binds, pk = nil, sequence_name = nil)
- val = exec_query(sql, name, binds)
- if !use_insert_returning? && pk
+ def exec_insert(sql, name = nil, binds = [], pk = nil, sequence_name = nil)
+ if use_insert_returning? || pk == false
+ super
+ else
+ result = exec_query(sql, name, binds)
unless sequence_name
table_ref = extract_table_ref_from_insert_sql(sql)
- sequence_name = default_sequence_name(table_ref, pk)
- return val unless sequence_name
+ if table_ref
+ pk = primary_key(table_ref) if pk.nil?
+ pk = suppress_composite_primary_key(pk)
+ sequence_name = default_sequence_name(table_ref, pk)
+ end
+ return result unless sequence_name
end
last_insert_id_result(sequence_name)
- else
- val
end
end
@@ -164,6 +173,12 @@ module ActiveRecord
def exec_rollback_db_transaction
execute "ROLLBACK"
end
+
+ private
+
+ def suppress_composite_primary_key(pk)
+ pk unless pk.is_a?(Array)
+ end
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb b/activerecord/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb
index 789b88912c..99f3a5bbdf 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb
@@ -26,12 +26,12 @@ module ActiveRecord
pp = []
pp << header.center(width).rstrip
- pp << '-' * width
+ pp << "-" * width
- pp += lines.map {|line| " #{line}"}
+ pp += lines.map { |line| " #{line}" }
nrows = result.rows.length
- rows_label = nrows == 1 ? 'row' : 'rows'
+ rows_label = nrows == 1 ? "row" : "rows"
pp << "(#{nrows} #{rows_label})"
pp.join("\n") + "\n"
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb
index 68752cdd80..0e526f6201 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb
@@ -1,25 +1,25 @@
-require 'active_record/connection_adapters/postgresql/oid/array'
-require 'active_record/connection_adapters/postgresql/oid/bit'
-require 'active_record/connection_adapters/postgresql/oid/bit_varying'
-require 'active_record/connection_adapters/postgresql/oid/bytea'
-require 'active_record/connection_adapters/postgresql/oid/cidr'
-require 'active_record/connection_adapters/postgresql/oid/date_time'
-require 'active_record/connection_adapters/postgresql/oid/decimal'
-require 'active_record/connection_adapters/postgresql/oid/enum'
-require 'active_record/connection_adapters/postgresql/oid/hstore'
-require 'active_record/connection_adapters/postgresql/oid/inet'
-require 'active_record/connection_adapters/postgresql/oid/json'
-require 'active_record/connection_adapters/postgresql/oid/jsonb'
-require 'active_record/connection_adapters/postgresql/oid/money'
-require 'active_record/connection_adapters/postgresql/oid/point'
-require 'active_record/connection_adapters/postgresql/oid/rails_5_1_point'
-require 'active_record/connection_adapters/postgresql/oid/range'
-require 'active_record/connection_adapters/postgresql/oid/specialized_string'
-require 'active_record/connection_adapters/postgresql/oid/uuid'
-require 'active_record/connection_adapters/postgresql/oid/vector'
-require 'active_record/connection_adapters/postgresql/oid/xml'
+require "active_record/connection_adapters/postgresql/oid/array"
+require "active_record/connection_adapters/postgresql/oid/bit"
+require "active_record/connection_adapters/postgresql/oid/bit_varying"
+require "active_record/connection_adapters/postgresql/oid/bytea"
+require "active_record/connection_adapters/postgresql/oid/cidr"
+require "active_record/connection_adapters/postgresql/oid/date_time"
+require "active_record/connection_adapters/postgresql/oid/decimal"
+require "active_record/connection_adapters/postgresql/oid/enum"
+require "active_record/connection_adapters/postgresql/oid/hstore"
+require "active_record/connection_adapters/postgresql/oid/inet"
+require "active_record/connection_adapters/postgresql/oid/json"
+require "active_record/connection_adapters/postgresql/oid/jsonb"
+require "active_record/connection_adapters/postgresql/oid/money"
+require "active_record/connection_adapters/postgresql/oid/point"
+require "active_record/connection_adapters/postgresql/oid/legacy_point"
+require "active_record/connection_adapters/postgresql/oid/range"
+require "active_record/connection_adapters/postgresql/oid/specialized_string"
+require "active_record/connection_adapters/postgresql/oid/uuid"
+require "active_record/connection_adapters/postgresql/oid/vector"
+require "active_record/connection_adapters/postgresql/oid/xml"
-require 'active_record/connection_adapters/postgresql/oid/type_map_initializer'
+require "active_record/connection_adapters/postgresql/oid/type_map_initializer"
module ActiveRecord
module ConnectionAdapters
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/array.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/array.rb
index 87593ef704..e1a75f8e5e 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/array.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/array.rb
@@ -5,10 +5,12 @@ module ActiveRecord
class Array < Type::Value # :nodoc:
include Type::Helpers::Mutable
+ Data = Struct.new(:encoder, :values) # :nodoc:
+
attr_reader :subtype, :delimiter
- delegate :type, :user_input_in_time_zone, :limit, to: :subtype
+ delegate :type, :user_input_in_time_zone, :limit, :precision, :scale, to: :subtype
- def initialize(subtype, delimiter = ',')
+ def initialize(subtype, delimiter = ",")
@subtype = subtype
@delimiter = delimiter
@@ -17,8 +19,11 @@ module ActiveRecord
end
def deserialize(value)
- if value.is_a?(::String)
+ case value
+ when ::String
type_cast_array(@pg_decoder.decode(value), :deserialize)
+ when Data
+ deserialize(value.values)
else
super
end
@@ -33,7 +38,8 @@ module ActiveRecord
def serialize(value)
if value.is_a?(::Array)
- @pg_encoder.encode(type_cast_array(value, :serialize))
+ casted_values = type_cast_array(value, :serialize)
+ Data.new(@pg_encoder, casted_values)
else
super
end
@@ -54,15 +60,19 @@ module ActiveRecord
value.map(&block)
end
+ def changed_in_place?(raw_old_value, new_value)
+ deserialize(raw_old_value) != new_value
+ end
+
private
- def type_cast_array(value, method)
- if value.is_a?(::Array)
- value.map { |item| type_cast_array(item, method) }
- else
- @subtype.public_send(method, value)
+ def type_cast_array(value, method)
+ if value.is_a?(::Array)
+ value.map { |item| type_cast_array(item, method) }
+ else
+ @subtype.public_send(method, value)
+ end
end
- end
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/bit.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/bit.rb
index ea0fa2517f..0a505f46a7 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/bit.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/bit.rb
@@ -7,7 +7,7 @@ module ActiveRecord
:bit
end
- def cast(value)
+ def cast_value(value)
if ::String === value
case value
when /^0x/i
@@ -16,7 +16,7 @@ module ActiveRecord
value # Bit-string notation
end
else
- value
+ value.to_s
end
end
@@ -34,16 +34,18 @@ module ActiveRecord
end
def binary?
- /\A[01]*\Z/ === value
+ /\A[01]*\Z/.match?(value)
end
def hex?
- /\A[0-9A-F]*\Z/i === value
+ /\A[0-9A-F]*\Z/i.match?(value)
end
+ # TODO Change this to private once we've dropped Ruby 2.2 support.
+ # Workaround for Ruby 2.2 "private attribute?" warning.
protected
- attr_reader :value
+ attr_reader :value
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/cidr.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/cidr.rb
index 838cb63281..5225609e37 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/cidr.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/cidr.rb
@@ -1,4 +1,4 @@
-require 'ipaddr'
+require "ipaddr"
module ActiveRecord
module ConnectionAdapters
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/date_time.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/date_time.rb
index 424769f765..b7acbf7178 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/date_time.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/date_time.rb
@@ -5,8 +5,8 @@ module ActiveRecord
class DateTime < Type::DateTime # :nodoc:
def cast_value(value)
case value
- when 'infinity' then ::Float::INFINITY
- when '-infinity' then -::Float::INFINITY
+ when "infinity" then ::Float::INFINITY
+ when "-infinity" then -::Float::INFINITY
when / BC$/
astronomical_year = format("%04d", -value[/^\d+/].to_i + 1)
super(value.sub(/ BC$/, "").sub(/^\d+/, astronomical_year))
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/enum.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/enum.rb
index 91d339f32c..950d23d516 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/enum.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/enum.rb
@@ -9,9 +9,9 @@ module ActiveRecord
private
- def cast_value(value)
- value.to_s
- end
+ def cast_value(value)
+ value.to_s
+ end
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/hstore.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/hstore.rb
index 9270fc9f21..d629ebca91 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/hstore.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/hstore.rb
@@ -12,8 +12,8 @@ module ActiveRecord
def deserialize(value)
if value.is_a?(::String)
::Hash[value.scan(HstorePair).map { |k, v|
- v = v.upcase == 'NULL' ? nil : v.gsub(/\A"(.*)"\Z/m,'\1').gsub(/\\(.)/, '\1')
- k = k.gsub(/\A"(.*)"\Z/m,'\1').gsub(/\\(.)/, '\1')
+ v = v.upcase == "NULL" ? nil : v.gsub(/\A"(.*)"\Z/m, '\1').gsub(/\\(.)/, '\1')
+ k = k.gsub(/\A"(.*)"\Z/m, '\1').gsub(/\\(.)/, '\1')
[k, v]
}]
else
@@ -23,7 +23,9 @@ module ActiveRecord
def serialize(value)
if value.is_a?(::Hash)
- value.map { |k, v| "#{escape_hstore(k)}=>#{escape_hstore(v)}" }.join(', ')
+ value.map { |k, v| "#{escape_hstore(k)}=>#{escape_hstore(v)}" }.join(", ")
+ elsif value.respond_to?(:to_unsafe_h)
+ serialize(value.to_unsafe_h)
else
value
end
@@ -35,23 +37,23 @@ module ActiveRecord
private
- HstorePair = begin
- quoted_string = /"[^"\\]*(?:\\.[^"\\]*)*"/
- unquoted_string = /(?:\\.|[^\s,])[^\s=,\\]*(?:\\.[^\s=,\\]*|=[^,>])*/
- /(#{quoted_string}|#{unquoted_string})\s*=>\s*(#{quoted_string}|#{unquoted_string})/
- end
+ HstorePair = begin
+ quoted_string = /"[^"\\]*(?:\\.[^"\\]*)*"/
+ unquoted_string = /(?:\\.|[^\s,])[^\s=,\\]*(?:\\.[^\s=,\\]*|=[^,>])*/
+ /(#{quoted_string}|#{unquoted_string})\s*=>\s*(#{quoted_string}|#{unquoted_string})/
+ end
- def escape_hstore(value)
- if value.nil?
- 'NULL'
- else
- if value == ""
- '""'
+ def escape_hstore(value)
+ if value.nil?
+ "NULL"
else
- '"%s"' % value.to_s.gsub(/(["\\])/, '\\\\\1')
+ if value == ""
+ '""'
+ else
+ '"%s"' % value.to_s.gsub(/(["\\])/, '\\\\\1')
+ end
end
end
- end
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb
new file mode 100644
index 0000000000..775eecaf85
--- /dev/null
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb
@@ -0,0 +1,43 @@
+module ActiveRecord
+ module ConnectionAdapters
+ module PostgreSQL
+ module OID # :nodoc:
+ class LegacyPoint < Type::Value # :nodoc:
+ include Type::Helpers::Mutable
+
+ def type
+ :point
+ end
+
+ def cast(value)
+ case value
+ when ::String
+ if value[0] == "(" && value[-1] == ")"
+ value = value[1...-1]
+ end
+ cast(value.split(","))
+ when ::Array
+ value.map { |v| Float(v) }
+ else
+ value
+ end
+ end
+
+ def serialize(value)
+ if value.is_a?(::Array)
+ "(#{number_for_point(value[0])},#{number_for_point(value[1])})"
+ else
+ super
+ end
+ end
+
+ private
+
+ def number_for_point(number)
+ number.to_s.gsub(/\.0$/, "")
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/money.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/money.rb
index dcc12ae2a4..7a91272d1c 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/money.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/money.rb
@@ -25,9 +25,9 @@ module ActiveRecord
value.sub!(/^\((.+)\)$/, '-\1') # (4)
case value
when /^-?\D+[\d,]+\.\d{2}$/ # (1)
- value.gsub!(/[^-\d.]/, '')
+ value.gsub!(/[^-\d.]/, "")
when /^-?\D+[\d.]+,\d{2}$/ # (2)
- value.gsub!(/[^-\d,]/, '').sub!(/,/, '.')
+ value.gsub!(/[^-\d,]/, "").sub!(/,/, ".")
end
super(value)
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/point.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/point.rb
index bf565bcf47..7c764e7287 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/point.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/point.rb
@@ -1,4 +1,6 @@
module ActiveRecord
+ Point = Struct.new(:x, :y)
+
module ConnectionAdapters
module PostgreSQL
module OID # :nodoc:
@@ -12,20 +14,34 @@ module ActiveRecord
def cast(value)
case value
when ::String
- if value[0] == '(' && value[-1] == ')'
+ return if value.blank?
+
+ if value[0] == "(" && value[-1] == ")"
value = value[1...-1]
end
- cast(value.split(','))
+ x, y = value.split(",")
+ build_point(x, y)
when ::Array
- value.map { |v| Float(v) }
+ build_point(*value)
else
value
end
end
def serialize(value)
- if value.is_a?(::Array)
- "(#{number_for_point(value[0])},#{number_for_point(value[1])})"
+ case value
+ when ActiveRecord::Point
+ "(#{number_for_point(value.x)},#{number_for_point(value.y)})"
+ when ::Array
+ serialize(build_point(*value))
+ else
+ super
+ end
+ end
+
+ def type_cast_for_schema(value)
+ if ActiveRecord::Point === value
+ [value.x, value.y]
else
super
end
@@ -33,9 +49,13 @@ module ActiveRecord
private
- def number_for_point(number)
- number.to_s.gsub(/\.0$/, '')
- end
+ def number_for_point(number)
+ number.to_s.gsub(/\.0$/, "")
+ end
+
+ def build_point(x, y)
+ ActiveRecord::Point.new(Float(x), Float(y))
+ end
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb
deleted file mode 100644
index 4da240edb2..0000000000
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb
+++ /dev/null
@@ -1,52 +0,0 @@
-module ActiveRecord
- Point = Struct.new(:x, :y)
-
- module ConnectionAdapters
- module PostgreSQL
- module OID # :nodoc:
- class Rails51Point < Type::Value # :nodoc:
- include Type::Helpers::Mutable
-
- def type
- :point
- end
-
- def cast(value)
- case value
- when ::String
- return if value.blank?
-
- if value[0] == '(' && value[-1] == ')'
- value = value[1...-1]
- end
- x, y = value.split(",")
- build_point(x, y)
- when ::Array
- build_point(*value)
- else
- value
- end
- end
-
- def serialize(value)
- if value.is_a?(ActiveRecord::Point)
- "(#{number_for_point(value.x)},#{number_for_point(value.y)})"
- else
- super
- end
- end
-
- private
-
- def number_for_point(number)
- number.to_s.gsub(/\.0$/, '')
- end
-
- def build_point(x, y)
- ActiveRecord::Point.new(Float(x), Float(y))
- end
- end
- end
- end
- end
-end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/range.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/range.rb
index a8d2310035..2c714f4018 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/range.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/range.rb
@@ -1,4 +1,4 @@
-require 'active_support/core_ext/string/filters'
+require "active_support/core_ext/string/filters"
module ActiveRecord
module ConnectionAdapters
@@ -14,11 +14,11 @@ module ActiveRecord
end
def type_cast_for_schema(value)
- value.inspect.gsub('Infinity', '::Float::INFINITY')
+ value.inspect.gsub("Infinity", "::Float::INFINITY")
end
def cast_value(value)
- return if value == 'empty'
+ return if value == "empty"
return value unless value.is_a?(::String)
extracted = extract_bounds(value)
@@ -55,37 +55,37 @@ module ActiveRecord
private
- def type_cast_single(value)
- infinity?(value) ? value : @subtype.deserialize(value)
- end
+ def type_cast_single(value)
+ infinity?(value) ? value : @subtype.deserialize(value)
+ end
- def type_cast_single_for_database(value)
- infinity?(value) ? '' : @subtype.serialize(value)
- end
+ def type_cast_single_for_database(value)
+ infinity?(value) ? "" : @subtype.serialize(value)
+ end
- def extract_bounds(value)
- from, to = value[1..-2].split(',')
- {
- from: (value[1] == ',' || from == '-infinity') ? infinity(negative: true) : from,
- to: (value[-2] == ',' || to == 'infinity') ? infinity : to,
- exclude_start: (value[0] == '('),
- exclude_end: (value[-1] == ')')
- }
- end
+ def extract_bounds(value)
+ from, to = value[1..-2].split(",")
+ {
+ from: (value[1] == "," || from == "-infinity") ? infinity(negative: true) : from,
+ to: (value[-2] == "," || to == "infinity") ? infinity : to,
+ exclude_start: (value[0] == "("),
+ exclude_end: (value[-1] == ")")
+ }
+ end
- def infinity(negative: false)
- if subtype.respond_to?(:infinity)
- subtype.infinity(negative: negative)
- elsif negative
- -::Float::INFINITY
- else
- ::Float::INFINITY
+ def infinity(negative: false)
+ if subtype.respond_to?(:infinity)
+ subtype.infinity(negative: negative)
+ elsif negative
+ -::Float::INFINITY
+ else
+ ::Float::INFINITY
+ end
end
- end
- def infinity?(value)
- value.respond_to?(:infinite?) && value.infinite?
- end
+ def infinity?(value)
+ value.respond_to?(:infinite?) && value.infinite?
+ end
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb
index 6155e53632..d9ae1aa7a2 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb
@@ -13,13 +13,13 @@ module ActiveRecord
end
def run(records)
- nodes = records.reject { |row| @store.key? row['oid'].to_i }
- mapped, nodes = nodes.partition { |row| @store.key? row['typname'] }
- ranges, nodes = nodes.partition { |row| row['typtype'] == 'r'.freeze }
- enums, nodes = nodes.partition { |row| row['typtype'] == 'e'.freeze }
- domains, nodes = nodes.partition { |row| row['typtype'] == 'd'.freeze }
- arrays, nodes = nodes.partition { |row| row['typinput'] == 'array_in'.freeze }
- composites, nodes = nodes.partition { |row| row['typelem'].to_i != 0 }
+ nodes = records.reject { |row| @store.key? row["oid"].to_i }
+ mapped, nodes = nodes.partition { |row| @store.key? row["typname"] }
+ ranges, nodes = nodes.partition { |row| row["typtype"] == "r".freeze }
+ enums, nodes = nodes.partition { |row| row["typtype"] == "e".freeze }
+ domains, nodes = nodes.partition { |row| row["typtype"] == "d".freeze }
+ arrays, nodes = nodes.partition { |row| row["typinput"] == "array_in".freeze }
+ composites, nodes = nodes.partition { |row| row["typelem"].to_i != 0 }
mapped.each { |row| register_mapped_type(row) }
enums.each { |row| register_enum_type(row) }
@@ -42,66 +42,66 @@ module ActiveRecord
end
private
- def register_mapped_type(row)
- alias_type row['oid'], row['typname']
- end
+ def register_mapped_type(row)
+ alias_type row["oid"], row["typname"]
+ end
- def register_enum_type(row)
- register row['oid'], OID::Enum.new
- end
+ def register_enum_type(row)
+ register row["oid"], OID::Enum.new
+ end
- def register_array_type(row)
- register_with_subtype(row['oid'], row['typelem'].to_i) do |subtype|
- OID::Array.new(subtype, row['typdelim'])
+ def register_array_type(row)
+ register_with_subtype(row["oid"], row["typelem"].to_i) do |subtype|
+ OID::Array.new(subtype, row["typdelim"])
+ end
end
- end
- def register_range_type(row)
- register_with_subtype(row['oid'], row['rngsubtype'].to_i) do |subtype|
- OID::Range.new(subtype, row['typname'].to_sym)
+ def register_range_type(row)
+ register_with_subtype(row["oid"], row["rngsubtype"].to_i) do |subtype|
+ OID::Range.new(subtype, row["typname"].to_sym)
+ end
end
- end
- def register_domain_type(row)
- if base_type = @store.lookup(row["typbasetype"].to_i)
- register row['oid'], base_type
- else
- warn "unknown base type (OID: #{row["typbasetype"]}) for domain #{row["typname"]}."
+ def register_domain_type(row)
+ if base_type = @store.lookup(row["typbasetype"].to_i)
+ register row["oid"], base_type
+ else
+ warn "unknown base type (OID: #{row["typbasetype"]}) for domain #{row["typname"]}."
+ end
end
- end
- def register_composite_type(row)
- if subtype = @store.lookup(row['typelem'].to_i)
- register row['oid'], OID::Vector.new(row['typdelim'], subtype)
+ def register_composite_type(row)
+ if subtype = @store.lookup(row["typelem"].to_i)
+ register row["oid"], OID::Vector.new(row["typdelim"], subtype)
+ end
end
- end
- def register(oid, oid_type = nil, &block)
- oid = assert_valid_registration(oid, oid_type || block)
- if block_given?
- @store.register_type(oid, &block)
- else
- @store.register_type(oid, oid_type)
+ def register(oid, oid_type = nil, &block)
+ oid = assert_valid_registration(oid, oid_type || block)
+ if block_given?
+ @store.register_type(oid, &block)
+ else
+ @store.register_type(oid, oid_type)
+ end
end
- end
- def alias_type(oid, target)
- oid = assert_valid_registration(oid, target)
- @store.alias_type(oid, target)
- end
+ def alias_type(oid, target)
+ oid = assert_valid_registration(oid, target)
+ @store.alias_type(oid, target)
+ end
- def register_with_subtype(oid, target_oid)
- if @store.key?(target_oid)
- register(oid) do |_, *args|
- yield @store.lookup(target_oid, *args)
+ def register_with_subtype(oid, target_oid)
+ if @store.key?(target_oid)
+ register(oid) do |_, *args|
+ yield @store.lookup(target_oid, *args)
+ end
end
end
- end
- def assert_valid_registration(oid, oid_type)
- raise ArgumentError, "can't register nil type for OID #{oid}" if oid_type.nil?
- oid.to_i
- end
+ def assert_valid_registration(oid, oid_type)
+ raise ArgumentError, "can't register nil type for OID #{oid}" if oid_type.nil?
+ oid.to_i
+ end
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb b/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb
index 6414459cd1..3783925954 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb
@@ -28,7 +28,7 @@ module ActiveRecord
# - "schema.name".table_name
# - "schema.name"."table.name"
def quote_table_name(name) # :nodoc:
- @quoted_table_names[name] ||= Utils.extract_schema_qualified_name(name.to_s).quoted
+ @quoted_table_names[name] ||= Utils.extract_schema_qualified_name(name.to_s).quoted.freeze
end
# Quotes schema names for use in SQL queries.
@@ -42,7 +42,7 @@ module ActiveRecord
# Quotes column names for use in SQL queries.
def quote_column_name(name) # :nodoc:
- @quoted_column_names[name] ||= PGconn.quote_ident(super)
+ @quoted_column_names[name] ||= PGconn.quote_ident(super).freeze
end
# Quote date/time values for use in SQL input.
@@ -58,7 +58,7 @@ module ActiveRecord
def quote_default_expression(value, column) # :nodoc:
if value.is_a?(Proc)
value.call
- elsif column.type == :uuid && value =~ /\(\)/
+ elsif column.type == :uuid && value.include?("()")
value # Does not quote function default values for UUID columns
elsif column.respond_to?(:array?)
value = type_cast_from_column(column, value)
@@ -74,42 +74,71 @@ module ActiveRecord
private
- def _quote(value)
- case value
- when Type::Binary::Data
- "'#{escape_bytea(value.to_s)}'"
- when OID::Xml::Data
- "xml '#{quote_string(value.to_s)}'"
- when OID::Bit::Data
- if value.binary?
- "B'#{value}'"
- elsif value.hex?
- "X'#{value}'"
+ def _quote(value)
+ case value
+ when Type::Binary::Data
+ "'#{escape_bytea(value.to_s)}'"
+ when OID::Xml::Data
+ "xml '#{quote_string(value.to_s)}'"
+ when OID::Bit::Data
+ if value.binary?
+ "B'#{value}'"
+ elsif value.hex?
+ "X'#{value}'"
+ end
+ when Float
+ if value.infinite? || value.nan?
+ "'#{value}'"
+ else
+ super
+ end
+ when OID::Array::Data
+ _quote(encode_array(value))
+ else
+ super
end
- when Float
- if value.infinite? || value.nan?
- "'#{value}'"
+ end
+
+ def _type_cast(value)
+ case value
+ when Type::Binary::Data
+ # Return a bind param hash with format as binary.
+ # See http://deveiate.org/code/pg/PGconn.html#method-i-exec_prepared-doc
+ # for more information
+ { value: value.to_s, format: 1 }
+ when OID::Xml::Data, OID::Bit::Data
+ value.to_s
+ when OID::Array::Data
+ encode_array(value)
else
super
end
- else
- super
end
- end
- def _type_cast(value)
- case value
- when Type::Binary::Data
- # Return a bind param hash with format as binary.
- # See http://deveiate.org/code/pg/PGconn.html#method-i-exec_prepared-doc
- # for more information
- { value: value.to_s, format: 1 }
- when OID::Xml::Data, OID::Bit::Data
- value.to_s
- else
- super
+ def encode_array(array_data)
+ encoder = array_data.encoder
+ values = type_cast_array(array_data.values)
+
+ result = encoder.encode(values)
+ if encoding = determine_encoding_of_strings_in_array(values)
+ result.force_encoding(encoding)
+ end
+ result
+ end
+
+ def determine_encoding_of_strings_in_array(value)
+ case value
+ when ::Array then determine_encoding_of_strings_in_array(value.first)
+ when ::String then value.encoding
+ end
+ end
+
+ def type_cast_array(values)
+ case values
+ when ::Array then values.map { |item| type_cast_array(item) }
+ else _type_cast(values)
+ end
end
- end
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/postgresql/schema_definitions.rb
index 6399bddbee..4afb4733eb 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_definitions.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_definitions.rb
@@ -11,11 +11,22 @@ module ActiveRecord
# t.timestamps
# end
#
- # By default, this will use the +uuid_generate_v4()+ function from the
- # +uuid-ossp+ extension, which MUST be enabled on your database. To enable
- # the +uuid-ossp+ extension, you can use the +enable_extension+ method in your
- # migrations. To use a UUID primary key without +uuid-ossp+ enabled, you can
- # set the +:default+ option to +nil+:
+ # By default, this will use the +gen_random_uuid()+ function from the
+ # +pgcrypto+ extension. As that extension is only available in
+ # PostgreSQL 9.4+, for earlier versions an explicit default can be set
+ # to use +uuid_generate_v4()+ from the +uuid-ossp+ extension instead:
+ #
+ # create_table :stuffs, id: false do |t|
+ # t.primary_key :id, :uuid, default: "uuid_generate_v4()"
+ # t.uuid :foo_id
+ # t.timestamps
+ # end
+ #
+ # To enable the appropriate extension, which is a requirement, use
+ # the +enable_extension+ method in your migrations.
+ #
+ # To use a UUID primary key without any of the extensions, set the
+ # +:default+ option to +nil+:
#
# create_table :stuffs, id: false do |t|
# t.primary_key :id, :uuid, default: nil
@@ -23,15 +34,24 @@ module ActiveRecord
# t.timestamps
# end
#
- # You may also pass a different UUID generation function from +uuid-ossp+
- # or another library.
+ # You may also pass a custom stored procedure that returns a UUID or use a
+ # different UUID generation function from another library.
#
# Note that setting the UUID primary key default value to +nil+ will
# require you to assure that you always provide a UUID value before saving
# a record (as primary keys cannot be +nil+). This might be done via the
# +SecureRandom.uuid+ method and a +before_save+ callback, for instance.
def primary_key(name, type = :primary_key, **options)
- options[:default] = options.fetch(:default, 'uuid_generate_v4()') if type == :uuid
+ if type == :uuid
+ options[:default] = options.fetch(:default, "gen_random_uuid()")
+ elsif options.delete(:auto_increment) == true && %i(integer bigint).include?(type)
+ type = if type == :bigint || options[:limit] == 8
+ :bigserial
+ else
+ :serial
+ end
+ end
+
super
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/schema_dumper.rb b/activerecord/lib/active_record/connection_adapters/postgresql/schema_dumper.rb
index a1e10fd364..7808d37deb 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_dumper.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_dumper.rb
@@ -5,7 +5,7 @@ module ActiveRecord
def column_spec_for_primary_key(column)
spec = super
if schema_type(column) == :uuid
- spec[:default] ||= 'nil'
+ spec[:default] ||= "nil"
end
spec
end
@@ -13,7 +13,7 @@ module ActiveRecord
# Adds +:array+ option to the default set
def prepare_column_options(column)
spec = super
- spec[:array] = 'true' if column.array?
+ spec[:array] = "true" if column.array?
spec
end
@@ -24,23 +24,23 @@ module ActiveRecord
private
- def default_primary_key?(column)
- schema_type(column) == :serial
- end
+ def default_primary_key?(column)
+ schema_type(column) == :bigserial
+ end
- def schema_type(column)
- return super unless column.serial?
+ def schema_type(column)
+ return super unless column.serial?
- if column.bigint?
- :bigserial
- else
- :serial
+ if column.bigint?
+ :bigserial
+ else
+ :serial
+ end
end
- end
- def schema_expression(column)
- super unless column.serial?
- end
+ def schema_expression(column)
+ super unless column.serial?
+ end
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
index 45507e206a..85836fc575 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
@@ -1,4 +1,4 @@
-require 'active_support/core_ext/string/strip'
+require "active_support/core_ext/string/strip"
module ActiveRecord
module ConnectionAdapters
@@ -6,17 +6,17 @@ module ActiveRecord
class SchemaCreation < AbstractAdapter::SchemaCreation
private
- def visit_ColumnDefinition(o)
- o.sql_type = type_to_sql(o.type, o.limit, o.precision, o.scale, o.array)
- super
- end
+ def visit_ColumnDefinition(o)
+ o.sql_type = type_to_sql(o.type, o.limit, o.precision, o.scale, o.array)
+ super
+ end
- def add_column_options!(sql, options)
- if options[:collation]
- sql << " COLLATE \"#{options[:collation]}\""
+ def add_column_options!(sql, options)
+ if options[:collation]
+ sql << " COLLATE \"#{options[:collation]}\""
+ end
+ super
end
- super
- end
end
module SchemaStatements
@@ -36,26 +36,26 @@ module ActiveRecord
# create_database config[:database], config
# create_database 'foo_development', encoding: 'unicode'
def create_database(name, options = {})
- options = { encoding: 'utf8' }.merge!(options.symbolize_keys)
+ options = { encoding: "utf8" }.merge!(options.symbolize_keys)
option_string = options.inject("") do |memo, (key, value)|
memo += case key
- when :owner
- " OWNER = \"#{value}\""
- when :template
- " TEMPLATE = \"#{value}\""
- when :encoding
- " ENCODING = '#{value}'"
- when :collation
- " LC_COLLATE = '#{value}'"
- when :ctype
- " LC_CTYPE = '#{value}'"
- when :tablespace
- " TABLESPACE = \"#{value}\""
- when :connection_limit
- " CONNECTION LIMIT = #{value}"
+ when :owner
+ " OWNER = \"#{value}\""
+ when :template
+ " TEMPLATE = \"#{value}\""
+ when :encoding
+ " ENCODING = '#{value}'"
+ when :collation
+ " LC_COLLATE = '#{value}'"
+ when :ctype
+ " LC_CTYPE = '#{value}'"
+ when :tablespace
+ " TABLESPACE = \"#{value}\""
+ when :connection_limit
+ " CONNECTION LIMIT = #{value}"
else
- ""
+ ""
end
end
@@ -71,22 +71,26 @@ module ActiveRecord
end
# Returns the list of all tables in the schema search path.
- def tables(name = nil)
- if name
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
- Passing arguments to #tables is deprecated without replacement.
- MSG
- end
-
- select_values("SELECT tablename FROM pg_tables WHERE schemaname = ANY(current_schemas(false))", 'SCHEMA')
+ def tables
+ select_values("SELECT tablename FROM pg_tables WHERE schemaname = ANY(current_schemas(false))", "SCHEMA")
end
def data_sources # :nodoc
- select_values(<<-SQL, 'SCHEMA')
+ select_values(<<-SQL, "SCHEMA")
SELECT c.relname
FROM pg_class c
LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
- WHERE c.relkind IN ('r', 'v','m') -- (r)elation/table, (v)iew, (m)aterialized view
+ WHERE c.relkind IN ('r','v','m') -- (r)elation/table, (v)iew, (m)aterialized view
+ AND n.nspname = ANY (current_schemas(false))
+ SQL
+ end
+
+ def views # :nodoc:
+ select_values(<<-SQL, "SCHEMA")
+ SELECT c.relname
+ FROM pg_class c
+ LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
+ WHERE c.relkind IN ('v','m') -- (v)iew, (m)aterialized view
AND n.nspname = ANY (current_schemas(false))
SQL
end
@@ -95,36 +99,28 @@ module ActiveRecord
# If the schema is not specified as part of +name+ then it will only find tables within
# the current schema search path (regardless of permissions to access tables in other schemas)
def table_exists?(name)
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
- #table_exists? currently checks both tables and views.
- This behavior is deprecated and will be changed with Rails 5.1 to only check tables.
- Use #data_source_exists? instead.
- MSG
-
- data_source_exists?(name)
- end
-
- def data_source_exists?(name)
name = Utils.extract_schema_qualified_name(name.to_s)
return false unless name.identifier
- select_value(<<-SQL, 'SCHEMA').to_i > 0
- SELECT COUNT(*)
- FROM pg_class c
- LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
- WHERE c.relkind IN ('r','v','m') -- (r)elation/table, (v)iew, (m)aterialized view
- AND c.relname = '#{name.identifier}'
- AND n.nspname = #{name.schema ? "'#{name.schema}'" : 'ANY (current_schemas(false))'}
+ select_values(<<-SQL, "SCHEMA").any?
+ SELECT tablename
+ FROM pg_tables
+ WHERE tablename = #{quote(name.identifier)}
+ AND schemaname = #{name.schema ? quote(name.schema) : "ANY (current_schemas(false))"}
SQL
end
- def views # :nodoc:
- select_values(<<-SQL, 'SCHEMA')
+ def data_source_exists?(name) # :nodoc:
+ name = Utils.extract_schema_qualified_name(name.to_s)
+ return false unless name.identifier
+
+ select_values(<<-SQL, "SCHEMA").any?
SELECT c.relname
FROM pg_class c
LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
- WHERE c.relkind IN ('v','m') -- (v)iew, (m)aterialized view
- AND n.nspname = ANY (current_schemas(false))
+ WHERE c.relkind IN ('r','v','m') -- (r)elation/table, (v)iew, (m)aterialized view
+ AND c.relname = #{quote(name.identifier)}
+ AND n.nspname = #{name.schema ? quote(name.schema) : "ANY (current_schemas(false))"}
SQL
end
@@ -132,13 +128,13 @@ module ActiveRecord
name = Utils.extract_schema_qualified_name(view_name.to_s)
return false unless name.identifier
- select_values(<<-SQL, 'SCHEMA').any?
+ select_values(<<-SQL, "SCHEMA").any?
SELECT c.relname
FROM pg_class c
LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE c.relkind IN ('v','m') -- (v)iew, (m)aterialized view
- AND c.relname = '#{name.identifier}'
- AND n.nspname = #{name.schema ? "'#{name.schema}'" : 'ANY (current_schemas(false))'}
+ AND c.relname = #{quote(name.identifier)}
+ AND n.nspname = #{name.schema ? quote(name.schema) : "ANY (current_schemas(false))"}
SQL
end
@@ -148,7 +144,7 @@ module ActiveRecord
# Returns true if schema exists.
def schema_exists?(name)
- select_value("SELECT COUNT(*) FROM pg_namespace WHERE nspname = '#{name}'", 'SCHEMA').to_i > 0
+ select_value("SELECT COUNT(*) FROM pg_namespace WHERE nspname = '#{name}'", "SCHEMA").to_i > 0
end
# Verifies existence of an index with a given name.
@@ -156,7 +152,7 @@ module ActiveRecord
table = Utils.extract_schema_qualified_name(table_name.to_s)
index = Utils.extract_schema_qualified_name(index_name.to_s)
- select_value(<<-SQL, 'SCHEMA').to_i > 0
+ select_value(<<-SQL, "SCHEMA").to_i > 0
SELECT COUNT(*)
FROM pg_class t
INNER JOIN pg_index d ON t.oid = d.indrelid
@@ -173,7 +169,7 @@ module ActiveRecord
def indexes(table_name, name = nil)
table = Utils.extract_schema_qualified_name(table_name.to_s)
- result = query(<<-SQL, 'SCHEMA')
+ result = query(<<-SQL, "SCHEMA")
SELECT distinct i.relname, d.indisunique, d.indkey, pg_get_indexdef(d.indexrelid), t.oid,
pg_catalog.obj_description(i.oid, 'pg_class') AS comment,
(SELECT COUNT(*) FROM pg_opclass o
@@ -221,28 +217,36 @@ module ActiveRecord
end.compact
end
- # Returns the list of all column definitions for a table.
- def columns(table_name) # :nodoc:
- table_name = table_name.to_s
- column_definitions(table_name).map do |column_name, type, default, notnull, oid, fmod, collation, comment|
- oid = oid.to_i
- fmod = fmod.to_i
- type_metadata = fetch_type_metadata(column_name, type, oid, fmod)
- default_value = extract_value_from_default(default)
- default_function = extract_default_function(default_value, default)
- new_column(column_name, default_value, type_metadata, !notnull, table_name, default_function, collation, comment: comment.presence)
- end
+ def new_column_from_field(table_name, field) # :nondoc:
+ column_name, type, default, notnull, oid, fmod, collation, comment = field
+ oid = oid.to_i
+ fmod = fmod.to_i
+ type_metadata = fetch_type_metadata(column_name, type, oid, fmod)
+ default_value = extract_value_from_default(default)
+ default_function = extract_default_function(default_value, default)
+ PostgreSQLColumn.new(
+ column_name,
+ default_value,
+ type_metadata,
+ !notnull,
+ table_name,
+ default_function,
+ collation,
+ comment: comment.presence
+ )
end
- def new_column(*args) # :nodoc:
- PostgreSQLColumn.new(*args)
+ def table_options(table_name) # :nodoc:
+ if comment = table_comment(table_name)
+ { comment: comment }
+ end
end
# Returns a comment stored in database for given table
def table_comment(table_name) # :nodoc:
name = Utils.extract_schema_qualified_name(table_name.to_s)
if name.identifier
- select_value(<<-SQL.strip_heredoc, 'SCHEMA')
+ select_value(<<-SQL.strip_heredoc, "SCHEMA")
SELECT pg_catalog.obj_description(c.oid, 'pg_class')
FROM pg_catalog.pg_class c
LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
@@ -255,32 +259,32 @@ module ActiveRecord
# Returns the current database name.
def current_database
- select_value('select current_database()', 'SCHEMA')
+ select_value("select current_database()", "SCHEMA")
end
# Returns the current schema name.
def current_schema
- select_value('SELECT current_schema', 'SCHEMA')
+ select_value("SELECT current_schema", "SCHEMA")
end
# Returns the current database encoding format.
def encoding
- select_value("SELECT pg_encoding_to_char(encoding) FROM pg_database WHERE datname LIKE '#{current_database}'", 'SCHEMA')
+ select_value("SELECT pg_encoding_to_char(encoding) FROM pg_database WHERE datname LIKE '#{current_database}'", "SCHEMA")
end
# Returns the current database collation.
def collation
- select_value("SELECT datcollate FROM pg_database WHERE datname LIKE '#{current_database}'", 'SCHEMA')
+ select_value("SELECT datcollate FROM pg_database WHERE datname LIKE '#{current_database}'", "SCHEMA")
end
# Returns the current database ctype.
def ctype
- select_value("SELECT datctype FROM pg_database WHERE datname LIKE '#{current_database}'", 'SCHEMA')
+ select_value("SELECT datctype FROM pg_database WHERE datname LIKE '#{current_database}'", "SCHEMA")
end
# Returns an array of schema names.
def schema_names
- select_values(<<-SQL, 'SCHEMA')
+ select_values(<<-SQL, "SCHEMA")
SELECT nspname
FROM pg_namespace
WHERE nspname !~ '^pg_.*'
@@ -290,7 +294,7 @@ module ActiveRecord
end
# Creates a schema for the given schema name.
- def create_schema schema_name
+ def create_schema(schema_name)
execute "CREATE SCHEMA #{quote_schema_name(schema_name)}"
end
@@ -306,37 +310,37 @@ module ActiveRecord
# This should be not be called manually but set in database.yml.
def schema_search_path=(schema_csv)
if schema_csv
- execute("SET search_path TO #{schema_csv}", 'SCHEMA')
+ execute("SET search_path TO #{schema_csv}", "SCHEMA")
@schema_search_path = schema_csv
end
end
# Returns the active schema search path.
def schema_search_path
- @schema_search_path ||= select_value('SHOW search_path', 'SCHEMA')
+ @schema_search_path ||= select_value("SHOW search_path", "SCHEMA")
end
# Returns the current client message level.
def client_min_messages
- select_value('SHOW client_min_messages', 'SCHEMA')
+ select_value("SHOW client_min_messages", "SCHEMA")
end
# Set the client message level.
def client_min_messages=(level)
- execute("SET client_min_messages TO '#{level}'", 'SCHEMA')
+ execute("SET client_min_messages TO '#{level}'", "SCHEMA")
end
# Returns the sequence name for a table's primary key or some other specified key.
- def default_sequence_name(table_name, pk = nil) #:nodoc:
- result = serial_sequence(table_name, pk || 'id')
+ def default_sequence_name(table_name, pk = "id") #:nodoc:
+ result = serial_sequence(table_name, pk)
return nil unless result
Utils.extract_schema_qualified_name(result).to_s
rescue ActiveRecord::StatementInvalid
- PostgreSQL::Name.new(nil, "#{table_name}_#{pk || 'id'}_seq").to_s
+ PostgreSQL::Name.new(nil, "#{table_name}_#{pk}_seq").to_s
end
def serial_sequence(table, column)
- select_value("SELECT pg_get_serial_sequence('#{table}', '#{column}')", 'SCHEMA')
+ select_value("SELECT pg_get_serial_sequence('#{table}', '#{column}')", "SCHEMA")
end
# Sets the sequence of a table's primary key to the specified value.
@@ -347,7 +351,7 @@ module ActiveRecord
if sequence
quoted_sequence = quote_table_name(sequence)
- select_value("SELECT setval('#{quoted_sequence}', #{value})", 'SCHEMA')
+ select_value("SELECT setval('#{quoted_sequence}', #{value})", "SCHEMA")
else
@logger.warn "#{table} has primary key #{pk} with no default sequence." if @logger
end
@@ -356,7 +360,7 @@ module ActiveRecord
# Resets the sequence of a table's primary key to the maximum value.
def reset_pk_sequence!(table, pk = nil, sequence = nil) #:nodoc:
- unless pk and sequence
+ unless pk && sequence
default_pk, default_sequence = pk_and_sequence_for(table)
pk ||= default_pk
@@ -370,7 +374,7 @@ module ActiveRecord
if pk && sequence
quoted_sequence = quote_table_name(sequence)
- select_value(<<-end_sql, 'SCHEMA')
+ select_value(<<-end_sql, "SCHEMA")
SELECT setval('#{quoted_sequence}', (SELECT COALESCE(MAX(#{quote_column_name pk})+(SELECT increment_by FROM #{quoted_sequence}), (SELECT min_value FROM #{quoted_sequence})) FROM #{quote_table_name(table)}), false)
end_sql
end
@@ -380,7 +384,7 @@ module ActiveRecord
def pk_and_sequence_for(table) #:nodoc:
# First try looking for a sequence with a dependency on the
# given table's primary key.
- result = query(<<-end_sql, 'SCHEMA')[0]
+ result = query(<<-end_sql, "SCHEMA")[0]
SELECT attr.attname, nsp.nspname, seq.relname
FROM pg_class seq,
pg_attribute attr,
@@ -399,8 +403,8 @@ module ActiveRecord
AND dep.refobjid = '#{quote_table_name(table)}'::regclass
end_sql
- if result.nil? or result.empty?
- result = query(<<-end_sql, 'SCHEMA')[0]
+ if result.nil? || result.empty?
+ result = query(<<-end_sql, "SCHEMA")[0]
SELECT attr.attname, nsp.nspname,
CASE
WHEN pg_get_expr(def.adbin, def.adrelid) !~* 'nextval' THEN NULL
@@ -431,11 +435,11 @@ module ActiveRecord
end
def primary_keys(table_name) # :nodoc:
- select_values(<<-SQL.strip_heredoc, 'SCHEMA')
+ select_values(<<-SQL.strip_heredoc, "SCHEMA")
WITH pk_constraint AS (
SELECT conrelid, unnest(conkey) AS connum FROM pg_constraint
WHERE contype = 'p'
- AND conrelid = '#{quote_table_name(table_name)}'::regclass
+ AND conrelid = #{quote(quote_table_name(table_name))}::regclass
), cons AS (
SELECT conrelid, connum, row_number() OVER() AS rownum FROM pk_constraint
)
@@ -579,7 +583,7 @@ module ActiveRecord
end
def foreign_keys(table_name)
- fk_info = select_all(<<-SQL.strip_heredoc, 'SCHEMA')
+ fk_info = select_all(<<-SQL.strip_heredoc, "SCHEMA")
SELECT t2.oid::regclass::text AS to_table, a1.attname AS column, a2.attname AS primary_key, c.conname AS name, c.confupdtype AS on_update, c.confdeltype AS on_delete
FROM pg_constraint c
JOIN pg_class t1 ON c.conrelid = t1.oid
@@ -595,23 +599,23 @@ module ActiveRecord
fk_info.map do |row|
options = {
- column: row['column'],
- name: row['name'],
- primary_key: row['primary_key']
+ column: row["column"],
+ name: row["name"],
+ primary_key: row["primary_key"]
}
- options[:on_delete] = extract_foreign_key_action(row['on_delete'])
- options[:on_update] = extract_foreign_key_action(row['on_update'])
+ options[:on_delete] = extract_foreign_key_action(row["on_delete"])
+ options[:on_update] = extract_foreign_key_action(row["on_update"])
- ForeignKeyDefinition.new(table_name, row['to_table'], options)
+ ForeignKeyDefinition.new(table_name, row["to_table"], options)
end
end
def extract_foreign_key_action(specifier) # :nodoc:
case specifier
- when 'c'; :cascade
- when 'n'; :nullify
- when 'r'; :restrict
+ when "c"; :cascade
+ when "n"; :nullify
+ when "r"; :restrict
end
end
@@ -621,48 +625,49 @@ module ActiveRecord
# Maps logical Rails types to PostgreSQL-specific data types.
def type_to_sql(type, limit = nil, precision = nil, scale = nil, array = nil)
- sql = case type.to_s
- when 'binary'
- # PostgreSQL doesn't support limits on binary (bytea) columns.
- # The hard limit is 1GB, because of a 32-bit size field, and TOAST.
- case limit
- when nil, 0..0x3fffffff; super(type)
- else raise(ActiveRecordError, "No binary type has byte size #{limit}.")
- end
- when 'text'
- # PostgreSQL doesn't support limits on text columns.
- # The hard limit is 1GB, according to section 8.3 in the manual.
- case limit
- when nil, 0..0x3fffffff; super(type)
- else raise(ActiveRecordError, "The limit on text can be at most 1GB - 1byte.")
- end
- when 'integer'
- case limit
- when 1, 2; 'smallint'
- when nil, 3, 4; 'integer'
- when 5..8; 'bigint'
- else raise(ActiveRecordError, "No integer type has byte size #{limit}. Use a numeric with scale 0 instead.")
+ sql = \
+ case type.to_s
+ when "binary"
+ # PostgreSQL doesn't support limits on binary (bytea) columns.
+ # The hard limit is 1GB, because of a 32-bit size field, and TOAST.
+ case limit
+ when nil, 0..0x3fffffff; super(type)
+ else raise(ActiveRecordError, "No binary type has byte size #{limit}.")
+ end
+ when "text"
+ # PostgreSQL doesn't support limits on text columns.
+ # The hard limit is 1GB, according to section 8.3 in the manual.
+ case limit
+ when nil, 0..0x3fffffff; super(type)
+ else raise(ActiveRecordError, "The limit on text can be at most 1GB - 1byte.")
+ end
+ when "integer"
+ case limit
+ when 1, 2; "smallint"
+ when nil, 3, 4; "integer"
+ when 5..8; "bigint"
+ else raise(ActiveRecordError, "No integer type has byte size #{limit}. Use a numeric with scale 0 instead.")
+ end
+ else
+ super(type, limit, precision, scale)
end
- else
- super(type, limit, precision, scale)
- end
- sql << '[]' if array && type != :primary_key
+ sql << "[]" if array && type != :primary_key
sql
end
# PostgreSQL requires the ORDER BY columns in the select list for distinct queries, and
# requires that the ORDER BY include the distinct column.
def columns_for_distinct(columns, orders) #:nodoc:
- order_columns = orders.reject(&:blank?).map{ |s|
+ order_columns = orders.reject(&:blank?).map { |s|
# Convert Arel node to string
s = s.to_sql unless s.is_a?(String)
# Remove any ASC/DESC modifiers
- s.gsub(/\s+(?:ASC|DESC)\b/i, '')
- .gsub(/\s+NULLS\s+(?:FIRST|LAST)\b/i, '')
+ s.gsub(/\s+(?:ASC|DESC)\b/i, "")
+ .gsub(/\s+NULLS\s+(?:FIRST|LAST)\b/i, "")
}.reject(&:blank?).map.with_index { |column, i| "#{column} AS alias_#{i}" }
- [super, *order_columns].join(', ')
+ [super, *order_columns].join(", ")
end
def fetch_type_metadata(column_name, sql_type, oid, fmod)
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/type_metadata.rb b/activerecord/lib/active_record/connection_adapters/postgresql/type_metadata.rb
index b2c49989a4..311988625f 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/type_metadata.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/type_metadata.rb
@@ -8,7 +8,7 @@ module ActiveRecord
@type_metadata = type_metadata
@oid = oid
@fmod = fmod
- @array = /\[\]$/ === type_metadata.sql_type
+ @array = /\[\]$/.match?(type_metadata.sql_type)
end
def sql_type
@@ -27,9 +27,9 @@ module ActiveRecord
protected
- def attributes_for_hash
- [self.class, @type_metadata, oid, fmod]
- end
+ def attributes_for_hash
+ [self.class, @type_metadata, oid, fmod]
+ end
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/utils.rb b/activerecord/lib/active_record/connection_adapters/postgresql/utils.rb
index 9a0b80d7d3..a3f9ce6d64 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/utils.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/utils.rb
@@ -35,6 +35,12 @@ module ActiveRecord
end
protected
+
+ def parts
+ @parts ||= [@schema, @identifier].compact
+ end
+
+ private
def unquote(part)
if part && part.start_with?('"')
part[1..-2]
@@ -42,10 +48,6 @@ module ActiveRecord
part
end
end
-
- def parts
- @parts ||= [@schema, @identifier].compact
- end
end
module Utils # :nodoc:
@@ -53,7 +55,7 @@ module ActiveRecord
# Returns an instance of <tt>ActiveRecord::ConnectionAdapters::PostgreSQL::Name</tt>
# extracted from +string+.
- # +schema+ is nil if not specified in +string+.
+ # +schema+ is +nil+ if not specified in +string+.
# +schema+ and +identifier+ exclude surrounding quotes (regardless of whether provided in +string+)
# +string+ supports the range of schema/table references understood by PostgreSQL, for example:
#
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
index ddfc560747..0ebd907cc0 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -1,6 +1,6 @@
# Make sure we're using pg high enough for type casts and Ruby 2.2+ compatibility
-gem 'pg', '~> 0.18'
-require 'pg'
+gem "pg", "~> 0.18"
+require "pg"
require "active_record/connection_adapters/abstract_adapter"
require "active_record/connection_adapters/postgresql/column"
@@ -67,10 +67,10 @@ module ActiveRecord
# In addition, default connection parameters of libpq can be set per environment variables.
# See http://www.postgresql.org/docs/current/static/libpq-envars.html .
class PostgreSQLAdapter < AbstractAdapter
- ADAPTER_NAME = 'PostgreSQL'.freeze
+ ADAPTER_NAME = "PostgreSQL".freeze
NATIVE_DATABASE_TYPES = {
- primary_key: "serial primary key",
+ primary_key: "bigserial primary key",
string: { name: "character varying" },
text: { name: "text" },
integer: { name: "integer" },
@@ -173,7 +173,7 @@ module ActiveRecord
end
def index_algorithms
- { concurrently: 'CONCURRENTLY' }
+ { concurrently: "CONCURRENTLY" }
end
class StatementPool < ConnectionAdapters::StatementPool
@@ -227,7 +227,7 @@ module ActiveRecord
@type_map = Type::HashLookupTypeMap.new
initialize_type_map(type_map)
- @local_tz = execute('SHOW TIME ZONE', 'SCHEMA').first["TimeZone"]
+ @local_tz = execute("SHOW TIME ZONE", "SCHEMA").first["TimeZone"]
@use_insert_returning = @config.key?(:insert_returning) ? self.class.type_cast_config_to_boolean(@config[:insert_returning]) : true
end
@@ -242,7 +242,7 @@ module ActiveRecord
# Is this connection alive and ready for queries?
def active?
- @connection.query 'SELECT 1'
+ @connection.query "SELECT 1"
true
rescue PGError
false
@@ -259,9 +259,9 @@ module ActiveRecord
clear_cache!
reset_transaction
unless @connection.transaction_status == ::PG::PQTRANS_IDLE
- @connection.query 'ROLLBACK'
+ @connection.query "ROLLBACK"
end
- @connection.query 'DISCARD ALL'
+ @connection.query "DISCARD ALL"
configure_connection
end
@@ -287,7 +287,7 @@ module ActiveRecord
end
def set_standard_conforming_strings
- execute('SET standard_conforming_strings = on', 'SCHEMA')
+ execute("SET standard_conforming_strings = on", "SCHEMA")
end
def supports_ddl_transactions?
@@ -315,6 +315,10 @@ module ActiveRecord
postgresql_version >= 90300
end
+ def supports_pgcrypto_uuid?
+ postgresql_version >= 90400
+ end
+
def get_advisory_lock(lock_id) # :nodoc:
unless lock_id.is_a?(Integer) && lock_id.bit_length <= 63
raise(ArgumentError, "Postgres requires advisory lock ids to be a signed 64 bit integer")
@@ -344,7 +348,7 @@ module ActiveRecord
def extension_enabled?(name)
if supports_extensions?
res = exec_query "SELECT EXISTS(SELECT * FROM pg_available_extensions WHERE name = '#{name}' AND installed_version IS NOT NULL) as enabled",
- 'SCHEMA'
+ "SCHEMA"
res.cast_values.first
end
end
@@ -359,7 +363,7 @@ module ActiveRecord
# Returns the configured supported identifier length supported by PostgreSQL
def table_alias_length
- @table_alias_length ||= query('SHOW max_identifier_length', 'SCHEMA')[0][0].to_i
+ @table_alias_length ||= query("SHOW max_identifier_length", "SCHEMA")[0][0].to_i
end
# Set the authorized user for this session
@@ -381,7 +385,7 @@ module ActiveRecord
end
def lookup_cast_type(sql_type) # :nodoc:
- oid = execute("SELECT #{quote(sql_type)}::regtype::oid", "SCHEMA").first['oid'].to_i
+ oid = execute("SELECT #{quote(sql_type)}::regtype::oid", "SCHEMA").first["oid"].to_i
super(oid)
end
@@ -400,13 +404,16 @@ module ActiveRecord
@connection.server_version
end
- protected
+ private
# See http://www.postgresql.org/docs/current/static/errcodes-appendix.html
VALUE_LIMIT_VIOLATION = "22001"
+ NUMERIC_VALUE_OUT_OF_RANGE = "22003"
+ NOT_NULL_VIOLATION = "23502"
FOREIGN_KEY_VIOLATION = "23503"
UNIQUE_VIOLATION = "23505"
SERIALIZATION_FAILURE = "40001"
+ DEADLOCK_DETECTED = "40P01"
def translate_exception(exception, message)
return exception unless exception.respond_to?(:result)
@@ -418,74 +425,78 @@ module ActiveRecord
InvalidForeignKey.new(message)
when VALUE_LIMIT_VIOLATION
ValueTooLong.new(message)
+ when NUMERIC_VALUE_OUT_OF_RANGE
+ RangeError.new(message)
+ when NOT_NULL_VIOLATION
+ NotNullViolation.new(message)
when SERIALIZATION_FAILURE
- TransactionSerializationError.new(message)
+ SerializationFailure.new(message)
+ when DEADLOCK_DETECTED
+ Deadlocked.new(message)
else
super
end
end
- private
-
- def get_oid_type(oid, fmod, column_name, sql_type = '') # :nodoc:
+ def get_oid_type(oid, fmod, column_name, sql_type = "")
if !type_map.key?(oid)
load_additional_types(type_map, [oid])
end
type_map.fetch(oid, fmod, sql_type) {
warn "unknown OID #{oid}: failed to recognize type of '#{column_name}'. It will be treated as String."
- Type::Value.new.tap do |cast_type|
+ Type.default_value.tap do |cast_type|
type_map.register_type(oid, cast_type)
end
}
end
- def initialize_type_map(m) # :nodoc:
- register_class_with_limit m, 'int2', Type::Integer
- register_class_with_limit m, 'int4', Type::Integer
- register_class_with_limit m, 'int8', Type::Integer
- m.alias_type 'oid', 'int2'
- m.register_type 'float4', Type::Float.new
- m.alias_type 'float8', 'float4'
- m.register_type 'text', Type::Text.new
- register_class_with_limit m, 'varchar', Type::String
- m.alias_type 'char', 'varchar'
- m.alias_type 'name', 'varchar'
- m.alias_type 'bpchar', 'varchar'
- m.register_type 'bool', Type::Boolean.new
- register_class_with_limit m, 'bit', OID::Bit
- register_class_with_limit m, 'varbit', OID::BitVarying
- m.alias_type 'timestamptz', 'timestamp'
- m.register_type 'date', Type::Date.new
-
- m.register_type 'money', OID::Money.new
- m.register_type 'bytea', OID::Bytea.new
- m.register_type 'point', OID::Point.new
- m.register_type 'hstore', OID::Hstore.new
- m.register_type 'json', OID::Json.new
- m.register_type 'jsonb', OID::Jsonb.new
- m.register_type 'cidr', OID::Cidr.new
- m.register_type 'inet', OID::Inet.new
- m.register_type 'uuid', OID::Uuid.new
- m.register_type 'xml', OID::Xml.new
- m.register_type 'tsvector', OID::SpecializedString.new(:tsvector)
- m.register_type 'macaddr', OID::SpecializedString.new(:macaddr)
- m.register_type 'citext', OID::SpecializedString.new(:citext)
- m.register_type 'ltree', OID::SpecializedString.new(:ltree)
- m.register_type 'line', OID::SpecializedString.new(:line)
- m.register_type 'lseg', OID::SpecializedString.new(:lseg)
- m.register_type 'box', OID::SpecializedString.new(:box)
- m.register_type 'path', OID::SpecializedString.new(:path)
- m.register_type 'polygon', OID::SpecializedString.new(:polygon)
- m.register_type 'circle', OID::SpecializedString.new(:circle)
+ def initialize_type_map(m)
+ register_class_with_limit m, "int2", Type::Integer
+ register_class_with_limit m, "int4", Type::Integer
+ register_class_with_limit m, "int8", Type::Integer
+ m.alias_type "oid", "int2"
+ m.register_type "float4", Type::Float.new
+ m.alias_type "float8", "float4"
+ m.register_type "text", Type::Text.new
+ register_class_with_limit m, "varchar", Type::String
+ m.alias_type "char", "varchar"
+ m.alias_type "name", "varchar"
+ m.alias_type "bpchar", "varchar"
+ m.register_type "bool", Type::Boolean.new
+ register_class_with_limit m, "bit", OID::Bit
+ register_class_with_limit m, "varbit", OID::BitVarying
+ m.alias_type "timestamptz", "timestamp"
+ m.register_type "date", Type::Date.new
+
+ m.register_type "money", OID::Money.new
+ m.register_type "bytea", OID::Bytea.new
+ m.register_type "point", OID::Point.new
+ m.register_type "hstore", OID::Hstore.new
+ m.register_type "json", OID::Json.new
+ m.register_type "jsonb", OID::Jsonb.new
+ m.register_type "cidr", OID::Cidr.new
+ m.register_type "inet", OID::Inet.new
+ m.register_type "uuid", OID::Uuid.new
+ m.register_type "xml", OID::Xml.new
+ m.register_type "tsvector", OID::SpecializedString.new(:tsvector)
+ m.register_type "macaddr", OID::SpecializedString.new(:macaddr)
+ m.register_type "citext", OID::SpecializedString.new(:citext)
+ m.register_type "ltree", OID::SpecializedString.new(:ltree)
+ m.register_type "line", OID::SpecializedString.new(:line)
+ m.register_type "lseg", OID::SpecializedString.new(:lseg)
+ m.register_type "box", OID::SpecializedString.new(:box)
+ m.register_type "path", OID::SpecializedString.new(:path)
+ m.register_type "polygon", OID::SpecializedString.new(:polygon)
+ m.register_type "circle", OID::SpecializedString.new(:circle)
# FIXME: why are we keeping these types as strings?
- m.alias_type 'interval', 'varchar'
+ m.alias_type "interval", "varchar"
- register_class_with_precision m, 'time', Type::Time
- register_class_with_precision m, 'timestamp', OID::DateTime
+ register_class_with_precision m, "time", Type::Time
+ register_class_with_precision m, "timestamp", OID::DateTime
- m.register_type 'numeric' do |_, fmod, sql_type|
+ m.register_type "numeric" do |_, fmod, sql_type|
precision = extract_precision(sql_type)
scale = extract_scale(sql_type)
@@ -508,7 +519,7 @@ module ActiveRecord
load_additional_types(m)
end
- def extract_limit(sql_type) # :nodoc:
+ def extract_limit(sql_type)
case sql_type
when /^bigint/i, /^int8/i
8
@@ -520,41 +531,41 @@ module ActiveRecord
end
# Extracts the value from a PostgreSQL column default definition.
- def extract_value_from_default(default) # :nodoc:
+ def extract_value_from_default(default)
case default
# Quoted types
- when /\A[\(B]?'(.*)'.*::"?([\w. ]+)"?(?:\[\])?\z/m
- # The default 'now'::date is CURRENT_DATE
- if $1 == "now".freeze && $2 == "date".freeze
- nil
- else
- $1.gsub("''".freeze, "'".freeze)
- end
+ when /\A[\(B]?'(.*)'.*::"?([\w. ]+)"?(?:\[\])?\z/m
+ # The default 'now'::date is CURRENT_DATE
+ if $1 == "now".freeze && $2 == "date".freeze
+ nil
+ else
+ $1.gsub("''".freeze, "'".freeze)
+ end
# Boolean types
- when 'true'.freeze, 'false'.freeze
- default
+ when "true".freeze, "false".freeze
+ default
# Numeric types
- when /\A\(?(-?\d+(\.\d*)?)\)?(::bigint)?\z/
- $1
+ when /\A\(?(-?\d+(\.\d*)?)\)?(::bigint)?\z/
+ $1
# Object identifier types
- when /\A-?\d+\z/
- $1
- else
- # Anything else is blank, some user type, or some function
- # and we can't know the value of that, so return nil.
- nil
+ when /\A-?\d+\z/
+ $1
+ else
+ # Anything else is blank, some user type, or some function
+ # and we can't know the value of that, so return nil.
+ nil
end
end
- def extract_default_function(default_value, default) # :nodoc:
+ def extract_default_function(default_value, default)
default if has_default_function?(default_value, default)
end
- def has_default_function?(default_value, default) # :nodoc:
+ def has_default_function?(default_value, default)
!default_value && (%r{\w+\(.*\)|\(.*\)::\w+} === default)
end
- def load_additional_types(type_map, oids = nil) # :nodoc:
+ def load_additional_types(type_map, oids = nil)
initializer = OID::TypeMapInitializer.new(type_map)
if supports_ranges?
@@ -576,7 +587,7 @@ module ActiveRecord
query += initializer.query_conditions_for_initial_load(type_map)
end
- execute_and_clear(query, 'SCHEMA', []) do |records|
+ execute_and_clear(query, "SCHEMA", []) do |records|
initializer.run(records)
end
end
@@ -597,16 +608,22 @@ module ActiveRecord
end
def exec_no_cache(sql, name, binds)
- type_casted_binds = binds.map { |attr| type_cast(attr.value_for_database) }
- log(sql, name, binds) { @connection.async_exec(sql, type_casted_binds) }
+ type_casted_binds = type_casted_binds(binds)
+ log(sql, name, binds, type_casted_binds) do
+ ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
+ @connection.async_exec(sql, type_casted_binds)
+ end
+ end
end
def exec_cache(sql, name, binds)
stmt_key = prepare_statement(sql)
- type_casted_binds = binds.map { |attr| type_cast(attr.value_for_database) }
+ type_casted_binds = type_casted_binds(binds)
- log(sql, name, binds, stmt_key) do
- @connection.exec_prepared(stmt_key, type_casted_binds)
+ log(sql, name, binds, type_casted_binds, stmt_key) do
+ ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
+ @connection.exec_prepared(stmt_key, type_casted_binds)
+ end
end
rescue ActiveRecord::StatementInvalid => e
raise unless is_cached_plan_failure?(e)
@@ -631,7 +648,7 @@ module ActiveRecord
#
# Check here for more details:
# http://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/utils/cache/plancache.c#l573
- CACHED_PLAN_HEURISTIC = 'cached plan must not change result type'.freeze
+ CACHED_PLAN_HEURISTIC = "cached plan must not change result type".freeze
def is_cached_plan_failure?(e)
pgerror = e.cause
code = pgerror.result.result_error_field(PGresult::PG_DIAG_SQLSTATE)
@@ -687,7 +704,7 @@ module ActiveRecord
if @config[:encoding]
@connection.set_client_encoding(@config[:encoding])
end
- self.client_min_messages = @config[:min_messages] || 'warning'
+ self.client_min_messages = @config[:min_messages] || "warning"
self.schema_search_path = @config[:schema_search_path] || @config[:schema_order]
# Use standard-conforming strings so we don't have to do the E'...' dance.
@@ -697,27 +714,27 @@ module ActiveRecord
# TIMESTAMP WITH ZONE types in UTC.
# (SET TIME ZONE does not use an equals sign like other SET variables)
if ActiveRecord::Base.default_timezone == :utc
- execute("SET time zone 'UTC'", 'SCHEMA')
+ execute("SET time zone 'UTC'", "SCHEMA")
elsif @local_tz
- execute("SET time zone '#{@local_tz}'", 'SCHEMA')
+ execute("SET time zone '#{@local_tz}'", "SCHEMA")
end
# SET statements from :variables config hash
# http://www.postgresql.org/docs/current/static/sql-set.html
variables = @config[:variables] || {}
variables.map do |k, v|
- if v == ':default' || v == :default
+ if v == ":default" || v == :default
# Sets the value to the global or compile default
- execute("SET SESSION #{k} TO DEFAULT", 'SCHEMA')
+ execute("SET SESSION #{k} TO DEFAULT", "SCHEMA")
elsif !v.nil?
- execute("SET SESSION #{k} TO #{quote(v)}", 'SCHEMA')
+ execute("SET SESSION #{k} TO #{quote(v)}", "SCHEMA")
end
end
end
# Returns the current ID of a table's sequence.
- def last_insert_id_result(sequence_name) # :nodoc:
- exec_query("SELECT currval('#{sequence_name}')", 'SQL')
+ def last_insert_id_result(sequence_name)
+ exec_query("SELECT currval('#{sequence_name}')", "SQL")
end
# Returns the list of a table's column names, data types, and default values.
@@ -738,8 +755,8 @@ module ActiveRecord
# Query implementation notes:
# - format_type includes the column size constraint, e.g. varchar(50)
# - ::regclass is a function that gives the id for a table name
- def column_definitions(table_name) # :nodoc:
- query(<<-end_sql, 'SCHEMA')
+ def column_definitions(table_name)
+ query(<<-end_sql, "SCHEMA")
SELECT a.attname, format_type(a.atttypid, a.atttypmod),
pg_get_expr(d.adbin, d.adrelid), a.attnotnull, a.atttypid, a.atttypmod,
(SELECT c.collname FROM pg_collation c, pg_type t
@@ -747,18 +764,18 @@ module ActiveRecord
col_description(a.attrelid, a.attnum) AS comment
FROM pg_attribute a LEFT JOIN pg_attrdef d
ON a.attrelid = d.adrelid AND a.attnum = d.adnum
- WHERE a.attrelid = '#{quote_table_name(table_name)}'::regclass
+ WHERE a.attrelid = #{quote(quote_table_name(table_name))}::regclass
AND a.attnum > 0 AND NOT a.attisdropped
ORDER BY a.attnum
end_sql
end
- def extract_table_ref_from_insert_sql(sql) # :nodoc:
+ def extract_table_ref_from_insert_sql(sql)
sql[/into\s("[A-Za-z0-9_."\[\]\s]+"|[A-Za-z0-9_."\[\]]+)\s*/im]
$1.strip if $1
end
- def create_table_definition(*args) # :nodoc:
+ def create_table_definition(*args)
PostgreSQL::TableDefinition.new(*args)
end
@@ -768,10 +785,14 @@ module ActiveRecord
sql = <<-end_sql
SELECT exists(
SELECT * FROM pg_proc
+ WHERE proname = 'lower'
+ AND proargtypes = ARRAY[#{quote column.sql_type}::regtype]::oidvector
+ ) OR exists(
+ SELECT * FROM pg_proc
INNER JOIN pg_cast
- ON casttarget::text::oidvector = proargtypes
+ ON ARRAY[casttarget]::oidvector = proargtypes
WHERE proname = 'lower'
- AND castsource = '#{column.sql_type}'::regtype::oid
+ AND castsource = #{quote column.sql_type}::regtype
)
end_sql
execute_and_clear(sql, "SCHEMA", []) do |result|
@@ -785,19 +806,18 @@ module ActiveRecord
map[Integer] = PG::TextEncoder::Integer.new
map[TrueClass] = PG::TextEncoder::Boolean.new
map[FalseClass] = PG::TextEncoder::Boolean.new
- map[Float] = PG::TextEncoder::Float.new
@connection.type_map_for_queries = map
end
def add_pg_decoders
coders_by_name = {
- 'int2' => PG::TextDecoder::Integer,
- 'int4' => PG::TextDecoder::Integer,
- 'int8' => PG::TextDecoder::Integer,
- 'oid' => PG::TextDecoder::Integer,
- 'float4' => PG::TextDecoder::Float,
- 'float8' => PG::TextDecoder::Float,
- 'bool' => PG::TextDecoder::Boolean,
+ "int2" => PG::TextDecoder::Integer,
+ "int4" => PG::TextDecoder::Integer,
+ "int8" => PG::TextDecoder::Integer,
+ "oid" => PG::TextDecoder::Integer,
+ "float4" => PG::TextDecoder::Float,
+ "float8" => PG::TextDecoder::Float,
+ "bool" => PG::TextDecoder::Boolean,
}
known_coder_types = coders_by_name.keys.map { |n| quote(n) }
query = <<-SQL % known_coder_types.join(", ")
@@ -807,7 +827,7 @@ module ActiveRecord
SQL
coders = execute_and_clear(query, "SCHEMA", []) do |result|
result
- .map { |row| construct_coder(row, coders_by_name[row['typname']]) }
+ .map { |row| construct_coder(row, coders_by_name[row["typname"]]) }
.compact
end
@@ -818,7 +838,7 @@ module ActiveRecord
def construct_coder(row, coder_class)
return unless coder_class
- coder_class.new(oid: row['oid'].to_i, name: row['typname'])
+ coder_class.new(oid: row["oid"].to_i, name: row["typname"])
end
ActiveRecord::Type.add_modifier({ array: true }, OID::Array, adapter: :postgresql)
@@ -835,8 +855,8 @@ module ActiveRecord
ActiveRecord::Type.register(:json, OID::Json, adapter: :postgresql)
ActiveRecord::Type.register(:jsonb, OID::Jsonb, adapter: :postgresql)
ActiveRecord::Type.register(:money, OID::Money, adapter: :postgresql)
- ActiveRecord::Type.register(:point, OID::Rails51Point, adapter: :postgresql)
- ActiveRecord::Type.register(:legacy_point, OID::Point, adapter: :postgresql)
+ ActiveRecord::Type.register(:point, OID::Point, adapter: :postgresql)
+ ActiveRecord::Type.register(:legacy_point, OID::LegacyPoint, adapter: :postgresql)
ActiveRecord::Type.register(:uuid, OID::Uuid, adapter: :postgresql)
ActiveRecord::Type.register(:vector, OID::Vector, adapter: :postgresql)
ActiveRecord::Type.register(:xml, OID::Xml, adapter: :postgresql)
diff --git a/activerecord/lib/active_record/connection_adapters/schema_cache.rb b/activerecord/lib/active_record/connection_adapters/schema_cache.rb
index eee142378c..4d339b0a8c 100644
--- a/activerecord/lib/active_record/connection_adapters/schema_cache.rb
+++ b/activerecord/lib/active_record/connection_adapters/schema_cache.rb
@@ -21,6 +21,22 @@ module ActiveRecord
@data_sources = @data_sources.dup
end
+ def encode_with(coder)
+ coder["columns"] = @columns
+ coder["columns_hash"] = @columns_hash
+ coder["primary_keys"] = @primary_keys
+ coder["data_sources"] = @data_sources
+ coder["version"] = ActiveRecord::Migrator.current_version
+ end
+
+ def init_with(coder)
+ @columns = coder["columns"]
+ @columns_hash = coder["columns_hash"]
+ @primary_keys = coder["primary_keys"]
+ @data_sources = coder["data_sources"]
+ @version = coder["version"]
+ end
+
def primary_keys(table_name)
@primary_keys[table_name] ||= data_source_exists?(table_name) ? connection.primary_key(table_name) : nil
end
@@ -32,9 +48,6 @@ module ActiveRecord
@data_sources[name] = connection.data_source_exists?(name)
end
- alias table_exists? data_source_exists?
- deprecate :table_exists? => "use #data_source_exists? instead"
-
# Add internal cache for table with +table_name+.
def add(table_name)
@@ -48,8 +61,6 @@ module ActiveRecord
def data_sources(name)
@data_sources[name]
end
- alias tables data_sources
- deprecate :tables => "use #data_sources instead"
# Get the columns for a table
def columns(table_name)
@@ -84,8 +95,6 @@ module ActiveRecord
@primary_keys.delete name
@data_sources.delete name
end
- alias clear_table_cache! clear_data_source_cache!
- deprecate :clear_table_cache! => "use #clear_data_source_cache! instead"
def marshal_dump
# if we get current version during initialization, it happens stack over flow.
diff --git a/activerecord/lib/active_record/connection_adapters/sql_type_metadata.rb b/activerecord/lib/active_record/connection_adapters/sql_type_metadata.rb
index ccb7e154ee..9e12ae0de8 100644
--- a/activerecord/lib/active_record/connection_adapters/sql_type_metadata.rb
+++ b/activerecord/lib/active_record/connection_adapters/sql_type_metadata.rb
@@ -24,9 +24,9 @@ module ActiveRecord
protected
- def attributes_for_hash
- [self.class, sql_type, type, limit, precision, scale]
- end
+ def attributes_for_hash
+ [self.class, sql_type, type, limit, precision, scale]
+ end
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb b/activerecord/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb
index a946f5ebd0..6fe3e1211e 100644
--- a/activerecord/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb
+++ b/activerecord/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb
@@ -10,7 +10,7 @@ module ActiveRecord
#
def pp(result)
result.rows.map do |row|
- row.join('|')
+ row.join("|")
end.join("\n") + "\n"
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3/quoting.rb b/activerecord/lib/active_record/connection_adapters/sqlite3/quoting.rb
index d5a181d3e2..f01ed67b0f 100644
--- a/activerecord/lib/active_record/connection_adapters/sqlite3/quoting.rb
+++ b/activerecord/lib/active_record/connection_adapters/sqlite3/quoting.rb
@@ -11,7 +11,7 @@ module ActiveRecord
end
def quote_column_name(name)
- @quoted_column_names[name] ||= %Q("#{super.gsub('"', '""')}")
+ @quoted_column_names[name] ||= %Q("#{super.gsub('"', '""')}").freeze
end
def quoted_time(value)
@@ -20,28 +20,28 @@ module ActiveRecord
private
- def _quote(value)
- if value.is_a?(Type::Binary::Data)
- "x'#{value.hex}'"
- else
- super
+ def _quote(value)
+ if value.is_a?(Type::Binary::Data)
+ "x'#{value.hex}'"
+ else
+ super
+ end
end
- end
- def _type_cast(value)
- case value
- when BigDecimal
- value.to_f
- when String
- if value.encoding == Encoding::ASCII_8BIT
- super(value.encode(Encoding::UTF_8))
+ def _type_cast(value)
+ case value
+ when BigDecimal
+ value.to_f
+ when String
+ if value.encoding == Encoding::ASCII_8BIT
+ super(value.encode(Encoding::UTF_8))
+ else
+ super
+ end
else
super
end
- else
- super
end
- end
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb
new file mode 100644
index 0000000000..d0b38dff4c
--- /dev/null
+++ b/activerecord/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb
@@ -0,0 +1,23 @@
+module ActiveRecord
+ module ConnectionAdapters
+ module SQLite3
+ module ColumnMethods
+ def primary_key(name, type = :primary_key, **options)
+ if options.delete(:auto_increment) == true && %i(integer bigint).include?(type)
+ type = :primary_key
+ end
+
+ super
+ end
+ end
+
+ class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition
+ include ColumnMethods
+ end
+
+ class Table < ActiveRecord::ConnectionAdapters::Table
+ include ColumnMethods
+ end
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb b/activerecord/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb
new file mode 100644
index 0000000000..c027fef83c
--- /dev/null
+++ b/activerecord/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb
@@ -0,0 +1,13 @@
+module ActiveRecord
+ module ConnectionAdapters
+ module SQLite3
+ module ColumnDumper
+ private
+
+ def default_primary_key?(column)
+ schema_type(column) == :integer
+ end
+ end
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
index eb2268157b..297e2997a9 100644
--- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
@@ -1,11 +1,13 @@
-require 'active_record/connection_adapters/abstract_adapter'
-require 'active_record/connection_adapters/statement_pool'
-require 'active_record/connection_adapters/sqlite3/explain_pretty_printer'
-require 'active_record/connection_adapters/sqlite3/quoting'
-require 'active_record/connection_adapters/sqlite3/schema_creation'
+require "active_record/connection_adapters/abstract_adapter"
+require "active_record/connection_adapters/statement_pool"
+require "active_record/connection_adapters/sqlite3/explain_pretty_printer"
+require "active_record/connection_adapters/sqlite3/quoting"
+require "active_record/connection_adapters/sqlite3/schema_creation"
+require "active_record/connection_adapters/sqlite3/schema_definitions"
+require "active_record/connection_adapters/sqlite3/schema_dumper"
-gem 'sqlite3', '~> 1.3.6'
-require 'sqlite3'
+gem "sqlite3", "~> 1.3.6"
+require "sqlite3"
module ActiveRecord
module ConnectionHandling # :nodoc:
@@ -18,7 +20,7 @@ module ActiveRecord
# Allow database path relative to Rails.root, but only if the database
# path is not the special path that tells sqlite to build a database only
# in memory.
- if ':memory:' != config[:database]
+ if ":memory:" != config[:database]
config[:database] = File.expand_path(config[:database], Rails.root) if defined?(Rails.root)
dirname = File.dirname(config[:database])
Dir.mkdir(dirname) unless File.directory?(dirname)
@@ -26,7 +28,7 @@ module ActiveRecord
db = SQLite3::Database.new(
config[:database].to_s,
- :results_as_hash => true
+ results_as_hash: true
)
db.busy_timeout(ConnectionAdapters::SQLite3Adapter.type_cast_config_to_integer(config[:timeout])) if config[:timeout]
@@ -49,12 +51,13 @@ module ActiveRecord
#
# * <tt>:database</tt> - Path to the database file.
class SQLite3Adapter < AbstractAdapter
- ADAPTER_NAME = 'SQLite'.freeze
+ ADAPTER_NAME = "SQLite".freeze
include SQLite3::Quoting
+ include SQLite3::ColumnDumper
NATIVE_DATABASE_TYPES = {
- primary_key: 'INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL',
+ primary_key: "INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL",
string: { name: "varchar" },
text: { name: "text" },
integer: { name: "integer" },
@@ -70,9 +73,13 @@ module ActiveRecord
class StatementPool < ConnectionAdapters::StatementPool
private
- def dealloc(stmt)
- stmt[:stmt].close unless stmt[:stmt].closed?
- end
+ def dealloc(stmt)
+ stmt[:stmt].close unless stmt[:stmt].closed?
+ end
+ end
+
+ def update_table_definition(table_name, base) # :nodoc:
+ SQLite3::Table.new(table_name, base)
end
def schema_creation # :nodoc:
@@ -99,7 +106,7 @@ module ActiveRecord
end
def supports_partial_index?
- sqlite_version >= '3.8.0'
+ sqlite_version >= "3.8.0"
end
# Returns true, since this connection adapter supports prepared statement
@@ -130,7 +137,7 @@ module ActiveRecord
end
def supports_multi_insert?
- sqlite_version >= '3.7.11'
+ sqlite_version >= "3.7.11"
end
def active?
@@ -159,7 +166,7 @@ module ActiveRecord
end
# Returns 62. SQLite supports index names up to 64
- # characters. The rest is used by rails internally to perform
+ # characters. The rest is used by Rails internally to perform
# temporary rename operations
def allowed_index_name_length
index_name_length - 2
@@ -184,41 +191,43 @@ module ActiveRecord
def explain(arel, binds = [])
sql = "EXPLAIN QUERY PLAN #{to_sql(arel, binds)}"
- SQLite3::ExplainPrettyPrinter.new.pp(exec_query(sql, 'EXPLAIN', []))
+ SQLite3::ExplainPrettyPrinter.new.pp(exec_query(sql, "EXPLAIN", []))
end
def exec_query(sql, name = nil, binds = [], prepare: false)
- type_casted_binds = binds.map { |attr| type_cast(attr.value_for_database) }
-
- log(sql, name, binds) do
- # Don't cache statements if they are not prepared
- unless prepare
- stmt = @connection.prepare(sql)
- begin
- cols = stmt.columns
- unless without_prepared_statement?(binds)
- stmt.bind_params(type_casted_binds)
+ type_casted_binds = type_casted_binds(binds)
+
+ log(sql, name, binds, type_casted_binds) do
+ ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
+ # Don't cache statements if they are not prepared
+ unless prepare
+ stmt = @connection.prepare(sql)
+ begin
+ cols = stmt.columns
+ unless without_prepared_statement?(binds)
+ stmt.bind_params(type_casted_binds)
+ end
+ records = stmt.to_a
+ ensure
+ stmt.close
end
+ else
+ cache = @statements[sql] ||= {
+ stmt: @connection.prepare(sql)
+ }
+ stmt = cache[:stmt]
+ cols = cache[:cols] ||= stmt.columns
+ stmt.reset!
+ stmt.bind_params(type_casted_binds)
records = stmt.to_a
- ensure
- stmt.close
end
- stmt = records
- else
- cache = @statements[sql] ||= {
- :stmt => @connection.prepare(sql)
- }
- stmt = cache[:stmt]
- cols = cache[:cols] ||= stmt.columns
- stmt.reset!
- stmt.bind_params(type_casted_binds)
- end
- ActiveRecord::Result.new(cols, stmt.to_a)
+ ActiveRecord::Result.new(cols, records)
+ end
end
end
- def exec_delete(sql, name = 'SQL', binds = [])
+ def exec_delete(sql, name = "SQL", binds = [])
exec_query(sql, name, binds)
@connection.changes
end
@@ -229,64 +238,55 @@ module ActiveRecord
end
def execute(sql, name = nil) #:nodoc:
- log(sql, name) { @connection.execute(sql) }
+ log(sql, name) do
+ ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
+ @connection.execute(sql)
+ end
+ end
end
def begin_db_transaction #:nodoc:
- log('begin transaction',nil) { @connection.transaction }
+ log("begin transaction", nil) { @connection.transaction }
end
def commit_db_transaction #:nodoc:
- log('commit transaction',nil) { @connection.commit }
+ log("commit transaction", nil) { @connection.commit }
end
def exec_rollback_db_transaction #:nodoc:
- log('rollback transaction',nil) { @connection.rollback }
+ log("rollback transaction", nil) { @connection.rollback }
end
# SCHEMA STATEMENTS ========================================
- def tables(name = nil) # :nodoc:
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
- #tables currently returns both tables and views.
- This behavior is deprecated and will be changed with Rails 5.1 to only return tables.
- Use #data_sources instead.
- MSG
-
- if name
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
- Passing arguments to #tables is deprecated without replacement.
- MSG
- end
+ def tables # :nodoc:
+ select_values("SELECT name FROM sqlite_master WHERE type = 'table' AND name <> 'sqlite_sequence'", "SCHEMA")
+ end
- data_sources
+ def data_sources # :nodoc:
+ select_values("SELECT name FROM sqlite_master WHERE type IN ('table','view') AND name <> 'sqlite_sequence'", "SCHEMA")
end
- def data_sources
- select_values("SELECT name FROM sqlite_master WHERE type IN ('table','view') AND name <> 'sqlite_sequence'", 'SCHEMA')
+ def views # :nodoc:
+ select_values("SELECT name FROM sqlite_master WHERE type = 'view' AND name <> 'sqlite_sequence'", "SCHEMA")
end
- def table_exists?(table_name)
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
- #table_exists? currently checks both tables and views.
- This behavior is deprecated and will be changed with Rails 5.1 to only check tables.
- Use #data_source_exists? instead.
- MSG
+ def table_exists?(table_name) # :nodoc:
+ return false unless table_name.present?
- data_source_exists?(table_name)
+ sql = "SELECT name FROM sqlite_master WHERE type = 'table' AND name <> 'sqlite_sequence'"
+ sql << " AND name = #{quote(table_name)}"
+
+ select_values(sql, "SCHEMA").any?
end
- def data_source_exists?(table_name)
+ def data_source_exists?(table_name) # :nodoc:
return false unless table_name.present?
sql = "SELECT name FROM sqlite_master WHERE type IN ('table','view') AND name <> 'sqlite_sequence'"
sql << " AND name = #{quote(table_name)}"
- select_values(sql, 'SCHEMA').any?
- end
-
- def views # :nodoc:
- select_values("SELECT name FROM sqlite_master WHERE type = 'view' AND name <> 'sqlite_sequence'", 'SCHEMA')
+ select_values(sql, "SCHEMA").any?
end
def view_exists?(view_name) # :nodoc:
@@ -295,32 +295,28 @@ module ActiveRecord
sql = "SELECT name FROM sqlite_master WHERE type = 'view' AND name <> 'sqlite_sequence'"
sql << " AND name = #{quote(view_name)}"
- select_values(sql, 'SCHEMA').any?
+ select_values(sql, "SCHEMA").any?
end
- # Returns an array of +Column+ objects for the table specified by +table_name+.
- def columns(table_name) # :nodoc:
- table_name = table_name.to_s
- table_structure(table_name).map do |field|
- case field["dflt_value"]
- when /^null$/i
- field["dflt_value"] = nil
- when /^'(.*)'$/m
- field["dflt_value"] = $1.gsub("''", "'")
- when /^"(.*)"$/m
- field["dflt_value"] = $1.gsub('""', '"')
- end
-
- collation = field['collation']
- sql_type = field['type']
- type_metadata = fetch_type_metadata(sql_type)
- new_column(field['name'], field['dflt_value'], type_metadata, field['notnull'].to_i == 0, table_name, nil, collation)
+ def new_column_from_field(table_name, field) # :nondoc:
+ case field["dflt_value"]
+ when /^null$/i
+ field["dflt_value"] = nil
+ when /^'(.*)'$/m
+ field["dflt_value"] = $1.gsub("''", "'")
+ when /^"(.*)"$/m
+ field["dflt_value"] = $1.gsub('""', '"')
end
+
+ collation = field["collation"]
+ sql_type = field["type"]
+ type_metadata = fetch_type_metadata(sql_type)
+ new_column(field["name"], field["dflt_value"], type_metadata, field["notnull"].to_i == 0, table_name, nil, collation)
end
# Returns an array of indexes for the given table.
def indexes(table_name, name = nil) #:nodoc:
- exec_query("PRAGMA index_list(#{quote_table_name(table_name)})", 'SCHEMA').map do |row|
+ exec_query("PRAGMA index_list(#{quote_table_name(table_name)})", "SCHEMA").map do |row|
sql = <<-SQL
SELECT sql
FROM sqlite_master
@@ -330,22 +326,22 @@ module ActiveRecord
FROM sqlite_temp_master
WHERE name=#{quote(row['name'])} AND type='index'
SQL
- index_sql = exec_query(sql).first['sql']
+ index_sql = exec_query(sql).first["sql"]
match = /\sWHERE\s+(.+)$/i.match(index_sql)
where = match[1] if match
IndexDefinition.new(
table_name,
- row['name'],
- row['unique'] != 0,
+ row["name"],
+ row["unique"] != 0,
exec_query("PRAGMA index_info('#{row['name']}')", "SCHEMA").map { |col|
- col['name']
+ col["name"]
}, nil, nil, where)
end
end
def primary_keys(table_name) # :nodoc:
- pks = table_structure(table_name).select { |f| f['pk'] > 0 }
- pks.sort_by { |f| f['pk'] }.map { |f| f['name'] }
+ pks = table_structure(table_name).select { |f| f["pk"] > 0 }
+ pks.sort_by { |f| f["pk"] }.map { |f| f["name"] }
end
def remove_index(table_name, options = {}) #:nodoc:
@@ -410,7 +406,7 @@ module ActiveRecord
self.default = options[:default] if include_default
self.null = options[:null] if options.include?(:null)
self.precision = options[:precision] if options.include?(:precision)
- self.scale = options[:scale] if options.include?(:scale)
+ self.scale = options[:scale] if options.include?(:scale)
self.collation = options[:collation] if options.include?(:collation)
end
end
@@ -418,35 +414,36 @@ module ActiveRecord
def rename_column(table_name, column_name, new_column_name) #:nodoc:
column = column_for(table_name, column_name)
- alter_table(table_name, rename: {column.name => new_column_name.to_s})
+ alter_table(table_name, rename: { column.name => new_column_name.to_s })
rename_column_indexes(table_name, column.name, new_column_name)
end
- protected
+ private
def table_structure(table_name)
- structure = exec_query("PRAGMA table_info(#{quote_table_name(table_name)})", 'SCHEMA')
+ structure = exec_query("PRAGMA table_info(#{quote_table_name(table_name)})", "SCHEMA")
raise(ActiveRecord::StatementInvalid, "Could not find table '#{table_name}'") if structure.empty?
table_structure_with_collation(table_name, structure)
end
+ alias column_definitions table_structure
- def alter_table(table_name, options = {}) #:nodoc:
+ def alter_table(table_name, options = {})
altered_table_name = "a#{table_name}"
- caller = lambda {|definition| yield definition if block_given?}
+ caller = lambda { |definition| yield definition if block_given? }
transaction do
move_table(table_name, altered_table_name,
- options.merge(:temporary => true))
+ options.merge(temporary: true))
move_table(altered_table_name, table_name, &caller)
end
end
- def move_table(from, to, options = {}, &block) #:nodoc:
+ def move_table(from, to, options = {}, &block)
copy_table(from, to, options, &block)
drop_table(from)
end
- def copy_table(from, to, options = {}) #:nodoc:
+ def copy_table(from, to, options = {})
from_primary_key = primary_key(from)
options[:id] = false
create_table(to, options) do |definition|
@@ -460,9 +457,9 @@ module ActiveRecord
next if column_name == from_primary_key
@definition.column(column_name, column.type,
- :limit => column.limit, :default => column.default,
- :precision => column.precision, :scale => column.scale,
- :null => column.null, collation: column.collation)
+ limit: column.limit, default: column.default,
+ precision: column.precision, scale: column.scale,
+ null: column.null, collation: column.collation)
end
yield @definition if block_given?
end
@@ -472,7 +469,7 @@ module ActiveRecord
options[:rename] || {})
end
- def copy_table_indexes(from, to, rename = {}) #:nodoc:
+ def copy_table_indexes(from, to, rename = {})
indexes(from).each do |index|
name = index.name
if to == "a#{from}"
@@ -482,7 +479,7 @@ module ActiveRecord
end
to_column_names = columns(to).map(&:name)
- columns = index.columns.map {|c| rename[c] || c }.select do |column|
+ columns = index.columns.map { |c| rename[c] || c }.select do |column|
to_column_names.include?(column)
end
@@ -495,21 +492,21 @@ module ActiveRecord
end
end
- def copy_table_contents(from, to, columns, rename = {}) #:nodoc:
- column_mappings = Hash[columns.map {|name| [name, name]}]
+ def copy_table_contents(from, to, columns, rename = {})
+ column_mappings = Hash[columns.map { |name| [name, name] }]
rename.each { |a| column_mappings[a.last] = a.first }
from_columns = columns(from).collect(&:name)
- columns = columns.find_all{|col| from_columns.include?(column_mappings[col])}
+ columns = columns.find_all { |col| from_columns.include?(column_mappings[col]) }
from_columns_to_copy = columns.map { |col| column_mappings[col] }
- quoted_columns = columns.map { |col| quote_column_name(col) } * ','
- quoted_from_columns = from_columns_to_copy.map { |col| quote_column_name(col) } * ','
+ quoted_columns = columns.map { |col| quote_column_name(col) } * ","
+ quoted_from_columns = from_columns_to_copy.map { |col| quote_column_name(col) } * ","
exec_query("INSERT INTO #{quote_table_name(to)} (#{quoted_columns})
SELECT #{quoted_from_columns} FROM #{quote_table_name(from)}")
end
def sqlite_version
- @sqlite_version ||= SQLite3Adapter::Version.new(select_value('select sqlite_version(*)'))
+ @sqlite_version ||= SQLite3Adapter::Version.new(select_value("select sqlite_version(*)"))
end
def translate_exception(exception, message)
@@ -520,42 +517,45 @@ module ActiveRecord
# column *column_name* is not unique
when /column(s)? .* (is|are) not unique/, /UNIQUE constraint failed: .*/
RecordNotUnique.new(message)
+ when /.* may not be NULL/, /NOT NULL constraint failed: .*/
+ NotNullViolation.new(message)
else
super
end
end
- private
COLLATE_REGEX = /.*\"(\w+)\".*collate\s+\"(\w+)\".*/i.freeze
def table_structure_with_collation(table_name, basic_structure)
collation_hash = {}
- sql = "SELECT sql FROM
- (SELECT * FROM sqlite_master UNION ALL
- SELECT * FROM sqlite_temp_master)
- WHERE type='table' and name='#{ table_name }' \;"
+ sql = <<-SQL
+ SELECT sql FROM
+ (SELECT * FROM sqlite_master UNION ALL
+ SELECT * FROM sqlite_temp_master)
+ WHERE type = 'table' AND name = #{quote(table_name)}
+ SQL
# Result will have following sample string
# CREATE TABLE "users" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
# "password_digest" varchar COLLATE "NOCASE");
- result = exec_query(sql, 'SCHEMA').first
+ result = exec_query(sql, "SCHEMA").first
if result
# Splitting with left parentheses and picking up last will return all
# columns separated with comma(,).
- columns_string = result["sql"].split('(').last
+ columns_string = result["sql"].split("(").last
- columns_string.split(',').each do |column_string|
+ columns_string.split(",").each do |column_string|
# This regex will match the column name and collation type and will save
# the value in $1 and $2 respectively.
- collation_hash[$1] = $2 if (COLLATE_REGEX =~ column_string)
+ collation_hash[$1] = $2 if COLLATE_REGEX =~ column_string
end
basic_structure.map! do |column|
- column_name = column['name']
+ column_name = column["name"]
if collation_hash.has_key? column_name
- column['collation'] = collation_hash[column_name]
+ column["collation"] = collation_hash[column_name]
end
column
@@ -564,6 +564,10 @@ module ActiveRecord
basic_structure.to_hash
end
end
+
+ def create_table_definition(*args)
+ SQLite3::TableDefinition.new(*args)
+ end
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/statement_pool.rb b/activerecord/lib/active_record/connection_adapters/statement_pool.rb
index 9b0ed3e08b..790db56185 100644
--- a/activerecord/lib/active_record/connection_adapters/statement_pool.rb
+++ b/activerecord/lib/active_record/connection_adapters/statement_pool.rb
@@ -6,7 +6,7 @@ module ActiveRecord
DEFAULT_STATEMENT_LIMIT = 1000
def initialize(statement_limit = nil)
- @cache = Hash.new { |h,pid| h[pid] = {} }
+ @cache = Hash.new { |h, pid| h[pid] = {} }
@statement_limit = statement_limit || DEFAULT_STATEMENT_LIMIT
end
@@ -47,13 +47,13 @@ module ActiveRecord
private
- def cache
- @cache[Process.pid]
- end
+ def cache
+ @cache[Process.pid]
+ end
- def dealloc(stmt)
- raise NotImplementedError
- end
+ def dealloc(stmt)
+ raise NotImplementedError
+ end
end
end
end
diff --git a/activerecord/lib/active_record/connection_handling.rb b/activerecord/lib/active_record/connection_handling.rb
index f735bc697b..2ede92feff 100644
--- a/activerecord/lib/active_record/connection_handling.rb
+++ b/activerecord/lib/active_record/connection_handling.rb
@@ -73,7 +73,7 @@ module ActiveRecord
private
def config
@raw_config.dup.tap do |cfg|
- if url = ENV['DATABASE_URL']
+ if url = ENV["DATABASE_URL"]
cfg[@env] ||= {}
cfg[@env]["url"] ||= url
end
@@ -109,7 +109,7 @@ module ActiveRecord
end
def connection_pool
- connection_handler.retrieve_connection_pool(connection_specification_name) or raise ConnectionNotEstablished
+ connection_handler.retrieve_connection_pool(connection_specification_name) || raise(ConnectionNotEstablished)
end
def retrieve_connection
@@ -138,6 +138,6 @@ module ActiveRecord
end
delegate :clear_active_connections!, :clear_reloadable_connections!,
- :clear_all_connections!, :to => :connection_handler
+ :clear_all_connections!, to: :connection_handler
end
end
diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb
index de337b24d6..8f2a48ae2e 100644
--- a/activerecord/lib/active_record/core.rb
+++ b/activerecord/lib/active_record/core.rb
@@ -1,7 +1,7 @@
-require 'thread'
-require 'active_support/core_ext/hash/indifferent_access'
-require 'active_support/core_ext/object/duplicable'
-require 'active_support/core_ext/string/filters'
+require "thread"
+require "active_support/core_ext/hash/indifferent_access"
+require "active_support/core_ext/object/duplicable"
+require "active_support/core_ext/string/filters"
module ActiveRecord
module Core
@@ -72,11 +72,31 @@ module ActiveRecord
##
# :singleton-method:
- # Specifies if an error should be raised on query limit or order being
+ # Specifies if an error should be raised if the query has an order being
# ignored when doing batch queries. Useful in applications where the
- # limit or scope being ignored is error-worthy, rather than a warning.
- mattr_accessor :error_on_ignored_order_or_limit, instance_writer: false
- self.error_on_ignored_order_or_limit = false
+ # scope being ignored is error-worthy, rather than a warning.
+ mattr_accessor :error_on_ignored_order, instance_writer: false
+ self.error_on_ignored_order = false
+
+ def self.error_on_ignored_order_or_limit
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
+ The flag error_on_ignored_order_or_limit is deprecated. Limits are
+ now supported. Please use error_on_ignored_order instead.
+ MSG
+ error_on_ignored_order
+ end
+
+ def error_on_ignored_order_or_limit
+ self.class.error_on_ignored_order_or_limit
+ end
+
+ def self.error_on_ignored_order_or_limit=(value)
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
+ The flag error_on_ignored_order_or_limit is deprecated. Limits are
+ now supported. Please use error_on_ignored_order= instead.
+ MSG
+ self.error_on_ignored_order = value
+ end
##
# :singleton-method:
@@ -154,7 +174,7 @@ module ActiveRecord
columns_hash.include?(inheritance_column) ||
ids.first.kind_of?(Array)
- id = ids.first
+ id = ids.first
if ActiveRecord::Base === id
id = id.id
ActiveSupport::Deprecation.warn(<<-MSG.squish)
@@ -174,7 +194,7 @@ module ActiveRecord
name, primary_key, id)
end
record
- rescue RangeError
+ rescue ::RangeError
raise RecordNotFound.new("Couldn't find #{name} with an out of range value for '#{primary_key}'",
name, primary_key)
end
@@ -203,13 +223,13 @@ module ActiveRecord
statement.execute(hash.values, self, connection).first
rescue TypeError
raise ActiveRecord::StatementInvalid
- rescue RangeError
+ rescue ::RangeError
nil
end
end
def find_by!(*args) # :nodoc:
- find_by(*args) or raise RecordNotFound.new("Couldn't find #{name}", name)
+ find_by(*args) || raise(RecordNotFound.new("Couldn't find #{name}", name))
end
def initialize_generated_modules # :nodoc:
@@ -219,7 +239,9 @@ module ActiveRecord
def generated_association_methods
@generated_association_methods ||= begin
mod = const_set(:GeneratedAssociationMethods, Module.new)
+ private_constant :GeneratedAssociationMethods
include mod
+
mod
end
end
@@ -233,7 +255,7 @@ module ActiveRecord
elsif !connected?
"#{super} (call '#{super}.connection' to establish a connection)"
elsif table_exists?
- attr_list = attribute_types.map { |name, type| "#{name}: #{type.type}" } * ', '
+ attr_list = attribute_types.map { |name, type| "#{name}: #{type.type}" } * ", "
"#{super}(#{attr_list})"
else
"#{super}(Table doesn't exist)"
@@ -248,7 +270,7 @@ module ActiveRecord
# Returns an instance of <tt>Arel::Table</tt> loaded with the current table name.
#
# class Post < ActiveRecord::Base
- # scope :published_and_commented, -> { published.and(self.arel_table[:comments_count].gt(0)) }
+ # scope :published_and_commented, -> { published.and(arel_table[:comments_count].gt(0)) }
# end
def arel_table # :nodoc:
@arel_table ||= Arel::Table.new(table_name, type_caster: type_caster)
@@ -279,26 +301,26 @@ module ActiveRecord
private
- def cached_find_by_statement(key, &block) # :nodoc:
- cache = @find_by_statement_cache[connection.prepared_statements]
- cache[key] || cache.synchronize {
- cache[key] ||= StatementCache.create(connection, &block)
- }
- end
+ def cached_find_by_statement(key, &block)
+ cache = @find_by_statement_cache[connection.prepared_statements]
+ cache[key] || cache.synchronize {
+ cache[key] ||= StatementCache.create(connection, &block)
+ }
+ end
- def relation # :nodoc:
- relation = Relation.create(self, arel_table, predicate_builder)
+ def relation
+ relation = Relation.create(self, arel_table, predicate_builder)
- if finder_needs_type_condition? && !ignore_default_scope?
- relation.where(type_condition).create_with(inheritance_column.to_sym => sti_name)
- else
- relation
+ if finder_needs_type_condition? && !ignore_default_scope?
+ relation.where(type_condition).create_with(inheritance_column.to_sym => sti_name)
+ else
+ relation
+ end
end
- end
- def table_metadata # :nodoc:
- TableMetadata.new(self, arel_table)
- end
+ def table_metadata
+ TableMetadata.new(self, arel_table)
+ end
end
# New objects can be instantiated as either empty (pass no construction parameter) or pre-set with
@@ -310,8 +332,8 @@ module ActiveRecord
# # Instantiates a single new object
# User.new(first_name: 'Jamie')
def initialize(attributes = nil)
- @attributes = self.class._default_attributes.deep_dup
self.class.define_attribute_methods
+ @attributes = self.class._default_attributes.deep_dup
init_internals
initialize_internals_callback
@@ -342,10 +364,12 @@ module ActiveRecord
init_internals
- @new_record = coder['new_record']
+ @new_record = coder["new_record"]
self.class.define_attribute_methods
+ yield self if block_given?
+
_run_find_callbacks
_run_initialize_callbacks
@@ -405,8 +429,8 @@ module ActiveRecord
# coder # => {"attributes" => {"id" => nil, ... }}
def encode_with(coder)
self.class.yaml_encoder.encode(@attributes, coder)
- coder['new_record'] = new_record?
- coder['active_record_yaml_version'] = 2
+ coder["new_record"] = new_record?
+ coder["active_record_yaml_version"] = 2
end
# Returns true if +comparison_object+ is the same exact object, or +comparison_object+
@@ -430,7 +454,7 @@ module ActiveRecord
# [ Person.find(1), Person.find(2), Person.find(3) ] & [ Person.find(1), Person.find(4) ] # => [ Person.find(1) ]
def hash
if id
- [self.class, id].hash
+ self.class.hash ^ self.id.hash
else
super
end
@@ -478,14 +502,15 @@ module ActiveRecord
# We check defined?(@attributes) not to issue warnings if the object is
# allocated but not initialized.
inspection = if defined?(@attributes) && @attributes
- self.class.column_names.collect { |name|
- if has_attribute?(name)
- "#{name}: #{attribute_for_inspect(name)}"
- end
- }.compact.join(", ")
- else
- "not initialized"
- end
+ self.class.attribute_names.collect do |name|
+ if has_attribute?(name)
+ "#{name}: #{attribute_for_inspect(name)}"
+ end
+ end.compact.join(", ")
+ else
+ "not initialized"
+ end
+
"#<#{self.class} #{inspection}>"
end
@@ -496,64 +521,64 @@ module ActiveRecord
pp.object_address_group(self) do
if defined?(@attributes) && @attributes
column_names = self.class.column_names.select { |name| has_attribute?(name) || new_record? }
- pp.seplist(column_names, proc { pp.text ',' }) do |column_name|
+ pp.seplist(column_names, proc { pp.text "," }) do |column_name|
column_value = read_attribute(column_name)
- pp.breakable ' '
+ pp.breakable " "
pp.group(1) do
pp.text column_name
- pp.text ':'
+ pp.text ":"
pp.breakable
pp.pp column_value
end
end
else
- pp.breakable ' '
- pp.text 'not initialized'
+ pp.breakable " "
+ pp.text "not initialized"
end
end
end
# Returns a hash of the given methods with their names as keys and returned values as values.
def slice(*methods)
- Hash[methods.map! { |method| [method, public_send(method)] }].with_indifferent_access
+ Hash[methods.flatten.map! { |method| [method, public_send(method)] }].with_indifferent_access
end
private
- # Under Ruby 1.9, Array#flatten will call #to_ary (recursively) on each of the elements
- # of the array, and then rescues from the possible NoMethodError. If those elements are
- # ActiveRecord::Base's, then this triggers the various method_missing's that we have,
- # which significantly impacts upon performance.
- #
- # So we can avoid the method_missing hit by explicitly defining #to_ary as nil here.
- #
- # See also http://tenderlovemaking.com/2011/06/28/til-its-ok-to-return-nil-from-to_ary.html
- def to_ary # :nodoc:
- nil
- end
+ # +Array#flatten+ will call +#to_ary+ (recursively) on each of the elements of
+ # the array, and then rescues from the possible +NoMethodError+. If those elements are
+ # +ActiveRecord::Base+'s, then this triggers the various +method_missing+'s that we have,
+ # which significantly impacts upon performance.
+ #
+ # So we can avoid the +method_missing+ hit by explicitly defining +#to_ary+ as +nil+ here.
+ #
+ # See also http://tenderlovemaking.com/2011/06/28/til-its-ok-to-return-nil-from-to_ary.html
+ def to_ary
+ nil
+ end
- def init_internals
- @readonly = false
- @destroyed = false
- @marked_for_destruction = false
- @destroyed_by_association = nil
- @new_record = true
- @txn = nil
- @_start_transaction_state = {}
- @transaction_state = nil
- end
+ def init_internals
+ @readonly = false
+ @destroyed = false
+ @marked_for_destruction = false
+ @destroyed_by_association = nil
+ @new_record = true
+ @txn = nil
+ @_start_transaction_state = {}
+ @transaction_state = nil
+ end
- def initialize_internals_callback
- end
+ def initialize_internals_callback
+ end
- def thaw
- if frozen?
- @attributes = @attributes.dup
+ def thaw
+ if frozen?
+ @attributes = @attributes.dup
+ end
end
- end
- def custom_inspect_method_defined?
- self.class.instance_method(:inspect).owner != ActiveRecord::Base.instance_method(:inspect).owner
- end
+ def custom_inspect_method_defined?
+ self.class.instance_method(:inspect).owner != ActiveRecord::Base.instance_method(:inspect).owner
+ end
end
end
diff --git a/activerecord/lib/active_record/counter_cache.rb b/activerecord/lib/active_record/counter_cache.rb
index 1b6817554d..93b9371206 100644
--- a/activerecord/lib/active_record/counter_cache.rb
+++ b/activerecord/lib/active_record/counter_cache.rb
@@ -12,13 +12,21 @@ module ActiveRecord
#
# * +id+ - The id of the object you wish to reset a counter on.
# * +counters+ - One or more association counters to reset. Association name or counter name can be given.
+ # * <tt>:touch</tt> - Touch timestamp columns when updating.
+ # Pass +true+ to touch +updated_at+ and/or +updated_on+. Pass a symbol to
+ # touch that column or an array of symbols to touch just those ones.
#
# ==== Examples
#
- # # For Post with id #1 records reset the comments_count
+ # # For the Post with id #1, reset the comments_count
# Post.reset_counters(1, :comments)
- def reset_counters(id, *counters)
+ #
+ # # Like above, but also touch the +updated_at+ and/or +updated_on+
+ # # attributes.
+ # Post.reset_counters(1, :comments, touch: true)
+ def reset_counters(id, *counters, touch: nil)
object = find(id)
+
counters.each do |counter_association|
has_many_association = _reflect_on_association(counter_association)
unless has_many_association
@@ -26,7 +34,7 @@ module ActiveRecord
has_many_association = has_many.find { |association| association.counter_cache_column && association.counter_cache_column.to_sym == counter_association.to_sym }
counter_association = has_many_association.plural_name if has_many_association
end
- raise ArgumentError, "'#{self.name}' has no association called '#{counter_association}'" unless has_many_association
+ raise ArgumentError, "'#{name}' has no association called '#{counter_association}'" unless has_many_association
if has_many_association.is_a? ActiveRecord::Reflection::ThroughReflection
has_many_association = has_many_association.through_reflection
@@ -37,10 +45,12 @@ module ActiveRecord
reflection = child_class._reflections.values.find { |e| e.belongs_to? && e.foreign_key.to_s == foreign_key && e.options[:counter_cache].present? }
counter_name = reflection.counter_cache_column
- unscoped.where(primary_key => object.id).update_all(
- counter_name => object.send(counter_association).count(:all)
- )
+ updates = { counter_name.to_sym => object.send(counter_association).count(:all) }
+ updates.merge!(touch_updates(touch)) if touch
+
+ unscoped.where(primary_key => object.id).update_all(updates)
end
+
return true
end
@@ -55,6 +65,9 @@ module ActiveRecord
# * +id+ - The id of the object you wish to update a counter on or an array of ids.
# * +counters+ - A Hash containing the names of the fields
# to update as keys and the amount to update the field by as values.
+ # * <tt>:touch</tt> option - Touch timestamp columns when updating.
+ # Pass +true+ to touch +updated_at+ and/or +updated_on+. Pass a symbol to
+ # touch that column or an array of symbols to touch just those ones.
#
# ==== Examples
#
@@ -73,14 +86,29 @@ module ActiveRecord
# # UPDATE posts
# # SET comment_count = COALESCE(comment_count, 0) + 1
# # WHERE id IN (10, 15)
+ #
+ # # For the Posts with id of 10 and 15, increment the comment_count by 1
+ # # and update the updated_at value for each counter.
+ # Post.update_counters [10, 15], comment_count: 1, touch: true
+ # # Executes the following SQL:
+ # # UPDATE posts
+ # # SET comment_count = COALESCE(comment_count, 0) + 1,
+ # # `updated_at` = '2016-10-13T09:59:23-05:00'
+ # # WHERE id IN (10, 15)
def update_counters(id, counters)
+ touch = counters.delete(:touch)
+
updates = counters.map do |counter_name, value|
- operator = value < 0 ? '-' : '+'
+ operator = value < 0 ? "-" : "+"
quoted_column = connection.quote_column_name(counter_name)
"#{quoted_column} = COALESCE(#{quoted_column}, 0) #{operator} #{value.abs}"
end
- unscoped.where(primary_key => id).update_all updates.join(', ')
+ if touch
+ updates << sanitize_sql_for_assignment(touch_updates(touch))
+ end
+
+ unscoped.where(primary_key => id).update_all updates.join(", ")
end
# Increment a numeric field by one, via a direct SQL update.
@@ -94,13 +122,20 @@ module ActiveRecord
#
# * +counter_name+ - The name of the field that should be incremented.
# * +id+ - The id of the object that should be incremented or an array of ids.
+ # * <tt>:touch</tt> - Touch timestamp columns when updating.
+ # Pass +true+ to touch +updated_at+ and/or +updated_on+. Pass a symbol to
+ # touch that column or an array of symbols to touch just those ones.
#
# ==== Examples
#
# # Increment the posts_count column for the record with an id of 5
# DiscussionBoard.increment_counter(:posts_count, 5)
- def increment_counter(counter_name, id)
- update_counters(id, counter_name => 1)
+ #
+ # # Increment the posts_count column for the record with an id of 5
+ # # and update the updated_at value.
+ # DiscussionBoard.increment_counter(:posts_count, 5, touch: true)
+ def increment_counter(counter_name, id, touch: nil)
+ update_counters(id, counter_name => 1, touch: touch)
end
# Decrement a numeric field by one, via a direct SQL update.
@@ -112,14 +147,28 @@ module ActiveRecord
#
# * +counter_name+ - The name of the field that should be decremented.
# * +id+ - The id of the object that should be decremented or an array of ids.
+ # * <tt>:touch</tt> - Touch timestamp columns when updating.
+ # Pass +true+ to touch +updated_at+ and/or +updated_on+. Pass a symbol to
+ # touch that column or an array of symbols to touch just those ones.
#
# ==== Examples
#
# # Decrement the posts_count column for the record with an id of 5
# DiscussionBoard.decrement_counter(:posts_count, 5)
- def decrement_counter(counter_name, id)
- update_counters(id, counter_name => -1)
+ #
+ # # Decrement the posts_count column for the record with an id of 5
+ # # and update the updated_at value.
+ # DiscussionBoard.decrement_counter(:posts_count, 5, touch: true)
+ def decrement_counter(counter_name, id, touch: nil)
+ update_counters(id, counter_name => -1, touch: touch)
end
+
+ private
+ def touch_updates(touch)
+ touch = timestamp_attributes_for_update_in_model if touch == true
+ touch_time = current_time_from_proper_timezone
+ Array(touch).map { |column| [ column, touch_time ] }.to_h
+ end
end
private
@@ -159,6 +208,5 @@ module ActiveRecord
yield association(name.to_sym) if reflection.belongs_to? && reflection.counter_cache_column
end
end
-
end
end
diff --git a/activerecord/lib/active_record/define_callbacks.rb b/activerecord/lib/active_record/define_callbacks.rb
new file mode 100644
index 0000000000..7d955a24be
--- /dev/null
+++ b/activerecord/lib/active_record/define_callbacks.rb
@@ -0,0 +1,20 @@
+module ActiveRecord
+ # This module exists because `ActiveRecord::AttributeMethods::Dirty` needs to
+ # define callbacks, but continue to have its version of `save` be the super
+ # method of `ActiveRecord::Callbacks`. This will be removed when the removal
+ # of deprecated code removes this need.
+ module DefineCallbacks
+ extend ActiveSupport::Concern
+
+ module ClassMethods # :nodoc:
+ include ActiveModel::Callbacks
+ end
+
+ included do
+ include ActiveModel::Validations::Callbacks
+
+ define_model_callbacks :initialize, :find, :touch, only: :after
+ define_model_callbacks :save, :create, :update, :destroy
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/dynamic_matchers.rb b/activerecord/lib/active_record/dynamic_matchers.rb
index b6dd6814db..08d42f3dd4 100644
--- a/activerecord/lib/active_record/dynamic_matchers.rb
+++ b/activerecord/lib/active_record/dynamic_matchers.rb
@@ -1,6 +1,7 @@
+
module ActiveRecord
module DynamicMatchers #:nodoc:
- def respond_to?(name, include_private = false)
+ def respond_to_missing?(name, include_private = false)
if self == Base
super
else
@@ -11,111 +12,111 @@ module ActiveRecord
private
- def method_missing(name, *arguments, &block)
- match = Method.match(self, name)
+ def method_missing(name, *arguments, &block)
+ match = Method.match(self, name)
- if match && match.valid?
- match.define
- send(name, *arguments, &block)
- else
- super
+ if match && match.valid?
+ match.define
+ send(name, *arguments, &block)
+ else
+ super
+ end
end
- end
- class Method
- @matchers = []
+ class Method
+ @matchers = []
- class << self
- attr_reader :matchers
+ class << self
+ attr_reader :matchers
- def match(model, name)
- klass = matchers.find { |k| name =~ k.pattern }
- klass.new(model, name) if klass
- end
+ def match(model, name)
+ klass = matchers.find { |k| k.pattern.match?(name) }
+ klass.new(model, name) if klass
+ end
- def pattern
- @pattern ||= /\A#{prefix}_([_a-zA-Z]\w*)#{suffix}\Z/
- end
+ def pattern
+ @pattern ||= /\A#{prefix}_([_a-zA-Z]\w*)#{suffix}\Z/
+ end
- def prefix
- raise NotImplementedError
- end
+ def prefix
+ raise NotImplementedError
+ end
- def suffix
- ''
+ def suffix
+ ""
+ end
end
- end
- attr_reader :model, :name, :attribute_names
+ attr_reader :model, :name, :attribute_names
- def initialize(model, name)
- @model = model
- @name = name.to_s
- @attribute_names = @name.match(self.class.pattern)[1].split('_and_')
- @attribute_names.map! { |n| @model.attribute_aliases[n] || n }
- end
+ def initialize(model, name)
+ @model = model
+ @name = name.to_s
+ @attribute_names = @name.match(self.class.pattern)[1].split("_and_")
+ @attribute_names.map! { |n| @model.attribute_aliases[n] || n }
+ end
- def valid?
- attribute_names.all? { |name| model.columns_hash[name] || model.reflect_on_aggregation(name.to_sym) }
- end
+ def valid?
+ attribute_names.all? { |name| model.columns_hash[name] || model.reflect_on_aggregation(name.to_sym) }
+ end
- def define
- model.class_eval <<-CODE, __FILE__, __LINE__ + 1
- def self.#{name}(#{signature})
- #{body}
- end
- CODE
- end
+ def define
+ model.class_eval <<-CODE, __FILE__, __LINE__ + 1
+ def self.#{name}(#{signature})
+ #{body}
+ end
+ CODE
+ end
- private
+ private
- def body
- "#{finder}(#{attributes_hash})"
- end
+ def body
+ "#{finder}(#{attributes_hash})"
+ end
- # The parameters in the signature may have reserved Ruby words, in order
- # to prevent errors, we start each param name with `_`.
- def signature
- attribute_names.map { |name| "_#{name}" }.join(', ')
- end
+ # The parameters in the signature may have reserved Ruby words, in order
+ # to prevent errors, we start each param name with `_`.
+ def signature
+ attribute_names.map { |name| "_#{name}" }.join(", ")
+ end
- # Given that the parameters starts with `_`, the finder needs to use the
- # same parameter name.
- def attributes_hash
- "{" + attribute_names.map { |name| ":#{name} => _#{name}" }.join(',') + "}"
- end
+ # Given that the parameters starts with `_`, the finder needs to use the
+ # same parameter name.
+ def attributes_hash
+ "{" + attribute_names.map { |name| ":#{name} => _#{name}" }.join(",") + "}"
+ end
- def finder
- raise NotImplementedError
+ def finder
+ raise NotImplementedError
+ end
end
- end
- class FindBy < Method
- Method.matchers << self
+ class FindBy < Method
+ Method.matchers << self
- def self.prefix
- "find_by"
- end
+ def self.prefix
+ "find_by"
+ end
- def finder
- "find_by"
+ def finder
+ "find_by"
+ end
end
- end
- class FindByBang < Method
- Method.matchers << self
+ class FindByBang < Method
+ Method.matchers << self
- def self.prefix
- "find_by"
- end
+ def self.prefix
+ "find_by"
+ end
- def self.suffix
- "!"
- end
+ def self.suffix
+ "!"
+ end
- def finder
- "find_by!"
+ def finder
+ "find_by!"
+ end
end
- end
end
end
diff --git a/activerecord/lib/active_record/enum.rb b/activerecord/lib/active_record/enum.rb
index b884edf920..0ab03b2ab3 100644
--- a/activerecord/lib/active_record/enum.rb
+++ b/activerecord/lib/active_record/enum.rb
@@ -1,4 +1,4 @@
-require 'active_support/core_ext/object/deep_dup'
+require "active_support/core_ext/object/deep_dup"
module ActiveRecord
# Declare an enum attribute where the values map to integers in the database,
@@ -140,9 +140,11 @@ module ActiveRecord
end
end
+ # TODO Change this to private once we've dropped Ruby 2.2 support.
+ # Workaround for Ruby 2.2 "private attribute?" warning.
protected
- attr_reader :name, :mapping, :subtype
+ attr_reader :name, :mapping, :subtype
end
def enum(definitions)
@@ -161,8 +163,9 @@ module ActiveRecord
detect_enum_conflict!(name, name)
detect_enum_conflict!(name, "#{name}=")
- decorate_attribute_type(name, :enum) do |subtype|
- EnumType.new(name, enum_values, subtype)
+ attr = attribute_alias?(name) ? attribute_alias(name) : name
+ decorate_attribute_type(attr, :enum) do |subtype|
+ EnumType.new(attr, enum_values, subtype)
end
_enum_methods_module.module_eval do
@@ -184,15 +187,15 @@ module ActiveRecord
# def active?() status == 0 end
klass.send(:detect_enum_conflict!, name, "#{value_method_name}?")
- define_method("#{value_method_name}?") { self[name] == value.to_s }
+ define_method("#{value_method_name}?") { self[attr] == value.to_s }
# def active!() update! status: :active end
klass.send(:detect_enum_conflict!, name, "#{value_method_name}!")
- define_method("#{value_method_name}!") { update! name => value }
+ define_method("#{value_method_name}!") { update!(attr => value) }
# scope :active, -> { where status: 0 }
klass.send(:detect_enum_conflict!, name, value_method_name, true)
- klass.scope value_method_name, -> { where(name => value) }
+ klass.scope value_method_name, -> { where(attr => value) }
end
end
defined_enums[name.to_s] = enum_values
@@ -215,18 +218,18 @@ module ActiveRecord
def detect_enum_conflict!(enum_name, method_name, klass_method = false)
if klass_method && dangerous_class_method?(method_name)
- raise_conflict_error(enum_name, method_name, type: 'class')
+ raise_conflict_error(enum_name, method_name, type: "class")
elsif !klass_method && dangerous_attribute_method?(method_name)
raise_conflict_error(enum_name, method_name)
elsif !klass_method && method_defined_within?(method_name, _enum_methods_module, Module)
- raise_conflict_error(enum_name, method_name, source: 'another enum')
+ raise_conflict_error(enum_name, method_name, source: "another enum")
end
end
- def raise_conflict_error(enum_name, method_name, type: 'instance', source: 'Active Record')
+ def raise_conflict_error(enum_name, method_name, type: "instance", source: "Active Record")
raise ArgumentError, ENUM_CONFLICT_MESSAGE % {
enum: enum_name,
- klass: self.name,
+ klass: name,
type: type,
method: method_name,
source: source
diff --git a/activerecord/lib/active_record/errors.rb b/activerecord/lib/active_record/errors.rb
index 38e4fbec8b..18fac5af1b 100644
--- a/activerecord/lib/active_record/errors.rb
+++ b/activerecord/lib/active_record/errors.rb
@@ -1,5 +1,4 @@
module ActiveRecord
-
# = Active Record Errors
#
# Generic Active Record exception class.
@@ -44,7 +43,7 @@ module ActiveRecord
# Raised when connection to the database could not been established (for example when
# {ActiveRecord::Base.connection=}[rdoc-ref:ConnectionHandling#connection]
- # is given a nil object).
+ # is given a +nil+ object).
class ConnectionNotEstablished < ActiveRecordError
end
@@ -96,20 +95,9 @@ module ActiveRecord
#
# Wraps the underlying database error as +cause+.
class StatementInvalid < ActiveRecordError
-
- def initialize(message = nil, original_exception = nil)
- if original_exception
- ActiveSupport::Deprecation.warn("Passing #original_exception is deprecated and has no effect. " \
- "Exceptions will automatically capture the original exception.", caller)
- end
-
+ def initialize(message = nil)
super(message || $!.try(:message))
end
-
- def original_exception
- ActiveSupport::Deprecation.warn("#original_exception is deprecated. Use #cause instead.", caller)
- cause
- end
end
# Defunct wrapper class kept for compatibility.
@@ -125,10 +113,46 @@ module ActiveRecord
class InvalidForeignKey < WrappedDatabaseException
end
+ # Raised when a foreign key constraint cannot be added because the column type does not match the referenced column type.
+ class MismatchedForeignKey < StatementInvalid
+ def initialize(adapter = nil, message: nil, table: nil, foreign_key: nil, target_table: nil, primary_key: nil)
+ @adapter = adapter
+ if table
+ msg = <<-EOM.strip_heredoc
+ Column `#{foreign_key}` on table `#{table}` has a type of `#{column_type(table, foreign_key)}`.
+ This does not match column `#{primary_key}` on `#{target_table}`, which has type `#{column_type(target_table, primary_key)}`.
+ To resolve this issue, change the type of the `#{foreign_key}` column on `#{table}` to be :integer. (For example `t.integer #{foreign_key}`).
+ EOM
+ else
+ msg = <<-EOM
+ There is a mismatch between the foreign key and primary key column types.
+ Verify that the foreign key column type and the primary key of the associated table match types.
+ EOM
+ end
+ if message
+ msg << "\nOriginal message: #{message}"
+ end
+ super(msg)
+ end
+
+ private
+ def column_type(table, column)
+ @adapter.columns(table).detect { |c| c.name == column }.sql_type
+ end
+ end
+
+ # Raised when a record cannot be inserted or updated because it would violate a not null constraint.
+ class NotNullViolation < StatementInvalid
+ end
+
# Raised when a record cannot be inserted or updated because a value too long for a column type.
class ValueTooLong < StatementInvalid
end
+ # Raised when values that executed are out of range.
+ class RangeError < StatementInvalid
+ end
+
# Raised when number of bind variables in statement given to +:condition+ key
# (for example, when using {ActiveRecord::Base.find}[rdoc-ref:FinderMethods#find] method)
# does not match number of expected values supplied.
@@ -166,7 +190,6 @@ module ActiveRecord
super("Stale object error.")
end
end
-
end
# Raised when association is being configured improperly or user tries to use
@@ -285,14 +308,24 @@ module ActiveRecord
class TransactionIsolationError < ActiveRecordError
end
- # TransactionSerializationError will be raised when a transaction is rolled
+ # TransactionRollbackError will be raised when a transaction is rolled
# back by the database due to a serialization failure or a deadlock.
#
# See the following:
#
# * http://www.postgresql.org/docs/current/static/transaction-iso.html
# * https://dev.mysql.com/doc/refman/5.7/en/error-messages-server.html#error_er_lock_deadlock
- class TransactionSerializationError < ActiveRecordError
+ class TransactionRollbackError < StatementInvalid
+ end
+
+ # SerializationFailure will be raised when a transaction is rolled
+ # back by the database due to a serialization failure.
+ class SerializationFailure < TransactionRollbackError
+ end
+
+ # Deadlocked will be raised when a transaction is rolled
+ # back by the database when a deadlock is encountered.
+ class Deadlocked < TransactionRollbackError
end
# IrreversibleOrderError is raised when a relation's order is too complex for
diff --git a/activerecord/lib/active_record/explain.rb b/activerecord/lib/active_record/explain.rb
index 727a9befc1..8f7ae2c33c 100644
--- a/activerecord/lib/active_record/explain.rb
+++ b/activerecord/lib/active_record/explain.rb
@@ -1,5 +1,4 @@
-require 'active_support/lazy_load_hooks'
-require 'active_record/explain_registry'
+require "active_record/explain_registry"
module ActiveRecord
module Explain
@@ -16,15 +15,14 @@ module ActiveRecord
# Makes the adapter execute EXPLAIN for the tuples of queries and bindings.
# Returns a formatted string ready to be logged.
def exec_explain(queries) # :nodoc:
- str = queries.map do |sql, bind|
- [].tap do |msg|
- msg << "EXPLAIN for: #{sql}"
- unless bind.empty?
- bind_msg = bind.map {|col, val| [col.name, val]}.inspect
- msg.last << " #{bind_msg}"
- end
- msg << connection.explain(sql, bind)
- end.join("\n")
+ str = queries.map do |sql, binds|
+ msg = "EXPLAIN for: #{sql}"
+ unless binds.empty?
+ msg << " "
+ msg << binds.map { |attr| render_bind(attr) }.inspect
+ end
+ msg << "\n"
+ msg << connection.explain(sql, binds)
end.join("\n")
# Overriding inspect to be more human readable, especially in the console.
@@ -34,5 +32,17 @@ module ActiveRecord
str
end
+
+ private
+
+ def render_bind(attr)
+ value = if attr.type.binary? && attr.value
+ "<#{attr.value_for_database.to_s.bytesize} bytes of binary data>"
+ else
+ connection.type_cast(attr.value_for_database)
+ end
+
+ [attr.name, value]
+ end
end
end
diff --git a/activerecord/lib/active_record/explain_registry.rb b/activerecord/lib/active_record/explain_registry.rb
index b652932f9c..ef1ce3dc85 100644
--- a/activerecord/lib/active_record/explain_registry.rb
+++ b/activerecord/lib/active_record/explain_registry.rb
@@ -1,4 +1,4 @@
-require 'active_support/per_thread_registry'
+require "active_support/per_thread_registry"
module ActiveRecord
# This is a thread locals registry for EXPLAIN. For example
diff --git a/activerecord/lib/active_record/explain_subscriber.rb b/activerecord/lib/active_record/explain_subscriber.rb
index 90bcf5a205..abd8cfc8f2 100644
--- a/activerecord/lib/active_record/explain_subscriber.rb
+++ b/activerecord/lib/active_record/explain_subscriber.rb
@@ -1,5 +1,5 @@
-require 'active_support/notifications'
-require 'active_record/explain_registry'
+require "active_support/notifications"
+require "active_record/explain_registry"
module ActiveRecord
class ExplainSubscriber # :nodoc:
@@ -18,10 +18,13 @@ module ActiveRecord
#
# On the other hand, we want to monitor the performance of our real database
# queries, not the performance of the access to the query cache.
- IGNORED_PAYLOADS = %w(SCHEMA EXPLAIN CACHE)
+ IGNORED_PAYLOADS = %w(SCHEMA EXPLAIN)
EXPLAINED_SQLS = /\A\s*(with|select|update|delete|insert)\b/i
def ignore_payload?(payload)
- payload[:exception] || IGNORED_PAYLOADS.include?(payload[:name]) || payload[:sql] !~ EXPLAINED_SQLS
+ payload[:exception] ||
+ payload[:cached] ||
+ IGNORED_PAYLOADS.include?(payload[:name]) ||
+ payload[:sql] !~ EXPLAINED_SQLS
end
ActiveSupport::Notifications.subscribe("sql.active_record", new)
diff --git a/activerecord/lib/active_record/fixture_set/file.rb b/activerecord/lib/active_record/fixture_set/file.rb
index e4a44244e2..6cf2e01179 100644
--- a/activerecord/lib/active_record/fixture_set/file.rb
+++ b/activerecord/lib/active_record/fixture_set/file.rb
@@ -1,5 +1,5 @@
-require 'erb'
-require 'yaml'
+require "erb"
+require "yaml"
module ActiveRecord
class FixtureSet
@@ -24,21 +24,21 @@ module ActiveRecord
end
def model_class
- config_row['model_class']
+ config_row["model_class"]
end
private
def rows
- @rows ||= raw_rows.reject { |fixture_name, _| fixture_name == '_fixture' }
+ @rows ||= raw_rows.reject { |fixture_name, _| fixture_name == "_fixture" }
end
def config_row
@config_row ||= begin
- row = raw_rows.find { |fixture_name, _| fixture_name == '_fixture' }
+ row = raw_rows.find { |fixture_name, _| fixture_name == "_fixture" }
if row
row.last
else
- {'model_class': nil}
+ { 'model_class': nil }
end
end
end
@@ -66,10 +66,13 @@ module ActiveRecord
# Validate our unmarshalled data.
def validate(data)
unless Hash === data || YAML::Omap === data
- raise Fixture::FormatError, 'fixture is not a hash'
+ raise Fixture::FormatError, "fixture is not a hash: #{@file}"
end
- raise Fixture::FormatError unless data.all? { |name, row| Hash === row }
+ invalid = data.reject { |_, row| Hash === row }
+ if invalid.any?
+ raise Fixture::FormatError, "fixture key is not a hash: #{@file}, keys: #{invalid.keys.inspect}"
+ end
data
end
end
diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb
index 51bf12d0bf..de1b0d63bc 100644
--- a/activerecord/lib/active_record/fixtures.rb
+++ b/activerecord/lib/active_record/fixtures.rb
@@ -1,11 +1,11 @@
-require 'erb'
-require 'yaml'
-require 'zlib'
-require 'set'
-require 'active_support/dependencies'
-require 'active_support/core_ext/digest/uuid'
-require 'active_record/fixture_set/file'
-require 'active_record/errors'
+require "erb"
+require "yaml"
+require "zlib"
+require "set"
+require "active_support/dependencies"
+require "active_support/core_ext/digest/uuid"
+require "active_record/fixture_set/file"
+require "active_record/errors"
module ActiveRecord
class FixtureClassNotFound < ActiveRecord::ActiveRecordError #:nodoc:
@@ -88,7 +88,7 @@ module ActiveRecord
# assert_equal "Ruby on Rails", @rubyonrails.name
# end
#
- # In order to use these methods to access fixtured data within your testcases, you must specify one of the
+ # In order to use these methods to access fixtured data within your test cases, you must specify one of the
# following in your ActiveSupport::TestCase-derived class:
#
# - to fully enable instantiated fixtures (enable alternate methods #1 and #2 above)
@@ -103,7 +103,7 @@ module ActiveRecord
#
# = Dynamic fixtures with ERB
#
- # Some times you don't care about the content of the fixtures as much as you care about the volume.
+ # Sometimes you don't care about the content of the fixtures as much as you care about the volume.
# In these cases, you can mix ERB in with your YAML fixtures to create a bunch of fixtures for load
# testing, like:
#
@@ -415,9 +415,9 @@ module ActiveRecord
# possibly in a folder with the same name.
#++
- MAX_ID = 2 ** 30 - 1
+ MAX_ID = 2**30 - 1
- @@all_cached_fixtures = Hash.new { |h,k| h[k] = {} }
+ @@all_cached_fixtures = Hash.new { |h, k| h[k] = {} }
def self.default_fixture_model_name(fixture_set_name, config = ActiveRecord::Base) # :nodoc:
config.pluralize_table_names ?
@@ -426,9 +426,9 @@ module ActiveRecord
end
def self.default_fixture_table_name(fixture_set_name, config = ActiveRecord::Base) # :nodoc:
- "#{ config.table_name_prefix }"\
- "#{ fixture_set_name.tr('/', '_') }"\
- "#{ config.table_name_suffix }".to_sym
+ "#{ config.table_name_prefix }"\
+ "#{ fixture_set_name.tr('/', '_') }"\
+ "#{ config.table_name_suffix }".to_sym
end
def self.reset_cache
@@ -494,18 +494,18 @@ module ActiveRecord
private
- def insert_class(class_names, name, klass)
- # We only want to deal with AR objects.
- if klass && klass < ActiveRecord::Base
- class_names[name] = klass
- else
- class_names[name] = nil
+ def insert_class(class_names, name, klass)
+ # We only want to deal with AR objects.
+ if klass && klass < ActiveRecord::Base
+ class_names[name] = klass
+ else
+ class_names[name] = nil
+ end
end
- end
- def default_fixture_model(fs_name, config)
- ActiveRecord::FixtureSet.default_fixture_model_name(fs_name, config)
- end
+ def default_fixture_model(fs_name, config)
+ ActiveRecord::FixtureSet.default_fixture_model_name(fs_name, config)
+ end
end
def self.create_fixtures(fixtures_directory, fixture_set_names, class_names = {}, config = ActiveRecord::Base)
@@ -535,17 +535,17 @@ module ActiveRecord
update_all_loaded_fixtures fixtures_map
- connection.transaction(:requires_new => true) do
- deleted_tables = Set.new
+ connection.transaction(requires_new: true) do
+ deleted_tables = Hash.new { |h, k| h[k] = Set.new }
fixture_sets.each do |fs|
conn = fs.model_class.respond_to?(:connection) ? fs.model_class.connection : connection
table_rows = fs.table_rows
table_rows.each_key do |table|
- unless deleted_tables.include? table
- conn.delete "DELETE FROM #{conn.quote_table_name(table)}", 'Fixture Delete'
+ unless deleted_tables[conn].include? table
+ conn.delete "DELETE FROM #{conn.quote_table_name(table)}", "Fixture Delete"
end
- deleted_tables << table
+ deleted_tables[conn] << table
end
table_rows.each do |fixture_set_name, rows|
@@ -597,18 +597,18 @@ module ActiveRecord
@fixtures = read_fixture_files(path)
- @connection = connection
+ @connection = connection
- @table_name = ( model_class.respond_to?(:table_name) ?
+ @table_name = (model_class.respond_to?(:table_name) ?
model_class.table_name :
- self.class.default_fixture_table_name(name, config) )
+ self.class.default_fixture_table_name(name, config))
end
def [](x)
fixtures[x]
end
- def []=(k,v)
+ def []=(k, v)
fixtures[k] = v
end
@@ -626,10 +626,10 @@ module ActiveRecord
now = config.default_timezone == :utc ? Time.now.utc : Time.now
# allow a standard key to be used for doing defaults in YAML
- fixtures.delete('DEFAULTS')
+ fixtures.delete("DEFAULTS")
# track any join tables we need to insert later
- rows = Hash.new { |h,table| h[table] = [] }
+ rows = Hash.new { |h, table| h[table] = [] }
rows[table_name] = fixtures.map do |label, fixture|
row = fixture.to_hash
@@ -799,7 +799,6 @@ module ActiveRecord
def yaml_file_path(path)
"#{path}.yml"
end
-
end
class Fixture #:nodoc:
@@ -859,33 +858,21 @@ module ActiveRecord
end
included do
- class_attribute :fixture_path, :instance_writer => false
+ class_attribute :fixture_path, instance_writer: false
class_attribute :fixture_table_names
class_attribute :fixture_class_names
class_attribute :use_transactional_tests
- class_attribute :use_transactional_fixtures
class_attribute :use_instantiated_fixtures # true, false, or :no_instances
class_attribute :pre_loaded_fixtures
class_attribute :config
- singleton_class.deprecate 'use_transactional_fixtures=' => 'use use_transactional_tests= instead'
-
self.fixture_table_names = []
self.use_instantiated_fixtures = false
self.pre_loaded_fixtures = false
self.config = ActiveRecord::Base
self.fixture_class_names = {}
-
- silence_warnings do
- define_singleton_method :use_transactional_tests do
- if use_transactional_fixtures.nil?
- true
- else
- use_transactional_fixtures
- end
- end
- end
+ self.use_transactional_tests = true
end
module ClassMethods
@@ -903,7 +890,7 @@ module ActiveRecord
def fixtures(*fixture_set_names)
if fixture_set_names.first == :all
- fixture_set_names = Dir["#{fixture_path}/{**,*}/*.{yml}"]
+ fixture_set_names = Dir["#{fixture_path}/{**,*}/*.{yml}"].uniq
fixture_set_names.map! { |f| f[(fixture_path.to_s.size + 1)..-5] }
else
fixture_set_names = fixture_set_names.flatten.map(&:to_s)
@@ -918,7 +905,7 @@ module ActiveRecord
methods = Module.new do
fixture_set_names.each do |fs_name|
fs_name = fs_name.to_s
- accessor_name = fs_name.tr('/', '_').to_sym
+ accessor_name = fs_name.tr("/", "_").to_sym
define_method(accessor_name) do |*fixture_names|
force_reload = fixture_names.pop if fixture_names.last == true || fixture_names.last == :reload
@@ -962,12 +949,13 @@ module ActiveRecord
def setup_fixtures(config = ActiveRecord::Base)
if pre_loaded_fixtures && !use_transactional_tests
- raise RuntimeError, 'pre_loaded_fixtures requires use_transactional_tests'
+ raise RuntimeError, "pre_loaded_fixtures requires use_transactional_tests"
end
@fixture_cache = {}
@fixture_connections = []
@@already_loaded_fixtures ||= {}
+ @connection_subscriber = nil
# Load fixtures once and begin transaction.
if run_in_transaction?
@@ -977,10 +965,31 @@ module ActiveRecord
@loaded_fixtures = load_fixtures(config)
@@already_loaded_fixtures[self.class] = @loaded_fixtures
end
+
+ # Begin transactions for connections already established
@fixture_connections = enlist_fixture_connections
@fixture_connections.each do |connection|
connection.begin_transaction joinable: false
end
+
+ # When connections are established in the future, begin a transaction too
+ @connection_subscriber = ActiveSupport::Notifications.subscribe("!connection.active_record") do |_, _, _, _, payload|
+ spec_name = payload[:spec_name] if payload.key?(:spec_name)
+
+ if spec_name
+ begin
+ connection = ActiveRecord::Base.connection_handler.retrieve_connection(spec_name)
+ rescue ConnectionNotEstablished
+ connection = nil
+ end
+
+ if connection && !@fixture_connections.include?(connection)
+ connection.begin_transaction joinable: false
+ @fixture_connections << connection
+ end
+ end
+ end
+
# Load fixtures for every test.
else
ActiveRecord::FixtureSet.reset_cache
@@ -995,6 +1004,7 @@ module ActiveRecord
def teardown_fixtures
# Rollback changes if a transaction is active.
if run_in_transaction?
+ ActiveSupport::Notifications.unsubscribe(@connection_subscriber) if @connection_subscriber
@fixture_connections.each do |connection|
connection.rollback_transaction if connection.transaction_open?
end
@@ -1018,10 +1028,10 @@ module ActiveRecord
def instantiate_fixtures
if pre_loaded_fixtures
- raise RuntimeError, 'Load fixtures before instantiating them.' if ActiveRecord::FixtureSet.all_loaded_fixtures.empty?
+ raise RuntimeError, "Load fixtures before instantiating them." if ActiveRecord::FixtureSet.all_loaded_fixtures.empty?
ActiveRecord::FixtureSet.instantiate_all_loaded_fixtures(self, load_instances?)
else
- raise RuntimeError, 'Load fixtures before instantiating them.' if @loaded_fixtures.nil?
+ raise RuntimeError, "Load fixtures before instantiating them." if @loaded_fixtures.nil?
@loaded_fixtures.each_value do |fixture_set|
ActiveRecord::FixtureSet.instantiate_fixtures(self, fixture_set, load_instances?)
end
diff --git a/activerecord/lib/active_record/inheritance.rb b/activerecord/lib/active_record/inheritance.rb
index b5fec57c8c..fbdaeaae51 100644
--- a/activerecord/lib/active_record/inheritance.rb
+++ b/activerecord/lib/active_record/inheritance.rb
@@ -1,4 +1,4 @@
-require 'active_support/core_ext/hash/indifferent_access'
+require "active_support/core_ext/hash/indifferent_access"
module ActiveRecord
# == Single table inheritance
@@ -130,87 +130,100 @@ module ActiveRecord
store_full_sti_class ? name : name.demodulize
end
+ def inherited(subclass)
+ subclass.instance_variable_set(:@_type_candidates_cache, Concurrent::Map.new)
+ super
+ end
+
protected
- # Returns the class type of the record using the current module as a prefix. So descendants of
- # MyApp::Business::Account would appear as MyApp::Business::AccountSubclass.
- def compute_type(type_name)
- if type_name.match(/^::/)
- # If the type is prefixed with a scope operator then we assume that
- # the type_name is an absolute reference.
- ActiveSupport::Dependencies.constantize(type_name)
- else
- # Build a list of candidates to search for
- candidates = []
- name.scan(/::|$/) { candidates.unshift "#{$`}::#{type_name}" }
- candidates << type_name
-
- candidates.each do |candidate|
- constant = ActiveSupport::Dependencies.safe_constantize(candidate)
- return constant if candidate == constant.to_s
+ # Returns the class type of the record using the current module as a prefix. So descendants of
+ # MyApp::Business::Account would appear as MyApp::Business::AccountSubclass.
+ def compute_type(type_name)
+ if type_name.start_with?("::".freeze)
+ # If the type is prefixed with a scope operator then we assume that
+ # the type_name is an absolute reference.
+ ActiveSupport::Dependencies.constantize(type_name)
+ else
+ type_candidate = @_type_candidates_cache[type_name]
+ if type_candidate && type_constant = ActiveSupport::Dependencies.safe_constantize(type_candidate)
+ return type_constant
+ end
+
+ # Build a list of candidates to search for
+ candidates = []
+ name.scan(/::|$/) { candidates.unshift "#{$`}::#{type_name}" }
+ candidates << type_name
+
+ candidates.each do |candidate|
+ constant = ActiveSupport::Dependencies.safe_constantize(candidate)
+ if candidate == constant.to_s
+ @_type_candidates_cache[type_name] = candidate
+ return constant
+ end
+ end
+
+ raise NameError.new("uninitialized constant #{candidates.first}", candidates.first)
end
-
- raise NameError.new("uninitialized constant #{candidates.first}", candidates.first)
end
- end
private
- # Called by +instantiate+ to decide which class to use for a new
- # record instance. For single-table inheritance, we check the record
- # for a +type+ column and return the corresponding class.
- def discriminate_class_for_record(record)
- if using_single_table_inheritance?(record)
- find_sti_class(record[inheritance_column])
- else
- super
+ # Called by +instantiate+ to decide which class to use for a new
+ # record instance. For single-table inheritance, we check the record
+ # for a +type+ column and return the corresponding class.
+ def discriminate_class_for_record(record)
+ if using_single_table_inheritance?(record)
+ find_sti_class(record[inheritance_column])
+ else
+ super
+ end
end
- end
- def using_single_table_inheritance?(record)
- record[inheritance_column].present? && has_attribute?(inheritance_column)
- end
+ def using_single_table_inheritance?(record)
+ record[inheritance_column].present? && has_attribute?(inheritance_column)
+ end
- def find_sti_class(type_name)
- type_name = base_class.type_for_attribute(inheritance_column).cast(type_name)
- subclass = begin
- if store_full_sti_class
- ActiveSupport::Dependencies.constantize(type_name)
- else
- compute_type(type_name)
+ def find_sti_class(type_name)
+ type_name = base_class.type_for_attribute(inheritance_column).cast(type_name)
+ subclass = begin
+ if store_full_sti_class
+ ActiveSupport::Dependencies.constantize(type_name)
+ else
+ compute_type(type_name)
+ end
+ rescue NameError
+ raise SubclassNotFound,
+ "The single-table inheritance mechanism failed to locate the subclass: '#{type_name}'. " \
+ "This error is raised because the column '#{inheritance_column}' is reserved for storing the class in case of inheritance. " \
+ "Please rename this column if you didn't intend it to be used for storing the inheritance class " \
+ "or overwrite #{name}.inheritance_column to use another column for that information."
end
- rescue NameError
- raise SubclassNotFound,
- "The single-table inheritance mechanism failed to locate the subclass: '#{type_name}'. " \
- "This error is raised because the column '#{inheritance_column}' is reserved for storing the class in case of inheritance. " \
- "Please rename this column if you didn't intend it to be used for storing the inheritance class " \
- "or overwrite #{name}.inheritance_column to use another column for that information."
- end
- unless subclass == self || descendants.include?(subclass)
- raise SubclassNotFound, "Invalid single-table inheritance type: #{subclass.name} is not a subclass of #{name}"
+ unless subclass == self || descendants.include?(subclass)
+ raise SubclassNotFound, "Invalid single-table inheritance type: #{subclass.name} is not a subclass of #{name}"
+ end
+ subclass
end
- subclass
- end
- def type_condition(table = arel_table)
- sti_column = arel_attribute(inheritance_column, table)
- sti_names = ([self] + descendants).map(&:sti_name)
+ def type_condition(table = arel_table)
+ sti_column = arel_attribute(inheritance_column, table)
+ sti_names = ([self] + descendants).map(&:sti_name)
- sti_column.in(sti_names)
- end
+ sti_column.in(sti_names)
+ end
- # Detect the subclass from the inheritance column of attrs. If the inheritance column value
- # is not self or a valid subclass, raises ActiveRecord::SubclassNotFound
- def subclass_from_attributes(attrs)
- attrs = attrs.to_h if attrs.respond_to?(:permitted?)
- if attrs.is_a?(Hash)
- subclass_name = attrs.with_indifferent_access[inheritance_column]
+ # Detect the subclass from the inheritance column of attrs. If the inheritance column value
+ # is not self or a valid subclass, raises ActiveRecord::SubclassNotFound
+ def subclass_from_attributes(attrs)
+ attrs = attrs.to_h if attrs.respond_to?(:permitted?)
+ if attrs.is_a?(Hash)
+ subclass_name = attrs.with_indifferent_access[inheritance_column]
- if subclass_name.present?
- find_sti_class(subclass_name)
+ if subclass_name.present?
+ find_sti_class(subclass_name)
+ end
end
end
- end
end
def initialize_dup(other)
@@ -220,21 +233,21 @@ module ActiveRecord
private
- def initialize_internals_callback
- super
- ensure_proper_type
- end
+ def initialize_internals_callback
+ super
+ ensure_proper_type
+ end
- # Sets the attribute used for single table inheritance to this class name if this is not the
- # ActiveRecord::Base descendant.
- # Considering the hierarchy Reply < Message < ActiveRecord::Base, this makes it possible to
- # do Reply.new without having to set <tt>Reply[Reply.inheritance_column] = "Reply"</tt> yourself.
- # No such attribute would be set for objects of the Message class in that example.
- def ensure_proper_type
- klass = self.class
- if klass.finder_needs_type_condition?
- write_attribute(klass.inheritance_column, klass.sti_name)
+ # Sets the attribute used for single table inheritance to this class name if this is not the
+ # ActiveRecord::Base descendant.
+ # Considering the hierarchy Reply < Message < ActiveRecord::Base, this makes it possible to
+ # do Reply.new without having to set <tt>Reply[Reply.inheritance_column] = "Reply"</tt> yourself.
+ # No such attribute would be set for objects of the Message class in that example.
+ def ensure_proper_type
+ klass = self.class
+ if klass.finder_needs_type_condition?
+ write_attribute(klass.inheritance_column, klass.sti_name)
+ end
end
- end
end
end
diff --git a/activerecord/lib/active_record/integration.rb b/activerecord/lib/active_record/integration.rb
index 466c8509a4..8e71b60b29 100644
--- a/activerecord/lib/active_record/integration.rb
+++ b/activerecord/lib/active_record/integration.rb
@@ -1,4 +1,4 @@
-require 'active_support/core_ext/string/filters'
+require "active_support/core_ext/string/filters"
module ActiveRecord
module Integration
@@ -11,13 +11,13 @@ module ActiveRecord
# Accepts any of the symbols in <tt>Time::DATE_FORMATS</tt>.
#
# This is +:usec+, by default.
- class_attribute :cache_timestamp_format, :instance_writer => false
+ class_attribute :cache_timestamp_format, instance_writer: false
self.cache_timestamp_format = :usec
end
- # Returns a String, which Action Pack uses for constructing a URL to this
- # object. The default implementation returns this record's id as a String,
- # or nil if this record's unsaved.
+ # Returns a +String+, which Action Pack uses for constructing a URL to this
+ # object. The default implementation returns this record's id as a +String+,
+ # or +nil+ if this record's unsaved.
#
# For example, suppose that you have a User model, and that you have a
# <tt>resources :users</tt> route. Normally, +user_path+ will
@@ -53,18 +53,21 @@ module ActiveRecord
#
# Person.find(5).cache_key(:updated_at, :last_reviewed_at)
def cache_key(*timestamp_names)
- case
- when new_record?
+ if new_record?
"#{model_name.cache_key}/new"
- when timestamp_names.any?
- timestamp = max_updated_column_timestamp(timestamp_names)
- timestamp = timestamp.utc.to_s(cache_timestamp_format)
- "#{model_name.cache_key}/#{id}-#{timestamp}"
- when timestamp = max_updated_column_timestamp
- timestamp = timestamp.utc.to_s(cache_timestamp_format)
- "#{model_name.cache_key}/#{id}-#{timestamp}"
else
- "#{model_name.cache_key}/#{id}"
+ timestamp = if timestamp_names.any?
+ max_updated_column_timestamp(timestamp_names)
+ else
+ max_updated_column_timestamp
+ end
+
+ if timestamp
+ timestamp = timestamp.utc.to_s(cache_timestamp_format)
+ "#{model_name.cache_key}/#{id}-#{timestamp}"
+ else
+ "#{model_name.cache_key}/#{id}"
+ end
end
end
@@ -86,7 +89,7 @@ module ActiveRecord
#
# user = User.find_by(name: 'David Heinemeier Hansson')
# user.id # => 125
- # user_path(user) # => "/users/125-david"
+ # user_path(user) # => "/users/125-david-heinemeier"
#
# Because the generated param begins with the record's +id+, it is
# suitable for passing to +find+. In a controller, for example:
@@ -100,7 +103,7 @@ module ActiveRecord
define_method :to_param do
if (default = super()) &&
(result = send(method_name).to_s).present? &&
- (param = result.squish.truncate(20, separator: /\s/, omission: nil).parameterize).present?
+ (param = result.squish.parameterize.truncate(20, separator: /-/, omission: "")).present?
"#{default}-#{param}"
else
default
diff --git a/activerecord/lib/active_record/internal_metadata.rb b/activerecord/lib/active_record/internal_metadata.rb
index 8b649eab0f..25ee9d6bfe 100644
--- a/activerecord/lib/active_record/internal_metadata.rb
+++ b/activerecord/lib/active_record/internal_metadata.rb
@@ -1,5 +1,5 @@
-require 'active_record/scoping/default'
-require 'active_record/scoping/named'
+require "active_record/scoping/default"
+require "active_record/scoping/named"
module ActiveRecord
# This class is used to create a table that keeps track of values and keys such
@@ -23,7 +23,7 @@ module ActiveRecord
end
def table_exists?
- ActiveSupport::Deprecation.silence { connection.table_exists?(table_name) }
+ connection.table_exists?(table_name)
end
# Creates an internal metadata table with columns +key+ and +value+
diff --git a/activerecord/lib/active_record/locking/optimistic.rb b/activerecord/lib/active_record/locking/optimistic.rb
index 67b8efac66..2659c60f1f 100644
--- a/activerecord/lib/active_record/locking/optimistic.rb
+++ b/activerecord/lib/active_record/locking/optimistic.rb
@@ -47,6 +47,8 @@ module ActiveRecord
# self.locking_column = :lock_person
# end
#
+ # Please note that the optimistic locking will be ignored if you update the
+ # locking column's value.
module Optimistic
extend ActiveSupport::Concern
@@ -60,13 +62,14 @@ module ActiveRecord
end
private
+
def increment_lock
lock_col = self.class.locking_column
previous_lock_value = send(lock_col).to_i
- send(lock_col + '=', previous_lock_value + 1)
+ send(lock_col + "=", previous_lock_value + 1)
end
- def _create_record(attribute_names = self.attribute_names, *) # :nodoc:
+ def _create_record(attribute_names = self.attribute_names, *)
if locking_enabled?
# We always want to persist the locking version, even if we don't detect
# a change from the default, since the database might have no default
@@ -75,23 +78,26 @@ module ActiveRecord
super
end
- def _update_record(attribute_names = self.attribute_names) #:nodoc:
+ def _update_record(attribute_names = self.attribute_names)
return super unless locking_enabled?
- return 0 if attribute_names.empty?
lock_col = self.class.locking_column
- previous_lock_value = send(lock_col).to_i
- increment_lock
- attribute_names += [lock_col]
- attribute_names.uniq!
+ return super if attribute_names.include?(lock_col)
+ return 0 if attribute_names.empty?
begin
+ previous_lock_value = read_attribute_before_type_cast(lock_col)
+
+ increment_lock
+
+ attribute_names.push(lock_col)
+
relation = self.class.unscoped
affected_rows = relation.where(
self.class.primary_key => id,
- lock_col => previous_lock_value,
+ lock_col => previous_lock_value
).update_all(
attributes_for_update(attribute_names).map do |name|
[name, _read_attribute(name)]
@@ -104,9 +110,9 @@ module ActiveRecord
affected_rows
- # If something went wrong, revert the version.
+ # If something went wrong, revert the locking_column value.
rescue Exception
- send(lock_col + '=', previous_lock_value)
+ send(lock_col + "=", previous_lock_value.to_i)
raise
end
end
@@ -132,59 +138,58 @@ module ActiveRecord
relation
end
- module ClassMethods
- DEFAULT_LOCKING_COLUMN = 'lock_version'
+ module ClassMethods
+ DEFAULT_LOCKING_COLUMN = "lock_version"
- # Returns true if the +lock_optimistically+ flag is set to true
- # (which it is, by default) and the table includes the
- # +locking_column+ column (defaults to +lock_version+).
- def locking_enabled?
- lock_optimistically && columns_hash[locking_column]
- end
+ # Returns true if the +lock_optimistically+ flag is set to true
+ # (which it is, by default) and the table includes the
+ # +locking_column+ column (defaults to +lock_version+).
+ def locking_enabled?
+ lock_optimistically && columns_hash[locking_column]
+ end
- # Set the column to use for optimistic locking. Defaults to +lock_version+.
- def locking_column=(value)
- reload_schema_from_cache
- @locking_column = value.to_s
- end
+ # Set the column to use for optimistic locking. Defaults to +lock_version+.
+ def locking_column=(value)
+ reload_schema_from_cache
+ @locking_column = value.to_s
+ end
- # The version column used for optimistic locking. Defaults to +lock_version+.
- def locking_column
- @locking_column = DEFAULT_LOCKING_COLUMN unless defined?(@locking_column)
- @locking_column
- end
+ # The version column used for optimistic locking. Defaults to +lock_version+.
+ def locking_column
+ @locking_column = DEFAULT_LOCKING_COLUMN unless defined?(@locking_column)
+ @locking_column
+ end
- # Reset the column used for optimistic locking back to the +lock_version+ default.
- def reset_locking_column
- self.locking_column = DEFAULT_LOCKING_COLUMN
- end
+ # Reset the column used for optimistic locking back to the +lock_version+ default.
+ def reset_locking_column
+ self.locking_column = DEFAULT_LOCKING_COLUMN
+ end
- # Make sure the lock version column gets updated when counters are
- # updated.
- def update_counters(id, counters)
- counters = counters.merge(locking_column => 1) if locking_enabled?
- super
- end
+ # Make sure the lock version column gets updated when counters are
+ # updated.
+ def update_counters(id, counters)
+ counters = counters.merge(locking_column => 1) if locking_enabled?
+ super
+ end
- private
-
- # We need to apply this decorator here, rather than on module inclusion. The closure
- # created by the matcher would otherwise evaluate for `ActiveRecord::Base`, not the
- # sub class being decorated. As such, changes to `lock_optimistically`, or
- # `locking_column` would not be picked up.
- def inherited(subclass)
- subclass.class_eval do
- is_lock_column = ->(name, _) { lock_optimistically && name == locking_column }
- decorate_matching_attribute_types(is_lock_column, :_optimistic_locking) do |type|
- LockingType.new(type)
+ private
+
+ # We need to apply this decorator here, rather than on module inclusion. The closure
+ # created by the matcher would otherwise evaluate for `ActiveRecord::Base`, not the
+ # sub class being decorated. As such, changes to `lock_optimistically`, or
+ # `locking_column` would not be picked up.
+ def inherited(subclass)
+ subclass.class_eval do
+ is_lock_column = ->(name, _) { lock_optimistically && name == locking_column }
+ decorate_matching_attribute_types(is_lock_column, :_optimistic_locking) do |type|
+ LockingType.new(type)
+ end
+ end
+ super
end
- end
- super
end
- end
end
-
# In de/serialize we change `nil` to 0, so that we can allow passing
# `nil` values to `lock_version`, and not result in `ActiveRecord::StaleObjectError`
# during update record.
@@ -198,11 +203,11 @@ module ActiveRecord
end
def init_with(coder)
- __setobj__(coder['subtype'])
+ __setobj__(coder["subtype"])
end
def encode_with(coder)
- coder['subtype'] = __getobj__
+ coder["subtype"] = __getobj__
end
end
end
diff --git a/activerecord/lib/active_record/locking/pessimistic.rb b/activerecord/lib/active_record/locking/pessimistic.rb
index 8ecdf76b72..e73cb4fc12 100644
--- a/activerecord/lib/active_record/locking/pessimistic.rb
+++ b/activerecord/lib/active_record/locking/pessimistic.rb
@@ -59,7 +59,7 @@ module ActiveRecord
# or pass true for "FOR UPDATE" (the default, an exclusive row lock). Returns
# the locked record.
def lock!(lock = true)
- reload(:lock => lock) if persisted?
+ reload(lock: lock) if persisted?
self
end
diff --git a/activerecord/lib/active_record/log_subscriber.rb b/activerecord/lib/active_record/log_subscriber.rb
index 8e32af1c49..4b8d8d9105 100644
--- a/activerecord/lib/active_record/log_subscriber.rb
+++ b/activerecord/lib/active_record/log_subscriber.rb
@@ -15,40 +15,24 @@ module ActiveRecord
rt
end
- def initialize
- super
- @odd = false
- end
-
- def render_bind(attribute)
- value = if attribute.type.binary? && attribute.value
- if attribute.value.is_a?(Hash)
- "<#{attribute.value_for_database.to_s.bytesize} bytes of binary data>"
- else
- "<#{attribute.value.bytesize} bytes of binary data>"
- end
- else
- attribute.value_for_database
- end
-
- [attribute.name, value]
- end
-
def sql(event)
- return unless logger.debug?
-
self.class.runtime += event.duration
+ return unless logger.debug?
payload = event.payload
return if IGNORE_PAYLOAD_NAMES.include?(payload[:name])
name = "#{payload[:name]} (#{event.duration.round(1)}ms)"
+ name = "CACHE #{name}" if payload[:cached]
sql = payload[:sql]
binds = nil
unless (payload[:binds] || []).empty?
- binds = " " + payload[:binds].map { |attr| render_bind(attr) }.inspect
+ casted_params = type_casted_binds(payload[:binds], payload[:type_casted_binds])
+ binds = " " + payload[:binds].zip(casted_params).map { |attr, value|
+ render_bind(attr, value)
+ }.inspect
end
name = colorize_payload_name(name, payload[:name])
@@ -59,16 +43,30 @@ module ActiveRecord
private
- def colorize_payload_name(name, payload_name)
- if payload_name.blank? || payload_name == "SQL" # SQL vs Model Load/Exists
- color(name, MAGENTA, true)
- else
- color(name, CYAN, true)
+ def type_casted_binds(binds, casted_binds)
+ casted_binds || binds.map { |attr| type_cast attr.value_for_database }
+ end
+
+ def render_bind(attr, type_casted_value)
+ value = if attr.type.binary? && attr.value
+ "<#{attr.value_for_database.to_s.bytesize} bytes of binary data>"
+ else
+ type_casted_value
+ end
+
+ [attr.name, value]
+ end
+
+ def colorize_payload_name(name, payload_name)
+ if payload_name.blank? || payload_name == "SQL" # SQL vs Model Load/Exists
+ color(name, MAGENTA, true)
+ else
+ color(name, CYAN, true)
+ end
end
- end
- def sql_color(sql)
- case sql
+ def sql_color(sql)
+ case sql
when /\A\s*rollback/mi
RED
when /select .*for update/mi, /\A\s*lock/mi
@@ -83,14 +81,18 @@ module ActiveRecord
RED
when /transaction\s*\Z/i
CYAN
- else
+ else
MAGENTA
+ end
end
- end
- def logger
- ActiveRecord::Base.logger
- end
+ def logger
+ ActiveRecord::Base.logger
+ end
+
+ def type_cast(value)
+ ActiveRecord::Base.connection.type_cast(value)
+ end
end
end
diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb
index 81fe053fe1..ed0c81b639 100644
--- a/activerecord/lib/active_record/migration.rb
+++ b/activerecord/lib/active_record/migration.rb
@@ -1,5 +1,6 @@
+require "set"
+require "zlib"
require "active_support/core_ext/module/attribute_accessors"
-require 'set'
module ActiveRecord
class MigrationError < ActiveRecordError#:nodoc:
@@ -126,9 +127,9 @@ module ActiveRecord
class PendingMigrationError < MigrationError#:nodoc:
def initialize(message = nil)
if !message && defined?(Rails.env)
- super("Migrations are pending. To resolve this issue, run:\n\n\tbin/rails db:migrate RAILS_ENV=#{::Rails.env}")
+ super("Migrations are pending. To resolve this issue, run:\n\n bin/rails db:migrate RAILS_ENV=#{::Rails.env}")
elsif !message
- super("Migrations are pending. To resolve this issue, run:\n\n\tbin/rails db:migrate")
+ super("Migrations are pending. To resolve this issue, run:\n\n bin/rails db:migrate")
else
super
end
@@ -145,7 +146,7 @@ module ActiveRecord
class NoEnvironmentInSchemaError < MigrationError #:nodoc:
def initialize
- msg = "Environment data not found in the schema. To resolve this issue, run: \n\n\tbin/rails db:environment:set"
+ msg = "Environment data not found in the schema. To resolve this issue, run: \n\n bin/rails db:environment:set"
if defined?(Rails.env)
super("#{msg} RAILS_ENV=#{::Rails.env}")
else
@@ -168,7 +169,7 @@ module ActiveRecord
msg = "You are attempting to modify a database that was last run in `#{ stored }` environment.\n"
msg << "You are running in `#{ current }` environment. "
msg << "If you are sure you want to continue, first set the environment using:\n\n"
- msg << "\tbin/rails db:environment:set"
+ msg << " bin/rails db:environment:set"
if defined?(Rails.env)
super("#{msg} RAILS_ENV=#{::Rails.env}\n\n")
else
@@ -276,8 +277,10 @@ module ActiveRecord
#
# * <tt>change_column(table_name, column_name, type, options)</tt>: Changes
# the column to a different type using the same parameters as add_column.
- # * <tt>change_column_default(table_name, column_name, default)</tt>: Sets a
- # default value for +column_name+ defined by +default+ on +table_name+.
+ # * <tt>change_column_default(table_name, column_name, default_or_changes)</tt>:
+ # Sets a default value for +column_name+ defined by +default_or_changes+ on
+ # +table_name+. Passing a hash containing <tt>:from</tt> and <tt>:to</tt>
+ # as +default_or_changes+ will make this change reversible in the migration.
# * <tt>change_column_null(table_name, column_name, null, default = nil)</tt>:
# Sets or removes a +NOT NULL+ constraint on +column_name+. The +null+ flag
# indicates whether the value can be +NULL+. See
@@ -509,8 +512,8 @@ module ActiveRecord
# Remember that you can still open your own transactions, even if you
# are in a Migration with <tt>self.disable_ddl_transaction!</tt>.
class Migration
- autoload :CommandRecorder, 'active_record/migration/command_recorder'
- autoload :Compatibility, 'active_record/migration/compatibility'
+ autoload :CommandRecorder, "active_record/migration/command_recorder"
+ autoload :Compatibility, "active_record/migration/compatibility"
# This must be defined before the inherited hook, below
class Current < Migration # :nodoc:
@@ -519,7 +522,10 @@ module ActiveRecord
def self.inherited(subclass) # :nodoc:
super
if subclass.superclass == Migration
- subclass.include Compatibility::Legacy
+ raise StandardError, "Directly inheriting from ActiveRecord::Migration is not supported. " \
+ "Please specify the Rails release the migration was written for:\n" \
+ "\n" \
+ " class #{self.class.name} < ActiveRecord::Migration[4.2]"
end
end
@@ -554,9 +560,9 @@ module ActiveRecord
private
- def connection
- ActiveRecord::Base.connection
- end
+ def connection
+ ActiveRecord::Base.connection
+ end
end
class << self
@@ -724,7 +730,7 @@ module ActiveRecord
# end
def reversible
helper = ReversibleBlockHelper.new(reverting?)
- execute_block{ yield helper }
+ execute_block { yield helper }
end
# Runs the given migration classes.
@@ -766,7 +772,7 @@ module ActiveRecord
when :down then announce "reverting"
end
- time = nil
+ time = nil
ActiveRecord::Base.connection_pool.with_connection do |conn|
time = Benchmark.measure do
exec_migration(conn, direction)
@@ -794,7 +800,7 @@ module ActiveRecord
@connection = nil
end
- def write(text="")
+ def write(text = "")
puts(text) if verbose
end
@@ -804,7 +810,7 @@ module ActiveRecord
write "== %s %s" % [text, "=" * length]
end
- def say(message, subitem=false)
+ def say(message, subitem = false)
write "#{subitem ? " ->" : "--"} #{message}"
end
@@ -829,7 +835,7 @@ module ActiveRecord
end
def method_missing(method, *arguments, &block)
- arg_list = arguments.map(&:inspect) * ', '
+ arg_list = arguments.map(&:inspect) * ", "
say_with_time "#{method}(#{arg_list})" do
unless connection.respond_to? :revert
@@ -921,19 +927,18 @@ module ActiveRecord
end
private
- def execute_block
- if connection.respond_to? :execute_block
- super # use normal delegation to record the block
- else
- yield
+ def execute_block
+ if connection.respond_to? :execute_block
+ super # use normal delegation to record the block
+ else
+ yield
+ end
end
- end
end
# MigrationProxy is used to defer loading of the actual migration classes
# until they are needed
class MigrationProxy < Struct.new(:name, :version, :filename, :scope)
-
def initialize(name, version, filename, scope)
super
@migration = nil
@@ -959,7 +964,6 @@ module ActiveRecord
require(File.expand_path(filename))
name.constantize.new(name, version)
end
-
end
class NullMigration < MigrationProxy #:nodoc:
@@ -990,11 +994,11 @@ module ActiveRecord
end
end
- def rollback(migrations_paths, steps=1)
+ def rollback(migrations_paths, steps = 1)
move(:down, migrations_paths, steps)
end
- def forward(migrations_paths, steps=1)
+ def forward(migrations_paths, steps = 1)
move(:up, migrations_paths, steps)
end
@@ -1025,12 +1029,10 @@ module ActiveRecord
end
def get_all_versions(connection = Base.connection)
- ActiveSupport::Deprecation.silence do
- if connection.table_exists?(schema_migrations_table_name)
- SchemaMigration.all.map { |x| x.version.to_i }.sort
- else
- []
- end
+ if connection.table_exists?(schema_migrations_table_name)
+ SchemaMigration.all.map { |x| x.version.to_i }.sort
+ else
+ []
end
end
@@ -1051,13 +1053,13 @@ module ActiveRecord
end
def migrations_paths
- @migrations_paths ||= ['db/migrate']
+ @migrations_paths ||= ["db/migrate"]
# just to not break things if someone uses: migrations_path = some_string
Array(@migrations_paths)
end
def match_to_migration_filename?(filename) # :nodoc:
- File.basename(filename) =~ Migration::MigrationFilenameRegexp
+ Migration::MigrationFilenameRegexp.match?(File.basename(filename))
end
def parse_migration_filename(filename) # :nodoc:
@@ -1164,146 +1166,146 @@ module ActiveRecord
private
- # Used for running a specific migration.
- def run_without_lock
- migration = migrations.detect { |m| m.version == @target_version }
- raise UnknownMigrationVersionError.new(@target_version) if migration.nil?
- execute_migration_in_transaction(migration, @direction)
-
- record_environment
- end
+ # Used for running a specific migration.
+ def run_without_lock
+ migration = migrations.detect { |m| m.version == @target_version }
+ raise UnknownMigrationVersionError.new(@target_version) if migration.nil?
+ execute_migration_in_transaction(migration, @direction)
- # Used for running multiple migrations up to or down to a certain value.
- def migrate_without_lock
- if invalid_target?
- raise UnknownMigrationVersionError.new(@target_version)
+ record_environment
end
- runnable.each do |migration|
- execute_migration_in_transaction(migration, @direction)
- end
+ # Used for running multiple migrations up to or down to a certain value.
+ def migrate_without_lock
+ if invalid_target?
+ raise UnknownMigrationVersionError.new(@target_version)
+ end
- record_environment
- end
+ runnable.each do |migration|
+ execute_migration_in_transaction(migration, @direction)
+ end
- # Stores the current environment in the database.
- def record_environment
- return if down?
- ActiveRecord::InternalMetadata[:environment] = ActiveRecord::Migrator.current_environment
- end
+ record_environment
+ end
- def ran?(migration)
- migrated.include?(migration.version.to_i)
- end
+ # Stores the current environment in the database.
+ def record_environment
+ return if down?
+ ActiveRecord::InternalMetadata[:environment] = ActiveRecord::Migrator.current_environment
+ end
- # Return true if a valid version is not provided.
- def invalid_target?
- !target && @target_version && @target_version > 0
- end
+ def ran?(migration)
+ migrated.include?(migration.version.to_i)
+ end
- def execute_migration_in_transaction(migration, direction)
- return if down? && !migrated.include?(migration.version.to_i)
- return if up? && migrated.include?(migration.version.to_i)
+ # Return true if a valid version is not provided.
+ def invalid_target?
+ !target && @target_version && @target_version > 0
+ end
+
+ def execute_migration_in_transaction(migration, direction)
+ return if down? && !migrated.include?(migration.version.to_i)
+ return if up? && migrated.include?(migration.version.to_i)
- Base.logger.info "Migrating to #{migration.name} (#{migration.version})" if Base.logger
+ Base.logger.info "Migrating to #{migration.name} (#{migration.version})" if Base.logger
- ddl_transaction(migration) do
- migration.migrate(direction)
- record_version_state_after_migrating(migration.version)
+ ddl_transaction(migration) do
+ migration.migrate(direction)
+ record_version_state_after_migrating(migration.version)
+ end
+ rescue => e
+ msg = "An error has occurred, "
+ msg << "this and " if use_transaction?(migration)
+ msg << "all later migrations canceled:\n\n#{e}"
+ raise StandardError, msg, e.backtrace
end
- rescue => e
- msg = "An error has occurred, "
- msg << "this and " if use_transaction?(migration)
- msg << "all later migrations canceled:\n\n#{e}"
- raise StandardError, msg, e.backtrace
- end
- def target
- migrations.detect { |m| m.version == @target_version }
- end
+ def target
+ migrations.detect { |m| m.version == @target_version }
+ end
- def finish
- migrations.index(target) || migrations.size - 1
- end
+ def finish
+ migrations.index(target) || migrations.size - 1
+ end
- def start
- up? ? 0 : (migrations.index(current) || 0)
- end
+ def start
+ up? ? 0 : (migrations.index(current) || 0)
+ end
- def validate(migrations)
- name ,= migrations.group_by(&:name).find { |_,v| v.length > 1 }
- raise DuplicateMigrationNameError.new(name) if name
+ def validate(migrations)
+ name , = migrations.group_by(&:name).find { |_, v| v.length > 1 }
+ raise DuplicateMigrationNameError.new(name) if name
- version ,= migrations.group_by(&:version).find { |_,v| v.length > 1 }
- raise DuplicateMigrationVersionError.new(version) if version
- end
+ version , = migrations.group_by(&:version).find { |_, v| v.length > 1 }
+ raise DuplicateMigrationVersionError.new(version) if version
+ end
- def record_version_state_after_migrating(version)
- if down?
- migrated.delete(version)
- ActiveRecord::SchemaMigration.where(:version => version.to_s).delete_all
- else
- migrated << version
- ActiveRecord::SchemaMigration.create!(version: version.to_s)
+ def record_version_state_after_migrating(version)
+ if down?
+ migrated.delete(version)
+ ActiveRecord::SchemaMigration.where(version: version.to_s).delete_all
+ else
+ migrated << version
+ ActiveRecord::SchemaMigration.create!(version: version.to_s)
+ end
end
- end
- def self.last_stored_environment
- return nil if current_version == 0
- raise NoEnvironmentInSchemaError unless ActiveRecord::InternalMetadata.table_exists?
+ def self.last_stored_environment
+ return nil if current_version == 0
+ raise NoEnvironmentInSchemaError unless ActiveRecord::InternalMetadata.table_exists?
- environment = ActiveRecord::InternalMetadata[:environment]
- raise NoEnvironmentInSchemaError unless environment
- environment
- end
+ environment = ActiveRecord::InternalMetadata[:environment]
+ raise NoEnvironmentInSchemaError unless environment
+ environment
+ end
- def self.current_environment
- ActiveRecord::ConnectionHandling::DEFAULT_ENV.call
- end
+ def self.current_environment
+ ActiveRecord::ConnectionHandling::DEFAULT_ENV.call
+ end
- def self.protected_environment?
- ActiveRecord::Base.protected_environments.include?(last_stored_environment) if last_stored_environment
- end
+ def self.protected_environment?
+ ActiveRecord::Base.protected_environments.include?(last_stored_environment) if last_stored_environment
+ end
- def up?
- @direction == :up
- end
+ def up?
+ @direction == :up
+ end
- def down?
- @direction == :down
- end
+ def down?
+ @direction == :down
+ end
- # Wrap the migration in a transaction only if supported by the adapter.
- def ddl_transaction(migration)
- if use_transaction?(migration)
- Base.transaction { yield }
- else
- yield
+ # Wrap the migration in a transaction only if supported by the adapter.
+ def ddl_transaction(migration)
+ if use_transaction?(migration)
+ Base.transaction { yield }
+ else
+ yield
+ end
end
- end
- def use_transaction?(migration)
- !migration.disable_ddl_transaction && Base.connection.supports_ddl_transactions?
- end
+ def use_transaction?(migration)
+ !migration.disable_ddl_transaction && Base.connection.supports_ddl_transactions?
+ end
- def use_advisory_lock?
- Base.connection.supports_advisory_locks?
- end
+ def use_advisory_lock?
+ Base.connection.supports_advisory_locks?
+ end
- def with_advisory_lock
- lock_id = generate_migrator_advisory_lock_id
- got_lock = Base.connection.get_advisory_lock(lock_id)
- raise ConcurrentMigrationError unless got_lock
- load_migrated # reload schema_migrations to be sure it wasn't changed by another process before we got the lock
- yield
- ensure
- Base.connection.release_advisory_lock(lock_id) if got_lock
- end
+ def with_advisory_lock
+ lock_id = generate_migrator_advisory_lock_id
+ got_lock = Base.connection.get_advisory_lock(lock_id)
+ raise ConcurrentMigrationError unless got_lock
+ load_migrated # reload schema_migrations to be sure it wasn't changed by another process before we got the lock
+ yield
+ ensure
+ Base.connection.release_advisory_lock(lock_id) if got_lock
+ end
- MIGRATOR_SALT = 2053462845
- def generate_migrator_advisory_lock_id
- db_name_hash = Zlib.crc32(Base.connection.current_database)
- MIGRATOR_SALT * db_name_hash
- end
+ MIGRATOR_SALT = 2053462845
+ def generate_migrator_advisory_lock_id
+ db_name_hash = Zlib.crc32(Base.connection.current_database)
+ MIGRATOR_SALT * db_name_hash
+ end
end
end
diff --git a/activerecord/lib/active_record/migration/command_recorder.rb b/activerecord/lib/active_record/migration/command_recorder.rb
index 0fa665c7e0..03103bba98 100644
--- a/activerecord/lib/active_record/migration/command_recorder.rb
+++ b/activerecord/lib/active_record/migration/command_recorder.rb
@@ -92,7 +92,7 @@ module ActiveRecord
send(method, args, &block)
end
- def respond_to?(*args) # :nodoc:
+ def respond_to_missing?(*args) # :nodoc:
super || delegate.respond_to?(*args)
end
@@ -112,127 +112,127 @@ module ActiveRecord
private
- module StraightReversions
- private
- { transaction: :transaction,
- execute_block: :execute_block,
- create_table: :drop_table,
- create_join_table: :drop_join_table,
- add_column: :remove_column,
- add_timestamps: :remove_timestamps,
- add_reference: :remove_reference,
- enable_extension: :disable_extension
- }.each do |cmd, inv|
- [[inv, cmd], [cmd, inv]].uniq.each do |method, inverse|
- class_eval <<-EOV, __FILE__, __LINE__ + 1
- def invert_#{method}(args, &block) # def invert_create_table(args, &block)
- [:#{inverse}, args, block] # [:drop_table, args, block]
- end # end
- EOV
- end
+ module StraightReversions
+ private
+ { transaction: :transaction,
+ execute_block: :execute_block,
+ create_table: :drop_table,
+ create_join_table: :drop_join_table,
+ add_column: :remove_column,
+ add_timestamps: :remove_timestamps,
+ add_reference: :remove_reference,
+ enable_extension: :disable_extension
+ }.each do |cmd, inv|
+ [[inv, cmd], [cmd, inv]].uniq.each do |method, inverse|
+ class_eval <<-EOV, __FILE__, __LINE__ + 1
+ def invert_#{method}(args, &block) # def invert_create_table(args, &block)
+ [:#{inverse}, args, block] # [:drop_table, args, block]
+ end # end
+ EOV
+ end
+ end
end
- end
- include StraightReversions
+ include StraightReversions
- def invert_drop_table(args, &block)
- if args.size == 1 && block == nil
- raise ActiveRecord::IrreversibleMigration, "To avoid mistakes, drop_table is only reversible if given options or a block (can be empty)."
+ def invert_drop_table(args, &block)
+ if args.size == 1 && block == nil
+ raise ActiveRecord::IrreversibleMigration, "To avoid mistakes, drop_table is only reversible if given options or a block (can be empty)."
+ end
+ super
end
- super
- end
- def invert_rename_table(args)
- [:rename_table, args.reverse]
- end
+ def invert_rename_table(args)
+ [:rename_table, args.reverse]
+ end
- def invert_remove_column(args)
- raise ActiveRecord::IrreversibleMigration, "remove_column is only reversible if given a type." if args.size <= 2
- super
- end
+ def invert_remove_column(args)
+ raise ActiveRecord::IrreversibleMigration, "remove_column is only reversible if given a type." if args.size <= 2
+ super
+ end
- def invert_rename_index(args)
- [:rename_index, [args.first] + args.last(2).reverse]
- end
+ def invert_rename_index(args)
+ [:rename_index, [args.first] + args.last(2).reverse]
+ end
- def invert_rename_column(args)
- [:rename_column, [args.first] + args.last(2).reverse]
- end
+ def invert_rename_column(args)
+ [:rename_column, [args.first] + args.last(2).reverse]
+ end
- def invert_add_index(args)
- table, columns, options = *args
- options ||= {}
+ def invert_add_index(args)
+ table, columns, options = *args
+ options ||= {}
- index_name = options[:name]
- options_hash = index_name ? { name: index_name } : { column: columns }
+ index_name = options[:name]
+ options_hash = index_name ? { name: index_name } : { column: columns }
- [:remove_index, [table, options_hash]]
- end
+ [:remove_index, [table, options_hash]]
+ end
- def invert_remove_index(args)
- table, options_or_column = *args
- if (options = options_or_column).is_a?(Hash)
- unless options[:column]
- raise ActiveRecord::IrreversibleMigration, "remove_index is only reversible if given a :column option."
+ def invert_remove_index(args)
+ table, options_or_column = *args
+ if (options = options_or_column).is_a?(Hash)
+ unless options[:column]
+ raise ActiveRecord::IrreversibleMigration, "remove_index is only reversible if given a :column option."
+ end
+ options = options.dup
+ [:add_index, [table, options.delete(:column), options]]
+ elsif (column = options_or_column).present?
+ [:add_index, [table, column]]
end
- options = options.dup
- [:add_index, [table, options.delete(:column), options]]
- elsif (column = options_or_column).present?
- [:add_index, [table, column]]
end
- end
- alias :invert_add_belongs_to :invert_add_reference
- alias :invert_remove_belongs_to :invert_remove_reference
+ alias :invert_add_belongs_to :invert_add_reference
+ alias :invert_remove_belongs_to :invert_remove_reference
+
+ def invert_change_column_default(args)
+ table, column, options = *args
- def invert_change_column_default(args)
- table, column, options = *args
+ unless options && options.is_a?(Hash) && options.has_key?(:from) && options.has_key?(:to)
+ raise ActiveRecord::IrreversibleMigration, "change_column_default is only reversible if given a :from and :to option."
+ end
- unless options && options.is_a?(Hash) && options.has_key?(:from) && options.has_key?(:to)
- raise ActiveRecord::IrreversibleMigration, "change_column_default is only reversible if given a :from and :to option."
+ [:change_column_default, [table, column, from: options[:to], to: options[:from]]]
end
- [:change_column_default, [table, column, from: options[:to], to: options[:from]]]
- end
+ def invert_change_column_null(args)
+ args[2] = !args[2]
+ [:change_column_null, args]
+ end
- def invert_change_column_null(args)
- args[2] = !args[2]
- [:change_column_null, args]
- end
+ def invert_add_foreign_key(args)
+ from_table, to_table, add_options = args
+ add_options ||= {}
- def invert_add_foreign_key(args)
- from_table, to_table, add_options = args
- add_options ||= {}
+ if add_options[:name]
+ options = { name: add_options[:name] }
+ elsif add_options[:column]
+ options = { column: add_options[:column] }
+ else
+ options = to_table
+ end
- if add_options[:name]
- options = { name: add_options[:name] }
- elsif add_options[:column]
- options = { column: add_options[:column] }
- else
- options = to_table
+ [:remove_foreign_key, [from_table, options]]
end
- [:remove_foreign_key, [from_table, options]]
- end
-
- def invert_remove_foreign_key(args)
- from_table, to_table, remove_options = args
- raise ActiveRecord::IrreversibleMigration, "remove_foreign_key is only reversible if given a second table" if to_table.nil? || to_table.is_a?(Hash)
+ def invert_remove_foreign_key(args)
+ from_table, to_table, remove_options = args
+ raise ActiveRecord::IrreversibleMigration, "remove_foreign_key is only reversible if given a second table" if to_table.nil? || to_table.is_a?(Hash)
- reversed_args = [from_table, to_table]
- reversed_args << remove_options if remove_options
+ reversed_args = [from_table, to_table]
+ reversed_args << remove_options if remove_options
- [:add_foreign_key, reversed_args]
- end
+ [:add_foreign_key, reversed_args]
+ end
- # Forwards any missing method call to the \target.
- def method_missing(method, *args, &block)
- if @delegate.respond_to?(method)
- @delegate.send(method, *args, &block)
- else
- super
+ # Forwards any missing method call to the \target.
+ def method_missing(method, *args, &block)
+ if @delegate.respond_to?(method)
+ @delegate.send(method, *args, &block)
+ else
+ super
+ end
end
- end
end
end
end
diff --git a/activerecord/lib/active_record/migration/compatibility.rb b/activerecord/lib/active_record/migration/compatibility.rb
index 74833f938f..2904634eb7 100644
--- a/activerecord/lib/active_record/migration/compatibility.rb
+++ b/activerecord/lib/active_record/migration/compatibility.rb
@@ -5,7 +5,7 @@ module ActiveRecord
version = version.to_s
name = "V#{version.tr('.', '_')}"
unless const_defined?(name)
- versions = constants.grep(/\AV[0-9_]+\z/).map { |s| s.to_s.delete('V').tr('_', '.').inspect }
+ versions = constants.grep(/\AV[0-9_]+\z/).map { |s| s.to_s.delete("V").tr("_", ".").inspect }
raise ArgumentError, "Unknown migration version #{version.inspect}; expected one of #{versions.sort.join(', ')}"
end
const_get(name)
@@ -13,7 +13,27 @@ module ActiveRecord
V5_1 = Current
- module FourTwoShared
+ class V5_0 < V5_1
+ def create_table(table_name, options = {})
+ if adapter_name == "PostgreSQL"
+ if options[:id] == :uuid && !options[:default]
+ options[:default] = "uuid_generate_v4()"
+ end
+ end
+
+ # Since 5.1 Postgres adapter uses bigserial type for primary
+ # keys by default and MySQL uses bigint. This compat layer makes old migrations utilize
+ # serial/int type instead -- the way it used to work before 5.1.
+ if options[:id].blank?
+ options[:id] = :integer
+ options[:auto_increment] = true
+ end
+
+ super
+ end
+ end
+
+ class V4_2 < V5_0
module TableDefinition
def references(*, **options)
options[:index] ||= false
@@ -21,7 +41,7 @@ module ActiveRecord
end
alias :belongs_to :references
- def timestamps(*, **options)
+ def timestamps(**options)
options[:null] = true if options[:null].nil?
super
end
@@ -59,7 +79,7 @@ module ActiveRecord
end
alias :add_belongs_to :add_reference
- def add_timestamps(*, **options)
+ def add_timestamps(_, **options)
options[:null] = true if options[:null].nil?
super
end
@@ -83,46 +103,23 @@ module ActiveRecord
private
- def index_name_for_remove(table_name, options = {})
- index_name = index_name(table_name, options)
+ def index_name_for_remove(table_name, options = {})
+ index_name = index_name(table_name, options)
+
+ unless index_name_exists?(table_name, index_name, true)
+ if options.is_a?(Hash) && options.has_key?(:name)
+ options_without_column = options.dup
+ options_without_column.delete :column
+ index_name_without_column = index_name(table_name, options_without_column)
- unless index_name_exists?(table_name, index_name, true)
- if options.is_a?(Hash) && options.has_key?(:name)
- options_without_column = options.dup
- options_without_column.delete :column
- index_name_without_column = index_name(table_name, options_without_column)
+ return index_name_without_column if index_name_exists?(table_name, index_name_without_column, false)
+ end
- return index_name_without_column if index_name_exists?(table_name, index_name_without_column, false)
+ raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' does not exist"
end
- raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' does not exist"
+ index_name
end
-
- index_name
- end
- end
-
- class V5_0 < V5_1
- end
-
- class V4_2 < V5_0
- # 4.2 is defined as a module because it needs to be shared with
- # Legacy. When the time comes, V5_0 should be defined straight
- # in its class.
- include FourTwoShared
- end
-
- module Legacy
- include FourTwoShared
-
- def migrate(*)
- ActiveSupport::Deprecation.warn \
- "Directly inheriting from ActiveRecord::Migration is deprecated. " \
- "Please specify the Rails release the migration was written for:\n" \
- "\n" \
- " class #{self.class.name} < ActiveRecord::Migration[4.2]"
- super
- end
end
end
end
diff --git a/activerecord/lib/active_record/migration/join_table.rb b/activerecord/lib/active_record/migration/join_table.rb
index 05569fadbd..89789f00ea 100644
--- a/activerecord/lib/active_record/migration/join_table.rb
+++ b/activerecord/lib/active_record/migration/join_table.rb
@@ -3,13 +3,13 @@ module ActiveRecord
module JoinTable #:nodoc:
private
- def find_join_table_name(table_1, table_2, options = {})
- options.delete(:table_name) || join_table_name(table_1, table_2)
- end
+ def find_join_table_name(table_1, table_2, options = {})
+ options.delete(:table_name) || join_table_name(table_1, table_2)
+ end
- def join_table_name(table_1, table_2)
- ModelSchema.derive_join_table_name(table_1, table_2).to_sym
- end
+ def join_table_name(table_1, table_2)
+ ModelSchema.derive_join_table_name(table_1, table_2).to_sym
+ end
end
end
end
diff --git a/activerecord/lib/active_record/model_schema.rb b/activerecord/lib/active_record/model_schema.rb
index 7996c32bbc..2a28c6bf6d 100644
--- a/activerecord/lib/active_record/model_schema.rb
+++ b/activerecord/lib/active_record/model_schema.rb
@@ -2,75 +2,154 @@ module ActiveRecord
module ModelSchema
extend ActiveSupport::Concern
+ ##
+ # :singleton-method: primary_key_prefix_type
+ # :call-seq: primary_key_prefix_type
+ #
+ # The prefix type that will be prepended to every primary key column name.
+ # The options are +:table_name+ and +:table_name_with_underscore+. If the first is specified,
+ # the Product class will look for "productid" instead of "id" as the primary column. If the
+ # latter is specified, the Product class will look for "product_id" instead of "id". Remember
+ # that this is a global setting for all Active Records.
+
+ ##
+ # :singleton-method: primary_key_prefix_type=
+ # :call-seq: primary_key_prefix_type=(prefix_type)
+ #
+ # Sets the prefix type that will be prepended to every primary key column name.
+ # The options are +:table_name+ and +:table_name_with_underscore+. If the first is specified,
+ # the Product class will look for "productid" instead of "id" as the primary column. If the
+ # latter is specified, the Product class will look for "product_id" instead of "id". Remember
+ # that this is a global setting for all Active Records.
+
+ ##
+ # :singleton-method: table_name_prefix
+ # :call-seq: table_name_prefix
+ #
+ # The prefix string to prepend to every table name.
+
+ ##
+ # :singleton-method: table_name_prefix=
+ # :call-seq: table_name_prefix=(prefix)
+ #
+ # Sets the prefix string to prepend to every table name. So if set to "basecamp_", all table
+ # names will be named like "basecamp_projects", "basecamp_people", etc. This is a convenient
+ # way of creating a namespace for tables in a shared database. By default, the prefix is the
+ # empty string.
+ #
+ # If you are organising your models within modules you can add a prefix to the models within
+ # a namespace by defining a singleton method in the parent module called table_name_prefix which
+ # returns your chosen prefix.
+
+ ##
+ # :singleton-method: table_name_suffix
+ # :call-seq: table_name_suffix
+ #
+ # The suffix string to append to every table name.
+
+ ##
+ # :singleton-method: table_name_suffix=
+ # :call-seq: table_name_suffix=(suffix)
+ #
+ # Works like +table_name_prefix=+, but appends instead of prepends (set to "_basecamp" gives "projects_basecamp",
+ # "people_basecamp"). By default, the suffix is the empty string.
+ #
+ # If you are organising your models within modules, you can add a suffix to the models within
+ # a namespace by defining a singleton method in the parent module called table_name_suffix which
+ # returns your chosen suffix.
+
+ ##
+ # :singleton-method: schema_migrations_table_name
+ # :call-seq: schema_migrations_table_name
+ #
+ # The name of the schema migrations table. By default, the value is <tt>"schema_migrations"</tt>.
+
+ ##
+ # :singleton-method: schema_migrations_table_name=
+ # :call-seq: schema_migrations_table_name=(table_name)
+ #
+ # Sets the name of the schema migrations table.
+
+ ##
+ # :singleton-method: internal_metadata_table_name
+ # :call-seq: internal_metadata_table_name
+ #
+ # The name of the internal metadata table. By default, the value is <tt>"ar_internal_metadata"</tt>.
+
+ ##
+ # :singleton-method: internal_metadata_table_name=
+ # :call-seq: internal_metadata_table_name=(table_name)
+ #
+ # Sets the name of the internal metadata table.
+
+ ##
+ # :singleton-method: protected_environments
+ # :call-seq: protected_environments
+ #
+ # The array of names of environments where destructive actions should be prohibited. By default,
+ # the value is <tt>["production"]</tt>.
+
+ ##
+ # :singleton-method: protected_environments=
+ # :call-seq: protected_environments=(environments)
+ #
+ # Sets an array of names of environments where destructive actions should be prohibited.
+
+ ##
+ # :singleton-method: pluralize_table_names
+ # :call-seq: pluralize_table_names
+ #
+ # Indicates whether table names should be the pluralized versions of the corresponding class names.
+ # If true, the default table name for a Product class will be "products". If false, it would just be "product".
+ # See table_name for the full rules on table/class naming. This is true, by default.
+
+ ##
+ # :singleton-method: pluralize_table_names=
+ # :call-seq: pluralize_table_names=(value)
+ #
+ # Set whether table names should be the pluralized versions of the corresponding class names.
+ # If true, the default table name for a Product class will be "products". If false, it would just be "product".
+ # See table_name for the full rules on table/class naming. This is true, by default.
+
+ ##
+ # :singleton-method: ignored_columns
+ # :call-seq: ignored_columns
+ #
+ # The list of columns names the model should ignore. Ignored columns won't have attribute
+ # accessors defined, and won't be referenced in SQL queries.
+
+ ##
+ # :singleton-method: ignored_columns=
+ # :call-seq: ignored_columns=(columns)
+ #
+ # Sets the columns names the model should ignore. Ignored columns won't have attribute
+ # accessors defined, and won't be referenced in SQL queries.
+
included do
- ##
- # :singleton-method:
- # Accessor for the prefix type that will be prepended to every primary key column name.
- # The options are :table_name and :table_name_with_underscore. If the first is specified,
- # the Product class will look for "productid" instead of "id" as the primary column. If the
- # latter is specified, the Product class will look for "product_id" instead of "id". Remember
- # that this is a global setting for all Active Records.
mattr_accessor :primary_key_prefix_type, instance_writer: false
- ##
- # :singleton-method:
- # Accessor for the name of the prefix string to prepend to every table name. So if set
- # to "basecamp_", all table names will be named like "basecamp_projects", "basecamp_people",
- # etc. This is a convenient way of creating a namespace for tables in a shared database.
- # By default, the prefix is the empty string.
- #
- # If you are organising your models within modules you can add a prefix to the models within
- # a namespace by defining a singleton method in the parent module called table_name_prefix which
- # returns your chosen prefix.
class_attribute :table_name_prefix, instance_writer: false
self.table_name_prefix = ""
- ##
- # :singleton-method:
- # Works like +table_name_prefix+, but appends instead of prepends (set to "_basecamp" gives "projects_basecamp",
- # "people_basecamp"). By default, the suffix is the empty string.
- #
- # If you are organising your models within modules, you can add a suffix to the models within
- # a namespace by defining a singleton method in the parent module called table_name_suffix which
- # returns your chosen suffix.
class_attribute :table_name_suffix, instance_writer: false
self.table_name_suffix = ""
- ##
- # :singleton-method:
- # Accessor for the name of the schema migrations table. By default, the value is "schema_migrations"
class_attribute :schema_migrations_table_name, instance_accessor: false
self.schema_migrations_table_name = "schema_migrations"
- ##
- # :singleton-method:
- # Accessor for the name of the internal metadata table. By default, the value is "ar_internal_metadata"
class_attribute :internal_metadata_table_name, instance_accessor: false
self.internal_metadata_table_name = "ar_internal_metadata"
- ##
- # :singleton-method:
- # Accessor for an array of names of environments where destructive actions should be prohibited. By default,
- # the value is ["production"]
class_attribute :protected_environments, instance_accessor: false
self.protected_environments = ["production"]
- ##
- # :singleton-method:
- # Indicates whether table names should be the pluralized versions of the corresponding class names.
- # If true, the default table name for a Product class will be +products+. If false, it would just be +product+.
- # See table_name for the full rules on table/class naming. This is true, by default.
class_attribute :pluralize_table_names, instance_writer: false
self.pluralize_table_names = true
- ##
- # :singleton-method:
- # Accessor for the list of columns names the model should ignore. Ignored columns won't have attribute
- # accessors defined, and won't be referenced in SQL queries.
class_attribute :ignored_columns, instance_accessor: false
self.ignored_columns = [].freeze
- self.inheritance_column = 'type'
+ self.inheritance_column = "type"
delegate :type_for_attribute, to: :class
end
@@ -173,11 +252,11 @@ module ActiveRecord
end
def full_table_name_prefix #:nodoc:
- (parents.detect{ |p| p.respond_to?(:table_name_prefix) } || self).table_name_prefix
+ (parents.detect { |p| p.respond_to?(:table_name_prefix) } || self).table_name_prefix
end
def full_table_name_suffix #:nodoc:
- (parents.detect {|p| p.respond_to?(:table_name_suffix) } || self).table_name_suffix
+ (parents.detect { |p| p.respond_to?(:table_name_suffix) } || self).table_name_suffix
end
# Defines the name of the table column which will store the class name on single-table
@@ -213,7 +292,7 @@ module ActiveRecord
end
# Sets the name of the sequence to use when generating ids to the given
- # value, or (if the value is nil or false) to the value returned by the
+ # value, or (if the value is +nil+ or +false+) to the value returned by the
# given block. This is required for Oracle and is useful for any
# database which relies on sequences for primary key generation.
#
@@ -249,7 +328,11 @@ module ActiveRecord
end
def attributes_builder # :nodoc:
- @attributes_builder ||= AttributeSet::Builder.new(attribute_types, primary_key)
+ @attributes_builder ||= AttributeSet::Builder.new(attribute_types, primary_key) do |name|
+ unless columns_hash.key?(name)
+ _default_attributes[name].dup
+ end
+ end
end
def columns_hash # :nodoc:
@@ -264,7 +347,7 @@ module ActiveRecord
def attribute_types # :nodoc:
load_schema
- @attribute_types ||= Hash.new(Type::Value.new)
+ @attribute_types ||= Hash.new(Type.default_value)
end
def yaml_encoder # :nodoc:
@@ -282,8 +365,12 @@ module ActiveRecord
#
# +attr_name+ The name of the attribute to retrieve the type for. Must be
# a string
- def type_for_attribute(attr_name)
- attribute_types[attr_name]
+ def type_for_attribute(attr_name, &block)
+ if block
+ attribute_types.fetch(attr_name, &block)
+ else
+ attribute_types[attr_name]
+ end
end
# Returns a hash where the keys are column names and the values are
@@ -305,7 +392,12 @@ module ActiveRecord
# Returns an array of column objects where the primary id, all columns ending in "_id" or "_count",
# and columns used for single table inheritance have been removed.
def content_columns
- @content_columns ||= columns.reject { |c| c.name == primary_key || c.name =~ /(_id|_count)$/ || c.name == inheritance_column }
+ @content_columns ||= columns.reject do |c|
+ c.name == primary_key ||
+ c.name == inheritance_column ||
+ c.name.end_with?("_id") ||
+ c.name.end_with?("_count")
+ end
end
# Resets all the cached information about columns, which will cause them
@@ -344,92 +436,69 @@ module ActiveRecord
private
- def schema_loaded?
- defined?(@columns_hash) && @columns_hash
- end
-
- def load_schema
- unless schema_loaded?
- load_schema!
+ def schema_loaded?
+ defined?(@columns_hash) && @columns_hash
end
- end
- def load_schema!
- @columns_hash = connection.schema_cache.columns_hash(table_name).except(*ignored_columns)
- @columns_hash.each do |name, column|
- warn_if_deprecated_type(column)
- define_attribute(
- name,
- connection.lookup_cast_type_from_column(column),
- default: column.default,
- user_provided_default: false
- )
+ def load_schema
+ unless schema_loaded?
+ load_schema!
+ end
end
- end
- def reload_schema_from_cache
- @arel_engine = nil
- @arel_table = nil
- @column_names = nil
- @attribute_types = nil
- @content_columns = nil
- @default_attributes = nil
- @inheritance_column = nil unless defined?(@explicit_inheritance_column) && @explicit_inheritance_column
- @attributes_builder = nil
- @columns = nil
- @columns_hash = nil
- @attribute_names = nil
- @yaml_encoder = nil
- direct_descendants.each do |descendant|
- descendant.send(:reload_schema_from_cache)
+ def load_schema!
+ @columns_hash = connection.schema_cache.columns_hash(table_name).except(*ignored_columns)
+ @columns_hash.each do |name, column|
+ define_attribute(
+ name,
+ connection.lookup_cast_type_from_column(column),
+ default: column.default,
+ user_provided_default: false
+ )
+ end
end
- end
- # Guesses the table name, but does not decorate it with prefix and suffix information.
- def undecorated_table_name(class_name = base_class.name)
- table_name = class_name.to_s.demodulize.underscore
- pluralize_table_names ? table_name.pluralize : table_name
- end
-
- # Computes and returns a table name according to default conventions.
- def compute_table_name
- base = base_class
- if self == base
- # Nested classes are prefixed with singular parent table name.
- if parent < Base && !parent.abstract_class?
- contained = parent.table_name
- contained = contained.singularize if parent.pluralize_table_names
- contained += '_'
+ def reload_schema_from_cache
+ @arel_engine = nil
+ @arel_table = nil
+ @column_names = nil
+ @attribute_types = nil
+ @content_columns = nil
+ @default_attributes = nil
+ @inheritance_column = nil unless defined?(@explicit_inheritance_column) && @explicit_inheritance_column
+ @attributes_builder = nil
+ @columns = nil
+ @columns_hash = nil
+ @attribute_names = nil
+ @yaml_encoder = nil
+ direct_descendants.each do |descendant|
+ descendant.send(:reload_schema_from_cache)
end
+ end
- "#{full_table_name_prefix}#{contained}#{undecorated_table_name(name)}#{full_table_name_suffix}"
- else
- # STI subclasses always use their superclass' table.
- base.table_name
+ # Guesses the table name, but does not decorate it with prefix and suffix information.
+ def undecorated_table_name(class_name = base_class.name)
+ table_name = class_name.to_s.demodulize.underscore
+ pluralize_table_names ? table_name.pluralize : table_name
end
- end
- def warn_if_deprecated_type(column)
- return if attributes_to_define_after_schema_loads.key?(column.name)
- if column.respond_to?(:oid) && column.sql_type.start_with?("point")
- if column.array?
- array_arguments = ", array: true"
+ # Computes and returns a table name according to default conventions.
+ def compute_table_name
+ base = base_class
+ if self == base
+ # Nested classes are prefixed with singular parent table name.
+ if parent < Base && !parent.abstract_class?
+ contained = parent.table_name
+ contained = contained.singularize if parent.pluralize_table_names
+ contained += "_"
+ end
+
+ "#{full_table_name_prefix}#{contained}#{undecorated_table_name(name)}#{full_table_name_suffix}"
else
- array_arguments = ""
+ # STI subclasses always use their superclass' table.
+ base.table_name
end
- ActiveSupport::Deprecation.warn(<<-WARNING.strip_heredoc)
- The behavior of the `:point` type will be changing in Rails 5.1 to
- return a `Point` object, rather than an `Array`. If you'd like to
- keep the old behavior, you can add this line to #{self.name}:
-
- attribute :#{column.name}, :legacy_point#{array_arguments}
-
- If you'd like the new behavior today, you can add this line:
-
- attribute :#{column.name}, :point#{array_arguments}
- WARNING
end
- end
end
end
end
diff --git a/activerecord/lib/active_record/nested_attributes.rb b/activerecord/lib/active_record/nested_attributes.rb
index fe68869143..e983026961 100644
--- a/activerecord/lib/active_record/nested_attributes.rb
+++ b/activerecord/lib/active_record/nested_attributes.rb
@@ -1,6 +1,6 @@
-require 'active_support/core_ext/hash/except'
-require 'active_support/core_ext/object/try'
-require 'active_support/core_ext/hash/indifferent_access'
+require "active_support/core_ext/hash/except"
+require "active_support/core_ext/object/try"
+require "active_support/core_ext/hash/indifferent_access"
module ActiveRecord
module NestedAttributes #:nodoc:
@@ -267,7 +267,7 @@ module ActiveRecord
# member.avatar_attributes = {icon: 'sad'}
# member.avatar.width # => 200
module ClassMethods
- REJECT_ALL_BLANK_PROC = proc { |attributes| attributes.all? { |key, value| key == '_destroy' || value.blank? } }
+ REJECT_ALL_BLANK_PROC = proc { |attributes| attributes.all? { |key, value| key == "_destroy" || value.blank? } }
# Defines an attributes writer for the specified association(s).
#
@@ -317,7 +317,7 @@ module ActiveRecord
# # creates avatar_attributes= and posts_attributes=
# accepts_nested_attributes_for :avatar, :posts, allow_destroy: true
def accepts_nested_attributes_for(*attr_names)
- options = { :allow_destroy => false, :update_only => false }
+ options = { allow_destroy: false, update_only: false }
options.update(attr_names.extract_options!)
options.assert_valid_keys(:allow_destroy, :reject_if, :limit, :update_only)
options[:reject_if] = REJECT_ALL_BLANK_PROC if options[:reject_if] == :all_blank
@@ -341,27 +341,27 @@ module ActiveRecord
private
- # Generates a writer method for this association. Serves as a point for
- # accessing the objects in the association. For example, this method
- # could generate the following:
- #
- # def pirate_attributes=(attributes)
- # assign_nested_attributes_for_one_to_one_association(:pirate, attributes)
- # end
- #
- # This redirects the attempts to write objects in an association through
- # the helper methods defined below. Makes it seem like the nested
- # associations are just regular associations.
- def generate_association_writer(association_name, type)
- generated_association_methods.module_eval <<-eoruby, __FILE__, __LINE__ + 1
- if method_defined?(:#{association_name}_attributes=)
- remove_method(:#{association_name}_attributes=)
- end
- def #{association_name}_attributes=(attributes)
- assign_nested_attributes_for_#{type}_association(:#{association_name}, attributes)
- end
- eoruby
- end
+ # Generates a writer method for this association. Serves as a point for
+ # accessing the objects in the association. For example, this method
+ # could generate the following:
+ #
+ # def pirate_attributes=(attributes)
+ # assign_nested_attributes_for_one_to_one_association(:pirate, attributes)
+ # end
+ #
+ # This redirects the attempts to write objects in an association through
+ # the helper methods defined below. Makes it seem like the nested
+ # associations are just regular associations.
+ def generate_association_writer(association_name, type)
+ generated_association_methods.module_eval <<-eoruby, __FILE__, __LINE__ + 1
+ if method_defined?(:#{association_name}_attributes=)
+ remove_method(:#{association_name}_attributes=)
+ end
+ def #{association_name}_attributes=(attributes)
+ assign_nested_attributes_for_#{type}_association(:#{association_name}, attributes)
+ end
+ eoruby
+ end
end
# Returns ActiveRecord::AutosaveAssociation::marked_for_destruction? It's
@@ -375,213 +375,214 @@ module ActiveRecord
private
- # Attribute hash keys that should not be assigned as normal attributes.
- # These hash keys are nested attributes implementation details.
- UNASSIGNABLE_KEYS = %w( id _destroy )
-
- # Assigns the given attributes to the association.
- #
- # If an associated record does not yet exist, one will be instantiated. If
- # an associated record already exists, the method's behavior depends on
- # the value of the update_only option. If update_only is +false+ and the
- # given attributes include an <tt>:id</tt> that matches the existing record's
- # id, then the existing record will be modified. If no <tt>:id</tt> is provided
- # it will be replaced with a new record. If update_only is +true+ the existing
- # record will be modified regardless of whether an <tt>:id</tt> is provided.
- #
- # If the given attributes include a matching <tt>:id</tt> attribute, or
- # update_only is true, and a <tt>:_destroy</tt> key set to a truthy value,
- # then the existing record will be marked for destruction.
- def assign_nested_attributes_for_one_to_one_association(association_name, attributes)
- options = self.nested_attributes_options[association_name]
- if attributes.respond_to?(:permitted?)
- attributes = attributes.to_h
- end
- attributes = attributes.with_indifferent_access
- existing_record = send(association_name)
+ # Attribute hash keys that should not be assigned as normal attributes.
+ # These hash keys are nested attributes implementation details.
+ UNASSIGNABLE_KEYS = %w( id _destroy )
- if (options[:update_only] || !attributes['id'].blank?) && existing_record &&
- (options[:update_only] || existing_record.id.to_s == attributes['id'].to_s)
- assign_to_or_mark_for_destruction(existing_record, attributes, options[:allow_destroy]) unless call_reject_if(association_name, attributes)
+ # Assigns the given attributes to the association.
+ #
+ # If an associated record does not yet exist, one will be instantiated. If
+ # an associated record already exists, the method's behavior depends on
+ # the value of the update_only option. If update_only is +false+ and the
+ # given attributes include an <tt>:id</tt> that matches the existing record's
+ # id, then the existing record will be modified. If no <tt>:id</tt> is provided
+ # it will be replaced with a new record. If update_only is +true+ the existing
+ # record will be modified regardless of whether an <tt>:id</tt> is provided.
+ #
+ # If the given attributes include a matching <tt>:id</tt> attribute, or
+ # update_only is true, and a <tt>:_destroy</tt> key set to a truthy value,
+ # then the existing record will be marked for destruction.
+ def assign_nested_attributes_for_one_to_one_association(association_name, attributes)
+ options = self.nested_attributes_options[association_name]
+ if attributes.respond_to?(:permitted?)
+ attributes = attributes.to_h
+ end
+ attributes = attributes.with_indifferent_access
+ existing_record = send(association_name)
- elsif attributes['id'].present?
- raise_nested_attributes_record_not_found!(association_name, attributes['id'])
+ if (options[:update_only] || !attributes["id"].blank?) && existing_record &&
+ (options[:update_only] || existing_record.id.to_s == attributes["id"].to_s)
+ assign_to_or_mark_for_destruction(existing_record, attributes, options[:allow_destroy]) unless call_reject_if(association_name, attributes)
- elsif !reject_new_record?(association_name, attributes)
- assignable_attributes = attributes.except(*UNASSIGNABLE_KEYS)
+ elsif attributes["id"].present?
+ raise_nested_attributes_record_not_found!(association_name, attributes["id"])
- if existing_record && existing_record.new_record?
- existing_record.assign_attributes(assignable_attributes)
- association(association_name).initialize_attributes(existing_record)
- else
- method = "build_#{association_name}"
- if respond_to?(method)
- send(method, assignable_attributes)
+ elsif !reject_new_record?(association_name, attributes)
+ assignable_attributes = attributes.except(*UNASSIGNABLE_KEYS)
+
+ if existing_record && existing_record.new_record?
+ existing_record.assign_attributes(assignable_attributes)
+ association(association_name).initialize_attributes(existing_record)
else
- raise ArgumentError, "Cannot build association `#{association_name}'. Are you trying to build a polymorphic one-to-one association?"
+ method = "build_#{association_name}"
+ if respond_to?(method)
+ send(method, assignable_attributes)
+ else
+ raise ArgumentError, "Cannot build association `#{association_name}'. Are you trying to build a polymorphic one-to-one association?"
+ end
end
end
end
- end
- # Assigns the given attributes to the collection association.
- #
- # Hashes with an <tt>:id</tt> value matching an existing associated record
- # will update that record. Hashes without an <tt>:id</tt> value will build
- # a new record for the association. Hashes with a matching <tt>:id</tt>
- # value and a <tt>:_destroy</tt> key set to a truthy value will mark the
- # matched record for destruction.
- #
- # For example:
- #
- # assign_nested_attributes_for_collection_association(:people, {
- # '1' => { id: '1', name: 'Peter' },
- # '2' => { name: 'John' },
- # '3' => { id: '2', _destroy: true }
- # })
- #
- # Will update the name of the Person with ID 1, build a new associated
- # person with the name 'John', and mark the associated Person with ID 2
- # for destruction.
- #
- # Also accepts an Array of attribute hashes:
- #
- # assign_nested_attributes_for_collection_association(:people, [
- # { id: '1', name: 'Peter' },
- # { name: 'John' },
- # { id: '2', _destroy: true }
- # ])
- def assign_nested_attributes_for_collection_association(association_name, attributes_collection)
- options = self.nested_attributes_options[association_name]
- if attributes_collection.respond_to?(:permitted?)
- attributes_collection = attributes_collection.to_h
- end
+ # Assigns the given attributes to the collection association.
+ #
+ # Hashes with an <tt>:id</tt> value matching an existing associated record
+ # will update that record. Hashes without an <tt>:id</tt> value will build
+ # a new record for the association. Hashes with a matching <tt>:id</tt>
+ # value and a <tt>:_destroy</tt> key set to a truthy value will mark the
+ # matched record for destruction.
+ #
+ # For example:
+ #
+ # assign_nested_attributes_for_collection_association(:people, {
+ # '1' => { id: '1', name: 'Peter' },
+ # '2' => { name: 'John' },
+ # '3' => { id: '2', _destroy: true }
+ # })
+ #
+ # Will update the name of the Person with ID 1, build a new associated
+ # person with the name 'John', and mark the associated Person with ID 2
+ # for destruction.
+ #
+ # Also accepts an Array of attribute hashes:
+ #
+ # assign_nested_attributes_for_collection_association(:people, [
+ # { id: '1', name: 'Peter' },
+ # { name: 'John' },
+ # { id: '2', _destroy: true }
+ # ])
+ def assign_nested_attributes_for_collection_association(association_name, attributes_collection)
+ options = self.nested_attributes_options[association_name]
+ if attributes_collection.respond_to?(:permitted?)
+ attributes_collection = attributes_collection.to_h
+ end
- unless attributes_collection.is_a?(Hash) || attributes_collection.is_a?(Array)
- raise ArgumentError, "Hash or Array expected, got #{attributes_collection.class.name} (#{attributes_collection.inspect})"
- end
+ unless attributes_collection.is_a?(Hash) || attributes_collection.is_a?(Array)
+ raise ArgumentError, "Hash or Array expected, got #{attributes_collection.class.name} (#{attributes_collection.inspect})"
+ end
- check_record_limit!(options[:limit], attributes_collection)
+ check_record_limit!(options[:limit], attributes_collection)
- if attributes_collection.is_a? Hash
- keys = attributes_collection.keys
- attributes_collection = if keys.include?('id') || keys.include?(:id)
- [attributes_collection]
- else
- attributes_collection.values
+ if attributes_collection.is_a? Hash
+ keys = attributes_collection.keys
+ attributes_collection = if keys.include?("id") || keys.include?(:id)
+ [attributes_collection]
+ else
+ attributes_collection.values
+ end
end
- end
- association = association(association_name)
+ association = association(association_name)
- existing_records = if association.loaded?
- association.target
- else
- attribute_ids = attributes_collection.map {|a| a['id'] || a[:id] }.compact
- attribute_ids.empty? ? [] : association.scope.where(association.klass.primary_key => attribute_ids)
- end
-
- attributes_collection.each do |attributes|
- if attributes.respond_to?(:permitted?)
- attributes = attributes.to_h
+ existing_records = if association.loaded?
+ association.target
+ else
+ attribute_ids = attributes_collection.map { |a| a["id"] || a[:id] }.compact
+ attribute_ids.empty? ? [] : association.scope.where(association.klass.primary_key => attribute_ids)
end
- attributes = attributes.with_indifferent_access
- if attributes['id'].blank?
- unless reject_new_record?(association_name, attributes)
- association.build(attributes.except(*UNASSIGNABLE_KEYS))
+ attributes_collection.each do |attributes|
+ if attributes.respond_to?(:permitted?)
+ attributes = attributes.to_h
end
- elsif existing_record = existing_records.detect { |record| record.id.to_s == attributes['id'].to_s }
- unless call_reject_if(association_name, attributes)
- # Make sure we are operating on the actual object which is in the association's
- # proxy_target array (either by finding it, or adding it if not found)
- # Take into account that the proxy_target may have changed due to callbacks
- target_record = association.target.detect { |record| record.id.to_s == attributes['id'].to_s }
- if target_record
- existing_record = target_record
- else
- association.add_to_target(existing_record, :skip_callbacks)
- end
+ attributes = attributes.with_indifferent_access
- assign_to_or_mark_for_destruction(existing_record, attributes, options[:allow_destroy])
+ if attributes["id"].blank?
+ unless reject_new_record?(association_name, attributes)
+ association.build(attributes.except(*UNASSIGNABLE_KEYS))
+ end
+ elsif existing_record = existing_records.detect { |record| record.id.to_s == attributes["id"].to_s }
+ unless call_reject_if(association_name, attributes)
+ # Make sure we are operating on the actual object which is in the association's
+ # proxy_target array (either by finding it, or adding it if not found)
+ # Take into account that the proxy_target may have changed due to callbacks
+ target_record = association.target.detect { |record| record.id.to_s == attributes["id"].to_s }
+ if target_record
+ existing_record = target_record
+ else
+ association.add_to_target(existing_record, :skip_callbacks)
+ end
+
+ assign_to_or_mark_for_destruction(existing_record, attributes, options[:allow_destroy])
+ end
+ else
+ raise_nested_attributes_record_not_found!(association_name, attributes["id"])
end
- else
- raise_nested_attributes_record_not_found!(association_name, attributes['id'])
end
end
- end
- # Takes in a limit and checks if the attributes_collection has too many
- # records. It accepts limit in the form of symbol, proc, or
- # number-like object (anything that can be compared with an integer).
- #
- # Raises TooManyRecords error if the attributes_collection is
- # larger than the limit.
- def check_record_limit!(limit, attributes_collection)
- if limit
- limit = case limit
- when Symbol
- send(limit)
- when Proc
- limit.call
- else
- limit
- end
+ # Takes in a limit and checks if the attributes_collection has too many
+ # records. It accepts limit in the form of symbol, proc, or
+ # number-like object (anything that can be compared with an integer).
+ #
+ # Raises TooManyRecords error if the attributes_collection is
+ # larger than the limit.
+ def check_record_limit!(limit, attributes_collection)
+ if limit
+ limit = \
+ case limit
+ when Symbol
+ send(limit)
+ when Proc
+ limit.call
+ else
+ limit
+ end
- if limit && attributes_collection.size > limit
- raise TooManyRecords, "Maximum #{limit} records are allowed. Got #{attributes_collection.size} records instead."
+ if limit && attributes_collection.size > limit
+ raise TooManyRecords, "Maximum #{limit} records are allowed. Got #{attributes_collection.size} records instead."
+ end
end
end
- end
- # Updates a record with the +attributes+ or marks it for destruction if
- # +allow_destroy+ is +true+ and has_destroy_flag? returns +true+.
- def assign_to_or_mark_for_destruction(record, attributes, allow_destroy)
- record.assign_attributes(attributes.except(*UNASSIGNABLE_KEYS))
- record.mark_for_destruction if has_destroy_flag?(attributes) && allow_destroy
- end
+ # Updates a record with the +attributes+ or marks it for destruction if
+ # +allow_destroy+ is +true+ and has_destroy_flag? returns +true+.
+ def assign_to_or_mark_for_destruction(record, attributes, allow_destroy)
+ record.assign_attributes(attributes.except(*UNASSIGNABLE_KEYS))
+ record.mark_for_destruction if has_destroy_flag?(attributes) && allow_destroy
+ end
- # Determines if a hash contains a truthy _destroy key.
- def has_destroy_flag?(hash)
- Type::Boolean.new.cast(hash['_destroy'])
- end
+ # Determines if a hash contains a truthy _destroy key.
+ def has_destroy_flag?(hash)
+ Type::Boolean.new.cast(hash["_destroy"])
+ end
- # Determines if a new record should be rejected by checking
- # has_destroy_flag? or if a <tt>:reject_if</tt> proc exists for this
- # association and evaluates to +true+.
- def reject_new_record?(association_name, attributes)
- will_be_destroyed?(association_name, attributes) || call_reject_if(association_name, attributes)
- end
+ # Determines if a new record should be rejected by checking
+ # has_destroy_flag? or if a <tt>:reject_if</tt> proc exists for this
+ # association and evaluates to +true+.
+ def reject_new_record?(association_name, attributes)
+ will_be_destroyed?(association_name, attributes) || call_reject_if(association_name, attributes)
+ end
- # Determines if a record with the particular +attributes+ should be
- # rejected by calling the reject_if Symbol or Proc (if defined).
- # The reject_if option is defined by +accepts_nested_attributes_for+.
- #
- # Returns false if there is a +destroy_flag+ on the attributes.
- def call_reject_if(association_name, attributes)
- return false if will_be_destroyed?(association_name, attributes)
+ # Determines if a record with the particular +attributes+ should be
+ # rejected by calling the reject_if Symbol or Proc (if defined).
+ # The reject_if option is defined by +accepts_nested_attributes_for+.
+ #
+ # Returns false if there is a +destroy_flag+ on the attributes.
+ def call_reject_if(association_name, attributes)
+ return false if will_be_destroyed?(association_name, attributes)
- case callback = self.nested_attributes_options[association_name][:reject_if]
- when Symbol
- method(callback).arity == 0 ? send(callback) : send(callback, attributes)
- when Proc
- callback.call(attributes)
+ case callback = self.nested_attributes_options[association_name][:reject_if]
+ when Symbol
+ method(callback).arity == 0 ? send(callback) : send(callback, attributes)
+ when Proc
+ callback.call(attributes)
+ end
end
- end
- # Only take into account the destroy flag if <tt>:allow_destroy</tt> is true
- def will_be_destroyed?(association_name, attributes)
- allow_destroy?(association_name) && has_destroy_flag?(attributes)
- end
+ # Only take into account the destroy flag if <tt>:allow_destroy</tt> is true
+ def will_be_destroyed?(association_name, attributes)
+ allow_destroy?(association_name) && has_destroy_flag?(attributes)
+ end
- def allow_destroy?(association_name)
- self.nested_attributes_options[association_name][:allow_destroy]
- end
+ def allow_destroy?(association_name)
+ nested_attributes_options[association_name][:allow_destroy]
+ end
- def raise_nested_attributes_record_not_found!(association_name, record_id)
- model = self.class._reflect_on_association(association_name).klass.name
- raise RecordNotFound.new("Couldn't find #{model} with ID=#{record_id} for #{self.class.name} with ID=#{id}",
- model, 'id', record_id)
- end
+ def raise_nested_attributes_record_not_found!(association_name, record_id)
+ model = self.class._reflect_on_association(association_name).klass.name
+ raise RecordNotFound.new("Couldn't find #{model} with ID=#{record_id} for #{self.class.name} with ID=#{id}",
+ model, "id", record_id)
+ end
end
end
diff --git a/activerecord/lib/active_record/no_touching.rb b/activerecord/lib/active_record/no_touching.rb
index edb5066fa0..4059020e25 100644
--- a/activerecord/lib/active_record/no_touching.rb
+++ b/activerecord/lib/active_record/no_touching.rb
@@ -45,6 +45,10 @@ module ActiveRecord
NoTouching.applied_to?(self.class)
end
+ def touch_later(*) # :nodoc:
+ super unless no_touching?
+ end
+
def touch(*) # :nodoc:
super unless no_touching?
end
diff --git a/activerecord/lib/active_record/null_relation.rb b/activerecord/lib/active_record/null_relation.rb
index 1ab4e0404f..2bb7ed6d5e 100644
--- a/activerecord/lib/active_record/null_relation.rb
+++ b/activerecord/lib/active_record/null_relation.rb
@@ -1,9 +1,5 @@
module ActiveRecord
module NullRelation # :nodoc:
- def exec_queries
- @records = [].freeze
- end
-
def pluck(*column_names)
[]
end
@@ -20,10 +16,6 @@ module ActiveRecord
0
end
- def size
- calculate :size, nil
- end
-
def empty?
true
end
@@ -48,33 +40,12 @@ module ActiveRecord
""
end
- def count(*)
- calculate :count, nil
- end
-
- def sum(*)
- calculate :sum, nil
- end
-
- def average(*)
- calculate :average, nil
- end
-
- def minimum(*)
- calculate :minimum, nil
- end
-
- def maximum(*)
- calculate :maximum, nil
- end
-
def calculate(operation, _column_name)
- if [:count, :sum, :size].include? operation
+ case operation
+ when :count, :sum
group_values.any? ? Hash.new : 0
- elsif [:average, :minimum, :maximum].include?(operation) && group_values.any?
- Hash.new
- else
- nil
+ when :average, :minimum, :maximum
+ group_values.any? ? Hash.new : nil
end
end
@@ -85,5 +56,11 @@ module ActiveRecord
def or(other)
other.spawn
end
+
+ private
+
+ def exec_queries
+ @records = [].freeze
+ end
end
end
diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb
index 510e8a6e43..60d8e95b21 100644
--- a/activerecord/lib/active_record/persistence.rb
+++ b/activerecord/lib/active_record/persistence.rb
@@ -63,10 +63,10 @@ module ActiveRecord
#
# See <tt>ActiveRecord::Inheritance#discriminate_class_for_record</tt> to see
# how this "single-table" inheritance mapping is implemented.
- def instantiate(attributes, column_types = {})
+ def instantiate(attributes, column_types = {}, &block)
klass = discriminate_class_for_record(attributes)
attributes = klass.attributes_builder.build_from_database(attributes, column_types)
- klass.allocate.init_with('attributes' => attributes, 'new_record' => false)
+ klass.allocate.init_with("attributes" => attributes, "new_record" => false, &block)
end
private
@@ -107,7 +107,7 @@ module ActiveRecord
#
# By default, save always runs validations. If any of them fail the action
# is cancelled and #save returns +false+, and the record won't be saved. However, if you supply
- # validate: false, validations are bypassed altogether. See
+ # <tt>validate: false</tt>, validations are bypassed altogether. See
# ActiveRecord::Validations for more information.
#
# By default, #save also sets the +updated_at+/+updated_on+ attributes to
@@ -134,7 +134,7 @@ module ActiveRecord
#
# By default, #save! always runs validations. If any of them fail
# ActiveRecord::RecordInvalid gets raised, and the record won't be saved. However, if you supply
- # validate: false, validations are bypassed altogether. See
+ # <tt>validate: false</tt>, validations are bypassed altogether. See
# ActiveRecord::Validations for more information.
#
# By default, #save! also sets the +updated_at+/+updated_on+ attributes to
@@ -178,10 +178,14 @@ module ActiveRecord
# and #destroy returns +false+.
# See ActiveRecord::Callbacks for further details.
def destroy
- raise ReadOnlyRecord, "#{self.class} is marked as readonly" if readonly?
+ _raise_readonly_record_error if readonly?
destroy_associations
self.class.connection.add_transaction_record(self)
- destroy_row if persisted?
+ @_trigger_destroy_callback = if persisted?
+ destroy_row > 0
+ else
+ true
+ end
@destroyed = true
freeze
end
@@ -252,7 +256,12 @@ module ActiveRecord
name = name.to_s
verify_readonly_attribute(name)
public_send("#{name}=", value)
- save(validate: false) if changed?
+
+ if has_changes_to_save?
+ save(validate: false)
+ else
+ true
+ end
end
# Updates the attributes of the model from the passed-in hash and saves the
@@ -335,7 +344,7 @@ module ActiveRecord
# record could be saved.
def increment!(attribute, by = 1)
increment(attribute, by)
- change = public_send(attribute) - (attribute_was(attribute.to_s) || 0)
+ change = public_send(attribute) - (attribute_in_database(attribute.to_s) || 0)
self.class.update_counters(id, attribute => change)
clear_attribute_change(attribute) # eww
self
@@ -439,7 +448,7 @@ module ActiveRecord
self.class.unscoped { self.class.find(id) }
end
- @attributes = fresh_object.instance_variable_get('@attributes')
+ @attributes = fresh_object.instance_variable_get("@attributes")
@new_record = false
self
end
@@ -498,7 +507,6 @@ module ActiveRecord
changes[column] = write_attribute(column, time)
end
- clear_attribute_changes(changes.keys)
primary_key = self.class.primary_key
scope = self.class.unscoped.where(primary_key => _read_attribute(primary_key))
@@ -508,12 +516,14 @@ module ActiveRecord
changes[locking_column] = increment_lock
end
+ clear_attribute_changes(changes.keys)
result = scope.update_all(changes) == 1
if !result && locking_enabled?
raise ActiveRecord::StaleObjectError.new(self, "touch")
end
+ @_trigger_update_callback = result
result
else
true
@@ -535,7 +545,7 @@ module ActiveRecord
end
def create_or_update(*args)
- raise ReadOnlyRecord, "#{self.class} is marked as readonly" if readonly?
+ _raise_readonly_record_error if readonly?
result = new_record? ? _create_record : _update_record(*args)
result != false
end
@@ -545,10 +555,13 @@ module ActiveRecord
def _update_record(attribute_names = self.attribute_names)
attributes_values = arel_attributes_with_values_for_update(attribute_names)
if attributes_values.empty?
- 0
+ rows_affected = 0
+ @_trigger_update_callback = true
else
- self.class.unscoped._update_record attributes_values, id, id_was
+ rows_affected = self.class.unscoped._update_record attributes_values, id, id_in_database
+ @_trigger_update_callback = rows_affected > 0
end
+ rows_affected
end
# Creates a record with values matching those of the instance attributes
@@ -577,5 +590,9 @@ module ActiveRecord
def belongs_to_touch_method
:touch
end
+
+ def _raise_readonly_record_error
+ raise ReadOnlyRecord, "#{self.class} is marked as readonly"
+ end
end
end
diff --git a/activerecord/lib/active_record/query_cache.rb b/activerecord/lib/active_record/query_cache.rb
index 387dd8e9bd..ec246e97bc 100644
--- a/activerecord/lib/active_record/query_cache.rb
+++ b/activerecord/lib/active_record/query_cache.rb
@@ -24,26 +24,24 @@ module ActiveRecord
end
def self.run
- connection = ActiveRecord::Base.connection
- enabled = connection.query_cache_enabled
- connection.enable_query_cache!
+ caching_pool = ActiveRecord::Base.connection_pool
+ caching_was_enabled = caching_pool.query_cache_enabled
- enabled
+ caching_pool.enable_query_cache!
+
+ [caching_pool, caching_was_enabled]
end
- def self.complete(enabled)
- ActiveRecord::Base.connection.clear_query_cache
- ActiveRecord::Base.connection.disable_query_cache! unless enabled
+ def self.complete((caching_pool, caching_was_enabled))
+ caching_pool.disable_query_cache! unless caching_was_enabled
+
+ ActiveRecord::Base.connection_handler.connection_pool_list.each do |pool|
+ pool.release_connection if pool.active_connection? && !pool.connection.transaction_open?
+ end
end
def self.install_executor_hooks(executor = ActiveSupport::Executor)
executor.register_hook(self)
-
- executor.to_complete do
- unless ActiveRecord::Base.connected? && ActiveRecord::Base.connection.transaction_open?
- ActiveRecord::Base.clear_active_connections!
- end
- end
end
end
end
diff --git a/activerecord/lib/active_record/querying.rb b/activerecord/lib/active_record/querying.rb
index 53ddd95bb0..36689f6559 100644
--- a/activerecord/lib/active_record/querying.rb
+++ b/activerecord/lib/active_record/querying.rb
@@ -9,7 +9,7 @@ module ActiveRecord
delegate :find_each, :find_in_batches, :in_batches, to: :all
delegate :select, :group, :order, :except, :reorder, :limit, :offset, :joins, :left_joins, :left_outer_joins, :or,
:where, :rewhere, :preload, :eager_load, :includes, :from, :lock, :readonly,
- :having, :create_with, :uniq, :distinct, :references, :none, :unscope, to: :all
+ :having, :create_with, :uniq, :distinct, :references, :none, :unscope, :merge, to: :all
delegate :count, :average, :minimum, :maximum, :sum, :calculate, to: :all
delegate :pluck, :ids, to: :all
@@ -35,7 +35,7 @@ module ActiveRecord
#
# Post.find_by_sql ["SELECT title FROM posts WHERE author = ? AND created > ?", author_id, start_date]
# Post.find_by_sql ["SELECT body FROM comments WHERE author = :user_id OR approved_by = :user_id", { :user_id => user_id }]
- def find_by_sql(sql, binds = [], preparable: nil)
+ def find_by_sql(sql, binds = [], preparable: nil, &block)
result_set = connection.select_all(sanitize_sql(sql), "#{name} Load", binds, preparable: preparable)
column_types = result_set.column_types.dup
columns_hash.each_key { |k| column_types.delete k }
@@ -46,8 +46,8 @@ module ActiveRecord
class_name: name
}
- message_bus.instrument('instantiation.active_record', payload) do
- result_set.map { |record| instantiate(record, column_types) }
+ message_bus.instrument("instantiation.active_record", payload) do
+ result_set.map { |record| instantiate(record, column_types, &block) }
end
end
@@ -62,8 +62,7 @@ module ActiveRecord
#
# * +sql+ - An SQL statement which should return a count query from the database, see the example above.
def count_by_sql(sql)
- sql = sanitize_conditions(sql)
- connection.select_value(sql, "#{name} Count").to_i
+ connection.select_value(sanitize_sql(sql), "#{name} Count").to_i
end
end
end
diff --git a/activerecord/lib/active_record/railtie.rb b/activerecord/lib/active_record/railtie.rb
index 98ea425d16..2701c5bca9 100644
--- a/activerecord/lib/active_record/railtie.rb
+++ b/activerecord/lib/active_record/railtie.rb
@@ -3,7 +3,7 @@ require "rails"
require "active_model/railtie"
# For now, action_controller must always be present with
-# rails, so let's make sure that it gets required before
+# Rails, so let's make sure that it gets required before
# here. This is needed for correctly setting up the middleware.
# In the future, this might become an optional require.
require "action_controller/railtie"
@@ -13,17 +13,16 @@ module ActiveRecord
class Railtie < Rails::Railtie # :nodoc:
config.active_record = ActiveSupport::OrderedOptions.new
- config.app_generators.orm :active_record, :migration => true,
- :timestamps => true
+ config.app_generators.orm :active_record, migration: true,
+ timestamps: true
config.action_dispatch.rescue_responses.merge!(
- 'ActiveRecord::RecordNotFound' => :not_found,
- 'ActiveRecord::StaleObjectError' => :conflict,
- 'ActiveRecord::RecordInvalid' => :unprocessable_entity,
- 'ActiveRecord::RecordNotSaved' => :unprocessable_entity
+ "ActiveRecord::RecordNotFound" => :not_found,
+ "ActiveRecord::StaleObjectError" => :conflict,
+ "ActiveRecord::RecordInvalid" => :unprocessable_entity,
+ "ActiveRecord::RecordNotSaved" => :unprocessable_entity
)
-
config.active_record.use_schema_cache_dump = true
config.active_record.maintain_test_schema = true
@@ -35,8 +34,8 @@ module ActiveRecord
ActiveRecord::Tasks::DatabaseTasks.database_configuration = Rails.application.config.database_configuration
if defined?(ENGINE_ROOT) && engine = Rails::Engine.find(ENGINE_ROOT)
- if engine.paths['db/migrate'].existent
- ActiveRecord::Tasks::DatabaseTasks.migrations_paths += engine.paths['db/migrate'].to_a
+ if engine.paths["db/migrate"].existent
+ ActiveRecord::Tasks::DatabaseTasks.migrations_paths += engine.paths["db/migrate"].to_a
end
end
end
@@ -83,15 +82,15 @@ module ActiveRecord
if config.active_record.delete(:use_schema_cache_dump)
config.after_initialize do |app|
ActiveSupport.on_load(:active_record) do
- filename = File.join(app.config.paths["db"].first, "schema_cache.dump")
+ filename = File.join(app.config.paths["db"].first, "schema_cache.yml")
if File.file?(filename)
- cache = Marshal.load File.binread filename
+ cache = YAML.load(File.read(filename))
if cache.version == ActiveRecord::Migrator.current_version
self.connection.schema_cache = cache
self.connection_pool.schema_cache = cache.dup
else
- warn "Ignoring db/schema_cache.dump because it has expired. The current schema version is #{ActiveRecord::Migrator.current_version}, but the one in the cache is #{cache.version}."
+ warn "Ignoring db/schema_cache.yml because it has expired. The current schema version is #{ActiveRecord::Migrator.current_version}, but the one in the cache is #{cache.version}."
end
end
end
@@ -102,14 +101,14 @@ module ActiveRecord
initializer "active_record.warn_on_records_fetched_greater_than" do
if config.active_record.warn_on_records_fetched_greater_than
ActiveSupport.on_load(:active_record) do
- require 'active_record/relation/record_fetch_warning'
+ require "active_record/relation/record_fetch_warning"
end
end
end
initializer "active_record.set_configs" do |app|
ActiveSupport.on_load(:active_record) do
- app.config.active_record.each do |k,v|
+ app.config.active_record.each do |k, v|
send "#{k}=", v
end
end
diff --git a/activerecord/lib/active_record/railties/controller_runtime.rb b/activerecord/lib/active_record/railties/controller_runtime.rb
index 8727e46cb3..8658188623 100644
--- a/activerecord/lib/active_record/railties/controller_runtime.rb
+++ b/activerecord/lib/active_record/railties/controller_runtime.rb
@@ -1,15 +1,19 @@
-require 'active_support/core_ext/module/attr_internal'
-require 'active_record/log_subscriber'
+require "active_support/core_ext/module/attr_internal"
+require "active_record/log_subscriber"
module ActiveRecord
module Railties # :nodoc:
module ControllerRuntime #:nodoc:
extend ActiveSupport::Concern
+ # TODO Change this to private once we've dropped Ruby 2.2 support.
+ # Workaround for Ruby 2.2 "private attribute?" warning.
protected
attr_internal :db_runtime
+ private
+
def process_action(action, *args)
# We also need to reset the runtime before each action
# because of queries in middleware or in cases we are streaming
@@ -19,7 +23,7 @@ module ActiveRecord
end
def cleanup_view_runtime
- if logger.info? && ActiveRecord::Base.connected?
+ if logger && logger.info? && ActiveRecord::Base.connected?
db_rt_before_render = ActiveRecord::LogSubscriber.reset_runtime
self.db_runtime = (db_runtime || 0) + db_rt_before_render
runtime = super
diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake
index fc1b62ee96..cf1c0fb423 100644
--- a/activerecord/lib/active_record/railties/databases.rake
+++ b/activerecord/lib/active_record/railties/databases.rake
@@ -1,4 +1,4 @@
-require 'active_record'
+require "active_record"
db_namespace = namespace :db do
desc "Set the environment value for the database"
@@ -7,7 +7,7 @@ db_namespace = namespace :db do
ActiveRecord::InternalMetadata[:environment] = ActiveRecord::Migrator.current_environment
end
- task :check_protected_environments => [:environment, :load_config] do
+ task check_protected_environments: [:environment, :load_config] do
ActiveRecord::Tasks::DatabaseTasks.check_protected_environments!
end
@@ -17,24 +17,24 @@ db_namespace = namespace :db do
end
namespace :create do
- task :all => :load_config do
+ task all: :load_config do
ActiveRecord::Tasks::DatabaseTasks.create_all
end
end
- desc 'Creates the database from DATABASE_URL or config/database.yml for the current RAILS_ENV (use db:create:all to create all databases in the config). Without RAILS_ENV or when RAILS_ENV is development, it defaults to creating the development and test databases.'
- task :create => [:load_config] do
+ desc "Creates the database from DATABASE_URL or config/database.yml for the current RAILS_ENV (use db:create:all to create all databases in the config). Without RAILS_ENV or when RAILS_ENV is development, it defaults to creating the development and test databases."
+ task create: [:load_config] do
ActiveRecord::Tasks::DatabaseTasks.create_current
end
namespace :drop do
- task :all => [:load_config, :check_protected_environments] do
+ task all: [:load_config, :check_protected_environments] do
ActiveRecord::Tasks::DatabaseTasks.drop_all
end
end
- desc 'Drops the database from DATABASE_URL or config/database.yml for the current RAILS_ENV (use db:drop:all to drop all databases in the config). Without RAILS_ENV or when RAILS_ENV is development, it defaults to dropping the development and test databases.'
- task :drop => [:load_config, :check_protected_environments] do
+ desc "Drops the database from DATABASE_URL or config/database.yml for the current RAILS_ENV (use db:drop:all to drop all databases in the config). Without RAILS_ENV or when RAILS_ENV is development, it defaults to dropping the development and test databases."
+ task drop: [:load_config, :check_protected_environments] do
db_namespace["drop:_unsafe"].invoke
end
@@ -43,20 +43,20 @@ db_namespace = namespace :db do
end
namespace :purge do
- task :all => [:load_config, :check_protected_environments] do
+ task all: [:load_config, :check_protected_environments] do
ActiveRecord::Tasks::DatabaseTasks.purge_all
end
end
# desc "Empty the database from DATABASE_URL or config/database.yml for the current RAILS_ENV (use db:purge:all to purge all databases in the config). Without RAILS_ENV it defaults to purging the development and test databases."
- task :purge => [:load_config, :check_protected_environments] do
+ task purge: [:load_config, :check_protected_environments] do
ActiveRecord::Tasks::DatabaseTasks.purge_current
end
desc "Migrate the database (options: VERSION=x, VERBOSE=false, SCOPE=blog)."
- task :migrate => [:environment, :load_config] do
+ task migrate: [:environment, :load_config] do
ActiveRecord::Tasks::DatabaseTasks.migrate
- db_namespace['_dump'].invoke
+ db_namespace["_dump"].invoke
end
# IMPORTANT: This task won't dump the schema if ActiveRecord::Base.dump_schema_after_migration is set to false
@@ -71,44 +71,44 @@ db_namespace = namespace :db do
end
# Allow this task to be called as many times as required. An example is the
# migrate:redo task, which calls other two internally that depend on this one.
- db_namespace['_dump'].reenable
+ db_namespace["_dump"].reenable
end
namespace :migrate do
# desc 'Rollbacks the database one migration and re migrate up (options: STEP=x, VERSION=x).'
- task :redo => [:environment, :load_config] do
- if ENV['VERSION']
- db_namespace['migrate:down'].invoke
- db_namespace['migrate:up'].invoke
+ task redo: [:environment, :load_config] do
+ if ENV["VERSION"]
+ db_namespace["migrate:down"].invoke
+ db_namespace["migrate:up"].invoke
else
- db_namespace['rollback'].invoke
- db_namespace['migrate'].invoke
+ db_namespace["rollback"].invoke
+ db_namespace["migrate"].invoke
end
end
# desc 'Resets your database using your migrations for the current environment'
- task :reset => ['db:drop', 'db:create', 'db:migrate']
+ task reset: ["db:drop", "db:create", "db:migrate"]
# desc 'Runs the "up" for a given migration VERSION.'
- task :up => [:environment, :load_config] do
- version = ENV['VERSION'] ? ENV['VERSION'].to_i : nil
- raise 'VERSION is required' unless version
+ task up: [:environment, :load_config] do
+ version = ENV["VERSION"] ? ENV["VERSION"].to_i : nil
+ raise "VERSION is required" unless version
ActiveRecord::Migrator.run(:up, ActiveRecord::Tasks::DatabaseTasks.migrations_paths, version)
- db_namespace['_dump'].invoke
+ db_namespace["_dump"].invoke
end
# desc 'Runs the "down" for a given migration VERSION.'
- task :down => [:environment, :load_config] do
- version = ENV['VERSION'] ? ENV['VERSION'].to_i : nil
- raise 'VERSION is required - To go down one migration, run db:rollback' unless version
+ task down: [:environment, :load_config] do
+ version = ENV["VERSION"] ? ENV["VERSION"].to_i : nil
+ raise "VERSION is required - To go down one migration, run db:rollback" unless version
ActiveRecord::Migrator.run(:down, ActiveRecord::Tasks::DatabaseTasks.migrations_paths, version)
- db_namespace['_dump'].invoke
+ db_namespace["_dump"].invoke
end
- desc 'Display status of migrations'
- task :status => [:environment, :load_config] do
+ desc "Display status of migrations"
+ task status: [:environment, :load_config] do
unless ActiveRecord::SchemaMigration.table_exists?
- abort 'Schema migrations table does not exist yet.'
+ abort "Schema migrations table does not exist yet."
end
db_list = ActiveRecord::SchemaMigration.normalized_versions
@@ -119,13 +119,13 @@ db_namespace = namespace :db do
version, name, scope = ActiveRecord::Migrator.parse_migration_filename(file)
version = ActiveRecord::SchemaMigration.normalize_migration_number(version)
- status = db_list.delete(version) ? 'up' : 'down'
+ status = db_list.delete(version) ? "up" : "down"
[status, version, (name + scope).humanize]
end.compact
end
db_list.map! do |version|
- ['up', version, '********** NO FILE **********']
+ ["up", version, "********** NO FILE **********"]
end
# output
puts "\ndatabase: #{ActiveRecord::Base.connection_config[:database]}\n\n"
@@ -138,93 +138,93 @@ db_namespace = namespace :db do
end
end
- desc 'Rolls the schema back to the previous version (specify steps w/ STEP=n).'
- task :rollback => [:environment, :load_config] do
- step = ENV['STEP'] ? ENV['STEP'].to_i : 1
+ desc "Rolls the schema back to the previous version (specify steps w/ STEP=n)."
+ task rollback: [:environment, :load_config] do
+ step = ENV["STEP"] ? ENV["STEP"].to_i : 1
ActiveRecord::Migrator.rollback(ActiveRecord::Tasks::DatabaseTasks.migrations_paths, step)
- db_namespace['_dump'].invoke
+ db_namespace["_dump"].invoke
end
# desc 'Pushes the schema to the next version (specify steps w/ STEP=n).'
- task :forward => [:environment, :load_config] do
- step = ENV['STEP'] ? ENV['STEP'].to_i : 1
+ task forward: [:environment, :load_config] do
+ step = ENV["STEP"] ? ENV["STEP"].to_i : 1
ActiveRecord::Migrator.forward(ActiveRecord::Tasks::DatabaseTasks.migrations_paths, step)
- db_namespace['_dump'].invoke
+ db_namespace["_dump"].invoke
end
# desc 'Drops and recreates the database from db/schema.rb for the current environment and loads the seeds.'
- task :reset => [ 'db:drop', 'db:setup' ]
+ task reset: [ "db:drop", "db:setup" ]
# desc "Retrieves the charset for the current environment's database"
- task :charset => [:environment, :load_config] do
+ task charset: [:environment, :load_config] do
puts ActiveRecord::Tasks::DatabaseTasks.charset_current
end
# desc "Retrieves the collation for the current environment's database"
- task :collation => [:environment, :load_config] do
+ task collation: [:environment, :load_config] do
begin
puts ActiveRecord::Tasks::DatabaseTasks.collation_current
rescue NoMethodError
- $stderr.puts 'Sorry, your database adapter is not supported yet. Feel free to submit a patch.'
+ $stderr.puts "Sorry, your database adapter is not supported yet. Feel free to submit a patch."
end
end
- desc 'Retrieves the current schema version number'
- task :version => [:environment, :load_config] do
+ desc "Retrieves the current schema version number"
+ task version: [:environment, :load_config] do
puts "Current version: #{ActiveRecord::Migrator.current_version}"
end
# desc "Raises an error if there are pending migrations"
- task :abort_if_pending_migrations => [:environment, :load_config] do
+ task abort_if_pending_migrations: [:environment, :load_config] do
pending_migrations = ActiveRecord::Migrator.open(ActiveRecord::Tasks::DatabaseTasks.migrations_paths).pending_migrations
if pending_migrations.any?
puts "You have #{pending_migrations.size} pending #{pending_migrations.size > 1 ? 'migrations:' : 'migration:'}"
pending_migrations.each do |pending_migration|
- puts ' %4d %s' % [pending_migration.version, pending_migration.name]
+ puts " %4d %s" % [pending_migration.version, pending_migration.name]
end
abort %{Run `rails db:migrate` to update your database then try again.}
end
end
- desc 'Creates the database, loads the schema, and initializes with the seed data (use db:reset to also drop the database first)'
- task :setup => ['db:schema:load_if_ruby', 'db:structure:load_if_sql', :seed]
+ desc "Creates the database, loads the schema, and initializes with the seed data (use db:reset to also drop the database first)"
+ task setup: ["db:schema:load_if_ruby", "db:structure:load_if_sql", :seed]
- desc 'Loads the seed data from db/seeds.rb'
+ desc "Loads the seed data from db/seeds.rb"
task :seed do
- db_namespace['abort_if_pending_migrations'].invoke
+ db_namespace["abort_if_pending_migrations"].invoke
ActiveRecord::Tasks::DatabaseTasks.load_seed
end
namespace :fixtures do
desc "Loads fixtures into the current environment's database. Load specific fixtures using FIXTURES=x,y. Load from subdirectory in test/fixtures using FIXTURES_DIR=z. Specify an alternative path (eg. spec/fixtures) using FIXTURES_PATH=spec/fixtures."
- task :load => [:environment, :load_config] do
- require 'active_record/fixtures'
+ task load: [:environment, :load_config] do
+ require "active_record/fixtures"
base_dir = ActiveRecord::Tasks::DatabaseTasks.fixtures_path
- fixtures_dir = if ENV['FIXTURES_DIR']
- File.join base_dir, ENV['FIXTURES_DIR']
- else
- base_dir
- end
+ fixtures_dir = if ENV["FIXTURES_DIR"]
+ File.join base_dir, ENV["FIXTURES_DIR"]
+ else
+ base_dir
+ end
- fixture_files = if ENV['FIXTURES']
- ENV['FIXTURES'].split(',')
- else
- # The use of String#[] here is to support namespaced fixtures
- Dir["#{fixtures_dir}/**/*.yml"].map {|f| f[(fixtures_dir.size + 1)..-5] }
- end
+ fixture_files = if ENV["FIXTURES"]
+ ENV["FIXTURES"].split(",")
+ else
+ # The use of String#[] here is to support namespaced fixtures.
+ Dir["#{fixtures_dir}/**/*.yml"].map { |f| f[(fixtures_dir.size + 1)..-5] }
+ end
ActiveRecord::FixtureSet.create_fixtures(fixtures_dir, fixture_files)
end
# desc "Search for a fixture given a LABEL or ID. Specify an alternative path (eg. spec/fixtures) using FIXTURES_PATH=spec/fixtures."
- task :identify => [:environment, :load_config] do
- require 'active_record/fixtures'
+ task identify: [:environment, :load_config] do
+ require "active_record/fixtures"
- label, id = ENV['LABEL'], ENV['ID']
- raise 'LABEL or ID required' if label.blank? && id.blank?
+ label, id = ENV["LABEL"], ENV["ID"]
+ raise "LABEL or ID required" if label.blank? && id.blank?
puts %Q(The fixture ID for "#{label}" is #{ActiveRecord::FixtureSet.identify(label)}.) if label
@@ -245,39 +245,36 @@ db_namespace = namespace :db do
end
namespace :schema do
- desc 'Creates a db/schema.rb file that is portable against any DB supported by Active Record'
- task :dump => [:environment, :load_config] do
- require 'active_record/schema_dumper'
- filename = ENV['SCHEMA'] || File.join(ActiveRecord::Tasks::DatabaseTasks.db_dir, 'schema.rb')
+ desc "Creates a db/schema.rb file that is portable against any DB supported by Active Record"
+ task dump: [:environment, :load_config] do
+ require "active_record/schema_dumper"
+ filename = ENV["SCHEMA"] || File.join(ActiveRecord::Tasks::DatabaseTasks.db_dir, "schema.rb")
File.open(filename, "w:utf-8") do |file|
ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, file)
end
- db_namespace['schema:dump'].reenable
+ db_namespace["schema:dump"].reenable
end
- desc 'Loads a schema.rb file into the database'
- task :load => [:environment, :load_config, :check_protected_environments] do
- ActiveRecord::Tasks::DatabaseTasks.load_schema_current(:ruby, ENV['SCHEMA'])
+ desc "Loads a schema.rb file into the database"
+ task load: [:environment, :load_config, :check_protected_environments] do
+ ActiveRecord::Tasks::DatabaseTasks.load_schema_current(:ruby, ENV["SCHEMA"])
end
- task :load_if_ruby => ['db:create', :environment] do
+ task load_if_ruby: ["db:create", :environment] do
db_namespace["schema:load"].invoke if ActiveRecord::Base.schema_format == :ruby
end
namespace :cache do
- desc 'Creates a db/schema_cache.dump file.'
- task :dump => [:environment, :load_config] do
- con = ActiveRecord::Base.connection
- filename = File.join(ActiveRecord::Tasks::DatabaseTasks.db_dir, "schema_cache.dump")
-
- con.schema_cache.clear!
- con.data_sources.each { |table| con.schema_cache.add(table) }
- open(filename, 'wb') { |f| f.write(Marshal.dump(con.schema_cache)) }
+ desc "Creates a db/schema_cache.yml file."
+ task dump: [:environment, :load_config] do
+ conn = ActiveRecord::Base.connection
+ filename = File.join(ActiveRecord::Tasks::DatabaseTasks.db_dir, "schema_cache.yml")
+ ActiveRecord::Tasks::DatabaseTasks.dump_schema_cache(conn, filename)
end
- desc 'Clears a db/schema_cache.dump file.'
- task :clear => [:environment, :load_config] do
- filename = File.join(ActiveRecord::Tasks::DatabaseTasks.db_dir, "schema_cache.dump")
+ desc "Clears a db/schema_cache.yml file."
+ task clear: [:environment, :load_config] do
+ filename = File.join(ActiveRecord::Tasks::DatabaseTasks.db_dir, "schema_cache.yml")
rm_f filename, verbose: false
end
end
@@ -285,9 +282,9 @@ db_namespace = namespace :db do
end
namespace :structure do
- desc 'Dumps the database structure to db/structure.sql. Specify another file with SCHEMA=db/my_structure.sql'
- task :dump => [:environment, :load_config] do
- filename = ENV['SCHEMA'] || File.join(ActiveRecord::Tasks::DatabaseTasks.db_dir, "structure.sql")
+ desc "Dumps the database structure to db/structure.sql. Specify another file with SCHEMA=db/my_structure.sql"
+ task dump: [:environment, :load_config] do
+ filename = ENV["SCHEMA"] || File.join(ActiveRecord::Tasks::DatabaseTasks.db_dir, "structure.sql")
current_config = ActiveRecord::Tasks::DatabaseTasks.current_config
ActiveRecord::Tasks::DatabaseTasks.structure_dump(current_config, filename)
@@ -298,15 +295,15 @@ db_namespace = namespace :db do
f.print "\n"
end
end
- db_namespace['structure:dump'].reenable
+ db_namespace["structure:dump"].reenable
end
desc "Recreates the databases from the structure.sql file"
- task :load => [:environment, :load_config, :check_protected_environments] do
- ActiveRecord::Tasks::DatabaseTasks.load_schema_current(:sql, ENV['SCHEMA'])
+ task load: [:environment, :load_config, :check_protected_environments] do
+ ActiveRecord::Tasks::DatabaseTasks.load_schema_current(:sql, ENV["SCHEMA"])
end
- task :load_if_sql => ['db:create', :environment] do
+ task load_if_sql: ["db:create", :environment] do
db_namespace["structure:load"].invoke if ActiveRecord::Base.schema_format == :sql
end
end
@@ -321,21 +318,21 @@ db_namespace = namespace :db do
end
# desc "Recreate the test database from the current schema"
- task :load => %w(db:test:purge) do
+ task load: %w(db:test:purge) do
case ActiveRecord::Base.schema_format
- when :ruby
- db_namespace["test:load_schema"].invoke
- when :sql
- db_namespace["test:load_structure"].invoke
+ when :ruby
+ db_namespace["test:load_schema"].invoke
+ when :sql
+ db_namespace["test:load_structure"].invoke
end
end
# desc "Recreate the test database from an existent schema.rb file"
- task :load_schema => %w(db:test:purge) do
+ task load_schema: %w(db:test:purge) do
begin
should_reconnect = ActiveRecord::Base.connection_pool.active_connection?
ActiveRecord::Schema.verbose = false
- ActiveRecord::Tasks::DatabaseTasks.load_schema ActiveRecord::Base.configurations['test'], :ruby, ENV['SCHEMA']
+ ActiveRecord::Tasks::DatabaseTasks.load_schema ActiveRecord::Base.configurations["test"], :ruby, ENV["SCHEMA"]
ensure
if should_reconnect
ActiveRecord::Base.establish_connection(ActiveRecord::Base.configurations[ActiveRecord::Tasks::DatabaseTasks.env])
@@ -344,35 +341,35 @@ db_namespace = namespace :db do
end
# desc "Recreate the test database from an existent structure.sql file"
- task :load_structure => %w(db:test:purge) do
- ActiveRecord::Tasks::DatabaseTasks.load_schema ActiveRecord::Base.configurations['test'], :sql, ENV['SCHEMA']
+ task load_structure: %w(db:test:purge) do
+ ActiveRecord::Tasks::DatabaseTasks.load_schema ActiveRecord::Base.configurations["test"], :sql, ENV["SCHEMA"]
end
# desc "Recreate the test database from a fresh schema"
- task :clone => %w(db:test:deprecated environment) do
+ task clone: %w(db:test:deprecated environment) do
case ActiveRecord::Base.schema_format
- when :ruby
- db_namespace["test:clone_schema"].invoke
- when :sql
- db_namespace["test:clone_structure"].invoke
+ when :ruby
+ db_namespace["test:clone_schema"].invoke
+ when :sql
+ db_namespace["test:clone_structure"].invoke
end
end
# desc "Recreate the test database from a fresh schema.rb file"
- task :clone_schema => %w(db:test:deprecated db:schema:dump db:test:load_schema)
+ task clone_schema: %w(db:test:deprecated db:schema:dump db:test:load_schema)
# desc "Recreate the test database from a fresh structure.sql file"
- task :clone_structure => %w(db:test:deprecated db:structure:dump db:test:load_structure)
+ task clone_structure: %w(db:test:deprecated db:structure:dump db:test:load_structure)
# desc "Empty the test database"
- task :purge => %w(environment load_config check_protected_environments) do
- ActiveRecord::Tasks::DatabaseTasks.purge ActiveRecord::Base.configurations['test']
+ task purge: %w(environment load_config check_protected_environments) do
+ ActiveRecord::Tasks::DatabaseTasks.purge ActiveRecord::Base.configurations["test"]
end
# desc 'Load the test schema'
- task :prepare => %w(environment load_config) do
+ task prepare: %w(environment load_config) do
unless ActiveRecord::Base.configurations.blank?
- db_namespace['test:load'].invoke
+ db_namespace["test:load"].invoke
end
end
end
@@ -381,13 +378,13 @@ end
namespace :railties do
namespace :install do
# desc "Copies missing migrations from Railties (e.g. engines). You can specify Railties to use with FROM=railtie1,railtie2"
- task :migrations => :'db:load_config' do
- to_load = ENV['FROM'].blank? ? :all : ENV['FROM'].split(",").map(&:strip)
+ task migrations: :'db:load_config' do
+ to_load = ENV["FROM"].blank? ? :all : ENV["FROM"].split(",").map(&:strip)
railties = {}
Rails.application.migration_railties.each do |railtie|
next unless to_load == :all || to_load.include?(railtie.railtie_name)
- if railtie.respond_to?(:paths) && (path = railtie.paths['db/migrate'].first)
+ if railtie.respond_to?(:paths) && (path = railtie.paths["db/migrate"].first)
railties[railtie.railtie_name] = path
end
end
@@ -401,7 +398,7 @@ namespace :railties do
end
ActiveRecord::Migration.copy(ActiveRecord::Tasks::DatabaseTasks.migrations_paths.first, railties,
- :on_skip => on_skip, :on_copy => on_copy)
+ on_skip: on_skip, on_copy: on_copy)
end
end
end
diff --git a/activerecord/lib/active_record/railties/jdbcmysql_error.rb b/activerecord/lib/active_record/railties/jdbcmysql_error.rb
index 6a38211bff..d7cf4df339 100644
--- a/activerecord/lib/active_record/railties/jdbcmysql_error.rb
+++ b/activerecord/lib/active_record/railties/jdbcmysql_error.rb
@@ -3,7 +3,7 @@ module ArJdbcMySQL #:nodoc:
class Error < StandardError #:nodoc:
attr_accessor :error_number, :sql_state
- def initialize msg
+ def initialize(msg)
super
@error_number = nil
@sql_state = nil
diff --git a/activerecord/lib/active_record/readonly_attributes.rb b/activerecord/lib/active_record/readonly_attributes.rb
index ce78f1756d..8ff265bdfa 100644
--- a/activerecord/lib/active_record/readonly_attributes.rb
+++ b/activerecord/lib/active_record/readonly_attributes.rb
@@ -16,7 +16,7 @@ module ActiveRecord
# Returns an array of all the attributes that have been specified as readonly.
def readonly_attributes
- self._attr_readonly
+ _attr_readonly
end
end
end
diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb
index bf398b0d40..f3e81ee1e2 100644
--- a/activerecord/lib/active_record/reflection.rb
+++ b/activerecord/lib/active_record/reflection.rb
@@ -1,5 +1,5 @@
-require 'thread'
-require 'active_support/core_ext/string/filters'
+require "thread"
+require "active_support/core_ext/string/filters"
module ActiveRecord
# = Active Record Reflection
@@ -14,18 +14,19 @@ module ActiveRecord
end
def self.create(macro, name, scope, options, ar)
- klass = case macro
- when :composed_of
- AggregateReflection
- when :has_many
- HasManyReflection
- when :has_one
- HasOneReflection
- when :belongs_to
- BelongsToReflection
- else
- raise "Unsupported Macro: #{macro}"
- end
+ klass = \
+ case macro
+ when :composed_of
+ AggregateReflection
+ when :has_many
+ HasManyReflection
+ when :has_one
+ HasOneReflection
+ when :belongs_to
+ BelongsToReflection
+ else
+ raise "Unsupported Macro: #{macro}"
+ end
reflection = klass.new(name, scope, options, ar)
options[:through] ? ThroughReflection.new(reflection) : reflection
@@ -135,8 +136,8 @@ module ActiveRecord
# BelongsToReflection
# HasAndBelongsToManyReflection
# ThroughReflection
- # PolymorphicReflection
- # RuntimeReflection
+ # PolymorphicReflection
+ # RuntimeReflection
class AbstractReflection # :nodoc:
def through_reflection?
false
@@ -281,7 +282,6 @@ module ActiveRecord
end
def autosave=(autosave)
- @automatic_inverse_of = false
@options[:autosave] = autosave
parent_reflection = self.parent_reflection
if parent_reflection
@@ -311,13 +311,16 @@ module ActiveRecord
active_record == other_aggregation.active_record
end
+ def scope_for(klass)
+ scope ? klass.unscoped.instance_exec(nil, &scope) : klass.unscoped
+ end
+
private
def derive_class_name
name.to_s.camelize
end
end
-
# Holds all the meta-data about an aggregation as it was specified in the
# Active Record class.
class AggregateReflection < MacroReflection #:nodoc:
@@ -394,6 +397,10 @@ module ActiveRecord
options[:primary_key] || primary_key(klass || self.klass)
end
+ def association_primary_key_type
+ klass.type_for_attribute(association_primary_key)
+ end
+
def active_record_primary_key
@active_record_primary_key ||= options[:primary_key] || primary_key(active_record)
end
@@ -533,14 +540,10 @@ module ActiveRecord
# Attempts to find the inverse association name automatically.
# If it cannot find a suitable inverse association name, it returns
- # nil.
+ # +nil+.
def inverse_name
options.fetch(:inverse_of) do
- if @automatic_inverse_of == false
- nil
- else
- @automatic_inverse_of ||= automatic_inverse_of
- end
+ @automatic_inverse_of ||= automatic_inverse_of
end
end
@@ -700,11 +703,11 @@ module ActiveRecord
class ThroughReflection < AbstractReflection #:nodoc:
attr_reader :delegate_reflection
delegate :foreign_key, :foreign_type, :association_foreign_key,
- :active_record_primary_key, :type, :to => :source_reflection
+ :active_record_primary_key, :type, to: :source_reflection
def initialize(delegate_reflection)
@delegate_reflection = delegate_reflection
- @klass = delegate_reflection.options[:anonymous_class]
+ @klass = delegate_reflection.options[:anonymous_class]
@source_reflection_name = delegate_reflection.options[:source]
end
@@ -847,6 +850,10 @@ module ActiveRecord
actual_source_reflection.options[:primary_key] || primary_key(klass || self.klass)
end
+ def association_primary_key_type
+ klass.type_for_attribute(association_primary_key)
+ end
+
# Gets an array of possible <tt>:through</tt> source reflection names in both singular and plural form.
#
# class Post < ActiveRecord::Base
@@ -871,15 +878,13 @@ module ActiveRecord
}
if names.length > 1
- example_options = options.dup
- example_options[:source] = source_reflection_names.first
- ActiveSupport::Deprecation.warn \
- "Ambiguous source reflection for through association. Please " \
- "specify a :source directive on your declaration like:\n" \
- "\n" \
- " class #{active_record.name} < ActiveRecord::Base\n" \
- " #{macro} :#{name}, #{example_options}\n" \
- " end"
+ raise AmbiguousSourceReflectionForThroughAssociation.new(
+ active_record.name,
+ macro,
+ name,
+ options,
+ source_reflection_names
+ )
end
@source_reflection_name = names.first
@@ -978,10 +983,9 @@ module ActiveRecord
public_instance_methods
delegate(*delegate_methods, to: :delegate_reflection)
-
end
- class PolymorphicReflection < ThroughReflection # :nodoc:
+ class PolymorphicReflection < AbstractReflection # :nodoc:
def initialize(reflection, previous_reflection)
@reflection = reflection
@previous_reflection = previous_reflection
diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb
index 93baa882ad..ccd75ec5d2 100644
--- a/activerecord/lib/active_record/relation.rb
+++ b/activerecord/lib/active_record/relation.rb
@@ -1,5 +1,3 @@
-require "arel/collectors/bind"
-
module ActiveRecord
# = Active Record \Relation
class Relation
@@ -31,9 +29,7 @@ module ActiveRecord
end
def initialize_copy(other)
- # This method is a hot spot, so for now, use Hash[] to dup the hash.
- # https://bugs.ruby-lang.org/issues/7166
- @values = Hash[@values]
+ @values = @values.dup
reset
end
@@ -64,7 +60,7 @@ module ActiveRecord
@klass.connection.insert(
im,
- 'SQL',
+ "SQL",
primary_key || false,
primary_key_value,
nil,
@@ -88,7 +84,7 @@ module ActiveRecord
@klass.connection.update(
um,
- 'SQL',
+ "SQL",
bvs,
)
end
@@ -247,7 +243,6 @@ module ActiveRecord
# Please see further details in the
# {Active Record Query Interface guide}[http://guides.rubyonrails.org/active_record_querying.html#running-explain].
def explain
- #TODO: Fix for binds.
exec_explain(collecting_queries_for_explain { exec_queries })
end
@@ -367,6 +362,9 @@ module ActiveRecord
#
# # Update all books that match conditions, but limit it to 5 ordered by date
# Book.where('title LIKE ?', '%Rails%').order(:created_at).limit(5).update_all(author: 'David')
+ #
+ # # Update all invoices and set the number column to its id value.
+ # Invoice.update_all('number = id')
def update_all(updates)
raise ArgumentError, "Empty list of attributes to change" if updates.blank?
@@ -375,7 +373,7 @@ module ActiveRecord
stmt.set Arel.sql(@klass.send(:sanitize_sql_for_assignment, updates))
stmt.table(table)
- if joins_values.any?
+ if has_join_values?
@klass.connection.join_to_update(stmt, arel, arel_attribute(primary_key))
else
stmt.key = arel_attribute(primary_key)
@@ -384,7 +382,7 @@ module ActiveRecord
stmt.wheres = arel.constraints
end
- @klass.connection.update stmt, 'SQL', bound_attributes
+ @klass.connection.update stmt, "SQL", bound_attributes
end
# Updates an object (or multiple objects) and saves it to the database, if validations pass.
@@ -448,16 +446,8 @@ module ActiveRecord
# ==== Examples
#
# Person.where(age: 0..18).destroy_all
- def destroy_all(conditions = nil)
- if conditions
- ActiveSupport::Deprecation.warn(<<-MESSAGE.squish)
- Passing conditions to destroy_all is deprecated and will be removed in Rails 5.1.
- To achieve the same use where(conditions).destroy_all.
- MESSAGE
- where(conditions).destroy_all
- else
- records.each(&:destroy).tap { reset }
- end
+ def destroy_all
+ records.each(&:destroy).tap { reset }
end
# Destroy an object (or multiple objects) that has the given id. The object is instantiated first,
@@ -505,41 +495,28 @@ module ActiveRecord
#
# Post.limit(100).delete_all
# # => ActiveRecord::ActiveRecordError: delete_all doesn't support limit
- def delete_all(conditions = nil)
- invalid_methods = INVALID_METHODS_FOR_DELETE_ALL.select { |method|
- if MULTI_VALUE_METHODS.include?(method)
- send("#{method}_values").any?
- elsif SINGLE_VALUE_METHODS.include?(method)
- send("#{method}_value")
- elsif CLAUSE_METHODS.include?(method)
- send("#{method}_clause").any?
- end
- }
+ def delete_all
+ invalid_methods = INVALID_METHODS_FOR_DELETE_ALL.select do |method|
+ value = get_value(method)
+ SINGLE_VALUE_METHODS.include?(method) ? value : value.any?
+ end
if invalid_methods.any?
raise ActiveRecordError.new("delete_all doesn't support #{invalid_methods.join(', ')}")
end
- if conditions
- ActiveSupport::Deprecation.warn(<<-MESSAGE.squish)
- Passing conditions to delete_all is deprecated and will be removed in Rails 5.1.
- To achieve the same use where(conditions).delete_all.
- MESSAGE
- where(conditions).delete_all
- else
- stmt = Arel::DeleteManager.new
- stmt.from(table)
+ stmt = Arel::DeleteManager.new
+ stmt.from(table)
- if joins_values.any?
- @klass.connection.join_to_delete(stmt, arel, arel_attribute(primary_key))
- else
- stmt.wheres = arel.constraints
- end
+ if has_join_values?
+ @klass.connection.join_to_delete(stmt, arel, arel_attribute(primary_key))
+ else
+ stmt.wheres = arel.constraints
+ end
- affected = @klass.connection.delete(stmt, 'SQL', bound_attributes)
+ affected = @klass.connection.delete(stmt, "SQL", bound_attributes)
- reset
- affected
- end
+ reset
+ affected
end
# Deletes the row with a primary key matching the +id+ argument, using a
@@ -571,8 +548,8 @@ module ActiveRecord
# return value is the relation itself, not the records.
#
# Post.where(published: true).load # => #<ActiveRecord::Relation>
- def load
- exec_queries unless loaded?
+ def load(&block)
+ exec_queries(&block) unless loaded?
self
end
@@ -597,19 +574,16 @@ module ActiveRecord
# # => SELECT "users".* FROM "users" WHERE "users"."name" = 'Oscar'
def to_sql
@to_sql ||= begin
- relation = self
- connection = klass.connection
- visitor = connection.visitor
+ relation = self
if eager_loading?
find_with_associations { |rel| relation = rel }
end
- binds = relation.bound_attributes
- binds = connection.prepare_binds_for_database(binds)
- binds.map! { |value| connection.quote(value) }
- collect = visitor.accept(relation.arel.ast, Arel::Collectors::Bind.new)
- collect.substitute_binds(binds).join
+ conn = klass.connection
+ conn.unprepared_statement {
+ conn.to_sql(relation.arel, relation.bound_attributes)
+ }
end
end
@@ -640,15 +614,6 @@ module ActiveRecord
includes_values & joins_values
end
- # {#uniq}[rdoc-ref:QueryMethods#uniq] and
- # {#uniq!}[rdoc-ref:QueryMethods#uniq!] are silently deprecated.
- # #uniq_value delegates to #distinct_value to maintain backwards compatibility.
- # Use #distinct_value instead.
- def uniq_value
- distinct_value
- end
- deprecate uniq_value: :distinct_value
-
# Compares two relations for equality.
def ==(other)
case other
@@ -662,7 +627,7 @@ module ActiveRecord
end
def pretty_print(q)
- q.pp(self.records)
+ q.pp(records)
end
# Returns true if relation is blank.
@@ -671,12 +636,12 @@ module ActiveRecord
end
def values
- Hash[@values]
+ @values.dup
end
def inspect
entries = records.take([limit_value, 11].compact.min).map!(&:inspect)
- entries[10] = '...' if entries.size == 11
+ entries[10] = "..." if entries.size == 11
"#<#{self.class.name} [#{entries.join(', ')}]>"
end
@@ -690,48 +655,53 @@ module ActiveRecord
private
- def exec_queries
- @records = eager_loading? ? find_with_associations.freeze : @klass.find_by_sql(arel, bound_attributes).freeze
-
- preload = preload_values
- preload += includes_values unless eager_loading?
- preloader = build_preloader
- preload.each do |associations|
- preloader.preload @records, associations
+ def has_join_values?
+ joins_values.any? || left_outer_joins_values.any?
end
- @records.each(&:readonly!) if readonly_value
+ def exec_queries(&block)
+ @records = eager_loading? ? find_with_associations.freeze : @klass.find_by_sql(arel, bound_attributes, &block).freeze
- @loaded = true
- @records
- end
+ preload = preload_values
+ preload += includes_values unless eager_loading?
+ preloader = nil
+ preload.each do |associations|
+ preloader ||= build_preloader
+ preloader.preload @records, associations
+ end
- def build_preloader
- ActiveRecord::Associations::Preloader.new
- end
+ @records.each(&:readonly!) if readonly_value
- def references_eager_loaded_tables?
- joined_tables = arel.join_sources.map do |join|
- if join.is_a?(Arel::Nodes::StringJoin)
- tables_in_string(join.left)
- else
- [join.left.table_name, join.left.table_alias]
- end
+ @loaded = true
+ @records
end
- joined_tables += [table.name, table.table_alias]
+ def build_preloader
+ ActiveRecord::Associations::Preloader.new
+ end
- # always convert table names to downcase as in Oracle quoted table names are in uppercase
- joined_tables = joined_tables.flatten.compact.map(&:downcase).uniq
+ def references_eager_loaded_tables?
+ joined_tables = arel.join_sources.map do |join|
+ if join.is_a?(Arel::Nodes::StringJoin)
+ tables_in_string(join.left)
+ else
+ [join.left.table_name, join.left.table_alias]
+ end
+ end
- (references_values - joined_tables).any?
- end
+ joined_tables += [table.name, table.table_alias]
- def tables_in_string(string)
- return [] if string.blank?
- # always convert table names to downcase as in Oracle quoted table names are in uppercase
- # ignore raw_sql_ that is used by Oracle adapter as alias for limit/offset subqueries
- string.scan(/([a-zA-Z_][.\w]+).?\./).flatten.map(&:downcase).uniq - ['raw_sql_']
- end
+ # always convert table names to downcase as in Oracle quoted table names are in uppercase
+ joined_tables = joined_tables.flatten.compact.map(&:downcase).uniq
+
+ (references_values - joined_tables).any?
+ end
+
+ def tables_in_string(string)
+ return [] if string.blank?
+ # always convert table names to downcase as in Oracle quoted table names are in uppercase
+ # ignore raw_sql_ that is used by Oracle adapter as alias for limit/offset subqueries
+ string.scan(/([a-zA-Z_][.\w]+).?\./).flatten.map(&:downcase).uniq - ["raw_sql_"]
+ end
end
end
diff --git a/activerecord/lib/active_record/relation/batches.rb b/activerecord/lib/active_record/relation/batches.rb
index 3639625722..4b2987ac6d 100644
--- a/activerecord/lib/active_record/relation/batches.rb
+++ b/activerecord/lib/active_record/relation/batches.rb
@@ -2,7 +2,7 @@ require "active_record/relation/batches/batch_enumerator"
module ActiveRecord
module Batches
- ORDER_OR_LIMIT_IGNORED_MESSAGE = "Scoped order and limit are ignored, it's forced to be batch order and batch size."
+ ORDER_IGNORE_MESSAGE = "Scoped order is ignored, it's forced to be batch order."
# Looping through a collection of records from the database
# (using the Scoping::Named::ClassMethods.all method, for example)
@@ -34,15 +34,19 @@ module ActiveRecord
# * <tt>:start</tt> - Specifies the primary key value to start from, inclusive of the value.
# * <tt>:finish</tt> - Specifies the primary key value to end at, inclusive of the value.
# * <tt>:error_on_ignore</tt> - Overrides the application config to specify if an error should be raised when
- # the order and limit have to be ignored due to batching.
+ # an order is present in the relation.
#
- # This is especially useful if you want multiple workers dealing with
- # the same processing queue. You can make worker 1 handle all the records
- # between id 0 and 10,000 and worker 2 handle from 10,000 and beyond
- # (by setting the +:start+ and +:finish+ option on each worker).
+ # Limits are honored, and if present there is no requirement for the batch
+ # size, it can be less than, equal, or greater than the limit.
#
- # # Let's process for a batch of 2000 records, skipping the first 2000 rows
- # Person.find_each(start: 2000, batch_size: 2000) do |person|
+ # The options +start+ and +finish+ are especially useful if you want
+ # multiple workers dealing with the same processing queue. You can make
+ # worker 1 handle all the records between id 1 and 9999 and worker 2
+ # handle from 10000 and beyond by setting the +:start+ and +:finish+
+ # option on each worker.
+ #
+ # # Let's process from record 10_000 on.
+ # Person.find_each(start: 10_000) do |person|
# person.party_all_night!
# end
#
@@ -51,8 +55,8 @@ module ActiveRecord
# work. This also means that this method only works when the primary key is
# orderable (e.g. an integer or string).
#
- # NOTE: You can't set the limit either, that's used to control
- # the batch sizes.
+ # NOTE: By its nature, batch processing is subject to race conditions if
+ # other processes are modifying the database.
def find_each(start: nil, finish: nil, batch_size: 1000, error_on_ignore: nil)
if block_given?
find_in_batches(start: start, finish: finish, batch_size: batch_size, error_on_ignore: error_on_ignore) do |records|
@@ -89,15 +93,19 @@ module ActiveRecord
# * <tt>:start</tt> - Specifies the primary key value to start from, inclusive of the value.
# * <tt>:finish</tt> - Specifies the primary key value to end at, inclusive of the value.
# * <tt>:error_on_ignore</tt> - Overrides the application config to specify if an error should be raised when
- # the order and limit have to be ignored due to batching.
+ # an order is present in the relation.
+ #
+ # Limits are honored, and if present there is no requirement for the batch
+ # size, it can be less than, equal, or greater than the limit.
#
- # This is especially useful if you want multiple workers dealing with
- # the same processing queue. You can make worker 1 handle all the records
- # between id 0 and 10,000 and worker 2 handle from 10,000 and beyond
- # (by setting the +:start+ and +:finish+ option on each worker).
+ # The options +start+ and +finish+ are especially useful if you want
+ # multiple workers dealing with the same processing queue. You can make
+ # worker 1 handle all the records between id 1 and 9999 and worker 2
+ # handle from 10000 and beyond by setting the +:start+ and +:finish+
+ # option on each worker.
#
- # # Let's process the next 2000 records
- # Person.find_in_batches(start: 2000, batch_size: 2000) do |group|
+ # # Let's process from record 10_000 on.
+ # Person.find_in_batches(start: 10_000) do |group|
# group.each { |person| person.party_all_night! }
# end
#
@@ -106,8 +114,8 @@ module ActiveRecord
# work. This also means that this method only works when the primary key is
# orderable (e.g. an integer or string).
#
- # NOTE: You can't set the limit either, that's used to control
- # the batch sizes.
+ # NOTE: By its nature, batch processing is subject to race conditions if
+ # other processes are modifying the database.
def find_in_batches(start: nil, finish: nil, batch_size: 1000, error_on_ignore: nil)
relation = self
unless block_given?
@@ -149,17 +157,19 @@ module ActiveRecord
# * <tt>:start</tt> - Specifies the primary key value to start from, inclusive of the value.
# * <tt>:finish</tt> - Specifies the primary key value to end at, inclusive of the value.
# * <tt>:error_on_ignore</tt> - Overrides the application config to specify if an error should be raised when
- # the order and limit have to be ignored due to batching.
+ # an order is present in the relation.
#
- # This is especially useful if you want to work with the
- # ActiveRecord::Relation object instead of the array of records, or if
- # you want multiple workers dealing with the same processing queue. You can
- # make worker 1 handle all the records between id 0 and 10,000 and worker 2
- # handle from 10,000 and beyond (by setting the +:start+ and +:finish+
- # option on each worker).
+ # Limits are honored, and if present there is no requirement for the batch
+ # size, it can be less than, equal, or greater than the limit.
#
- # # Let's process the next 2000 records
- # Person.in_batches(of: 2000, start: 2000).update_all(awesome: true)
+ # The options +start+ and +finish+ are especially useful if you want
+ # multiple workers dealing with the same processing queue. You can make
+ # worker 1 handle all the records between id 1 and 9999 and worker 2
+ # handle from 10000 and beyond by setting the +:start+ and +:finish+
+ # option on each worker.
+ #
+ # # Let's process from record 10_000 on.
+ # Person.in_batches(start: 10_000).update_all(awesome: true)
#
# An example of calling where query method on the relation:
#
@@ -179,19 +189,25 @@ module ActiveRecord
# consistent. Therefore the primary key must be orderable, e.g an integer
# or a string.
#
- # NOTE: You can't set the limit either, that's used to control the batch
- # sizes.
+ # NOTE: By its nature, batch processing is subject to race conditions if
+ # other processes are modifying the database.
def in_batches(of: 1000, start: nil, finish: nil, load: false, error_on_ignore: nil)
relation = self
unless block_given?
return BatchEnumerator.new(of: of, start: start, finish: finish, relation: self)
end
- if arel.orders.present? || arel.taken.present?
- act_on_order_or_limit_ignored(error_on_ignore)
+ if arel.orders.present?
+ act_on_ignored_order(error_on_ignore)
+ end
+
+ batch_limit = of
+ if limit_value
+ remaining = limit_value
+ batch_limit = remaining if remaining < batch_limit
end
- relation = relation.reorder(batch_order).limit(of)
+ relation = relation.reorder(batch_order).limit(batch_limit)
relation = apply_limits(relation, start, finish)
batch_relation = relation
@@ -199,11 +215,11 @@ module ActiveRecord
if load
records = batch_relation.records
ids = records.map(&:id)
- yielded_relation = self.where(primary_key => ids)
+ yielded_relation = where(primary_key => ids)
yielded_relation.load_records(records)
else
ids = batch_relation.pluck(primary_key)
- yielded_relation = self.where(primary_key => ids)
+ yielded_relation = where(primary_key => ids)
end
break if ids.empty?
@@ -213,31 +229,44 @@ module ActiveRecord
yield yielded_relation
- break if ids.length < of
+ break if ids.length < batch_limit
+
+ if limit_value
+ remaining -= ids.length
+
+ if remaining == 0
+ # Saves a useless iteration when the limit is a multiple of the
+ # batch size.
+ break
+ elsif remaining < batch_limit
+ relation = relation.limit(remaining)
+ end
+ end
+
batch_relation = relation.where(arel_attribute(primary_key).gt(primary_key_offset))
end
end
private
- def apply_limits(relation, start, finish)
- relation = relation.where(arel_attribute(primary_key).gteq(start)) if start
- relation = relation.where(arel_attribute(primary_key).lteq(finish)) if finish
- relation
- end
+ def apply_limits(relation, start, finish)
+ relation = relation.where(arel_attribute(primary_key).gteq(start)) if start
+ relation = relation.where(arel_attribute(primary_key).lteq(finish)) if finish
+ relation
+ end
- def batch_order
- "#{quoted_table_name}.#{quoted_primary_key} ASC"
- end
+ def batch_order
+ "#{quoted_table_name}.#{quoted_primary_key} ASC"
+ end
- def act_on_order_or_limit_ignored(error_on_ignore)
- raise_error = (error_on_ignore.nil? ? self.klass.error_on_ignored_order_or_limit : error_on_ignore)
+ def act_on_ignored_order(error_on_ignore)
+ raise_error = (error_on_ignore.nil? ? self.klass.error_on_ignored_order : error_on_ignore)
- if raise_error
- raise ArgumentError.new(ORDER_OR_LIMIT_IGNORED_MESSAGE)
- elsif logger
- logger.warn(ORDER_OR_LIMIT_IGNORED_MESSAGE)
+ if raise_error
+ raise ArgumentError.new(ORDER_IGNORE_MESSAGE)
+ elsif logger
+ logger.warn(ORDER_IGNORE_MESSAGE)
+ end
end
- end
end
end
diff --git a/activerecord/lib/active_record/relation/batches/batch_enumerator.rb b/activerecord/lib/active_record/relation/batches/batch_enumerator.rb
index 333b3a63cf..3555779ec2 100644
--- a/activerecord/lib/active_record/relation/batches/batch_enumerator.rb
+++ b/activerecord/lib/active_record/relation/batches/batch_enumerator.rb
@@ -7,7 +7,7 @@ module ActiveRecord
@of = of
@relation = relation
@start = start
- @finish = finish
+ @finish = finish
end
# Looping through a collection of records from the database (using the
diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb
index d6d92b8607..827688a663 100644
--- a/activerecord/lib/active_record/relation/calculations.rb
+++ b/activerecord/lib/active_record/relation/calculations.rb
@@ -112,12 +112,11 @@ module ActiveRecord
# ...
# end
def calculate(operation, column_name)
- if column_name.is_a?(Symbol) && attribute_alias?(column_name)
- column_name = attribute_alias(column_name)
- end
-
if has_include?(column_name)
- construct_relation_for_association_calculations.calculate(operation, column_name)
+ relation = construct_relation_for_association_calculations
+ relation = relation.distinct if operation.to_s.downcase == "count"
+
+ relation.calculate(operation, column_name)
else
perform_calculation(operation, column_name)
end
@@ -160,7 +159,7 @@ module ActiveRecord
#
def pluck(*column_names)
if loaded? && (column_names.map(&:to_s) - @klass.attribute_names - @klass.attribute_aliases.keys).empty?
- return @records.pluck(*column_names)
+ return records.pluck(*column_names)
end
if has_include?(column_names.first)
@@ -185,201 +184,196 @@ module ActiveRecord
private
- def has_include?(column_name)
- eager_loading? || (includes_values.present? && column_name && column_name != :all)
- end
-
- def perform_calculation(operation, column_name)
- operation = operation.to_s.downcase
+ def has_include?(column_name)
+ eager_loading? || (includes_values.present? && column_name && column_name != :all)
+ end
- # If #count is used with #distinct (i.e. `relation.distinct.count`) it is
- # considered distinct.
- distinct = self.distinct_value
+ def perform_calculation(operation, column_name)
+ operation = operation.to_s.downcase
- if operation == "count"
- column_name ||= select_for_count
+ # If #count is used with #distinct (i.e. `relation.distinct.count`) it is
+ # considered distinct.
+ distinct = self.distinct_value
- unless arel.ast.grep(Arel::Nodes::OuterJoin).empty?
- distinct = true
+ if operation == "count"
+ column_name ||= select_for_count
+ column_name = primary_key if column_name == :all && distinct
+ distinct = nil if column_name =~ /\s*DISTINCT[\s(]+/i
end
- column_name = primary_key if column_name == :all && distinct
- distinct = nil if column_name =~ /\s*DISTINCT[\s(]+/i
- end
-
- if group_values.any?
- execute_grouped_calculation(operation, column_name, distinct)
- else
- execute_simple_calculation(operation, column_name, distinct)
+ if group_values.any?
+ execute_grouped_calculation(operation, column_name, distinct)
+ else
+ execute_simple_calculation(operation, column_name, distinct)
+ end
end
- end
- def aggregate_column(column_name)
- return column_name if Arel::Expressions === column_name
+ def aggregate_column(column_name)
+ return column_name if Arel::Expressions === column_name
- if @klass.column_names.include?(column_name.to_s)
- Arel::Attribute.new(@klass.unscoped.table, column_name)
- else
- Arel.sql(column_name == :all ? "*" : column_name.to_s)
+ if @klass.has_attribute?(column_name.to_s) || @klass.attribute_alias?(column_name.to_s)
+ @klass.arel_attribute(column_name)
+ else
+ Arel.sql(column_name == :all ? "*" : column_name.to_s)
+ end
end
- end
-
- def operation_over_aggregate_column(column, operation, distinct)
- operation == 'count' ? column.count(distinct) : column.send(operation)
- end
- def execute_simple_calculation(operation, column_name, distinct) #:nodoc:
- # PostgreSQL doesn't like ORDER BY when there are no GROUP BY
- relation = unscope(:order)
-
- column_alias = column_name
+ def operation_over_aggregate_column(column, operation, distinct)
+ operation == "count" ? column.count(distinct) : column.send(operation)
+ end
- if operation == "count" && (relation.limit_value || relation.offset_value)
- # Shortcut when limit is zero.
- return 0 if relation.limit_value == 0
+ def execute_simple_calculation(operation, column_name, distinct) #:nodoc:
+ column_alias = column_name
- query_builder = build_count_subquery(relation, column_name, distinct)
- else
- column = aggregate_column(column_name)
+ if operation == "count" && (limit_value || offset_value)
+ # Shortcut when limit is zero.
+ return 0 if limit_value == 0
- select_value = operation_over_aggregate_column(column, operation, distinct)
+ query_builder = build_count_subquery(spawn, column_name, distinct)
+ else
+ # PostgreSQL doesn't like ORDER BY when there are no GROUP BY
+ relation = unscope(:order)
- column_alias = select_value.alias
- column_alias ||= @klass.connection.column_name_for_operation(operation, select_value)
- relation.select_values = [select_value]
+ column = aggregate_column(column_name)
- query_builder = relation.arel
- end
+ select_value = operation_over_aggregate_column(column, operation, distinct)
- result = @klass.connection.select_all(query_builder, nil, bound_attributes)
- row = result.first
- value = row && row.values.first
- column = result.column_types.fetch(column_alias) do
- type_for(column_name)
- end
+ column_alias = select_value.alias
+ column_alias ||= @klass.connection.column_name_for_operation(operation, select_value)
+ relation.select_values = [select_value]
- type_cast_calculated_value(value, column, operation)
- end
+ query_builder = relation.arel
+ end
- def execute_grouped_calculation(operation, column_name, distinct) #:nodoc:
- group_attrs = group_values
+ result = @klass.connection.select_all(query_builder, nil, bound_attributes)
+ row = result.first
+ value = row && row.values.first
+ type = result.column_types.fetch(column_alias) do
+ type_for(column_name)
+ end
- if group_attrs.first.respond_to?(:to_sym)
- association = @klass._reflect_on_association(group_attrs.first)
- associated = group_attrs.size == 1 && association && association.belongs_to? # only count belongs_to associations
- group_fields = Array(associated ? association.foreign_key : group_attrs)
- else
- group_fields = group_attrs
+ type_cast_calculated_value(value, type, operation)
end
- group_fields = arel_columns(group_fields)
-
- group_aliases = group_fields.map { |field| column_alias_for(field) }
- group_columns = group_aliases.zip(group_fields)
- if operation == 'count' && column_name == :all
- aggregate_alias = 'count_all'
- else
- aggregate_alias = column_alias_for([operation, column_name].join(' '))
- end
+ def execute_grouped_calculation(operation, column_name, distinct) #:nodoc:
+ group_attrs = group_values
- select_values = [
- operation_over_aggregate_column(
- aggregate_column(column_name),
- operation,
- distinct).as(aggregate_alias)
- ]
- select_values += select_values unless having_clause.empty?
-
- select_values.concat group_columns.map { |aliaz, field|
- if field.respond_to?(:as)
- field.as(aliaz)
+ if group_attrs.first.respond_to?(:to_sym)
+ association = @klass._reflect_on_association(group_attrs.first)
+ associated = group_attrs.size == 1 && association && association.belongs_to? # only count belongs_to associations
+ group_fields = Array(associated ? association.foreign_key : group_attrs)
else
- "#{field} AS #{aliaz}"
+ group_fields = group_attrs
end
- }
+ group_fields = arel_columns(group_fields)
- relation = except(:group)
- relation.group_values = group_fields
- relation.select_values = select_values
+ group_aliases = group_fields.map { |field| column_alias_for(field) }
+ group_columns = group_aliases.zip(group_fields)
- calculated_data = @klass.connection.select_all(relation, nil, relation.bound_attributes)
-
- if association
- key_ids = calculated_data.collect { |row| row[group_aliases.first] }
- key_records = association.klass.base_class.where(association.klass.base_class.primary_key => key_ids)
- key_records = Hash[key_records.map { |r| [r.id, r] }]
- end
+ if operation == "count" && column_name == :all
+ aggregate_alias = "count_all"
+ else
+ aggregate_alias = column_alias_for([operation, column_name].join(" "))
+ end
- Hash[calculated_data.map do |row|
- key = group_columns.map { |aliaz, col_name|
- column = calculated_data.column_types.fetch(aliaz) do
- type_for(col_name)
+ select_values = [
+ operation_over_aggregate_column(
+ aggregate_column(column_name),
+ operation,
+ distinct).as(aggregate_alias)
+ ]
+ select_values += select_values unless having_clause.empty?
+
+ select_values.concat group_columns.map { |aliaz, field|
+ if field.respond_to?(:as)
+ field.as(aliaz)
+ else
+ "#{field} AS #{aliaz}"
end
- type_cast_calculated_value(row[aliaz], column)
}
- key = key.first if key.size == 1
- key = key_records[key] if associated
- column_type = calculated_data.column_types.fetch(aggregate_alias) { type_for(column_name) }
- [key, type_cast_calculated_value(row[aggregate_alias], column_type, operation)]
- end]
- end
+ relation = except(:group)
+ relation.group_values = group_fields
+ relation.select_values = select_values
- # Converts the given keys to the value that the database adapter returns as
- # a usable column name:
- #
- # column_alias_for("users.id") # => "users_id"
- # column_alias_for("sum(id)") # => "sum_id"
- # column_alias_for("count(distinct users.id)") # => "count_distinct_users_id"
- # column_alias_for("count(*)") # => "count_all"
- def column_alias_for(keys)
- if keys.respond_to? :name
- keys = "#{keys.relation.name}.#{keys.name}"
+ calculated_data = @klass.connection.select_all(relation, nil, relation.bound_attributes)
+
+ if association
+ key_ids = calculated_data.collect { |row| row[group_aliases.first] }
+ key_records = association.klass.base_class.where(association.klass.base_class.primary_key => key_ids)
+ key_records = Hash[key_records.map { |r| [r.id, r] }]
+ end
+
+ Hash[calculated_data.map do |row|
+ key = group_columns.map { |aliaz, col_name|
+ type = type_for(col_name) do
+ calculated_data.column_types.fetch(aliaz, Type.default_value)
+ end
+ type_cast_calculated_value(row[aliaz], type)
+ }
+ key = key.first if key.size == 1
+ key = key_records[key] if associated
+
+ type = calculated_data.column_types.fetch(aggregate_alias) { type_for(column_name) }
+ [key, type_cast_calculated_value(row[aggregate_alias], type, operation)]
+ end]
end
- table_name = keys.to_s.downcase
- table_name.gsub!(/\*/, 'all')
- table_name.gsub!(/\W+/, ' ')
- table_name.strip!
- table_name.gsub!(/ +/, '_')
+ # Converts the given keys to the value that the database adapter returns as
+ # a usable column name:
+ #
+ # column_alias_for("users.id") # => "users_id"
+ # column_alias_for("sum(id)") # => "sum_id"
+ # column_alias_for("count(distinct users.id)") # => "count_distinct_users_id"
+ # column_alias_for("count(*)") # => "count_all"
+ def column_alias_for(keys)
+ if keys.respond_to? :name
+ keys = "#{keys.relation.name}.#{keys.name}"
+ end
- @klass.connection.table_alias_for(table_name)
- end
+ table_name = keys.to_s.downcase
+ table_name.gsub!(/\*/, "all")
+ table_name.gsub!(/\W+/, " ")
+ table_name.strip!
+ table_name.gsub!(/ +/, "_")
- def type_for(field)
- field_name = field.respond_to?(:name) ? field.name.to_s : field.to_s.split('.').last
- @klass.type_for_attribute(field_name)
- end
+ @klass.connection.table_alias_for(table_name)
+ end
- def type_cast_calculated_value(value, type, operation = nil)
- case operation
- when 'count' then value.to_i
- when 'sum' then type.deserialize(value || 0)
- when 'average' then value.respond_to?(:to_d) ? value.to_d : value
+ def type_for(field, &block)
+ field_name = field.respond_to?(:name) ? field.name.to_s : field.to_s.split(".").last
+ @klass.type_for_attribute(field_name, &block)
+ end
+
+ def type_cast_calculated_value(value, type, operation = nil)
+ case operation
+ when "count" then value.to_i
+ when "sum" then type.deserialize(value || 0)
+ when "average" then value.respond_to?(:to_d) ? value.to_d : value
else type.deserialize(value)
+ end
end
- end
- def select_for_count
- if select_values.present?
- return select_values.first if select_values.one?
- select_values.join(", ")
- else
- :all
+ def select_for_count
+ if select_values.present?
+ return select_values.first if select_values.one?
+ select_values.join(", ")
+ else
+ :all
+ end
end
- end
- def build_count_subquery(relation, column_name, distinct)
- column_alias = Arel.sql('count_column')
- subquery_alias = Arel.sql('subquery_for_count')
+ def build_count_subquery(relation, column_name, distinct)
+ column_alias = Arel.sql("count_column")
+ subquery_alias = Arel.sql("subquery_for_count")
- aliased_column = aggregate_column(column_name == :all ? 1 : column_name).as(column_alias)
- relation.select_values = [aliased_column]
- subquery = relation.arel.as(subquery_alias)
+ aliased_column = aggregate_column(column_name == :all ? 1 : column_name).as(column_alias)
+ relation.select_values = [aliased_column]
+ subquery = relation.arel.as(subquery_alias)
- sm = Arel::SelectManager.new relation.engine
- select_value = operation_over_aggregate_column(column_alias, 'count', distinct)
- sm.project(select_value).from(subquery)
- end
+ sm = Arel::SelectManager.new relation.engine
+ select_value = operation_over_aggregate_column(column_alias, "count", distinct)
+ sm.project(select_value).from(subquery)
+ end
end
end
diff --git a/activerecord/lib/active_record/relation/delegation.rb b/activerecord/lib/active_record/relation/delegation.rb
index 2484cb3264..3c1dea8c6c 100644
--- a/activerecord/lib/active_record/relation/delegation.rb
+++ b/activerecord/lib/active_record/relation/delegation.rb
@@ -1,5 +1,3 @@
-require 'active_support/concern'
-
module ActiveRecord
module Delegation # :nodoc:
module DelegateCache # :nodoc:
@@ -17,7 +15,10 @@ module ActiveRecord
delegate = Class.new(klass) {
include ClassSpecificRelation
}
- const_set klass.name.gsub('::'.freeze, '_'.freeze), delegate
+ mangled_name = klass.name.gsub("::".freeze, "_".freeze)
+ const_set mangled_name, delegate
+ private_constant mangled_name
+
cache[klass] = delegate
end
end
@@ -37,10 +38,10 @@ module ActiveRecord
delegate :to_xml, :encode_with, :length, :collect, :map, :each, :all?, :include?, :to_ary, :join,
:[], :&, :|, :+, :-, :sample, :reverse, :compact, :in_groups, :in_groups_of,
- :shuffle, :split, to: :records
+ :shuffle, :split, :index, to: :records
delegate :table_name, :quoted_table_name, :primary_key, :quoted_primary_key,
- :connection, :columns_hash, :to => :klass
+ :connection, :columns_hash, to: :klass
module ClassSpecificRelation # :nodoc:
extend ActiveSupport::Concern
@@ -58,7 +59,7 @@ module ActiveRecord
@delegation_mutex.synchronize do
return if method_defined?(method)
- if method.to_s =~ /\A[a-zA-Z_]\w*[!?]?\z/
+ if /\A[a-zA-Z_]\w*[!?]?\z/.match?(method)
module_eval <<-RUBY, __FILE__, __LINE__ + 1
def #{method}(*args, &block)
scoping { @klass.#{method}(*args, &block) }
@@ -80,19 +81,19 @@ module ActiveRecord
end
end
- protected
+ private
- def method_missing(method, *args, &block)
- if @klass.respond_to?(method)
- self.class.delegate_to_scoped_klass(method)
- scoping { @klass.public_send(method, *args, &block) }
- elsif arel.respond_to?(method)
- self.class.delegate method, :to => :arel
- arel.public_send(method, *args, &block)
- else
- super
+ def method_missing(method, *args, &block)
+ if @klass.respond_to?(method)
+ self.class.delegate_to_scoped_klass(method)
+ scoping { @klass.public_send(method, *args, &block) }
+ elsif arel.respond_to?(method)
+ self.class.delegate method, to: :arel
+ arel.public_send(method, *args, &block)
+ else
+ super
+ end
end
- end
end
module ClassMethods # :nodoc:
@@ -102,26 +103,26 @@ module ActiveRecord
private
- def relation_class_for(klass)
- klass.relation_delegate_class(self)
- end
+ def relation_class_for(klass)
+ klass.relation_delegate_class(self)
+ end
end
- def respond_to?(method, include_private = false)
+ def respond_to_missing?(method, include_private = false)
super || @klass.respond_to?(method, include_private) ||
arel.respond_to?(method, include_private)
end
protected
- def method_missing(method, *args, &block)
- if @klass.respond_to?(method)
- scoping { @klass.public_send(method, *args, &block) }
- elsif arel.respond_to?(method)
- arel.public_send(method, *args, &block)
- else
- super
+ def method_missing(method, *args, &block)
+ if @klass.respond_to?(method)
+ scoping { @klass.public_send(method, *args, &block) }
+ elsif arel.respond_to?(method)
+ arel.public_send(method, *args, &block)
+ else
+ super
+ end
end
- end
end
end
diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb
index d255cad91b..dd92f40dee 100644
--- a/activerecord/lib/active_record/relation/finder_methods.rb
+++ b/activerecord/lib/active_record/relation/finder_methods.rb
@@ -1,8 +1,8 @@
-require 'active_support/core_ext/string/filters'
+require "active_support/core_ext/string/filters"
module ActiveRecord
module FinderMethods
- ONE_AS_ONE = '1 AS one'
+ ONE_AS_ONE = "1 AS one"
# Find by id - This can either be a specific id (1), a list of ids (1, 5, 6), or an array of ids ([5, 6, 10]).
# If one or more records can not be found for the requested ids, then RecordNotFound will be raised. If the primary key
@@ -76,7 +76,7 @@ module ActiveRecord
# Post.find_by "published_at < ?", 2.weeks.ago
def find_by(arg, *args)
where(arg, *args).take
- rescue RangeError
+ rescue ::RangeError
nil
end
@@ -84,7 +84,7 @@ module ActiveRecord
# an ActiveRecord::RecordNotFound error.
def find_by!(arg, *args)
where(arg, *args).take!
- rescue RangeError
+ rescue ::RangeError
raise RecordNotFound.new("Couldn't find #{@klass.name} with an out of range value",
@klass.name)
end
@@ -97,13 +97,13 @@ module ActiveRecord
# Person.take(5) # returns 5 objects fetched by SELECT * FROM people LIMIT 5
# Person.where(["name LIKE '%?'", name]).take
def take(limit = nil)
- limit ? limit(limit).to_a : find_take
+ limit ? find_take_with_limit(limit) : find_take
end
# Same as #take but raises ActiveRecord::RecordNotFound if no record
# is found. Note that #take! accepts no arguments.
def take!
- take or raise RecordNotFound.new("Couldn't find #{@klass.name} with [#{arel.where_sql(@klass.arel_engine)}]")
+ take || raise_record_not_found_exception!
end
# Find the first record (or first N records if a parameter is supplied).
@@ -117,7 +117,7 @@ module ActiveRecord
#
def first(limit = nil)
if limit
- find_nth_with_limit_and_offset(0, limit, offset: offset_index)
+ find_nth_with_limit(0, limit)
else
find_nth 0
end
@@ -126,7 +126,7 @@ module ActiveRecord
# Same as #first but raises ActiveRecord::RecordNotFound if no record
# is found. Note that #first! accepts no arguments.
def first!
- find_nth! 0
+ first || raise_record_not_found_exception!
end
# Find the last record (or last N records if a parameter is supplied).
@@ -152,20 +152,12 @@ module ActiveRecord
result = result.reverse_order!
limit ? result.reverse : result.first
- rescue ActiveRecord::IrreversibleOrderError
- ActiveSupport::Deprecation.warn(<<-WARNING.squish)
- Finding a last element by loading the relation when SQL ORDER
- can not be reversed is deprecated.
- Rails 5.1 will raise ActiveRecord::IrreversibleOrderError in this case.
- Please call `to_a.last` if you still want to load the relation.
- WARNING
- find_last(limit)
end
# Same as #last but raises ActiveRecord::RecordNotFound if no record
# is found. Note that #last! accepts no arguments.
def last!
- last or raise RecordNotFound.new("Couldn't find #{@klass.name} with [#{arel.where_sql(@klass.arel_engine)}]")
+ last || raise_record_not_found_exception!
end
# Find the second record.
@@ -181,7 +173,7 @@ module ActiveRecord
# Same as #second but raises ActiveRecord::RecordNotFound if no record
# is found.
def second!
- find_nth! 1
+ second || raise_record_not_found_exception!
end
# Find the third record.
@@ -197,7 +189,7 @@ module ActiveRecord
# Same as #third but raises ActiveRecord::RecordNotFound if no record
# is found.
def third!
- find_nth! 2
+ third || raise_record_not_found_exception!
end
# Find the fourth record.
@@ -213,7 +205,7 @@ module ActiveRecord
# Same as #fourth but raises ActiveRecord::RecordNotFound if no record
# is found.
def fourth!
- find_nth! 3
+ fourth || raise_record_not_found_exception!
end
# Find the fifth record.
@@ -229,7 +221,7 @@ module ActiveRecord
# Same as #fifth but raises ActiveRecord::RecordNotFound if no record
# is found.
def fifth!
- find_nth! 4
+ fifth || raise_record_not_found_exception!
end
# Find the forty-second record. Also known as accessing "the reddit".
@@ -245,7 +237,7 @@ module ActiveRecord
# Same as #forty_two but raises ActiveRecord::RecordNotFound if no record
# is found.
def forty_two!
- find_nth! 41
+ forty_two || raise_record_not_found_exception!
end
# Find the third-to-last record.
@@ -261,7 +253,7 @@ module ActiveRecord
# Same as #third_to_last but raises ActiveRecord::RecordNotFound if no record
# is found.
def third_to_last!
- find_nth_from_last 3 or raise RecordNotFound.new("Couldn't find #{@klass.name} with [#{arel.where_sql(@klass.arel_engine)}]")
+ third_to_last || raise_record_not_found_exception!
end
# Find the second-to-last record.
@@ -277,7 +269,7 @@ module ActiveRecord
# Same as #second_to_last but raises ActiveRecord::RecordNotFound if no record
# is found.
def second_to_last!
- find_nth_from_last 2 or raise RecordNotFound.new("Couldn't find #{@klass.name} with [#{arel.where_sql(@klass.arel_engine)}]")
+ second_to_last || raise_record_not_found_exception!
end
# Returns true if a record exists in the table that matches the +id+ or
@@ -321,7 +313,7 @@ module ActiveRecord
relation = apply_join_dependency(self, construct_join_dependency(eager_loading: false))
return false if ActiveRecord::NullRelation === relation
- relation = relation.except(:select, :order).select(ONE_AS_ONE).limit(1)
+ relation = relation.except(:select, :distinct).select(ONE_AS_ONE).limit(1)
case conditions
when Array, Hash
@@ -333,7 +325,7 @@ module ActiveRecord
end
connection.select_value(relation, "#{name} Exists", relation.bound_attributes) ? true : false
- rescue RangeError
+ rescue ::RangeError
false
end
@@ -345,248 +337,235 @@ module ActiveRecord
# of results obtained should be provided in the +result_size+ argument and
# the expected number of results should be provided in the +expected_size+
# argument.
- def raise_record_not_found_exception!(ids, result_size, expected_size) #:nodoc:
+ def raise_record_not_found_exception!(ids = nil, result_size = nil, expected_size = nil, key = primary_key) # :nodoc:
conditions = arel.where_sql(@klass.arel_engine)
conditions = " [#{conditions}]" if conditions
-
- if Array(ids).size == 1
- error = "Couldn't find #{@klass.name} with '#{primary_key}'=#{ids}#{conditions}"
+ name = @klass.name
+
+ if ids.nil?
+ error = "Couldn't find #{name}"
+ error << " with#{conditions}" if conditions
+ raise RecordNotFound.new(error, name)
+ elsif Array(ids).size == 1
+ error = "Couldn't find #{name} with '#{key}'=#{ids}#{conditions}"
+ raise RecordNotFound.new(error, name, key, ids)
else
- error = "Couldn't find all #{@klass.name.pluralize} with '#{primary_key}': "
+ error = "Couldn't find all #{name.pluralize} with '#{key}': "
error << "(#{ids.join(", ")})#{conditions} (found #{result_size} results, but was looking for #{expected_size})"
- end
- raise RecordNotFound, error
+ raise RecordNotFound.new(error, name, primary_key, ids)
+ end
end
private
- def offset_index
- offset_value || 0
- end
+ def offset_index
+ offset_value || 0
+ end
- def find_with_associations
- # NOTE: the JoinDependency constructed here needs to know about
- # any joins already present in `self`, so pass them in
- #
- # failing to do so means that in cases like activerecord/test/cases/associations/inner_join_association_test.rb:136
- # incorrect SQL is generated. In that case, the join dependency for
- # SpecialCategorizations is constructed without knowledge of the
- # preexisting join in joins_values to categorizations (by way of
- # the `has_many :through` for categories).
- #
- join_dependency = construct_join_dependency(joins_values)
-
- aliases = join_dependency.aliases
- relation = select aliases.columns
- relation = apply_join_dependency(relation, join_dependency)
-
- if block_given?
- yield relation
- else
- if ActiveRecord::NullRelation === relation
- []
+ def find_with_associations
+ # NOTE: the JoinDependency constructed here needs to know about
+ # any joins already present in `self`, so pass them in
+ #
+ # failing to do so means that in cases like activerecord/test/cases/associations/inner_join_association_test.rb:136
+ # incorrect SQL is generated. In that case, the join dependency for
+ # SpecialCategorizations is constructed without knowledge of the
+ # preexisting join in joins_values to categorizations (by way of
+ # the `has_many :through` for categories).
+ #
+ join_dependency = construct_join_dependency(joins_values)
+
+ aliases = join_dependency.aliases
+ relation = select aliases.columns
+ relation = apply_join_dependency(relation, join_dependency)
+
+ if block_given?
+ yield relation
else
- arel = relation.arel
- rows = connection.select_all(arel, 'SQL', relation.bound_attributes)
- join_dependency.instantiate(rows, aliases)
+ if ActiveRecord::NullRelation === relation
+ []
+ else
+ arel = relation.arel
+ rows = connection.select_all(arel, "SQL", relation.bound_attributes)
+ join_dependency.instantiate(rows, aliases)
+ end
end
end
- end
- def construct_join_dependency(joins = [], eager_loading: true)
- including = eager_load_values + includes_values
- ActiveRecord::Associations::JoinDependency.new(@klass, including, joins, eager_loading: eager_loading)
- end
+ def construct_join_dependency(joins = [], eager_loading: true)
+ including = eager_load_values + includes_values
+ ActiveRecord::Associations::JoinDependency.new(@klass, including, joins, eager_loading: eager_loading)
+ end
- def construct_relation_for_association_calculations
- apply_join_dependency(self, construct_join_dependency(joins_values))
- end
+ def construct_relation_for_association_calculations
+ apply_join_dependency(self, construct_join_dependency(joins_values))
+ end
- def apply_join_dependency(relation, join_dependency)
- relation = relation.except(:includes, :eager_load, :preload)
- relation = relation.joins join_dependency
+ def apply_join_dependency(relation, join_dependency)
+ relation = relation.except(:includes, :eager_load, :preload)
+ relation = relation.joins join_dependency
- if using_limitable_reflections?(join_dependency.reflections)
- relation
- else
- if relation.limit_value
- limited_ids = limited_ids_for(relation)
- limited_ids.empty? ? relation.none! : relation.where!(primary_key => limited_ids)
+ if using_limitable_reflections?(join_dependency.reflections)
+ relation
+ else
+ if relation.limit_value
+ limited_ids = limited_ids_for(relation)
+ limited_ids.empty? ? relation.none! : relation.where!(primary_key => limited_ids)
+ end
+ relation.except(:limit, :offset)
end
- relation.except(:limit, :offset)
end
- end
-
- def limited_ids_for(relation)
- values = @klass.connection.columns_for_distinct(
- "#{quoted_table_name}.#{quoted_primary_key}", relation.order_values)
- relation = relation.except(:select).select(values).distinct!
- arel = relation.arel
+ def limited_ids_for(relation)
+ values = @klass.connection.columns_for_distinct(
+ "#{quoted_table_name}.#{quoted_primary_key}", relation.order_values)
- id_rows = @klass.connection.select_all(arel, 'SQL', relation.bound_attributes)
- id_rows.map {|row| row[primary_key]}
- end
+ relation = relation.except(:select).select(values).distinct!
+ arel = relation.arel
- def using_limitable_reflections?(reflections)
- reflections.none?(&:collection?)
- end
-
- protected
-
- def find_with_ids(*ids)
- raise UnknownPrimaryKey.new(@klass) if primary_key.nil?
+ id_rows = @klass.connection.select_all(arel, "SQL", relation.bound_attributes)
+ id_rows.map { |row| row[primary_key] }
+ end
- expects_array = ids.first.kind_of?(Array)
- return ids.first if expects_array && ids.first.empty?
+ def using_limitable_reflections?(reflections)
+ reflections.none?(&:collection?)
+ end
- ids = ids.flatten.compact.uniq
+ private
- case ids.size
- when 0
- raise RecordNotFound, "Couldn't find #{@klass.name} without an ID"
- when 1
- result = find_one(ids.first)
- expects_array ? [ result ] : result
- else
- find_some(ids)
- end
- rescue RangeError
- raise RecordNotFound, "Couldn't find #{@klass.name} with an out of range ID"
- end
+ def find_with_ids(*ids)
+ raise UnknownPrimaryKey.new(@klass) if primary_key.nil?
- def find_one(id)
- if ActiveRecord::Base === id
- id = id.id
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
- You are passing an instance of ActiveRecord::Base to `find`.
- Please pass the id of the object by calling `.id`.
- MSG
- end
+ expects_array = ids.first.kind_of?(Array)
+ return ids.first if expects_array && ids.first.empty?
- relation = where(primary_key => id)
- record = relation.take
+ ids = ids.flatten.compact.uniq
- raise_record_not_found_exception!(id, 0, 1) unless record
+ case ids.size
+ when 0
+ raise RecordNotFound, "Couldn't find #{@klass.name} without an ID"
+ when 1
+ result = find_one(ids.first)
+ expects_array ? [ result ] : result
+ else
+ find_some(ids)
+ end
+ rescue ::RangeError
+ raise RecordNotFound, "Couldn't find #{@klass.name} with an out of range ID"
+ end
- record
- end
+ def find_one(id)
+ if ActiveRecord::Base === id
+ id = id.id
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
+ You are passing an instance of ActiveRecord::Base to `find`.
+ Please pass the id of the object by calling `.id`.
+ MSG
+ end
- def find_some(ids)
- return find_some_ordered(ids) unless order_values.present?
+ relation = where(primary_key => id)
+ record = relation.take
- result = where(primary_key => ids).to_a
+ raise_record_not_found_exception!(id, 0, 1) unless record
- expected_size =
- if limit_value && ids.size > limit_value
- limit_value
- else
- ids.size
+ record
end
- # 11 ids with limit 3, offset 9 should give 2 results.
- if offset_value && (ids.size - offset_value < expected_size)
- expected_size = ids.size - offset_value
- end
+ def find_some(ids)
+ return find_some_ordered(ids) unless order_values.present?
- if result.size == expected_size
- result
- else
- raise_record_not_found_exception!(ids, result.size, expected_size)
- end
- end
+ result = where(primary_key => ids).to_a
- def find_some_ordered(ids)
- ids = ids.slice(offset_value || 0, limit_value || ids.size) || []
+ expected_size =
+ if limit_value && ids.size > limit_value
+ limit_value
+ else
+ ids.size
+ end
- result = except(:limit, :offset).where(primary_key => ids).records
+ # 11 ids with limit 3, offset 9 should give 2 results.
+ if offset_value && (ids.size - offset_value < expected_size)
+ expected_size = ids.size - offset_value
+ end
- if result.size == ids.size
- pk_type = @klass.type_for_attribute(primary_key)
+ if result.size == expected_size
+ result
+ else
+ raise_record_not_found_exception!(ids, result.size, expected_size)
+ end
+ end
- records_by_id = result.index_by(&:id)
- ids.map { |id| records_by_id.fetch(pk_type.cast(id)) }
- else
- raise_record_not_found_exception!(ids, result.size, ids.size)
- end
- end
+ def find_some_ordered(ids)
+ ids = ids.slice(offset_value || 0, limit_value || ids.size) || []
- def find_take
- if loaded?
- @records.first
- else
- @take ||= limit(1).records.first
- end
- end
+ result = except(:limit, :offset).where(primary_key => ids).records
- def find_nth(index, offset = nil)
- # TODO: once the offset argument is removed we rely on offset_index
- # within find_nth_with_limit, rather than pass it in via
- # find_nth_with_limit_and_offset
- if offset
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
- Passing an offset argument to find_nth is deprecated,
- please use Relation#offset instead.
- MSG
- end
- if loaded?
- @records[index]
- else
- offset ||= offset_index
- @offsets[offset + index] ||= find_nth_with_limit_and_offset(index, 1, offset: offset).first
- end
- end
+ if result.size == ids.size
+ pk_type = @klass.type_for_attribute(primary_key)
- def find_nth!(index)
- find_nth(index) or raise RecordNotFound.new("Couldn't find #{@klass.name} with [#{arel.where_sql(@klass.arel_engine)}]")
- end
+ records_by_id = result.index_by(&:id)
+ ids.map { |id| records_by_id.fetch(pk_type.cast(id)) }
+ else
+ raise_record_not_found_exception!(ids, result.size, ids.size)
+ end
+ end
- def find_nth_with_limit(index, limit)
- # TODO: once the offset argument is removed from find_nth,
- # find_nth_with_limit_and_offset can be merged into this method
- relation = if order_values.empty? && primary_key
- order(arel_attribute(primary_key).asc)
- else
- self
- end
-
- relation = relation.offset(index) unless index.zero?
- relation.limit(limit).to_a
- end
+ def find_take
+ if loaded?
+ records.first
+ else
+ @take ||= limit(1).records.first
+ end
+ end
- def find_nth_from_last(index)
- if loaded?
- @records[-index]
- else
- relation = if order_values.empty? && primary_key
- order(arel_attribute(primary_key).asc)
- else
- self
- end
-
- relation.to_a[-index]
- # TODO: can be made more performant on large result sets by
- # for instance, last(index)[-index] (which would require
- # refactoring the last(n) finder method to make test suite pass),
- # or by using a combination of reverse_order, limit, and offset,
- # e.g., reverse_order.offset(index-1).first
- end
- end
+ def find_take_with_limit(limit)
+ if loaded?
+ records.take(limit)
+ else
+ limit(limit).to_a
+ end
+ end
- private
+ def find_nth(index)
+ @offsets[offset_index + index] ||= find_nth_with_limit(index, 1).first
+ end
- def find_nth_with_limit_and_offset(index, limit, offset:) # :nodoc:
- if loaded?
- @records[index, limit]
- else
- index += offset
- find_nth_with_limit(index, limit)
- end
- end
+ def find_nth_with_limit(index, limit)
+ if loaded?
+ records[index, limit] || []
+ else
+ relation = if order_values.empty? && primary_key
+ order(arel_attribute(primary_key).asc)
+ else
+ self
+ end
+
+ relation = relation.offset(offset_index + index) unless index.zero?
+ relation.limit(limit).to_a
+ end
+ end
- def find_last(limit)
- limit ? records.last(limit) : records.last
- end
+ def find_nth_from_last(index)
+ if loaded?
+ records[-index]
+ else
+ relation = if order_values.empty? && primary_key
+ order(arel_attribute(primary_key).asc)
+ else
+ self
+ end
+
+ relation.to_a[-index]
+ # TODO: can be made more performant on large result sets by
+ # for instance, last(index)[-index] (which would require
+ # refactoring the last(n) finder method to make test suite pass),
+ # or by using a combination of reverse_order, limit, and offset,
+ # e.g., reverse_order.offset(index-1).first
+ end
+ end
+
+ def find_last(limit)
+ limit ? records.last(limit) : records.last
+ end
end
end
diff --git a/activerecord/lib/active_record/relation/merger.rb b/activerecord/lib/active_record/relation/merger.rb
index 396638d74d..5dac00724a 100644
--- a/activerecord/lib/active_record/relation/merger.rb
+++ b/activerecord/lib/active_record/relation/merger.rb
@@ -1,4 +1,4 @@
-require 'active_support/core_ext/hash/keys'
+require "active_support/core_ext/hash/keys"
module ActiveRecord
class Relation
@@ -83,85 +83,81 @@ module ActiveRecord
private
- def merge_preloads
- return if other.preload_values.empty? && other.includes_values.empty?
+ def merge_preloads
+ return if other.preload_values.empty? && other.includes_values.empty?
- if other.klass == relation.klass
- relation.preload!(*other.preload_values) unless other.preload_values.empty?
- relation.includes!(other.includes_values) unless other.includes_values.empty?
- else
- reflection = relation.klass.reflect_on_all_associations.find do |r|
- r.class_name == other.klass.name
- end || return
+ if other.klass == relation.klass
+ relation.preload!(*other.preload_values) unless other.preload_values.empty?
+ relation.includes!(other.includes_values) unless other.includes_values.empty?
+ else
+ reflection = relation.klass.reflect_on_all_associations.find do |r|
+ r.class_name == other.klass.name
+ end || return
- unless other.preload_values.empty?
- relation.preload! reflection.name => other.preload_values
- end
+ unless other.preload_values.empty?
+ relation.preload! reflection.name => other.preload_values
+ end
- unless other.includes_values.empty?
- relation.includes! reflection.name => other.includes_values
+ unless other.includes_values.empty?
+ relation.includes! reflection.name => other.includes_values
+ end
end
end
- end
- def merge_joins
- return if other.joins_values.blank?
+ def merge_joins
+ return if other.joins_values.blank?
- if other.klass == relation.klass
- relation.joins!(*other.joins_values)
- else
- joins_dependency, rest = other.joins_values.partition do |join|
- case join
- when Hash, Symbol, Array
- true
- else
- false
+ if other.klass == relation.klass
+ relation.joins!(*other.joins_values)
+ else
+ joins_dependency, rest = other.joins_values.partition do |join|
+ case join
+ when Hash, Symbol, Array
+ true
+ else
+ false
+ end
end
- end
-
- join_dependency = ActiveRecord::Associations::JoinDependency.new(other.klass,
- joins_dependency,
- [])
- relation.joins! rest
- @relation = relation.joins join_dependency
- end
- end
+ join_dependency = ActiveRecord::Associations::JoinDependency.new(other.klass,
+ joins_dependency,
+ [])
+ relation.joins! rest
- def merge_multi_values
- if other.reordering_value
- # override any order specified in the original relation
- relation.reorder! other.order_values
- elsif other.order_values
- # merge in order_values from relation
- relation.order! other.order_values
+ @relation = relation.joins join_dependency
+ end
end
- relation.extend(*other.extending_values) unless other.extending_values.blank?
- end
+ def merge_multi_values
+ if other.reordering_value
+ # override any order specified in the original relation
+ relation.reorder! other.order_values
+ elsif other.order_values
+ # merge in order_values from relation
+ relation.order! other.order_values
+ end
- def merge_single_values
- if relation.from_clause.empty?
- relation.from_clause = other.from_clause
+ relation.extend(*other.extending_values) unless other.extending_values.blank?
end
- relation.lock_value ||= other.lock_value
- unless other.create_with_value.blank?
- relation.create_with_value = (relation.create_with_value || {}).merge(other.create_with_value)
- end
- end
+ def merge_single_values
+ if relation.from_clause.empty?
+ relation.from_clause = other.from_clause
+ end
+ relation.lock_value ||= other.lock_value
- CLAUSE_METHOD_NAMES = CLAUSE_METHODS.map do |name|
- ["#{name}_clause", "#{name}_clause="]
- end
+ unless other.create_with_value.blank?
+ relation.create_with_value = (relation.create_with_value || {}).merge(other.create_with_value)
+ end
+ end
- def merge_clauses
- CLAUSE_METHOD_NAMES.each do |(reader, writer)|
- clause = relation.send(reader)
- other_clause = other.send(reader)
- relation.send(writer, clause.merge(other_clause))
+ def merge_clauses
+ CLAUSE_METHODS.each do |method|
+ clause = relation.get_value(method)
+ other_clause = other.get_value(method)
+ relation.set_value(method, clause.merge(other_clause))
+ end
end
- end
end
end
end
diff --git a/activerecord/lib/active_record/relation/predicate_builder.rb b/activerecord/lib/active_record/relation/predicate_builder.rb
index e5496c02b2..18ae10a652 100644
--- a/activerecord/lib/active_record/relation/predicate_builder.rb
+++ b/activerecord/lib/active_record/relation/predicate_builder.rb
@@ -1,13 +1,12 @@
module ActiveRecord
class PredicateBuilder # :nodoc:
- require 'active_record/relation/predicate_builder/array_handler'
- require 'active_record/relation/predicate_builder/association_query_handler'
- require 'active_record/relation/predicate_builder/base_handler'
- require 'active_record/relation/predicate_builder/basic_object_handler'
- require 'active_record/relation/predicate_builder/class_handler'
- require 'active_record/relation/predicate_builder/polymorphic_array_handler'
- require 'active_record/relation/predicate_builder/range_handler'
- require 'active_record/relation/predicate_builder/relation_handler'
+ require "active_record/relation/predicate_builder/array_handler"
+ require "active_record/relation/predicate_builder/association_query_handler"
+ require "active_record/relation/predicate_builder/base_handler"
+ require "active_record/relation/predicate_builder/basic_object_handler"
+ require "active_record/relation/predicate_builder/polymorphic_array_handler"
+ require "active_record/relation/predicate_builder/range_handler"
+ require "active_record/relation/predicate_builder/relation_handler"
delegate :resolve_column_aliases, to: :table
@@ -16,7 +15,6 @@ module ActiveRecord
@handlers = []
register_handler(BasicObject, BasicObjectHandler.new)
- register_handler(Class, ClassHandler.new(self))
register_handler(Base, BaseHandler.new(self))
register_handler(Range, RangeHandler.new)
register_handler(RangeHandler::RangeWithBinds, RangeHandler.new)
@@ -36,23 +34,13 @@ module ActiveRecord
create_binds_for_hash(attributes)
end
- def expand(column, value)
- # Find the foreign key when using queries such as:
- # Post.where(author: author)
- #
- # For polymorphic relationships, find the foreign key and type:
- # PriceEstimate.where(estimate_of: treasure)
- value = AssociationQueryHandler.value_for(table, column, value) if table.associated_with?(column)
- build(table.arel_attribute(column), value)
- end
-
def self.references(attributes)
attributes.map do |key, value|
if value.is_a?(Hash)
key
else
key = key.to_s
- key.split('.'.freeze).first if key.include?('.'.freeze)
+ key.split(".".freeze).first if key.include?(".".freeze)
end
end.compact
end
@@ -76,93 +64,108 @@ module ActiveRecord
handler_for(value).call(attribute, value)
end
+ # TODO Change this to private once we've dropped Ruby 2.2 support.
+ # Workaround for Ruby 2.2 "private attribute?" warning.
protected
- attr_reader :table
+ attr_reader :table
- def expand_from_hash(attributes)
- return ["1=0"] if attributes.empty?
+ def expand_from_hash(attributes)
+ return ["1=0"] if attributes.empty?
- attributes.flat_map do |key, value|
- if value.is_a?(Hash)
- associated_predicate_builder(key).expand_from_hash(value)
- else
- expand(key, value)
+ attributes.flat_map do |key, value|
+ if value.is_a?(Hash) && !table.has_column?(key)
+ associated_predicate_builder(key).expand_from_hash(value)
+ else
+ build(table.arel_attribute(key), value)
+ end
end
end
- end
-
-
- def create_binds_for_hash(attributes)
- result = attributes.dup
- binds = []
- attributes.each do |column_name, value|
- case value
- when Hash
- attrs, bvs = associated_predicate_builder(column_name).create_binds_for_hash(value)
- result[column_name] = attrs
- binds += bvs
- when Relation
- binds += value.bound_attributes
- when Range
- first = value.begin
- last = value.end
- unless first.respond_to?(:infinite?) && first.infinite?
- binds << build_bind_param(column_name, first)
- first = Arel::Nodes::BindParam.new
- end
- unless last.respond_to?(:infinite?) && last.infinite?
- binds << build_bind_param(column_name, last)
- last = Arel::Nodes::BindParam.new
+ def create_binds_for_hash(attributes)
+ result = attributes.dup
+ binds = []
+
+ attributes.each do |column_name, value|
+ case
+ when value.is_a?(Hash) && !table.has_column?(column_name)
+ attrs, bvs = associated_predicate_builder(column_name).create_binds_for_hash(value)
+ result[column_name] = attrs
+ binds += bvs
+ next
+ when value.is_a?(Relation)
+ binds += value.bound_attributes
+ when value.is_a?(Range) && !table.type(column_name).respond_to?(:subtype)
+ first = value.begin
+ last = value.end
+ unless first.respond_to?(:infinite?) && first.infinite?
+ binds << build_bind_param(column_name, first)
+ first = Arel::Nodes::BindParam.new
+ end
+ unless last.respond_to?(:infinite?) && last.infinite?
+ binds << build_bind_param(column_name, last)
+ last = Arel::Nodes::BindParam.new
+ end
+
+ result[column_name] = RangeHandler::RangeWithBinds.new(first, last, value.exclude_end?)
+ else
+ if can_be_bound?(column_name, value)
+ result[column_name] = Arel::Nodes::BindParam.new
+ binds << build_bind_param(column_name, value)
+ end
end
- result[column_name] = RangeHandler::RangeWithBinds.new(first, last, value.exclude_end?)
- else
- if can_be_bound?(column_name, value)
- result[column_name] = Arel::Nodes::BindParam.new
- binds << build_bind_param(column_name, value)
+ # Find the foreign key when using queries such as:
+ # Post.where(author: author)
+ #
+ # For polymorphic relationships, find the foreign key and type:
+ # PriceEstimate.where(estimate_of: treasure)
+ if table.associated_with?(column_name)
+ result[column_name] = AssociationQueryHandler.value_for(table, column_name, value)
end
end
- end
- [result, binds]
- end
+ [result, binds]
+ end
private
- def associated_predicate_builder(association_name)
- self.class.new(table.associated_table(association_name))
- end
-
- def convert_dot_notation_to_hash(attributes)
- dot_notation = attributes.select do |k, v|
- k.include?(".".freeze) && !v.is_a?(Hash)
+ def associated_predicate_builder(association_name)
+ self.class.new(table.associated_table(association_name))
end
- dot_notation.each_key do |key|
- table_name, column_name = key.split(".".freeze)
- value = attributes.delete(key)
- attributes[table_name] ||= {}
+ def convert_dot_notation_to_hash(attributes)
+ dot_notation = attributes.select do |k, v|
+ k.include?(".".freeze) && !v.is_a?(Hash)
+ end
- attributes[table_name] = attributes[table_name].merge(column_name => value)
- end
+ dot_notation.each_key do |key|
+ table_name, column_name = key.split(".".freeze)
+ value = attributes.delete(key)
+ attributes[table_name] ||= {}
- attributes
- end
+ attributes[table_name] = attributes[table_name].merge(column_name => value)
+ end
- def handler_for(object)
- @handlers.detect { |klass, _| klass === object }.last
- end
+ attributes
+ end
- def can_be_bound?(column_name, value)
- !value.nil? &&
- handler_for(value).is_a?(BasicObjectHandler) &&
- !table.associated_with?(column_name)
- end
+ def handler_for(object)
+ @handlers.detect { |klass, _| klass === object }.last
+ end
- def build_bind_param(column_name, value)
- Relation::QueryAttribute.new(column_name.to_s, value, table.type(column_name))
- end
+ def can_be_bound?(column_name, value)
+ return if table.associated_with?(column_name)
+ case value
+ when Array, Range
+ table.type(column_name).respond_to?(:subtype)
+ else
+ !value.nil? && handler_for(value).is_a?(BasicObjectHandler)
+ end
+ end
+
+ def build_bind_param(column_name, value)
+ Relation::QueryAttribute.new(column_name.to_s, value, table.type(column_name))
+ end
end
end
diff --git a/activerecord/lib/active_record/relation/predicate_builder/array_handler.rb b/activerecord/lib/active_record/relation/predicate_builder/array_handler.rb
index 95dbd6a77f..88b6c37d43 100644
--- a/activerecord/lib/active_record/relation/predicate_builder/array_handler.rb
+++ b/activerecord/lib/active_record/relation/predicate_builder/array_handler.rb
@@ -29,15 +29,17 @@ module ActiveRecord
array_predicates.inject { |composite, predicate| composite.or(predicate) }
end
+ # TODO Change this to private once we've dropped Ruby 2.2 support.
+ # Workaround for Ruby 2.2 "private attribute?" warning.
protected
- attr_reader :predicate_builder
+ attr_reader :predicate_builder
- module NullPredicate # :nodoc:
- def self.or(other)
- other
+ module NullPredicate # :nodoc:
+ def self.or(other)
+ other
+ end
end
- end
end
end
end
diff --git a/activerecord/lib/active_record/relation/predicate_builder/association_query_handler.rb b/activerecord/lib/active_record/relation/predicate_builder/association_query_handler.rb
index 413cb9fd84..29860ec677 100644
--- a/activerecord/lib/active_record/relation/predicate_builder/association_query_handler.rb
+++ b/activerecord/lib/active_record/relation/predicate_builder/association_query_handler.rb
@@ -28,9 +28,11 @@ module ActiveRecord
predicate_builder.build_from_hash(queries)
end
+ # TODO Change this to private once we've dropped Ruby 2.2 support.
+ # Workaround for Ruby 2.2 "private attribute?" warning.
protected
- attr_reader :predicate_builder
+ attr_reader :predicate_builder
end
class AssociationQueryValue # :nodoc:
@@ -60,30 +62,27 @@ module ActiveRecord
private
- def primary_key
- associated_table.association_primary_key(base_class)
- end
+ def primary_key
+ associated_table.association_primary_key(base_class)
+ end
- def polymorphic_base_class_from_value
- case value
- when Relation
- value.klass.base_class
- when Array
- val = value.compact.first
- val.class.base_class if val.is_a?(Base)
- when Base
- value.class.base_class
+ def polymorphic_base_class_from_value
+ case value
+ when Relation
+ value.klass.base_class
+ when Base
+ value.class.base_class
+ end
end
- end
- def convert_to_id(value)
- case value
- when Base
- value._read_attribute(primary_key)
- else
- value
+ def convert_to_id(value)
+ case value
+ when Base
+ value._read_attribute(primary_key)
+ else
+ value
+ end
end
- end
end
end
end
diff --git a/activerecord/lib/active_record/relation/predicate_builder/base_handler.rb b/activerecord/lib/active_record/relation/predicate_builder/base_handler.rb
index 6fa5b16f73..3bb1037885 100644
--- a/activerecord/lib/active_record/relation/predicate_builder/base_handler.rb
+++ b/activerecord/lib/active_record/relation/predicate_builder/base_handler.rb
@@ -9,9 +9,11 @@ module ActiveRecord
predicate_builder.build(attribute, value.id)
end
+ # TODO Change this to private once we've dropped Ruby 2.2 support.
+ # Workaround for Ruby 2.2 "private attribute?" warning.
protected
- attr_reader :predicate_builder
+ attr_reader :predicate_builder
end
end
end
diff --git a/activerecord/lib/active_record/relation/predicate_builder/class_handler.rb b/activerecord/lib/active_record/relation/predicate_builder/class_handler.rb
deleted file mode 100644
index ed313fc9d4..0000000000
--- a/activerecord/lib/active_record/relation/predicate_builder/class_handler.rb
+++ /dev/null
@@ -1,27 +0,0 @@
-module ActiveRecord
- class PredicateBuilder
- class ClassHandler # :nodoc:
- def initialize(predicate_builder)
- @predicate_builder = predicate_builder
- end
-
- def call(attribute, value)
- print_deprecation_warning
- predicate_builder.build(attribute, value.name)
- end
-
- protected
-
- attr_reader :predicate_builder
-
- private
-
- def print_deprecation_warning
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
- Passing a class as a value in an Active Record query is deprecated and
- will be removed. Pass a string instead.
- MSG
- end
- end
- end
-end
diff --git a/activerecord/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb b/activerecord/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb
index b6c6240343..335124c952 100644
--- a/activerecord/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb
+++ b/activerecord/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb
@@ -21,9 +21,11 @@ module ActiveRecord
end
end
+ # TODO Change this to private once we've dropped Ruby 2.2 support.
+ # Workaround for Ruby 2.2 "private attribute?" warning.
protected
- attr_reader :predicate_builder
+ attr_reader :predicate_builder
end
class PolymorphicArrayValue # :nodoc:
@@ -41,17 +43,17 @@ module ActiveRecord
private
- def primary_key(value)
- associated_table.association_primary_key(base_class(value))
- end
+ def primary_key(value)
+ associated_table.association_primary_key(base_class(value))
+ end
- def base_class(value)
- value.class.base_class
- end
+ def base_class(value)
+ value.class.base_class
+ end
- def convert_to_id(value)
- value._read_attribute(primary_key(value))
- end
+ def convert_to_id(value)
+ value._read_attribute(primary_key(value))
+ end
end
end
end
diff --git a/activerecord/lib/active_record/relation/query_attribute.rb b/activerecord/lib/active_record/relation/query_attribute.rb
index 7ba964e802..a68e508fcc 100644
--- a/activerecord/lib/active_record/relation/query_attribute.rb
+++ b/activerecord/lib/active_record/relation/query_attribute.rb
@@ -1,4 +1,4 @@
-require 'active_record/attribute'
+require "active_record/attribute"
module ActiveRecord
class Relation
diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb
index 8a87015e44..78c046b07f 100644
--- a/activerecord/lib/active_record/relation/query_methods.rb
+++ b/activerecord/lib/active_record/relation/query_methods.rb
@@ -2,8 +2,8 @@ require "active_record/relation/from_clause"
require "active_record/relation/query_attribute"
require "active_record/relation/where_clause"
require "active_record/relation/where_clause_factory"
-require 'active_model/forbidden_attributes_protection'
-require 'active_support/core_ext/string/filters'
+require "active_model/forbidden_attributes_protection"
+require "active_support/core_ext/string/filters"
module ActiveRecord
module QueryMethods
@@ -55,62 +55,39 @@ module ActiveRecord
end
FROZEN_EMPTY_ARRAY = [].freeze
- Relation::MULTI_VALUE_METHODS.each do |name|
- class_eval <<-CODE, __FILE__, __LINE__ + 1
- def #{name}_values
- @values[:#{name}] || FROZEN_EMPTY_ARRAY
- end
+ FROZEN_EMPTY_HASH = {}.freeze
- def #{name}_values=(values)
- assert_mutability!
- @values[:#{name}] = values
+ Relation::VALUE_METHODS.each do |name|
+ method_name = \
+ case name
+ when *Relation::MULTI_VALUE_METHODS then "#{name}_values"
+ when *Relation::SINGLE_VALUE_METHODS then "#{name}_value"
+ when *Relation::CLAUSE_METHODS then "#{name}_clause"
end
- CODE
- end
-
- (Relation::SINGLE_VALUE_METHODS - [:create_with]).each do |name|
class_eval <<-CODE, __FILE__, __LINE__ + 1
- def #{name}_value # def readonly_value
- @values[:#{name}] # @values[:readonly]
+ def #{method_name} # def includes_values
+ get_value(#{name.inspect}) # get_value(:includes)
end # end
- CODE
- end
- Relation::SINGLE_VALUE_METHODS.each do |name|
- class_eval <<-CODE, __FILE__, __LINE__ + 1
- def #{name}_value=(value) # def readonly_value=(value)
- assert_mutability! # assert_mutability!
- @values[:#{name}] = value # @values[:readonly] = value
+ def #{method_name}=(value) # def includes_values=(value)
+ set_value(#{name.inspect}, value) # set_value(:includes, value)
end # end
CODE
end
- Relation::CLAUSE_METHODS.each do |name|
- class_eval <<-CODE, __FILE__, __LINE__ + 1
- def #{name}_clause # def where_clause
- @values[:#{name}] || new_#{name}_clause # @values[:where] || new_where_clause
- end # end
- #
- def #{name}_clause=(value) # def where_clause=(value)
- assert_mutability! # assert_mutability!
- @values[:#{name}] = value # @values[:where] = value
- end # end
- CODE
- end
-
def bound_attributes
- if limit_value && !string_containing_comma?(limit_value)
+ if limit_value
limit_bind = Attribute.with_cast_value(
"LIMIT".freeze,
connection.sanitize_limit(limit_value),
- Type::Value.new,
+ Type.default_value,
)
end
if offset_value
offset_bind = Attribute.with_cast_value(
"OFFSET".freeze,
offset_value.to_i,
- Type::Value.new,
+ Type.default_value,
)
end
connection.combine_bind_parameters(
@@ -123,11 +100,6 @@ module ActiveRecord
)
end
- FROZEN_EMPTY_HASH = {}.freeze
- def create_with_value # :nodoc:
- @values[:create_with] || FROZEN_EMPTY_HASH
- end
-
alias extensions extending_values
# Specify relationships to be included in the result set. For
@@ -269,8 +241,15 @@ module ActiveRecord
# Model.select(:field).first.other_field
# # => ActiveModel::MissingAttributeError: missing attribute: other_field
def select(*fields)
- return super if block_given?
- raise ArgumentError, 'Call this with at least one field' if fields.empty?
+ if block_given?
+ if fields.any?
+ raise ArgumentError, "`select' with block doesn't take arguments."
+ end
+
+ return super()
+ end
+
+ raise ArgumentError, "Call this with at least one field" if fields.empty?
spawn._select!(*fields)
end
@@ -417,7 +396,10 @@ module ActiveRecord
args.each do |scope|
case scope
when Symbol
- symbol_unscoping(scope)
+ if !VALID_UNSCOPING_VALUES.include?(scope)
+ raise ArgumentError, "Called unscope() with invalid unscoping argument ':#{scope}'. Valid arguments are :#{VALID_UNSCOPING_VALUES.to_a.join(", :")}."
+ end
+ set_value(scope, nil)
when Hash
scope.each do |key, target_value|
if key != :where
@@ -495,7 +477,6 @@ module ActiveRecord
self.left_outer_joins_values += args
self
end
- alias :left_joins! :left_outer_joins!
# Returns a new relation, which is the result of filtering the current relation
# according to the conditions in the arguments.
@@ -707,13 +688,6 @@ module ActiveRecord
end
def limit!(value) # :nodoc:
- if string_containing_comma?(value)
- # Remove `string_containing_comma?` when removing this deprecation
- ActiveSupport::Deprecation.warn(<<-WARNING.squish)
- Passing a string to limit in the form "1,2" is deprecated and will be
- removed in Rails 5.1. Please call `offset` explicitly instead.
- WARNING
- end
self.limit_value = value
self
end
@@ -780,7 +754,7 @@ module ActiveRecord
# end
#
def none
- where("1=0").extending!(NullRelation)
+ spawn.none!
end
def none! # :nodoc:
@@ -865,16 +839,12 @@ module ActiveRecord
def distinct(value = true)
spawn.distinct!(value)
end
- alias uniq distinct
- deprecate uniq: :distinct
# Like #distinct, but modifies relation in place.
def distinct!(value = true) # :nodoc:
self.distinct_value = value
self
end
- alias uniq! distinct!
- deprecate uniq!: :distinct!
# Used to extend a scope with additional methods, either through
# a module or through a block provided.
@@ -949,288 +919,277 @@ module ActiveRecord
@arel ||= build_arel
end
- private
-
- def assert_mutability!
- raise ImmutableRelation if @loaded
- raise ImmutableRelation if defined?(@arel) && @arel
+ # Returns a relation value with a given name
+ def get_value(name) # :nodoc:
+ @values[name] || default_value_for(name)
end
- def build_arel
- arel = Arel::SelectManager.new(table)
+ # Sets the relation value with the given name
+ def set_value(name, value) # :nodoc:
+ assert_mutability!
+ @values[name] = value
+ end
- build_joins(arel, joins_values.flatten) unless joins_values.empty?
- build_left_outer_joins(arel, left_outer_joins_values.flatten) unless left_outer_joins_values.empty?
+ private
- arel.where(where_clause.ast) unless where_clause.empty?
- arel.having(having_clause.ast) unless having_clause.empty?
- if limit_value
- if string_containing_comma?(limit_value)
- arel.take(connection.sanitize_limit(limit_value))
- else
- arel.take(Arel::Nodes::BindParam.new)
- end
+ def assert_mutability!
+ raise ImmutableRelation if @loaded
+ raise ImmutableRelation if defined?(@arel) && @arel
end
- arel.skip(Arel::Nodes::BindParam.new) if offset_value
- arel.group(*arel_columns(group_values.uniq.reject(&:blank?))) unless group_values.empty?
-
- build_order(arel)
- build_select(arel)
+ def build_arel
+ arel = Arel::SelectManager.new(table)
- arel.distinct(distinct_value)
- arel.from(build_from) unless from_clause.empty?
- arel.lock(lock_value) if lock_value
+ build_joins(arel, joins_values.flatten) unless joins_values.empty?
+ build_left_outer_joins(arel, left_outer_joins_values.flatten) unless left_outer_joins_values.empty?
- arel
- end
-
- def symbol_unscoping(scope)
- if !VALID_UNSCOPING_VALUES.include?(scope)
- raise ArgumentError, "Called unscope() with invalid unscoping argument ':#{scope}'. Valid arguments are :#{VALID_UNSCOPING_VALUES.to_a.join(", :")}."
- end
+ arel.where(where_clause.ast) unless where_clause.empty?
+ arel.having(having_clause.ast) unless having_clause.empty?
+ arel.take(Arel::Nodes::BindParam.new) if limit_value
+ arel.skip(Arel::Nodes::BindParam.new) if offset_value
+ arel.group(*arel_columns(group_values.uniq.reject(&:blank?))) unless group_values.empty?
- clause_method = Relation::CLAUSE_METHODS.include?(scope)
- multi_val_method = Relation::MULTI_VALUE_METHODS.include?(scope)
- if clause_method
- unscope_code = "#{scope}_clause="
- else
- unscope_code = "#{scope}_value#{'s' if multi_val_method}="
- end
+ build_order(arel)
- case scope
- when :order
- result = []
- else
- result = [] if multi_val_method
- end
+ build_select(arel)
- self.send(unscope_code, result)
- end
+ arel.distinct(distinct_value)
+ arel.from(build_from) unless from_clause.empty?
+ arel.lock(lock_value) if lock_value
- def build_from
- opts = from_clause.value
- name = from_clause.name
- case opts
- when Relation
- name ||= 'subquery'
- opts.arel.as(name.to_s)
- else
- opts
+ arel
end
- end
- def build_left_outer_joins(manager, outer_joins)
- buckets = outer_joins.group_by do |join|
- case join
- when Hash, Symbol, Array
- :association_join
+ def build_from
+ opts = from_clause.value
+ name = from_clause.name
+ case opts
+ when Relation
+ name ||= "subquery"
+ opts.arel.as(name.to_s)
else
- raise ArgumentError, 'only Hash, Symbol and Array are allowed'
+ opts
end
end
- build_join_query(manager, buckets, Arel::Nodes::OuterJoin)
- end
-
- def build_joins(manager, joins)
- buckets = joins.group_by do |join|
- case join
- when String
- :string_join
- when Hash, Symbol, Array
- :association_join
- when ActiveRecord::Associations::JoinDependency
- :stashed_join
- when Arel::Nodes::Join
- :join_node
- else
- raise 'unknown class: %s' % join.class.name
+ def build_left_outer_joins(manager, outer_joins)
+ buckets = outer_joins.group_by do |join|
+ case join
+ when Hash, Symbol, Array
+ :association_join
+ else
+ raise ArgumentError, "only Hash, Symbol and Array are allowed"
+ end
end
+
+ build_join_query(manager, buckets, Arel::Nodes::OuterJoin)
end
- build_join_query(manager, buckets, Arel::Nodes::InnerJoin)
- end
+ def build_joins(manager, joins)
+ buckets = joins.group_by do |join|
+ case join
+ when String
+ :string_join
+ when Hash, Symbol, Array
+ :association_join
+ when ActiveRecord::Associations::JoinDependency
+ :stashed_join
+ when Arel::Nodes::Join
+ :join_node
+ else
+ raise "unknown class: %s" % join.class.name
+ end
+ end
- def build_join_query(manager, buckets, join_type)
- buckets.default = []
+ build_join_query(manager, buckets, Arel::Nodes::InnerJoin)
+ end
- association_joins = buckets[:association_join]
- stashed_association_joins = buckets[:stashed_join]
- join_nodes = buckets[:join_node].uniq
- string_joins = buckets[:string_join].map(&:strip).uniq
+ def build_join_query(manager, buckets, join_type)
+ buckets.default = []
- join_list = join_nodes + convert_join_strings_to_ast(manager, string_joins)
+ association_joins = buckets[:association_join]
+ stashed_association_joins = buckets[:stashed_join]
+ join_nodes = buckets[:join_node].uniq
+ string_joins = buckets[:string_join].map(&:strip).uniq
- join_dependency = ActiveRecord::Associations::JoinDependency.new(
- @klass,
- association_joins,
- join_list
- )
+ join_list = join_nodes + convert_join_strings_to_ast(manager, string_joins)
- join_infos = join_dependency.join_constraints stashed_association_joins, join_type
+ join_dependency = ActiveRecord::Associations::JoinDependency.new(
+ @klass,
+ association_joins,
+ join_list
+ )
- join_infos.each do |info|
- info.joins.each { |join| manager.from(join) }
- manager.bind_values.concat info.binds
- end
+ join_infos = join_dependency.join_constraints stashed_association_joins, join_type
- manager.join_sources.concat(join_list)
+ join_infos.each do |info|
+ info.joins.each { |join| manager.from(join) }
+ manager.bind_values.concat info.binds
+ end
- manager
- end
+ manager.join_sources.concat(join_list)
- def convert_join_strings_to_ast(table, joins)
- joins
- .flatten
- .reject(&:blank?)
- .map { |join| table.create_string_join(Arel.sql(join)) }
- end
+ manager
+ end
- def build_select(arel)
- if select_values.any?
- arel.project(*arel_columns(select_values.uniq))
- else
- arel.project(@klass.arel_table[Arel.star])
+ def convert_join_strings_to_ast(table, joins)
+ joins
+ .flatten
+ .reject(&:blank?)
+ .map { |join| table.create_string_join(Arel.sql(join)) }
end
- end
- def arel_columns(columns)
- columns.map do |field|
- if (Symbol === field || String === field) && (klass.has_attribute?(field) || klass.attribute_alias?(field)) && !from_clause.value
- arel_attribute(field)
- elsif Symbol === field
- connection.quote_table_name(field.to_s)
+ def build_select(arel)
+ if select_values.any?
+ arel.project(*arel_columns(select_values.uniq))
else
- field
+ arel.project(@klass.arel_table[Arel.star])
end
end
- end
- def reverse_sql_order(order_query)
- if order_query.empty?
- return [arel_attribute(primary_key).desc] if primary_key
- raise IrreversibleOrderError,
- "Relation has no current order and table has no primary key to be used as default order"
+ def arel_columns(columns)
+ columns.map do |field|
+ if (Symbol === field || String === field) && (klass.has_attribute?(field) || klass.attribute_alias?(field)) && !from_clause.value
+ arel_attribute(field)
+ elsif Symbol === field
+ connection.quote_table_name(field.to_s)
+ else
+ field
+ end
+ end
end
- order_query.flat_map do |o|
- case o
- when Arel::Attribute
- o.desc
- when Arel::Nodes::Ordering
- o.reverse
- when String
- if does_not_support_reverse?(o)
- raise IrreversibleOrderError, "Order #{o.inspect} can not be reversed automatically"
- end
- o.split(',').map! do |s|
- s.strip!
- s.gsub!(/\sasc\Z/i, ' DESC') || s.gsub!(/\sdesc\Z/i, ' ASC') || s.concat(' DESC')
+ def reverse_sql_order(order_query)
+ if order_query.empty?
+ return [arel_attribute(primary_key).desc] if primary_key
+ raise IrreversibleOrderError,
+ "Relation has no current order and table has no primary key to be used as default order"
+ end
+
+ order_query.flat_map do |o|
+ case o
+ when Arel::Attribute
+ o.desc
+ when Arel::Nodes::Ordering
+ o.reverse
+ when String
+ if does_not_support_reverse?(o)
+ raise IrreversibleOrderError, "Order #{o.inspect} can not be reversed automatically"
+ end
+ o.split(",").map! do |s|
+ s.strip!
+ s.gsub!(/\sasc\Z/i, " DESC") || s.gsub!(/\sdesc\Z/i, " ASC") || s.concat(" DESC")
+ end
+ else
+ o
end
- else
- o
end
end
- end
- def does_not_support_reverse?(order)
- #uses sql function with multiple arguments
- order =~ /\([^()]*,[^()]*\)/ ||
- # uses "nulls first" like construction
- order =~ /nulls (first|last)\Z/i
- end
+ def does_not_support_reverse?(order)
+ # Uses SQL function with multiple arguments.
+ (order.include?(",") && order.split(",").find { |section| section.count("(") != section.count(")") }) ||
+ # Uses "nulls first" like construction.
+ /nulls (first|last)\Z/i.match?(order)
+ end
- def build_order(arel)
- orders = order_values.uniq
- orders.reject!(&:blank?)
+ def build_order(arel)
+ orders = order_values.uniq
+ orders.reject!(&:blank?)
- arel.order(*orders) unless orders.empty?
- end
+ arel.order(*orders) unless orders.empty?
+ end
- VALID_DIRECTIONS = [:asc, :desc, :ASC, :DESC,
- 'asc', 'desc', 'ASC', 'DESC'] # :nodoc:
+ VALID_DIRECTIONS = [:asc, :desc, :ASC, :DESC,
+ "asc", "desc", "ASC", "DESC"] # :nodoc:
- def validate_order_args(args)
- args.each do |arg|
- next unless arg.is_a?(Hash)
- arg.each do |_key, value|
- raise ArgumentError, "Direction \"#{value}\" is invalid. Valid " \
- "directions are: #{VALID_DIRECTIONS.inspect}" unless VALID_DIRECTIONS.include?(value)
+ def validate_order_args(args)
+ args.each do |arg|
+ next unless arg.is_a?(Hash)
+ arg.each do |_key, value|
+ raise ArgumentError, "Direction \"#{value}\" is invalid. Valid " \
+ "directions are: #{VALID_DIRECTIONS.inspect}" unless VALID_DIRECTIONS.include?(value)
+ end
end
end
- end
- def preprocess_order_args(order_args)
- order_args.map! do |arg|
- klass.send(:sanitize_sql_for_order, arg)
+ def preprocess_order_args(order_args)
+ order_args.map! do |arg|
+ klass.send(:sanitize_sql_for_order, arg)
+ end
+ order_args.flatten!
+ validate_order_args(order_args)
+
+ references = order_args.grep(String)
+ references.map! { |arg| arg =~ /^([a-zA-Z]\w*)\.(\w+)/ && $1 }.compact!
+ references!(references) if references.any?
+
+ # if a symbol is given we prepend the quoted table name
+ order_args.map! do |arg|
+ case arg
+ when Symbol
+ arel_attribute(arg).asc
+ when Hash
+ arg.map { |field, dir|
+ arel_attribute(field).send(dir.downcase)
+ }
+ else
+ arg
+ end
+ end.flatten!
end
- order_args.flatten!
- validate_order_args(order_args)
-
- references = order_args.grep(String)
- references.map! { |arg| arg =~ /^([a-zA-Z]\w*)\.(\w+)/ && $1 }.compact!
- references!(references) if references.any?
- # if a symbol is given we prepend the quoted table name
- order_args.map! do |arg|
- case arg
- when Symbol
- arel_attribute(arg).asc
- when Hash
- arg.map { |field, dir|
- arel_attribute(field).send(dir.downcase)
- }
- else
- arg
+ # Checks to make sure that the arguments are not blank. Note that if some
+ # blank-like object were initially passed into the query method, then this
+ # method will not raise an error.
+ #
+ # Example:
+ #
+ # Post.references() # raises an error
+ # Post.references([]) # does not raise an error
+ #
+ # This particular method should be called with a method_name and the args
+ # passed into that method as an input. For example:
+ #
+ # def references(*args)
+ # check_if_method_has_arguments!("references", args)
+ # ...
+ # end
+ def check_if_method_has_arguments!(method_name, args)
+ if args.blank?
+ raise ArgumentError, "The method .#{method_name}() must contain arguments."
end
- end.flatten!
- end
-
- # Checks to make sure that the arguments are not blank. Note that if some
- # blank-like object were initially passed into the query method, then this
- # method will not raise an error.
- #
- # Example:
- #
- # Post.references() # raises an error
- # Post.references([]) # does not raise an error
- #
- # This particular method should be called with a method_name and the args
- # passed into that method as an input. For example:
- #
- # def references(*args)
- # check_if_method_has_arguments!("references", args)
- # ...
- # end
- def check_if_method_has_arguments!(method_name, args)
- if args.blank?
- raise ArgumentError, "The method .#{method_name}() must contain arguments."
end
- end
- def structurally_incompatible_values_for_or(other)
- Relation::SINGLE_VALUE_METHODS.reject { |m| send("#{m}_value") == other.send("#{m}_value") } +
- (Relation::MULTI_VALUE_METHODS - [:extending]).reject { |m| send("#{m}_values") == other.send("#{m}_values") } +
- (Relation::CLAUSE_METHODS - [:having, :where]).reject { |m| send("#{m}_clause") == other.send("#{m}_clause") }
- end
-
- def new_where_clause
- Relation::WhereClause.empty
- end
- alias new_having_clause new_where_clause
-
- def where_clause_factory
- @where_clause_factory ||= Relation::WhereClauseFactory.new(klass, predicate_builder)
- end
- alias having_clause_factory where_clause_factory
-
- def new_from_clause
- Relation::FromClause.empty
- end
+ STRUCTURAL_OR_METHODS = Relation::VALUE_METHODS - [:extending, :where, :having]
+ def structurally_incompatible_values_for_or(other)
+ STRUCTURAL_OR_METHODS.reject do |method|
+ get_value(method) == other.get_value(method)
+ end
+ end
- def string_containing_comma?(value)
- ::String === value && value.include?(",")
- end
+ def where_clause_factory
+ @where_clause_factory ||= Relation::WhereClauseFactory.new(klass, predicate_builder)
+ end
+ alias having_clause_factory where_clause_factory
+
+ def default_value_for(name)
+ case name
+ when :create_with
+ FROZEN_EMPTY_HASH
+ when :readonly
+ false
+ when :where, :having
+ Relation::WhereClause.empty
+ when :from
+ Relation::FromClause.empty
+ when *Relation::MULTI_VALUE_METHODS
+ FROZEN_EMPTY_ARRAY
+ when *Relation::SINGLE_VALUE_METHODS
+ nil
+ else
+ raise ArgumentError, "unknown relation value #{name.inspect}"
+ end
+ end
end
end
diff --git a/activerecord/lib/active_record/relation/record_fetch_warning.rb b/activerecord/lib/active_record/relation/record_fetch_warning.rb
index dbd08811fa..31544c730e 100644
--- a/activerecord/lib/active_record/relation/record_fetch_warning.rb
+++ b/activerecord/lib/active_record/relation/record_fetch_warning.rb
@@ -2,15 +2,15 @@ module ActiveRecord
class Relation
module RecordFetchWarning
# When this module is prepended to ActiveRecord::Relation and
- # `config.active_record.warn_on_records_fetched_greater_than` is
+ # +config.active_record.warn_on_records_fetched_greater_than+ is
# set to an integer, if the number of records a query returns is
- # greater than the value of `warn_on_records_fetched_greater_than`,
+ # greater than the value of +warn_on_records_fetched_greater_than+,
# a warning is logged. This allows for the detection of queries that
# return a large number of records, which could cause memory bloat.
#
# In most cases, fetching large number of records can be performed
# efficiently using the ActiveRecord::Batches methods.
- # See active_record/lib/relation/batches.rb for more information.
+ # See ActiveRecord::Batches for more information.
def exec_queries
QueryRegistry.reset
diff --git a/activerecord/lib/active_record/relation/spawn_methods.rb b/activerecord/lib/active_record/relation/spawn_methods.rb
index d5c18a2a4a..ada89b5ec3 100644
--- a/activerecord/lib/active_record/relation/spawn_methods.rb
+++ b/activerecord/lib/active_record/relation/spawn_methods.rb
@@ -1,10 +1,9 @@
-require 'active_support/core_ext/hash/except'
-require 'active_support/core_ext/hash/slice'
-require 'active_record/relation/merger'
+require "active_support/core_ext/hash/except"
+require "active_support/core_ext/hash/slice"
+require "active_record/relation/merger"
module ActiveRecord
module SpawnMethods
-
# This is overridden by Associations::CollectionProxy
def spawn #:nodoc:
clone
@@ -67,7 +66,7 @@ module ActiveRecord
private
- def relation_with(values) # :nodoc:
+ def relation_with(values)
result = Relation.create(klass, table, predicate_builder, values)
result.extend(*extending_values) if extending_values.any?
result
diff --git a/activerecord/lib/active_record/relation/where_clause.rb b/activerecord/lib/active_record/relation/where_clause.rb
index 89396b518c..ef0d059d1c 100644
--- a/activerecord/lib/active_record/relation/where_clause.rb
+++ b/activerecord/lib/active_record/relation/where_clause.rb
@@ -84,91 +84,93 @@ module ActiveRecord
@empty ||= new([], [])
end
+ # TODO Change this to private once we've dropped Ruby 2.2 support.
+ # Workaround for Ruby 2.2 "private attribute?" warning.
protected
- attr_reader :predicates
+ attr_reader :predicates
- def referenced_columns
- @referenced_columns ||= begin
- equality_nodes = predicates.select { |n| equality_node?(n) }
- Set.new(equality_nodes, &:left)
+ def referenced_columns
+ @referenced_columns ||= begin
+ equality_nodes = predicates.select { |n| equality_node?(n) }
+ Set.new(equality_nodes, &:left)
+ end
end
- end
private
- def predicates_unreferenced_by(other)
- predicates.reject do |n|
- equality_node?(n) && other.referenced_columns.include?(n.left)
+ def predicates_unreferenced_by(other)
+ predicates.reject do |n|
+ equality_node?(n) && other.referenced_columns.include?(n.left)
+ end
end
- end
- def equality_node?(node)
- node.respond_to?(:operator) && node.operator == :==
- end
-
- def non_conflicting_binds(other)
- conflicts = referenced_columns & other.referenced_columns
- conflicts.map! { |node| node.name.to_s }
- binds.reject { |attr| conflicts.include?(attr.name) }
- end
+ def equality_node?(node)
+ node.respond_to?(:operator) && node.operator == :==
+ end
- def inverted_predicates
- predicates.map { |node| invert_predicate(node) }
- end
+ def non_conflicting_binds(other)
+ conflicts = referenced_columns & other.referenced_columns
+ conflicts.map! { |node| node.name.to_s }
+ binds.reject { |attr| conflicts.include?(attr.name) }
+ end
- def invert_predicate(node)
- case node
- when NilClass
- raise ArgumentError, 'Invalid argument for .where.not(), got nil.'
- when Arel::Nodes::In
- Arel::Nodes::NotIn.new(node.left, node.right)
- when Arel::Nodes::Equality
- Arel::Nodes::NotEqual.new(node.left, node.right)
- when String
- Arel::Nodes::Not.new(Arel::Nodes::SqlLiteral.new(node))
- else
- Arel::Nodes::Not.new(node)
+ def inverted_predicates
+ predicates.map { |node| invert_predicate(node) }
end
- end
- def predicates_except(columns)
- predicates.reject do |node|
+ def invert_predicate(node)
case node
- when Arel::Nodes::Between, Arel::Nodes::In, Arel::Nodes::NotIn, Arel::Nodes::Equality, Arel::Nodes::NotEqual, Arel::Nodes::LessThan, Arel::Nodes::LessThanOrEqual, Arel::Nodes::GreaterThan, Arel::Nodes::GreaterThanOrEqual
- subrelation = (node.left.kind_of?(Arel::Attributes::Attribute) ? node.left : node.right)
- columns.include?(subrelation.name.to_s)
+ when NilClass
+ raise ArgumentError, "Invalid argument for .where.not(), got nil."
+ when Arel::Nodes::In
+ Arel::Nodes::NotIn.new(node.left, node.right)
+ when Arel::Nodes::Equality
+ Arel::Nodes::NotEqual.new(node.left, node.right)
+ when String
+ Arel::Nodes::Not.new(Arel::Nodes::SqlLiteral.new(node))
+ else
+ Arel::Nodes::Not.new(node)
end
end
- end
- def binds_except(columns)
- binds.reject do |attr|
- columns.include?(attr.name)
+ def predicates_except(columns)
+ predicates.reject do |node|
+ case node
+ when Arel::Nodes::Between, Arel::Nodes::In, Arel::Nodes::NotIn, Arel::Nodes::Equality, Arel::Nodes::NotEqual, Arel::Nodes::LessThan, Arel::Nodes::LessThanOrEqual, Arel::Nodes::GreaterThan, Arel::Nodes::GreaterThanOrEqual
+ subrelation = (node.left.kind_of?(Arel::Attributes::Attribute) ? node.left : node.right)
+ columns.include?(subrelation.name.to_s)
+ end
+ end
end
- end
- def predicates_with_wrapped_sql_literals
- non_empty_predicates.map do |node|
- if Arel::Nodes::Equality === node
- node
- else
- wrap_sql_literal(node)
+ def binds_except(columns)
+ binds.reject do |attr|
+ columns.include?(attr.name)
end
end
- end
- ARRAY_WITH_EMPTY_STRING = ['']
- def non_empty_predicates
- predicates - ARRAY_WITH_EMPTY_STRING
- end
+ def predicates_with_wrapped_sql_literals
+ non_empty_predicates.map do |node|
+ if Arel::Nodes::Equality === node
+ node
+ else
+ wrap_sql_literal(node)
+ end
+ end
+ end
- def wrap_sql_literal(node)
- if ::String === node
- node = Arel.sql(node)
+ ARRAY_WITH_EMPTY_STRING = [""]
+ def non_empty_predicates
+ predicates - ARRAY_WITH_EMPTY_STRING
+ end
+
+ def wrap_sql_literal(node)
+ if ::String === node
+ node = Arel.sql(node)
+ end
+ Arel::Nodes::Grouping.new(node)
end
- Arel::Nodes::Grouping.new(node)
- end
end
end
end
diff --git a/activerecord/lib/active_record/relation/where_clause_factory.rb b/activerecord/lib/active_record/relation/where_clause_factory.rb
index dbf172a577..737bc278bd 100644
--- a/activerecord/lib/active_record/relation/where_clause_factory.rb
+++ b/activerecord/lib/active_record/relation/where_clause_factory.rb
@@ -7,8 +7,6 @@ module ActiveRecord
end
def build(opts, other)
- binds = []
-
case opts
when String, Array
parts = [klass.send(:sanitize_sql, other.empty? ? opts : ([opts] + other))]
@@ -22,17 +20,18 @@ module ActiveRecord
parts = predicate_builder.build_from_hash(attributes)
when Arel::Nodes::Node
parts = [opts]
- binds = other
else
raise ArgumentError, "Unsupported argument type: #{opts} (#{opts.class})"
end
- WhereClause.new(parts, binds)
+ WhereClause.new(parts, binds || [])
end
+ # TODO Change this to private once we've dropped Ruby 2.2 support.
+ # Workaround for Ruby 2.2 "private attribute?" warning.
protected
- attr_reader :klass, :predicate_builder
+ attr_reader :klass, :predicate_builder
end
end
end
diff --git a/activerecord/lib/active_record/result.rb b/activerecord/lib/active_record/result.rb
index b9fd0f5326..9ed70a9c2b 100644
--- a/activerecord/lib/active_record/result.rb
+++ b/activerecord/lib/active_record/result.rb
@@ -32,8 +32,6 @@ module ActiveRecord
class Result
include Enumerable
- IDENTITY_TYPE = Type::Value.new # :nodoc:
-
attr_reader :columns, :rows, :column_types
def initialize(columns, rows, column_types = {})
@@ -103,36 +101,36 @@ module ActiveRecord
private
- def column_type(name, type_overrides = {})
- type_overrides.fetch(name) do
- column_types.fetch(name, IDENTITY_TYPE)
+ def column_type(name, type_overrides = {})
+ type_overrides.fetch(name) do
+ column_types.fetch(name, Type.default_value)
+ end
end
- end
- def hash_rows
- @hash_rows ||=
- begin
- # We freeze the strings to prevent them getting duped when
- # used as keys in ActiveRecord::Base's @attributes hash
- columns = @columns.map { |c| c.dup.freeze }
- @rows.map { |row|
- # In the past we used Hash[columns.zip(row)]
- # though elegant, the verbose way is much more efficient
- # both time and memory wise cause it avoids a big array allocation
- # this method is called a lot and needs to be micro optimised
- hash = {}
-
- index = 0
- length = columns.length
-
- while index < length
- hash[columns[index]] = row[index]
- index += 1
- end
-
- hash
- }
- end
- end
+ def hash_rows
+ @hash_rows ||=
+ begin
+ # We freeze the strings to prevent them getting duped when
+ # used as keys in ActiveRecord::Base's @attributes hash
+ columns = @columns.map { |c| c.dup.freeze }
+ @rows.map { |row|
+ # In the past we used Hash[columns.zip(row)]
+ # though elegant, the verbose way is much more efficient
+ # both time and memory wise cause it avoids a big array allocation
+ # this method is called a lot and needs to be micro optimised
+ hash = {}
+
+ index = 0
+ length = columns.length
+
+ while index < length
+ hash[columns[index]] = row[index]
+ index += 1
+ end
+
+ hash
+ }
+ end
+ end
end
end
diff --git a/activerecord/lib/active_record/runtime_registry.rb b/activerecord/lib/active_record/runtime_registry.rb
index 9e86999c1b..b79eb2263f 100644
--- a/activerecord/lib/active_record/runtime_registry.rb
+++ b/activerecord/lib/active_record/runtime_registry.rb
@@ -1,4 +1,4 @@
-require 'active_support/per_thread_registry'
+require "active_support/per_thread_registry"
module ActiveRecord
# This is a thread locals registry for Active Record. For example:
diff --git a/activerecord/lib/active_record/sanitization.rb b/activerecord/lib/active_record/sanitization.rb
index a9e1fd0dad..647834b12e 100644
--- a/activerecord/lib/active_record/sanitization.rb
+++ b/activerecord/lib/active_record/sanitization.rb
@@ -1,220 +1,215 @@
+
module ActiveRecord
module Sanitization
extend ActiveSupport::Concern
module ClassMethods
- # Used to sanitize objects before they're used in an SQL SELECT statement.
- # Delegates to {connection.quote}[rdoc-ref:ConnectionAdapters::Quoting#quote].
- def sanitize(object) # :nodoc:
- connection.quote(object)
- end
- alias_method :quote_value, :sanitize
-
- protected
-
- # Accepts an array or string of SQL conditions and sanitizes
- # them into a valid SQL fragment for a WHERE clause.
- #
- # sanitize_sql_for_conditions(["name=? and group_id=?", "foo'bar", 4])
- # # => "name='foo''bar' and group_id=4"
- #
- # sanitize_sql_for_conditions(["name=:name and group_id=:group_id", name: "foo'bar", group_id: 4])
- # # => "name='foo''bar' and group_id='4'"
- #
- # sanitize_sql_for_conditions(["name='%s' and group_id='%s'", "foo'bar", 4])
- # # => "name='foo''bar' and group_id='4'"
- #
- # sanitize_sql_for_conditions("name='foo''bar' and group_id='4'")
- # # => "name='foo''bar' and group_id='4'"
- def sanitize_sql_for_conditions(condition)
- return nil if condition.blank?
-
- case condition
- when Array; sanitize_sql_array(condition)
- else condition
+ private
+
+ # Accepts an array or string of SQL conditions and sanitizes
+ # them into a valid SQL fragment for a WHERE clause.
+ #
+ # sanitize_sql_for_conditions(["name=? and group_id=?", "foo'bar", 4])
+ # # => "name='foo''bar' and group_id=4"
+ #
+ # sanitize_sql_for_conditions(["name=:name and group_id=:group_id", name: "foo'bar", group_id: 4])
+ # # => "name='foo''bar' and group_id='4'"
+ #
+ # sanitize_sql_for_conditions(["name='%s' and group_id='%s'", "foo'bar", 4])
+ # # => "name='foo''bar' and group_id='4'"
+ #
+ # sanitize_sql_for_conditions("name='foo''bar' and group_id='4'")
+ # # => "name='foo''bar' and group_id='4'"
+ def sanitize_sql_for_conditions(condition) # :doc:
+ return nil if condition.blank?
+
+ case condition
+ when Array; sanitize_sql_array(condition)
+ else condition
+ end
end
- end
- alias_method :sanitize_sql, :sanitize_sql_for_conditions
- alias_method :sanitize_conditions, :sanitize_sql
-
- # Accepts an array, hash, or string of SQL conditions and sanitizes
- # them into a valid SQL fragment for a SET clause.
- #
- # sanitize_sql_for_assignment(["name=? and group_id=?", nil, 4])
- # # => "name=NULL and group_id=4"
- #
- # sanitize_sql_for_assignment(["name=:name and group_id=:group_id", name: nil, group_id: 4])
- # # => "name=NULL and group_id=4"
- #
- # Post.send(:sanitize_sql_for_assignment, { name: nil, group_id: 4 })
- # # => "`posts`.`name` = NULL, `posts`.`group_id` = 4"
- #
- # sanitize_sql_for_assignment("name=NULL and group_id='4'")
- # # => "name=NULL and group_id='4'"
- def sanitize_sql_for_assignment(assignments, default_table_name = self.table_name)
- case assignments
- when Array; sanitize_sql_array(assignments)
- when Hash; sanitize_sql_hash_for_assignment(assignments, default_table_name)
- else assignments
+ alias :sanitize_sql :sanitize_sql_for_conditions
+ alias :sanitize_conditions :sanitize_sql
+ deprecate sanitize_conditions: :sanitize_sql
+
+ # Accepts an array, hash, or string of SQL conditions and sanitizes
+ # them into a valid SQL fragment for a SET clause.
+ #
+ # sanitize_sql_for_assignment(["name=? and group_id=?", nil, 4])
+ # # => "name=NULL and group_id=4"
+ #
+ # sanitize_sql_for_assignment(["name=:name and group_id=:group_id", name: nil, group_id: 4])
+ # # => "name=NULL and group_id=4"
+ #
+ # Post.send(:sanitize_sql_for_assignment, { name: nil, group_id: 4 })
+ # # => "`posts`.`name` = NULL, `posts`.`group_id` = 4"
+ #
+ # sanitize_sql_for_assignment("name=NULL and group_id='4'")
+ # # => "name=NULL and group_id='4'"
+ def sanitize_sql_for_assignment(assignments, default_table_name = self.table_name) # :doc:
+ case assignments
+ when Array; sanitize_sql_array(assignments)
+ when Hash; sanitize_sql_hash_for_assignment(assignments, default_table_name)
+ else assignments
+ end
end
- end
-
- # Accepts an array, or string of SQL conditions and sanitizes
- # them into a valid SQL fragment for an ORDER clause.
- #
- # sanitize_sql_for_order(["field(id, ?)", [1,3,2]])
- # # => "field(id, 1,3,2)"
- #
- # sanitize_sql_for_order("id ASC")
- # # => "id ASC"
- def sanitize_sql_for_order(condition)
- if condition.is_a?(Array) && condition.first.to_s.include?('?')
- sanitize_sql_array(condition)
- else
- condition
+
+ # Accepts an array, or string of SQL conditions and sanitizes
+ # them into a valid SQL fragment for an ORDER clause.
+ #
+ # sanitize_sql_for_order(["field(id, ?)", [1,3,2]])
+ # # => "field(id, 1,3,2)"
+ #
+ # sanitize_sql_for_order("id ASC")
+ # # => "id ASC"
+ def sanitize_sql_for_order(condition) # :doc:
+ if condition.is_a?(Array) && condition.first.to_s.include?("?")
+ sanitize_sql_array(condition)
+ else
+ condition
+ end
end
- end
-
- # Accepts a hash of SQL conditions and replaces those attributes
- # that correspond to a {#composed_of}[rdoc-ref:Aggregations::ClassMethods#composed_of]
- # relationship with their expanded aggregate attribute values.
- #
- # Given:
- #
- # class Person < ActiveRecord::Base
- # composed_of :address, class_name: "Address",
- # mapping: [%w(address_street street), %w(address_city city)]
- # end
- #
- # Then:
- #
- # { address: Address.new("813 abc st.", "chicago") }
- # # => { address_street: "813 abc st.", address_city: "chicago" }
- def expand_hash_conditions_for_aggregates(attrs)
- expanded_attrs = {}
- attrs.each do |attr, value|
- if aggregation = reflect_on_aggregation(attr.to_sym)
- mapping = aggregation.mapping
- mapping.each do |field_attr, aggregate_attr|
- if mapping.size == 1 && !value.respond_to?(aggregate_attr)
- expanded_attrs[field_attr] = value
- else
- expanded_attrs[field_attr] = value.send(aggregate_attr)
+
+ # Accepts a hash of SQL conditions and replaces those attributes
+ # that correspond to a {#composed_of}[rdoc-ref:Aggregations::ClassMethods#composed_of]
+ # relationship with their expanded aggregate attribute values.
+ #
+ # Given:
+ #
+ # class Person < ActiveRecord::Base
+ # composed_of :address, class_name: "Address",
+ # mapping: [%w(address_street street), %w(address_city city)]
+ # end
+ #
+ # Then:
+ #
+ # { address: Address.new("813 abc st.", "chicago") }
+ # # => { address_street: "813 abc st.", address_city: "chicago" }
+ def expand_hash_conditions_for_aggregates(attrs) # :doc:
+ expanded_attrs = {}
+ attrs.each do |attr, value|
+ if aggregation = reflect_on_aggregation(attr.to_sym)
+ mapping = aggregation.mapping
+ mapping.each do |field_attr, aggregate_attr|
+ if mapping.size == 1 && !value.respond_to?(aggregate_attr)
+ expanded_attrs[field_attr] = value
+ else
+ expanded_attrs[field_attr] = value.send(aggregate_attr)
+ end
end
+ else
+ expanded_attrs[attr] = value
end
- else
- expanded_attrs[attr] = value
end
+ expanded_attrs
end
- expanded_attrs
- end
-
- # Sanitizes a hash of attribute/value pairs into SQL conditions for a SET clause.
- #
- # sanitize_sql_hash_for_assignment({ status: nil, group_id: 1 }, "posts")
- # # => "`posts`.`status` = NULL, `posts`.`group_id` = 1"
- def sanitize_sql_hash_for_assignment(attrs, table)
- c = connection
- attrs.map do |attr, value|
- value = type_for_attribute(attr.to_s).serialize(value)
- "#{c.quote_table_name_for_assignment(table, attr)} = #{c.quote(value)}"
- end.join(', ')
- end
-
- # Sanitizes a +string+ so that it is safe to use within an SQL
- # LIKE statement. This method uses +escape_character+ to escape all occurrences of "\", "_" and "%".
- #
- # sanitize_sql_like("100%")
- # # => "100\\%"
- #
- # sanitize_sql_like("snake_cased_string")
- # # => "snake\\_cased\\_string"
- #
- # sanitize_sql_like("100%", "!")
- # # => "100!%"
- #
- # sanitize_sql_like("snake_cased_string", "!")
- # # => "snake!_cased!_string"
- def sanitize_sql_like(string, escape_character = "\\")
- pattern = Regexp.union(escape_character, "%", "_")
- string.gsub(pattern) { |x| [escape_character, x].join }
- end
-
- # Accepts an array of conditions. The array has each value
- # sanitized and interpolated into the SQL statement.
- #
- # sanitize_sql_array(["name=? and group_id=?", "foo'bar", 4])
- # # => "name='foo''bar' and group_id=4"
- #
- # sanitize_sql_array(["name=:name and group_id=:group_id", name: "foo'bar", group_id: 4])
- # # => "name='foo''bar' and group_id=4"
- #
- # sanitize_sql_array(["name='%s' and group_id='%s'", "foo'bar", 4])
- # # => "name='foo''bar' and group_id='4'"
- def sanitize_sql_array(ary)
- statement, *values = ary
- if values.first.is_a?(Hash) && statement =~ /:\w+/
- replace_named_bind_variables(statement, values.first)
- elsif statement.include?('?')
- replace_bind_variables(statement, values)
- elsif statement.blank?
- statement
- else
- statement % values.collect { |value| connection.quote_string(value.to_s) }
+
+ # Sanitizes a hash of attribute/value pairs into SQL conditions for a SET clause.
+ #
+ # sanitize_sql_hash_for_assignment({ status: nil, group_id: 1 }, "posts")
+ # # => "`posts`.`status` = NULL, `posts`.`group_id` = 1"
+ def sanitize_sql_hash_for_assignment(attrs, table) # :doc:
+ c = connection
+ attrs.map do |attr, value|
+ value = type_for_attribute(attr.to_s).serialize(value)
+ "#{c.quote_table_name_for_assignment(table, attr)} = #{c.quote(value)}"
+ end.join(", ")
+ end
+
+ # Sanitizes a +string+ so that it is safe to use within an SQL
+ # LIKE statement. This method uses +escape_character+ to escape all occurrences of "\", "_" and "%".
+ #
+ # sanitize_sql_like("100%")
+ # # => "100\\%"
+ #
+ # sanitize_sql_like("snake_cased_string")
+ # # => "snake\\_cased\\_string"
+ #
+ # sanitize_sql_like("100%", "!")
+ # # => "100!%"
+ #
+ # sanitize_sql_like("snake_cased_string", "!")
+ # # => "snake!_cased!_string"
+ def sanitize_sql_like(string, escape_character = "\\") # :doc:
+ pattern = Regexp.union(escape_character, "%", "_")
+ string.gsub(pattern) { |x| [escape_character, x].join }
end
- end
-
- def replace_bind_variables(statement, values) # :nodoc:
- raise_if_bind_arity_mismatch(statement, statement.count('?'), values.size)
- bound = values.dup
- c = connection
- statement.gsub(/\?/) do
- replace_bind_variable(bound.shift, c)
+
+ # Accepts an array of conditions. The array has each value
+ # sanitized and interpolated into the SQL statement.
+ #
+ # sanitize_sql_array(["name=? and group_id=?", "foo'bar", 4])
+ # # => "name='foo''bar' and group_id=4"
+ #
+ # sanitize_sql_array(["name=:name and group_id=:group_id", name: "foo'bar", group_id: 4])
+ # # => "name='foo''bar' and group_id=4"
+ #
+ # sanitize_sql_array(["name='%s' and group_id='%s'", "foo'bar", 4])
+ # # => "name='foo''bar' and group_id='4'"
+ def sanitize_sql_array(ary) # :doc:
+ statement, *values = ary
+ if values.first.is_a?(Hash) && /:\w+/.match?(statement)
+ replace_named_bind_variables(statement, values.first)
+ elsif statement.include?("?")
+ replace_bind_variables(statement, values)
+ elsif statement.blank?
+ statement
+ else
+ statement % values.collect { |value| connection.quote_string(value.to_s) }
+ end
end
- end
- def replace_bind_variable(value, c = connection) # :nodoc:
- if ActiveRecord::Relation === value
- value.to_sql
- else
- quote_bound_value(value, c)
+ def replace_bind_variables(statement, values)
+ raise_if_bind_arity_mismatch(statement, statement.count("?"), values.size)
+ bound = values.dup
+ c = connection
+ statement.gsub(/\?/) do
+ replace_bind_variable(bound.shift, c)
+ end
end
- end
-
- def replace_named_bind_variables(statement, bind_vars) # :nodoc:
- statement.gsub(/(:?):([a-zA-Z]\w*)/) do |match|
- if $1 == ':' # skip postgresql casts
- match # return the whole match
- elsif bind_vars.include?(match = $2.to_sym)
- replace_bind_variable(bind_vars[match])
+
+ def replace_bind_variable(value, c = connection)
+ if ActiveRecord::Relation === value
+ value.to_sql
else
- raise PreparedStatementInvalid, "missing value for :#{match} in #{statement}"
+ quote_bound_value(value, c)
end
end
- end
- def quote_bound_value(value, c = connection) # :nodoc:
- if value.respond_to?(:map) && !value.acts_like?(:string)
- if value.respond_to?(:empty?) && value.empty?
- c.quote(nil)
+ def replace_named_bind_variables(statement, bind_vars)
+ statement.gsub(/(:?):([a-zA-Z]\w*)/) do |match|
+ if $1 == ":" # skip postgresql casts
+ match # return the whole match
+ elsif bind_vars.include?(match = $2.to_sym)
+ replace_bind_variable(bind_vars[match])
+ else
+ raise PreparedStatementInvalid, "missing value for :#{match} in #{statement}"
+ end
+ end
+ end
+
+ def quote_bound_value(value, c = connection)
+ if value.respond_to?(:map) && !value.acts_like?(:string)
+ if value.respond_to?(:empty?) && value.empty?
+ c.quote(nil)
+ else
+ value.map { |v| c.quote(v) }.join(",")
+ end
else
- value.map { |v| c.quote(v) }.join(',')
+ c.quote(value)
end
- else
- c.quote(value)
end
- end
- def raise_if_bind_arity_mismatch(statement, expected, provided) # :nodoc:
- unless expected == provided
- raise PreparedStatementInvalid, "wrong number of bind variables (#{provided} for #{expected}) in: #{statement}"
+ def raise_if_bind_arity_mismatch(statement, expected, provided)
+ unless expected == provided
+ raise PreparedStatementInvalid, "wrong number of bind variables (#{provided} for #{expected}) in: #{statement}"
+ end
end
- end
end
# TODO: Deprecate this
def quoted_id # :nodoc:
- self.class.quote_value(@attributes[self.class.primary_key].value_for_database)
+ self.class.connection.quote(@attributes[self.class.primary_key].value_for_database)
end
end
end
diff --git a/activerecord/lib/active_record/schema.rb b/activerecord/lib/active_record/schema.rb
index 784a02d2c3..7a2bc9c8af 100644
--- a/activerecord/lib/active_record/schema.rb
+++ b/activerecord/lib/active_record/schema.rb
@@ -40,7 +40,7 @@ module ActiveRecord
# ActiveRecord::Schema.define(version: 20380119000001) do
# ...
# end
- def self.define(info={}, &block)
+ def self.define(info = {}, &block)
new.define(info, &block)
end
@@ -61,7 +61,7 @@ module ActiveRecord
#
# ActiveRecord::Schema.new.migrations_paths
# # => ["db/migrate"] # Rails migration path by default.
- def migrations_paths # :nodoc:
+ def migrations_paths
ActiveRecord::Migrator.migrations_paths
end
end
diff --git a/activerecord/lib/active_record/schema_dumper.rb b/activerecord/lib/active_record/schema_dumper.rb
index d769376d1a..12289511b7 100644
--- a/activerecord/lib/active_record/schema_dumper.rb
+++ b/activerecord/lib/active_record/schema_dumper.rb
@@ -1,4 +1,4 @@
-require 'stringio'
+require "stringio"
module ActiveRecord
# = Active Record Schema Dumper
@@ -17,7 +17,7 @@ module ActiveRecord
@@ignore_tables = []
class << self
- def dump(connection=ActiveRecord::Base.connection, stream=STDOUT, config = ActiveRecord::Base)
+ def dump(connection = ActiveRecord::Base.connection, stream = STDOUT, config = ActiveRecord::Base)
new(connection, generate_options(config)).dump(stream)
stream
end
@@ -105,24 +105,17 @@ HEADER
tbl = StringIO.new
# first dump primary key column
- if @connection.respond_to?(:primary_keys)
- pk = @connection.primary_keys(table)
- pk = pk.first unless pk.size > 1
- else
- pk = @connection.primary_key(table)
- end
+ pk = @connection.primary_key(table)
tbl.print " create_table #{remove_prefix_and_suffix(table).inspect}"
case pk
when String
- tbl.print ", primary_key: #{pk.inspect}" unless pk == 'id'
+ tbl.print ", primary_key: #{pk.inspect}" unless pk == "id"
pkcol = columns.detect { |c| c.name == pk }
pkcolspec = @connection.column_spec_for_primary_key(pkcol)
if pkcolspec.present?
- pkcolspec.each do |key, value|
- tbl.print ", #{key}: #{value}"
- end
+ tbl.print ", #{format_colspec(pkcolspec)}"
end
when Array
tbl.print ", primary_key: #{pk.inspect}"
@@ -132,46 +125,19 @@ HEADER
tbl.print ", force: :cascade"
table_options = @connection.table_options(table)
- tbl.print ", options: #{table_options.inspect}" unless table_options.blank?
-
- if comment = @connection.table_comment(table).presence
- tbl.print ", comment: #{comment.inspect}"
+ if table_options.present?
+ tbl.print ", #{format_options(table_options)}"
end
tbl.puts " do |t|"
# then dump all non-primary key columns
- column_specs = columns.map do |column|
+ columns.each do |column|
raise StandardError, "Unknown type '#{column.sql_type}' for column '#{column.name}'" unless @connection.valid_type?(column.type)
next if column.name == pk
- @connection.column_spec(column)
- end.compact
-
- # find all migration keys used in this table
- keys = @connection.migration_keys
-
- # figure out the lengths for each column based on above keys
- lengths = keys.map { |key|
- column_specs.map { |spec|
- spec[key] ? spec[key].length + 2 : 0
- }.max
- }
-
- # the string we're going to sprintf our values against, with standardized column widths
- format_string = lengths.map{ |len| "%-#{len}s" }
-
- # find the max length for the 'type' column, which is special
- type_length = column_specs.map{ |column| column[:type].length }.max
-
- # add column type definition to our format string
- format_string.unshift " t.%-#{type_length}s "
-
- format_string *= ''
-
- column_specs.each do |colspec|
- values = keys.zip(lengths).map{ |key, len| colspec.key?(key) ? colspec[key] + ", " : " " * len }
- values.unshift colspec[:type]
- tbl.print((format_string % values).gsub(/,\s*$/, ''))
+ type, colspec = @connection.column_spec(column)
+ tbl.print " t.#{type} #{column.name.inspect}"
+ tbl.print ", #{format_colspec(colspec)}" if colspec.present?
tbl.puts
end
@@ -196,7 +162,7 @@ HEADER
if (indexes = @connection.indexes(table)).any?
add_index_statements = indexes.map do |index|
table_name = remove_prefix_and_suffix(index.table).inspect
- " add_index #{([table_name]+index_parts(index)).join(', ')}"
+ " add_index #{([table_name] + index_parts(index)).join(', ')}"
end
stream.puts add_index_statements.sort.join("\n")
@@ -218,13 +184,9 @@ HEADER
index.columns.inspect,
"name: #{index.name.inspect}",
]
- index_parts << 'unique: true' if index.unique
-
- index_lengths = (index.lengths || []).compact
- index_parts << "length: #{Hash[index.columns.zip(index.lengths)].inspect}" if index_lengths.any?
-
- index_orders = index.orders || {}
- index_parts << "order: #{index.orders.inspect}" if index_orders.any?
+ index_parts << "unique: true" if index.unique
+ index_parts << "length: { #{format_options(index.lengths)} }" if index.lengths.present?
+ index_parts << "order: { #{format_options(index.orders)} }" if index.orders.present?
index_parts << "where: #{index.where.inspect}" if index.where
index_parts << "using: #{index.using.inspect}" if index.using
index_parts << "type: #{index.type.inspect}" if index.type
@@ -262,6 +224,14 @@ HEADER
end
end
+ def format_colspec(colspec)
+ colspec.map { |key, value| "#{key}: #{value}" }.join(", ")
+ end
+
+ def format_options(options)
+ options.map { |key, value| "#{key}: #{value.inspect}" }.join(", ")
+ end
+
def remove_prefix_and_suffix(table)
table.gsub(/^(#{@options[:table_name_prefix]})(.+)(#{@options[:table_name_suffix]})$/, "\\2")
end
diff --git a/activerecord/lib/active_record/schema_migration.rb b/activerecord/lib/active_record/schema_migration.rb
index b6cb233e03..5efbcff96a 100644
--- a/activerecord/lib/active_record/schema_migration.rb
+++ b/activerecord/lib/active_record/schema_migration.rb
@@ -1,5 +1,5 @@
-require 'active_record/scoping/default'
-require 'active_record/scoping/named'
+require "active_record/scoping/default"
+require "active_record/scoping/named"
module ActiveRecord
# This class is used to create a table that keeps track of which migrations
@@ -17,7 +17,7 @@ module ActiveRecord
end
def table_exists?
- ActiveSupport::Deprecation.silence { connection.table_exists?(table_name) }
+ connection.table_exists?(table_name)
end
def create_table
diff --git a/activerecord/lib/active_record/scoping.rb b/activerecord/lib/active_record/scoping.rb
index 7794af8ca4..7c00e7e4ed 100644
--- a/activerecord/lib/active_record/scoping.rb
+++ b/activerecord/lib/active_record/scoping.rb
@@ -1,4 +1,4 @@
-require 'active_support/per_thread_registry'
+require "active_support/per_thread_registry"
module ActiveRecord
module Scoping
@@ -33,7 +33,7 @@ module ActiveRecord
def populate_with_current_scope_attributes # :nodoc:
return unless self.class.scope_attributes?
- self.class.scope_attributes.each do |att,value|
+ self.class.scope_attributes.each do |att, value|
send("#{att}=", value) if respond_to?("#{att}=")
end
end
@@ -94,11 +94,11 @@ module ActiveRecord
private
- def raise_invalid_scope_type!(scope_type)
- if !VALID_SCOPE_TYPES.include?(scope_type)
- raise ArgumentError, "Invalid scope type '#{scope_type}' sent to the registry. Scope types must be included in VALID_SCOPE_TYPES"
+ def raise_invalid_scope_type!(scope_type)
+ if !VALID_SCOPE_TYPES.include?(scope_type)
+ raise ArgumentError, "Invalid scope type '#{scope_type}' sent to the registry. Scope types must be included in VALID_SCOPE_TYPES"
+ end
end
- end
end
end
end
diff --git a/activerecord/lib/active_record/scoping/default.rb b/activerecord/lib/active_record/scoping/default.rb
index 9eab59ac78..2daa48859a 100644
--- a/activerecord/lib/active_record/scoping/default.rb
+++ b/activerecord/lib/active_record/scoping/default.rb
@@ -44,105 +44,105 @@ module ActiveRecord
self.current_scope = nil
end
- protected
+ private
+
+ # Use this macro in your model to set a default scope for all operations on
+ # the model.
+ #
+ # class Article < ActiveRecord::Base
+ # default_scope { where(published: true) }
+ # end
+ #
+ # Article.all # => SELECT * FROM articles WHERE published = true
+ #
+ # The #default_scope is also applied while creating/building a record.
+ # It is not applied while updating a record.
+ #
+ # Article.new.published # => true
+ # Article.create.published # => true
+ #
+ # (You can also pass any object which responds to +call+ to the
+ # +default_scope+ macro, and it will be called when building the
+ # default scope.)
+ #
+ # If you use multiple #default_scope declarations in your model then
+ # they will be merged together:
+ #
+ # class Article < ActiveRecord::Base
+ # default_scope { where(published: true) }
+ # default_scope { where(rating: 'G') }
+ # end
+ #
+ # Article.all # => SELECT * FROM articles WHERE published = true AND rating = 'G'
+ #
+ # This is also the case with inheritance and module includes where the
+ # parent or module defines a #default_scope and the child or including
+ # class defines a second one.
+ #
+ # If you need to do more complex things with a default scope, you can
+ # alternatively define it as a class method:
+ #
+ # class Article < ActiveRecord::Base
+ # def self.default_scope
+ # # Should return a scope, you can call 'super' here etc.
+ # end
+ # end
+ def default_scope(scope = nil) # :doc:
+ scope = Proc.new if block_given?
+
+ if scope.is_a?(Relation) || !scope.respond_to?(:call)
+ raise ArgumentError,
+ "Support for calling #default_scope without a block is removed. For example instead " \
+ "of `default_scope where(color: 'red')`, please use " \
+ "`default_scope { where(color: 'red') }`. (Alternatively you can just redefine " \
+ "self.default_scope.)"
+ end
- # Use this macro in your model to set a default scope for all operations on
- # the model.
- #
- # class Article < ActiveRecord::Base
- # default_scope { where(published: true) }
- # end
- #
- # Article.all # => SELECT * FROM articles WHERE published = true
- #
- # The #default_scope is also applied while creating/building a record.
- # It is not applied while updating a record.
- #
- # Article.new.published # => true
- # Article.create.published # => true
- #
- # (You can also pass any object which responds to +call+ to the
- # +default_scope+ macro, and it will be called when building the
- # default scope.)
- #
- # If you use multiple #default_scope declarations in your model then
- # they will be merged together:
- #
- # class Article < ActiveRecord::Base
- # default_scope { where(published: true) }
- # default_scope { where(rating: 'G') }
- # end
- #
- # Article.all # => SELECT * FROM articles WHERE published = true AND rating = 'G'
- #
- # This is also the case with inheritance and module includes where the
- # parent or module defines a #default_scope and the child or including
- # class defines a second one.
- #
- # If you need to do more complex things with a default scope, you can
- # alternatively define it as a class method:
- #
- # class Article < ActiveRecord::Base
- # def self.default_scope
- # # Should return a scope, you can call 'super' here etc.
- # end
- # end
- def default_scope(scope = nil)
- scope = Proc.new if block_given?
-
- if scope.is_a?(Relation) || !scope.respond_to?(:call)
- raise ArgumentError,
- "Support for calling #default_scope without a block is removed. For example instead " \
- "of `default_scope where(color: 'red')`, please use " \
- "`default_scope { where(color: 'red') }`. (Alternatively you can just redefine " \
- "self.default_scope.)"
+ self.default_scopes += [scope]
end
- self.default_scopes += [scope]
- end
+ def build_default_scope(base_rel = nil)
+ return if abstract_class?
- def build_default_scope(base_rel = nil) # :nodoc:
- return if abstract_class?
-
- if self.default_scope_override.nil?
- self.default_scope_override = !Base.is_a?(method(:default_scope).owner)
- end
+ if default_scope_override.nil?
+ self.default_scope_override = !Base.is_a?(method(:default_scope).owner)
+ end
- if self.default_scope_override
- # The user has defined their own default scope method, so call that
- evaluate_default_scope { default_scope }
- elsif default_scopes.any?
- base_rel ||= relation
- evaluate_default_scope do
- default_scopes.inject(base_rel) do |default_scope, scope|
- scope = scope.respond_to?(:to_proc) ? scope : scope.method(:call)
- default_scope.merge(base_rel.instance_exec(&scope))
+ if default_scope_override
+ # The user has defined their own default scope method, so call that
+ evaluate_default_scope { default_scope }
+ elsif default_scopes.any?
+ base_rel ||= relation
+ evaluate_default_scope do
+ default_scopes.inject(base_rel) do |default_scope, scope|
+ scope = scope.respond_to?(:to_proc) ? scope : scope.method(:call)
+ default_scope.merge(base_rel.instance_exec(&scope))
+ end
end
end
end
- end
- def ignore_default_scope? # :nodoc:
- ScopeRegistry.value_for(:ignore_default_scope, base_class)
- end
+ def ignore_default_scope?
+ ScopeRegistry.value_for(:ignore_default_scope, base_class)
+ end
- def ignore_default_scope=(ignore) # :nodoc:
- ScopeRegistry.set_value_for(:ignore_default_scope, base_class, ignore)
- end
+ def ignore_default_scope=(ignore)
+ ScopeRegistry.set_value_for(:ignore_default_scope, base_class, ignore)
+ end
- # The ignore_default_scope flag is used to prevent an infinite recursion
- # situation where a default scope references a scope which has a default
- # scope which references a scope...
- def evaluate_default_scope # :nodoc:
- return if ignore_default_scope?
-
- begin
- self.ignore_default_scope = true
- yield
- ensure
- self.ignore_default_scope = false
+ # The ignore_default_scope flag is used to prevent an infinite recursion
+ # situation where a default scope references a scope which has a default
+ # scope which references a scope...
+ def evaluate_default_scope
+ return if ignore_default_scope?
+
+ begin
+ self.ignore_default_scope = true
+ yield
+ ensure
+ self.ignore_default_scope = false
+ end
end
- end
end
end
end
diff --git a/activerecord/lib/active_record/scoping/named.rb b/activerecord/lib/active_record/scoping/named.rb
index 5395bd6076..27cdf8cb7e 100644
--- a/activerecord/lib/active_record/scoping/named.rb
+++ b/activerecord/lib/active_record/scoping/named.rb
@@ -1,6 +1,6 @@
-require 'active_support/core_ext/array'
-require 'active_support/core_ext/hash/except'
-require 'active_support/core_ext/kernel/singleton_class'
+require "active_support/core_ext/array"
+require "active_support/core_ext/hash/except"
+require "active_support/core_ext/kernel/singleton_class"
module ActiveRecord
# = Active Record \Named \Scopes
@@ -42,7 +42,7 @@ module ActiveRecord
# Adds a class method for retrieving and querying objects.
# The method is intended to return an ActiveRecord::Relation
# object, which is composable with other scopes.
- # If it returns nil or false, an
+ # If it returns +nil+ or +false+, an
# {all}[rdoc-ref:Scoping::Named::ClassMethods#all] scope is returned instead.
#
# A \scope represents a narrowing of a database query, such as
@@ -142,7 +142,7 @@ module ActiveRecord
# Article.featured.titles
def scope(name, body, &block)
unless body.respond_to?(:call)
- raise ArgumentError, 'The scope body needs to be callable.'
+ raise ArgumentError, "The scope body needs to be callable."
end
if dangerous_class_method?(name)
@@ -171,14 +171,14 @@ module ActiveRecord
end
end
- protected
+ private
- def valid_scope_name?(name)
- if respond_to?(name, true)
- logger.warn "Creating scope :#{name}. " \
- "Overwriting existing method #{self.name}.#{name}."
+ def valid_scope_name?(name)
+ if respond_to?(name, true) && logger
+ logger.warn "Creating scope :#{name}. " \
+ "Overwriting existing method #{self.name}.#{name}."
+ end
end
- end
end
end
end
diff --git a/activerecord/lib/active_record/secure_token.rb b/activerecord/lib/active_record/secure_token.rb
index 8abda2ac49..7606961e2e 100644
--- a/activerecord/lib/active_record/secure_token.rb
+++ b/activerecord/lib/active_record/secure_token.rb
@@ -25,9 +25,9 @@ module ActiveRecord
# You're encouraged to add a unique index in the database to deal with this even more unlikely scenario.
def has_secure_token(attribute = :token)
# Load securerandom only when has_secure_token is used.
- require 'active_support/core_ext/securerandom'
+ require "active_support/core_ext/securerandom"
define_method("regenerate_#{attribute}") { update! attribute => self.class.generate_unique_secure_token }
- before_create { self.send("#{attribute}=", self.class.generate_unique_secure_token) unless self.send("#{attribute}?")}
+ before_create { self.send("#{attribute}=", self.class.generate_unique_secure_token) unless self.send("#{attribute}?") }
end
def generate_unique_secure_token
diff --git a/activerecord/lib/active_record/statement_cache.rb b/activerecord/lib/active_record/statement_cache.rb
index 6c896ccea6..1877489e55 100644
--- a/activerecord/lib/active_record/statement_cache.rb
+++ b/activerecord/lib/active_record/statement_cache.rb
@@ -1,5 +1,4 @@
module ActiveRecord
-
# Statement cache is used to cache a single statement in order to avoid creating the AST again.
# Initializing the cache is done by passing the statement in the create block:
#
@@ -8,12 +7,12 @@ module ActiveRecord
# end
#
# The cached statement is executed by using the
- # [connection.execute]{rdoc-ref:ConnectionAdapters::DatabaseStatements#execute} method:
+ # {connection.execute}[rdoc-ref:ConnectionAdapters::DatabaseStatements#execute] method:
#
# cache.execute([], Book, Book.connection)
#
# The relation returned by the block is cached, and for each
- # [execute]{rdoc-ref:ConnectionAdapters::DatabaseStatements#execute}
+ # {execute}[rdoc-ref:ConnectionAdapters::DatabaseStatements#execute]
# call the cached relation gets duped. Database is queried when +to_a+ is called on the relation.
#
# If you want to cache the statement without the values you can use the +bind+ method of the
@@ -40,28 +39,27 @@ module ActiveRecord
end
class PartialQuery < Query # :nodoc:
- def initialize values
+ def initialize(values)
@values = values
- @indexes = values.each_with_index.find_all { |thing,i|
+ @indexes = values.each_with_index.find_all { |thing, i|
Arel::Nodes::BindParam === thing
}.map(&:last)
end
def sql_for(binds, connection)
val = @values.dup
- binds = connection.prepare_binds_for_database(binds)
- @indexes.each { |i| val[i] = connection.quote(binds.shift) }
+ casted_binds = binds.map(&:value_for_database)
+ @indexes.each { |i| val[i] = connection.quote(casted_binds.shift) }
val.join
end
end
- def self.query(visitor, ast)
- Query.new visitor.accept(ast, Arel::Collectors::SQLString.new).value
+ def self.query(sql)
+ Query.new(sql)
end
- def self.partial_query(visitor, ast, collector)
- collected = visitor.accept(ast, collector).value
- PartialQuery.new collected
+ def self.partial_query(values)
+ PartialQuery.new(values)
end
class Params # :nodoc:
@@ -70,7 +68,7 @@ module ActiveRecord
class BindMap # :nodoc:
def initialize(bound_attributes)
- @indexes = []
+ @indexes = []
@bound_attributes = bound_attributes
bound_attributes.each_with_index do |attr, i|
@@ -82,7 +80,7 @@ module ActiveRecord
def bind(values)
bas = @bound_attributes.dup
- @indexes.each_with_index { |offset,i| bas[offset] = bas[offset].with_cast_value(values[i]) }
+ @indexes.each_with_index { |offset, i| bas[offset] = bas[offset].with_cast_value(values[i]) }
bas
end
end
@@ -92,7 +90,7 @@ module ActiveRecord
def self.create(connection, block = Proc.new)
relation = block.call Params.new
bind_map = BindMap.new relation.bound_attributes
- query_builder = connection.cacheable_query relation.arel
+ query_builder = connection.cacheable_query(self, relation.arel)
new query_builder, bind_map
end
@@ -101,12 +99,12 @@ module ActiveRecord
@bind_map = bind_map
end
- def execute(params, klass, connection)
+ def execute(params, klass, connection, &block)
bind_values = bind_map.bind params
sql = query_builder.sql_for bind_values, connection
- klass.find_by_sql(sql, bind_values, preparable: true)
+ klass.find_by_sql(sql, bind_values, preparable: true, &block)
end
alias :call :execute
end
diff --git a/activerecord/lib/active_record/store.rb b/activerecord/lib/active_record/store.rb
index 1b407f7702..d4be20d999 100644
--- a/activerecord/lib/active_record/store.rb
+++ b/activerecord/lib/active_record/store.rb
@@ -1,4 +1,4 @@
-require 'active_support/core_ext/hash/indifferent_access'
+require "active_support/core_ext/hash/indifferent_access"
module ActiveRecord
# Store gives you a thin wrapper around serialize for the purpose of storing hashes in a single column.
@@ -114,25 +114,24 @@ module ActiveRecord
def stored_attributes
parent = superclass.respond_to?(:stored_attributes) ? superclass.stored_attributes : {}
- if self.local_stored_attributes
- parent.merge!(self.local_stored_attributes) { |k, a, b| a | b }
+ if local_stored_attributes
+ parent.merge!(local_stored_attributes) { |k, a, b| a | b }
end
parent
end
end
- protected
- def read_store_attribute(store_attribute, key)
+ private
+ def read_store_attribute(store_attribute, key) # :doc:
accessor = store_accessor_for(store_attribute)
accessor.read(self, store_attribute, key)
end
- def write_store_attribute(store_attribute, key, value)
+ def write_store_attribute(store_attribute, key, value) # :doc:
accessor = store_accessor_for(store_attribute)
accessor.write(self, store_attribute, key, value)
end
- private
def store_accessor_for(store_attribute)
type_for_attribute(store_attribute.to_s).accessor
end
@@ -177,34 +176,34 @@ module ActiveRecord
end
end
- class IndifferentCoder # :nodoc:
- def initialize(coder_or_class_name)
- @coder =
- if coder_or_class_name.respond_to?(:load) && coder_or_class_name.respond_to?(:dump)
- coder_or_class_name
- else
- ActiveRecord::Coders::YAMLColumn.new(coder_or_class_name || Object)
- end
- end
+ class IndifferentCoder # :nodoc:
+ def initialize(coder_or_class_name)
+ @coder =
+ if coder_or_class_name.respond_to?(:load) && coder_or_class_name.respond_to?(:dump)
+ coder_or_class_name
+ else
+ ActiveRecord::Coders::YAMLColumn.new(coder_or_class_name || Object)
+ end
+ end
- def dump(obj)
- @coder.dump self.class.as_indifferent_hash(obj)
- end
+ def dump(obj)
+ @coder.dump self.class.as_indifferent_hash(obj)
+ end
- def load(yaml)
- self.class.as_indifferent_hash(@coder.load(yaml || ''))
- end
+ def load(yaml)
+ self.class.as_indifferent_hash(@coder.load(yaml || ""))
+ end
- def self.as_indifferent_hash(obj)
- case obj
- when ActiveSupport::HashWithIndifferentAccess
- obj
- when Hash
- obj.with_indifferent_access
- else
- ActiveSupport::HashWithIndifferentAccess.new
+ def self.as_indifferent_hash(obj)
+ case obj
+ when ActiveSupport::HashWithIndifferentAccess
+ obj
+ when Hash
+ obj.with_indifferent_access
+ else
+ ActiveSupport::HashWithIndifferentAccess.new
+ end
end
end
- end
end
end
diff --git a/activerecord/lib/active_record/table_metadata.rb b/activerecord/lib/active_record/table_metadata.rb
index a1326aa359..b618e5cfcd 100644
--- a/activerecord/lib/active_record/table_metadata.rb
+++ b/activerecord/lib/active_record/table_metadata.rb
@@ -10,9 +10,7 @@ module ActiveRecord
end
def resolve_column_aliases(hash)
- # This method is a hot spot, so for now, use Hash[] to dup the hash.
- # https://bugs.ruby-lang.org/issues/7166
- new_hash = Hash[hash]
+ new_hash = hash.dup
hash.each do |key, _|
if (key.is_a?(Symbol)) && klass.attribute_alias?(key)
new_hash[klass.attribute_alias(key)] = new_hash.delete(key)
@@ -33,20 +31,24 @@ module ActiveRecord
if klass
klass.type_for_attribute(column_name.to_s)
else
- Type::Value.new
+ Type.default_value
end
end
+ def has_column?(column_name)
+ klass && klass.columns_hash.key?(column_name.to_s)
+ end
+
def associated_with?(association_name)
klass && klass._reflect_on_association(association_name)
end
def associated_table(table_name)
- return self if table_name == arel_table.name
-
association = klass._reflect_on_association(table_name) || klass._reflect_on_association(table_name.singularize)
- if association && !association.polymorphic?
+ if !association && table_name == arel_table.name
+ return self
+ elsif association && !association.polymorphic?
association_klass = association.klass
arel_table = association_klass.arel_table.alias(table_name)
else
@@ -62,8 +64,10 @@ module ActiveRecord
association && association.polymorphic?
end
+ # TODO Change this to private once we've dropped Ruby 2.2 support.
+ # Workaround for Ruby 2.2 "private attribute?" warning.
protected
- attr_reader :klass, :arel_table, :association
+ attr_reader :klass, :arel_table, :association
end
end
diff --git a/activerecord/lib/active_record/tasks/database_tasks.rb b/activerecord/lib/active_record/tasks/database_tasks.rb
index e3e665e149..1423e6008f 100644
--- a/activerecord/lib/active_record/tasks/database_tasks.rb
+++ b/activerecord/lib/active_record/tasks/database_tasks.rb
@@ -1,4 +1,4 @@
-require 'active_support/core_ext/string/filters'
+require "active_support/core_ext/string/filters"
module ActiveRecord
module Tasks # :nodoc:
@@ -35,15 +35,25 @@ module ActiveRecord
#
# DatabaseTasks.create_current('production')
module DatabaseTasks
+ ##
+ # :singleton-method:
+ # Extra flags passed to database CLI tool (mysqldump/pg_dump) when calling db:structure:dump
+ mattr_accessor :structure_dump_flags, instance_accessor: false
+
+ ##
+ # :singleton-method:
+ # Extra flags passed to database CLI tool when calling db:structure:load
+ mattr_accessor :structure_load_flags, instance_accessor: false
+
extend self
attr_writer :current_config, :db_dir, :migrations_paths, :fixtures_path, :root, :env, :seed_loader
attr_accessor :database_configuration
- LOCAL_HOSTS = ['127.0.0.1', 'localhost']
+ LOCAL_HOSTS = ["127.0.0.1", "localhost"]
def check_protected_environments!
- unless ENV['DISABLE_DATABASE_ENVIRONMENT_CHECK']
+ unless ENV["DISABLE_DATABASE_ENVIRONMENT_CHECK"]
current = ActiveRecord::Migrator.current_environment
stored = ActiveRecord::Migrator.last_stored_environment
@@ -72,15 +82,15 @@ module ActiveRecord
end
def migrations_paths
- @migrations_paths ||= Rails.application.paths['db/migrate'].to_a
+ @migrations_paths ||= Rails.application.paths["db/migrate"].to_a
end
def fixtures_path
- @fixtures_path ||= if ENV['FIXTURES_PATH']
- File.join(root, ENV['FIXTURES_PATH'])
- else
- File.join(root, 'test', 'fixtures')
- end
+ @fixtures_path ||= if ENV["FIXTURES_PATH"]
+ File.join(root, ENV["FIXTURES_PATH"])
+ else
+ File.join(root, "test", "fixtures")
+ end
end
def root
@@ -96,7 +106,7 @@ module ActiveRecord
end
def current_config(options = {})
- options.reverse_merge! :env => env
+ options.reverse_merge! env: env
if options.has_key?(:config)
@current_config = options[:config]
else
@@ -106,7 +116,7 @@ module ActiveRecord
def create(*arguments)
configuration = arguments.first
- class_for_adapter(configuration['adapter']).new(*arguments).create
+ class_for_adapter(configuration["adapter"]).new(*arguments).create
$stdout.puts "Created database '#{configuration['database']}'"
rescue DatabaseAlreadyExists
$stderr.puts "Database '#{configuration['database']}' already exists"
@@ -133,7 +143,7 @@ module ActiveRecord
def drop(*arguments)
configuration = arguments.first
- class_for_adapter(configuration['adapter']).new(*arguments).drop
+ class_for_adapter(configuration["adapter"]).new(*arguments).drop
$stdout.puts "Dropped database '#{configuration['database']}'"
rescue ActiveRecord::NoDatabaseError
$stderr.puts "Database '#{configuration['database']}' does not exist"
@@ -156,7 +166,7 @@ module ActiveRecord
def migrate
verbose = ENV["VERBOSE"] ? ENV["VERBOSE"] == "true" : true
version = ENV["VERSION"] ? ENV["VERSION"].to_i : nil
- scope = ENV['SCOPE']
+ scope = ENV["SCOPE"]
verbose_was, Migration.verbose = Migration.verbose, verbose
Migrator.migrate(migrations_paths, version) do |migration|
scope.blank? || scope == migration.scope
@@ -172,7 +182,7 @@ module ActiveRecord
def charset(*arguments)
configuration = arguments.first
- class_for_adapter(configuration['adapter']).new(*arguments).charset
+ class_for_adapter(configuration["adapter"]).new(*arguments).charset
end
def collation_current(environment = env)
@@ -181,11 +191,11 @@ module ActiveRecord
def collation(*arguments)
configuration = arguments.first
- class_for_adapter(configuration['adapter']).new(*arguments).collation
+ class_for_adapter(configuration["adapter"]).new(*arguments).collation
end
def purge(configuration)
- class_for_adapter(configuration['adapter']).new(configuration).purge
+ class_for_adapter(configuration["adapter"]).new(configuration).purge
end
def purge_all
@@ -204,13 +214,13 @@ module ActiveRecord
def structure_dump(*arguments)
configuration = arguments.first
filename = arguments.delete_at 1
- class_for_adapter(configuration['adapter']).new(*arguments).structure_dump(filename)
+ class_for_adapter(configuration["adapter"]).new(*arguments).structure_dump(filename, structure_dump_flags)
end
def structure_load(*arguments)
configuration = arguments.first
filename = arguments.delete_at 1
- class_for_adapter(configuration['adapter']).new(*arguments).structure_load(filename)
+ class_for_adapter(configuration["adapter"]).new(*arguments).structure_load(filename, structure_load_flags)
end
def load_schema(configuration, format = ActiveRecord::Base.schema_format, file = nil) # :nodoc:
@@ -231,14 +241,6 @@ module ActiveRecord
ActiveRecord::InternalMetadata[:environment] = ActiveRecord::Migrator.current_environment
end
- def load_schema_for(*args)
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
- This method was renamed to `#load_schema` and will be removed in the future.
- Use `#load_schema` instead.
- MSG
- load_schema(*args)
- end
-
def schema_file(format = ActiveRecord::Base.schema_format)
case format
when :ruby
@@ -273,41 +275,51 @@ module ActiveRecord
end
end
+ # Dumps the schema cache in YAML format for the connection into the file
+ #
+ # ==== Examples:
+ # ActiveRecord::Tasks::DatabaseTasks.dump_schema_cache(ActiveRecord::Base.connection, "tmp/schema_dump.yaml")
+ def dump_schema_cache(conn, filename)
+ conn.schema_cache.clear!
+ conn.data_sources.each { |table| conn.schema_cache.add(table) }
+ open(filename, "wb") { |f| f.write(YAML.dump(conn.schema_cache)) }
+ end
+
private
- def class_for_adapter(adapter)
- key = @tasks.keys.detect { |pattern| adapter[pattern] }
- unless key
- raise DatabaseNotSupported, "Rake tasks not supported by '#{adapter}' adapter"
+ def class_for_adapter(adapter)
+ key = @tasks.keys.detect { |pattern| adapter[pattern] }
+ unless key
+ raise DatabaseNotSupported, "Rake tasks not supported by '#{adapter}' adapter"
+ end
+ @tasks[key]
end
- @tasks[key]
- end
- def each_current_configuration(environment)
- environments = [environment]
- environments << 'test' if environment == 'development'
+ def each_current_configuration(environment)
+ environments = [environment]
+ environments << "test" if environment == "development"
- configurations = ActiveRecord::Base.configurations.values_at(*environments)
- configurations.compact.each do |configuration|
- yield configuration unless configuration['database'].blank?
+ configurations = ActiveRecord::Base.configurations.values_at(*environments)
+ configurations.compact.each do |configuration|
+ yield configuration unless configuration["database"].blank?
+ end
end
- end
- def each_local_configuration
- ActiveRecord::Base.configurations.each_value do |configuration|
- next unless configuration['database']
+ def each_local_configuration
+ ActiveRecord::Base.configurations.each_value do |configuration|
+ next unless configuration["database"]
- if local_database?(configuration)
- yield configuration
- else
- $stderr.puts "This task only modifies local databases. #{configuration['database']} is on a remote host."
+ if local_database?(configuration)
+ yield configuration
+ else
+ $stderr.puts "This task only modifies local databases. #{configuration['database']} is on a remote host."
+ end
end
end
- end
- def local_database?(configuration)
- configuration['host'].blank? || LOCAL_HOSTS.include?(configuration['host'])
- end
+ def local_database?(configuration)
+ configuration["host"].blank? || LOCAL_HOSTS.include?(configuration["host"])
+ end
end
end
end
diff --git a/activerecord/lib/active_record/tasks/mysql_database_tasks.rb b/activerecord/lib/active_record/tasks/mysql_database_tasks.rb
index b1a0ad0115..920830b9cf 100644
--- a/activerecord/lib/active_record/tasks/mysql_database_tasks.rb
+++ b/activerecord/lib/active_record/tasks/mysql_database_tasks.rb
@@ -11,10 +11,10 @@ module ActiveRecord
def create
establish_connection configuration_without_database
- connection.create_database configuration['database'], creation_options
+ connection.create_database configuration["database"], creation_options
establish_connection configuration
rescue ActiveRecord::StatementInvalid => error
- if /database exists/ === error.message
+ if error.message.include?("database exists")
raise DatabaseAlreadyExists
else
raise
@@ -23,26 +23,26 @@ module ActiveRecord
if error.respond_to?(:errno) && error.errno == ACCESS_DENIED_ERROR
$stdout.print error.message
establish_connection root_configuration_without_database
- connection.create_database configuration['database'], creation_options
- if configuration['username'] != 'root'
- connection.execute grant_statement.gsub(/\s+/, ' ').strip
+ connection.create_database configuration["database"], creation_options
+ if configuration["username"] != "root"
+ connection.execute grant_statement.gsub(/\s+/, " ").strip
end
establish_connection configuration
else
$stderr.puts error.inspect
$stderr.puts "Couldn't create database for #{configuration.inspect}, #{creation_options.inspect}"
- $stderr.puts "(If you set the charset manually, make sure you have a matching collation)" if configuration['encoding']
+ $stderr.puts "(If you set the charset manually, make sure you have a matching collation)" if configuration["encoding"]
end
end
def drop
establish_connection configuration
- connection.drop_database configuration['database']
+ connection.drop_database configuration["database"]
end
def purge
establish_connection configuration
- connection.recreate_database configuration['database'], creation_options
+ connection.recreate_database configuration["database"], creation_options
end
def charset
@@ -53,100 +53,102 @@ module ActiveRecord
connection.collation
end
- def structure_dump(filename)
+ def structure_dump(filename, extra_flags)
args = prepare_command_options
args.concat(["--result-file", "#{filename}"])
args.concat(["--no-data"])
args.concat(["--routines"])
args.concat(["--skip-comments"])
+ args.concat(Array(extra_flags)) if extra_flags
args.concat(["#{configuration['database']}"])
- run_cmd('mysqldump', args, 'dumping')
+ run_cmd("mysqldump", args, "dumping")
end
- def structure_load(filename)
+ def structure_load(filename, extra_flags)
args = prepare_command_options
- args.concat(['--execute', %{SET FOREIGN_KEY_CHECKS = 0; SOURCE #{filename}; SET FOREIGN_KEY_CHECKS = 1}])
+ args.concat(["--execute", %{SET FOREIGN_KEY_CHECKS = 0; SOURCE #{filename}; SET FOREIGN_KEY_CHECKS = 1}])
args.concat(["--database", "#{configuration['database']}"])
+ args.concat(Array(extra_flags)) if extra_flags
- run_cmd('mysql', args, 'loading')
+ run_cmd("mysql", args, "loading")
end
private
- def configuration
- @configuration
- end
+ def configuration
+ @configuration
+ end
- def configuration_without_database
- configuration.merge('database' => nil)
- end
+ def configuration_without_database
+ configuration.merge("database" => nil)
+ end
- def creation_options
- Hash.new.tap do |options|
- options[:charset] = configuration['encoding'] if configuration.include? 'encoding'
- options[:collation] = configuration['collation'] if configuration.include? 'collation'
+ def creation_options
+ Hash.new.tap do |options|
+ options[:charset] = configuration["encoding"] if configuration.include? "encoding"
+ options[:collation] = configuration["collation"] if configuration.include? "collation"
+ end
end
- end
- def error_class
- if configuration['adapter'] =~ /jdbc/
- require 'active_record/railties/jdbcmysql_error'
- ArJdbcMySQL::Error
- elsif defined?(Mysql2)
- Mysql2::Error
- else
- StandardError
+ def error_class
+ if configuration["adapter"].include?("jdbc")
+ require "active_record/railties/jdbcmysql_error"
+ ArJdbcMySQL::Error
+ elsif defined?(Mysql2)
+ Mysql2::Error
+ else
+ StandardError
+ end
end
- end
- def grant_statement
- <<-SQL
+ def grant_statement
+ <<-SQL
GRANT ALL PRIVILEGES ON #{configuration['database']}.*
TO '#{configuration['username']}'@'localhost'
IDENTIFIED BY '#{configuration['password']}' WITH GRANT OPTION;
- SQL
- end
+ SQL
+ end
- def root_configuration_without_database
- configuration_without_database.merge(
- 'username' => 'root',
- 'password' => root_password
- )
- end
+ def root_configuration_without_database
+ configuration_without_database.merge(
+ "username" => "root",
+ "password" => root_password
+ )
+ end
- def root_password
- $stdout.print "Please provide the root password for your MySQL installation\n>"
- $stdin.gets.strip
- end
+ def root_password
+ $stdout.print "Please provide the root password for your MySQL installation\n>"
+ $stdin.gets.strip
+ end
- def prepare_command_options
- args = {
- 'host' => '--host',
- 'port' => '--port',
- 'socket' => '--socket',
- 'username' => '--user',
- 'password' => '--password',
- 'encoding' => '--default-character-set',
- 'sslca' => '--ssl-ca',
- 'sslcert' => '--ssl-cert',
- 'sslcapath' => '--ssl-capath',
- 'sslcipher' => '--ssl-cipher',
- 'sslkey' => '--ssl-key'
- }.map { |opt, arg| "#{arg}=#{configuration[opt]}" if configuration[opt] }.compact
-
- args
- end
+ def prepare_command_options
+ args = {
+ "host" => "--host",
+ "port" => "--port",
+ "socket" => "--socket",
+ "username" => "--user",
+ "password" => "--password",
+ "encoding" => "--default-character-set",
+ "sslca" => "--ssl-ca",
+ "sslcert" => "--ssl-cert",
+ "sslcapath" => "--ssl-capath",
+ "sslcipher" => "--ssl-cipher",
+ "sslkey" => "--ssl-key"
+ }.map { |opt, arg| "#{arg}=#{configuration[opt]}" if configuration[opt] }.compact
+
+ args
+ end
- def run_cmd(cmd, args, action)
- fail run_cmd_error(cmd, args, action) unless Kernel.system(cmd, *args)
- end
+ def run_cmd(cmd, args, action)
+ fail run_cmd_error(cmd, args, action) unless Kernel.system(cmd, *args)
+ end
- def run_cmd_error(cmd, args, action)
- msg = "failed to execute: `#{cmd}`\n"
- msg << "Please check the output above for any errors and make sure that `#{cmd}` is installed in your PATH and has proper permissions.\n\n"
- msg
- end
+ def run_cmd_error(cmd, args, action)
+ msg = "failed to execute: `#{cmd}`\n"
+ msg << "Please check the output above for any errors and make sure that `#{cmd}` is installed in your PATH and has proper permissions.\n\n"
+ msg
+ end
end
end
end
diff --git a/activerecord/lib/active_record/tasks/postgresql_database_tasks.rb b/activerecord/lib/active_record/tasks/postgresql_database_tasks.rb
index b19ab57ee4..5155ced0e2 100644
--- a/activerecord/lib/active_record/tasks/postgresql_database_tasks.rb
+++ b/activerecord/lib/active_record/tasks/postgresql_database_tasks.rb
@@ -1,8 +1,8 @@
module ActiveRecord
module Tasks # :nodoc:
class PostgreSQLDatabaseTasks # :nodoc:
- DEFAULT_ENCODING = ENV['CHARSET'] || 'utf8'
- ON_ERROR_STOP_1 = 'ON_ERROR_STOP=1'.freeze
+ DEFAULT_ENCODING = ENV["CHARSET"] || "utf8"
+ ON_ERROR_STOP_1 = "ON_ERROR_STOP=1".freeze
delegate :connection, :establish_connection, :clear_active_connections!,
to: ActiveRecord::Base
@@ -13,11 +13,11 @@ module ActiveRecord
def create(master_established = false)
establish_master_connection unless master_established
- connection.create_database configuration['database'],
- configuration.merge('encoding' => encoding)
+ connection.create_database configuration["database"],
+ configuration.merge("encoding" => encoding)
establish_connection configuration
rescue ActiveRecord::StatementInvalid => error
- if /database .* already exists/ === error.message
+ if /database .* already exists/.match?(error.message)
raise DatabaseAlreadyExists
else
raise
@@ -26,7 +26,7 @@ module ActiveRecord
def drop
establish_master_connection
- connection.drop_database configuration['database']
+ connection.drop_database configuration["database"]
end
def charset
@@ -43,69 +43,73 @@ module ActiveRecord
create true
end
- def structure_dump(filename)
+ def structure_dump(filename, extra_flags)
set_psql_env
- search_path = case ActiveRecord::Base.dump_schemas
- when :schema_search_path
- configuration['schema_search_path']
- when :all
- nil
- when String
- ActiveRecord::Base.dump_schemas
- end
+ search_path = \
+ case ActiveRecord::Base.dump_schemas
+ when :schema_search_path
+ configuration["schema_search_path"]
+ when :all
+ nil
+ when String
+ ActiveRecord::Base.dump_schemas
+ end
- args = ['-s', '-x', '-O', '-f', filename]
+ args = ["-s", "-x", "-O", "-f", filename]
+ args.concat(Array(extra_flags)) if extra_flags
unless search_path.blank?
- args += search_path.split(',').map do |part|
+ args += search_path.split(",").map do |part|
"--schema=#{part.strip}"
end
end
- args << configuration['database']
- run_cmd('pg_dump', args, 'dumping')
+ args << configuration["database"]
+ run_cmd("pg_dump", args, "dumping")
File.open(filename, "a") { |f| f << "SET search_path TO #{connection.schema_search_path};\n\n" }
end
- def structure_load(filename)
+ def structure_load(filename, extra_flags)
set_psql_env
- args = [ '-v', ON_ERROR_STOP_1, '-q', '-f', filename, configuration['database'] ]
- run_cmd('psql', args, 'loading' )
+ args = ["-v", ON_ERROR_STOP_1, "-q", "-f", filename]
+ args.concat(Array(extra_flags)) if extra_flags
+ args << configuration["database"]
+ run_cmd("psql", args, "loading")
end
private
- def configuration
- @configuration
- end
+ def configuration
+ @configuration
+ end
- def encoding
- configuration['encoding'] || DEFAULT_ENCODING
- end
+ def encoding
+ configuration["encoding"] || DEFAULT_ENCODING
+ end
- def establish_master_connection
- establish_connection configuration.merge(
- 'database' => 'postgres',
- 'schema_search_path' => 'public'
- )
- end
+ def establish_master_connection
+ establish_connection configuration.merge(
+ "database" => "postgres",
+ "schema_search_path" => "public"
+ )
+ end
- def set_psql_env
- ENV['PGHOST'] = configuration['host'] if configuration['host']
- ENV['PGPORT'] = configuration['port'].to_s if configuration['port']
- ENV['PGPASSWORD'] = configuration['password'].to_s if configuration['password']
- ENV['PGUSER'] = configuration['username'].to_s if configuration['username']
- end
+ def set_psql_env
+ ENV["PGHOST"] = configuration["host"] if configuration["host"]
+ ENV["PGPORT"] = configuration["port"].to_s if configuration["port"]
+ ENV["PGPASSWORD"] = configuration["password"].to_s if configuration["password"]
+ ENV["PGUSER"] = configuration["username"].to_s if configuration["username"]
+ end
- def run_cmd(cmd, args, action)
- fail run_cmd_error(cmd, args, action) unless Kernel.system(cmd, *args)
- end
+ def run_cmd(cmd, args, action)
+ fail run_cmd_error(cmd, args, action) unless Kernel.system(cmd, *args)
+ end
- def run_cmd_error(cmd, args, action)
- msg = "failed to execute:\n"
- msg << "#{cmd} #{args.join(' ')}\n\n"
- msg << "Please check the output above for any errors and make sure that `#{cmd}` is installed in your PATH and has proper permissions.\n\n"
- msg
- end
+ def run_cmd_error(cmd, args, action)
+ msg = "failed to execute:\n"
+ msg << "#{cmd} #{args.join(' ')}\n\n"
+ msg << "Please check the output above for any errors and make sure that `#{cmd}` is installed in your PATH and has proper permissions.\n\n"
+ msg
+ end
end
end
end
diff --git a/activerecord/lib/active_record/tasks/sqlite_database_tasks.rb b/activerecord/lib/active_record/tasks/sqlite_database_tasks.rb
index 9ec3c8a94a..1f756c2979 100644
--- a/activerecord/lib/active_record/tasks/sqlite_database_tasks.rb
+++ b/activerecord/lib/active_record/tasks/sqlite_database_tasks.rb
@@ -8,20 +8,20 @@ module ActiveRecord
end
def create
- raise DatabaseAlreadyExists if File.exist?(configuration['database'])
+ raise DatabaseAlreadyExists if File.exist?(configuration["database"])
establish_connection configuration
connection
end
def drop
- require 'pathname'
- path = Pathname.new configuration['database']
+ require "pathname"
+ path = Pathname.new configuration["database"]
file = path.absolute? ? path.to_s : File.join(root, path)
FileUtils.rm(file)
rescue Errno::ENOENT => error
- raise NoDatabaseError.new(error.message, error)
+ raise NoDatabaseError.new(error.message)
end
def purge
@@ -35,25 +35,27 @@ module ActiveRecord
connection.encoding
end
- def structure_dump(filename)
- dbfile = configuration['database']
- `sqlite3 #{dbfile} .schema > #{filename}`
+ def structure_dump(filename, extra_flags)
+ dbfile = configuration["database"]
+ flags = extra_flags.join(" ") if extra_flags
+ `sqlite3 #{flags} #{dbfile} .schema > #{filename}`
end
- def structure_load(filename)
- dbfile = configuration['database']
- `sqlite3 #{dbfile} < "#{filename}"`
+ def structure_load(filename, extra_flags)
+ dbfile = configuration["database"]
+ flags = extra_flags.join(" ") if extra_flags
+ `sqlite3 #{flags} #{dbfile} < "#{filename}"`
end
private
- def configuration
- @configuration
- end
+ def configuration
+ @configuration
+ end
- def root
- @root
- end
+ def root
+ @root
+ end
end
end
end
diff --git a/activerecord/lib/active_record/timestamp.rb b/activerecord/lib/active_record/timestamp.rb
index d9c18a5e38..09d8d1cdd4 100644
--- a/activerecord/lib/active_record/timestamp.rb
+++ b/activerecord/lib/active_record/timestamp.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: true
module ActiveRecord
# = Active Record \Timestamp
#
@@ -51,15 +52,41 @@ module ActiveRecord
clear_timestamp_attributes
end
+ class_methods do
+ private
+ def timestamp_attributes_for_create_in_model
+ timestamp_attributes_for_create.select { |c| column_names.include?(c) }
+ end
+
+ def timestamp_attributes_for_update_in_model
+ timestamp_attributes_for_update.select { |c| column_names.include?(c) }
+ end
+
+ def all_timestamp_attributes_in_model
+ timestamp_attributes_for_create_in_model + timestamp_attributes_for_update_in_model
+ end
+
+ def timestamp_attributes_for_create
+ ["created_at", "created_on"]
+ end
+
+ def timestamp_attributes_for_update
+ ["updated_at", "updated_on"]
+ end
+
+ def current_time_from_proper_timezone
+ default_timezone == :utc ? Time.now.utc : Time.now
+ end
+ end
+
private
def _create_record
- if self.record_timestamps
+ if record_timestamps
current_time = current_time_from_proper_timezone
- all_timestamp_attributes.each do |column|
- column = column.to_s
- if has_attribute?(column) && !attribute_present?(column)
+ all_timestamp_attributes_in_model.each do |column|
+ if !attribute_present?(column)
write_attribute(column, current_time)
end
end
@@ -73,8 +100,7 @@ module ActiveRecord
current_time = current_time_from_proper_timezone
timestamp_attributes_for_update_in_model.each do |column|
- column = column.to_s
- next if attribute_changed?(column)
+ next if will_save_change_to_attribute?(column)
write_attribute(column, current_time)
end
end
@@ -82,34 +108,26 @@ module ActiveRecord
end
def should_record_timestamps?
- self.record_timestamps && (!partial_writes? || changed?)
+ record_timestamps && (!partial_writes? || has_changes_to_save?)
end
def timestamp_attributes_for_create_in_model
- timestamp_attributes_for_create.select { |c| self.class.column_names.include?(c.to_s) }
+ self.class.send(:timestamp_attributes_for_create_in_model)
end
def timestamp_attributes_for_update_in_model
- timestamp_attributes_for_update.select { |c| self.class.column_names.include?(c.to_s) }
+ self.class.send(:timestamp_attributes_for_update_in_model)
end
def all_timestamp_attributes_in_model
- timestamp_attributes_for_create_in_model + timestamp_attributes_for_update_in_model
- end
-
- def timestamp_attributes_for_update
- [:updated_at, :updated_on]
- end
-
- def timestamp_attributes_for_create
- [:created_at, :created_on]
+ self.class.send(:all_timestamp_attributes_in_model)
end
- def all_timestamp_attributes
- timestamp_attributes_for_create + timestamp_attributes_for_update
+ def current_time_from_proper_timezone
+ self.class.send(:current_time_from_proper_timezone)
end
- def max_updated_column_timestamp(timestamp_names = timestamp_attributes_for_update)
+ def max_updated_column_timestamp(timestamp_names = self.class.send(:timestamp_attributes_for_update))
timestamp_names
.map { |attr| self[attr] }
.compact
@@ -117,10 +135,6 @@ module ActiveRecord
.max
end
- def current_time_from_proper_timezone
- self.class.default_timezone == :utc ? Time.now.utc : Time.now
- end
-
# Clear attributes and changed_attributes
def clear_timestamp_attributes
all_timestamp_attributes_in_model.each do |attribute_name|
diff --git a/activerecord/lib/active_record/touch_later.rb b/activerecord/lib/active_record/touch_later.rb
index 598873ea3a..cacde9c881 100644
--- a/activerecord/lib/active_record/touch_later.rb
+++ b/activerecord/lib/active_record/touch_later.rb
@@ -25,7 +25,7 @@ module ActiveRecord
# touch the parents as we are not calling the after_save callbacks
self.class.reflect_on_all_associations(:belongs_to).each do |r|
if touch = r.options[:touch]
- ActiveRecord::Associations::Builder::BelongsTo.touch_record(self, r.foreign_key, r.name, touch, :touch_later)
+ ActiveRecord::Associations::Builder::BelongsTo.touch_record(self, changes_to_save, r.foreign_key, r.name, touch, :touch_later)
end
end
end
@@ -58,6 +58,5 @@ module ActiveRecord
def belongs_to_touch_method
:touch_later
end
-
end
end
diff --git a/activerecord/lib/active_record/transactions.rb b/activerecord/lib/active_record/transactions.rb
index 0ee1ebcfbe..56b75540e3 100644
--- a/activerecord/lib/active_record/transactions.rb
+++ b/activerecord/lib/active_record/transactions.rb
@@ -274,35 +274,25 @@ module ActiveRecord
set_callback(:rollback_without_transaction_enrollment, :after, *args, &block)
end
- def raise_in_transactional_callbacks
- ActiveSupport::Deprecation.warn('ActiveRecord::Base.raise_in_transactional_callbacks is deprecated and will be removed without replacement.')
- true
- end
-
- def raise_in_transactional_callbacks=(value)
- ActiveSupport::Deprecation.warn('ActiveRecord::Base.raise_in_transactional_callbacks= is deprecated, has no effect and will be removed without replacement.')
- value
- end
-
private
- def set_options_for_callbacks!(args, enforced_options = {})
- options = args.extract_options!.merge!(enforced_options)
- args << options
+ def set_options_for_callbacks!(args, enforced_options = {})
+ options = args.extract_options!.merge!(enforced_options)
+ args << options
- if options[:on]
- fire_on = Array(options[:on])
- assert_valid_transaction_action(fire_on)
- options[:if] = Array(options[:if])
- options[:if] << "transaction_include_any_action?(#{fire_on})"
+ if options[:on]
+ fire_on = Array(options[:on])
+ assert_valid_transaction_action(fire_on)
+ options[:if] = Array(options[:if])
+ options[:if] << "transaction_include_any_action?(#{fire_on})"
+ end
end
- end
- def assert_valid_transaction_action(actions)
- if (actions - ACTIONS).any?
- raise ArgumentError, ":on conditions for after_commit and after_rollback callbacks have to be one of #{ACTIONS}"
+ def assert_valid_transaction_action(actions)
+ if (actions - ACTIONS).any?
+ raise ArgumentError, ":on conditions for after_commit and after_rollback callbacks have to be one of #{ACTIONS}"
+ end
end
- end
end
# See ActiveRecord::Transactions::ClassMethods for detailed documentation.
@@ -407,103 +397,102 @@ module ActiveRecord
end
end
- protected
-
- # Save the new record state and id of a record so it can be restored later if a transaction fails.
- def remember_transaction_record_state #:nodoc:
- @_start_transaction_state[:id] = id
- @_start_transaction_state.reverse_merge!(
- new_record: @new_record,
- destroyed: @destroyed,
- frozen?: frozen?,
- )
- @_start_transaction_state[:level] = (@_start_transaction_state[:level] || 0) + 1
- end
+ private
- # Clear the new record state and id of a record.
- def clear_transaction_record_state #:nodoc:
- @_start_transaction_state[:level] = (@_start_transaction_state[:level] || 0) - 1
- force_clear_transaction_record_state if @_start_transaction_state[:level] < 1
- end
+ # Save the new record state and id of a record so it can be restored later if a transaction fails.
+ def remember_transaction_record_state
+ @_start_transaction_state[:id] = id
+ @_start_transaction_state.reverse_merge!(
+ new_record: @new_record,
+ destroyed: @destroyed,
+ frozen?: frozen?,
+ )
+ @_start_transaction_state[:level] = (@_start_transaction_state[:level] || 0) + 1
+ end
- # Force to clear the transaction record state.
- def force_clear_transaction_record_state #:nodoc:
- @_start_transaction_state.clear
- end
+ # Clear the new record state and id of a record.
+ def clear_transaction_record_state
+ @_start_transaction_state[:level] = (@_start_transaction_state[:level] || 0) - 1
+ force_clear_transaction_record_state if @_start_transaction_state[:level] < 1
+ end
- # Restore the new record state and id of a record that was previously saved by a call to save_record_state.
- def restore_transaction_record_state(force = false) #:nodoc:
- unless @_start_transaction_state.empty?
- transaction_level = (@_start_transaction_state[:level] || 0) - 1
- if transaction_level < 1 || force
- restore_state = @_start_transaction_state
- thaw
- @new_record = restore_state[:new_record]
- @destroyed = restore_state[:destroyed]
- pk = self.class.primary_key
- if pk && read_attribute(pk) != restore_state[:id]
- write_attribute(pk, restore_state[:id])
+ # Force to clear the transaction record state.
+ def force_clear_transaction_record_state
+ @_start_transaction_state.clear
+ end
+
+ # Restore the new record state and id of a record that was previously saved by a call to save_record_state.
+ def restore_transaction_record_state(force = false)
+ unless @_start_transaction_state.empty?
+ transaction_level = (@_start_transaction_state[:level] || 0) - 1
+ if transaction_level < 1 || force
+ restore_state = @_start_transaction_state
+ thaw
+ @new_record = restore_state[:new_record]
+ @destroyed = restore_state[:destroyed]
+ pk = self.class.primary_key
+ if pk && read_attribute(pk) != restore_state[:id]
+ write_attribute(pk, restore_state[:id])
+ end
+ freeze if restore_state[:frozen?]
end
- freeze if restore_state[:frozen?]
end
end
- end
- # Determine if a record was created or destroyed in a transaction. State should be one of :new_record or :destroyed.
- def transaction_record_state(state) #:nodoc:
- @_start_transaction_state[state]
- end
+ # Determine if a record was created or destroyed in a transaction. State should be one of :new_record or :destroyed.
+ def transaction_record_state(state)
+ @_start_transaction_state[state]
+ end
- # Determine if a transaction included an action for :create, :update, or :destroy. Used in filtering callbacks.
- def transaction_include_any_action?(actions) #:nodoc:
- actions.any? do |action|
- case action
- when :create
- transaction_record_state(:new_record)
- when :destroy
- destroyed?
- when :update
- !(transaction_record_state(:new_record) || destroyed?)
+ # Determine if a transaction included an action for :create, :update, or :destroy. Used in filtering callbacks.
+ def transaction_include_any_action?(actions)
+ actions.any? do |action|
+ case action
+ when :create
+ transaction_record_state(:new_record)
+ when :destroy
+ defined?(@_trigger_destroy_callback) && @_trigger_destroy_callback
+ when :update
+ !(transaction_record_state(:new_record) || destroyed?) &&
+ (defined?(@_trigger_update_callback) && @_trigger_update_callback)
+ end
end
end
- end
-
- private
- def set_transaction_state(state) # :nodoc:
- @transaction_state = state
- end
+ def set_transaction_state(state)
+ @transaction_state = state
+ end
- def has_transactional_callbacks? # :nodoc:
- !_rollback_callbacks.empty? || !_commit_callbacks.empty? || !_before_commit_callbacks.empty?
- end
+ def has_transactional_callbacks?
+ !_rollback_callbacks.empty? || !_commit_callbacks.empty? || !_before_commit_callbacks.empty?
+ end
- # Updates the attributes on this particular Active Record object so that
- # if it's associated with a transaction, then the state of the Active Record
- # object will be updated to reflect the current state of the transaction.
- #
- # The +@transaction_state+ variable stores the states of the associated
- # transaction. This relies on the fact that a transaction can only be in
- # one rollback or commit (otherwise a list of states would be required).
- # Each Active Record object inside of a transaction carries that transaction's
- # TransactionState.
- #
- # This method checks to see if the ActiveRecord object's state reflects
- # the TransactionState, and rolls back or commits the Active Record object
- # as appropriate.
- #
- # Since Active Record objects can be inside multiple transactions, this
- # method recursively goes through the parent of the TransactionState and
- # checks if the Active Record object reflects the state of the object.
- def sync_with_transaction_state
- update_attributes_from_transaction_state(@transaction_state)
- end
+ # Updates the attributes on this particular Active Record object so that
+ # if it's associated with a transaction, then the state of the Active Record
+ # object will be updated to reflect the current state of the transaction.
+ #
+ # The +@transaction_state+ variable stores the states of the associated
+ # transaction. This relies on the fact that a transaction can only be in
+ # one rollback or commit (otherwise a list of states would be required).
+ # Each Active Record object inside of a transaction carries that transaction's
+ # TransactionState.
+ #
+ # This method checks to see if the ActiveRecord object's state reflects
+ # the TransactionState, and rolls back or commits the Active Record object
+ # as appropriate.
+ #
+ # Since Active Record objects can be inside multiple transactions, this
+ # method recursively goes through the parent of the TransactionState and
+ # checks if the Active Record object reflects the state of the object.
+ def sync_with_transaction_state
+ update_attributes_from_transaction_state(@transaction_state)
+ end
- def update_attributes_from_transaction_state(transaction_state)
- if transaction_state && transaction_state.finalized?
- restore_transaction_record_state if transaction_state.rolledback?
- clear_transaction_record_state
+ def update_attributes_from_transaction_state(transaction_state)
+ if transaction_state && transaction_state.finalized?
+ restore_transaction_record_state if transaction_state.rolledback?
+ clear_transaction_record_state
+ end
end
- end
end
end
diff --git a/activerecord/lib/active_record/type.rb b/activerecord/lib/active_record/type.rb
index 4911d93dd9..4f632660a8 100644
--- a/activerecord/lib/active_record/type.rb
+++ b/activerecord/lib/active_record/type.rb
@@ -1,17 +1,20 @@
-require 'active_model/type'
+require "active_model/type"
-require 'active_record/type/internal/abstract_json'
-require 'active_record/type/internal/timezone'
+require "active_record/type/internal/abstract_json"
+require "active_record/type/internal/timezone"
-require 'active_record/type/date'
-require 'active_record/type/date_time'
-require 'active_record/type/time'
+require "active_record/type/date"
+require "active_record/type/date_time"
+require "active_record/type/decimal_without_scale"
+require "active_record/type/time"
+require "active_record/type/text"
+require "active_record/type/unsigned_integer"
-require 'active_record/type/serialized'
-require 'active_record/type/adapter_specific_registry'
+require "active_record/type/serialized"
+require "active_record/type/adapter_specific_registry"
-require 'active_record/type/type_map'
-require 'active_record/type/hash_lookup_type_map'
+require "active_record/type/type_map"
+require "active_record/type/hash_lookup_type_map"
module ActiveRecord
module Type
@@ -37,6 +40,10 @@ module ActiveRecord
registry.lookup(*args, adapter: adapter, **kwargs)
end
+ def default_value # :nodoc:
+ @default_value ||= Value.new
+ end
+
private
def current_adapter_name
@@ -49,12 +56,9 @@ module ActiveRecord
Binary = ActiveModel::Type::Binary
Boolean = ActiveModel::Type::Boolean
Decimal = ActiveModel::Type::Decimal
- DecimalWithoutScale = ActiveModel::Type::DecimalWithoutScale
Float = ActiveModel::Type::Float
Integer = ActiveModel::Type::Integer
String = ActiveModel::Type::String
- Text = ActiveModel::Type::Text
- UnsignedInteger = ActiveModel::Type::UnsignedInteger
Value = ActiveModel::Type::Value
register(:big_integer, Type::BigInteger, override: false)
diff --git a/activerecord/lib/active_record/type/adapter_specific_registry.rb b/activerecord/lib/active_record/type/adapter_specific_registry.rb
index d440eac619..7cc866f7a7 100644
--- a/activerecord/lib/active_record/type/adapter_specific_registry.rb
+++ b/activerecord/lib/active_record/type/adapter_specific_registry.rb
@@ -1,4 +1,4 @@
-require 'active_model/type/registry'
+require "active_model/type/registry"
module ActiveRecord
# :stopdoc:
@@ -10,15 +10,15 @@ module ActiveRecord
private
- def registration_klass
- Registration
- end
+ def registration_klass
+ Registration
+ end
- def find_registration(symbol, *args)
- registrations
- .select { |registration| registration.matches?(symbol, *args) }
- .max
- end
+ def find_registration(symbol, *args)
+ registrations
+ .select { |registration| registration.matches?(symbol, *args) }
+ .max
+ end
end
class Registration
@@ -50,44 +50,46 @@ module ActiveRecord
priority <=> other.priority
end
+ # TODO Change this to private once we've dropped Ruby 2.2 support.
+ # Workaround for Ruby 2.2 "private attribute?" warning.
protected
- attr_reader :name, :block, :adapter, :override
-
- def priority
- result = 0
- if adapter
- result |= 1
- end
- if override
- result |= 2
+ attr_reader :name, :block, :adapter, :override
+
+ def priority
+ result = 0
+ if adapter
+ result |= 1
+ end
+ if override
+ result |= 2
+ end
+ result
end
- result
- end
- def priority_except_adapter
- priority & 0b111111100
- end
+ def priority_except_adapter
+ priority & 0b111111100
+ end
private
- def matches_adapter?(adapter: nil, **)
- (self.adapter.nil? || adapter == self.adapter)
- end
+ def matches_adapter?(adapter: nil, **)
+ (self.adapter.nil? || adapter == self.adapter)
+ end
- def conflicts_with?(other)
- same_priority_except_adapter?(other) &&
- has_adapter_conflict?(other)
- end
+ def conflicts_with?(other)
+ same_priority_except_adapter?(other) &&
+ has_adapter_conflict?(other)
+ end
- def same_priority_except_adapter?(other)
- priority_except_adapter == other.priority_except_adapter
- end
+ def same_priority_except_adapter?(other)
+ priority_except_adapter == other.priority_except_adapter
+ end
- def has_adapter_conflict?(other)
- (override.nil? && other.adapter) ||
- (adapter && other.override.nil?)
- end
+ def has_adapter_conflict?(other)
+ (override.nil? && other.adapter) ||
+ (adapter && other.override.nil?)
+ end
end
class DecorationRegistration < Registration
@@ -110,17 +112,19 @@ module ActiveRecord
super | 4
end
+ # TODO Change this to private once we've dropped Ruby 2.2 support.
+ # Workaround for Ruby 2.2 "private attribute?" warning.
protected
- attr_reader :options, :klass
+ attr_reader :options, :klass
private
- def matches_options?(**kwargs)
- options.all? do |key, value|
- kwargs[key] == value
+ def matches_options?(**kwargs)
+ options.all? do |key, value|
+ kwargs[key] == value
+ end
end
- end
end
end
diff --git a/activerecord/lib/active_record/type/decimal_without_scale.rb b/activerecord/lib/active_record/type/decimal_without_scale.rb
new file mode 100644
index 0000000000..7ce33e9cd3
--- /dev/null
+++ b/activerecord/lib/active_record/type/decimal_without_scale.rb
@@ -0,0 +1,9 @@
+module ActiveRecord
+ module Type
+ class DecimalWithoutScale < ActiveModel::Type::BigInteger # :nodoc:
+ def type
+ :decimal
+ end
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/type/hash_lookup_type_map.rb b/activerecord/lib/active_record/type/hash_lookup_type_map.rb
index 3b01e3f8ca..0145d5d6c1 100644
--- a/activerecord/lib/active_record/type/hash_lookup_type_map.rb
+++ b/activerecord/lib/active_record/type/hash_lookup_type_map.rb
@@ -15,9 +15,9 @@ module ActiveRecord
private
- def perform_fetch(type, *args, &block)
- @mapping.fetch(type, block).call(type, *args)
- end
+ def perform_fetch(type, *args, &block)
+ @mapping.fetch(type, block).call(type, *args)
+ end
end
end
end
diff --git a/activerecord/lib/active_record/type/internal/abstract_json.rb b/activerecord/lib/active_record/type/internal/abstract_json.rb
index 513c938088..e19c5a14da 100644
--- a/activerecord/lib/active_record/type/internal/abstract_json.rb
+++ b/activerecord/lib/active_record/type/internal/abstract_json.rb
@@ -17,7 +17,11 @@ module ActiveRecord
end
def serialize(value)
- ::ActiveSupport::JSON.encode(value)
+ if value.nil?
+ nil
+ else
+ ::ActiveSupport::JSON.encode(value)
+ end
end
def accessor
diff --git a/activerecord/lib/active_record/type/serialized.rb b/activerecord/lib/active_record/type/serialized.rb
index a3a5241780..ac9134bfcb 100644
--- a/activerecord/lib/active_record/type/serialized.rb
+++ b/activerecord/lib/active_record/type/serialized.rb
@@ -49,15 +49,15 @@ module ActiveRecord
private
- def default_value?(value)
- value == coder.load(nil)
- end
+ def default_value?(value)
+ value == coder.load(nil)
+ end
- def encoded(value)
- unless default_value?(value)
- coder.dump(value)
+ def encoded(value)
+ unless default_value?(value)
+ coder.dump(value)
+ end
end
- end
end
end
end
diff --git a/activerecord/lib/active_record/type/text.rb b/activerecord/lib/active_record/type/text.rb
new file mode 100644
index 0000000000..cb1949700a
--- /dev/null
+++ b/activerecord/lib/active_record/type/text.rb
@@ -0,0 +1,9 @@
+module ActiveRecord
+ module Type
+ class Text < ActiveModel::Type::String # :nodoc:
+ def type
+ :text
+ end
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/type/time.rb b/activerecord/lib/active_record/type/time.rb
index 7da49e43c7..b9bac87c67 100644
--- a/activerecord/lib/active_record/type/time.rb
+++ b/activerecord/lib/active_record/type/time.rb
@@ -17,4 +17,3 @@ module ActiveRecord
end
end
end
-
diff --git a/activerecord/lib/active_record/type/type_map.rb b/activerecord/lib/active_record/type/type_map.rb
index 850a7a4e09..7bce82a1ff 100644
--- a/activerecord/lib/active_record/type/type_map.rb
+++ b/activerecord/lib/active_record/type/type_map.rb
@@ -1,4 +1,4 @@
-require 'concurrent/map'
+require "concurrent/map"
module ActiveRecord
module Type
@@ -11,7 +11,7 @@ module ActiveRecord
end
def lookup(lookup_key, *args)
- fetch(lookup_key, *args) { default_value }
+ fetch(lookup_key, *args) { Type.default_value }
end
def fetch(lookup_key, *args, &block)
@@ -44,21 +44,17 @@ module ActiveRecord
private
- def perform_fetch(lookup_key, *args)
- matching_pair = @mapping.reverse_each.detect do |key, _|
- key === lookup_key
- end
+ def perform_fetch(lookup_key, *args)
+ matching_pair = @mapping.reverse_each.detect do |key, _|
+ key === lookup_key
+ end
- if matching_pair
- matching_pair.last.call(lookup_key, *args)
- else
- yield lookup_key, *args
+ if matching_pair
+ matching_pair.last.call(lookup_key, *args)
+ else
+ yield lookup_key, *args
+ end
end
- end
-
- def default_value
- @default_value ||= ActiveModel::Type::Value.new
- end
end
end
end
diff --git a/activerecord/lib/active_record/type/unsigned_integer.rb b/activerecord/lib/active_record/type/unsigned_integer.rb
new file mode 100644
index 0000000000..9ae0109f9f
--- /dev/null
+++ b/activerecord/lib/active_record/type/unsigned_integer.rb
@@ -0,0 +1,15 @@
+module ActiveRecord
+ module Type
+ class UnsignedInteger < ActiveModel::Type::Integer # :nodoc:
+ private
+
+ def max_value
+ super * 2
+ end
+
+ def min_value
+ 0
+ end
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/type_caster.rb b/activerecord/lib/active_record/type_caster.rb
index accc339d00..f1686e4913 100644
--- a/activerecord/lib/active_record/type_caster.rb
+++ b/activerecord/lib/active_record/type_caster.rb
@@ -1,5 +1,5 @@
-require 'active_record/type_caster/map'
-require 'active_record/type_caster/connection'
+require "active_record/type_caster/map"
+require "active_record/type_caster/connection"
module ActiveRecord
module TypeCaster # :nodoc:
diff --git a/activerecord/lib/active_record/type_caster/connection.rb b/activerecord/lib/active_record/type_caster/connection.rb
index 7ed8dcc313..9f7bbe8843 100644
--- a/activerecord/lib/active_record/type_caster/connection.rb
+++ b/activerecord/lib/active_record/type_caster/connection.rb
@@ -12,18 +12,20 @@ module ActiveRecord
connection.type_cast_from_column(column, value)
end
+ # TODO Change this to private once we've dropped Ruby 2.2 support.
+ # Workaround for Ruby 2.2 "private attribute?" warning.
protected
- attr_reader :table_name
- delegate :connection, to: :@klass
+ attr_reader :table_name
+ delegate :connection, to: :@klass
private
- def column_for(attribute_name)
- if connection.schema_cache.data_source_exists?(table_name)
- connection.schema_cache.columns_hash(table_name)[attribute_name.to_s]
+ def column_for(attribute_name)
+ if connection.schema_cache.data_source_exists?(table_name)
+ connection.schema_cache.columns_hash(table_name)[attribute_name.to_s]
+ end
end
- end
end
end
end
diff --git a/activerecord/lib/active_record/type_caster/map.rb b/activerecord/lib/active_record/type_caster/map.rb
index 3a367b3999..9f79723125 100644
--- a/activerecord/lib/active_record/type_caster/map.rb
+++ b/activerecord/lib/active_record/type_caster/map.rb
@@ -11,9 +11,11 @@ module ActiveRecord
type.serialize(value)
end
+ # TODO Change this to private once we've dropped Ruby 2.2 support.
+ # Workaround for Ruby 2.2 "private attribute?" warning.
protected
- attr_reader :types
+ attr_reader :types
end
end
end
diff --git a/activerecord/lib/active_record/validations.rb b/activerecord/lib/active_record/validations.rb
index ecaf04e39e..9633f226f0 100644
--- a/activerecord/lib/active_record/validations.rb
+++ b/activerecord/lib/active_record/validations.rb
@@ -40,13 +40,13 @@ module ActiveRecord
# The validation process on save can be skipped by passing <tt>validate: false</tt>.
# The regular {ActiveRecord::Base#save}[rdoc-ref:Persistence#save] method is replaced
# with this when the validations module is mixed in, which it is by default.
- def save(options={})
+ def save(options = {})
perform_validations(options) ? super : false
end
# Attempts to save the record just like {ActiveRecord::Base#save}[rdoc-ref:Base#save] but
# will raise an ActiveRecord::RecordInvalid exception instead of returning +false+ if the record is not valid.
- def save!(options={})
+ def save!(options = {})
perform_validations(options) ? super : raise_validation_error
end
@@ -68,7 +68,7 @@ module ActiveRecord
alias_method :validate, :valid?
- protected
+ private
def default_validation_context
new_record? ? :create : :update
@@ -78,7 +78,7 @@ module ActiveRecord
raise(RecordInvalid.new(self))
end
- def perform_validations(options={}) # :nodoc:
+ def perform_validations(options = {})
options[:validate] == false || valid?(options[:context])
end
end
diff --git a/activerecord/lib/active_record/validations/associated.rb b/activerecord/lib/active_record/validations/associated.rb
index b14db85167..c695965d7b 100644
--- a/activerecord/lib/active_record/validations/associated.rb
+++ b/activerecord/lib/active_record/validations/associated.rb
@@ -37,7 +37,7 @@ module ActiveRecord
#
# * <tt>:message</tt> - A custom error message (default is: "is invalid").
# * <tt>:on</tt> - Specifies the contexts where this validation is active.
- # Runs in all validation contexts by default (nil). You can pass a symbol
+ # Runs in all validation contexts by default +nil+. You can pass a symbol
# or an array of symbols. (e.g. <tt>on: :create</tt> or
# <tt>on: :custom_validation_context</tt> or
# <tt>on: [:create, :custom_validation_context]</tt>)
diff --git a/activerecord/lib/active_record/validations/presence.rb b/activerecord/lib/active_record/validations/presence.rb
index ad82ea66c4..ca5eda2f84 100644
--- a/activerecord/lib/active_record/validations/presence.rb
+++ b/activerecord/lib/active_record/validations/presence.rb
@@ -44,7 +44,7 @@ module ActiveRecord
# Configuration options:
# * <tt>:message</tt> - A custom error message (default is: "can't be blank").
# * <tt>:on</tt> - Specifies the contexts where this validation is active.
- # Runs in all validation contexts by default (nil). You can pass a symbol
+ # Runs in all validation contexts by default +nil+. You can pass a symbol
# or an array of symbols. (e.g. <tt>on: :create</tt> or
# <tt>on: :custom_validation_context</tt> or
# <tt>on: [:create, :custom_validation_context]</tt>)
diff --git a/activerecord/lib/active_record/validations/uniqueness.rb b/activerecord/lib/active_record/validations/uniqueness.rb
index ec9f498c40..9e8edfbfaf 100644
--- a/activerecord/lib/active_record/validations/uniqueness.rb
+++ b/activerecord/lib/active_record/validations/uniqueness.rb
@@ -12,18 +12,17 @@ module ActiveRecord
def validate_each(record, attribute, value)
finder_class = find_finder_class_for(record)
- table = finder_class.arel_table
value = map_enum_attribute(finder_class, attribute, value)
- relation = build_relation(finder_class, table, attribute, value)
+ relation = build_relation(finder_class, attribute, value)
if record.persisted?
if finder_class.primary_key
- relation = relation.where.not(finder_class.primary_key => record.id_was || record.id)
+ relation = relation.where.not(finder_class.primary_key => record.id_in_database || record.id)
else
raise UnknownPrimaryKey.new(finder_class, "Can not validate uniqueness for persisted record without primary key.")
end
end
- relation = scope_relation(record, table, relation)
+ relation = scope_relation(record, relation)
relation = relation.merge(options[:conditions]) if options[:conditions]
if relation.exists?
@@ -34,13 +33,13 @@ module ActiveRecord
end
end
- protected
+ private
# The check for an existing value should be run from a class that
# isn't abstract. This means working down from the current class
# (self), to the first non-abstract class. Since classes don't know
# their subclasses, we have to build the hierarchy between self and
# the record's class.
- def find_finder_class_for(record) #:nodoc:
+ def find_finder_class_for(record)
class_hierarchy = [record.class]
while class_hierarchy.first != @klass
@@ -50,12 +49,16 @@ module ActiveRecord
class_hierarchy.detect { |klass| !klass.abstract_class? }
end
- def build_relation(klass, table, attribute, value) #:nodoc:
+ def build_relation(klass, attribute, value)
if reflection = klass._reflect_on_association(attribute)
attribute = reflection.foreign_key
value = value.attributes[reflection.klass.primary_key] unless value.nil?
end
+ if value.nil?
+ return klass.unscoped.where!(attribute => value)
+ end
+
# the attribute may be an aliased attribute
if klass.attribute_alias?(attribute)
attribute = klass.attribute_alias(attribute)
@@ -63,30 +66,29 @@ module ActiveRecord
attribute_name = attribute.to_s
+ table = klass.arel_table
column = klass.columns_hash[attribute_name]
cast_type = klass.type_for_attribute(attribute_name)
- comparison = if !options[:case_sensitive] && !value.nil?
+ comparison = if !options[:case_sensitive]
# will use SQL LOWER function before comparison, unless it detects a case insensitive collation
klass.connection.case_insensitive_comparison(table, attribute, column, value)
else
klass.connection.case_sensitive_comparison(table, attribute, column, value)
end
- if value.nil?
- klass.unscoped.where(comparison)
- else
- bind = Relation::QueryAttribute.new(attribute_name, value, cast_type)
- klass.unscoped.where(comparison, bind)
+ klass.unscoped.tap do |scope|
+ parts = [comparison]
+ binds = [Relation::QueryAttribute.new(attribute_name, value, cast_type)]
+ scope.where_clause += Relation::WhereClause.new(parts, binds)
end
end
- def scope_relation(record, table, relation)
+ def scope_relation(record, relation)
Array(options[:scope]).each do |scope_item|
- if reflection = record.class._reflect_on_association(scope_item)
- scope_value = record.send(reflection.foreign_key)
- scope_item = reflection.foreign_key
+ scope_value = if record.class._reflect_on_association(scope_item)
+ record.association(scope_item).reader
else
- scope_value = record._read_attribute(scope_item)
+ record._read_attribute(scope_item)
end
relation = relation.where(scope_item => scope_value)
end
diff --git a/activerecord/lib/active_record/version.rb b/activerecord/lib/active_record/version.rb
index cf76a13b44..146cfacc18 100644
--- a/activerecord/lib/active_record/version.rb
+++ b/activerecord/lib/active_record/version.rb
@@ -1,4 +1,4 @@
-require_relative 'gem_version'
+require_relative "gem_version"
module ActiveRecord
# Returns the version of the currently loaded ActiveRecord as a <tt>Gem::Version</tt>
diff --git a/activerecord/lib/rails/generators/active_record.rb b/activerecord/lib/rails/generators/active_record.rb
index dc29213235..68fca44e3b 100644
--- a/activerecord/lib/rails/generators/active_record.rb
+++ b/activerecord/lib/rails/generators/active_record.rb
@@ -1,7 +1,7 @@
-require 'rails/generators/named_base'
-require 'rails/generators/active_model'
-require 'rails/generators/active_record/migration'
-require 'active_record'
+require "rails/generators/named_base"
+require "rails/generators/active_model"
+require "rails/generators/active_record/migration"
+require "active_record"
module ActiveRecord
module Generators # :nodoc:
diff --git a/activerecord/lib/rails/generators/active_record/migration.rb b/activerecord/lib/rails/generators/active_record/migration.rb
index c2b2209638..4263c11ffc 100644
--- a/activerecord/lib/rails/generators/active_record/migration.rb
+++ b/activerecord/lib/rails/generators/active_record/migration.rb
@@ -1,4 +1,4 @@
-require 'rails/generators/migration'
+require "rails/generators/migration"
module ActiveRecord
module Generators # :nodoc:
diff --git a/activerecord/lib/rails/generators/active_record/migration/migration_generator.rb b/activerecord/lib/rails/generators/active_record/migration/migration_generator.rb
index 4e5872b585..8511531af7 100644
--- a/activerecord/lib/rails/generators/active_record/migration/migration_generator.rb
+++ b/activerecord/lib/rails/generators/active_record/migration/migration_generator.rb
@@ -1,9 +1,9 @@
-require 'rails/generators/active_record'
+require "rails/generators/active_record"
module ActiveRecord
module Generators # :nodoc:
class MigrationGenerator < Base # :nodoc:
- argument :attributes, :type => :array, :default => [], :banner => "field[:type][:index] field[:type][:index]"
+ argument :attributes, type: :array, default: [], banner: "field[:type][:index] field[:type][:index]"
class_option :primary_key_type, type: :string, desc: "The type for primary key"
@@ -13,46 +13,49 @@ module ActiveRecord
migration_template @migration_template, "db/migrate/#{file_name}.rb"
end
+ # TODO Change this to private once we've dropped Ruby 2.2 support.
+ # Workaround for Ruby 2.2 "private attribute?" warning.
protected
- attr_reader :migration_action, :join_tables
+ attr_reader :migration_action, :join_tables
- # Sets the default migration template that is being used for the generation of the migration.
- # Depending on command line arguments, the migration template and the table name instance
- # variables are set up.
- def set_local_assigns!
- @migration_template = "migration.rb"
- case file_name
- when /^(add|remove)_.*_(?:to|from)_(.*)/
- @migration_action = $1
- @table_name = normalize_table_name($2)
- when /join_table/
- if attributes.length == 2
- @migration_action = 'join'
- @join_tables = pluralize_table_names? ? attributes.map(&:plural_name) : attributes.map(&:singular_name)
+ private
+
+ # Sets the default migration template that is being used for the generation of the migration.
+ # Depending on command line arguments, the migration template and the table name instance
+ # variables are set up.
+ def set_local_assigns!
+ @migration_template = "migration.rb"
+ case file_name
+ when /^(add|remove)_.*_(?:to|from)_(.*)/
+ @migration_action = $1
+ @table_name = normalize_table_name($2)
+ when /join_table/
+ if attributes.length == 2
+ @migration_action = "join"
+ @join_tables = pluralize_table_names? ? attributes.map(&:plural_name) : attributes.map(&:singular_name)
- set_index_names
+ set_index_names
+ end
+ when /^create_(.+)/
+ @table_name = normalize_table_name($1)
+ @migration_template = "create_table_migration.rb"
end
- when /^create_(.+)/
- @table_name = normalize_table_name($1)
- @migration_template = "create_table_migration.rb"
end
- end
- def set_index_names
- attributes.each_with_index do |attr, i|
- attr.index_name = [attr, attributes[i - 1]].map{ |a| index_name_for(a) }
+ def set_index_names
+ attributes.each_with_index do |attr, i|
+ attr.index_name = [attr, attributes[i - 1]].map { |a| index_name_for(a) }
+ end
end
- end
- def index_name_for(attribute)
- if attribute.foreign_key?
- attribute.name
- else
- attribute.name.singularize.foreign_key
- end.to_sym
- end
+ def index_name_for(attribute)
+ if attribute.foreign_key?
+ attribute.name
+ else
+ attribute.name.singularize.foreign_key
+ end.to_sym
+ end
- private
def attributes_with_index
attributes.select { |a| !a.reference? && a.has_index? }
end
@@ -60,7 +63,7 @@ module ActiveRecord
# A migration file name can only contain underscores (_), lowercase characters,
# and numbers 0-9. Any other file name will raise an IllegalMigrationNameError.
def validate_file_name!
- unless file_name =~ /^[_a-z0-9]+$/
+ unless /^[_a-z0-9]+$/.match?(file_name)
raise IllegalMigrationNameError.new(file_name)
end
end
diff --git a/activerecord/lib/rails/generators/active_record/model/model_generator.rb b/activerecord/lib/rails/generators/active_record/model/model_generator.rb
index 0d72913258..61a8d3c100 100644
--- a/activerecord/lib/rails/generators/active_record/model/model_generator.rb
+++ b/activerecord/lib/rails/generators/active_record/model/model_generator.rb
@@ -1,9 +1,9 @@
-require 'rails/generators/active_record'
+require "rails/generators/active_record"
module ActiveRecord
module Generators # :nodoc:
class ModelGenerator < Base # :nodoc:
- argument :attributes, :type => :array, :default => [], :banner => "field[:type][:index] field[:type][:index]"
+ argument :attributes, type: :array, default: [], banner: "field[:type][:index] field[:type][:index]"
check_class_collision
@@ -22,18 +22,18 @@ module ActiveRecord
def create_model_file
generate_application_record
- template 'model.rb', File.join('app/models', class_path, "#{file_name}.rb")
+ template "model.rb", File.join("app/models", class_path, "#{file_name}.rb")
end
def create_module_file
return if regular_class_path.empty?
generate_application_record
- template 'module.rb', File.join('app/models', "#{class_path.join('/')}.rb") if behavior == :invoke
+ template "module.rb", File.join("app/models", "#{class_path.join('/')}.rb") if behavior == :invoke
end
hook_for :test_framework
- protected
+ private
def attributes_with_index
attributes.select { |a| !a.reference? && a.has_index? }
@@ -42,13 +42,13 @@ module ActiveRecord
# FIXME: Change this file to a symlink once RubyGems 2.5.0 is required.
def generate_application_record
if self.behavior == :invoke && !application_record_exist?
- template 'application_record.rb', application_record_file_name
+ template "application_record.rb", application_record_file_name
end
end
# Used by the migration template to determine the parent name of the model
def parent_class_name
- options[:parent] || 'ApplicationRecord'
+ options[:parent] || "ApplicationRecord"
end
def application_record_exist?
@@ -61,7 +61,7 @@ module ActiveRecord
@application_record_file_name ||= if mountable_engine?
"app/models/#{namespaced_path}/application_record.rb"
else
- 'app/models/application_record.rb'
+ "app/models/application_record.rb"
end
end
end
diff --git a/activerecord/test/active_record/connection_adapters/fake_adapter.rb b/activerecord/test/active_record/connection_adapters/fake_adapter.rb
index 43c817e057..b0d8050721 100644
--- a/activerecord/test/active_record/connection_adapters/fake_adapter.rb
+++ b/activerecord/test/active_record/connection_adapters/fake_adapter.rb
@@ -9,7 +9,7 @@ module ActiveRecord
class FakeAdapter < AbstractAdapter
attr_accessor :data_sources, :primary_keys
- @columns = Hash.new { |h,k| h[k] = [] }
+ @columns = Hash.new { |h, k| h[k] = [] }
class << self
attr_reader :columns
end
diff --git a/activerecord/test/assets/schema_dump_5_1.yml b/activerecord/test/assets/schema_dump_5_1.yml
new file mode 100644
index 0000000000..f37977daf2
--- /dev/null
+++ b/activerecord/test/assets/schema_dump_5_1.yml
@@ -0,0 +1,345 @@
+--- !ruby/object:ActiveRecord::ConnectionAdapters::SchemaCache
+columns:
+ posts:
+ - &1 !ruby/object:ActiveRecord::ConnectionAdapters::Column
+ name: id
+ table_name: posts
+ sql_type_metadata: !ruby/object:ActiveRecord::ConnectionAdapters::SqlTypeMetadata
+ sql_type: INTEGER
+ type: :integer
+ limit:
+ precision:
+ scale:
+ 'null': false
+ default:
+ default_function:
+ collation:
+ comment:
+ - &2 !ruby/object:ActiveRecord::ConnectionAdapters::Column
+ name: author_id
+ table_name: posts
+ sql_type_metadata: !ruby/object:ActiveRecord::ConnectionAdapters::SqlTypeMetadata
+ sql_type: integer
+ type: :integer
+ limit:
+ precision:
+ scale:
+ 'null': true
+ default:
+ default_function:
+ collation:
+ comment:
+ - &3 !ruby/object:ActiveRecord::ConnectionAdapters::Column
+ name: title
+ table_name: posts
+ sql_type_metadata: !ruby/object:ActiveRecord::ConnectionAdapters::SqlTypeMetadata
+ sql_type: varchar
+ type: :string
+ limit:
+ precision:
+ scale:
+ 'null': false
+ default:
+ default_function:
+ collation:
+ comment:
+ - &4 !ruby/object:ActiveRecord::ConnectionAdapters::Column
+ name: body
+ table_name: posts
+ sql_type_metadata: !ruby/object:ActiveRecord::ConnectionAdapters::SqlTypeMetadata
+ sql_type: text
+ type: :text
+ limit:
+ precision:
+ scale:
+ 'null': false
+ default:
+ default_function:
+ collation:
+ comment:
+ - &5 !ruby/object:ActiveRecord::ConnectionAdapters::Column
+ name: type
+ table_name: posts
+ sql_type_metadata: !ruby/object:ActiveRecord::ConnectionAdapters::SqlTypeMetadata
+ sql_type: varchar
+ type: :string
+ limit:
+ precision:
+ scale:
+ 'null': true
+ default:
+ default_function:
+ collation:
+ comment:
+ - &6 !ruby/object:ActiveRecord::ConnectionAdapters::Column
+ name: comments_count
+ table_name: posts
+ sql_type_metadata: !ruby/object:ActiveRecord::ConnectionAdapters::SqlTypeMetadata
+ sql_type: integer
+ type: :integer
+ limit:
+ precision:
+ scale:
+ 'null': true
+ default: '0'
+ default_function:
+ collation:
+ comment:
+ - &7 !ruby/object:ActiveRecord::ConnectionAdapters::Column
+ name: taggings_with_delete_all_count
+ table_name: posts
+ sql_type_metadata: !ruby/object:ActiveRecord::ConnectionAdapters::SqlTypeMetadata
+ sql_type: integer
+ type: :integer
+ limit:
+ precision:
+ scale:
+ 'null': true
+ default: '0'
+ default_function:
+ collation:
+ comment:
+ - &8 !ruby/object:ActiveRecord::ConnectionAdapters::Column
+ name: taggings_with_destroy_count
+ table_name: posts
+ sql_type_metadata: !ruby/object:ActiveRecord::ConnectionAdapters::SqlTypeMetadata
+ sql_type: integer
+ type: :integer
+ limit:
+ precision:
+ scale:
+ 'null': true
+ default: '0'
+ default_function:
+ collation:
+ comment:
+ - &9 !ruby/object:ActiveRecord::ConnectionAdapters::Column
+ name: tags_count
+ table_name: posts
+ sql_type_metadata: !ruby/object:ActiveRecord::ConnectionAdapters::SqlTypeMetadata
+ sql_type: integer
+ type: :integer
+ limit:
+ precision:
+ scale:
+ 'null': true
+ default: '0'
+ default_function:
+ collation:
+ comment:
+ - &10 !ruby/object:ActiveRecord::ConnectionAdapters::Column
+ name: tags_with_destroy_count
+ table_name: posts
+ sql_type_metadata: !ruby/object:ActiveRecord::ConnectionAdapters::SqlTypeMetadata
+ sql_type: integer
+ type: :integer
+ limit:
+ precision:
+ scale:
+ 'null': true
+ default: '0'
+ default_function:
+ collation:
+ comment:
+ - &11 !ruby/object:ActiveRecord::ConnectionAdapters::Column
+ name: tags_with_nullify_count
+ table_name: posts
+ sql_type_metadata: !ruby/object:ActiveRecord::ConnectionAdapters::SqlTypeMetadata
+ sql_type: integer
+ type: :integer
+ limit:
+ precision:
+ scale:
+ 'null': true
+ default: '0'
+ default_function:
+ collation:
+ comment:
+columns_hash:
+ posts:
+ id: *1
+ author_id: *2
+ title: *3
+ body: *4
+ type: *5
+ comments_count: *6
+ taggings_with_delete_all_count: *7
+ taggings_with_destroy_count: *8
+ tags_count: *9
+ tags_with_destroy_count: *10
+ tags_with_nullify_count: *11
+primary_keys:
+ posts: id
+data_sources:
+ ar_internal_metadata: true
+ table_with_autoincrement: true
+ accounts: true
+ admin_accounts: true
+ admin_users: true
+ aircraft: true
+ articles: true
+ articles_magazines: true
+ articles_tags: true
+ audit_logs: true
+ authors: true
+ author_addresses: true
+ author_favorites: true
+ auto_id_tests: true
+ binaries: true
+ birds: true
+ books: true
+ booleans: true
+ bulbs: true
+ CamelCase: true
+ cars: true
+ carriers: true
+ categories: true
+ categories_posts: true
+ categorizations: true
+ citations: true
+ clubs: true
+ collections: true
+ colnametests: true
+ columns: true
+ comments: true
+ companies: true
+ content: true
+ content_positions: true
+ vegetables: true
+ computers: true
+ computers_developers: true
+ contracts: true
+ customers: true
+ customer_carriers: true
+ dashboards: true
+ developers: true
+ developers_projects: true
+ dog_lovers: true
+ dogs: true
+ doubloons: true
+ edges: true
+ engines: true
+ entrants: true
+ essays: true
+ events: true
+ eyes: true
+ funny_jokes: true
+ cold_jokes: true
+ friendships: true
+ goofy_string_id: true
+ having: true
+ guids: true
+ guitars: true
+ inept_wizards: true
+ integer_limits: true
+ invoices: true
+ iris: true
+ items: true
+ jobs: true
+ jobs_pool: true
+ keyboards: true
+ legacy_things: true
+ lessons: true
+ lessons_students: true
+ students: true
+ lint_models: true
+ line_items: true
+ lions: true
+ lock_without_defaults: true
+ lock_without_defaults_cust: true
+ magazines: true
+ mateys: true
+ members: true
+ member_details: true
+ member_friends: true
+ memberships: true
+ member_types: true
+ mentors: true
+ minivans: true
+ minimalistics: true
+ mixed_case_monkeys: true
+ mixins: true
+ movies: true
+ notifications: true
+ numeric_data: true
+ orders: true
+ organizations: true
+ owners: true
+ paint_colors: true
+ paint_textures: true
+ parrots: true
+ parrots_pirates: true
+ parrots_treasures: true
+ people: true
+ peoples_treasures: true
+ personal_legacy_things: true
+ pets: true
+ pets_treasures: true
+ pirates: true
+ posts: true
+ serialized_posts: true
+ images: true
+ price_estimates: true
+ products: true
+ product_types: true
+ projects: true
+ randomly_named_table1: true
+ randomly_named_table2: true
+ randomly_named_table3: true
+ ratings: true
+ readers: true
+ references: true
+ shape_expressions: true
+ ships: true
+ ship_parts: true
+ prisoners: true
+ shop_accounts: true
+ speedometers: true
+ sponsors: true
+ string_key_objects: true
+ subscribers: true
+ subscriptions: true
+ tags: true
+ taggings: true
+ tasks: true
+ topics: true
+ toys: true
+ traffic_lights: true
+ treasures: true
+ tuning_pegs: true
+ tyres: true
+ variants: true
+ vertices: true
+ warehouse-things: true
+ circles: true
+ squares: true
+ triangles: true
+ non_poly_ones: true
+ non_poly_twos: true
+ men: true
+ faces: true
+ interests: true
+ zines: true
+ wheels: true
+ countries: true
+ treaties: true
+ countries_treaties: true
+ liquid: true
+ molecules: true
+ electrons: true
+ weirds: true
+ nodes: true
+ trees: true
+ hotels: true
+ departments: true
+ cake_designers: true
+ drink_designers: true
+ chefs: true
+ recipes: true
+ records: true
+ overloaded_types: true
+ users: true
+ test_with_keyword_column_name: true
+ fk_test_has_pk: true
+ fk_test_has_fk: true
+version: 0
diff --git a/activerecord/test/cases/adapter_test.rb b/activerecord/test/cases/adapter_test.rb
index 34e3bc9d66..9828e682ef 100644
--- a/activerecord/test/cases/adapter_test.rb
+++ b/activerecord/test/cases/adapter_test.rb
@@ -32,99 +32,91 @@ module ActiveRecord
def test_tables
tables = nil
- ActiveSupport::Deprecation.silence { tables = @connection.tables }
- assert tables.include?("accounts")
- assert tables.include?("authors")
- assert tables.include?("tasks")
- assert tables.include?("topics")
+ tables = @connection.tables
+ assert_includes tables, "accounts"
+ assert_includes tables, "authors"
+ assert_includes tables, "tasks"
+ assert_includes tables, "topics"
end
def test_table_exists?
- ActiveSupport::Deprecation.silence do
- assert @connection.table_exists?("accounts")
- assert !@connection.table_exists?("nonexistingtable")
- assert !@connection.table_exists?(nil)
- end
- end
-
- def test_table_exists_checking_both_tables_and_views_is_deprecated
- assert_deprecated { @connection.table_exists?("accounts") }
+ assert @connection.table_exists?("accounts")
+ assert @connection.table_exists?(:accounts)
+ assert_not @connection.table_exists?("nonexistingtable")
+ assert_not @connection.table_exists?("'")
+ assert_not @connection.table_exists?(nil)
end
def test_data_sources
data_sources = @connection.data_sources
- assert data_sources.include?("accounts")
- assert data_sources.include?("authors")
- assert data_sources.include?("tasks")
- assert data_sources.include?("topics")
+ assert_includes data_sources, "accounts"
+ assert_includes data_sources, "authors"
+ assert_includes data_sources, "tasks"
+ assert_includes data_sources, "topics"
end
def test_data_source_exists?
assert @connection.data_source_exists?("accounts")
assert @connection.data_source_exists?(:accounts)
assert_not @connection.data_source_exists?("nonexistingtable")
+ assert_not @connection.data_source_exists?("'")
assert_not @connection.data_source_exists?(nil)
end
def test_indexes
idx_name = "accounts_idx"
- if @connection.respond_to?(:indexes)
- indexes = @connection.indexes("accounts")
- assert indexes.empty?
-
- @connection.add_index :accounts, :firm_id, :name => idx_name
- indexes = @connection.indexes("accounts")
- assert_equal "accounts", indexes.first.table
- assert_equal idx_name, indexes.first.name
- assert !indexes.first.unique
- assert_equal ["firm_id"], indexes.first.columns
- else
- warn "#{@connection.class} does not respond to #indexes"
- end
+ indexes = @connection.indexes("accounts")
+ assert indexes.empty?
+ @connection.add_index :accounts, :firm_id, name: idx_name
+ indexes = @connection.indexes("accounts")
+ assert_equal "accounts", indexes.first.table
+ assert_equal idx_name, indexes.first.name
+ assert !indexes.first.unique
+ assert_equal ["firm_id"], indexes.first.columns
ensure
- @connection.remove_index(:accounts, :name => idx_name) rescue nil
+ @connection.remove_index(:accounts, name: idx_name) rescue nil
end
def test_remove_index_when_name_and_wrong_column_name_specified
index_name = "accounts_idx"
- @connection.add_index :accounts, :firm_id, :name => index_name
+ @connection.add_index :accounts, :firm_id, name: index_name
assert_raises ArgumentError do
- @connection.remove_index :accounts, :name => index_name, :column => :wrong_column_name
+ @connection.remove_index :accounts, name: index_name, column: :wrong_column_name
end
ensure
- @connection.remove_index(:accounts, :name => index_name)
+ @connection.remove_index(:accounts, name: index_name)
end
def test_current_database
if @connection.respond_to?(:current_database)
- assert_equal ARTest.connection_config['arunit']['database'], @connection.current_database
+ assert_equal ARTest.connection_config["arunit"]["database"], @connection.current_database
end
end
if current_adapter?(:Mysql2Adapter)
def test_charset
assert_not_nil @connection.charset
- assert_not_equal 'character_set_database', @connection.charset
- assert_equal @connection.show_variable('character_set_database'), @connection.charset
+ assert_not_equal "character_set_database", @connection.charset
+ assert_equal @connection.show_variable("character_set_database"), @connection.charset
end
def test_collation
assert_not_nil @connection.collation
- assert_not_equal 'collation_database', @connection.collation
- assert_equal @connection.show_variable('collation_database'), @connection.collation
+ assert_not_equal "collation_database", @connection.collation
+ assert_equal @connection.show_variable("collation_database"), @connection.collation
end
def test_show_nonexistent_variable_returns_nil
- assert_nil @connection.show_variable('foo_bar_baz')
+ assert_nil @connection.show_variable("foo_bar_baz")
end
def test_not_specifying_database_name_for_cross_database_selects
begin
assert_nothing_raised do
- ActiveRecord::Base.establish_connection(ActiveRecord::Base.configurations['arunit'].except(:database))
+ ActiveRecord::Base.establish_connection(ActiveRecord::Base.configurations["arunit"].except(:database))
config = ARTest.connection_config
ActiveRecord::Base.connection.execute(
@@ -145,9 +137,9 @@ module ActiveRecord
alias_method :table_alias_length, :test_table_alias_length
end
- assert_equal 'posts', @connection.table_alias_for('posts')
- assert_equal 'posts_comm', @connection.table_alias_for('posts_comments')
- assert_equal 'dbo_posts', @connection.table_alias_for('dbo.posts')
+ assert_equal "posts", @connection.table_alias_for("posts")
+ assert_equal "posts_comm", @connection.table_alias_for("posts_comments")
+ assert_equal "dbo_posts", @connection.table_alias_for("dbo.posts")
class << @connection
remove_method :table_alias_length
@@ -157,20 +149,20 @@ module ActiveRecord
# test resetting sequences in odd tables in PostgreSQL
if ActiveRecord::Base.connection.respond_to?(:reset_pk_sequence!)
- require 'models/movie'
- require 'models/subscriber'
+ require "models/movie"
+ require "models/subscriber"
def test_reset_empty_table_with_custom_pk
Movie.delete_all
- Movie.connection.reset_pk_sequence! 'movies'
- assert_equal 1, Movie.create(:name => 'fight club').id
+ Movie.connection.reset_pk_sequence! "movies"
+ assert_equal 1, Movie.create(name: "fight club").id
end
def test_reset_table_with_non_integer_pk
Subscriber.delete_all
- Subscriber.connection.reset_pk_sequence! 'subscribers'
- sub = Subscriber.new(:name => 'robert drake')
- sub.id = 'bob drake'
+ Subscriber.connection.reset_pk_sequence! "subscribers"
+ sub = Subscriber.new(name: "robert drake")
+ sub.id = "bob drake"
assert_nothing_raised { sub.save! }
end
end
@@ -184,6 +176,14 @@ module ActiveRecord
assert_not_nil error.cause
end
+ def test_not_null_violations_are_translated_to_specific_exception
+ error = assert_raises(ActiveRecord::NotNullViolation) do
+ Post.create
+ end
+
+ assert_not_nil error.cause
+ end
+
unless current_adapter?(:SQLite3Adapter)
def test_foreign_key_violations_are_translated_to_specific_exception
error = assert_raises(ActiveRecord::InvalidForeignKey) do
@@ -201,7 +201,7 @@ module ActiveRecord
def test_foreign_key_violations_are_translated_to_specific_exception_with_validate_false
klass_has_fk = Class.new(ActiveRecord::Base) do
- self.table_name = 'fk_test_has_fk'
+ self.table_name = "fk_test_has_fk"
end
error = assert_raises(ActiveRecord::InvalidForeignKey) do
@@ -215,7 +215,15 @@ module ActiveRecord
def test_value_limit_violations_are_translated_to_specific_exception
error = assert_raises(ActiveRecord::ValueTooLong) do
- Event.create(title: 'abcdefgh')
+ Event.create(title: "abcdefgh")
+ end
+
+ assert_not_nil error.cause
+ end
+
+ def test_numeric_value_out_of_ranges_are_translated_to_specific_exception
+ error = assert_raises(ActiveRecord::RangeError) do
+ Book.connection.create("INSERT INTO books(author_id) VALUES (2147483648)")
end
assert_not_nil error.cause
@@ -245,21 +253,21 @@ module ActiveRecord
end
def test_select_methods_passing_a_association_relation
- author = Author.create!(name: 'john')
- Post.create!(author: author, title: 'foo', body: 'bar')
- query = author.posts.where(title: 'foo').select(:title)
- assert_equal({"title" => "foo"}, @connection.select_one(query.arel, nil, query.bound_attributes))
- assert_equal({"title" => "foo"}, @connection.select_one(query))
+ author = Author.create!(name: "john")
+ Post.create!(author: author, title: "foo", body: "bar")
+ query = author.posts.where(title: "foo").select(:title)
+ assert_equal({ "title" => "foo" }, @connection.select_one(query.arel, nil, query.bound_attributes))
+ assert_equal({ "title" => "foo" }, @connection.select_one(query))
assert @connection.select_all(query).is_a?(ActiveRecord::Result)
assert_equal "foo", @connection.select_value(query)
assert_equal ["foo"], @connection.select_values(query)
end
def test_select_methods_passing_a_relation
- Post.create!(title: 'foo', body: 'bar')
- query = Post.where(title: 'foo').select(:title)
- assert_equal({"title" => "foo"}, @connection.select_one(query.arel, nil, query.bound_attributes))
- assert_equal({"title" => "foo"}, @connection.select_one(query))
+ Post.create!(title: "foo", body: "bar")
+ query = Post.where(title: "foo").select(:title)
+ assert_equal({ "title" => "foo" }, @connection.select_one(query.arel, nil, query.bound_attributes))
+ assert_equal({ "title" => "foo" }, @connection.select_one(query))
assert @connection.select_all(query).is_a?(ActiveRecord::Result)
assert_equal "foo", @connection.select_value(query)
assert_equal ["foo"], @connection.select_values(query)
@@ -271,25 +279,15 @@ module ActiveRecord
unless current_adapter?(:PostgreSQLAdapter)
def test_log_invalid_encoding
- error = assert_raise ActiveRecord::StatementInvalid do
+ error = assert_raises RuntimeError do
@connection.send :log, "SELECT 'ы' FROM DUAL" do
- raise 'ы'.force_encoding(Encoding::ASCII_8BIT)
+ raise "ы".force_encoding(Encoding::ASCII_8BIT)
end
end
- assert_not_nil error.cause
- end
- end
-
- if current_adapter?(:Mysql2Adapter, :SQLite3Adapter)
- def test_tables_returning_both_tables_and_views_is_deprecated
- assert_deprecated { @connection.tables }
+ assert_not_nil error.message
end
end
-
- def test_passing_arguments_to_tables_is_deprecated
- assert_deprecated { @connection.tables(:books) }
- end
end
class AdapterTestWithoutTransaction < ActiveRecord::TestCase
diff --git a/activerecord/test/cases/adapters/mysql2/active_schema_test.rb b/activerecord/test/cases/adapters/mysql2/active_schema_test.rb
index 95d1f6b8a3..2a528b2cb1 100644
--- a/activerecord/test/cases/adapters/mysql2/active_schema_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/active_schema_test.rb
@@ -1,5 +1,5 @@
require "cases/helper"
-require 'support/connection_helper'
+require "support/connection_helper"
class Mysql2ActiveSchemaTest < ActiveRecord::Mysql2TestCase
include ConnectionHelper
@@ -21,42 +21,45 @@ class Mysql2ActiveSchemaTest < ActiveRecord::Mysql2TestCase
def (ActiveRecord::Base.connection).index_name_exists?(*); false; end
expected = "CREATE INDEX `index_people_on_last_name` ON `people` (`last_name`) "
- assert_equal expected, add_index(:people, :last_name, :length => nil)
+ assert_equal expected, add_index(:people, :last_name, length: nil)
expected = "CREATE INDEX `index_people_on_last_name` ON `people` (`last_name`(10)) "
- assert_equal expected, add_index(:people, :last_name, :length => 10)
+ assert_equal expected, add_index(:people, :last_name, length: 10)
expected = "CREATE INDEX `index_people_on_last_name_and_first_name` ON `people` (`last_name`(15), `first_name`(15)) "
- assert_equal expected, add_index(:people, [:last_name, :first_name], :length => 15)
+ assert_equal expected, add_index(:people, [:last_name, :first_name], length: 15)
+ assert_equal expected, add_index(:people, ["last_name", "first_name"], length: 15)
expected = "CREATE INDEX `index_people_on_last_name_and_first_name` ON `people` (`last_name`(15), `first_name`) "
- assert_equal expected, add_index(:people, [:last_name, :first_name], :length => {:last_name => 15})
+ assert_equal expected, add_index(:people, [:last_name, :first_name], length: { last_name: 15 })
+ assert_equal expected, add_index(:people, ["last_name", "first_name"], length: { last_name: 15 })
expected = "CREATE INDEX `index_people_on_last_name_and_first_name` ON `people` (`last_name`(15), `first_name`(10)) "
- assert_equal expected, add_index(:people, [:last_name, :first_name], :length => {:last_name => 15, :first_name => 10})
+ assert_equal expected, add_index(:people, [:last_name, :first_name], length: { last_name: 15, first_name: 10 })
+ assert_equal expected, add_index(:people, ["last_name", :first_name], length: { last_name: 15, "first_name" => 10 })
%w(SPATIAL FULLTEXT UNIQUE).each do |type|
expected = "CREATE #{type} INDEX `index_people_on_last_name` ON `people` (`last_name`) "
- assert_equal expected, add_index(:people, :last_name, :type => type)
+ assert_equal expected, add_index(:people, :last_name, type: type)
end
%w(btree hash).each do |using|
expected = "CREATE INDEX `index_people_on_last_name` USING #{using} ON `people` (`last_name`) "
- assert_equal expected, add_index(:people, :last_name, :using => using)
+ assert_equal expected, add_index(:people, :last_name, using: using)
end
expected = "CREATE INDEX `index_people_on_last_name` USING btree ON `people` (`last_name`(10)) "
- assert_equal expected, add_index(:people, :last_name, :length => 10, :using => :btree)
+ assert_equal expected, add_index(:people, :last_name, length: 10, using: :btree)
expected = "CREATE INDEX `index_people_on_last_name` USING btree ON `people` (`last_name`(10)) ALGORITHM = COPY"
- assert_equal expected, add_index(:people, :last_name, :length => 10, using: :btree, algorithm: :copy)
+ assert_equal expected, add_index(:people, :last_name, length: 10, using: :btree, algorithm: :copy)
assert_raise ArgumentError do
add_index(:people, :last_name, algorithm: :coyp)
end
expected = "CREATE INDEX `index_people_on_last_name_and_first_name` USING btree ON `people` (`last_name`(15), `first_name`(15)) "
- assert_equal expected, add_index(:people, [:last_name, :first_name], :length => 15, :using => :btree)
+ assert_equal expected, add_index(:people, [:last_name, :first_name], length: 15, using: :btree)
end
def test_index_in_create
@@ -102,13 +105,13 @@ class Mysql2ActiveSchemaTest < ActiveRecord::Mysql2TestCase
def test_create_mysql_database_with_encoding
assert_equal "CREATE DATABASE `matt` DEFAULT CHARACTER SET `utf8`", create_database(:matt)
- assert_equal "CREATE DATABASE `aimonetti` DEFAULT CHARACTER SET `latin1`", create_database(:aimonetti, {:charset => 'latin1'})
- assert_equal "CREATE DATABASE `matt_aimonetti` DEFAULT CHARACTER SET `big5` COLLATE `big5_chinese_ci`", create_database(:matt_aimonetti, {:charset => :big5, :collation => :big5_chinese_ci})
+ assert_equal "CREATE DATABASE `aimonetti` DEFAULT CHARACTER SET `latin1`", create_database(:aimonetti, charset: "latin1")
+ assert_equal "CREATE DATABASE `matt_aimonetti` DEFAULT CHARACTER SET `big5` COLLATE `big5_chinese_ci`", create_database(:matt_aimonetti, charset: :big5, collation: :big5_chinese_ci)
end
def test_recreate_mysql_database_with_encoding
- create_database(:luca, {:charset => 'latin1'})
- assert_equal "CREATE DATABASE `luca` DEFAULT CHARACTER SET `latin1`", recreate_database(:luca, {:charset => 'latin1'})
+ create_database(:luca, charset: "latin1")
+ assert_equal "CREATE DATABASE `luca` DEFAULT CHARACTER SET `latin1`", recreate_database(:luca, charset: "latin1")
end
def test_add_column
@@ -116,11 +119,11 @@ class Mysql2ActiveSchemaTest < ActiveRecord::Mysql2TestCase
end
def test_add_column_with_limit
- assert_equal "ALTER TABLE `people` ADD `key` varchar(32)", add_column(:people, :key, :string, :limit => 32)
+ assert_equal "ALTER TABLE `people` ADD `key` varchar(32)", add_column(:people, :key, :string, limit: 32)
end
def test_drop_table_with_specific_database
- assert_equal "DROP TABLE `otherdb`.`people`", drop_table('otherdb.people')
+ assert_equal "DROP TABLE `otherdb`.`people`", drop_table("otherdb.people")
end
def test_add_timestamps
@@ -128,8 +131,8 @@ class Mysql2ActiveSchemaTest < ActiveRecord::Mysql2TestCase
begin
ActiveRecord::Base.connection.create_table :delete_me
ActiveRecord::Base.connection.add_timestamps :delete_me, null: true
- assert column_present?('delete_me', 'updated_at', 'datetime')
- assert column_present?('delete_me', 'created_at', 'datetime')
+ assert column_present?("delete_me", "updated_at", "datetime")
+ assert column_present?("delete_me", "created_at", "datetime")
ensure
ActiveRecord::Base.connection.drop_table :delete_me rescue nil
end
@@ -142,9 +145,9 @@ class Mysql2ActiveSchemaTest < ActiveRecord::Mysql2TestCase
ActiveRecord::Base.connection.create_table :delete_me do |t|
t.timestamps null: true
end
- ActiveRecord::Base.connection.remove_timestamps :delete_me, { null: true }
- assert !column_present?('delete_me', 'updated_at', 'datetime')
- assert !column_present?('delete_me', 'created_at', 'datetime')
+ ActiveRecord::Base.connection.remove_timestamps :delete_me, null: true
+ assert !column_present?("delete_me", "updated_at", "datetime")
+ assert !column_present?("delete_me", "created_at", "datetime")
ensure
ActiveRecord::Base.connection.drop_table :delete_me rescue nil
end
@@ -185,6 +188,6 @@ class Mysql2ActiveSchemaTest < ActiveRecord::Mysql2TestCase
def column_present?(table_name, column_name, type)
results = ActiveRecord::Base.connection.select_all("SHOW FIELDS FROM #{table_name} LIKE '#{column_name}'")
- results.first && results.first['Type'] == type
+ results.first && results.first["Type"] == type
end
end
diff --git a/activerecord/test/cases/adapters/mysql2/bind_parameter_test.rb b/activerecord/test/cases/adapters/mysql2/bind_parameter_test.rb
index abdf3dbf5b..8f7c803a21 100644
--- a/activerecord/test/cases/adapters/mysql2/bind_parameter_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/bind_parameter_test.rb
@@ -1,5 +1,5 @@
require "cases/helper"
-require 'models/topic'
+require "models/topic"
module ActiveRecord
module ConnectionAdapters
@@ -20,7 +20,7 @@ module ActiveRecord
def test_create_question_marks
str = "foo?bar"
- x = Topic.create!(:title => str, :content => str)
+ x = Topic.create!(title: str, content: str)
x.reload
assert_equal str, x.title
assert_equal str, x.content
@@ -39,7 +39,7 @@ module ActiveRecord
def test_create_null_bytes
str = "foo\0bar"
- x = Topic.create!(:title => str, :content => str)
+ x = Topic.create!(title: str, content: str)
x.reload
assert_equal str, x.title
assert_equal str, x.content
diff --git a/activerecord/test/cases/adapters/mysql2/boolean_test.rb b/activerecord/test/cases/adapters/mysql2/boolean_test.rb
index 739bb275ce..2fa39282fb 100644
--- a/activerecord/test/cases/adapters/mysql2/boolean_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/boolean_test.rb
@@ -86,11 +86,11 @@ class Mysql2BooleanTest < ActiveRecord::Mysql2TestCase
end
def boolean_column
- BooleanType.columns.find { |c| c.name == 'archived' }
+ BooleanType.columns.find { |c| c.name == "archived" }
end
def string_column
- BooleanType.columns.find { |c| c.name == 'published' }
+ BooleanType.columns.find { |c| c.name == "published" }
end
def emulate_booleans(value)
diff --git a/activerecord/test/cases/adapters/mysql2/case_sensitivity_test.rb b/activerecord/test/cases/adapters/mysql2/case_sensitivity_test.rb
index 9cb05119a2..50ba9ab831 100644
--- a/activerecord/test/cases/adapters/mysql2/case_sensitivity_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/case_sensitivity_test.rb
@@ -7,46 +7,46 @@ class Mysql2CaseSensitivityTest < ActiveRecord::Mysql2TestCase
repair_validations(CollationTest)
def test_columns_include_collation_different_from_table
- assert_equal 'utf8_bin', CollationTest.columns_hash['string_cs_column'].collation
- assert_equal 'utf8_general_ci', CollationTest.columns_hash['string_ci_column'].collation
+ assert_equal "utf8_bin", CollationTest.columns_hash["string_cs_column"].collation
+ assert_equal "utf8_general_ci", CollationTest.columns_hash["string_ci_column"].collation
end
def test_case_sensitive
- assert !CollationTest.columns_hash['string_ci_column'].case_sensitive?
- assert CollationTest.columns_hash['string_cs_column'].case_sensitive?
+ assert !CollationTest.columns_hash["string_ci_column"].case_sensitive?
+ assert CollationTest.columns_hash["string_cs_column"].case_sensitive?
end
def test_case_insensitive_comparison_for_ci_column
- CollationTest.validates_uniqueness_of(:string_ci_column, :case_sensitive => false)
- CollationTest.create!(:string_ci_column => 'A')
- invalid = CollationTest.new(:string_ci_column => 'a')
+ CollationTest.validates_uniqueness_of(:string_ci_column, case_sensitive: false)
+ CollationTest.create!(string_ci_column: "A")
+ invalid = CollationTest.new(string_ci_column: "a")
queries = assert_sql { invalid.save }
ci_uniqueness_query = queries.detect { |q| q.match(/string_ci_column/) }
assert_no_match(/lower/i, ci_uniqueness_query)
end
def test_case_insensitive_comparison_for_cs_column
- CollationTest.validates_uniqueness_of(:string_cs_column, :case_sensitive => false)
- CollationTest.create!(:string_cs_column => 'A')
- invalid = CollationTest.new(:string_cs_column => 'a')
+ CollationTest.validates_uniqueness_of(:string_cs_column, case_sensitive: false)
+ CollationTest.create!(string_cs_column: "A")
+ invalid = CollationTest.new(string_cs_column: "a")
queries = assert_sql { invalid.save }
- cs_uniqueness_query = queries.detect { |q| q.match(/string_cs_column/)}
+ cs_uniqueness_query = queries.detect { |q| q.match(/string_cs_column/) }
assert_match(/lower/i, cs_uniqueness_query)
end
def test_case_sensitive_comparison_for_ci_column
- CollationTest.validates_uniqueness_of(:string_ci_column, :case_sensitive => true)
- CollationTest.create!(:string_ci_column => 'A')
- invalid = CollationTest.new(:string_ci_column => 'A')
+ CollationTest.validates_uniqueness_of(:string_ci_column, case_sensitive: true)
+ CollationTest.create!(string_ci_column: "A")
+ invalid = CollationTest.new(string_ci_column: "A")
queries = assert_sql { invalid.save }
ci_uniqueness_query = queries.detect { |q| q.match(/string_ci_column/) }
assert_match(/binary/i, ci_uniqueness_query)
end
def test_case_sensitive_comparison_for_cs_column
- CollationTest.validates_uniqueness_of(:string_cs_column, :case_sensitive => true)
- CollationTest.create!(:string_cs_column => 'A')
- invalid = CollationTest.new(:string_cs_column => 'A')
+ CollationTest.validates_uniqueness_of(:string_cs_column, case_sensitive: true)
+ CollationTest.create!(string_cs_column: "A")
+ invalid = CollationTest.new(string_cs_column: "A")
queries = assert_sql { invalid.save }
cs_uniqueness_query = queries.detect { |q| q.match(/string_cs_column/) }
assert_no_match(/binary/i, cs_uniqueness_query)
@@ -54,8 +54,8 @@ class Mysql2CaseSensitivityTest < ActiveRecord::Mysql2TestCase
def test_case_sensitive_comparison_for_binary_column
CollationTest.validates_uniqueness_of(:binary_column, case_sensitive: true)
- CollationTest.create!(binary_column: 'A')
- invalid = CollationTest.new(binary_column: 'A')
+ CollationTest.create!(binary_column: "A")
+ invalid = CollationTest.new(binary_column: "A")
queries = assert_sql { invalid.save }
bin_uniqueness_query = queries.detect { |q| q.match(/binary_column/) }
assert_no_match(/\bBINARY\b/, bin_uniqueness_query)
diff --git a/activerecord/test/cases/adapters/mysql2/charset_collation_test.rb b/activerecord/test/cases/adapters/mysql2/charset_collation_test.rb
index c8028b6b36..8826ad7fd1 100644
--- a/activerecord/test/cases/adapters/mysql2/charset_collation_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/charset_collation_test.rb
@@ -1,5 +1,5 @@
require "cases/helper"
-require 'support/schema_dumping_helper'
+require "support/schema_dumping_helper"
class Mysql2CharsetCollationTest < ActiveRecord::Mysql2TestCase
include SchemaDumpingHelper
@@ -8,8 +8,8 @@ class Mysql2CharsetCollationTest < ActiveRecord::Mysql2TestCase
setup do
@connection = ActiveRecord::Base.connection
@connection.create_table :charset_collations, force: true do |t|
- t.string :string_ascii_bin, charset: 'ascii', collation: 'ascii_bin'
- t.text :text_ucs2_unicode_ci, charset: 'ucs2', collation: 'ucs2_unicode_ci'
+ t.string :string_ascii_bin, charset: "ascii", collation: "ascii_bin"
+ t.text :text_ucs2_unicode_ci, charset: "ucs2", collation: "ucs2_unicode_ci"
end
end
@@ -18,32 +18,32 @@ class Mysql2CharsetCollationTest < ActiveRecord::Mysql2TestCase
end
test "string column with charset and collation" do
- column = @connection.columns(:charset_collations).find { |c| c.name == 'string_ascii_bin' }
+ column = @connection.columns(:charset_collations).find { |c| c.name == "string_ascii_bin" }
assert_equal :string, column.type
- assert_equal 'ascii_bin', column.collation
+ assert_equal "ascii_bin", column.collation
end
test "text column with charset and collation" do
- column = @connection.columns(:charset_collations).find { |c| c.name == 'text_ucs2_unicode_ci' }
+ column = @connection.columns(:charset_collations).find { |c| c.name == "text_ucs2_unicode_ci" }
assert_equal :text, column.type
- assert_equal 'ucs2_unicode_ci', column.collation
+ assert_equal "ucs2_unicode_ci", column.collation
end
test "add column with charset and collation" do
- @connection.add_column :charset_collations, :title, :string, charset: 'utf8', collation: 'utf8_bin'
+ @connection.add_column :charset_collations, :title, :string, charset: "utf8", collation: "utf8_bin"
- column = @connection.columns(:charset_collations).find { |c| c.name == 'title' }
+ column = @connection.columns(:charset_collations).find { |c| c.name == "title" }
assert_equal :string, column.type
- assert_equal 'utf8_bin', column.collation
+ assert_equal "utf8_bin", column.collation
end
test "change column with charset and collation" do
- @connection.add_column :charset_collations, :description, :string, charset: 'utf8', collation: 'utf8_unicode_ci'
- @connection.change_column :charset_collations, :description, :text, charset: 'utf8', collation: 'utf8_general_ci'
+ @connection.add_column :charset_collations, :description, :string, charset: "utf8", collation: "utf8_unicode_ci"
+ @connection.change_column :charset_collations, :description, :text, charset: "utf8", collation: "utf8_general_ci"
- column = @connection.columns(:charset_collations).find { |c| c.name == 'description' }
+ column = @connection.columns(:charset_collations).find { |c| c.name == "description" }
assert_equal :text, column.type
- assert_equal 'utf8_general_ci', column.collation
+ assert_equal "utf8_general_ci", column.collation
end
test "schema dump includes collation" do
diff --git a/activerecord/test/cases/adapters/mysql2/connection_test.rb b/activerecord/test/cases/adapters/mysql2/connection_test.rb
index fe610ae951..c1de2218e2 100644
--- a/activerecord/test/cases/adapters/mysql2/connection_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/connection_test.rb
@@ -1,5 +1,5 @@
require "cases/helper"
-require 'support/connection_helper'
+require "support/connection_helper"
class Mysql2ConnectionTest < ActiveRecord::Mysql2TestCase
include ConnectionHelper
@@ -9,7 +9,7 @@ class Mysql2ConnectionTest < ActiveRecord::Mysql2TestCase
def setup
super
@subscriber = SQLSubscriber.new
- @subscription = ActiveSupport::Notifications.subscribe('sql.active_record', @subscriber)
+ @subscription = ActiveSupport::Notifications.subscribe("sql.active_record", @subscriber)
@connection = ActiveRecord::Base.connection
end
@@ -20,9 +20,9 @@ class Mysql2ConnectionTest < ActiveRecord::Mysql2TestCase
def test_bad_connection
assert_raise ActiveRecord::NoDatabaseError do
- configuration = ActiveRecord::Base.configurations['arunit'].merge(database: 'inexistent_activerecord_unittest')
+ configuration = ActiveRecord::Base.configurations["arunit"].merge(database: "inexistent_activerecord_unittest")
connection = ActiveRecord::Base.mysql2_connection(configuration)
- connection.drop_table 'ex', if_exists: true
+ connection.drop_table "ex", if_exists: true
end
end
@@ -39,7 +39,7 @@ class Mysql2ConnectionTest < ActiveRecord::Mysql2TestCase
def test_no_automatic_reconnection_after_timeout
assert @connection.active?
- @connection.update('set @@wait_timeout=1')
+ @connection.update("set @@wait_timeout=1")
sleep 2
assert !@connection.active?
@@ -49,7 +49,7 @@ class Mysql2ConnectionTest < ActiveRecord::Mysql2TestCase
def test_successful_reconnection_after_timeout_with_manual_reconnect
assert @connection.active?
- @connection.update('set @@wait_timeout=1')
+ @connection.update("set @@wait_timeout=1")
sleep 2
@connection.reconnect!
assert @connection.active?
@@ -57,15 +57,36 @@ class Mysql2ConnectionTest < ActiveRecord::Mysql2TestCase
def test_successful_reconnection_after_timeout_with_verify
assert @connection.active?
- @connection.update('set @@wait_timeout=1')
+ @connection.update("set @@wait_timeout=1")
sleep 2
@connection.verify!
assert @connection.active?
end
+ def test_execute_after_disconnect
+ @connection.disconnect!
+
+ assert_raise(ActiveRecord::StatementInvalid) do
+ @connection.execute("SELECT 1")
+ end
+ end
+
+ def test_quote_after_disconnect
+ @connection.disconnect!
+
+ assert_raise(Mysql2::Error) do
+ @connection.quote("string")
+ end
+ end
+
+ def test_active_after_disconnect
+ @connection.disconnect!
+ assert_equal false, @connection.active?
+ end
+
def test_mysql_connection_collation_is_configured
- assert_equal 'utf8_unicode_ci', @connection.show_variable('collation_connection')
- assert_equal 'utf8_general_ci', ARUnit2Model.connection.show_variable('collation_connection')
+ assert_equal "utf8_unicode_ci", @connection.show_variable("collation_connection")
+ assert_equal "utf8_general_ci", ARUnit2Model.connection.show_variable("collation_connection")
end
def test_mysql_default_in_strict_mode
@@ -92,29 +113,29 @@ class Mysql2ConnectionTest < ActiveRecord::Mysql2TestCase
def test_mysql_sql_mode_variable_overrides_strict_mode
run_without_connection do |orig_connection|
- ActiveRecord::Base.establish_connection(orig_connection.deep_merge(variables: { 'sql_mode' => 'ansi' }))
- result = ActiveRecord::Base.connection.select_value('SELECT @@SESSION.sql_mode')
+ ActiveRecord::Base.establish_connection(orig_connection.deep_merge(variables: { "sql_mode" => "ansi" }))
+ result = ActiveRecord::Base.connection.select_value("SELECT @@SESSION.sql_mode")
assert_no_match %r(STRICT_ALL_TABLES), result
end
end
def test_passing_arbitary_flags_to_adapter
run_without_connection do |orig_connection|
- ActiveRecord::Base.establish_connection(orig_connection.merge({flags: Mysql2::Client::COMPRESS}))
- assert_equal (Mysql2::Client::COMPRESS | Mysql2::Client::FOUND_ROWS), ActiveRecord::Base.connection.raw_connection.query_options[:flags]
+ ActiveRecord::Base.establish_connection(orig_connection.merge(flags: Mysql2::Client::COMPRESS))
+ assert_equal (Mysql2::Client::COMPRESS | Mysql2::Client::FOUND_ROWS), ActiveRecord::Base.connection.raw_connection.query_options[:flags]
end
end
def test_passing_flags_by_array_to_adapter
run_without_connection do |orig_connection|
- ActiveRecord::Base.establish_connection(orig_connection.merge({flags: ['COMPRESS'] }))
+ ActiveRecord::Base.establish_connection(orig_connection.merge(flags: ["COMPRESS"]))
assert_equal ["COMPRESS", "FOUND_ROWS"], ActiveRecord::Base.connection.raw_connection.query_options[:flags]
end
end
def test_mysql_set_session_variable
run_without_connection do |orig_connection|
- ActiveRecord::Base.establish_connection(orig_connection.deep_merge({:variables => {:default_week_format => 3}}))
+ ActiveRecord::Base.establish_connection(orig_connection.deep_merge(variables: { default_week_format: 3 }))
session_mode = ActiveRecord::Base.connection.exec_query "SELECT @@SESSION.DEFAULT_WEEK_FORMAT"
assert_equal 3, session_mode.rows.first.first.to_i
end
@@ -122,7 +143,7 @@ class Mysql2ConnectionTest < ActiveRecord::Mysql2TestCase
def test_mysql_set_session_variable_to_default
run_without_connection do |orig_connection|
- ActiveRecord::Base.establish_connection(orig_connection.deep_merge({:variables => {:default_week_format => :default}}))
+ ActiveRecord::Base.establish_connection(orig_connection.deep_merge(variables: { default_week_format: :default }))
global_mode = ActiveRecord::Base.connection.exec_query "SELECT @@GLOBAL.DEFAULT_WEEK_FORMAT"
session_mode = ActiveRecord::Base.connection.exec_query "SELECT @@SESSION.DEFAULT_WEEK_FORMAT"
assert_equal global_mode.rows, session_mode.rows
@@ -130,14 +151,14 @@ class Mysql2ConnectionTest < ActiveRecord::Mysql2TestCase
end
def test_logs_name_show_variable
- @connection.show_variable 'foo'
+ @connection.show_variable "foo"
assert_equal "SCHEMA", @subscriber.logged[0][1]
end
def test_logs_name_rename_column_sql
@connection.execute "CREATE TABLE `bar_baz` (`foo` varchar(255))"
@subscriber.logged.clear
- @connection.send(:rename_column_sql, 'bar_baz', 'foo', 'foo2')
+ @connection.send(:rename_column_sql, "bar_baz", "foo", "foo2")
assert_equal "SCHEMA", @subscriber.logged[0][1]
ensure
@connection.execute "DROP TABLE `bar_baz`"
@@ -155,19 +176,19 @@ class Mysql2ConnectionTest < ActiveRecord::Mysql2TestCase
released_lock = @connection.release_advisory_lock(lock_name)
assert released_lock, "expected release_advisory_lock to return true but it didn't"
- assert test_lock_free(lock_name), 'expected the test lock to be available after releasing'
+ assert test_lock_free(lock_name), "expected the test lock to be available after releasing"
end
def test_release_non_existent_advisory_lock
lock_name = "fake lock'n'name"
released_non_existent_lock = @connection.release_advisory_lock(lock_name)
assert_equal released_non_existent_lock, false,
- 'expected release_advisory_lock to return false when there was no lock to release'
+ "expected release_advisory_lock to return false when there was no lock to release"
end
- protected
+ private
- def test_lock_free(lock_name)
- @connection.select_value("SELECT IS_FREE_LOCK(#{@connection.quote(lock_name)})") == 1
- end
+ def test_lock_free(lock_name)
+ @connection.select_value("SELECT IS_FREE_LOCK(#{@connection.quote(lock_name)})") == 1
+ end
end
diff --git a/activerecord/test/cases/adapters/mysql2/datetime_precision_quoting_test.rb b/activerecord/test/cases/adapters/mysql2/datetime_precision_quoting_test.rb
index e349c67c93..135789a57d 100644
--- a/activerecord/test/cases/adapters/mysql2/datetime_precision_quoting_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/datetime_precision_quoting_test.rb
@@ -5,23 +5,23 @@ class Mysql2DatetimePrecisionQuotingTest < ActiveRecord::Mysql2TestCase
@connection = ActiveRecord::Base.connection
end
- test 'microsecond precision for MySQL gte 5.6.4' do
- stub_version '5.6.4'
+ test "microsecond precision for MySQL gte 5.6.4" do
+ stub_version "5.6.4"
assert_microsecond_precision
end
- test 'no microsecond precision for MySQL lt 5.6.4' do
- stub_version '5.6.3'
+ test "no microsecond precision for MySQL lt 5.6.4" do
+ stub_version "5.6.3"
assert_no_microsecond_precision
end
- test 'microsecond precision for MariaDB gte 5.3.0' do
- stub_version '5.5.5-10.1.8-MariaDB-log'
+ test "microsecond precision for MariaDB gte 5.3.0" do
+ stub_version "5.5.5-10.1.8-MariaDB-log"
assert_microsecond_precision
end
- test 'no microsecond precision for MariaDB lt 5.3.0' do
- stub_version '5.2.9-MariaDB'
+ test "no microsecond precision for MariaDB lt 5.3.0" do
+ stub_version "5.2.9-MariaDB"
assert_no_microsecond_precision
end
diff --git a/activerecord/test/cases/adapters/mysql2/enum_test.rb b/activerecord/test/cases/adapters/mysql2/enum_test.rb
index 35dbc76d1b..7ad3e3ca2d 100644
--- a/activerecord/test/cases/adapters/mysql2/enum_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/enum_test.rb
@@ -5,22 +5,17 @@ class Mysql2EnumTest < ActiveRecord::Mysql2TestCase
end
def test_enum_limit
- column = EnumTest.columns_hash['enum_column']
+ column = EnumTest.columns_hash["enum_column"]
assert_equal 8, column.limit
end
- def test_should_not_be_blob_or_text_column
- column = EnumTest.columns_hash['enum_column']
- assert_not column.blob_or_text_column?
- end
-
def test_should_not_be_unsigned
- column = EnumTest.columns_hash['enum_column']
+ column = EnumTest.columns_hash["enum_column"]
assert_not column.unsigned?
end
def test_should_not_be_bigint
- column = EnumTest.columns_hash['enum_column']
+ column = EnumTest.columns_hash["enum_column"]
assert_not column.bigint?
end
end
diff --git a/activerecord/test/cases/adapters/mysql2/explain_test.rb b/activerecord/test/cases/adapters/mysql2/explain_test.rb
index b783b5fcd9..7916921e5a 100644
--- a/activerecord/test/cases/adapters/mysql2/explain_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/explain_test.rb
@@ -1,6 +1,6 @@
require "cases/helper"
-require 'models/developer'
-require 'models/computer'
+require "models/developer"
+require "models/computer"
class Mysql2ExplainTest < ActiveRecord::Mysql2TestCase
fixtures :developers
diff --git a/activerecord/test/cases/adapters/mysql2/json_test.rb b/activerecord/test/cases/adapters/mysql2/json_test.rb
index 9c3fef1b59..6954006003 100644
--- a/activerecord/test/cases/adapters/mysql2/json_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/json_test.rb
@@ -1,179 +1,195 @@
-require 'cases/helper'
-require 'support/schema_dumping_helper'
+require "cases/helper"
+require "support/schema_dumping_helper"
if ActiveRecord::Base.connection.supports_json?
-class Mysql2JSONTest < ActiveRecord::Mysql2TestCase
- include SchemaDumpingHelper
- self.use_transactional_tests = false
+ class Mysql2JSONTest < ActiveRecord::Mysql2TestCase
+ include SchemaDumpingHelper
+ self.use_transactional_tests = false
- class JsonDataType < ActiveRecord::Base
- self.table_name = 'json_data_type'
+ class JsonDataType < ActiveRecord::Base
+ self.table_name = "json_data_type"
- store_accessor :settings, :resolution
- end
+ store_accessor :settings, :resolution
+ end
- def setup
- @connection = ActiveRecord::Base.connection
- begin
- @connection.create_table('json_data_type') do |t|
- t.json 'payload'
- t.json 'settings'
+ def setup
+ @connection = ActiveRecord::Base.connection
+ begin
+ @connection.create_table("json_data_type") do |t|
+ t.json "payload"
+ t.json "settings"
+ end
end
end
- end
- def teardown
- @connection.drop_table :json_data_type, if_exists: true
- JsonDataType.reset_column_information
- end
+ def teardown
+ @connection.drop_table :json_data_type, if_exists: true
+ JsonDataType.reset_column_information
+ end
- def test_column
- column = JsonDataType.columns_hash["payload"]
- assert_equal :json, column.type
- assert_equal 'json', column.sql_type
+ def test_column
+ column = JsonDataType.columns_hash["payload"]
+ assert_equal :json, column.type
+ assert_equal "json", column.sql_type
- type = JsonDataType.type_for_attribute("payload")
- assert_not type.binary?
- end
+ type = JsonDataType.type_for_attribute("payload")
+ assert_not type.binary?
+ end
- def test_change_table_supports_json
- @connection.change_table('json_data_type') do |t|
- t.json 'users'
+ def test_change_table_supports_json
+ @connection.change_table("json_data_type") do |t|
+ t.json "users"
+ end
+ JsonDataType.reset_column_information
+ column = JsonDataType.columns_hash["users"]
+ assert_equal :json, column.type
end
- JsonDataType.reset_column_information
- column = JsonDataType.columns_hash['users']
- assert_equal :json, column.type
- end
- def test_schema_dumping
- output = dump_table_schema("json_data_type")
- assert_match(/t\.json\s+"settings"/, output)
- end
+ def test_schema_dumping
+ output = dump_table_schema("json_data_type")
+ assert_match(/t\.json\s+"settings"/, output)
+ end
- def test_cast_value_on_write
- x = JsonDataType.new payload: {"string" => "foo", :symbol => :bar}
- assert_equal({"string" => "foo", :symbol => :bar}, x.payload_before_type_cast)
- assert_equal({"string" => "foo", "symbol" => "bar"}, x.payload)
- x.save
- assert_equal({"string" => "foo", "symbol" => "bar"}, x.reload.payload)
- end
+ def test_cast_value_on_write
+ x = JsonDataType.new payload: { "string" => "foo", :symbol => :bar }
+ assert_equal({ "string" => "foo", :symbol => :bar }, x.payload_before_type_cast)
+ assert_equal({ "string" => "foo", "symbol" => "bar" }, x.payload)
+ x.save
+ assert_equal({ "string" => "foo", "symbol" => "bar" }, x.reload.payload)
+ end
- def test_type_cast_json
- type = JsonDataType.type_for_attribute("payload")
+ def test_type_cast_json
+ type = JsonDataType.type_for_attribute("payload")
- data = "{\"a_key\":\"a_value\"}"
- hash = type.deserialize(data)
- assert_equal({'a_key' => 'a_value'}, hash)
- assert_equal({'a_key' => 'a_value'}, type.deserialize(data))
+ data = "{\"a_key\":\"a_value\"}"
+ hash = type.deserialize(data)
+ assert_equal({ "a_key" => "a_value" }, hash)
+ assert_equal({ "a_key" => "a_value" }, type.deserialize(data))
- assert_equal({}, type.deserialize("{}"))
- assert_equal({'key'=>nil}, type.deserialize('{"key": null}'))
- assert_equal({'c'=>'}','"a"'=>'b "a b'}, type.deserialize(%q({"c":"}", "\"a\"":"b \"a b"})))
- end
+ assert_equal({}, type.deserialize("{}"))
+ assert_equal({ "key" => nil }, type.deserialize('{"key": null}'))
+ assert_equal({ "c" => "}", '"a"' => 'b "a b' }, type.deserialize(%q({"c":"}", "\"a\"":"b \"a b"})))
+ end
- def test_rewrite
- @connection.execute "insert into json_data_type (payload) VALUES ('{\"k\":\"v\"}')"
- x = JsonDataType.first
- x.payload = { '"a\'' => 'b' }
- assert x.save!
- end
+ def test_rewrite
+ @connection.execute "insert into json_data_type (payload) VALUES ('{\"k\":\"v\"}')"
+ x = JsonDataType.first
+ x.payload = { '"a\'' => "b" }
+ assert x.save!
+ end
- def test_select
- @connection.execute "insert into json_data_type (payload) VALUES ('{\"k\":\"v\"}')"
- x = JsonDataType.first
- assert_equal({'k' => 'v'}, x.payload)
- end
+ def test_select
+ @connection.execute "insert into json_data_type (payload) VALUES ('{\"k\":\"v\"}')"
+ x = JsonDataType.first
+ assert_equal({ "k" => "v" }, x.payload)
+ end
- def test_select_multikey
- @connection.execute %q|insert into json_data_type (payload) VALUES ('{"k1":"v1", "k2":"v2", "k3":[1,2,3]}')|
- x = JsonDataType.first
- assert_equal({'k1' => 'v1', 'k2' => 'v2', 'k3' => [1,2,3]}, x.payload)
- end
+ def test_select_multikey
+ @connection.execute %q|insert into json_data_type (payload) VALUES ('{"k1":"v1", "k2":"v2", "k3":[1,2,3]}')|
+ x = JsonDataType.first
+ assert_equal({ "k1" => "v1", "k2" => "v2", "k3" => [1, 2, 3] }, x.payload)
+ end
- def test_null_json
- @connection.execute %q|insert into json_data_type (payload) VALUES(null)|
- x = JsonDataType.first
- assert_equal(nil, x.payload)
- end
+ def test_null_json
+ @connection.execute "insert into json_data_type (payload) VALUES(null)"
+ x = JsonDataType.first
+ assert_nil(x.payload)
+ end
- def test_select_array_json_value
- @connection.execute %q|insert into json_data_type (payload) VALUES ('["v0",{"k1":"v1"}]')|
- x = JsonDataType.first
- assert_equal(['v0', {'k1' => 'v1'}], x.payload)
- end
+ def test_select_array_json_value
+ @connection.execute %q|insert into json_data_type (payload) VALUES ('["v0",{"k1":"v1"}]')|
+ x = JsonDataType.first
+ assert_equal(["v0", { "k1" => "v1" }], x.payload)
+ end
- def test_rewrite_array_json_value
- @connection.execute %q|insert into json_data_type (payload) VALUES ('["v0",{"k1":"v1"}]')|
- x = JsonDataType.first
- x.payload = ['v1', {'k2' => 'v2'}, 'v3']
- assert x.save!
- end
+ def test_select_nil_json_after_create
+ json = JsonDataType.create(payload: nil)
+ x = JsonDataType.where(payload: nil).first
+ assert_equal(json, x)
+ end
- def test_with_store_accessors
- x = JsonDataType.new(resolution: "320×480")
- assert_equal "320×480", x.resolution
+ def test_select_nil_json_after_update
+ json = JsonDataType.create(payload: "foo")
+ x = JsonDataType.where(payload: nil).first
+ assert_nil(x)
- x.save!
- x = JsonDataType.first
- assert_equal "320×480", x.resolution
+ json.update_attributes payload: nil
+ x = JsonDataType.where(payload: nil).first
+ assert_equal(json.reload, x)
+ end
- x.resolution = "640×1136"
- x.save!
+ def test_rewrite_array_json_value
+ @connection.execute %q|insert into json_data_type (payload) VALUES ('["v0",{"k1":"v1"}]')|
+ x = JsonDataType.first
+ x.payload = ["v1", { "k2" => "v2" }, "v3"]
+ assert x.save!
+ end
- x = JsonDataType.first
- assert_equal "640×1136", x.resolution
- end
+ def test_with_store_accessors
+ x = JsonDataType.new(resolution: "320×480")
+ assert_equal "320×480", x.resolution
- def test_duplication_with_store_accessors
- x = JsonDataType.new(resolution: "320×480")
- assert_equal "320×480", x.resolution
+ x.save!
+ x = JsonDataType.first
+ assert_equal "320×480", x.resolution
- y = x.dup
- assert_equal "320×480", y.resolution
- end
+ x.resolution = "640×1136"
+ x.save!
- def test_yaml_round_trip_with_store_accessors
- x = JsonDataType.new(resolution: "320×480")
- assert_equal "320×480", x.resolution
+ x = JsonDataType.first
+ assert_equal "640×1136", x.resolution
+ end
- y = YAML.load(YAML.dump(x))
- assert_equal "320×480", y.resolution
- end
+ def test_duplication_with_store_accessors
+ x = JsonDataType.new(resolution: "320×480")
+ assert_equal "320×480", x.resolution
- def test_changes_in_place
- json = JsonDataType.new
- assert_not json.changed?
+ y = x.dup
+ assert_equal "320×480", y.resolution
+ end
- json.payload = { 'one' => 'two' }
- assert json.changed?
- assert json.payload_changed?
+ def test_yaml_round_trip_with_store_accessors
+ x = JsonDataType.new(resolution: "320×480")
+ assert_equal "320×480", x.resolution
- json.save!
- assert_not json.changed?
+ y = YAML.load(YAML.dump(x))
+ assert_equal "320×480", y.resolution
+ end
- json.payload['three'] = 'four'
- assert json.payload_changed?
+ def test_changes_in_place
+ json = JsonDataType.new
+ assert_not json.changed?
- json.save!
- json.reload
+ json.payload = { "one" => "two" }
+ assert json.changed?
+ assert json.payload_changed?
- assert_equal({ 'one' => 'two', 'three' => 'four' }, json.payload)
- assert_not json.changed?
- end
+ json.save!
+ assert_not json.changed?
- def test_assigning_string_literal
- json = JsonDataType.create(payload: "foo")
- assert_equal "foo", json.payload
- end
+ json.payload["three"] = "four"
+ assert json.payload_changed?
- def test_assigning_number
- json = JsonDataType.create(payload: 1.234)
- assert_equal 1.234, json.payload
- end
+ json.save!
+ json.reload
- def test_assigning_boolean
- json = JsonDataType.create(payload: true)
- assert_equal true, json.payload
+ assert_equal({ "one" => "two", "three" => "four" }, json.payload)
+ assert_not json.changed?
+ end
+
+ def test_assigning_string_literal
+ json = JsonDataType.create(payload: "foo")
+ assert_equal "foo", json.payload
+ end
+
+ def test_assigning_number
+ json = JsonDataType.create(payload: 1.234)
+ assert_equal 1.234, json.payload
+ end
+
+ def test_assigning_boolean
+ json = JsonDataType.create(payload: true)
+ assert_equal true, json.payload
+ end
end
end
-end
diff --git a/activerecord/test/cases/adapters/mysql2/legacy_migration_test.rb b/activerecord/test/cases/adapters/mysql2/legacy_migration_test.rb
new file mode 100644
index 0000000000..5d3125c2be
--- /dev/null
+++ b/activerecord/test/cases/adapters/mysql2/legacy_migration_test.rb
@@ -0,0 +1,60 @@
+require "cases/helper"
+
+class MysqlLegacyMigrationTest < ActiveRecord::Mysql2TestCase
+ self.use_transactional_tests = false
+
+ class GenerateTableWithoutBigint < ActiveRecord::Migration[5.0]
+ def change
+ create_table :legacy_integer_pk do |table|
+ table.string :foo
+ end
+
+ create_table :override_pk, id: :bigint do |table|
+ table.string :bar
+ end
+ end
+ end
+
+ def setup
+ super
+ @connection = ActiveRecord::Base.connection
+
+ @migration_verbose_old = ActiveRecord::Migration.verbose
+ ActiveRecord::Migration.verbose = false
+
+ migrations = [GenerateTableWithoutBigint.new(nil, 1)]
+
+ ActiveRecord::Migrator.new(:up, migrations).migrate
+ end
+
+ def teardown
+ ActiveRecord::Migration.verbose = @migration_verbose_old
+ @connection.drop_table("legacy_integer_pk")
+ @connection.drop_table("override_pk")
+ ActiveRecord::SchemaMigration.delete_all rescue nil
+ super
+ end
+
+ def test_create_table_uses_integer_as_pkey_by_default
+ col = column(:legacy_integer_pk, :id)
+ assert_equal "int(11)", sql_type_for(col)
+ assert col.auto_increment?
+ end
+
+ def test_create_tables_respects_pk_column_type_override
+ col = column(:override_pk, :id)
+ assert_equal "bigint(20)", sql_type_for(col)
+ end
+
+ private
+
+ def column(table_name, column_name)
+ ActiveRecord::Base.connection
+ .columns(table_name.to_s)
+ .detect { |c| c.name == column_name.to_s }
+ end
+
+ def sql_type_for(col)
+ col && col.sql_type
+ end
+end
diff --git a/activerecord/test/cases/adapters/mysql2/mysql2_adapter_test.rb b/activerecord/test/cases/adapters/mysql2/mysql2_adapter_test.rb
index 61dd0828d0..aab3dcb724 100644
--- a/activerecord/test/cases/adapters/mysql2/mysql2_adapter_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/mysql2_adapter_test.rb
@@ -11,15 +11,15 @@ class Mysql2AdapterTest < ActiveRecord::Mysql2TestCase
def test_exec_query_nothing_raises_with_no_result_queries
assert_nothing_raised do
with_example_table do
- @conn.exec_query('INSERT INTO ex (number) VALUES (1)')
- @conn.exec_query('DELETE FROM ex WHERE number = 1')
+ @conn.exec_query("INSERT INTO ex (number) VALUES (1)")
+ @conn.exec_query("DELETE FROM ex WHERE number = 1")
end
end
end
def test_valid_column
with_example_table do
- column = @conn.columns('ex').find { |col| col.name == 'id' }
+ column = @conn.columns("ex").find { |col| col.name == "id" }
assert @conn.valid_type?(column.type)
end
end
@@ -45,8 +45,8 @@ class Mysql2AdapterTest < ActiveRecord::Mysql2TestCase
def test_columns_for_distinct_with_case
assert_equal(
- 'posts.id, CASE WHEN author.is_active THEN UPPER(author.name) ELSE UPPER(author.email) END AS alias_0',
- @conn.columns_for_distinct('posts.id',
+ "posts.id, CASE WHEN author.is_active THEN UPPER(author.name) ELSE UPPER(author.email) END AS alias_0",
+ @conn.columns_for_distinct("posts.id",
["CASE WHEN author.is_active THEN UPPER(author.name) ELSE UPPER(author.email) END"])
)
end
@@ -65,9 +65,22 @@ class Mysql2AdapterTest < ActiveRecord::Mysql2TestCase
@conn.columns_for_distinct("posts.id", [order])
end
- private
+ def test_errors_for_bigint_fks_on_integer_pk_table
+ # table old_cars has primary key of integer
+
+ error = assert_raises(ActiveRecord::MismatchedForeignKey) do
+ @conn.add_reference :engines, :old_car
+ @conn.add_foreign_key :engines, :old_cars
+ end
- def with_example_table(definition = 'id int auto_increment primary key, number int, data varchar(255)', &block)
- super(@conn, 'ex', definition, &block)
+ assert_match "Column `old_car_id` on table `engines` has a type of `bigint(20)`", error.message
+ assert_not_nil error.cause
+ @conn.exec_query("ALTER TABLE engines DROP COLUMN old_car_id")
end
+
+ private
+
+ def with_example_table(definition = "id int auto_increment primary key, number int, data varchar(255)", &block)
+ super(@conn, "ex", definition, &block)
+ end
end
diff --git a/activerecord/test/cases/adapters/mysql2/reserved_word_test.rb b/activerecord/test/cases/adapters/mysql2/reserved_word_test.rb
index ffb4e2c5cf..2c778b1150 100644
--- a/activerecord/test/cases/adapters/mysql2/reserved_word_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/reserved_word_test.rb
@@ -4,24 +4,24 @@ require "cases/helper"
# reserved word names (ie: group, order, values, etc...)
class Mysql2ReservedWordTest < ActiveRecord::Mysql2TestCase
class Group < ActiveRecord::Base
- Group.table_name = 'group'
+ Group.table_name = "group"
belongs_to :select
has_one :values
end
class Select < ActiveRecord::Base
- Select.table_name = 'select'
+ Select.table_name = "select"
has_many :groups
end
class Values < ActiveRecord::Base
- Values.table_name = 'values'
+ Values.table_name = "values"
end
class Distinct < ActiveRecord::Base
- Distinct.table_name = 'distinct'
+ Distinct.table_name = "distinct"
has_and_belongs_to_many :selects
- has_many :values, :through => :groups
+ has_many :values, through: :groups
end
def setup
@@ -30,15 +30,15 @@ class Mysql2ReservedWordTest < ActiveRecord::Mysql2TestCase
# we call execute directly here (and do similar below) because ActiveRecord::Base#create_table()
# will fail with these table names if these test cases fail
- create_tables_directly 'group'=>'id int auto_increment primary key, `order` varchar(255), select_id int',
- 'select'=>'id int auto_increment primary key',
- 'values'=>'id int auto_increment primary key, group_id int',
- 'distinct'=>'id int auto_increment primary key',
- 'distinct_select'=>'distinct_id int, select_id int'
+ create_tables_directly "group" => "id int auto_increment primary key, `order` varchar(255), select_id int",
+ "select" => "id int auto_increment primary key",
+ "values" => "id int auto_increment primary key, group_id int",
+ "distinct" => "id int auto_increment primary key",
+ "distinct_select" => "distinct_id int, select_id int"
end
teardown do
- drop_tables_directly ['group', 'select', 'values', 'distinct', 'distinct_select', 'order']
+ drop_tables_directly ["group", "select", "values", "distinct", "distinct_select", "order"]
end
# create tables with reserved-word names and columns
@@ -57,9 +57,9 @@ class Mysql2ReservedWordTest < ActiveRecord::Mysql2TestCase
# alter column with a reserved-word name in a table with a reserved-word name
def test_change_columns
- assert_nothing_raised { @connection.change_column_default(:group, :order, 'whatever') }
+ assert_nothing_raised { @connection.change_column_default(:group, :order, "whatever") }
#the quoting here will reveal any double quoting issues in change_column's interaction with the column method in the adapter
- assert_nothing_raised { @connection.change_column('group', 'order', :Int, :default => 0) }
+ assert_nothing_raised { @connection.change_column("group", "order", :Int, default: 0) }
assert_nothing_raised { @connection.rename_column(:group, :order, :values) }
end
@@ -78,11 +78,11 @@ class Mysql2ReservedWordTest < ActiveRecord::Mysql2TestCase
create_test_fixtures :select, :distinct, :group, :values, :distinct_select
x = nil
assert_nothing_raised { x = Group.new }
- x.order = 'x'
+ x.order = "x"
assert_nothing_raised { x.save }
- x.order = 'y'
+ x.order = "y"
assert_nothing_raised { x.save }
- assert_nothing_raised { Group.find_by_order('y') }
+ assert_nothing_raised { Group.find_by_order("y") }
assert_nothing_raised { Group.find(1) }
end
@@ -124,29 +124,28 @@ class Mysql2ReservedWordTest < ActiveRecord::Mysql2TestCase
end
def test_associations_work_with_reserved_words
- assert_nothing_raised { Select.all.merge!(:includes => [:groups]).to_a }
+ assert_nothing_raised { Select.all.merge!(includes: [:groups]).to_a }
end
#the following functions were added to DRY test cases
private
- # custom fixture loader, uses FixtureSet#create_fixtures and appends base_path to the current file's path
- def create_test_fixtures(*fixture_names)
- ActiveRecord::FixtureSet.create_fixtures(FIXTURES_ROOT + "/reserved_words", fixture_names)
- end
-
- # custom drop table, uses execute on connection to drop a table if it exists. note: escapes table_name
- def drop_tables_directly(table_names, connection = @connection)
- table_names.each do |name|
- connection.drop_table name, if_exists: true
+ # custom fixture loader, uses FixtureSet#create_fixtures and appends base_path to the current file's path
+ def create_test_fixtures(*fixture_names)
+ ActiveRecord::FixtureSet.create_fixtures(FIXTURES_ROOT + "/reserved_words", fixture_names)
end
- end
- # custom create table, uses execute on connection to create a table, note: escapes table_name, does NOT escape columns
- def create_tables_directly (tables, connection = @connection)
- tables.each do |table_name, column_properties|
- connection.execute("CREATE TABLE `#{table_name}` ( #{column_properties} )")
+ # custom drop table, uses execute on connection to drop a table if it exists. note: escapes table_name
+ def drop_tables_directly(table_names, connection = @connection)
+ table_names.each do |name|
+ connection.drop_table name, if_exists: true
+ end
end
- end
+ # custom create table, uses execute on connection to create a table, note: escapes table_name, does NOT escape columns
+ def create_tables_directly(tables, connection = @connection)
+ tables.each do |table_name, column_properties|
+ connection.execute("CREATE TABLE `#{table_name}` ( #{column_properties} )")
+ end
+ end
end
diff --git a/activerecord/test/cases/adapters/mysql2/schema_migrations_test.rb b/activerecord/test/cases/adapters/mysql2/schema_migrations_test.rb
index 7c89fda582..fa54aac6b3 100644
--- a/activerecord/test/cases/adapters/mysql2/schema_migrations_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/schema_migrations_test.rb
@@ -18,7 +18,7 @@ class SchemaMigrationsTest < ActiveRecord::Mysql2TestCase
connection.initialize_schema_migrations_table
- assert connection.column_exists?(table_name, :version, :string, collation: 'utf8_general_ci')
+ assert connection.column_exists?(table_name, :version, :string, collation: "utf8_general_ci")
end
end
@@ -29,31 +29,31 @@ class SchemaMigrationsTest < ActiveRecord::Mysql2TestCase
connection.initialize_internal_metadata_table
- assert connection.column_exists?(table_name, :key, :string, collation: 'utf8_general_ci')
+ assert connection.column_exists?(table_name, :key, :string, collation: "utf8_general_ci")
end
end
private
- def with_encoding_utf8mb4
- database_name = connection.current_database
- database_info = connection.select_one("SELECT * FROM information_schema.schemata WHERE schema_name = '#{database_name}'")
+ def with_encoding_utf8mb4
+ database_name = connection.current_database
+ database_info = connection.select_one("SELECT * FROM information_schema.schemata WHERE schema_name = '#{database_name}'")
- original_charset = database_info["DEFAULT_CHARACTER_SET_NAME"]
- original_collation = database_info["DEFAULT_COLLATION_NAME"]
+ original_charset = database_info["DEFAULT_CHARACTER_SET_NAME"]
+ original_collation = database_info["DEFAULT_COLLATION_NAME"]
- execute("ALTER DATABASE #{database_name} DEFAULT CHARACTER SET utf8mb4")
+ execute("ALTER DATABASE #{database_name} DEFAULT CHARACTER SET utf8mb4")
- yield
- ensure
- execute("ALTER DATABASE #{database_name} DEFAULT CHARACTER SET #{original_charset} COLLATE #{original_collation}")
- end
+ yield
+ ensure
+ execute("ALTER DATABASE #{database_name} DEFAULT CHARACTER SET #{original_charset} COLLATE #{original_collation}")
+ end
- def connection
- @connection ||= ActiveRecord::Base.connection
- end
+ def connection
+ @connection ||= ActiveRecord::Base.connection
+ end
- def execute(sql)
- connection.execute(sql)
- end
+ def execute(sql)
+ connection.execute(sql)
+ end
end
diff --git a/activerecord/test/cases/adapters/mysql2/schema_test.rb b/activerecord/test/cases/adapters/mysql2/schema_test.rb
index 57ea8258d1..1fad5585de 100644
--- a/activerecord/test/cases/adapters/mysql2/schema_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/schema_test.rb
@@ -1,6 +1,6 @@
require "cases/helper"
-require 'models/post'
-require 'models/comment'
+require "models/post"
+require "models/comment"
module ActiveRecord
module ConnectionAdapters
@@ -16,7 +16,7 @@ module ActiveRecord
@omgpost = Class.new(ActiveRecord::Base) do
self.inheritance_column = :disabled
self.table_name = "#{db}.#{table}"
- def self.name; 'Post'; end
+ def self.name; "Post"; end
end
end
@@ -31,13 +31,13 @@ module ActiveRecord
t.float :float_25, limit: 25
end
- column_no_limit = @connection.columns(:mysql_doubles).find { |c| c.name == 'float_no_limit' }
- column_short = @connection.columns(:mysql_doubles).find { |c| c.name == 'float_short' }
- column_long = @connection.columns(:mysql_doubles).find { |c| c.name == 'float_long' }
+ column_no_limit = @connection.columns(:mysql_doubles).find { |c| c.name == "float_no_limit" }
+ column_short = @connection.columns(:mysql_doubles).find { |c| c.name == "float_short" }
+ column_long = @connection.columns(:mysql_doubles).find { |c| c.name == "float_long" }
- column_23 = @connection.columns(:mysql_doubles).find { |c| c.name == 'float_23' }
- column_24 = @connection.columns(:mysql_doubles).find { |c| c.name == 'float_24' }
- column_25 = @connection.columns(:mysql_doubles).find { |c| c.name == 'float_25' }
+ column_23 = @connection.columns(:mysql_doubles).find { |c| c.name == "float_23" }
+ column_24 = @connection.columns(:mysql_doubles).find { |c| c.name == "float_24" }
+ column_25 = @connection.columns(:mysql_doubles).find { |c| c.name == "float_25" }
# Mysql floats are precision 0..24, Mysql doubles are precision 25..53
assert_equal 24, column_no_limit.limit
@@ -56,7 +56,7 @@ module ActiveRecord
end
def test_primary_key
- assert_equal 'id', @omgpost.primary_key
+ assert_equal "id", @omgpost.primary_key
end
def test_data_source_exists?
@@ -69,18 +69,18 @@ module ActiveRecord
end
def test_dump_indexes
- index_a_name = 'index_key_tests_on_snack'
- index_b_name = 'index_key_tests_on_pizza'
- index_c_name = 'index_key_tests_on_awesome'
+ index_a_name = "index_key_tests_on_snack"
+ index_b_name = "index_key_tests_on_pizza"
+ index_c_name = "index_key_tests_on_awesome"
- table = 'key_tests'
+ table = "key_tests"
indexes = @connection.indexes(table).sort_by(&:name)
- assert_equal 3,indexes.size
+ assert_equal 3, indexes.size
- index_a = indexes.select{|i| i.name == index_a_name}[0]
- index_b = indexes.select{|i| i.name == index_b_name}[0]
- index_c = indexes.select{|i| i.name == index_c_name}[0]
+ index_a = indexes.select { |i| i.name == index_a_name }[0]
+ index_b = indexes.select { |i| i.name == index_b_name }[0]
+ index_c = indexes.select { |i| i.name == index_c_name }[0]
assert_equal :btree, index_a.using
assert_nil index_a.type
assert_equal :btree, index_b.using
@@ -121,6 +121,6 @@ class Mysql2AnsiQuotesTest < ActiveRecord::Mysql2TestCase
def test_foreign_keys_method_with_ansi_quotes
fks = @connection.foreign_keys("lessons_students")
assert_equal([["lessons_students", "students", :cascade]],
- fks.map {|fk| [fk.from_table, fk.to_table, fk.on_delete] })
+ fks.map { |fk| [fk.from_table, fk.to_table, fk.on_delete] })
end
end
diff --git a/activerecord/test/cases/adapters/mysql2/sp_test.rb b/activerecord/test/cases/adapters/mysql2/sp_test.rb
index 4197ba45f1..4182532535 100644
--- a/activerecord/test/cases/adapters/mysql2/sp_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/sp_test.rb
@@ -1,13 +1,13 @@
require "cases/helper"
-require 'models/topic'
-require 'models/reply'
+require "models/topic"
+require "models/reply"
class Mysql2StoredProcedureTest < ActiveRecord::Mysql2TestCase
fixtures :topics
def setup
@connection = ActiveRecord::Base.connection
- unless ActiveRecord::Base.connection.version >= '5.6.0'
+ unless ActiveRecord::Base.connection.version >= "5.6.0"
skip("no stored procedure support")
end
end
@@ -17,19 +17,19 @@ class Mysql2StoredProcedureTest < ActiveRecord::Mysql2TestCase
# In MySQL 5.6, CLIENT_MULTI_RESULTS is enabled by default.
# http://dev.mysql.com/doc/refman/5.6/en/call.html
def test_multi_results
- rows = @connection.select_rows('CALL ten();')
+ rows = @connection.select_rows("CALL ten();")
assert_equal 10, rows[0][0].to_i, "ten() did not return 10 as expected: #{rows.inspect}"
assert @connection.active?, "Bad connection use by 'Mysql2Adapter.select_rows'"
end
def test_multi_results_from_select_one
- row = @connection.select_one('CALL topics(1);')
- assert_equal 'David', row['author_name']
+ row = @connection.select_one("CALL topics(1);")
+ assert_equal "David", row["author_name"]
assert @connection.active?, "Bad connection use by 'Mysql2Adapter.select_one'"
end
def test_multi_results_from_find_by_sql
- topics = Topic.find_by_sql 'CALL topics(3);'
+ topics = Topic.find_by_sql "CALL topics(3);"
assert_equal 3, topics.size
assert @connection.active?, "Bad connection use by 'Mysql2Adapter.select'"
end
diff --git a/activerecord/test/cases/adapters/mysql2/sql_types_test.rb b/activerecord/test/cases/adapters/mysql2/sql_types_test.rb
index 4926bc2267..bee42d48f1 100644
--- a/activerecord/test/cases/adapters/mysql2/sql_types_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/sql_types_test.rb
@@ -2,10 +2,10 @@ require "cases/helper"
class Mysql2SqlTypesTest < ActiveRecord::Mysql2TestCase
def test_binary_types
- assert_equal 'varbinary(64)', type_to_sql(:binary, 64)
- assert_equal 'varbinary(4095)', type_to_sql(:binary, 4095)
- assert_equal 'blob', type_to_sql(:binary, 4096)
- assert_equal 'blob', type_to_sql(:binary)
+ assert_equal "varbinary(64)", type_to_sql(:binary, 64)
+ assert_equal "varbinary(4095)", type_to_sql(:binary, 4095)
+ assert_equal "blob", type_to_sql(:binary, 4096)
+ assert_equal "blob", type_to_sql(:binary)
end
def type_to_sql(*args)
diff --git a/activerecord/test/cases/adapters/mysql2/table_options_test.rb b/activerecord/test/cases/adapters/mysql2/table_options_test.rb
index af121ee7d9..61a8ce9bc0 100644
--- a/activerecord/test/cases/adapters/mysql2/table_options_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/table_options_test.rb
@@ -1,5 +1,5 @@
require "cases/helper"
-require 'support/schema_dumping_helper'
+require "support/schema_dumping_helper"
class Mysql2TableOptionsTest < ActiveRecord::Mysql2TestCase
include SchemaDumpingHelper
diff --git a/activerecord/test/cases/adapters/mysql2/transaction_test.rb b/activerecord/test/cases/adapters/mysql2/transaction_test.rb
index 0e37c70e5c..16101e38cb 100644
--- a/activerecord/test/cases/adapters/mysql2/transaction_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/transaction_test.rb
@@ -1,22 +1,24 @@
require "cases/helper"
-require 'support/connection_helper'
+require "support/connection_helper"
module ActiveRecord
class Mysql2TransactionTest < ActiveRecord::Mysql2TestCase
self.use_transactional_tests = false
class Sample < ActiveRecord::Base
- self.table_name = 'samples'
+ self.table_name = "samples"
end
setup do
+ @abort, Thread.abort_on_exception = Thread.abort_on_exception, false
+
@connection = ActiveRecord::Base.connection
@connection.clear_cache!
@connection.transaction do
- @connection.drop_table 'samples', if_exists: true
- @connection.create_table('samples') do |t|
- t.integer 'value'
+ @connection.drop_table "samples", if_exists: true
+ @connection.create_table("samples") do |t|
+ t.integer "value"
end
end
@@ -24,38 +26,35 @@ module ActiveRecord
end
teardown do
- @connection.drop_table 'samples', if_exists: true
+ @connection.drop_table "samples", if_exists: true
+
+ Thread.abort_on_exception = @abort
end
- test "raises error when a serialization failure occurs" do
- assert_raises(ActiveRecord::TransactionSerializationError) do
- thread = Thread.new do
- Sample.transaction isolation: :serializable do
- Sample.delete_all
+ test "raises Deadlocked when a deadlock is encountered" do
+ assert_raises(ActiveRecord::Deadlocked) do
+ barrier = Concurrent::CyclicBarrier.new(2)
- 10.times do |i|
- sleep 0.1
+ s1 = Sample.create value: 1
+ s2 = Sample.create value: 2
- Sample.create value: i
- end
+ thread = Thread.new do
+ Sample.transaction do
+ s1.lock!
+ barrier.wait
+ s2.update_attributes value: 1
end
end
- sleep 0.1
-
- Sample.transaction isolation: :serializable do
- Sample.delete_all
-
- 10.times do |i|
- sleep 0.1
-
- Sample.create value: i
+ begin
+ Sample.transaction do
+ s2.lock!
+ barrier.wait
+ s1.update_attributes value: 2
end
-
- sleep 1
+ ensure
+ thread.join
end
-
- thread.join
end
end
end
diff --git a/activerecord/test/cases/adapters/mysql2/unsigned_type_test.rb b/activerecord/test/cases/adapters/mysql2/unsigned_type_test.rb
index 3df11ce11b..a0823be143 100644
--- a/activerecord/test/cases/adapters/mysql2/unsigned_type_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/unsigned_type_test.rb
@@ -15,6 +15,7 @@ class Mysql2UnsignedTypeTest < ActiveRecord::Mysql2TestCase
t.bigint :unsigned_bigint, unsigned: true
t.float :unsigned_float, unsigned: true
t.decimal :unsigned_decimal, unsigned: true, precision: 10, scale: 2
+ t.column :unsigned_zerofill, "int unsigned zerofill"
end
end
@@ -34,10 +35,10 @@ class Mysql2UnsignedTypeTest < ActiveRecord::Mysql2TestCase
assert_raise(ActiveModel::RangeError) do
UnsignedType.create(unsigned_bigint: -10)
end
- assert_raise(ActiveRecord::StatementInvalid) do
+ assert_raise(ActiveRecord::RangeError) do
UnsignedType.create(unsigned_float: -10.0)
end
- assert_raise(ActiveRecord::StatementInvalid) do
+ assert_raise(ActiveRecord::RangeError) do
UnsignedType.create(unsigned_decimal: -10.0)
end
end
@@ -50,7 +51,7 @@ class Mysql2UnsignedTypeTest < ActiveRecord::Mysql2TestCase
t.unsigned_decimal :unsigned_decimal_t, precision: 10, scale: 2
end
- @connection.columns("unsigned_types").select { |c| /^unsigned_/ === c.name }.each do |column|
+ @connection.columns("unsigned_types").select { |c| /^unsigned_/.match?(c.name) }.each do |column|
assert column.unsigned?
end
end
diff --git a/activerecord/test/cases/adapters/postgresql/active_schema_test.rb b/activerecord/test/cases/adapters/postgresql/active_schema_test.rb
index 439e2ce6f7..b787de8453 100644
--- a/activerecord/test/cases/adapters/postgresql/active_schema_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/active_schema_test.rb
@@ -1,4 +1,4 @@
-require 'cases/helper'
+require "cases/helper"
class PostgresqlActiveSchemaTest < ActiveRecord::PostgreSQLTestCase
def setup
@@ -15,12 +15,12 @@ class PostgresqlActiveSchemaTest < ActiveRecord::PostgreSQLTestCase
def test_create_database_with_encoding
assert_equal %(CREATE DATABASE "matt" ENCODING = 'utf8'), create_database(:matt)
- assert_equal %(CREATE DATABASE "aimonetti" ENCODING = 'latin1'), create_database(:aimonetti, :encoding => :latin1)
- assert_equal %(CREATE DATABASE "aimonetti" ENCODING = 'latin1'), create_database(:aimonetti, 'encoding' => :latin1)
+ assert_equal %(CREATE DATABASE "aimonetti" ENCODING = 'latin1'), create_database(:aimonetti, encoding: :latin1)
+ assert_equal %(CREATE DATABASE "aimonetti" ENCODING = 'latin1'), create_database(:aimonetti, "encoding" => :latin1)
end
def test_create_database_with_collation_and_ctype
- assert_equal %(CREATE DATABASE "aimonetti" ENCODING = 'UTF8' LC_COLLATE = 'ja_JP.UTF8' LC_CTYPE = 'ja_JP.UTF8'), create_database(:aimonetti, :encoding => :"UTF8", :collation => :"ja_JP.UTF8", :ctype => :"ja_JP.UTF8")
+ assert_equal %(CREATE DATABASE "aimonetti" ENCODING = 'UTF8' LC_COLLATE = 'ja_JP.UTF8' LC_CTYPE = 'ja_JP.UTF8'), create_database(:aimonetti, encoding: :"UTF8", collation: :"ja_JP.UTF8", ctype: :"ja_JP.UTF8")
end
def test_add_index
@@ -31,14 +31,18 @@ class PostgresqlActiveSchemaTest < ActiveRecord::PostgreSQLTestCase
assert_equal expected, add_index(:people, :last_name, unique: true, where: "state = 'active'")
expected = %(CREATE UNIQUE INDEX "index_people_on_lower_last_name" ON "people" (lower(last_name)))
- assert_equal expected, add_index(:people, 'lower(last_name)', unique: true)
+ assert_equal expected, add_index(:people, "lower(last_name)", unique: true)
expected = %(CREATE UNIQUE INDEX "index_people_on_last_name_varchar_pattern_ops" ON "people" (last_name varchar_pattern_ops))
- assert_equal expected, add_index(:people, 'last_name varchar_pattern_ops', unique: true)
+ assert_equal expected, add_index(:people, "last_name varchar_pattern_ops", unique: true)
expected = %(CREATE INDEX CONCURRENTLY "index_people_on_last_name" ON "people" ("last_name"))
assert_equal expected, add_index(:people, :last_name, algorithm: :concurrently)
+ expected = %(CREATE INDEX "index_people_on_last_name_and_first_name" ON "people" ("last_name" DESC, "first_name" ASC))
+ assert_equal expected, add_index(:people, [:last_name, :first_name], order: { last_name: :desc, first_name: :asc })
+ assert_equal expected, add_index(:people, ["last_name", :first_name], order: { last_name: :desc, "first_name" => :asc })
+
%w(gin gist hash btree).each do |type|
expected = %(CREATE INDEX "index_people_on_last_name" ON "people" USING #{type} ("last_name"))
assert_equal expected, add_index(:people, :last_name, using: type)
@@ -50,7 +54,7 @@ class PostgresqlActiveSchemaTest < ActiveRecord::PostgreSQLTestCase
assert_equal expected, add_index(:people, :last_name, using: type, unique: true, where: "state = 'active'")
expected = %(CREATE UNIQUE INDEX "index_people_on_lower_last_name" ON "people" USING #{type} (lower(last_name)))
- assert_equal expected, add_index(:people, 'lower(last_name)', using: type, unique: true)
+ assert_equal expected, add_index(:people, "lower(last_name)", using: type, unique: true)
end
assert_raise ArgumentError do
@@ -63,7 +67,7 @@ class PostgresqlActiveSchemaTest < ActiveRecord::PostgreSQLTestCase
def test_remove_index
# remove_index calls index_name_for_remove which can't work since execute is stubbed
ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.send(:define_method, :index_name_for_remove) do |*|
- 'index_people_on_last_name'
+ "index_people_on_last_name"
end
expected = %(DROP INDEX CONCURRENTLY "index_people_on_last_name")
@@ -81,6 +85,12 @@ class PostgresqlActiveSchemaTest < ActiveRecord::PostgreSQLTestCase
assert_equal expected, remove_index(:people, name: "index_people_on_last_name", algorithm: :concurrently)
end
+ def test_remove_index_with_wrong_option
+ assert_raises ArgumentError do
+ remove_index(:people, coulmn: :last_name)
+ end
+ end
+
private
def method_missing(method_symbol, *arguments)
ActiveRecord::Base.connection.send(method_symbol, *arguments)
diff --git a/activerecord/test/cases/adapters/postgresql/array_test.rb b/activerecord/test/cases/adapters/postgresql/array_test.rb
index 70792e937c..c78c6178ff 100644
--- a/activerecord/test/cases/adapters/postgresql/array_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/array_test.rb
@@ -1,63 +1,65 @@
require "cases/helper"
-require 'support/schema_dumping_helper'
+require "support/schema_dumping_helper"
class PostgresqlArrayTest < ActiveRecord::PostgreSQLTestCase
include SchemaDumpingHelper
include InTimeZone
class PgArray < ActiveRecord::Base
- self.table_name = 'pg_arrays'
+ self.table_name = "pg_arrays"
end
def setup
@connection = ActiveRecord::Base.connection
- enable_extension!('hstore', @connection)
+ enable_extension!("hstore", @connection)
@connection.transaction do
- @connection.create_table('pg_arrays') do |t|
- t.string 'tags', array: true
- t.integer 'ratings', array: true
+ @connection.create_table("pg_arrays") do |t|
+ t.string "tags", array: true, limit: 255
+ t.integer "ratings", array: true
t.datetime :datetimes, array: true
t.hstore :hstores, array: true
+ t.decimal :decimals, array: true, default: [], precision: 10, scale: 2
+ t.timestamp :timestamps, array: true, default: [], precision: 6
end
end
PgArray.reset_column_information
- @column = PgArray.columns_hash['tags']
+ @column = PgArray.columns_hash["tags"]
@type = PgArray.type_for_attribute("tags")
end
teardown do
- @connection.drop_table 'pg_arrays', if_exists: true
- disable_extension!('hstore', @connection)
+ @connection.drop_table "pg_arrays", if_exists: true
+ disable_extension!("hstore", @connection)
end
def test_column
assert_equal :string, @column.type
- assert_equal "character varying", @column.sql_type
+ assert_equal "character varying(255)", @column.sql_type
assert @column.array?
assert_not @type.binary?
- ratings_column = PgArray.columns_hash['ratings']
+ ratings_column = PgArray.columns_hash["ratings"]
assert_equal :integer, ratings_column.type
assert ratings_column.array?
end
def test_default
- @connection.add_column 'pg_arrays', 'score', :integer, array: true, default: [4, 4, 2]
+ @connection.add_column "pg_arrays", "score", :integer, array: true, default: [4, 4, 2]
PgArray.reset_column_information
- assert_equal([4, 4, 2], PgArray.column_defaults['score'])
+ assert_equal([4, 4, 2], PgArray.column_defaults["score"])
assert_equal([4, 4, 2], PgArray.new.score)
ensure
PgArray.reset_column_information
end
def test_default_strings
- @connection.add_column 'pg_arrays', 'names', :string, array: true, default: ["foo", "bar"]
+ @connection.add_column "pg_arrays", "names", :string, array: true, default: ["foo", "bar"]
PgArray.reset_column_information
- assert_equal(["foo", "bar"], PgArray.column_defaults['names'])
+ assert_equal(["foo", "bar"], PgArray.column_defaults["names"])
assert_equal(["foo", "bar"], PgArray.new.names)
ensure
PgArray.reset_column_information
@@ -68,10 +70,10 @@ class PostgresqlArrayTest < ActiveRecord::PostgreSQLTestCase
@connection.change_column :pg_arrays, :snippets, :text, array: true, default: []
PgArray.reset_column_information
- column = PgArray.columns_hash['snippets']
+ column = PgArray.columns_hash["snippets"]
assert_equal :text, column.type
- assert_equal [], PgArray.column_defaults['snippets']
+ assert_equal [], PgArray.column_defaults["snippets"]
assert column.array?
end
@@ -88,17 +90,17 @@ class PostgresqlArrayTest < ActiveRecord::PostgreSQLTestCase
@connection.change_column_default :pg_arrays, :tags, []
PgArray.reset_column_information
- assert_equal [], PgArray.column_defaults['tags']
+ assert_equal [], PgArray.column_defaults["tags"]
end
def test_type_cast_array
- assert_equal(['1', '2', '3'], @type.deserialize('{1,2,3}'))
- assert_equal([], @type.deserialize('{}'))
- assert_equal([nil], @type.deserialize('{NULL}'))
+ assert_equal(["1", "2", "3"], @type.deserialize("{1,2,3}"))
+ assert_equal([], @type.deserialize("{}"))
+ assert_equal([nil], @type.deserialize("{NULL}"))
end
def test_type_cast_integers
- x = PgArray.new(ratings: ['1', '2'])
+ x = PgArray.new(ratings: ["1", "2"])
assert_equal([1, 2], x.ratings)
@@ -110,22 +112,23 @@ class PostgresqlArrayTest < ActiveRecord::PostgreSQLTestCase
def test_schema_dump_with_shorthand
output = dump_table_schema "pg_arrays"
- assert_match %r[t\.string\s+"tags",\s+array: true], output
+ assert_match %r[t\.string\s+"tags",\s+limit: 255,\s+array: true], output
assert_match %r[t\.integer\s+"ratings",\s+array: true], output
+ assert_match %r[t\.decimal\s+"decimals",\s+precision: 10,\s+scale: 2,\s+default: \[\],\s+array: true], output
end
def test_select_with_strings
@connection.execute "insert into pg_arrays (tags) VALUES ('{1,2,3}')"
x = PgArray.first
- assert_equal(['1','2','3'], x.tags)
+ assert_equal(["1", "2", "3"], x.tags)
end
def test_rewrite_with_strings
@connection.execute "insert into pg_arrays (tags) VALUES ('{1,2,3}')"
x = PgArray.first
- x.tags = ['1','2','3','4']
+ x.tags = ["1", "2", "3", "4"]
x.save!
- assert_equal ['1','2','3','4'], x.reload.tags
+ assert_equal ["1", "2", "3", "4"], x.reload.tags
end
def test_select_with_integers
@@ -137,25 +140,25 @@ class PostgresqlArrayTest < ActiveRecord::PostgreSQLTestCase
def test_rewrite_with_integers
@connection.execute "insert into pg_arrays (ratings) VALUES ('{1,2,3}')"
x = PgArray.first
- x.ratings = [2, '3', 4]
+ x.ratings = [2, "3", 4]
x.save!
assert_equal [2, 3, 4], x.reload.ratings
end
def test_multi_dimensional_with_strings
- assert_cycle(:tags, [[['1'], ['2']], [['2'], ['3']]])
+ assert_cycle(:tags, [[["1"], ["2"]], [["2"], ["3"]]])
end
def test_with_empty_strings
- assert_cycle(:tags, [ '1', '2', '', '4', '', '5' ])
+ assert_cycle(:tags, [ "1", "2", "", "4", "", "5" ])
end
def test_with_multi_dimensional_empty_strings
- assert_cycle(:tags, [[['1', '2'], ['', '4'], ['', '5']]])
+ assert_cycle(:tags, [[["1", "2"], ["", "4"], ["", "5"]]])
end
def test_with_arbitrary_whitespace
- assert_cycle(:tags, [[['1', '2'], [' ', '4'], [' ', '5']]])
+ assert_cycle(:tags, [[["1", "2"], [" ", "4"], [" ", "5"]]])
end
def test_multi_dimensional_with_integers
@@ -163,28 +166,28 @@ class PostgresqlArrayTest < ActiveRecord::PostgreSQLTestCase
end
def test_strings_with_quotes
- assert_cycle(:tags, ['this has','some "s that need to be escaped"'])
+ assert_cycle(:tags, ["this has", 'some "s that need to be escaped"'])
end
def test_strings_with_commas
- assert_cycle(:tags, ['this,has','many,values'])
+ assert_cycle(:tags, ["this,has", "many,values"])
end
def test_strings_with_array_delimiters
- assert_cycle(:tags, ['{','}'])
+ assert_cycle(:tags, ["{", "}"])
end
def test_strings_with_null_strings
- assert_cycle(:tags, ['NULL','NULL'])
+ assert_cycle(:tags, ["NULL", "NULL"])
end
def test_contains_nils
- assert_cycle(:tags, ['1',nil,nil])
+ assert_cycle(:tags, ["1", nil, nil])
end
def test_insert_fixture
tag_values = ["val1", "val2", "val3_with_'_multiple_quote_'_chars"]
- @connection.insert_fixture({"tags" => tag_values}, "pg_arrays" )
+ @connection.insert_fixture({ "tags" => tag_values }, "pg_arrays")
assert_equal(PgArray.last.tags, tag_values)
end
@@ -211,17 +214,18 @@ class PostgresqlArrayTest < ActiveRecord::PostgreSQLTestCase
x = PgArray.create!(tags: tags)
x.reload
- assert_equal x.tags_before_type_cast, PgArray.type_for_attribute('tags').serialize(tags)
+ refute x.changed?
end
def test_quoting_non_standard_delimiters
strings = ["hello,", "world;"]
oid = ActiveRecord::ConnectionAdapters::PostgreSQL::OID
- comma_delim = oid::Array.new(ActiveRecord::Type::String.new, ',')
- semicolon_delim = oid::Array.new(ActiveRecord::Type::String.new, ';')
+ comma_delim = oid::Array.new(ActiveRecord::Type::String.new, ",")
+ semicolon_delim = oid::Array.new(ActiveRecord::Type::String.new, ";")
+ conn = PgArray.connection
- assert_equal %({"hello,",world;}), comma_delim.serialize(strings)
- assert_equal %({hello,;"world;"}), semicolon_delim.serialize(strings)
+ assert_equal %({"hello,",world;}), conn.type_cast(comma_delim.serialize(strings))
+ assert_equal %({hello,;"world;"}), conn.type_cast(semicolon_delim.serialize(strings))
end
def test_mutate_array
@@ -236,13 +240,13 @@ class PostgresqlArrayTest < ActiveRecord::PostgreSQLTestCase
end
def test_mutate_value_in_array
- x = PgArray.create!(hstores: [{ a: 'a' }, { b: 'b' }])
+ x = PgArray.create!(hstores: [{ a: "a" }, { b: "b" }])
- x.hstores.first['a'] = 'c'
+ x.hstores.first["a"] = "c"
x.save!
x.reload
- assert_equal [{ 'a' => 'c' }, { 'b' => 'b' }], x.hstores
+ assert_equal [{ "a" => "c" }, { "b" => "b" }], x.hstores
assert_not x.changed?
end
@@ -290,6 +294,12 @@ class PostgresqlArrayTest < ActiveRecord::PostgreSQLTestCase
assert_equal record.tags, record.reload.tags
end
+ def test_where_by_attribute_with_array
+ tags = ["black", "blue"]
+ record = PgArray.create!(tags: tags)
+ assert_equal record, PgArray.where(tags: tags).take
+ end
+
def test_uniqueness_validation
klass = Class.new(PgArray) do
validates_uniqueness_of :tags
@@ -305,18 +315,33 @@ class PostgresqlArrayTest < ActiveRecord::PostgreSQLTestCase
assert_equal ["has already been taken"], e2.errors[:tags], "Should have uniqueness message for tags"
end
- private
- def assert_cycle field, array
- # test creation
- x = PgArray.create!(field => array)
- x.reload
- assert_equal(array, x.public_send(field))
+ def test_encoding_arrays_of_utf8_strings
+ arrays_of_utf8_strings = %w(nový ファイル)
+ assert_equal arrays_of_utf8_strings, @type.deserialize(@type.serialize(arrays_of_utf8_strings))
+ assert_equal [arrays_of_utf8_strings], @type.deserialize(@type.serialize([arrays_of_utf8_strings]))
+ end
- # test updating
- x = PgArray.create!(field => [])
- x.public_send("#{field}=", array)
- x.save!
- x.reload
- assert_equal(array, x.public_send(field))
+ def test_precision_is_respected_on_timestamp_columns
+ time = Time.now.change(usec: 123)
+ record = PgArray.create!(timestamps: [time])
+
+ assert_equal 123, record.timestamps.first.usec
+ record.reload
+ assert_equal 123, record.timestamps.first.usec
end
+
+ private
+ def assert_cycle(field, array)
+ # test creation
+ x = PgArray.create!(field => array)
+ x.reload
+ assert_equal(array, x.public_send(field))
+
+ # test updating
+ x = PgArray.create!(field => [])
+ x.public_send("#{field}=", array)
+ x.save!
+ x.reload
+ assert_equal(array, x.public_send(field))
+ end
end
diff --git a/activerecord/test/cases/adapters/postgresql/bit_string_test.rb b/activerecord/test/cases/adapters/postgresql/bit_string_test.rb
index cec6081aec..7712e809a2 100644
--- a/activerecord/test/cases/adapters/postgresql/bit_string_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/bit_string_test.rb
@@ -1,6 +1,6 @@
require "cases/helper"
-require 'support/connection_helper'
-require 'support/schema_dumping_helper'
+require "support/connection_helper"
+require "support/schema_dumping_helper"
class PostgresqlBitStringTest < ActiveRecord::PostgreSQLTestCase
include ConnectionHelper
@@ -10,7 +10,7 @@ class PostgresqlBitStringTest < ActiveRecord::PostgreSQLTestCase
def setup
@connection = ActiveRecord::Base.connection
- @connection.create_table('postgresql_bit_strings', :force => true) do |t|
+ @connection.create_table("postgresql_bit_strings", force: true) do |t|
t.bit :a_bit, default: "00000011", limit: 8
t.bit_varying :a_bit_varying, default: "0011", limit: 4
t.bit :another_bit
@@ -20,7 +20,7 @@ class PostgresqlBitStringTest < ActiveRecord::PostgreSQLTestCase
def teardown
return unless @connection
- @connection.drop_table 'postgresql_bit_strings', if_exists: true
+ @connection.drop_table "postgresql_bit_strings", if_exists: true
end
def test_bit_string_column
@@ -44,10 +44,10 @@ class PostgresqlBitStringTest < ActiveRecord::PostgreSQLTestCase
end
def test_default
- assert_equal "00000011", PostgresqlBitString.column_defaults['a_bit']
+ assert_equal "00000011", PostgresqlBitString.column_defaults["a_bit"]
assert_equal "00000011", PostgresqlBitString.new.a_bit
- assert_equal "0011", PostgresqlBitString.column_defaults['a_bit_varying']
+ assert_equal "0011", PostgresqlBitString.column_defaults["a_bit_varying"]
assert_equal "0011", PostgresqlBitString.new.a_bit_varying
end
@@ -65,10 +65,11 @@ class PostgresqlBitStringTest < ActiveRecord::PostgreSQLTestCase
end
def test_roundtrip
- PostgresqlBitString.create! a_bit: "00001010", a_bit_varying: "0101"
- record = PostgresqlBitString.first
+ record = PostgresqlBitString.create!(a_bit: "00001010", a_bit_varying: "0101")
assert_equal "00001010", record.a_bit
assert_equal "0101", record.a_bit_varying
+ assert_nil record.another_bit
+ assert_nil record.another_bit_varying
record.a_bit = "11111111"
record.a_bit_varying = "0xF"
diff --git a/activerecord/test/cases/adapters/postgresql/bytea_test.rb b/activerecord/test/cases/adapters/postgresql/bytea_test.rb
index 7adc070430..5c207116c4 100644
--- a/activerecord/test/cases/adapters/postgresql/bytea_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/bytea_test.rb
@@ -1,29 +1,29 @@
require "cases/helper"
-require 'support/schema_dumping_helper'
+require "support/schema_dumping_helper"
class PostgresqlByteaTest < ActiveRecord::PostgreSQLTestCase
include SchemaDumpingHelper
class ByteaDataType < ActiveRecord::Base
- self.table_name = 'bytea_data_type'
+ self.table_name = "bytea_data_type"
end
def setup
@connection = ActiveRecord::Base.connection
begin
@connection.transaction do
- @connection.create_table('bytea_data_type') do |t|
- t.binary 'payload'
- t.binary 'serialized'
+ @connection.create_table("bytea_data_type") do |t|
+ t.binary "payload"
+ t.binary "serialized"
end
end
end
- @column = ByteaDataType.columns_hash['payload']
+ @column = ByteaDataType.columns_hash["payload"]
@type = ByteaDataType.type_for_attribute("payload")
end
teardown do
- @connection.drop_table 'bytea_data_type', if_exists: true
+ @connection.drop_table "bytea_data_type", if_exists: true
end
def test_column
@@ -32,7 +32,7 @@ class PostgresqlByteaTest < ActiveRecord::PostgreSQLTestCase
end
def test_binary_columns_are_limitless_the_upper_limit_is_one_GB
- assert_equal 'bytea', @connection.type_to_sql(:binary, 100_000)
+ assert_equal "bytea", @connection.type_to_sql(:binary, 100_000)
assert_raise ActiveRecord::ActiveRecordError do
@connection.type_to_sql :binary, 4294967295
end
@@ -42,8 +42,8 @@ class PostgresqlByteaTest < ActiveRecord::PostgreSQLTestCase
assert @column
data = "\u001F\x8B"
- assert_equal('UTF-8', data.encoding.name)
- assert_equal('ASCII-8BIT', @type.deserialize(data).encoding.name)
+ assert_equal("UTF-8", data.encoding.name)
+ assert_equal("ASCII-8BIT", @type.deserialize(data).encoding.name)
end
def test_type_cast_binary_value
@@ -52,7 +52,7 @@ class PostgresqlByteaTest < ActiveRecord::PostgreSQLTestCase
end
def test_type_case_nil
- assert_equal(nil, @type.deserialize(nil))
+ assert_nil(@type.deserialize(nil))
end
def test_read_value
@@ -66,7 +66,7 @@ class PostgresqlByteaTest < ActiveRecord::PostgreSQLTestCase
def test_read_nil_value
@connection.execute "insert into bytea_data_type (payload) VALUES (null)"
record = ByteaDataType.first
- assert_equal(nil, record.payload)
+ assert_nil(record.payload)
record.delete
end
@@ -88,14 +88,14 @@ class PostgresqlByteaTest < ActiveRecord::PostgreSQLTestCase
def test_via_to_sql_with_complicating_connection
Thread.new do
other_conn = ActiveRecord::Base.connection
- other_conn.execute('SET standard_conforming_strings = off')
+ other_conn.execute("SET standard_conforming_strings = off")
end.join
test_via_to_sql
end
def test_write_binary
- data = File.read(File.join(File.dirname(__FILE__), '..', '..', '..', 'assets', 'example.log'))
+ data = File.read(File.join(File.dirname(__FILE__), "..", "..", "..", "assets", "example.log"))
assert(data.size > 1)
record = ByteaDataType.create(payload: data)
assert_not record.new_record?
@@ -106,8 +106,8 @@ class PostgresqlByteaTest < ActiveRecord::PostgreSQLTestCase
def test_write_nil
record = ByteaDataType.create(payload: nil)
assert_not record.new_record?
- assert_equal(nil, record.payload)
- assert_equal(nil, ByteaDataType.where(id: record.id).first.payload)
+ assert_nil(record.payload)
+ assert_nil(ByteaDataType.where(id: record.id).first.payload)
end
class Serializer
diff --git a/activerecord/test/cases/adapters/postgresql/case_insensitive_test.rb b/activerecord/test/cases/adapters/postgresql/case_insensitive_test.rb
new file mode 100644
index 0000000000..03b44feab6
--- /dev/null
+++ b/activerecord/test/cases/adapters/postgresql/case_insensitive_test.rb
@@ -0,0 +1,26 @@
+require "cases/helper"
+
+class PostgresqlCaseInsensitiveTest < ActiveRecord::PostgreSQLTestCase
+ class Default < ActiveRecord::Base; end
+
+ def test_case_insensitiveness
+ connection = ActiveRecord::Base.connection
+ table = Default.arel_table
+
+ column = Default.columns_hash["char1"]
+ comparison = connection.case_insensitive_comparison table, :char1, column, nil
+ assert_match(/lower/i, comparison.to_sql)
+
+ column = Default.columns_hash["char2"]
+ comparison = connection.case_insensitive_comparison table, :char2, column, nil
+ assert_match(/lower/i, comparison.to_sql)
+
+ column = Default.columns_hash["char3"]
+ comparison = connection.case_insensitive_comparison table, :char3, column, nil
+ assert_match(/lower/i, comparison.to_sql)
+
+ column = Default.columns_hash["multiline_default"]
+ comparison = connection.case_insensitive_comparison table, :multiline_default, column, nil
+ assert_match(/lower/i, comparison.to_sql)
+ end
+end
diff --git a/activerecord/test/cases/adapters/postgresql/change_schema_test.rb b/activerecord/test/cases/adapters/postgresql/change_schema_test.rb
index bc12df668d..ea642069d2 100644
--- a/activerecord/test/cases/adapters/postgresql/change_schema_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/change_schema_test.rb
@@ -1,4 +1,4 @@
-require 'cases/helper'
+require "cases/helper"
module ActiveRecord
class Migration
@@ -19,17 +19,17 @@ module ActiveRecord
def test_change_string_to_date
connection.change_column :strings, :somedate, :timestamp, using: 'CAST("somedate" AS timestamp)'
- assert_equal :datetime, connection.columns(:strings).find { |c| c.name == 'somedate' }.type
+ assert_equal :datetime, connection.columns(:strings).find { |c| c.name == "somedate" }.type
end
def test_change_type_with_symbol
connection.change_column :strings, :somedate, :timestamp, cast_as: :timestamp
- assert_equal :datetime, connection.columns(:strings).find { |c| c.name == 'somedate' }.type
+ assert_equal :datetime, connection.columns(:strings).find { |c| c.name == "somedate" }.type
end
def test_change_type_with_array
connection.change_column :strings, :somedate, :timestamp, array: true, cast_as: :timestamp
- column = connection.columns(:strings).find { |c| c.name == 'somedate' }
+ column = connection.columns(:strings).find { |c| c.name == "somedate" }
assert_equal :datetime, column.type
assert column.array?
end
diff --git a/activerecord/test/cases/adapters/postgresql/citext_test.rb b/activerecord/test/cases/adapters/postgresql/citext_test.rb
index bd62041e79..ca95e4b626 100644
--- a/activerecord/test/cases/adapters/postgresql/citext_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/citext_test.rb
@@ -1,49 +1,49 @@
-require 'cases/helper'
-require 'support/schema_dumping_helper'
+require "cases/helper"
+require "support/schema_dumping_helper"
if ActiveRecord::Base.connection.supports_extensions?
class PostgresqlCitextTest < ActiveRecord::PostgreSQLTestCase
include SchemaDumpingHelper
class Citext < ActiveRecord::Base
- self.table_name = 'citexts'
+ self.table_name = "citexts"
end
def setup
@connection = ActiveRecord::Base.connection
- enable_extension!('citext', @connection)
+ enable_extension!("citext", @connection)
- @connection.create_table('citexts') do |t|
- t.citext 'cival'
+ @connection.create_table("citexts") do |t|
+ t.citext "cival"
end
end
teardown do
- @connection.drop_table 'citexts', if_exists: true
- disable_extension!('citext', @connection)
+ @connection.drop_table "citexts", if_exists: true
+ disable_extension!("citext", @connection)
end
def test_citext_enabled
- assert @connection.extension_enabled?('citext')
+ assert @connection.extension_enabled?("citext")
end
def test_column
- column = Citext.columns_hash['cival']
+ column = Citext.columns_hash["cival"]
assert_equal :citext, column.type
- assert_equal 'citext', column.sql_type
+ assert_equal "citext", column.sql_type
assert_not column.array?
- type = Citext.type_for_attribute('cival')
+ type = Citext.type_for_attribute("cival")
assert_not type.binary?
end
def test_change_table_supports_json
@connection.transaction do
- @connection.change_table('citexts') do |t|
- t.citext 'username'
+ @connection.change_table("citexts") do |t|
+ t.citext "username"
end
Citext.reset_column_information
- column = Citext.columns_hash['username']
+ column = Citext.columns_hash["username"]
assert_equal :citext, column.type
raise ActiveRecord::Rollback # reset the schema change
@@ -53,7 +53,7 @@ if ActiveRecord::Base.connection.supports_extensions?
end
def test_write
- x = Citext.new(cival: 'Some CI Text')
+ x = Citext.new(cival: "Some CI Text")
x.save!
citext = Citext.first
assert_equal "Some CI Text", citext.cival
@@ -66,8 +66,8 @@ if ActiveRecord::Base.connection.supports_extensions?
def test_select_case_insensitive
@connection.execute "insert into citexts (cival) values('Cased Text')"
- x = Citext.where(cival: 'cased text').first
- assert_equal 'Cased Text', x.cival
+ x = Citext.where(cival: "cased text").first
+ assert_equal "Cased Text", x.cival
end
def test_schema_dump_with_shorthand
diff --git a/activerecord/test/cases/adapters/postgresql/collation_test.rb b/activerecord/test/cases/adapters/postgresql/collation_test.rb
index 8470329c35..b39e298a5d 100644
--- a/activerecord/test/cases/adapters/postgresql/collation_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/collation_test.rb
@@ -1,5 +1,5 @@
require "cases/helper"
-require 'support/schema_dumping_helper'
+require "support/schema_dumping_helper"
class PostgresqlCollationTest < ActiveRecord::PostgreSQLTestCase
include SchemaDumpingHelper
@@ -7,8 +7,8 @@ class PostgresqlCollationTest < ActiveRecord::PostgreSQLTestCase
def setup
@connection = ActiveRecord::Base.connection
@connection.create_table :postgresql_collations, force: true do |t|
- t.string :string_c, collation: 'C'
- t.text :text_posix, collation: 'POSIX'
+ t.string :string_c, collation: "C"
+ t.text :text_posix, collation: "POSIX"
end
end
@@ -17,32 +17,32 @@ class PostgresqlCollationTest < ActiveRecord::PostgreSQLTestCase
end
test "string column with collation" do
- column = @connection.columns(:postgresql_collations).find { |c| c.name == 'string_c' }
+ column = @connection.columns(:postgresql_collations).find { |c| c.name == "string_c" }
assert_equal :string, column.type
- assert_equal 'C', column.collation
+ assert_equal "C", column.collation
end
test "text column with collation" do
- column = @connection.columns(:postgresql_collations).find { |c| c.name == 'text_posix' }
+ column = @connection.columns(:postgresql_collations).find { |c| c.name == "text_posix" }
assert_equal :text, column.type
- assert_equal 'POSIX', column.collation
+ assert_equal "POSIX", column.collation
end
test "add column with collation" do
- @connection.add_column :postgresql_collations, :title, :string, collation: 'C'
+ @connection.add_column :postgresql_collations, :title, :string, collation: "C"
- column = @connection.columns(:postgresql_collations).find { |c| c.name == 'title' }
+ column = @connection.columns(:postgresql_collations).find { |c| c.name == "title" }
assert_equal :string, column.type
- assert_equal 'C', column.collation
+ assert_equal "C", column.collation
end
test "change column with collation" do
@connection.add_column :postgresql_collations, :description, :string
- @connection.change_column :postgresql_collations, :description, :text, collation: 'POSIX'
+ @connection.change_column :postgresql_collations, :description, :text, collation: "POSIX"
- column = @connection.columns(:postgresql_collations).find { |c| c.name == 'description' }
+ column = @connection.columns(:postgresql_collations).find { |c| c.name == "description" }
assert_equal :text, column.type
- assert_equal 'POSIX', column.collation
+ assert_equal "POSIX", column.collation
end
test "schema dump includes collation" do
diff --git a/activerecord/test/cases/adapters/postgresql/composite_test.rb b/activerecord/test/cases/adapters/postgresql/composite_test.rb
index 1de87e5f01..1da2a9e2ac 100644
--- a/activerecord/test/cases/adapters/postgresql/composite_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/composite_test.rb
@@ -1,5 +1,5 @@
require "cases/helper"
-require 'support/connection_helper'
+require "support/connection_helper"
module PostgresqlCompositeBehavior
include ConnectionHelper
@@ -20,7 +20,7 @@ module PostgresqlCompositeBehavior
street VARCHAR(90)
);
SQL
- @connection.create_table('postgresql_composites') do |t|
+ @connection.create_table("postgresql_composites") do |t|
t.column :address, :full_address
end
end
@@ -29,8 +29,8 @@ module PostgresqlCompositeBehavior
def teardown
super
- @connection.drop_table 'postgresql_composites', if_exists: true
- @connection.execute 'DROP TYPE IF EXISTS full_address'
+ @connection.drop_table "postgresql_composites", if_exists: true
+ @connection.execute "DROP TYPE IF EXISTS full_address"
reset_connection
PostgresqlComposite.reset_column_information
end
@@ -69,12 +69,12 @@ class PostgresqlCompositeTest < ActiveRecord::PostgreSQLTestCase
end
private
- def ensure_warning_is_issued
- warning = capture(:stderr) do
- PostgresqlComposite.columns_hash
+ def ensure_warning_is_issued
+ warning = capture(:stderr) do
+ PostgresqlComposite.columns_hash
+ end
+ assert_match(/unknown OID \d+: failed to recognize type of 'address'\. It will be treated as String\./, warning)
end
- assert_match(/unknown OID \d+: failed to recognize type of 'address'\. It will be treated as String\./, warning)
- end
end
class PostgresqlCompositeWithCustomOIDTest < ActiveRecord::PostgreSQLTestCase
@@ -126,7 +126,7 @@ class PostgresqlCompositeWithCustomOIDTest < ActiveRecord::PostgreSQLTestCase
composite.address = FullAddress.new("Paris", "Rue Basse")
composite.save!
- assert_equal 'Paris', composite.reload.address.city
- assert_equal 'Rue Basse', composite.reload.address.street
+ assert_equal "Paris", composite.reload.address.city
+ assert_equal "Rue Basse", composite.reload.address.street
end
end
diff --git a/activerecord/test/cases/adapters/postgresql/connection_test.rb b/activerecord/test/cases/adapters/postgresql/connection_test.rb
index f8403bfe1a..e916d15f7f 100644
--- a/activerecord/test/cases/adapters/postgresql/connection_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/connection_test.rb
@@ -1,5 +1,5 @@
require "cases/helper"
-require 'support/connection_helper'
+require "support/connection_helper"
module ActiveRecord
class PostgresqlConnectionTest < ActiveRecord::PostgreSQLTestCase
@@ -13,7 +13,7 @@ module ActiveRecord
def setup
super
@subscriber = SQLSubscriber.new
- @subscription = ActiveSupport::Notifications.subscribe('sql.active_record', @subscriber)
+ @subscription = ActiveSupport::Notifications.subscribe("sql.active_record", @subscriber)
@connection = ActiveRecord::Base.connection
end
@@ -23,10 +23,10 @@ module ActiveRecord
end
def test_truncate
- count = ActiveRecord::Base.connection.execute("select count(*) from comments").first['count'].to_i
+ count = ActiveRecord::Base.connection.execute("select count(*) from comments").first["count"].to_i
assert_operator count, :>, 0
ActiveRecord::Base.connection.truncate("comments")
- count = ActiveRecord::Base.connection.execute("select count(*) from comments").first['count'].to_i
+ count = ActiveRecord::Base.connection.execute("select count(*) from comments").first["count"].to_i
assert_equal 0, count
end
@@ -54,85 +54,85 @@ module ActiveRecord
NonExistentTable.establish_connection(params)
# Verify the connection param has been applied.
- expect = NonExistentTable.connection.query('show geqo').first.first
- assert_equal 'off', expect
+ expect = NonExistentTable.connection.query("show geqo").first.first
+ assert_equal "off", expect
end
def test_reset
- @connection.query('ROLLBACK')
- @connection.query('SET geqo TO off')
+ @connection.query("ROLLBACK")
+ @connection.query("SET geqo TO off")
# Verify the setting has been applied.
- expect = @connection.query('show geqo').first.first
- assert_equal 'off', expect
+ expect = @connection.query("show geqo").first.first
+ assert_equal "off", expect
@connection.reset!
# Verify the setting has been cleared.
- expect = @connection.query('show geqo').first.first
- assert_equal 'on', expect
+ expect = @connection.query("show geqo").first.first
+ assert_equal "on", expect
end
def test_reset_with_transaction
- @connection.query('ROLLBACK')
- @connection.query('SET geqo TO off')
+ @connection.query("ROLLBACK")
+ @connection.query("SET geqo TO off")
# Verify the setting has been applied.
- expect = @connection.query('show geqo').first.first
- assert_equal 'off', expect
+ expect = @connection.query("show geqo").first.first
+ assert_equal "off", expect
- @connection.query('BEGIN')
+ @connection.query("BEGIN")
@connection.reset!
# Verify the setting has been cleared.
- expect = @connection.query('show geqo').first.first
- assert_equal 'on', expect
+ expect = @connection.query("show geqo").first.first
+ assert_equal "on", expect
end
def test_tables_logs_name
- ActiveSupport::Deprecation.silence { @connection.tables('hello') }
- assert_equal 'SCHEMA', @subscriber.logged[0][1]
+ @connection.tables
+ assert_equal "SCHEMA", @subscriber.logged[0][1]
end
def test_indexes_logs_name
- @connection.indexes('items', 'hello')
- assert_equal 'SCHEMA', @subscriber.logged[0][1]
+ @connection.indexes("items", "hello")
+ assert_equal "SCHEMA", @subscriber.logged[0][1]
end
def test_table_exists_logs_name
- ActiveSupport::Deprecation.silence { @connection.table_exists?('items') }
- assert_equal 'SCHEMA', @subscriber.logged[0][1]
+ @connection.table_exists?("items")
+ assert_equal "SCHEMA", @subscriber.logged[0][1]
end
def test_table_alias_length_logs_name
@connection.instance_variable_set("@table_alias_length", nil)
@connection.table_alias_length
- assert_equal 'SCHEMA', @subscriber.logged[0][1]
+ assert_equal "SCHEMA", @subscriber.logged[0][1]
end
def test_current_database_logs_name
@connection.current_database
- assert_equal 'SCHEMA', @subscriber.logged[0][1]
+ assert_equal "SCHEMA", @subscriber.logged[0][1]
end
def test_encoding_logs_name
@connection.encoding
- assert_equal 'SCHEMA', @subscriber.logged[0][1]
+ assert_equal "SCHEMA", @subscriber.logged[0][1]
end
def test_schema_names_logs_name
@connection.schema_names
- assert_equal 'SCHEMA', @subscriber.logged[0][1]
+ assert_equal "SCHEMA", @subscriber.logged[0][1]
end
if ActiveRecord::Base.connection.prepared_statements
def test_statement_key_is_logged
bind = Relation::QueryAttribute.new(nil, 1, Type::Value.new)
- @connection.exec_query('SELECT $1::integer', 'SQL', [bind], prepare: true)
+ @connection.exec_query("SELECT $1::integer", "SQL", [bind], prepare: true)
name = @subscriber.payloads.last[:statement_name]
assert name
res = @connection.exec_query("EXPLAIN (FORMAT JSON) EXECUTE #{name}(1)")
- plan = res.column_types['QUERY PLAN'].deserialize res.rows.first.first
+ plan = res.column_types["QUERY PLAN"].deserialize res.rows.first.first
assert_operator plan.length, :>, 0
end
end
@@ -146,7 +146,7 @@ module ActiveRecord
# To restart PostgreSQL 9.1 on OS X, installed via MacPorts, ...
# sudo su postgres -c "pg_ctl restart -D /opt/local/var/db/postgresql91/defaultdb/ -m fast"
def test_reconnection_after_actual_disconnection_with_verify
- original_connection_pid = @connection.query('select pg_backend_pid()')
+ original_connection_pid = @connection.query("select pg_backend_pid()")
# Sanity check.
assert @connection.active?
@@ -155,8 +155,8 @@ module ActiveRecord
secondary_connection = ActiveRecord::Base.connection_pool.checkout
secondary_connection.query("select pg_terminate_backend(#{original_connection_pid.first.first})")
ActiveRecord::Base.connection_pool.checkin(secondary_connection)
- elsif ARTest.config['with_manual_interventions']
- puts 'Kill the connection now (e.g. by restarting the PostgreSQL ' +
+ elsif ARTest.config["with_manual_interventions"]
+ puts "Kill the connection now (e.g. by restarting the PostgreSQL " +
'server with the "-m fast" option) and then press enter.'
$stdin.gets
else
@@ -172,7 +172,7 @@ module ActiveRecord
# If we get no exception here, then either we re-connected successfully, or
# we never actually got disconnected.
- new_connection_pid = @connection.query('select pg_backend_pid()')
+ new_connection_pid = @connection.query("select pg_backend_pid()")
assert_not_equal original_connection_pid, new_connection_pid,
"umm -- looks like you didn't break the connection, because we're still " +
@@ -184,7 +184,7 @@ module ActiveRecord
def test_set_session_variable_true
run_without_connection do |orig_connection|
- ActiveRecord::Base.establish_connection(orig_connection.deep_merge({:variables => {:debug_print_plan => true}}))
+ ActiveRecord::Base.establish_connection(orig_connection.deep_merge(variables: { debug_print_plan: true }))
set_true = ActiveRecord::Base.connection.exec_query "SHOW DEBUG_PRINT_PLAN"
assert_equal set_true.rows, [["on"]]
end
@@ -192,7 +192,7 @@ module ActiveRecord
def test_set_session_variable_false
run_without_connection do |orig_connection|
- ActiveRecord::Base.establish_connection(orig_connection.deep_merge({:variables => {:debug_print_plan => false}}))
+ ActiveRecord::Base.establish_connection(orig_connection.deep_merge(variables: { debug_print_plan: false }))
set_false = ActiveRecord::Base.connection.exec_query "SHOW DEBUG_PRINT_PLAN"
assert_equal set_false.rows, [["off"]]
end
@@ -201,14 +201,14 @@ module ActiveRecord
def test_set_session_variable_nil
run_without_connection do |orig_connection|
# This should be a no-op that does not raise an error
- ActiveRecord::Base.establish_connection(orig_connection.deep_merge({:variables => {:debug_print_plan => nil}}))
+ ActiveRecord::Base.establish_connection(orig_connection.deep_merge(variables: { debug_print_plan: nil }))
end
end
def test_set_session_variable_default
run_without_connection do |orig_connection|
# This should execute a query that does not raise an error
- ActiveRecord::Base.establish_connection(orig_connection.deep_merge({:variables => {:debug_print_plan => :default}}))
+ ActiveRecord::Base.establish_connection(orig_connection.deep_merge(variables: { debug_print_plan: :default }))
end
end
@@ -224,14 +224,14 @@ module ActiveRecord
got_lock = @connection.get_advisory_lock(lock_id)
assert got_lock, "get_advisory_lock should have returned true but it didn't"
- advisory_lock = @connection.query(list_advisory_locks).find {|l| l[1] == lock_id}
+ advisory_lock = @connection.query(list_advisory_locks).find { |l| l[1] == lock_id }
assert advisory_lock,
"expected to find an advisory lock with lock_id #{lock_id} but there wasn't one"
released_lock = @connection.release_advisory_lock(lock_id)
assert released_lock, "expected release_advisory_lock to return true but it didn't"
- advisory_locks = @connection.query(list_advisory_locks).select {|l| l[1] == lock_id}
+ advisory_locks = @connection.query(list_advisory_locks).select { |l| l[1] == lock_id }
assert_empty advisory_locks,
"expected to have released advisory lock with lock_id #{lock_id} but it was still held"
end
@@ -241,17 +241,17 @@ module ActiveRecord
with_warning_suppression do
released_non_existent_lock = @connection.release_advisory_lock(fake_lock_id)
assert_equal released_non_existent_lock, false,
- 'expected release_advisory_lock to return false when there was no lock to release'
+ "expected release_advisory_lock to return false when there was no lock to release"
end
end
- protected
+ private
- def with_warning_suppression
- log_level = @connection.client_min_messages
- @connection.client_min_messages = 'error'
- yield
- @connection.client_min_messages = log_level
- end
+ def with_warning_suppression
+ log_level = @connection.client_min_messages
+ @connection.client_min_messages = "error"
+ yield
+ @connection.client_min_messages = log_level
+ end
end
end
diff --git a/activerecord/test/cases/adapters/postgresql/datatype_test.rb b/activerecord/test/cases/adapters/postgresql/datatype_test.rb
index 232c25cb3b..0ac8b7339b 100644
--- a/activerecord/test/cases/adapters/postgresql/datatype_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/datatype_test.rb
@@ -1,6 +1,5 @@
require "cases/helper"
-require 'support/ddl_helper'
-
+require "support/ddl_helper"
class PostgresqlTime < ActiveRecord::Base
end
@@ -38,8 +37,8 @@ class PostgresqlDataTypeTest < ActiveRecord::PostgreSQLTestCase
end
def test_time_values
- assert_equal '-1 years -2 days', @first_time.time_interval
- assert_equal '-21 days', @first_time.scaled_time_interval
+ assert_equal "-1 years -2 days", @first_time.time_interval
+ assert_equal "-21 days", @first_time.scaled_time_interval
end
def test_oid_values
@@ -47,10 +46,10 @@ class PostgresqlDataTypeTest < ActiveRecord::PostgreSQLTestCase
end
def test_update_time
- @first_time.time_interval = '2 years 3 minutes'
+ @first_time.time_interval = "2 years 3 minutes"
assert @first_time.save
assert @first_time.reload
- assert_equal '2 years 00:03:00', @first_time.time_interval
+ assert_equal "2 years 00:03:00", @first_time.time_interval
end
def test_update_oid
@@ -62,7 +61,7 @@ class PostgresqlDataTypeTest < ActiveRecord::PostgreSQLTestCase
end
def test_text_columns_are_limitless_the_upper_limit_is_one_GB
- assert_equal 'text', @connection.type_to_sql(:text, 100_000)
+ assert_equal "text", @connection.type_to_sql(:text, 100_000)
assert_raise ActiveRecord::ActiveRecordError do
@connection.type_to_sql :text, 4294967295
end
@@ -77,15 +76,15 @@ class PostgresqlInternalDataTypeTest < ActiveRecord::PostgreSQLTestCase
end
def test_name_column_type
- with_example_table @connection, 'ex', 'data name' do
- column = @connection.columns('ex').find { |col| col.name == 'data' }
+ with_example_table @connection, "ex", "data name" do
+ column = @connection.columns("ex").find { |col| col.name == "data" }
assert_equal :string, column.type
end
end
def test_char_column_type
- with_example_table @connection, 'ex', 'data "char"' do
- column = @connection.columns('ex').find { |col| col.name == 'data' }
+ with_example_table @connection, "ex", 'data "char"' do
+ column = @connection.columns("ex").find { |col| col.name == "data" }
assert_equal :string, column.type
end
end
diff --git a/activerecord/test/cases/adapters/postgresql/domain_test.rb b/activerecord/test/cases/adapters/postgresql/domain_test.rb
index 6102ddacd1..f1eb8adb15 100644
--- a/activerecord/test/cases/adapters/postgresql/domain_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/domain_test.rb
@@ -1,5 +1,5 @@
require "cases/helper"
-require 'support/connection_helper'
+require "support/connection_helper"
class PostgresqlDomainTest < ActiveRecord::PostgreSQLTestCase
include ConnectionHelper
@@ -12,15 +12,15 @@ class PostgresqlDomainTest < ActiveRecord::PostgreSQLTestCase
@connection = ActiveRecord::Base.connection
@connection.transaction do
@connection.execute "CREATE DOMAIN custom_money as numeric(8,2)"
- @connection.create_table('postgresql_domains') do |t|
+ @connection.create_table("postgresql_domains") do |t|
t.column :price, :custom_money
end
end
end
teardown do
- @connection.drop_table 'postgresql_domains', if_exists: true
- @connection.execute 'DROP DOMAIN IF EXISTS custom_money'
+ @connection.drop_table "postgresql_domains", if_exists: true
+ @connection.execute "DROP DOMAIN IF EXISTS custom_money"
reset_connection
end
diff --git a/activerecord/test/cases/adapters/postgresql/enum_test.rb b/activerecord/test/cases/adapters/postgresql/enum_test.rb
index 6816a6514b..5e5a3158ba 100644
--- a/activerecord/test/cases/adapters/postgresql/enum_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/enum_test.rb
@@ -1,5 +1,5 @@
require "cases/helper"
-require 'support/connection_helper'
+require "support/connection_helper"
class PostgresqlEnumTest < ActiveRecord::PostgreSQLTestCase
include ConnectionHelper
@@ -14,15 +14,15 @@ class PostgresqlEnumTest < ActiveRecord::PostgreSQLTestCase
@connection.execute <<-SQL
CREATE TYPE mood AS ENUM ('sad', 'ok', 'happy');
SQL
- @connection.create_table('postgresql_enums') do |t|
+ @connection.create_table("postgresql_enums") do |t|
t.column :current_mood, :mood
end
end
end
teardown do
- @connection.drop_table 'postgresql_enums', if_exists: true
- @connection.execute 'DROP TYPE IF EXISTS mood'
+ @connection.drop_table "postgresql_enums", if_exists: true
+ @connection.execute "DROP TYPE IF EXISTS mood"
reset_connection
end
@@ -37,10 +37,10 @@ class PostgresqlEnumTest < ActiveRecord::PostgreSQLTestCase
end
def test_enum_defaults
- @connection.add_column 'postgresql_enums', 'good_mood', :mood, default: 'happy'
+ @connection.add_column "postgresql_enums", "good_mood", :mood, default: "happy"
PostgresqlEnum.reset_column_information
- assert_equal "happy", PostgresqlEnum.column_defaults['good_mood']
+ assert_equal "happy", PostgresqlEnum.column_defaults["good_mood"]
assert_equal "happy", PostgresqlEnum.new.good_mood
ensure
PostgresqlEnum.reset_column_information
diff --git a/activerecord/test/cases/adapters/postgresql/explain_test.rb b/activerecord/test/cases/adapters/postgresql/explain_test.rb
index 29bf2c15ea..7493bce4fb 100644
--- a/activerecord/test/cases/adapters/postgresql/explain_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/explain_test.rb
@@ -1,20 +1,20 @@
require "cases/helper"
-require 'models/developer'
-require 'models/computer'
+require "models/developer"
+require "models/computer"
class PostgreSQLExplainTest < ActiveRecord::PostgreSQLTestCase
fixtures :developers
def test_explain_for_one_query
explain = Developer.where(id: 1).explain
- assert_match %r(EXPLAIN for: SELECT "developers".* FROM "developers" WHERE "developers"."id" = \$?1), explain
+ assert_match %r(EXPLAIN for: SELECT "developers".* FROM "developers" WHERE "developers"."id" = (?:\$1 \[\["id", 1\]\]|1)), explain
assert_match %(QUERY PLAN), explain
end
def test_explain_with_eager_loading
explain = Developer.where(id: 1).includes(:audit_logs).explain
assert_match %(QUERY PLAN), explain
- assert_match %r(EXPLAIN for: SELECT "developers".* FROM "developers" WHERE "developers"."id" = \$?1), explain
+ assert_match %r(EXPLAIN for: SELECT "developers".* FROM "developers" WHERE "developers"."id" = (?:\$1 \[\["id", 1\]\]|1)), explain
assert_match %(EXPLAIN for: SELECT "audit_logs".* FROM "audit_logs" WHERE "audit_logs"."developer_id" = 1), explain
end
end
diff --git a/activerecord/test/cases/adapters/postgresql/full_text_test.rb b/activerecord/test/cases/adapters/postgresql/full_text_test.rb
index bde7513339..5ddfe32007 100644
--- a/activerecord/test/cases/adapters/postgresql/full_text_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/full_text_test.rb
@@ -1,5 +1,5 @@
require "cases/helper"
-require 'support/schema_dumping_helper'
+require "support/schema_dumping_helper"
class PostgresqlFullTextTest < ActiveRecord::PostgreSQLTestCase
include SchemaDumpingHelper
@@ -7,13 +7,13 @@ class PostgresqlFullTextTest < ActiveRecord::PostgreSQLTestCase
setup do
@connection = ActiveRecord::Base.connection
- @connection.create_table('tsvectors') do |t|
- t.tsvector 'text_vector'
+ @connection.create_table("tsvectors") do |t|
+ t.tsvector "text_vector"
end
end
teardown do
- @connection.drop_table 'tsvectors', if_exists: true
+ @connection.drop_table "tsvectors", if_exists: true
end
def test_tsvector_column
diff --git a/activerecord/test/cases/adapters/postgresql/geometric_test.rb b/activerecord/test/cases/adapters/postgresql/geometric_test.rb
index 66f0a70394..c1f3a4ae2c 100644
--- a/activerecord/test/cases/adapters/postgresql/geometric_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/geometric_test.rb
@@ -1,6 +1,6 @@
require "cases/helper"
-require 'support/connection_helper'
-require 'support/schema_dumping_helper'
+require "support/connection_helper"
+require "support/schema_dumping_helper"
class PostgresqlPointTest < ActiveRecord::PostgreSQLTestCase
include ConnectionHelper
@@ -18,7 +18,7 @@ class PostgresqlPointTest < ActiveRecord::PostgreSQLTestCase
def setup
@connection = ActiveRecord::Base.connection
- @connection.create_table('postgresql_points') do |t|
+ @connection.create_table("postgresql_points") do |t|
t.point :x
t.point :y, default: [12.2, 13.3]
t.point :z, default: "(14.4,15.5)"
@@ -27,22 +27,10 @@ class PostgresqlPointTest < ActiveRecord::PostgreSQLTestCase
t.point :legacy_y, default: [12.2, 13.3]
t.point :legacy_z, default: "(14.4,15.5)"
end
- @connection.create_table('deprecated_points') do |t|
- t.point :x
- end
end
teardown do
- @connection.drop_table 'postgresql_points', if_exists: true
- @connection.drop_table 'deprecated_points', if_exists: true
- end
-
- class DeprecatedPoint < ActiveRecord::Base; end
-
- def test_deprecated_legacy_type
- assert_deprecated do
- DeprecatedPoint.new
- end
+ @connection.drop_table "postgresql_points", if_exists: true
end
def test_column
@@ -56,10 +44,10 @@ class PostgresqlPointTest < ActiveRecord::PostgreSQLTestCase
end
def test_default
- assert_equal ActiveRecord::Point.new(12.2, 13.3), PostgresqlPoint.column_defaults['y']
+ assert_equal ActiveRecord::Point.new(12.2, 13.3), PostgresqlPoint.column_defaults["y"]
assert_equal ActiveRecord::Point.new(12.2, 13.3), PostgresqlPoint.new.y
- assert_equal ActiveRecord::Point.new(14.4, 15.5), PostgresqlPoint.column_defaults['z']
+ assert_equal ActiveRecord::Point.new(14.4, 15.5), PostgresqlPoint.column_defaults["z"]
assert_equal ActiveRecord::Point.new(14.4, 15.5), PostgresqlPoint.new.z
end
@@ -108,7 +96,7 @@ class PostgresqlPointTest < ActiveRecord::PostgreSQLTestCase
assert_nothing_raised { PostgresqlPoint.new(x: "") }
p = PostgresqlPoint.new(x: "")
- assert_equal nil, p.x
+ assert_nil p.x
end
def test_array_of_points_round_trip
@@ -136,10 +124,10 @@ class PostgresqlPointTest < ActiveRecord::PostgreSQLTestCase
end
def test_legacy_default
- assert_equal [12.2, 13.3], PostgresqlPoint.column_defaults['legacy_y']
+ assert_equal [12.2, 13.3], PostgresqlPoint.column_defaults["legacy_y"]
assert_equal [12.2, 13.3], PostgresqlPoint.new.legacy_y
- assert_equal [14.4, 15.5], PostgresqlPoint.column_defaults['legacy_z']
+ assert_equal [14.4, 15.5], PostgresqlPoint.column_defaults["legacy_z"]
assert_equal [14.4, 15.5], PostgresqlPoint.new.legacy_z
end
@@ -190,51 +178,51 @@ class PostgresqlGeometricTest < ActiveRecord::PostgreSQLTestCase
end
teardown do
- @connection.drop_table 'postgresql_geometrics', if_exists: true
+ @connection.drop_table "postgresql_geometrics", if_exists: true
end
def test_geometric_types
g = PostgresqlGeometric.new(
- :a_line_segment => '(2.0, 3), (5.5, 7.0)',
- :a_box => '2.0, 3, 5.5, 7.0',
- :a_path => '[(2.0, 3), (5.5, 7.0), (8.5, 11.0)]',
- :a_polygon => '((2.0, 3), (5.5, 7.0), (8.5, 11.0))',
- :a_circle => '<(5.3, 10.4), 2>'
+ a_line_segment: "(2.0, 3), (5.5, 7.0)",
+ a_box: "2.0, 3, 5.5, 7.0",
+ a_path: "[(2.0, 3), (5.5, 7.0), (8.5, 11.0)]",
+ a_polygon: "((2.0, 3), (5.5, 7.0), (8.5, 11.0))",
+ a_circle: "<(5.3, 10.4), 2>"
)
g.save!
h = PostgresqlGeometric.find(g.id)
- assert_equal '[(2,3),(5.5,7)]', h.a_line_segment
- assert_equal '(5.5,7),(2,3)', h.a_box # reordered to store upper right corner then bottom left corner
- assert_equal '[(2,3),(5.5,7),(8.5,11)]', h.a_path
- assert_equal '((2,3),(5.5,7),(8.5,11))', h.a_polygon
- assert_equal '<(5.3,10.4),2>', h.a_circle
+ assert_equal "[(2,3),(5.5,7)]", h.a_line_segment
+ assert_equal "(5.5,7),(2,3)", h.a_box # reordered to store upper right corner then bottom left corner
+ assert_equal "[(2,3),(5.5,7),(8.5,11)]", h.a_path
+ assert_equal "((2,3),(5.5,7),(8.5,11))", h.a_polygon
+ assert_equal "<(5.3,10.4),2>", h.a_circle
end
def test_alternative_format
g = PostgresqlGeometric.new(
- :a_line_segment => '((2.0, 3), (5.5, 7.0))',
- :a_box => '(2.0, 3), (5.5, 7.0)',
- :a_path => '((2.0, 3), (5.5, 7.0), (8.5, 11.0))',
- :a_polygon => '2.0, 3, 5.5, 7.0, 8.5, 11.0',
- :a_circle => '((5.3, 10.4), 2)'
+ a_line_segment: "((2.0, 3), (5.5, 7.0))",
+ a_box: "(2.0, 3), (5.5, 7.0)",
+ a_path: "((2.0, 3), (5.5, 7.0), (8.5, 11.0))",
+ a_polygon: "2.0, 3, 5.5, 7.0, 8.5, 11.0",
+ a_circle: "((5.3, 10.4), 2)"
)
g.save!
h = PostgresqlGeometric.find(g.id)
- assert_equal '[(2,3),(5.5,7)]', h.a_line_segment
- assert_equal '(5.5,7),(2,3)', h.a_box # reordered to store upper right corner then bottom left corner
- assert_equal '((2,3),(5.5,7),(8.5,11))', h.a_path
- assert_equal '((2,3),(5.5,7),(8.5,11))', h.a_polygon
- assert_equal '<(5.3,10.4),2>', h.a_circle
+ assert_equal "[(2,3),(5.5,7)]", h.a_line_segment
+ assert_equal "(5.5,7),(2,3)", h.a_box # reordered to store upper right corner then bottom left corner
+ assert_equal "((2,3),(5.5,7),(8.5,11))", h.a_path
+ assert_equal "((2,3),(5.5,7),(8.5,11))", h.a_polygon
+ assert_equal "<(5.3,10.4),2>", h.a_circle
end
def test_geometric_function
- PostgresqlGeometric.create! a_path: '[(2.0, 3), (5.5, 7.0), (8.5, 11.0)]' # [ ] is an open path
- PostgresqlGeometric.create! a_path: '((2.0, 3), (5.5, 7.0), (8.5, 11.0))' # ( ) is a closed path
+ PostgresqlGeometric.create! a_path: "[(2.0, 3), (5.5, 7.0), (8.5, 11.0)]" # [ ] is an open path
+ PostgresqlGeometric.create! a_path: "((2.0, 3), (5.5, 7.0), (8.5, 11.0))" # ( ) is a closed path
objs = PostgresqlGeometric.find_by_sql "SELECT isopen(a_path) FROM postgresql_geometrics ORDER BY id ASC"
assert_equal [true, false], objs.map(&:isopen)
@@ -270,28 +258,28 @@ class PostgreSQLGeometricLineTest < ActiveRecord::PostgreSQLTestCase
teardown do
if defined?(@connection)
- @connection.drop_table 'postgresql_lines', if_exists: true
+ @connection.drop_table "postgresql_lines", if_exists: true
end
end
def test_geometric_line_type
g = PostgresqlLine.new(
- a_line: '{2.0, 3, 5.5}'
+ a_line: "{2.0, 3, 5.5}"
)
g.save!
h = PostgresqlLine.find(g.id)
- assert_equal '{2,3,5.5}', h.a_line
+ assert_equal "{2,3,5.5}", h.a_line
end
def test_alternative_format_line_type
g = PostgresqlLine.new(
- a_line: '(2.0, 3), (4.0, 6.0)'
+ a_line: "(2.0, 3), (4.0, 6.0)"
)
g.save!
h = PostgresqlLine.find(g.id)
- assert_equal '{1.5,-1,0}', h.a_line
+ assert_equal "{1.5,-1,0}", h.a_line
end
def test_schema_dumping_for_line_type
@@ -374,12 +362,12 @@ class PostgreSQLGeometricTypesTest < ActiveRecord::PostgreSQLTestCase
private
- def assert_column_exists(column_name)
- assert connection.column_exists?(table_name, column_name)
- end
+ def assert_column_exists(column_name)
+ assert connection.column_exists?(table_name, column_name)
+ end
- def assert_type_correct(column_name, type)
- column = connection.columns(table_name).find { |c| c.name == column_name.to_s }
- assert_equal type, column.type
- end
+ def assert_type_correct(column_name, type)
+ column = connection.columns(table_name).find { |c| c.name == column_name.to_s }
+ assert_equal type, column.type
+ end
end
diff --git a/activerecord/test/cases/adapters/postgresql/hstore_test.rb b/activerecord/test/cases/adapters/postgresql/hstore_test.rb
index 27cc65a643..1f35300739 100644
--- a/activerecord/test/cases/adapters/postgresql/hstore_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/hstore_test.rb
@@ -1,52 +1,58 @@
require "cases/helper"
-require 'support/schema_dumping_helper'
+require "support/schema_dumping_helper"
if ActiveRecord::Base.connection.supports_extensions?
class PostgresqlHstoreTest < ActiveRecord::PostgreSQLTestCase
include SchemaDumpingHelper
class Hstore < ActiveRecord::Base
- self.table_name = 'hstores'
+ self.table_name = "hstores"
store_accessor :settings, :language, :timezone
end
+ class FakeParameters
+ def to_unsafe_h
+ { "hi" => "hi" }
+ end
+ end
+
def setup
@connection = ActiveRecord::Base.connection
- unless @connection.extension_enabled?('hstore')
- @connection.enable_extension 'hstore'
+ unless @connection.extension_enabled?("hstore")
+ @connection.enable_extension "hstore"
@connection.commit_db_transaction
end
@connection.reconnect!
@connection.transaction do
- @connection.create_table('hstores') do |t|
- t.hstore 'tags', :default => ''
- t.hstore 'payload', array: true
- t.hstore 'settings'
+ @connection.create_table("hstores") do |t|
+ t.hstore "tags", default: ""
+ t.hstore "payload", array: true
+ t.hstore "settings"
end
end
Hstore.reset_column_information
- @column = Hstore.columns_hash['tags']
+ @column = Hstore.columns_hash["tags"]
@type = Hstore.type_for_attribute("tags")
end
teardown do
- @connection.drop_table 'hstores', if_exists: true
+ @connection.drop_table "hstores", if_exists: true
end
def test_hstore_included_in_extensions
assert @connection.respond_to?(:extensions), "connection should have a list of extensions"
- assert @connection.extensions.include?('hstore'), "extension list should include hstore"
+ assert_includes @connection.extensions, "hstore", "extension list should include hstore"
end
def test_disable_enable_hstore
- assert @connection.extension_enabled?('hstore')
- @connection.disable_extension 'hstore'
- assert_not @connection.extension_enabled?('hstore')
- @connection.enable_extension 'hstore'
- assert @connection.extension_enabled?('hstore')
+ assert @connection.extension_enabled?("hstore")
+ @connection.disable_extension "hstore"
+ assert_not @connection.extension_enabled?("hstore")
+ @connection.enable_extension "hstore"
+ assert @connection.extension_enabled?("hstore")
ensure
# Restore column(s) dropped by `drop extension hstore cascade;`
load_schema
@@ -61,22 +67,22 @@ if ActiveRecord::Base.connection.supports_extensions?
end
def test_default
- @connection.add_column 'hstores', 'permissions', :hstore, default: '"users"=>"read", "articles"=>"write"'
+ @connection.add_column "hstores", "permissions", :hstore, default: '"users"=>"read", "articles"=>"write"'
Hstore.reset_column_information
- assert_equal({"users"=>"read", "articles"=>"write"}, Hstore.column_defaults['permissions'])
- assert_equal({"users"=>"read", "articles"=>"write"}, Hstore.new.permissions)
+ assert_equal({ "users" => "read", "articles" => "write" }, Hstore.column_defaults["permissions"])
+ assert_equal({ "users" => "read", "articles" => "write" }, Hstore.new.permissions)
ensure
Hstore.reset_column_information
end
def test_change_table_supports_hstore
@connection.transaction do
- @connection.change_table('hstores') do |t|
- t.hstore 'users', default: ''
+ @connection.change_table("hstores") do |t|
+ t.hstore "users", default: ""
end
Hstore.reset_column_information
- column = Hstore.columns_hash['users']
+ column = Hstore.columns_hash["users"]
assert_equal :hstore, column.type
raise ActiveRecord::Rollback # reset the schema change
@@ -103,18 +109,18 @@ if ActiveRecord::Base.connection.supports_extensions?
end
def test_cast_value_on_write
- x = Hstore.new tags: {"bool" => true, "number" => 5}
- assert_equal({"bool" => true, "number" => 5}, x.tags_before_type_cast)
- assert_equal({"bool" => "true", "number" => "5"}, x.tags)
+ x = Hstore.new tags: { "bool" => true, "number" => 5 }
+ assert_equal({ "bool" => true, "number" => 5 }, x.tags_before_type_cast)
+ assert_equal({ "bool" => "true", "number" => "5" }, x.tags)
x.save
- assert_equal({"bool" => "true", "number" => "5"}, x.reload.tags)
+ assert_equal({ "bool" => "true", "number" => "5" }, x.reload.tags)
end
def test_type_cast_hstore
- assert_equal({'1' => '2'}, @type.deserialize("\"1\"=>\"2\""))
+ assert_equal({ "1" => "2" }, @type.deserialize("\"1\"=>\"2\""))
assert_equal({}, @type.deserialize(""))
- assert_equal({'key'=>nil}, @type.deserialize('key => NULL'))
- assert_equal({'c'=>'}','"a"'=>'b "a b'}, @type.deserialize(%q(c=>"}", "\"a\""=>"b \"a b")))
+ assert_equal({ "key" => nil }, @type.deserialize("key => NULL"))
+ assert_equal({ "c" => "}", '"a"' => 'b "a b' }, @type.deserialize(%q(c=>"}", "\"a\""=>"b \"a b")))
end
def test_with_store_accessors
@@ -156,132 +162,132 @@ if ActiveRecord::Base.connection.supports_extensions?
end
def test_changes_in_place
- hstore = Hstore.create!(settings: { 'one' => 'two' })
- hstore.settings['three'] = 'four'
+ hstore = Hstore.create!(settings: { "one" => "two" })
+ hstore.settings["three"] = "four"
hstore.save!
hstore.reload
- assert_equal 'four', hstore.settings['three']
+ assert_equal "four", hstore.settings["three"]
assert_not hstore.changed?
end
def test_gen1
- assert_equal(%q(" "=>""), @type.serialize({' '=>''}))
+ assert_equal('" "=>""', @type.serialize(" " => ""))
end
def test_gen2
- assert_equal(%q(","=>""), @type.serialize({','=>''}))
+ assert_equal('","=>""', @type.serialize("," => ""))
end
def test_gen3
- assert_equal(%q("="=>""), @type.serialize({'='=>''}))
+ assert_equal('"="=>""', @type.serialize("=" => ""))
end
def test_gen4
- assert_equal(%q(">"=>""), @type.serialize({'>'=>''}))
+ assert_equal('">"=>""', @type.serialize(">" => ""))
end
def test_parse1
- assert_equal({'a'=>nil,'b'=>nil,'c'=>'NuLl','null'=>'c'}, @type.deserialize('a=>null,b=>NuLl,c=>"NuLl",null=>c'))
+ assert_equal({ "a" => nil, "b" => nil, "c" => "NuLl", "null" => "c" }, @type.deserialize('a=>null,b=>NuLl,c=>"NuLl",null=>c'))
end
def test_parse2
- assert_equal({" " => " "}, @type.deserialize("\\ =>\\ "))
+ assert_equal({ " " => " " }, @type.deserialize("\\ =>\\ "))
end
def test_parse3
- assert_equal({"=" => ">"}, @type.deserialize("==>>"))
+ assert_equal({ "=" => ">" }, @type.deserialize("==>>"))
end
def test_parse4
- assert_equal({"=a"=>"q=w"}, @type.deserialize('\=a=>q=w'))
+ assert_equal({ "=a" => "q=w" }, @type.deserialize('\=a=>q=w'))
end
def test_parse5
- assert_equal({"=a"=>"q=w"}, @type.deserialize('"=a"=>q\=w'))
+ assert_equal({ "=a" => "q=w" }, @type.deserialize('"=a"=>q\=w'))
end
def test_parse6
- assert_equal({"\"a"=>"q>w"}, @type.deserialize('"\"a"=>q>w'))
+ assert_equal({ "\"a" => "q>w" }, @type.deserialize('"\"a"=>q>w'))
end
def test_parse7
- assert_equal({"\"a"=>"q\"w"}, @type.deserialize('\"a=>q"w'))
+ assert_equal({ "\"a" => "q\"w" }, @type.deserialize('\"a=>q"w'))
end
def test_rewrite
@connection.execute "insert into hstores (tags) VALUES ('1=>2')"
x = Hstore.first
- x.tags = { '"a\'' => 'b' }
+ x.tags = { '"a\'' => "b" }
assert x.save!
end
def test_select
@connection.execute "insert into hstores (tags) VALUES ('1=>2')"
x = Hstore.first
- assert_equal({'1' => '2'}, x.tags)
+ assert_equal({ "1" => "2" }, x.tags)
end
def test_array_cycle
- assert_array_cycle([{"AA" => "BB", "CC" => "DD"}, {"AA" => nil}])
+ assert_array_cycle([{ "AA" => "BB", "CC" => "DD" }, { "AA" => nil }])
end
def test_array_strings_with_quotes
- assert_array_cycle([{'this has' => 'some "s that need to be escaped"'}])
+ assert_array_cycle([{ "this has" => 'some "s that need to be escaped"' }])
end
def test_array_strings_with_commas
- assert_array_cycle([{'this,has' => 'many,values'}])
+ assert_array_cycle([{ "this,has" => "many,values" }])
end
def test_array_strings_with_array_delimiters
- assert_array_cycle(['{' => '}'])
+ assert_array_cycle(["{" => "}"])
end
def test_array_strings_with_null_strings
- assert_array_cycle([{'NULL' => 'NULL'}])
+ assert_array_cycle([{ "NULL" => "NULL" }])
end
def test_contains_nils
- assert_array_cycle([{'NULL' => nil}])
+ assert_array_cycle([{ "NULL" => nil }])
end
def test_select_multikey
@connection.execute "insert into hstores (tags) VALUES ('1=>2,2=>3')"
x = Hstore.first
- assert_equal({'1' => '2', '2' => '3'}, x.tags)
+ assert_equal({ "1" => "2", "2" => "3" }, x.tags)
end
def test_create
- assert_cycle('a' => 'b', '1' => '2')
+ assert_cycle("a" => "b", "1" => "2")
end
def test_nil
- assert_cycle('a' => nil)
+ assert_cycle("a" => nil)
end
def test_quotes
- assert_cycle('a' => 'b"ar', '1"foo' => '2')
+ assert_cycle("a" => 'b"ar', '1"foo' => "2")
end
def test_whitespace
- assert_cycle('a b' => 'b ar', '1"foo' => '2')
+ assert_cycle("a b" => "b ar", '1"foo' => "2")
end
def test_backslash
- assert_cycle('a\\b' => 'b\\ar', '1"foo' => '2')
+ assert_cycle('a\\b' => 'b\\ar', '1"foo' => "2")
end
def test_comma
- assert_cycle('a, b' => 'bar', '1"foo' => '2')
+ assert_cycle("a, b" => "bar", '1"foo' => "2")
end
def test_arrow
- assert_cycle('a=>b' => 'bar', '1"foo' => '2')
+ assert_cycle("a=>b" => "bar", '1"foo' => "2")
end
def test_quoting_special_characters
- assert_cycle('ca' => 'cà', 'ac' => 'àc')
+ assert_cycle("ca" => "cà", "ac" => "àc")
end
def test_multiline
@@ -300,20 +306,20 @@ if ActiveRecord::Base.connection.supports_extensions?
end
def test_hstore_with_serialized_attributes
- HstoreWithSerialize.create! tags: TagCollection.new({"one" => "two"})
+ HstoreWithSerialize.create! tags: TagCollection.new("one" => "two")
record = HstoreWithSerialize.first
assert_instance_of TagCollection, record.tags
- assert_equal({"one" => "two"}, record.tags.to_hash)
+ assert_equal({ "one" => "two" }, record.tags.to_hash)
record.tags = TagCollection.new("three" => "four")
record.save!
- assert_equal({"three" => "four"}, HstoreWithSerialize.first.tags.to_hash)
+ assert_equal({ "three" => "four" }, HstoreWithSerialize.first.tags.to_hash)
end
def test_clone_hstore_with_serialized_attributes
- HstoreWithSerialize.create! tags: TagCollection.new({"one" => "two"})
+ HstoreWithSerialize.create! tags: TagCollection.new("one" => "two")
record = HstoreWithSerialize.first
dupe = record.dup
- assert_equal({"one" => "two"}, dupe.tags.to_hash)
+ assert_equal({ "one" => "two" }, dupe.tags.to_hash)
end
def test_schema_dump_with_shorthand
@@ -321,33 +327,37 @@ if ActiveRecord::Base.connection.supports_extensions?
assert_match %r[t\.hstore "tags",\s+default: {}], output
end
- private
- def assert_array_cycle(array)
- # test creation
- x = Hstore.create!(payload: array)
- x.reload
- assert_equal(array, x.payload)
-
- # test updating
- x = Hstore.create!(payload: [])
- x.payload = array
- x.save!
- x.reload
- assert_equal(array, x.payload)
+ def test_supports_to_unsafe_h_values
+ assert_equal("\"hi\"=>\"hi\"", @type.serialize(FakeParameters.new))
end
- def assert_cycle(hash)
- # test creation
- x = Hstore.create!(:tags => hash)
- x.reload
- assert_equal(hash, x.tags)
+ private
+ def assert_array_cycle(array)
+ # test creation
+ x = Hstore.create!(payload: array)
+ x.reload
+ assert_equal(array, x.payload)
+
+ # test updating
+ x = Hstore.create!(payload: [])
+ x.payload = array
+ x.save!
+ x.reload
+ assert_equal(array, x.payload)
+ end
- # test updating
- x = Hstore.create!(:tags => {})
- x.tags = hash
- x.save!
- x.reload
- assert_equal(hash, x.tags)
- end
+ def assert_cycle(hash)
+ # test creation
+ x = Hstore.create!(tags: hash)
+ x.reload
+ assert_equal(hash, x.tags)
+
+ # test updating
+ x = Hstore.create!(tags: {})
+ x.tags = hash
+ x.save!
+ x.reload
+ assert_equal(hash, x.tags)
+ end
end
end
diff --git a/activerecord/test/cases/adapters/postgresql/infinity_test.rb b/activerecord/test/cases/adapters/postgresql/infinity_test.rb
index bfda933fa4..19b00258b6 100644
--- a/activerecord/test/cases/adapters/postgresql/infinity_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/infinity_test.rb
@@ -15,7 +15,7 @@ class PostgresqlInfinityTest < ActiveRecord::PostgreSQLTestCase
end
teardown do
- @connection.drop_table 'postgresql_infinities', if_exists: true
+ @connection.drop_table "postgresql_infinities", if_exists: true
end
test "type casting infinity on a float column" do
@@ -25,11 +25,11 @@ class PostgresqlInfinityTest < ActiveRecord::PostgreSQLTestCase
end
test "type casting string on a float column" do
- record = PostgresqlInfinity.new(float: 'Infinity')
+ record = PostgresqlInfinity.new(float: "Infinity")
assert_equal Float::INFINITY, record.float
- record = PostgresqlInfinity.new(float: '-Infinity')
+ record = PostgresqlInfinity.new(float: "-Infinity")
assert_equal(-Float::INFINITY, record.float)
- record = PostgresqlInfinity.new(float: 'NaN')
+ record = PostgresqlInfinity.new(float: "NaN")
assert_send [record.float, :nan?]
end
diff --git a/activerecord/test/cases/adapters/postgresql/json_test.rb b/activerecord/test/cases/adapters/postgresql/json_test.rb
index 663de680b5..93558ac4d2 100644
--- a/activerecord/test/cases/adapters/postgresql/json_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/json_test.rb
@@ -1,11 +1,11 @@
require "cases/helper"
-require 'support/schema_dumping_helper'
+require "support/schema_dumping_helper"
module PostgresqlJSONSharedTestCases
include SchemaDumpingHelper
class JsonDataType < ActiveRecord::Base
- self.table_name = 'json_data_type'
+ self.table_name = "json_data_type"
store_accessor :settings, :resolution
end
@@ -13,9 +13,9 @@ module PostgresqlJSONSharedTestCases
def setup
@connection = ActiveRecord::Base.connection
begin
- @connection.create_table('json_data_type') do |t|
- t.public_send column_type, 'payload', default: {} # t.json 'payload', default: {}
- t.public_send column_type, 'settings' # t.json 'settings'
+ @connection.create_table("json_data_type") do |t|
+ t.public_send column_type, "payload", default: {} # t.json 'payload', default: {}
+ t.public_send column_type, "settings" # t.json 'settings'
end
rescue ActiveRecord::StatementInvalid
skip "do not test on PostgreSQL without #{column_type} type."
@@ -38,22 +38,22 @@ module PostgresqlJSONSharedTestCases
end
def test_default
- @connection.add_column 'json_data_type', 'permissions', column_type, default: {"users": "read", "posts": ["read", "write"]}
+ @connection.add_column "json_data_type", "permissions", column_type, default: { "users": "read", "posts": ["read", "write"] }
JsonDataType.reset_column_information
- assert_equal({"users"=>"read", "posts"=>["read", "write"]}, JsonDataType.column_defaults['permissions'])
- assert_equal({"users"=>"read", "posts"=>["read", "write"]}, JsonDataType.new.permissions)
+ assert_equal({ "users" => "read", "posts" => ["read", "write"] }, JsonDataType.column_defaults["permissions"])
+ assert_equal({ "users" => "read", "posts" => ["read", "write"] }, JsonDataType.new.permissions)
ensure
JsonDataType.reset_column_information
end
def test_change_table_supports_json
@connection.transaction do
- @connection.change_table('json_data_type') do |t|
- t.public_send column_type, 'users', default: '{}' # t.json 'users', default: '{}'
+ @connection.change_table("json_data_type") do |t|
+ t.public_send column_type, "users", default: "{}" # t.json 'users', default: '{}'
end
JsonDataType.reset_column_information
- column = JsonDataType.columns_hash['users']
+ column = JsonDataType.columns_hash["users"]
assert_equal column_type, column.type
raise ActiveRecord::Rollback # reset the schema change
@@ -68,11 +68,11 @@ module PostgresqlJSONSharedTestCases
end
def test_cast_value_on_write
- x = JsonDataType.new payload: {"string" => "foo", :symbol => :bar}
- assert_equal({"string" => "foo", :symbol => :bar}, x.payload_before_type_cast)
- assert_equal({"string" => "foo", "symbol" => "bar"}, x.payload)
+ x = JsonDataType.new payload: { "string" => "foo", :symbol => :bar }
+ assert_equal({ "string" => "foo", :symbol => :bar }, x.payload_before_type_cast)
+ assert_equal({ "string" => "foo", "symbol" => "bar" }, x.payload)
x.save
- assert_equal({"string" => "foo", "symbol" => "bar"}, x.reload.payload)
+ assert_equal({ "string" => "foo", "symbol" => "bar" }, x.reload.payload)
end
def test_type_cast_json
@@ -80,49 +80,65 @@ module PostgresqlJSONSharedTestCases
data = "{\"a_key\":\"a_value\"}"
hash = type.deserialize(data)
- assert_equal({'a_key' => 'a_value'}, hash)
- assert_equal({'a_key' => 'a_value'}, type.deserialize(data))
+ assert_equal({ "a_key" => "a_value" }, hash)
+ assert_equal({ "a_key" => "a_value" }, type.deserialize(data))
assert_equal({}, type.deserialize("{}"))
- assert_equal({'key'=>nil}, type.deserialize('{"key": null}'))
- assert_equal({'c'=>'}','"a"'=>'b "a b'}, type.deserialize(%q({"c":"}", "\"a\"":"b \"a b"})))
+ assert_equal({ "key" => nil }, type.deserialize('{"key": null}'))
+ assert_equal({ "c" => "}", '"a"' => 'b "a b' }, type.deserialize(%q({"c":"}", "\"a\"":"b \"a b"})))
end
def test_rewrite
@connection.execute "insert into json_data_type (payload) VALUES ('{\"k\":\"v\"}')"
x = JsonDataType.first
- x.payload = { '"a\'' => 'b' }
+ x.payload = { '"a\'' => "b" }
assert x.save!
end
def test_select
@connection.execute "insert into json_data_type (payload) VALUES ('{\"k\":\"v\"}')"
x = JsonDataType.first
- assert_equal({'k' => 'v'}, x.payload)
+ assert_equal({ "k" => "v" }, x.payload)
end
def test_select_multikey
@connection.execute %q|insert into json_data_type (payload) VALUES ('{"k1":"v1", "k2":"v2", "k3":[1,2,3]}')|
x = JsonDataType.first
- assert_equal({'k1' => 'v1', 'k2' => 'v2', 'k3' => [1,2,3]}, x.payload)
+ assert_equal({ "k1" => "v1", "k2" => "v2", "k3" => [1, 2, 3] }, x.payload)
end
def test_null_json
- @connection.execute %q|insert into json_data_type (payload) VALUES(null)|
+ @connection.execute "insert into json_data_type (payload) VALUES(null)"
x = JsonDataType.first
- assert_equal(nil, x.payload)
+ assert_nil(x.payload)
+ end
+
+ def test_select_nil_json_after_create
+ json = JsonDataType.create(payload: nil)
+ x = JsonDataType.where(payload: nil).first
+ assert_equal(json, x)
+ end
+
+ def test_select_nil_json_after_update
+ json = JsonDataType.create(payload: "foo")
+ x = JsonDataType.where(payload: nil).first
+ assert_nil(x)
+
+ json.update_attributes payload: nil
+ x = JsonDataType.where(payload: nil).first
+ assert_equal(json.reload, x)
end
def test_select_array_json_value
@connection.execute %q|insert into json_data_type (payload) VALUES ('["v0",{"k1":"v1"}]')|
x = JsonDataType.first
- assert_equal(['v0', {'k1' => 'v1'}], x.payload)
+ assert_equal(["v0", { "k1" => "v1" }], x.payload)
end
def test_rewrite_array_json_value
@connection.execute %q|insert into json_data_type (payload) VALUES ('["v0",{"k1":"v1"}]')|
x = JsonDataType.first
- x.payload = ['v1', {'k2' => 'v2'}, 'v3']
+ x.payload = ["v1", { "k2" => "v2" }, "v3"]
assert x.save!
end
@@ -161,20 +177,20 @@ module PostgresqlJSONSharedTestCases
json = JsonDataType.new
assert_not json.changed?
- json.payload = { 'one' => 'two' }
+ json.payload = { "one" => "two" }
assert json.changed?
assert json.payload_changed?
json.save!
assert_not json.changed?
- json.payload['three'] = 'four'
+ json.payload["three"] = "four"
assert json.payload_changed?
json.save!
json.reload
- assert_equal({ 'one' => 'two', 'three' => 'four' }, json.payload)
+ assert_equal({ "one" => "two", "three" => "four" }, json.payload)
assert_not json.changed?
end
diff --git a/activerecord/test/cases/adapters/postgresql/legacy_migration_test.rb b/activerecord/test/cases/adapters/postgresql/legacy_migration_test.rb
new file mode 100644
index 0000000000..082fe95053
--- /dev/null
+++ b/activerecord/test/cases/adapters/postgresql/legacy_migration_test.rb
@@ -0,0 +1,54 @@
+require "cases/helper"
+
+class PostgresqlLegacyMigrationTest < ActiveRecord::PostgreSQLTestCase
+ class GenerateTableWithoutBigserial < ActiveRecord::Migration[5.0]
+ def change
+ create_table :legacy_integer_pk do |table|
+ table.string :foo
+ end
+
+ create_table :override_pk, id: :bigint do |table|
+ table.string :bar
+ end
+ end
+ end
+
+ def setup
+ super
+
+ @migration_verbose_old = ActiveRecord::Migration.verbose
+ ActiveRecord::Migration.verbose = false
+
+ migrations = [GenerateTableWithoutBigserial.new(nil, 1)]
+ ActiveRecord::Migrator.new(:up, migrations).migrate
+ end
+
+ def teardown
+ ActiveRecord::Migration.verbose = @migration_verbose_old
+
+ super
+ end
+
+ def test_create_table_uses_serial_as_pkey_by_default
+ col = column(:legacy_integer_pk, :id)
+ assert_equal "integer", sql_type_for(col)
+ assert col.serial?
+ end
+
+ def test_create_tables_respects_pk_column_type_override
+ col = column(:override_pk, :id)
+ assert_equal "bigint", sql_type_for(col)
+ end
+
+ private
+
+ def column(table_name, column_name)
+ ActiveRecord::Base.connection.
+ columns(table_name.to_s).
+ detect { |c| c.name == column_name.to_s }
+ end
+
+ def sql_type_for(col)
+ col && col.sql_type
+ end
+end
diff --git a/activerecord/test/cases/adapters/postgresql/ltree_test.rb b/activerecord/test/cases/adapters/postgresql/ltree_test.rb
index 56516c82b4..2b5ac1cac6 100644
--- a/activerecord/test/cases/adapters/postgresql/ltree_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/ltree_test.rb
@@ -1,20 +1,20 @@
require "cases/helper"
-require 'support/schema_dumping_helper'
+require "support/schema_dumping_helper"
class PostgresqlLtreeTest < ActiveRecord::PostgreSQLTestCase
include SchemaDumpingHelper
class Ltree < ActiveRecord::Base
- self.table_name = 'ltrees'
+ self.table_name = "ltrees"
end
def setup
@connection = ActiveRecord::Base.connection
- enable_extension!('ltree', @connection)
+ enable_extension!("ltree", @connection)
@connection.transaction do
- @connection.create_table('ltrees') do |t|
- t.ltree 'path'
+ @connection.create_table("ltrees") do |t|
+ t.ltree "path"
end
end
rescue ActiveRecord::StatementInvalid
@@ -22,28 +22,28 @@ class PostgresqlLtreeTest < ActiveRecord::PostgreSQLTestCase
end
teardown do
- @connection.drop_table 'ltrees', if_exists: true
+ @connection.drop_table "ltrees", if_exists: true
end
def test_column
- column = Ltree.columns_hash['path']
+ column = Ltree.columns_hash["path"]
assert_equal :ltree, column.type
assert_equal "ltree", column.sql_type
assert_not column.array?
- type = Ltree.type_for_attribute('path')
+ type = Ltree.type_for_attribute("path")
assert_not type.binary?
end
def test_write
- ltree = Ltree.new(path: '1.2.3.4')
+ ltree = Ltree.new(path: "1.2.3.4")
assert ltree.save!
end
def test_select
@connection.execute "insert into ltrees (path) VALUES ('1.2.3')"
ltree = Ltree.first
- assert_equal '1.2.3', ltree.path
+ assert_equal "1.2.3", ltree.path
end
def test_schema_dump_with_shorthand
diff --git a/activerecord/test/cases/adapters/postgresql/money_test.rb b/activerecord/test/cases/adapters/postgresql/money_test.rb
index c031178479..1b5d8362af 100644
--- a/activerecord/test/cases/adapters/postgresql/money_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/money_test.rb
@@ -1,5 +1,5 @@
require "cases/helper"
-require 'support/schema_dumping_helper'
+require "support/schema_dumping_helper"
class PostgresqlMoneyTest < ActiveRecord::PostgreSQLTestCase
include SchemaDumpingHelper
@@ -9,14 +9,14 @@ class PostgresqlMoneyTest < ActiveRecord::PostgreSQLTestCase
setup do
@connection = ActiveRecord::Base.connection
@connection.execute("set lc_monetary = 'C'")
- @connection.create_table('postgresql_moneys', force: true) do |t|
+ @connection.create_table("postgresql_moneys", force: true) do |t|
t.money "wealth"
t.money "depth", default: "150.55"
end
end
teardown do
- @connection.drop_table 'postgresql_moneys', if_exists: true
+ @connection.drop_table "postgresql_moneys", if_exists: true
end
def test_column
@@ -31,7 +31,7 @@ class PostgresqlMoneyTest < ActiveRecord::PostgreSQLTestCase
end
def test_default
- assert_equal BigDecimal.new("150.55"), PostgresqlMoney.column_defaults['depth']
+ assert_equal BigDecimal.new("150.55"), PostgresqlMoney.column_defaults["depth"]
assert_equal BigDecimal.new("150.55"), PostgresqlMoney.new.depth
end
@@ -46,7 +46,7 @@ class PostgresqlMoneyTest < ActiveRecord::PostgreSQLTestCase
end
def test_money_type_cast
- type = PostgresqlMoney.type_for_attribute('wealth')
+ type = PostgresqlMoney.type_for_attribute("wealth")
assert_equal(12345678.12, type.cast("$12,345,678.12"))
assert_equal(12345678.12, type.cast("$12.345.678,12"))
assert_equal(-1.15, type.cast("-$1.15"))
@@ -63,7 +63,7 @@ class PostgresqlMoneyTest < ActiveRecord::PostgreSQLTestCase
money = PostgresqlMoney.create(wealth: "987.65")
assert_equal 987.65, money.wealth
- new_value = BigDecimal.new('123.45')
+ new_value = BigDecimal.new("123.45")
money.wealth = new_value
money.save!
money.reload
@@ -80,7 +80,7 @@ class PostgresqlMoneyTest < ActiveRecord::PostgreSQLTestCase
def test_update_all_with_money_big_decimal
money = PostgresqlMoney.create!
- PostgresqlMoney.update_all(wealth: '123.45'.to_d)
+ PostgresqlMoney.update_all(wealth: "123.45".to_d)
money.reload
assert_equal 123.45, money.wealth
diff --git a/activerecord/test/cases/adapters/postgresql/network_test.rb b/activerecord/test/cases/adapters/postgresql/network_test.rb
index fe6ee4e2d9..a33b0ef8a7 100644
--- a/activerecord/test/cases/adapters/postgresql/network_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/network_test.rb
@@ -1,5 +1,5 @@
require "cases/helper"
-require 'support/schema_dumping_helper'
+require "support/schema_dumping_helper"
class PostgresqlNetworkTest < ActiveRecord::PostgreSQLTestCase
include SchemaDumpingHelper
@@ -7,15 +7,15 @@ class PostgresqlNetworkTest < ActiveRecord::PostgreSQLTestCase
setup do
@connection = ActiveRecord::Base.connection
- @connection.create_table('postgresql_network_addresses', force: true) do |t|
- t.inet 'inet_address', default: "192.168.1.1"
- t.cidr 'cidr_address', default: "192.168.1.0/24"
- t.macaddr 'mac_address', default: "ff:ff:ff:ff:ff:ff"
+ @connection.create_table("postgresql_network_addresses", force: true) do |t|
+ t.inet "inet_address", default: "192.168.1.1"
+ t.cidr "cidr_address", default: "192.168.1.0/24"
+ t.macaddr "mac_address", default: "ff:ff:ff:ff:ff:ff"
end
end
teardown do
- @connection.drop_table 'postgresql_network_addresses', if_exists: true
+ @connection.drop_table "postgresql_network_addresses", if_exists: true
end
def test_cidr_column
@@ -49,33 +49,33 @@ class PostgresqlNetworkTest < ActiveRecord::PostgreSQLTestCase
end
def test_network_types
- PostgresqlNetworkAddress.create(cidr_address: '192.168.0.0/24',
- inet_address: '172.16.1.254/32',
- mac_address: '01:23:45:67:89:0a')
+ PostgresqlNetworkAddress.create(cidr_address: "192.168.0.0/24",
+ inet_address: "172.16.1.254/32",
+ mac_address: "01:23:45:67:89:0a")
address = PostgresqlNetworkAddress.first
- assert_equal IPAddr.new('192.168.0.0/24'), address.cidr_address
- assert_equal IPAddr.new('172.16.1.254'), address.inet_address
- assert_equal '01:23:45:67:89:0a', address.mac_address
+ assert_equal IPAddr.new("192.168.0.0/24"), address.cidr_address
+ assert_equal IPAddr.new("172.16.1.254"), address.inet_address
+ assert_equal "01:23:45:67:89:0a", address.mac_address
- address.cidr_address = '10.1.2.3/32'
- address.inet_address = '10.0.0.0/8'
- address.mac_address = 'bc:de:f0:12:34:56'
+ address.cidr_address = "10.1.2.3/32"
+ address.inet_address = "10.0.0.0/8"
+ address.mac_address = "bc:de:f0:12:34:56"
address.save!
assert address.reload
- assert_equal IPAddr.new('10.1.2.3/32'), address.cidr_address
- assert_equal IPAddr.new('10.0.0.0/8'), address.inet_address
- assert_equal 'bc:de:f0:12:34:56', address.mac_address
+ assert_equal IPAddr.new("10.1.2.3/32"), address.cidr_address
+ assert_equal IPAddr.new("10.0.0.0/8"), address.inet_address
+ assert_equal "bc:de:f0:12:34:56", address.mac_address
end
def test_invalid_network_address
- invalid_address = PostgresqlNetworkAddress.new(cidr_address: 'invalid addr',
- inet_address: 'invalid addr')
+ invalid_address = PostgresqlNetworkAddress.new(cidr_address: "invalid addr",
+ inet_address: "invalid addr")
assert_nil invalid_address.cidr_address
assert_nil invalid_address.inet_address
- assert_equal 'invalid addr', invalid_address.cidr_address_before_type_cast
- assert_equal 'invalid addr', invalid_address.inet_address_before_type_cast
+ assert_equal "invalid addr", invalid_address.cidr_address_before_type_cast
+ assert_equal "invalid addr", invalid_address.inet_address_before_type_cast
assert invalid_address.save
invalid_address.reload
diff --git a/activerecord/test/cases/adapters/postgresql/numbers_test.rb b/activerecord/test/cases/adapters/postgresql/numbers_test.rb
index ba7e7dc9a3..834354dcc9 100644
--- a/activerecord/test/cases/adapters/postgresql/numbers_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/numbers_test.rb
@@ -5,14 +5,14 @@ class PostgresqlNumberTest < ActiveRecord::PostgreSQLTestCase
setup do
@connection = ActiveRecord::Base.connection
- @connection.create_table('postgresql_numbers', force: true) do |t|
- t.column 'single', 'REAL'
- t.column 'double', 'DOUBLE PRECISION'
+ @connection.create_table("postgresql_numbers", force: true) do |t|
+ t.column "single", "REAL"
+ t.column "double", "DOUBLE PRECISION"
end
end
teardown do
- @connection.drop_table 'postgresql_numbers', if_exists: true
+ @connection.drop_table "postgresql_numbers", if_exists: true
end
def test_data_type
diff --git a/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb b/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb
index 9832df7839..e6af93a53e 100644
--- a/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb
@@ -1,6 +1,6 @@
require "cases/helper"
-require 'support/ddl_helper'
-require 'support/connection_helper'
+require "support/ddl_helper"
+require "support/connection_helper"
module ActiveRecord
module ConnectionAdapters
@@ -15,15 +15,15 @@ module ActiveRecord
def test_bad_connection
assert_raise ActiveRecord::NoDatabaseError do
- configuration = ActiveRecord::Base.configurations['arunit'].merge(database: 'should_not_exist-cinco-dog-db')
+ configuration = ActiveRecord::Base.configurations["arunit"].merge(database: "should_not_exist-cinco-dog-db")
connection = ActiveRecord::Base.postgresql_connection(configuration)
- connection.exec_query('SELECT 1')
+ connection.exec_query("SELECT 1")
end
end
def test_valid_column
with_example_table do
- column = @connection.columns('ex').find { |col| col.name == 'id' }
+ column = @connection.columns("ex").find { |col| col.name == "id" }
assert @connection.valid_type?(column.type)
end
end
@@ -34,144 +34,138 @@ module ActiveRecord
def test_primary_key
with_example_table do
- assert_equal 'id', @connection.primary_key('ex')
+ assert_equal "id", @connection.primary_key("ex")
end
end
def test_primary_key_works_tables_containing_capital_letters
- assert_equal 'id', @connection.primary_key('CamelCase')
+ assert_equal "id", @connection.primary_key("CamelCase")
end
def test_non_standard_primary_key
- with_example_table 'data character varying(255) primary key' do
- assert_equal 'data', @connection.primary_key('ex')
+ with_example_table "data character varying(255) primary key" do
+ assert_equal "data", @connection.primary_key("ex")
end
end
def test_primary_key_returns_nil_for_no_pk
- with_example_table 'id integer' do
- assert_nil @connection.primary_key('ex')
+ with_example_table "id integer" do
+ assert_nil @connection.primary_key("ex")
end
end
def test_primary_key_raises_error_if_table_not_found
assert_raises(ActiveRecord::StatementInvalid) do
- @connection.primary_key('unobtainium')
+ @connection.primary_key("unobtainium")
end
end
def test_exec_insert_with_returning_disabled
connection = connection_without_insert_returning
- result = connection.exec_insert("insert into postgresql_partitioned_table_parent (number) VALUES (1)", nil, [], 'id', 'postgresql_partitioned_table_parent_id_seq')
- expect = connection.query('select max(id) from postgresql_partitioned_table_parent').first.first
+ result = connection.exec_insert("insert into postgresql_partitioned_table_parent (number) VALUES (1)", nil, [], "id", "postgresql_partitioned_table_parent_id_seq")
+ expect = connection.query("select max(id) from postgresql_partitioned_table_parent").first.first
assert_equal expect.to_i, result.rows.first.first
end
def test_exec_insert_with_returning_disabled_and_no_sequence_name_given
connection = connection_without_insert_returning
- result = connection.exec_insert("insert into postgresql_partitioned_table_parent (number) VALUES (1)", nil, [], 'id')
- expect = connection.query('select max(id) from postgresql_partitioned_table_parent').first.first
+ result = connection.exec_insert("insert into postgresql_partitioned_table_parent (number) VALUES (1)", nil, [], "id")
+ expect = connection.query("select max(id) from postgresql_partitioned_table_parent").first.first
assert_equal expect.to_i, result.rows.first.first
end
def test_exec_insert_default_values_with_returning_disabled_and_no_sequence_name_given
connection = connection_without_insert_returning
- result = connection.exec_insert("insert into postgresql_partitioned_table_parent DEFAULT VALUES", nil, [], 'id')
- expect = connection.query('select max(id) from postgresql_partitioned_table_parent').first.first
+ result = connection.exec_insert("insert into postgresql_partitioned_table_parent DEFAULT VALUES", nil, [], "id")
+ expect = connection.query("select max(id) from postgresql_partitioned_table_parent").first.first
assert_equal expect.to_i, result.rows.first.first
end
def test_exec_insert_default_values_quoted_schema_with_returning_disabled_and_no_sequence_name_given
connection = connection_without_insert_returning
- result = connection.exec_insert('insert into "public"."postgresql_partitioned_table_parent" DEFAULT VALUES', nil, [], 'id')
- expect = connection.query('select max(id) from postgresql_partitioned_table_parent').first.first
+ result = connection.exec_insert('insert into "public"."postgresql_partitioned_table_parent" DEFAULT VALUES', nil, [], "id")
+ expect = connection.query("select max(id) from postgresql_partitioned_table_parent").first.first
assert_equal expect.to_i, result.rows.first.first
end
- def test_sql_for_insert_with_returning_disabled
- connection = connection_without_insert_returning
- sql, binds = connection.sql_for_insert('sql', nil, nil, nil, 'binds')
- assert_equal ['sql', 'binds'], [sql, binds]
- end
-
def test_serial_sequence
- assert_equal 'public.accounts_id_seq',
- @connection.serial_sequence('accounts', 'id')
+ assert_equal "public.accounts_id_seq",
+ @connection.serial_sequence("accounts", "id")
assert_raises(ActiveRecord::StatementInvalid) do
- @connection.serial_sequence('zomg', 'id')
+ @connection.serial_sequence("zomg", "id")
end
end
def test_default_sequence_name
- assert_equal 'public.accounts_id_seq',
- @connection.default_sequence_name('accounts', 'id')
+ assert_equal "public.accounts_id_seq",
+ @connection.default_sequence_name("accounts", "id")
- assert_equal 'public.accounts_id_seq',
- @connection.default_sequence_name('accounts')
+ assert_equal "public.accounts_id_seq",
+ @connection.default_sequence_name("accounts")
end
def test_default_sequence_name_bad_table
- assert_equal 'zomg_id_seq',
- @connection.default_sequence_name('zomg', 'id')
+ assert_equal "zomg_id_seq",
+ @connection.default_sequence_name("zomg", "id")
- assert_equal 'zomg_id_seq',
- @connection.default_sequence_name('zomg')
+ assert_equal "zomg_id_seq",
+ @connection.default_sequence_name("zomg")
end
def test_pk_and_sequence_for
with_example_table do
- pk, seq = @connection.pk_and_sequence_for('ex')
- assert_equal 'id', pk
- assert_equal @connection.default_sequence_name('ex', 'id'), seq.to_s
+ pk, seq = @connection.pk_and_sequence_for("ex")
+ assert_equal "id", pk
+ assert_equal @connection.default_sequence_name("ex", "id"), seq.to_s
end
end
def test_pk_and_sequence_for_with_non_standard_primary_key
- with_example_table 'code serial primary key' do
- pk, seq = @connection.pk_and_sequence_for('ex')
- assert_equal 'code', pk
- assert_equal @connection.default_sequence_name('ex', 'code'), seq.to_s
+ with_example_table "code serial primary key" do
+ pk, seq = @connection.pk_and_sequence_for("ex")
+ assert_equal "code", pk
+ assert_equal @connection.default_sequence_name("ex", "code"), seq.to_s
end
end
def test_pk_and_sequence_for_returns_nil_if_no_seq
- with_example_table 'id integer primary key' do
- assert_nil @connection.pk_and_sequence_for('ex')
+ with_example_table "id integer primary key" do
+ assert_nil @connection.pk_and_sequence_for("ex")
end
end
def test_pk_and_sequence_for_returns_nil_if_no_pk
- with_example_table 'id integer' do
- assert_nil @connection.pk_and_sequence_for('ex')
+ with_example_table "id integer" do
+ assert_nil @connection.pk_and_sequence_for("ex")
end
end
def test_pk_and_sequence_for_returns_nil_if_table_not_found
- assert_nil @connection.pk_and_sequence_for('unobtainium')
+ assert_nil @connection.pk_and_sequence_for("unobtainium")
end
def test_pk_and_sequence_for_with_collision_pg_class_oid
- @connection.exec_query('create table ex(id serial primary key)')
- @connection.exec_query('create table ex2(id serial primary key)')
+ @connection.exec_query("create table ex(id serial primary key)")
+ @connection.exec_query("create table ex2(id serial primary key)")
correct_depend_record = [
"'pg_class'::regclass",
"'ex_id_seq'::regclass",
- '0',
+ "0",
"'pg_class'::regclass",
"'ex'::regclass",
- '1',
+ "1",
"'a'"
]
collision_depend_record = [
"'pg_attrdef'::regclass",
"'ex2_id_seq'::regclass",
- '0',
+ "0",
"'pg_class'::regclass",
"'ex'::regclass",
- '1',
+ "1",
"'a'"
]
@@ -185,15 +179,15 @@ module ActiveRecord
"INSERT INTO pg_depend VALUES(#{correct_depend_record.join(',')})"
)
- seq = @connection.pk_and_sequence_for('ex').last
+ seq = @connection.pk_and_sequence_for("ex").last
assert_equal PostgreSQL::Name.new("public", "ex_id_seq"), seq
@connection.exec_query(
"DELETE FROM pg_depend WHERE objid = 'ex2_id_seq'::regclass AND refobjid = 'ex'::regclass AND deptype = 'a'"
)
ensure
- @connection.drop_table 'ex', if_exists: true
- @connection.drop_table 'ex2', if_exists: true
+ @connection.drop_table "ex", if_exists: true
+ @connection.drop_table "ex2", if_exists: true
end
def test_table_alias_length
@@ -204,74 +198,74 @@ module ActiveRecord
def test_exec_no_binds
with_example_table do
- result = @connection.exec_query('SELECT id, data FROM ex')
+ result = @connection.exec_query("SELECT id, data FROM ex")
assert_equal 0, result.rows.length
assert_equal 2, result.columns.length
assert_equal %w{ id data }, result.columns
- string = @connection.quote('foo')
+ string = @connection.quote("foo")
@connection.exec_query("INSERT INTO ex (id, data) VALUES (1, #{string})")
- result = @connection.exec_query('SELECT id, data FROM ex')
+ result = @connection.exec_query("SELECT id, data FROM ex")
assert_equal 1, result.rows.length
assert_equal 2, result.columns.length
- assert_equal [[1, 'foo']], result.rows
+ assert_equal [[1, "foo"]], result.rows
end
end
if ActiveRecord::Base.connection.prepared_statements
def test_exec_with_binds
with_example_table do
- string = @connection.quote('foo')
+ string = @connection.quote("foo")
@connection.exec_query("INSERT INTO ex (id, data) VALUES (1, #{string})")
bind = Relation::QueryAttribute.new("id", 1, Type::Value.new)
- result = @connection.exec_query('SELECT id, data FROM ex WHERE id = $1', nil, [bind])
+ result = @connection.exec_query("SELECT id, data FROM ex WHERE id = $1", nil, [bind])
assert_equal 1, result.rows.length
assert_equal 2, result.columns.length
- assert_equal [[1, 'foo']], result.rows
+ assert_equal [[1, "foo"]], result.rows
end
end
def test_exec_typecasts_bind_vals
with_example_table do
- string = @connection.quote('foo')
+ string = @connection.quote("foo")
@connection.exec_query("INSERT INTO ex (id, data) VALUES (1, #{string})")
bind = Relation::QueryAttribute.new("id", "1-fuu", Type::Integer.new)
- result = @connection.exec_query('SELECT id, data FROM ex WHERE id = $1', nil, [bind])
+ result = @connection.exec_query("SELECT id, data FROM ex WHERE id = $1", nil, [bind])
assert_equal 1, result.rows.length
assert_equal 2, result.columns.length
- assert_equal [[1, 'foo']], result.rows
+ assert_equal [[1, "foo"]], result.rows
end
end
end
def test_partial_index
with_example_table do
- @connection.add_index 'ex', %w{ id number }, :name => 'partial', :where => "number > 100"
- index = @connection.indexes('ex').find { |idx| idx.name == 'partial' }
+ @connection.add_index "ex", %w{ id number }, name: "partial", where: "number > 100"
+ index = @connection.indexes("ex").find { |idx| idx.name == "partial" }
assert_equal "(number > 100)", index.where
end
end
def test_expression_index
with_example_table do
- @connection.add_index 'ex', 'mod(id, 10), abs(number)', name: 'expression'
- index = @connection.indexes('ex').find { |idx| idx.name == 'expression' }
- assert_equal 'mod(id, 10), abs(number)', index.columns
+ @connection.add_index "ex", "mod(id, 10), abs(number)", name: "expression"
+ index = @connection.indexes("ex").find { |idx| idx.name == "expression" }
+ assert_equal "mod(id, 10), abs(number)", index.columns
end
end
def test_index_with_opclass
with_example_table do
- @connection.add_index 'ex', 'data varchar_pattern_ops', name: 'with_opclass'
- index = @connection.indexes('ex').find { |idx| idx.name == 'with_opclass' }
- assert_equal 'data varchar_pattern_ops', index.columns
+ @connection.add_index "ex", "data varchar_pattern_ops", name: "with_opclass"
+ index = @connection.indexes("ex").find { |idx| idx.name == "with_opclass" }
+ assert_equal "data varchar_pattern_ops", index.columns
end
end
@@ -292,8 +286,8 @@ module ActiveRecord
def test_columns_for_distinct_with_case
assert_equal(
- 'posts.id, CASE WHEN author.is_active THEN UPPER(author.name) ELSE UPPER(author.email) END AS alias_0',
- @connection.columns_for_distinct('posts.id',
+ "posts.id, CASE WHEN author.is_active THEN UPPER(author.name) ELSE UPPER(author.email) END AS alias_0",
+ @connection.columns_for_distinct("posts.id",
["CASE WHEN author.is_active THEN UPPER(author.name) ELSE UPPER(author.email) END"])
)
end
@@ -374,7 +368,7 @@ module ActiveRecord
def test_unparsed_defaults_are_at_least_set_when_saving
with_example_table "id SERIAL PRIMARY KEY, number INTEGER NOT NULL DEFAULT (4 + 4) * 2 / 4" do
number_klass = Class.new(ActiveRecord::Base) do
- self.table_name = 'ex'
+ self.table_name = "ex"
end
column = number_klass.columns_hash["number"]
assert_nil column.default
@@ -390,13 +384,13 @@ module ActiveRecord
private
- def with_example_table(definition = 'id serial primary key, number integer, data character varying(255)', &block)
- super(@connection, 'ex', definition, &block)
- end
+ def with_example_table(definition = "id serial primary key, number integer, data character varying(255)", &block)
+ super(@connection, "ex", definition, &block)
+ end
- def connection_without_insert_returning
- ActiveRecord::Base.postgresql_connection(ActiveRecord::Base.configurations['arunit'].merge(:insert_returning => false))
- end
+ def connection_without_insert_returning
+ ActiveRecord::Base.postgresql_connection(ActiveRecord::Base.configurations["arunit"].merge(insert_returning: false))
+ end
end
end
end
diff --git a/activerecord/test/cases/adapters/postgresql/prepared_statements_disabled_test.rb b/activerecord/test/cases/adapters/postgresql/prepared_statements_disabled_test.rb
new file mode 100644
index 0000000000..8c62690866
--- /dev/null
+++ b/activerecord/test/cases/adapters/postgresql/prepared_statements_disabled_test.rb
@@ -0,0 +1,25 @@
+require "cases/helper"
+require "models/computer"
+require "models/developer"
+
+class PreparedStatementsDisabledTest < ActiveRecord::PostgreSQLTestCase
+ fixtures :developers
+
+ def setup
+ @conn = ActiveRecord::Base.establish_connection :arunit_without_prepared_statements
+ end
+
+ def teardown
+ @conn.release_connection
+ ActiveRecord::Base.establish_connection :arunit
+ end
+
+ def test_select_query_works_even_when_prepared_statements_are_disabled
+ assert_not Developer.connection.prepared_statements
+
+ david = developers(:david)
+
+ assert_equal david, Developer.where(name: "David").last # With Binds
+ assert_operator Developer.count, :>, 0 # Without Binds
+ end
+end
diff --git a/activerecord/test/cases/adapters/postgresql/prepared_statements_test.rb b/activerecord/test/cases/adapters/postgresql/prepared_statements_test.rb
deleted file mode 100644
index f1519db48b..0000000000
--- a/activerecord/test/cases/adapters/postgresql/prepared_statements_test.rb
+++ /dev/null
@@ -1,22 +0,0 @@
-require "cases/helper"
-require "models/developer"
-
-class PreparedStatementsTest < ActiveRecord::PostgreSQLTestCase
- fixtures :developers
-
- def setup
- @default_prepared_statements = Developer.connection_config[:prepared_statements]
- Developer.connection_config[:prepared_statements] = false
- end
-
- def teardown
- Developer.connection_config[:prepared_statements] = @default_prepared_statements
- end
-
- def nothing_raised_with_falsy_prepared_statements
- assert_nothing_raised do
- Developer.where(id: 1)
- end
- end
-
-end
diff --git a/activerecord/test/cases/adapters/postgresql/quoting_test.rb b/activerecord/test/cases/adapters/postgresql/quoting_test.rb
index 5e6f4dbbb8..141baffa5b 100644
--- a/activerecord/test/cases/adapters/postgresql/quoting_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/quoting_test.rb
@@ -1,5 +1,5 @@
require "cases/helper"
-require 'ipaddr'
+require "ipaddr"
module ActiveRecord
module ConnectionAdapters
@@ -10,20 +10,20 @@ module ActiveRecord
end
def test_type_cast_true
- assert_equal 't', @conn.type_cast(true)
+ assert_equal "t", @conn.type_cast(true)
end
def test_type_cast_false
- assert_equal 'f', @conn.type_cast(false)
+ assert_equal "f", @conn.type_cast(false)
end
def test_quote_float_nan
- nan = 0.0/0
+ nan = 0.0 / 0
assert_equal "'NaN'", @conn.quote(nan)
end
def test_quote_float_infinity
- infinity = 1.0/0
+ infinity = 1.0 / 0
assert_equal "'Infinity'", @conn.quote(infinity)
end
@@ -36,7 +36,7 @@ module ActiveRecord
def test_quote_bit_string
value = "'); SELECT * FROM users; /*\n01\n*/--"
type = OID::Bit.new
- assert_equal nil, @conn.quote(type.serialize(value))
+ assert_nil @conn.quote(type.serialize(value))
end
end
end
diff --git a/activerecord/test/cases/adapters/postgresql/range_test.rb b/activerecord/test/cases/adapters/postgresql/range_test.rb
index 0edfa4ed9d..f411884dfd 100644
--- a/activerecord/test/cases/adapters/postgresql/range_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/range_test.rb
@@ -1,5 +1,5 @@
require "cases/helper"
-require 'support/connection_helper'
+require "support/connection_helper"
if ActiveRecord::Base.connection.respond_to?(:supports_ranges?) && ActiveRecord::Base.connection.supports_ranges?
class PostgresqlRange < ActiveRecord::Base
@@ -23,7 +23,7 @@ if ActiveRecord::Base.connection.respond_to?(:supports_ranges?) && ActiveRecord:
);
_SQL
- @connection.create_table('postgresql_ranges') do |t|
+ @connection.create_table("postgresql_ranges") do |t|
t.daterange :date_range
t.numrange :num_range
t.tsrange :ts_range
@@ -32,7 +32,7 @@ _SQL
t.int8range :int8_range
end
- @connection.add_column 'postgresql_ranges', 'float_range', 'floatrange'
+ @connection.add_column "postgresql_ranges", "float_range", "floatrange"
end
PostgresqlRange.reset_column_information
rescue ActiveRecord::StatementInvalid
@@ -93,8 +93,8 @@ _SQL
end
teardown do
- @connection.drop_table 'postgresql_ranges', if_exists: true
- @connection.execute 'DROP TYPE IF EXISTS floatrange'
+ @connection.drop_table "postgresql_ranges", if_exists: true
+ @connection.execute "DROP TYPE IF EXISTS floatrange"
reset_connection
end
@@ -132,10 +132,10 @@ _SQL
end
def test_numrange_values
- assert_equal BigDecimal.new('0.1')..BigDecimal.new('0.2'), @first_range.num_range
- assert_equal BigDecimal.new('0.1')...BigDecimal.new('0.2'), @second_range.num_range
- assert_equal BigDecimal.new('0.1')...BigDecimal.new('Infinity'), @third_range.num_range
- assert_equal BigDecimal.new('-Infinity')...BigDecimal.new('Infinity'), @fourth_range.num_range
+ assert_equal BigDecimal.new("0.1")..BigDecimal.new("0.2"), @first_range.num_range
+ assert_equal BigDecimal.new("0.1")...BigDecimal.new("0.2"), @second_range.num_range
+ assert_equal BigDecimal.new("0.1")...BigDecimal.new("Infinity"), @third_range.num_range
+ assert_equal BigDecimal.new("-Infinity")...BigDecimal.new("Infinity"), @fourth_range.num_range
assert_nil @empty_range.num_range
end
@@ -148,8 +148,8 @@ _SQL
end
def test_tstzrange_values
- assert_equal Time.parse('2010-01-01 09:30:00 UTC')..Time.parse('2011-01-01 17:30:00 UTC'), @first_range.tstz_range
- assert_equal Time.parse('2010-01-01 09:30:00 UTC')...Time.parse('2011-01-01 17:30:00 UTC'), @second_range.tstz_range
+ assert_equal Time.parse("2010-01-01 09:30:00 UTC")..Time.parse("2011-01-01 17:30:00 UTC"), @first_range.tstz_range
+ assert_equal Time.parse("2010-01-01 09:30:00 UTC")...Time.parse("2011-01-01 17:30:00 UTC"), @second_range.tstz_range
assert_equal(-Float::INFINITY...Float::INFINITY, @fourth_range.tstz_range)
assert_nil @empty_range.tstz_range
end
@@ -183,17 +183,17 @@ _SQL
end
def test_create_tstzrange
- tstzrange = Time.parse('2010-01-01 14:30:00 +0100')...Time.parse('2011-02-02 14:30:00 CDT')
+ tstzrange = Time.parse("2010-01-01 14:30:00 +0100")...Time.parse("2011-02-02 14:30:00 CDT")
round_trip(@new_range, :tstz_range, tstzrange)
assert_equal @new_range.tstz_range, tstzrange
- assert_equal @new_range.tstz_range, Time.parse('2010-01-01 13:30:00 UTC')...Time.parse('2011-02-02 19:30:00 UTC')
+ assert_equal @new_range.tstz_range, Time.parse("2010-01-01 13:30:00 UTC")...Time.parse("2011-02-02 19:30:00 UTC")
end
def test_update_tstzrange
assert_equal_round_trip(@first_range, :tstz_range,
- Time.parse('2010-01-01 14:30:00 CDT')...Time.parse('2011-02-02 14:30:00 CET'))
+ Time.parse("2010-01-01 14:30:00 CDT")...Time.parse("2011-02-02 14:30:00 CET"))
assert_nil_round_trip(@first_range, :tstz_range,
- Time.parse('2010-01-01 14:30:00 +0100')...Time.parse('2010-01-01 13:30:00 +0000'))
+ Time.parse("2010-01-01 14:30:00 +0100")...Time.parse("2010-01-01 13:30:00 +0000"))
end
def test_create_tsrange
@@ -232,14 +232,14 @@ _SQL
def test_create_numrange
assert_equal_round_trip(@new_range, :num_range,
- BigDecimal.new('0.5')...BigDecimal.new('1'))
+ BigDecimal.new("0.5")...BigDecimal.new("1"))
end
def test_update_numrange
assert_equal_round_trip(@first_range, :num_range,
- BigDecimal.new('0.5')...BigDecimal.new('1'))
+ BigDecimal.new("0.5")...BigDecimal.new("1"))
assert_nil_round_trip(@first_range, :num_range,
- BigDecimal.new('0.5')...BigDecimal.new('0.5'))
+ BigDecimal.new("0.5")...BigDecimal.new("0.5"))
end
def test_create_daterange
@@ -282,6 +282,12 @@ _SQL
assert_raises(ArgumentError) { PostgresqlRange.create!(tstz_range: "(''2010-01-01 14:30:00+05'', ''2011-01-01 14:30:00-03'']") }
end
+ def test_where_by_attribute_with_range
+ range = 1..100
+ record = PostgresqlRange.create!(int4_range: range)
+ assert_equal record, PostgresqlRange.where(int4_range: range).take
+ end
+
def test_update_all_with_ranges
PostgresqlRange.create!
diff --git a/activerecord/test/cases/adapters/postgresql/referential_integrity_test.rb b/activerecord/test/cases/adapters/postgresql/referential_integrity_test.rb
index c895ab9db5..0ff04bfa27 100644
--- a/activerecord/test/cases/adapters/postgresql/referential_integrity_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/referential_integrity_test.rb
@@ -1,5 +1,5 @@
-require 'cases/helper'
-require 'support/connection_helper'
+require "cases/helper"
+require "support/connection_helper"
class PostgreSQLReferentialIntegrityTest < ActiveRecord::PostgreSQLTestCase
self.use_transactional_tests = false
@@ -14,7 +14,7 @@ class PostgreSQLReferentialIntegrityTest < ActiveRecord::PostgreSQLTestCase
def execute(sql)
if IS_REFERENTIAL_INTEGRITY_SQL.call(sql)
super "BROKEN;" rescue nil # put transaction in broken state
- raise ActiveRecord::StatementInvalid, 'PG::InsufficientPrivilege'
+ raise ActiveRecord::StatementInvalid, "PG::InsufficientPrivilege"
else
super
end
@@ -24,7 +24,7 @@ class PostgreSQLReferentialIntegrityTest < ActiveRecord::PostgreSQLTestCase
module ProgrammerMistake
def execute(sql)
if IS_REFERENTIAL_INTEGRITY_SQL.call(sql)
- raise ArgumentError, 'something is not right.'
+ raise ArgumentError, "something is not right."
else
super
end
@@ -48,10 +48,10 @@ class PostgreSQLReferentialIntegrityTest < ActiveRecord::PostgreSQLTestCase
warning = capture(:stderr) do
e = assert_raises(ActiveRecord::InvalidForeignKey) do
@connection.disable_referential_integrity do
- raise ActiveRecord::InvalidForeignKey, 'Should be re-raised'
+ raise ActiveRecord::InvalidForeignKey, "Should be re-raised"
end
end
- assert_equal 'Should be re-raised', e.message
+ assert_equal "Should be re-raised", e.message
end
assert_match (/WARNING: Rails was not able to disable referential integrity/), warning
assert_match (/cause: PG::InsufficientPrivilege/), warning
@@ -63,10 +63,10 @@ class PostgreSQLReferentialIntegrityTest < ActiveRecord::PostgreSQLTestCase
warning = capture(:stderr) do
e = assert_raises(ActiveRecord::StatementInvalid) do
@connection.disable_referential_integrity do
- raise ActiveRecord::StatementInvalid, 'Should be re-raised'
+ raise ActiveRecord::StatementInvalid, "Should be re-raised"
end
end
- assert_equal 'Should be re-raised', e.message
+ assert_equal "Should be re-raised", e.message
end
assert warning.blank?, "expected no warnings but got:\n#{warning}"
end
@@ -105,7 +105,7 @@ class PostgreSQLReferentialIntegrityTest < ActiveRecord::PostgreSQLTestCase
private
- def assert_transaction_is_not_broken
- assert_equal 1, @connection.select_value("SELECT 1")
- end
+ def assert_transaction_is_not_broken
+ assert_equal 1, @connection.select_value("SELECT 1")
+ end
end
diff --git a/activerecord/test/cases/adapters/postgresql/rename_table_test.rb b/activerecord/test/cases/adapters/postgresql/rename_table_test.rb
index bd64bae308..e9e7f717ac 100644
--- a/activerecord/test/cases/adapters/postgresql/rename_table_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/rename_table_test.rb
@@ -24,11 +24,11 @@ class PostgresqlRenameTableTest < ActiveRecord::PostgreSQLTestCase
private
- def num_indices_named(name)
- @connection.execute(<<-SQL).values.length
- SELECT 1 FROM "pg_index"
- JOIN "pg_class" ON "pg_index"."indexrelid" = "pg_class"."oid"
- WHERE "pg_class"."relname" = '#{name}'
- SQL
- end
+ def num_indices_named(name)
+ @connection.execute(<<-SQL).values.length
+ SELECT 1 FROM "pg_index"
+ JOIN "pg_class" ON "pg_index"."indexrelid" = "pg_class"."oid"
+ WHERE "pg_class"."relname" = '#{name}'
+ SQL
+ end
end
diff --git a/activerecord/test/cases/adapters/postgresql/schema_authorization_test.rb b/activerecord/test/cases/adapters/postgresql/schema_authorization_test.rb
index 285a92f60e..f6a07da85f 100644
--- a/activerecord/test/cases/adapters/postgresql/schema_authorization_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/schema_authorization_test.rb
@@ -6,12 +6,12 @@ end
class SchemaAuthorizationTest < ActiveRecord::PostgreSQLTestCase
self.use_transactional_tests = false
- TABLE_NAME = 'schema_things'
+ TABLE_NAME = "schema_things"
COLUMNS = [
- 'id serial primary key',
- 'name character varying(50)'
+ "id serial primary key",
+ "name character varying(50)"
]
- USERS = ['rails_pg_schema_user1', 'rails_pg_schema_user2']
+ USERS = ["rails_pg_schema_user1", "rails_pg_schema_user2"]
def setup
@connection = ActiveRecord::Base.connection
@@ -45,7 +45,7 @@ class SchemaAuthorizationTest < ActiveRecord::PostgreSQLTestCase
def test_session_auth=
assert_raise(ActiveRecord::StatementInvalid) do
- @connection.session_auth = 'DEFAULT'
+ @connection.session_auth = "DEFAULT"
@connection.execute "SELECT * FROM #{TABLE_NAME}"
end
end
@@ -68,7 +68,7 @@ class SchemaAuthorizationTest < ActiveRecord::PostgreSQLTestCase
USERS.each do |u|
@connection.clear_cache!
set_session_auth u
- assert_equal u, @connection.select_value("SELECT name FROM #{TABLE_NAME} WHERE id = $1", 'SQL', [bind_param(1)])
+ assert_equal u, @connection.select_value("SELECT name FROM #{TABLE_NAME} WHERE id = $1", "SQL", [bind_param(1)])
set_session_auth
end
end
@@ -90,9 +90,9 @@ class SchemaAuthorizationTest < ActiveRecord::PostgreSQLTestCase
assert_nothing_raised do
USERS.each do |u|
set_session_auth u
- st = SchemaThing.new :name => 'TEST1'
+ st = SchemaThing.new name: "TEST1"
st.save!
- st = SchemaThing.new :id => 5, :name => 'TEST2'
+ st = SchemaThing.new id: 5, name: "TEST2"
st.save!
set_session_auth
end
@@ -100,17 +100,17 @@ class SchemaAuthorizationTest < ActiveRecord::PostgreSQLTestCase
end
def test_tables_in_current_schemas
- assert !@connection.tables.include?(TABLE_NAME)
+ assert_not_includes @connection.tables, TABLE_NAME
USERS.each do |u|
set_session_auth u
- assert @connection.tables.include?(TABLE_NAME)
+ assert_includes @connection.tables, TABLE_NAME
set_session_auth
end
end
private
- def set_session_auth auth = nil
- @connection.session_auth = auth || 'default'
+ def set_session_auth(auth = nil)
+ @connection.session_auth = auth || "default"
end
def bind_param(value)
diff --git a/activerecord/test/cases/adapters/postgresql/schema_test.rb b/activerecord/test/cases/adapters/postgresql/schema_test.rb
index 52ef07f654..237e9ff6a5 100644
--- a/activerecord/test/cases/adapters/postgresql/schema_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/schema_test.rb
@@ -1,6 +1,6 @@
require "cases/helper"
-require 'models/default'
-require 'support/schema_dumping_helper'
+require "models/default"
+require "support/schema_dumping_helper"
module PGSchemaHelper
def with_schema_search_path(schema_search_path)
@@ -17,32 +17,32 @@ class SchemaTest < ActiveRecord::PostgreSQLTestCase
include PGSchemaHelper
self.use_transactional_tests = false
- SCHEMA_NAME = 'test_schema'
- SCHEMA2_NAME = 'test_schema2'
- TABLE_NAME = 'things'
- CAPITALIZED_TABLE_NAME = 'Things'
- INDEX_A_NAME = 'a_index_things_on_name'
- INDEX_B_NAME = 'b_index_things_on_different_columns_in_each_schema'
- INDEX_C_NAME = 'c_index_full_text_search'
- INDEX_D_NAME = 'd_index_things_on_description_desc'
- INDEX_E_NAME = 'e_index_things_on_name_vector'
- INDEX_A_COLUMN = 'name'
- INDEX_B_COLUMN_S1 = 'email'
- INDEX_B_COLUMN_S2 = 'moment'
- INDEX_C_COLUMN = %q{(to_tsvector('english', coalesce(things.name, '')))}
- INDEX_D_COLUMN = 'description'
- INDEX_E_COLUMN = 'name_vector'
+ SCHEMA_NAME = "test_schema"
+ SCHEMA2_NAME = "test_schema2"
+ TABLE_NAME = "things"
+ CAPITALIZED_TABLE_NAME = "Things"
+ INDEX_A_NAME = "a_index_things_on_name"
+ INDEX_B_NAME = "b_index_things_on_different_columns_in_each_schema"
+ INDEX_C_NAME = "c_index_full_text_search"
+ INDEX_D_NAME = "d_index_things_on_description_desc"
+ INDEX_E_NAME = "e_index_things_on_name_vector"
+ INDEX_A_COLUMN = "name"
+ INDEX_B_COLUMN_S1 = "email"
+ INDEX_B_COLUMN_S2 = "moment"
+ INDEX_C_COLUMN = "(to_tsvector('english', coalesce(things.name, '')))"
+ INDEX_D_COLUMN = "description"
+ INDEX_E_COLUMN = "name_vector"
COLUMNS = [
- 'id integer',
- 'name character varying(50)',
- 'email character varying(50)',
- 'description character varying(100)',
- 'name_vector tsvector',
- 'moment timestamp without time zone default now()'
+ "id integer",
+ "name character varying(50)",
+ "email character varying(50)",
+ "description character varying(100)",
+ "name_vector tsvector",
+ "moment timestamp without time zone default now()"
]
- PK_TABLE_NAME = 'table_with_pk'
- UNMATCHED_SEQUENCE_NAME = 'unmatched_primary_key_default_value_seq'
- UNMATCHED_PK_TABLE_NAME = 'table_with_unmatched_sequence_for_pk'
+ PK_TABLE_NAME = "table_with_pk"
+ UNMATCHED_SEQUENCE_NAME = "unmatched_primary_key_default_value_seq"
+ UNMATCHED_PK_TABLE_NAME = "table_with_unmatched_sequence_for_pk"
class Thing1 < ActiveRecord::Base
self.table_name = "test_schema.things"
@@ -61,7 +61,7 @@ class SchemaTest < ActiveRecord::PostgreSQLTestCase
end
class Thing5 < ActiveRecord::Base
- self.table_name = 'things'
+ self.table_name = "things"
end
class Song < ActiveRecord::Base
@@ -130,7 +130,7 @@ class SchemaTest < ActiveRecord::PostgreSQLTestCase
ensure
@connection.drop_schema "test_schema3"
end
- assert !@connection.schema_names.include?("test_schema3")
+ assert_not_includes @connection.schema_names, "test_schema3"
end
def test_drop_schema_if_exists
@@ -168,21 +168,21 @@ class SchemaTest < ActiveRecord::PostgreSQLTestCase
def test_raise_wrapped_exception_on_bad_prepare
assert_raises(ActiveRecord::StatementInvalid) do
- @connection.exec_query "select * from developers where id = ?", 'sql', [bind_param(1)]
+ @connection.exec_query "select * from developers where id = ?", "sql", [bind_param(1)]
end
end
if ActiveRecord::Base.connection.prepared_statements
def test_schema_change_with_prepared_stmt
altered = false
- @connection.exec_query "select * from developers where id = $1", 'sql', [bind_param(1)]
- @connection.exec_query "alter table developers add column zomg int", 'sql', []
+ @connection.exec_query "select * from developers where id = $1", "sql", [bind_param(1)]
+ @connection.exec_query "alter table developers add column zomg int", "sql", []
altered = true
- @connection.exec_query "select * from developers where id = $1", 'sql', [bind_param(1)]
+ @connection.exec_query "select * from developers where id = $1", "sql", [bind_param(1)]
ensure
# We are not using DROP COLUMN IF EXISTS because that syntax is only
# supported by pg 9.X
- @connection.exec_query("alter table developers drop column zomg", 'sql', []) if altered
+ @connection.exec_query("alter table developers drop column zomg", "sql", []) if altered
end
end
@@ -200,7 +200,7 @@ class SchemaTest < ActiveRecord::PostgreSQLTestCase
end
def test_data_source_exists_when_not_on_schema_search_path
- with_schema_search_path('PUBLIC') do
+ with_schema_search_path("PUBLIC") do
assert(!@connection.data_source_exists?(TABLE_NAME), "data_source exists but should not be found")
end
end
@@ -246,9 +246,9 @@ class SchemaTest < ActiveRecord::PostgreSQLTestCase
end
def test_proper_encoding_of_table_name
- assert_equal '"table_name"', @connection.quote_table_name('table_name')
+ assert_equal '"table_name"', @connection.quote_table_name("table_name")
assert_equal '"table.name"', @connection.quote_table_name('"table.name"')
- assert_equal '"schema_name"."table_name"', @connection.quote_table_name('schema_name.table_name')
+ assert_equal '"schema_name"."table_name"', @connection.quote_table_name("schema_name.table_name")
assert_equal '"schema_name"."table.name"', @connection.quote_table_name('schema_name."table.name"')
assert_equal '"schema.name"."table_name"', @connection.quote_table_name('"schema.name".table_name')
assert_equal '"schema.name"."table.name"', @connection.quote_table_name('"schema.name"."table.name"')
@@ -260,25 +260,25 @@ class SchemaTest < ActiveRecord::PostgreSQLTestCase
assert_equal 0, Thing3.count
assert_equal 0, Thing4.count
- Thing1.create(:id => 1, :name => "thing1", :email => "thing1@localhost", :moment => Time.now)
+ Thing1.create(id: 1, name: "thing1", email: "thing1@localhost", moment: Time.now)
assert_equal 1, Thing1.count
assert_equal 0, Thing2.count
assert_equal 0, Thing3.count
assert_equal 0, Thing4.count
- Thing2.create(:id => 1, :name => "thing1", :email => "thing1@localhost", :moment => Time.now)
+ Thing2.create(id: 1, name: "thing1", email: "thing1@localhost", moment: Time.now)
assert_equal 1, Thing1.count
assert_equal 1, Thing2.count
assert_equal 0, Thing3.count
assert_equal 0, Thing4.count
- Thing3.create(:id => 1, :name => "thing1", :email => "thing1@localhost", :moment => Time.now)
+ Thing3.create(id: 1, name: "thing1", email: "thing1@localhost", moment: Time.now)
assert_equal 1, Thing1.count
assert_equal 1, Thing2.count
assert_equal 1, Thing3.count
assert_equal 0, Thing4.count
- Thing4.create(:id => 1, :name => "thing1", :email => "thing1@localhost", :moment => Time.now)
+ Thing4.create(id: 1, name: "thing1", email: "thing1@localhost", moment: Time.now)
assert_equal 1, Thing1.count
assert_equal 1, Thing2.count
assert_equal 1, Thing3.count
@@ -287,7 +287,7 @@ class SchemaTest < ActiveRecord::PostgreSQLTestCase
def test_raise_on_unquoted_schema_name
assert_raises(ActiveRecord::StatementInvalid) do
- with_schema_search_path '$user,public'
+ with_schema_search_path "$user,public"
end
end
@@ -307,7 +307,7 @@ class SchemaTest < ActiveRecord::PostgreSQLTestCase
assert @connection.index_name_exists?(TABLE_NAME, INDEX_D_NAME, true)
assert @connection.index_name_exists?(TABLE_NAME, INDEX_E_NAME, true)
assert @connection.index_name_exists?(TABLE_NAME, INDEX_E_NAME, true)
- assert_not @connection.index_name_exists?(TABLE_NAME, 'missing_index', true)
+ assert_not @connection.index_name_exists?(TABLE_NAME, "missing_index", true)
end
end
@@ -332,7 +332,7 @@ class SchemaTest < ActiveRecord::PostgreSQLTestCase
@connection.execute "CREATE INDEX \"things_Index\" ON #{SCHEMA_NAME}.things (name)"
with_schema_search_path SCHEMA_NAME do
- assert_nothing_raised { @connection.remove_index "things", name: "things_Index"}
+ assert_nothing_raised { @connection.remove_index "things", name: "things_Index" }
end
end
@@ -356,13 +356,13 @@ class SchemaTest < ActiveRecord::PostgreSQLTestCase
%(#{SCHEMA_NAME}."#{PK_TABLE_NAME}"),
%(#{SCHEMA_NAME}.#{PK_TABLE_NAME})
].each do |given|
- assert_equal 'id', @connection.primary_key(given), "primary key should be found when table referenced as #{given}"
+ assert_equal "id", @connection.primary_key(given), "primary key should be found when table referenced as #{given}"
end
end
def test_primary_key_assuming_schema_search_path
with_schema_search_path(SCHEMA_NAME) do
- assert_equal 'id', @connection.primary_key(PK_TABLE_NAME), "primary key should be found"
+ assert_equal "id", @connection.primary_key(PK_TABLE_NAME), "primary key should be found"
end
end
@@ -381,19 +381,19 @@ class SchemaTest < ActiveRecord::PostgreSQLTestCase
%("#{SCHEMA_NAME}"."#{UNMATCHED_PK_TABLE_NAME}")
].each do |given|
pk, seq = @connection.pk_and_sequence_for(given)
- assert_equal 'id', pk, "primary key should be found when table referenced as #{given}"
+ assert_equal "id", pk, "primary key should be found when table referenced as #{given}"
assert_equal pg_name.new(SCHEMA_NAME, "#{PK_TABLE_NAME}_id_seq"), seq, "sequence name should be found when table referenced as #{given}" if given == %("#{SCHEMA_NAME}"."#{PK_TABLE_NAME}")
- assert_equal pg_name.new(SCHEMA_NAME, UNMATCHED_SEQUENCE_NAME), seq, "sequence name should be found when table referenced as #{given}" if given == %("#{SCHEMA_NAME}"."#{UNMATCHED_PK_TABLE_NAME}")
+ assert_equal pg_name.new(SCHEMA_NAME, UNMATCHED_SEQUENCE_NAME), seq, "sequence name should be found when table referenced as #{given}" if given == %("#{SCHEMA_NAME}"."#{UNMATCHED_PK_TABLE_NAME}")
end
end
def test_current_schema
{
- %('$user',public) => 'public',
+ %('$user',public) => "public",
SCHEMA_NAME => SCHEMA_NAME,
%(#{SCHEMA2_NAME},#{SCHEMA_NAME},public) => SCHEMA2_NAME,
- %(public,#{SCHEMA2_NAME},#{SCHEMA_NAME}) => 'public'
- }.each do |given,expect|
+ %(public,#{SCHEMA2_NAME},#{SCHEMA_NAME}) => "public"
+ }.each do |given, expect|
with_schema_search_path(given) { assert_equal expect, @connection.current_schema }
end
end
@@ -401,7 +401,7 @@ class SchemaTest < ActiveRecord::PostgreSQLTestCase
def test_prepared_statements_with_multiple_schemas
[SCHEMA_NAME, SCHEMA2_NAME].each do |schema_name|
with_schema_search_path schema_name do
- Thing5.create(:id => 1, :name => "thing inside #{SCHEMA_NAME}", :email => "thing1@localhost", :moment => Time.now)
+ Thing5.create(id: 1, name: "thing inside #{SCHEMA_NAME}", email: "thing1@localhost", moment: Time.now)
end
end
@@ -414,11 +414,11 @@ class SchemaTest < ActiveRecord::PostgreSQLTestCase
def test_schema_exists?
{
- 'public' => true,
+ "public" => true,
SCHEMA_NAME => true,
SCHEMA2_NAME => true,
- 'darkside' => false
- }.each do |given,expect|
+ "darkside" => false
+ }.each do |given, expect|
assert_equal expect, @connection.schema_exists?(given)
end
end
@@ -442,7 +442,7 @@ class SchemaTest < ActiveRecord::PostgreSQLTestCase
private
def columns(table_name)
@connection.send(:column_definitions, table_name).map do |name, type, default|
- "#{name} #{type}" + (default ? " default #{default}" : '')
+ "#{name} #{type}" + (default ? " default #{default}" : "")
end
end
diff --git a/activerecord/test/cases/adapters/postgresql/serial_test.rb b/activerecord/test/cases/adapters/postgresql/serial_test.rb
index 8abe064bf1..d711b3b729 100644
--- a/activerecord/test/cases/adapters/postgresql/serial_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/serial_test.rb
@@ -1,5 +1,5 @@
require "cases/helper"
-require 'support/schema_dumping_helper'
+require "support/schema_dumping_helper"
class PostgresqlSerialTest < ActiveRecord::PostgreSQLTestCase
include SchemaDumpingHelper
diff --git a/activerecord/test/cases/adapters/postgresql/statement_pool_test.rb b/activerecord/test/cases/adapters/postgresql/statement_pool_test.rb
index 5aab246c99..eb9978a898 100644
--- a/activerecord/test/cases/adapters/postgresql/statement_pool_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/statement_pool_test.rb
@@ -1,4 +1,4 @@
-require 'cases/helper'
+require "cases/helper"
module ActiveRecord
module ConnectionAdapters
@@ -17,22 +17,22 @@ module ActiveRecord
if Process.respond_to?(:fork)
def test_cache_is_per_pid
cache = StatementPool.new nil, 10
- cache['foo'] = 'bar'
- assert_equal 'bar', cache['foo']
+ cache["foo"] = "bar"
+ assert_equal "bar", cache["foo"]
pid = fork {
- lookup = cache['foo'];
+ lookup = cache["foo"];
exit!(!lookup)
}
Process.waitpid pid
- assert $?.success?, 'process should exit successfully'
+ assert $?.success?, "process should exit successfully"
end
end
def test_dealloc_does_not_raise_on_inactive_connection
cache = StatementPool.new InactivePGconn.new, 10
- cache['foo'] = 'bar'
+ cache["foo"] = "bar"
assert_nothing_raised { cache.clear }
end
end
diff --git a/activerecord/test/cases/adapters/postgresql/timestamp_test.rb b/activerecord/test/cases/adapters/postgresql/timestamp_test.rb
index 4c4866b46b..962450aada 100644
--- a/activerecord/test/cases/adapters/postgresql/timestamp_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/timestamp_test.rb
@@ -1,6 +1,6 @@
-require 'cases/helper'
-require 'models/developer'
-require 'models/topic'
+require "cases/helper"
+require "models/developer"
+require "models/topic"
class PostgresqlTimestampTest < ActiveRecord::PostgreSQLTestCase
class PostgresqlTimestampWithZone < ActiveRecord::Base; end
@@ -21,7 +21,7 @@ class PostgresqlTimestampTest < ActiveRecord::PostgreSQLTestCase
@connection.reconnect!
timestamp = PostgresqlTimestampWithZone.find(1)
- assert_equal Time.utc(2010,1,1, 11,0,0), timestamp.time
+ assert_equal Time.utc(2010, 1, 1, 11, 0, 0), timestamp.time
assert_instance_of Time, timestamp.time
end
ensure
@@ -32,10 +32,10 @@ class PostgresqlTimestampTest < ActiveRecord::PostgreSQLTestCase
with_timezone_config default: :local, aware_attributes: false do
@connection.reconnect!
# make sure to use a non-UTC time zone
- @connection.execute("SET time zone 'America/Jamaica'", 'SCHEMA')
+ @connection.execute("SET time zone 'America/Jamaica'", "SCHEMA")
timestamp = PostgresqlTimestampWithZone.find(1)
- assert_equal Time.utc(2010,1,1, 11,0,0), timestamp.time
+ assert_equal Time.utc(2010, 1, 1, 11, 0, 0), timestamp.time
assert_instance_of Time, timestamp.time
end
ensure
@@ -54,37 +54,37 @@ class PostgresqlTimestampFixtureTest < ActiveRecord::PostgreSQLTestCase
def test_load_infinity_and_beyond
d = Developer.find_by_sql("select 'infinity'::timestamp as updated_at")
- assert d.first.updated_at.infinite?, 'timestamp should be infinite'
+ assert d.first.updated_at.infinite?, "timestamp should be infinite"
d = Developer.find_by_sql("select '-infinity'::timestamp as updated_at")
time = d.first.updated_at
- assert time.infinite?, 'timestamp should be infinite'
+ assert time.infinite?, "timestamp should be infinite"
assert_operator time, :<, 0
end
def test_save_infinity_and_beyond
- d = Developer.create!(:name => 'aaron', :updated_at => 1.0 / 0.0)
+ d = Developer.create!(name: "aaron", updated_at: 1.0 / 0.0)
assert_equal(1.0 / 0.0, d.updated_at)
- d = Developer.create!(:name => 'aaron', :updated_at => -1.0 / 0.0)
+ d = Developer.create!(name: "aaron", updated_at: -1.0 / 0.0)
assert_equal(-1.0 / 0.0, d.updated_at)
end
def test_bc_timestamp
date = Date.new(0) - 1.week
- Developer.create!(:name => "aaron", :updated_at => date)
+ Developer.create!(name: "aaron", updated_at: date)
assert_equal date, Developer.find_by_name("aaron").updated_at
end
def test_bc_timestamp_leap_year
date = Time.utc(-4, 2, 29)
- Developer.create!(:name => "taihou", :updated_at => date)
+ Developer.create!(name: "taihou", updated_at: date)
assert_equal date, Developer.find_by_name("taihou").updated_at
end
def test_bc_timestamp_year_zero
date = Time.utc(0, 4, 7)
- Developer.create!(:name => "yahagi", :updated_at => date)
+ Developer.create!(name: "yahagi", updated_at: date)
assert_equal date, Developer.find_by_name("yahagi").updated_at
end
end
diff --git a/activerecord/test/cases/adapters/postgresql/transaction_test.rb b/activerecord/test/cases/adapters/postgresql/transaction_test.rb
index e76705a802..9b42d0383d 100644
--- a/activerecord/test/cases/adapters/postgresql/transaction_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/transaction_test.rb
@@ -1,21 +1,24 @@
require "cases/helper"
-require 'support/connection_helper'
+require "support/connection_helper"
+require "concurrent/atomic/cyclic_barrier"
module ActiveRecord
class PostgresqlTransactionTest < ActiveRecord::PostgreSQLTestCase
self.use_transactional_tests = false
class Sample < ActiveRecord::Base
- self.table_name = 'samples'
+ self.table_name = "samples"
end
setup do
+ @abort, Thread.abort_on_exception = Thread.abort_on_exception, false
+
@connection = ActiveRecord::Base.connection
@connection.transaction do
- @connection.drop_table 'samples', if_exists: true
- @connection.create_table('samples') do |t|
- t.integer 'value'
+ @connection.drop_table "samples", if_exists: true
+ @connection.create_table("samples") do |t|
+ t.integer "value"
end
end
@@ -23,50 +26,77 @@ module ActiveRecord
end
teardown do
- @connection.drop_table 'samples', if_exists: true
- end
+ @connection.drop_table "samples", if_exists: true
- test "raises error when a serialization failure occurs" do
- with_warning_suppression do
- assert_raises(ActiveRecord::TransactionSerializationError) do
- thread = Thread.new do
- Sample.transaction isolation: :serializable do
- Sample.delete_all
+ Thread.abort_on_exception = @abort
+ end
- 10.times do |i|
- sleep 0.1
+ test "raises SerializationFailure when a serialization failure occurs" do
+ assert_raises(ActiveRecord::SerializationFailure) do
+ before = Concurrent::CyclicBarrier.new(2)
+ after = Concurrent::CyclicBarrier.new(2)
- Sample.create value: i
- end
+ thread = Thread.new do
+ with_warning_suppression do
+ Sample.transaction isolation: :serializable do
+ before.wait
+ Sample.create value: Sample.sum(:value)
+ after.wait
end
end
+ end
- sleep 0.1
+ begin
+ with_warning_suppression do
+ Sample.transaction isolation: :serializable do
+ before.wait
+ Sample.create value: Sample.sum(:value)
+ after.wait
+ end
+ end
+ ensure
+ thread.join
+ end
+ end
+ end
- Sample.transaction isolation: :serializable do
- Sample.delete_all
+ test "raises Deadlocked when a deadlock is encountered" do
+ with_warning_suppression do
+ assert_raises(ActiveRecord::Deadlocked) do
+ barrier = Concurrent::CyclicBarrier.new(2)
- 10.times do |i|
- sleep 0.1
+ s1 = Sample.create value: 1
+ s2 = Sample.create value: 2
- Sample.create value: i
+ thread = Thread.new do
+ Sample.transaction do
+ s1.lock!
+ barrier.wait
+ s2.update_attributes value: 1
end
-
- sleep 1
end
- thread.join
+ begin
+ Sample.transaction do
+ s2.lock!
+ barrier.wait
+ s1.update_attributes value: 2
+ end
+ ensure
+ thread.join
+ end
end
end
end
- protected
+ private
- def with_warning_suppression
- log_level = @connection.client_min_messages
- @connection.client_min_messages = 'error'
- yield
- @connection.client_min_messages = log_level
- end
+ def with_warning_suppression
+ log_level = ActiveRecord::Base.connection.client_min_messages
+ ActiveRecord::Base.connection.client_min_messages = "error"
+ yield
+ ensure
+ ActiveRecord::Base.connection.client_min_messages = log_level
+ end
end
end
diff --git a/activerecord/test/cases/adapters/postgresql/type_lookup_test.rb b/activerecord/test/cases/adapters/postgresql/type_lookup_test.rb
index ea0f0b8fa5..784d77a8d1 100644
--- a/activerecord/test/cases/adapters/postgresql/type_lookup_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/type_lookup_test.rb
@@ -1,4 +1,4 @@
-require 'cases/helper'
+require "cases/helper"
class PostgresqlTypeLookupTest < ActiveRecord::PostgreSQLTestCase
setup do
@@ -9,8 +9,8 @@ class PostgresqlTypeLookupTest < ActiveRecord::PostgreSQLTestCase
box_array = @connection.type_map.lookup(1020)
int_array = @connection.type_map.lookup(1007)
- assert_equal ';', box_array.delimiter
- assert_equal ',', int_array.delimiter
+ assert_equal ";", box_array.delimiter
+ assert_equal ",", int_array.delimiter
end
test "array types correctly respect registration of subtypes" do
@@ -19,7 +19,7 @@ class PostgresqlTypeLookupTest < ActiveRecord::PostgreSQLTestCase
big_array = [123456789123456789]
assert_raises(ActiveModel::RangeError) { int_array.serialize(big_array) }
- assert_equal "{123456789123456789}", bigint_array.serialize(big_array)
+ assert_equal "{123456789123456789}", @connection.type_cast(bigint_array.serialize(big_array))
end
test "range types correctly respect registration of subtypes" do
diff --git a/activerecord/test/cases/adapters/postgresql/utils_test.rb b/activerecord/test/cases/adapters/postgresql/utils_test.rb
index 095c1826e5..9f9e3bda2f 100644
--- a/activerecord/test/cases/adapters/postgresql/utils_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/utils_test.rb
@@ -1,5 +1,5 @@
-require 'cases/helper'
-require 'active_record/connection_adapters/postgresql/utils'
+require "cases/helper"
+require "active_record/connection_adapters/postgresql/utils"
class PostgreSQLUtilsTest < ActiveRecord::PostgreSQLTestCase
Name = ActiveRecord::ConnectionAdapters::PostgreSQL::Name
@@ -7,14 +7,14 @@ class PostgreSQLUtilsTest < ActiveRecord::PostgreSQLTestCase
def test_extract_schema_qualified_name
{
- %(table_name) => [nil,'table_name'],
- %("table.name") => [nil,'table.name'],
+ %(table_name) => [nil, "table_name"],
+ %("table.name") => [nil, "table.name"],
%(schema.table_name) => %w{schema table_name},
%("schema".table_name) => %w{schema table_name},
%(schema."table_name") => %w{schema table_name},
%("schema"."table_name") => %w{schema table_name},
- %("even spaces".table) => ['even spaces','table'],
- %(schema."table.name") => ['schema', 'table.name']
+ %("even spaces".table) => ["even spaces", "table"],
+ %(schema."table.name") => ["schema", "table.name"]
}.each do |given, expect|
assert_equal Name.new(*expect), extract_schema_qualified_name(given)
end
@@ -54,9 +54,9 @@ class PostgreSQLNameTest < ActiveRecord::PostgreSQLTestCase
end
test "can be used as hash key" do
- hash = {Name.new("schema", "article_seq") => "success"}
+ hash = { Name.new("schema", "article_seq") => "success" }
assert_equal "success", hash[Name.new("schema", "article_seq")]
- assert_equal nil, hash[Name.new("schema", "articles")]
- assert_equal nil, hash[Name.new("public", "article_seq")]
+ assert_nil hash[Name.new("schema", "articles")]
+ assert_nil hash[Name.new("public", "article_seq")]
end
end
diff --git a/activerecord/test/cases/adapters/postgresql/uuid_test.rb b/activerecord/test/cases/adapters/postgresql/uuid_test.rb
index 7628075ad2..f34d50e25c 100644
--- a/activerecord/test/cases/adapters/postgresql/uuid_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/uuid_test.rb
@@ -1,5 +1,5 @@
require "cases/helper"
-require 'support/schema_dumping_helper'
+require "support/schema_dumping_helper"
module PostgresqlUUIDHelper
def connection
@@ -9,6 +9,10 @@ module PostgresqlUUIDHelper
def drop_table(name)
connection.drop_table name, if_exists: true
end
+
+ def uuid_function
+ connection.supports_pgcrypto_uuid? ? "gen_random_uuid()" : "uuid_generate_v4()"
+ end
end
class PostgresqlUUIDTest < ActiveRecord::PostgreSQLTestCase
@@ -20,10 +24,11 @@ class PostgresqlUUIDTest < ActiveRecord::PostgreSQLTestCase
end
setup do
- enable_extension!('uuid-ossp', connection)
+ enable_extension!("uuid-ossp", connection)
+ enable_extension!("pgcrypto", connection) if connection.supports_pgcrypto_uuid?
connection.create_table "uuid_data_type" do |t|
- t.uuid 'guid'
+ t.uuid "guid"
end
end
@@ -31,16 +36,24 @@ class PostgresqlUUIDTest < ActiveRecord::PostgreSQLTestCase
drop_table "uuid_data_type"
end
+ if ActiveRecord::Base.connection.supports_pgcrypto_uuid?
+ def test_uuid_column_default
+ connection.add_column :uuid_data_type, :thingy, :uuid, null: false, default: "gen_random_uuid()"
+ UUIDType.reset_column_information
+ column = UUIDType.columns_hash["thingy"]
+ assert_equal "gen_random_uuid()", column.default_function
+ end
+ end
+
def test_change_column_default
- @connection.add_column :uuid_data_type, :thingy, :uuid, null: false, default: "uuid_generate_v1()"
+ connection.add_column :uuid_data_type, :thingy, :uuid, null: false, default: "uuid_generate_v1()"
UUIDType.reset_column_information
- column = UUIDType.columns_hash['thingy']
+ column = UUIDType.columns_hash["thingy"]
assert_equal "uuid_generate_v1()", column.default_function
- @connection.change_column :uuid_data_type, :thingy, :uuid, null: false, default: "uuid_generate_v4()"
-
+ connection.change_column :uuid_data_type, :thingy, :uuid, null: false, default: "uuid_generate_v4()"
UUIDType.reset_column_information
- column = UUIDType.columns_hash['thingy']
+ column = UUIDType.columns_hash["thingy"]
assert_equal "uuid_generate_v4()", column.default_function
ensure
UUIDType.reset_column_information
@@ -57,46 +70,46 @@ class PostgresqlUUIDTest < ActiveRecord::PostgreSQLTestCase
end
def test_treat_blank_uuid_as_nil
- UUIDType.create! guid: ''
- assert_equal(nil, UUIDType.last.guid)
+ UUIDType.create! guid: ""
+ assert_nil(UUIDType.last.guid)
end
def test_treat_invalid_uuid_as_nil
- uuid = UUIDType.create! guid: 'foobar'
- assert_equal(nil, uuid.guid)
+ uuid = UUIDType.create! guid: "foobar"
+ assert_nil(uuid.guid)
end
def test_invalid_uuid_dont_modify_before_type_cast
- uuid = UUIDType.new guid: 'foobar'
- assert_equal 'foobar', uuid.guid_before_type_cast
+ uuid = UUIDType.new guid: "foobar"
+ assert_equal "foobar", uuid.guid_before_type_cast
end
def test_acceptable_uuid_regex
# Valid uuids
- ['A0EEBC99-9C0B-4EF8-BB6D-6BB9BD380A11',
- '{a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11}',
- 'a0eebc999c0b4ef8bb6d6bb9bd380a11',
- 'a0ee-bc99-9c0b-4ef8-bb6d-6bb9-bd38-0a11',
- '{a0eebc99-9c0b4ef8-bb6d6bb9-bd380a11}',
+ ["A0EEBC99-9C0B-4EF8-BB6D-6BB9BD380A11",
+ "{a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11}",
+ "a0eebc999c0b4ef8bb6d6bb9bd380a11",
+ "a0ee-bc99-9c0b-4ef8-bb6d-6bb9-bd38-0a11",
+ "{a0eebc99-9c0b4ef8-bb6d6bb9-bd380a11}",
# The following is not a valid RFC 4122 UUID, but PG doesn't seem to care,
# so we shouldn't block it either. (Pay attention to "fb6d" – the "f" here
# is invalid – it must be one of 8, 9, A, B, a, b according to the spec.)
- '{a0eebc99-9c0b-4ef8-fb6d-6bb9bd380a11}',
+ "{a0eebc99-9c0b-4ef8-fb6d-6bb9bd380a11}",
].each do |valid_uuid|
uuid = UUIDType.new guid: valid_uuid
assert_not_nil uuid.guid
end
# Invalid uuids
- [['A0EEBC99-9C0B-4EF8-BB6D-6BB9BD380A11'],
+ [["A0EEBC99-9C0B-4EF8-BB6D-6BB9BD380A11"],
Hash.new,
0,
0.0,
true,
- 'Z0000C99-9C0B-4EF8-BB6D-6BB9BD380A11',
- 'a0eebc999r0b4ef8ab6d6bb9bd380a11',
- 'a0ee-bc99------4ef8-bb6d-6bb9-bd38-0a11',
- '{a0eebc99-bb6d6bb9-bd380a11}'].each do |invalid_uuid|
+ "Z0000C99-9C0B-4EF8-BB6D-6BB9BD380A11",
+ "a0eebc999r0b4ef8ab6d6bb9bd380a11",
+ "a0ee-bc99------4ef8-bb6d-6bb9-bd38-0a11",
+ "{a0eebc99-bb6d6bb9-bd380a11}"].each do |invalid_uuid|
uuid = UUIDType.new guid: invalid_uuid
assert_nil uuid.guid
end
@@ -142,39 +155,44 @@ class PostgresqlUUIDGenerationTest < ActiveRecord::PostgreSQLTestCase
include SchemaDumpingHelper
class UUID < ActiveRecord::Base
- self.table_name = 'pg_uuids'
+ self.table_name = "pg_uuids"
end
setup do
- connection.create_table('pg_uuids', id: :uuid, default: 'uuid_generate_v1()') do |t|
- t.string 'name'
- t.uuid 'other_uuid', default: 'uuid_generate_v4()'
+ connection.create_table("pg_uuids", id: :uuid, default: "uuid_generate_v1()") do |t|
+ t.string "name"
+ t.uuid "other_uuid", default: "uuid_generate_v4()"
end
# Create custom PostgreSQL function to generate UUIDs
# to test dumping tables which columns have defaults with custom functions
connection.execute <<-SQL
CREATE OR REPLACE FUNCTION my_uuid_generator() RETURNS uuid
- AS $$ SELECT * FROM uuid_generate_v4() $$
+ AS $$ SELECT * FROM #{uuid_function} $$
LANGUAGE SQL VOLATILE;
SQL
# Create such a table with custom function as default value generator
- connection.create_table('pg_uuids_2', id: :uuid, default: 'my_uuid_generator()') do |t|
- t.string 'name'
- t.uuid 'other_uuid_2', default: 'my_uuid_generator()'
+ connection.create_table("pg_uuids_2", id: :uuid, default: "my_uuid_generator()") do |t|
+ t.string "name"
+ t.uuid "other_uuid_2", default: "my_uuid_generator()"
+ end
+
+ connection.create_table("pg_uuids_3", id: :uuid) do |t|
+ t.string "name"
end
end
teardown do
drop_table "pg_uuids"
- drop_table 'pg_uuids_2'
- connection.execute 'DROP FUNCTION IF EXISTS my_uuid_generator();'
+ drop_table "pg_uuids_2"
+ drop_table "pg_uuids_3"
+ connection.execute "DROP FUNCTION IF EXISTS my_uuid_generator();"
end
if ActiveRecord::Base.connection.supports_extensions?
def test_id_is_uuid
- assert_equal :uuid, UUID.columns_hash['id'].type
+ assert_equal :uuid, UUID.columns_hash["id"].type
assert UUID.primary_key
end
@@ -190,21 +208,51 @@ class PostgresqlUUIDGenerationTest < ActiveRecord::PostgreSQLTestCase
end
def test_pk_and_sequence_for_uuid_primary_key
- pk, seq = connection.pk_and_sequence_for('pg_uuids')
- assert_equal 'id', pk
- assert_equal nil, seq
+ pk, seq = connection.pk_and_sequence_for("pg_uuids")
+ assert_equal "id", pk
+ assert_nil seq
end
def test_schema_dumper_for_uuid_primary_key
schema = dump_table_schema "pg_uuids"
assert_match(/\bcreate_table "pg_uuids", id: :uuid, default: -> { "uuid_generate_v1\(\)" }/, schema)
- assert_match(/t\.uuid "other_uuid", default: -> { "uuid_generate_v4\(\)" }/, schema)
+ assert_match(/t\.uuid "other_uuid", default: -> { "uuid_generate_v4\(\)" }/, schema)
end
def test_schema_dumper_for_uuid_primary_key_with_custom_default
schema = dump_table_schema "pg_uuids_2"
assert_match(/\bcreate_table "pg_uuids_2", id: :uuid, default: -> { "my_uuid_generator\(\)" }/, schema)
- assert_match(/t\.uuid "other_uuid_2", default: -> { "my_uuid_generator\(\)" }/, schema)
+ assert_match(/t\.uuid "other_uuid_2", default: -> { "my_uuid_generator\(\)" }/, schema)
+ end
+
+ def test_schema_dumper_for_uuid_primary_key_default
+ schema = dump_table_schema "pg_uuids_3"
+ if connection.supports_pgcrypto_uuid?
+ assert_match(/\bcreate_table "pg_uuids_3", id: :uuid, default: -> { "gen_random_uuid\(\)" }/, schema)
+ else
+ assert_match(/\bcreate_table "pg_uuids_3", id: :uuid, default: -> { "uuid_generate_v4\(\)" }/, schema)
+ end
+ end
+
+ if ActiveRecord::Base.connection.supports_pgcrypto_uuid?
+ def test_schema_dumper_for_uuid_primary_key_default_in_legacy_migration
+ @verbose_was = ActiveRecord::Migration.verbose
+ ActiveRecord::Migration.verbose = false
+
+ migration = Class.new(ActiveRecord::Migration[4.2]) do
+ def version; 101 end
+ def migrate(x)
+ create_table("pg_uuids_4", id: :uuid)
+ end
+ end.new
+ ActiveRecord::Migrator.new(:up, [migration]).migrate
+
+ schema = dump_table_schema "pg_uuids_4"
+ assert_match(/\bcreate_table "pg_uuids_4", id: :uuid, default: -> { "uuid_generate_v4\(\)" }/, schema)
+ ensure
+ drop_table "pg_uuids_4"
+ ActiveRecord::Migration.verbose = @verbose_was
+ end
end
end
end
@@ -214,9 +262,9 @@ class PostgresqlUUIDTestNilDefault < ActiveRecord::PostgreSQLTestCase
include SchemaDumpingHelper
setup do
- connection.create_table('pg_uuids', id: false) do |t|
+ connection.create_table("pg_uuids", id: false) do |t|
t.primary_key :id, :uuid, default: nil
- t.string 'name'
+ t.string "name"
end
end
@@ -244,30 +292,30 @@ class PostgresqlUUIDTestInverseOf < ActiveRecord::PostgreSQLTestCase
include PostgresqlUUIDHelper
class UuidPost < ActiveRecord::Base
- self.table_name = 'pg_uuid_posts'
+ self.table_name = "pg_uuid_posts"
has_many :uuid_comments, inverse_of: :uuid_post
end
class UuidComment < ActiveRecord::Base
- self.table_name = 'pg_uuid_comments'
+ self.table_name = "pg_uuid_comments"
belongs_to :uuid_post
end
setup do
connection.transaction do
- connection.create_table('pg_uuid_posts', id: :uuid) do |t|
- t.string 'title'
+ connection.create_table("pg_uuid_posts", id: :uuid) do |t|
+ t.string "title"
end
- connection.create_table('pg_uuid_comments', id: :uuid) do |t|
+ connection.create_table("pg_uuid_comments", id: :uuid) do |t|
t.references :uuid_post, type: :uuid
- t.string 'content'
+ t.string "content"
end
end
end
teardown do
- drop_table "pg_uuid_comments"
- drop_table "pg_uuid_posts"
+ drop_table "pg_uuid_comments"
+ drop_table "pg_uuid_posts"
end
if ActiveRecord::Base.connection.supports_extensions?
@@ -290,5 +338,4 @@ class PostgresqlUUIDTestInverseOf < ActiveRecord::PostgreSQLTestCase
assert_nil UuidPost.find_by(id: 789)
end
end
-
end
diff --git a/activerecord/test/cases/adapters/postgresql/xml_test.rb b/activerecord/test/cases/adapters/postgresql/xml_test.rb
index add32699fa..826b384fb3 100644
--- a/activerecord/test/cases/adapters/postgresql/xml_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/xml_test.rb
@@ -1,28 +1,28 @@
-require 'cases/helper'
-require 'support/schema_dumping_helper'
+require "cases/helper"
+require "support/schema_dumping_helper"
class PostgresqlXMLTest < ActiveRecord::PostgreSQLTestCase
include SchemaDumpingHelper
class XmlDataType < ActiveRecord::Base
- self.table_name = 'xml_data_type'
+ self.table_name = "xml_data_type"
end
def setup
@connection = ActiveRecord::Base.connection
begin
@connection.transaction do
- @connection.create_table('xml_data_type') do |t|
- t.xml 'payload'
+ @connection.create_table("xml_data_type") do |t|
+ t.xml "payload"
end
end
rescue ActiveRecord::StatementInvalid
skip "do not test on PG without xml"
end
- @column = XmlDataType.columns_hash['payload']
+ @column = XmlDataType.columns_hash["payload"]
end
teardown do
- @connection.drop_table 'xml_data_type', if_exists: true
+ @connection.drop_table "xml_data_type", if_exists: true
end
def test_column
@@ -30,7 +30,7 @@ class PostgresqlXMLTest < ActiveRecord::PostgreSQLTestCase
end
def test_null_xml
- @connection.execute %q|insert into xml_data_type (payload) VALUES(null)|
+ @connection.execute "insert into xml_data_type (payload) VALUES(null)"
assert_nil XmlDataType.first.payload
end
diff --git a/activerecord/test/cases/adapters/sqlite3/collation_test.rb b/activerecord/test/cases/adapters/sqlite3/collation_test.rb
index 58a9469ce5..28e8f12c18 100644
--- a/activerecord/test/cases/adapters/sqlite3/collation_test.rb
+++ b/activerecord/test/cases/adapters/sqlite3/collation_test.rb
@@ -1,5 +1,5 @@
require "cases/helper"
-require 'support/schema_dumping_helper'
+require "support/schema_dumping_helper"
class SQLite3CollationTest < ActiveRecord::SQLite3TestCase
include SchemaDumpingHelper
@@ -7,8 +7,8 @@ class SQLite3CollationTest < ActiveRecord::SQLite3TestCase
def setup
@connection = ActiveRecord::Base.connection
@connection.create_table :collation_table_sqlite3, force: true do |t|
- t.string :string_nocase, collation: 'NOCASE'
- t.text :text_rtrim, collation: 'RTRIM'
+ t.string :string_nocase, collation: "NOCASE"
+ t.text :text_rtrim, collation: "RTRIM"
end
end
@@ -17,32 +17,32 @@ class SQLite3CollationTest < ActiveRecord::SQLite3TestCase
end
test "string column with collation" do
- column = @connection.columns(:collation_table_sqlite3).find { |c| c.name == 'string_nocase' }
+ column = @connection.columns(:collation_table_sqlite3).find { |c| c.name == "string_nocase" }
assert_equal :string, column.type
- assert_equal 'NOCASE', column.collation
+ assert_equal "NOCASE", column.collation
end
test "text column with collation" do
- column = @connection.columns(:collation_table_sqlite3).find { |c| c.name == 'text_rtrim' }
+ column = @connection.columns(:collation_table_sqlite3).find { |c| c.name == "text_rtrim" }
assert_equal :text, column.type
- assert_equal 'RTRIM', column.collation
+ assert_equal "RTRIM", column.collation
end
test "add column with collation" do
- @connection.add_column :collation_table_sqlite3, :title, :string, collation: 'RTRIM'
+ @connection.add_column :collation_table_sqlite3, :title, :string, collation: "RTRIM"
- column = @connection.columns(:collation_table_sqlite3).find { |c| c.name == 'title' }
+ column = @connection.columns(:collation_table_sqlite3).find { |c| c.name == "title" }
assert_equal :string, column.type
- assert_equal 'RTRIM', column.collation
+ assert_equal "RTRIM", column.collation
end
test "change column with collation" do
@connection.add_column :collation_table_sqlite3, :description, :string
- @connection.change_column :collation_table_sqlite3, :description, :text, collation: 'RTRIM'
+ @connection.change_column :collation_table_sqlite3, :description, :text, collation: "RTRIM"
- column = @connection.columns(:collation_table_sqlite3).find { |c| c.name == 'description' }
+ column = @connection.columns(:collation_table_sqlite3).find { |c| c.name == "description" }
assert_equal :text, column.type
- assert_equal 'RTRIM', column.collation
+ assert_equal "RTRIM", column.collation
end
test "schema dump includes collation" do
diff --git a/activerecord/test/cases/adapters/sqlite3/copy_table_test.rb b/activerecord/test/cases/adapters/sqlite3/copy_table_test.rb
index 34e3b2e023..91967c1e33 100644
--- a/activerecord/test/cases/adapters/sqlite3/copy_table_test.rb
+++ b/activerecord/test/cases/adapters/sqlite3/copy_table_test.rb
@@ -10,8 +10,8 @@ class CopyTableTest < ActiveRecord::SQLite3TestCase
end
end
- def test_copy_table(from = 'customers', to = 'customers2', options = {})
- assert_nothing_raised {copy_table(from, to, options)}
+ def test_copy_table(from = "customers", to = "customers2", options = {})
+ assert_nothing_raised { copy_table(from, to, options) }
assert_equal row_count(from), row_count(to)
if block_given?
@@ -24,39 +24,39 @@ class CopyTableTest < ActiveRecord::SQLite3TestCase
end
def test_copy_table_renaming_column
- test_copy_table('customers', 'customers2',
- :rename => {'name' => 'person_name'}) do |from, to, options|
- expected = column_values(from, 'name')
- assert_equal expected, column_values(to, 'person_name')
+ test_copy_table("customers", "customers2",
+ rename: { "name" => "person_name" }) do |from, to, options|
+ expected = column_values(from, "name")
+ assert_equal expected, column_values(to, "person_name")
assert expected.any?, "No values in table: #{expected.inspect}"
end
end
def test_copy_table_allows_to_pass_options_to_create_table
- @connection.create_table('blocker_table')
- test_copy_table('customers', 'blocker_table', force: true)
+ @connection.create_table("blocker_table")
+ test_copy_table("customers", "blocker_table", force: true)
end
def test_copy_table_with_index
- test_copy_table('comments', 'comments_with_index') do
- @connection.add_index('comments_with_index', ['post_id', 'type'])
- test_copy_table('comments_with_index', 'comments_with_index2') do
- assert_equal table_indexes_without_name('comments_with_index'),
- table_indexes_without_name('comments_with_index2')
+ test_copy_table("comments", "comments_with_index") do
+ @connection.add_index("comments_with_index", ["post_id", "type"])
+ test_copy_table("comments_with_index", "comments_with_index2") do
+ assert_equal table_indexes_without_name("comments_with_index"),
+ table_indexes_without_name("comments_with_index2")
end
end
end
def test_copy_table_without_primary_key
- test_copy_table('developers_projects', 'programmers_projects') do
- assert_nil @connection.primary_key('programmers_projects')
+ test_copy_table("developers_projects", "programmers_projects") do
+ assert_nil @connection.primary_key("programmers_projects")
end
end
def test_copy_table_with_id_col_that_is_not_primary_key
- test_copy_table('goofy_string_id', 'goofy_string_id2') do
- original_id = @connection.columns('goofy_string_id').detect{|col| col.name == 'id' }
- copied_id = @connection.columns('goofy_string_id2').detect{|col| col.name == 'id' }
+ test_copy_table("goofy_string_id", "goofy_string_id2") do
+ original_id = @connection.columns("goofy_string_id").detect { |col| col.name == "id" }
+ copied_id = @connection.columns("goofy_string_id2").detect { |col| col.name == "id" }
assert_equal original_id.type, copied_id.type
assert_equal original_id.sql_type, copied_id.sql_type
assert_equal original_id.limit, copied_id.limit
@@ -64,28 +64,28 @@ class CopyTableTest < ActiveRecord::SQLite3TestCase
end
def test_copy_table_with_unconventional_primary_key
- test_copy_table('owners', 'owners_unconventional') do
- original_pk = @connection.primary_key('owners')
- copied_pk = @connection.primary_key('owners_unconventional')
+ test_copy_table("owners", "owners_unconventional") do
+ original_pk = @connection.primary_key("owners")
+ copied_pk = @connection.primary_key("owners_unconventional")
assert_equal original_pk, copied_pk
end
end
def test_copy_table_with_binary_column
- test_copy_table 'binaries', 'binaries2'
+ test_copy_table "binaries", "binaries2"
end
-protected
+private
def copy_table(from, to, options = {})
- @connection.copy_table(from, to, {:temporary => true}.merge(options))
+ @connection.copy_table(from, to, { temporary: true }.merge(options))
end
def column_names(table)
- @connection.table_structure(table).map {|column| column['name']}
+ @connection.table_structure(table).map { |column| column["name"] }
end
def column_values(table, column)
- @connection.select_all("SELECT #{column} FROM #{table} ORDER BY id").map {|row| row[column]}
+ @connection.select_all("SELECT #{column} FROM #{table} ORDER BY id").map { |row| row[column] }
end
def table_indexes_without_name(table)
@@ -93,6 +93,6 @@ protected
end
def row_count(table)
- @connection.select_one("SELECT COUNT(*) AS count FROM #{table}")['count']
+ @connection.select_one("SELECT COUNT(*) AS count FROM #{table}")["count"]
end
end
diff --git a/activerecord/test/cases/adapters/sqlite3/explain_test.rb b/activerecord/test/cases/adapters/sqlite3/explain_test.rb
index a1a6e5f16a..128acb79cf 100644
--- a/activerecord/test/cases/adapters/sqlite3/explain_test.rb
+++ b/activerecord/test/cases/adapters/sqlite3/explain_test.rb
@@ -1,19 +1,19 @@
require "cases/helper"
-require 'models/developer'
-require 'models/computer'
+require "models/developer"
+require "models/computer"
class SQLite3ExplainTest < ActiveRecord::SQLite3TestCase
fixtures :developers
def test_explain_for_one_query
explain = Developer.where(id: 1).explain
- assert_match %r(EXPLAIN for: SELECT "developers".* FROM "developers" WHERE "developers"."id" = (?:\?|1)), explain
+ assert_match %r(EXPLAIN for: SELECT "developers".* FROM "developers" WHERE "developers"."id" = (?:\? \[\["id", 1\]\]|1)), explain
assert_match(/(SEARCH )?TABLE developers USING (INTEGER )?PRIMARY KEY/, explain)
end
def test_explain_with_eager_loading
explain = Developer.where(id: 1).includes(:audit_logs).explain
- assert_match %r(EXPLAIN for: SELECT "developers".* FROM "developers" WHERE "developers"."id" = (?:\?|1)), explain
+ assert_match %r(EXPLAIN for: SELECT "developers".* FROM "developers" WHERE "developers"."id" = (?:\? \[\["id", 1\]\]|1)), explain
assert_match(/(SEARCH )?TABLE developers USING (INTEGER )?PRIMARY KEY/, explain)
assert_match %(EXPLAIN for: SELECT "audit_logs".* FROM "audit_logs" WHERE "audit_logs"."developer_id" = 1), explain
assert_match(/(SCAN )?TABLE audit_logs/, explain)
diff --git a/activerecord/test/cases/adapters/sqlite3/legacy_migration_test.rb b/activerecord/test/cases/adapters/sqlite3/legacy_migration_test.rb
new file mode 100644
index 0000000000..fcca8d66b5
--- /dev/null
+++ b/activerecord/test/cases/adapters/sqlite3/legacy_migration_test.rb
@@ -0,0 +1,59 @@
+require "cases/helper"
+
+class SqliteLegacyMigrationTest < ActiveRecord::SQLite3TestCase
+ self.use_transactional_tests = false
+
+ class GenerateTableWithoutBigint < ActiveRecord::Migration[5.0]
+ def change
+ create_table :legacy_integer_pk do |table|
+ table.string :foo
+ end
+
+ create_table :override_pk, id: :bigint do |table|
+ table.string :bar
+ end
+ end
+ end
+
+ def setup
+ super
+ @connection = ActiveRecord::Base.connection
+
+ @migration_verbose_old = ActiveRecord::Migration.verbose
+ ActiveRecord::Migration.verbose = false
+
+ migrations = [GenerateTableWithoutBigint.new(nil, 1)]
+
+ ActiveRecord::Migrator.new(:up, migrations).migrate
+ end
+
+ def teardown
+ ActiveRecord::Migration.verbose = @migration_verbose_old
+ @connection.drop_table("legacy_integer_pk")
+ @connection.drop_table("override_pk")
+ ActiveRecord::SchemaMigration.delete_all rescue nil
+ super
+ end
+
+ def test_create_table_uses_integer_as_pkey_by_default
+ col = column(:legacy_integer_pk, :id)
+ assert_equal "INTEGER", sql_type_for(col)
+ assert primary_key?(:legacy_integer_pk, "id"), "id is not primary key"
+ end
+
+ private
+
+ def column(table_name, column_name)
+ ActiveRecord::Base.connection
+ .columns(table_name.to_s)
+ .detect { |c| c.name == column_name.to_s }
+ end
+
+ def sql_type_for(col)
+ col && col.sql_type
+ end
+
+ def primary_key?(table_name, column)
+ ActiveRecord::Base.connection.execute("PRAGMA table_info(#{table_name})").find { |col| col["name"] == column }["pk"] == 1
+ end
+end
diff --git a/activerecord/test/cases/adapters/sqlite3/quoting_test.rb b/activerecord/test/cases/adapters/sqlite3/quoting_test.rb
index fe2425b845..9750840051 100644
--- a/activerecord/test/cases/adapters/sqlite3/quoting_test.rb
+++ b/activerecord/test/cases/adapters/sqlite3/quoting_test.rb
@@ -1,7 +1,7 @@
require "cases/helper"
-require 'bigdecimal'
-require 'yaml'
-require 'securerandom'
+require "bigdecimal"
+require "yaml"
+require "securerandom"
class SQLite3QuotingTest < ActiveRecord::SQLite3TestCase
def setup
@@ -16,7 +16,7 @@ class SQLite3QuotingTest < ActiveRecord::SQLite3TestCase
end
def test_type_cast_symbol
- assert_equal 'foo', @conn.type_cast(:foo)
+ assert_equal "foo", @conn.type_cast(:foo)
end
def test_type_cast_date
@@ -37,19 +37,19 @@ class SQLite3QuotingTest < ActiveRecord::SQLite3TestCase
end
def test_type_cast_nil
- assert_equal nil, @conn.type_cast(nil)
+ assert_nil @conn.type_cast(nil)
end
def test_type_cast_true
- assert_equal 't', @conn.type_cast(true)
+ assert_equal "t", @conn.type_cast(true)
end
def test_type_cast_false
- assert_equal 'f', @conn.type_cast(false)
+ assert_equal "f", @conn.type_cast(false)
end
def test_type_cast_bigdecimal
- bd = BigDecimal.new '10.0'
+ bd = BigDecimal.new "10.0"
assert_equal bd.to_f, @conn.type_cast(bd)
end
@@ -79,7 +79,7 @@ class SQLite3QuotingTest < ActiveRecord::SQLite3TestCase
end
def test_quoting_binary_strings
- value = "hello".encode('ascii-8bit')
+ value = "hello".encode("ascii-8bit")
type = ActiveRecord::Type::String.new
assert_equal "'hello'", @conn.quote(type.serialize(value))
diff --git a/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb b/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb
index bbc9f978bf..a6109348cc 100644
--- a/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb
+++ b/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb
@@ -1,7 +1,7 @@
require "cases/helper"
-require 'models/owner'
-require 'tempfile'
-require 'support/ddl_helper'
+require "models/owner"
+require "tempfile"
+require "support/ddl_helper"
module ActiveRecord
module ConnectionAdapters
@@ -14,22 +14,22 @@ module ActiveRecord
end
def setup
- @conn = Base.sqlite3_connection database: ':memory:',
- adapter: 'sqlite3',
+ @conn = Base.sqlite3_connection database: ":memory:",
+ adapter: "sqlite3",
timeout: 100
end
def test_bad_connection
assert_raise ActiveRecord::NoDatabaseError do
connection = ActiveRecord::Base.sqlite3_connection(adapter: "sqlite3", database: "/tmp/should/_not/_exist/-cinco-dog.db")
- connection.drop_table 'ex', if_exists: true
+ connection.drop_table "ex", if_exists: true
end
end
unless in_memory_db?
def test_connect_with_url
original_connection = ActiveRecord::Base.remove_connection
- tf = Tempfile.open 'whatever'
+ tf = Tempfile.open "whatever"
url = "sqlite3:#{tf.path}"
ActiveRecord::Base.establish_connection(url)
assert ActiveRecord::Base.connection
@@ -51,7 +51,7 @@ module ActiveRecord
def test_valid_column
with_example_table do
- column = @conn.columns('ex').find { |col| col.name == 'id' }
+ column = @conn.columns("ex").find { |col| col.name == "id" }
assert @conn.valid_type?(column.type)
end
end
@@ -66,9 +66,9 @@ module ActiveRecord
end
def test_column_types
- owner = Owner.create!(name: "hello".encode('ascii-8bit'))
+ owner = Owner.create!(name: "hello".encode("ascii-8bit"))
owner.reload
- select = Owner.columns.map { |c| "typeof(#{c.name})" }.join ', '
+ select = Owner.columns.map { |c| "typeof(#{c.name})" }.join ", "
result = Owner.connection.exec_query <<-esql
SELECT #{select}
FROM #{Owner.table_name}
@@ -83,10 +83,10 @@ module ActiveRecord
def test_exec_insert
with_example_table do
vals = [Relation::QueryAttribute.new("number", 10, Type::Value.new)]
- @conn.exec_insert('insert into ex (number) VALUES (?)', 'SQL', vals)
+ @conn.exec_insert("insert into ex (number) VALUES (?)", "SQL", vals)
result = @conn.exec_query(
- 'select number from ex where number = ?', 'SQL', vals)
+ "select number from ex where number = ?", "SQL", vals)
assert_equal 1, result.rows.length
assert_equal 10, result.rows.first.first
@@ -94,8 +94,8 @@ module ActiveRecord
end
def test_primary_key_returns_nil_for_no_pk
- with_example_table 'id int, data string' do
- assert_nil @conn.primary_key('ex')
+ with_example_table "id int, data string" do
+ assert_nil @conn.primary_key("ex")
end
end
@@ -107,69 +107,69 @@ module ActiveRecord
def test_bad_timeout
assert_raises(TypeError) do
- Base.sqlite3_connection database: ':memory:',
- adapter: 'sqlite3',
- timeout: 'usa'
+ Base.sqlite3_connection database: ":memory:",
+ adapter: "sqlite3",
+ timeout: "usa"
end
end
# connection is OK with a nil timeout
def test_nil_timeout
- conn = Base.sqlite3_connection database: ':memory:',
- adapter: 'sqlite3',
+ conn = Base.sqlite3_connection database: ":memory:",
+ adapter: "sqlite3",
timeout: nil
- assert conn, 'made a connection'
+ assert conn, "made a connection"
end
def test_connect
- assert @conn, 'should have connection'
+ assert @conn, "should have connection"
end
# sqlite3 defaults to UTF-8 encoding
def test_encoding
- assert_equal 'UTF-8', @conn.encoding
+ assert_equal "UTF-8", @conn.encoding
end
def test_exec_no_binds
- with_example_table 'id int, data string' do
- result = @conn.exec_query('SELECT id, data FROM ex')
+ with_example_table "id int, data string" do
+ result = @conn.exec_query("SELECT id, data FROM ex")
assert_equal 0, result.rows.length
assert_equal 2, result.columns.length
assert_equal %w{ id data }, result.columns
@conn.exec_query('INSERT INTO ex (id, data) VALUES (1, "foo")')
- result = @conn.exec_query('SELECT id, data FROM ex')
+ result = @conn.exec_query("SELECT id, data FROM ex")
assert_equal 1, result.rows.length
assert_equal 2, result.columns.length
- assert_equal [[1, 'foo']], result.rows
+ assert_equal [[1, "foo"]], result.rows
end
end
def test_exec_query_with_binds
- with_example_table 'id int, data string' do
+ with_example_table "id int, data string" do
@conn.exec_query('INSERT INTO ex (id, data) VALUES (1, "foo")')
result = @conn.exec_query(
- 'SELECT id, data FROM ex WHERE id = ?', nil, [Relation::QueryAttribute.new(nil, 1, Type::Value.new)])
+ "SELECT id, data FROM ex WHERE id = ?", nil, [Relation::QueryAttribute.new(nil, 1, Type::Value.new)])
assert_equal 1, result.rows.length
assert_equal 2, result.columns.length
- assert_equal [[1, 'foo']], result.rows
+ assert_equal [[1, "foo"]], result.rows
end
end
def test_exec_query_typecasts_bind_vals
- with_example_table 'id int, data string' do
+ with_example_table "id int, data string" do
@conn.exec_query('INSERT INTO ex (id, data) VALUES (1, "foo")')
result = @conn.exec_query(
- 'SELECT id, data FROM ex WHERE id = ?', nil, [Relation::QueryAttribute.new("id", "1-fuu", Type::Integer.new)])
+ "SELECT id, data FROM ex WHERE id = ?", nil, [Relation::QueryAttribute.new("id", "1-fuu", Type::Integer.new)])
assert_equal 1, result.rows.length
assert_equal 2, result.columns.length
- assert_equal [[1, 'foo']], result.rows
+ assert_equal [[1, "foo"]], result.rows
end
end
@@ -182,15 +182,15 @@ module ActiveRecord
)
eosql
str = "\x80".force_encoding("ASCII-8BIT")
- binary = DualEncoding.new name: 'いただきます!', data: str
+ binary = DualEncoding.new name: "いただきます!", data: str
binary.save!
assert_equal str, binary.data
ensure
- DualEncoding.connection.drop_table 'dual_encodings', if_exists: true
+ DualEncoding.connection.drop_table "dual_encodings", if_exists: true
end
def test_type_cast_should_not_mutate_encoding
- name = 'hello'.force_encoding(Encoding::ASCII_8BIT)
+ name = "hello".force_encoding(Encoding::ASCII_8BIT)
Owner.create(name: name)
assert_equal Encoding::ASCII_8BIT, name.encoding
ensure
@@ -204,8 +204,8 @@ module ActiveRecord
assert_equal 1, records.length
record = records.first
- assert_equal 10, record['number']
- assert_equal 1, record['id']
+ assert_equal 10, record["number"]
+ assert_equal 1, record["id"]
end
end
@@ -226,7 +226,7 @@ module ActiveRecord
def test_insert_id_value_returned
with_example_table do
sql = "INSERT INTO ex (number) VALUES (10)"
- idval = 'vuvuzela'
+ idval = "vuvuzela"
id = @conn.insert(sql, nil, nil, idval)
assert_equal idval, id
end
@@ -237,7 +237,7 @@ module ActiveRecord
2.times do |i|
@conn.create "INSERT INTO ex (number) VALUES (#{i})"
end
- rows = @conn.select_rows 'select number, id from ex'
+ rows = @conn.select_rows "select number, id from ex"
assert_equal [[0, 1], [1, 2]], rows
end
end
@@ -254,7 +254,7 @@ module ActiveRecord
def test_transaction
with_example_table do
- count_sql = 'select count(*) from ex'
+ count_sql = "select count(*) from ex"
@conn.begin_db_transaction
@conn.create "INSERT INTO ex (number) VALUES (10)"
@@ -267,9 +267,9 @@ module ActiveRecord
def test_tables
with_example_table do
- ActiveSupport::Deprecation.silence { assert_equal %w{ ex }, @conn.tables }
- with_example_table 'id integer PRIMARY KEY AUTOINCREMENT, number integer', 'people' do
- ActiveSupport::Deprecation.silence { assert_equal %w{ ex people }.sort, @conn.tables.sort }
+ assert_equal %w{ ex }, @conn.tables
+ with_example_table "id integer PRIMARY KEY AUTOINCREMENT, number integer", "people" do
+ assert_equal %w{ ex people }.sort, @conn.tables.sort
end
end
end
@@ -277,19 +277,17 @@ module ActiveRecord
def test_tables_logs_name
sql = <<-SQL
SELECT name FROM sqlite_master
- WHERE type IN ('table','view') AND name <> 'sqlite_sequence'
+ WHERE type = 'table' AND name <> 'sqlite_sequence'
SQL
- assert_logged [[sql.squish, 'SCHEMA', []]] do
- ActiveSupport::Deprecation.silence do
- @conn.tables('hello')
- end
+ assert_logged [[sql.squish, "SCHEMA", []]] do
+ @conn.tables
end
end
def test_indexes_logs_name
with_example_table do
- assert_logged [["PRAGMA index_list(\"ex\")", 'SCHEMA', []]] do
- @conn.indexes('ex', 'hello')
+ assert_logged [["PRAGMA index_list(\"ex\")", "SCHEMA", []]] do
+ @conn.indexes("ex", "hello")
end
end
end
@@ -298,19 +296,17 @@ module ActiveRecord
with_example_table do
sql = <<-SQL
SELECT name FROM sqlite_master
- WHERE type IN ('table','view') AND name <> 'sqlite_sequence' AND name = 'ex'
+ WHERE type = 'table' AND name <> 'sqlite_sequence' AND name = 'ex'
SQL
- assert_logged [[sql.squish, 'SCHEMA', []]] do
- ActiveSupport::Deprecation.silence do
- assert @conn.table_exists?('ex')
- end
+ assert_logged [[sql.squish, "SCHEMA", []]] do
+ assert @conn.table_exists?("ex")
end
end
end
def test_columns
with_example_table do
- columns = @conn.columns('ex').sort_by(&:name)
+ columns = @conn.columns("ex").sort_by(&:name)
assert_equal 2, columns.length
assert_equal %w{ id number }.sort, columns.map(&:name)
assert_equal [nil, nil], columns.map(&:default)
@@ -319,17 +315,17 @@ module ActiveRecord
end
def test_columns_with_default
- with_example_table 'id integer PRIMARY KEY AUTOINCREMENT, number integer default 10' do
- column = @conn.columns('ex').find { |x|
- x.name == 'number'
+ with_example_table "id integer PRIMARY KEY AUTOINCREMENT, number integer default 10" do
+ column = @conn.columns("ex").find { |x|
+ x.name == "number"
}
- assert_equal '10', column.default
+ assert_equal "10", column.default
end
end
def test_columns_with_not_null
- with_example_table 'id integer PRIMARY KEY AUTOINCREMENT, number integer not null' do
- column = @conn.columns('ex').find { |x| x.name == 'number' }
+ with_example_table "id integer PRIMARY KEY AUTOINCREMENT, number integer not null" do
+ column = @conn.columns("ex").find { |x| x.name == "number" }
assert_not column.null, "column should not be null"
end
end
@@ -337,59 +333,59 @@ module ActiveRecord
def test_indexes_logs
with_example_table do
assert_logged [["PRAGMA index_list(\"ex\")", "SCHEMA", []]] do
- @conn.indexes('ex')
+ @conn.indexes("ex")
end
end
end
def test_no_indexes
- assert_equal [], @conn.indexes('items')
+ assert_equal [], @conn.indexes("items")
end
def test_index
with_example_table do
- @conn.add_index 'ex', 'id', unique: true, name: 'fun'
- index = @conn.indexes('ex').find { |idx| idx.name == 'fun' }
+ @conn.add_index "ex", "id", unique: true, name: "fun"
+ index = @conn.indexes("ex").find { |idx| idx.name == "fun" }
- assert_equal 'ex', index.table
- assert index.unique, 'index is unique'
- assert_equal ['id'], index.columns
+ assert_equal "ex", index.table
+ assert index.unique, "index is unique"
+ assert_equal ["id"], index.columns
end
end
def test_non_unique_index
with_example_table do
- @conn.add_index 'ex', 'id', name: 'fun'
- index = @conn.indexes('ex').find { |idx| idx.name == 'fun' }
- assert_not index.unique, 'index is not unique'
+ @conn.add_index "ex", "id", name: "fun"
+ index = @conn.indexes("ex").find { |idx| idx.name == "fun" }
+ assert_not index.unique, "index is not unique"
end
end
def test_compound_index
with_example_table do
- @conn.add_index 'ex', %w{ id number }, name: 'fun'
- index = @conn.indexes('ex').find { |idx| idx.name == 'fun' }
+ @conn.add_index "ex", %w{ id number }, name: "fun"
+ index = @conn.indexes("ex").find { |idx| idx.name == "fun" }
assert_equal %w{ id number }.sort, index.columns.sort
end
end
def test_primary_key
with_example_table do
- assert_equal 'id', @conn.primary_key('ex')
- with_example_table 'internet integer PRIMARY KEY AUTOINCREMENT, number integer not null', 'foos' do
- assert_equal 'internet', @conn.primary_key('foos')
+ assert_equal "id", @conn.primary_key("ex")
+ with_example_table "internet integer PRIMARY KEY AUTOINCREMENT, number integer not null", "foos" do
+ assert_equal "internet", @conn.primary_key("foos")
end
end
end
def test_no_primary_key
- with_example_table 'number integer not null' do
- assert_nil @conn.primary_key('ex')
+ with_example_table "number integer not null" do
+ assert_nil @conn.primary_key("ex")
end
end
def test_supports_extensions
- assert_not @conn.supports_extensions?, 'does not support extensions'
+ assert_not @conn.supports_extensions?, "does not support extensions"
end
def test_respond_to_enable_extension
@@ -402,15 +398,15 @@ module ActiveRecord
def test_statement_closed
db = ::SQLite3::Database.new(ActiveRecord::Base.
- configurations['arunit']['database'])
+ configurations["arunit"]["database"])
statement = ::SQLite3::Statement.new(db,
- 'CREATE TABLE statement_test (number integer not null)')
- statement.stub(:step, ->{ raise ::SQLite3::BusyException.new('busy') }) do
+ "CREATE TABLE statement_test (number integer not null)")
+ statement.stub(:step, -> { raise ::SQLite3::BusyException.new("busy") }) do
assert_called(statement, :columns, returns: []) do
assert_called(statement, :close) do
::SQLite3::Statement.stub(:new, statement) do
assert_raises ActiveRecord::StatementInvalid do
- @conn.exec_query 'select * from statement_test'
+ @conn.exec_query "select * from statement_test"
end
end
end
@@ -420,22 +416,22 @@ module ActiveRecord
private
- def assert_logged logs
- subscriber = SQLSubscriber.new
- subscription = ActiveSupport::Notifications.subscribe('sql.active_record', subscriber)
- yield
- assert_equal logs, subscriber.logged
- ensure
- ActiveSupport::Notifications.unsubscribe(subscription)
- end
+ def assert_logged(logs)
+ subscriber = SQLSubscriber.new
+ subscription = ActiveSupport::Notifications.subscribe("sql.active_record", subscriber)
+ yield
+ assert_equal logs, subscriber.logged
+ ensure
+ ActiveSupport::Notifications.unsubscribe(subscription)
+ end
- def with_example_table(definition = nil, table_name = 'ex', &block)
- definition ||= <<-SQL
- id integer PRIMARY KEY AUTOINCREMENT,
- number integer
- SQL
- super(@conn, table_name, definition, &block)
- end
+ def with_example_table(definition = nil, table_name = "ex", &block)
+ definition ||= <<-SQL
+ id integer PRIMARY KEY AUTOINCREMENT,
+ number integer
+ SQL
+ super(@conn, table_name, definition, &block)
+ end
end
end
end
diff --git a/activerecord/test/cases/adapters/sqlite3/sqlite3_create_folder_test.rb b/activerecord/test/cases/adapters/sqlite3/sqlite3_create_folder_test.rb
index 9b675b804b..b1b4463bf1 100644
--- a/activerecord/test/cases/adapters/sqlite3/sqlite3_create_folder_test.rb
+++ b/activerecord/test/cases/adapters/sqlite3/sqlite3_create_folder_test.rb
@@ -1,5 +1,5 @@
require "cases/helper"
-require 'models/owner'
+require "models/owner"
module ActiveRecord
module ConnectionAdapters
@@ -8,12 +8,12 @@ module ActiveRecord
Dir.mktmpdir do |dir|
begin
dir = Pathname.new(dir)
- @conn = Base.sqlite3_connection :database => dir.join("db/foo.sqlite3"),
- :adapter => 'sqlite3',
- :timeout => 100
+ @conn = Base.sqlite3_connection database: dir.join("db/foo.sqlite3"),
+ adapter: "sqlite3",
+ timeout: 100
- assert Dir.exist? dir.join('db')
- assert File.exist? dir.join('db/foo.sqlite3')
+ assert Dir.exist? dir.join("db")
+ assert File.exist? dir.join("db/foo.sqlite3")
ensure
@conn.disconnect! if @conn
end
diff --git a/activerecord/test/cases/adapters/sqlite3/statement_pool_test.rb b/activerecord/test/cases/adapters/sqlite3/statement_pool_test.rb
index 24cc6875ab..aebcce3691 100644
--- a/activerecord/test/cases/adapters/sqlite3/statement_pool_test.rb
+++ b/activerecord/test/cases/adapters/sqlite3/statement_pool_test.rb
@@ -1,20 +1,20 @@
-require 'cases/helper'
+require "cases/helper"
class SQLite3StatementPoolTest < ActiveRecord::SQLite3TestCase
if Process.respond_to?(:fork)
def test_cache_is_per_pid
cache = ActiveRecord::ConnectionAdapters::SQLite3Adapter::StatementPool.new(10)
- cache['foo'] = 'bar'
- assert_equal 'bar', cache['foo']
+ cache["foo"] = "bar"
+ assert_equal "bar", cache["foo"]
pid = fork {
- lookup = cache['foo'];
+ lookup = cache["foo"];
exit!(!lookup)
}
Process.waitpid pid
- assert $?.success?, 'process should exit successfully'
+ assert $?.success?, "process should exit successfully"
end
end
end
diff --git a/activerecord/test/cases/aggregations_test.rb b/activerecord/test/cases/aggregations_test.rb
index 8a728902a8..f8136fde72 100644
--- a/activerecord/test/cases/aggregations_test.rb
+++ b/activerecord/test/cases/aggregations_test.rb
@@ -1,5 +1,5 @@
require "cases/helper"
-require 'models/customer'
+require "models/customer"
class AggregationsTest < ActiveRecord::TestCase
fixtures :customers
@@ -51,17 +51,17 @@ class AggregationsTest < ActiveRecord::TestCase
Customer.update_all("gps_location = '24x113'")
customers(:david).reload
- assert_equal '24x113', customers(:david)['gps_location']
+ assert_equal "24x113", customers(:david)["gps_location"]
- assert_equal GpsLocation.new('24x113'), customers(:david).gps_location
+ assert_equal GpsLocation.new("24x113"), customers(:david).gps_location
end
def test_gps_equality
- assert_equal GpsLocation.new('39x110'), GpsLocation.new('39x110')
+ assert_equal GpsLocation.new("39x110"), GpsLocation.new("39x110")
end
def test_gps_inequality
- assert_not_equal GpsLocation.new('39x110'), GpsLocation.new('39x111')
+ assert_not_equal GpsLocation.new("39x110"), GpsLocation.new("39x111")
end
def test_allow_nil_gps_is_nil
@@ -102,7 +102,7 @@ class AggregationsTest < ActiveRecord::TestCase
end
def test_nil_assignment_results_in_nil
- customers(:david).gps_location = GpsLocation.new('39x111')
+ customers(:david).gps_location = GpsLocation.new("39x111")
assert_not_nil customers(:david).gps_location
customers(:david).gps_location = nil
assert_nil customers(:david).gps_location
@@ -129,13 +129,13 @@ class AggregationsTest < ActiveRecord::TestCase
end
def test_custom_constructor
- assert_equal 'Barney GUMBLE', customers(:barney).fullname.to_s
+ assert_equal "Barney GUMBLE", customers(:barney).fullname.to_s
assert_kind_of Fullname, customers(:barney).fullname
end
def test_custom_converter
- customers(:barney).fullname = 'Barnoit Gumbleau'
- assert_equal 'Barnoit GUMBLEAU', customers(:barney).fullname.to_s
+ customers(:barney).fullname = "Barnoit Gumbleau"
+ assert_equal "Barnoit GUMBLEAU", customers(:barney).fullname.to_s
assert_kind_of Fullname, customers(:barney).fullname
end
@@ -143,17 +143,22 @@ class AggregationsTest < ActiveRecord::TestCase
customers(:barney).fullname = { first: "Barney", last: "Stinson" }
assert_equal "Barney STINSON", customers(:barney).name
end
+
+ def test_assigning_hash_without_custom_converter
+ customers(:barney).fullname_no_converter = { first: "Barney", last: "Stinson" }
+ assert_equal({ first: "Barney", last: "Stinson" }.to_s, customers(:barney).name)
+ end
end
class OverridingAggregationsTest < ActiveRecord::TestCase
class DifferentName; end
class Person < ActiveRecord::Base
- composed_of :composed_of, :mapping => %w(person_first_name first_name)
+ composed_of :composed_of, mapping: %w(person_first_name first_name)
end
class DifferentPerson < Person
- composed_of :composed_of, :class_name => 'DifferentName', :mapping => %w(different_person_first_name first_name)
+ composed_of :composed_of, class_name: "DifferentName", mapping: %w(different_person_first_name first_name)
end
def test_composed_of_aggregation_redefinition_reflections_should_differ_and_not_inherited
diff --git a/activerecord/test/cases/ar_schema_test.rb b/activerecord/test/cases/ar_schema_test.rb
index 9c99689c1e..397ac599b9 100644
--- a/activerecord/test/cases/ar_schema_test.rb
+++ b/activerecord/test/cases/ar_schema_test.rb
@@ -17,6 +17,7 @@ if ActiveRecord::Base.connection.supports_migrations?
@connection.drop_table :nep_fruits rescue nil
@connection.drop_table :nep_schema_migrations rescue nil
@connection.drop_table :has_timestamps rescue nil
+ @connection.drop_table :multiple_indexes rescue nil
ActiveRecord::SchemaMigration.delete_all rescue nil
ActiveRecord::Migration.verbose = @original_verbose
end
@@ -36,7 +37,7 @@ if ActiveRecord::Base.connection.supports_migrations?
end
def test_schema_define
- ActiveRecord::Schema.define(:version => 7) do
+ ActiveRecord::Schema.define(version: 7) do
create_table :fruits do |t|
t.column :color, :string
t.column :fruit_size, :string # NOTE: "size" is reserved in Oracle
@@ -53,9 +54,9 @@ if ActiveRecord::Base.connection.supports_migrations?
def test_schema_define_w_table_name_prefix
table_name = ActiveRecord::SchemaMigration.table_name
old_table_name_prefix = ActiveRecord::Base.table_name_prefix
- ActiveRecord::Base.table_name_prefix = "nep_"
+ ActiveRecord::Base.table_name_prefix = "nep_"
ActiveRecord::SchemaMigration.table_name = "nep_#{table_name}"
- ActiveRecord::Schema.define(:version => 7) do
+ ActiveRecord::Schema.define(version: 7) do
create_table :fruits do |t|
t.column :color, :string
t.column :fruit_size, :string # NOTE: "size" is reserved in Oracle
@@ -65,13 +66,13 @@ if ActiveRecord::Base.connection.supports_migrations?
end
assert_equal 7, ActiveRecord::Migrator::current_version
ensure
- ActiveRecord::Base.table_name_prefix = old_table_name_prefix
+ ActiveRecord::Base.table_name_prefix = old_table_name_prefix
ActiveRecord::SchemaMigration.table_name = table_name
end
def test_schema_raises_an_error_for_invalid_column_type
assert_raise NoMethodError do
- ActiveRecord::Schema.define(:version => 8) do
+ ActiveRecord::Schema.define(version: 8) do
create_table :vegetables do |t|
t.unknown :color
end
@@ -80,7 +81,7 @@ if ActiveRecord::Base.connection.supports_migrations?
end
def test_schema_subclass
- Class.new(ActiveRecord::Schema).define(:version => 9) do
+ Class.new(ActiveRecord::Schema).define(version: 9) do
create_table :fruits
end
assert_nothing_raised { @connection.select_all "SELECT * FROM fruits" }
@@ -93,6 +94,21 @@ if ActiveRecord::Base.connection.supports_migrations?
assert_equal "20131219224947", ActiveRecord::SchemaMigration.normalize_migration_number("20131219224947")
end
+ def test_schema_load_with_multiple_indexes_for_column_of_different_names
+ ActiveRecord::Schema.define do
+ create_table :multiple_indexes do |t|
+ t.string "foo"
+ t.index ["foo"], name: "multiple_indexes_foo_1"
+ t.index ["foo"], name: "multiple_indexes_foo_2"
+ end
+ end
+
+ indexes = @connection.indexes("multiple_indexes")
+
+ assert_equal 2, indexes.length
+ assert_equal ["multiple_indexes_foo_1", "multiple_indexes_foo_2"], indexes.collect(&:name).sort
+ end
+
def test_timestamps_without_null_set_null_to_false_on_create_table
ActiveRecord::Schema.define do
create_table :has_timestamps do |t|
@@ -100,8 +116,8 @@ if ActiveRecord::Base.connection.supports_migrations?
end
end
- assert !@connection.columns(:has_timestamps).find { |c| c.name == 'created_at' }.null
- assert !@connection.columns(:has_timestamps).find { |c| c.name == 'updated_at' }.null
+ assert !@connection.columns(:has_timestamps).find { |c| c.name == "created_at" }.null
+ assert !@connection.columns(:has_timestamps).find { |c| c.name == "updated_at" }.null
end
def test_timestamps_without_null_set_null_to_false_on_change_table
@@ -113,8 +129,8 @@ if ActiveRecord::Base.connection.supports_migrations?
end
end
- assert !@connection.columns(:has_timestamps).find { |c| c.name == 'created_at' }.null
- assert !@connection.columns(:has_timestamps).find { |c| c.name == 'updated_at' }.null
+ assert !@connection.columns(:has_timestamps).find { |c| c.name == "created_at" }.null
+ assert !@connection.columns(:has_timestamps).find { |c| c.name == "updated_at" }.null
end
def test_timestamps_without_null_set_null_to_false_on_add_timestamps
@@ -123,8 +139,8 @@ if ActiveRecord::Base.connection.supports_migrations?
add_timestamps :has_timestamps, default: Time.now
end
- assert !@connection.columns(:has_timestamps).find { |c| c.name == 'created_at' }.null
- assert !@connection.columns(:has_timestamps).find { |c| c.name == 'updated_at' }.null
+ assert !@connection.columns(:has_timestamps).find { |c| c.name == "created_at" }.null
+ assert !@connection.columns(:has_timestamps).find { |c| c.name == "updated_at" }.null
end
end
end
diff --git a/activerecord/test/cases/associations/association_scope_test.rb b/activerecord/test/cases/associations/association_scope_test.rb
index 472e270f8c..c322333f6d 100644
--- a/activerecord/test/cases/associations/association_scope_test.rb
+++ b/activerecord/test/cases/associations/association_scope_test.rb
@@ -1,11 +1,11 @@
-require 'cases/helper'
-require 'models/post'
-require 'models/author'
+require "cases/helper"
+require "models/post"
+require "models/author"
module ActiveRecord
module Associations
class AssociationScopeTest < ActiveRecord::TestCase
- test 'does not duplicate conditions' do
+ test "does not duplicate conditions" do
scope = AssociationScope.scope(Author.new.association(:welcome_posts),
Author.connection)
binds = scope.where_clause.binds.map(&:value)
diff --git a/activerecord/test/cases/associations/belongs_to_associations_test.rb b/activerecord/test/cases/associations/belongs_to_associations_test.rb
index 9dadd114a1..5875a1871f 100644
--- a/activerecord/test/cases/associations/belongs_to_associations_test.rb
+++ b/activerecord/test/cases/associations/belongs_to_associations_test.rb
@@ -1,28 +1,28 @@
-require 'cases/helper'
-require 'models/developer'
-require 'models/project'
-require 'models/company'
-require 'models/topic'
-require 'models/reply'
-require 'models/computer'
-require 'models/post'
-require 'models/author'
-require 'models/tag'
-require 'models/tagging'
-require 'models/comment'
-require 'models/sponsor'
-require 'models/member'
-require 'models/essay'
-require 'models/toy'
-require 'models/invoice'
-require 'models/line_item'
-require 'models/column'
-require 'models/record'
-require 'models/admin'
-require 'models/admin/user'
-require 'models/ship'
-require 'models/treasure'
-require 'models/parrot'
+require "cases/helper"
+require "models/developer"
+require "models/project"
+require "models/company"
+require "models/topic"
+require "models/reply"
+require "models/computer"
+require "models/post"
+require "models/author"
+require "models/tag"
+require "models/tagging"
+require "models/comment"
+require "models/sponsor"
+require "models/member"
+require "models/essay"
+require "models/toy"
+require "models/invoice"
+require "models/line_item"
+require "models/column"
+require "models/record"
+require "models/admin"
+require "models/admin/user"
+require "models/ship"
+require "models/treasure"
+require "models/parrot"
class BelongsToAssociationsTest < ActiveRecord::TestCase
fixtures :accounts, :companies, :developers, :projects, :topics,
@@ -43,11 +43,11 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
ActiveRecord::SQLCounter.clear_log
Client.find(3).firm
ensure
- assert ActiveRecord::SQLCounter.log_all.all? { |sql| /order by/i !~ sql }, 'ORDER BY was used in the query'
+ assert ActiveRecord::SQLCounter.log_all.all? { |sql| /order by/i !~ sql }, "ORDER BY was used in the query"
end
def test_belongs_to_with_primary_key
- client = Client.create(:name => "Primary key client", :firm_name => companies(:first_firm).name)
+ client = Client.create(name: "Primary key client", firm_name: companies(:first_firm).name)
assert_equal companies(:first_firm).name, client.firm_with_primary_key.name
end
@@ -94,7 +94,7 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
account = model.new
assert_not account.valid?
- assert_equal [{error: :blank}], account.errors.details[:company]
+ assert_equal [{ error: :blank }], account.errors.details[:company]
ensure
ActiveRecord::Base.belongs_to_required_by_default = original_value
end
@@ -111,7 +111,7 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
account = model.new
assert_not account.valid?
- assert_equal [{error: :blank}], account.errors.details[:company]
+ assert_equal [{ error: :blank }], account.errors.details[:company]
ensure
ActiveRecord::Base.belongs_to_required_by_default = original_value
end
@@ -120,21 +120,21 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
counter = 0
comments = Class.new(ActiveRecord::Base) {
- self.table_name = 'comments'
- self.inheritance_column = 'not_there'
+ self.table_name = "comments"
+ self.inheritance_column = "not_there"
posts = Class.new(ActiveRecord::Base) {
- self.table_name = 'posts'
- self.inheritance_column = 'not_there'
+ self.table_name = "posts"
+ self.inheritance_column = "not_there"
default_scope -> {
counter += 1
- where("id = :inc", :inc => counter)
+ where("id = :inc", inc: counter)
}
- has_many :comments, :anonymous_class => comments
+ has_many :comments, anonymous_class: comments
}
- belongs_to :post, :anonymous_class => posts, :inverse_of => false
+ belongs_to :post, anonymous_class: posts, inverse_of: false
}
assert_equal 0, counter
@@ -166,7 +166,7 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
Admin.const_set "Region", Class.new(ActiveRecord::Base)
e = assert_raise(ActiveRecord::AssociationTypeMismatch) {
- Admin::RegionalUser.new(region: 'wrong value')
+ Admin::RegionalUser.new(region: "wrong value")
}
assert_match(/^Region\([^)]+\) expected, got "wrong value" which is an instance of String\([^)]+\)$/, e.message)
ensure
@@ -203,14 +203,14 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
def test_eager_loading_with_primary_key
Firm.create("name" => "Apple")
Client.create("name" => "Citibank", :firm_name => "Apple")
- citibank_result = Client.all.merge!(:where => {:name => "Citibank"}, :includes => :firm_with_primary_key).first
+ citibank_result = Client.all.merge!(where: { name: "Citibank" }, includes: :firm_with_primary_key).first
assert citibank_result.association(:firm_with_primary_key).loaded?
end
def test_eager_loading_with_primary_key_as_symbol
Firm.create("name" => "Apple")
Client.create("name" => "Citibank", :firm_name => "Apple")
- citibank_result = Client.all.merge!(:where => {:name => "Citibank"}, :includes => :firm_with_primary_key_symbols).first
+ citibank_result = Client.all.merge!(where: { name: "Citibank" }, includes: :firm_with_primary_key_symbols).first
assert citibank_result.association(:firm_with_primary_key_symbols).loaded?
end
@@ -224,7 +224,7 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
end
def test_creating_the_belonging_object_with_primary_key
- client = Client.create(:name => "Primary key client")
+ client = Client.create(name: "Primary key client")
apple = client.create_firm_with_primary_key("name" => "Apple")
assert_equal apple, client.firm_with_primary_key
client.save
@@ -247,36 +247,36 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
def test_building_the_belonging_object_with_explicit_sti_base_class
account = Account.new
- company = account.build_firm(:type => "Company")
+ company = account.build_firm(type: "Company")
assert_kind_of Company, company, "Expected #{company.class} to be a Company"
end
def test_building_the_belonging_object_with_sti_subclass
account = Account.new
- company = account.build_firm(:type => "Firm")
+ company = account.build_firm(type: "Firm")
assert_kind_of Firm, company, "Expected #{company.class} to be a Firm"
end
def test_building_the_belonging_object_with_an_invalid_type
account = Account.new
- assert_raise(ActiveRecord::SubclassNotFound) { account.build_firm(:type => "InvalidType") }
+ assert_raise(ActiveRecord::SubclassNotFound) { account.build_firm(type: "InvalidType") }
end
def test_building_the_belonging_object_with_an_unrelated_type
account = Account.new
- assert_raise(ActiveRecord::SubclassNotFound) { account.build_firm(:type => "Account") }
+ assert_raise(ActiveRecord::SubclassNotFound) { account.build_firm(type: "Account") }
end
def test_building_the_belonging_object_with_primary_key
- client = Client.create(:name => "Primary key client")
+ client = Client.create(name: "Primary key client")
apple = client.build_firm_with_primary_key("name" => "Apple")
client.save
assert_equal apple.name, client.firm_name
end
def test_create!
- client = Client.create!(:name => "Jimmy")
- account = client.create_account!(:credit_limit => 10)
+ client = Client.create!(name: "Jimmy")
+ account = client.create_account!(credit_limit: 10)
assert_equal account, client.account
assert account.persisted?
client.save
@@ -285,12 +285,22 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
end
def test_failing_create!
- client = Client.create!(:name => "Jimmy")
+ client = Client.create!(name: "Jimmy")
assert_raise(ActiveRecord::RecordInvalid) { client.create_account! }
assert_not_nil client.account
assert client.account.new_record?
end
+ def test_reloading_the_belonging_object
+ odegy_account = accounts(:odegy_account)
+
+ assert_equal "Odegy", odegy_account.firm.name
+ Company.where(id: odegy_account.firm_id).update_all(name: "ODEGY")
+ assert_equal "Odegy", odegy_account.firm.name
+
+ assert_equal "ODEGY", odegy_account.reload_firm.name
+ end
+
def test_natural_assignment_to_nil
client = Client.find(3)
client.firm = nil
@@ -301,7 +311,7 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
end
def test_natural_assignment_to_nil_with_primary_key
- client = Client.create(:name => "Primary key client", :firm_name => companies(:first_firm).name)
+ client = Client.create(name: "Primary key client", firm_name: companies(:first_firm).name)
client.firm_with_primary_key = nil
client.save
client.association(:firm_with_primary_key).reload
@@ -325,19 +335,19 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
sponsor.association(:sponsorable).reload
assert_nil sponsor.sponsorable
- sponsor.sponsorable_type = '' # the column doesn't have to be declared NOT NULL
+ sponsor.sponsorable_type = "" # the column doesn't have to be declared NOT NULL
assert_nil sponsor.association(:sponsorable).send(:klass)
sponsor.association(:sponsorable).reload
assert_nil sponsor.sponsorable
- sponsor.sponsorable = Member.new :name => "Bert"
+ sponsor.sponsorable = Member.new name: "Bert"
assert_equal Member, sponsor.association(:sponsorable).send(:klass)
assert_equal "members", sponsor.association(:sponsorable).aliased_table_name
end
def test_with_polymorphic_and_condition
sponsor = Sponsor.create
- member = Member.create :name => "Bert"
+ member = Member.create name: "Bert"
sponsor.sponsorable = member
assert_equal member, sponsor.sponsorable
@@ -346,16 +356,16 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
def test_with_select
assert_equal 1, Company.find(2).firm_with_select.attributes.size
- assert_equal 1, Company.all.merge!(:includes => :firm_with_select ).find(2).firm_with_select.attributes.size
+ assert_equal 1, Company.all.merge!(includes: :firm_with_select).find(2).firm_with_select.attributes.size
end
def test_belongs_to_without_counter_cache_option
# Ship has a conventionally named `treasures_count` column, but the counter_cache
# option is not given on the association.
- ship = Ship.create(name: 'Countless')
+ ship = Ship.create(name: "Countless")
assert_no_difference lambda { ship.reload.treasures_count }, "treasures_count should not be changed unless counter_cache is given on the relation" do
- treasure = Treasure.new(name: 'Gold', ship: ship)
+ treasure = Treasure.new(name: "Gold", ship: ship)
treasure.save
end
@@ -461,8 +471,8 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
end
def test_belongs_to_counter_after_save
- topic = Topic.create!(:title => "monday night")
- topic.replies.create!(:title => "re: monday night", :content => "football")
+ topic = Topic.create!(title: "monday night")
+ topic.replies.create!(title: "re: monday night", content: "football")
assert_equal 1, Topic.find(topic.id)[:replies_count]
topic.save!
@@ -564,8 +574,8 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
end
def test_belongs_to_counter_when_update_columns
- topic = Topic.create!(:title => "37s")
- topic.replies.create!(:title => "re: 37s", :content => "rails")
+ topic = Topic.create!(title: "37s")
+ topic.replies.create!(title: "re: 37s", content: "rails")
assert_equal 1, Topic.find(topic.id)[:replies_count]
topic.update_columns(content: "rails is wonderful")
@@ -601,7 +611,7 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
def test_new_record_with_foreign_key_but_no_object
client = Client.new("firm_id" => 1)
# sometimes tests on Oracle fail if ORDER BY is not provided therefore add always :order with :first
- assert_equal Firm.all.merge!(:order => "id").first, client.firm_with_basic_id
+ assert_equal Firm.all.merge!(order: "id").first, client.firm_with_basic_id
end
def test_setting_foreign_key_after_nil_target_loaded
@@ -626,16 +636,22 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
assert_queries(0) { tagging.super_tag }
end
+ def test_dont_find_target_when_saving_foreign_key_after_stale_association_loaded
+ client = Client.create!(name: "Test client", firm_with_basic_id: Firm.find(1))
+ client.firm_id = Firm.create!(name: "Test firm").id
+ assert_queries(1) { client.save! }
+ end
+
def test_field_name_same_as_foreign_key
computer = Computer.find(1)
assert_not_nil computer.developer, ":foreign key == attribute didn't lock up" # '
end
def test_counter_cache
- topic = Topic.create :title => "Zoom-zoom-zoom"
+ topic = Topic.create title: "Zoom-zoom-zoom"
assert_equal 0, topic[:replies_count]
- reply = Reply.create(:title => "re: zoom", :content => "speedy quick!")
+ reply = Reply.create(title: "re: zoom", content: "speedy quick!")
reply.topic = topic
assert_equal 1, topic.reload[:replies_count]
@@ -646,10 +662,10 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
end
def test_counter_cache_double_destroy
- topic = Topic.create :title => "Zoom-zoom-zoom"
+ topic = Topic.create title: "Zoom-zoom-zoom"
5.times do
- topic.replies.create(:title => "re: zoom", :content => "speedy quick!")
+ topic.replies.create(title: "re: zoom", content: "speedy quick!")
end
assert_equal 5, topic.reload[:replies_count]
@@ -666,10 +682,10 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
end
def test_concurrent_counter_cache_double_destroy
- topic = Topic.create :title => "Zoom-zoom-zoom"
+ topic = Topic.create title: "Zoom-zoom-zoom"
5.times do
- topic.replies.create(:title => "re: zoom", :content => "speedy quick!")
+ topic.replies.create(title: "re: zoom", content: "speedy quick!")
end
assert_equal 5, topic.reload[:replies_count]
@@ -687,10 +703,10 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
end
def test_custom_counter_cache
- reply = Reply.create(:title => "re: zoom", :content => "speedy quick!")
+ reply = Reply.create(title: "re: zoom", content: "speedy quick!")
assert_equal 0, reply[:replies_count]
- silly = SillyReply.create(:title => "gaga", :content => "boo-boo")
+ silly = SillyReply.create(title: "gaga", content: "boo-boo")
silly.reply = reply
assert_equal 1, reply.reload[:replies_count]
@@ -714,7 +730,7 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
def test_association_assignment_sticks
post = Post.first
- author1, author2 = Author.all.merge!(:limit => 2).to_a
+ author1, author2 = Author.all.merge!(limit: 2).to_a
assert_not_nil author1
assert_not_nil author2
@@ -767,7 +783,7 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
def test_polymorphic_assignment_with_primary_key_foreign_type_field_updating
# should update when assigning a saved record
essay = Essay.new
- writer = Author.create(:name => "David")
+ writer = Author.create(name: "David")
essay.writer = writer
assert_equal "Author", essay.writer_type
@@ -792,7 +808,7 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
def test_assignment_updates_foreign_id_field_for_new_and_saved_records
client = Client.new
- saved_firm = Firm.create :name => "Saved"
+ saved_firm = Firm.create name: "Saved"
new_firm = Firm.new
client.firm = saved_firm
@@ -804,7 +820,7 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
def test_polymorphic_assignment_with_primary_key_updates_foreign_id_field_for_new_and_saved_records
essay = Essay.new
- saved_writer = Author.create(:name => "David")
+ saved_writer = Author.create(name: "David")
new_writer = Author.new
essay.writer = saved_writer
@@ -820,7 +836,7 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
assert_nil essay.writer_type
essay.writer_id = 1
- essay.writer_type = 'Author'
+ essay.writer_type = "Author"
essay.writer = nil
assert_nil essay.writer_id
@@ -842,14 +858,14 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
assert_nothing_raised do
Account.find(@account.id).save!
- Account.all.merge!(:includes => :firm).find(@account.id).save!
+ Account.all.merge!(includes: :firm).find(@account.id).save!
end
@account.firm.delete
assert_nothing_raised do
Account.find(@account.id).save!
- Account.all.merge!(:includes => :firm).find(@account.id).save!
+ Account.all.merge!(includes: :firm).find(@account.id).save!
end
end
@@ -870,18 +886,18 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
def test_belongs_to_invalid_dependent_option_raises_exception
error = assert_raise ArgumentError do
- Class.new(Author).belongs_to :special_author_address, :dependent => :nullify
+ Class.new(Author).belongs_to :special_author_address, dependent: :nullify
end
- assert_equal error.message, 'The :dependent option must be one of [:destroy, :delete], but is :nullify'
+ assert_equal error.message, "The :dependent option must be one of [:destroy, :delete], but is :nullify"
end
def test_attributes_are_being_set_when_initialized_from_belongs_to_association_with_where_clause
- new_firm = accounts(:signals37).build_firm(:name => 'Apple')
+ new_firm = accounts(:signals37).build_firm(name: "Apple")
assert_equal new_firm.name, "Apple"
end
def test_attributes_are_set_without_error_when_initialized_from_belongs_to_association_with_array_in_where_clause
- new_account = Account.where(:credit_limit => [ 50, 60 ]).new
+ new_account = Account.where(credit_limit: [ 50, 60 ]).new
assert_nil new_account.credit_limit
end
@@ -930,7 +946,7 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
assert !proxy.stale_target?
assert_equal members(:groucho), sponsor.sponsorable
- sponsor.sponsorable_type = 'Firm'
+ sponsor.sponsorable_type = "Firm"
assert proxy.stale_target?
assert_equal companies(:first_firm), sponsor.sponsorable
@@ -955,7 +971,7 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
comment = comments(:greetings)
assert_difference lambda { post.reload.tags_count }, -1 do
- assert_difference 'comment.reload.tags_count', +1 do
+ assert_difference "comment.reload.tags_count", +1 do
tagging.taggable = comment
end
end
@@ -1002,46 +1018,46 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
end
def test_build_with_block
- client = Client.create(:name => 'Client Company')
+ client = Client.create(name: "Client Company")
- firm = client.build_firm{ |f| f.name = 'Agency Company' }
- assert_equal 'Agency Company', firm.name
+ firm = client.build_firm { |f| f.name = "Agency Company" }
+ assert_equal "Agency Company", firm.name
end
def test_create_with_block
- client = Client.create(:name => 'Client Company')
+ client = Client.create(name: "Client Company")
- firm = client.create_firm{ |f| f.name = 'Agency Company' }
- assert_equal 'Agency Company', firm.name
+ firm = client.create_firm { |f| f.name = "Agency Company" }
+ assert_equal "Agency Company", firm.name
end
def test_create_bang_with_block
- client = Client.create(:name => 'Client Company')
+ client = Client.create(name: "Client Company")
- firm = client.create_firm!{ |f| f.name = 'Agency Company' }
- assert_equal 'Agency Company', firm.name
+ firm = client.create_firm! { |f| f.name = "Agency Company" }
+ assert_equal "Agency Company", firm.name
end
def test_should_set_foreign_key_on_create_association
- client = Client.create! :name => "fuu"
+ client = Client.create! name: "fuu"
- firm = client.create_firm :name => "baa"
+ firm = client.create_firm name: "baa"
assert_equal firm.id, client.client_of
end
def test_should_set_foreign_key_on_create_association!
- client = Client.create! :name => "fuu"
+ client = Client.create! name: "fuu"
- firm = client.create_firm! :name => "baa"
+ firm = client.create_firm! name: "baa"
assert_equal firm.id, client.client_of
end
def test_self_referential_belongs_to_with_counter_cache_assigning_nil
- comment = Comment.create! :post => posts(:thinking), :body => "fuu"
+ comment = Comment.create! post: posts(:thinking), body: "fuu"
comment.parent = nil
comment.save!
- assert_equal nil, comment.reload.parent
+ assert_nil comment.reload.parent
assert_equal 0, comments(:greetings).reload.children_count
end
@@ -1056,9 +1072,23 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
assert_equal 1, parent.reload.children_count
end
+ def test_belongs_to_with_out_of_range_value_assigning
+ model = Class.new(Comment) do
+ def self.name; "Temp"; end
+ validates :post, presence: true
+ end
+
+ comment = model.new
+ comment.post_id = 9223372036854775808 # out of range in the bigint
+
+ assert_nil comment.post
+ assert_not comment.valid?
+ assert_equal [{ error: :blank }], comment.errors.details[:post]
+ end
+
def test_polymorphic_with_custom_primary_key
toy = Toy.create!
- sponsor = Sponsor.create!(:sponsorable => toy)
+ sponsor = Sponsor.create!(sponsorable: toy)
assert_equal toy, sponsor.reload.sponsorable
end
@@ -1077,7 +1107,7 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
def test_reflect_the_most_recent_change
author1, author2 = Author.limit(2)
- post = Post.new(:title => "foo", :body=> "bar")
+ post = Post.new(title: "foo", body: "bar")
post.author = author1
post.author_id = author2.id
@@ -1086,8 +1116,8 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
assert_equal post.author_id, author2.id
end
- test 'dangerous association name raises ArgumentError' do
- [:errors, 'errors', :save, 'save'].each do |name|
+ test "dangerous association name raises ArgumentError" do
+ [:errors, "errors", :save, "save"].each do |name|
assert_raises(ArgumentError, "Association #{name} should not be allowed") do
Class.new(ActiveRecord::Base) do
belongs_to name
@@ -1096,17 +1126,11 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
end
end
- test 'belongs_to works with model called Record' do
+ test "belongs_to works with model called Record" do
record = Record.create!
Column.create! record: record
assert_equal 1, Column.count
end
-
- def test_association_force_reload_with_only_true_is_deprecated
- client = Client.find(3)
-
- assert_deprecated { client.firm(true) }
- end
end
class BelongsToWithForeignKeyTest < ActiveRecord::TestCase
diff --git a/activerecord/test/cases/associations/bidirectional_destroy_dependencies_test.rb b/activerecord/test/cases/associations/bidirectional_destroy_dependencies_test.rb
index 2b867965ba..8a0e041864 100644
--- a/activerecord/test/cases/associations/bidirectional_destroy_dependencies_test.rb
+++ b/activerecord/test/cases/associations/bidirectional_destroy_dependencies_test.rb
@@ -1,5 +1,5 @@
-require 'cases/helper'
-require 'models/content'
+require "cases/helper"
+require "models/content"
class BidirectionalDestroyDependenciesTest < ActiveRecord::TestCase
fixtures :content, :content_positions
diff --git a/activerecord/test/cases/associations/callbacks_test.rb b/activerecord/test/cases/associations/callbacks_test.rb
index a4298a25a6..5fd2411f6f 100644
--- a/activerecord/test/cases/associations/callbacks_test.rb
+++ b/activerecord/test/cases/associations/callbacks_test.rb
@@ -1,10 +1,10 @@
require "cases/helper"
-require 'models/post'
-require 'models/author'
-require 'models/project'
-require 'models/developer'
-require 'models/computer'
-require 'models/company'
+require "models/post"
+require "models/author"
+require "models/project"
+require "models/developer"
+require "models/computer"
+require "models/company"
class AssociationCallbacksTest < ActiveRecord::TestCase
fixtures :posts, :authors, :projects, :developers
@@ -61,20 +61,20 @@ class AssociationCallbacksTest < ActiveRecord::TestCase
end
def test_has_many_callbacks_with_create
- morten = Author.create :name => "Morten"
- post = morten.posts_with_proc_callbacks.create! :title => "Hello", :body => "How are you doing?"
+ morten = Author.create name: "Morten"
+ post = morten.posts_with_proc_callbacks.create! title: "Hello", body: "How are you doing?"
assert_equal ["before_adding<new>", "after_adding#{post.id}"], morten.post_log
end
def test_has_many_callbacks_with_create!
- morten = Author.create! :name => "Morten"
- post = morten.posts_with_proc_callbacks.create :title => "Hello", :body => "How are you doing?"
+ morten = Author.create! name: "Morten"
+ post = morten.posts_with_proc_callbacks.create title: "Hello", body: "How are you doing?"
assert_equal ["before_adding<new>", "after_adding#{post.id}"], morten.post_log
end
def test_has_many_callbacks_for_save_on_parent
- jack = Author.new :name => "Jack"
- jack.posts_with_callbacks.build :title => "Call me back!", :body => "Before you wake up and after you sleep"
+ jack = Author.new name: "Jack"
+ jack.posts_with_callbacks.build title: "Call me back!", body: "Before you wake up and after you sleep"
callback_log = ["before_adding<new>", "after_adding#{jack.posts_with_callbacks.first.id}"]
assert_equal callback_log, jack.post_log
@@ -84,8 +84,8 @@ class AssociationCallbacksTest < ActiveRecord::TestCase
end
def test_has_many_callbacks_for_destroy_on_parent
- firm = Firm.create! :name => "Firm"
- client = firm.clients.create! :name => "Client"
+ firm = Firm.create! name: "Firm"
+ client = firm.clients.create! name: "Client"
firm.destroy
assert_equal ["before_remove#{client.id}", "after_remove#{client.id}"], firm.log
@@ -108,14 +108,14 @@ class AssociationCallbacksTest < ActiveRecord::TestCase
klass = Class.new(Project) do
def self.name; Project.name; end
has_and_belongs_to_many :developers_with_callbacks,
- :class_name => "Developer",
- :before_add => lambda { |o,r|
+ class_name: "Developer",
+ before_add: lambda { |o, r|
dev = r
new_dev = r.new_record?
}
end
rec = klass.create!
- alice = Developer.new(:name => 'alice')
+ alice = Developer.new(name: "alice")
rec.developers_with_callbacks << alice
assert_equal alice, dev
assert_not_nil new_dev
@@ -126,18 +126,17 @@ class AssociationCallbacksTest < ActiveRecord::TestCase
def test_has_and_belongs_to_many_after_add_called_after_save
ar = projects(:active_record)
assert ar.developers_log.empty?
- alice = Developer.new(:name => 'alice')
+ alice = Developer.new(name: "alice")
ar.developers_with_callbacks << alice
assert_equal"after_adding#{alice.id}", ar.developers_log.last
- bob = ar.developers_with_callbacks.create(:name => 'bob')
+ bob = ar.developers_with_callbacks.create(name: "bob")
assert_equal "after_adding#{bob.id}", ar.developers_log.last
- ar.developers_with_callbacks.build(:name => 'charlie')
+ ar.developers_with_callbacks.build(name: "charlie")
assert_equal "after_adding<new>", ar.developers_log.last
end
-
def test_has_and_belongs_to_many_remove_callback
david = developers(:david)
jamis = developers(:jamis)
@@ -160,14 +159,14 @@ class AssociationCallbacksTest < ActiveRecord::TestCase
activerecord.reload
assert activerecord.developers_with_callbacks.size == 2
end
- activerecord.developers_with_callbacks.flat_map {|d| ["before_removing#{d.id}","after_removing#{d.id}"]}.sort
+ activerecord.developers_with_callbacks.flat_map { |d| ["before_removing#{d.id}", "after_removing#{d.id}"] }.sort
assert activerecord.developers_with_callbacks.clear
assert_predicate activerecord.developers_log, :empty?
end
def test_has_many_and_belongs_to_many_callbacks_for_save_on_parent
- project = Project.new :name => "Callbacks"
- project.developers_with_callbacks.build :name => "Jack", :salary => 95000
+ project = Project.new name: "Callbacks"
+ project.developers_with_callbacks.build name: "Jack", salary: 95000
callback_log = ["before_adding<new>", "after_adding<new>"]
assert_equal callback_log, project.developers_log
@@ -177,14 +176,14 @@ class AssociationCallbacksTest < ActiveRecord::TestCase
end
def test_dont_add_if_before_callback_raises_exception
- assert !@david.unchangeable_posts.include?(@authorless)
+ assert_not_includes @david.unchangeable_posts, @authorless
begin
@david.unchangeable_posts << @authorless
rescue Exception
end
assert @david.post_log.empty?
- assert !@david.unchangeable_posts.include?(@authorless)
+ assert_not_includes @david.unchangeable_posts, @authorless
@david.reload
- assert !@david.unchangeable_posts.include?(@authorless)
+ assert_not_includes @david.unchangeable_posts, @authorless
end
end
diff --git a/activerecord/test/cases/associations/cascaded_eager_loading_test.rb b/activerecord/test/cases/associations/cascaded_eager_loading_test.rb
index 51d8e0523e..ddb5c7a4aa 100644
--- a/activerecord/test/cases/associations/cascaded_eager_loading_test.rb
+++ b/activerecord/test/cases/associations/cascaded_eager_loading_test.rb
@@ -1,56 +1,56 @@
require "cases/helper"
-require 'models/post'
-require 'models/comment'
-require 'models/author'
-require 'models/categorization'
-require 'models/category'
-require 'models/company'
-require 'models/topic'
-require 'models/reply'
-require 'models/person'
-require 'models/vertex'
-require 'models/edge'
+require "models/post"
+require "models/comment"
+require "models/author"
+require "models/categorization"
+require "models/category"
+require "models/company"
+require "models/topic"
+require "models/reply"
+require "models/person"
+require "models/vertex"
+require "models/edge"
class CascadedEagerLoadingTest < ActiveRecord::TestCase
fixtures :authors, :mixins, :companies, :posts, :topics, :accounts, :comments,
:categorizations, :people, :categories, :edges, :vertices
def test_eager_association_loading_with_cascaded_two_levels
- authors = Author.all.merge!(:includes=>{:posts=>:comments}, :order=>"authors.id").to_a
+ authors = Author.all.merge!(includes: { posts: :comments }, order: "authors.id").to_a
assert_equal 3, authors.size
assert_equal 5, authors[0].posts.size
assert_equal 3, authors[1].posts.size
- assert_equal 10, authors[0].posts.collect{|post| post.comments.size }.inject(0){|sum,i| sum+i}
+ assert_equal 10, authors[0].posts.collect { |post| post.comments.size }.inject(0) { |sum, i| sum + i }
end
def test_eager_association_loading_with_cascaded_two_levels_and_one_level
- authors = Author.all.merge!(:includes=>[{:posts=>:comments}, :categorizations], :order=>"authors.id").to_a
+ authors = Author.all.merge!(includes: [{ posts: :comments }, :categorizations], order: "authors.id").to_a
assert_equal 3, authors.size
assert_equal 5, authors[0].posts.size
assert_equal 3, authors[1].posts.size
- assert_equal 10, authors[0].posts.collect{|post| post.comments.size }.inject(0){|sum,i| sum+i}
+ assert_equal 10, authors[0].posts.collect { |post| post.comments.size }.inject(0) { |sum, i| sum + i }
assert_equal 1, authors[0].categorizations.size
assert_equal 2, authors[1].categorizations.size
end
def test_eager_association_loading_with_hmt_does_not_table_name_collide_when_joining_associations
assert_nothing_raised do
- Author.joins(:posts).eager_load(:comments).where(:posts => {:tags_count => 1}).to_a
+ Author.joins(:posts).eager_load(:comments).where(posts: { tags_count: 1 }).to_a
end
- authors = Author.joins(:posts).eager_load(:comments).where(:posts => {:tags_count => 1}).to_a
+ authors = Author.joins(:posts).eager_load(:comments).where(posts: { tags_count: 1 }).to_a
assert_equal 1, assert_no_queries { authors.size }
assert_equal 10, assert_no_queries { authors[0].comments.size }
end
def test_eager_association_loading_grafts_stashed_associations_to_correct_parent
assert_nothing_raised do
- Person.eager_load(:primary_contact => :primary_contact).where('primary_contacts_people_2.first_name = ?', 'Susan').order('people.id').to_a
+ Person.eager_load(primary_contact: :primary_contact).where("primary_contacts_people_2.first_name = ?", "Susan").order("people.id").to_a
end
- assert_equal people(:michael), Person.eager_load(:primary_contact => :primary_contact).where('primary_contacts_people_2.first_name = ?', 'Susan').order('people.id').first
+ assert_equal people(:michael), Person.eager_load(primary_contact: :primary_contact).where("primary_contacts_people_2.first_name = ?", "Susan").order("people.id").first
end
def test_cascaded_eager_association_loading_with_join_for_count
- categories = Category.joins(:categorizations).includes([{:posts=>:comments}, :authors])
+ categories = Category.joins(:categorizations).includes([{ posts: :comments }, :authors])
assert_equal 4, categories.count
assert_equal 4, categories.to_a.count
@@ -59,7 +59,7 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase
end
def test_cascaded_eager_association_loading_with_duplicated_includes
- categories = Category.includes(:categorizations).includes(:categorizations => :author).where("categorizations.id is not null").references(:categorizations)
+ categories = Category.includes(:categorizations).includes(categorizations: :author).where("categorizations.id is not null").references(:categorizations)
assert_nothing_raised do
assert_equal 3, categories.count
assert_equal 3, categories.to_a.size
@@ -67,7 +67,7 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase
end
def test_cascaded_eager_association_loading_with_twice_includes_edge_cases
- categories = Category.includes(:categorizations => :author).includes(:categorizations => :post).where("posts.id is not null").references(:posts)
+ categories = Category.includes(categorizations: :author).includes(categorizations: :post).where("posts.id is not null").references(:posts)
assert_nothing_raised do
assert_equal 3, categories.count
assert_equal 3, categories.to_a.size
@@ -82,29 +82,29 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase
end
def test_eager_association_loading_with_cascaded_two_levels_with_two_has_many_associations
- authors = Author.all.merge!(:includes=>{:posts=>[:comments, :categorizations]}, :order=>"authors.id").to_a
+ authors = Author.all.merge!(includes: { posts: [:comments, :categorizations] }, order: "authors.id").to_a
assert_equal 3, authors.size
assert_equal 5, authors[0].posts.size
assert_equal 3, authors[1].posts.size
- assert_equal 10, authors[0].posts.collect{|post| post.comments.size }.inject(0){|sum,i| sum+i}
+ assert_equal 10, authors[0].posts.collect { |post| post.comments.size }.inject(0) { |sum, i| sum + i }
end
def test_eager_association_loading_with_cascaded_two_levels_and_self_table_reference
- authors = Author.all.merge!(:includes=>{:posts=>[:comments, :author]}, :order=>"authors.id").to_a
+ authors = Author.all.merge!(includes: { posts: [:comments, :author] }, order: "authors.id").to_a
assert_equal 3, authors.size
assert_equal 5, authors[0].posts.size
assert_equal authors(:david).name, authors[0].name
- assert_equal [authors(:david).name], authors[0].posts.collect{|post| post.author.name}.uniq
+ assert_equal [authors(:david).name], authors[0].posts.collect { |post| post.author.name }.uniq
end
def test_eager_association_loading_with_cascaded_two_levels_with_condition
- authors = Author.all.merge!(:includes=>{:posts=>:comments}, :where=>"authors.id=1", :order=>"authors.id").to_a
+ authors = Author.all.merge!(includes: { posts: :comments }, where: "authors.id=1", order: "authors.id").to_a
assert_equal 1, authors.size
assert_equal 5, authors[0].posts.size
end
def test_eager_association_loading_with_cascaded_three_levels_by_ping_pong
- firms = Firm.all.merge!(:includes=>{:account=>{:firm=>:account}}, :order=>"companies.id").to_a
+ firms = Firm.all.merge!(includes: { account: { firm: :account } }, order: "companies.id").to_a
assert_equal 2, firms.size
assert_equal firms.first.account, firms.first.account.firm.account
assert_equal companies(:first_firm).account, assert_no_queries { firms.first.account.firm.account }
@@ -112,7 +112,7 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase
end
def test_eager_association_loading_with_has_many_sti
- topics = Topic.all.merge!(:includes => :replies, :order => 'topics.id').to_a
+ topics = Topic.all.merge!(includes: :replies, order: "topics.id").to_a
first, second, = topics(:first).replies.size, topics(:second).replies.size
assert_no_queries do
assert_equal first, topics[0].replies.size
@@ -121,11 +121,11 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase
end
def test_eager_association_loading_with_has_many_sti_and_subclasses
- silly = SillyReply.new(:title => "gaga", :content => "boo-boo", :parent_id => 1)
+ silly = SillyReply.new(title: "gaga", content: "boo-boo", parent_id: 1)
silly.parent_id = 1
assert silly.save
- topics = Topic.all.merge!(:includes => :replies, :order => ['topics.id', 'replies_topics.id']).to_a
+ topics = Topic.all.merge!(includes: :replies, order: ["topics.id", "replies_topics.id"]).to_a
assert_no_queries do
assert_equal 2, topics[0].replies.size
assert_equal 0, topics[1].replies.size
@@ -133,14 +133,14 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase
end
def test_eager_association_loading_with_belongs_to_sti
- replies = Reply.all.merge!(:includes => :topic, :order => 'topics.id').to_a
- assert replies.include?(topics(:second))
- assert !replies.include?(topics(:first))
+ replies = Reply.all.merge!(includes: :topic, order: "topics.id").to_a
+ assert_includes replies, topics(:second)
+ assert_not_includes replies, topics(:first)
assert_equal topics(:first), assert_no_queries { replies.first.topic }
end
def test_eager_association_loading_with_multiple_stis_and_order
- author = Author.all.merge!(:includes => { :posts => [ :special_comments , :very_special_comment ] }, :order => ['authors.name', 'comments.body', 'very_special_comments_posts.body'], :where => 'posts.id = 4').first
+ author = Author.all.merge!(includes: { posts: [ :special_comments , :very_special_comment ] }, order: ["authors.name", "comments.body", "very_special_comments_posts.body"], where: "posts.id = 4").first
assert_equal authors(:david), author
assert_no_queries do
author.posts.first.special_comments
@@ -149,7 +149,7 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase
end
def test_eager_association_loading_of_stis_with_multiple_references
- authors = Author.all.merge!(:includes => { :posts => { :special_comments => { :post => [ :special_comments, :very_special_comment ] } } }, :order => 'comments.body, very_special_comments_posts.body', :where => 'posts.id = 4').to_a
+ authors = Author.all.merge!(includes: { posts: { special_comments: { post: [ :special_comments, :very_special_comment ] } } }, order: "comments.body, very_special_comments_posts.body", where: "posts.id = 4").to_a
assert_equal [authors(:david)], authors
assert_no_queries do
authors.first.posts.first.special_comments.first.post.special_comments
@@ -158,7 +158,7 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase
end
def test_eager_association_loading_where_first_level_returns_nil
- authors = Author.all.merge!(:includes => {:post_about_thinking => :comments}, :order => 'authors.id DESC').to_a
+ authors = Author.all.merge!(includes: { post_about_thinking: :comments }, order: "authors.id DESC").to_a
assert_equal [authors(:bob), authors(:mary), authors(:david)], authors
assert_no_queries do
authors[2].post_about_thinking.comments.first
@@ -166,12 +166,12 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase
end
def test_eager_association_loading_with_recursive_cascading_four_levels_has_many_through
- source = Vertex.all.merge!(:includes=>{:sinks=>{:sinks=>{:sinks=>:sinks}}}, :order => 'vertices.id').first
+ source = Vertex.all.merge!(includes: { sinks: { sinks: { sinks: :sinks } } }, order: "vertices.id").first
assert_equal vertices(:vertex_4), assert_no_queries { source.sinks.first.sinks.first.sinks.first }
end
def test_eager_association_loading_with_recursive_cascading_four_levels_has_and_belongs_to_many
- sink = Vertex.all.merge!(:includes=>{:sources=>{:sources=>{:sources=>:sources}}}, :order => 'vertices.id DESC').first
+ sink = Vertex.all.merge!(includes: { sources: { sources: { sources: :sources } } }, order: "vertices.id DESC").first
assert_equal vertices(:vertex_1), assert_no_queries { sink.sources.first.sources.first.sources.first.sources.first }
end
@@ -183,6 +183,6 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase
assert_equal 1, authors[1].comments.size
assert_equal 5, authors[0].posts.size
assert_equal 3, authors[1].posts.size
- assert_equal 3, authors[0].posts.collect { |post| post.categorizations.size }.inject(0) { |sum, i| sum+i }
+ assert_equal 3, authors[0].posts.collect { |post| post.categorizations.size }.inject(0) { |sum, i| sum + i }
end
end
diff --git a/activerecord/test/cases/associations/eager_load_includes_full_sti_class_test.rb b/activerecord/test/cases/associations/eager_load_includes_full_sti_class_test.rb
index 75a6295350..4f0fe3236e 100644
--- a/activerecord/test/cases/associations/eager_load_includes_full_sti_class_test.rb
+++ b/activerecord/test/cases/associations/eager_load_includes_full_sti_class_test.rb
@@ -1,34 +1,33 @@
-require 'cases/helper'
-require 'models/post'
-require 'models/tagging'
+require "cases/helper"
+require "models/post"
+require "models/tagging"
module Namespaced
class Post < ActiveRecord::Base
- self.table_name = 'posts'
- has_one :tagging, :as => :taggable, :class_name => 'Tagging'
+ self.table_name = "posts"
+ has_one :tagging, as: :taggable, class_name: "Tagging"
end
end
class EagerLoadIncludeFullStiClassNamesTest < ActiveRecord::TestCase
-
def setup
generate_test_objects
end
def generate_test_objects
- post = Namespaced::Post.create( :title => 'Great stuff', :body => 'This is not', :author_id => 1 )
- Tagging.create( :taggable => post )
+ post = Namespaced::Post.create(title: "Great stuff", body: "This is not", author_id: 1)
+ Tagging.create(taggable: post)
end
def test_class_names
old = ActiveRecord::Base.store_full_sti_class
ActiveRecord::Base.store_full_sti_class = false
- post = Namespaced::Post.includes(:tagging).find_by_title('Great stuff')
+ post = Namespaced::Post.includes(:tagging).find_by_title("Great stuff")
assert_nil post.tagging
ActiveRecord::Base.store_full_sti_class = true
- post = Namespaced::Post.includes(:tagging).find_by_title('Great stuff')
+ post = Namespaced::Post.includes(:tagging).find_by_title("Great stuff")
assert_instance_of Tagging, post.tagging
ensure
ActiveRecord::Base.store_full_sti_class = old
diff --git a/activerecord/test/cases/associations/eager_load_nested_include_test.rb b/activerecord/test/cases/associations/eager_load_nested_include_test.rb
index f571198079..e9f551b6b2 100644
--- a/activerecord/test/cases/associations/eager_load_nested_include_test.rb
+++ b/activerecord/test/cases/associations/eager_load_nested_include_test.rb
@@ -1,18 +1,18 @@
-require 'cases/helper'
-require 'models/post'
-require 'models/tag'
-require 'models/author'
-require 'models/comment'
-require 'models/category'
-require 'models/categorization'
-require 'models/tagging'
+require "cases/helper"
+require "models/post"
+require "models/tag"
+require "models/author"
+require "models/comment"
+require "models/category"
+require "models/categorization"
+require "models/tagging"
module Remembered
extend ActiveSupport::Concern
included do
after_create :remember
- protected
+ private
def remember; self.class.remembered << self; end
end
@@ -23,30 +23,30 @@ module Remembered
end
class ShapeExpression < ActiveRecord::Base
- belongs_to :shape, :polymorphic => true
- belongs_to :paint, :polymorphic => true
+ belongs_to :shape, polymorphic: true
+ belongs_to :paint, polymorphic: true
end
class Circle < ActiveRecord::Base
- has_many :shape_expressions, :as => :shape
+ has_many :shape_expressions, as: :shape
include Remembered
end
class Square < ActiveRecord::Base
- has_many :shape_expressions, :as => :shape
+ has_many :shape_expressions, as: :shape
include Remembered
end
class Triangle < ActiveRecord::Base
- has_many :shape_expressions, :as => :shape
+ has_many :shape_expressions, as: :shape
include Remembered
end
-class PaintColor < ActiveRecord::Base
- has_many :shape_expressions, :as => :paint
- belongs_to :non_poly, :foreign_key => "non_poly_one_id", :class_name => "NonPolyOne"
+class PaintColor < ActiveRecord::Base
+ has_many :shape_expressions, as: :paint
+ belongs_to :non_poly, foreign_key: "non_poly_one_id", class_name: "NonPolyOne"
include Remembered
end
class PaintTexture < ActiveRecord::Base
- has_many :shape_expressions, :as => :paint
- belongs_to :non_poly, :foreign_key => "non_poly_two_id", :class_name => "NonPolyTwo"
+ has_many :shape_expressions, as: :paint
+ belongs_to :non_poly, foreign_key: "non_poly_two_id", class_name: "NonPolyTwo"
include Remembered
end
class NonPolyOne < ActiveRecord::Base
@@ -58,8 +58,6 @@ class NonPolyTwo < ActiveRecord::Base
include Remembered
end
-
-
class EagerLoadPolyAssocsTest < ActiveRecord::TestCase
NUM_SIMPLE_OBJS = 50
NUM_SHAPE_EXPRESSIONS = 100
@@ -78,19 +76,19 @@ class EagerLoadPolyAssocsTest < ActiveRecord::TestCase
[Circle, Square, Triangle, NonPolyOne, NonPolyTwo].map(&:create!)
end
1.upto(NUM_SIMPLE_OBJS) do
- PaintColor.create!(:non_poly_one_id => NonPolyOne.sample.id)
- PaintTexture.create!(:non_poly_two_id => NonPolyTwo.sample.id)
+ PaintColor.create!(non_poly_one_id: NonPolyOne.sample.id)
+ PaintTexture.create!(non_poly_two_id: NonPolyTwo.sample.id)
end
1.upto(NUM_SHAPE_EXPRESSIONS) do
shape_type = [Circle, Square, Triangle].sample
paint_type = [PaintColor, PaintTexture].sample
- ShapeExpression.create!(:shape_type => shape_type.to_s, :shape_id => shape_type.sample.id,
- :paint_type => paint_type.to_s, :paint_id => paint_type.sample.id)
+ ShapeExpression.create!(shape_type: shape_type.to_s, shape_id: shape_type.sample.id,
+ paint_type: paint_type.to_s, paint_id: paint_type.sample.id)
end
end
def test_include_query
- res = ShapeExpression.all.merge!(:includes => [ :shape, { :paint => :non_poly } ]).to_a
+ res = ShapeExpression.all.merge!(includes: [ :shape, { paint: :non_poly } ]).to_a
assert_equal NUM_SHAPE_EXPRESSIONS, res.size
assert_queries(0) do
res.each do |se|
@@ -103,10 +101,10 @@ end
class EagerLoadNestedIncludeWithMissingDataTest < ActiveRecord::TestCase
def setup
- @davey_mcdave = Author.create(:name => 'Davey McDave')
- @first_post = @davey_mcdave.posts.create(:title => 'Davey Speaks', :body => 'Expressive wordage')
- @first_comment = @first_post.comments.create(:body => 'Inflamatory doublespeak')
- @first_categorization = @davey_mcdave.categorizations.create(:category => Category.first, :post => @first_post)
+ @davey_mcdave = Author.create(name: "Davey McDave")
+ @first_post = @davey_mcdave.posts.create(title: "Davey Speaks", body: "Expressive wordage")
+ @first_comment = @first_post.comments.create(body: "Inflamatory doublespeak")
+ @first_categorization = @davey_mcdave.categorizations.create(category: Category.first, post: @first_post)
end
teardown do
@@ -119,8 +117,8 @@ class EagerLoadNestedIncludeWithMissingDataTest < ActiveRecord::TestCase
def test_missing_data_in_a_nested_include_should_not_cause_errors_when_constructing_objects
assert_nothing_raised do
# @davey_mcdave doesn't have any author_favorites
- includes = {:posts => :comments, :categorizations => :category, :author_favorites => :favorite_author }
- Author.all.merge!(:includes => includes, :where => {:authors => {:name => @davey_mcdave.name}}, :order => 'categories.name').to_a
+ includes = { posts: :comments, categorizations: :category, author_favorites: :favorite_author }
+ Author.all.merge!(includes: includes, where: { authors: { name: @davey_mcdave.name } }, order: "categories.name").to_a
end
end
end
diff --git a/activerecord/test/cases/associations/eager_singularization_test.rb b/activerecord/test/cases/associations/eager_singularization_test.rb
index a61a070331..5d1c1c4b9b 100644
--- a/activerecord/test/cases/associations/eager_singularization_test.rb
+++ b/activerecord/test/cases/associations/eager_singularization_test.rb
@@ -1,148 +1,147 @@
require "cases/helper"
-
if ActiveRecord::Base.connection.supports_migrations?
-class EagerSingularizationTest < ActiveRecord::TestCase
- class Virus < ActiveRecord::Base
- belongs_to :octopus
- end
-
- class Octopus < ActiveRecord::Base
- has_one :virus
- end
-
- class Pass < ActiveRecord::Base
- belongs_to :bus
- end
-
- class Bus < ActiveRecord::Base
- has_many :passes
- end
-
- class Mess < ActiveRecord::Base
- has_and_belongs_to_many :crises
- end
-
- class Crisis < ActiveRecord::Base
- has_and_belongs_to_many :messes
- has_many :analyses, :dependent => :destroy
- has_many :successes, :through => :analyses
- has_many :dresses, :dependent => :destroy
- has_many :compresses, :through => :dresses
- end
-
- class Analysis < ActiveRecord::Base
- belongs_to :crisis
- belongs_to :success
- end
-
- class Success < ActiveRecord::Base
- has_many :analyses, :dependent => :destroy
- has_many :crises, :through => :analyses
- end
+ class EagerSingularizationTest < ActiveRecord::TestCase
+ class Virus < ActiveRecord::Base
+ belongs_to :octopus
+ end
+
+ class Octopus < ActiveRecord::Base
+ has_one :virus
+ end
+
+ class Pass < ActiveRecord::Base
+ belongs_to :bus
+ end
- class Dress < ActiveRecord::Base
- belongs_to :crisis
- has_many :compresses
- end
+ class Bus < ActiveRecord::Base
+ has_many :passes
+ end
- class Compress < ActiveRecord::Base
- belongs_to :dress
- end
-
- def setup
- connection.create_table :viri do |t|
- t.column :octopus_id, :integer
- t.column :species, :string
- end
- connection.create_table :octopi do |t|
- t.column :species, :string
+ class Mess < ActiveRecord::Base
+ has_and_belongs_to_many :crises
end
- connection.create_table :passes do |t|
- t.column :bus_id, :integer
- t.column :rides, :integer
- end
- connection.create_table :buses do |t|
- t.column :name, :string
- end
- connection.create_table :crises_messes, :id => false do |t|
- t.column :crisis_id, :integer
- t.column :mess_id, :integer
- end
- connection.create_table :messes do |t|
- t.column :name, :string
- end
- connection.create_table :crises do |t|
- t.column :name, :string
- end
- connection.create_table :successes do |t|
- t.column :name, :string
- end
- connection.create_table :analyses do |t|
- t.column :crisis_id, :integer
- t.column :success_id, :integer
- end
- connection.create_table :dresses do |t|
- t.column :crisis_id, :integer
+
+ class Crisis < ActiveRecord::Base
+ has_and_belongs_to_many :messes
+ has_many :analyses, dependent: :destroy
+ has_many :successes, through: :analyses
+ has_many :dresses, dependent: :destroy
+ has_many :compresses, through: :dresses
end
- connection.create_table :compresses do |t|
- t.column :dress_id, :integer
+
+ class Analysis < ActiveRecord::Base
+ belongs_to :crisis
+ belongs_to :success
end
- end
- teardown do
- connection.drop_table :viri
- connection.drop_table :octopi
- connection.drop_table :passes
- connection.drop_table :buses
- connection.drop_table :crises_messes
- connection.drop_table :messes
- connection.drop_table :crises
- connection.drop_table :successes
- connection.drop_table :analyses
- connection.drop_table :dresses
- connection.drop_table :compresses
- end
+ class Success < ActiveRecord::Base
+ has_many :analyses, dependent: :destroy
+ has_many :crises, through: :analyses
+ end
- def connection
- ActiveRecord::Base.connection
- end
+ class Dress < ActiveRecord::Base
+ belongs_to :crisis
+ has_many :compresses
+ end
- def test_eager_no_extra_singularization_belongs_to
- assert_nothing_raised do
- Virus.all.merge!(:includes => :octopus).to_a
+ class Compress < ActiveRecord::Base
+ belongs_to :dress
end
- end
- def test_eager_no_extra_singularization_has_one
- assert_nothing_raised do
- Octopus.all.merge!(:includes => :virus).to_a
+ def setup
+ connection.create_table :viri do |t|
+ t.column :octopus_id, :integer
+ t.column :species, :string
+ end
+ connection.create_table :octopi do |t|
+ t.column :species, :string
+ end
+ connection.create_table :passes do |t|
+ t.column :bus_id, :integer
+ t.column :rides, :integer
+ end
+ connection.create_table :buses do |t|
+ t.column :name, :string
+ end
+ connection.create_table :crises_messes, id: false do |t|
+ t.column :crisis_id, :integer
+ t.column :mess_id, :integer
+ end
+ connection.create_table :messes do |t|
+ t.column :name, :string
+ end
+ connection.create_table :crises do |t|
+ t.column :name, :string
+ end
+ connection.create_table :successes do |t|
+ t.column :name, :string
+ end
+ connection.create_table :analyses do |t|
+ t.column :crisis_id, :integer
+ t.column :success_id, :integer
+ end
+ connection.create_table :dresses do |t|
+ t.column :crisis_id, :integer
+ end
+ connection.create_table :compresses do |t|
+ t.column :dress_id, :integer
+ end
end
- end
- def test_eager_no_extra_singularization_has_many
- assert_nothing_raised do
- Bus.all.merge!(:includes => :passes).to_a
+ teardown do
+ connection.drop_table :viri
+ connection.drop_table :octopi
+ connection.drop_table :passes
+ connection.drop_table :buses
+ connection.drop_table :crises_messes
+ connection.drop_table :messes
+ connection.drop_table :crises
+ connection.drop_table :successes
+ connection.drop_table :analyses
+ connection.drop_table :dresses
+ connection.drop_table :compresses
end
- end
- def test_eager_no_extra_singularization_has_and_belongs_to_many
- assert_nothing_raised do
- Crisis.all.merge!(:includes => :messes).to_a
- Mess.all.merge!(:includes => :crises).to_a
+ def connection
+ ActiveRecord::Base.connection
end
- end
- def test_eager_no_extra_singularization_has_many_through_belongs_to
- assert_nothing_raised do
- Crisis.all.merge!(:includes => :successes).to_a
+ def test_eager_no_extra_singularization_belongs_to
+ assert_nothing_raised do
+ Virus.all.merge!(includes: :octopus).to_a
+ end
end
- end
- def test_eager_no_extra_singularization_has_many_through_has_many
- assert_nothing_raised do
- Crisis.all.merge!(:includes => :compresses).to_a
+ def test_eager_no_extra_singularization_has_one
+ assert_nothing_raised do
+ Octopus.all.merge!(includes: :virus).to_a
+ end
+ end
+
+ def test_eager_no_extra_singularization_has_many
+ assert_nothing_raised do
+ Bus.all.merge!(includes: :passes).to_a
+ end
+ end
+
+ def test_eager_no_extra_singularization_has_and_belongs_to_many
+ assert_nothing_raised do
+ Crisis.all.merge!(includes: :messes).to_a
+ Mess.all.merge!(includes: :crises).to_a
+ end
+ end
+
+ def test_eager_no_extra_singularization_has_many_through_belongs_to
+ assert_nothing_raised do
+ Crisis.all.merge!(includes: :successes).to_a
+ end
+ end
+
+ def test_eager_no_extra_singularization_has_many_through_has_many
+ assert_nothing_raised do
+ Crisis.all.merge!(includes: :compresses).to_a
+ end
end
end
end
-end
diff --git a/activerecord/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb
index 80d9a6083b..ff1bf8acd4 100644
--- a/activerecord/test/cases/associations/eager_test.rb
+++ b/activerecord/test/cases/associations/eager_test.rb
@@ -1,31 +1,31 @@
require "cases/helper"
-require 'models/post'
-require 'models/tagging'
-require 'models/tag'
-require 'models/comment'
-require 'models/author'
-require 'models/essay'
-require 'models/category'
-require 'models/company'
-require 'models/person'
-require 'models/reader'
-require 'models/owner'
-require 'models/pet'
-require 'models/reference'
-require 'models/job'
-require 'models/subscriber'
-require 'models/subscription'
-require 'models/book'
-require 'models/developer'
-require 'models/computer'
-require 'models/project'
-require 'models/member'
-require 'models/membership'
-require 'models/club'
-require 'models/categorization'
-require 'models/sponsor'
-require 'models/mentor'
-require 'models/contract'
+require "models/post"
+require "models/tagging"
+require "models/tag"
+require "models/comment"
+require "models/author"
+require "models/essay"
+require "models/category"
+require "models/company"
+require "models/person"
+require "models/reader"
+require "models/owner"
+require "models/pet"
+require "models/reference"
+require "models/job"
+require "models/subscriber"
+require "models/subscription"
+require "models/book"
+require "models/developer"
+require "models/computer"
+require "models/project"
+require "models/member"
+require "models/membership"
+require "models/club"
+require "models/categorization"
+require "models/sponsor"
+require "models/mentor"
+require "models/contract"
class EagerAssociationTest < ActiveRecord::TestCase
fixtures :posts, :comments, :authors, :essays, :author_addresses, :categories, :categories_posts,
@@ -34,42 +34,42 @@ class EagerAssociationTest < ActiveRecord::TestCase
:developers, :projects, :developers_projects, :members, :memberships, :clubs, :sponsors
def test_eager_with_has_one_through_join_model_with_conditions_on_the_through
- member = Member.all.merge!(:includes => :favourite_club).find(members(:some_other_guy).id)
+ member = Member.all.merge!(includes: :favourite_club).find(members(:some_other_guy).id)
assert_nil member.favourite_club
end
def test_loading_with_one_association
- posts = Post.all.merge!(:includes => :comments).to_a
+ posts = Post.all.merge!(includes: :comments).to_a
post = posts.find { |p| p.id == 1 }
assert_equal 2, post.comments.size
- assert post.comments.include?(comments(:greetings))
+ assert_includes post.comments, comments(:greetings)
- post = Post.all.merge!(:includes => :comments, :where => "posts.title = 'Welcome to the weblog'").first
+ post = Post.all.merge!(includes: :comments, where: "posts.title = 'Welcome to the weblog'").first
assert_equal 2, post.comments.size
- assert post.comments.include?(comments(:greetings))
+ assert_includes post.comments, comments(:greetings)
- posts = Post.all.merge!(:includes => :last_comment).to_a
+ posts = Post.all.merge!(includes: :last_comment).to_a
post = posts.find { |p| p.id == 1 }
assert_equal Post.find(1).last_comment, post.last_comment
end
def test_loading_with_one_association_with_non_preload
- posts = Post.all.merge!(:includes => :last_comment, :order => 'comments.id DESC').to_a
+ posts = Post.all.merge!(includes: :last_comment, order: "comments.id DESC").to_a
post = posts.find { |p| p.id == 1 }
assert_equal Post.find(1).last_comment, post.last_comment
end
def test_loading_conditions_with_or
posts = authors(:david).posts.references(:comments).merge(
- :includes => :comments,
- :where => "comments.body like 'Normal%' OR comments.#{QUOTED_TYPE} = 'SpecialComment'"
+ includes: :comments,
+ where: "comments.body like 'Normal%' OR comments.#{QUOTED_TYPE} = 'SpecialComment'"
).to_a
assert_nil posts.detect { |p| p.author_id != authors(:david).id },
"expected to find only david's posts"
end
def test_with_ordering
- list = Post.all.merge!(:includes => :comments, :order => "posts.id DESC").to_a
+ list = Post.all.merge!(includes: :comments, order: "posts.id DESC").to_a
[:other_by_mary, :other_by_bob, :misc_by_mary, :misc_by_bob, :eager_other,
:sti_habtm, :sti_post_and_comments, :sti_comments, :authorless, :thinking, :welcome
].each_with_index do |post, index|
@@ -100,43 +100,43 @@ class EagerAssociationTest < ActiveRecord::TestCase
end
def test_loading_with_multiple_associations
- posts = Post.all.merge!(:includes => [ :comments, :author, :categories ], :order => "posts.id").to_a
+ posts = Post.all.merge!(includes: [ :comments, :author, :categories ], order: "posts.id").to_a
assert_equal 2, posts.first.comments.size
assert_equal 2, posts.first.categories.size
- assert posts.first.comments.include?(comments(:greetings))
+ assert_includes posts.first.comments, comments(:greetings)
end
def test_duplicate_middle_objects
- comments = Comment.all.merge!(:where => 'post_id = 1', :includes => [:post => :author]).to_a
+ comments = Comment.all.merge!(where: "post_id = 1", includes: [post: :author]).to_a
assert_no_queries do
- comments.each {|comment| comment.post.author.name}
+ comments.each { |comment| comment.post.author.name }
end
end
def test_preloading_has_many_in_multiple_queries_with_more_ids_than_database_can_handle
assert_called(Comment.connection, :in_clause_length, returns: 5) do
- posts = Post.all.merge!(:includes=>:comments).to_a
+ posts = Post.all.merge!(includes: :comments).to_a
assert_equal 11, posts.size
end
end
def test_preloading_has_many_in_one_queries_when_database_has_no_limit_on_ids_it_can_handle
assert_called(Comment.connection, :in_clause_length, returns: nil) do
- posts = Post.all.merge!(:includes=>:comments).to_a
+ posts = Post.all.merge!(includes: :comments).to_a
assert_equal 11, posts.size
end
end
def test_preloading_habtm_in_multiple_queries_with_more_ids_than_database_can_handle
assert_called(Comment.connection, :in_clause_length, times: 2, returns: 5) do
- posts = Post.all.merge!(:includes=>:categories).to_a
+ posts = Post.all.merge!(includes: :categories).to_a
assert_equal 11, posts.size
end
end
def test_preloading_habtm_in_one_queries_when_database_has_no_limit_on_ids_it_can_handle
assert_called(Comment.connection, :in_clause_length, times: 2, returns: nil) do
- posts = Post.all.merge!(:includes=>:categories).to_a
+ posts = Post.all.merge!(includes: :categories).to_a
assert_equal 11, posts.size
end
end
@@ -145,7 +145,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
assert_called(Comment.connection, :in_clause_length, returns: nil) do
post = posts(:welcome)
assert_queries(2) do
- Post.includes(:comments).where(:id => post.id).to_a
+ Post.includes(:comments).where(id: post.id).to_a
end
end
end
@@ -154,7 +154,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
assert_called(Comment.connection, :in_clause_length, returns: 1) do
post1, post2 = posts(:welcome), posts(:thinking)
assert_queries(3) do
- Post.includes(:comments).where(:id => [post1.id, post2.id]).to_a
+ Post.includes(:comments).where(id: [post1.id, post2.id]).to_a
end
end
end
@@ -163,50 +163,50 @@ class EagerAssociationTest < ActiveRecord::TestCase
assert_called(Comment.connection, :in_clause_length, returns: 3) do
post = posts(:welcome)
assert_queries(2) do
- Post.includes(:comments).where(:id => post.id).to_a
+ Post.includes(:comments).where(id: post.id).to_a
end
end
end
def test_including_duplicate_objects_from_belongs_to
- popular_post = Post.create!(:title => 'foo', :body => "I like cars!")
- comment = popular_post.comments.create!(:body => "lol")
- popular_post.readers.create!(:person => people(:michael))
- popular_post.readers.create!(:person => people(:david))
+ popular_post = Post.create!(title: "foo", body: "I like cars!")
+ comment = popular_post.comments.create!(body: "lol")
+ popular_post.readers.create!(person: people(:michael))
+ popular_post.readers.create!(person: people(:david))
- readers = Reader.all.merge!(:where => ["post_id = ?", popular_post.id],
- :includes => {:post => :comments}).to_a
+ readers = Reader.all.merge!(where: ["post_id = ?", popular_post.id],
+ includes: { post: :comments }).to_a
readers.each do |reader|
assert_equal [comment], reader.post.comments
end
end
def test_including_duplicate_objects_from_has_many
- car_post = Post.create!(:title => 'foo', :body => "I like cars!")
+ car_post = Post.create!(title: "foo", body: "I like cars!")
car_post.categories << categories(:general)
car_post.categories << categories(:technology)
- comment = car_post.comments.create!(:body => "hmm")
- categories = Category.all.merge!(:where => { 'posts.id' => car_post.id },
- :includes => {:posts => :comments}).to_a
+ comment = car_post.comments.create!(body: "hmm")
+ categories = Category.all.merge!(where: { "posts.id" => car_post.id },
+ includes: { posts: :comments }).to_a
categories.each do |category|
assert_equal [comment], category.posts[0].comments
end
end
def test_associations_loaded_for_all_records
- post = Post.create!(:title => 'foo', :body => "I like cars!")
- SpecialComment.create!(:body => 'Come on!', :post => post)
- first_category = Category.create! :name => 'First!', :posts => [post]
- second_category = Category.create! :name => 'Second!', :posts => [post]
+ post = Post.create!(title: "foo", body: "I like cars!")
+ SpecialComment.create!(body: "Come on!", post: post)
+ first_category = Category.create! name: "First!", posts: [post]
+ second_category = Category.create! name: "Second!", posts: [post]
- categories = Category.where(:id => [first_category.id, second_category.id]).includes(:posts => :special_comments)
+ categories = Category.where(id: [first_category.id, second_category.id]).includes(posts: :special_comments)
assert_equal categories.map { |category| category.posts.first.special_comments.loaded? }, [true, true]
end
def test_finding_with_includes_on_has_many_association_with_same_include_includes_only_once
author_id = authors(:david).id
- author = assert_queries(3) { Author.all.merge!(:includes => {:posts_with_comments => :comments}).find(author_id) } # find the author, then find the posts, then find the comments
+ author = assert_queries(3) { Author.all.merge!(includes: { posts_with_comments: :comments }).find(author_id) } # find the author, then find the posts, then find the comments
author.posts_with_comments.each do |post_with_comments|
assert_equal post_with_comments.comments.length, post_with_comments.comments.count
assert_nil post_with_comments.comments.to_a.uniq!
@@ -217,7 +217,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
author = authors(:david)
post = author.post_about_thinking_with_last_comment
last_comment = post.last_comment
- author = assert_queries(3) { Author.all.merge!(:includes => {:post_about_thinking_with_last_comment => :last_comment}).find(author.id)} # find the author, then find the posts, then find the comments
+ author = assert_queries(3) { Author.all.merge!(includes: { post_about_thinking_with_last_comment: :last_comment }).find(author.id) } # find the author, then find the posts, then find the comments
assert_no_queries do
assert_equal post, author.post_about_thinking_with_last_comment
assert_equal last_comment, author.post_about_thinking_with_last_comment.last_comment
@@ -228,7 +228,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
post = posts(:welcome)
author = post.author
author_address = author.author_address
- post = assert_queries(3) { Post.all.merge!(:includes => {:author_with_address => :author_address}).find(post.id) } # find the post, then find the author, then find the address
+ post = assert_queries(3) { Post.all.merge!(includes: { author_with_address: :author_address }).find(post.id) } # find the post, then find the author, then find the address
assert_no_queries do
assert_equal author, post.author_with_address
assert_equal author_address, post.author_with_address.author_address
@@ -238,53 +238,53 @@ class EagerAssociationTest < ActiveRecord::TestCase
def test_finding_with_includes_on_null_belongs_to_association_with_same_include_includes_only_once
post = posts(:welcome)
post.update!(author: nil)
- post = assert_queries(1) { Post.all.merge!(includes: {author_with_address: :author_address}).find(post.id) }
+ post = assert_queries(1) { Post.all.merge!(includes: { author_with_address: :author_address }).find(post.id) }
# find the post, then find the author which is null so no query for the author or address
assert_no_queries do
- assert_equal nil, post.author_with_address
+ assert_nil post.author_with_address
end
end
def test_finding_with_includes_on_null_belongs_to_polymorphic_association
sponsor = sponsors(:moustache_club_sponsor_for_groucho)
sponsor.update!(sponsorable: nil)
- sponsor = assert_queries(1) { Sponsor.all.merge!(:includes => :sponsorable).find(sponsor.id) }
+ sponsor = assert_queries(1) { Sponsor.all.merge!(includes: :sponsorable).find(sponsor.id) }
assert_no_queries do
- assert_equal nil, sponsor.sponsorable
+ assert_nil sponsor.sponsorable
end
end
def test_finding_with_includes_on_empty_polymorphic_type_column
sponsor = sponsors(:moustache_club_sponsor_for_groucho)
- sponsor.update!(sponsorable_type: '', sponsorable_id: nil) # sponsorable_type column might be declared NOT NULL
+ sponsor.update!(sponsorable_type: "", sponsorable_id: nil) # sponsorable_type column might be declared NOT NULL
sponsor = assert_queries(1) do
- assert_nothing_raised { Sponsor.all.merge!(:includes => :sponsorable).find(sponsor.id) }
+ assert_nothing_raised { Sponsor.all.merge!(includes: :sponsorable).find(sponsor.id) }
end
assert_no_queries do
- assert_equal nil, sponsor.sponsorable
+ assert_nil sponsor.sponsorable
end
end
def test_loading_from_an_association
- posts = authors(:david).posts.merge(:includes => :comments, :order => "posts.id").to_a
+ posts = authors(:david).posts.merge(includes: :comments, order: "posts.id").to_a
assert_equal 2, posts.first.comments.size
end
def test_loading_from_an_association_that_has_a_hash_of_conditions
assert_nothing_raised do
- Author.all.merge!(:includes => :hello_posts_with_hash_conditions).to_a
+ Author.all.merge!(includes: :hello_posts_with_hash_conditions).to_a
end
- assert !Author.all.merge!(:includes => :hello_posts_with_hash_conditions).find(authors(:david).id).hello_posts.empty?
+ assert !Author.all.merge!(includes: :hello_posts_with_hash_conditions).find(authors(:david).id).hello_posts.empty?
end
def test_loading_with_no_associations
- assert_nil Post.all.merge!(:includes => :author).find(posts(:authorless).id).author
+ assert_nil Post.all.merge!(includes: :author).find(posts(:authorless).id).author
end
# Regression test for 21c75e5
def test_nested_loading_does_not_raise_exception_when_association_does_not_exist
assert_nothing_raised do
- Post.all.merge!(:includes => {:author => :author_addresss}).find(posts(:authorless).id)
+ Post.all.merge!(includes: { author: :author_addresss }).find(posts(:authorless).id)
end
end
@@ -292,117 +292,117 @@ class EagerAssociationTest < ActiveRecord::TestCase
post_id = Comment.where(author_id: nil).where.not(post_id: nil).first.post_id
assert_nothing_raised do
- Post.preload(:comments => [{:author => :essays}]).find(post_id)
+ Post.preload(comments: [{ author: :essays }]).find(post_id)
end
end
def test_nested_loading_through_has_one_association
- aa = AuthorAddress.all.merge!(:includes => {:author => :posts}).find(author_addresses(:david_address).id)
+ aa = AuthorAddress.all.merge!(includes: { author: :posts }).find(author_addresses(:david_address).id)
assert_equal aa.author.posts.count, aa.author.posts.length
end
def test_nested_loading_through_has_one_association_with_order
- aa = AuthorAddress.all.merge!(:includes => {:author => :posts}, :order => 'author_addresses.id').find(author_addresses(:david_address).id)
+ aa = AuthorAddress.all.merge!(includes: { author: :posts }, order: "author_addresses.id").find(author_addresses(:david_address).id)
assert_equal aa.author.posts.count, aa.author.posts.length
end
def test_nested_loading_through_has_one_association_with_order_on_association
- aa = AuthorAddress.all.merge!(:includes => {:author => :posts}, :order => 'authors.id').find(author_addresses(:david_address).id)
+ aa = AuthorAddress.all.merge!(includes: { author: :posts }, order: "authors.id").find(author_addresses(:david_address).id)
assert_equal aa.author.posts.count, aa.author.posts.length
end
def test_nested_loading_through_has_one_association_with_order_on_nested_association
- aa = AuthorAddress.all.merge!(:includes => {:author => :posts}, :order => 'posts.id').find(author_addresses(:david_address).id)
+ aa = AuthorAddress.all.merge!(includes: { author: :posts }, order: "posts.id").find(author_addresses(:david_address).id)
assert_equal aa.author.posts.count, aa.author.posts.length
end
def test_nested_loading_through_has_one_association_with_conditions
aa = AuthorAddress.references(:author_addresses).merge(
- :includes => {:author => :posts},
- :where => "author_addresses.id > 0"
+ includes: { author: :posts },
+ where: "author_addresses.id > 0"
).find author_addresses(:david_address).id
assert_equal aa.author.posts.count, aa.author.posts.length
end
def test_nested_loading_through_has_one_association_with_conditions_on_association
aa = AuthorAddress.references(:authors).merge(
- :includes => {:author => :posts},
- :where => "authors.id > 0"
+ includes: { author: :posts },
+ where: "authors.id > 0"
).find author_addresses(:david_address).id
assert_equal aa.author.posts.count, aa.author.posts.length
end
def test_nested_loading_through_has_one_association_with_conditions_on_nested_association
aa = AuthorAddress.references(:posts).merge(
- :includes => {:author => :posts},
- :where => "posts.id > 0"
+ includes: { author: :posts },
+ where: "posts.id > 0"
).find author_addresses(:david_address).id
assert_equal aa.author.posts.count, aa.author.posts.length
end
def test_eager_association_loading_with_belongs_to_and_foreign_keys
- pets = Pet.all.merge!(:includes => :owner).to_a
+ pets = Pet.all.merge!(includes: :owner).to_a
assert_equal 4, pets.length
end
def test_eager_association_loading_with_belongs_to
- comments = Comment.all.merge!(:includes => :post).to_a
+ comments = Comment.all.merge!(includes: :post).to_a
assert_equal 11, comments.length
titles = comments.map { |c| c.post.title }
- assert titles.include?(posts(:welcome).title)
- assert titles.include?(posts(:sti_post_and_comments).title)
+ assert_includes titles, posts(:welcome).title
+ assert_includes titles, posts(:sti_post_and_comments).title
end
def test_eager_association_loading_with_belongs_to_and_limit
- comments = Comment.all.merge!(:includes => :post, :limit => 5, :order => 'comments.id').to_a
+ comments = Comment.all.merge!(includes: :post, limit: 5, order: "comments.id").to_a
assert_equal 5, comments.length
- assert_equal [1,2,3,5,6], comments.collect(&:id)
+ assert_equal [1, 2, 3, 5, 6], comments.collect(&:id)
end
def test_eager_association_loading_with_belongs_to_and_limit_and_conditions
- comments = Comment.all.merge!(:includes => :post, :where => 'post_id = 4', :limit => 3, :order => 'comments.id').to_a
+ comments = Comment.all.merge!(includes: :post, where: "post_id = 4", limit: 3, order: "comments.id").to_a
assert_equal 3, comments.length
- assert_equal [5,6,7], comments.collect(&:id)
+ assert_equal [5, 6, 7], comments.collect(&:id)
end
def test_eager_association_loading_with_belongs_to_and_limit_and_offset
- comments = Comment.all.merge!(:includes => :post, :limit => 3, :offset => 2, :order => 'comments.id').to_a
+ comments = Comment.all.merge!(includes: :post, limit: 3, offset: 2, order: "comments.id").to_a
assert_equal 3, comments.length
- assert_equal [3,5,6], comments.collect(&:id)
+ assert_equal [3, 5, 6], comments.collect(&:id)
end
def test_eager_association_loading_with_belongs_to_and_limit_and_offset_and_conditions
- comments = Comment.all.merge!(:includes => :post, :where => 'post_id = 4', :limit => 3, :offset => 1, :order => 'comments.id').to_a
+ comments = Comment.all.merge!(includes: :post, where: "post_id = 4", limit: 3, offset: 1, order: "comments.id").to_a
assert_equal 3, comments.length
- assert_equal [6,7,8], comments.collect(&:id)
+ assert_equal [6, 7, 8], comments.collect(&:id)
end
def test_eager_association_loading_with_belongs_to_and_limit_and_offset_and_conditions_array
- comments = Comment.all.merge!(:includes => :post, :where => ['post_id = ?',4], :limit => 3, :offset => 1, :order => 'comments.id').to_a
+ comments = Comment.all.merge!(includes: :post, where: ["post_id = ?", 4], limit: 3, offset: 1, order: "comments.id").to_a
assert_equal 3, comments.length
- assert_equal [6,7,8], comments.collect(&:id)
+ assert_equal [6, 7, 8], comments.collect(&:id)
end
def test_eager_association_loading_with_belongs_to_and_conditions_string_with_unquoted_table_name
assert_nothing_raised do
- Comment.includes(:post).references(:posts).where('posts.id = ?', 4)
+ Comment.includes(:post).references(:posts).where("posts.id = ?", 4)
end
end
def test_eager_association_loading_with_belongs_to_and_conditions_hash
comments = []
assert_nothing_raised do
- comments = Comment.all.merge!(:includes => :post, :where => {:posts => {:id => 4}}, :limit => 3, :order => 'comments.id').to_a
+ comments = Comment.all.merge!(includes: :post, where: { posts: { id: 4 } }, limit: 3, order: "comments.id").to_a
end
assert_equal 3, comments.length
- assert_equal [5,6,7], comments.collect(&:id)
+ assert_equal [5, 6, 7], comments.collect(&:id)
assert_no_queries do
comments.first.post
end
end
def test_eager_association_loading_with_belongs_to_and_conditions_string_with_quoted_table_name
- quoted_posts_id= Comment.connection.quote_table_name('posts') + '.' + Comment.connection.quote_column_name('id')
+ quoted_posts_id = Comment.connection.quote_table_name("posts") + "." + Comment.connection.quote_column_name("id")
assert_nothing_raised do
Comment.includes(:post).references(:posts).where("#{quoted_posts_id} = ?", 4)
end
@@ -410,61 +410,61 @@ class EagerAssociationTest < ActiveRecord::TestCase
def test_eager_association_loading_with_belongs_to_and_order_string_with_unquoted_table_name
assert_nothing_raised do
- Comment.all.merge!(:includes => :post, :order => 'posts.id').to_a
+ Comment.all.merge!(includes: :post, order: "posts.id").to_a
end
end
def test_eager_association_loading_with_belongs_to_and_order_string_with_quoted_table_name
- quoted_posts_id= Comment.connection.quote_table_name('posts') + '.' + Comment.connection.quote_column_name('id')
+ quoted_posts_id = Comment.connection.quote_table_name("posts") + "." + Comment.connection.quote_column_name("id")
assert_nothing_raised do
Comment.includes(:post).references(:posts).order(quoted_posts_id)
end
end
def test_eager_association_loading_with_belongs_to_and_limit_and_multiple_associations
- posts = Post.all.merge!(:includes => [:author, :very_special_comment], :limit => 1, :order => 'posts.id').to_a
+ posts = Post.all.merge!(includes: [:author, :very_special_comment], limit: 1, order: "posts.id").to_a
assert_equal 1, posts.length
assert_equal [1], posts.collect(&:id)
end
def test_eager_association_loading_with_belongs_to_and_limit_and_offset_and_multiple_associations
- posts = Post.all.merge!(:includes => [:author, :very_special_comment], :limit => 1, :offset => 1, :order => 'posts.id').to_a
+ posts = Post.all.merge!(includes: [:author, :very_special_comment], limit: 1, offset: 1, order: "posts.id").to_a
assert_equal 1, posts.length
assert_equal [2], posts.collect(&:id)
end
def test_eager_association_loading_with_belongs_to_inferred_foreign_key_from_association_name
- author_favorite = AuthorFavorite.all.merge!(:includes => :favorite_author).first
+ author_favorite = AuthorFavorite.all.merge!(includes: :favorite_author).first
assert_equal authors(:mary), assert_no_queries { author_favorite.favorite_author }
end
def test_eager_load_belongs_to_quotes_table_and_column_names
job = Job.includes(:ideal_reference).find jobs(:unicyclist).id
references(:michael_unicyclist)
- assert_no_queries{ assert_equal references(:michael_unicyclist), job.ideal_reference}
+ assert_no_queries { assert_equal references(:michael_unicyclist), job.ideal_reference }
end
def test_eager_load_has_one_quotes_table_and_column_names
- michael = Person.all.merge!(:includes => :favourite_reference).find(people(:michael).id)
+ michael = Person.all.merge!(includes: :favourite_reference).find(people(:michael).id)
references(:michael_unicyclist)
- assert_no_queries{ assert_equal references(:michael_unicyclist), michael.favourite_reference}
+ assert_no_queries { assert_equal references(:michael_unicyclist), michael.favourite_reference }
end
def test_eager_load_has_many_quotes_table_and_column_names
- michael = Person.all.merge!(:includes => :references).find(people(:michael).id)
- references(:michael_magician,:michael_unicyclist)
- assert_no_queries{ assert_equal references(:michael_magician,:michael_unicyclist), michael.references.sort_by(&:id) }
+ michael = Person.all.merge!(includes: :references).find(people(:michael).id)
+ references(:michael_magician, :michael_unicyclist)
+ assert_no_queries { assert_equal references(:michael_magician, :michael_unicyclist), michael.references.sort_by(&:id) }
end
def test_eager_load_has_many_through_quotes_table_and_column_names
- michael = Person.all.merge!(:includes => :jobs).find(people(:michael).id)
+ michael = Person.all.merge!(includes: :jobs).find(people(:michael).id)
jobs(:magician, :unicyclist)
- assert_no_queries{ assert_equal jobs(:unicyclist, :magician), michael.jobs.sort_by(&:id) }
+ assert_no_queries { assert_equal jobs(:unicyclist, :magician), michael.jobs.sort_by(&:id) }
end
def test_eager_load_has_many_with_string_keys
subscriptions = subscriptions(:webster_awdr, :webster_rfr)
- subscriber =Subscriber.all.merge!(:includes => :subscriptions).find(subscribers(:second).id)
+ subscriber = Subscriber.all.merge!(includes: :subscriptions).find(subscribers(:second).id)
assert_equal subscriptions, subscriber.subscriptions.sort_by(&:id)
end
@@ -475,32 +475,32 @@ class EagerAssociationTest < ActiveRecord::TestCase
b = Book.create!
- Subscription.create!(:subscriber_id => "PL", :book_id => b.id)
+ Subscription.create!(subscriber_id: "PL", book_id: b.id)
s.reload
s.book_ids = s.book_ids
end
def test_eager_load_has_many_through_with_string_keys
books = books(:awdr, :rfr)
- subscriber = Subscriber.all.merge!(:includes => :books).find(subscribers(:second).id)
+ subscriber = Subscriber.all.merge!(includes: :books).find(subscribers(:second).id)
assert_equal books, subscriber.books.sort_by(&:id)
end
def test_eager_load_belongs_to_with_string_keys
subscriber = subscribers(:second)
- subscription = Subscription.all.merge!(:includes => :subscriber).find(subscriptions(:webster_awdr).id)
+ subscription = Subscription.all.merge!(includes: :subscriber).find(subscriptions(:webster_awdr).id)
assert_equal subscriber, subscription.subscriber
end
def test_eager_association_loading_with_explicit_join
- posts = Post.all.merge!(:includes => :comments, :joins => "INNER JOIN authors ON posts.author_id = authors.id AND authors.name = 'Mary'", :limit => 1, :order => 'author_id').to_a
+ posts = Post.all.merge!(includes: :comments, joins: "INNER JOIN authors ON posts.author_id = authors.id AND authors.name = 'Mary'", limit: 1, order: "author_id").to_a
assert_equal 1, posts.length
end
def test_eager_with_has_many_through
- posts_with_comments = people(:michael).posts.merge(:includes => :comments, :order => 'posts.id').to_a
- posts_with_author = people(:michael).posts.merge(:includes => :author, :order => 'posts.id').to_a
- posts_with_comments_and_author = people(:michael).posts.merge(:includes => [ :comments, :author ], :order => 'posts.id').to_a
+ posts_with_comments = people(:michael).posts.merge(includes: :comments, order: "posts.id").to_a
+ posts_with_author = people(:michael).posts.merge(includes: :author, order: "posts.id").to_a
+ posts_with_comments_and_author = people(:michael).posts.merge(includes: [ :comments, :author ], order: "posts.id").to_a
assert_equal 2, posts_with_comments.inject(0) { |sum, post| sum + post.comments.size }
assert_equal authors(:david), assert_no_queries { posts_with_author.first.author }
assert_equal authors(:david), assert_no_queries { posts_with_comments_and_author.first.author }
@@ -508,35 +508,35 @@ class EagerAssociationTest < ActiveRecord::TestCase
def test_eager_with_has_many_through_a_belongs_to_association
author = authors(:mary)
- Post.create!(:author => author, :title => "TITLE", :body => "BODY")
- author.author_favorites.create(:favorite_author_id => 1)
- author.author_favorites.create(:favorite_author_id => 2)
- posts_with_author_favorites = author.posts.merge(:includes => :author_favorites).to_a
+ Post.create!(author: author, title: "TITLE", body: "BODY")
+ author.author_favorites.create(favorite_author_id: 1)
+ author.author_favorites.create(favorite_author_id: 2)
+ posts_with_author_favorites = author.posts.merge(includes: :author_favorites).to_a
assert_no_queries { posts_with_author_favorites.first.author_favorites.first.author_id }
end
def test_eager_with_has_many_through_an_sti_join_model
- author = Author.all.merge!(:includes => :special_post_comments, :order => 'authors.id').first
+ author = Author.all.merge!(includes: :special_post_comments, order: "authors.id").first
assert_equal [comments(:does_it_hurt)], assert_no_queries { author.special_post_comments }
end
def test_eager_with_has_many_through_an_sti_join_model_with_conditions_on_both
- author = Author.all.merge!(:includes => :special_nonexistent_post_comments, :order => 'authors.id').first
+ author = Author.all.merge!(includes: :special_nonexistent_post_comments, order: "authors.id").first
assert_equal [], author.special_nonexistent_post_comments
end
def test_eager_with_has_many_through_join_model_with_conditions
- assert_equal Author.all.merge!(:includes => :hello_post_comments,
- :order => 'authors.id').first.hello_post_comments.sort_by(&:id),
- Author.all.merge!(:order => 'authors.id').first.hello_post_comments.sort_by(&:id)
+ assert_equal Author.all.merge!(includes: :hello_post_comments,
+ order: "authors.id").first.hello_post_comments.sort_by(&:id),
+ Author.all.merge!(order: "authors.id").first.hello_post_comments.sort_by(&:id)
end
def test_eager_with_has_many_through_join_model_with_conditions_on_top_level
- assert_equal comments(:more_greetings), Author.all.merge!(:includes => :comments_with_order_and_conditions).find(authors(:david).id).comments_with_order_and_conditions.first
+ assert_equal comments(:more_greetings), Author.all.merge!(includes: :comments_with_order_and_conditions).find(authors(:david).id).comments_with_order_and_conditions.first
end
def test_eager_with_has_many_through_join_model_with_include
- author_comments = Author.all.merge!(:includes => :comments_with_include).find(authors(:david).id).comments_with_include.to_a
+ author_comments = Author.all.merge!(includes: :comments_with_include).find(authors(:david).id).comments_with_include.to_a
assert_no_queries do
author_comments.first.post.title
end
@@ -544,7 +544,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
def test_eager_with_has_many_through_with_conditions_join_model_with_include
post_tags = Post.find(posts(:welcome).id).misc_tags
- eager_post_tags = Post.all.merge!(:includes => :misc_tags).find(1).misc_tags
+ eager_post_tags = Post.all.merge!(includes: :misc_tags).find(1).misc_tags
assert_equal post_tags, eager_post_tags
end
@@ -555,67 +555,67 @@ class EagerAssociationTest < ActiveRecord::TestCase
end
def test_eager_with_has_many_and_limit
- posts = Post.all.merge!(:order => 'posts.id asc', :includes => [ :author, :comments ], :limit => 2).to_a
+ posts = Post.all.merge!(order: "posts.id asc", includes: [ :author, :comments ], limit: 2).to_a
assert_equal 2, posts.size
assert_equal 3, posts.inject(0) { |sum, post| sum + post.comments.size }
end
def test_eager_with_has_many_and_limit_and_conditions
- posts = Post.all.merge!(:includes => [ :author, :comments ], :limit => 2, :where => "posts.body = 'hello'", :order => "posts.id").to_a
+ posts = Post.all.merge!(includes: [ :author, :comments ], limit: 2, where: "posts.body = 'hello'", order: "posts.id").to_a
assert_equal 2, posts.size
- assert_equal [4,5], posts.collect(&:id)
+ assert_equal [4, 5], posts.collect(&:id)
end
def test_eager_with_has_many_and_limit_and_conditions_array
- posts = Post.all.merge!(:includes => [ :author, :comments ], :limit => 2, :where => [ "posts.body = ?", 'hello' ], :order => "posts.id").to_a
+ posts = Post.all.merge!(includes: [ :author, :comments ], limit: 2, where: [ "posts.body = ?", "hello" ], order: "posts.id").to_a
assert_equal 2, posts.size
- assert_equal [4,5], posts.collect(&:id)
+ assert_equal [4, 5], posts.collect(&:id)
end
def test_eager_with_has_many_and_limit_and_conditions_array_on_the_eagers
- posts = Post.includes(:author, :comments).limit(2).references(:author).where("authors.name = ?", 'David')
+ posts = Post.includes(:author, :comments).limit(2).references(:author).where("authors.name = ?", "David")
assert_equal 2, posts.size
- count = Post.includes(:author, :comments).limit(2).references(:author).where("authors.name = ?", 'David').count
+ count = Post.includes(:author, :comments).limit(2).references(:author).where("authors.name = ?", "David").count
assert_equal posts.size, count
end
def test_eager_with_has_many_and_limit_and_high_offset
- posts = Post.all.merge!(:includes => [ :author, :comments ], :limit => 2, :offset => 10, :where => { 'authors.name' => 'David' }).to_a
+ posts = Post.all.merge!(includes: [ :author, :comments ], limit: 2, offset: 10, where: { "authors.name" => "David" }).to_a
assert_equal 0, posts.size
end
def test_eager_with_has_many_and_limit_and_high_offset_and_multiple_array_conditions
assert_queries(1) do
posts = Post.references(:authors, :comments).
- merge(:includes => [ :author, :comments ], :limit => 2, :offset => 10,
- :where => [ "authors.name = ? and comments.body = ?", 'David', 'go crazy' ]).to_a
+ merge(includes: [ :author, :comments ], limit: 2, offset: 10,
+ where: [ "authors.name = ? and comments.body = ?", "David", "go crazy" ]).to_a
assert_equal 0, posts.size
end
end
def test_eager_with_has_many_and_limit_and_high_offset_and_multiple_hash_conditions
assert_queries(1) do
- posts = Post.all.merge!(:includes => [ :author, :comments ], :limit => 2, :offset => 10,
- :where => { 'authors.name' => 'David', 'comments.body' => 'go crazy' }).to_a
+ posts = Post.all.merge!(includes: [ :author, :comments ], limit: 2, offset: 10,
+ where: { "authors.name" => "David", "comments.body" => "go crazy" }).to_a
assert_equal 0, posts.size
end
end
def test_count_eager_with_has_many_and_limit_and_high_offset
- posts = Post.all.merge!(:includes => [ :author, :comments ], :limit => 2, :offset => 10, :where => { 'authors.name' => 'David' }).count(:all)
+ posts = Post.all.merge!(includes: [ :author, :comments ], limit: 2, offset: 10, where: { "authors.name" => "David" }).count(:all)
assert_equal 0, posts
end
def test_eager_with_has_many_and_limit_with_no_results
- posts = Post.all.merge!(:includes => [ :author, :comments ], :limit => 2, :where => "posts.title = 'magic forest'").to_a
+ posts = Post.all.merge!(includes: [ :author, :comments ], limit: 2, where: "posts.title = 'magic forest'").to_a
assert_equal 0, posts.size
end
def test_eager_count_performed_on_a_has_many_association_with_multi_table_conditional
author = authors(:david)
author_posts_without_comments = author.posts.select { |post| post.comments.blank? }
- assert_equal author_posts_without_comments.size, author.posts.includes(:comments).where('comments.id is null').references(:comments).count
+ assert_equal author_posts_without_comments.size, author.posts.includes(:comments).where("comments.id is null").references(:comments).count
end
def test_eager_count_performed_on_a_has_many_through_association_with_multi_table_conditional
@@ -625,13 +625,13 @@ class EagerAssociationTest < ActiveRecord::TestCase
end
def test_eager_with_has_and_belongs_to_many_and_limit
- posts = Post.all.merge!(:includes => :categories, :order => "posts.id", :limit => 3).to_a
+ posts = Post.all.merge!(includes: :categories, order: "posts.id", limit: 3).to_a
assert_equal 3, posts.size
assert_equal 2, posts[0].categories.size
assert_equal 1, posts[1].categories.size
assert_equal 0, posts[2].categories.size
- assert posts[0].categories.include?(categories(:technology))
- assert posts[1].categories.include?(categories(:general))
+ assert_includes posts[0].categories, categories(:technology)
+ assert_includes posts[1].categories, categories(:general)
end
# Since the preloader for habtm gets raw row hashes from the database and then
@@ -691,32 +691,32 @@ class EagerAssociationTest < ActiveRecord::TestCase
end
def test_eager_association_loading_with_habtm
- posts = Post.all.merge!(:includes => :categories, :order => "posts.id").to_a
+ posts = Post.all.merge!(includes: :categories, order: "posts.id").to_a
assert_equal 2, posts[0].categories.size
assert_equal 1, posts[1].categories.size
assert_equal 0, posts[2].categories.size
- assert posts[0].categories.include?(categories(:technology))
- assert posts[1].categories.include?(categories(:general))
+ assert_includes posts[0].categories, categories(:technology)
+ assert_includes posts[1].categories, categories(:general)
end
def test_eager_with_inheritance
- SpecialPost.all.merge!(:includes => [ :comments ]).to_a
+ SpecialPost.all.merge!(includes: [ :comments ]).to_a
end
def test_eager_has_one_with_association_inheritance
- post = Post.all.merge!(:includes => [ :very_special_comment ]).find(4)
+ post = Post.all.merge!(includes: [ :very_special_comment ]).find(4)
assert_equal "VerySpecialComment", post.very_special_comment.class.to_s
end
def test_eager_has_many_with_association_inheritance
- post = Post.all.merge!(:includes => [ :special_comments ]).find(4)
+ post = Post.all.merge!(includes: [ :special_comments ]).find(4)
post.special_comments.each do |special_comment|
assert special_comment.is_a?(SpecialComment)
end
end
def test_eager_habtm_with_association_inheritance
- post = Post.all.merge!(:includes => [ :special_categories ]).find(6)
+ post = Post.all.merge!(includes: [ :special_categories ]).find(6)
assert_equal 1, post.special_categories.size
post.special_categories.each do |special_category|
assert_equal "SpecialCategory", special_category.class.to_s
@@ -725,8 +725,8 @@ class EagerAssociationTest < ActiveRecord::TestCase
def test_eager_with_has_one_dependent_does_not_destroy_dependent
assert_not_nil companies(:first_firm).account
- f = Firm.all.merge!(:includes => :account,
- :where => ["companies.name = ?", "37signals"]).first
+ f = Firm.all.merge!(includes: :account,
+ where: ["companies.name = ?", "37signals"]).first
assert_not_nil f.account
assert_equal companies(:first_firm, :reload).account, f.account
end
@@ -740,37 +740,37 @@ class EagerAssociationTest < ActiveRecord::TestCase
def test_eager_with_invalid_association_reference
assert_raise(ActiveRecord::AssociationNotFoundError, "Association was not found; perhaps you misspelled it? You specified :include => :monkeys") {
- Post.all.merge!(:includes=> :monkeys ).find(6)
+ Post.all.merge!(includes: :monkeys).find(6)
}
assert_raise(ActiveRecord::AssociationNotFoundError, "Association was not found; perhaps you misspelled it? You specified :include => :monkeys") {
- Post.all.merge!(:includes=>[ :monkeys ]).find(6)
+ Post.all.merge!(includes: [ :monkeys ]).find(6)
}
assert_raise(ActiveRecord::AssociationNotFoundError, "Association was not found; perhaps you misspelled it? You specified :include => :monkeys") {
- Post.all.merge!(:includes=>[ 'monkeys' ]).find(6)
+ Post.all.merge!(includes: [ "monkeys" ]).find(6)
}
assert_raise(ActiveRecord::AssociationNotFoundError, "Association was not found; perhaps you misspelled it? You specified :include => :monkeys, :elephants") {
- Post.all.merge!(:includes=>[ :monkeys, :elephants ]).find(6)
+ Post.all.merge!(includes: [ :monkeys, :elephants ]).find(6)
}
end
def test_eager_has_many_through_with_order
- tag = OrderedTag.create(name: 'Foo')
- post1 = Post.create!(title: 'Beaches', body: "I like beaches!")
- post2 = Post.create!(title: 'Pools', body: "I like pools!")
+ tag = OrderedTag.create(name: "Foo")
+ post1 = Post.create!(title: "Beaches", body: "I like beaches!")
+ post2 = Post.create!(title: "Pools", body: "I like pools!")
- Tagging.create!(taggable_type: 'Post', taggable_id: post1.id, tag: tag)
- Tagging.create!(taggable_type: 'Post', taggable_id: post2.id, tag: tag)
+ Tagging.create!(taggable_type: "Post", taggable_id: post1.id, tag: tag)
+ Tagging.create!(taggable_type: "Post", taggable_id: post2.id, tag: tag)
tag_with_includes = OrderedTag.includes(:tagged_posts).find(tag.id)
assert_equal(tag_with_includes.taggings.map(&:taggable).map(&:title), tag_with_includes.tagged_posts.map(&:title))
end
def test_eager_has_many_through_multiple_with_order
- tag1 = OrderedTag.create!(name: 'Bar')
- tag2 = OrderedTag.create!(name: 'Foo')
+ tag1 = OrderedTag.create!(name: "Bar")
+ tag2 = OrderedTag.create!(name: "Foo")
- post1 = Post.create!(title: 'Beaches', body: "I like beaches!")
- post2 = Post.create!(title: 'Pools', body: "I like pools!")
+ post1 = Post.create!(title: "Beaches", body: "I like beaches!")
+ post2 = Post.create!(title: "Pools", body: "I like pools!")
Tagging.create!(taggable: post1, tag: tag1)
Tagging.create!(taggable: post2, tag: tag1)
@@ -786,7 +786,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
end
def test_eager_with_default_scope
- developer = EagerDeveloperWithDefaultScope.where(:name => 'David').first
+ developer = EagerDeveloperWithDefaultScope.where(name: "David").first
projects = Project.order(:id).to_a
assert_no_queries do
assert_equal(projects, developer.projects)
@@ -794,7 +794,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
end
def test_eager_with_default_scope_as_class_method
- developer = EagerDeveloperWithClassMethodDefaultScope.where(:name => 'David').first
+ developer = EagerDeveloperWithClassMethodDefaultScope.where(name: "David").first
projects = Project.order(:id).to_a
assert_no_queries do
assert_equal(projects, developer.projects)
@@ -811,7 +811,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
end
def test_eager_with_default_scope_as_class_method_using_find_by_method
- developer = EagerDeveloperWithClassMethodDefaultScope.find_by(name: 'David')
+ developer = EagerDeveloperWithClassMethodDefaultScope.find_by(name: "David")
projects = Project.order(:id).to_a
assert_no_queries do
assert_equal(projects, developer.projects)
@@ -819,7 +819,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
end
def test_eager_with_default_scope_as_lambda
- developer = EagerDeveloperWithLambdaDefaultScope.where(:name => 'David').first
+ developer = EagerDeveloperWithLambdaDefaultScope.where(name: "David").first
projects = Project.order(:id).to_a
assert_no_queries do
assert_equal(projects, developer.projects)
@@ -828,8 +828,8 @@ class EagerAssociationTest < ActiveRecord::TestCase
def test_eager_with_default_scope_as_block
# warm up the habtm cache
- EagerDeveloperWithBlockDefaultScope.where(:name => 'David').first.projects
- developer = EagerDeveloperWithBlockDefaultScope.where(:name => 'David').first
+ EagerDeveloperWithBlockDefaultScope.where(name: "David").first.projects
+ developer = EagerDeveloperWithBlockDefaultScope.where(name: "David").first
projects = Project.order(:id).to_a
assert_no_queries do
assert_equal(projects, developer.projects)
@@ -837,30 +837,30 @@ class EagerAssociationTest < ActiveRecord::TestCase
end
def test_eager_with_default_scope_as_callable
- developer = EagerDeveloperWithCallableDefaultScope.where(:name => 'David').first
+ developer = EagerDeveloperWithCallableDefaultScope.where(name: "David").first
projects = Project.order(:id).to_a
assert_no_queries do
assert_equal(projects, developer.projects)
end
end
- def find_all_ordered(className, include=nil)
- className.all.merge!(:order=>"#{className.table_name}.#{className.primary_key}", :includes=>include).to_a
+ def find_all_ordered(className, include = nil)
+ className.all.merge!(order: "#{className.table_name}.#{className.primary_key}", includes: include).to_a
end
def test_limited_eager_with_order
assert_equal(
posts(:thinking, :sti_comments),
Post.all.merge!(
- :includes => [:author, :comments], :where => { 'authors.name' => 'David' },
- :order => 'UPPER(posts.title)', :limit => 2, :offset => 1
+ includes: [:author, :comments], where: { "authors.name" => "David" },
+ order: "UPPER(posts.title)", limit: 2, offset: 1
).to_a
)
assert_equal(
posts(:sti_post_and_comments, :sti_comments),
Post.all.merge!(
- :includes => [:author, :comments], :where => { 'authors.name' => 'David' },
- :order => 'UPPER(posts.title) DESC', :limit => 2, :offset => 1
+ includes: [:author, :comments], where: { "authors.name" => "David" },
+ order: "UPPER(posts.title) DESC", limit: 2, offset: 1
).to_a
)
end
@@ -869,15 +869,15 @@ class EagerAssociationTest < ActiveRecord::TestCase
assert_equal(
posts(:thinking, :sti_comments),
Post.all.merge!(
- :includes => [:author, :comments], :where => { 'authors.name' => 'David' },
- :order => ['UPPER(posts.title)', 'posts.id'], :limit => 2, :offset => 1
+ includes: [:author, :comments], where: { "authors.name" => "David" },
+ order: ["UPPER(posts.title)", "posts.id"], limit: 2, offset: 1
).to_a
)
assert_equal(
posts(:sti_post_and_comments, :sti_comments),
Post.all.merge!(
- :includes => [:author, :comments], :where => { 'authors.name' => 'David' },
- :order => ['UPPER(posts.title) DESC', 'posts.id'], :limit => 2, :offset => 1
+ includes: [:author, :comments], where: { "authors.name" => "David" },
+ order: ["UPPER(posts.title) DESC", "posts.id"], limit: 2, offset: 1
).to_a
)
end
@@ -886,25 +886,25 @@ class EagerAssociationTest < ActiveRecord::TestCase
assert_equal(
people(:david, :susan),
Person.references(:number1_fans_people).merge(
- :includes => [:readers, :primary_contact, :number1_fan],
- :where => "number1_fans_people.first_name like 'M%'",
- :order => 'people.id', :limit => 2, :offset => 0
+ includes: [:readers, :primary_contact, :number1_fan],
+ where: "number1_fans_people.first_name like 'M%'",
+ order: "people.id", limit: 2, offset: 0
).to_a
)
end
def test_polymorphic_type_condition
- post = Post.all.merge!(:includes => :taggings).find(posts(:thinking).id)
- assert post.taggings.include?(taggings(:thinking_general))
- post = SpecialPost.all.merge!(:includes => :taggings).find(posts(:thinking).id)
- assert post.taggings.include?(taggings(:thinking_general))
+ post = Post.all.merge!(includes: :taggings).find(posts(:thinking).id)
+ assert_includes post.taggings, taggings(:thinking_general)
+ post = SpecialPost.all.merge!(includes: :taggings).find(posts(:thinking).id)
+ assert_includes post.taggings, taggings(:thinking_general)
end
def test_eager_with_multiple_associations_with_same_table_has_many_and_habtm
# Eager includes of has many and habtm associations aren't necessarily sorted in the same way
def assert_equal_after_sort(item1, item2, item3 = nil)
- assert_equal(item1.sort{|a,b| a.id <=> b.id}, item2.sort{|a,b| a.id <=> b.id})
- assert_equal(item3.sort{|a,b| a.id <=> b.id}, item2.sort{|a,b| a.id <=> b.id}) if item3
+ assert_equal(item1.sort { |a, b| a.id <=> b.id }, item2.sort { |a, b| a.id <=> b.id })
+ assert_equal(item3.sort { |a, b| a.id <=> b.id }, item2.sort { |a, b| a.id <=> b.id }) if item3
end
# Test regular association, association with conditions, association with
# STI, and association with conditions assured not to be true
@@ -947,24 +947,24 @@ class EagerAssociationTest < ActiveRecord::TestCase
end
end
def test_eager_with_valid_association_as_string_not_symbol
- assert_nothing_raised { Post.all.merge!(:includes => 'comments').to_a }
+ assert_nothing_raised { Post.all.merge!(includes: "comments").to_a }
end
def test_eager_with_floating_point_numbers
assert_queries(2) do
# Before changes, the floating point numbers will be interpreted as table names and will cause this to run in one query
- Comment.all.merge!(:where => "123.456 = 123.456", :includes => :post).to_a
+ Comment.all.merge!(where: "123.456 = 123.456", includes: :post).to_a
end
end
def test_preconfigured_includes_with_belongs_to
author = posts(:welcome).author_with_posts
- assert_no_queries {assert_equal 5, author.posts.size}
+ assert_no_queries { assert_equal 5, author.posts.size }
end
def test_preconfigured_includes_with_has_one
comment = posts(:sti_comments).very_special_comment_with_post
- assert_no_queries {assert_equal posts(:sti_comments), comment.post}
+ assert_no_queries { assert_equal posts(:sti_comments), comment.post }
end
def test_eager_association_with_scope_with_joins
@@ -1006,13 +1006,13 @@ class EagerAssociationTest < ActiveRecord::TestCase
end
def test_association_loading_notification
- notifications = messages_for('instantiation.active_record') do
- Developer.all.merge!(:includes => 'projects', :where => { 'developers_projects.access_level' => 1 }, :limit => 5).to_a.size
+ notifications = messages_for("instantiation.active_record") do
+ Developer.all.merge!(includes: "projects", where: { "developers_projects.access_level" => 1 }, limit: 5).to_a.size
end
message = notifications.first
payload = message.last
- count = Developer.all.merge!(:includes => 'projects', :where => { 'developers_projects.access_level' => 1 }, :limit => 5).to_a.size
+ count = Developer.all.merge!(includes: "projects", where: { "developers_projects.access_level" => 1 }, limit: 5).to_a.size
# eagerloaded row count should be greater than just developer count
assert_operator payload[:record_count], :>, count
@@ -1020,7 +1020,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
end
def test_base_messages
- notifications = messages_for('instantiation.active_record') do
+ notifications = messages_for("instantiation.active_record") do
Developer.all.to_a
end
message = notifications.first
@@ -1048,55 +1048,55 @@ class EagerAssociationTest < ActiveRecord::TestCase
end
def test_conditions_on_join_table_with_include_and_limit
- assert_equal 3, Developer.all.merge!(:includes => 'projects', :where => { 'developers_projects.access_level' => 1 }, :limit => 5).to_a.size
+ assert_equal 3, Developer.all.merge!(includes: "projects", where: { "developers_projects.access_level" => 1 }, limit: 5).to_a.size
end
def test_dont_create_temporary_active_record_instances
Developer.instance_count = 0
- developers = Developer.all.merge!(:includes => 'projects', :where => { 'developers_projects.access_level' => 1 }, :limit => 5).to_a
+ developers = Developer.all.merge!(includes: "projects", where: { "developers_projects.access_level" => 1 }, limit: 5).to_a
assert_equal developers.count, Developer.instance_count
end
def test_order_on_join_table_with_include_and_limit
- assert_equal 5, Developer.all.merge!(:includes => 'projects', :order => 'developers_projects.joined_on DESC', :limit => 5).to_a.size
+ assert_equal 5, Developer.all.merge!(includes: "projects", order: "developers_projects.joined_on DESC", limit: 5).to_a.size
end
def test_eager_loading_with_order_on_joined_table_preloads
posts = assert_queries(2) do
- Post.all.merge!(:joins => :comments, :includes => :author, :order => 'comments.id DESC').to_a
+ Post.all.merge!(joins: :comments, includes: :author, order: "comments.id DESC").to_a
end
assert_equal posts(:eager_other), posts[1]
- assert_equal authors(:mary), assert_no_queries { posts[1].author}
+ assert_equal authors(:mary), assert_no_queries { posts[1].author }
end
def test_eager_loading_with_conditions_on_joined_table_preloads
posts = assert_queries(2) do
- Post.all.merge!(:select => 'distinct posts.*', :includes => :author, :joins => [:comments], :where => "comments.body like 'Thank you%'", :order => 'posts.id').to_a
+ Post.all.merge!(select: "distinct posts.*", includes: :author, joins: [:comments], where: "comments.body like 'Thank you%'", order: "posts.id").to_a
end
assert_equal [posts(:welcome)], posts
- assert_equal authors(:david), assert_no_queries { posts[0].author}
+ assert_equal authors(:david), assert_no_queries { posts[0].author }
posts = assert_queries(2) do
- Post.all.merge!(:select => 'distinct posts.*', :includes => :author, :joins => [:comments], :where => "comments.body like 'Thank you%'", :order => 'posts.id').to_a
+ Post.all.merge!(select: "distinct posts.*", includes: :author, joins: [:comments], where: "comments.body like 'Thank you%'", order: "posts.id").to_a
end
assert_equal [posts(:welcome)], posts
- assert_equal authors(:david), assert_no_queries { posts[0].author}
+ assert_equal authors(:david), assert_no_queries { posts[0].author }
posts = assert_queries(2) do
- Post.all.merge!(:includes => :author, :joins => {:taggings => :tag}, :where => "tags.name = 'General'", :order => 'posts.id').to_a
+ Post.all.merge!(includes: :author, joins: { taggings: :tag }, where: "tags.name = 'General'", order: "posts.id").to_a
end
assert_equal posts(:welcome, :thinking), posts
posts = assert_queries(2) do
- Post.all.merge!(:includes => :author, :joins => {:taggings => {:tag => :taggings}}, :where => "taggings_tags.super_tag_id=2", :order => 'posts.id').to_a
+ Post.all.merge!(includes: :author, joins: { taggings: { tag: :taggings } }, where: "taggings_tags.super_tag_id=2", order: "posts.id").to_a
end
assert_equal posts(:welcome, :thinking), posts
end
def test_preload_has_many_with_association_condition_and_default_scope
- post = Post.create!(:title => 'Beaches', :body => "I like beaches!")
- Reader.create! :person => people(:david), :post => post
- LazyReader.create! :person => people(:susan), :post => post
+ post = Post.create!(title: "Beaches", body: "I like beaches!")
+ Reader.create! person: people(:david), post: post
+ LazyReader.create! person: people(:susan), post: post
assert_equal 1, post.lazy_readers.to_a.size
assert_equal 2, post.lazy_readers_skimmers_or_not.to_a.size
@@ -1107,39 +1107,39 @@ class EagerAssociationTest < ActiveRecord::TestCase
def test_eager_loading_with_conditions_on_string_joined_table_preloads
posts = assert_queries(2) do
- Post.all.merge!(:select => 'distinct posts.*', :includes => :author, :joins => "INNER JOIN comments on comments.post_id = posts.id", :where => "comments.body like 'Thank you%'", :order => 'posts.id').to_a
+ Post.all.merge!(select: "distinct posts.*", includes: :author, joins: "INNER JOIN comments on comments.post_id = posts.id", where: "comments.body like 'Thank you%'", order: "posts.id").to_a
end
assert_equal [posts(:welcome)], posts
- assert_equal authors(:david), assert_no_queries { posts[0].author}
+ assert_equal authors(:david), assert_no_queries { posts[0].author }
posts = assert_queries(2) do
- Post.all.merge!(:select => 'distinct posts.*', :includes => :author, :joins => ["INNER JOIN comments on comments.post_id = posts.id"], :where => "comments.body like 'Thank you%'", :order => 'posts.id').to_a
+ Post.all.merge!(select: "distinct posts.*", includes: :author, joins: ["INNER JOIN comments on comments.post_id = posts.id"], where: "comments.body like 'Thank you%'", order: "posts.id").to_a
end
assert_equal [posts(:welcome)], posts
- assert_equal authors(:david), assert_no_queries { posts[0].author}
+ assert_equal authors(:david), assert_no_queries { posts[0].author }
end
def test_eager_loading_with_select_on_joined_table_preloads
posts = assert_queries(2) do
- Post.all.merge!(:select => 'posts.*, authors.name as author_name', :includes => :comments, :joins => :author, :order => 'posts.id').to_a
+ Post.all.merge!(select: "posts.*, authors.name as author_name", includes: :comments, joins: :author, order: "posts.id").to_a
end
- assert_equal 'David', posts[0].author_name
- assert_equal posts(:welcome).comments, assert_no_queries { posts[0].comments}
+ assert_equal "David", posts[0].author_name
+ assert_equal posts(:welcome).comments, assert_no_queries { posts[0].comments }
end
def test_eager_loading_with_conditions_on_join_model_preloads
authors = assert_queries(2) do
- Author.all.merge!(:includes => :author_address, :joins => :comments, :where => "posts.title like 'Welcome%'").to_a
+ Author.all.merge!(includes: :author_address, joins: :comments, where: "posts.title like 'Welcome%'").to_a
end
assert_equal authors(:david), authors[0]
assert_equal author_addresses(:david_address), authors[0].author_address
end
def test_preload_belongs_to_uses_exclusive_scope
- people = Person.males.merge(:includes => :primary_contact).to_a
+ people = Person.males.merge(includes: :primary_contact).to_a
assert_not_equal people.length, 0
people.each do |person|
- assert_no_queries {assert_not_nil person.primary_contact}
+ assert_no_queries { assert_not_nil person.primary_contact }
assert_equal Person.find(person.id).primary_contact, person.primary_contact
end
end
@@ -1163,9 +1163,9 @@ class EagerAssociationTest < ActiveRecord::TestCase
expected = Firm.find(1).clients_using_primary_key.sort_by(&:name)
# Oracle adapter truncates alias to 30 characters
if current_adapter?(:OracleAdapter)
- firm = Firm.all.merge!(:includes => :clients_using_primary_key, :order => 'clients_using_primary_keys_companies'[0,30]+'.name').find(1)
+ firm = Firm.all.merge!(includes: :clients_using_primary_key, order: "clients_using_primary_keys_companies"[0, 30] + ".name").find(1)
else
- firm = Firm.all.merge!(:includes => :clients_using_primary_key, :order => 'clients_using_primary_keys_companies.name').find(1)
+ firm = Firm.all.merge!(includes: :clients_using_primary_key, order: "clients_using_primary_keys_companies.name").find(1)
end
assert_no_queries do
assert_equal expected, firm.clients_using_primary_key
@@ -1174,7 +1174,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
def test_preload_has_one_using_primary_key
expected = accounts(:signals37)
- firm = Firm.all.merge!(:includes => :account_using_primary_key, :order => 'companies.id').first
+ firm = Firm.all.merge!(includes: :account_using_primary_key, order: "companies.id").first
assert_no_queries do
assert_equal expected, firm.account_using_primary_key
end
@@ -1182,35 +1182,35 @@ class EagerAssociationTest < ActiveRecord::TestCase
def test_include_has_one_using_primary_key
expected = accounts(:signals37)
- firm = Firm.all.merge!(:includes => :account_using_primary_key, :order => 'accounts.id').to_a.detect {|f| f.id == 1}
+ firm = Firm.all.merge!(includes: :account_using_primary_key, order: "accounts.id").to_a.detect { |f| f.id == 1 }
assert_no_queries do
assert_equal expected, firm.account_using_primary_key
end
end
def test_preloading_empty_belongs_to
- c = Client.create!(:name => 'Foo', :client_of => Company.maximum(:id) + 1)
+ c = Client.create!(name: "Foo", client_of: Company.maximum(:id) + 1)
client = assert_queries(2) { Client.preload(:firm).find(c.id) }
assert_no_queries { assert_nil client.firm }
end
def test_preloading_empty_belongs_to_polymorphic
- t = Tagging.create!(:taggable_type => 'Post', :taggable_id => Post.maximum(:id) + 1, :tag => tags(:general))
+ t = Tagging.create!(taggable_type: "Post", taggable_id: Post.maximum(:id) + 1, tag: tags(:general))
tagging = assert_queries(2) { Tagging.preload(:taggable).find(t.id) }
assert_no_queries { assert_nil tagging.taggable }
end
def test_preloading_through_empty_belongs_to
- c = Client.create!(:name => 'Foo', :client_of => Company.maximum(:id) + 1)
+ c = Client.create!(name: "Foo", client_of: Company.maximum(:id) + 1)
client = assert_queries(2) { Client.preload(:accounts).find(c.id) }
assert_no_queries { assert client.accounts.empty? }
end
def test_preloading_has_many_through_with_distinct
- mary = Author.includes(:unique_categorized_posts).where(:id => authors(:mary).id).first
+ mary = Author.includes(:unique_categorized_posts).where(id: authors(:mary).id).first
assert_equal 1, mary.unique_categorized_posts.length
assert_equal 1, mary.unique_categorized_post_ids.length
end
@@ -1238,13 +1238,13 @@ class EagerAssociationTest < ActiveRecord::TestCase
groucho = members(:groucho)
sponsor = assert_queries(2) {
- Sponsor.includes(:thing).where(:id => sponsor.id).first
+ Sponsor.includes(:thing).where(id: sponsor.id).first
}
assert_no_queries { assert_equal groucho, sponsor.thing }
end
def test_joins_with_includes_should_preload_via_joins
- post = assert_queries(1) { Post.includes(:comments).joins(:comments).order('posts.id desc').to_a.first }
+ post = assert_queries(1) { Post.includes(:comments).joins(:comments).order("posts.id desc").to_a.first }
assert_queries(0) do
assert_not_equal 0, post.comments.to_a.count
@@ -1253,16 +1253,16 @@ class EagerAssociationTest < ActiveRecord::TestCase
def test_join_eager_with_empty_order_should_generate_valid_sql
assert_nothing_raised do
- Post.includes(:comments).order("").where(:comments => {:body => "Thank you for the welcome"}).first
+ Post.includes(:comments).order("").where(comments: { body: "Thank you for the welcome" }).first
end
end
def test_deep_including_through_habtm
# warm up habtm cache
- posts = Post.all.merge!(:includes => {:categories => :categorizations}, :order => "posts.id").to_a
+ posts = Post.all.merge!(includes: { categories: :categorizations }, order: "posts.id").to_a
posts[0].categories[0].categorizations.length
- posts = Post.all.merge!(:includes => {:categories => :categorizations}, :order => "posts.id").to_a
+ posts = Post.all.merge!(includes: { categories: :categorizations }, order: "posts.id").to_a
assert_no_queries { assert_equal 2, posts[0].categories[0].categorizations.length }
assert_no_queries { assert_equal 1, posts[0].categories[1].categorizations.length }
assert_no_queries { assert_equal 2, posts[1].categories[0].categorizations.length }
@@ -1279,19 +1279,19 @@ class EagerAssociationTest < ActiveRecord::TestCase
end
test "scoping with a circular preload" do
- assert_equal Comment.find(1), Comment.preload(:post => :comments).scoping { Comment.find(1) }
+ assert_equal Comment.find(1), Comment.preload(post: :comments).scoping { Comment.find(1) }
end
test "circular preload does not modify unscoped" do
expected = FirstPost.unscoped.find(2)
- FirstPost.preload(:comments => :first_post).find(1)
+ FirstPost.preload(comments: :first_post).find(1)
assert_equal expected, FirstPost.unscoped.find(2)
end
test "preload ignores the scoping" do
assert_equal(
Comment.find(1).post,
- Post.where('1 = 0').scoping { Comment.preload(:post).find(1).post }
+ Post.where("1 = 0").scoping { Comment.preload(:post).find(1).post }
)
end
@@ -1316,10 +1316,10 @@ class EagerAssociationTest < ActiveRecord::TestCase
end
test "works in combination with order(:symbol) and reorder(:symbol)" do
- author = Author.includes(:posts).references(:posts).order(:name).find_by('posts.title IS NOT NULL')
+ author = Author.includes(:posts).references(:posts).order(:name).find_by("posts.title IS NOT NULL")
assert_equal authors(:bob), author
- author = Author.includes(:posts).references(:posts).reorder(:name).find_by('posts.title IS NOT NULL')
+ author = Author.includes(:posts).references(:posts).reorder(:name).find_by("posts.title IS NOT NULL")
assert_equal authors(:bob), author
end
@@ -1360,7 +1360,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
test "including associations with where.not adds implicit references" do
author = assert_queries(2) {
- Author.includes(:posts).where.not(posts: { title: 'Welcome to the weblog'} ).last
+ Author.includes(:posts).where.not(posts: { title: "Welcome to the weblog" }).last
}
assert_no_queries {
@@ -1394,7 +1394,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
exception = assert_raises(ArgumentError) do
Author.preload(10).to_a
end
- assert_equal('10 was not recognized for preload', exception.message)
+ assert_equal("10 was not recognized for preload", exception.message)
end
test "associations with extensions are not instance dependent" do
@@ -1424,6 +1424,24 @@ class EagerAssociationTest < ActiveRecord::TestCase
assert david.readonly_comments.first.readonly?
end
+ test "eager-loading non-readonly association" do
+ # has_one
+ firm = Firm.where(id: "1").eager_load(:account).first!
+ assert_not firm.account.readonly?
+
+ # has_and_belongs_to_many
+ project = Project.where(id: "2").eager_load(:developers).first!
+ assert_not project.developers.first.readonly?
+
+ # has_many :through
+ david = Author.where(id: "1").eager_load(:comments).first!
+ assert_not david.comments.first.readonly?
+
+ # belongs_to
+ post = Post.where(id: "1").eager_load(:author).first!
+ assert_not post.author.readonly?
+ end
+
test "eager-loading readonly association" do
# has-one
firm = Firm.where(id: "1").eager_load(:readonly_account).first!
@@ -1438,17 +1456,17 @@ class EagerAssociationTest < ActiveRecord::TestCase
assert david.readonly_comments.first.readonly?
# belongs_to
- post = Post.where(id: "1").eager_load(:author).first!
- assert post.author.readonly?
+ post = Post.where(id: "1").eager_load(:readonly_author).first!
+ assert post.readonly_author.readonly?
end
test "preloading a polymorphic association with references to the associated table" do
- post = Post.includes(:tags).references(:tags).where('tags.name = ?', 'General').first
+ post = Post.includes(:tags).references(:tags).where("tags.name = ?", "General").first
assert_equal posts(:welcome), post
end
test "eager-loading a polymorphic association with references to the associated table" do
- post = Post.eager_load(:tags).where('tags.name = ?', 'General').first
+ post = Post.eager_load(:tags).where("tags.name = ?", "General").first
assert_equal posts(:welcome), post
end
diff --git a/activerecord/test/cases/associations/extension_test.rb b/activerecord/test/cases/associations/extension_test.rb
index b161cde335..974a3080d4 100644
--- a/activerecord/test/cases/associations/extension_test.rb
+++ b/activerecord/test/cases/associations/extension_test.rb
@@ -1,10 +1,10 @@
require "cases/helper"
-require 'models/post'
-require 'models/comment'
-require 'models/project'
-require 'models/developer'
-require 'models/computer'
-require 'models/company_in_module'
+require "models/post"
+require "models/comment"
+require "models/project"
+require "models/developer"
+require "models/computer"
+require "models/company_in_module"
class AssociationsExtensionsTest < ActiveRecord::TestCase
fixtures :projects, :developers, :developers_projects, :comments, :posts
@@ -45,7 +45,7 @@ class AssociationsExtensionsTest < ActiveRecord::TestCase
# Marshaling an association shouldn't make it unusable by wiping its reflection.
assert_not_nil david.association(:projects).reflection
- david_too = Marshal.load(marshalled)
+ david_too = Marshal.load(marshalled)
assert_equal projects(:action_controller), david_too.projects.find_most_recent
end
@@ -63,19 +63,19 @@ class AssociationsExtensionsTest < ActiveRecord::TestCase
extend!(Developer)
extend!(MyApplication::Business::Developer)
- assert Object.const_get 'DeveloperAssociationNameAssociationExtension'
- assert MyApplication::Business.const_get 'DeveloperAssociationNameAssociationExtension'
+ assert Object.const_get "DeveloperAssociationNameAssociationExtension"
+ assert MyApplication::Business.const_get "DeveloperAssociationNameAssociationExtension"
end
def test_proxy_association_after_scoped
post = posts(:welcome)
assert_equal post.association(:comments), post.comments.the_association
- assert_equal post.association(:comments), post.comments.where('1=1').the_association
+ assert_equal post.association(:comments), post.comments.where("1=1").the_association
end
private
def extend!(model)
- ActiveRecord::Associations::Builder::HasMany.define_extensions(model, :association_name) { }
+ ActiveRecord::Associations::Builder::HasMany.define_extensions(model, :association_name) {}
end
end
diff --git a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb
index 1bbca84bb2..54fb61d6a5 100644
--- a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb
+++ b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb
@@ -1,94 +1,99 @@
require "cases/helper"
-require 'models/developer'
-require 'models/computer'
-require 'models/project'
-require 'models/company'
-require 'models/course'
-require 'models/customer'
-require 'models/order'
-require 'models/categorization'
-require 'models/category'
-require 'models/post'
-require 'models/author'
-require 'models/tag'
-require 'models/tagging'
-require 'models/parrot'
-require 'models/person'
-require 'models/pirate'
-require 'models/professor'
-require 'models/treasure'
-require 'models/price_estimate'
-require 'models/club'
-require 'models/member'
-require 'models/membership'
-require 'models/sponsor'
-require 'models/country'
-require 'models/treaty'
-require 'models/vertex'
-require 'models/publisher'
-require 'models/publisher/article'
-require 'models/publisher/magazine'
-require 'active_support/core_ext/string/conversions'
+require "models/developer"
+require "models/computer"
+require "models/project"
+require "models/company"
+require "models/course"
+require "models/customer"
+require "models/order"
+require "models/categorization"
+require "models/category"
+require "models/post"
+require "models/author"
+require "models/tag"
+require "models/tagging"
+require "models/parrot"
+require "models/person"
+require "models/pirate"
+require "models/professor"
+require "models/treasure"
+require "models/price_estimate"
+require "models/club"
+require "models/user"
+require "models/member"
+require "models/membership"
+require "models/sponsor"
+require "models/country"
+require "models/treaty"
+require "models/vertex"
+require "models/publisher"
+require "models/publisher/article"
+require "models/publisher/magazine"
+require "active_support/core_ext/string/conversions"
class ProjectWithAfterCreateHook < ActiveRecord::Base
- self.table_name = 'projects'
+ self.table_name = "projects"
has_and_belongs_to_many :developers,
- :class_name => "DeveloperForProjectWithAfterCreateHook",
- :join_table => "developers_projects",
- :foreign_key => "project_id",
- :association_foreign_key => "developer_id"
+ class_name: "DeveloperForProjectWithAfterCreateHook",
+ join_table: "developers_projects",
+ foreign_key: "project_id",
+ association_foreign_key: "developer_id"
after_create :add_david
def add_david
- david = DeveloperForProjectWithAfterCreateHook.find_by_name('David')
+ david = DeveloperForProjectWithAfterCreateHook.find_by_name("David")
david.projects << self
end
end
class DeveloperForProjectWithAfterCreateHook < ActiveRecord::Base
- self.table_name = 'developers'
+ self.table_name = "developers"
has_and_belongs_to_many :projects,
- :class_name => "ProjectWithAfterCreateHook",
- :join_table => "developers_projects",
- :association_foreign_key => "project_id",
- :foreign_key => "developer_id"
+ class_name: "ProjectWithAfterCreateHook",
+ join_table: "developers_projects",
+ association_foreign_key: "project_id",
+ foreign_key: "developer_id"
end
class ProjectWithSymbolsForKeys < ActiveRecord::Base
- self.table_name = 'projects'
+ self.table_name = "projects"
has_and_belongs_to_many :developers,
- :class_name => "DeveloperWithSymbolsForKeys",
- :join_table => :developers_projects,
- :foreign_key => :project_id,
- :association_foreign_key => "developer_id"
+ class_name: "DeveloperWithSymbolsForKeys",
+ join_table: :developers_projects,
+ foreign_key: :project_id,
+ association_foreign_key: "developer_id"
end
class DeveloperWithSymbolsForKeys < ActiveRecord::Base
- self.table_name = 'developers'
+ self.table_name = "developers"
has_and_belongs_to_many :projects,
- :class_name => "ProjectWithSymbolsForKeys",
- :join_table => :developers_projects,
- :association_foreign_key => :project_id,
- :foreign_key => "developer_id"
+ class_name: "ProjectWithSymbolsForKeys",
+ join_table: :developers_projects,
+ association_foreign_key: :project_id,
+ foreign_key: "developer_id"
end
class SubDeveloper < Developer
- self.table_name = 'developers'
+ self.table_name = "developers"
has_and_belongs_to_many :special_projects,
- :join_table => 'developers_projects',
- :foreign_key => "project_id",
- :association_foreign_key => "developer_id"
+ join_table: "developers_projects",
+ foreign_key: "project_id",
+ association_foreign_key: "developer_id"
end
class DeveloperWithSymbolClassName < Developer
has_and_belongs_to_many :projects, class_name: :ProjectWithSymbolsForKeys
end
+class DeveloperWithConstantClassName < Developer
+ has_and_belongs_to_many :projects, class_name: ProjectWithSymbolsForKeys
+end
+
class DeveloperWithExtendOption < Developer
module NamedExtension
def category
- 'sns'
+ "sns"
end
end
@@ -96,8 +101,8 @@ class DeveloperWithExtendOption < Developer
end
class ProjectUnscopingDavidDefaultScope < ActiveRecord::Base
- self.table_name = 'projects'
- has_and_belongs_to_many :developers, -> { unscope(where: 'name') },
+ self.table_name = "projects"
+ has_and_belongs_to_many :developers, -> { unscope(where: "name") },
class_name: "LazyBlockDeveloperCalledDavid",
join_table: "developers_projects",
foreign_key: "project_id",
@@ -109,14 +114,14 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
:parrots, :pirates, :parrots_pirates, :treasures, :price_estimates, :tags, :taggings, :computers
def setup_data_for_habtm_case
- ActiveRecord::Base.connection.execute('delete from countries_treaties')
+ ActiveRecord::Base.connection.execute("delete from countries_treaties")
- country = Country.new(:name => 'India')
- country.country_id = 'c1'
+ country = Country.new(name: "India")
+ country.country_id = "c1"
country.save!
- treaty = Treaty.new(:name => 'peace')
- treaty.treaty_id = 't1'
+ treaty = Treaty.new(name: "peace")
+ treaty.treaty_id = "t1"
country.treaties << treaty
end
@@ -130,33 +135,33 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
setup_data_for_habtm_case
con = ActiveRecord::Base.connection
- sql = 'select * from countries_treaties'
+ sql = "select * from countries_treaties"
record = con.select_rows(sql).last
- assert_equal 'c1', record[0]
- assert_equal 't1', record[1]
+ assert_equal "c1", record[0]
+ assert_equal "t1", record[1]
end
def test_proper_usage_of_primary_keys_and_join_table
setup_data_for_habtm_case
- assert_equal 'country_id', Country.primary_key
- assert_equal 'treaty_id', Treaty.primary_key
+ assert_equal "country_id", Country.primary_key
+ assert_equal "treaty_id", Treaty.primary_key
country = Country.first
assert_equal 1, country.treaties.count
end
def test_join_table_composite_primary_key_should_not_warn
- country = Country.new(:name => 'India')
- country.country_id = 'c1'
+ country = Country.new(name: "India")
+ country.country_id = "c1"
country.save!
- treaty = Treaty.new(:name => 'peace')
- treaty.treaty_id = 't1'
+ treaty = Treaty.new(name: "peace")
+ treaty.treaty_id = "t1"
warning = capture(:stderr) do
country.treaties << treaty
end
- assert_no_match(/WARNING: Rails does not support composite primary key\./, warning)
+ assert_no_match(/WARNING: Active Record does not support composite primary key\./, warning)
end
def test_has_and_belongs_to_many
@@ -168,7 +173,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
active_record = Project.find(1)
assert !active_record.developers.empty?
assert_equal 3, active_record.developers.size
- assert active_record.developers.include?(david)
+ assert_includes active_record.developers, david
end
def test_adding_single
@@ -248,8 +253,8 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
assert !p.persisted?
assert aredridel.save
assert aredridel.persisted?
- assert_equal no_of_devels+1, Developer.count
- assert_equal no_of_projects+1, Project.count
+ assert_equal no_of_devels + 1, Developer.count
+ assert_equal no_of_projects + 1, Project.count
assert_equal 2, aredridel.projects.size
assert_equal 2, aredridel.projects.reload.size
end
@@ -257,7 +262,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
def test_habtm_saving_multiple_relationships
new_project = Project.new("name" => "Grimetime")
amount_of_developers = 4
- developers = (0...amount_of_developers).collect {|i| Developer.create(:name => "JME #{i}") }.reverse
+ developers = (0...amount_of_developers).collect { |i| Developer.create(name: "JME #{i}") }.reverse
new_project.developer_ids = [developers[0].id, developers[1].id]
new_project.developers_with_callback_ids = [developers[2].id, developers[3].id]
@@ -282,11 +287,10 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
end
def test_habtm_collection_size_from_params
- devel = Developer.new({
+ devel = Developer.new(
projects_attributes: {
- '0' => {}
- }
- })
+ "0" => {}
+ })
assert_equal 1, devel.projects.size
end
@@ -322,9 +326,9 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
end
def test_build_by_new_record
- devel = Developer.new(:name => "Marcel", :salary => 75000)
- devel.projects.build(:name => "Make bed")
- proj2 = devel.projects.build(:name => "Lie in it")
+ devel = Developer.new(name: "Marcel", salary: 75000)
+ devel.projects.build(name: "Make bed")
+ proj2 = devel.projects.build(name: "Lie in it")
assert_equal devel.projects.last, proj2
assert !proj2.persisted?
devel.save
@@ -347,9 +351,9 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
end
def test_create_by_new_record
- devel = Developer.new(:name => "Marcel", :salary => 75000)
- devel.projects.build(:name => "Make bed")
- proj2 = devel.projects.build(:name => "Lie in it")
+ devel = Developer.new(name: "Marcel", salary: 75000)
+ devel.projects.build(name: "Make bed")
+ proj2 = devel.projects.build(name: "Lie in it")
assert_equal devel.projects.last, proj2
assert !proj2.persisted?
devel.save
@@ -361,16 +365,16 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
def test_creation_respects_hash_condition
# in Oracle '' is saved as null therefore need to save ' ' in not null column
- post = categories(:general).post_with_conditions.build(:body => ' ')
+ post = categories(:general).post_with_conditions.build(body: " ")
assert post.save
- assert_equal 'Yet Another Testing Title', post.title
+ assert_equal "Yet Another Testing Title", post.title
# in Oracle '' is saved as null therefore need to save ' ' in not null column
- another_post = categories(:general).post_with_conditions.create(:body => ' ')
+ another_post = categories(:general).post_with_conditions.create(body: " ")
assert another_post.persisted?
- assert_equal 'Yet Another Testing Title', another_post.title
+ assert_equal "Yet Another Testing Title", another_post.title
end
def test_distinct_after_the_fact
@@ -379,7 +383,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
dev.projects << projects(:active_record)
assert_equal 3, dev.projects.size
- assert_equal 1, dev.projects.distinct.size
+ assert_equal 1, dev.projects.uniq.size
end
def test_distinct_before_the_fact
@@ -544,7 +548,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
assert_no_queries(ignore_none: false) do
assert project.developers.loaded?
- assert project.developers.include?(developer)
+ assert_includes project.developers, developer
end
end
@@ -555,14 +559,14 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
project.reload
assert ! project.developers.loaded?
assert_queries(1) do
- assert project.developers.include?(developer)
+ assert_includes project.developers, developer
end
assert ! project.developers.loaded?
end
def test_include_returns_false_for_non_matching_record_to_verify_scoping
project = projects(:active_record)
- developer = Developer.create :name => "Bryan", :salary => 50_000
+ developer = Developer.create name: "Bryan", salary: 50_000
assert ! project.developers.loaded?
assert ! project.developers.include?(developer)
@@ -576,32 +580,32 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
def test_dynamic_find_should_respect_association_order
# Developers are ordered 'name DESC, id DESC'
- high_id_jamis = projects(:active_record).developers.create(:name => 'Jamis')
+ high_id_jamis = projects(:active_record).developers.create(name: "Jamis")
- assert_equal high_id_jamis, projects(:active_record).developers.merge(:where => "name = 'Jamis'").first
- assert_equal high_id_jamis, projects(:active_record).developers.find_by_name('Jamis')
+ assert_equal high_id_jamis, projects(:active_record).developers.merge(where: "name = 'Jamis'").first
+ assert_equal high_id_jamis, projects(:active_record).developers.find_by_name("Jamis")
end
def test_find_should_append_to_association_order
- ordered_developers = projects(:active_record).developers.order('projects.id')
- assert_equal ['developers.name desc, developers.id desc', 'projects.id'], ordered_developers.order_values
+ ordered_developers = projects(:active_record).developers.order("projects.id")
+ assert_equal ["developers.name desc, developers.id desc", "projects.id"], ordered_developers.order_values
end
def test_dynamic_find_all_should_respect_readonly_access
- projects(:active_record).readonly_developers.each { |d| assert_raise(ActiveRecord::ReadOnlyRecord) { d.save! } if d.valid?}
+ projects(:active_record).readonly_developers.each { |d| assert_raise(ActiveRecord::ReadOnlyRecord) { d.save! } if d.valid? }
projects(:active_record).readonly_developers.each(&:readonly?)
end
def test_new_with_values_in_collection
- jamis = DeveloperForProjectWithAfterCreateHook.find_by_name('Jamis')
- david = DeveloperForProjectWithAfterCreateHook.find_by_name('David')
- project = ProjectWithAfterCreateHook.new(:name => "Cooking with Bertie")
+ jamis = DeveloperForProjectWithAfterCreateHook.find_by_name("Jamis")
+ david = DeveloperForProjectWithAfterCreateHook.find_by_name("David")
+ project = ProjectWithAfterCreateHook.new(name: "Cooking with Bertie")
project.developers << jamis
project.save!
project.reload
- assert project.developers.include?(jamis)
- assert project.developers.include?(david)
+ assert_includes project.developers, jamis
+ assert_includes project.developers, david
end
def test_find_in_association_with_options
@@ -612,8 +616,8 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
end
def test_association_with_extend_option
- eponine = DeveloperWithExtendOption.create(name: 'Eponine')
- assert_equal 'sns', eponine.projects.category
+ eponine = DeveloperWithExtendOption.create(name: "Eponine")
+ assert_equal "sns", eponine.projects.category
end
def test_replace_with_less
@@ -628,7 +632,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
david.projects = [projects(:action_controller), Project.new("name" => "ActionWebSearch")]
david.save
assert_equal 2, david.projects.length
- assert !david.projects.include?(projects(:active_record))
+ assert_not_includes david.projects, projects(:active_record)
end
def test_replace_on_new_object
@@ -646,9 +650,9 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
developer.special_projects << special_project
developer.reload
- assert developer.projects.include?(special_project)
- assert developer.special_projects.include?(special_project)
- assert !developer.special_projects.include?(other_project)
+ assert_includes developer.projects, special_project
+ assert_includes developer.special_projects, special_project
+ assert_not_includes developer.special_projects, other_project
end
def test_symbol_join_table
@@ -689,7 +693,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
end
def test_habtm_respects_select_query_method
- assert_equal ['id'], developers(:david).projects.select(:id).first.attributes.keys
+ assert_equal ["id"], developers(:david).projects.select(:id).first.attributes.keys
end
def test_join_table_alias
@@ -700,8 +704,8 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
assert_equal(
3,
Developer.references(:developers_projects_join).merge(
- :includes => {:projects => :developers},
- :where => 'projects_developers_projects_join.joined_on IS NOT NULL'
+ includes: { projects: :developers },
+ where: "projects_developers_projects_join.joined_on IS NOT NULL"
).to_a.size
)
end
@@ -720,15 +724,15 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
assert_equal(
3,
Developer.references(:developers_projects_join).merge(
- :includes => {:projects => :developers}, :where => 'projects_developers_projects_join.joined_on IS NOT NULL',
- :group => group.join(",")
+ includes: { projects: :developers }, where: "projects_developers_projects_join.joined_on IS NOT NULL",
+ group: group.join(",")
).to_a.size
)
end
def test_find_grouped
- all_posts_from_category1 = Post.all.merge!(:where => "category_id = 1", :joins => :categories).to_a
- grouped_posts_of_category1 = Post.all.merge!(:where => "category_id = 1", :group => "author_id", :select => 'count(posts.id) as posts_count', :joins => :categories).to_a
+ all_posts_from_category1 = Post.all.merge!(where: "category_id = 1", joins: :categories).to_a
+ grouped_posts_of_category1 = Post.all.merge!(where: "category_id = 1", group: "author_id", select: "count(posts.id) as posts_count", joins: :categories).to_a
assert_equal 5, all_posts_from_category1.size
assert_equal 2, grouped_posts_of_category1.size
end
@@ -775,7 +779,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
def test_assign_ids_ignoring_blanks
developer = Developer.new("name" => "Joe")
- developer.project_ids = [projects(:active_record).id, nil, projects(:action_controller).id, '']
+ developer.project_ids = [projects(:active_record).id, nil, projects(:action_controller).id, ""]
developer.save
developer.reload
assert_equal 2, developer.projects.length
@@ -795,8 +799,8 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
end
def test_symbols_as_keys
- developer = DeveloperWithSymbolsForKeys.new(:name => 'David')
- project = ProjectWithSymbolsForKeys.new(:name => 'Rails Testing')
+ developer = DeveloperWithSymbolsForKeys.new(name: "David")
+ project = ProjectWithSymbolsForKeys.new(name: "Rails Testing")
project.developers << developer
project.save!
@@ -809,7 +813,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
def test_dynamic_find_should_respect_association_include
# SQL error in sort clause if :include is not included
# due to Unknown column 'authors.id'
- assert Category.find(1).posts_with_authors_sorted_by_author_id.find_by_title('Welcome to the weblog')
+ assert Category.find(1).posts_with_authors_sorted_by_author_id.find_by_title("Welcome to the weblog")
end
def test_count
@@ -841,12 +845,12 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
end
def test_attributes_are_being_set_when_initialized_from_habtm_association_with_where_clause
- new_developer = projects(:action_controller).developers.where(:name => "Marcelo").build
+ new_developer = projects(:action_controller).developers.where(name: "Marcelo").build
assert_equal new_developer.name, "Marcelo"
end
def test_attributes_are_being_set_when_initialized_from_habtm_association_with_multiple_where_clauses
- new_developer = projects(:action_controller).developers.where(:name => "Marcelo").where(:salary => 90_000).build
+ new_developer = projects(:action_controller).developers.where(name: "Marcelo").where(salary: 90_000).build
assert_equal new_developer.name, "Marcelo"
assert_equal new_developer.salary, 90_000
end
@@ -854,7 +858,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
def test_include_method_in_has_and_belongs_to_many_association_should_return_true_for_instance_added_with_build
project = Project.new
developer = project.developers.build
- assert project.developers.include?(developer)
+ assert_includes project.developers, developer
end
def test_destruction_does_not_error_without_primary_key
@@ -871,7 +875,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
projects = Developer.new.projects
assert_no_queries(ignore_none: false) do
assert_equal [], projects
- assert_equal [], projects.where(title: 'omg')
+ assert_equal [], projects.where(title: "omg")
assert_equal [], projects.pluck(:title)
assert_equal 0, projects.count
end
@@ -885,7 +889,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
treasure.valid?
assert_equal 1, treasure.rich_people.size
- assert_nil rich_person.first_name, 'should not run associated person validation on create when validate: false'
+ assert_nil rich_person.first_name, "should not run associated person validation on create when validate: false"
end
def test_association_with_validate_false_does_not_run_associated_validation_callbacks_on_update
@@ -898,11 +902,11 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
treasure.valid?
assert_equal 1, treasure.rich_people.size
- assert_equal person_first_name, rich_person.first_name, 'should not run associated person validation on update when validate: false'
+ assert_equal person_first_name, rich_person.first_name, "should not run associated person validation on update when validate: false"
end
def test_custom_join_table
- assert_equal 'edges', Vertex.reflect_on_association(:sources).join_table
+ assert_equal "edges", Vertex.reflect_on_association(:sources).join_table
end
def test_has_and_belongs_to_many_in_a_namespaced_model_pointing_to_a_namespaced_model
@@ -926,11 +930,11 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
def test_redefine_habtm
child = SubDeveloper.new("name" => "Aredridel")
child.special_projects << SpecialProject.new("name" => "Special Project")
- assert child.save, 'child object should be saved'
+ assert child.save, "child object should be saved"
end
def test_habtm_with_reflection_using_class_name_and_fixtures
- assert_not_nil Developer._reflections['shared_computers']
+ assert_not_nil Developer._reflections["shared_computers"]
# Checking the fixture for named association is important here, because it's the only way
# we've been able to reproduce this bug
assert_not_nil File.read(File.expand_path("../../../fixtures/developers.yml", __FILE__)).index("shared_computers")
@@ -939,14 +943,16 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
def test_with_symbol_class_name
assert_nothing_raised do
- DeveloperWithSymbolClassName.new
+ developer = DeveloperWithSymbolClassName.new
+ developer.projects
end
end
- def test_association_force_reload_with_only_true_is_deprecated
- developer = Developer.find(1)
-
- assert_deprecated { developer.projects(true) }
+ def test_with_constant_class_name
+ assert_nothing_raised do
+ developer = DeveloperWithConstantClassName.new
+ developer.projects
+ end
end
def test_alternate_database
@@ -995,4 +1001,22 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
Project.first.developers_required_by_default.create!(name: "Sean", salary: 50000)
end
end
+
+ def test_association_name_is_the_same_as_join_table_name
+ user = User.create!
+ assert_nothing_raised { user.jobs_pool.clear }
+ end
+
+ def test_has_and_belongs_to_many_while_partial_writes_false
+ begin
+ original_partial_writes = ActiveRecord::Base.partial_writes
+ ActiveRecord::Base.partial_writes = false
+ developer = Developer.new(name: "Mehmet Emin İNAÇ")
+ developer.projects << Project.new(name: "Bounty")
+
+ assert developer.save
+ ensure
+ ActiveRecord::Base.partial_writes = original_partial_writes
+ end
+ end
end
diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb
index 7ec0dfce7a..cbecfa84ff 100644
--- a/activerecord/test/cases/associations/has_many_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_associations_test.rb
@@ -1,43 +1,43 @@
require "cases/helper"
-require 'models/developer'
-require 'models/computer'
-require 'models/project'
-require 'models/company'
-require 'models/contract'
-require 'models/topic'
-require 'models/reply'
-require 'models/category'
-require 'models/image'
-require 'models/post'
-require 'models/author'
-require 'models/essay'
-require 'models/comment'
-require 'models/person'
-require 'models/reader'
-require 'models/tagging'
-require 'models/tag'
-require 'models/invoice'
-require 'models/line_item'
-require 'models/car'
-require 'models/bulb'
-require 'models/engine'
-require 'models/categorization'
-require 'models/minivan'
-require 'models/speedometer'
-require 'models/reference'
-require 'models/job'
-require 'models/college'
-require 'models/student'
-require 'models/pirate'
-require 'models/ship'
-require 'models/ship_part'
-require 'models/treasure'
-require 'models/parrot'
-require 'models/tyre'
-require 'models/subscriber'
-require 'models/subscription'
-require 'models/zine'
-require 'models/interest'
+require "models/developer"
+require "models/computer"
+require "models/project"
+require "models/company"
+require "models/contract"
+require "models/topic"
+require "models/reply"
+require "models/category"
+require "models/image"
+require "models/post"
+require "models/author"
+require "models/essay"
+require "models/comment"
+require "models/person"
+require "models/reader"
+require "models/tagging"
+require "models/tag"
+require "models/invoice"
+require "models/line_item"
+require "models/car"
+require "models/bulb"
+require "models/engine"
+require "models/categorization"
+require "models/minivan"
+require "models/speedometer"
+require "models/reference"
+require "models/job"
+require "models/college"
+require "models/student"
+require "models/pirate"
+require "models/ship"
+require "models/ship_part"
+require "models/treasure"
+require "models/parrot"
+require "models/tyre"
+require "models/subscriber"
+require "models/subscription"
+require "models/zine"
+require "models/interest"
class HasManyAssociationsTestForReorderWithJoinDependency < ActiveRecord::TestCase
fixtures :authors, :posts, :comments
@@ -46,7 +46,7 @@ class HasManyAssociationsTestForReorderWithJoinDependency < ActiveRecord::TestCa
author = authors(:david)
# this can fail on adapters which require ORDER BY expressions to be included in the SELECT expression
# if the reorder clauses are not correctly handled
- assert author.posts_with_comments_sorted_by_comment_id.where('comments.id > 0').reorder('posts.comments_count DESC', 'posts.tags_count DESC').last
+ assert author.posts_with_comments_sorted_by_comment_id.where("comments.id > 0").reorder("posts.comments_count DESC", "posts.tags_count DESC").last
end
end
@@ -54,18 +54,18 @@ class HasManyAssociationsTestPrimaryKeys < ActiveRecord::TestCase
fixtures :authors, :essays, :subscribers, :subscriptions, :people
def test_custom_primary_key_on_new_record_should_fetch_with_query
- subscriber = Subscriber.new(nick: 'webster132')
+ subscriber = Subscriber.new(nick: "webster132")
assert !subscriber.subscriptions.loaded?
assert_queries 1 do
assert_equal 2, subscriber.subscriptions.size
end
- assert_equal subscriber.subscriptions, Subscription.where(subscriber_id: 'webster132')
+ assert_equal subscriber.subscriptions, Subscription.where(subscriber_id: "webster132")
end
def test_association_primary_key_on_new_record_should_fetch_with_query
- author = Author.new(:name => "David")
+ author = Author.new(name: "David")
assert !author.essays.loaded?
assert_queries 1 do
@@ -84,7 +84,7 @@ class HasManyAssociationsTestPrimaryKeys < ActiveRecord::TestCase
david = people(:david)
assert_equal ["A Modest Proposal"], david.essays.map(&:name)
- david.essays = [Essay.create!(name: "Remote Work" )]
+ david.essays = [Essay.create!(name: "Remote Work")]
assert_equal ["Remote Work"], david.essays.map(&:name)
end
@@ -116,14 +116,14 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_anonymous_has_many
developer = Class.new(ActiveRecord::Base) {
- self.table_name = 'developers'
+ self.table_name = "developers"
dev = self
developer_project = Class.new(ActiveRecord::Base) {
- self.table_name = 'developers_projects'
- belongs_to :developer, :anonymous_class => dev
+ self.table_name = "developers_projects"
+ belongs_to :developer, anonymous_class: dev
}
- has_many :developer_projects, :anonymous_class => developer_project, :foreign_key => 'developer_id'
+ has_many :developer_projects, anonymous_class: developer_project, foreign_key: "developer_id"
}
dev = developer.first
named = Developer.find(dev.id)
@@ -135,20 +135,20 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_default_scope_on_relations_is_not_cached
counter = 0
posts = Class.new(ActiveRecord::Base) {
- self.table_name = 'posts'
- self.inheritance_column = 'not_there'
+ self.table_name = "posts"
+ self.inheritance_column = "not_there"
post = self
comments = Class.new(ActiveRecord::Base) {
- self.table_name = 'comments'
- self.inheritance_column = 'not_there'
- belongs_to :post, :anonymous_class => post
+ self.table_name = "comments"
+ self.inheritance_column = "not_there"
+ belongs_to :post, anonymous_class: post
default_scope -> {
counter += 1
- where("id = :inc", :inc => counter)
+ where("id = :inc", inc: counter)
}
}
- has_many :comments, :anonymous_class => comments, :foreign_key => 'post_id'
+ has_many :comments, anonymous_class: comments, foreign_key: "post_id"
}
assert_equal 0, counter
post = posts.first
@@ -159,15 +159,15 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_has_many_build_with_options
- college = College.create(name: 'UFMT')
- Student.create(active: true, college_id: college.id, name: 'Sarah')
+ college = College.create(name: "UFMT")
+ Student.create(active: true, college_id: college.id, name: "Sarah")
assert_equal college.students, Student.where(active: true, college_id: college.id)
end
def test_add_record_to_collection_should_change_its_updated_at
- ship = Ship.create(name: 'dauntless')
- part = ShipPart.create(name: 'cockpit')
+ ship = Ship.create(name: "dauntless")
+ part = ShipPart.create(name: "cockpit")
updated_at = part.updated_at
travel(1.second) do
@@ -181,38 +181,38 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_clear_collection_should_not_change_updated_at
# GH#17161: .clear calls delete_all (and returns the association),
# which is intended to not touch associated objects's updated_at field
- ship = Ship.create(name: 'dauntless')
- part = ShipPart.create(name: 'cockpit', ship_id: ship.id)
+ ship = Ship.create(name: "dauntless")
+ part = ShipPart.create(name: "cockpit", ship_id: ship.id)
ship.parts.clear
part.reload
- assert_equal nil, part.ship
+ assert_nil part.ship
assert !part.updated_at_changed?
end
def test_create_from_association_should_respect_default_scope
- car = Car.create(:name => 'honda')
- assert_equal 'honda', car.name
+ car = Car.create(name: "honda")
+ assert_equal "honda", car.name
bulb = Bulb.create
- assert_equal 'defaulty', bulb.name
+ assert_equal "defaulty", bulb.name
bulb = car.bulbs.build
- assert_equal 'defaulty', bulb.name
+ assert_equal "defaulty", bulb.name
bulb = car.bulbs.create
- assert_equal 'defaulty', bulb.name
+ assert_equal "defaulty", bulb.name
end
def test_build_and_create_from_association_should_respect_passed_attributes_over_default_scope
- car = Car.create(name: 'honda')
+ car = Car.create(name: "honda")
- bulb = car.bulbs.build(name: 'exotic')
- assert_equal 'exotic', bulb.name
+ bulb = car.bulbs.build(name: "exotic")
+ assert_equal "exotic", bulb.name
- bulb = car.bulbs.create(name: 'exotic')
- assert_equal 'exotic', bulb.name
+ bulb = car.bulbs.create(name: "exotic")
+ assert_equal "exotic", bulb.name
bulb = car.awesome_bulbs.build(frickinawesome: false)
assert_equal false, bulb.frickinawesome
@@ -225,36 +225,37 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
author = Author.new
post = author.thinking_posts.build
- assert_equal 'So I was thinking', post.title
+ assert_equal "So I was thinking", post.title
end
def test_create_from_association_with_nil_values_should_work
- car = Car.create(:name => 'honda')
+ car = Car.create(name: "honda")
bulb = car.bulbs.new(nil)
- assert_equal 'defaulty', bulb.name
+ assert_equal "defaulty", bulb.name
bulb = car.bulbs.build(nil)
- assert_equal 'defaulty', bulb.name
+ assert_equal "defaulty", bulb.name
bulb = car.bulbs.create(nil)
- assert_equal 'defaulty', bulb.name
+ assert_equal "defaulty", bulb.name
end
def test_do_not_call_callbacks_for_delete_all
- car = Car.create(:name => 'honda')
+ car = Car.create(name: "honda")
car.funky_bulbs.create!
+ assert_equal 1, car.funky_bulbs.count
assert_nothing_raised { car.reload.funky_bulbs.delete_all }
- assert_equal 0, Bulb.count, "bulbs should have been deleted using :delete_all strategy"
+ assert_equal 0, car.funky_bulbs.count, "bulbs should have been deleted using :delete_all strategy"
end
def test_delete_all_on_association_is_the_same_as_not_loaded
author = authors :david
- author.thinking_posts.create!(:body => "test")
+ author.thinking_posts.create!(body: "test")
author.reload
expected_sql = capture_sql { author.thinking_posts.delete_all }
- author.thinking_posts.create!(:body => "test")
+ author.thinking_posts.create!(body: "test")
author.reload
author.thinking_posts.inspect
loaded_sql = capture_sql { author.thinking_posts.delete_all }
@@ -263,11 +264,11 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_delete_all_on_association_with_nil_dependency_is_the_same_as_not_loaded
author = authors :david
- author.posts.create!(:title => "test", :body => "body")
+ author.posts.create!(title: "test", body: "body")
author.reload
expected_sql = capture_sql { author.posts.delete_all }
- author.posts.create!(:title => "test", :body => "body")
+ author.posts.create!(title: "test", body: "body")
author.reload
author.posts.to_a
loaded_sql = capture_sql { author.posts.delete_all }
@@ -282,29 +283,29 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_building_the_associated_object_with_explicit_sti_base_class
firm = DependentFirm.new
- company = firm.companies.build(:type => "Company")
+ company = firm.companies.build(type: "Company")
assert_kind_of Company, company, "Expected #{company.class} to be a Company"
end
def test_building_the_associated_object_with_sti_subclass
firm = DependentFirm.new
- company = firm.companies.build(:type => "Client")
+ company = firm.companies.build(type: "Client")
assert_kind_of Client, company, "Expected #{company.class} to be a Client"
end
def test_building_the_associated_object_with_an_invalid_type
firm = DependentFirm.new
- assert_raise(ActiveRecord::SubclassNotFound) { firm.companies.build(:type => "Invalid") }
+ assert_raise(ActiveRecord::SubclassNotFound) { firm.companies.build(type: "Invalid") }
end
def test_building_the_associated_object_with_an_unrelated_type
firm = DependentFirm.new
- assert_raise(ActiveRecord::SubclassNotFound) { firm.companies.build(:type => "Account") }
+ assert_raise(ActiveRecord::SubclassNotFound) { firm.companies.build(type: "Account") }
end
test "building the association with an array" do
speedometer = Speedometer.new(speedometer_id: "a")
- data = [{name: "first"}, {name: "second"}]
+ data = [{ name: "first" }, { name: "second" }]
speedometer.minivans.build(data)
assert_equal 2, speedometer.minivans.size
@@ -313,24 +314,24 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_association_keys_bypass_attribute_protection
- car = Car.create(:name => 'honda')
+ car = Car.create(name: "honda")
bulb = car.bulbs.new
assert_equal car.id, bulb.car_id
- bulb = car.bulbs.new :car_id => car.id + 1
+ bulb = car.bulbs.new car_id: car.id + 1
assert_equal car.id, bulb.car_id
bulb = car.bulbs.build
assert_equal car.id, bulb.car_id
- bulb = car.bulbs.build :car_id => car.id + 1
+ bulb = car.bulbs.build car_id: car.id + 1
assert_equal car.id, bulb.car_id
bulb = car.bulbs.create
assert_equal car.id, bulb.car_id
- bulb = car.bulbs.create :car_id => car.id + 1
+ bulb = car.bulbs.create car_id: car.id + 1
assert_equal car.id, bulb.car_id
end
@@ -340,19 +341,19 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
line_item = invoice.line_items.new
assert_equal invoice.id, line_item.invoice_id
- line_item = invoice.line_items.new :invoice_id => invoice.id + 1
+ line_item = invoice.line_items.new invoice_id: invoice.id + 1
assert_equal invoice.id, line_item.invoice_id
line_item = invoice.line_items.build
assert_equal invoice.id, line_item.invoice_id
- line_item = invoice.line_items.build :invoice_id => invoice.id + 1
+ line_item = invoice.line_items.build invoice_id: invoice.id + 1
assert_equal invoice.id, line_item.invoice_id
line_item = invoice.line_items.create
assert_equal invoice.id, line_item.invoice_id
- line_item = invoice.line_items.create :invoice_id => invoice.id + 1
+ line_item = invoice.line_items.create invoice_id: invoice.id + 1
assert_equal invoice.id, line_item.invoice_id
end
@@ -373,64 +374,95 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_no_sql_should_be_fired_if_association_already_loaded
- Car.create(:name => 'honda')
+ Car.create(name: "honda")
bulbs = Car.first.bulbs
bulbs.to_a # to load all instances of bulbs
assert_no_queries do
bulbs.first()
- bulbs.first({})
end
assert_no_queries do
bulbs.second()
- bulbs.second({})
end
assert_no_queries do
bulbs.third()
- bulbs.third({})
end
assert_no_queries do
bulbs.fourth()
- bulbs.fourth({})
end
assert_no_queries do
bulbs.fifth()
- bulbs.fifth({})
end
assert_no_queries do
bulbs.forty_two()
- bulbs.forty_two({})
end
assert_no_queries do
bulbs.third_to_last()
- bulbs.third_to_last({})
end
assert_no_queries do
bulbs.second_to_last()
- bulbs.second_to_last({})
end
assert_no_queries do
bulbs.last()
- bulbs.last({})
+ end
+ end
+
+ def test_finder_method_with_dirty_target
+ company = companies(:first_firm)
+ new_clients = []
+ assert_no_queries(ignore_none: false) do
+ new_clients << company.clients_of_firm.build(name: "Another Client")
+ new_clients << company.clients_of_firm.build(name: "Another Client II")
+ new_clients << company.clients_of_firm.build(name: "Another Client III")
+ end
+
+ assert_not company.clients_of_firm.loaded?
+ assert_queries(1) do
+ assert_same new_clients[0], company.clients_of_firm.third
+ assert_same new_clients[1], company.clients_of_firm.fourth
+ assert_same new_clients[2], company.clients_of_firm.fifth
+ assert_same new_clients[0], company.clients_of_firm.third_to_last
+ assert_same new_clients[1], company.clients_of_firm.second_to_last
+ assert_same new_clients[2], company.clients_of_firm.last
+ end
+ end
+
+ def test_finder_bang_method_with_dirty_target
+ company = companies(:first_firm)
+ new_clients = []
+ assert_no_queries(ignore_none: false) do
+ new_clients << company.clients_of_firm.build(name: "Another Client")
+ new_clients << company.clients_of_firm.build(name: "Another Client II")
+ new_clients << company.clients_of_firm.build(name: "Another Client III")
+ end
+
+ assert_not company.clients_of_firm.loaded?
+ assert_queries(1) do
+ assert_same new_clients[0], company.clients_of_firm.third!
+ assert_same new_clients[1], company.clients_of_firm.fourth!
+ assert_same new_clients[2], company.clients_of_firm.fifth!
+ assert_same new_clients[0], company.clients_of_firm.third_to_last!
+ assert_same new_clients[1], company.clients_of_firm.second_to_last!
+ assert_same new_clients[2], company.clients_of_firm.last!
end
end
def test_create_resets_cached_counters
- person = Person.create!(:first_name => 'tenderlove')
+ person = Person.create!(first_name: "tenderlove")
post = Post.first
assert_equal [], person.readers
assert_nil person.readers.find_by_post_id(post.id)
- person.readers.create(:post_id => post.id)
+ person.readers.create(post_id: post.id)
assert_equal 1, person.readers.count
assert_equal 1, person.readers.length
@@ -438,25 +470,41 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_equal person, person.readers.first.person
end
- def force_signal37_to_load_all_clients_of_firm
- companies(:first_firm).clients_of_firm.each {|f| }
+ def test_update_all_respects_association_scope
+ person = Person.new
+ person.first_name = "Naruto"
+ person.references << Reference.new
+ person.id = 10
+ person.references
+ person.save!
+ assert_equal 1, person.references.update_all(favourite: true)
+ end
+
+ def test_exists_respects_association_scope
+ person = Person.new
+ person.first_name = "Sasuke"
+ person.references << Reference.new
+ person.id = 10
+ person.references
+ person.save!
+ assert_predicate person.references, :exists?
end
# sometimes tests on Oracle fail if ORDER BY is not provided therefore add always :order with :first
def test_counting_with_counter_sql
- assert_equal 3, Firm.all.merge!(:order => "id").first.clients.count
+ assert_equal 3, Firm.all.merge!(order: "id").first.clients.count
end
def test_counting
- assert_equal 3, Firm.all.merge!(:order => "id").first.plain_clients.count
+ assert_equal 3, Firm.all.merge!(order: "id").first.plain_clients.count
end
def test_counting_with_single_hash
- assert_equal 1, Firm.all.merge!(:order => "id").first.plain_clients.where(:name => "Microsoft").count
+ assert_equal 1, Firm.all.merge!(order: "id").first.plain_clients.where(name: "Microsoft").count
end
def test_counting_with_column_name_and_hash
- assert_equal 3, Firm.all.merge!(:order => "id").first.plain_clients.count(:name)
+ assert_equal 3, Firm.all.merge!(order: "id").first.plain_clients.count(:name)
end
def test_counting_with_association_limit
@@ -466,11 +514,11 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_finding
- assert_equal 3, Firm.all.merge!(:order => "id").first.clients.length
+ assert_equal 3, Firm.all.merge!(order: "id").first.clients.length
end
def test_finding_array_compatibility
- assert_equal 3, Firm.order(:id).find{|f| f.id > 0}.clients.length
+ assert_equal 3, Firm.order(:id).find { |f| f.id > 0 }.clients.length
end
def test_find_many_with_merged_options
@@ -480,13 +528,13 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_find_should_append_to_association_order
- ordered_clients = companies(:first_firm).clients_sorted_desc.order('companies.id')
- assert_equal ['id DESC', 'companies.id'], ordered_clients.order_values
+ ordered_clients = companies(:first_firm).clients_sorted_desc.order("companies.id")
+ assert_equal ["id DESC", "companies.id"], ordered_clients.order_values
end
def test_dynamic_find_should_respect_association_order
assert_equal companies(:another_first_firm_client), companies(:first_firm).clients_sorted_desc.where("type = 'Client'").first
- assert_equal companies(:another_first_firm_client), companies(:first_firm).clients_sorted_desc.find_by_type('Client')
+ assert_equal companies(:another_first_firm_client), companies(:first_firm).clients_sorted_desc.find_by_type("Client")
end
def test_taking
@@ -508,14 +556,18 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_taking_with_a_number
# taking from unloaded Relation
bob = Author.find(authors(:bob).id)
+ new_post = bob.posts.build
+ assert_not bob.posts.loaded?
assert_equal [posts(:misc_by_bob)], bob.posts.take(1)
- bob = Author.find(authors(:bob).id)
assert_equal [posts(:misc_by_bob), posts(:other_by_bob)], bob.posts.take(2)
+ assert_equal [posts(:misc_by_bob), posts(:other_by_bob), new_post], bob.posts.take(3)
# taking from loaded Relation
- bob.posts.to_a
- assert_equal [posts(:misc_by_bob)], authors(:bob).posts.take(1)
- assert_equal [posts(:misc_by_bob), posts(:other_by_bob)], authors(:bob).posts.take(2)
+ bob.posts.load
+ assert bob.posts.loaded?
+ assert_equal [posts(:misc_by_bob)], bob.posts.take(1)
+ assert_equal [posts(:misc_by_bob), posts(:other_by_bob)], bob.posts.take(2)
+ assert_equal [posts(:misc_by_bob), posts(:other_by_bob), new_post], bob.posts.take(3)
end
def test_taking_with_inverse_of
@@ -534,45 +586,45 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_finding_default_orders
- assert_equal "Summit", Firm.all.merge!(:order => "id").first.clients.first.name
+ assert_equal "Summit", Firm.all.merge!(order: "id").first.clients.first.name
end
def test_finding_with_different_class_name_and_order
- assert_equal "Apex", Firm.all.merge!(:order => "id").first.clients_sorted_desc.first.name
+ assert_equal "Apex", Firm.all.merge!(order: "id").first.clients_sorted_desc.first.name
end
def test_finding_with_foreign_key
- assert_equal "Microsoft", Firm.all.merge!(:order => "id").first.clients_of_firm.first.name
+ assert_equal "Microsoft", Firm.all.merge!(order: "id").first.clients_of_firm.first.name
end
def test_finding_with_condition
- assert_equal "Microsoft", Firm.all.merge!(:order => "id").first.clients_like_ms.first.name
+ assert_equal "Microsoft", Firm.all.merge!(order: "id").first.clients_like_ms.first.name
end
def test_finding_with_condition_hash
- assert_equal "Microsoft", Firm.all.merge!(:order => "id").first.clients_like_ms_with_hash_conditions.first.name
+ assert_equal "Microsoft", Firm.all.merge!(order: "id").first.clients_like_ms_with_hash_conditions.first.name
end
def test_finding_using_primary_key
- assert_equal "Summit", Firm.all.merge!(:order => "id").first.clients_using_primary_key.first.name
+ assert_equal "Summit", Firm.all.merge!(order: "id").first.clients_using_primary_key.first.name
end
def test_update_all_on_association_accessed_before_save
- firm = Firm.new(name: 'Firm')
+ firm = Firm.new(name: "Firm")
clients_proxy_id = firm.clients.object_id
firm.clients << Client.first
firm.save!
- assert_equal firm.clients.count, firm.clients.update_all(description: 'Great!')
+ assert_equal firm.clients.count, firm.clients.update_all(description: "Great!")
assert_not_equal clients_proxy_id, firm.clients.object_id
end
def test_update_all_on_association_accessed_before_save_with_explicit_foreign_key
# We can use the same cached proxy object because the id is available for the scope
- firm = Firm.new(name: 'Firm', id: 100)
+ firm = Firm.new(name: "Firm", id: 100)
clients_proxy_id = firm.clients.object_id
firm.clients << Client.first
firm.save!
- assert_equal firm.clients.count, firm.clients.update_all(description: 'Great!')
+ assert_equal firm.clients.count, firm.clients.update_all(description: "Great!")
assert_equal clients_proxy_id, firm.clients.object_id
end
@@ -582,7 +634,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_find_ids
- firm = Firm.all.merge!(:order => "id").first
+ firm = Firm.all.merge!(order: "id").first
assert_raise(ActiveRecord::RecordNotFound) { firm.clients.find }
@@ -601,9 +653,23 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_raise(ActiveRecord::RecordNotFound) { firm.clients.find(2, 99) }
end
+ def test_find_one_message_on_primary_key
+ firm = Firm.all.merge!(order: "id").first
+
+ e = assert_raises(ActiveRecord::RecordNotFound) do
+ firm.clients.find(0)
+ end
+ assert_equal 0, e.id
+ assert_equal "id", e.primary_key
+ assert_equal "Client", e.model
+ assert_match (/\ACouldn't find Client with 'id'=0/), e.message
+ end
+
def test_find_ids_and_inverse_of
force_signal37_to_load_all_clients_of_firm
+ assert_predicate companies(:first_firm).clients_of_firm, :loaded?
+
firm = companies(:first_firm)
client = firm.clients_of_firm.find(3)
assert_kind_of Client, client
@@ -614,7 +680,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_find_all
- firm = Firm.all.merge!(:order => "id").first
+ firm = Firm.all.merge!(order: "id").first
assert_equal 3, firm.clients.where("#{QUOTED_TYPE} = 'Client'").to_a.length
assert_equal 1, firm.clients.where("name = 'Summit'").to_a.length
end
@@ -625,7 +691,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert ! firm.clients.loaded?
assert_queries(4) do
- firm.clients.find_each(:batch_size => 1) {|c| assert_equal firm.id, c.firm_id }
+ firm.clients.find_each(batch_size: 1) { |c| assert_equal firm.id, c.firm_id }
end
assert ! firm.clients.loaded?
@@ -635,7 +701,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
firm = companies(:first_firm)
assert_queries(2) do
- firm.clients.where(name: 'Microsoft').find_each(batch_size: 1) do |c|
+ firm.clients.where(name: "Microsoft").find_each(batch_size: 1) do |c|
assert_equal firm.id, c.firm_id
assert_equal "Microsoft", c.name
end
@@ -650,8 +716,8 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert ! firm.clients.loaded?
assert_queries(2) do
- firm.clients.find_in_batches(:batch_size => 2) do |clients|
- clients.each {|c| assert_equal firm.id, c.firm_id }
+ firm.clients.find_in_batches(batch_size: 2) do |clients|
+ clients.each { |c| assert_equal firm.id, c.firm_id }
end
end
@@ -660,29 +726,29 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_find_all_sanitized
# sometimes tests on Oracle fail if ORDER BY is not provided therefore add always :order with :first
- firm = Firm.all.merge!(:order => "id").first
+ firm = Firm.all.merge!(order: "id").first
summit = firm.clients.where("name = 'Summit'").to_a
assert_equal summit, firm.clients.where("name = ?", "Summit").to_a
- assert_equal summit, firm.clients.where("name = :name", { :name => "Summit" }).to_a
+ assert_equal summit, firm.clients.where("name = :name", name: "Summit").to_a
end
def test_find_first
- firm = Firm.all.merge!(:order => "id").first
+ firm = Firm.all.merge!(order: "id").first
client2 = Client.find(2)
assert_equal firm.clients.first, firm.clients.order("id").first
assert_equal client2, firm.clients.where("#{QUOTED_TYPE} = 'Client'").order("id").first
end
def test_find_first_sanitized
- firm = Firm.all.merge!(:order => "id").first
+ firm = Firm.all.merge!(order: "id").first
client2 = Client.find(2)
- assert_equal client2, firm.clients.merge!(:where => ["#{QUOTED_TYPE} = ?", 'Client'], :order => "id").first
- assert_equal client2, firm.clients.merge!(:where => ["#{QUOTED_TYPE} = :type", { :type => 'Client' }], :order => "id").first
+ assert_equal client2, firm.clients.merge!(where: ["#{QUOTED_TYPE} = ?", "Client"], order: "id").first
+ assert_equal client2, firm.clients.merge!(where: ["#{QUOTED_TYPE} = :type", { type: "Client" }], order: "id").first
end
def test_find_all_with_include_and_conditions
assert_nothing_raised do
- Developer.all.merge!(:joins => :audit_logs, :where => {'audit_logs.message' => nil, :name => 'Smith'}).to_a
+ Developer.all.merge!(joins: :audit_logs, where: { "audit_logs.message" => nil, :name => "Smith" }).to_a
end
end
@@ -692,8 +758,8 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_find_grouped
- all_clients_of_firm1 = Client.all.merge!(:where => "firm_id = 1").to_a
- grouped_clients_of_firm1 = Client.all.merge!(:where => "firm_id = 1", :group => "firm_id", :select => 'firm_id, count(id) as clients_count').to_a
+ all_clients_of_firm1 = Client.all.merge!(where: "firm_id = 1").to_a
+ grouped_clients_of_firm1 = Client.all.merge!(where: "firm_id = 1", group: "firm_id", select: "firm_id, count(id) as clients_count").to_a
assert_equal 3, all_clients_of_firm1.size
assert_equal 1, grouped_clients_of_firm1.size
end
@@ -715,7 +781,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_select_query_method
- assert_equal ['id', 'body'], posts(:welcome).comments.select(:id, :body).first.attributes.keys
+ assert_equal ["id", "body"], posts(:welcome).comments.select(:id, :body).first.attributes.keys
end
def test_select_with_block
@@ -728,6 +794,9 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_adding
force_signal37_to_load_all_clients_of_firm
+
+ assert_predicate companies(:first_firm).clients_of_firm, :loaded?
+
natural = Client.new("name" => "Natural Company")
companies(:first_firm).clients_of_firm << natural
assert_equal 3, companies(:first_firm).clients_of_firm.size # checking via the collection
@@ -738,7 +807,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_adding_using_create
first_firm = companies(:first_firm)
assert_equal 3, first_firm.plain_clients.size
- first_firm.plain_clients.create(:name => "Natural Company")
+ first_firm.plain_clients.create(name: "Natural Company")
assert_equal 4, first_firm.plain_clients.length
assert_equal 4, first_firm.plain_clients.size
end
@@ -746,7 +815,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_create_with_bang_on_has_many_when_parent_is_new_raises
error = assert_raise(ActiveRecord::RecordNotSaved) do
firm = Firm.new
- firm.plain_clients.create! :name=>"Whoever"
+ firm.plain_clients.create! name: "Whoever"
end
assert_equal "You cannot call create unless the parent is saved", error.message
@@ -755,7 +824,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_regular_create_on_has_many_when_parent_is_new_raises
error = assert_raise(ActiveRecord::RecordNotSaved) do
firm = Firm.new
- firm.plain_clients.create :name=>"Whoever"
+ firm.plain_clients.create name: "Whoever"
end
assert_equal "You cannot call create unless the parent is saved", error.message
@@ -763,7 +832,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_create_with_bang_on_has_many_raises_when_record_not_saved
assert_raise(ActiveRecord::RecordInvalid) do
- firm = Firm.all.merge!(:order => "id").first
+ firm = Firm.all.merge!(order: "id").first
firm.plain_clients.create!
end
end
@@ -784,21 +853,24 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_adding_a_collection
force_signal37_to_load_all_clients_of_firm
+
+ assert_predicate companies(:first_firm).clients_of_firm, :loaded?
+
companies(:first_firm).clients_of_firm.concat([Client.new("name" => "Natural Company"), Client.new("name" => "Apple")])
assert_equal 4, companies(:first_firm).clients_of_firm.size
assert_equal 4, companies(:first_firm).clients_of_firm.reload.size
end
def test_transactions_when_adding_to_persisted
- good = Client.new(:name => "Good")
- bad = Client.new(:name => "Bad", :raise_on_save => true)
+ good = Client.new(name: "Good")
+ bad = Client.new(name: "Bad", raise_on_save: true)
begin
companies(:first_firm).clients_of_firm.concat(good, bad)
rescue Client::RaisedOnSave
end
- assert !companies(:first_firm).clients_of_firm.reload.include?(good)
+ assert_not_includes companies(:first_firm).clients_of_firm.reload, good
end
def test_transactions_when_adding_to_new_record
@@ -840,6 +912,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
company.clients_of_firm.build("name" => "Another Client")
company.clients_of_firm.build("name" => "Yet Another Client")
assert_equal 4, company.clients_of_firm.size
+ assert_equal 4, company.clients_of_firm.uniq.size
end
def test_collection_not_empty_after_building
@@ -863,7 +936,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_build_many
company = companies(:first_firm)
- new_clients = assert_no_queries(ignore_none: false) { company.clients_of_firm.build([{"name" => "Another Client"}, {"name" => "Another Client II"}]) }
+ new_clients = assert_no_queries(ignore_none: false) { company.clients_of_firm.build([{ "name" => "Another Client" }, { "name" => "Another Client II" }]) }
assert_equal 2, new_clients.size
end
@@ -880,7 +953,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_equal 1, first_topic.replies.length
assert_no_queries do
- first_topic.replies.build(:title => "Not saved", :content => "Superstars")
+ first_topic.replies.build(title: "Not saved", content: "Superstars")
assert_equal 2, first_topic.replies.size
end
@@ -889,7 +962,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_build_via_block
company = companies(:first_firm)
- new_client = assert_no_queries(ignore_none: false) { company.clients_of_firm.build {|client| client.name = "Another Client" } }
+ new_client = assert_no_queries(ignore_none: false) { company.clients_of_firm.build { |client| client.name = "Another Client" } }
assert !company.clients_of_firm.loaded?
assert_equal "Another Client", new_client.name
@@ -900,7 +973,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_build_many_via_block
company = companies(:first_firm)
new_clients = assert_no_queries(ignore_none: false) do
- company.clients_of_firm.build([{"name" => "Another Client"}, {"name" => "Another Client II"}]) do |client|
+ company.clients_of_firm.build([{ "name" => "Another Client" }, { "name" => "Another Client II" }]) do |client|
client.name = "changed"
end
end
@@ -911,7 +984,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_create_without_loading_association
- first_firm = companies(:first_firm)
+ first_firm = companies(:first_firm)
Firm.column_names
Client.column_names
@@ -919,7 +992,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
first_firm.clients_of_firm.reset
assert_queries(1) do
- first_firm.clients_of_firm.create(:name => "Superstars")
+ first_firm.clients_of_firm.create(name: "Superstars")
end
assert_equal 3, first_firm.clients_of_firm.size
@@ -927,6 +1000,9 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_create
force_signal37_to_load_all_clients_of_firm
+
+ assert_predicate companies(:first_firm).clients_of_firm, :loaded?
+
new_client = companies(:first_firm).clients_of_firm.create("name" => "Another Client")
assert new_client.persisted?
assert_equal new_client, companies(:first_firm).clients_of_firm.last
@@ -934,7 +1010,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_create_many
- companies(:first_firm).clients_of_firm.create([{"name" => "Another Client"}, {"name" => "Another Client II"}])
+ companies(:first_firm).clients_of_firm.create([{ "name" => "Another Client" }, { "name" => "Another Client II" }])
assert_equal 4, companies(:first_firm).clients_of_firm.reload.size
end
@@ -946,6 +1022,9 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_deleting
force_signal37_to_load_all_clients_of_firm
+
+ assert_predicate companies(:first_firm).clients_of_firm, :loaded?
+
companies(:first_firm).clients_of_firm.delete(companies(:first_firm).clients_of_firm.first)
assert_equal 1, companies(:first_firm).clients_of_firm.size
assert_equal 1, companies(:first_firm).clients_of_firm.reload.size
@@ -962,7 +1041,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_has_many_without_counter_cache_option
# Ship has a conventionally named `treasures_count` column, but the counter_cache
# option is not given on the association.
- ship = Ship.create(name: 'Countless', treasures_count: 10)
+ ship = Ship.create(name: "Countless", treasures_count: 10)
assert_not Ship.reflect_on_association(:treasures).has_cached_counter?
@@ -970,7 +1049,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_equal ship.treasures.size, 0
assert_no_difference lambda { ship.reload.treasures_count }, "treasures_count should not be changed" do
- ship.treasures.create(name: 'Gold')
+ ship.treasures.create(name: "Gold")
end
assert_no_difference lambda { ship.reload.treasures_count }, "treasures_count should not be changed" do
@@ -1073,10 +1152,10 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_equal original_count, topic.replies_count
first_reply = topic.replies.first
- first_reply.update_attributes(:parent_id => nil)
+ first_reply.update_attributes(parent_id: nil)
assert_equal original_count - 1, topic.reload.replies_count
- first_reply.update_attributes(:parent_id => topic.id)
+ first_reply.update_attributes(parent_id: topic.id)
assert_equal original_count, topic.reload.replies_count
end
@@ -1089,17 +1168,20 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
reply1 = topic1.replies.first
reply2 = topic2.replies.first
- reply1.update_attributes(:parent_id => topic2.id)
+ reply1.update_attributes(parent_id: topic2.id)
assert_equal original_count1 - 1, topic1.reload.replies_count
assert_equal original_count2 + 1, topic2.reload.replies_count
- reply2.update_attributes(:parent_id => topic1.id)
+ reply2.update_attributes(parent_id: topic1.id)
assert_equal original_count1, topic1.reload.replies_count
assert_equal original_count2, topic2.reload.replies_count
end
def test_deleting_a_collection
force_signal37_to_load_all_clients_of_firm
+
+ assert_predicate companies(:first_firm).clients_of_firm, :loaded?
+
companies(:first_firm).clients_of_firm.create("name" => "Another Client")
assert_equal 3, companies(:first_firm).clients_of_firm.size
companies(:first_firm).clients_of_firm.delete([companies(:first_firm).clients_of_firm[0], companies(:first_firm).clients_of_firm[1], companies(:first_firm).clients_of_firm[2]])
@@ -1109,6 +1191,9 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_delete_all
force_signal37_to_load_all_clients_of_firm
+
+ assert_predicate companies(:first_firm).clients_of_firm, :loaded?
+
companies(:first_firm).dependent_clients_of_firm.create("name" => "Another Client")
clients = companies(:first_firm).dependent_clients_of_firm.to_a
assert_equal 3, clients.count
@@ -1120,6 +1205,9 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_delete_all_with_not_yet_loaded_association_collection
force_signal37_to_load_all_clients_of_firm
+
+ assert_predicate companies(:first_firm).clients_of_firm, :loaded?
+
companies(:first_firm).clients_of_firm.create("name" => "Another Client")
assert_equal 3, companies(:first_firm).clients_of_firm.size
companies(:first_firm).clients_of_firm.reset
@@ -1129,8 +1217,8 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_transaction_when_deleting_persisted
- good = Client.new(:name => "Good")
- bad = Client.new(:name => "Bad", :raise_on_destroy => true)
+ good = Client.new(name: "Good")
+ bad = Client.new(name: "Bad", raise_on_destroy: true)
companies(:first_firm).clients_of_firm = [good, bad]
@@ -1171,7 +1259,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_clearing_updates_counter_cache
topic = Topic.first
- assert_difference 'topic.reload.replies_count', -1 do
+ assert_difference "topic.reload.replies_count", -1 do
topic.replies.clear
end
end
@@ -1180,7 +1268,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
car = Car.first
car.engines.create!
- assert_difference 'car.reload.engines_count', -1 do
+ assert_difference "car.reload.engines_count", -1 do
car.engines.clear
end
end
@@ -1238,8 +1326,8 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_dependent_association_respects_optional_conditions_on_delete
firm = companies(:odegy)
- Client.create(:client_of => firm.id, :name => "BigShot Inc.")
- Client.create(:client_of => firm.id, :name => "SmallTime Inc.")
+ Client.create(client_of: firm.id, name: "BigShot Inc.")
+ Client.create(client_of: firm.id, name: "SmallTime Inc.")
# only one of two clients is included in the association due to the :conditions key
assert_equal 2, Client.where(client_of: firm.id).size
assert_equal 1, firm.dependent_conditional_clients_of_firm.size
@@ -1250,8 +1338,8 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_dependent_association_respects_optional_sanitized_conditions_on_delete
firm = companies(:odegy)
- Client.create(:client_of => firm.id, :name => "BigShot Inc.")
- Client.create(:client_of => firm.id, :name => "SmallTime Inc.")
+ Client.create(client_of: firm.id, name: "BigShot Inc.")
+ Client.create(client_of: firm.id, name: "SmallTime Inc.")
# only one of two clients is included in the association due to the :conditions key
assert_equal 2, Client.where(client_of: firm.id).size
assert_equal 1, firm.dependent_sanitized_conditional_clients_of_firm.size
@@ -1262,8 +1350,8 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_dependent_association_respects_optional_hash_conditions_on_delete
firm = companies(:odegy)
- Client.create(:client_of => firm.id, :name => "BigShot Inc.")
- Client.create(:client_of => firm.id, :name => "SmallTime Inc.")
+ Client.create(client_of: firm.id, name: "BigShot Inc.")
+ Client.create(client_of: firm.id, name: "SmallTime Inc.")
# only one of two clients is included in the association due to the :conditions key
assert_equal 2, Client.where(client_of: firm.id).size
assert_equal 1, firm.dependent_sanitized_conditional_clients_of_firm.size
@@ -1289,12 +1377,12 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
ms_client = companies(:first_firm).clients_like_ms_with_hash_conditions.build
assert ms_client.save
- assert_equal 'Microsoft', ms_client.name
+ assert_equal "Microsoft", ms_client.name
another_ms_client = companies(:first_firm).clients_like_ms_with_hash_conditions.create
assert another_ms_client.persisted?
- assert_equal 'Microsoft', another_ms_client.name
+ assert_equal "Microsoft", another_ms_client.name
end
def test_clearing_without_initial_access
@@ -1308,7 +1396,10 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_deleting_a_item_which_is_not_in_the_collection
force_signal37_to_load_all_clients_of_firm
- summit = Client.find_by_name('Summit')
+
+ assert_predicate companies(:first_firm).clients_of_firm, :loaded?
+
+ summit = Client.find_by_name("Summit")
companies(:first_firm).clients_of_firm.delete(summit)
assert_equal 2, companies(:first_firm).clients_of_firm.size
assert_equal 2, companies(:first_firm).clients_of_firm.reload.size
@@ -1318,7 +1409,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_deleting_by_integer_id
david = Developer.find(1)
- assert_difference 'david.projects.count', -1 do
+ assert_difference "david.projects.count", -1 do
assert_equal 1, david.projects.delete(1).size
end
@@ -1328,8 +1419,8 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_deleting_by_string_id
david = Developer.find(1)
- assert_difference 'david.projects.count', -1 do
- assert_equal 1, david.projects.delete('1').size
+ assert_difference "david.projects.count", -1 do
+ assert_equal 1, david.projects.delete("1").size
end
assert_equal 1, david.projects.size
@@ -1344,6 +1435,8 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_destroying
force_signal37_to_load_all_clients_of_firm
+ assert_predicate companies(:first_firm).clients_of_firm, :loaded?
+
assert_difference "Client.count", -1 do
companies(:first_firm).clients_of_firm.destroy(companies(:first_firm).clients_of_firm.first)
end
@@ -1355,6 +1448,8 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_destroying_by_integer_id
force_signal37_to_load_all_clients_of_firm
+ assert_predicate companies(:first_firm).clients_of_firm, :loaded?
+
assert_difference "Client.count", -1 do
companies(:first_firm).clients_of_firm.destroy(companies(:first_firm).clients_of_firm.first.id)
end
@@ -1366,6 +1461,8 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_destroying_by_string_id
force_signal37_to_load_all_clients_of_firm
+ assert_predicate companies(:first_firm).clients_of_firm, :loaded?
+
assert_difference "Client.count", -1 do
companies(:first_firm).clients_of_firm.destroy(companies(:first_firm).clients_of_firm.first.id.to_s)
end
@@ -1376,6 +1473,9 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_destroying_a_collection
force_signal37_to_load_all_clients_of_firm
+
+ assert_predicate companies(:first_firm).clients_of_firm, :loaded?
+
companies(:first_firm).clients_of_firm.create("name" => "Another Client")
assert_equal 3, companies(:first_firm).clients_of_firm.size
@@ -1389,6 +1489,9 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_destroy_all
force_signal37_to_load_all_clients_of_firm
+
+ assert_predicate companies(:first_firm).clients_of_firm, :loaded?
+
clients = companies(:first_firm).clients_of_firm.to_a
assert !clients.empty?, "37signals has clients after load"
destroyed = companies(:first_firm).clients_of_firm.destroy_all
@@ -1402,17 +1505,17 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
firm = companies(:first_firm)
assert_equal 3, firm.clients.size
firm.destroy
- assert Client.all.merge!(:where => "firm_id=#{firm.id}").to_a.empty?
+ assert Client.all.merge!(where: "firm_id=#{firm.id}").to_a.empty?
end
def test_dependence_for_associations_with_hash_condition
david = authors(:david)
- assert_difference('Post.count', -1) { assert david.destroy }
+ assert_difference("Post.count", -1) { assert david.destroy }
end
def test_destroy_dependent_when_deleted_from_association
# sometimes tests on Oracle fail if ORDER BY is not provided therefore add always :order with :first
- firm = Firm.all.merge!(:order => "id").first
+ firm = Firm.all.merge!(order: "id").first
assert_equal 3, firm.clients.size
client = firm.clients.first
@@ -1439,7 +1542,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
firm.destroy rescue "do nothing"
- assert_equal 3, Client.all.merge!(:where => "firm_id=#{firm.id}").to_a.size
+ assert_equal 3, Client.all.merge!(where: "firm_id=#{firm.id}").to_a.size
end
def test_dependence_on_account
@@ -1463,38 +1566,18 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_restrict_with_exception
- firm = RestrictedWithExceptionFirm.create!(:name => 'restrict')
- firm.companies.create(:name => 'child')
+ firm = RestrictedWithExceptionFirm.create!(name: "restrict")
+ firm.companies.create(name: "child")
assert !firm.companies.empty?
assert_raise(ActiveRecord::DeleteRestrictionError) { firm.destroy }
- assert RestrictedWithExceptionFirm.exists?(:name => 'restrict')
- assert firm.companies.exists?(:name => 'child')
- end
-
- def test_restrict_with_error_is_deprecated_using_key_many
- I18n.backend = I18n::Backend::Simple.new
- I18n.backend.store_translations :en, activerecord: { errors: { messages: { restrict_dependent_destroy: { many: 'message for deprecated key' } } } }
-
- firm = RestrictedWithErrorFirm.create!(name: 'restrict')
- firm.companies.create(name: 'child')
-
- assert !firm.companies.empty?
-
- assert_deprecated { firm.destroy }
-
- assert !firm.errors.empty?
-
- assert_equal 'message for deprecated key', firm.errors[:base].first
- assert RestrictedWithErrorFirm.exists?(name: 'restrict')
- assert firm.companies.exists?(name: 'child')
- ensure
- I18n.backend.reload!
+ assert RestrictedWithExceptionFirm.exists?(name: "restrict")
+ assert firm.companies.exists?(name: "child")
end
def test_restrict_with_error
- firm = RestrictedWithErrorFirm.create!(:name => 'restrict')
- firm.companies.create(:name => 'child')
+ firm = RestrictedWithErrorFirm.create!(name: "restrict")
+ firm.companies.create(name: "child")
assert !firm.companies.empty?
@@ -1503,15 +1586,15 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert !firm.errors.empty?
assert_equal "Cannot delete record because dependent companies exist", firm.errors[:base].first
- assert RestrictedWithErrorFirm.exists?(:name => 'restrict')
- assert firm.companies.exists?(:name => 'child')
+ assert RestrictedWithErrorFirm.exists?(name: "restrict")
+ assert firm.companies.exists?(name: "child")
end
def test_restrict_with_error_with_locale
I18n.backend = I18n::Backend::Simple.new
- I18n.backend.store_translations 'en', activerecord: {attributes: {restricted_with_error_firm: {companies: 'client companies'}}}
- firm = RestrictedWithErrorFirm.create!(name: 'restrict')
- firm.companies.create(name: 'child')
+ I18n.backend.store_translations "en", activerecord: { attributes: { restricted_with_error_firm: { companies: "client companies" } } }
+ firm = RestrictedWithErrorFirm.create!(name: "restrict")
+ firm.companies.create(name: "child")
assert !firm.companies.empty?
@@ -1520,8 +1603,8 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert !firm.errors.empty?
assert_equal "Cannot delete record because dependent client companies exist", firm.errors[:base].first
- assert RestrictedWithErrorFirm.exists?(name: 'restrict')
- assert firm.companies.exists?(name: 'child')
+ assert RestrictedWithErrorFirm.exists?(name: "restrict")
+ assert firm.companies.exists?(name: "child")
ensure
I18n.backend.reload!
end
@@ -1531,10 +1614,10 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_included_in_collection_for_new_records
- client = Client.create(:name => 'Persisted')
+ client = Client.create(name: "Persisted")
assert_nil client.client_of
assert_equal false, Firm.new.clients_of_firm.include?(client),
- 'includes a client that does not belong to any firm'
+ "includes a client that does not belong to any firm"
end
def test_adding_array_and_collection
@@ -1542,7 +1625,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_replace_with_less
- firm = Firm.all.merge!(:order => "id").first
+ firm = Firm.all.merge!(order: "id").first
firm.clients = [companies(:first_client)]
assert firm.save, "Could not save firm"
firm.reload
@@ -1556,7 +1639,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_replace_with_new
- firm = Firm.all.merge!(:order => "id").first
+ firm = Firm.all.merge!(order: "id").first
firm.clients = [companies(:second_client), Client.new("name" => "New Client")]
firm.save
firm.reload
@@ -1589,12 +1672,12 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
firm.clients = []
end
- assert_equal [], firm.send('clients=', [])
+ assert_equal [], firm.send("clients=", [])
end
def test_transactions_when_replacing_on_persisted
- good = Client.new(:name => "Good")
- bad = Client.new(:name => "Bad", :raise_on_save => true)
+ good = Client.new(name: "Good")
+ bad = Client.new(name: "Bad", raise_on_save: true)
companies(:first_firm).clients_of_firm = [good]
@@ -1633,6 +1716,11 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert !company.clients.loaded?
end
+ def test_counter_cache_on_unloaded_association
+ car = Car.create(name: "My AppliCar")
+ assert_equal car.engines.size, 0
+ end
+
def test_get_ids_ignores_include_option
assert_equal [readers(:michael_welcome).id], posts(:welcome).readers_with_person_ids
end
@@ -1657,7 +1745,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
contract_a = Contract.create!
contract_b = Contract.create!
Contract.create! # another contract
- company = Company.new(:name => "Some Company")
+ company = Company.new(name: "Some Company")
company.contract_ids = [contract_a.id, contract_b.id]
assert_equal [contract_a.id, contract_b.id], company.contract_ids
@@ -1669,8 +1757,8 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_assign_ids_ignoring_blanks
- firm = Firm.create!(:name => 'Apple')
- firm.client_ids = [companies(:first_client).id, nil, companies(:second_client).id, '']
+ firm = Firm.create!(name: "Apple")
+ firm.client_ids = [companies(:first_client).id, nil, companies(:second_client).id, ""]
firm.save!
assert_equal 2, firm.clients.reload.size
@@ -1685,14 +1773,14 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
[
lambda { authors(:mary).comment_ids = [comments(:greetings).id, comments(:more_greetings).id] },
lambda { authors(:mary).comments = [comments(:greetings), comments(:more_greetings)] },
- lambda { authors(:mary).comments << Comment.create!(:body => "Yay", :post_id => 424242) },
+ lambda { authors(:mary).comments << Comment.create!(body: "Yay", post_id: 424242) },
lambda { authors(:mary).comments.delete(authors(:mary).comments.first) },
- ].each {|block| assert_raise(ActiveRecord::HasManyThroughCantAssociateThroughHasOneOrManyReflection, &block) }
+ ].each { |block| assert_raise(ActiveRecord::HasManyThroughCantAssociateThroughHasOneOrManyReflection, &block) }
end
def test_dynamic_find_should_respect_association_order_for_through
assert_equal Comment.find(10), authors(:david).comments_desc.where("comments.type = 'SpecialComment'").first
- assert_equal Comment.find(10), authors(:david).comments_desc.find_by_type('SpecialComment')
+ assert_equal Comment.find(10), authors(:david).comments_desc.find_by_type("SpecialComment")
end
def test_has_many_through_respects_hash_conditions
@@ -1726,7 +1814,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_include_returns_false_for_non_matching_record_to_verify_scoping
firm = companies(:first_firm)
- client = Client.create!(:name => 'Not Associated')
+ client = Client.create!(name: "Not Associated")
assert ! firm.clients.loaded?
assert_equal false, firm.clients.include?(client)
@@ -1755,7 +1843,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_calling_first_or_last_on_existing_record_with_build_should_load_association
firm = companies(:first_firm)
- firm.clients.build(:name => 'Foo')
+ firm.clients.build(name: "Foo")
assert !firm.clients.loaded?
assert_queries 1 do
@@ -1769,7 +1857,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_calling_first_nth_or_last_on_existing_record_with_create_should_not_load_association
firm = companies(:first_firm)
- firm.clients.create(:name => 'Foo')
+ firm.clients.create(name: "Foo")
assert !firm.clients.loaded?
assert_queries 3 do
@@ -1793,7 +1881,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_calling_first_or_last_with_integer_on_association_should_not_load_association
firm = companies(:first_firm)
- firm.clients.create(:name => 'Foo')
+ firm.clients.create(name: "Foo")
assert !firm.clients.loaded?
assert_queries 2 do
@@ -1923,13 +2011,13 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
old = ActiveRecord::Base.store_full_sti_class
ActiveRecord::Base.store_full_sti_class = true
- firm = Namespaced::Firm.create({ :name => 'Some Company' })
- firm.clients.create({ :name => 'Some Client' })
+ firm = Namespaced::Firm.create(name: "Some Company")
+ firm.clients.create(name: "Some Client")
stats = Namespaced::Firm.all.merge!(
- :select => "#{Namespaced::Firm.table_name}.id, COUNT(#{Namespaced::Client.table_name}.id) AS num_clients",
- :joins => :clients,
- :group => "#{Namespaced::Firm.table_name}.id"
+ select: "#{Namespaced::Firm.table_name}.id, COUNT(#{Namespaced::Client.table_name}.id) AS num_clients",
+ joins: :clients,
+ group: "#{Namespaced::Firm.table_name}.id"
).find firm.id
assert_equal 1, stats.num_clients.to_i
ensure
@@ -1955,8 +2043,8 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_creating_using_primary_key
- firm = Firm.all.merge!(:order => "id").first
- client = firm.clients_using_primary_key.create!(:name => 'test')
+ firm = Firm.all.merge!(order: "id").first
+ client = firm.clients_using_primary_key.create!(name: "test")
assert_equal firm.name, client.firm_name
end
@@ -1979,12 +2067,12 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_attributes_are_being_set_when_initialized_from_has_many_association_with_where_clause
- new_comment = posts(:welcome).comments.where(:body => "Some content").build
+ new_comment = posts(:welcome).comments.where(body: "Some content").build
assert_equal new_comment.body, "Some content"
end
def test_attributes_are_being_set_when_initialized_from_has_many_association_with_multiple_where_clauses
- new_comment = posts(:welcome).comments.where(:body => "Some content").where(:type => 'SpecialComment').build
+ new_comment = posts(:welcome).comments.where(body: "Some content").where(type: "SpecialComment").build
assert_equal new_comment.body, "Some content"
assert_equal new_comment.type, "SpecialComment"
assert_equal new_comment.post_id, posts(:welcome).id
@@ -1998,7 +2086,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_load_target_respects_protected_attributes
topic = Topic.create!
- reply = topic.replies.create(:title => "reply 1")
+ reply = topic.replies.create(title: "reply 1")
reply.approved = false
reply.save!
@@ -2025,7 +2113,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_merging_with_custom_attribute_writer
- bulb = Bulb.new(:color => "red")
+ bulb = Bulb.new(color: "red")
assert_equal "RED!", bulb.color
car = Car.create!
@@ -2035,13 +2123,13 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_abstract_class_with_polymorphic_has_many
- post = SubStiPost.create! :title => "fooo", :body => "baa"
- tagging = Tagging.create! :taggable => post
+ post = SubStiPost.create! title: "fooo", body: "baa"
+ tagging = Tagging.create! taggable: post
assert_equal [tagging], post.taggings
end
def test_with_polymorphic_has_many_with_custom_columns_name
- post = Post.create! :title => 'foo', :body => 'bar'
+ post = Post.create! title: "foo", body: "bar"
image = Image.create!
post.images << image
@@ -2051,10 +2139,10 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_build_with_polymorphic_has_many_does_not_allow_to_override_type_and_id
welcome = posts(:welcome)
- tagging = welcome.taggings.build(:taggable_id => 99, :taggable_type => 'ShouldNotChange')
+ tagging = welcome.taggings.build(taggable_id: 99, taggable_type: "ShouldNotChange")
assert_equal welcome.id, tagging.taggable_id
- assert_equal 'Post', tagging.taggable_type
+ assert_equal "Post", tagging.taggable_type
end
def test_dont_call_save_callbacks_twice_on_has_many
@@ -2066,30 +2154,30 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_association_attributes_are_available_to_after_initialize
- car = Car.create(:name => 'honda')
+ car = Car.create(name: "honda")
bulb = car.bulbs.build
- assert_equal car.id, bulb.attributes_after_initialize['car_id']
+ assert_equal car.id, bulb.attributes_after_initialize["car_id"]
end
def test_attributes_are_set_when_initialized_from_has_many_null_relationship
- car = Car.new name: 'honda'
- bulb = car.bulbs.where(name: 'headlight').first_or_initialize
- assert_equal 'headlight', bulb.name
+ car = Car.new name: "honda"
+ bulb = car.bulbs.where(name: "headlight").first_or_initialize
+ assert_equal "headlight", bulb.name
end
def test_attributes_are_set_when_initialized_from_polymorphic_has_many_null_relationship
- post = Post.new title: 'title', body: 'bar'
- tag = Tag.create!(name: 'foo')
+ post = Post.new title: "title", body: "bar"
+ tag = Tag.create!(name: "foo")
tagging = post.taggings.where(tag: tag).first_or_initialize
assert_equal tag.id, tagging.tag_id
- assert_equal 'Post', tagging.taggable_type
+ assert_equal "Post", tagging.taggable_type
end
def test_replace
- car = Car.create(:name => 'honda')
+ car = Car.create(name: "honda")
bulb1 = car.bulbs.create
bulb2 = Bulb.create
@@ -2100,7 +2188,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_replace_returns_target
- car = Car.create(:name => 'honda')
+ car = Car.create(name: "honda")
bulb1 = car.bulbs.create
bulb2 = car.bulbs.create
bulb3 = Bulb.create
@@ -2117,15 +2205,15 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
test "first_or_initialize adds the record to the association" do
- firm = Firm.create! name: 'omg'
+ firm = Firm.create! name: "omg"
client = firm.clients_of_firm.first_or_initialize
assert_equal [client], firm.clients_of_firm
end
test "first_or_create adds the record to the association" do
- firm = Firm.create! name: 'omg'
+ firm = Firm.create! name: "omg"
firm.clients_of_firm.load_target
- client = firm.clients_of_firm.first_or_create name: 'lol'
+ client = firm.clients_of_firm.first_or_create name: "lol"
assert_equal [client], firm.clients_of_firm
assert_equal [client], firm.reload.clients_of_firm
end
@@ -2147,7 +2235,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_no_queries(ignore_none: false) do
assert_equal [], post.comments
- assert_equal [], post.comments.where(body: 'omg')
+ assert_equal [], post.comments.where(body: "omg")
assert_equal [], post.comments.pluck(:body)
assert_equal 0, post.comments.sum(:id)
assert_equal 0, post.comments.count
@@ -2175,7 +2263,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
david = authors(:david)
post = david.posts.first
- post.type = 'PostWithSpecialCategorization'
+ post.type = "PostWithSpecialCategorization"
post.save
categorization = post.categorizations.first
@@ -2188,8 +2276,8 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
test "does not duplicate associations when used with natural primary keys" do
- speedometer = Speedometer.create!(id: '4')
- speedometer.minivans.create!(minivan_id: 'a-van-red' ,name: 'a van', color: 'red')
+ speedometer = Speedometer.create!(id: "4")
+ speedometer.minivans.create!(minivan_id: "a-van-red" , name: "a van", color: "red")
assert_equal 1, speedometer.minivans.to_a.size, "Only one association should be present:\n#{speedometer.minivans.to_a}"
assert_equal 1, speedometer.reload.minivans.to_a.size
@@ -2205,7 +2293,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
test "can unscope and where the default scope of the associated model" do
- Car.has_many :other_bulbs, -> { unscope(where: [:name]).where(name: 'other') }, class_name: "Bulb"
+ Car.has_many :other_bulbs, -> { unscope(where: [:name]).where(name: "other") }, class_name: "Bulb"
car = Car.create!
bulb1 = Bulb.create! name: "defaulty", car: car
bulb2 = Bulb.create! name: "other", car: car
@@ -2215,7 +2303,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
test "can rewhere the default scope of the associated model" do
- Car.has_many :old_bulbs, -> { rewhere(name: 'old') }, class_name: "Bulb"
+ Car.has_many :old_bulbs, -> { rewhere(name: "old") }, class_name: "Bulb"
car = Car.create!
bulb1 = Bulb.create! name: "defaulty", car: car
bulb2 = Bulb.create! name: "old", car: car
@@ -2224,7 +2312,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_equal [bulb2], car.old_bulbs
end
- test 'unscopes the default scope of associated model when used with include' do
+ test "unscopes the default scope of associated model when used with include" do
car = Car.create!
bulb = Bulb.create! name: "other", car: car
@@ -2244,7 +2332,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_equal "Failed to destroy the record", error.message
end
- test 'updates counter cache when default scope is given' do
+ test "updates counter cache when default scope is given" do
topic = DefaultRejectedTopic.create approved: true
assert_difference "topic.reload.replies_count", 1 do
@@ -2252,8 +2340,8 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
end
- test 'dangerous association name raises ArgumentError' do
- [:errors, 'errors', :save, 'save'].each do |name|
+ test "dangerous association name raises ArgumentError" do
+ [:errors, "errors", :save, "save"].each do |name|
assert_raises(ArgumentError, "Association #{name} should not be allowed") do
Class.new(ActiveRecord::Base) do
has_many name
@@ -2262,7 +2350,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
end
- test 'passes custom context validation to validate children' do
+ test "passes custom context validation to validate children" do
pirate = FamousPirate.new
pirate.famous_ships << ship = FamousShip.new
@@ -2271,7 +2359,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_equal "can't be blank", ship.errors[:name].first
end
- test 'association with instance dependent scope' do
+ test "association with instance dependent scope" do
bob = authors(:bob)
Post.create!(title: "signed post by bob", body: "stuff", author: authors(:bob))
Post.create!(title: "anonymous post", body: "more stuff", author: authors(:bob))
@@ -2281,7 +2369,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_equal [], authors(:david).posts_with_signature.map(&:title)
end
- test 'associations autosaves when object is already persisted' do
+ test "associations autosaves when object is already persisted" do
bulb = Bulb.create!
tyre = Tyre.create!
@@ -2294,7 +2382,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_equal 1, car.tyres.count
end
- test 'associations replace in memory when records have the same id' do
+ test "associations replace in memory when records have the same id" do
bulb = Bulb.create!
car = Car.create!(bulbs: [bulb])
@@ -2305,7 +2393,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_equal "foo", car.bulbs.first.name
end
- test 'in memory replacement executes no queries' do
+ test "in memory replacement executes no queries" do
bulb = Bulb.create!
car = Car.create!(bulbs: [bulb])
@@ -2316,7 +2404,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
end
- test 'in memory replacements do not execute callbacks' do
+ test "in memory replacements do not execute callbacks" do
raise_after_add = false
klass = Class.new(ActiveRecord::Base) do
self.table_name = :cars
@@ -2337,7 +2425,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
end
- test 'in memory replacements sets inverse instance' do
+ test "in memory replacements sets inverse instance" do
bulb = Bulb.create!
car = Car.create!(bulbs: [bulb])
@@ -2347,7 +2435,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_same car, new_bulb.car
end
- test 'in memory replacement maintains order' do
+ test "in memory replacement maintains order" do
first_bulb = Bulb.create!
second_bulb = Bulb.create!
car = Car.create!(bulbs: [first_bulb, second_bulb])
@@ -2358,16 +2446,13 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_equal [first_bulb, second_bulb], car.bulbs
end
- test 'double insertion of new object to association when same association used in the after create callback of a new object' do
- car = Car.create!
- car.bulbs << TrickyBulb.new
- assert_equal 1, car.bulbs.size
- end
-
- def test_association_force_reload_with_only_true_is_deprecated
- company = Company.find(1)
-
- assert_deprecated { company.clients_of_firm(true) }
+ test "double insertion of new object to association when same association used in the after create callback of a new object" do
+ reset_callbacks(:save, Bulb) do
+ Bulb.after_save { |record| record.car.bulbs.load }
+ car = Car.create!
+ car.bulbs << Bulb.new
+ assert_equal 1, car.bulbs.size
+ end
end
class AuthorWithErrorDestroyingAssociation < ActiveRecord::Base
@@ -2401,10 +2486,41 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_ids_reader_memoization
- car = Car.create!(name: 'Tofaş')
+ car = Car.create!(name: "Tofaş")
bulb = Bulb.create!(car: car)
assert_equal [bulb.id], car.bulb_ids
assert_no_queries { car.bulb_ids }
end
+
+ def test_loading_association_in_validate_callback_doesnt_affect_persistence
+ reset_callbacks(:validation, Bulb) do
+ Bulb.after_validation { |m| m.car.bulbs.load }
+
+ car = Car.create!(name: "Car")
+ bulb = car.bulbs.create!
+
+ assert_equal [bulb], car.bulbs
+ end
+ end
+
+ private
+
+ def force_signal37_to_load_all_clients_of_firm
+ companies(:first_firm).clients_of_firm.load_target
+ end
+
+ def reset_callbacks(kind, klass)
+ old_callbacks = {}
+ old_callbacks[klass] = klass.send("_#{kind}_callbacks").dup
+ klass.subclasses.each do |subclass|
+ old_callbacks[subclass] = subclass.send("_#{kind}_callbacks").dup
+ end
+ yield
+ ensure
+ klass.send("_#{kind}_callbacks=", old_callbacks[klass])
+ klass.subclasses.each do |subclass|
+ subclass.send("_#{kind}_callbacks=", old_callbacks[subclass])
+ end
+ end
end
diff --git a/activerecord/test/cases/associations/has_many_through_associations_test.rb b/activerecord/test/cases/associations/has_many_through_associations_test.rb
index aa35844a03..47c6480a8e 100644
--- a/activerecord/test/cases/associations/has_many_through_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb
@@ -1,33 +1,33 @@
require "cases/helper"
-require 'models/post'
-require 'models/person'
-require 'models/reference'
-require 'models/job'
-require 'models/reader'
-require 'models/comment'
-require 'models/rating'
-require 'models/tag'
-require 'models/tagging'
-require 'models/author'
-require 'models/owner'
-require 'models/pet'
-require 'models/pet_treasure'
-require 'models/toy'
-require 'models/treasure'
-require 'models/contract'
-require 'models/company'
-require 'models/developer'
-require 'models/computer'
-require 'models/subscriber'
-require 'models/book'
-require 'models/subscription'
-require 'models/essay'
-require 'models/category'
-require 'models/categorization'
-require 'models/member'
-require 'models/membership'
-require 'models/club'
-require 'models/organization'
+require "models/post"
+require "models/person"
+require "models/reference"
+require "models/job"
+require "models/reader"
+require "models/comment"
+require "models/rating"
+require "models/tag"
+require "models/tagging"
+require "models/author"
+require "models/owner"
+require "models/pet"
+require "models/pet_treasure"
+require "models/toy"
+require "models/treasure"
+require "models/contract"
+require "models/company"
+require "models/developer"
+require "models/computer"
+require "models/subscriber"
+require "models/book"
+require "models/subscription"
+require "models/essay"
+require "models/category"
+require "models/categorization"
+require "models/member"
+require "models/membership"
+require "models/club"
+require "models/organization"
class HasManyThroughAssociationsTest < ActiveRecord::TestCase
fixtures :posts, :readers, :people, :comments, :authors, :categories, :taggings, :tags,
@@ -37,8 +37,8 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
# Dummies to force column loads so query counts are clean.
def setup
- Person.create :first_name => 'gummy'
- Reader.create :person_id => 0, :post_id => 0
+ Person.create first_name: "gummy"
+ Reader.create person_id: 0, post_id: 0
end
def test_preload_sti_rhs_class
@@ -49,9 +49,9 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
end
def test_preload_sti_middle_relation
- club = Club.create!(name: 'Aaron cool banana club')
- member1 = Member.create!(name: 'Aaron')
- member2 = Member.create!(name: 'Cat')
+ club = Club.create!(name: "Aaron cool banana club")
+ member1 = Member.create!(name: "Aaron")
+ member2 = Member.create!(name: "Cat")
SuperMembership.create! club: club, member: member1
CurrentMembership.create! club: club, member: member2
@@ -67,15 +67,15 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
def test_ordered_has_many_through
person_prime = Class.new(ActiveRecord::Base) do
- def self.name; 'Person'; end
+ def self.name; "Person"; end
has_many :readers
- has_many :posts, -> { order('posts.id DESC') }, :through => :readers
+ has_many :posts, -> { order("posts.id DESC") }, through: :readers
end
posts = person_prime.includes(:posts).first.posts
assert_operator posts.length, :>, 1
- posts.each_cons(2) do |left,right|
+ posts.each_cons(2) do |left, right|
assert_operator left.id, :>, right.id
end
end
@@ -85,7 +85,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
subscription = make_model "Subscription"
subscriber = make_model "Subscriber"
- subscriber.primary_key = 'nick'
+ subscriber.primary_key = "nick"
subscription.belongs_to :book, anonymous_class: book
subscription.belongs_to :subscriber, anonymous_class: subscriber
@@ -106,8 +106,8 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
def test_no_pk_join_table_append
lesson, _, student = make_no_pk_hm_t
- sicp = lesson.new(:name => "SICP")
- ben = student.new(:name => "Ben Bitdiddle")
+ sicp = lesson.new(name: "SICP")
+ ben = student.new(name: "Ben Bitdiddle")
sicp.students << ben
assert sicp.save!
end
@@ -115,17 +115,17 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
def test_no_pk_join_table_delete
lesson, lesson_student, student = make_no_pk_hm_t
- sicp = lesson.new(:name => "SICP")
- ben = student.new(:name => "Ben Bitdiddle")
- louis = student.new(:name => "Louis Reasoner")
+ sicp = lesson.new(name: "SICP")
+ ben = student.new(name: "Ben Bitdiddle")
+ louis = student.new(name: "Louis Reasoner")
sicp.students << ben
sicp.students << louis
assert sicp.save!
sicp.students.reload
assert_operator lesson_student.count, :>=, 2
- assert_no_difference('student.count') do
- assert_difference('lesson_student.count', -2) do
+ assert_no_difference("student.count") do
+ assert_difference("lesson_student.count", -2) do
sicp.students.destroy(*student.all.to_a)
end
end
@@ -139,8 +139,8 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
after_destroy_called = true
end
- sicp = lesson.new(:name => "SICP")
- ben = student.new(:name => "Ben Bitdiddle")
+ sicp = lesson.new(name: "SICP")
+ ben = student.new(name: "Ben Bitdiddle")
sicp.students << ben
assert sicp.save!
@@ -150,16 +150,16 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
end
def make_no_pk_hm_t
- lesson = make_model 'Lesson'
- student = make_model 'Student'
+ lesson = make_model "Lesson"
+ student = make_model "Student"
- lesson_student = make_model 'LessonStudent'
- lesson_student.table_name = 'lessons_students'
+ lesson_student = make_model "LessonStudent"
+ lesson_student.table_name = "lessons_students"
- lesson_student.belongs_to :lesson, :anonymous_class => lesson
- lesson_student.belongs_to :student, :anonymous_class => student
- lesson.has_many :lesson_students, :anonymous_class => lesson_student
- lesson.has_many :students, :through => :lesson_students, :anonymous_class => student
+ lesson_student.belongs_to :lesson, anonymous_class: lesson
+ lesson_student.belongs_to :student, anonymous_class: student
+ lesson.has_many :lesson_students, anonymous_class: lesson_student
+ lesson.has_many :students, through: :lesson_students, anonymous_class: student
[lesson, lesson_student, student]
end
@@ -175,7 +175,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
person = Person.new
post = Post.new
person.posts << post
- assert person.posts.include?(post)
+ assert_includes person.posts, post
end
def test_associate_existing
@@ -187,18 +187,18 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
end
assert_queries(1) do
- assert post.people.include?(person)
+ assert_includes post.people, person
end
- assert post.reload.people.reload.include?(person)
+ assert_includes post.reload.people.reload, person
end
def test_delete_all_for_with_dependent_option_destroy
person = people(:david)
assert_equal 1, person.jobs_with_dependent_destroy.count
- assert_no_difference 'Job.count' do
- assert_difference 'Reference.count', -1 do
+ assert_no_difference "Job.count" do
+ assert_difference "Reference.count", -1 do
person.reload.jobs_with_dependent_destroy.delete_all
end
end
@@ -208,8 +208,8 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
person = people(:david)
assert_equal 1, person.jobs_with_dependent_nullify.count
- assert_no_difference 'Job.count' do
- assert_no_difference 'Reference.count' do
+ assert_no_difference "Job.count" do
+ assert_no_difference "Reference.count" do
person.reload.jobs_with_dependent_nullify.delete_all
end
end
@@ -219,8 +219,8 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
person = people(:david)
assert_equal 1, person.jobs_with_dependent_delete_all.count
- assert_no_difference 'Job.count' do
- assert_difference 'Reference.count', -1 do
+ assert_no_difference "Job.count" do
+ assert_difference "Reference.count", -1 do
person.reload.jobs_with_dependent_delete_all.delete_all
end
end
@@ -238,7 +238,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
post = posts(:thinking)
person = people(:david)
- assert_difference 'post.people.to_a.count', 2 do
+ assert_difference "post.people.to_a.count", 2 do
post.people << person
post.people << person
end
@@ -248,7 +248,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
post = posts(:thinking)
person = people(:david)
- assert_difference 'post.people.count', 2 do
+ assert_difference "post.people.count", 2 do
post.people << person
post.people << person
end
@@ -261,12 +261,12 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
post.people << person
post.people << person
- counts = ['post.people.count', 'post.people.to_a.count', 'post.readers.count', 'post.readers.to_a.count']
+ counts = ["post.people.count", "post.people.to_a.count", "post.readers.count", "post.readers.to_a.count"]
assert_difference counts, -2 do
post.people.delete(person)
end
- assert !post.people.reload.include?(person)
+ assert_not_includes post.people.reload, person
end
def test_associating_new
@@ -274,7 +274,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
new_person = nil # so block binding catches it
assert_queries(0) do
- new_person = Person.new :first_name => 'bob'
+ new_person = Person.new first_name: "bob"
end
# Associating new records always saves them
@@ -284,59 +284,59 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
end
assert_queries(1) do
- assert posts(:thinking).people.include?(new_person)
+ assert_includes posts(:thinking).people, new_person
end
- assert posts(:thinking).reload.people.reload.include?(new_person)
+ assert_includes posts(:thinking).reload.people.reload, new_person
end
def test_associate_new_by_building
assert_queries(1) { posts(:thinking) }
assert_queries(0) do
- posts(:thinking).people.build(:first_name => "Bob")
- posts(:thinking).people.new(:first_name => "Ted")
+ posts(:thinking).people.build(first_name: "Bob")
+ posts(:thinking).people.new(first_name: "Ted")
end
# Should only need to load the association once
assert_queries(1) do
- assert posts(:thinking).people.collect(&:first_name).include?("Bob")
- assert posts(:thinking).people.collect(&:first_name).include?("Ted")
+ assert_includes posts(:thinking).people.collect(&:first_name), "Bob"
+ assert_includes posts(:thinking).people.collect(&:first_name), "Ted"
end
# 2 queries for each new record (1 to save the record itself, 1 for the join model)
# * 2 new records = 4
# + 1 query to save the actual post = 5
assert_queries(5) do
- posts(:thinking).body += '-changed'
+ posts(:thinking).body += "-changed"
posts(:thinking).save
end
- assert posts(:thinking).reload.people.reload.collect(&:first_name).include?("Bob")
- assert posts(:thinking).reload.people.reload.collect(&:first_name).include?("Ted")
+ assert_includes posts(:thinking).reload.people.reload.collect(&:first_name), "Bob"
+ assert_includes posts(:thinking).reload.people.reload.collect(&:first_name), "Ted"
end
def test_build_then_save_with_has_many_inverse
post = posts(:thinking)
- person = post.people.build(:first_name => "Bob")
+ person = post.people.build(first_name: "Bob")
person.save
post.reload
- assert post.people.include?(person)
+ assert_includes post.people, person
end
def test_build_then_save_with_has_one_inverse
post = posts(:thinking)
- person = post.single_people.build(:first_name => "Bob")
+ person = post.single_people.build(first_name: "Bob")
person.save
post.reload
- assert post.single_people.include?(person)
+ assert_includes post.single_people, person
end
def test_both_parent_ids_set_when_saving_new
- post = Post.new(title: 'Hello', body: 'world')
- person = Person.new(first_name: 'Sean')
+ post = Post.new(title: "Hello", body: "world")
+ person = Person.new(first_name: "Sean")
post.people = [person]
post.save
@@ -348,7 +348,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
end
def test_delete_association
- assert_queries(2){posts(:welcome);people(:michael); }
+ assert_queries(2) { posts(:welcome);people(:michael); }
assert_queries(1) do
posts(:welcome).people.delete(people(:michael))
@@ -394,15 +394,15 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
person = people(:michael)
job = jobs(:magician)
- reference = Reference.where(:job_id => job.id, :person_id => person.id).first
+ reference = Reference.where(job_id: job.id, person_id: person.id).first
- assert_no_difference ['Job.count', 'Reference.count'] do
- assert_difference 'person.jobs.count', -1 do
+ assert_no_difference ["Job.count", "Reference.count"] do
+ assert_difference "person.jobs.count", -1 do
person.jobs_with_dependent_nullify.delete(job)
end
end
- assert_equal nil, reference.reload.job_id
+ assert_nil reference.reload.job_id
ensure
Reference.make_comments = false
end
@@ -416,14 +416,14 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
# Make sure we're not deleting everything
assert person.jobs.count >= 2
- assert_no_difference 'Job.count' do
- assert_difference ['person.jobs.count', 'Reference.count'], -1 do
+ assert_no_difference "Job.count" do
+ assert_difference ["person.jobs.count", "Reference.count"], -1 do
person.jobs_with_dependent_delete_all.delete(job)
end
end
# Check that the destroy callback on Reference did not run
- assert_equal nil, person.reload.comments
+ assert_nil person.reload.comments
ensure
Reference.make_comments = false
end
@@ -437,8 +437,8 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
# Make sure we're not deleting everything
assert person.jobs.count >= 2
- assert_no_difference 'Job.count' do
- assert_difference ['person.jobs.count', 'Reference.count'], -1 do
+ assert_no_difference "Job.count" do
+ assert_difference ["person.jobs.count", "Reference.count"], -1 do
person.jobs_with_dependent_destroy.delete(job)
end
end
@@ -455,8 +455,8 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
# Create a reference which is not linked to a job. This should not be destroyed.
person.references.create!
- assert_no_difference 'Job.count' do
- assert_difference 'Reference.count', -person.jobs.count do
+ assert_no_difference "Job.count" do
+ assert_difference "Reference.count", -person.jobs.count do
person.destroy
end
end
@@ -468,8 +468,8 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
# Create a reference which is not linked to a job. This should not be destroyed.
person.references.create!
- assert_no_difference 'Job.count' do
- assert_difference 'Reference.count', -person.jobs.count do
+ assert_no_difference "Job.count" do
+ assert_difference "Reference.count", -person.jobs.count do
person.destroy
end
end
@@ -480,41 +480,41 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
references = person.references.to_a
- assert_no_difference ['Reference.count', 'Job.count'] do
+ assert_no_difference ["Reference.count", "Job.count"] do
person.destroy
end
references.each do |reference|
- assert_equal nil, reference.reload.job_id
+ assert_nil reference.reload.job_id
end
end
def test_update_counter_caches_on_delete
post = posts(:welcome)
- tag = post.tags.create!(:name => 'doomed')
+ tag = post.tags.create!(name: "doomed")
- assert_difference ['post.reload.tags_count'], -1 do
+ assert_difference ["post.reload.tags_count"], -1 do
posts(:welcome).tags.delete(tag)
end
end
def test_update_counter_caches_on_delete_with_dependent_destroy
post = posts(:welcome)
- tag = post.tags.create!(:name => 'doomed')
+ tag = post.tags.create!(name: "doomed")
post.update_columns(tags_with_destroy_count: post.tags.count)
- assert_difference ['post.reload.tags_with_destroy_count'], -1 do
+ assert_difference ["post.reload.tags_with_destroy_count"], -1 do
posts(:welcome).tags_with_destroy.delete(tag)
end
end
def test_update_counter_caches_on_delete_with_dependent_nullify
post = posts(:welcome)
- tag = post.tags.create!(:name => 'doomed')
+ tag = post.tags.create!(name: "doomed")
post.update_columns(tags_with_nullify_count: post.tags.count)
- assert_no_difference 'post.reload.tags_count' do
- assert_difference 'post.reload.tags_with_nullify_count', -1 do
+ assert_no_difference "post.reload.tags_count" do
+ assert_difference "post.reload.tags_with_nullify_count", -1 do
posts(:welcome).tags_with_nullify.delete(tag)
end
end
@@ -522,7 +522,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
def test_update_counter_caches_on_replace_association
post = posts(:welcome)
- tag = post.tags.create!(:name => 'doomed')
+ tag = post.tags.create!(name: "doomed")
tag.tagged_posts << posts(:thinking)
tag.tagged_posts = []
@@ -533,15 +533,15 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
def test_update_counter_caches_on_destroy
post = posts(:welcome)
- tag = post.tags.create!(name: 'doomed')
+ tag = post.tags.create!(name: "doomed")
- assert_difference 'post.reload.tags_count', -1 do
+ assert_difference "post.reload.tags_count", -1 do
tag.tagged_posts.destroy(post)
end
end
def test_replace_association
- assert_queries(4){posts(:welcome);people(:david);people(:michael); posts(:welcome).people.reload}
+ assert_queries(4) { posts(:welcome);people(:david);people(:michael); posts(:welcome).people.reload }
# 1 query to delete the existing reader (michael)
# 1 query to associate the new reader (david)
@@ -549,35 +549,35 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
posts(:welcome).people = [people(:david)]
end
- assert_queries(0){
- assert posts(:welcome).people.include?(people(:david))
- assert !posts(:welcome).people.include?(people(:michael))
+ assert_queries(0) {
+ assert_includes posts(:welcome).people, people(:david)
+ assert_not_includes posts(:welcome).people, people(:michael)
}
- assert posts(:welcome).reload.people.reload.include?(people(:david))
- assert !posts(:welcome).reload.people.reload.include?(people(:michael))
+ assert_includes posts(:welcome).reload.people.reload, people(:david)
+ assert_not_includes posts(:welcome).reload.people.reload, people(:michael)
end
def test_replace_order_is_preserved
posts(:welcome).people.clear
posts(:welcome).people = [people(:david), people(:michael)]
- assert_equal [people(:david).id, people(:michael).id], posts(:welcome).readers.order('id').map(&:person_id)
+ assert_equal [people(:david).id, people(:michael).id], posts(:welcome).readers.order("id").map(&:person_id)
# Test the inverse order in case the first success was a coincidence
posts(:welcome).people.clear
posts(:welcome).people = [people(:michael), people(:david)]
- assert_equal [people(:michael).id, people(:david).id], posts(:welcome).readers.order('id').map(&:person_id)
+ assert_equal [people(:michael).id, people(:david).id], posts(:welcome).readers.order("id").map(&:person_id)
end
def test_replace_by_id_order_is_preserved
posts(:welcome).people.clear
posts(:welcome).person_ids = [people(:david).id, people(:michael).id]
- assert_equal [people(:david).id, people(:michael).id], posts(:welcome).readers.order('id').map(&:person_id)
+ assert_equal [people(:david).id, people(:michael).id], posts(:welcome).readers.order("id").map(&:person_id)
# Test the inverse order in case the first success was a coincidence
posts(:welcome).people.clear
posts(:welcome).person_ids = [people(:michael).id, people(:david).id]
- assert_equal [people(:michael).id, people(:david).id], posts(:welcome).readers.order('id').map(&:person_id)
+ assert_equal [people(:michael).id, people(:david).id], posts(:welcome).readers.order("id").map(&:person_id)
end
def test_associate_with_create
@@ -586,15 +586,15 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
# 1 query for the new record, 1 for the join table record
# No need to update the actual collection yet!
assert_queries(2) do
- posts(:thinking).people.create(:first_name=>"Jeb")
+ posts(:thinking).people.create(first_name: "Jeb")
end
# *Now* we actually need the collection so it's loaded
assert_queries(1) do
- assert posts(:thinking).people.collect(&:first_name).include?("Jeb")
+ assert_includes posts(:thinking).people.collect(&:first_name), "Jeb"
end
- assert posts(:thinking).reload.people.reload.collect(&:first_name).include?("Jeb")
+ assert_includes posts(:thinking).reload.people.reload.collect(&:first_name), "Jeb"
end
def test_through_record_is_built_when_created_with_where
@@ -605,66 +605,66 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
def test_associate_with_create_and_no_options
peeps = posts(:thinking).people.count
- posts(:thinking).people.create(:first_name => 'foo')
+ posts(:thinking).people.create(first_name: "foo")
assert_equal peeps + 1, posts(:thinking).people.count
end
def test_associate_with_create_with_through_having_conditions
impatient_people = posts(:thinking).impatient_people.count
- posts(:thinking).impatient_people.create!(:first_name => 'foo')
+ posts(:thinking).impatient_people.create!(first_name: "foo")
assert_equal impatient_people + 1, posts(:thinking).impatient_people.count
end
def test_associate_with_create_exclamation_and_no_options
peeps = posts(:thinking).people.count
- posts(:thinking).people.create!(:first_name => 'foo')
+ posts(:thinking).people.create!(first_name: "foo")
assert_equal peeps + 1, posts(:thinking).people.count
end
def test_create_on_new_record
p = Post.new
- error = assert_raises(ActiveRecord::RecordNotSaved) { p.people.create(:first_name => "mew") }
+ error = assert_raises(ActiveRecord::RecordNotSaved) { p.people.create(first_name: "mew") }
assert_equal "You cannot call create unless the parent is saved", error.message
- error = assert_raises(ActiveRecord::RecordNotSaved) { p.people.create!(:first_name => "snow") }
+ error = assert_raises(ActiveRecord::RecordNotSaved) { p.people.create!(first_name: "snow") }
assert_equal "You cannot call create unless the parent is saved", error.message
end
def test_associate_with_create_and_invalid_options
firm = companies(:first_firm)
- assert_no_difference('firm.developers.count') { assert_nothing_raised { firm.developers.create(:name => '0') } }
+ assert_no_difference("firm.developers.count") { assert_nothing_raised { firm.developers.create(name: "0") } }
end
def test_associate_with_create_and_valid_options
firm = companies(:first_firm)
- assert_difference('firm.developers.count', 1) { firm.developers.create(:name => 'developer') }
+ assert_difference("firm.developers.count", 1) { firm.developers.create(name: "developer") }
end
def test_associate_with_create_bang_and_invalid_options
firm = companies(:first_firm)
- assert_no_difference('firm.developers.count') { assert_raises(ActiveRecord::RecordInvalid) { firm.developers.create!(:name => '0') } }
+ assert_no_difference("firm.developers.count") { assert_raises(ActiveRecord::RecordInvalid) { firm.developers.create!(name: "0") } }
end
def test_associate_with_create_bang_and_valid_options
firm = companies(:first_firm)
- assert_difference('firm.developers.count', 1) { firm.developers.create!(:name => 'developer') }
+ assert_difference("firm.developers.count", 1) { firm.developers.create!(name: "developer") }
end
def test_push_with_invalid_record
firm = companies(:first_firm)
- assert_raises(ActiveRecord::RecordInvalid) { firm.developers << Developer.new(:name => '0') }
+ assert_raises(ActiveRecord::RecordInvalid) { firm.developers << Developer.new(name: "0") }
end
def test_push_with_invalid_join_record
repair_validations(Contract) do
- Contract.validate {|r| r.errors[:base] << 'Invalid Contract' }
+ Contract.validate { |r| r.errors[:base] << "Invalid Contract" }
firm = companies(:first_firm)
- lifo = Developer.new(:name => 'lifo')
+ lifo = Developer.new(name: "lifo")
assert_raises(ActiveRecord::RecordInvalid) { firm.developers << lifo }
- lifo = Developer.create!(:name => 'lifo')
+ lifo = Developer.create!(name: "lifo")
assert_raises(ActiveRecord::RecordInvalid) { firm.developers << lifo }
end
end
@@ -694,7 +694,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
[:added, :after, "Michael"]
], log.last(2)
- post.people_with_callbacks.push(people(:david), Person.create!(:first_name => "Bob"), Person.new(:first_name => "Lary"))
+ post.people_with_callbacks.push(people(:david), Person.create!(first_name: "Bob"), Person.new(first_name: "Lary"))
assert_equal [
[:added, :before, "David"],
[:added, :after, "David"],
@@ -702,21 +702,21 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
[:added, :after, "Bob"],
[:added, :before, "Lary"],
[:added, :after, "Lary"]
- ],log.last(6)
+ ], log.last(6)
- post.people_with_callbacks.build(:first_name => "Ted")
+ post.people_with_callbacks.build(first_name: "Ted")
assert_equal [
[:added, :before, "Ted"],
[:added, :after, "Ted"]
], log.last(2)
- post.people_with_callbacks.create(:first_name => "Sam")
+ post.people_with_callbacks.create(first_name: "Sam")
assert_equal [
[:added, :before, "Sam"],
[:added, :after, "Sam"]
], log.last(2)
- post.people_with_callbacks = [people(:michael),people(:david), Person.new(:first_name => "Julian"), Person.create!(:first_name => "Roger")]
+ post.people_with_callbacks = [people(:michael), people(:david), Person.new(first_name: "Julian"), Person.create!(first_name: "Roger")]
assert_equal((%w(Ted Bob Sam Lary) * 2).sort, log[-12..-5].collect(&:last).sort)
assert_equal [
[:added, :before, "Julian"],
@@ -729,7 +729,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
def test_dynamic_find_should_respect_association_include
# SQL error in sort clause if :include is not included
# due to Unknown column 'comments.id'
- assert Person.find(1).posts_with_comments_sorted_by_comment_id.find_by_title('Welcome to the weblog')
+ assert Person.find(1).posts_with_comments_sorted_by_comment_id.find_by_title("Welcome to the weblog")
end
def test_count_with_include_should_alias_join_table
@@ -745,7 +745,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
end
def test_get_ids_for_has_many_through_with_conditions_should_not_preload
- Tagging.create!(:taggable_type => 'Post', :taggable_id => posts(:welcome).id, :tag => tags(:misc))
+ Tagging.create!(taggable_type: "Post", taggable_id: posts(:welcome).id, tag: tags(:misc))
assert_not_called(ActiveRecord::Associations::Preloader, :new) do
posts(:welcome).misc_tag_ids
end
@@ -776,16 +776,16 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
end
def test_has_many_association_through_a_belongs_to_association_where_the_association_doesnt_exist
- post = Post.create!(:title => "TITLE", :body => "BODY")
+ post = Post.create!(title: "TITLE", body: "BODY")
assert_equal [], post.author_favorites
end
def test_has_many_association_through_a_belongs_to_association
author = authors(:mary)
- post = Post.create!(:author => author, :title => "TITLE", :body => "BODY")
- author.author_favorites.create(:favorite_author_id => 1)
- author.author_favorites.create(:favorite_author_id => 2)
- author.author_favorites.create(:favorite_author_id => 3)
+ post = Post.create!(author: author, title: "TITLE", body: "BODY")
+ author.author_favorites.create(favorite_author_id: 1)
+ author.author_favorites.create(favorite_author_id: 2)
+ author.author_favorites.create(favorite_author_id: 3)
assert_equal post.author.author_favorites, post.author_favorites
end
@@ -809,37 +809,37 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
def test_modifying_has_many_through_has_one_reflection_should_raise
[
- lambda { authors(:david).very_special_comments = [VerySpecialComment.create!(:body => "Gorp!", :post_id => 1011), VerySpecialComment.create!(:body => "Eep!", :post_id => 1012)] },
- lambda { authors(:david).very_special_comments << VerySpecialComment.create!(:body => "Hoohah!", :post_id => 1013) },
+ lambda { authors(:david).very_special_comments = [VerySpecialComment.create!(body: "Gorp!", post_id: 1011), VerySpecialComment.create!(body: "Eep!", post_id: 1012)] },
+ lambda { authors(:david).very_special_comments << VerySpecialComment.create!(body: "Hoohah!", post_id: 1013) },
lambda { authors(:david).very_special_comments.delete(authors(:david).very_special_comments.first) },
- ].each {|block| assert_raise(ActiveRecord::HasManyThroughCantAssociateThroughHasOneOrManyReflection, &block) }
+ ].each { |block| assert_raise(ActiveRecord::HasManyThroughCantAssociateThroughHasOneOrManyReflection, &block) }
end
def test_has_many_association_through_a_has_many_association_to_self
- sarah = Person.create!(:first_name => 'Sarah', :primary_contact_id => people(:susan).id, :gender => 'F', :number1_fan_id => 1)
- john = Person.create!(:first_name => 'John', :primary_contact_id => sarah.id, :gender => 'M', :number1_fan_id => 1)
+ sarah = Person.create!(first_name: "Sarah", primary_contact_id: people(:susan).id, gender: "F", number1_fan_id: 1)
+ john = Person.create!(first_name: "John", primary_contact_id: sarah.id, gender: "M", number1_fan_id: 1)
assert_equal sarah.agents, [john]
assert_equal people(:susan).agents.flat_map(&:agents), people(:susan).agents_of_agents
end
def test_associate_existing_with_nonstandard_primary_key_on_belongs_to
- Categorization.create(:author => authors(:mary), :named_category_name => categories(:general).name)
+ Categorization.create(author: authors(:mary), named_category_name: categories(:general).name)
assert_equal categories(:general), authors(:mary).named_categories.first
end
def test_collection_build_with_nonstandard_primary_key_on_belongs_to
author = authors(:mary)
- category = author.named_categories.build(:name => "Primary")
+ category = author.named_categories.build(name: "Primary")
author.save
- assert Categorization.exists?(:author_id => author.id, :named_category_name => category.name)
- assert author.named_categories.reload.include?(category)
+ assert Categorization.exists?(author_id: author.id, named_category_name: category.name)
+ assert_includes author.named_categories.reload, category
end
def test_collection_create_with_nonstandard_primary_key_on_belongs_to
author = authors(:mary)
- category = author.named_categories.create(:name => "Primary")
- assert Categorization.exists?(:author_id => author.id, :named_category_name => category.name)
- assert author.named_categories.reload.include?(category)
+ category = author.named_categories.create(name: "Primary")
+ assert Categorization.exists?(author_id: author.id, named_category_name: category.name)
+ assert_includes author.named_categories.reload, category
end
def test_collection_exists
@@ -851,9 +851,9 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
def test_collection_delete_with_nonstandard_primary_key_on_belongs_to
author = authors(:mary)
- category = author.named_categories.create(:name => "Primary")
+ category = author.named_categories.create(name: "Primary")
author.named_categories.delete(category)
- assert !Categorization.exists?(:author_id => author.id, :named_category_name => category.name)
+ assert !Categorization.exists?(author_id: author.id, named_category_name: category.name)
assert author.named_categories.reload.empty?
end
@@ -883,23 +883,45 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
end
+ def test_collection_singular_ids_setter_with_changed_primary_key
+ company = companies(:first_firm)
+ client = companies(:first_client)
+ company.clients_using_primary_key_ids = [client.name]
+ assert_equal [client], company.clients_using_primary_key
+ end
+
def test_collection_singular_ids_setter_raises_exception_when_invalid_ids_set
company = companies(:rails_core)
- ids = [Developer.first.id, -9999]
- assert_raises(ActiveRecord::AssociationTypeMismatch) {company.developer_ids= ids}
+ ids = [Developer.first.id, -9999]
+ e = assert_raises(ActiveRecord::RecordNotFound) { company.developer_ids = ids }
+ assert_match(/Couldn't find all Developers with 'id'/, e.message)
+ end
+
+ def test_collection_singular_ids_setter_raises_exception_when_invalid_ids_set_with_changed_primary_key
+ company = companies(:first_firm)
+ ids = [Client.first.name, "unknown client"]
+ e = assert_raises(ActiveRecord::RecordNotFound) { company.clients_using_primary_key_ids = ids }
+ assert_match(/Couldn't find all Clients with 'name'/, e.message)
+ end
+
+ def test_collection_singular_ids_through_setter_raises_exception_when_invalid_ids_set
+ author = authors(:david)
+ ids = [categories(:general).name, "Unknown"]
+ e = assert_raises(ActiveRecord::RecordNotFound) { author.essay_category_ids = ids }
+ assert_equal "Couldn't find all Categories with 'name': (General, Unknown) (found 1 results, but was looking for 2)", e.message
end
def test_build_a_model_from_hm_through_association_with_where_clause
- assert_nothing_raised { books(:awdr).subscribers.where(:nick => "marklazz").build }
+ assert_nothing_raised { books(:awdr).subscribers.where(nick: "marklazz").build }
end
def test_attributes_are_being_set_when_initialized_from_hm_through_association_with_where_clause
- new_subscriber = books(:awdr).subscribers.where(:nick => "marklazz").build
+ new_subscriber = books(:awdr).subscribers.where(nick: "marklazz").build
assert_equal new_subscriber.nick, "marklazz"
end
def test_attributes_are_being_set_when_initialized_from_hm_through_association_with_multiple_where_clauses
- new_subscriber = books(:awdr).subscribers.where(:nick => "marklazz").where(:name => 'Marcelo Giorgi').build
+ new_subscriber = books(:awdr).subscribers.where(nick: "marklazz").where(name: "Marcelo Giorgi").build
assert_equal new_subscriber.nick, "marklazz"
assert_equal new_subscriber.name, "Marcelo Giorgi"
end
@@ -908,14 +930,14 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
person = Person.new
reference = person.references.build
job = reference.build_job
- assert person.jobs.include?(job)
+ assert_includes person.jobs, job
end
def test_include_method_in_association_through_should_return_true_for_instance_added_with_nested_builds
author = Author.new
post = author.posts.build
comment = post.comments.build
- assert author.comments.include?(comment)
+ assert_includes author.comments, comment
end
def test_through_association_readonly_should_be_false
@@ -932,7 +954,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
def test_has_many_through_polymorphic_with_primary_key_option
assert_equal [categories(:general)], authors(:david).essay_categories
- authors = Author.joins(:essay_categories).where('categories.id' => categories(:general).id)
+ authors = Author.joins(:essay_categories).where("categories.id" => categories(:general).id)
assert_equal authors(:david), authors.first
assert_equal [owners(:blackbeard)], authors(:david).essay_owners
@@ -944,7 +966,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
def test_has_many_through_with_primary_key_option
assert_equal [categories(:general)], authors(:david).essay_categories_2
- authors = Author.joins(:essay_categories_2).where('categories.id' => categories(:general).id)
+ authors = Author.joins(:essay_categories_2).where("categories.id" => categories(:general).id)
assert_equal authors(:david), authors.first
end
@@ -956,30 +978,30 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
end
def test_has_many_through_with_default_scope_on_join_model
- assert_equal posts(:welcome).comments.order('id').to_a, authors(:david).comments_on_first_posts
+ assert_equal posts(:welcome).comments.order("id").to_a, authors(:david).comments_on_first_posts
end
def test_create_has_many_through_with_default_scope_on_join_model
- category = authors(:david).special_categories.create(:name => "Foo")
- assert_equal 1, category.categorizations.where(:special => true).count
+ category = authors(:david).special_categories.create(name: "Foo")
+ assert_equal 1, category.categorizations.where(special: true).count
end
def test_joining_has_many_through_with_distinct
- mary = Author.joins(:unique_categorized_posts).where(:id => authors(:mary).id).first
+ mary = Author.joins(:unique_categorized_posts).where(id: authors(:mary).id).first
assert_equal 1, mary.unique_categorized_posts.length
assert_equal 1, mary.unique_categorized_post_ids.length
end
def test_joining_has_many_through_belongs_to
- posts = Post.joins(:author_categorizations).order('posts.id').
- where('categorizations.id' => categorizations(:mary_thinking_sti).id)
+ posts = Post.joins(:author_categorizations).order("posts.id").
+ where("categorizations.id" => categorizations(:mary_thinking_sti).id)
assert_equal [posts(:eager_other), posts(:misc_by_mary), posts(:other_by_mary)], posts
end
def test_select_chosen_fields_only
author = authors(:david)
- assert_equal ['body', 'id'].sort, author.comments.select('comments.body').first.attributes.keys.sort
+ assert_equal ["body", "id"].sort, author.comments.select("comments.body").first.attributes.keys.sort
end
def test_get_has_many_through_belongs_to_ids_with_conditions
@@ -1022,7 +1044,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
post = posts(:welcome)
address = author_addresses(:david_address)
- assert post.author_addresses.include?(address)
+ assert_includes post.author_addresses, address
post.author_addresses.delete(address)
assert post[:author_count].nil?
end
@@ -1030,7 +1052,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
def test_primary_key_option_on_source
post = posts(:welcome)
category = categories(:general)
- Categorization.create!(:post_id => post.id, :named_category_name => category.name)
+ Categorization.create!(post_id: post.id, named_category_name: category.name)
assert_equal [category], post.named_categories
assert_equal [category.name], post.named_category_ids # checks when target loaded
@@ -1039,29 +1061,29 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
def test_create_should_not_raise_exception_when_join_record_has_errors
repair_validations(Categorization) do
- Categorization.validate { |r| r.errors[:base] << 'Invalid Categorization' }
- Category.create(:name => 'Fishing', :authors => [Author.first])
+ Categorization.validate { |r| r.errors[:base] << "Invalid Categorization" }
+ Category.create(name: "Fishing", authors: [Author.first])
end
end
def test_assign_array_to_new_record_builds_join_records
- c = Category.new(:name => 'Fishing', :authors => [Author.first])
+ c = Category.new(name: "Fishing", authors: [Author.first])
assert_equal 1, c.categorizations.size
end
def test_create_bang_should_raise_exception_when_join_record_has_errors
repair_validations(Categorization) do
- Categorization.validate { |r| r.errors[:base] << 'Invalid Categorization' }
+ Categorization.validate { |r| r.errors[:base] << "Invalid Categorization" }
assert_raises(ActiveRecord::RecordInvalid) do
- Category.create!(:name => 'Fishing', :authors => [Author.first])
+ Category.create!(name: "Fishing", authors: [Author.first])
end
end
end
def test_save_bang_should_raise_exception_when_join_record_has_errors
repair_validations(Categorization) do
- Categorization.validate { |r| r.errors[:base] << 'Invalid Categorization' }
- c = Category.new(:name => 'Fishing', :authors => [Author.first])
+ Categorization.validate { |r| r.errors[:base] << "Invalid Categorization" }
+ c = Category.new(name: "Fishing", authors: [Author.first])
assert_raises(ActiveRecord::RecordInvalid) do
c.save!
end
@@ -1070,17 +1092,17 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
def test_save_returns_falsy_when_join_record_has_errors
repair_validations(Categorization) do
- Categorization.validate { |r| r.errors[:base] << 'Invalid Categorization' }
- c = Category.new(:name => 'Fishing', :authors => [Author.first])
+ Categorization.validate { |r| r.errors[:base] << "Invalid Categorization" }
+ c = Category.new(name: "Fishing", authors: [Author.first])
assert_not c.save
end
end
def test_preloading_empty_through_association_via_joins
- person = Person.create!(:first_name => "Gaga")
- person = Person.where(:id => person.id).where('readers.id = 1 or 1=1').references(:readers).includes(:posts).to_a.first
+ person = Person.create!(first_name: "Gaga")
+ person = Person.where(id: person.id).where("readers.id = 1 or 1=1").references(:readers).includes(:posts).to_a.first
- assert person.posts.loaded?, 'person.posts should be loaded'
+ assert person.posts.loaded?, "person.posts should be loaded"
assert_equal [], person.posts
end
@@ -1101,13 +1123,13 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
end
def test_has_many_through_with_polymorphic_source
- post = tags(:general).tagged_posts.create! :title => "foo", :body => "bar"
+ post = tags(:general).tagged_posts.create! title: "foo", body: "bar"
assert_equal [tags(:general)], post.reload.tags
end
def test_has_many_through_obeys_order_on_through_association
owner = owners(:blackbeard)
- assert owner.toys.to_sql.include?("pets.name desc")
+ assert_includes owner.toys.to_sql, "pets.name desc"
assert_equal ["parrot", "bulbul"], owner.toys.map { |r| r.pet.name }
end
@@ -1116,7 +1138,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
assert_no_queries(ignore_none: false) do
assert_equal [], person.posts
- assert_equal [], person.posts.where(body: 'omg')
+ assert_equal [], person.posts.where(body: "omg")
assert_equal [], person.posts.pluck(:body)
assert_equal 0, person.posts.sum(:tags_count)
assert_equal 0, person.posts.count
@@ -1148,9 +1170,9 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
end
def test_has_many_through_unscope_default_scope
- post = Post.create!(:title => 'Beaches', :body => "I like beaches!")
- Reader.create! :person => people(:david), :post => post
- LazyReader.create! :person => people(:susan), :post => post
+ post = Post.create!(title: "Beaches", body: "I like beaches!")
+ Reader.create! person: people(:david), post: post
+ LazyReader.create! person: people(:susan), post: post
assert_equal 2, post.people.to_a.size
assert_equal 1, post.lazy_people.to_a.size
@@ -1160,8 +1182,8 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
end
def test_has_many_through_add_with_sti_middle_relation
- club = SuperClub.create!(name: 'Fight Club')
- member = Member.create!(name: 'Tyler Durden')
+ club = SuperClub.create!(name: "Fight Club")
+ member = Member.create!(name: "Tyler Durden")
club.members << member
assert_equal 1, SuperMembership.where(member_id: member.id, club_id: club.id).count
@@ -1182,12 +1204,6 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
assert_nil Club.new.special_favourites.distinct_value
end
- def test_association_force_reload_with_only_true_is_deprecated
- post = Post.find(1)
-
- assert_deprecated { post.people(true) }
- end
-
def test_has_many_through_do_not_cache_association_reader_if_the_though_method_has_default_scopes
member = Member.create!
club = Club.create!
diff --git a/activerecord/test/cases/associations/has_one_associations_test.rb b/activerecord/test/cases/associations/has_one_associations_test.rb
index 1574f373c2..ed22a9802f 100644
--- a/activerecord/test/cases/associations/has_one_associations_test.rb
+++ b/activerecord/test/cases/associations/has_one_associations_test.rb
@@ -1,15 +1,15 @@
require "cases/helper"
-require 'models/developer'
-require 'models/computer'
-require 'models/project'
-require 'models/company'
-require 'models/ship'
-require 'models/pirate'
-require 'models/car'
-require 'models/bulb'
-require 'models/author'
-require 'models/image'
-require 'models/post'
+require "models/developer"
+require "models/computer"
+require "models/project"
+require "models/company"
+require "models/ship"
+require "models/pirate"
+require "models/car"
+require "models/bulb"
+require "models/author"
+require "models/image"
+require "models/post"
class HasOneAssociationsTest < ActiveRecord::TestCase
self.use_transactional_tests = false unless supports_savepoints?
@@ -28,7 +28,7 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
ActiveRecord::SQLCounter.clear_log
companies(:first_firm).account
ensure
- assert ActiveRecord::SQLCounter.log_all.all? { |sql| /order by/i !~ sql }, 'ORDER BY was used in the query'
+ assert ActiveRecord::SQLCounter.log_all.all? { |sql| /order by/i !~ sql }, "ORDER BY was used in the query"
end
def test_has_one_cache_nils
@@ -36,13 +36,13 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
assert_queries(1) { assert_nil firm.account }
assert_queries(0) { assert_nil firm.account }
- firms = Firm.all.merge!(:includes => :account).to_a
+ firms = Firm.all.merge!(includes: :account).to_a
assert_queries(0) { firms.each(&:account) }
end
def test_with_select
assert_equal Firm.find(1).account_with_select.attributes.size, 2
- assert_equal Firm.all.merge!(:includes => :account_with_select).find(1).account_with_select.attributes.size, 2
+ assert_equal Firm.all.merge!(includes: :account_with_select).find(1).account_with_select.attributes.size, 2
end
def test_finding_using_primary_key
@@ -102,7 +102,7 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
def test_nullification_on_association_change
firm = companies(:rails_core)
old_account_id = firm.account.id
- firm.account = Account.new(:credit_limit => 5)
+ firm.account = Account.new(credit_limit: 5)
# account is dependent with nullify, therefore its firm_id should be nil
assert_nil Account.find(old_account_id).firm_id
end
@@ -125,12 +125,12 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
end
def test_association_change_calls_delete
- companies(:first_firm).deletable_account = Account.new(:credit_limit => 5)
+ companies(:first_firm).deletable_account = Account.new(credit_limit: 5)
assert_equal [], Account.destroyed_account_ids[companies(:first_firm).id]
end
def test_association_change_calls_destroy
- companies(:first_firm).account = Account.new(:credit_limit => 5)
+ companies(:first_firm).account = Account.new(credit_limit: 5)
assert_equal [companies(:first_firm).id], Account.destroyed_account_ids[companies(:first_firm).id]
end
@@ -170,44 +170,25 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
end
def test_dependence_with_nil_associate
- firm = DependentFirm.new(:name => 'nullify')
+ firm = DependentFirm.new(name: "nullify")
firm.save!
assert_nothing_raised { firm.destroy }
end
def test_restrict_with_exception
- firm = RestrictedWithExceptionFirm.create!(:name => 'restrict')
- firm.create_account(:credit_limit => 10)
-
- assert_not_nil firm.account
-
- assert_raise(ActiveRecord::DeleteRestrictionError) { firm.destroy }
- assert RestrictedWithExceptionFirm.exists?(:name => 'restrict')
- assert firm.account.present?
- end
-
- def test_restrict_with_error_is_deprecated_using_key_one
- I18n.backend = I18n::Backend::Simple.new
- I18n.backend.store_translations :en, activerecord: { errors: { messages: { restrict_dependent_destroy: { one: 'message for deprecated key' } } } }
-
- firm = RestrictedWithErrorFirm.create!(name: 'restrict')
+ firm = RestrictedWithExceptionFirm.create!(name: "restrict")
firm.create_account(credit_limit: 10)
assert_not_nil firm.account
- assert_deprecated { firm.destroy }
-
- assert !firm.errors.empty?
- assert_equal 'message for deprecated key', firm.errors[:base].first
- assert RestrictedWithErrorFirm.exists?(name: 'restrict')
+ assert_raise(ActiveRecord::DeleteRestrictionError) { firm.destroy }
+ assert RestrictedWithExceptionFirm.exists?(name: "restrict")
assert firm.account.present?
- ensure
- I18n.backend.reload!
end
def test_restrict_with_error
- firm = RestrictedWithErrorFirm.create!(:name => 'restrict')
- firm.create_account(:credit_limit => 10)
+ firm = RestrictedWithErrorFirm.create!(name: "restrict")
+ firm.create_account(credit_limit: 10)
assert_not_nil firm.account
@@ -215,14 +196,14 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
assert !firm.errors.empty?
assert_equal "Cannot delete record because a dependent account exists", firm.errors[:base].first
- assert RestrictedWithErrorFirm.exists?(:name => 'restrict')
+ assert RestrictedWithErrorFirm.exists?(name: "restrict")
assert firm.account.present?
end
def test_restrict_with_error_with_locale
I18n.backend = I18n::Backend::Simple.new
- I18n.backend.store_translations 'en', activerecord: {attributes: {restricted_with_error_firm: {account: 'firm account'}}}
- firm = RestrictedWithErrorFirm.create!(name: 'restrict')
+ I18n.backend.store_translations "en", activerecord: { attributes: { restricted_with_error_firm: { account: "firm account" } } }
+ firm = RestrictedWithErrorFirm.create!(name: "restrict")
firm.create_account(credit_limit: 10)
assert_not_nil firm.account
@@ -231,7 +212,7 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
assert !firm.errors.empty?
assert_equal "Cannot delete record because a dependent firm account exists", firm.errors[:base].first
- assert RestrictedWithErrorFirm.exists?(name: 'restrict')
+ assert RestrictedWithErrorFirm.exists?(name: "restrict")
assert firm.account.present?
ensure
I18n.backend.reload!
@@ -260,24 +241,24 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
def test_building_the_associated_object_with_explicit_sti_base_class
firm = DependentFirm.new
- company = firm.build_company(:type => "Company")
+ company = firm.build_company(type: "Company")
assert_kind_of Company, company, "Expected #{company.class} to be a Company"
end
def test_building_the_associated_object_with_sti_subclass
firm = DependentFirm.new
- company = firm.build_company(:type => "Client")
+ company = firm.build_company(type: "Client")
assert_kind_of Client, company, "Expected #{company.class} to be a Client"
end
def test_building_the_associated_object_with_an_invalid_type
firm = DependentFirm.new
- assert_raise(ActiveRecord::SubclassNotFound) { firm.build_company(:type => "Invalid") }
+ assert_raise(ActiveRecord::SubclassNotFound) { firm.build_company(type: "Invalid") }
end
def test_building_the_associated_object_with_an_unrelated_type
firm = DependentFirm.new
- assert_raise(ActiveRecord::SubclassNotFound) { firm.build_company(:type => "Account") }
+ assert_raise(ActiveRecord::SubclassNotFound) { firm.build_company(type: "Account") }
end
def test_build_and_create_should_not_happen_within_scope
@@ -295,19 +276,19 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
end
def test_create_association
- firm = Firm.create(:name => "GlobalMegaCorp")
- account = firm.create_account(:credit_limit => 1000)
+ firm = Firm.create(name: "GlobalMegaCorp")
+ account = firm.create_account(credit_limit: 1000)
assert_equal account, firm.reload.account
end
def test_create_association_with_bang
- firm = Firm.create(:name => "GlobalMegaCorp")
- account = firm.create_account!(:credit_limit => 1000)
+ firm = Firm.create(name: "GlobalMegaCorp")
+ account = firm.create_account!(credit_limit: 1000)
assert_equal account, firm.reload.account
end
def test_create_association_with_bang_failing
- firm = Firm.create(:name => "GlobalMegaCorp")
+ firm = Firm.create(name: "GlobalMegaCorp")
assert_raise ActiveRecord::RecordInvalid do
firm.create_account!
end
@@ -319,13 +300,23 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
end
def test_create_with_inexistent_foreign_key_failing
- firm = Firm.create(name: 'GlobalMegaCorp')
+ firm = Firm.create(name: "GlobalMegaCorp")
assert_raises(ActiveRecord::UnknownAttributeError) do
firm.create_account_with_inexistent_foreign_key
end
end
+ def test_reload_association
+ odegy = companies(:odegy)
+
+ assert_equal 53, odegy.account.credit_limit
+ Account.where(id: odegy.account.id).update_all(credit_limit: 80)
+ assert_equal 53, odegy.account.credit_limit
+
+ assert_equal 80, odegy.reload_account.credit_limit
+ end
+
def test_build
firm = Firm.new("name" => "GlobalMegaCorp")
firm.save
@@ -365,7 +356,7 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
def test_finding_with_interpolated_condition
firm = Firm.first
- superior = firm.clients.create(:name => 'SuperiorCo')
+ superior = firm.clients.create(name: "SuperiorCo")
superior.rating = 10
superior.save
assert_equal 10, firm.clients_with_interpolated_conditions.first.rating
@@ -382,7 +373,7 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
end
def test_save_still_works_after_accessing_nil_has_one
- jp = Company.new :name => 'Jaded Pixel'
+ jp = Company.new name: "Jaded Pixel"
jp.dummy_account.nil?
assert_nothing_raised do
@@ -411,14 +402,14 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
assert_nothing_raised do
Firm.find(@firm.id).save!
- Firm.all.merge!(:includes => :account).find(@firm.id).save!
+ Firm.all.merge!(includes: :account).find(@firm.id).save!
end
@firm.account.destroy
assert_nothing_raised do
Firm.find(@firm.id).save!
- Firm.all.merge!(:includes => :account).find(@firm.id).save!
+ Firm.all.merge!(includes: :account).find(@firm.id).save!
end
end
@@ -435,7 +426,7 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
end
def test_attributes_are_being_set_when_initialized_from_has_one_association_with_where_clause
- new_account = companies(:first_firm).build_account(:firm_name => 'Account')
+ new_account = companies(:first_firm).build_account(firm_name: "Account")
assert_equal new_account.firm_name, "Account"
end
@@ -505,63 +496,63 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
end
def test_association_keys_bypass_attribute_protection
- car = Car.create(:name => 'honda')
+ car = Car.create(name: "honda")
bulb = car.build_bulb
assert_equal car.id, bulb.car_id
- bulb = car.build_bulb :car_id => car.id + 1
+ bulb = car.build_bulb car_id: car.id + 1
assert_equal car.id, bulb.car_id
bulb = car.create_bulb
assert_equal car.id, bulb.car_id
- bulb = car.create_bulb :car_id => car.id + 1
+ bulb = car.create_bulb car_id: car.id + 1
assert_equal car.id, bulb.car_id
end
def test_association_protect_foreign_key
- pirate = Pirate.create!(:catchphrase => "Don' botharrr talkin' like one, savvy?")
+ pirate = Pirate.create!(catchphrase: "Don' botharrr talkin' like one, savvy?")
ship = pirate.build_ship
assert_equal pirate.id, ship.pirate_id
- ship = pirate.build_ship :pirate_id => pirate.id + 1
+ ship = pirate.build_ship pirate_id: pirate.id + 1
assert_equal pirate.id, ship.pirate_id
ship = pirate.create_ship
assert_equal pirate.id, ship.pirate_id
- ship = pirate.create_ship :pirate_id => pirate.id + 1
+ ship = pirate.create_ship pirate_id: pirate.id + 1
assert_equal pirate.id, ship.pirate_id
end
def test_build_with_block
- car = Car.create(:name => 'honda')
+ car = Car.create(name: "honda")
- bulb = car.build_bulb{ |b| b.color = 'Red' }
- assert_equal 'RED!', bulb.color
+ bulb = car.build_bulb { |b| b.color = "Red" }
+ assert_equal "RED!", bulb.color
end
def test_create_with_block
- car = Car.create(:name => 'honda')
+ car = Car.create(name: "honda")
- bulb = car.create_bulb{ |b| b.color = 'Red' }
- assert_equal 'RED!', bulb.color
+ bulb = car.create_bulb { |b| b.color = "Red" }
+ assert_equal "RED!", bulb.color
end
def test_create_bang_with_block
- car = Car.create(:name => 'honda')
+ car = Car.create(name: "honda")
- bulb = car.create_bulb!{ |b| b.color = 'Red' }
- assert_equal 'RED!', bulb.color
+ bulb = car.create_bulb! { |b| b.color = "Red" }
+ assert_equal "RED!", bulb.color
end
def test_association_attributes_are_available_to_after_initialize
- car = Car.create(:name => 'honda')
+ car = Car.create(name: "honda")
bulb = car.create_bulb
- assert_equal car.id, bulb.attributes_after_initialize['car_id']
+ assert_equal car.id, bulb.attributes_after_initialize["car_id"]
end
def test_has_one_transaction
@@ -581,36 +572,36 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
def test_has_one_assignment_dont_trigger_save_on_change_of_same_object
pirate = Pirate.create!(catchphrase: "Don' botharrr talkin' like one, savvy?")
- ship = pirate.build_ship(name: 'old name')
+ ship = pirate.build_ship(name: "old name")
ship.save!
- ship.name = 'new name'
+ ship.name = "new name"
assert ship.changed?
assert_queries(1) do
# One query for updating name, not triggering query for updating pirate_id
pirate.ship = ship
end
- assert_equal 'new name', pirate.ship.reload.name
+ assert_equal "new name", pirate.ship.reload.name
end
def test_has_one_assignment_triggers_save_on_change_on_replacing_object
pirate = Pirate.create!(catchphrase: "Don' botharrr talkin' like one, savvy?")
- ship = pirate.build_ship(name: 'old name')
+ ship = pirate.build_ship(name: "old name")
ship.save!
- new_ship = Ship.create(name: 'new name')
+ new_ship = Ship.create(name: "new name")
assert_queries(2) do
- # One query for updating name and second query for updating pirate_id
+ # One query to nullify the old ship, one query to update the new ship
pirate.ship = new_ship
end
- assert_equal 'new name', pirate.ship.reload.name
+ assert_equal "new name", pirate.ship.reload.name
end
def test_has_one_autosave_with_primary_key_manually_set
- post = Post.create(id: 1234, title: "Some title", body: 'Some content')
- author = Author.new(id: 33, name: 'Hank Moody')
+ post = Post.create(id: 1234, title: "Some title", body: "Some content")
+ author = Author.new(id: 33, name: "Hank Moody")
author.post = post
author.save
@@ -621,7 +612,7 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
end
def test_has_one_loading_for_new_record
- post = Post.create!(author_id: 42, title: 'foo', body: 'bar')
+ post = Post.create!(author_id: 42, title: "foo", body: "bar")
author = Author.new(id: 42)
assert_equal post, author.post
end
@@ -635,7 +626,7 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
end
def test_with_polymorphic_has_one_with_custom_columns_name
- post = Post.create! :title => 'foo', :body => 'bar'
+ post = Post.create! title: "foo", body: "bar"
image = Image.create!
post.main_image = image
@@ -644,8 +635,8 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
assert_equal image, post.main_image
end
- test 'dangerous association name raises ArgumentError' do
- [:errors, 'errors', :save, 'save'].each do |name|
+ test "dangerous association name raises ArgumentError" do
+ [:errors, "errors", :save, "save"].each do |name|
assert_raises(ArgumentError, "Association #{name} should not be allowed") do
Class.new(ActiveRecord::Base) do
has_one name
@@ -654,27 +645,21 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
end
end
- def test_association_force_reload_with_only_true_is_deprecated
- firm = Firm.find(1)
-
- assert_deprecated { firm.account(true) }
- end
-
class SpecialBook < ActiveRecord::Base
- self.table_name = 'books'
- belongs_to :author, class_name: 'SpecialAuthor'
+ self.table_name = "books"
+ belongs_to :author, class_name: "SpecialAuthor"
end
class SpecialAuthor < ActiveRecord::Base
- self.table_name = 'authors'
- has_one :book, class_name: 'SpecialBook', foreign_key: 'author_id'
+ self.table_name = "authors"
+ has_one :book, class_name: "SpecialBook", foreign_key: "author_id"
end
def test_assocation_enum_works_properly
- author = SpecialAuthor.create!(name: 'Test')
- book = SpecialBook.create!(status: 'published')
+ author = SpecialAuthor.create!(name: "Test")
+ book = SpecialBook.create!(status: "published")
author.book = book
- refute_equal 0, SpecialAuthor.joins(:book).where(books: { status: 'published' } ).count
+ refute_equal 0, SpecialAuthor.joins(:book).where(books: { status: "published" }).count
end
end
diff --git a/activerecord/test/cases/associations/has_one_through_associations_test.rb b/activerecord/test/cases/associations/has_one_through_associations_test.rb
index b2b46812b9..432c3526a5 100644
--- a/activerecord/test/cases/associations/has_one_through_associations_test.rb
+++ b/activerecord/test/cases/associations/has_one_through_associations_test.rb
@@ -1,25 +1,25 @@
require "cases/helper"
-require 'models/club'
-require 'models/member_type'
-require 'models/member'
-require 'models/membership'
-require 'models/sponsor'
-require 'models/organization'
-require 'models/member_detail'
-require 'models/minivan'
-require 'models/dashboard'
-require 'models/speedometer'
-require 'models/category'
-require 'models/author'
-require 'models/essay'
-require 'models/owner'
-require 'models/post'
-require 'models/comment'
-require 'models/categorization'
-require 'models/customer'
-require 'models/carrier'
-require 'models/shop_account'
-require 'models/customer_carrier'
+require "models/club"
+require "models/member_type"
+require "models/member"
+require "models/membership"
+require "models/sponsor"
+require "models/organization"
+require "models/member_detail"
+require "models/minivan"
+require "models/dashboard"
+require "models/speedometer"
+require "models/category"
+require "models/author"
+require "models/essay"
+require "models/owner"
+require "models/post"
+require "models/comment"
+require "models/categorization"
+require "models/customer"
+require "models/carrier"
+require "models/shop_account"
+require "models/customer_carrier"
class HasOneThroughAssociationsTest < ActiveRecord::TestCase
fixtures :member_types, :members, :clubs, :memberships, :sponsors, :organizations, :minivans,
@@ -34,14 +34,14 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase
end
def test_creating_association_creates_through_record
- new_member = Member.create(:name => "Chris")
- new_member.club = Club.create(:name => "LRUG")
+ new_member = Member.create(name: "Chris")
+ new_member.club = Club.create(name: "LRUG")
assert_not_nil new_member.current_membership
assert_not_nil new_member.club
end
def test_creating_association_builds_through_record_for_new
- new_member = Member.new(:name => "Jane")
+ new_member = Member.new(name: "Jane")
new_member.club = clubs(:moustache_club)
assert new_member.current_membership
assert_equal clubs(:moustache_club), new_member.current_membership.club
@@ -51,8 +51,8 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase
end
def test_creating_association_sets_both_parent_ids_for_new
- member = Member.new(name: 'Sean Griffin')
- club = Club.new(name: 'Da Club')
+ member = Member.new(name: "Sean Griffin")
+ club = Club.new(name: "Da Club")
member.club = club
@@ -65,7 +65,7 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase
end
def test_replace_target_record
- new_club = Club.create(:name => "Marx Bros")
+ new_club = Club.create(name: "Marx Bros")
@member.club = new_club
@member.reload
assert_equal new_club, @member.club
@@ -73,7 +73,7 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase
def test_replacing_target_record_deletes_old_association
assert_no_difference "Membership.count" do
- new_club = Club.create(:name => "Bananarama")
+ new_club = Club.create(name: "Bananarama")
@member.club = new_club
@member.reload
end
@@ -82,7 +82,7 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase
def test_set_record_to_nil_should_delete_association
@member.club = nil
@member.reload
- assert_equal nil, @member.current_membership
+ assert_nil @member.current_membership
assert_nil @member.club
end
@@ -92,30 +92,30 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase
def test_has_one_through_eager_loading
members = assert_queries(3) do #base table, through table, clubs table
- Member.all.merge!(:includes => :club, :where => ["name = ?", "Groucho Marx"]).to_a
+ Member.all.merge!(includes: :club, where: ["name = ?", "Groucho Marx"]).to_a
end
assert_equal 1, members.size
- assert_not_nil assert_no_queries {members[0].club}
+ assert_not_nil assert_no_queries { members[0].club }
end
def test_has_one_through_eager_loading_through_polymorphic
members = assert_queries(3) do #base table, through table, clubs table
- Member.all.merge!(:includes => :sponsor_club, :where => ["name = ?", "Groucho Marx"]).to_a
+ Member.all.merge!(includes: :sponsor_club, where: ["name = ?", "Groucho Marx"]).to_a
end
assert_equal 1, members.size
- assert_not_nil assert_no_queries {members[0].sponsor_club}
+ assert_not_nil assert_no_queries { members[0].sponsor_club }
end
def test_has_one_through_with_conditions_eager_loading
# conditions on the through table
- assert_equal clubs(:moustache_club), Member.all.merge!(:includes => :favourite_club).find(@member.id).favourite_club
+ assert_equal clubs(:moustache_club), Member.all.merge!(includes: :favourite_club).find(@member.id).favourite_club
memberships(:membership_of_favourite_club).update_columns(favourite: false)
- assert_equal nil, Member.all.merge!(:includes => :favourite_club).find(@member.id).reload.favourite_club
+ assert_nil Member.all.merge!(includes: :favourite_club).find(@member.id).reload.favourite_club
# conditions on the source table
- assert_equal clubs(:moustache_club), Member.all.merge!(:includes => :hairy_club).find(@member.id).hairy_club
+ assert_equal clubs(:moustache_club), Member.all.merge!(includes: :hairy_club).find(@member.id).hairy_club
clubs(:moustache_club).update_columns(name: "Association of Clean-Shaven Persons")
- assert_equal nil, Member.all.merge!(:includes => :hairy_club).find(@member.id).reload.hairy_club
+ assert_nil Member.all.merge!(includes: :hairy_club).find(@member.id).reload.hairy_club
end
def test_has_one_through_polymorphic_with_source_type
@@ -123,31 +123,31 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase
end
def test_eager_has_one_through_polymorphic_with_source_type
- clubs = Club.all.merge!(:includes => :sponsored_member, :where => ["name = ?","Moustache and Eyebrow Fancier Club"]).to_a
+ clubs = Club.all.merge!(includes: :sponsored_member, where: ["name = ?", "Moustache and Eyebrow Fancier Club"]).to_a
# Only the eyebrow fanciers club has a sponsored_member
- assert_not_nil assert_no_queries {clubs[0].sponsored_member}
+ assert_not_nil assert_no_queries { clubs[0].sponsored_member }
end
def test_has_one_through_nonpreload_eagerloading
members = assert_queries(1) do
- Member.all.merge!(:includes => :club, :where => ["members.name = ?", "Groucho Marx"], :order => 'clubs.name').to_a #force fallback
+ Member.all.merge!(includes: :club, where: ["members.name = ?", "Groucho Marx"], order: "clubs.name").to_a #force fallback
end
assert_equal 1, members.size
- assert_not_nil assert_no_queries {members[0].club}
+ assert_not_nil assert_no_queries { members[0].club }
end
def test_has_one_through_nonpreload_eager_loading_through_polymorphic
members = assert_queries(1) do
- Member.all.merge!(:includes => :sponsor_club, :where => ["members.name = ?", "Groucho Marx"], :order => 'clubs.name').to_a #force fallback
+ Member.all.merge!(includes: :sponsor_club, where: ["members.name = ?", "Groucho Marx"], order: "clubs.name").to_a #force fallback
end
assert_equal 1, members.size
- assert_not_nil assert_no_queries {members[0].sponsor_club}
+ assert_not_nil assert_no_queries { members[0].sponsor_club }
end
def test_has_one_through_nonpreload_eager_loading_through_polymorphic_with_more_than_one_through_record
- Sponsor.new(:sponsor_club => clubs(:crazy_club), :sponsorable => members(:groucho)).save!
+ Sponsor.new(sponsor_club: clubs(:crazy_club), sponsorable: members(:groucho)).save!
members = assert_queries(1) do
- Member.all.merge!(:includes => :sponsor_club, :where => ["members.name = ?", "Groucho Marx"], :order => 'clubs.name DESC').to_a #force fallback
+ Member.all.merge!(includes: :sponsor_club, where: ["members.name = ?", "Groucho Marx"], order: "clubs.name DESC").to_a #force fallback
end
assert_equal 1, members.size
assert_not_nil assert_no_queries { members[0].sponsor_club }
@@ -159,8 +159,8 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase
end
def test_assigning_association_correctly_assigns_target
- new_member = Member.create(:name => "Chris")
- new_member.club = new_club = Club.create(:name => "LRUG")
+ new_member = Member.create(name: "Chris")
+ new_member.club = new_club = Club.create(name: "LRUG")
assert_equal new_club, new_member.association(:club).target
end
@@ -176,37 +176,37 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase
def test_assigning_to_has_one_through_preserves_decorated_join_record
@organization = organizations(:nsa)
- assert_difference 'MemberDetail.count', 1 do
- @member_detail = MemberDetail.new(:extra_data => 'Extra')
+ assert_difference "MemberDetail.count", 1 do
+ @member_detail = MemberDetail.new(extra_data: "Extra")
@member.member_detail = @member_detail
@member.organization = @organization
end
assert_equal @organization, @member.organization
- assert @organization.members.include?(@member)
- assert_equal 'Extra', @member.member_detail.extra_data
+ assert_includes @organization.members, @member
+ assert_equal "Extra", @member.member_detail.extra_data
end
def test_reassigning_has_one_through
@organization = organizations(:nsa)
@new_organization = organizations(:discordians)
- assert_difference 'MemberDetail.count', 1 do
- @member_detail = MemberDetail.new(:extra_data => 'Extra')
+ assert_difference "MemberDetail.count", 1 do
+ @member_detail = MemberDetail.new(extra_data: "Extra")
@member.member_detail = @member_detail
@member.organization = @organization
end
assert_equal @organization, @member.organization
- assert_equal 'Extra', @member.member_detail.extra_data
- assert @organization.members.include?(@member)
- assert !@new_organization.members.include?(@member)
+ assert_equal "Extra", @member.member_detail.extra_data
+ assert_includes @organization.members, @member
+ assert_not_includes @new_organization.members, @member
- assert_no_difference 'MemberDetail.count' do
+ assert_no_difference "MemberDetail.count" do
@member.organization = @new_organization
end
assert_equal @new_organization, @member.organization
- assert_equal 'Extra', @member.member_detail.extra_data
- assert !@organization.members.include?(@member)
- assert @new_organization.members.include?(@member)
+ assert_equal "Extra", @member.member_detail.extra_data
+ assert_not_includes @organization.members, @member
+ assert_includes @new_organization.members, @member
end
def test_preloading_has_one_through_on_belongs_to
@@ -217,7 +217,7 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase
@member.member_detail = @member_detail
@member.organization = @organization
@member_details = assert_queries(3) do
- MemberDetail.all.merge!(:includes => :member_type).to_a
+ MemberDetail.all.merge!(includes: :member_type).to_a
end
@new_detail = @member_details[0]
assert @new_detail.send(:association, :member_type).loaded?
@@ -230,19 +230,19 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase
assert_nothing_raised do
Club.find(@club.id).save!
- Club.all.merge!(:includes => :sponsored_member).find(@club.id).save!
+ Club.all.merge!(includes: :sponsored_member).find(@club.id).save!
end
@club.sponsor.destroy
assert_nothing_raised do
Club.find(@club.id).save!
- Club.all.merge!(:includes => :sponsored_member).find(@club.id).save!
+ Club.all.merge!(includes: :sponsored_member).find(@club.id).save!
end
end
def test_through_belongs_to_after_destroy
- @member_detail = MemberDetail.new(:extra_data => 'Extra')
+ @member_detail = MemberDetail.new(extra_data: "Extra")
@member.member_detail = @member_detail
@member.save!
@@ -261,7 +261,7 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase
end
def test_value_is_properly_quoted
- minivan = Minivan.find('m1')
+ minivan = Minivan.find("m1")
assert_nothing_raised do
minivan.dashboard
end
@@ -270,7 +270,7 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase
def test_has_one_through_polymorphic_with_primary_key_option
assert_equal categories(:general), authors(:david).essay_category
- authors = Author.joins(:essay_category).where('categories.id' => categories(:general).id)
+ authors = Author.joins(:essay_category).where("categories.id" => categories(:general).id)
assert_equal authors(:david), authors.first
assert_equal owners(:blackbeard), authors(:david).essay_owner
@@ -282,12 +282,12 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase
def test_has_one_through_with_primary_key_option
assert_equal categories(:general), authors(:david).essay_category_2
- authors = Author.joins(:essay_category_2).where('categories.id' => categories(:general).id)
+ authors = Author.joins(:essay_category_2).where("categories.id" => categories(:general).id)
assert_equal authors(:david), authors.first
end
def test_has_one_through_with_default_scope_on_join_model
- assert_equal posts(:welcome).comments.order('id').first, authors(:david).comment_on_first_post
+ assert_equal posts(:welcome).comments.order("id").first, authors(:david).comment_on_first_post
end
def test_has_one_through_many_raises_exception
diff --git a/activerecord/test/cases/associations/inner_join_association_test.rb b/activerecord/test/cases/associations/inner_join_association_test.rb
index b3fe759ad9..7414869c8f 100644
--- a/activerecord/test/cases/associations/inner_join_association_test.rb
+++ b/activerecord/test/cases/associations/inner_join_association_test.rb
@@ -1,13 +1,13 @@
require "cases/helper"
-require 'models/post'
-require 'models/comment'
-require 'models/author'
-require 'models/essay'
-require 'models/category'
-require 'models/categorization'
-require 'models/person'
-require 'models/tagging'
-require 'models/tag'
+require "models/post"
+require "models/comment"
+require "models/author"
+require "models/essay"
+require "models/category"
+require "models/categorization"
+require "models/person"
+require "models/tagging"
+require "models/tag"
class InnerJoinAssociationTest < ActiveRecord::TestCase
fixtures :authors, :essays, :posts, :comments, :categories, :categories_posts, :categorizations,
@@ -20,7 +20,7 @@ class InnerJoinAssociationTest < ActiveRecord::TestCase
def test_construct_finder_sql_does_not_table_name_collide_on_duplicate_associations
assert_nothing_raised do
- sql = Person.joins(:agents => {:agents => :agents}).joins(:agents => {:agents => {:primary_contact => :agents}}).to_sql
+ sql = Person.joins(agents: { agents: :agents }).joins(agents: { agents: { primary_contact: :agents } }).to_sql
assert_match(/agents_people_4/i, sql)
end
end
@@ -47,7 +47,7 @@ class InnerJoinAssociationTest < ActiveRecord::TestCase
end
def test_join_conditions_allow_nil_associations
- authors = Author.includes(:essays).where(:essays => {:id => nil})
+ authors = Author.includes(:essays).where(essays: { id: nil })
assert_equal 2, authors.count
end
@@ -58,41 +58,41 @@ class InnerJoinAssociationTest < ActiveRecord::TestCase
end
def test_find_with_implicit_inner_joins_honors_readonly_with_select
- authors = Author.joins(:posts).select('authors.*').to_a
+ authors = Author.joins(:posts).select("authors.*").to_a
assert !authors.empty?, "expected authors to be non-empty"
- assert authors.all? {|a| !a.readonly? }, "expected no authors to be readonly"
+ assert authors.all? { |a| !a.readonly? }, "expected no authors to be readonly"
end
def test_find_with_implicit_inner_joins_honors_readonly_false
authors = Author.joins(:posts).readonly(false).to_a
assert !authors.empty?, "expected authors to be non-empty"
- assert authors.all? {|a| !a.readonly? }, "expected no authors to be readonly"
+ assert authors.all? { |a| !a.readonly? }, "expected no authors to be readonly"
end
def test_find_with_implicit_inner_joins_does_not_set_associations
- authors = Author.joins(:posts).select('authors.*').to_a
+ authors = Author.joins(:posts).select("authors.*").to_a
assert !authors.empty?, "expected authors to be non-empty"
assert authors.all? { |a| !a.instance_variable_defined?(:@posts) }, "expected no authors to have the @posts association loaded"
end
def test_count_honors_implicit_inner_joins
- real_count = Author.all.to_a.sum{|a| a.posts.count }
+ real_count = Author.all.to_a.sum { |a| a.posts.count }
assert_equal real_count, Author.joins(:posts).count, "plain inner join count should match the number of referenced posts records"
end
def test_calculate_honors_implicit_inner_joins
- real_count = Author.all.to_a.sum{|a| a.posts.count }
- assert_equal real_count, Author.joins(:posts).calculate(:count, 'authors.id'), "plain inner join count should match the number of referenced posts records"
+ real_count = Author.all.to_a.sum { |a| a.posts.count }
+ assert_equal real_count, Author.joins(:posts).calculate(:count, "authors.id"), "plain inner join count should match the number of referenced posts records"
end
def test_calculate_honors_implicit_inner_joins_and_distinct_and_conditions
- real_count = Author.all.to_a.select {|a| a.posts.any? {|p| p.title =~ /^Welcome/} }.length
- authors_with_welcoming_post_titles = Author.all.merge!(joins: :posts, where: "posts.title like 'Welcome%'").distinct.calculate(:count, 'authors.id')
+ real_count = Author.all.to_a.select { |a| a.posts.any? { |p| p.title.start_with?("Welcome") } }.length
+ authors_with_welcoming_post_titles = Author.all.merge!(joins: :posts, where: "posts.title like 'Welcome%'").distinct.calculate(:count, "authors.id")
assert_equal real_count, authors_with_welcoming_post_titles, "inner join and conditions should have only returned authors posting titles starting with 'Welcome'"
end
def test_find_with_sti_join
- scope = Post.joins(:special_comments).where(:id => posts(:sti_comments).id)
+ scope = Post.joins(:special_comments).where(id: posts(:sti_comments).id)
# The join should match SpecialComment and its subclasses only
assert scope.where("comments.type" => "Comment").empty?
@@ -102,12 +102,12 @@ class InnerJoinAssociationTest < ActiveRecord::TestCase
def test_find_with_conditions_on_reflection
assert !posts(:welcome).comments.empty?
- assert Post.joins(:nonexistent_comments).where(:id => posts(:welcome).id).empty? # [sic!]
+ assert Post.joins(:nonexistent_comments).where(id: posts(:welcome).id).empty? # [sic!]
end
def test_find_with_conditions_on_through_reflection
assert !posts(:welcome).tags.empty?
- assert Post.joins(:misc_tags).where(:id => posts(:welcome).id).empty?
+ assert Post.joins(:misc_tags).where(id: posts(:welcome).id).empty?
end
test "the default scope of the target is applied when joining associations" do
@@ -120,8 +120,8 @@ class InnerJoinAssociationTest < ActiveRecord::TestCase
test "the default scope of the target is correctly aliased when joining associations" do
author = Author.create! name: "Jon"
- author.categories.create! name: 'Not Special'
- author.special_categories.create! name: 'Special'
+ author.categories.create! name: "Not Special"
+ author.special_categories.create! name: "Special"
categories = author.categories.includes(:special_categorizations).references(:special_categorizations).to_a
assert_equal 2, categories.size
@@ -129,8 +129,8 @@ class InnerJoinAssociationTest < ActiveRecord::TestCase
test "the correct records are loaded when including an aliased association" do
author = Author.create! name: "Jon"
- author.categories.create! name: 'Not Special'
- author.special_categories.create! name: 'Special'
+ author.categories.create! name: "Not Special"
+ author.special_categories.create! name: "Special"
categories = author.categories.eager_load(:special_categorizations).order(:name).to_a
assert_equal 0, categories.first.special_categorizations.size
diff --git a/activerecord/test/cases/associations/inverse_associations_test.rb b/activerecord/test/cases/associations/inverse_associations_test.rb
index c9743e80d3..6fe6ee6783 100644
--- a/activerecord/test/cases/associations/inverse_associations_test.rb
+++ b/activerecord/test/cases/associations/inverse_associations_test.rb
@@ -1,21 +1,21 @@
require "cases/helper"
-require 'models/man'
-require 'models/face'
-require 'models/interest'
-require 'models/zine'
-require 'models/club'
-require 'models/sponsor'
-require 'models/rating'
-require 'models/comment'
-require 'models/car'
-require 'models/bulb'
-require 'models/mixed_case_monkey'
-require 'models/admin'
-require 'models/admin/account'
-require 'models/admin/user'
-require 'models/developer'
-require 'models/company'
-require 'models/project'
+require "models/man"
+require "models/face"
+require "models/interest"
+require "models/zine"
+require "models/club"
+require "models/sponsor"
+require "models/rating"
+require "models/comment"
+require "models/car"
+require "models/bulb"
+require "models/mixed_case_monkey"
+require "models/admin"
+require "models/admin/account"
+require "models/admin/user"
+require "models/developer"
+require "models/company"
+require "models/project"
class AutomaticInverseFindingTests < ActiveRecord::TestCase
fixtures :ratings, :comments, :cars
@@ -131,15 +131,15 @@ end
class InverseAssociationTests < ActiveRecord::TestCase
def test_should_allow_for_inverse_of_options_in_associations
assert_nothing_raised do
- Class.new(ActiveRecord::Base).has_many(:wheels, :inverse_of => :car)
+ Class.new(ActiveRecord::Base).has_many(:wheels, inverse_of: :car)
end
assert_nothing_raised do
- Class.new(ActiveRecord::Base).has_one(:engine, :inverse_of => :car)
+ Class.new(ActiveRecord::Base).has_one(:engine, inverse_of: :car)
end
assert_nothing_raised do
- Class.new(ActiveRecord::Base).belongs_to(:car, :inverse_of => :driver)
+ Class.new(ActiveRecord::Base).belongs_to(:car, inverse_of: :driver)
end
end
@@ -203,9 +203,9 @@ class InverseAssociationTests < ActiveRecord::TestCase
end
def test_this_inverse_stuff
- firm = Firm.create!(name: 'Adequate Holdings')
- Project.create!(name: 'Project 1', firm: firm)
- Developer.create!(name: 'Gorbypuff', firm: firm)
+ firm = Firm.create!(name: "Adequate Holdings")
+ Project.create!(name: "Project 1", firm: firm)
+ Developer.create!(name: "Gorbypuff", firm: firm)
new_project = Project.last
assert Project.reflect_on_association(:lead_developer).inverse_of.present?, "Expected inverse of to be present"
@@ -220,73 +220,72 @@ class InverseHasOneTests < ActiveRecord::TestCase
m = men(:gordon)
f = m.face
assert_equal m.name, f.man.name, "Name of man should be the same before changes to parent instance"
- m.name = 'Bongo'
+ m.name = "Bongo"
assert_equal m.name, f.man.name, "Name of man should be the same after changes to parent instance"
- f.man.name = 'Mungo'
+ f.man.name = "Mungo"
assert_equal m.name, f.man.name, "Name of man should be the same after changes to child-owned instance"
end
-
def test_parent_instance_should_be_shared_with_eager_loaded_child_on_find
- m = Man.all.merge!(:where => {:name => 'Gordon'}, :includes => :face).first
+ m = Man.all.merge!(where: { name: "Gordon" }, includes: :face).first
f = m.face
assert_equal m.name, f.man.name, "Name of man should be the same before changes to parent instance"
- m.name = 'Bongo'
+ m.name = "Bongo"
assert_equal m.name, f.man.name, "Name of man should be the same after changes to parent instance"
- f.man.name = 'Mungo'
+ f.man.name = "Mungo"
assert_equal m.name, f.man.name, "Name of man should be the same after changes to child-owned instance"
- m = Man.all.merge!(:where => {:name => 'Gordon'}, :includes => :face, :order => 'faces.id').first
+ m = Man.all.merge!(where: { name: "Gordon" }, includes: :face, order: "faces.id").first
f = m.face
assert_equal m.name, f.man.name, "Name of man should be the same before changes to parent instance"
- m.name = 'Bongo'
+ m.name = "Bongo"
assert_equal m.name, f.man.name, "Name of man should be the same after changes to parent instance"
- f.man.name = 'Mungo'
+ f.man.name = "Mungo"
assert_equal m.name, f.man.name, "Name of man should be the same after changes to child-owned instance"
end
def test_parent_instance_should_be_shared_with_newly_built_child
m = Man.first
- f = m.build_face(:description => 'haunted')
+ f = m.build_face(description: "haunted")
assert_not_nil f.man
assert_equal m.name, f.man.name, "Name of man should be the same before changes to parent instance"
- m.name = 'Bongo'
+ m.name = "Bongo"
assert_equal m.name, f.man.name, "Name of man should be the same after changes to parent instance"
- f.man.name = 'Mungo'
+ f.man.name = "Mungo"
assert_equal m.name, f.man.name, "Name of man should be the same after changes to just-built-child-owned instance"
end
def test_parent_instance_should_be_shared_with_newly_created_child
m = Man.first
- f = m.create_face(:description => 'haunted')
+ f = m.create_face(description: "haunted")
assert_not_nil f.man
assert_equal m.name, f.man.name, "Name of man should be the same before changes to parent instance"
- m.name = 'Bongo'
+ m.name = "Bongo"
assert_equal m.name, f.man.name, "Name of man should be the same after changes to parent instance"
- f.man.name = 'Mungo'
+ f.man.name = "Mungo"
assert_equal m.name, f.man.name, "Name of man should be the same after changes to newly-created-child-owned instance"
end
def test_parent_instance_should_be_shared_with_newly_created_child_via_bang_method
m = Man.first
- f = m.create_face!(:description => 'haunted')
+ f = m.create_face!(description: "haunted")
assert_not_nil f.man
assert_equal m.name, f.man.name, "Name of man should be the same before changes to parent instance"
- m.name = 'Bongo'
+ m.name = "Bongo"
assert_equal m.name, f.man.name, "Name of man should be the same after changes to parent instance"
- f.man.name = 'Mungo'
+ f.man.name = "Mungo"
assert_equal m.name, f.man.name, "Name of man should be the same after changes to newly-created-child-owned instance"
end
def test_parent_instance_should_be_shared_with_replaced_via_accessor_child
m = Man.first
- f = Face.new(:description => 'haunted')
+ f = Face.new(description: "haunted")
m.face = f
assert_not_nil f.man
assert_equal m.name, f.man.name, "Name of man should be the same before changes to parent instance"
- m.name = 'Bongo'
+ m.name = "Bongo"
assert_equal m.name, f.man.name, "Name of man should be the same after changes to parent instance"
- f.man.name = 'Mungo'
+ f.man.name = "Mungo"
assert_equal m.name, f.man.name, "Name of man should be the same after changes to replaced-child-owned instance"
end
@@ -303,67 +302,67 @@ class InverseHasManyTests < ActiveRecord::TestCase
is = m.interests
is.each do |i|
assert_equal m.name, i.man.name, "Name of man should be the same before changes to parent instance"
- m.name = 'Bongo'
+ m.name = "Bongo"
assert_equal m.name, i.man.name, "Name of man should be the same after changes to parent instance"
- i.man.name = 'Mungo'
+ i.man.name = "Mungo"
assert_equal m.name, i.man.name, "Name of man should be the same after changes to child-owned instance"
end
end
def test_parent_instance_should_be_shared_with_eager_loaded_children
- m = Man.all.merge!(:where => {:name => 'Gordon'}, :includes => :interests).first
+ m = Man.all.merge!(where: { name: "Gordon" }, includes: :interests).first
is = m.interests
is.each do |i|
assert_equal m.name, i.man.name, "Name of man should be the same before changes to parent instance"
- m.name = 'Bongo'
+ m.name = "Bongo"
assert_equal m.name, i.man.name, "Name of man should be the same after changes to parent instance"
- i.man.name = 'Mungo'
+ i.man.name = "Mungo"
assert_equal m.name, i.man.name, "Name of man should be the same after changes to child-owned instance"
end
- m = Man.all.merge!(:where => {:name => 'Gordon'}, :includes => :interests, :order => 'interests.id').first
+ m = Man.all.merge!(where: { name: "Gordon" }, includes: :interests, order: "interests.id").first
is = m.interests
is.each do |i|
assert_equal m.name, i.man.name, "Name of man should be the same before changes to parent instance"
- m.name = 'Bongo'
+ m.name = "Bongo"
assert_equal m.name, i.man.name, "Name of man should be the same after changes to parent instance"
- i.man.name = 'Mungo'
+ i.man.name = "Mungo"
assert_equal m.name, i.man.name, "Name of man should be the same after changes to child-owned instance"
end
end
def test_parent_instance_should_be_shared_with_newly_block_style_built_child
m = Man.first
- i = m.interests.build {|ii| ii.topic = 'Industrial Revolution Re-enactment'}
+ i = m.interests.build { |ii| ii.topic = "Industrial Revolution Re-enactment" }
assert_not_nil i.topic, "Child attributes supplied to build via blocks should be populated"
assert_not_nil i.man
assert_equal m.name, i.man.name, "Name of man should be the same before changes to parent instance"
- m.name = 'Bongo'
+ m.name = "Bongo"
assert_equal m.name, i.man.name, "Name of man should be the same after changes to parent instance"
- i.man.name = 'Mungo'
+ i.man.name = "Mungo"
assert_equal m.name, i.man.name, "Name of man should be the same after changes to just-built-child-owned instance"
end
def test_parent_instance_should_be_shared_with_newly_created_via_bang_method_child
m = Man.first
- i = m.interests.create!(:topic => 'Industrial Revolution Re-enactment')
+ i = m.interests.create!(topic: "Industrial Revolution Re-enactment")
assert_not_nil i.man
assert_equal m.name, i.man.name, "Name of man should be the same before changes to parent instance"
- m.name = 'Bongo'
+ m.name = "Bongo"
assert_equal m.name, i.man.name, "Name of man should be the same after changes to parent instance"
- i.man.name = 'Mungo'
+ i.man.name = "Mungo"
assert_equal m.name, i.man.name, "Name of man should be the same after changes to newly-created-child-owned instance"
end
def test_parent_instance_should_be_shared_with_newly_block_style_created_child
m = Man.first
- i = m.interests.create {|ii| ii.topic = 'Industrial Revolution Re-enactment'}
+ i = m.interests.create { |ii| ii.topic = "Industrial Revolution Re-enactment" }
assert_not_nil i.topic, "Child attributes supplied to create via blocks should be populated"
assert_not_nil i.man
assert_equal m.name, i.man.name, "Name of man should be the same before changes to parent instance"
- m.name = 'Bongo'
+ m.name = "Bongo"
assert_equal m.name, i.man.name, "Name of man should be the same after changes to parent instance"
- i.man.name = 'Mungo'
+ i.man.name = "Mungo"
assert_equal m.name, i.man.name, "Name of man should be the same after changes to newly-created-child-owned instance"
end
@@ -385,25 +384,25 @@ class InverseHasManyTests < ActiveRecord::TestCase
def test_parent_instance_should_be_shared_with_poked_in_child
m = men(:gordon)
- i = Interest.create(:topic => 'Industrial Revolution Re-enactment')
+ i = Interest.create(topic: "Industrial Revolution Re-enactment")
m.interests << i
assert_not_nil i.man
assert_equal m.name, i.man.name, "Name of man should be the same before changes to parent instance"
- m.name = 'Bongo'
+ m.name = "Bongo"
assert_equal m.name, i.man.name, "Name of man should be the same after changes to parent instance"
- i.man.name = 'Mungo'
+ i.man.name = "Mungo"
assert_equal m.name, i.man.name, "Name of man should be the same after changes to newly-created-child-owned instance"
end
def test_parent_instance_should_be_shared_with_replaced_via_accessor_children
m = Man.first
- i = Interest.new(:topic => 'Industrial Revolution Re-enactment')
+ i = Interest.new(topic: "Industrial Revolution Re-enactment")
m.interests = [i]
assert_not_nil i.man
assert_equal m.name, i.man.name, "Name of man should be the same before changes to parent instance"
- m.name = 'Bongo'
+ m.name = "Bongo"
assert_equal m.name, i.man.name, "Name of man should be the same after changes to parent instance"
- i.man.name = 'Mungo'
+ i.man.name = "Mungo"
assert_equal m.name, i.man.name, "Name of man should be the same after changes to replaced-child-owned instance"
end
@@ -485,7 +484,7 @@ class InverseHasManyTests < ActiveRecord::TestCase
def test_child_instance_should_point_to_parent_without_saving
man = Man.new
- i = Interest.create(:topic => 'Industrial Revolution Re-enactment')
+ i = Interest.create(topic: "Industrial Revolution Re-enactment")
man.interests << i
assert_not_nil i.man
@@ -495,6 +494,33 @@ class InverseHasManyTests < ActiveRecord::TestCase
assert !man.persisted?
end
+
+ def test_inverse_instance_should_be_set_before_find_callbacks_are_run
+ reset_callbacks(Interest, :find) do
+ Interest.after_find { raise unless association(:man).loaded? && man.present? }
+
+ assert Man.first.interests.reload.any?
+ assert Man.includes(:interests).first.interests.any?
+ assert Man.joins(:interests).includes(:interests).first.interests.any?
+ end
+ end
+
+ def test_inverse_instance_should_be_set_before_initialize_callbacks_are_run
+ reset_callbacks(Interest, :initialize) do
+ Interest.after_initialize { raise unless association(:man).loaded? && man.present? }
+
+ assert Man.first.interests.reload.any?
+ assert Man.includes(:interests).first.interests.any?
+ assert Man.joins(:interests).includes(:interests).first.interests.any?
+ end
+ end
+
+ def reset_callbacks(target, type)
+ old_callbacks = target.send(:get_callbacks, type).deep_dup
+ yield
+ ensure
+ target.send(:set_callbacks, type, old_callbacks) if old_callbacks
+ end
end
class InverseBelongsToTests < ActiveRecord::TestCase
@@ -504,49 +530,49 @@ class InverseBelongsToTests < ActiveRecord::TestCase
f = faces(:trusting)
m = f.man
assert_equal f.description, m.face.description, "Description of face should be the same before changes to child instance"
- f.description = 'gormless'
+ f.description = "gormless"
assert_equal f.description, m.face.description, "Description of face should be the same after changes to child instance"
- m.face.description = 'pleasing'
+ m.face.description = "pleasing"
assert_equal f.description, m.face.description, "Description of face should be the same after changes to parent-owned instance"
end
def test_eager_loaded_child_instance_should_be_shared_with_parent_on_find
- f = Face.all.merge!(:includes => :man, :where => {:description => 'trusting'}).first
+ f = Face.all.merge!(includes: :man, where: { description: "trusting" }).first
m = f.man
assert_equal f.description, m.face.description, "Description of face should be the same before changes to child instance"
- f.description = 'gormless'
+ f.description = "gormless"
assert_equal f.description, m.face.description, "Description of face should be the same after changes to child instance"
- m.face.description = 'pleasing'
+ m.face.description = "pleasing"
assert_equal f.description, m.face.description, "Description of face should be the same after changes to parent-owned instance"
- f = Face.all.merge!(:includes => :man, :order => 'men.id', :where => {:description => 'trusting'}).first
+ f = Face.all.merge!(includes: :man, order: "men.id", where: { description: "trusting" }).first
m = f.man
assert_equal f.description, m.face.description, "Description of face should be the same before changes to child instance"
- f.description = 'gormless'
+ f.description = "gormless"
assert_equal f.description, m.face.description, "Description of face should be the same after changes to child instance"
- m.face.description = 'pleasing'
+ m.face.description = "pleasing"
assert_equal f.description, m.face.description, "Description of face should be the same after changes to parent-owned instance"
end
def test_child_instance_should_be_shared_with_newly_built_parent
f = faces(:trusting)
- m = f.build_man(:name => 'Charles')
+ m = f.build_man(name: "Charles")
assert_not_nil m.face
assert_equal f.description, m.face.description, "Description of face should be the same before changes to child instance"
- f.description = 'gormless'
+ f.description = "gormless"
assert_equal f.description, m.face.description, "Description of face should be the same after changes to child instance"
- m.face.description = 'pleasing'
+ m.face.description = "pleasing"
assert_equal f.description, m.face.description, "Description of face should be the same after changes to just-built-parent-owned instance"
end
def test_child_instance_should_be_shared_with_newly_created_parent
f = faces(:trusting)
- m = f.create_man(:name => 'Charles')
+ m = f.create_man(name: "Charles")
assert_not_nil m.face
assert_equal f.description, m.face.description, "Description of face should be the same before changes to child instance"
- f.description = 'gormless'
+ f.description = "gormless"
assert_equal f.description, m.face.description, "Description of face should be the same after changes to child instance"
- m.face.description = 'pleasing'
+ m.face.description = "pleasing"
assert_equal f.description, m.face.description, "Description of face should be the same after changes to newly-created-parent-owned instance"
end
@@ -554,24 +580,24 @@ class InverseBelongsToTests < ActiveRecord::TestCase
i = interests(:trainspotting)
m = i.man
assert_not_nil m.interests
- iz = m.interests.detect { |_iz| _iz.id == i.id}
+ iz = m.interests.detect { |_iz| _iz.id == i.id }
assert_not_nil iz
assert_equal i.topic, iz.topic, "Interest topics should be the same before changes to child"
- i.topic = 'Eating cheese with a spoon'
+ i.topic = "Eating cheese with a spoon"
assert_not_equal i.topic, iz.topic, "Interest topics should not be the same after changes to child"
- iz.topic = 'Cow tipping'
+ iz.topic = "Cow tipping"
assert_not_equal i.topic, iz.topic, "Interest topics should not be the same after changes to parent-owned instance"
end
def test_child_instance_should_be_shared_with_replaced_via_accessor_parent
f = Face.first
- m = Man.new(:name => 'Charles')
+ m = Man.new(name: "Charles")
f.man = m
assert_not_nil m.face
assert_equal f.description, m.face.description, "Description of face should be the same before changes to child instance"
- f.description = 'gormless'
+ f.description = "gormless"
assert_equal f.description, m.face.description, "Description of face should be the same after changes to child instance"
- m.face.description = 'pleasing'
+ m.face.description = "pleasing"
assert_equal f.description, m.face.description, "Description of face should be the same after changes to replaced-parent-owned instance"
end
@@ -584,30 +610,30 @@ class InversePolymorphicBelongsToTests < ActiveRecord::TestCase
fixtures :men, :faces, :interests
def test_child_instance_should_be_shared_with_parent_on_find
- f = Face.all.merge!(:where => {:description => 'confused'}).first
+ f = Face.all.merge!(where: { description: "confused" }).first
m = f.polymorphic_man
assert_equal f.description, m.polymorphic_face.description, "Description of face should be the same before changes to child instance"
- f.description = 'gormless'
+ f.description = "gormless"
assert_equal f.description, m.polymorphic_face.description, "Description of face should be the same after changes to child instance"
- m.polymorphic_face.description = 'pleasing'
+ m.polymorphic_face.description = "pleasing"
assert_equal f.description, m.polymorphic_face.description, "Description of face should be the same after changes to parent-owned instance"
end
def test_eager_loaded_child_instance_should_be_shared_with_parent_on_find
- f = Face.all.merge!(:where => {:description => 'confused'}, :includes => :man).first
+ f = Face.all.merge!(where: { description: "confused" }, includes: :man).first
m = f.polymorphic_man
assert_equal f.description, m.polymorphic_face.description, "Description of face should be the same before changes to child instance"
- f.description = 'gormless'
+ f.description = "gormless"
assert_equal f.description, m.polymorphic_face.description, "Description of face should be the same after changes to child instance"
- m.polymorphic_face.description = 'pleasing'
+ m.polymorphic_face.description = "pleasing"
assert_equal f.description, m.polymorphic_face.description, "Description of face should be the same after changes to parent-owned instance"
- f = Face.all.merge!(:where => {:description => 'confused'}, :includes => :man, :order => 'men.id').first
+ f = Face.all.merge!(where: { description: "confused" }, includes: :man, order: "men.id").first
m = f.polymorphic_man
assert_equal f.description, m.polymorphic_face.description, "Description of face should be the same before changes to child instance"
- f.description = 'gormless'
+ f.description = "gormless"
assert_equal f.description, m.polymorphic_face.description, "Description of face should be the same after changes to child instance"
- m.polymorphic_face.description = 'pleasing'
+ m.polymorphic_face.description = "pleasing"
assert_equal f.description, m.polymorphic_face.description, "Description of face should be the same after changes to parent-owned instance"
end
@@ -619,9 +645,9 @@ class InversePolymorphicBelongsToTests < ActiveRecord::TestCase
face.polymorphic_man = new_man
assert_equal face.description, new_man.polymorphic_face.description, "Description of face should be the same before changes to parent instance"
- face.description = 'Bongo'
+ face.description = "Bongo"
assert_equal face.description, new_man.polymorphic_face.description, "Description of face should be the same after changes to parent instance"
- new_man.polymorphic_face.description = 'Mungo'
+ new_man.polymorphic_face.description = "Mungo"
assert_equal face.description, new_man.polymorphic_face.description, "Description of face should be the same after changes to replaced-parent-owned instance"
end
@@ -633,9 +659,9 @@ class InversePolymorphicBelongsToTests < ActiveRecord::TestCase
face.polymorphic_man = new_man
assert_equal face.description, new_man.polymorphic_face.description, "Description of face should be the same before changes to parent instance"
- face.description = 'Bongo'
+ face.description = "Bongo"
assert_equal face.description, new_man.polymorphic_face.description, "Description of face should be the same after changes to parent instance"
- new_man.polymorphic_face.description = 'Mungo'
+ new_man.polymorphic_face.description = "Mungo"
assert_equal face.description, new_man.polymorphic_face.description, "Description of face should be the same after changes to replaced-parent-owned instance"
end
@@ -655,12 +681,12 @@ class InversePolymorphicBelongsToTests < ActiveRecord::TestCase
i = interests(:llama_wrangling)
m = i.polymorphic_man
assert_not_nil m.polymorphic_interests
- iz = m.polymorphic_interests.detect { |_iz| _iz.id == i.id}
+ iz = m.polymorphic_interests.detect { |_iz| _iz.id == i.id }
assert_not_nil iz
assert_equal i.topic, iz.topic, "Interest topics should be the same before changes to child"
- i.topic = 'Eating cheese with a spoon'
+ i.topic = "Eating cheese with a spoon"
assert_not_equal i.topic, iz.topic, "Interest topics should not be the same after changes to child"
- iz.topic = 'Cow tipping'
+ iz.topic = "Cow tipping"
assert_not_equal i.topic, iz.topic, "Interest topics should not be the same after changes to parent-owned instance"
end
@@ -698,8 +724,8 @@ class InverseMultipleHasManyInversesForSameModel < ActiveRecord::TestCase
def test_that_we_can_create_associations_that_have_the_same_reciprocal_name_from_different_models
assert_nothing_raised do
i = Interest.first
- i.build_zine(:title => 'Get Some in Winter! 2008')
- i.build_man(:name => 'Gordon')
+ i.build_zine(title: "Get Some in Winter! 2008")
+ i.build_man(name: "Gordon")
i.save!
end
end
diff --git a/activerecord/test/cases/associations/join_model_test.rb b/activerecord/test/cases/associations/join_model_test.rb
index 3047914b70..a4345f3857 100644
--- a/activerecord/test/cases/associations/join_model_test.rb
+++ b/activerecord/test/cases/associations/join_model_test.rb
@@ -1,20 +1,20 @@
require "cases/helper"
-require 'models/tag'
-require 'models/tagging'
-require 'models/post'
-require 'models/rating'
-require 'models/item'
-require 'models/comment'
-require 'models/author'
-require 'models/category'
-require 'models/categorization'
-require 'models/vertex'
-require 'models/edge'
-require 'models/book'
-require 'models/citation'
-require 'models/aircraft'
-require 'models/engine'
-require 'models/car'
+require "models/tag"
+require "models/tagging"
+require "models/post"
+require "models/rating"
+require "models/item"
+require "models/comment"
+require "models/author"
+require "models/category"
+require "models/categorization"
+require "models/vertex"
+require "models/edge"
+require "models/book"
+require "models/citation"
+require "models/aircraft"
+require "models/engine"
+require "models/car"
class AssociationsJoinModelTest < ActiveRecord::TestCase
self.use_transactional_tests = false unless supports_savepoints?
@@ -24,15 +24,15 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
:edges
def test_has_many
- assert authors(:david).categories.include?(categories(:general))
+ assert_includes authors(:david).categories, categories(:general)
end
def test_has_many_inherited
- assert authors(:mary).categories.include?(categories(:sti_test))
+ assert_includes authors(:mary).categories, categories(:sti_test)
end
def test_inherited_has_many
- assert categories(:sti_test).authors.include?(authors(:mary))
+ assert_includes categories(:sti_test).authors, authors(:mary)
end
def test_has_many_distinct_through_join_model
@@ -97,10 +97,10 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
end
def test_polymorphic_has_many_create_model_with_inheritance_and_custom_base_class
- post = SubStiPost.create :title => 'SubStiPost', :body => 'SubStiPost body'
+ post = SubStiPost.create title: "SubStiPost", body: "SubStiPost body"
assert_instance_of SubStiPost, post
- tagging = tags(:misc).taggings.create(:taggable => post)
+ tagging = tags(:misc).taggings.create(taggable: post)
assert_equal "SubStiPost", tagging.taggable_type
end
@@ -116,12 +116,12 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
post = posts(:thinking)
assert_instance_of SpecialPost, post
- tagging = tags(:misc).taggings.create(:taggable => post)
+ tagging = tags(:misc).taggings.create(taggable: post)
assert_equal "Post", tagging.taggable_type
end
def test_polymorphic_has_one_create_model_with_inheritance
- tagging = tags(:misc).create_tagging(:taggable => posts(:thinking))
+ tagging = tags(:misc).create_tagging(taggable: posts(:thinking))
assert_equal "Post", tagging.taggable_type
end
@@ -142,7 +142,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
def test_set_polymorphic_has_one_on_new_record
tagging = tags(:misc).taggings.create
- post = Post.new :title => "foo", :body => "bar"
+ post = Post.new title: "foo", body: "bar"
post.tagging = tagging
post.save!
@@ -153,50 +153,50 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
def test_create_polymorphic_has_many_with_scope
old_count = posts(:welcome).taggings.count
- tagging = posts(:welcome).taggings.create(:tag => tags(:misc))
+ tagging = posts(:welcome).taggings.create(tag: tags(:misc))
assert_equal "Post", tagging.taggable_type
- assert_equal old_count+1, posts(:welcome).taggings.count
+ assert_equal old_count + 1, posts(:welcome).taggings.count
end
def test_create_bang_polymorphic_with_has_many_scope
old_count = posts(:welcome).taggings.count
- tagging = posts(:welcome).taggings.create!(:tag => tags(:misc))
+ tagging = posts(:welcome).taggings.create!(tag: tags(:misc))
assert_equal "Post", tagging.taggable_type
- assert_equal old_count+1, posts(:welcome).taggings.count
+ assert_equal old_count + 1, posts(:welcome).taggings.count
end
def test_create_polymorphic_has_one_with_scope
old_count = Tagging.count
- tagging = posts(:welcome).create_tagging(:tag => tags(:misc))
+ tagging = posts(:welcome).create_tagging(tag: tags(:misc))
assert_equal "Post", tagging.taggable_type
- assert_equal old_count+1, Tagging.count
+ assert_equal old_count + 1, Tagging.count
end
def test_delete_polymorphic_has_many_with_delete_all
assert_equal 1, posts(:welcome).taggings.count
- posts(:welcome).taggings.first.update_columns taggable_type: 'PostWithHasManyDeleteAll'
+ posts(:welcome).taggings.first.update_columns taggable_type: "PostWithHasManyDeleteAll"
post = find_post_with_dependency(1, :has_many, :taggings, :delete_all)
old_count = Tagging.count
post.destroy
- assert_equal old_count-1, Tagging.count
+ assert_equal old_count - 1, Tagging.count
assert_equal 0, posts(:welcome).taggings.count
end
def test_delete_polymorphic_has_many_with_destroy
assert_equal 1, posts(:welcome).taggings.count
- posts(:welcome).taggings.first.update_columns taggable_type: 'PostWithHasManyDestroy'
+ posts(:welcome).taggings.first.update_columns taggable_type: "PostWithHasManyDestroy"
post = find_post_with_dependency(1, :has_many, :taggings, :destroy)
old_count = Tagging.count
post.destroy
- assert_equal old_count-1, Tagging.count
+ assert_equal old_count - 1, Tagging.count
assert_equal 0, posts(:welcome).taggings.count
end
def test_delete_polymorphic_has_many_with_nullify
assert_equal 1, posts(:welcome).taggings.count
- posts(:welcome).taggings.first.update_columns taggable_type: 'PostWithHasManyNullify'
+ posts(:welcome).taggings.first.update_columns taggable_type: "PostWithHasManyNullify"
post = find_post_with_dependency(1, :has_many, :taggings, :nullify)
old_count = Tagging.count
@@ -207,19 +207,19 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
def test_delete_polymorphic_has_one_with_destroy
assert posts(:welcome).tagging
- posts(:welcome).tagging.update_columns taggable_type: 'PostWithHasOneDestroy'
+ posts(:welcome).tagging.update_columns taggable_type: "PostWithHasOneDestroy"
post = find_post_with_dependency(1, :has_one, :tagging, :destroy)
old_count = Tagging.count
post.destroy
- assert_equal old_count-1, Tagging.count
+ assert_equal old_count - 1, Tagging.count
posts(:welcome).association(:tagging).reload
assert_nil posts(:welcome).tagging
end
def test_delete_polymorphic_has_one_with_nullify
assert posts(:welcome).tagging
- posts(:welcome).tagging.update_columns taggable_type: 'PostWithHasOneNullify'
+ posts(:welcome).tagging.update_columns taggable_type: "PostWithHasOneNullify"
post = find_post_with_dependency(1, :has_one, :tagging, :nullify)
old_count = Tagging.count
@@ -235,15 +235,15 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
def test_create_through_has_many_with_piggyback
category = categories(:sti_test)
- ernie = category.authors_with_select.create(:name => 'Ernie')
+ ernie = category.authors_with_select.create(name: "Ernie")
assert_nothing_raised do
- assert_equal ernie, category.authors_with_select.detect {|a| a.name == 'Ernie'}
+ assert_equal ernie, category.authors_with_select.detect { |a| a.name == "Ernie" }
end
end
def test_include_has_many_through
- posts = Post.all.merge!(:order => 'posts.id').to_a
- posts_with_authors = Post.all.merge!(:includes => :authors, :order => 'posts.id').to_a
+ posts = Post.all.merge!(order: "posts.id").to_a
+ posts_with_authors = Post.all.merge!(includes: :authors, order: "posts.id").to_a
assert_equal posts.length, posts_with_authors.length
posts.length.times do |i|
assert_equal posts[i].authors.length, assert_no_queries { posts_with_authors[i].authors.length }
@@ -267,8 +267,8 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
end
def test_include_polymorphic_has_many_through
- posts = Post.all.merge!(:order => 'posts.id').to_a
- posts_with_tags = Post.all.merge!(:includes => :tags, :order => 'posts.id').to_a
+ posts = Post.all.merge!(order: "posts.id").to_a
+ posts_with_tags = Post.all.merge!(includes: :tags, order: "posts.id").to_a
assert_equal posts.length, posts_with_tags.length
posts.length.times do |i|
assert_equal posts[i].tags.length, assert_no_queries { posts_with_tags[i].tags.length }
@@ -276,8 +276,8 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
end
def test_include_polymorphic_has_many
- posts = Post.all.merge!(:order => 'posts.id').to_a
- posts_with_taggings = Post.all.merge!(:includes => :taggings, :order => 'posts.id').to_a
+ posts = Post.all.merge!(order: "posts.id").to_a
+ posts_with_taggings = Post.all.merge!(includes: :taggings, order: "posts.id").to_a
assert_equal posts.length, posts_with_taggings.length
posts.length.times do |i|
assert_equal posts[i].taggings.length, assert_no_queries { posts_with_taggings[i].taggings.length }
@@ -302,7 +302,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
end
def test_has_many_array_methods_called_by_method_missing
- assert authors(:david).categories.any? { |category| category.name == 'General' }
+ assert authors(:david).categories.any? { |category| category.name == "General" }
assert_nothing_raised { authors(:david).categories.sort }
end
@@ -324,12 +324,12 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
end
def test_has_many_through_with_custom_primary_key_on_has_many_source
- assert_equal [authors(:david), authors(:bob)], posts(:thinking).authors_using_custom_pk.order('authors.id')
+ assert_equal [authors(:david), authors(:bob)], posts(:thinking).authors_using_custom_pk.order("authors.id")
end
def test_belongs_to_polymorphic_with_counter_cache
assert_equal 1, posts(:welcome)[:tags_count]
- tagging = posts(:welcome).taggings.create(:tag => tags(:general))
+ tagging = posts(:welcome).taggings.create(tag: tags(:general))
assert_equal 2, posts(:welcome, :reload)[:tags_count]
tagging.destroy
assert_equal 1, posts(:welcome, :reload)[:tags_count]
@@ -354,7 +354,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
end
assert_raise ActiveRecord::EagerLoadPolymorphicError do
- tags(:general).taggings.includes(:taggable).where('bogus_table.column = 1').references(:bogus_table).to_a
+ tags(:general).taggings.includes(:taggable).where("bogus_table.column = 1").references(:bogus_table).to_a
end
end
@@ -365,13 +365,13 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
def test_has_many_polymorphic_associations_merges_through_scope
Tag.has_many :null_taggings, -> { none }, class_name: :Tagging
- Tag.has_many :null_tagged_posts, :through => :null_taggings, :source => 'taggable', :source_type => 'Post'
+ Tag.has_many :null_tagged_posts, through: :null_taggings, source: "taggable", source_type: "Post"
assert_equal [], tags(:general).null_tagged_posts
refute_equal [], tags(:general).tagged_posts
end
def test_eager_has_many_polymorphic_with_source_type
- tag_with_include = Tag.all.merge!(:includes => :tagged_posts).find(tags(:general).id)
+ tag_with_include = Tag.all.merge!(includes: :tagged_posts).find(tags(:general).id)
desired = posts(:welcome, :thinking)
assert_no_queries do
# added sort by ID as otherwise test using JRuby was failing as array elements were in different order
@@ -381,19 +381,19 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
end
def test_has_many_through_has_many_find_all
- assert_equal comments(:greetings), authors(:david).comments.order('comments.id').to_a.first
+ assert_equal comments(:greetings), authors(:david).comments.order("comments.id").to_a.first
end
def test_has_many_through_has_many_find_all_with_custom_class
- assert_equal comments(:greetings), authors(:david).funky_comments.order('comments.id').to_a.first
+ assert_equal comments(:greetings), authors(:david).funky_comments.order("comments.id").to_a.first
end
def test_has_many_through_has_many_find_first
- assert_equal comments(:greetings), authors(:david).comments.order('comments.id').first
+ assert_equal comments(:greetings), authors(:david).comments.order("comments.id").first
end
def test_has_many_through_has_many_find_conditions
- options = { :where => "comments.#{QUOTED_TYPE}='SpecialComment'", :order => 'comments.id' }
+ options = { where: "comments.#{QUOTED_TYPE}='SpecialComment'", order: "comments.id" }
assert_equal comments(:does_it_hurt), authors(:david).comments.merge(options).first
end
@@ -402,7 +402,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
end
def test_has_many_through_polymorphic_has_one
- assert_equal Tagging.find(1,2).sort_by(&:id), authors(:david).taggings_2
+ assert_equal Tagging.find(1, 2).sort_by(&:id), authors(:david).taggings_2
end
def test_has_many_through_polymorphic_has_many
@@ -413,20 +413,20 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
author = Author.includes(:taggings).find authors(:david).id
expected_taggings = taggings(:welcome_general, :thinking_general)
assert_no_queries do
- assert_equal expected_taggings, author.taggings.distinct.sort_by(&:id)
+ assert_equal expected_taggings, author.taggings.uniq.sort_by(&:id)
end
end
def test_eager_load_has_many_through_has_many
- author = Author.all.merge!(:where => ['name = ?', 'David'], :includes => :comments, :order => 'comments.id').first
+ author = Author.all.merge!(where: ["name = ?", "David"], includes: :comments, order: "comments.id").first
SpecialComment.new; VerySpecialComment.new
assert_no_queries do
- assert_equal [1,2,3,5,6,7,8,9,10,12], author.comments.collect(&:id)
+ assert_equal [1, 2, 3, 5, 6, 7, 8, 9, 10, 12], author.comments.collect(&:id)
end
end
def test_eager_load_has_many_through_has_many_with_conditions
- post = Post.all.merge!(:includes => :invalid_tags).first
+ post = Post.all.merge!(includes: :invalid_tags).first
assert_no_queries do
post.invalid_tags
end
@@ -434,8 +434,8 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
def test_eager_belongs_to_and_has_one_not_singularized
assert_nothing_raised do
- Author.all.merge!(:includes => :author_address).first
- AuthorAddress.all.merge!(:includes => :author).first
+ Author.all.merge!(includes: :author_address).first
+ AuthorAddress.all.merge!(includes: :author).first
end
end
@@ -445,8 +445,8 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
end
def test_add_to_self_referential_has_many_through
- new_author = Author.create(:name => "Bob")
- authors(:david).author_favorites.create :favorite_author => new_author
+ new_author = Author.create(name: "Bob")
+ authors(:david).author_favorites.create favorite_author: new_author
assert_equal new_author, authors(:david).reload.favorite_authors.first
end
@@ -462,28 +462,27 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
def test_associating_unsaved_records_with_has_many_through
saved_post = posts(:thinking)
- new_tag = Tag.new(:name => "new")
+ new_tag = Tag.new(name: "new")
saved_post.tags << new_tag
assert new_tag.persisted? #consistent with habtm!
assert saved_post.persisted?
- assert saved_post.tags.include?(new_tag)
+ assert_includes saved_post.tags, new_tag
assert new_tag.persisted?
- assert saved_post.reload.tags.reload.include?(new_tag)
+ assert_includes saved_post.reload.tags.reload, new_tag
-
- new_post = Post.new(:title => "Association replacement works!", :body => "You best believe it.")
+ new_post = Post.new(title: "Association replacement works!", body: "You best believe it.")
saved_tag = tags(:general)
new_post.tags << saved_tag
assert !new_post.persisted?
assert saved_tag.persisted?
- assert new_post.tags.include?(saved_tag)
+ assert_includes new_post.tags, saved_tag
new_post.save!
assert new_post.persisted?
- assert new_post.reload.tags.reload.include?(saved_tag)
+ assert_includes new_post.reload.tags.reload, saved_tag
assert !posts(:thinking).tags.build.persisted?
assert !posts(:thinking).tags.new.persisted?
@@ -491,28 +490,28 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
def test_create_associate_when_adding_to_has_many_through
count = posts(:thinking).tags.count
- push = Tag.create!(:name => 'pushme')
+ push = Tag.create!(name: "pushme")
post_thinking = posts(:thinking)
assert_nothing_raised { post_thinking.tags << push }
- assert_nil( wrong = post_thinking.tags.detect { |t| t.class != Tag },
+ assert_nil(wrong = post_thinking.tags.detect { |t| t.class != Tag },
message = "Expected a Tag in tags collection, got #{wrong.class}.")
- assert_nil( wrong = post_thinking.taggings.detect { |t| t.class != Tagging },
+ assert_nil(wrong = post_thinking.taggings.detect { |t| t.class != Tagging },
message = "Expected a Tagging in taggings collection, got #{wrong.class}.")
assert_equal(count + 1, post_thinking.reload.tags.size)
assert_equal(count + 1, post_thinking.tags.reload.size)
- assert_kind_of Tag, post_thinking.tags.create!(:name => 'foo')
- assert_nil( wrong = post_thinking.tags.detect { |t| t.class != Tag },
+ assert_kind_of Tag, post_thinking.tags.create!(name: "foo")
+ assert_nil(wrong = post_thinking.tags.detect { |t| t.class != Tag },
message = "Expected a Tag in tags collection, got #{wrong.class}.")
- assert_nil( wrong = post_thinking.taggings.detect { |t| t.class != Tagging },
+ assert_nil(wrong = post_thinking.taggings.detect { |t| t.class != Tagging },
message = "Expected a Tagging in taggings collection, got #{wrong.class}.")
assert_equal(count + 2, post_thinking.reload.tags.size)
assert_equal(count + 2, post_thinking.tags.reload.size)
- assert_nothing_raised { post_thinking.tags.concat(Tag.create!(:name => 'abc'), Tag.create!(:name => 'def')) }
- assert_nil( wrong = post_thinking.tags.detect { |t| t.class != Tag },
+ assert_nothing_raised { post_thinking.tags.concat(Tag.create!(name: "abc"), Tag.create!(name: "def")) }
+ assert_nil(wrong = post_thinking.tags.detect { |t| t.class != Tag },
message = "Expected a Tag in tags collection, got #{wrong.class}.")
- assert_nil( wrong = post_thinking.taggings.detect { |t| t.class != Tagging },
+ assert_nil(wrong = post_thinking.taggings.detect { |t| t.class != Tagging },
message = "Expected a Tagging in taggings collection, got #{wrong.class}.")
assert_equal(count + 4, post_thinking.reload.tags.size)
assert_equal(count + 4, post_thinking.tags.reload.size)
@@ -550,7 +549,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
def test_delete_associate_when_deleting_from_has_many_through_with_nonstandard_id
count = books(:awdr).references.count
references_before = books(:awdr).references
- book = Book.create!(:name => 'Getting Real')
+ book = Book.create!(name: "Getting Real")
book_awdr = books(:awdr)
book_awdr.references << book
assert_equal(count + 1, book_awdr.references.reload.size)
@@ -564,7 +563,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
def test_delete_associate_when_deleting_from_has_many_through
count = posts(:thinking).tags.count
tags_before = posts(:thinking).tags.sort
- tag = Tag.create!(:name => 'doomed')
+ tag = Tag.create!(name: "doomed")
post_thinking = posts(:thinking)
post_thinking.tags << tag
assert_equal(count + 1, post_thinking.taggings.reload.size)
@@ -581,9 +580,9 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
def test_delete_associate_when_deleting_from_has_many_through_with_multiple_tags
count = posts(:thinking).tags.count
tags_before = posts(:thinking).tags.sort
- doomed = Tag.create!(:name => 'doomed')
- doomed2 = Tag.create!(:name => 'doomed2')
- quaked = Tag.create!(:name => 'quaked')
+ doomed = Tag.create!(name: "doomed")
+ doomed2 = Tag.create!(name: "doomed2")
+ quaked = Tag.create!(name: "quaked")
post_thinking = posts(:thinking)
post_thinking.tags << doomed << doomed2
assert_equal(count + 2, post_thinking.reload.tags.reload.size)
@@ -601,7 +600,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
def test_deleting_by_integer_id_from_has_many_through
post = posts(:thinking)
- assert_difference 'post.tags.count', -1 do
+ assert_difference "post.tags.count", -1 do
assert_equal 1, post.tags.delete(1).size
end
@@ -611,8 +610,8 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
def test_deleting_by_string_id_from_has_many_through
post = posts(:thinking)
- assert_difference 'post.tags.count', -1 do
- assert_equal 1, post.tags.delete('1').size
+ assert_difference "post.tags.count", -1 do
+ assert_equal 1, post.tags.delete("1").size
end
assert_equal 0, post.tags.size
@@ -642,26 +641,26 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
def test_polymorphic_has_many
expected = taggings(:welcome_general)
- p = Post.all.merge!(:includes => :taggings).find(posts(:welcome).id)
- assert_no_queries {assert p.taggings.include?(expected)}
- assert posts(:welcome).taggings.include?(taggings(:welcome_general))
+ p = Post.all.merge!(includes: :taggings).find(posts(:welcome).id)
+ assert_no_queries { assert_includes p.taggings, expected }
+ assert_includes posts(:welcome).taggings, taggings(:welcome_general)
end
def test_polymorphic_has_one
expected = posts(:welcome)
- tagging = Tagging.all.merge!(:includes => :taggable).find(taggings(:welcome_general).id)
- assert_no_queries { assert_equal expected, tagging.taggable}
+ tagging = Tagging.all.merge!(includes: :taggable).find(taggings(:welcome_general).id)
+ assert_no_queries { assert_equal expected, tagging.taggable }
end
def test_polymorphic_belongs_to
- p = Post.all.merge!(:includes => {:taggings => :taggable}).find(posts(:welcome).id)
- assert_no_queries {assert_equal posts(:welcome), p.taggings.first.taggable}
+ p = Post.all.merge!(includes: { taggings: :taggable }).find(posts(:welcome).id)
+ assert_no_queries { assert_equal posts(:welcome), p.taggings.first.taggable }
end
def test_preload_polymorphic_has_many_through
- posts = Post.all.merge!(:order => 'posts.id').to_a
- posts_with_tags = Post.all.merge!(:includes => :tags, :order => 'posts.id').to_a
+ posts = Post.all.merge!(order: "posts.id").to_a
+ posts_with_tags = Post.all.merge!(includes: :tags, order: "posts.id").to_a
assert_equal posts.length, posts_with_tags.length
posts.length.times do |i|
assert_equal posts[i].tags.length, assert_no_queries { posts_with_tags[i].tags.length }
@@ -669,26 +668,26 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
end
def test_preload_polymorph_many_types
- taggings = Tagging.all.merge!(:includes => :taggable, :where => ['taggable_type != ?', 'FakeModel']).to_a
+ taggings = Tagging.all.merge!(includes: :taggable, where: ["taggable_type != ?", "FakeModel"]).to_a
assert_no_queries do
taggings.first.taggable.id
taggings[1].taggable.id
end
taggables = taggings.map(&:taggable)
- assert taggables.include?(items(:dvd))
- assert taggables.include?(posts(:welcome))
+ assert_includes taggables, items(:dvd)
+ assert_includes taggables, posts(:welcome)
end
def test_preload_nil_polymorphic_belongs_to
assert_nothing_raised do
- Tagging.all.merge!(:includes => :taggable, :where => ['taggable_type IS NULL']).to_a
+ Tagging.all.merge!(includes: :taggable, where: ["taggable_type IS NULL"]).to_a
end
end
def test_preload_polymorphic_has_many
- posts = Post.all.merge!(:order => 'posts.id').to_a
- posts_with_taggings = Post.all.merge!(:includes => :taggings, :order => 'posts.id').to_a
+ posts = Post.all.merge!(order: "posts.id").to_a
+ posts_with_taggings = Post.all.merge!(includes: :taggings, order: "posts.id").to_a
assert_equal posts.length, posts_with_taggings.length
posts.length.times do |i|
assert_equal posts[i].taggings.length, assert_no_queries { posts_with_taggings[i].taggings.length }
@@ -696,7 +695,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
end
def test_belongs_to_shared_parent
- comments = Comment.all.merge!(:includes => :post, :where => 'post_id = 1').to_a
+ comments = Comment.all.merge!(includes: :post, where: "post_id = 1").to_a
assert_no_queries do
assert_equal comments.first.post, comments[1].post
end
@@ -710,7 +709,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
assert_no_queries do
assert david.categories.loaded?
- assert david.categories.include?(category)
+ assert_includes david.categories, category
end
end
@@ -721,45 +720,45 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
david.reload
assert ! david.categories.loaded?
assert_queries(1) do
- assert david.categories.include?(category)
+ assert_includes david.categories, category
end
assert ! david.categories.loaded?
end
def test_has_many_through_include_returns_false_for_non_matching_record_to_verify_scoping
david = authors(:david)
- category = Category.create!(:name => 'Not Associated')
+ category = Category.create!(name: "Not Associated")
assert ! david.categories.loaded?
assert ! david.categories.include?(category)
end
def test_has_many_through_goes_through_all_sti_classes
- sub_sti_post = SubStiPost.create!(:title => 'test', :body => 'test', :author_id => 1)
- new_comment = sub_sti_post.comments.create(:body => 'test')
+ sub_sti_post = SubStiPost.create!(title: "test", body: "test", author_id: 1)
+ new_comment = sub_sti_post.comments.create(body: "test")
assert_equal [9, 10, new_comment.id], authors(:david).sti_post_comments.map(&:id).sort
end
def test_has_many_with_pluralize_table_names_false
- aircraft = Aircraft.create!(:name => "Airbus 380")
- engine = Engine.create!(:car_id => aircraft.id)
+ aircraft = Aircraft.create!(name: "Airbus 380")
+ engine = Engine.create!(car_id: aircraft.id)
assert_equal aircraft.engines, [engine]
end
def test_proper_error_message_for_eager_load_and_includes_association_errors
includes_error = assert_raises(ActiveRecord::ConfigurationError) {
- Post.includes(:nonexistent_relation).where(nonexistent_relation: {name: 'Rochester'}).find(1)
+ Post.includes(:nonexistent_relation).where(nonexistent_relation: { name: "Rochester" }).find(1)
}
assert_equal("Can't join 'Post' to association named 'nonexistent_relation'; perhaps you misspelled it?", includes_error.message)
eager_load_error = assert_raises(ActiveRecord::ConfigurationError) {
- Post.eager_load(:nonexistent_relation).where(nonexistent_relation: {name: 'Rochester'}).find(1)
+ Post.eager_load(:nonexistent_relation).where(nonexistent_relation: { name: "Rochester" }).find(1)
}
assert_equal("Can't join 'Post' to association named 'nonexistent_relation'; perhaps you misspelled it?", eager_load_error.message)
includes_and_eager_load_error = assert_raises(ActiveRecord::ConfigurationError) {
- Post.eager_load(:nonexistent_relation).includes(:nonexistent_relation).where(nonexistent_relation: {name: 'Rochester'}).find(1)
+ Post.eager_load(:nonexistent_relation).includes(:nonexistent_relation).where(nonexistent_relation: { name: "Rochester" }).find(1)
}
assert_equal("Can't join 'Post' to association named 'nonexistent_relation'; perhaps you misspelled it?", includes_and_eager_load_error.message)
end
@@ -770,8 +769,8 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
class_name = "PostWith#{association.to_s.classify}#{dependency.to_s.classify}"
Post.find(post_id).update_columns type: class_name
klass = Object.const_set(class_name, Class.new(ActiveRecord::Base))
- klass.table_name = 'posts'
- klass.send(association, association_name, :as => :taggable, :dependent => dependency)
+ klass.table_name = "posts"
+ klass.send(association, association_name, as: :taggable, dependent: dependency)
klass.find(post_id)
end
end
diff --git a/activerecord/test/cases/associations/left_outer_join_association_test.rb b/activerecord/test/cases/associations/left_outer_join_association_test.rb
index eee135cfb8..42dbbad1c8 100644
--- a/activerecord/test/cases/associations/left_outer_join_association_test.rb
+++ b/activerecord/test/cases/associations/left_outer_join_association_test.rb
@@ -1,10 +1,10 @@
require "cases/helper"
-require 'models/post'
-require 'models/comment'
-require 'models/author'
-require 'models/essay'
-require 'models/categorization'
-require 'models/person'
+require "models/post"
+require "models/comment"
+require "models/author"
+require "models/essay"
+require "models/categorization"
+require "models/person"
class LeftOuterJoinAssociationTest < ActiveRecord::TestCase
fixtures :authors, :essays, :posts, :comments, :categorizations, :people
@@ -17,45 +17,54 @@ class LeftOuterJoinAssociationTest < ActiveRecord::TestCase
def test_construct_finder_sql_does_not_table_name_collide_on_duplicate_associations
assert_nothing_raised do
queries = capture_sql do
- Person.left_outer_joins(:agents => {:agents => :agents})
- .left_outer_joins(:agents => {:agents => {:primary_contact => :agents}}).to_a
+ Person.left_outer_joins(agents: { agents: :agents })
+ .left_outer_joins(agents: { agents: { primary_contact: :agents } }).to_a
end
- assert queries.any? { |sql| /agents_people_4/i =~ sql }
+ assert queries.any? { |sql| /agents_people_4/i.match?(sql) }
end
end
- def test_construct_finder_sql_executes_a_left_outer_join
- assert_not_equal Author.count, Author.joins(:posts).count
- assert_equal Author.count, Author.left_outer_joins(:posts).count
+ def test_left_outer_joins_count_is_same_as_size_of_loaded_results
+ assert_equal 17, Post.left_outer_joins(:comments).to_a.size
+ assert_equal 17, Post.left_outer_joins(:comments).count
end
- def test_left_outer_join_by_left_joins
- assert_not_equal Author.count, Author.joins(:posts).count
- assert_equal Author.count, Author.left_joins(:posts).count
+ def test_left_joins_aliases_left_outer_joins
+ assert_equal Post.left_outer_joins(:comments).to_sql, Post.left_joins(:comments).to_sql
+ end
+
+ def test_left_outer_joins_return_has_value_for_every_comment
+ all_post_ids = Post.pluck(:id)
+ assert_equal all_post_ids, all_post_ids & Post.left_outer_joins(:comments).pluck(:id)
+ end
+
+ def test_left_outer_joins_actually_does_a_left_outer_join
+ queries = capture_sql { Author.left_outer_joins(:posts).to_a }
+ assert queries.any? { |sql| /LEFT OUTER JOIN/i.match?(sql) }
end
def test_construct_finder_sql_ignores_empty_left_outer_joins_hash
- queries = capture_sql { Author.left_outer_joins({}) }
- assert queries.none? { |sql| /LEFT OUTER JOIN/i =~ sql }
+ queries = capture_sql { Author.left_outer_joins({}).to_a }
+ assert queries.none? { |sql| /LEFT OUTER JOIN/i.match?(sql) }
end
def test_construct_finder_sql_ignores_empty_left_outer_joins_array
- queries = capture_sql { Author.left_outer_joins([]) }
- assert queries.none? { |sql| /LEFT OUTER JOIN/i =~ sql }
+ queries = capture_sql { Author.left_outer_joins([]).to_a }
+ assert queries.none? { |sql| /LEFT OUTER JOIN/i.match?(sql) }
end
def test_left_outer_joins_forbids_to_use_string_as_argument
- assert_raise(ArgumentError){ Author.left_outer_joins('LEFT OUTER JOIN "posts" ON "posts"."user_id" = "users"."id"').to_a }
+ assert_raise(ArgumentError) { Author.left_outer_joins('LEFT OUTER JOIN "posts" ON "posts"."user_id" = "users"."id"').to_a }
end
def test_join_conditions_added_to_join_clause
queries = capture_sql { Author.left_outer_joins(:essays).to_a }
- assert queries.any? { |sql| /writer_type.*?=.*?(Author|\?|\$1|\:a1)/i =~ sql }
- assert queries.none? { |sql| /WHERE/i =~ sql }
+ assert queries.any? { |sql| /writer_type.*?=.*?(Author|\?|\$1|\:a1)/i.match?(sql) }
+ assert queries.none? { |sql| /WHERE/i.match?(sql) }
end
def test_find_with_sti_join
- scope = Post.left_outer_joins(:special_comments).where(:id => posts(:sti_comments).id)
+ scope = Post.left_outer_joins(:special_comments).where(id: posts(:sti_comments).id)
# The join should match SpecialComment and its subclasses only
assert scope.where("comments.type" => "Comment").empty?
diff --git a/activerecord/test/cases/associations/nested_through_associations_test.rb b/activerecord/test/cases/associations/nested_through_associations_test.rb
index b040485d99..dc26f6a383 100644
--- a/activerecord/test/cases/associations/nested_through_associations_test.rb
+++ b/activerecord/test/cases/associations/nested_through_associations_test.rb
@@ -1,27 +1,27 @@
require "cases/helper"
-require 'models/author'
-require 'models/post'
-require 'models/person'
-require 'models/reference'
-require 'models/job'
-require 'models/reader'
-require 'models/comment'
-require 'models/tag'
-require 'models/tagging'
-require 'models/subscriber'
-require 'models/book'
-require 'models/subscription'
-require 'models/rating'
-require 'models/member'
-require 'models/member_detail'
-require 'models/member_type'
-require 'models/sponsor'
-require 'models/club'
-require 'models/organization'
-require 'models/category'
-require 'models/categorization'
-require 'models/membership'
-require 'models/essay'
+require "models/author"
+require "models/post"
+require "models/person"
+require "models/reference"
+require "models/job"
+require "models/reader"
+require "models/comment"
+require "models/tag"
+require "models/tagging"
+require "models/subscriber"
+require "models/book"
+require "models/subscription"
+require "models/rating"
+require "models/member"
+require "models/member_detail"
+require "models/member_type"
+require "models/sponsor"
+require "models/club"
+require "models/organization"
+require "models/category"
+require "models/categorization"
+require "models/membership"
+require "models/essay"
class NestedThroughAssociationsTest < ActiveRecord::TestCase
fixtures :authors, :books, :posts, :subscriptions, :subscribers, :tags, :taggings,
@@ -65,12 +65,12 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
def test_has_many_through_has_many_with_has_many_through_source_reflection_preload_via_joins
assert_includes_and_joins_equal(
- Author.where('tags.id' => tags(:general).id),
+ Author.where("tags.id" => tags(:general).id),
[authors(:david)], :tags
)
# This ensures that the polymorphism of taggings is being observed correctly
- authors = Author.joins(:tags).where('taggings.taggable_type' => 'FakeModel')
+ authors = Author.joins(:tags).where("taggings.taggable_type" => "FakeModel")
assert authors.empty?
end
@@ -79,7 +79,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
# Through: has_many through
def test_has_many_through_has_many_through_with_has_many_source_reflection
luke, david = subscribers(:first), subscribers(:second)
- assert_equal [luke, david, david], authors(:david).subscribers.order('subscribers.nick')
+ assert_equal [luke, david, david], authors(:david).subscribers.order("subscribers.nick")
end
def test_has_many_through_has_many_through_with_has_many_source_reflection_preload
@@ -93,7 +93,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
def test_has_many_through_has_many_through_with_has_many_source_reflection_preload_via_joins
# All authors with subscribers where one of the subscribers' nick is 'alterself'
assert_includes_and_joins_equal(
- Author.where('subscribers.nick' => 'alterself'),
+ Author.where("subscribers.nick" => "alterself"),
[authors(:david)], :subscribers
)
end
@@ -115,7 +115,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
def test_has_many_through_has_one_with_has_one_through_source_reflection_preload_via_joins
assert_includes_and_joins_equal(
- Member.where('member_types.id' => member_types(:founding).id),
+ Member.where("member_types.id" => member_types(:founding).id),
[members(:groucho)], :nested_member_types
)
end
@@ -137,7 +137,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
def test_has_many_through_has_one_through_with_has_one_source_reflection_preload_via_joins
assert_includes_and_joins_equal(
- Member.where('sponsors.id' => sponsors(:moustache_club_sponsor_for_groucho).id),
+ Member.where("sponsors.id" => sponsors(:moustache_club_sponsor_for_groucho).id),
[members(:groucho)], :nested_sponsors
)
end
@@ -149,7 +149,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
groucho_details, other_details = member_details(:groucho), member_details(:some_other_guy)
assert_equal [groucho_details, other_details],
- members(:groucho).organization_member_details.order('member_details.id')
+ members(:groucho).organization_member_details.order("member_details.id")
end
def test_has_many_through_has_one_with_has_many_through_source_reflection_preload
@@ -164,12 +164,12 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
def test_has_many_through_has_one_with_has_many_through_source_reflection_preload_via_joins
assert_includes_and_joins_equal(
- Member.where('member_details.id' => member_details(:groucho).id).order('member_details.id'),
+ Member.where("member_details.id" => member_details(:groucho).id).order("member_details.id"),
[members(:groucho), members(:some_other_guy)], :organization_member_details
)
members = Member.joins(:organization_member_details).
- where('member_details.id' => 9)
+ where("member_details.id" => 9)
assert members.empty?
end
@@ -180,7 +180,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
groucho_details, other_details = member_details(:groucho), member_details(:some_other_guy)
assert_equal [groucho_details, other_details],
- members(:groucho).organization_member_details_2.order('member_details.id')
+ members(:groucho).organization_member_details_2.order("member_details.id")
end
def test_has_many_through_has_one_through_with_has_many_source_reflection_preload
@@ -196,12 +196,12 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
def test_has_many_through_has_one_through_with_has_many_source_reflection_preload_via_joins
assert_includes_and_joins_equal(
- Member.where('member_details.id' => member_details(:groucho).id).order('member_details.id'),
+ Member.where("member_details.id" => member_details(:groucho).id).order("member_details.id"),
[members(:groucho), members(:some_other_guy)], :organization_member_details_2
)
members = Member.joins(:organization_member_details_2).
- where('member_details.id' => 9)
+ where("member_details.id" => 9)
assert members.empty?
end
@@ -211,7 +211,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
def test_has_many_through_has_many_with_has_and_belongs_to_many_source_reflection
general, cooking = categories(:general), categories(:cooking)
- assert_equal [general, cooking], authors(:bob).post_categories.order('categories.id')
+ assert_equal [general, cooking], authors(:bob).post_categories.order("categories.id")
end
def test_has_many_through_has_many_with_has_and_belongs_to_many_source_reflection_preload
@@ -228,7 +228,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
Author.joins(:post_categories).first
assert_includes_and_joins_equal(
- Author.where('categories.id' => categories(:cooking).id),
+ Author.where("categories.id" => categories(:cooking).id),
[authors(:bob)], :post_categories
)
end
@@ -239,7 +239,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
def test_has_many_through_has_and_belongs_to_many_with_has_many_source_reflection
greetings, more = comments(:greetings), comments(:more_greetings)
- assert_equal [greetings, more], categories(:technology).post_comments.order('comments.id')
+ assert_equal [greetings, more], categories(:technology).post_comments.order("comments.id")
end
def test_has_many_through_has_and_belongs_to_many_with_has_many_source_reflection_preload
@@ -257,7 +257,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
Category.joins(:post_comments).first
assert_includes_and_joins_equal(
- Category.where('comments.id' => comments(:more_greetings).id).order('categories.id'),
+ Category.where("comments.id" => comments(:more_greetings).id).order("categories.id"),
[categories(:general), categories(:technology)], :post_comments
)
end
@@ -268,7 +268,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
def test_has_many_through_has_many_with_has_many_through_habtm_source_reflection
greetings, more = comments(:greetings), comments(:more_greetings)
- assert_equal [greetings, more], authors(:bob).category_post_comments.order('comments.id')
+ assert_equal [greetings, more], authors(:bob).category_post_comments.order("comments.id")
end
def test_has_many_through_has_many_with_has_many_through_habtm_source_reflection_preload
@@ -285,7 +285,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
Author.joins(:category_post_comments).first
assert_includes_and_joins_equal(
- Author.where('comments.id' => comments(:does_it_hurt).id).order('authors.id'),
+ Author.where("comments.id" => comments(:does_it_hurt).id).order("authors.id"),
[authors(:david), authors(:mary)], :category_post_comments
)
end
@@ -308,7 +308,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
def test_has_many_through_has_many_through_with_belongs_to_source_reflection_preload_via_joins
assert_includes_and_joins_equal(
- Author.where('tags.id' => tags(:general).id),
+ Author.where("tags.id" => tags(:general).id),
[authors(:david)], :tagging_tags
)
end
@@ -320,7 +320,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
welcome_general, thinking_general = taggings(:welcome_general), taggings(:thinking_general)
assert_equal [welcome_general, thinking_general],
- categorizations(:david_welcome_general).post_taggings.order('taggings.id')
+ categorizations(:david_welcome_general).post_taggings.order("taggings.id")
end
def test_has_many_through_belongs_to_with_has_many_through_source_reflection_preload
@@ -334,7 +334,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
def test_has_many_through_belongs_to_with_has_many_through_source_reflection_preload_via_joins
assert_includes_and_joins_equal(
- Categorization.where('taggings.id' => taggings(:welcome_general).id).order('taggings.id'),
+ Categorization.where("taggings.id" => taggings(:welcome_general).id).order("taggings.id"),
[categorizations(:david_welcome_general)], :post_taggings
)
end
@@ -357,7 +357,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
def test_has_one_through_has_one_with_has_one_through_source_reflection_preload_via_joins
assert_includes_and_joins_equal(
- Member.where('member_types.id' => member_types(:founding).id),
+ Member.where("member_types.id" => member_types(:founding).id),
[members(:groucho)], :nested_member_type
)
end
@@ -391,7 +391,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
def test_has_one_through_has_one_through_with_belongs_to_source_reflection_preload_via_joins
assert_includes_and_joins_equal(
- Member.where('categories.id' => categories(:technology).id),
+ Member.where("categories.id" => categories(:technology).id),
[members(:blarpy_winkup)], :club_category
)
end
@@ -404,7 +404,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
def test_distinct_has_many_through_a_has_many_through_association_on_through_reflection
author = authors(:david)
assert_equal [subscribers(:first), subscribers(:second)],
- author.distinct_subscribers.order('subscribers.nick')
+ author.distinct_subscribers.order("subscribers.nick")
end
def test_nested_has_many_through_with_a_table_referenced_multiple_times
@@ -413,26 +413,26 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
author.similar_posts.sort_by(&:id)
# Mary and Bob both have posts in misc, but they are the only ones.
- authors = Author.joins(:similar_posts).where('posts.id' => posts(:misc_by_bob).id)
+ authors = Author.joins(:similar_posts).where("posts.id" => posts(:misc_by_bob).id)
assert_equal [authors(:mary), authors(:bob)], authors.distinct.sort_by(&:id)
# Check the polymorphism of taggings is being observed correctly (in both joins)
- authors = Author.joins(:similar_posts).where('taggings.taggable_type' => 'FakeModel')
+ authors = Author.joins(:similar_posts).where("taggings.taggable_type" => "FakeModel")
assert authors.empty?
- authors = Author.joins(:similar_posts).where('taggings_authors_join.taggable_type' => 'FakeModel')
+ authors = Author.joins(:similar_posts).where("taggings_authors_join.taggable_type" => "FakeModel")
assert authors.empty?
end
def test_has_many_through_with_foreign_key_option_on_through_reflection
- assert_equal [posts(:welcome), posts(:authorless)], people(:david).agents_posts.order('posts.id')
+ assert_equal [posts(:welcome), posts(:authorless)], people(:david).agents_posts.order("posts.id")
assert_equal [authors(:david)], references(:david_unicyclist).agents_posts_authors
- references = Reference.joins(:agents_posts_authors).where('authors.id' => authors(:david).id)
+ references = Reference.joins(:agents_posts_authors).where("authors.id" => authors(:david).id)
assert_equal [references(:david_unicyclist)], references
end
def test_has_many_through_with_foreign_key_option_on_source_reflection
- assert_equal [people(:michael), people(:susan)], jobs(:unicyclist).agents.order('people.id')
+ assert_equal [people(:michael), people(:susan)], jobs(:unicyclist).agents.order("people.id")
jobs = Job.joins(:agents)
assert_equal [jobs(:unicyclist), jobs(:unicyclist)], jobs
@@ -443,7 +443,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
assert_equal [ratings(:special_comment_rating), ratings(:sub_special_comment_rating)], ratings
# Ensure STI is respected in the join
- scope = Post.joins(:special_comments_ratings).where(:id => posts(:sti_comments).id)
+ scope = Post.joins(:special_comments_ratings).where(id: posts(:sti_comments).id)
assert scope.where("comments.type" => "Comment").empty?
assert !scope.where("comments.type" => "SpecialComment").empty?
assert !scope.where("comments.type" => "SubSpecialComment").empty?
@@ -453,7 +453,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
taggings = posts(:sti_comments).special_comments_ratings_taggings
assert_equal [taggings(:special_comment_rating)], taggings
- scope = Post.joins(:special_comments_ratings_taggings).where(:id => posts(:sti_comments).id)
+ scope = Post.joins(:special_comments_ratings_taggings).where(id: posts(:sti_comments).id)
assert scope.where("comments.type" => "Comment").empty?
assert !scope.where("comments.type" => "SpecialComment").empty?
end
@@ -505,7 +505,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
end
def test_nested_has_many_through_with_conditions_on_through_associations_preload
- assert Author.where('tags.id' => 100).joins(:misc_post_first_blue_tags).empty?
+ assert Author.where("tags.id" => 100).joins(:misc_post_first_blue_tags).empty?
authors = assert_queries(3) { Author.includes(:misc_post_first_blue_tags).to_a.sort_by(&:id) }
blue = tags(:blue)
@@ -518,7 +518,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
def test_nested_has_many_through_with_conditions_on_through_associations_preload_via_joins
# Pointless condition to force single-query loading
assert_includes_and_joins_equal(
- Author.where('tags.id = tags.id').references(:tags),
+ Author.where("tags.id = tags.id").references(:tags),
[authors(:bob)], :misc_post_first_blue_tags
)
end
@@ -539,7 +539,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
def test_nested_has_many_through_with_conditions_on_source_associations_preload_via_joins
# Pointless condition to force single-query loading
assert_includes_and_joins_equal(
- Author.where('tags.id = tags.id').references(:tags),
+ Author.where("tags.id = tags.id").references(:tags),
[authors(:bob)], :misc_post_first_blue_tags_2
)
end
@@ -548,13 +548,13 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
assert_equal [categories(:general)], organizations(:nsa).author_essay_categories
organizations = Organization.joins(:author_essay_categories).
- where('categories.id' => categories(:general).id)
+ where("categories.id" => categories(:general).id)
assert_equal [organizations(:nsa)], organizations
assert_equal categories(:general), organizations(:nsa).author_owned_essay_category
organizations = Organization.joins(:author_owned_essay_category).
- where('categories.id' => categories(:general).id)
+ where("categories.id" => categories(:general).id)
assert_equal [organizations(:nsa)], organizations
end
diff --git a/activerecord/test/cases/associations/required_test.rb b/activerecord/test/cases/associations/required_test.rb
index 3e5494e897..f8b686721e 100644
--- a/activerecord/test/cases/associations/required_test.rb
+++ b/activerecord/test/cases/associations/required_test.rb
@@ -18,8 +18,8 @@ class RequiredAssociationsTest < ActiveRecord::TestCase
end
teardown do
- @connection.drop_table 'parents', if_exists: true
- @connection.drop_table 'children', if_exists: true
+ @connection.drop_table "parents", if_exists: true
+ @connection.drop_table "children", if_exists: true
end
test "belongs_to associations are not required by default" do
@@ -92,11 +92,11 @@ class RequiredAssociationsTest < ActiveRecord::TestCase
private
- def subclass_of(klass, &block)
- subclass = Class.new(klass, &block)
- def subclass.name
- superclass.name
+ def subclass_of(klass, &block)
+ subclass = Class.new(klass, &block)
+ def subclass.name
+ superclass.name
+ end
+ subclass
end
- subclass
- end
end
diff --git a/activerecord/test/cases/associations_test.rb b/activerecord/test/cases/associations_test.rb
index 01a058918a..a223b4338f 100644
--- a/activerecord/test/cases/associations_test.rb
+++ b/activerecord/test/cases/associations_test.rb
@@ -1,80 +1,77 @@
require "cases/helper"
-require 'models/computer'
-require 'models/developer'
-require 'models/project'
-require 'models/company'
-require 'models/categorization'
-require 'models/category'
-require 'models/post'
-require 'models/author'
-require 'models/comment'
-require 'models/tag'
-require 'models/tagging'
-require 'models/person'
-require 'models/reader'
-require 'models/ship_part'
-require 'models/ship'
-require 'models/liquid'
-require 'models/molecule'
-require 'models/electron'
-require 'models/man'
-require 'models/interest'
+require "models/computer"
+require "models/developer"
+require "models/project"
+require "models/company"
+require "models/categorization"
+require "models/category"
+require "models/post"
+require "models/author"
+require "models/comment"
+require "models/tag"
+require "models/tagging"
+require "models/person"
+require "models/reader"
+require "models/ship_part"
+require "models/ship"
+require "models/liquid"
+require "models/molecule"
+require "models/electron"
+require "models/man"
+require "models/interest"
class AssociationsTest < ActiveRecord::TestCase
fixtures :accounts, :companies, :developers, :projects, :developers_projects,
:computers, :people, :readers, :authors, :author_favorites
def test_eager_loading_should_not_change_count_of_children
- liquid = Liquid.create(:name => 'salty')
- molecule = liquid.molecules.create(:name => 'molecule_1')
- molecule.electrons.create(:name => 'electron_1')
- molecule.electrons.create(:name => 'electron_2')
+ liquid = Liquid.create(name: "salty")
+ molecule = liquid.molecules.create(name: "molecule_1")
+ molecule.electrons.create(name: "electron_1")
+ molecule.electrons.create(name: "electron_2")
- liquids = Liquid.includes(:molecules => :electrons).references(:molecules).where('molecules.id is not null')
+ liquids = Liquid.includes(molecules: :electrons).references(:molecules).where("molecules.id is not null")
assert_equal 1, liquids[0].molecules.length
end
def test_subselect
author = authors :david
favs = author.author_favorites
- fav2 = author.author_favorites.where(:author => Author.where(id: author.id)).to_a
+ fav2 = author.author_favorites.where(author: Author.where(id: author.id)).to_a
assert_equal favs, fav2
end
def test_loading_the_association_target_should_keep_child_records_marked_for_destruction
- ship = Ship.create!(:name => "The good ship Dollypop")
- part = ship.parts.create!(:name => "Mast")
+ ship = Ship.create!(name: "The good ship Dollypop")
+ part = ship.parts.create!(name: "Mast")
part.mark_for_destruction
- ship.parts.send(:load_target)
assert ship.parts[0].marked_for_destruction?
end
def test_loading_the_association_target_should_load_most_recent_attributes_for_child_records_marked_for_destruction
- ship = Ship.create!(:name => "The good ship Dollypop")
- part = ship.parts.create!(:name => "Mast")
+ ship = Ship.create!(name: "The good ship Dollypop")
+ part = ship.parts.create!(name: "Mast")
part.mark_for_destruction
- ShipPart.find(part.id).update_columns(name: 'Deck')
- ship.parts.send(:load_target)
- assert_equal 'Deck', ship.parts[0].name
+ ShipPart.find(part.id).update_columns(name: "Deck")
+ assert_equal "Deck", ship.parts[0].name
end
-
def test_include_with_order_works
- assert_nothing_raised {Account.all.merge!(:order => 'id', :includes => :firm).first}
- assert_nothing_raised {Account.all.merge!(:order => :id, :includes => :firm).first}
+ assert_nothing_raised { Account.all.merge!(order: "id", includes: :firm).first }
+ assert_nothing_raised { Account.all.merge!(order: :id, includes: :firm).first }
end
def test_bad_collection_keys
- assert_raise(ArgumentError, 'ActiveRecord should have barked on bad collection keys') do
- Class.new(ActiveRecord::Base).has_many(:wheels, :name => 'wheels')
+ assert_raise(ArgumentError, "ActiveRecord should have barked on bad collection keys") do
+ Class.new(ActiveRecord::Base).has_many(:wheels, name: "wheels")
end
end
def test_should_construct_new_finder_sql_after_create
- person = Person.new :first_name => 'clark'
+ person = Person.new first_name: "clark"
assert_equal [], person.readers.to_a
person.save!
- reader = Reader.create! :person => person, :post => Post.new(:title => "foo", :body => "bar")
+ reader = Reader.create! person: person, post: Post.new(title: "foo", body: "bar")
assert person.readers.find(reader.id)
end
@@ -91,10 +88,10 @@ class AssociationsTest < ActiveRecord::TestCase
assert firm.clients.empty?, "New firm should have cached no client objects"
assert_equal 0, firm.clients.size, "New firm should have cached 0 clients count"
- ActiveSupport::Deprecation.silence do
- assert !firm.clients(true).empty?, "New firm should have reloaded client objects"
- assert_equal 1, firm.clients(true).size, "New firm should have reloaded clients count"
- end
+ firm.clients.reload
+
+ assert !firm.clients.empty?, "New firm should have reloaded client objects"
+ assert_equal 1, firm.clients.size, "New firm should have reloaded clients count"
end
def test_using_limitable_reflections_helper
@@ -107,24 +104,10 @@ class AssociationsTest < ActiveRecord::TestCase
assert !using_limitable_reflections.call(mixed_reflections), "No collection associations (has many style) should pass"
end
- def test_force_reload_is_uncached
- firm = Firm.create!("name" => "A New Firm, Inc")
- Client.create!("name" => "TheClient.com", :firm => firm)
-
- ActiveSupport::Deprecation.silence do
- ActiveRecord::Base.cache do
- firm.clients.each {}
- assert_queries(0) { assert_not_nil firm.clients.each {} }
- assert_queries(1) { assert_not_nil firm.clients(true).each {} }
- end
- end
- end
-
def test_association_with_references
firm = companies(:first_firm)
- assert_includes firm.association_with_references.references_values, 'foo'
+ assert_includes firm.association_with_references.references_values, "foo"
end
-
end
class AssociationProxyTest < ActiveRecord::TestCase
@@ -133,9 +116,9 @@ class AssociationProxyTest < ActiveRecord::TestCase
def test_push_does_not_load_target
david = authors(:david)
- david.posts << (post = Post.new(:title => "New on Edge", :body => "More cool stuff!"))
+ david.posts << (post = Post.new(title: "New on Edge", body: "More cool stuff!"))
assert !david.posts.loaded?
- assert david.posts.include?(post)
+ assert_includes david.posts, post
end
def test_push_has_many_through_does_not_load_target
@@ -143,35 +126,35 @@ class AssociationProxyTest < ActiveRecord::TestCase
david.categories << categories(:technology)
assert !david.categories.loaded?
- assert david.categories.include?(categories(:technology))
+ assert_includes david.categories, categories(:technology)
end
def test_push_followed_by_save_does_not_load_target
david = authors(:david)
- david.posts << (post = Post.new(:title => "New on Edge", :body => "More cool stuff!"))
+ david.posts << (post = Post.new(title: "New on Edge", body: "More cool stuff!"))
assert !david.posts.loaded?
david.save
assert !david.posts.loaded?
- assert david.posts.include?(post)
+ assert_includes david.posts, post
end
def test_push_does_not_lose_additions_to_new_record
- josh = Author.new(:name => "Josh")
- josh.posts << Post.new(:title => "New on Edge", :body => "More cool stuff!")
+ josh = Author.new(name: "Josh")
+ josh.posts << Post.new(title: "New on Edge", body: "More cool stuff!")
assert josh.posts.loaded?
assert_equal 1, josh.posts.size
end
def test_append_behaves_like_push
- josh = Author.new(:name => "Josh")
- josh.posts.append Post.new(:title => "New on Edge", :body => "More cool stuff!")
+ josh = Author.new(name: "Josh")
+ josh.posts.append Post.new(title: "New on Edge", body: "More cool stuff!")
assert josh.posts.loaded?
assert_equal 1, josh.posts.size
end
def test_prepend_is_not_defined
- josh = Author.new(:name => "Josh")
+ josh = Author.new(name: "Josh")
assert_raises(NoMethodError) { josh.posts.prepend Post.new }
end
@@ -183,25 +166,33 @@ class AssociationProxyTest < ActiveRecord::TestCase
assert !david.projects.loaded?
end
+ def test_load_does_load_target
+ david = developers(:david)
+
+ assert !david.projects.loaded?
+ david.projects.load
+ assert david.projects.loaded?
+ end
+
def test_inspect_does_not_reload_a_not_yet_loaded_target
- andreas = Developer.new :name => 'Andreas', :log => 'new developer added'
+ andreas = Developer.new name: "Andreas", log: "new developer added"
assert !andreas.audit_logs.loaded?
assert_match(/message: "new developer added"/, andreas.audit_logs.inspect)
end
def test_save_on_parent_saves_children
- developer = Developer.create :name => "Bryan", :salary => 50_000
+ developer = Developer.create name: "Bryan", salary: 50_000
assert_equal 1, developer.reload.audit_logs.size
end
def test_create_via_association_with_block
- post = authors(:david).posts.create(:title => "New on Edge") {|p| p.body = "More cool stuff!"}
+ post = authors(:david).posts.create(title: "New on Edge") { |p| p.body = "More cool stuff!" }
assert_equal post.title, "New on Edge"
assert_equal post.body, "More cool stuff!"
end
def test_create_with_bang_via_association_with_block
- post = authors(:david).posts.create!(:title => "New on Edge") {|p| p.body = "More cool stuff!"}
+ post = authors(:david).posts.create!(title: "New on Edge") { |p| p.body = "More cool stuff!" }
assert_equal post.title, "New on Edge"
assert_equal post.body, "More cool stuff!"
end
@@ -219,7 +210,7 @@ class AssociationProxyTest < ActiveRecord::TestCase
end
def test_scoped_allows_conditions
- assert developers(:david).projects.merge(where: 'foo').to_sql.include?('foo')
+ assert developers(:david).projects.merge(where: "foo").to_sql.include?("foo")
end
test "getting a scope from an association" do
@@ -248,6 +239,15 @@ class AssociationProxyTest < ActiveRecord::TestCase
test "first! works on loaded associations" do
david = authors(:david)
assert_equal david.posts.first, david.posts.reload.first!
+ assert david.posts.loaded?
+ assert_no_queries { david.posts.first! }
+ end
+
+ def test_pluck_uses_loaded_target
+ david = authors(:david)
+ assert_equal david.posts.pluck(:title), david.posts.load.pluck(:title)
+ assert david.posts.loaded?
+ assert_no_queries { david.posts.pluck(:title) }
end
def test_reset_unloads_target
@@ -264,18 +264,18 @@ class OverridingAssociationsTest < ActiveRecord::TestCase
class DifferentPerson < ActiveRecord::Base; end
class PeopleList < ActiveRecord::Base
- has_and_belongs_to_many :has_and_belongs_to_many, :before_add => :enlist
- has_many :has_many, :before_add => :enlist
+ has_and_belongs_to_many :has_and_belongs_to_many, before_add: :enlist
+ has_many :has_many, before_add: :enlist
belongs_to :belongs_to
has_one :has_one
end
class DifferentPeopleList < PeopleList
# Different association with the same name, callbacks should be omitted here.
- has_and_belongs_to_many :has_and_belongs_to_many, :class_name => 'DifferentPerson'
- has_many :has_many, :class_name => 'DifferentPerson'
- belongs_to :belongs_to, :class_name => 'DifferentPerson'
- has_one :has_one, :class_name => 'DifferentPerson'
+ has_and_belongs_to_many :has_and_belongs_to_many, class_name: "DifferentPerson"
+ has_many :has_many, class_name: "DifferentPerson"
+ belongs_to :belongs_to, class_name: "DifferentPerson"
+ has_one :has_one, class_name: "DifferentPerson"
end
def test_habtm_association_redefinition_callbacks_should_differ_and_not_inherited
diff --git a/activerecord/test/cases/attribute_decorators_test.rb b/activerecord/test/cases/attribute_decorators_test.rb
index 2aeb2601c2..cfa6ed1da6 100644
--- a/activerecord/test/cases/attribute_decorators_test.rb
+++ b/activerecord/test/cases/attribute_decorators_test.rb
@@ -1,9 +1,9 @@
-require 'cases/helper'
+require "cases/helper"
module ActiveRecord
class AttributeDecoratorsTest < ActiveRecord::TestCase
class Model < ActiveRecord::Base
- self.table_name = 'attribute_decorators_model'
+ self.table_name = "attribute_decorators_model"
end
class StringDecorator < SimpleDelegator
@@ -28,19 +28,19 @@ module ActiveRecord
teardown do
return unless @connection
- @connection.drop_table 'attribute_decorators_model', if_exists: true
+ @connection.drop_table "attribute_decorators_model", if_exists: true
Model.attribute_type_decorations.clear
Model.reset_column_information
end
test "attributes can be decorated" do
- model = Model.new(a_string: 'Hello')
- assert_equal 'Hello', model.a_string
+ model = Model.new(a_string: "Hello")
+ assert_equal "Hello", model.a_string
Model.decorate_attribute_type(:a_string, :test) { |t| StringDecorator.new(t) }
- model = Model.new(a_string: 'Hello')
- assert_equal 'Hello decorated!', model.a_string
+ model = Model.new(a_string: "Hello")
+ assert_equal "Hello decorated!", model.a_string
end
test "decoration does not eagerly load existing columns" do
@@ -51,54 +51,54 @@ module ActiveRecord
end
test "undecorated columns are not touched" do
- Model.attribute :another_string, :string, default: 'something or other'
+ Model.attribute :another_string, :string, default: "something or other"
Model.decorate_attribute_type(:a_string, :test) { |t| StringDecorator.new(t) }
- assert_equal 'something or other', Model.new.another_string
+ assert_equal "something or other", Model.new.another_string
end
test "decorators can be chained" do
Model.decorate_attribute_type(:a_string, :test) { |t| StringDecorator.new(t) }
Model.decorate_attribute_type(:a_string, :other) { |t| StringDecorator.new(t) }
- model = Model.new(a_string: 'Hello!')
+ model = Model.new(a_string: "Hello!")
- assert_equal 'Hello! decorated! decorated!', model.a_string
+ assert_equal "Hello! decorated! decorated!", model.a_string
end
test "decoration of the same type multiple times is idempotent" do
Model.decorate_attribute_type(:a_string, :test) { |t| StringDecorator.new(t) }
Model.decorate_attribute_type(:a_string, :test) { |t| StringDecorator.new(t) }
- model = Model.new(a_string: 'Hello')
- assert_equal 'Hello decorated!', model.a_string
+ model = Model.new(a_string: "Hello")
+ assert_equal "Hello decorated!", model.a_string
end
test "decorations occur in order of declaration" do
Model.decorate_attribute_type(:a_string, :test) { |t| StringDecorator.new(t) }
Model.decorate_attribute_type(:a_string, :other) do |type|
- StringDecorator.new(type, 'decorated again!')
+ StringDecorator.new(type, "decorated again!")
end
- model = Model.new(a_string: 'Hello!')
+ model = Model.new(a_string: "Hello!")
- assert_equal 'Hello! decorated! decorated again!', model.a_string
+ assert_equal "Hello! decorated! decorated again!", model.a_string
end
test "decorating attributes does not modify parent classes" do
- Model.attribute :another_string, :string, default: 'whatever'
+ Model.attribute :another_string, :string, default: "whatever"
Model.decorate_attribute_type(:a_string, :test) { |t| StringDecorator.new(t) }
child_class = Class.new(Model)
child_class.decorate_attribute_type(:another_string, :test) { |t| StringDecorator.new(t) }
child_class.decorate_attribute_type(:a_string, :other) { |t| StringDecorator.new(t) }
- model = Model.new(a_string: 'Hello!')
- child = child_class.new(a_string: 'Hello!')
+ model = Model.new(a_string: "Hello!")
+ child = child_class.new(a_string: "Hello!")
- assert_equal 'Hello! decorated!', model.a_string
- assert_equal 'whatever', model.another_string
- assert_equal 'Hello! decorated! decorated!', child.a_string
- assert_equal 'whatever decorated!', child.another_string
+ assert_equal "Hello! decorated!", model.a_string
+ assert_equal "whatever", model.another_string
+ assert_equal "Hello! decorated! decorated!", child.a_string
+ assert_equal "whatever decorated!", child.another_string
end
class Multiplier < SimpleDelegator
@@ -116,9 +116,9 @@ module ActiveRecord
Multiplier.new(type)
end
- model = Model.new(a_string: 'whatever', an_int: 1)
+ model = Model.new(a_string: "whatever", an_int: 1)
- assert_equal 'whatever', model.a_string
+ assert_equal "whatever", model.a_string
assert_equal 2, model.an_int
end
end
diff --git a/activerecord/test/cases/attribute_methods/read_test.rb b/activerecord/test/cases/attribute_methods/read_test.rb
index 74e556211b..978dd93fa4 100644
--- a/activerecord/test/cases/attribute_methods/read_test.rb
+++ b/activerecord/test/cases/attribute_methods/read_test.rb
@@ -1,5 +1,4 @@
require "cases/helper"
-require 'thread'
module ActiveRecord
module AttributeMethods
@@ -15,6 +14,7 @@ module ActiveRecord
def self.decorate_matching_attribute_types(*); end
def self.initialize_generated_modules; end
+ include ActiveRecord::DefineCallbacks
include ActiveRecord::AttributeMethods
def self.attribute_names
@@ -40,13 +40,13 @@ module ActiveRecord
instance = @klass.new
@klass.attribute_names.each do |name|
- assert !instance.methods.map(&:to_s).include?(name)
+ assert_not_includes instance.methods.map(&:to_s), name
end
@klass.define_attribute_methods
@klass.attribute_names.each do |name|
- assert instance.methods.map(&:to_s).include?(name), "#{name} is not defined"
+ assert_includes instance.methods.map(&:to_s), name, "#{name} is not defined"
end
end
diff --git a/activerecord/test/cases/attribute_methods_test.rb b/activerecord/test/cases/attribute_methods_test.rb
index 04126e87e4..3dc0c0ce53 100644
--- a/activerecord/test/cases/attribute_methods_test.rb
+++ b/activerecord/test/cases/attribute_methods_test.rb
@@ -1,15 +1,15 @@
require "cases/helper"
-require 'models/minimalistic'
-require 'models/developer'
-require 'models/auto_id'
-require 'models/boolean'
-require 'models/computer'
-require 'models/topic'
-require 'models/company'
-require 'models/category'
-require 'models/reply'
-require 'models/contact'
-require 'models/keyboard'
+require "models/minimalistic"
+require "models/developer"
+require "models/auto_id"
+require "models/boolean"
+require "models/computer"
+require "models/topic"
+require "models/company"
+require "models/category"
+require "models/reply"
+require "models/contact"
+require "models/keyboard"
class AttributeMethodsTest < ActiveRecord::TestCase
include InTimeZone
@@ -19,7 +19,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
def setup
@old_matchers = ActiveRecord::Base.send(:attribute_method_matchers).dup
@target = Class.new(ActiveRecord::Base)
- @target.table_name = 'topics'
+ @target.table_name = "topics"
end
teardown do
@@ -27,34 +27,34 @@ class AttributeMethodsTest < ActiveRecord::TestCase
ActiveRecord::Base.send(:attribute_method_matchers).concat(@old_matchers)
end
- def test_attribute_for_inspect_string
+ test "attribute_for_inspect with a string" do
t = topics(:first)
t.title = "The First Topic Now Has A Title With\nNewlines And More Than 50 Characters"
assert_equal '"The First Topic Now Has A Title With\nNewlines And ..."', t.attribute_for_inspect(:title)
end
- def test_attribute_for_inspect_date
+ test "attribute_for_inspect with a date" do
t = topics(:first)
assert_equal %("#{t.written_on.to_s(:db)}"), t.attribute_for_inspect(:written_on)
end
- def test_attribute_for_inspect_array
+ test "attribute_for_inspect with an array" do
t = topics(:first)
t.content = [Object.new]
assert_match %r(\[#<Object:0x[0-9a-f]+>\]), t.attribute_for_inspect(:content)
end
- def test_attribute_for_inspect_long_array
+ test "attribute_for_inspect with a long array" do
t = topics(:first)
t.content = (1..11).to_a
assert_equal "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]", t.attribute_for_inspect(:content)
end
- def test_attribute_present
+ test "attribute_present" do
t = Topic.new
t.title = "hello there!"
t.written_on = Time.now
@@ -65,7 +65,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
assert !t.attribute_present?("author_name")
end
- def test_attribute_present_with_booleans
+ test "attribute_present with booleans" do
b1 = Boolean.new
b1.value = false
assert b1.attribute_present?(:value)
@@ -83,44 +83,44 @@ class AttributeMethodsTest < ActiveRecord::TestCase
assert Boolean.find(b4.id).attribute_present?(:value)
end
- def test_caching_nil_primary_key
+ test "caching a nil primary key" do
klass = Class.new(Minimalistic)
assert_called(klass, :reset_primary_key, returns: nil) do
2.times { klass.primary_key }
end
end
- def test_attribute_keys_on_new_instance
+ test "attribute keys on a new instance" do
t = Topic.new
- assert_equal nil, t.title, "The topics table has a title column, so it should be nil"
+ assert_nil t.title, "The topics table has a title column, so it should be nil"
assert_raise(NoMethodError) { t.title2 }
end
- def test_boolean_attributes
+ test "boolean attributes" do
assert !Topic.find(1).approved?
assert Topic.find(2).approved?
end
- def test_set_attributes
+ test "set attributes" do
topic = Topic.find(1)
- topic.attributes = { "title" => "Budget", "author_name" => "Jason" }
+ topic.attributes = { title: "Budget", author_name: "Jason" }
topic.save
assert_equal("Budget", topic.title)
assert_equal("Jason", topic.author_name)
assert_equal(topics(:first).author_email_address, Topic.find(1).author_email_address)
end
- def test_set_attributes_without_hash
+ test "set attributes without a hash" do
topic = Topic.new
- assert_raise(ArgumentError) { topic.attributes = '' }
+ assert_raise(ArgumentError) { topic.attributes = "" }
end
- def test_integers_as_nil
- test = AutoId.create('value' => '')
+ test "integers as nil" do
+ test = AutoId.create(value: "")
assert_nil AutoId.find(test.id).value
end
- def test_set_attributes_with_block
+ test "set attributes with a block" do
topic = Topic.new do |t|
t.title = "Budget"
t.author_name = "Jason"
@@ -130,7 +130,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
assert_equal("Jason", topic.author_name)
end
- def test_respond_to?
+ test "respond_to?" do
topic = Topic.find(1)
assert_respond_to topic, "title"
assert_respond_to topic, "title?"
@@ -144,27 +144,27 @@ class AttributeMethodsTest < ActiveRecord::TestCase
assert !topic.respond_to?(:nothingness)
end
- def test_respond_to_with_custom_primary_key
+ test "respond_to? with a custom primary key" do
keyboard = Keyboard.create
assert_not_nil keyboard.key_number
assert_equal keyboard.key_number, keyboard.id
- assert keyboard.respond_to?('key_number')
- assert keyboard.respond_to?('id')
+ assert keyboard.respond_to?("key_number")
+ assert keyboard.respond_to?("id")
end
- def test_id_before_type_cast_with_custom_primary_key
+ test "id_before_type_cast with a custom primary key" do
keyboard = Keyboard.create
- keyboard.key_number = '10'
- assert_equal '10', keyboard.id_before_type_cast
- assert_equal nil, keyboard.read_attribute_before_type_cast('id')
- assert_equal '10', keyboard.read_attribute_before_type_cast('key_number')
- assert_equal '10', keyboard.read_attribute_before_type_cast(:key_number)
+ keyboard.key_number = "10"
+ assert_equal "10", keyboard.id_before_type_cast
+ assert_nil keyboard.read_attribute_before_type_cast("id")
+ assert_equal "10", keyboard.read_attribute_before_type_cast("key_number")
+ assert_equal "10", keyboard.read_attribute_before_type_cast(:key_number)
end
- # Syck calls respond_to? before actually calling initialize
- def test_respond_to_with_allocated_object
+ # Syck calls respond_to? before actually calling initialize.
+ test "respond_to? with an allocated object" do
klass = Class.new(ActiveRecord::Base) do
- self.table_name = 'topics'
+ self.table_name = "topics"
end
topic = klass.allocate
@@ -174,31 +174,32 @@ class AttributeMethodsTest < ActiveRecord::TestCase
assert_respond_to topic, :title
end
- # IRB inspects the return value of "MyModel.allocate".
- def test_allocated_object_can_be_inspected
+ # IRB inspects the return value of MyModel.allocate.
+ test "allocated objects can be inspected" do
topic = Topic.allocate
assert_equal "#<Topic not initialized>", topic.inspect
end
- def test_array_content
+ test "array content" do
+ content = %w( one two three )
topic = Topic.new
- topic.content = %w( one two three )
+ topic.content = content
topic.save
- assert_equal(%w( one two three ), Topic.find(topic.id).content)
+ assert_equal content, Topic.find(topic.id).content
end
- def test_read_attributes_before_type_cast
- category = Category.new({:name=>"Test category", :type => nil})
- category_attrs = {"name"=>"Test category", "id" => nil, "type" => nil, "categorizations_count" => nil}
- assert_equal category_attrs , category.attributes_before_type_cast
+ test "read attributes_before_type_cast" do
+ category = Category.new(name: "Test category", type: nil)
+ category_attrs = { "name" => "Test category", "id" => nil, "type" => nil, "categorizations_count" => nil }
+ assert_equal category_attrs, category.attributes_before_type_cast
end
if current_adapter?(:Mysql2Adapter)
- def test_read_attributes_before_type_cast_on_boolean
- bool = Boolean.create!({ "value" => false })
- if RUBY_PLATFORM =~ /java/
- # JRuby will return the value before typecast as string
+ test "read attributes_before_type_cast on a boolean" do
+ bool = Boolean.create!("value" => false)
+ if RUBY_PLATFORM.include?("java")
+ # JRuby will return the value before typecast as string.
assert_equal "0", bool.reload.attributes_before_type_cast["value"]
else
assert_equal 0, bool.reload.attributes_before_type_cast["value"]
@@ -206,13 +207,13 @@ class AttributeMethodsTest < ActiveRecord::TestCase
end
end
- def test_read_attributes_before_type_cast_on_datetime
+ test "read attributes_before_type_cast on a datetime" do
in_time_zone "Pacific Time (US & Canada)" do
record = @target.new
record.written_on = "345643456"
assert_equal "345643456", record.written_on_before_type_cast
- assert_equal nil, record.written_on
+ assert_nil record.written_on
record.written_on = "2009-10-11 12:13:14"
assert_equal "2009-10-11 12:13:14", record.written_on_before_type_cast
@@ -221,7 +222,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
end
end
- def test_read_attributes_after_type_cast_on_datetime
+ test "read attributes_after_type_cast on a date" do
tz = "Pacific Time (US & Canada)"
in_time_zone tz do
@@ -242,7 +243,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
end
end
- def test_hash_content
+ test "hash content" do
topic = Topic.new
topic.content = { "one" => 1, "two" => 2 }
topic.save
@@ -256,7 +257,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
assert_equal 3, Topic.find(topic.id).content["three"]
end
- def test_update_array_content
+ test "update array content" do
topic = Topic.new
topic.content = %w( one two three )
@@ -270,40 +271,40 @@ class AttributeMethodsTest < ActiveRecord::TestCase
assert_equal(%w( one two three four five ), topic.content)
end
- def test_case_sensitive_attributes_hash
- # DB2 is not case-sensitive
+ test "case-sensitive attributes hash" do
+ # DB2 is not case-sensitive.
return true if current_adapter?(:DB2Adapter)
- assert_equal @loaded_fixtures['computers']['workstation'].to_hash, Computer.first.attributes
+ assert_equal @loaded_fixtures["computers"]["workstation"].to_hash, Computer.first.attributes
end
- def test_attributes_without_primary_key
+ test "attributes without primary key" do
klass = Class.new(ActiveRecord::Base) do
- self.table_name = 'developers_projects'
+ self.table_name = "developers_projects"
end
assert_equal klass.column_names, klass.new.attributes.keys
- assert_not klass.new.has_attribute?('id')
+ assert_not klass.new.has_attribute?("id")
end
- def test_hashes_not_mangled
- new_topic = { :title => "New Topic" }
- new_topic_values = { :title => "AnotherTopic" }
+ test "hashes are not mangled" do
+ new_topic = { title: "New Topic" }
+ new_topic_values = { title: "AnotherTopic" }
topic = Topic.new(new_topic)
assert_equal new_topic[:title], topic.title
- topic.attributes= new_topic_values
+ topic.attributes = new_topic_values
assert_equal new_topic_values[:title], topic.title
end
- def test_create_through_factory
- topic = Topic.create("title" => "New Topic")
+ test "create through factory" do
+ topic = Topic.create(title: "New Topic")
topicReloaded = Topic.find(topic.id)
assert_equal(topic, topicReloaded)
end
- def test_write_attribute
+ test "write_attribute" do
topic = Topic.new
topic.send(:write_attribute, :title, "Still another topic")
assert_equal "Still another topic", topic.title
@@ -318,7 +319,14 @@ class AttributeMethodsTest < ActiveRecord::TestCase
assert_equal "Still another topic: part 4", topic.title
end
- def test_read_attribute
+ test "write_attribute can write aliased attributes as well" do
+ topic = Topic.new(title: "Don't change the topic")
+ topic.write_attribute :heading, "New topic"
+
+ assert_equal "New topic", topic.title
+ end
+
+ test "read_attribute" do
topic = Topic.new
topic.title = "Don't change the topic"
assert_equal "Don't change the topic", topic.read_attribute("title")
@@ -328,15 +336,25 @@ class AttributeMethodsTest < ActiveRecord::TestCase
assert_equal "Don't change the topic", topic[:title]
end
- def test_read_attribute_raises_missing_attribute_error_when_not_exists
- computer = Computer.select('id').first
+ test "read_attribute can read aliased attributes as well" do
+ topic = Topic.new(title: "Don't change the topic")
+
+ assert_equal "Don't change the topic", topic.read_attribute("heading")
+ assert_equal "Don't change the topic", topic["heading"]
+
+ assert_equal "Don't change the topic", topic.read_attribute(:heading)
+ assert_equal "Don't change the topic", topic[:heading]
+ end
+
+ test "read_attribute raises ActiveModel::MissingAttributeError when the attribute does not exist" do
+ computer = Computer.select("id").first
assert_raises(ActiveModel::MissingAttributeError) { computer[:developer] }
assert_raises(ActiveModel::MissingAttributeError) { computer[:extendedWarranty] }
- assert_raises(ActiveModel::MissingAttributeError) { computer[:no_column_exists] = 'Hello!' }
- assert_nothing_raised { computer[:developer] = 'Hello!' }
+ assert_raises(ActiveModel::MissingAttributeError) { computer[:no_column_exists] = "Hello!" }
+ assert_nothing_raised { computer[:developer] = "Hello!" }
end
- def test_read_attribute_when_false
+ test "read_attribute when false" do
topic = topics(:first)
topic.approved = false
assert !topic.approved?, "approved should be false"
@@ -344,7 +362,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
assert !topic.approved?, "approved should be false"
end
- def test_read_attribute_when_true
+ test "read_attribute when true" do
topic = topics(:first)
topic.approved = true
assert topic.approved?, "approved should be true"
@@ -352,7 +370,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
assert topic.approved?, "approved should be true"
end
- def test_read_write_boolean_attribute
+ test "boolean attributes writing and reading" do
topic = Topic.new
topic.approved = "false"
assert !topic.approved?, "approved should be false"
@@ -367,7 +385,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
assert topic.approved?, "approved should be true"
end
- def test_overridden_write_attribute
+ test "overridden write_attribute" do
topic = Topic.new
def topic.write_attribute(attr_name, value)
super(attr_name, value.downcase)
@@ -386,7 +404,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
assert_equal "yet another topic: part 4", topic.title
end
- def test_overridden_read_attribute
+ test "overridden read_attribute" do
topic = Topic.new
topic.title = "Stop changing the topic"
def topic.read_attribute(attr_name)
@@ -400,40 +418,40 @@ class AttributeMethodsTest < ActiveRecord::TestCase
assert_equal "STOP CHANGING THE TOPIC", topic[:title]
end
- def test_read_overridden_attribute
- topic = Topic.new(:title => 'a')
- def topic.title() 'b' end
- assert_equal 'a', topic[:title]
+ test "read overridden attribute" do
+ topic = Topic.new(title: "a")
+ def topic.title() "b" end
+ assert_equal "a", topic[:title]
end
- def test_query_attribute_string
+ test "string attribute predicate" do
[nil, "", " "].each do |value|
- assert_equal false, Topic.new(:author_name => value).author_name?
+ assert_equal false, Topic.new(author_name: value).author_name?
end
- assert_equal true, Topic.new(:author_name => "Name").author_name?
+ assert_equal true, Topic.new(author_name: "Name").author_name?
end
- def test_query_attribute_number
+ test "number attribute predicate" do
[nil, 0, "0"].each do |value|
- assert_equal false, Developer.new(:salary => value).salary?
+ assert_equal false, Developer.new(salary: value).salary?
end
- assert_equal true, Developer.new(:salary => 1).salary?
- assert_equal true, Developer.new(:salary => "1").salary?
+ assert_equal true, Developer.new(salary: 1).salary?
+ assert_equal true, Developer.new(salary: "1").salary?
end
- def test_query_attribute_boolean
+ test "boolean attribute predicate" do
[nil, "", false, "false", "f", 0].each do |value|
- assert_equal false, Topic.new(:approved => value).approved?
+ assert_equal false, Topic.new(approved: value).approved?
end
[true, "true", "1", 1].each do |value|
- assert_equal true, Topic.new(:approved => value).approved?
+ assert_equal true, Topic.new(approved: value).approved?
end
end
- def test_query_attribute_with_custom_fields
+ test "custom field attribute predicate" do
object = Company.find_by_sql(<<-SQL).first
SELECT c1.*, c2.type as string_value, c2.rating as int_value
FROM companies c1, companies c2
@@ -454,95 +472,95 @@ class AttributeMethodsTest < ActiveRecord::TestCase
assert !object.int_value?
end
- def test_non_attribute_access_and_assignment
+ test "non-attribute read and write" do
topic = Topic.new
assert !topic.respond_to?("mumbo")
assert_raise(NoMethodError) { topic.mumbo }
assert_raise(NoMethodError) { topic.mumbo = 5 }
end
- def test_undeclared_attribute_method_does_not_affect_respond_to_and_method_missing
- topic = @target.new(:title => 'Budget')
- assert topic.respond_to?('title')
- assert_equal 'Budget', topic.title
- assert !topic.respond_to?('title_hello_world')
+ test "undeclared attribute method does not affect respond_to? and method_missing" do
+ topic = @target.new(title: "Budget")
+ assert topic.respond_to?("title")
+ assert_equal "Budget", topic.title
+ assert !topic.respond_to?("title_hello_world")
assert_raise(NoMethodError) { topic.title_hello_world }
end
- def test_declared_prefixed_attribute_method_affects_respond_to_and_method_missing
- topic = @target.new(:title => 'Budget')
+ test "declared prefixed attribute method affects respond_to? and method_missing" do
+ topic = @target.new(title: "Budget")
%w(default_ title_).each do |prefix|
@target.class_eval "def #{prefix}attribute(*args) args end"
@target.attribute_method_prefix prefix
meth = "#{prefix}title"
assert topic.respond_to?(meth)
- assert_equal ['title'], topic.send(meth)
- assert_equal ['title', 'a'], topic.send(meth, 'a')
- assert_equal ['title', 1, 2, 3], topic.send(meth, 1, 2, 3)
+ assert_equal ["title"], topic.send(meth)
+ assert_equal ["title", "a"], topic.send(meth, "a")
+ assert_equal ["title", 1, 2, 3], topic.send(meth, 1, 2, 3)
end
end
- def test_declared_suffixed_attribute_method_affects_respond_to_and_method_missing
+ test "declared suffixed attribute method affects respond_to? and method_missing" do
%w(_default _title_default _it! _candidate= able?).each do |suffix|
@target.class_eval "def attribute#{suffix}(*args) args end"
@target.attribute_method_suffix suffix
- topic = @target.new(:title => 'Budget')
+ topic = @target.new(title: "Budget")
meth = "title#{suffix}"
assert topic.respond_to?(meth)
- assert_equal ['title'], topic.send(meth)
- assert_equal ['title', 'a'], topic.send(meth, 'a')
- assert_equal ['title', 1, 2, 3], topic.send(meth, 1, 2, 3)
+ assert_equal ["title"], topic.send(meth)
+ assert_equal ["title", "a"], topic.send(meth, "a")
+ assert_equal ["title", 1, 2, 3], topic.send(meth, 1, 2, 3)
end
end
- def test_declared_affixed_attribute_method_affects_respond_to_and_method_missing
- [['mark_', '_for_update'], ['reset_', '!'], ['default_', '_value?']].each do |prefix, suffix|
+ test "declared affixed attribute method affects respond_to? and method_missing" do
+ [["mark_", "_for_update"], ["reset_", "!"], ["default_", "_value?"]].each do |prefix, suffix|
@target.class_eval "def #{prefix}attribute#{suffix}(*args) args end"
- @target.attribute_method_affix({ :prefix => prefix, :suffix => suffix })
- topic = @target.new(:title => 'Budget')
+ @target.attribute_method_affix(prefix: prefix, suffix: suffix)
+ topic = @target.new(title: "Budget")
meth = "#{prefix}title#{suffix}"
assert topic.respond_to?(meth)
- assert_equal ['title'], topic.send(meth)
- assert_equal ['title', 'a'], topic.send(meth, 'a')
- assert_equal ['title', 1, 2, 3], topic.send(meth, 1, 2, 3)
+ assert_equal ["title"], topic.send(meth)
+ assert_equal ["title", "a"], topic.send(meth, "a")
+ assert_equal ["title", 1, 2, 3], topic.send(meth, 1, 2, 3)
end
end
- def test_should_unserialize_attributes_for_frozen_records
- myobj = {:value1 => :value2}
- topic = Topic.create("content" => myobj)
+ test "should unserialize attributes for frozen records" do
+ myobj = { value1: :value2 }
+ topic = Topic.create(content: myobj)
topic.freeze
assert_equal myobj, topic.content
end
- def test_typecast_attribute_from_select_to_false
- Topic.create(:title => 'Budget')
- # Oracle does not support boolean expressions in SELECT
+ test "typecast attribute from select to false" do
+ Topic.create(title: "Budget")
+ # Oracle does not support boolean expressions in SELECT.
if current_adapter?(:OracleAdapter, :FbAdapter)
- topic = Topic.all.merge!(:select => "topics.*, 0 as is_test").first
+ topic = Topic.all.merge!(select: "topics.*, 0 as is_test").first
else
- topic = Topic.all.merge!(:select => "topics.*, 1=2 as is_test").first
+ topic = Topic.all.merge!(select: "topics.*, 1=2 as is_test").first
end
assert !topic.is_test?
end
- def test_typecast_attribute_from_select_to_true
- Topic.create(:title => 'Budget')
- # Oracle does not support boolean expressions in SELECT
+ test "typecast attribute from select to true" do
+ Topic.create(title: "Budget")
+ # Oracle does not support boolean expressions in SELECT.
if current_adapter?(:OracleAdapter, :FbAdapter)
- topic = Topic.all.merge!(:select => "topics.*, 1 as is_test").first
+ topic = Topic.all.merge!(select: "topics.*, 1 as is_test").first
else
- topic = Topic.all.merge!(:select => "topics.*, 2=2 as is_test").first
+ topic = Topic.all.merge!(select: "topics.*, 2=2 as is_test").first
end
assert topic.is_test?
end
- def test_raises_dangerous_attribute_error_when_defining_activerecord_method_in_model
+ test "raises ActiveRecord::DangerousAttributeError when defining an AR method in a model" do
%w(save create_or_update).each do |method|
- klass = Class.new ActiveRecord::Base
+ klass = Class.new(ActiveRecord::Base)
klass.class_eval "def #{method}() 'defined #{method}' end"
assert_raise ActiveRecord::DangerousAttributeError do
klass.instance_method_already_implemented?(method)
@@ -550,7 +568,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
end
end
- def test_converted_values_are_returned_after_assignment
+ test "converted values are returned after assignment" do
developer = Developer.new(name: 1337, salary: "50000")
assert_equal "50000", developer.salary_before_type_cast
@@ -565,7 +583,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
assert_equal "1337", developer.name
end
- def test_write_nil_to_time_attributes
+ test "write nil to time attribute" do
in_time_zone "Pacific Time (US & Canada)" do
record = @target.new
record.written_on = nil
@@ -573,7 +591,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
end
end
- def test_write_time_to_date_attributes
+ test "write time to date attribute" do
in_time_zone "Pacific Time (US & Canada)" do
record = @target.new
record.last_read = Time.utc(2010, 1, 1, 10)
@@ -581,7 +599,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
end
end
- def test_time_attributes_are_retrieved_in_current_time_zone
+ test "time attributes are retrieved in the current time zone" do
in_time_zone "Pacific Time (US & Canada)" do
utc_time = Time.utc(2008, 1, 1)
record = @target.new
@@ -593,7 +611,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
end
end
- def test_setting_time_zone_aware_attribute_to_utc
+ test "setting a time zone-aware attribute to UTC" do
in_time_zone "Pacific Time (US & Canada)" do
utc_time = Time.utc(2008, 1, 1)
record = @target.new
@@ -604,11 +622,11 @@ class AttributeMethodsTest < ActiveRecord::TestCase
end
end
- def test_setting_time_zone_aware_attribute_in_other_time_zone
+ test "setting time zone-aware attribute in other time zone" do
utc_time = Time.utc(2008, 1, 1)
cst_time = utc_time.in_time_zone("Central Time (US & Canada)")
in_time_zone "Pacific Time (US & Canada)" do
- record = @target.new
+ record = @target.new
record.written_on = cst_time
assert_equal utc_time, record.written_on
assert_equal ActiveSupport::TimeZone["Pacific Time (US & Canada)"], record.written_on.time_zone
@@ -616,23 +634,23 @@ class AttributeMethodsTest < ActiveRecord::TestCase
end
end
- def test_setting_time_zone_aware_read_attribute
+ test "setting time zone-aware read attribute" do
utc_time = Time.utc(2008, 1, 1)
cst_time = utc_time.in_time_zone("Central Time (US & Canada)")
in_time_zone "Pacific Time (US & Canada)" do
- record = @target.create(:written_on => cst_time).reload
+ record = @target.create(written_on: cst_time).reload
assert_equal utc_time, record[:written_on]
assert_equal ActiveSupport::TimeZone["Pacific Time (US & Canada)"], record[:written_on].time_zone
assert_equal Time.utc(2007, 12, 31, 16), record[:written_on].time
end
end
- def test_setting_time_zone_aware_attribute_with_string
+ test "setting time zone-aware attribute with a string" do
utc_time = Time.utc(2008, 1, 1)
(-11..13).each do |timezone_offset|
time_string = utc_time.in_time_zone(timezone_offset).to_s
in_time_zone "Pacific Time (US & Canada)" do
- record = @target.new
+ record = @target.new
record.written_on = time_string
assert_equal Time.zone.parse(time_string), record.written_on
assert_equal ActiveSupport::TimeZone["Pacific Time (US & Canada)"], record.written_on.time_zone
@@ -641,30 +659,30 @@ class AttributeMethodsTest < ActiveRecord::TestCase
end
end
- def test_time_zone_aware_attribute_saved
+ test "time zone-aware attribute saved" do
in_time_zone 1 do
- record = @target.create(:written_on => '2012-02-20 10:00')
+ record = @target.create(written_on: "2012-02-20 10:00")
- record.written_on = '2012-02-20 09:00'
+ record.written_on = "2012-02-20 09:00"
record.save
assert_equal Time.zone.local(2012, 02, 20, 9), record.reload.written_on
end
end
- def test_setting_time_zone_aware_attribute_to_blank_string_returns_nil
+ test "setting a time zone-aware attribute to a blank string returns nil" do
in_time_zone "Pacific Time (US & Canada)" do
- record = @target.new
- record.written_on = ' '
+ record = @target.new
+ record.written_on = " "
assert_nil record.written_on
assert_nil record[:written_on]
end
end
- def test_setting_time_zone_aware_attribute_interprets_time_zone_unaware_string_in_time_zone
- time_string = 'Tue Jan 01 00:00:00 2008'
+ test "setting a time zone-aware attribute interprets time zone-unaware string in time zone" do
+ time_string = "Tue Jan 01 00:00:00 2008"
(-11..13).each do |timezone_offset|
in_time_zone timezone_offset do
- record = @target.new
+ record = @target.new
record.written_on = time_string
assert_equal Time.zone.parse(time_string), record.written_on
assert_equal ActiveSupport::TimeZone[timezone_offset], record.written_on.time_zone
@@ -673,10 +691,10 @@ class AttributeMethodsTest < ActiveRecord::TestCase
end
end
- def test_setting_time_zone_aware_datetime_in_current_time_zone
+ test "setting a time zone-aware datetime in the current time zone" do
utc_time = Time.utc(2008, 1, 1)
in_time_zone "Pacific Time (US & Canada)" do
- record = @target.new
+ record = @target.new
record.written_on = utc_time.in_time_zone
assert_equal utc_time, record.written_on
assert_equal ActiveSupport::TimeZone["Pacific Time (US & Canada)"], record.written_on.time_zone
@@ -684,7 +702,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
end
end
- def test_yaml_dumping_record_with_time_zone_aware_attribute
+ test "YAML dumping a record with time zone-aware attribute" do
in_time_zone "Pacific Time (US & Canada)" do
record = Topic.new(id: 1)
record.written_on = "Jan 01 00:00:00 2014"
@@ -692,7 +710,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
end
end
- def test_setting_time_zone_aware_time_in_current_time_zone
+ test "setting a time zone-aware time in the current time zone" do
in_time_zone "Pacific Time (US & Canada)" do
record = @target.new
time_string = "10:00:00"
@@ -702,12 +720,12 @@ class AttributeMethodsTest < ActiveRecord::TestCase
assert_equal expected_time, record.bonus_time
assert_equal ActiveSupport::TimeZone["Pacific Time (US & Canada)"], record.bonus_time.time_zone
- record.bonus_time = ''
+ record.bonus_time = ""
assert_nil record.bonus_time
end
end
- def test_setting_time_zone_aware_time_with_dst
+ test "setting a time zone-aware time with DST" do
in_time_zone "Pacific Time (US & Canada)" do
current_time = Time.zone.local(2014, 06, 15, 10)
record = @target.new(bonus_time: current_time)
@@ -721,7 +739,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
end
end
- def test_removing_time_zone_aware_types
+ test "removing time zone-aware types" do
with_time_zone_aware_types(:datetime) do
in_time_zone "Pacific Time (US & Canada)" do
record = @target.new(bonus_time: "10:00:00")
@@ -733,14 +751,14 @@ class AttributeMethodsTest < ActiveRecord::TestCase
end
end
- def test_time_zone_aware_attributes_dont_recurse_infinitely_on_invalid_values
+ test "time zone-aware attributes do not recurse infinitely on invalid values" do
in_time_zone "Pacific Time (US & Canada)" do
record = @target.new(bonus_time: [])
- assert_equal nil, record.bonus_time
+ assert_nil record.bonus_time
end
end
- def test_setting_time_zone_conversion_for_attributes_should_write_value_on_class_variable
+ test "setting a time_zone_conversion_for_attributes should write the value on a class variable" do
Topic.skip_time_zone_conversion_for_attributes = [:field_a]
Minimalistic.skip_time_zone_conversion_for_attributes = [:field_b]
@@ -748,44 +766,44 @@ class AttributeMethodsTest < ActiveRecord::TestCase
assert_equal [:field_b], Minimalistic.skip_time_zone_conversion_for_attributes
end
- def test_read_attributes_respect_access_control
+ test "attribute readers respect access control" do
privatize("title")
- topic = @target.new(:title => "The pros and cons of programming naked.")
+ topic = @target.new(title: "The pros and cons of programming naked.")
assert !topic.respond_to?(:title)
exception = assert_raise(NoMethodError) { topic.title }
- assert exception.message.include?("private method")
+ assert_includes exception.message, "private method"
assert_equal "I'm private", topic.send(:title)
end
- def test_write_attributes_respect_access_control
+ test "attribute writers respect access control" do
privatize("title=(value)")
topic = @target.new
assert !topic.respond_to?(:title=)
- exception = assert_raise(NoMethodError) { topic.title = "Pants"}
- assert exception.message.include?("private method")
+ exception = assert_raise(NoMethodError) { topic.title = "Pants" }
+ assert_includes exception.message, "private method"
topic.send(:title=, "Very large pants")
end
- def test_question_attributes_respect_access_control
+ test "attribute predicates respect access control" do
privatize("title?")
- topic = @target.new(:title => "Isaac Newton's pants")
+ topic = @target.new(title: "Isaac Newton's pants")
assert !topic.respond_to?(:title?)
exception = assert_raise(NoMethodError) { topic.title? }
- assert exception.message.include?("private method")
+ assert_includes exception.message, "private method"
assert topic.send(:title?)
end
- def test_bulk_update_respects_access_control
+ test "bulk updates respect access control" do
privatize("title=(value)")
- assert_raise(ActiveRecord::UnknownAttributeError) { @target.new(:title => "Rants about pants") }
- assert_raise(ActiveRecord::UnknownAttributeError) { @target.new.attributes = { :title => "Ants in pants" } }
+ assert_raise(ActiveRecord::UnknownAttributeError) { @target.new(title: "Rants about pants") }
+ assert_raise(ActiveRecord::UnknownAttributeError) { @target.new.attributes = { title: "Ants in pants" } }
end
- def test_bulk_update_raise_unknown_attribute_error
+ test "bulk update raises ActiveRecord::UnknownAttributeError" do
error = assert_raises(ActiveRecord::UnknownAttributeError) {
Topic.new(hello: "world")
}
@@ -794,22 +812,22 @@ class AttributeMethodsTest < ActiveRecord::TestCase
assert_equal "unknown attribute 'hello' for Topic.", error.message
end
- def test_methods_override_in_multi_level_subclass
+ test "method overrides in multi-level subclasses" do
klass = Class.new(Developer) do
def name
"dev:#{read_attribute(:name)}"
end
end
- 2.times { klass = Class.new klass }
- dev = klass.new(name: 'arthurnn')
+ 2.times { klass = Class.new(klass) }
+ dev = klass.new(name: "arthurnn")
dev.save!
- assert_equal 'dev:arthurnn', dev.reload.name
+ assert_equal "dev:arthurnn", dev.reload.name
end
- def test_global_methods_are_overwritten
+ test "global methods are overwritten" do
klass = Class.new(ActiveRecord::Base) do
- self.table_name = 'computers'
+ self.table_name = "computers"
end
assert !klass.instance_method_already_implemented?(:system)
@@ -817,11 +835,13 @@ class AttributeMethodsTest < ActiveRecord::TestCase
assert_nil computer.system
end
- def test_global_methods_are_overwritten_when_subclassing
- klass = Class.new(ActiveRecord::Base) { self.abstract_class = true }
+ test "global methods are overwritten when subclassing" do
+ klass = Class.new(ActiveRecord::Base) do
+ self.abstract_class = true
+ end
subklass = Class.new(klass) do
- self.table_name = 'computers'
+ self.table_name = "computers"
end
assert !klass.instance_method_already_implemented?(:system)
@@ -830,7 +850,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
assert_nil computer.system
end
- def test_instance_method_should_be_defined_on_the_base_class
+ test "instance methods should be defined on the base class" do
subklass = Class.new(Topic)
Topic.define_attribute_methods
@@ -846,14 +866,14 @@ class AttributeMethodsTest < ActiveRecord::TestCase
assert subklass.method_defined?(:id), "subklass is missing id method"
end
- def test_read_attribute_with_nil_should_not_asplode
- assert_equal nil, Topic.new.read_attribute(nil)
+ test "read_attribute with nil should not asplode" do
+ assert_nil Topic.new.read_attribute(nil)
end
# If B < A, and A defines an accessor for 'foo', we don't want to override
# that by defining a 'foo' method in the generated methods module for B.
# (That module will be inserted between the two, e.g. [B, <GeneratedAttributes>, A].)
- def test_inherited_custom_accessors
+ test "inherited custom accessors" do
klass = new_topic_like_ar_class do
self.abstract_class = true
def title; "omg"; end
@@ -869,9 +889,9 @@ class AttributeMethodsTest < ActiveRecord::TestCase
assert_equal "lol", topic.author_name
end
- def test_inherited_custom_accessors_with_reserved_names
+ test "inherited custom accessors with reserved names" do
klass = Class.new(ActiveRecord::Base) do
- self.table_name = 'computers'
+ self.table_name = "computers"
self.abstract_class = true
def system; "omg"; end
def system=(val); self.developer = val; end
@@ -887,18 +907,18 @@ class AttributeMethodsTest < ActiveRecord::TestCase
assert_equal 99, computer.developer
end
- def test_on_the_fly_super_invokable_generated_attribute_methods_via_method_missing
+ test "on_the_fly_super_invokable_generated_attribute_methods_via_method_missing" do
klass = new_topic_like_ar_class do
def title
- super + '!'
+ super + "!"
end
end
real_topic = topics(:first)
- assert_equal real_topic.title + '!', klass.find(real_topic.id).title
+ assert_equal real_topic.title + "!", klass.find(real_topic.id).title
end
- def test_on_the_fly_super_invokable_generated_predicate_attribute_methods_via_method_missing
+ test "on-the-fly super-invokable generated attribute predicates via method_missing" do
klass = new_topic_like_ar_class do
def title?
!super
@@ -909,7 +929,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
assert_equal !real_topic.title?, klass.find(real_topic.id).title?
end
- def test_calling_super_when_parent_does_not_define_method_raises_error
+ test "calling super when the parent does not define method raises NoMethodError" do
klass = new_topic_like_ar_class do
def some_method_that_is_not_on_super
super
@@ -921,38 +941,38 @@ class AttributeMethodsTest < ActiveRecord::TestCase
end
end
- def test_attribute_method?
+ test "attribute_method?" do
assert @target.attribute_method?(:title)
assert @target.attribute_method?(:title=)
assert_not @target.attribute_method?(:wibble)
end
- def test_attribute_method_returns_false_if_table_does_not_exist
- @target.table_name = 'wibble'
+ test "attribute_method? returns false if the table does not exist" do
+ @target.table_name = "wibble"
assert_not @target.attribute_method?(:title)
end
- def test_attribute_names_on_new_record
+ test "attribute_names on a new record" do
model = @target.new
assert_equal @target.column_names, model.attribute_names
end
- def test_attribute_names_on_queried_record
+ test "attribute_names on a queried record" do
model = @target.last!
assert_equal @target.column_names, model.attribute_names
end
- def test_attribute_names_with_custom_select
- model = @target.select('id').last!
+ test "attribute_names with a custom select" do
+ model = @target.select("id").last!
- assert_equal ['id'], model.attribute_names
- # Sanity check, make sure other columns exist
- assert_not_equal ['id'], @target.column_names
+ assert_equal ["id"], model.attribute_names
+ # Sanity check, make sure other columns exist.
+ assert_not_equal ["id"], @target.column_names
end
- def test_came_from_user
+ test "came_from_user?" do
model = @target.first
assert_not model.id_came_from_user?
@@ -960,7 +980,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
assert model.id_came_from_user?
end
- def test_accessed_fields
+ test "accessed_fields" do
model = @target.first
assert_equal [], model.accessed_fields
@@ -972,38 +992,38 @@ class AttributeMethodsTest < ActiveRecord::TestCase
private
- def new_topic_like_ar_class(&block)
- klass = Class.new(ActiveRecord::Base) do
- self.table_name = 'topics'
- class_eval(&block)
- end
+ def new_topic_like_ar_class(&block)
+ klass = Class.new(ActiveRecord::Base) do
+ self.table_name = "topics"
+ class_eval(&block)
+ end
- assert_empty klass.generated_attribute_methods.instance_methods(false)
- klass
- end
+ assert_empty klass.generated_attribute_methods.instance_methods(false)
+ klass
+ end
- def with_time_zone_aware_types(*types)
- old_types = ActiveRecord::Base.time_zone_aware_types
- ActiveRecord::Base.time_zone_aware_types = types
- yield
- ensure
- ActiveRecord::Base.time_zone_aware_types = old_types
- end
+ def with_time_zone_aware_types(*types)
+ old_types = ActiveRecord::Base.time_zone_aware_types
+ ActiveRecord::Base.time_zone_aware_types = types
+ yield
+ ensure
+ ActiveRecord::Base.time_zone_aware_types = old_types
+ end
- def cached_columns
- Topic.columns.map(&:name)
- end
+ def cached_columns
+ Topic.columns.map(&:name)
+ end
- def time_related_columns_on_topic
- Topic.columns.select { |c| [:time, :date, :datetime, :timestamp].include?(c.type) }
- end
+ def time_related_columns_on_topic
+ Topic.columns.select { |c| [:time, :date, :datetime, :timestamp].include?(c.type) }
+ end
- def privatize(method_signature)
- @target.class_eval(<<-private_method, __FILE__, __LINE__ + 1)
- private
- def #{method_signature}
- "I'm private"
- end
- private_method
- end
+ def privatize(method_signature)
+ @target.class_eval(<<-private_method, __FILE__, __LINE__ + 1)
+ private
+ def #{method_signature}
+ "I'm private"
+ end
+ private_method
+ end
end
diff --git a/activerecord/test/cases/attribute_set_test.rb b/activerecord/test/cases/attribute_set_test.rb
index 7a24b85a36..bd4b200735 100644
--- a/activerecord/test/cases/attribute_set_test.rb
+++ b/activerecord/test/cases/attribute_set_test.rb
@@ -1,10 +1,10 @@
-require 'cases/helper'
+require "cases/helper"
module ActiveRecord
class AttributeSetTest < ActiveRecord::TestCase
test "building a new set from raw attributes" do
builder = AttributeSet::Builder.new(foo: Type::Integer.new, bar: Type::Float.new)
- attributes = builder.build_from_database(foo: '1.1', bar: '2.2')
+ attributes = builder.build_from_database(foo: "1.1", bar: "2.2")
assert_equal 1, attributes[:foo].value
assert_equal 2.2, attributes[:bar].value
@@ -14,7 +14,7 @@ module ActiveRecord
test "building with custom types" do
builder = AttributeSet::Builder.new(foo: Type::Float.new)
- attributes = builder.build_from_database({ foo: '3.3', bar: '4.4' }, { bar: Type::Integer.new })
+ attributes = builder.build_from_database({ foo: "3.3", bar: "4.4" }, bar: Type::Integer.new)
assert_equal 3.3, attributes[:foo].value
assert_equal 4, attributes[:bar].value
@@ -22,16 +22,16 @@ module ActiveRecord
test "[] returns a null object" do
builder = AttributeSet::Builder.new(foo: Type::Float.new)
- attributes = builder.build_from_database(foo: '3.3')
+ attributes = builder.build_from_database(foo: "3.3")
- assert_equal '3.3', attributes[:foo].value_before_type_cast
- assert_equal nil, attributes[:bar].value_before_type_cast
+ assert_equal "3.3", attributes[:foo].value_before_type_cast
+ assert_nil attributes[:bar].value_before_type_cast
assert_equal :bar, attributes[:bar].name
end
test "duping creates a new hash, but does not dup the attributes" do
builder = AttributeSet::Builder.new(foo: Type::Integer.new, bar: Type::String.new)
- attributes = builder.build_from_database(foo: 1, bar: 'foo')
+ attributes = builder.build_from_database(foo: 1, bar: "foo")
# Ensure the type cast value is cached
attributes[:foo].value
@@ -39,17 +39,17 @@ module ActiveRecord
duped = attributes.dup
duped.write_from_database(:foo, 2)
- duped[:bar].value << 'bar'
+ duped[:bar].value << "bar"
assert_equal 1, attributes[:foo].value
assert_equal 2, duped[:foo].value
- assert_equal 'foobar', attributes[:bar].value
- assert_equal 'foobar', duped[:bar].value
+ assert_equal "foobar", attributes[:bar].value
+ assert_equal "foobar", duped[:bar].value
end
test "deep_duping creates a new hash and dups each attribute" do
builder = AttributeSet::Builder.new(foo: Type::Integer.new, bar: Type::String.new)
- attributes = builder.build_from_database(foo: 1, bar: 'foo')
+ attributes = builder.build_from_database(foo: 1, bar: "foo")
# Ensure the type cast value is cached
attributes[:foo].value
@@ -57,12 +57,12 @@ module ActiveRecord
duped = attributes.deep_dup
duped.write_from_database(:foo, 2)
- duped[:bar].value << 'bar'
+ duped[:bar].value << "bar"
assert_equal 1, attributes[:foo].value
assert_equal 2, duped[:foo].value
- assert_equal 'foo', attributes[:bar].value
- assert_equal 'foobar', duped[:bar].value
+ assert_equal "foo", attributes[:bar].value
+ assert_equal "foobar", duped[:bar].value
end
test "freezing cloned set does not freeze original" do
@@ -77,7 +77,7 @@ module ActiveRecord
test "to_hash returns a hash of the type cast values" do
builder = AttributeSet::Builder.new(foo: Type::Integer.new, bar: Type::Float.new)
- attributes = builder.build_from_database(foo: '1.1', bar: '2.2')
+ attributes = builder.build_from_database(foo: "1.1", bar: "2.2")
assert_equal({ foo: 1, bar: 2.2 }, attributes.to_hash)
assert_equal({ foo: 1, bar: 2.2 }, attributes.to_h)
@@ -85,7 +85,7 @@ module ActiveRecord
test "to_hash maintains order" do
builder = AttributeSet::Builder.new(foo: Type::Integer.new, bar: Type::Float.new)
- attributes = builder.build_from_database(foo: '2.2', bar: '3.3')
+ attributes = builder.build_from_database(foo: "2.2", bar: "3.3")
attributes[:bar]
hash = attributes.to_h
@@ -95,9 +95,9 @@ module ActiveRecord
test "values_before_type_cast" do
builder = AttributeSet::Builder.new(foo: Type::Integer.new, bar: Type::Integer.new)
- attributes = builder.build_from_database(foo: '1.1', bar: '2.2')
+ attributes = builder.build_from_database(foo: "1.1", bar: "2.2")
- assert_equal({ foo: '1.1', bar: '2.2' }, attributes.values_before_type_cast)
+ assert_equal({ foo: "1.1", bar: "2.2" }, attributes.values_before_type_cast)
end
test "known columns are built with uninitialized attributes" do
@@ -129,7 +129,7 @@ module ActiveRecord
test "fetch_value returns the value for the given initialized attribute" do
builder = AttributeSet::Builder.new(foo: Type::Integer.new, bar: Type::Float.new)
- attributes = builder.build_from_database(foo: '1.1', bar: '2.2')
+ attributes = builder.build_from_database(foo: "1.1", bar: "2.2")
assert_equal 1, attributes.fetch_value(:foo)
assert_equal 2.2, attributes.fetch_value(:bar)
@@ -150,8 +150,8 @@ module ActiveRecord
test "fetch_value uses the given block for uninitialized attributes" do
attributes = attributes_with_uninitialized_key
- value = attributes.fetch_value(:bar) { |n| n.to_s + '!' }
- assert_equal 'bar!', value
+ value = attributes.fetch_value(:bar) { |n| n.to_s + "!" }
+ assert_equal "bar!", value
end
test "fetch_value returns nil for uninitialized attributes if no block is given" do
@@ -207,7 +207,7 @@ module ActiveRecord
def attributes_with_uninitialized_key
builder = AttributeSet::Builder.new(foo: Type::Integer.new, bar: Type::Float.new)
- builder.build_from_database(foo: '1.1')
+ builder.build_from_database(foo: "1.1")
end
test "freezing doesn't prevent the set from materializing" do
diff --git a/activerecord/test/cases/attribute_test.rb b/activerecord/test/cases/attribute_test.rb
index b1b8639696..7cf6b498c9 100644
--- a/activerecord/test/cases/attribute_test.rb
+++ b/activerecord/test/cases/attribute_test.rb
@@ -1,4 +1,4 @@
-require 'cases/helper'
+require "cases/helper"
module ActiveRecord
class AttributeTest < ActiveRecord::TestCase
@@ -11,91 +11,91 @@ module ActiveRecord
end
test "from_database + read type casts from database" do
- @type.expect(:deserialize, 'type cast from database', ['a value'])
- attribute = Attribute.from_database(nil, 'a value', @type)
+ @type.expect(:deserialize, "type cast from database", ["a value"])
+ attribute = Attribute.from_database(nil, "a value", @type)
type_cast_value = attribute.value
- assert_equal 'type cast from database', type_cast_value
+ assert_equal "type cast from database", type_cast_value
end
test "from_user + read type casts from user" do
- @type.expect(:cast, 'type cast from user', ['a value'])
- attribute = Attribute.from_user(nil, 'a value', @type)
+ @type.expect(:cast, "type cast from user", ["a value"])
+ attribute = Attribute.from_user(nil, "a value", @type)
type_cast_value = attribute.value
- assert_equal 'type cast from user', type_cast_value
+ assert_equal "type cast from user", type_cast_value
end
test "reading memoizes the value" do
- @type.expect(:deserialize, 'from the database', ['whatever'])
- attribute = Attribute.from_database(nil, 'whatever', @type)
+ @type.expect(:deserialize, "from the database", ["whatever"])
+ attribute = Attribute.from_database(nil, "whatever", @type)
type_cast_value = attribute.value
second_read = attribute.value
- assert_equal 'from the database', type_cast_value
+ assert_equal "from the database", type_cast_value
assert_same type_cast_value, second_read
end
test "reading memoizes falsy values" do
- @type.expect(:deserialize, false, ['whatever'])
- attribute = Attribute.from_database(nil, 'whatever', @type)
+ @type.expect(:deserialize, false, ["whatever"])
+ attribute = Attribute.from_database(nil, "whatever", @type)
attribute.value
attribute.value
end
test "read_before_typecast returns the given value" do
- attribute = Attribute.from_database(nil, 'raw value', @type)
+ attribute = Attribute.from_database(nil, "raw value", @type)
raw_value = attribute.value_before_type_cast
- assert_equal 'raw value', raw_value
+ assert_equal "raw value", raw_value
end
test "from_database + read_for_database type casts to and from database" do
- @type.expect(:deserialize, 'read from database', ['whatever'])
- @type.expect(:serialize, 'ready for database', ['read from database'])
- attribute = Attribute.from_database(nil, 'whatever', @type)
+ @type.expect(:deserialize, "read from database", ["whatever"])
+ @type.expect(:serialize, "ready for database", ["read from database"])
+ attribute = Attribute.from_database(nil, "whatever", @type)
serialize = attribute.value_for_database
- assert_equal 'ready for database', serialize
+ assert_equal "ready for database", serialize
end
test "from_user + read_for_database type casts from the user to the database" do
- @type.expect(:cast, 'read from user', ['whatever'])
- @type.expect(:serialize, 'ready for database', ['read from user'])
- attribute = Attribute.from_user(nil, 'whatever', @type)
+ @type.expect(:cast, "read from user", ["whatever"])
+ @type.expect(:serialize, "ready for database", ["read from user"])
+ attribute = Attribute.from_user(nil, "whatever", @type)
serialize = attribute.value_for_database
- assert_equal 'ready for database', serialize
+ assert_equal "ready for database", serialize
end
test "duping dups the value" do
- @type.expect(:deserialize, 'type cast', ['a value'])
- attribute = Attribute.from_database(nil, 'a value', @type)
+ @type.expect(:deserialize, "type cast", ["a value"])
+ attribute = Attribute.from_database(nil, "a value", @type)
value_from_orig = attribute.value
value_from_clone = attribute.dup.value
- value_from_orig << ' foo'
+ value_from_orig << " foo"
- assert_equal 'type cast foo', value_from_orig
- assert_equal 'type cast', value_from_clone
+ assert_equal "type cast foo", value_from_orig
+ assert_equal "type cast", value_from_clone
end
test "duping does not dup the value if it is not dupable" do
- @type.expect(:deserialize, false, ['a value'])
- attribute = Attribute.from_database(nil, 'a value', @type)
+ @type.expect(:deserialize, false, ["a value"])
+ attribute = Attribute.from_database(nil, "a value", @type)
assert_same attribute.value, attribute.dup.value
end
test "duping does not eagerly type cast if we have not yet type cast" do
- attribute = Attribute.from_database(nil, 'a value', @type)
+ attribute = Attribute.from_database(nil, "a value", @type)
attribute.dup
end
diff --git a/activerecord/test/cases/attributes_test.rb b/activerecord/test/cases/attributes_test.rb
index 7bcaa53aa2..3705a6be89 100644
--- a/activerecord/test/cases/attributes_test.rb
+++ b/activerecord/test/cases/attributes_test.rb
@@ -1,10 +1,10 @@
-require 'cases/helper'
+require "cases/helper"
class OverloadedType < ActiveRecord::Base
attribute :overloaded_float, :integer
attribute :overloaded_string_with_limit, :string, limit: 50
attribute :non_existent_decimal, :decimal
- attribute :string_with_default, :string, default: 'the overloaded default'
+ attribute :string_with_default, :string, default: "the overloaded default"
end
class ChildOfOverloadedType < OverloadedType
@@ -15,7 +15,7 @@ class GrandchildOfOverloadedType < ChildOfOverloadedType
end
class UnoverloadedType < ActiveRecord::Base
- self.table_name = 'overloaded_types'
+ self.table_name = "overloaded_types"
end
module ActiveRecord
@@ -44,14 +44,14 @@ module ActiveRecord
end
test "properties assigned in constructor" do
- data = OverloadedType.new(overloaded_float: '3.3')
+ data = OverloadedType.new(overloaded_float: "3.3")
assert_equal 3, data.overloaded_float
end
test "overloaded properties with limit" do
- assert_equal 50, OverloadedType.type_for_attribute('overloaded_string_with_limit').limit
- assert_equal 255, UnoverloadedType.type_for_attribute('overloaded_string_with_limit').limit
+ assert_equal 50, OverloadedType.type_for_attribute("overloaded_string_with_limit").limit
+ assert_equal 255, UnoverloadedType.type_for_attribute("overloaded_string_with_limit").limit
end
test "nonexistent attribute" do
@@ -65,7 +65,7 @@ module ActiveRecord
test "model with nonexistent attribute with default value can be saved" do
klass = Class.new(OverloadedType) do
- attribute :non_existent_string_with_default, :string, default: 'nonexistent'
+ attribute :non_existent_string_with_default, :string, default: "nonexistent"
end
model = klass.new
@@ -76,22 +76,22 @@ module ActiveRecord
data = OverloadedType.new
unoverloaded_data = UnoverloadedType.new
- assert_equal 'the overloaded default', data.string_with_default
- assert_equal 'the original default', unoverloaded_data.string_with_default
+ assert_equal "the overloaded default", data.string_with_default
+ assert_equal "the original default", unoverloaded_data.string_with_default
end
test "defaults are not touched on the columns" do
- assert_equal 'the original default', OverloadedType.columns_hash['string_with_default'].default
+ assert_equal "the original default", OverloadedType.columns_hash["string_with_default"].default
end
test "children inherit custom properties" do
- data = ChildOfOverloadedType.new(overloaded_float: '4.4')
+ data = ChildOfOverloadedType.new(overloaded_float: "4.4")
assert_equal 4, data.overloaded_float
end
test "children can override parents" do
- data = GrandchildOfOverloadedType.new(overloaded_float: '4.4')
+ data = GrandchildOfOverloadedType.new(overloaded_float: "4.4")
assert_equal 4.4, data.overloaded_float
end
@@ -106,13 +106,13 @@ module ActiveRecord
assert_equal 6, klass.attribute_types.length
assert_equal 6, klass.column_defaults.length
- assert_not klass.attribute_types.include?('wibble')
+ assert_not klass.attribute_types.include?("wibble")
klass.attribute :wibble, Type::Value.new
assert_equal 7, klass.attribute_types.length
assert_equal 7, klass.column_defaults.length
- assert klass.attribute_types.include?('wibble')
+ assert_includes klass.attribute_types, "wibble"
end
test "the given default value is cast from user" do
@@ -205,5 +205,63 @@ module ActiveRecord
assert_equal(:bar, child.new(foo: :bar).foo)
end
+
+ test "attributes not backed by database columns are not dirty when unchanged" do
+ refute OverloadedType.new.non_existent_decimal_changed?
+ end
+
+ test "attributes not backed by database columns are always initialized" do
+ OverloadedType.create!
+ model = OverloadedType.first
+
+ assert_nil model.non_existent_decimal
+ model.non_existent_decimal = "123"
+ assert_equal 123, model.non_existent_decimal
+ end
+
+ test "attributes not backed by database columns return the default on models loaded from database" do
+ child = Class.new(OverloadedType) do
+ attribute :non_existent_decimal, :decimal, default: 123
+ end
+ child.create!
+ model = child.first
+
+ assert_equal 123, model.non_existent_decimal
+ end
+
+ test "attributes not backed by database columns properly interact with mutation and dirty" do
+ child = Class.new(ActiveRecord::Base) do
+ self.table_name = "topics"
+ attribute :foo, :string, default: "lol"
+ end
+ child.create!
+ model = child.first
+
+ assert_equal "lol", model.foo
+
+ model.foo << "asdf"
+ assert_equal "lolasdf", model.foo
+ assert model.foo_changed?
+
+ model.reload
+ assert_equal "lol", model.foo
+
+ model.foo = "lol"
+ refute model.changed?
+ end
+
+ test "attributes not backed by database columns appear in inspect" do
+ inspection = OverloadedType.new.inspect
+
+ assert_includes inspection, "non_existent_decimal"
+ end
+
+ test "attributes do not require a type" do
+ klass = Class.new(OverloadedType) do
+ attribute :no_type
+ end
+ assert_equal 1, klass.new(no_type: 1).no_type
+ assert_equal "foo", klass.new(no_type: "foo").no_type
+ end
end
end
diff --git a/activerecord/test/cases/autosave_association_test.rb b/activerecord/test/cases/autosave_association_test.rb
index 9e3266b7d6..6d31b7a091 100644
--- a/activerecord/test/cases/autosave_association_test.rb
+++ b/activerecord/test/cases/autosave_association_test.rb
@@ -1,55 +1,55 @@
-require 'cases/helper'
-require 'models/bird'
-require 'models/comment'
-require 'models/company'
-require 'models/customer'
-require 'models/developer'
-require 'models/computer'
-require 'models/invoice'
-require 'models/line_item'
-require 'models/order'
-require 'models/parrot'
-require 'models/person'
-require 'models/pirate'
-require 'models/post'
-require 'models/reader'
-require 'models/ship'
-require 'models/ship_part'
-require 'models/tag'
-require 'models/tagging'
-require 'models/treasure'
-require 'models/eye'
-require 'models/electron'
-require 'models/molecule'
-require 'models/member'
-require 'models/member_detail'
-require 'models/organization'
-require 'models/guitar'
-require 'models/tuning_peg'
+require "cases/helper"
+require "models/bird"
+require "models/post"
+require "models/comment"
+require "models/company"
+require "models/customer"
+require "models/developer"
+require "models/computer"
+require "models/invoice"
+require "models/line_item"
+require "models/order"
+require "models/parrot"
+require "models/person"
+require "models/pirate"
+require "models/reader"
+require "models/ship"
+require "models/ship_part"
+require "models/tag"
+require "models/tagging"
+require "models/treasure"
+require "models/eye"
+require "models/electron"
+require "models/molecule"
+require "models/member"
+require "models/member_detail"
+require "models/organization"
+require "models/guitar"
+require "models/tuning_peg"
class TestAutosaveAssociationsInGeneral < ActiveRecord::TestCase
def test_autosave_validation
person = Class.new(ActiveRecord::Base) {
- self.table_name = 'people'
- validate :should_be_cool, :on => :create
- def self.name; 'Person'; end
+ self.table_name = "people"
+ validate :should_be_cool, on: :create
+ def self.name; "Person"; end
private
- def should_be_cool
- unless self.first_name == 'cool'
- errors.add :first_name, "not cool"
+ def should_be_cool
+ unless self.first_name == "cool"
+ errors.add :first_name, "not cool"
+ end
end
- end
}
reference = Class.new(ActiveRecord::Base) {
self.table_name = "references"
- def self.name; 'Reference'; end
+ def self.name; "Reference"; end
belongs_to :person, autosave: true, anonymous_class: person
}
- u = person.create!(first_name: 'cool')
- u.update_attributes!(first_name: 'nah') # still valid because validation only applies on 'create'
+ u = person.create!(first_name: "cool")
+ u.update_attributes!(first_name: "nah") # still valid because validation only applies on 'create'
assert reference.create!(person: u).persisted?
end
@@ -79,25 +79,25 @@ class TestAutosaveAssociationsInGeneral < ActiveRecord::TestCase
private
- def assert_no_difference_when_adding_callbacks_twice_for(model, association_name)
- reflection = model.reflect_on_association(association_name)
- assert_no_difference "callbacks_for_model(#{model.name}).length" do
- model.send(:add_autosave_association_callbacks, reflection)
+ def assert_no_difference_when_adding_callbacks_twice_for(model, association_name)
+ reflection = model.reflect_on_association(association_name)
+ assert_no_difference "callbacks_for_model(#{model.name}).length" do
+ model.send(:add_autosave_association_callbacks, reflection)
+ end
end
- end
- def callbacks_for_model(model)
- model.instance_variables.grep(/_callbacks$/).flat_map do |ivar|
- model.instance_variable_get(ivar)
+ def callbacks_for_model(model)
+ model.instance_variables.grep(/_callbacks$/).flat_map do |ivar|
+ model.instance_variable_get(ivar)
+ end
end
- end
end
class TestDefaultAutosaveAssociationOnAHasOneAssociation < ActiveRecord::TestCase
fixtures :companies, :accounts
def test_should_save_parent_but_not_invalid_child
- firm = Firm.new(:name => 'GlobalMegaCorp')
+ firm = Firm.new(name: "GlobalMegaCorp")
assert firm.valid?
firm.build_account_using_primary_key
@@ -178,8 +178,8 @@ class TestDefaultAutosaveAssociationOnAHasOneAssociation < ActiveRecord::TestCas
end
def test_not_resaved_when_unchanged
- firm = Firm.all.merge!(:includes => :account).first
- firm.name += '-changed'
+ firm = Firm.all.merge!(includes: :account).first
+ firm.name += "-changed"
assert_queries(1) { firm.save! }
firm = Firm.first
@@ -196,21 +196,21 @@ class TestDefaultAutosaveAssociationOnAHasOneAssociation < ActiveRecord::TestCas
end
def test_callbacks_firing_order_on_create
- eye = Eye.create(:iris_attributes => {:color => 'honey'})
+ eye = Eye.create(iris_attributes: { color: "honey" })
assert_equal [true, false], eye.after_create_callbacks_stack
end
def test_callbacks_firing_order_on_update
- eye = Eye.create(iris_attributes: {color: 'honey'})
- eye.update(iris_attributes: {color: 'green'})
+ eye = Eye.create(iris_attributes: { color: "honey" })
+ eye.update(iris_attributes: { color: "green" })
assert_equal [true, false], eye.after_update_callbacks_stack
end
def test_callbacks_firing_order_on_save
- eye = Eye.create(iris_attributes: {color: 'honey'})
+ eye = Eye.create(iris_attributes: { color: "honey" })
assert_equal [false, false], eye.after_save_callbacks_stack
- eye.update(iris_attributes: {color: 'blue'})
+ eye.update(iris_attributes: { color: "blue" })
assert_equal [false, false, false, false], eye.after_save_callbacks_stack
end
end
@@ -219,7 +219,7 @@ class TestDefaultAutosaveAssociationOnABelongsToAssociation < ActiveRecord::Test
fixtures :companies, :posts, :tags, :taggings
def test_should_save_parent_but_not_invalid_child
- client = Client.new(:name => 'Joe (the Plumber)')
+ client = Client.new(name: "Joe (the Plumber)")
assert client.valid?
client.build_firm
@@ -231,7 +231,7 @@ class TestDefaultAutosaveAssociationOnABelongsToAssociation < ActiveRecord::Test
def test_save_fails_for_invalid_belongs_to
# Oracle saves empty string as NULL therefore :message changed to one space
- assert log = AuditLog.create(:developer_id => 0, :message => " ")
+ assert log = AuditLog.create(developer_id: 0, message: " ")
log.developer = Developer.new
assert !log.developer.valid?
@@ -242,7 +242,7 @@ class TestDefaultAutosaveAssociationOnABelongsToAssociation < ActiveRecord::Test
def test_save_succeeds_for_invalid_belongs_to_with_validate_false
# Oracle saves empty string as NULL therefore :message changed to one space
- assert log = AuditLog.create(:developer_id => 0, :message=> " ")
+ assert log = AuditLog.create(developer_id: 0, message: " ")
log.unvalidated_developer = Developer.new
assert !log.unvalidated_developer.valid?
@@ -362,22 +362,22 @@ class TestDefaultAutosaveAssociationOnABelongsToAssociation < ActiveRecord::Test
def test_store_association_with_a_polymorphic_relationship
num_tagging = Tagging.count
- tags(:misc).create_tagging(:taggable => posts(:thinking))
+ tags(:misc).create_tagging(taggable: posts(:thinking))
assert_equal num_tagging + 1, Tagging.count
end
def test_build_and_then_save_parent_should_not_reload_target
client = Client.first
- apple = client.build_firm(:name => "Apple")
+ apple = client.build_firm(name: "Apple")
client.save!
assert_no_queries { assert_equal apple, client.firm }
end
def test_validation_does_not_validate_stale_association_target
- valid_developer = Developer.create!(:name => "Dude", :salary => 50_000)
+ valid_developer = Developer.create!(name: "Dude", salary: 50_000)
invalid_developer = Developer.new()
- auditlog = AuditLog.new(:message => "foo")
+ auditlog = AuditLog.new(message: "foo")
auditlog.developer = invalid_developer
auditlog.developer_id = valid_developer.id
@@ -388,7 +388,7 @@ end
class TestDefaultAutosaveAssociationOnAHasManyAssociationWithAcceptsNestedAttributes < ActiveRecord::TestCase
def test_invalid_adding_with_nested_attributes
molecule = Molecule.new
- valid_electron = Electron.new(name: 'electron')
+ valid_electron = Electron.new(name: "electron")
invalid_electron = Electron.new
molecule.electrons = [valid_electron, invalid_electron]
@@ -396,7 +396,7 @@ class TestDefaultAutosaveAssociationOnAHasManyAssociationWithAcceptsNestedAttrib
assert_not invalid_electron.valid?
assert valid_electron.valid?
- assert_not molecule.persisted?, 'Molecule should not be persisted when its electrons are invalid'
+ assert_not molecule.persisted?, "Molecule should not be persisted when its electrons are invalid"
end
def test_errors_should_be_indexed_when_passed_as_array
@@ -419,7 +419,7 @@ class TestDefaultAutosaveAssociationOnAHasManyAssociationWithAcceptsNestedAttrib
ActiveRecord::Base.index_nested_attribute_errors = true
molecule = Molecule.new
- valid_electron = Electron.new(name: 'electron')
+ valid_electron = Electron.new(name: "electron")
invalid_electron = Electron.new
molecule.electrons = [valid_electron, invalid_electron]
@@ -435,7 +435,7 @@ class TestDefaultAutosaveAssociationOnAHasManyAssociationWithAcceptsNestedAttrib
def test_errors_details_should_be_set
molecule = Molecule.new
- valid_electron = Electron.new(name: 'electron')
+ valid_electron = Electron.new(name: "electron")
invalid_electron = Electron.new
molecule.electrons = [valid_electron, invalid_electron]
@@ -443,7 +443,7 @@ class TestDefaultAutosaveAssociationOnAHasManyAssociationWithAcceptsNestedAttrib
assert_not invalid_electron.valid?
assert valid_electron.valid?
assert_not molecule.valid?
- assert_equal [{error: :blank}], molecule.errors.details["electrons.name"]
+ assert_equal [{ error: :blank }], molecule.errors.details[:"electrons.name"]
end
def test_errors_details_should_be_indexed_when_passed_as_array
@@ -457,8 +457,8 @@ class TestDefaultAutosaveAssociationOnAHasManyAssociationWithAcceptsNestedAttrib
assert_not tuning_peg_invalid.valid?
assert tuning_peg_valid.valid?
assert_not guitar.valid?
- assert_equal [{error: :not_a_number, value: nil}] , guitar.errors.details["tuning_pegs[1].pitch"]
- assert_equal [], guitar.errors.details["tuning_pegs.pitch"]
+ assert_equal [{ error: :not_a_number, value: nil }], guitar.errors.details[:"tuning_pegs[1].pitch"]
+ assert_equal [], guitar.errors.details[:"tuning_pegs.pitch"]
end
def test_errors_details_should_be_indexed_when_global_flag_is_set
@@ -466,7 +466,7 @@ class TestDefaultAutosaveAssociationOnAHasManyAssociationWithAcceptsNestedAttrib
ActiveRecord::Base.index_nested_attribute_errors = true
molecule = Molecule.new
- valid_electron = Electron.new(name: 'electron')
+ valid_electron = Electron.new(name: "electron")
invalid_electron = Electron.new
molecule.electrons = [valid_electron, invalid_electron]
@@ -474,15 +474,15 @@ class TestDefaultAutosaveAssociationOnAHasManyAssociationWithAcceptsNestedAttrib
assert_not invalid_electron.valid?
assert valid_electron.valid?
assert_not molecule.valid?
- assert_equal [{error: :blank}], molecule.errors.details["electrons[1].name"]
- assert_equal [], molecule.errors.details["electrons.name"]
+ assert_equal [{ error: :blank }], molecule.errors.details[:"electrons[1].name"]
+ assert_equal [], molecule.errors.details[:"electrons.name"]
ensure
ActiveRecord::Base.index_nested_attribute_errors = old_attribute_config
end
def test_valid_adding_with_nested_attributes
molecule = Molecule.new
- valid_electron = Electron.new(name: 'electron')
+ valid_electron = Electron.new(name: "electron")
molecule.electrons = [valid_electron]
molecule.save
@@ -585,16 +585,16 @@ class TestDefaultAutosaveAssociationOnAHasManyAssociation < ActiveRecord::TestCa
firm.save
firm.reload
assert_equal 2, firm.clients.length
- assert firm.clients.include?(companies(:second_client))
+ assert_includes firm.clients, companies(:second_client)
end
def test_assign_ids_for_through_a_belongs_to
- post = Post.new(:title => "Assigning IDs works!", :body => "You heard it here first, folks!")
+ post = Post.new(title: "Assigning IDs works!", body: "You heard it here first, folks!")
post.person_ids = [people(:david).id, people(:michael).id]
post.save
post.reload
assert_equal 2, post.people.length
- assert post.people.include?(people(:david))
+ assert_includes post.people, people(:david)
end
def test_build_before_save
@@ -602,7 +602,7 @@ class TestDefaultAutosaveAssociationOnAHasManyAssociation < ActiveRecord::TestCa
new_client = assert_no_queries(ignore_none: false) { company.clients_of_firm.build("name" => "Another Client") }
assert !company.clients_of_firm.loaded?
- company.name += '-changed'
+ company.name += "-changed"
assert_queries(2) { assert company.save }
assert new_client.persisted?
assert_equal 3, company.clients_of_firm.reload.size
@@ -610,19 +610,19 @@ class TestDefaultAutosaveAssociationOnAHasManyAssociation < ActiveRecord::TestCa
def test_build_many_before_save
company = companies(:first_firm)
- assert_no_queries(ignore_none: false) { company.clients_of_firm.build([{"name" => "Another Client"}, {"name" => "Another Client II"}]) }
+ assert_no_queries(ignore_none: false) { company.clients_of_firm.build([{ "name" => "Another Client" }, { "name" => "Another Client II" }]) }
- company.name += '-changed'
+ company.name += "-changed"
assert_queries(3) { assert company.save }
assert_equal 4, company.clients_of_firm.reload.size
end
def test_build_via_block_before_save
company = companies(:first_firm)
- new_client = assert_no_queries(ignore_none: false) { company.clients_of_firm.build {|client| client.name = "Another Client" } }
+ new_client = assert_no_queries(ignore_none: false) { company.clients_of_firm.build { |client| client.name = "Another Client" } }
assert !company.clients_of_firm.loaded?
- company.name += '-changed'
+ company.name += "-changed"
assert_queries(2) { assert company.save }
assert new_client.persisted?
assert_equal 3, company.clients_of_firm.reload.size
@@ -631,12 +631,12 @@ class TestDefaultAutosaveAssociationOnAHasManyAssociation < ActiveRecord::TestCa
def test_build_many_via_block_before_save
company = companies(:first_firm)
assert_no_queries(ignore_none: false) do
- company.clients_of_firm.build([{"name" => "Another Client"}, {"name" => "Another Client II"}]) do |client|
+ company.clients_of_firm.build([{ "name" => "Another Client" }, { "name" => "Another Client II" }]) do |client|
client.name = "changed"
end
end
- company.name += '-changed'
+ company.name += "-changed"
assert_queries(3) { assert company.save }
assert_equal 4, company.clients_of_firm.reload.size
end
@@ -647,7 +647,7 @@ class TestDefaultAutosaveAssociationOnAHasManyAssociation < ActiveRecord::TestCa
assert firm.save
firm.reload
assert_equal 2, firm.clients.length
- assert firm.clients.include?(Client.find_by_name("New Client"))
+ assert_includes firm.clients, Client.find_by_name("New Client")
end
end
@@ -715,8 +715,8 @@ class TestDefaultAutosaveAssociationOnNewRecord < ActiveRecord::TestCase
end
def test_autosave_new_record_with_after_create_callback
- post = PostWithAfterCreateCallback.new(title: 'Captain Murphy', body: 'is back')
- post.comments.build(body: 'foo')
+ post = PostWithAfterCreateCallback.new(title: "Captain Murphy", body: "is back")
+ post.comments.build(body: "foo")
post.save!
assert_not_nil post.author_id
@@ -727,8 +727,8 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase
self.use_transactional_tests = false
setup do
- @pirate = Pirate.create(:catchphrase => "Don' botharrr talkin' like one, savvy?")
- @ship = @pirate.create_ship(:name => 'Nights Dirty Lightning')
+ @pirate = Pirate.create(catchphrase: "Don' botharrr talkin' like one, savvy?")
+ @ship = @pirate.create_ship(name: "Nights Dirty Lightning")
end
teardown do
@@ -764,12 +764,12 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase
end
def test_should_skip_validation_on_a_child_association_if_marked_for_destruction
- @pirate.ship.name = ''
+ @pirate.ship.name = ""
assert !@pirate.valid?
@pirate.ship.mark_for_destruction
@pirate.ship.expects(:valid?).never
- assert_difference('Ship.count', -1) { @pirate.save! }
+ assert_difference("Ship.count", -1) { @pirate.save! }
end
def test_a_child_marked_for_destruction_should_not_be_destroyed_twice
@@ -787,11 +787,12 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase
def save(*args)
super
destroy
- raise 'Oh noes!'
+ raise "Oh noes!"
end
end
@ship.pirate.catchphrase = "Changed Catchphrase"
+ @ship.name_will_change!
assert_raise(RuntimeError) { assert !@pirate.save }
assert_not_nil @pirate.reload.ship
@@ -824,12 +825,12 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase
end
def test_should_skip_validation_on_a_parent_association_if_marked_for_destruction
- @ship.pirate.catchphrase = ''
+ @ship.pirate.catchphrase = ""
assert !@ship.valid?
@ship.pirate.mark_for_destruction
@ship.pirate.expects(:valid?).never
- assert_difference('Pirate.count', -1) { @ship.save! }
+ assert_difference("Pirate.count", -1) { @ship.save! }
end
def test_a_parent_marked_for_destruction_should_not_be_destroyed_twice
@@ -847,7 +848,7 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase
def save(*args)
super
destroy
- raise 'Oh noes!'
+ raise "Oh noes!"
end
end
@@ -858,16 +859,16 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase
end
def test_should_save_changed_child_objects_if_parent_is_saved
- @pirate = @ship.create_pirate(:catchphrase => "Don' botharrr talkin' like one, savvy?")
- @parrot = @pirate.parrots.create!(:name => 'Posideons Killer')
+ @pirate = @ship.create_pirate(catchphrase: "Don' botharrr talkin' like one, savvy?")
+ @parrot = @pirate.parrots.create!(name: "Posideons Killer")
@parrot.name = "NewName"
@ship.save
- assert_equal 'NewName', @parrot.reload.name
+ assert_equal "NewName", @parrot.reload.name
end
def test_should_destroy_has_many_as_part_of_the_save_transaction_if_they_were_marked_for_destruction
- 2.times { |i| @pirate.birds.create!(:name => "birds_#{i}") }
+ 2.times { |i| @pirate.birds.create!(name: "birds_#{i}") }
assert !@pirate.birds.any?(&:marked_for_destruction?)
@@ -891,9 +892,9 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase
end
def test_should_skip_validation_on_has_many_if_marked_for_destruction
- 2.times { |i| @pirate.birds.create!(:name => "birds_#{i}") }
+ 2.times { |i| @pirate.birds.create!(name: "birds_#{i}") }
- @pirate.birds.each { |bird| bird.name = '' }
+ @pirate.birds.each { |bird| bird.name = "" }
assert !@pirate.valid?
@pirate.birds.each do |bird|
@@ -904,9 +905,9 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase
end
def test_should_skip_validation_on_has_many_if_destroyed
- @pirate.birds.create!(:name => "birds_1")
+ @pirate.birds.create!(name: "birds_1")
- @pirate.birds.each { |bird| bird.name = '' }
+ @pirate.birds.each { |bird| bird.name = "" }
assert !@pirate.valid?
@pirate.birds.each(&:destroy)
@@ -914,7 +915,7 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase
end
def test_a_child_marked_for_destruction_should_not_be_destroyed_twice_while_saving_has_many
- @pirate.birds.create!(:name => "birds_1")
+ @pirate.birds.create!(name: "birds_1")
@pirate.birds.each(&:mark_for_destruction)
assert @pirate.save
@@ -924,14 +925,14 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase
end
def test_should_rollback_destructions_if_an_exception_occurred_while_saving_has_many
- 2.times { |i| @pirate.birds.create!(:name => "birds_#{i}") }
+ 2.times { |i| @pirate.birds.create!(name: "birds_#{i}") }
before = @pirate.birds.map { |c| c.mark_for_destruction ; c }
# Stub the destroy method of the second child to raise an exception
class << before.last
def destroy(*args)
super
- raise 'Oh noes!'
+ raise "Oh noes!"
end
end
@@ -940,9 +941,9 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase
end
def test_when_new_record_a_child_marked_for_destruction_should_not_affect_other_records_from_saving
- @pirate = @ship.build_pirate(:catchphrase => "Arr' now I shall keep me eye on you matey!") # new record
+ @pirate = @ship.build_pirate(catchphrase: "Arr' now I shall keep me eye on you matey!") # new record
- 3.times { |i| @pirate.birds.build(:name => "birds_#{i}") }
+ 3.times { |i| @pirate.birds.build(name: "birds_#{i}") }
@pirate.birds[1].mark_for_destruction
@pirate.save!
@@ -968,8 +969,8 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase
define_method("test_should_run_add_callback_#{callback_type}s_for_has_many") do
association_name_with_callbacks = "birds_with_#{callback_type}_callbacks"
- pirate = Pirate.new(:catchphrase => "Arr")
- pirate.send(association_name_with_callbacks).build(:name => "Crowe the One-Eyed")
+ pirate = Pirate.new(catchphrase: "Arr")
+ pirate.send(association_name_with_callbacks).build(name: "Crowe the One-Eyed")
expected = [
"before_adding_#{callback_type}_bird_<new>",
@@ -982,7 +983,7 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase
define_method("test_should_run_remove_callback_#{callback_type}s_for_has_many") do
association_name_with_callbacks = "birds_with_#{callback_type}_callbacks"
- @pirate.send(association_name_with_callbacks).create!(:name => "Crowe the One-Eyed")
+ @pirate.send(association_name_with_callbacks).create!(name: "Crowe the One-Eyed")
@pirate.send(association_name_with_callbacks).each(&:mark_for_destruction)
child_id = @pirate.send(association_name_with_callbacks).first.id
@@ -999,7 +1000,7 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase
end
def test_should_destroy_habtm_as_part_of_the_save_transaction_if_they_were_marked_for_destruction
- 2.times { |i| @pirate.parrots.create!(:name => "parrots_#{i}") }
+ 2.times { |i| @pirate.parrots.create!(name: "parrots_#{i}") }
assert !@pirate.parrots.any?(&:marked_for_destruction?)
@pirate.parrots.each(&:mark_for_destruction)
@@ -1015,9 +1016,9 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase
end
def test_should_skip_validation_on_habtm_if_marked_for_destruction
- 2.times { |i| @pirate.parrots.create!(:name => "parrots_#{i}") }
+ 2.times { |i| @pirate.parrots.create!(name: "parrots_#{i}") }
- @pirate.parrots.each { |parrot| parrot.name = '' }
+ @pirate.parrots.each { |parrot| parrot.name = "" }
assert !@pirate.valid?
@pirate.parrots.each do |parrot|
@@ -1030,9 +1031,9 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase
end
def test_should_skip_validation_on_habtm_if_destroyed
- @pirate.parrots.create!(:name => "parrots_1")
+ @pirate.parrots.create!(name: "parrots_1")
- @pirate.parrots.each { |parrot| parrot.name = '' }
+ @pirate.parrots.each { |parrot| parrot.name = "" }
assert !@pirate.valid?
@pirate.parrots.each(&:destroy)
@@ -1040,7 +1041,7 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase
end
def test_a_child_marked_for_destruction_should_not_be_destroyed_twice_while_saving_habtm
- @pirate.parrots.create!(:name => "parrots_1")
+ @pirate.parrots.create!(name: "parrots_1")
@pirate.parrots.each(&:mark_for_destruction)
assert @pirate.save
@@ -1053,13 +1054,13 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase
end
def test_should_rollback_destructions_if_an_exception_occurred_while_saving_habtm
- 2.times { |i| @pirate.parrots.create!(:name => "parrots_#{i}") }
+ 2.times { |i| @pirate.parrots.create!(name: "parrots_#{i}") }
before = @pirate.parrots.map { |c| c.mark_for_destruction ; c }
class << @pirate.association(:parrots)
def destroy(*args)
super
- raise 'Oh noes!'
+ raise "Oh noes!"
end
end
@@ -1072,8 +1073,8 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase
define_method("test_should_run_add_callback_#{callback_type}s_for_habtm") do
association_name_with_callbacks = "parrots_with_#{callback_type}_callbacks"
- pirate = Pirate.new(:catchphrase => "Arr")
- pirate.send(association_name_with_callbacks).build(:name => "Crowe the One-Eyed")
+ pirate = Pirate.new(catchphrase: "Arr")
+ pirate.send(association_name_with_callbacks).build(name: "Crowe the One-Eyed")
expected = [
"before_adding_#{callback_type}_parrot_<new>",
@@ -1086,7 +1087,7 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase
define_method("test_should_run_remove_callback_#{callback_type}s_for_habtm") do
association_name_with_callbacks = "parrots_with_#{callback_type}_callbacks"
- @pirate.send(association_name_with_callbacks).create!(:name => "Crowe the One-Eyed")
+ @pirate.send(association_name_with_callbacks).create!(name: "Crowe the One-Eyed")
@pirate.send(association_name_with_callbacks).each(&:mark_for_destruction)
child_id = @pirate.send(association_name_with_callbacks).first.id
@@ -1108,21 +1109,21 @@ class TestAutosaveAssociationOnAHasOneAssociation < ActiveRecord::TestCase
def setup
super
- @pirate = Pirate.create(:catchphrase => "Don' botharrr talkin' like one, savvy?")
- @ship = @pirate.create_ship(:name => 'Nights Dirty Lightning')
+ @pirate = Pirate.create(catchphrase: "Don' botharrr talkin' like one, savvy?")
+ @ship = @pirate.create_ship(name: "Nights Dirty Lightning")
end
def test_should_still_work_without_an_associated_model
@ship.destroy
@pirate.reload.catchphrase = "Arr"
@pirate.save
- assert_equal 'Arr', @pirate.reload.catchphrase
+ assert_equal "Arr", @pirate.reload.catchphrase
end
def test_should_automatically_save_the_associated_model
- @pirate.ship.name = 'The Vile Insanity'
+ @pirate.ship.name = "The Vile Insanity"
@pirate.save
- assert_equal 'The Vile Insanity', @pirate.reload.ship.name
+ assert_equal "The Vile Insanity", @pirate.reload.ship.name
end
def test_changed_for_autosave_should_handle_cycles
@@ -1130,19 +1131,19 @@ class TestAutosaveAssociationOnAHasOneAssociation < ActiveRecord::TestCase
assert_queries(0) { @ship.save! }
@parrot = @pirate.parrots.create(name: "some_name")
- @parrot.name="changed_name"
+ @parrot.name = "changed_name"
assert_queries(1) { @ship.save! }
assert_queries(0) { @ship.save! }
end
def test_should_automatically_save_bang_the_associated_model
- @pirate.ship.name = 'The Vile Insanity'
+ @pirate.ship.name = "The Vile Insanity"
@pirate.save!
- assert_equal 'The Vile Insanity', @pirate.reload.ship.name
+ assert_equal "The Vile Insanity", @pirate.reload.ship.name
end
def test_should_automatically_validate_the_associated_model
- @pirate.ship.name = ''
+ @pirate.ship.name = ""
assert @pirate.invalid?
assert @pirate.errors[:"ship.name"].any?
end
@@ -1158,7 +1159,7 @@ class TestAutosaveAssociationOnAHasOneAssociation < ActiveRecord::TestCase
def test_should_not_ignore_different_error_messages_on_the_same_attribute
old_validators = Ship._validators.deep_dup
old_callbacks = Ship._validate_callbacks.deep_dup
- Ship.validates_format_of :name, :with => /\w/
+ Ship.validates_format_of :name, with: /\w/
@pirate.ship.name = ""
@pirate.catchphrase = nil
assert @pirate.invalid?
@@ -1169,48 +1170,48 @@ class TestAutosaveAssociationOnAHasOneAssociation < ActiveRecord::TestCase
end
def test_should_still_allow_to_bypass_validations_on_the_associated_model
- @pirate.catchphrase = ''
- @pirate.ship.name = ''
- @pirate.save(:validate => false)
+ @pirate.catchphrase = ""
+ @pirate.ship.name = ""
+ @pirate.save(validate: false)
# Oracle saves empty string as NULL
if current_adapter?(:OracleAdapter)
assert_equal [nil, nil], [@pirate.reload.catchphrase, @pirate.ship.name]
else
- assert_equal ['', ''], [@pirate.reload.catchphrase, @pirate.ship.name]
+ assert_equal ["", ""], [@pirate.reload.catchphrase, @pirate.ship.name]
end
end
def test_should_allow_to_bypass_validations_on_associated_models_at_any_depth
- 2.times { |i| @pirate.ship.parts.create!(:name => "part #{i}") }
+ 2.times { |i| @pirate.ship.parts.create!(name: "part #{i}") }
- @pirate.catchphrase = ''
- @pirate.ship.name = ''
- @pirate.ship.parts.each { |part| part.name = '' }
- @pirate.save(:validate => false)
+ @pirate.catchphrase = ""
+ @pirate.ship.name = ""
+ @pirate.ship.parts.each { |part| part.name = "" }
+ @pirate.save(validate: false)
values = [@pirate.reload.catchphrase, @pirate.ship.name, *@pirate.ship.parts.map(&:name)]
# Oracle saves empty string as NULL
if current_adapter?(:OracleAdapter)
assert_equal [nil, nil, nil, nil], values
else
- assert_equal ['', '', '', ''], values
+ assert_equal ["", "", "", ""], values
end
end
def test_should_still_raise_an_ActiveRecordRecord_Invalid_exception_if_we_want_that
- @pirate.ship.name = ''
+ @pirate.ship.name = ""
assert_raise(ActiveRecord::RecordInvalid) do
@pirate.save!
end
end
def test_should_not_save_and_return_false_if_a_callback_cancelled_saving
- pirate = Pirate.new(:catchphrase => 'Arr')
- ship = pirate.build_ship(:name => 'The Vile Insanity')
+ pirate = Pirate.new(catchphrase: "Arr")
+ ship = pirate.build_ship(name: "The Vile Insanity")
ship.cancel_save_from_callback = true
- assert_no_difference 'Pirate.count' do
- assert_no_difference 'Ship.count' do
+ assert_no_difference "Pirate.count" do
+ assert_no_difference "Ship.count" do
assert !pirate.save
end
end
@@ -1219,14 +1220,14 @@ class TestAutosaveAssociationOnAHasOneAssociation < ActiveRecord::TestCase
def test_should_rollback_any_changes_if_an_exception_occurred_while_saving
before = [@pirate.catchphrase, @pirate.ship.name]
- @pirate.catchphrase = 'Arr'
- @pirate.ship.name = 'The Vile Insanity'
+ @pirate.catchphrase = "Arr"
+ @pirate.ship.name = "The Vile Insanity"
# Stub the save method of the @pirate.ship instance to raise an exception
class << @pirate.ship
def save(*args)
super
- raise 'Oh noes!'
+ raise "Oh noes!"
end
end
@@ -1235,7 +1236,7 @@ class TestAutosaveAssociationOnAHasOneAssociation < ActiveRecord::TestCase
end
def test_should_not_load_the_associated_model
- assert_queries(1) { @pirate.catchphrase = 'Arr'; @pirate.save! }
+ assert_queries(1) { @pirate.catchphrase = "Arr"; @pirate.save! }
end
def test_mark_for_destruction_is_ignored_without_autosave_true
@@ -1260,7 +1261,7 @@ class TestAutosaveAssociationOnAHasOneThroughAssociation < ActiveRecord::TestCas
class << @member.organization
def save(*args)
super
- raise 'Oh noes!'
+ raise "Oh noes!"
end
end
assert_nothing_raised { @member.save }
@@ -1272,31 +1273,31 @@ class TestAutosaveAssociationOnABelongsToAssociation < ActiveRecord::TestCase
def setup
super
- @ship = Ship.create(:name => 'Nights Dirty Lightning')
- @pirate = @ship.create_pirate(:catchphrase => "Don' botharrr talkin' like one, savvy?")
+ @ship = Ship.create(name: "Nights Dirty Lightning")
+ @pirate = @ship.create_pirate(catchphrase: "Don' botharrr talkin' like one, savvy?")
end
def test_should_still_work_without_an_associated_model
@pirate.destroy
@ship.reload.name = "The Vile Insanity"
@ship.save
- assert_equal 'The Vile Insanity', @ship.reload.name
+ assert_equal "The Vile Insanity", @ship.reload.name
end
def test_should_automatically_save_the_associated_model
- @ship.pirate.catchphrase = 'Arr'
+ @ship.pirate.catchphrase = "Arr"
@ship.save
- assert_equal 'Arr', @ship.reload.pirate.catchphrase
+ assert_equal "Arr", @ship.reload.pirate.catchphrase
end
def test_should_automatically_save_bang_the_associated_model
- @ship.pirate.catchphrase = 'Arr'
+ @ship.pirate.catchphrase = "Arr"
@ship.save!
- assert_equal 'Arr', @ship.reload.pirate.catchphrase
+ assert_equal "Arr", @ship.reload.pirate.catchphrase
end
def test_should_automatically_validate_the_associated_model
- @ship.pirate.catchphrase = ''
+ @ship.pirate.catchphrase = ""
assert @ship.invalid?
assert @ship.errors[:"pirate.catchphrase"].any?
end
@@ -1310,31 +1311,31 @@ class TestAutosaveAssociationOnABelongsToAssociation < ActiveRecord::TestCase
end
def test_should_still_allow_to_bypass_validations_on_the_associated_model
- @ship.pirate.catchphrase = ''
- @ship.name = ''
- @ship.save(:validate => false)
+ @ship.pirate.catchphrase = ""
+ @ship.name = ""
+ @ship.save(validate: false)
# Oracle saves empty string as NULL
if current_adapter?(:OracleAdapter)
assert_equal [nil, nil], [@ship.reload.name, @ship.pirate.catchphrase]
else
- assert_equal ['', ''], [@ship.reload.name, @ship.pirate.catchphrase]
+ assert_equal ["", ""], [@ship.reload.name, @ship.pirate.catchphrase]
end
end
def test_should_still_raise_an_ActiveRecordRecord_Invalid_exception_if_we_want_that
- @ship.pirate.catchphrase = ''
+ @ship.pirate.catchphrase = ""
assert_raise(ActiveRecord::RecordInvalid) do
@ship.save!
end
end
def test_should_not_save_and_return_false_if_a_callback_cancelled_saving
- ship = Ship.new(:name => 'The Vile Insanity')
- pirate = ship.build_pirate(:catchphrase => 'Arr')
+ ship = Ship.new(name: "The Vile Insanity")
+ pirate = ship.build_pirate(catchphrase: "Arr")
pirate.cancel_save_from_callback = true
- assert_no_difference 'Ship.count' do
- assert_no_difference 'Pirate.count' do
+ assert_no_difference "Ship.count" do
+ assert_no_difference "Pirate.count" do
assert !ship.save
end
end
@@ -1343,14 +1344,14 @@ class TestAutosaveAssociationOnABelongsToAssociation < ActiveRecord::TestCase
def test_should_rollback_any_changes_if_an_exception_occurred_while_saving
before = [@ship.pirate.catchphrase, @ship.name]
- @ship.pirate.catchphrase = 'Arr'
- @ship.name = 'The Vile Insanity'
+ @ship.pirate.catchphrase = "Arr"
+ @ship.name = "The Vile Insanity"
# Stub the save method of the @ship.pirate instance to raise an exception
class << @ship.pirate
def save(*args)
super
- raise 'Oh noes!'
+ raise "Oh noes!"
end
end
@@ -1359,13 +1360,13 @@ class TestAutosaveAssociationOnABelongsToAssociation < ActiveRecord::TestCase
end
def test_should_not_load_the_associated_model
- assert_queries(1) { @ship.name = 'The Vile Insanity'; @ship.save! }
+ assert_queries(1) { @ship.name = "The Vile Insanity"; @ship.save! }
end
end
module AutosaveAssociationOnACollectionAssociationTests
def test_should_automatically_save_the_associated_models
- new_names = ['Grace OMalley', 'Privateers Greed']
+ new_names = ["Grace OMalley", "Privateers Greed"]
@pirate.send(@association_name).each_with_index { |child, i| child.name = new_names[i] }
@pirate.save
@@ -1373,7 +1374,7 @@ module AutosaveAssociationOnACollectionAssociationTests
end
def test_should_automatically_save_bang_the_associated_models
- new_names = ['Grace OMalley', 'Privateers Greed']
+ new_names = ["Grace OMalley", "Privateers Greed"]
@pirate.send(@association_name).each_with_index { |child, i| child.name = new_names[i] }
@pirate.save!
@@ -1390,8 +1391,16 @@ module AutosaveAssociationOnACollectionAssociationTests
assert_equal "Squawky", parrot.reload.name
end
+ def test_should_not_update_children_when_parent_creation_with_no_reason
+ parrot = Parrot.create!(name: "Polly")
+ assert_equal 0, parrot.updated_count
+
+ Pirate.create!(parrot_ids: [parrot.id], catchphrase: "Arrrr")
+ assert_equal 0, parrot.reload.updated_count
+ end
+
def test_should_automatically_validate_the_associated_models
- @pirate.send(@association_name).each { |child| child.name = '' }
+ @pirate.send(@association_name).each { |child| child.name = "" }
assert !@pirate.valid?
assert_equal ["can't be blank"], @pirate.errors["#{@association_name}.name"]
@@ -1399,7 +1408,7 @@ module AutosaveAssociationOnACollectionAssociationTests
end
def test_should_not_use_default_invalid_error_on_associated_models
- @pirate.send(@association_name).build(:name => '')
+ @pirate.send(@association_name).build(name: "")
assert !@pirate.valid?
assert_equal ["can't be blank"], @pirate.errors["#{@association_name}.name"]
@@ -1407,11 +1416,11 @@ module AutosaveAssociationOnACollectionAssociationTests
end
def test_should_default_invalid_error_from_i18n
- I18n.backend.store_translations(:en, activerecord: {errors: { models:
+ I18n.backend.store_translations(:en, activerecord: { errors: { models:
{ @associated_model_name.to_s.to_sym => { blank: "cannot be blank" } }
- }})
+ } })
- @pirate.send(@association_name).build(name: '')
+ @pirate.send(@association_name).build(name: "")
assert !@pirate.valid?
assert_equal ["cannot be blank"], @pirate.errors["#{@association_name}.name"]
@@ -1422,7 +1431,7 @@ module AutosaveAssociationOnACollectionAssociationTests
end
def test_should_merge_errors_on_the_associated_models_onto_the_parent_even_if_it_is_not_valid
- @pirate.send(@association_name).each { |child| child.name = '' }
+ @pirate.send(@association_name).each { |child| child.name = "" }
@pirate.catchphrase = nil
assert !@pirate.valid?
@@ -1431,10 +1440,10 @@ module AutosaveAssociationOnACollectionAssociationTests
end
def test_should_allow_to_bypass_validations_on_the_associated_models_on_update
- @pirate.catchphrase = ''
- @pirate.send(@association_name).each { |child| child.name = '' }
+ @pirate.catchphrase = ""
+ @pirate.send(@association_name).each { |child| child.name = "" }
- assert @pirate.save(:validate => false)
+ assert @pirate.save(validate: false)
# Oracle saves empty string as NULL
if current_adapter?(:OracleAdapter)
assert_equal [nil, nil, nil], [
@@ -1443,7 +1452,7 @@ module AutosaveAssociationOnACollectionAssociationTests
@pirate.send(@association_name).last.name
]
else
- assert_equal ['', '', ''], [
+ assert_equal ["", "", ""], [
@pirate.reload.catchphrase,
@pirate.send(@association_name).first.name,
@pirate.send(@association_name).last.name
@@ -1461,24 +1470,24 @@ module AutosaveAssociationOnACollectionAssociationTests
def test_should_allow_to_bypass_validations_on_the_associated_models_on_create
assert_difference("#{ @association_name == :birds ? 'Bird' : 'Parrot' }.count", 2) do
2.times { @pirate.send(@association_name).build }
- @pirate.save(:validate => false)
+ @pirate.save(validate: false)
end
end
def test_should_not_save_and_return_false_if_a_callback_cancelled_saving_in_either_create_or_update
- @pirate.catchphrase = 'Changed'
- @child_1.name = 'Changed'
+ @pirate.catchphrase = "Changed"
+ @child_1.name = "Changed"
@child_1.cancel_save_from_callback = true
assert !@pirate.save
assert_equal "Don' botharrr talkin' like one, savvy?", @pirate.reload.catchphrase
assert_equal "Posideons Killer", @child_1.reload.name
- new_pirate = Pirate.new(:catchphrase => 'Arr')
- new_child = new_pirate.send(@association_name).build(:name => 'Grace OMalley')
+ new_pirate = Pirate.new(catchphrase: "Arr")
+ new_child = new_pirate.send(@association_name).build(name: "Grace OMalley")
new_child.cancel_save_from_callback = true
- assert_no_difference 'Pirate.count' do
+ assert_no_difference "Pirate.count" do
assert_no_difference "#{new_child.class.name}.count" do
assert !new_pirate.save
end
@@ -1487,16 +1496,16 @@ module AutosaveAssociationOnACollectionAssociationTests
def test_should_rollback_any_changes_if_an_exception_occurred_while_saving
before = [@pirate.catchphrase, *@pirate.send(@association_name).map(&:name)]
- new_names = ['Grace OMalley', 'Privateers Greed']
+ new_names = ["Grace OMalley", "Privateers Greed"]
- @pirate.catchphrase = 'Arr'
+ @pirate.catchphrase = "Arr"
@pirate.send(@association_name).each_with_index { |child, i| child.name = new_names[i] }
# Stub the save method of the first child instance to raise an exception
class << @pirate.send(@association_name).first
def save(*args)
super
- raise 'Oh noes!'
+ raise "Oh noes!"
end
end
@@ -1505,20 +1514,20 @@ module AutosaveAssociationOnACollectionAssociationTests
end
def test_should_still_raise_an_ActiveRecordRecord_Invalid_exception_if_we_want_that
- @pirate.send(@association_name).each { |child| child.name = '' }
+ @pirate.send(@association_name).each { |child| child.name = "" }
assert_raise(ActiveRecord::RecordInvalid) do
@pirate.save!
end
end
def test_should_not_load_the_associated_models_if_they_were_not_loaded_yet
- assert_queries(1) { @pirate.catchphrase = 'Arr'; @pirate.save! }
+ assert_queries(1) { @pirate.catchphrase = "Arr"; @pirate.save! }
@pirate.send(@association_name).load_target
assert_queries(3) do
- @pirate.catchphrase = 'Yarr'
- new_names = ['Grace OMalley', 'Privateers Greed']
+ @pirate.catchphrase = "Yarr"
+ new_names = ["Grace OMalley", "Privateers Greed"]
@pirate.send(@association_name).each_with_index { |child, i| child.name = new_names[i] }
@pirate.save!
end
@@ -1533,9 +1542,9 @@ class TestAutosaveAssociationOnAHasManyAssociation < ActiveRecord::TestCase
@association_name = :birds
@associated_model_name = :bird
- @pirate = Pirate.create(:catchphrase => "Don' botharrr talkin' like one, savvy?")
- @child_1 = @pirate.birds.create(:name => 'Posideons Killer')
- @child_2 = @pirate.birds.create(:name => 'Killer bandita Dionne')
+ @pirate = Pirate.create(catchphrase: "Don' botharrr talkin' like one, savvy?")
+ @child_1 = @pirate.birds.create(name: "Posideons Killer")
+ @child_2 = @pirate.birds.create(name: "Killer bandita Dionne")
end
include AutosaveAssociationOnACollectionAssociationTests
@@ -1551,8 +1560,8 @@ class TestAutosaveAssociationOnAHasAndBelongsToManyAssociation < ActiveRecord::T
@habtm = true
@pirate = Pirate.create(catchphrase: "Don' botharrr talkin' like one, savvy?")
- @child_1 = @pirate.parrots.create(name: 'Posideons Killer')
- @child_2 = @pirate.parrots.create(name: 'Killer bandita Dionne')
+ @child_1 = @pirate.parrots.create(name: "Posideons Killer")
+ @child_2 = @pirate.parrots.create(name: "Killer bandita Dionne")
end
include AutosaveAssociationOnACollectionAssociationTests
@@ -1568,8 +1577,8 @@ class TestAutosaveAssociationOnAHasAndBelongsToManyAssociationWithAcceptsNestedA
@habtm = true
@pirate = Pirate.create(catchphrase: "Don' botharrr talkin' like one, savvy?")
- @child_1 = @pirate.parrots.create(name: 'Posideons Killer')
- @child_2 = @pirate.parrots.create(name: 'Killer bandita Dionne')
+ @child_1 = @pirate.parrots.create(name: "Posideons Killer")
+ @child_2 = @pirate.parrots.create(name: "Killer bandita Dionne")
end
include AutosaveAssociationOnACollectionAssociationTests
@@ -1580,13 +1589,13 @@ class TestAutosaveAssociationValidationsOnAHasManyAssociation < ActiveRecord::Te
def setup
super
- @pirate = Pirate.create(:catchphrase => "Don' botharrr talkin' like one, savvy?")
- @pirate.birds.create(:name => 'cookoo')
+ @pirate = Pirate.create(catchphrase: "Don' botharrr talkin' like one, savvy?")
+ @pirate.birds.create(name: "cookoo")
end
test "should automatically validate associations" do
assert @pirate.valid?
- @pirate.birds.each { |bird| bird.name = '' }
+ @pirate.birds.each { |bird| bird.name = "" }
assert !@pirate.valid?
end
@@ -1597,20 +1606,20 @@ class TestAutosaveAssociationValidationsOnAHasOneAssociation < ActiveRecord::Tes
def setup
super
- @pirate = Pirate.create(:catchphrase => "Don' botharrr talkin' like one, savvy?")
- @pirate.create_ship(:name => 'titanic')
+ @pirate = Pirate.create(catchphrase: "Don' botharrr talkin' like one, savvy?")
+ @pirate.create_ship(name: "titanic")
super
end
test "should automatically validate associations with :validate => true" do
assert @pirate.valid?
- @pirate.ship.name = ''
+ @pirate.ship.name = ""
assert !@pirate.valid?
end
test "should not automatically add validate associations without :validate => true" do
assert @pirate.valid?
- @pirate.non_validated_ship.name = ''
+ @pirate.non_validated_ship.name = ""
assert @pirate.valid?
end
end
@@ -1620,18 +1629,18 @@ class TestAutosaveAssociationValidationsOnABelongsToAssociation < ActiveRecord::
def setup
super
- @pirate = Pirate.create(:catchphrase => "Don' botharrr talkin' like one, savvy?")
+ @pirate = Pirate.create(catchphrase: "Don' botharrr talkin' like one, savvy?")
end
test "should automatically validate associations with :validate => true" do
assert @pirate.valid?
- @pirate.parrot = Parrot.new(:name => '')
+ @pirate.parrot = Parrot.new(name: "")
assert !@pirate.valid?
end
test "should not automatically validate associations without :validate => true" do
assert @pirate.valid?
- @pirate.non_validated_parrot = Parrot.new(:name => '')
+ @pirate.non_validated_parrot = Parrot.new(name: "")
assert @pirate.valid?
end
end
@@ -1641,20 +1650,20 @@ class TestAutosaveAssociationValidationsOnAHABTMAssociation < ActiveRecord::Test
def setup
super
- @pirate = Pirate.create(:catchphrase => "Don' botharrr talkin' like one, savvy?")
+ @pirate = Pirate.create(catchphrase: "Don' botharrr talkin' like one, savvy?")
end
test "should automatically validate associations with :validate => true" do
assert @pirate.valid?
- @pirate.parrots = [ Parrot.new(:name => 'popuga') ]
- @pirate.parrots.each { |parrot| parrot.name = '' }
+ @pirate.parrots = [ Parrot.new(name: "popuga") ]
+ @pirate.parrots.each { |parrot| parrot.name = "" }
assert !@pirate.valid?
end
test "should not automatically validate associations without :validate => true" do
assert @pirate.valid?
- @pirate.non_validated_parrots = [ Parrot.new(:name => 'popuga') ]
- @pirate.non_validated_parrots.each { |parrot| parrot.name = '' }
+ @pirate.non_validated_parrots = [ Parrot.new(name: "popuga") ]
+ @pirate.non_validated_parrots.each { |parrot| parrot.name = "" }
assert @pirate.valid?
end
end
@@ -1695,6 +1704,30 @@ end
class TestAutosaveAssociationWithTouch < ActiveRecord::TestCase
def test_autosave_with_touch_should_not_raise_system_stack_error
invoice = Invoice.create
- assert_nothing_raised { invoice.line_items.create(:amount => 10) }
+ assert_nothing_raised { invoice.line_items.create(amount: 10) }
+ end
+end
+
+class TestAutosaveAssociationOnAHasManyAssociationWithInverse < ActiveRecord::TestCase
+ class Post < ActiveRecord::Base
+ has_many :comments, inverse_of: :post
+ end
+
+ class Comment < ActiveRecord::Base
+ belongs_to :post, inverse_of: :comments
+
+ attr_accessor :post_comments_count
+ after_save do
+ self.post_comments_count = post.comments.count
+ end
+ end
+
+ def test_after_save_callback_with_autosave
+ post = Post.new(title: "Test", body: "...")
+ comment = post.comments.build(body: "...")
+ post.save!
+
+ assert_equal 1, post.comments.count
+ assert_equal 1, comment.post_comments_count
end
end
diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb
index 7b1ce40c0d..a611cc208c 100644
--- a/activerecord/test/cases/base_test.rb
+++ b/activerecord/test/cases/base_test.rb
@@ -1,30 +1,31 @@
require "cases/helper"
-require 'models/post'
-require 'models/author'
-require 'models/topic'
-require 'models/reply'
-require 'models/category'
-require 'models/company'
-require 'models/customer'
-require 'models/developer'
-require 'models/computer'
-require 'models/project'
-require 'models/default'
-require 'models/auto_id'
-require 'models/boolean'
-require 'models/column_name'
-require 'models/subscriber'
-require 'models/comment'
-require 'models/minimalistic'
-require 'models/warehouse_thing'
-require 'models/parrot'
-require 'models/person'
-require 'models/edge'
-require 'models/joke'
-require 'models/bird'
-require 'models/car'
-require 'models/bulb'
-require 'concurrent/atomic/count_down_latch'
+require "models/post"
+require "models/author"
+require "models/topic"
+require "models/reply"
+require "models/category"
+require "models/categorization"
+require "models/company"
+require "models/customer"
+require "models/developer"
+require "models/computer"
+require "models/project"
+require "models/default"
+require "models/auto_id"
+require "models/boolean"
+require "models/column_name"
+require "models/subscriber"
+require "models/comment"
+require "models/minimalistic"
+require "models/warehouse_thing"
+require "models/parrot"
+require "models/person"
+require "models/edge"
+require "models/joke"
+require "models/bird"
+require "models/car"
+require "models/bulb"
+require "concurrent/atomic/count_down_latch"
class FirstAbstractClass < ActiveRecord::Base
self.abstract_class = true
@@ -33,8 +34,6 @@ class SecondAbstractClass < FirstAbstractClass
self.abstract_class = true
end
class Photo < SecondAbstractClass; end
-class Category < ActiveRecord::Base; end
-class Categorization < ActiveRecord::Base; end
class Smarts < ActiveRecord::Base; end
class CreditCard < ActiveRecord::Base
class PinNumber < ActiveRecord::Base
@@ -45,8 +44,6 @@ class CreditCard < ActiveRecord::Base
class Brand < Category; end
end
class MasterCreditCard < ActiveRecord::Base; end
-class Post < ActiveRecord::Base; end
-class Computer < ActiveRecord::Base; end
class NonExistentTable < ActiveRecord::Base; end
class TestOracleDefault < ActiveRecord::Base; end
@@ -56,12 +53,6 @@ end
class Weird < ActiveRecord::Base; end
-class Boolean < ActiveRecord::Base
- def has_fun
- super
- end
-end
-
class LintTest < ActiveRecord::TestCase
include ActiveModel::Lint::Tests
@@ -73,17 +64,17 @@ class LintTest < ActiveRecord::TestCase
end
class BasicsTest < ActiveRecord::TestCase
- fixtures :topics, :companies, :developers, :projects, :computers, :accounts, :minimalistics, 'warehouse-things', :authors, :categorizations, :categories, :posts
+ fixtures :topics, :companies, :developers, :projects, :computers, :accounts, :minimalistics, "warehouse-things", :authors, :categorizations, :categories, :posts
def test_column_names_are_escaped
conn = ActiveRecord::Base.connection
classname = conn.class.name[/[^:]*$/]
badchar = {
- 'SQLite3Adapter' => '"',
- 'Mysql2Adapter' => '`',
- 'PostgreSQLAdapter' => '"',
- 'OracleAdapter' => '"',
- 'FbAdapter' => '"'
+ "SQLite3Adapter" => '"',
+ "Mysql2Adapter" => "`",
+ "PostgreSQLAdapter" => '"',
+ "OracleAdapter" => '"',
+ "FbAdapter" => '"'
}.fetch(classname) {
raise "need a bad char for #{classname}"
}
@@ -100,19 +91,18 @@ class BasicsTest < ActiveRecord::TestCase
def test_columns_should_obey_set_primary_key
pk = Subscriber.columns_hash[Subscriber.primary_key]
- assert_equal 'nick', pk.name, 'nick should be primary key'
+ assert_equal "nick", pk.name, "nick should be primary key"
end
def test_primary_key_with_no_id
assert_nil Edge.primary_key
end
- unless current_adapter?(:PostgreSQLAdapter, :OracleAdapter, :SQLServerAdapter, :FbAdapter)
- def test_limit_with_comma
- assert_deprecated do
- assert Topic.limit("1,2").to_a
- end
- end
+ def test_many_mutations
+ car = Car.new name: "<3<3<3"
+ car.engines_count = 0
+ 20_000.times { car.engines_count += 1 }
+ assert car.save
end
def test_limit_without_comma
@@ -137,10 +127,8 @@ class BasicsTest < ActiveRecord::TestCase
end
def test_limit_should_sanitize_sql_injection_for_limit_with_commas
- assert_deprecated do
- assert_raises(ArgumentError) do
- Topic.limit("1, 7 procedure help()").to_a
- end
+ assert_raises(ArgumentError) do
+ Topic.limit("1, 7 procedure help()").to_a
end
end
@@ -164,17 +152,17 @@ class BasicsTest < ActiveRecord::TestCase
def test_previously_changed
topic = Topic.first
- topic.title = '<3<3<3'
+ topic.title = "<3<3<3"
assert_equal({}, topic.previous_changes)
topic.save!
expected = ["The First Topic", "<3<3<3"]
- assert_equal(expected, topic.previous_changes['title'])
+ assert_equal(expected, topic.previous_changes["title"])
end
def test_previously_changed_dup
topic = Topic.first
- topic.title = '<3<3<3'
+ topic.title = "<3<3<3"
topic.save!
t2 = topic.dup
@@ -211,7 +199,7 @@ class BasicsTest < ActiveRecord::TestCase
with_env_tz eastern_time_zone do
with_timezone_config default: :utc do
time = Time.local(2000)
- topic = Topic.create('written_on' => time)
+ topic = Topic.create("written_on" => time)
saved_time = Topic.find(topic.id).reload.written_on
assert_equal time, saved_time
assert_equal [0, 0, 0, 1, 1, 2000, 6, 1, false, "EST"], time.to_a
@@ -223,9 +211,9 @@ class BasicsTest < ActiveRecord::TestCase
def test_preserving_time_objects_with_time_with_zone_conversion_to_default_timezone_utc
with_env_tz eastern_time_zone do
with_timezone_config default: :utc do
- Time.use_zone 'Central Time (US & Canada)' do
+ Time.use_zone "Central Time (US & Canada)" do
time = Time.zone.local(2000)
- topic = Topic.create('written_on' => time)
+ topic = Topic.create("written_on" => time)
saved_time = Topic.find(topic.id).reload.written_on
assert_equal time, saved_time
assert_equal [0, 0, 0, 1, 1, 2000, 6, 1, false, "CST"], time.to_a
@@ -239,7 +227,7 @@ class BasicsTest < ActiveRecord::TestCase
with_env_tz eastern_time_zone do
with_timezone_config default: :local do
time = Time.utc(2000)
- topic = Topic.create('written_on' => time)
+ topic = Topic.create("written_on" => time)
saved_time = Topic.find(topic.id).reload.written_on
assert_equal time, saved_time
assert_equal [0, 0, 0, 1, 1, 2000, 6, 1, false, "UTC"], time.to_a
@@ -251,9 +239,9 @@ class BasicsTest < ActiveRecord::TestCase
def test_preserving_time_objects_with_time_with_zone_conversion_to_default_timezone_local
with_env_tz eastern_time_zone do
with_timezone_config default: :local do
- Time.use_zone 'Central Time (US & Canada)' do
+ Time.use_zone "Central Time (US & Canada)" do
time = Time.zone.local(2000)
- topic = Topic.create('written_on' => time)
+ topic = Topic.create("written_on" => time)
saved_time = Topic.find(topic.id).reload.written_on
assert_equal time, saved_time
assert_equal [0, 0, 0, 1, 1, 2000, 6, 1, false, "CST"], time.to_a
@@ -279,49 +267,48 @@ class BasicsTest < ActiveRecord::TestCase
end
def test_initialize_with_attributes
- topic = Topic.new({
- "title" => "initialized from attributes", "written_on" => "2003-12-12 23:23"
- })
+ topic = Topic.new(
+ "title" => "initialized from attributes", "written_on" => "2003-12-12 23:23")
assert_equal("initialized from attributes", topic.title)
end
def test_initialize_with_invalid_attribute
- Topic.new({ "title" => "test",
- "last_read(1i)" => "2005", "last_read(2i)" => "2", "last_read(3i)" => "31"})
+ Topic.new("title" => "test",
+ "last_read(1i)" => "2005", "last_read(2i)" => "2", "last_read(3i)" => "31")
rescue ActiveRecord::MultiparameterAssignmentErrors => ex
assert_equal(1, ex.errors.size)
assert_equal("last_read", ex.errors[0].attribute)
end
def test_create_after_initialize_without_block
- cb = CustomBulb.create(:name => 'Dude')
- assert_equal('Dude', cb.name)
+ cb = CustomBulb.create(name: "Dude")
+ assert_equal("Dude", cb.name)
assert_equal(true, cb.frickinawesome)
end
def test_create_after_initialize_with_block
- cb = CustomBulb.create {|c| c.name = 'Dude' }
- assert_equal('Dude', cb.name)
+ cb = CustomBulb.create { |c| c.name = "Dude" }
+ assert_equal("Dude", cb.name)
assert_equal(true, cb.frickinawesome)
end
def test_create_after_initialize_with_array_param
- cbs = CustomBulb.create([{ name: 'Dude' }, { name: 'Bob' }])
- assert_equal 'Dude', cbs[0].name
- assert_equal 'Bob', cbs[1].name
+ cbs = CustomBulb.create([{ name: "Dude" }, { name: "Bob" }])
+ assert_equal "Dude", cbs[0].name
+ assert_equal "Bob", cbs[1].name
assert cbs[0].frickinawesome
assert !cbs[1].frickinawesome
end
def test_load
- topics = Topic.all.merge!(:order => 'id').to_a
+ topics = Topic.all.merge!(order: "id").to_a
assert_equal(5, topics.size)
assert_equal(topics(:first).title, topics.first.title)
end
def test_load_with_condition
- topics = Topic.all.merge!(:where => "author_name = 'Mary'").to_a
+ topics = Topic.all.merge!(where: "author_name = 'Mary'").to_a
assert_equal(1, topics.size)
assert_equal(topics(:second).title, topics.first.title)
@@ -443,7 +430,7 @@ class BasicsTest < ActiveRecord::TestCase
if current_adapter?(:Mysql2Adapter)
def test_update_all_with_order_and_limit
- assert_equal 1, Topic.limit(1).order('id DESC').update_all(:content => 'bulk updated!')
+ assert_equal 1, Topic.limit(1).order("id DESC").update_all(content: "bulk updated!")
end
end
@@ -488,12 +475,12 @@ class BasicsTest < ActiveRecord::TestCase
def test_utc_as_time_zone_and_new
with_timezone_config default: :utc do
- attributes = { "bonus_time(1i)"=>"2000",
- "bonus_time(2i)"=>"1",
- "bonus_time(3i)"=>"1",
- "bonus_time(4i)"=>"10",
- "bonus_time(5i)"=>"35",
- "bonus_time(6i)"=>"50" }
+ attributes = { "bonus_time(1i)" => "2000",
+ "bonus_time(2i)" => "1",
+ "bonus_time(3i)" => "1",
+ "bonus_time(4i)" => "10",
+ "bonus_time(5i)" => "35",
+ "bonus_time(6i)" => "50" }
topic = Topic.new(attributes)
assert_equal Time.utc(2000, 1, 1, 10, 35, 50), topic.bonus_time
end
@@ -518,16 +505,16 @@ class BasicsTest < ActiveRecord::TestCase
end
def test_find_by_slug
- assert_equal Topic.find('1-meowmeow'), Topic.find(1)
+ assert_equal Topic.find("1-meowmeow"), Topic.find(1)
end
def test_find_by_slug_with_array
- assert_equal Topic.find([1, 2]), Topic.find(['1-meowmeow', '2-hello'])
- assert_equal 'The Second Topic of the day', Topic.find(['2-hello', '1-meowmeow']).first.title
+ assert_equal Topic.find([1, 2]), Topic.find(["1-meowmeow", "2-hello"])
+ assert_equal "The Second Topic of the day", Topic.find(["2-hello", "1-meowmeow"]).first.title
end
def test_find_by_slug_with_range
- assert_equal Topic.where(id: '1-meowmeow'..'2-hello'), Topic.where(id: 1..2)
+ assert_equal Topic.where(id: "1-meowmeow".."2-hello"), Topic.where(id: 1..2)
end
def test_equality_of_new_records
@@ -536,7 +523,7 @@ class BasicsTest < ActiveRecord::TestCase
end
def test_equality_of_destroyed_records
- topic_1 = Topic.new(:title => 'test_1')
+ topic_1 = Topic.new(title: "test_1")
topic_1.save
topic_2 = Topic.find(topic_1.id)
topic_1.destroy
@@ -545,8 +532,8 @@ class BasicsTest < ActiveRecord::TestCase
end
def test_equality_with_blank_ids
- one = Subscriber.new(:id => '')
- two = Subscriber.new(:id => '')
+ one = Subscriber.new(id: "")
+ two = Subscriber.new(id: "")
assert_equal one, two
end
@@ -555,8 +542,8 @@ class BasicsTest < ActiveRecord::TestCase
car.bulbs.build
car.save
- assert car.bulbs == Bulb.where(car_id: car.id), 'CollectionProxy should be comparable with Relation'
- assert Bulb.where(car_id: car.id) == car.bulbs, 'Relation should be comparable with CollectionProxy'
+ assert car.bulbs == Bulb.where(car_id: car.id), "CollectionProxy should be comparable with Relation"
+ assert Bulb.where(car_id: car.id) == car.bulbs, "Relation should be comparable with CollectionProxy"
end
def test_equality_of_relation_and_array
@@ -564,7 +551,7 @@ class BasicsTest < ActiveRecord::TestCase
car.bulbs.build
car.save
- assert Bulb.where(car_id: car.id) == car.bulbs.to_a, 'Relation should be comparable with Array'
+ assert Bulb.where(car_id: car.id) == car.bulbs.to_a, "Relation should be comparable with Array"
end
def test_equality_of_relation_and_association_relation
@@ -572,8 +559,8 @@ class BasicsTest < ActiveRecord::TestCase
car.bulbs.build
car.save
- assert_equal Bulb.where(car_id: car.id), car.bulbs.includes(:car), 'Relation should be comparable with AssociationRelation'
- assert_equal car.bulbs.includes(:car), Bulb.where(car_id: car.id), 'AssociationRelation should be comparable with Relation'
+ assert_equal Bulb.where(car_id: car.id), car.bulbs.includes(:car), "Relation should be comparable with AssociationRelation"
+ assert_equal car.bulbs.includes(:car), Bulb.where(car_id: car.id), "AssociationRelation should be comparable with Relation"
end
def test_equality_of_collection_proxy_and_association_relation
@@ -581,8 +568,8 @@ class BasicsTest < ActiveRecord::TestCase
car.bulbs.build
car.save
- assert_equal car.bulbs, car.bulbs.includes(:car), 'CollectionProxy should be comparable with AssociationRelation'
- assert_equal car.bulbs.includes(:car), car.bulbs, 'AssociationRelation should be comparable with CollectionProxy'
+ assert_equal car.bulbs, car.bulbs.includes(:car), "CollectionProxy should be comparable with AssociationRelation"
+ assert_equal car.bulbs.includes(:car), car.bulbs, "AssociationRelation should be comparable with CollectionProxy"
end
def test_hashing
@@ -604,24 +591,24 @@ class BasicsTest < ActiveRecord::TestCase
def test_create_without_prepared_statement
topic = Topic.connection.unprepared_statement do
- Topic.create(:title => 'foo')
+ Topic.create(title: "foo")
end
assert_equal topic, Topic.find(topic.id)
end
def test_destroy_without_prepared_statement
- topic = Topic.create(title: 'foo')
+ topic = Topic.create(title: "foo")
Topic.connection.unprepared_statement do
Topic.find(topic.id).destroy
end
- assert_equal nil, Topic.find_by_id(topic.id)
+ assert_nil Topic.find_by_id(topic.id)
end
def test_comparison_with_different_objects
topic = Topic.create
- category = Category.create(:name => "comparison")
+ category = Category.create(name: "comparison")
assert_nil topic <=> category
end
@@ -633,9 +620,9 @@ class BasicsTest < ActiveRecord::TestCase
end
def test_readonly_attributes
- assert_equal Set.new([ 'title' , 'comments_count' ]), ReadonlyTitlePost.readonly_attributes
+ assert_equal Set.new([ "title" , "comments_count" ]), ReadonlyTitlePost.readonly_attributes
- post = ReadonlyTitlePost.create(:title => "cannot change this", :body => "changeable")
+ post = ReadonlyTitlePost.create(title: "cannot change this", body: "changeable")
post.reload
assert_equal "cannot change this", post.title
@@ -647,8 +634,8 @@ class BasicsTest < ActiveRecord::TestCase
def test_unicode_column_name
Weird.reset_column_information
- weird = Weird.create(:なまえ => 'たこ焼き仮面')
- assert_equal 'たこ焼き仮面', weird.なまえ
+ weird = Weird.create(なまえ: "たこ焼き仮面")
+ assert_equal "たこ焼き仮面", weird.なまえ
end
unless current_adapter?(:PostgreSQLAdapter)
@@ -658,7 +645,7 @@ class BasicsTest < ActiveRecord::TestCase
Weird.reset_column_information
- assert_equal ["EUC-JP"], Weird.columns.map {|c| c.name.encoding.name }.uniq
+ assert_equal ["EUC-JP"], Weird.columns.map { |c| c.name.encoding.name }.uniq
ensure
silence_warnings { Encoding.default_internal = old_default_internal }
Weird.reset_column_information
@@ -666,21 +653,21 @@ class BasicsTest < ActiveRecord::TestCase
end
def test_non_valid_identifier_column_name
- weird = Weird.create('a$b' => 'value')
+ weird = Weird.create("a$b" => "value")
weird.reload
- assert_equal 'value', weird.send('a$b')
- assert_equal 'value', weird.read_attribute('a$b')
+ assert_equal "value", weird.send("a$b")
+ assert_equal "value", weird.read_attribute("a$b")
- weird.update_columns('a$b' => 'value2')
+ weird.update_columns("a$b" => "value2")
weird.reload
- assert_equal 'value2', weird.send('a$b')
- assert_equal 'value2', weird.read_attribute('a$b')
+ assert_equal "value2", weird.send("a$b")
+ assert_equal "value2", weird.read_attribute("a$b")
end
def test_group_weirds_by_from
- Weird.create('a$b' => 'value', :from => 'aaron')
+ Weird.create("a$b" => "value", :from => "aaron")
count = Weird.group(Weird.arel_table[:from]).count
- assert_equal 1, count['aaron']
+ assert_equal 1, count["aaron"]
end
def test_attributes_on_dummy_time
@@ -710,11 +697,11 @@ class BasicsTest < ActiveRecord::TestCase
end
def test_boolean
- b_nil = Boolean.create({ "value" => nil })
+ b_nil = Boolean.create("value" => nil)
nil_id = b_nil.id
- b_false = Boolean.create({ "value" => false })
+ b_false = Boolean.create("value" => false)
false_id = b_false.id
- b_true = Boolean.create({ "value" => true })
+ b_true = Boolean.create("value" => true)
true_id = b_true.id
b_nil = Boolean.find(nil_id)
@@ -726,7 +713,7 @@ class BasicsTest < ActiveRecord::TestCase
end
def test_boolean_without_questionmark
- b_true = Boolean.create({ "value" => true })
+ b_true = Boolean.create("value" => true)
true_id = b_true.id
subclass = Class.new(Boolean).find true_id
@@ -736,11 +723,11 @@ class BasicsTest < ActiveRecord::TestCase
end
def test_boolean_cast_from_string
- b_blank = Boolean.create({ "value" => "" })
+ b_blank = Boolean.create("value" => "")
blank_id = b_blank.id
- b_false = Boolean.create({ "value" => "0" })
+ b_false = Boolean.create("value" => "0")
false_id = b_false.id
- b_true = Boolean.create({ "value" => "1" })
+ b_true = Boolean.create("value" => "1")
true_id = b_true.id
b_blank = Boolean.find(blank_id)
@@ -789,8 +776,8 @@ class BasicsTest < ActiveRecord::TestCase
DeveloperSalary = Struct.new(:amount)
def test_dup_with_aggregate_of_same_name_as_attribute
developer_with_aggregate = Class.new(ActiveRecord::Base) do
- self.table_name = 'developers'
- composed_of :salary, :class_name => 'BasicsTest::DeveloperSalary', :mapping => [%w(salary amount)]
+ self.table_name = "developers"
+ composed_of :salary, class_name: "BasicsTest::DeveloperSalary", mapping: [%w(salary amount)]
end
dev = developer_with_aggregate.find(1)
@@ -837,7 +824,7 @@ class BasicsTest < ActiveRecord::TestCase
end
def test_clone_of_new_object_marks_attributes_as_dirty
- developer = Developer.new :name => 'Bjorn', :salary => 100000
+ developer = Developer.new name: "Bjorn", salary: 100000
assert developer.name_changed?
assert developer.salary_changed?
@@ -847,7 +834,7 @@ class BasicsTest < ActiveRecord::TestCase
end
def test_clone_of_new_object_marks_as_dirty_only_changed_attributes
- developer = Developer.new :name => 'Bjorn'
+ developer = Developer.new name: "Bjorn"
assert developer.name_changed? # obviously
assert !developer.salary_changed? # attribute has non-nil default value, so treated as not changed
@@ -857,7 +844,7 @@ class BasicsTest < ActiveRecord::TestCase
end
def test_dup_of_saved_object_marks_attributes_as_dirty
- developer = Developer.create! :name => 'Bjorn', :salary => 100000
+ developer = Developer.create! name: "Bjorn", salary: 100000
assert !developer.name_changed?
assert !developer.salary_changed?
@@ -867,7 +854,7 @@ class BasicsTest < ActiveRecord::TestCase
end
def test_dup_of_saved_object_marks_as_dirty_only_changed_attributes
- developer = Developer.create! :name => 'Bjorn'
+ developer = Developer.create! name: "Bjorn"
assert !developer.name_changed? # both attributes of saved object should be treated as not changed
assert !developer.salary_changed?
@@ -892,18 +879,18 @@ class BasicsTest < ActiveRecord::TestCase
# fixed dates / times
assert_equal Date.new(2004, 1, 1), default.fixed_date
- assert_equal Time.local(2004, 1,1,0,0,0,0), default.fixed_time
+ assert_equal Time.local(2004, 1, 1, 0, 0, 0, 0), default.fixed_time
# char types
- assert_equal 'Y', default.char1
- assert_equal 'a varchar field', default.char2
- assert_equal 'a text field', default.char3
+ assert_equal "Y", default.char1
+ assert_equal "a varchar field", default.char2
+ assert_equal "a text field", default.char3
end
end
end
class NumericData < ActiveRecord::Base
- self.table_name = 'numeric_data'
+ self.table_name = "numeric_data"
attribute :my_house_population, :integer
attribute :atoms_in_universe, :integer
@@ -911,10 +898,10 @@ class BasicsTest < ActiveRecord::TestCase
def test_big_decimal_conditions
m = NumericData.new(
- :bank_balance => 1586.43,
- :big_bank_balance => BigDecimal("1000234000567.95"),
- :world_population => 6000000000,
- :my_house_population => 3
+ bank_balance: 1586.43,
+ big_bank_balance: BigDecimal("1000234000567.95"),
+ world_population: 6000000000,
+ my_house_population: 3
)
assert m.save
assert_equal 0, NumericData.where("bank_balance > ?", 2000.0).count
@@ -922,10 +909,10 @@ class BasicsTest < ActiveRecord::TestCase
def test_numeric_fields
m = NumericData.new(
- :bank_balance => 1586.43,
- :big_bank_balance => BigDecimal("1000234000567.95"),
- :world_population => 6000000000,
- :my_house_population => 3
+ bank_balance: 1586.43,
+ big_bank_balance: BigDecimal("1000234000567.95"),
+ world_population: 6000000000,
+ my_house_population: 3
)
assert m.save
@@ -950,10 +937,10 @@ class BasicsTest < ActiveRecord::TestCase
def test_numeric_fields_with_scale
m = NumericData.new(
- :bank_balance => 1586.43122334,
- :big_bank_balance => BigDecimal("234000567.952344"),
- :world_population => 6000000000,
- :my_house_population => 3
+ bank_balance: 1586.43122334,
+ big_bank_balance: BigDecimal("234000567.952344"),
+ world_population: 6000000000,
+ my_house_population: 3
)
assert m.save
@@ -999,16 +986,16 @@ class BasicsTest < ActiveRecord::TestCase
end
def test_quoting_arrays
- replies = Reply.all.merge!(:where => [ "id IN (?)", topics(:first).replies.collect(&:id) ]).to_a
+ replies = Reply.all.merge!(where: [ "id IN (?)", topics(:first).replies.collect(&:id) ]).to_a
assert_equal topics(:first).replies.size, replies.size
- replies = Reply.all.merge!(:where => [ "id IN (?)", [] ]).to_a
+ replies = Reply.all.merge!(where: [ "id IN (?)", [] ]).to_a
assert_equal 0, replies.size
end
def test_quote
author_name = "\\ \001 ' \n \\n \""
- topic = Topic.create('author_name' => author_name)
+ topic = Topic.create("author_name" => author_name)
assert_equal author_name, Topic.find(topic.id).author_name
end
@@ -1087,7 +1074,7 @@ class BasicsTest < ActiveRecord::TestCase
def test_set_table_name_symbol_converted_to_string
k = Class.new(Joke)
k.table_name = :cold_jokes
- assert_equal 'cold_jokes', k.table_name
+ assert_equal "cold_jokes", k.table_name
end
def test_quoted_table_name_after_set_table_name
@@ -1103,7 +1090,7 @@ class BasicsTest < ActiveRecord::TestCase
end
def test_set_table_name_with_inheritance
- k = Class.new( ActiveRecord::Base )
+ k = Class.new(ActiveRecord::Base)
def k.name; "Foo"; end
def k.table_name; super + "ks"; end
assert_equal "foosks", k.table_name
@@ -1149,17 +1136,17 @@ class BasicsTest < ActiveRecord::TestCase
def test_no_limit_offset
assert_nothing_raised do
- Developer.all.merge!(:offset => 2).to_a
+ Developer.all.merge!(offset: 2).to_a
end
end
def test_find_last
- last = Developer.last
- assert_equal last, Developer.all.merge!(:order => 'id desc').first
+ last = Developer.last
+ assert_equal last, Developer.all.merge!(order: "id desc").first
end
def test_last
- assert_equal Developer.all.merge!(:order => 'id desc').first, Developer.last
+ assert_equal Developer.all.merge!(order: "id desc").first, Developer.last
end
def test_all
@@ -1169,37 +1156,37 @@ class BasicsTest < ActiveRecord::TestCase
end
def test_all_with_conditions
- assert_equal Developer.all.merge!(:order => 'id desc').to_a, Developer.order('id desc').to_a
+ assert_equal Developer.all.merge!(order: "id desc").to_a, Developer.order("id desc").to_a
end
def test_find_ordered_last
- last = Developer.all.merge!(:order => 'developers.salary ASC').last
- assert_equal last, Developer.all.merge!(:order => 'developers.salary ASC').to_a.last
+ last = Developer.all.merge!(order: "developers.salary ASC").last
+ assert_equal last, Developer.all.merge!(order: "developers.salary ASC").to_a.last
end
def test_find_reverse_ordered_last
- last = Developer.all.merge!(:order => 'developers.salary DESC').last
- assert_equal last, Developer.all.merge!(:order => 'developers.salary DESC').to_a.last
+ last = Developer.all.merge!(order: "developers.salary DESC").last
+ assert_equal last, Developer.all.merge!(order: "developers.salary DESC").to_a.last
end
def test_find_multiple_ordered_last
- last = Developer.all.merge!(:order => 'developers.name, developers.salary DESC').last
- assert_equal last, Developer.all.merge!(:order => 'developers.name, developers.salary DESC').to_a.last
+ last = Developer.all.merge!(order: "developers.name, developers.salary DESC").last
+ assert_equal last, Developer.all.merge!(order: "developers.name, developers.salary DESC").to_a.last
end
def test_find_keeps_multiple_order_values
- combined = Developer.all.merge!(:order => 'developers.name, developers.salary').to_a
- assert_equal combined, Developer.all.merge!(:order => ['developers.name', 'developers.salary']).to_a
+ combined = Developer.all.merge!(order: "developers.name, developers.salary").to_a
+ assert_equal combined, Developer.all.merge!(order: ["developers.name", "developers.salary"]).to_a
end
def test_find_keeps_multiple_group_values
- combined = Developer.all.merge!(:group => 'developers.name, developers.salary, developers.id, developers.created_at, developers.updated_at, developers.created_on, developers.updated_on').to_a
- assert_equal combined, Developer.all.merge!(:group => ['developers.name', 'developers.salary', 'developers.id', 'developers.created_at', 'developers.updated_at', 'developers.created_on', 'developers.updated_on']).to_a
+ combined = Developer.all.merge!(group: "developers.name, developers.salary, developers.id, developers.created_at, developers.updated_at, developers.created_on, developers.updated_on").to_a
+ assert_equal combined, Developer.all.merge!(group: ["developers.name", "developers.salary", "developers.id", "developers.created_at", "developers.updated_at", "developers.created_on", "developers.updated_on"]).to_a
end
def test_find_symbol_ordered_last
- last = Developer.all.merge!(:order => :salary).last
- assert_equal last, Developer.all.merge!(:order => :salary).to_a.last
+ last = Developer.all.merge!(order: :salary).last
+ assert_equal last, Developer.all.merge!(order: :salary).to_a.last
end
def test_abstract_class_table_name
@@ -1210,7 +1197,7 @@ class BasicsTest < ActiveRecord::TestCase
old_class = LooseDescendant
Object.send :remove_const, :LooseDescendant
- descendant = old_class.create! :first_name => 'bob'
+ descendant = old_class.create! first_name: "bob"
assert_not_nil LoosePerson.find(descendant.id), "Should have found instance of LooseDescendant when finding abstract LoosePerson: #{descendant.inspect}"
ensure
unless Object.const_defined?(:LooseDescendant)
@@ -1219,7 +1206,7 @@ class BasicsTest < ActiveRecord::TestCase
end
def test_assert_queries
- query = lambda { ActiveRecord::Base.connection.execute 'select count(*) from developers' }
+ query = lambda { ActiveRecord::Base.connection.execute "select count(*) from developers" }
assert_queries(2) { 2.times { query.call } }
assert_queries 1, &query
assert_no_queries { assert true }
@@ -1230,9 +1217,9 @@ class BasicsTest < ActiveRecord::TestCase
log = StringIO.new
ActiveRecord::Base.logger = ActiveSupport::Logger.new(log)
ActiveRecord::Base.logger.level = Logger::WARN
- ActiveRecord::Base.benchmark("Debug Topic Count", :level => :debug) { Topic.count }
- ActiveRecord::Base.benchmark("Warn Topic Count", :level => :warn) { Topic.count }
- ActiveRecord::Base.benchmark("Error Topic Count", :level => :error) { Topic.count }
+ ActiveRecord::Base.benchmark("Debug Topic Count", level: :debug) { Topic.count }
+ ActiveRecord::Base.benchmark("Warn Topic Count", level: :warn) { Topic.count }
+ ActiveRecord::Base.benchmark("Error Topic Count", level: :error) { Topic.count }
assert_no_match(/Debug Topic Count/, log.string)
assert_match(/Warn Topic Count/, log.string)
assert_match(/Error Topic Count/, log.string)
@@ -1245,7 +1232,7 @@ class BasicsTest < ActiveRecord::TestCase
log = StringIO.new
ActiveRecord::Base.logger = ActiveSupport::Logger.new(log)
ActiveRecord::Base.logger.level = Logger::DEBUG
- ActiveRecord::Base.benchmark("Logging", :level => :debug, :silence => false) { ActiveRecord::Base.logger.debug "Quiet" }
+ ActiveRecord::Base.benchmark("Logging", level: :debug, silence: false) { ActiveRecord::Base.logger.debug "Quiet" }
assert_match(/Quiet/, log.string)
ensure
ActiveRecord::Base.logger = original_logger
@@ -1253,9 +1240,9 @@ class BasicsTest < ActiveRecord::TestCase
def test_clear_cache!
# preheat cache
- c1 = Post.connection.schema_cache.columns('posts')
+ c1 = Post.connection.schema_cache.columns("posts")
ActiveRecord::Base.clear_cache!
- c2 = Post.connection.schema_cache.columns('posts')
+ c2 = Post.connection.schema_cache.columns("posts")
c1.each_with_index do |v, i|
assert_not_same v, c2[i]
end
@@ -1272,13 +1259,13 @@ class BasicsTest < ActiveRecord::TestCase
ActiveSupport::Dependencies.remove_unloadable_constants!
assert_nil ActiveRecord::Scoping::ScopeRegistry.value_for(:current_scope, klass)
ensure
- Object.class_eval{ remove_const :UnloadablePost } if defined?(UnloadablePost)
+ Object.class_eval { remove_const :UnloadablePost } if defined?(UnloadablePost)
end
def test_marshal_round_trip
expected = posts(:welcome)
marshalled = Marshal.dump(expected)
- actual = Marshal.load(marshalled)
+ actual = Marshal.load(marshalled)
assert_equal expected.attributes, actual.attributes
end
@@ -1346,11 +1333,11 @@ class BasicsTest < ActiveRecord::TestCase
end
def test_has_attribute
- assert Company.has_attribute?('id')
- assert Company.has_attribute?('type')
- assert Company.has_attribute?('name')
- assert_not Company.has_attribute?('lastname')
- assert_not Company.has_attribute?('age')
+ assert Company.has_attribute?("id")
+ assert Company.has_attribute?("type")
+ assert Company.has_attribute?("name")
+ assert_not Company.has_attribute?("lastname")
+ assert_not Company.has_attribute?("age")
end
def test_has_attribute_with_symbol
@@ -1367,18 +1354,12 @@ class BasicsTest < ActiveRecord::TestCase
end
def test_touch_should_raise_error_on_a_new_object
- company = Company.new(:rating => 1, :name => "37signals", :firm_name => "37signals")
+ company = Company.new(rating: 1, name: "37signals", firm_name: "37signals")
assert_raises(ActiveRecord::ActiveRecordError) do
company.touch :updated_at
end
end
- def test_uniq_delegates_to_scoped
- assert_deprecated do
- assert_equal Bird.all.distinct, Bird.uniq
- end
- end
-
def test_distinct_delegates_to_scoped
assert_equal Bird.all.distinct, Bird.distinct
end
@@ -1389,37 +1370,47 @@ class BasicsTest < ActiveRecord::TestCase
def test_column_types_typecast
topic = Topic.first
- assert_not_equal 't.lo', topic.author_name
+ assert_not_equal "t.lo", topic.author_name
attrs = topic.attributes.dup
- attrs.delete 'id'
+ attrs.delete "id"
typecast = Class.new(ActiveRecord::Type::Value) {
- def cast value
+ def cast(value)
"t.lo"
end
}
- types = { 'author_name' => typecast.new }
+ types = { "author_name" => typecast.new }
topic = Topic.instantiate(attrs, types)
- assert_equal 't.lo', topic.author_name
+ assert_equal "t.lo", topic.author_name
end
def test_typecasting_aliases
- assert_equal 10, Topic.select('10 as tenderlove').first.tenderlove
+ assert_equal 10, Topic.select("10 as tenderlove").first.tenderlove
end
def test_slice
- company = Company.new(:rating => 1, :name => "37signals", :firm_name => "37signals")
+ company = Company.new(rating: 1, name: "37signals", firm_name: "37signals")
hash = company.slice(:name, :rating, "arbitrary_method")
assert_equal hash[:name], company.name
- assert_equal hash['name'], company.name
+ assert_equal hash["name"], company.name
assert_equal hash[:rating], company.rating
- assert_equal hash['arbitrary_method'], company.arbitrary_method
+ assert_equal hash["arbitrary_method"], company.arbitrary_method
assert_equal hash[:arbitrary_method], company.arbitrary_method
assert_nil hash[:firm_name]
- assert_nil hash['firm_name']
+ assert_nil hash["firm_name"]
+ end
+
+ def test_slice_accepts_array_argument
+ attrs = {
+ title: "slice",
+ author_name: "@Cohen-Carlisle",
+ content: "accept arrays so I don't have to splat"
+ }.with_indifferent_access
+ topic = Topic.new(attrs)
+ assert_equal attrs, topic.slice(attrs.keys)
end
def test_default_values_are_deeply_dupped
@@ -1430,7 +1421,7 @@ class BasicsTest < ActiveRecord::TestCase
test "scoped can take a values hash" do
klass = Class.new(ActiveRecord::Base)
- assert_equal ['foo'], klass.all.merge!(select: 'foo').select_values
+ assert_equal ["foo"], klass.all.merge!(select: "foo").select_values
end
test "connection_handler can be overridden" do
@@ -1512,8 +1503,8 @@ class BasicsTest < ActiveRecord::TestCase
test "ignored columns are not present in columns_hash" do
cache_columns = Developer.connection.schema_cache.columns_hash(Developer.table_name)
- assert_includes cache_columns.keys, 'first_name'
- refute_includes Developer.columns_hash.keys, 'first_name'
+ assert_includes cache_columns.keys, "first_name"
+ assert_not_includes Developer.columns_hash.keys, "first_name"
end
test "ignored columns have no attribute methods" do
diff --git a/activerecord/test/cases/batches_test.rb b/activerecord/test/cases/batches_test.rb
index db71840658..f7e21faf0f 100644
--- a/activerecord/test/cases/batches_test.rb
+++ b/activerecord/test/cases/batches_test.rb
@@ -1,6 +1,6 @@
-require 'cases/helper'
-require 'models/post'
-require 'models/subscriber'
+require "cases/helper"
+require "models/post"
+require "models/subscriber"
class EachTest < ActiveRecord::TestCase
fixtures :posts, :subscribers
@@ -8,12 +8,12 @@ class EachTest < ActiveRecord::TestCase
def setup
@posts = Post.order("id asc")
@total = Post.count
- Post.count('id') # preheat arel's table cache
+ Post.count("id") # preheat arel's table cache
end
def test_each_should_execute_one_query_per_batch
assert_queries(@total + 1) do
- Post.find_each(:batch_size => 1) do |post|
+ Post.find_each(batch_size: 1) do |post|
assert_kind_of Post, post
end
end
@@ -21,14 +21,14 @@ class EachTest < ActiveRecord::TestCase
def test_each_should_not_return_query_chain_and_execute_only_one_query
assert_queries(1) do
- result = Post.find_each(:batch_size => 100000){ }
+ result = Post.find_each(batch_size: 100000) {}
assert_nil result
end
end
def test_each_should_return_an_enumerator_if_no_block_is_present
assert_queries(1) do
- Post.find_each(:batch_size => 100000).with_index do |post, index|
+ Post.find_each(batch_size: 100000).with_index do |post, index|
assert_kind_of Post, post
assert_kind_of Integer, index
end
@@ -45,7 +45,7 @@ class EachTest < ActiveRecord::TestCase
def test_each_enumerator_should_execute_one_query_per_batch
assert_queries(@total + 1) do
- Post.find_each(:batch_size => 1).with_index do |post, index|
+ Post.find_each(batch_size: 1).with_index do |post, index|
assert_kind_of Post, post
assert_kind_of Integer, index
end
@@ -62,16 +62,32 @@ class EachTest < ActiveRecord::TestCase
def test_each_should_execute_if_id_is_in_select
assert_queries(6) do
- Post.select("id, title, type").find_each(:batch_size => 2) do |post|
+ Post.select("id, title, type").find_each(batch_size: 2) do |post|
assert_kind_of Post, post
end
end
end
- def test_warn_if_limit_scope_is_set
- assert_called(ActiveRecord::Base.logger, :warn) do
- Post.limit(1).find_each { |post| post }
+ test "find_each should honor limit if passed a block" do
+ limit = @total - 1
+ total = 0
+
+ Post.limit(limit).find_each do |post|
+ total += 1
+ end
+
+ assert_equal limit, total
+ end
+
+ test "find_each should honor limit if no block is passed" do
+ limit = @total - 1
+ total = 0
+
+ Post.limit(limit).find_each.each do |post|
+ total += 1
end
+
+ assert_equal limit, total
end
def test_warn_if_order_scope_is_set
@@ -84,7 +100,7 @@ class EachTest < ActiveRecord::TestCase
previous_logger = ActiveRecord::Base.logger
ActiveRecord::Base.logger = nil
assert_nothing_raised do
- Post.limit(1).find_each { |post| post }
+ Post.order("comments_count DESC").find_each { |post| post }
end
ensure
ActiveRecord::Base.logger = previous_logger
@@ -92,7 +108,7 @@ class EachTest < ActiveRecord::TestCase
def test_find_in_batches_should_return_batches
assert_queries(@total + 1) do
- Post.find_in_batches(:batch_size => 1) do |batch|
+ Post.find_in_batches(batch_size: 1) do |batch|
assert_kind_of Array, batch
assert_kind_of Post, batch.first
end
@@ -119,18 +135,18 @@ class EachTest < ActiveRecord::TestCase
def test_find_in_batches_shouldnt_execute_query_unless_needed
assert_queries(2) do
- Post.find_in_batches(:batch_size => @total) {|batch| assert_kind_of Array, batch }
+ Post.find_in_batches(batch_size: @total) { |batch| assert_kind_of Array, batch }
end
assert_queries(1) do
- Post.find_in_batches(:batch_size => @total + 1) {|batch| assert_kind_of Array, batch }
+ Post.find_in_batches(batch_size: @total + 1) { |batch| assert_kind_of Array, batch }
end
end
def test_find_in_batches_should_quote_batch_order
c = Post.connection
assert_sql(/ORDER BY #{c.quote_table_name('posts')}.#{c.quote_column_name('id')}/) do
- Post.find_in_batches(:batch_size => 1) do |batch|
+ Post.find_in_batches(batch_size: 1) do |batch|
assert_kind_of Array, batch
assert_kind_of Post, batch.first
end
@@ -140,9 +156,9 @@ class EachTest < ActiveRecord::TestCase
def test_find_in_batches_should_not_use_records_after_yielding_them_in_case_original_array_is_modified
not_a_post = "not a post"
def not_a_post.id; end
- not_a_post.stub(:id, ->{ raise StandardError.new("not_a_post had #id called on it") }) do
+ not_a_post.stub(:id, -> { raise StandardError.new("not_a_post had #id called on it") }) do
assert_nothing_raised do
- Post.find_in_batches(:batch_size => 1) do |batch|
+ Post.find_in_batches(batch_size: 1) do |batch|
assert_kind_of Array, batch
assert_kind_of Post, batch.first
@@ -166,37 +182,37 @@ class EachTest < ActiveRecord::TestCase
def test_find_in_batches_should_error_on_ignore_the_order
assert_raise(ArgumentError) do
- PostWithDefaultScope.find_in_batches(error_on_ignore: true){}
+ PostWithDefaultScope.find_in_batches(error_on_ignore: true) {}
end
end
def test_find_in_batches_should_not_error_if_config_overridden
# Set the config option which will be overridden
- prev = ActiveRecord::Base.error_on_ignored_order_or_limit
- ActiveRecord::Base.error_on_ignored_order_or_limit = true
+ prev = ActiveRecord::Base.error_on_ignored_order
+ ActiveRecord::Base.error_on_ignored_order = true
assert_nothing_raised do
- PostWithDefaultScope.find_in_batches(error_on_ignore: false){}
+ PostWithDefaultScope.find_in_batches(error_on_ignore: false) {}
end
ensure
# Set back to default
- ActiveRecord::Base.error_on_ignored_order_or_limit = prev
+ ActiveRecord::Base.error_on_ignored_order = prev
end
def test_find_in_batches_should_error_on_config_specified_to_error
# Set the config option
- prev = ActiveRecord::Base.error_on_ignored_order_or_limit
- ActiveRecord::Base.error_on_ignored_order_or_limit = true
+ prev = ActiveRecord::Base.error_on_ignored_order
+ ActiveRecord::Base.error_on_ignored_order = true
assert_raise(ArgumentError) do
- PostWithDefaultScope.find_in_batches(){}
+ PostWithDefaultScope.find_in_batches() {}
end
ensure
# Set back to default
- ActiveRecord::Base.error_on_ignored_order_or_limit = prev
+ ActiveRecord::Base.error_on_ignored_order = prev
end
def test_find_in_batches_should_not_error_by_default
assert_nothing_raised do
- PostWithDefaultScope.find_in_batches(){}
+ PostWithDefaultScope.find_in_batches() {}
end
end
@@ -211,12 +227,12 @@ class EachTest < ActiveRecord::TestCase
def test_find_in_batches_should_not_modify_passed_options
assert_nothing_raised do
- Post.find_in_batches({ batch_size: 42, start: 1 }.freeze){}
+ Post.find_in_batches({ batch_size: 42, start: 1 }.freeze) {}
end
end
def test_find_in_batches_should_use_any_column_as_primary_key
- nick_order_subscribers = Subscriber.order('nick asc')
+ nick_order_subscribers = Subscriber.order("nick asc")
start_nick = nick_order_subscribers.second.nick
subscribers = []
@@ -239,7 +255,7 @@ class EachTest < ActiveRecord::TestCase
def test_find_in_batches_should_return_an_enumerator
enum = nil
assert_no_queries do
- enum = Post.find_in_batches(:batch_size => 1)
+ enum = Post.find_in_batches(batch_size: 1)
end
assert_queries(4) do
enum.first(4) do |batch|
@@ -249,6 +265,28 @@ class EachTest < ActiveRecord::TestCase
end
end
+ test "find_in_batches should honor limit if passed a block" do
+ limit = @total - 1
+ total = 0
+
+ Post.limit(limit).find_in_batches do |batch|
+ total += batch.size
+ end
+
+ assert_equal limit, total
+ end
+
+ test "find_in_batches should honor limit if no block is passed" do
+ limit = @total - 1
+ total = 0
+
+ Post.limit(limit).find_in_batches.each do |batch|
+ total += batch.size
+ end
+
+ assert_equal limit, total
+ end
+
def test_in_batches_should_not_execute_any_query
assert_no_queries do
assert_kind_of ActiveRecord::Batches::BatchEnumerator, Post.in_batches(of: 2)
@@ -290,7 +328,7 @@ class EachTest < ActiveRecord::TestCase
end
def test_in_batches_each_record_should_be_ordered_by_id
- ids = Post.order('id ASC').pluck(:id)
+ ids = Post.order("id ASC").pluck(:id)
assert_queries(6) do
Post.in_batches(of: 2).each_record.with_index do |post, i|
assert_equal ids[i], post.id
@@ -306,9 +344,9 @@ class EachTest < ActiveRecord::TestCase
end
def test_in_batches_delete_all_should_not_delete_records_in_other_batches
- not_deleted_count = Post.where('id <= 2').count
- Post.where('id > 2').in_batches(of: 2).delete_all
- assert_equal 0, Post.where('id > 2').count
+ not_deleted_count = Post.where("id <= 2").count
+ Post.where("id > 2").in_batches(of: 2).delete_all
+ assert_equal 0, Post.where("id > 2").count
assert_equal not_deleted_count, Post.count
end
@@ -345,7 +383,7 @@ class EachTest < ActiveRecord::TestCase
end
def test_in_batches_should_start_from_the_start_option
- post = Post.order('id ASC').where('id >= ?', 2).first
+ post = Post.order("id ASC").where("id >= ?", 2).first
assert_queries(2) do
relation = Post.in_batches(of: 1, start: 2).first
assert_equal post, relation.first
@@ -353,7 +391,7 @@ class EachTest < ActiveRecord::TestCase
end
def test_in_batches_should_end_at_the_finish_option
- post = Post.order('id DESC').where('id <= ?', 5).first
+ post = Post.order("id DESC").where("id <= ?", 5).first
assert_queries(7) do
relation = Post.in_batches(of: 1, finish: 5, load: true).reverse_each.first
assert_equal post, relation.last
@@ -407,12 +445,12 @@ class EachTest < ActiveRecord::TestCase
def test_in_batches_should_not_modify_passed_options
assert_nothing_raised do
- Post.in_batches({ of: 42, start: 1 }.freeze){}
+ Post.in_batches({ of: 42, start: 1 }.freeze) {}
end
end
def test_in_batches_should_use_any_column_as_primary_key
- nick_order_subscribers = Subscriber.order('nick asc')
+ nick_order_subscribers = Subscriber.order("nick asc")
start_nick = nick_order_subscribers.second.nick
subscribers = []
@@ -472,18 +510,108 @@ class EachTest < ActiveRecord::TestCase
person.update_attributes(author_id: 1)
Post.in_batches(of: 2) do |batch|
- batch.where('author_id >= 1').update_all('author_id = author_id + 1')
+ batch.where("author_id >= 1").update_all("author_id = author_id + 1")
end
assert_equal 2, person.reload.author_id # incremented only once
end
if Enumerator.method_defined? :size
def test_find_in_batches_should_return_a_sized_enumerator
- assert_equal 11, Post.find_in_batches(:batch_size => 1).size
- assert_equal 6, Post.find_in_batches(:batch_size => 2).size
+ assert_equal 11, Post.find_in_batches(batch_size: 1).size
+ assert_equal 6, Post.find_in_batches(batch_size: 2).size
assert_equal 4, Post.find_in_batches(batch_size: 2, start: 4).size
- assert_equal 4, Post.find_in_batches(:batch_size => 3).size
- assert_equal 1, Post.find_in_batches(:batch_size => 10_000).size
+ assert_equal 4, Post.find_in_batches(batch_size: 3).size
+ assert_equal 1, Post.find_in_batches(batch_size: 10_000).size
+ end
+ end
+
+ [true, false].each do |load|
+ test "in_batches should return limit records when limit is less than batch size and load is #{load}" do
+ limit = 3
+ batch_size = 5
+ total = 0
+
+ Post.limit(limit).in_batches(of: batch_size, load: load) do |batch|
+ total += batch.count
+ end
+
+ assert_equal limit, total
+ end
+
+ test "in_batches should return limit records when limit is greater than batch size and load is #{load}" do
+ limit = 5
+ batch_size = 3
+ total = 0
+
+ Post.limit(limit).in_batches(of: batch_size, load: load) do |batch|
+ total += batch.count
+ end
+
+ assert_equal limit, total
+ end
+
+ test "in_batches should return limit records when limit is a multiple of the batch size and load is #{load}" do
+ limit = 6
+ batch_size = 3
+ total = 0
+
+ Post.limit(limit).in_batches(of: batch_size, load: load) do |batch|
+ total += batch.count
+ end
+
+ assert_equal limit, total
+ end
+
+ test "in_batches should return no records if the limit is 0 and load is #{load}" do
+ limit = 0
+ batch_size = 1
+ total = 0
+
+ Post.limit(limit).in_batches(of: batch_size, load: load) do |batch|
+ total += batch.count
+ end
+
+ assert_equal limit, total
+ end
+
+ test "in_batches should return all if the limit is greater than the number of records when load is #{load}" do
+ limit = @total + 1
+ batch_size = 1
+ total = 0
+
+ Post.limit(limit).in_batches(of: batch_size, load: load) do |batch|
+ total += batch.count
+ end
+
+ assert_equal @total, total
+ end
+ end
+
+ test ".error_on_ignored_order_or_limit= is deprecated" do
+ begin
+ prev = ActiveRecord::Base.error_on_ignored_order
+ assert_deprecated "Please use error_on_ignored_order= instead." do
+ ActiveRecord::Base.error_on_ignored_order_or_limit = true
+ end
+ assert ActiveRecord::Base.error_on_ignored_order
+ ensure
+ ActiveRecord::Base.error_on_ignored_order = prev
+ end
+ end
+
+ test ".error_on_ignored_order_or_limit is deprecated" do
+ expected = ActiveRecord::Base.error_on_ignored_order
+ actual = assert_deprecated "Please use error_on_ignored_order instead." do
+ ActiveRecord::Base.error_on_ignored_order_or_limit
+ end
+ assert_equal expected, actual
+ end
+
+ test "#error_on_ignored_order_or_limit is deprecated" do
+ expected = ActiveRecord::Base.error_on_ignored_order
+ actual = assert_deprecated "Please use error_on_ignored_order instead." do
+ Post.new.error_on_ignored_order_or_limit
end
+ assert_equal expected, actual
end
end
diff --git a/activerecord/test/cases/binary_test.rb b/activerecord/test/cases/binary_test.rb
index 9eb5352150..1fc30e24d2 100644
--- a/activerecord/test/cases/binary_test.rb
+++ b/activerecord/test/cases/binary_test.rb
@@ -4,23 +4,23 @@ require "cases/helper"
# BLOB data with DB2, because the length of a statement
# is limited to 32KB.
unless current_adapter?(:DB2Adapter)
- require 'models/binary'
+ require "models/binary"
class BinaryTest < ActiveRecord::TestCase
FIXTURES = %w(flowers.jpg example.log test.txt)
def test_mixed_encoding
str = "\x80"
- str.force_encoding('ASCII-8BIT')
+ str.force_encoding("ASCII-8BIT")
- binary = Binary.new :name => 'いただきます!', :data => str
+ binary = Binary.new name: "いただきます!", data: str
binary.save!
binary.reload
assert_equal str, binary.data
name = binary.name
- assert_equal 'いただきます!', name
+ assert_equal "いただきます!", name
end
def test_load_save
@@ -28,16 +28,16 @@ unless current_adapter?(:DB2Adapter)
FIXTURES.each do |filename|
data = File.read(ASSETS_ROOT + "/#{filename}")
- data.force_encoding('ASCII-8BIT')
+ data.force_encoding("ASCII-8BIT")
data.freeze
- bin = Binary.new(:data => data)
- assert_equal data, bin.data, 'Newly assigned data differs from original'
+ bin = Binary.new(data: data)
+ assert_equal data, bin.data, "Newly assigned data differs from original"
bin.save!
- assert_equal data, bin.data, 'Data differs from original after save'
+ assert_equal data, bin.data, "Data differs from original after save"
- assert_equal data, bin.reload.data, 'Reloaded data differs from original'
+ assert_equal data, bin.reload.data, "Reloaded data differs from original"
end
end
end
diff --git a/activerecord/test/cases/bind_parameter_test.rb b/activerecord/test/cases/bind_parameter_test.rb
index fa924fe4cb..98d202dd79 100644
--- a/activerecord/test/cases/bind_parameter_test.rb
+++ b/activerecord/test/cases/bind_parameter_test.rb
@@ -1,7 +1,7 @@
-require 'cases/helper'
-require 'models/topic'
-require 'models/author'
-require 'models/post'
+require "cases/helper"
+require "models/topic"
+require "models/author"
+require "models/post"
module ActiveRecord
class BindParameterTest < ActiveRecord::TestCase
@@ -24,7 +24,7 @@ module ActiveRecord
@connection = ActiveRecord::Base.connection
@subscriber = LogListener.new
@pk = Topic.columns_hash[Topic.primary_key]
- @subscription = ActiveSupport::Notifications.subscribe('sql.active_record', @subscriber)
+ @subscription = ActiveSupport::Notifications.subscribe("sql.active_record", @subscriber)
end
teardown do
@@ -34,8 +34,8 @@ module ActiveRecord
if ActiveRecord::Base.connection.supports_statement_cache? &&
ActiveRecord::Base.connection.prepared_statements
def test_bind_from_join_in_subquery
- subquery = Author.joins(:thinking_posts).where(name: 'David')
- scope = Author.from(subquery, 'authors').where(id: 1)
+ subquery = Author.joins(:thinking_posts).where(name: "David")
+ scope = Author.from(subquery, "authors").where(id: 1)
assert_equal 1, scope.count
end
@@ -44,7 +44,7 @@ module ActiveRecord
binds = [Relation::QueryAttribute.new("id", 1, Type::Value.new)]
sql = "select * from topics where id = #{sub.to_sql}"
- @connection.exec_query(sql, 'SQL', binds)
+ @connection.exec_query(sql, "SQL", binds)
message = @subscriber.calls.find { |args| args[4][:sql] == sql }
assert_equal binds, message[4][:binds]
@@ -53,17 +53,20 @@ module ActiveRecord
def test_find_one_uses_binds
Topic.find(1)
message = @subscriber.calls.find { |args| args[4][:binds].any? { |attr| attr.value == 1 } }
- assert message, 'expected a message with binds'
+ assert message, "expected a message with binds"
end
def test_logs_bind_vars_after_type_cast
+ binds = [Relation::QueryAttribute.new("id", "10", Type::Integer.new)]
+ type_casted_binds = binds.map { |attr| type_cast(attr.value_for_database) }
payload = {
- :name => 'SQL',
- :sql => 'select * from topics where id = ?',
- :binds => [Relation::QueryAttribute.new("id", "10", Type::Integer.new)]
+ name: "SQL",
+ sql: "select * from topics where id = ?",
+ binds: binds,
+ type_casted_binds: type_casted_binds
}
event = ActiveSupport::Notifications::Event.new(
- 'foo',
+ "foo",
Time.now,
Time.now,
123,
@@ -76,7 +79,7 @@ module ActiveRecord
@debugs = []
end
- def debug str
+ def debug(str)
@debugs << str
end
}.new
@@ -84,6 +87,12 @@ module ActiveRecord
logger.sql event
assert_match([[@pk.name, 10]].inspect, logger.debugs.first)
end
+
+ private
+
+ def type_cast(value)
+ ActiveRecord::Base.connection.type_cast(value)
+ end
end
end
end
diff --git a/activerecord/test/cases/calculations_test.rb b/activerecord/test/cases/calculations_test.rb
index cfae700159..ffd5c1395d 100644
--- a/activerecord/test/cases/calculations_test.rb
+++ b/activerecord/test/cases/calculations_test.rb
@@ -1,23 +1,24 @@
require "cases/helper"
-require 'models/club'
-require 'models/company'
+require "models/book"
+require "models/club"
+require "models/company"
require "models/contract"
-require 'models/edge'
-require 'models/organization'
-require 'models/possession'
-require 'models/topic'
-require 'models/reply'
-require 'models/minivan'
-require 'models/speedometer'
-require 'models/ship_part'
-require 'models/treasure'
-require 'models/developer'
-require 'models/comment'
-require 'models/rating'
-require 'models/post'
+require "models/edge"
+require "models/organization"
+require "models/possession"
+require "models/topic"
+require "models/reply"
+require "models/minivan"
+require "models/speedometer"
+require "models/ship_part"
+require "models/treasure"
+require "models/developer"
+require "models/comment"
+require "models/rating"
+require "models/post"
class NumericData < ActiveRecord::Base
- self.table_name = 'numeric_data'
+ self.table_name = "numeric_data"
attribute :world_population, :integer
attribute :my_house_population, :integer
@@ -25,7 +26,7 @@ class NumericData < ActiveRecord::Base
end
class CalculationsTest < ActiveRecord::TestCase
- fixtures :companies, :accounts, :topics, :speedometers, :minivans
+ fixtures :companies, :accounts, :topics, :speedometers, :minivans, :books
def test_should_sum_field
assert_equal 318, Account.sum(:credit_limit)
@@ -56,7 +57,7 @@ class CalculationsTest < ActiveRecord::TestCase
def test_should_return_integer_average_if_db_returns_such
ShipPart.delete_all
- ShipPart.create!(:id => 3, :name => 'foo')
+ ShipPart.create!(id: 3, name: "foo")
value = ShipPart.average(:id)
assert_equal 3, value
end
@@ -91,25 +92,25 @@ class CalculationsTest < ActiveRecord::TestCase
def test_should_group_by_field
c = Account.group(:firm_id).sum(:credit_limit)
- [1,6,2].each do |firm_id|
- assert c.keys.include?(firm_id), "Group #{c.inspect} does not contain firm_id #{firm_id}"
+ [1, 6, 2].each do |firm_id|
+ assert_includes c.keys, firm_id, "Group #{c.inspect} does not contain firm_id #{firm_id}"
end
end
def test_should_group_by_arel_attribute
c = Account.group(Account.arel_table[:firm_id]).sum(:credit_limit)
- [1,6,2].each do |firm_id|
- assert c.keys.include?(firm_id), "Group #{c.inspect} does not contain firm_id #{firm_id}"
+ [1, 6, 2].each do |firm_id|
+ assert_includes c.keys, firm_id, "Group #{c.inspect} does not contain firm_id #{firm_id}"
end
end
def test_should_group_by_multiple_fields
- c = Account.group('firm_id', :credit_limit).count(:all)
- [ [nil, 50], [1, 50], [6, 50], [6, 55], [9, 53], [2, 60] ].each { |firm_and_limit| assert c.keys.include?(firm_and_limit) }
+ c = Account.group("firm_id", :credit_limit).count(:all)
+ [ [nil, 50], [1, 50], [6, 50], [6, 55], [9, 53], [2, 60] ].each { |firm_and_limit| assert_includes c.keys, firm_and_limit }
end
def test_should_group_by_multiple_fields_having_functions
- c = Topic.group(:author_name, 'COALESCE(type, title)').count(:all)
+ c = Topic.group(:author_name, "COALESCE(type, title)").count(:all)
assert_equal 1, c[["Carl", "The Third Topic of the day"]]
assert_equal 1, c[["Mary", "Reply"]]
assert_equal 1, c[["David", "The First Topic"]]
@@ -165,14 +166,14 @@ class CalculationsTest < ActiveRecord::TestCase
end
def test_limit_should_apply_before_count
- accounts = Account.limit(3).where('firm_id IS NOT NULL')
+ accounts = Account.limit(3).where("firm_id IS NOT NULL")
assert_equal 3, accounts.count(:firm_id)
assert_equal 3, accounts.select(:firm_id).count
end
def test_limit_should_apply_before_count_arel_attribute
- accounts = Account.limit(3).where('firm_id IS NOT NULL')
+ accounts = Account.limit(3).where("firm_id IS NOT NULL")
firm_id_attribute = Account.arel_table[:firm_id]
assert_equal 3, accounts.count(firm_id_attribute)
@@ -227,7 +228,7 @@ class CalculationsTest < ActiveRecord::TestCase
end
def test_should_group_by_summed_field_having_condition
- c = Account.group(:firm_id).having('sum(credit_limit) > 50').sum(:credit_limit)
+ c = Account.group(:firm_id).having("sum(credit_limit) > 50").sum(:credit_limit)
assert_nil c[1]
assert_equal 105, c[6]
assert_equal 60, c[2]
@@ -248,52 +249,52 @@ class CalculationsTest < ActiveRecord::TestCase
end
def test_should_sum_field_with_conditions
- assert_equal 105, Account.where('firm_id = 6').sum(:credit_limit)
+ assert_equal 105, Account.where("firm_id = 6").sum(:credit_limit)
end
def test_should_return_zero_if_sum_conditions_return_nothing
- assert_equal 0, Account.where('1 = 2').sum(:credit_limit)
- assert_equal 0, companies(:rails_core).companies.where('1 = 2').sum(:id)
+ assert_equal 0, Account.where("1 = 2").sum(:credit_limit)
+ assert_equal 0, companies(:rails_core).companies.where("1 = 2").sum(:id)
end
def test_sum_should_return_valid_values_for_decimals
- NumericData.create(:bank_balance => 19.83)
+ NumericData.create(bank_balance: 19.83)
assert_equal 19.83, NumericData.sum(:bank_balance)
end
def test_should_return_type_casted_values_with_group_and_expression
- assert_equal 0.5, Account.group(:firm_name).sum('0.01 * credit_limit')['37signals']
+ assert_equal 0.5, Account.group(:firm_name).sum("0.01 * credit_limit")["37signals"]
end
def test_should_group_by_summed_field_with_conditions
- c = Account.where('firm_id > 1').group(:firm_id).sum(:credit_limit)
+ c = Account.where("firm_id > 1").group(:firm_id).sum(:credit_limit)
assert_nil c[1]
assert_equal 105, c[6]
assert_equal 60, c[2]
end
def test_should_group_by_summed_field_with_conditions_and_having
- c = Account.where('firm_id > 1').group(:firm_id).
- having('sum(credit_limit) > 60').sum(:credit_limit)
+ c = Account.where("firm_id > 1").group(:firm_id).
+ having("sum(credit_limit) > 60").sum(:credit_limit)
assert_nil c[1]
assert_equal 105, c[6]
assert_nil c[2]
end
def test_should_group_by_fields_with_table_alias
- c = Account.group('accounts.firm_id').sum(:credit_limit)
+ c = Account.group("accounts.firm_id").sum(:credit_limit)
assert_equal 50, c[1]
assert_equal 105, c[6]
assert_equal 60, c[2]
end
def test_should_calculate_with_invalid_field
- assert_equal 6, Account.calculate(:count, '*')
+ assert_equal 6, Account.calculate(:count, "*")
assert_equal 6, Account.calculate(:count, :all)
end
def test_should_calculate_grouped_with_invalid_field
- c = Account.group('accounts.firm_id').count(:all)
+ c = Account.group("accounts.firm_id").count(:all)
assert_equal 1, c[1]
assert_equal 2, c[6]
assert_equal 1, c[2]
@@ -307,8 +308,8 @@ class CalculationsTest < ActiveRecord::TestCase
end
def test_should_group_by_association_with_non_numeric_foreign_key
- Speedometer.create! id: 'ABC'
- Minivan.create! id: 'OMG', speedometer_id: 'ABC'
+ Speedometer.create! id: "ABC"
+ Minivan.create! id: "OMG", speedometer_id: "ABC"
c = Minivan.group(:speedometer).count(:all)
first_key = c.keys.first
@@ -317,7 +318,7 @@ class CalculationsTest < ActiveRecord::TestCase
end
def test_should_calculate_grouped_association_with_foreign_key_option
- Account.belongs_to :another_firm, :class_name => 'Firm', :foreign_key => 'firm_id'
+ Account.belongs_to :another_firm, class_name: "Firm", foreign_key: "firm_id"
c = Account.group(:another_firm).count(:all)
assert_equal 1, c[companies(:first_firm)]
assert_equal 2, c[companies(:rails_core)]
@@ -327,17 +328,17 @@ class CalculationsTest < ActiveRecord::TestCase
def test_should_calculate_grouped_by_function
c = Company.group("UPPER(#{QUOTED_TYPE})").count(:all)
assert_equal 2, c[nil]
- assert_equal 1, c['DEPENDENTFIRM']
- assert_equal 5, c['CLIENT']
- assert_equal 2, c['FIRM']
+ assert_equal 1, c["DEPENDENTFIRM"]
+ assert_equal 5, c["CLIENT"]
+ assert_equal 2, c["FIRM"]
end
def test_should_calculate_grouped_by_function_with_table_alias
c = Company.group("UPPER(companies.#{QUOTED_TYPE})").count(:all)
assert_equal 2, c[nil]
- assert_equal 1, c['DEPENDENTFIRM']
- assert_equal 5, c['CLIENT']
- assert_equal 2, c['FIRM']
+ assert_equal 1, c["DEPENDENTFIRM"]
+ assert_equal 5, c["CLIENT"]
+ assert_equal 2, c["FIRM"]
end
def test_should_not_overshadow_enumerable_sum
@@ -353,19 +354,19 @@ class CalculationsTest < ActiveRecord::TestCase
end
def test_should_sum_scoped_field_with_conditions
- assert_equal 8, companies(:rails_core).companies.where('id > 7').sum(:id)
+ assert_equal 8, companies(:rails_core).companies.where("id > 7").sum(:id)
end
def test_should_group_by_scoped_field
c = companies(:rails_core).companies.group(:name).sum(:id)
- assert_equal 7, c['Leetsoft']
- assert_equal 8, c['Jadedpixel']
+ assert_equal 7, c["Leetsoft"]
+ assert_equal 8, c["Jadedpixel"]
end
def test_should_group_by_summed_field_through_association_and_having
- c = companies(:rails_core).companies.group(:name).having('sum(id) > 7').sum(:id)
- assert_nil c['Leetsoft']
- assert_equal 8, c['Jadedpixel']
+ c = companies(:rails_core).companies.group(:name).having("sum(id) > 7").sum(:id)
+ assert_nil c["Leetsoft"]
+ assert_equal 8, c["Jadedpixel"]
end
def test_should_count_selected_field_with_include
@@ -380,7 +381,7 @@ class CalculationsTest < ActiveRecord::TestCase
end
def test_should_perform_joined_include_when_referencing_included_tables
- joined_count = Account.includes(:firm).where(:companies => {:name => '37signals'}).count
+ joined_count = Account.includes(:firm).where(companies: { name: "37signals" }).count
assert_equal 1, joined_count
end
@@ -391,10 +392,10 @@ class CalculationsTest < ActiveRecord::TestCase
def test_should_count_scoped_select_with_options
Account.update_all("credit_limit = NULL")
- Account.last.update_columns('credit_limit' => 49)
- Account.first.update_columns('credit_limit' => 51)
+ Account.last.update_columns("credit_limit" => 49)
+ Account.first.update_columns("credit_limit" => 51)
- assert_equal 1, Account.select("credit_limit").where('credit_limit >= 50').count
+ assert_equal 1, Account.select("credit_limit").where("credit_limit >= 50").count
end
def test_should_count_manual_select_with_include
@@ -420,10 +421,6 @@ class CalculationsTest < ActiveRecord::TestCase
def test_count_with_distinct
assert_equal 4, Account.select(:credit_limit).distinct.count
-
- assert_deprecated do
- assert_equal 4, Account.select(:credit_limit).uniq.count
- end
end
def test_count_with_aliased_attribute
@@ -435,8 +432,8 @@ class CalculationsTest < ActiveRecord::TestCase
end
def test_should_count_field_in_joined_table
- assert_equal 5, Account.joins(:firm).count('companies.id')
- assert_equal 4, Account.joins(:firm).distinct.count('companies.id')
+ assert_equal 5, Account.joins(:firm).count("companies.id")
+ assert_equal 4, Account.joins(:firm).distinct.count("companies.id")
end
def test_count_arel_attribute_in_joined_table_with
@@ -450,14 +447,14 @@ class CalculationsTest < ActiveRecord::TestCase
end
def test_should_count_field_in_joined_table_with_group_by
- c = Account.group('accounts.firm_id').joins(:firm).count('companies.id')
+ c = Account.group("accounts.firm_id").joins(:firm).count("companies.id")
- [1,6,2,9].each { |firm_id| assert c.keys.include?(firm_id) }
+ [1, 6, 2, 9].each { |firm_id| assert_includes c.keys, firm_id }
end
def test_should_count_field_of_root_table_with_conflicting_group_by_column
assert_equal({ 1 => 1 }, Firm.joins(:accounts).group(:firm_id).count)
- assert_equal({ 1 => 1 }, Firm.joins(:accounts).group('accounts.firm_id').count)
+ assert_equal({ 1 => 1 }, Firm.joins(:accounts).group("accounts.firm_id").count)
end
def test_count_with_no_parameters_isnt_deprecated
@@ -477,9 +474,9 @@ class CalculationsTest < ActiveRecord::TestCase
end
def test_count_with_where_and_order
- assert_equal 1, Account.where(firm_name: '37signals').count
- assert_equal 1, Account.where(firm_name: '37signals').order(:firm_name).count
- assert_equal 1, Account.where(firm_name: '37signals').order(:firm_name).reverse_order.count
+ assert_equal 1, Account.where(firm_name: "37signals").count
+ assert_equal 1, Account.where(firm_name: "37signals").order(:firm_name).count
+ assert_equal 1, Account.where(firm_name: "37signals").order(:firm_name).reverse_order.count
end
def test_count_with_block
@@ -496,69 +493,69 @@ class CalculationsTest < ActiveRecord::TestCase
end
def test_sum_expression_returns_zero_when_no_records_to_sum
- assert_equal 0, Account.where('1 = 2').sum("2 * credit_limit")
+ assert_equal 0, Account.where("1 = 2").sum("2 * credit_limit")
end
def test_count_with_from_option
- assert_equal Company.count(:all), Company.from('companies').count(:all)
+ assert_equal Company.count(:all), Company.from("companies").count(:all)
assert_equal Account.where("credit_limit = 50").count(:all),
- Account.from('accounts').where("credit_limit = 50").count(:all)
- assert_equal Company.where(:type => "Firm").count(:type),
- Company.where(:type => "Firm").from('companies').count(:type)
+ Account.from("accounts").where("credit_limit = 50").count(:all)
+ assert_equal Company.where(type: "Firm").count(:type),
+ Company.where(type: "Firm").from("companies").count(:type)
end
def test_sum_with_from_option
- assert_equal Account.sum(:credit_limit), Account.from('accounts').sum(:credit_limit)
+ assert_equal Account.sum(:credit_limit), Account.from("accounts").sum(:credit_limit)
assert_equal Account.where("credit_limit > 50").sum(:credit_limit),
- Account.where("credit_limit > 50").from('accounts').sum(:credit_limit)
+ Account.where("credit_limit > 50").from("accounts").sum(:credit_limit)
end
def test_average_with_from_option
- assert_equal Account.average(:credit_limit), Account.from('accounts').average(:credit_limit)
+ assert_equal Account.average(:credit_limit), Account.from("accounts").average(:credit_limit)
assert_equal Account.where("credit_limit > 50").average(:credit_limit),
- Account.where("credit_limit > 50").from('accounts').average(:credit_limit)
+ Account.where("credit_limit > 50").from("accounts").average(:credit_limit)
end
def test_minimum_with_from_option
- assert_equal Account.minimum(:credit_limit), Account.from('accounts').minimum(:credit_limit)
+ assert_equal Account.minimum(:credit_limit), Account.from("accounts").minimum(:credit_limit)
assert_equal Account.where("credit_limit > 50").minimum(:credit_limit),
- Account.where("credit_limit > 50").from('accounts').minimum(:credit_limit)
+ Account.where("credit_limit > 50").from("accounts").minimum(:credit_limit)
end
def test_maximum_with_from_option
- assert_equal Account.maximum(:credit_limit), Account.from('accounts').maximum(:credit_limit)
+ assert_equal Account.maximum(:credit_limit), Account.from("accounts").maximum(:credit_limit)
assert_equal Account.where("credit_limit > 50").maximum(:credit_limit),
- Account.where("credit_limit > 50").from('accounts').maximum(:credit_limit)
+ Account.where("credit_limit > 50").from("accounts").maximum(:credit_limit)
end
def test_maximum_with_not_auto_table_name_prefix_if_column_included
- Company.create!(:name => "test", :contracts => [Contract.new(:developer_id => 7)])
+ Company.create!(name: "test", contracts: [Contract.new(developer_id: 7)])
assert_equal 7, Company.includes(:contracts).maximum(:developer_id)
end
def test_minimum_with_not_auto_table_name_prefix_if_column_included
- Company.create!(:name => "test", :contracts => [Contract.new(:developer_id => 7)])
+ Company.create!(name: "test", contracts: [Contract.new(developer_id: 7)])
assert_equal 7, Company.includes(:contracts).minimum(:developer_id)
end
def test_sum_with_not_auto_table_name_prefix_if_column_included
- Company.create!(:name => "test", :contracts => [Contract.new(:developer_id => 7)])
+ Company.create!(name: "test", contracts: [Contract.new(developer_id: 7)])
assert_equal 7, Company.includes(:contracts).sum(:developer_id)
end
if current_adapter?(:Mysql2Adapter)
def test_from_option_with_specified_index
- assert_equal Edge.count(:all), Edge.from('edges USE INDEX(unique_edge_index)').count(:all)
- assert_equal Edge.where('sink_id < 5').count(:all),
- Edge.from('edges USE INDEX(unique_edge_index)').where('sink_id < 5').count(:all)
+ assert_equal Edge.count(:all), Edge.from("edges USE INDEX(unique_edge_index)").count(:all)
+ assert_equal Edge.where("sink_id < 5").count(:all),
+ Edge.from("edges USE INDEX(unique_edge_index)").where("sink_id < 5").count(:all)
end
end
def test_from_option_with_table_different_than_class
- assert_equal Account.count(:all), Company.from('accounts').count(:all)
+ assert_equal Account.count(:all), Company.from("accounts").count(:all)
end
def test_distinct_is_honored_when_used_with_count_operation_after_group
@@ -571,7 +568,7 @@ class CalculationsTest < ActiveRecord::TestCase
end
def test_pluck
- assert_equal [1,2,3,4,5], Topic.order(:id).pluck(:id)
+ assert_equal [1, 2, 3, 4, 5], Topic.order(:id).pluck(:id)
end
def test_pluck_without_column_names
@@ -581,7 +578,7 @@ class CalculationsTest < ActiveRecord::TestCase
def test_pluck_type_cast
topic = topics(:first)
- relation = Topic.where(:id => topic.id)
+ relation = Topic.where(id: topic.id)
assert_equal [ topic.approved ], relation.pluck(:approved)
assert_equal [ topic.last_read ], relation.pluck(:last_read)
assert_equal [ topic.written_on ], relation.pluck(:written_on)
@@ -598,42 +595,42 @@ class CalculationsTest < ActiveRecord::TestCase
end
def test_pluck_on_aliased_attribute
- assert_equal 'The First Topic', Topic.order(:id).pluck(:heading).first
+ assert_equal "The First Topic", Topic.order(:id).pluck(:heading).first
end
def test_pluck_with_serialization
- t = Topic.create!(:content => { :foo => :bar })
- assert_equal [{:foo => :bar}], Topic.where(:id => t.id).pluck(:content)
+ t = Topic.create!(content: { foo: :bar })
+ assert_equal [{ foo: :bar }], Topic.where(id: t.id).pluck(:content)
end
def test_pluck_with_qualified_column_name
- assert_equal [1,2,3,4,5], Topic.order(:id).pluck("topics.id")
+ assert_equal [1, 2, 3, 4, 5], Topic.order(:id).pluck("topics.id")
end
def test_pluck_auto_table_name_prefix
- c = Company.create!(:name => "test", :contracts => [Contract.new])
+ c = Company.create!(name: "test", contracts: [Contract.new])
assert_equal [c.id], Company.joins(:contracts).pluck(:id)
end
def test_pluck_if_table_included
- c = Company.create!(:name => "test", :contracts => [Contract.new(:developer_id => 7)])
+ c = Company.create!(name: "test", contracts: [Contract.new(developer_id: 7)])
assert_equal [c.id], Company.includes(:contracts).where("contracts.id" => c.contracts.first).pluck(:id)
end
def test_pluck_not_auto_table_name_prefix_if_column_joined
- Company.create!(:name => "test", :contracts => [Contract.new(:developer_id => 7)])
+ Company.create!(name: "test", contracts: [Contract.new(developer_id: 7)])
assert_equal [7], Company.joins(:contracts).pluck(:developer_id)
end
def test_pluck_with_selection_clause
- assert_equal [50, 53, 55, 60], Account.pluck('DISTINCT credit_limit').sort
- assert_equal [50, 53, 55, 60], Account.pluck('DISTINCT accounts.credit_limit').sort
- assert_equal [50, 53, 55, 60], Account.pluck('DISTINCT(credit_limit)').sort
+ assert_equal [50, 53, 55, 60], Account.pluck("DISTINCT credit_limit").sort
+ assert_equal [50, 53, 55, 60], Account.pluck("DISTINCT accounts.credit_limit").sort
+ assert_equal [50, 53, 55, 60], Account.pluck("DISTINCT(credit_limit)").sort
# MySQL returns "SUM(DISTINCT(credit_limit))" as the column name unless
# an alias is provided. Without the alias, the column cannot be found
# and properly typecast.
- assert_equal [50 + 53 + 55 + 60], Account.pluck('SUM(DISTINCT(credit_limit)) as credit_limit')
+ assert_equal [50 + 53 + 55 + 60], Account.pluck("SUM(DISTINCT(credit_limit)) as credit_limit")
end
def test_plucks_with_ids
@@ -642,11 +639,11 @@ class CalculationsTest < ActiveRecord::TestCase
def test_pluck_with_includes_limit_and_empty_result
assert_equal [], Topic.includes(:replies).limit(0).pluck(:id)
- assert_equal [], Topic.includes(:replies).limit(1).where('0 = 1').pluck(:id)
+ assert_equal [], Topic.includes(:replies).limit(1).where("0 = 1").pluck(:id)
end
def test_pluck_not_auto_table_name_prefix_if_column_included
- Company.create!(:name => "test", :contracts => [Contract.new(:developer_id => 7)])
+ Company.create!(name: "test", contracts: [Contract.new(developer_id: 7)])
ids = Company.includes(:contracts).pluck(:developer_id)
assert_equal Company.count, ids.length
assert_equal [7], ids.compact
@@ -667,12 +664,12 @@ class CalculationsTest < ActiveRecord::TestCase
def test_pluck_with_multiple_columns_and_selection_clause
assert_equal [[1, 50], [2, 50], [3, 50], [4, 60], [5, 55], [6, 53]],
- Account.pluck('id, credit_limit')
+ Account.pluck("id, credit_limit")
end
def test_pluck_with_multiple_columns_and_includes
- Company.create!(:name => "test", :contracts => [Contract.new(:developer_id => 7)])
- companies_and_developers = Company.order('companies.id').includes(:contracts).pluck(:name, :developer_id)
+ Company.create!(name: "test", contracts: [Contract.new(developer_id: 7)])
+ companies_and_developers = Company.order("companies.id").includes(:contracts).pluck(:name, :developer_id)
assert_equal Company.count, companies_and_developers.length
assert_equal ["37signals", nil], companies_and_developers.first
@@ -680,21 +677,21 @@ class CalculationsTest < ActiveRecord::TestCase
end
def test_pluck_with_reserved_words
- Possession.create!(:where => "Over There")
+ Possession.create!(where: "Over There")
assert_equal ["Over There"], Possession.pluck(:where)
end
def test_pluck_replaces_select_clause
taks_relation = Topic.select(:approved, :id).order(:id)
- assert_equal [1,2,3,4,5], taks_relation.pluck(:id)
+ assert_equal [1, 2, 3, 4, 5], taks_relation.pluck(:id)
assert_equal [false, true, true, true, true], taks_relation.pluck(:approved)
end
def test_pluck_columns_with_same_name
expected = [["The First Topic", "The Second Topic of the day"], ["The Third Topic of the day", "The Fourth Topic of the day"]]
actual = Topic.joins(:replies)
- .pluck('topics.title', 'replies_topics.title')
+ .pluck("topics.title", "replies_topics.title")
assert_equal expected, actual
end
@@ -715,21 +712,21 @@ class CalculationsTest < ActiveRecord::TestCase
def test_pluck_loaded_relation
companies = Company.order(:id).limit(3).load
assert_no_queries do
- assert_equal ['37signals', 'Summit', 'Microsoft'], companies.pluck(:name)
+ assert_equal ["37signals", "Summit", "Microsoft"], companies.pluck(:name)
end
end
def test_pluck_loaded_relation_multiple_columns
companies = Company.order(:id).limit(3).load
assert_no_queries do
- assert_equal [[1, '37signals'], [2, 'Summit'], [3, 'Microsoft']], companies.pluck(:id, :name)
+ assert_equal [[1, "37signals"], [2, "Summit"], [3, "Microsoft"]], companies.pluck(:id, :name)
end
end
def test_pluck_loaded_relation_sql_fragment
companies = Company.order(:name).limit(3).load
assert_queries 1 do
- assert_equal ['37signals', 'Apex', 'Ex Nihilo'], companies.pluck('DISTINCT name')
+ assert_equal ["37signals", "Apex", "Ex Nihilo"], companies.pluck("DISTINCT name")
end
end
@@ -747,7 +744,7 @@ class CalculationsTest < ActiveRecord::TestCase
def test_should_reference_correct_aliases_while_joining_tables_of_has_many_through_association
assert_nothing_raised do
- developer = Developer.create!(name: 'developer')
+ developer = Developer.create!(name: "developer")
developer.ratings.includes(comment: :post).where(posts: { id: 1 }).count
end
end
@@ -782,7 +779,7 @@ class CalculationsTest < ActiveRecord::TestCase
end
end
- params = protected_params.new(credit_limit: '50')
+ params = protected_params.new(credit_limit: "50")
assert_raises(ActiveModel::ForbiddenAttributesError) do
Account.group(:id).having(params)
@@ -793,4 +790,8 @@ class CalculationsTest < ActiveRecord::TestCase
assert_equal 50, result[1].credit_limit
assert_equal 50, result[2].credit_limit
end
+
+ def test_group_by_attribute_with_custom_type
+ assert_equal({ "proposed" => 2, "published" => 2 }, Book.group(:status).count)
+ end
end
diff --git a/activerecord/test/cases/callbacks_test.rb b/activerecord/test/cases/callbacks_test.rb
index 4f70ae3a1d..11ec6fb2c5 100644
--- a/activerecord/test/cases/callbacks_test.rb
+++ b/activerecord/test/cases/callbacks_test.rb
@@ -1,9 +1,9 @@
require "cases/helper"
-require 'models/developer'
-require 'models/computer'
+require "models/developer"
+require "models/computer"
class CallbackDeveloper < ActiveRecord::Base
- self.table_name = 'developers'
+ self.table_name = "developers"
class << self
def callback_string(callback_method)
@@ -16,7 +16,7 @@ class CallbackDeveloper < ActiveRecord::Base
def define_callback_method(callback_method)
define_method(callback_method) do
- self.history << [callback_method, :method]
+ history << [callback_method, :method]
end
send(callback_method, :"#{callback_method}")
end
@@ -31,7 +31,7 @@ class CallbackDeveloper < ActiveRecord::Base
end
ActiveRecord::Callbacks::CALLBACKS.each do |callback_method|
- next if callback_method.to_s =~ /^around_/
+ next if callback_method.to_s.start_with?("around_")
define_callback_method(callback_method)
ActiveSupport::Deprecation.silence { send(callback_method, callback_string(callback_method)) }
send(callback_method, callback_proc(callback_method))
@@ -55,19 +55,18 @@ class CallbackDeveloperWithHaltedValidation < CallbackDeveloper
end
class ParentDeveloper < ActiveRecord::Base
- self.table_name = 'developers'
+ self.table_name = "developers"
attr_accessor :after_save_called
- before_validation {|record| record.after_save_called = true}
+ before_validation { |record| record.after_save_called = true }
end
class ChildDeveloper < ParentDeveloper
-
end
class ImmutableDeveloper < ActiveRecord::Base
- self.table_name = 'developers'
+ self.table_name = "developers"
- validates_inclusion_of :salary, :in => 50000..200000
+ validates_inclusion_of :salary, in: 50000..200000
before_save :cancel
before_destroy :cancel
@@ -79,7 +78,7 @@ class ImmutableDeveloper < ActiveRecord::Base
end
class DeveloperWithCanceledCallbacks < ActiveRecord::Base
- self.table_name = 'developers'
+ self.table_name = "developers"
validates_inclusion_of :salary, in: 50000..200000
@@ -93,19 +92,19 @@ class DeveloperWithCanceledCallbacks < ActiveRecord::Base
end
class OnCallbacksDeveloper < ActiveRecord::Base
- self.table_name = 'developers'
+ self.table_name = "developers"
before_validation { history << :before_validation }
- before_validation(:on => :create){ history << :before_validation_on_create }
- before_validation(:on => :update){ history << :before_validation_on_update }
+ before_validation(on: :create) { history << :before_validation_on_create }
+ before_validation(on: :update) { history << :before_validation_on_update }
validate do
history << :validate
end
after_validation { history << :after_validation }
- after_validation(:on => :create){ history << :after_validation_on_create }
- after_validation(:on => :update){ history << :after_validation_on_update }
+ after_validation(on: :create) { history << :after_validation_on_create }
+ after_validation(on: :update) { history << :after_validation_on_update }
def history
@history ||= []
@@ -113,17 +112,17 @@ class OnCallbacksDeveloper < ActiveRecord::Base
end
class ContextualCallbacksDeveloper < ActiveRecord::Base
- self.table_name = 'developers'
+ self.table_name = "developers"
before_validation { history << :before_validation }
- before_validation :before_validation_on_create_and_update, :on => [ :create, :update ]
+ before_validation :before_validation_on_create_and_update, on: [ :create, :update ]
validate do
history << :validate
end
after_validation { history << :after_validation }
- after_validation :after_validation_on_create_and_update, :on => [ :create, :update ]
+ after_validation :after_validation_on_create_and_update, on: [ :create, :update ]
def before_validation_on_create_and_update
history << "before_validation_on_#{self.validation_context}".to_sym
@@ -139,12 +138,12 @@ class ContextualCallbacksDeveloper < ActiveRecord::Base
end
class CallbackCancellationDeveloper < ActiveRecord::Base
- self.table_name = 'developers'
+ self.table_name = "developers"
attr_reader :after_save_called, :after_create_called, :after_update_called, :after_destroy_called
attr_accessor :cancel_before_save, :cancel_before_create, :cancel_before_update, :cancel_before_destroy
- before_save {defined?(@cancel_before_save) ? !@cancel_before_save : false}
+ before_save { defined?(@cancel_before_save) ? !@cancel_before_save : false }
before_create { !@cancel_before_create }
before_update { !@cancel_before_update }
before_destroy { !@cancel_before_destroy }
@@ -156,7 +155,7 @@ class CallbackCancellationDeveloper < ActiveRecord::Base
end
class CallbackHaltedDeveloper < ActiveRecord::Base
- self.table_name = 'developers'
+ self.table_name = "developers"
attr_reader :after_save_called, :after_create_called, :after_update_called, :after_destroy_called
attr_accessor :cancel_before_save, :cancel_before_create, :cancel_before_update, :cancel_before_destroy
@@ -252,7 +251,7 @@ class CallbacksTest < ActiveRecord::TestCase
end
def test_create
- david = CallbackDeveloper.create('name' => 'David', 'salary' => 1000000)
+ david = CallbackDeveloper.create("name" => "David", "salary" => 1000000)
assert_equal [
[ :after_initialize, :method ],
[ :after_initialize, :string ],
@@ -298,7 +297,7 @@ class CallbacksTest < ActiveRecord::TestCase
end
def test_validate_on_create
- david = OnCallbacksDeveloper.create('name' => 'David', 'salary' => 1000000)
+ david = OnCallbacksDeveloper.create("name" => "David", "salary" => 1000000)
assert_equal [
:before_validation,
:before_validation_on_create,
@@ -309,7 +308,7 @@ class CallbacksTest < ActiveRecord::TestCase
end
def test_validate_on_contextual_create
- david = ContextualCallbacksDeveloper.create('name' => 'David', 'salary' => 1000000)
+ david = ContextualCallbacksDeveloper.create("name" => "David", "salary" => 1000000)
assert_equal [
:before_validation,
:before_validation_on_create,
@@ -450,7 +449,7 @@ class CallbacksTest < ActiveRecord::TestCase
assert david.valid?
assert !david.save
exc = assert_raise(ActiveRecord::RecordNotSaved) { david.save! }
- assert_equal exc.record, david
+ assert_equal david, exc.record
assert_equal "Failed to save the record", exc.message
end
@@ -494,7 +493,7 @@ class CallbacksTest < ActiveRecord::TestCase
assert_deprecated do
assert !david.destroy
exc = assert_raise(ActiveRecord::RecordNotDestroyed) { david.destroy! }
- assert_equal exc.record, david
+ assert_equal david, exc.record
assert_equal "Failed to destroy the record", exc.message
end
assert_not_nil ImmutableDeveloper.find_by_id(1)
@@ -528,7 +527,7 @@ class CallbacksTest < ActiveRecord::TestCase
assert david.valid?
assert !david.save
exc = assert_raise(ActiveRecord::RecordNotSaved) { david.save! }
- assert_equal exc.record, david
+ assert_equal david, exc.record
david = DeveloperWithCanceledCallbacks.find(1)
david.salary = 10_000_000
@@ -555,7 +554,7 @@ class CallbacksTest < ActiveRecord::TestCase
david = DeveloperWithCanceledCallbacks.find(1)
assert !david.destroy
exc = assert_raise(ActiveRecord::RecordNotDestroyed) { david.destroy! }
- assert_equal exc.record, david
+ assert_equal david, exc.record
assert_not_nil ImmutableDeveloper.find_by_id(1)
someone = CallbackHaltedDeveloper.find(1)
@@ -632,5 +631,4 @@ class CallbacksTest < ActiveRecord::TestCase
child.save
assert child.after_save_called
end
-
end
diff --git a/activerecord/test/cases/clone_test.rb b/activerecord/test/cases/clone_test.rb
index 5e43082c33..b89294c094 100644
--- a/activerecord/test/cases/clone_test.rb
+++ b/activerecord/test/cases/clone_test.rb
@@ -1,5 +1,5 @@
require "cases/helper"
-require 'models/topic'
+require "models/topic"
module ActiveRecord
class CloneTest < ActiveRecord::TestCase
@@ -8,9 +8,9 @@ module ActiveRecord
def test_persisted
topic = Topic.first
cloned = topic.clone
- assert topic.persisted?, 'topic persisted'
- assert cloned.persisted?, 'topic persisted'
- assert !cloned.new_record?, 'topic is not new'
+ assert topic.persisted?, "topic persisted"
+ assert cloned.persisted?, "topic persisted"
+ assert !cloned.new_record?, "topic is not new"
end
def test_stays_frozen
@@ -18,16 +18,16 @@ module ActiveRecord
topic.freeze
cloned = topic.clone
- assert cloned.persisted?, 'topic persisted'
- assert !cloned.new_record?, 'topic is not new'
- assert cloned.frozen?, 'topic should be frozen'
+ assert cloned.persisted?, "topic persisted"
+ assert !cloned.new_record?, "topic is not new"
+ assert cloned.frozen?, "topic should be frozen"
end
def test_shallow
topic = Topic.first
cloned = topic.clone
- topic.author_name = 'Aaron'
- assert_equal 'Aaron', cloned.author_name
+ topic.author_name = "Aaron"
+ assert_equal "Aaron", cloned.author_name
end
def test_freezing_a_cloned_model_does_not_freeze_clone
diff --git a/activerecord/test/cases/coders/yaml_column_test.rb b/activerecord/test/cases/coders/yaml_column_test.rb
index b72c54f97b..b9c6224425 100644
--- a/activerecord/test/cases/coders/yaml_column_test.rb
+++ b/activerecord/test/cases/coders/yaml_column_test.rb
@@ -35,7 +35,7 @@ module ActiveRecord
def test_returns_string_unless_starts_with_dash
coder = YAMLColumn.new
- assert_equal 'foo', coder.load("foo")
+ assert_equal "foo", coder.load("foo")
end
def test_load_handles_other_classes
@@ -45,7 +45,7 @@ module ActiveRecord
def test_load_doesnt_swallow_yaml_exceptions
coder = YAMLColumn.new
- bad_yaml = '--- {'
+ bad_yaml = "--- {"
assert_raises(Psych::SyntaxError) do
coder.load(bad_yaml)
end
diff --git a/activerecord/test/cases/collection_cache_key_test.rb b/activerecord/test/cases/collection_cache_key_test.rb
index a2874438c1..381a78a8e2 100644
--- a/activerecord/test/cases/collection_cache_key_test.rb
+++ b/activerecord/test/cases/collection_cache_key_test.rb
@@ -28,20 +28,20 @@ module ActiveRecord
end
test "it triggers at most one query" do
- developers = Developer.where(name: "David")
+ developers = Developer.where(name: "David")
assert_queries(1) { developers.cache_key }
assert_queries(0) { developers.cache_key }
end
test "it doesn't trigger any query if the relation is already loaded" do
- developers = Developer.where(name: "David").load
+ developers = Developer.where(name: "David").load
assert_queries(0) { developers.cache_key }
end
test "relation cache_key changes when the sql query changes" do
developers = Developer.where(name: "David")
- other_relation = Developer.where(name: "David").where("1 = 1")
+ other_relation = Developer.where(name: "David").where("1 = 1")
assert_not_equal developers.cache_key, other_relation.cache_key
end
diff --git a/activerecord/test/cases/column_alias_test.rb b/activerecord/test/cases/column_alias_test.rb
index 40707d9cb2..9893ba9580 100644
--- a/activerecord/test/cases/column_alias_test.rb
+++ b/activerecord/test/cases/column_alias_test.rb
@@ -1,17 +1,17 @@
require "cases/helper"
-require 'models/topic'
+require "models/topic"
class TestColumnAlias < ActiveRecord::TestCase
fixtures :topics
- QUERY = if 'Oracle' == ActiveRecord::Base.connection.adapter_name
- 'SELECT id AS pk FROM topics WHERE ROWNUM < 2'
- else
- 'SELECT id AS pk FROM topics'
- end
+ QUERY = if "Oracle" == ActiveRecord::Base.connection.adapter_name
+ "SELECT id AS pk FROM topics WHERE ROWNUM < 2"
+ else
+ "SELECT id AS pk FROM topics"
+ end
def test_column_alias
records = Topic.connection.select_all(QUERY)
- assert_equal 'pk', records[0].keys[0]
+ assert_equal "pk", records[0].keys[0]
end
end
diff --git a/activerecord/test/cases/column_definition_test.rb b/activerecord/test/cases/column_definition_test.rb
index 78c0853992..a65bb89052 100644
--- a/activerecord/test/cases/column_definition_test.rb
+++ b/activerecord/test/cases/column_definition_test.rb
@@ -6,7 +6,7 @@ module ActiveRecord
def setup
@adapter = AbstractAdapter.new(nil)
def @adapter.native_database_types
- {:string => "varchar"}
+ { string: "varchar" }
end
@viz = @adapter.schema_creation
end
@@ -18,7 +18,7 @@ module ActiveRecord
column_def = ColumnDefinition.new(
column.name, "string",
column.limit, column.precision, column.scale, column.default, column.null)
- assert_equal "title varchar(20)", @viz.accept(column_def)
+ assert_equal "title varchar(20)", @viz.accept(column_def)
end
def test_should_include_default_clause_when_default_is_present
@@ -26,7 +26,7 @@ module ActiveRecord
column_def = ColumnDefinition.new(
column.name, "string",
column.limit, column.precision, column.scale, column.default, column.null)
- assert_equal %Q{title varchar(20) DEFAULT 'Hello'}, @viz.accept(column_def)
+ assert_equal "title varchar(20) DEFAULT 'Hello'", @viz.accept(column_def)
end
def test_should_specify_not_null_if_null_option_is_false
@@ -35,7 +35,7 @@ module ActiveRecord
column_def = ColumnDefinition.new(
column.name, "string",
column.limit, column.precision, column.scale, column.default, column.null)
- assert_equal %Q{title varchar(20) DEFAULT 'Hello' NOT NULL}, @viz.accept(column_def)
+ assert_equal "title varchar(20) DEFAULT 'Hello' NOT NULL", @viz.accept(column_def)
end
if current_adapter?(:Mysql2Adapter)
@@ -60,14 +60,13 @@ module ActiveRecord
end
def test_should_not_set_default_for_blob_and_text_data_types
- text_type = MySQL::TypeMetadata.new(
- SqlTypeMetadata.new(type: :text))
+ text_type = MySQL::TypeMetadata.new(SqlTypeMetadata.new(type: :text))
text_column = MySQL::Column.new("title", nil, text_type)
- assert_equal nil, text_column.default
+ assert_nil text_column.default
not_null_text_column = MySQL::Column.new("title", nil, text_type, false)
- assert_equal "", not_null_text_column.default
+ assert_nil not_null_text_column.default
end
def test_has_default_should_return_false_for_blob_and_text_data_types
diff --git a/activerecord/test/cases/comment_test.rb b/activerecord/test/cases/comment_test.rb
index 839fdbe578..a625299e8d 100644
--- a/activerecord/test/cases/comment_test.rb
+++ b/activerecord/test/cases/comment_test.rb
@@ -1,139 +1,138 @@
-require 'cases/helper'
-require 'support/schema_dumping_helper'
+require "cases/helper"
+require "support/schema_dumping_helper"
if ActiveRecord::Base.connection.supports_comments?
+ class CommentTest < ActiveRecord::TestCase
+ include SchemaDumpingHelper
-class CommentTest < ActiveRecord::TestCase
- include SchemaDumpingHelper
-
- class Commented < ActiveRecord::Base
- self.table_name = 'commenteds'
- end
-
- class BlankComment < ActiveRecord::Base
- end
-
- setup do
- @connection = ActiveRecord::Base.connection
-
- @connection.create_table('commenteds', comment: 'A table with comment', force: true) do |t|
- t.string 'name', comment: 'Comment should help clarify the column purpose'
- t.boolean 'obvious', comment: 'Question is: should you comment obviously named objects?'
- t.string 'content'
- t.index 'name', comment: %Q["Very important" index that powers all the performance.\nAnd it's fun!]
+ class Commented < ActiveRecord::Base
+ self.table_name = "commenteds"
end
- @connection.create_table('blank_comments', comment: ' ', force: true) do |t|
- t.string :space_comment, comment: ' '
- t.string :empty_comment, comment: ''
- t.string :nil_comment, comment: nil
- t.string :absent_comment
- t.index :space_comment, comment: ' '
- t.index :empty_comment, comment: ''
- t.index :nil_comment, comment: nil
- t.index :absent_comment
+ class BlankComment < ActiveRecord::Base
end
- Commented.reset_column_information
- BlankComment.reset_column_information
- end
-
- teardown do
- @connection.drop_table 'commenteds', if_exists: true
- @connection.drop_table 'blank_comments', if_exists: true
- end
+ setup do
+ @connection = ActiveRecord::Base.connection
+
+ @connection.create_table("commenteds", comment: "A table with comment", force: true) do |t|
+ t.string "name", comment: "Comment should help clarify the column purpose"
+ t.boolean "obvious", comment: "Question is: should you comment obviously named objects?"
+ t.string "content"
+ t.index "name", comment: %Q["Very important" index that powers all the performance.\nAnd it's fun!]
+ end
+
+ @connection.create_table("blank_comments", comment: " ", force: true) do |t|
+ t.string :space_comment, comment: " "
+ t.string :empty_comment, comment: ""
+ t.string :nil_comment, comment: nil
+ t.string :absent_comment
+ t.index :space_comment, comment: " "
+ t.index :empty_comment, comment: ""
+ t.index :nil_comment, comment: nil
+ t.index :absent_comment
+ end
+
+ Commented.reset_column_information
+ BlankComment.reset_column_information
+ end
- def test_column_created_in_block
- column = Commented.columns_hash['name']
- assert_equal :string, column.type
- assert_equal 'Comment should help clarify the column purpose', column.comment
- end
+ teardown do
+ @connection.drop_table "commenteds", if_exists: true
+ @connection.drop_table "blank_comments", if_exists: true
+ end
- def test_blank_columns_created_in_block
- %w[ space_comment empty_comment nil_comment absent_comment ].each do |field|
- column = BlankComment.columns_hash[field]
+ def test_column_created_in_block
+ column = Commented.columns_hash["name"]
assert_equal :string, column.type
- assert_nil column.comment
+ assert_equal "Comment should help clarify the column purpose", column.comment
end
- end
- def test_blank_indexes_created_in_block
- @connection.indexes('blank_comments').each do |index|
- assert_nil index.comment
+ def test_blank_columns_created_in_block
+ %w[ space_comment empty_comment nil_comment absent_comment ].each do |field|
+ column = BlankComment.columns_hash[field]
+ assert_equal :string, column.type
+ assert_nil column.comment
+ end
end
- end
- def test_add_column_with_comment_later
- @connection.add_column :commenteds, :rating, :integer, comment: 'I am running out of imagination'
- Commented.reset_column_information
- column = Commented.columns_hash['rating']
+ def test_blank_indexes_created_in_block
+ @connection.indexes("blank_comments").each do |index|
+ assert_nil index.comment
+ end
+ end
- assert_equal :integer, column.type
- assert_equal 'I am running out of imagination', column.comment
- end
+ def test_add_column_with_comment_later
+ @connection.add_column :commenteds, :rating, :integer, comment: "I am running out of imagination"
+ Commented.reset_column_information
+ column = Commented.columns_hash["rating"]
- def test_add_index_with_comment_later
- @connection.add_index :commenteds, :obvious, name: 'idx_obvious', comment: 'We need to see obvious comments'
- index = @connection.indexes('commenteds').find { |idef| idef.name == 'idx_obvious' }
- assert_equal 'We need to see obvious comments', index.comment
- end
+ assert_equal :integer, column.type
+ assert_equal "I am running out of imagination", column.comment
+ end
- def test_add_comment_to_column
- @connection.change_column :commenteds, :content, :string, comment: 'Whoa, content describes itself!'
+ def test_add_index_with_comment_later
+ @connection.add_index :commenteds, :obvious, name: "idx_obvious", comment: "We need to see obvious comments"
+ index = @connection.indexes("commenteds").find { |idef| idef.name == "idx_obvious" }
+ assert_equal "We need to see obvious comments", index.comment
+ end
- Commented.reset_column_information
- column = Commented.columns_hash['content']
+ def test_add_comment_to_column
+ @connection.change_column :commenteds, :content, :string, comment: "Whoa, content describes itself!"
- assert_equal :string, column.type
- assert_equal 'Whoa, content describes itself!', column.comment
- end
+ Commented.reset_column_information
+ column = Commented.columns_hash["content"]
- def test_remove_comment_from_column
- @connection.change_column :commenteds, :obvious, :string, comment: nil
+ assert_equal :string, column.type
+ assert_equal "Whoa, content describes itself!", column.comment
+ end
- Commented.reset_column_information
- column = Commented.columns_hash['obvious']
+ def test_remove_comment_from_column
+ @connection.change_column :commenteds, :obvious, :string, comment: nil
- assert_equal :string, column.type
- assert_nil column.comment
- end
+ Commented.reset_column_information
+ column = Commented.columns_hash["obvious"]
- def test_schema_dump_with_comments
- # Do all the stuff from other tests
- @connection.add_column :commenteds, :rating, :integer, comment: 'I am running out of imagination'
- @connection.change_column :commenteds, :content, :string, comment: 'Whoa, content describes itself!'
- @connection.change_column :commenteds, :obvious, :string, comment: nil
- @connection.add_index :commenteds, :obvious, name: 'idx_obvious', comment: 'We need to see obvious comments'
-
- # And check that these changes are reflected in dump
- output = dump_table_schema 'commenteds'
- assert_match %r[create_table "commenteds",.+\s+comment: "A table with comment"], output
- assert_match %r[t\.string\s+"name",\s+comment: "Comment should help clarify the column purpose"], output
- assert_match %r[t\.string\s+"obvious"\n], output
- assert_match %r[t\.string\s+"content",\s+comment: "Whoa, content describes itself!"], output
- assert_match %r[t\.integer\s+"rating",\s+comment: "I am running out of imagination"], output
- assert_match %r[t\.index\s+.+\s+comment: "\\\"Very important\\\" index that powers all the performance.\\nAnd it's fun!"], output
- assert_match %r[t\.index\s+.+\s+name: "idx_obvious",.+\s+comment: "We need to see obvious comments"], output
- end
+ assert_equal :string, column.type
+ assert_nil column.comment
+ end
+
+ def test_schema_dump_with_comments
+ # Do all the stuff from other tests
+ @connection.add_column :commenteds, :rating, :integer, comment: "I am running out of imagination"
+ @connection.change_column :commenteds, :content, :string, comment: "Whoa, content describes itself!"
+ @connection.change_column :commenteds, :content, :string
+ @connection.change_column :commenteds, :obvious, :string, comment: nil
+ @connection.add_index :commenteds, :obvious, name: "idx_obvious", comment: "We need to see obvious comments"
+
+ # And check that these changes are reflected in dump
+ output = dump_table_schema "commenteds"
+ assert_match %r[create_table "commenteds",.+\s+comment: "A table with comment"], output
+ assert_match %r[t\.string\s+"name",\s+comment: "Comment should help clarify the column purpose"], output
+ assert_match %r[t\.string\s+"obvious"\n], output
+ assert_match %r[t\.string\s+"content",\s+comment: "Whoa, content describes itself!"], output
+ assert_match %r[t\.integer\s+"rating",\s+comment: "I am running out of imagination"], output
+ assert_match %r[t\.index\s+.+\s+comment: "\\\"Very important\\\" index that powers all the performance.\\nAnd it's fun!"], output
+ assert_match %r[t\.index\s+.+\s+name: "idx_obvious",.+\s+comment: "We need to see obvious comments"], output
+ end
- def test_schema_dump_omits_blank_comments
- output = dump_table_schema 'blank_comments'
+ def test_schema_dump_omits_blank_comments
+ output = dump_table_schema "blank_comments"
- assert_match %r[create_table "blank_comments"], output
- assert_no_match %r[create_table "blank_comments",.+comment:], output
+ assert_match %r[create_table "blank_comments"], output
+ assert_no_match %r[create_table "blank_comments",.+comment:], output
- assert_match %r[t\.string\s+"space_comment"\n], output
- assert_no_match %r[t\.string\s+"space_comment", comment:\n], output
+ assert_match %r[t\.string\s+"space_comment"\n], output
+ assert_no_match %r[t\.string\s+"space_comment", comment:\n], output
- assert_match %r[t\.string\s+"empty_comment"\n], output
- assert_no_match %r[t\.string\s+"empty_comment", comment:\n], output
+ assert_match %r[t\.string\s+"empty_comment"\n], output
+ assert_no_match %r[t\.string\s+"empty_comment", comment:\n], output
- assert_match %r[t\.string\s+"nil_comment"\n], output
- assert_no_match %r[t\.string\s+"nil_comment", comment:\n], output
+ assert_match %r[t\.string\s+"nil_comment"\n], output
+ assert_no_match %r[t\.string\s+"nil_comment", comment:\n], output
- assert_match %r[t\.string\s+"absent_comment"\n], output
- assert_no_match %r[t\.string\s+"absent_comment", comment:\n], output
+ assert_match %r[t\.string\s+"absent_comment"\n], output
+ assert_no_match %r[t\.string\s+"absent_comment", comment:\n], output
+ end
end
end
-
-end
diff --git a/activerecord/test/cases/connection_adapters/adapter_leasing_test.rb b/activerecord/test/cases/connection_adapters/adapter_leasing_test.rb
index c7ca428ab7..64189381cb 100644
--- a/activerecord/test/cases/connection_adapters/adapter_leasing_test.rb
+++ b/activerecord/test/cases/connection_adapters/adapter_leasing_test.rb
@@ -17,23 +17,23 @@ module ActiveRecord
end
def test_in_use?
- assert_not @adapter.in_use?, 'adapter is not in use'
- assert @adapter.lease, 'lease adapter'
- assert @adapter.in_use?, 'adapter is in use'
+ assert_not @adapter.in_use?, "adapter is not in use"
+ assert @adapter.lease, "lease adapter"
+ assert @adapter.in_use?, "adapter is in use"
end
def test_lease_twice
- assert @adapter.lease, 'should lease adapter'
+ assert @adapter.lease, "should lease adapter"
assert_raises(ActiveRecordError) do
@adapter.lease
end
end
def test_expire_mutates_in_use
- assert @adapter.lease, 'lease adapter'
- assert @adapter.in_use?, 'adapter is in use'
+ assert @adapter.lease, "lease adapter"
+ assert @adapter.in_use?, "adapter is in use"
@adapter.expire
- assert_not @adapter.in_use?, 'adapter is in use'
+ assert_not @adapter.in_use?, "adapter is in use"
end
def test_close
diff --git a/activerecord/test/cases/connection_adapters/connection_handler_test.rb b/activerecord/test/cases/connection_adapters/connection_handler_test.rb
index a019cc6490..2c33bf22ab 100644
--- a/activerecord/test/cases/connection_adapters/connection_handler_test.rb
+++ b/activerecord/test/cases/connection_adapters/connection_handler_test.rb
@@ -6,18 +6,18 @@ module ActiveRecord
def setup
@handler = ConnectionHandler.new
@spec_name = "primary"
- @pool = @handler.establish_connection(ActiveRecord::Base.configurations['arunit'])
+ @pool = @handler.establish_connection(ActiveRecord::Base.configurations["arunit"])
end
def test_establish_connection_uses_spec_name
- config = {"readonly" => {"adapter" => 'sqlite3'}}
+ config = { "readonly" => { "adapter" => "sqlite3" } }
resolver = ConnectionAdapters::ConnectionSpecification::Resolver.new(config)
spec = resolver.spec(:readonly)
@handler.establish_connection(spec.to_hash)
- assert_not_nil @handler.retrieve_connection_pool('readonly')
+ assert_not_nil @handler.retrieve_connection_pool("readonly")
ensure
- @handler.remove_connection('readonly')
+ @handler.remove_connection("readonly")
end
def test_retrieve_connection
@@ -68,7 +68,7 @@ module ActiveRecord
def test_retrieve_connection_pool_copies_schema_cache_from_ancestor_pool
@pool.schema_cache = @pool.connection.schema_cache
- @pool.schema_cache.add('posts')
+ @pool.schema_cache.add("posts")
rd, wr = IO.pipe
rd.binmode
@@ -90,7 +90,7 @@ module ActiveRecord
end
def test_a_class_using_custom_pool_and_switching_back_to_primary
- klass2 = Class.new(Base) { def self.name; 'klass2'; end }
+ klass2 = Class.new(Base) { def self.name; "klass2"; end }
assert_equal klass2.connection.object_id, ActiveRecord::Base.connection.object_id
@@ -113,7 +113,7 @@ module ActiveRecord
end
def test_remove_connection_should_not_remove_parent
- klass2 = Class.new(Base) { def self.name; 'klass2'; end }
+ klass2 = Class.new(Base) { def self.name; "klass2"; end }
klass2.remove_connection
refute_nil ActiveRecord::Base.connection.object_id
assert_equal klass2.connection.object_id, ActiveRecord::Base.connection.object_id
diff --git a/activerecord/test/cases/connection_adapters/connection_specification_test.rb b/activerecord/test/cases/connection_adapters/connection_specification_test.rb
index d204fce59f..10a3521c79 100644
--- a/activerecord/test/cases/connection_adapters/connection_specification_test.rb
+++ b/activerecord/test/cases/connection_adapters/connection_specification_test.rb
@@ -4,7 +4,7 @@ module ActiveRecord
module ConnectionAdapters
class ConnectionSpecificationTest < ActiveRecord::TestCase
def test_dup_deep_copy_config
- spec = ConnectionSpecification.new("primary", { :a => :b }, "bar")
+ spec = ConnectionSpecification.new("primary", { a: :b }, "bar")
assert_not_equal(spec.config.object_id, spec.dup.config.object_id)
end
end
diff --git a/activerecord/test/cases/connection_adapters/merge_and_resolve_default_url_config_test.rb b/activerecord/test/cases/connection_adapters/merge_and_resolve_default_url_config_test.rb
index f25b85e8a7..8faa67255d 100644
--- a/activerecord/test/cases/connection_adapters/merge_and_resolve_default_url_config_test.rb
+++ b/activerecord/test/cases/connection_adapters/merge_and_resolve_default_url_config_test.rb
@@ -24,54 +24,54 @@ module ActiveRecord
end
def test_resolver_with_database_uri_and_current_env_symbol_key
- ENV['DATABASE_URL'] = "postgres://localhost/foo"
+ ENV["DATABASE_URL"] = "postgres://localhost/foo"
config = { "not_production" => { "adapter" => "not_postgres", "database" => "not_foo" } }
actual = resolve_spec(:default_env, config)
- expected = { "adapter"=>"postgresql", "database"=>"foo", "host"=>"localhost", "name"=>"default_env" }
+ expected = { "adapter" => "postgresql", "database" => "foo", "host" => "localhost", "name" => "default_env" }
assert_equal expected, actual
end
def test_resolver_with_database_uri_and_current_env_symbol_key_and_rails_env
- ENV['DATABASE_URL'] = "postgres://localhost/foo"
- ENV['RAILS_ENV'] = "foo"
+ ENV["DATABASE_URL"] = "postgres://localhost/foo"
+ ENV["RAILS_ENV"] = "foo"
config = { "not_production" => { "adapter" => "not_postgres", "database" => "not_foo" } }
actual = resolve_spec(:foo, config)
- expected = { "adapter" => "postgresql", "database" => "foo", "host" => "localhost","name"=>"foo" }
+ expected = { "adapter" => "postgresql", "database" => "foo", "host" => "localhost", "name" => "foo" }
assert_equal expected, actual
end
def test_resolver_with_database_uri_and_current_env_symbol_key_and_rack_env
- ENV['DATABASE_URL'] = "postgres://localhost/foo"
- ENV['RACK_ENV'] = "foo"
+ ENV["DATABASE_URL"] = "postgres://localhost/foo"
+ ENV["RACK_ENV"] = "foo"
config = { "not_production" => { "adapter" => "not_postgres", "database" => "not_foo" } }
actual = resolve_spec(:foo, config)
- expected = { "adapter" => "postgresql", "database" => "foo", "host" => "localhost","name"=>"foo" }
+ expected = { "adapter" => "postgresql", "database" => "foo", "host" => "localhost", "name" => "foo" }
assert_equal expected, actual
end
def test_resolver_with_database_uri_and_known_key
- ENV['DATABASE_URL'] = "postgres://localhost/foo"
+ ENV["DATABASE_URL"] = "postgres://localhost/foo"
config = { "production" => { "adapter" => "not_postgres", "database" => "not_foo", "host" => "localhost" } }
actual = resolve_spec(:production, config)
- expected = { "adapter"=>"not_postgres", "database"=>"not_foo", "host"=>"localhost", "name"=>"production" }
+ expected = { "adapter" => "not_postgres", "database" => "not_foo", "host" => "localhost", "name" => "production" }
assert_equal expected, actual
end
def test_resolver_with_database_uri_and_unknown_symbol_key
- ENV['DATABASE_URL'] = "postgres://localhost/foo"
- config = { "not_production" => { "adapter" => "not_postgres", "database" => "not_foo" } }
+ ENV["DATABASE_URL"] = "postgres://localhost/foo"
+ config = { "not_production" => { "adapter" => "not_postgres", "database" => "not_foo" } }
assert_raises AdapterNotSpecified do
resolve_spec(:production, config)
end
end
def test_resolver_with_database_uri_and_supplied_url
- ENV['DATABASE_URL'] = "not-postgres://not-localhost/not_foo"
+ ENV["DATABASE_URL"] = "not-postgres://not-localhost/not_foo"
config = { "production" => { "adapter" => "also_not_postgres", "database" => "also_not_foo" } }
actual = resolve_spec("postgres://localhost/foo", config)
- expected = { "adapter"=>"postgresql", "database"=>"foo", "host"=>"localhost" }
+ expected = { "adapter" => "postgresql", "database" => "foo", "host" => "localhost" }
assert_equal expected, actual
end
@@ -82,18 +82,18 @@ module ActiveRecord
end
def test_environment_does_not_exist_in_config_url_does_exist
- ENV['DATABASE_URL'] = "postgres://localhost/foo"
+ ENV["DATABASE_URL"] = "postgres://localhost/foo"
config = { "not_default_env" => { "adapter" => "not_postgres", "database" => "not_foo" } }
actual = resolve_config(config)
- expect_prod = { "adapter"=>"postgresql", "database"=>"foo", "host"=>"localhost" }
+ expect_prod = { "adapter" => "postgresql", "database" => "foo", "host" => "localhost" }
assert_equal expect_prod, actual["default_env"]
end
def test_url_with_hyphenated_scheme
- ENV['DATABASE_URL'] = "ibm-db://localhost/foo"
+ ENV["DATABASE_URL"] = "ibm-db://localhost/foo"
config = { "default_env" => { "adapter" => "not_postgres", "database" => "not_foo", "host" => "localhost" } }
actual = resolve_spec(:default_env, config)
- expected = { "adapter"=>"ibm_db", "database"=>"foo", "host"=>"localhost", "name"=>"default_env" }
+ expected = { "adapter" => "ibm_db", "database" => "foo", "host" => "localhost", "name" => "default_env" }
assert_equal expected, actual
end
@@ -134,7 +134,7 @@ module ActiveRecord
end
def test_blank_with_database_url
- ENV['DATABASE_URL'] = "postgres://localhost/foo"
+ ENV["DATABASE_URL"] = "postgres://localhost/foo"
config = {}
actual = resolve_config(config)
@@ -142,18 +142,18 @@ module ActiveRecord
"database" => "foo",
"host" => "localhost" }
assert_equal expected, actual["default_env"]
- assert_equal nil, actual["production"]
- assert_equal nil, actual["development"]
- assert_equal nil, actual["test"]
- assert_equal nil, actual[:default_env]
- assert_equal nil, actual[:production]
- assert_equal nil, actual[:development]
- assert_equal nil, actual[:test]
+ assert_nil actual["production"]
+ assert_nil actual["development"]
+ assert_nil actual["test"]
+ assert_nil actual[:default_env]
+ assert_nil actual[:production]
+ assert_nil actual[:development]
+ assert_nil actual[:test]
end
def test_blank_with_database_url_with_rails_env
- ENV['RAILS_ENV'] = "not_production"
- ENV['DATABASE_URL'] = "postgres://localhost/foo"
+ ENV["RAILS_ENV"] = "not_production"
+ ENV["DATABASE_URL"] = "postgres://localhost/foo"
config = {}
actual = resolve_config(config)
@@ -162,20 +162,20 @@ module ActiveRecord
"host" => "localhost" }
assert_equal expected, actual["not_production"]
- assert_equal nil, actual["production"]
- assert_equal nil, actual["default_env"]
- assert_equal nil, actual["development"]
- assert_equal nil, actual["test"]
- assert_equal nil, actual[:default_env]
- assert_equal nil, actual[:not_production]
- assert_equal nil, actual[:production]
- assert_equal nil, actual[:development]
- assert_equal nil, actual[:test]
+ assert_nil actual["production"]
+ assert_nil actual["default_env"]
+ assert_nil actual["development"]
+ assert_nil actual["test"]
+ assert_nil actual[:default_env]
+ assert_nil actual[:not_production]
+ assert_nil actual[:production]
+ assert_nil actual[:development]
+ assert_nil actual[:test]
end
def test_blank_with_database_url_with_rack_env
- ENV['RACK_ENV'] = "not_production"
- ENV['DATABASE_URL'] = "postgres://localhost/foo"
+ ENV["RACK_ENV"] = "not_production"
+ ENV["DATABASE_URL"] = "postgres://localhost/foo"
config = {}
actual = resolve_config(config)
@@ -184,19 +184,19 @@ module ActiveRecord
"host" => "localhost" }
assert_equal expected, actual["not_production"]
- assert_equal nil, actual["production"]
- assert_equal nil, actual["default_env"]
- assert_equal nil, actual["development"]
- assert_equal nil, actual["test"]
- assert_equal nil, actual[:default_env]
- assert_equal nil, actual[:not_production]
- assert_equal nil, actual[:production]
- assert_equal nil, actual[:development]
- assert_equal nil, actual[:test]
+ assert_nil actual["production"]
+ assert_nil actual["default_env"]
+ assert_nil actual["development"]
+ assert_nil actual["test"]
+ assert_nil actual[:default_env]
+ assert_nil actual[:not_production]
+ assert_nil actual[:production]
+ assert_nil actual[:development]
+ assert_nil actual[:test]
end
def test_database_url_with_ipv6_host_and_port
- ENV['DATABASE_URL'] = "postgres://[::1]:5454/foo"
+ ENV["DATABASE_URL"] = "postgres://[::1]:5454/foo"
config = {}
actual = resolve_config(config)
@@ -208,12 +208,12 @@ module ActiveRecord
end
def test_url_sub_key_with_database_url
- ENV['DATABASE_URL'] = "NOT-POSTGRES://localhost/NOT_FOO"
+ ENV["DATABASE_URL"] = "NOT-POSTGRES://localhost/NOT_FOO"
config = { "default_env" => { "url" => "postgres://localhost/foo" } }
actual = resolve_config(config)
expected = { "default_env" =>
- { "adapter" => "postgresql",
+ { "adapter" => "postgresql",
"database" => "foo",
"host" => "localhost"
}
@@ -222,9 +222,9 @@ module ActiveRecord
end
def test_merge_no_conflicts_with_database_url
- ENV['DATABASE_URL'] = "postgres://localhost/foo"
+ ENV["DATABASE_URL"] = "postgres://localhost/foo"
- config = {"default_env" => { "pool" => "5" } }
+ config = { "default_env" => { "pool" => "5" } }
actual = resolve_config(config)
expected = { "default_env" =>
{ "adapter" => "postgresql",
@@ -237,9 +237,9 @@ module ActiveRecord
end
def test_merge_conflicts_with_database_url
- ENV['DATABASE_URL'] = "postgres://localhost/foo"
+ ENV["DATABASE_URL"] = "postgres://localhost/foo"
- config = {"default_env" => { "adapter" => "NOT-POSTGRES", "database" => "NOT-FOO", "pool" => "5" } }
+ config = { "default_env" => { "adapter" => "NOT-POSTGRES", "database" => "NOT-FOO", "pool" => "5" } }
actual = resolve_config(config)
expected = { "default_env" =>
{ "adapter" => "postgresql",
diff --git a/activerecord/test/cases/connection_adapters/mysql_type_lookup_test.rb b/activerecord/test/cases/connection_adapters/mysql_type_lookup_test.rb
index f2b1d9e4e7..3657b8340d 100644
--- a/activerecord/test/cases/connection_adapters/mysql_type_lookup_test.rb
+++ b/activerecord/test/cases/connection_adapters/mysql_type_lookup_test.rb
@@ -1,69 +1,69 @@
require "cases/helper"
if current_adapter?(:Mysql2Adapter)
-module ActiveRecord
- module ConnectionAdapters
- class MysqlTypeLookupTest < ActiveRecord::TestCase
- setup do
- @connection = ActiveRecord::Base.connection
- end
+ module ActiveRecord
+ module ConnectionAdapters
+ class MysqlTypeLookupTest < ActiveRecord::TestCase
+ setup do
+ @connection = ActiveRecord::Base.connection
+ end
- def test_boolean_types
- emulate_booleans(true) do
- assert_lookup_type :boolean, 'tinyint(1)'
- assert_lookup_type :boolean, 'TINYINT(1)'
+ def test_boolean_types
+ emulate_booleans(true) do
+ assert_lookup_type :boolean, "tinyint(1)"
+ assert_lookup_type :boolean, "TINYINT(1)"
+ end
end
- end
- def test_string_types
- assert_lookup_type :string, "enum('one', 'two', 'three')"
- assert_lookup_type :string, "ENUM('one', 'two', 'three')"
- assert_lookup_type :string, "set('one', 'two', 'three')"
- assert_lookup_type :string, "SET('one', 'two', 'three')"
- end
+ def test_string_types
+ assert_lookup_type :string, "enum('one', 'two', 'three')"
+ assert_lookup_type :string, "ENUM('one', 'two', 'three')"
+ assert_lookup_type :string, "set('one', 'two', 'three')"
+ assert_lookup_type :string, "SET('one', 'two', 'three')"
+ end
- def test_set_type_with_value_matching_other_type
- assert_lookup_type :string, "SET('unicode', '8bit', 'none', 'time')"
- end
+ def test_set_type_with_value_matching_other_type
+ assert_lookup_type :string, "SET('unicode', '8bit', 'none', 'time')"
+ end
- def test_enum_type_with_value_matching_other_type
- assert_lookup_type :string, "ENUM('unicode', '8bit', 'none')"
- end
+ def test_enum_type_with_value_matching_other_type
+ assert_lookup_type :string, "ENUM('unicode', '8bit', 'none')"
+ end
- def test_binary_types
- assert_lookup_type :binary, 'bit'
- assert_lookup_type :binary, 'BIT'
- end
+ def test_binary_types
+ assert_lookup_type :binary, "bit"
+ assert_lookup_type :binary, "BIT"
+ end
- def test_integer_types
- emulate_booleans(false) do
- assert_lookup_type :integer, 'tinyint(1)'
- assert_lookup_type :integer, 'TINYINT(1)'
- assert_lookup_type :integer, 'year'
- assert_lookup_type :integer, 'YEAR'
+ def test_integer_types
+ emulate_booleans(false) do
+ assert_lookup_type :integer, "tinyint(1)"
+ assert_lookup_type :integer, "TINYINT(1)"
+ assert_lookup_type :integer, "year"
+ assert_lookup_type :integer, "YEAR"
+ end
end
- end
- private
+ private
- def assert_lookup_type(type, lookup)
- cast_type = @connection.type_map.lookup(lookup)
- assert_equal type, cast_type.type
- end
+ def assert_lookup_type(type, lookup)
+ cast_type = @connection.type_map.lookup(lookup)
+ assert_equal type, cast_type.type
+ end
- def emulate_booleans(value)
- old_emulate_booleans = @connection.emulate_booleans
- change_emulate_booleans(value)
- yield
- ensure
- change_emulate_booleans(old_emulate_booleans)
- end
+ def emulate_booleans(value)
+ old_emulate_booleans = @connection.emulate_booleans
+ change_emulate_booleans(value)
+ yield
+ ensure
+ change_emulate_booleans(old_emulate_booleans)
+ end
- def change_emulate_booleans(value)
- @connection.emulate_booleans = value
- @connection.clear_cache!
+ def change_emulate_booleans(value)
+ @connection.emulate_booleans = value
+ @connection.clear_cache!
+ end
end
end
end
end
-end
diff --git a/activerecord/test/cases/connection_adapters/quoting_test.rb b/activerecord/test/cases/connection_adapters/quoting_test.rb
deleted file mode 100644
index 59dcb96ebc..0000000000
--- a/activerecord/test/cases/connection_adapters/quoting_test.rb
+++ /dev/null
@@ -1,13 +0,0 @@
-require "cases/helper"
-
-module ActiveRecord
- module ConnectionAdapters
- module Quoting
- class QuotingTest < ActiveRecord::TestCase
- def test_quoting_classes
- assert_equal "'Object'", AbstractAdapter.new(nil).quote(Object)
- end
- end
- end
- end
-end
diff --git a/activerecord/test/cases/connection_adapters/schema_cache_test.rb b/activerecord/test/cases/connection_adapters/schema_cache_test.rb
index db832fe55d..106323ccc9 100644
--- a/activerecord/test/cases/connection_adapters/schema_cache_test.rb
+++ b/activerecord/test/cases/connection_adapters/schema_cache_test.rb
@@ -9,28 +9,55 @@ module ActiveRecord
end
def test_primary_key
- assert_equal 'id', @cache.primary_keys('posts')
+ assert_equal "id", @cache.primary_keys("posts")
+ end
+
+ def test_yaml_dump_and_load
+ @cache.columns("posts")
+ @cache.columns_hash("posts")
+ @cache.data_sources("posts")
+ @cache.primary_keys("posts")
+
+ new_cache = YAML.load(YAML.dump(@cache))
+ assert_no_queries do
+ assert_equal 11, new_cache.columns("posts").size
+ assert_equal 11, new_cache.columns_hash("posts").size
+ assert new_cache.data_sources("posts")
+ assert_equal "id", new_cache.primary_keys("posts")
+ end
+ end
+
+ def test_yaml_loads_5_1_dump
+ body = File.open(schema_dump_path).read
+ cache = YAML.load(body)
+
+ assert_no_queries do
+ assert_equal 11, cache.columns("posts").size
+ assert_equal 11, cache.columns_hash("posts").size
+ assert cache.data_sources("posts")
+ assert_equal "id", cache.primary_keys("posts")
+ end
end
def test_primary_key_for_non_existent_table
- assert_nil @cache.primary_keys('omgponies')
+ assert_nil @cache.primary_keys("omgponies")
end
def test_caches_columns
- columns = @cache.columns('posts')
- assert_equal columns, @cache.columns('posts')
+ columns = @cache.columns("posts")
+ assert_equal columns, @cache.columns("posts")
end
def test_caches_columns_hash
- columns_hash = @cache.columns_hash('posts')
- assert_equal columns_hash, @cache.columns_hash('posts')
+ columns_hash = @cache.columns_hash("posts")
+ assert_equal columns_hash, @cache.columns_hash("posts")
end
def test_clearing
- @cache.columns('posts')
- @cache.columns_hash('posts')
- @cache.data_sources('posts')
- @cache.primary_keys('posts')
+ @cache.columns("posts")
+ @cache.columns_hash("posts")
+ @cache.data_sources("posts")
+ @cache.primary_keys("posts")
@cache.clear!
@@ -38,24 +65,35 @@ module ActiveRecord
end
def test_dump_and_load
- @cache.columns('posts')
- @cache.columns_hash('posts')
- @cache.data_sources('posts')
- @cache.primary_keys('posts')
+ @cache.columns("posts")
+ @cache.columns_hash("posts")
+ @cache.data_sources("posts")
+ @cache.primary_keys("posts")
@cache = Marshal.load(Marshal.dump(@cache))
- assert_equal 11, @cache.columns('posts').size
- assert_equal 11, @cache.columns_hash('posts').size
- assert @cache.data_sources('posts')
- assert_equal 'id', @cache.primary_keys('posts')
+ assert_no_queries do
+ assert_equal 11, @cache.columns("posts").size
+ assert_equal 11, @cache.columns_hash("posts").size
+ assert @cache.data_sources("posts")
+ assert_equal "id", @cache.primary_keys("posts")
+ end
+ end
+
+ def test_data_source_exist
+ assert @cache.data_source_exists?("posts")
+ assert_not @cache.data_source_exists?("foo")
end
- def test_table_methods_deprecation
- assert_deprecated { assert @cache.table_exists?('posts') }
- assert_deprecated { assert @cache.tables('posts') }
- assert_deprecated { @cache.clear_table_cache!('posts') }
+ def test_clear_data_source_cache
+ @cache.clear_data_source_cache!("posts")
end
+
+ private
+
+ def schema_dump_path
+ "test/assets/schema_dump_5_1.yml"
+ end
end
end
end
diff --git a/activerecord/test/cases/connection_adapters/type_lookup_test.rb b/activerecord/test/cases/connection_adapters/type_lookup_test.rb
index 3acbafbff4..e2e5445a4e 100644
--- a/activerecord/test/cases/connection_adapters/type_lookup_test.rb
+++ b/activerecord/test/cases/connection_adapters/type_lookup_test.rb
@@ -1,110 +1,110 @@
require "cases/helper"
unless current_adapter?(:PostgreSQLAdapter) # PostgreSQL does not use type strings for lookup
-module ActiveRecord
- module ConnectionAdapters
- class TypeLookupTest < ActiveRecord::TestCase
- setup do
- @connection = ActiveRecord::Base.connection
- end
+ module ActiveRecord
+ module ConnectionAdapters
+ class TypeLookupTest < ActiveRecord::TestCase
+ setup do
+ @connection = ActiveRecord::Base.connection
+ end
- def test_boolean_types
- assert_lookup_type :boolean, 'boolean'
- assert_lookup_type :boolean, 'BOOLEAN'
- end
+ def test_boolean_types
+ assert_lookup_type :boolean, "boolean"
+ assert_lookup_type :boolean, "BOOLEAN"
+ end
- def test_string_types
- assert_lookup_type :string, 'char'
- assert_lookup_type :string, 'varchar'
- assert_lookup_type :string, 'VARCHAR'
- assert_lookup_type :string, 'varchar(255)'
- assert_lookup_type :string, 'character varying'
- end
+ def test_string_types
+ assert_lookup_type :string, "char"
+ assert_lookup_type :string, "varchar"
+ assert_lookup_type :string, "VARCHAR"
+ assert_lookup_type :string, "varchar(255)"
+ assert_lookup_type :string, "character varying"
+ end
- def test_binary_types
- assert_lookup_type :binary, 'binary'
- assert_lookup_type :binary, 'BINARY'
- assert_lookup_type :binary, 'blob'
- assert_lookup_type :binary, 'BLOB'
- end
+ def test_binary_types
+ assert_lookup_type :binary, "binary"
+ assert_lookup_type :binary, "BINARY"
+ assert_lookup_type :binary, "blob"
+ assert_lookup_type :binary, "BLOB"
+ end
- def test_text_types
- assert_lookup_type :text, 'text'
- assert_lookup_type :text, 'TEXT'
- assert_lookup_type :text, 'clob'
- assert_lookup_type :text, 'CLOB'
- end
+ def test_text_types
+ assert_lookup_type :text, "text"
+ assert_lookup_type :text, "TEXT"
+ assert_lookup_type :text, "clob"
+ assert_lookup_type :text, "CLOB"
+ end
- def test_date_types
- assert_lookup_type :date, 'date'
- assert_lookup_type :date, 'DATE'
- end
+ def test_date_types
+ assert_lookup_type :date, "date"
+ assert_lookup_type :date, "DATE"
+ end
- def test_time_types
- assert_lookup_type :time, 'time'
- assert_lookup_type :time, 'TIME'
- end
+ def test_time_types
+ assert_lookup_type :time, "time"
+ assert_lookup_type :time, "TIME"
+ end
- def test_datetime_types
- assert_lookup_type :datetime, 'datetime'
- assert_lookup_type :datetime, 'DATETIME'
- assert_lookup_type :datetime, 'timestamp'
- assert_lookup_type :datetime, 'TIMESTAMP'
- end
+ def test_datetime_types
+ assert_lookup_type :datetime, "datetime"
+ assert_lookup_type :datetime, "DATETIME"
+ assert_lookup_type :datetime, "timestamp"
+ assert_lookup_type :datetime, "TIMESTAMP"
+ end
- def test_decimal_types
- assert_lookup_type :decimal, 'decimal'
- assert_lookup_type :decimal, 'decimal(2,8)'
- assert_lookup_type :decimal, 'DECIMAL'
- assert_lookup_type :decimal, 'numeric'
- assert_lookup_type :decimal, 'numeric(2,8)'
- assert_lookup_type :decimal, 'NUMERIC'
- assert_lookup_type :decimal, 'number'
- assert_lookup_type :decimal, 'number(2,8)'
- assert_lookup_type :decimal, 'NUMBER'
- end
+ def test_decimal_types
+ assert_lookup_type :decimal, "decimal"
+ assert_lookup_type :decimal, "decimal(2,8)"
+ assert_lookup_type :decimal, "DECIMAL"
+ assert_lookup_type :decimal, "numeric"
+ assert_lookup_type :decimal, "numeric(2,8)"
+ assert_lookup_type :decimal, "NUMERIC"
+ assert_lookup_type :decimal, "number"
+ assert_lookup_type :decimal, "number(2,8)"
+ assert_lookup_type :decimal, "NUMBER"
+ end
- def test_float_types
- assert_lookup_type :float, 'float'
- assert_lookup_type :float, 'FLOAT'
- assert_lookup_type :float, 'double'
- assert_lookup_type :float, 'DOUBLE'
- end
+ def test_float_types
+ assert_lookup_type :float, "float"
+ assert_lookup_type :float, "FLOAT"
+ assert_lookup_type :float, "double"
+ assert_lookup_type :float, "DOUBLE"
+ end
- def test_integer_types
- assert_lookup_type :integer, 'integer'
- assert_lookup_type :integer, 'INTEGER'
- assert_lookup_type :integer, 'tinyint'
- assert_lookup_type :integer, 'smallint'
- assert_lookup_type :integer, 'bigint'
- end
+ def test_integer_types
+ assert_lookup_type :integer, "integer"
+ assert_lookup_type :integer, "INTEGER"
+ assert_lookup_type :integer, "tinyint"
+ assert_lookup_type :integer, "smallint"
+ assert_lookup_type :integer, "bigint"
+ end
- def test_bigint_limit
- cast_type = @connection.type_map.lookup("bigint")
- if current_adapter?(:OracleAdapter)
- assert_equal 19, cast_type.limit
- else
- assert_equal 8, cast_type.limit
+ def test_bigint_limit
+ cast_type = @connection.type_map.lookup("bigint")
+ if current_adapter?(:OracleAdapter)
+ assert_equal 19, cast_type.limit
+ else
+ assert_equal 8, cast_type.limit
+ end
end
- end
- def test_decimal_without_scale
- types = %w{decimal(2) decimal(2,0) numeric(2) numeric(2,0) number(2) number(2,0)}
- types.each do |type|
- cast_type = @connection.type_map.lookup(type)
+ def test_decimal_without_scale
+ types = %w{decimal(2) decimal(2,0) numeric(2) numeric(2,0) number(2) number(2,0)}
+ types.each do |type|
+ cast_type = @connection.type_map.lookup(type)
- assert_equal :decimal, cast_type.type
- assert_equal 2, cast_type.cast(2.1)
+ assert_equal :decimal, cast_type.type
+ assert_equal 2, cast_type.cast(2.1)
+ end
end
- end
- private
+ private
- def assert_lookup_type(type, lookup)
- cast_type = @connection.type_map.lookup(lookup)
- assert_equal type, cast_type.type
+ def assert_lookup_type(type, lookup)
+ cast_type = @connection.type_map.lookup(lookup)
+ assert_equal type, cast_type.type
+ end
end
end
end
end
-end
diff --git a/activerecord/test/cases/connection_management_test.rb b/activerecord/test/cases/connection_management_test.rb
index 1f9b6add7a..d1e946d401 100644
--- a/activerecord/test/cases/connection_management_test.rb
+++ b/activerecord/test/cases/connection_management_test.rb
@@ -14,7 +14,7 @@ module ActiveRecord
def call(env)
@calls << env
- [200, {}, ['hi mom']]
+ [200, {}, ["hi mom"]]
end
end
@@ -39,7 +39,7 @@ module ActiveRecord
_, _, body = @management.call(@env)
bits = []
body.each { |bit| bits << bit }
- assert_equal ['hi mom'], bits
+ assert_equal ["hi mom"], bits
end
def test_connections_are_cleared_after_body_close
@@ -88,10 +88,10 @@ module ActiveRecord
end
test "doesn't mutate the original response" do
- original_response = [200, {}, 'hi']
+ original_response = [200, {}, "hi"]
app = lambda { |_| original_response }
middleware(app).call(@env)[2]
- assert_equal 'hi', original_response.last
+ assert_equal "hi", original_response.last
end
private
@@ -104,7 +104,7 @@ module ActiveRecord
def middleware(app)
lambda do |env|
a, b, c = executor.wrap { app.call(env) }
- [a, b, Rack::BodyProxy.new(c) { }]
+ [a, b, Rack::BodyProxy.new(c) {}]
end
end
end
diff --git a/activerecord/test/cases/connection_pool_test.rb b/activerecord/test/cases/connection_pool_test.rb
index 09e7848bda..42600e53fd 100644
--- a/activerecord/test/cases/connection_pool_test.rb
+++ b/activerecord/test/cases/connection_pool_test.rb
@@ -1,5 +1,5 @@
require "cases/helper"
-require 'concurrent/atomic/count_down_latch'
+require "concurrent/atomic/count_down_latch"
module ActiveRecord
module ConnectionAdapters
@@ -184,14 +184,14 @@ module ActiveRecord
def test_checkout_behaviour
pool = ConnectionPool.new ActiveRecord::Base.connection_pool.spec
- connection = pool.connection
- assert_not_nil connection
+ main_connection = pool.connection
+ assert_not_nil main_connection
threads = []
4.times do |i|
threads << Thread.new(i) do
- connection = pool.connection
- assert_not_nil connection
- connection.close
+ thread_connection = pool.connection
+ assert_not_nil thread_connection
+ thread_connection.close
end
end
@@ -341,6 +341,18 @@ module ActiveRecord
end
end
+ def test_connection_notification_is_called
+ payloads = []
+ subscription = ActiveSupport::Notifications.subscribe("!connection.active_record") do |name, started, finished, unique_id, payload|
+ payloads << payload
+ end
+ ActiveRecord::Base.establish_connection :arunit
+ assert_equal [:config, :connection_id, :spec_name], payloads[0].keys.sort
+ assert_equal "primary", payloads[0][:spec_name]
+ ensure
+ ActiveSupport::Notifications.unsubscribe(subscription) if subscription
+ end
+
def test_pool_sets_connection_schema_cache
connection = pool.checkout
schema_cache = SchemaCache.new connection
@@ -383,8 +395,8 @@ module ActiveRecord
all_threads_in_new_connection.wait
end
rescue Timeout::Error
- flunk 'pool unable to establish connections concurrently or implementation has ' <<
- 'changed, this test then needs to patch a different :new_connection method'
+ flunk "pool unable to establish connections concurrently or implementation has " <<
+ "changed, this test then needs to patch a different :new_connection method"
ensure
# clean up the threads
all_go.count_down
@@ -443,49 +455,63 @@ module ActiveRecord
with_single_connection_pool do |pool|
[:disconnect, :disconnect!, :clear_reloadable_connections, :clear_reloadable_connections!].each do |group_action_method|
conn = pool.connection # drain the only available connection
- second_thread_done = Concurrent::CountDownLatch.new
-
- # create a first_thread and let it get into the FIFO queue first
- first_thread = Thread.new do
- pool.with_connection { second_thread_done.wait }
- end
-
- # wait for first_thread to get in queue
- Thread.pass until pool.num_waiting_in_queue == 1
+ second_thread_done = Concurrent::Event.new
- # create a different, later thread, that will attempt to do a "group action",
- # but because of the group action semantics it should be able to preempt the
- # first_thread when a connection is made available
- second_thread = Thread.new do
- pool.send(group_action_method)
- second_thread_done.count_down
- end
-
- # wait for second_thread to get in queue
- Thread.pass until pool.num_waiting_in_queue == 2
-
- # return the only available connection
- pool.checkin(conn)
-
- # if the second_thread is not able to preempt the first_thread,
- # they will temporarily (until either of them timeouts with ConnectionTimeoutError)
- # deadlock and a join(2) timeout will be reached
- failed = true unless second_thread.join(2)
-
- #--- post test clean up start
- second_thread_done.count_down if failed
-
- # after `pool.disconnect()` the first thread will be left stuck in queue, no need to wait for
- # it to timeout with ConnectionTimeoutError
- if (group_action_method == :disconnect || group_action_method == :disconnect!) && pool.num_waiting_in_queue > 0
- pool.with_connection {} # create a new connection in case there are threads still stuck in a queue
+ begin
+ # create a first_thread and let it get into the FIFO queue first
+ first_thread = Thread.new do
+ pool.with_connection { second_thread_done.wait }
+ end
+
+ # wait for first_thread to get in queue
+ Thread.pass until pool.num_waiting_in_queue == 1
+
+ # create a different, later thread, that will attempt to do a "group action",
+ # but because of the group action semantics it should be able to preempt the
+ # first_thread when a connection is made available
+ second_thread = Thread.new do
+ pool.send(group_action_method)
+ second_thread_done.set
+ end
+
+ # wait for second_thread to get in queue
+ Thread.pass until pool.num_waiting_in_queue == 2
+
+ # return the only available connection
+ pool.checkin(conn)
+
+ # if the second_thread is not able to preempt the first_thread,
+ # they will temporarily (until either of them timeouts with ConnectionTimeoutError)
+ # deadlock and a join(2) timeout will be reached
+ assert second_thread.join(2), "#{group_action_method} is not able to preempt other waiting threads"
+
+ ensure
+ # post test clean up
+ failed = !second_thread_done.set?
+
+ if failed
+ second_thread_done.set
+
+ puts
+ puts ">>> test_disconnect_and_clear_reloadable_connections_are_able_to_preempt_other_waiting_threads / #{group_action_method}"
+ p [first_thread, second_thread]
+ p pool.stat
+ p pool.connections.map(&:owner)
+
+ first_thread.join(2)
+ second_thread.join(2)
+
+ puts "---"
+ p [first_thread, second_thread]
+ p pool.stat
+ p pool.connections.map(&:owner)
+ puts "<<<"
+ puts
+ end
+
+ first_thread.join(10) || raise("first_thread got stuck")
+ second_thread.join(10) || raise("second_thread got stuck")
end
-
- first_thread.join
- second_thread.join
- #--- post test clean up end
-
- flunk "#{group_action_method} is not able to preempt other waiting threads" if failed
end
end
end
@@ -507,21 +533,41 @@ module ActiveRecord
pool.clear_reloadable_connections
unless stuck_thread.join(2)
- flunk 'clear_reloadable_connections must not let other connection waiting threads get stuck in queue'
+ flunk "clear_reloadable_connections must not let other connection waiting threads get stuck in queue"
end
assert_equal 0, pool.num_waiting_in_queue
end
end
- private
- def with_single_connection_pool
- one_conn_spec = ActiveRecord::Base.connection_pool.spec.dup
- one_conn_spec.config[:pool] = 1 # this is safe to do, because .dupped ConnectionSpecification also auto-dups its config
- yield(pool = ConnectionPool.new(one_conn_spec))
- ensure
- pool.disconnect! if pool
+ def test_connection_pool_stat
+ with_single_connection_pool do |pool|
+ pool.with_connection do |connection|
+ stats = pool.stat
+ assert_equal({ size: 1, connections: 1, busy: 1, dead: 0, idle: 0, waiting: 0, checkout_timeout: 5 }, stats)
+ end
+
+ stats = pool.stat
+ assert_equal({ size: 1, connections: 1, busy: 0, dead: 0, idle: 1, waiting: 0, checkout_timeout: 5 }, stats)
+
+ Thread.new do
+ pool.checkout
+ Thread.current.kill
+ end.join
+
+ stats = pool.stat
+ assert_equal({ size: 1, connections: 1, busy: 0, dead: 1, idle: 0, waiting: 0, checkout_timeout: 5 }, stats)
+ end
end
+
+ private
+ def with_single_connection_pool
+ one_conn_spec = ActiveRecord::Base.connection_pool.spec.dup
+ one_conn_spec.config[:pool] = 1 # this is safe to do, because .dupped ConnectionSpecification also auto-dups its config
+ yield(pool = ConnectionPool.new(one_conn_spec))
+ ensure
+ pool.disconnect! if pool
+ end
end
end
end
diff --git a/activerecord/test/cases/connection_specification/resolver_test.rb b/activerecord/test/cases/connection_specification/resolver_test.rb
index b30a83d9ce..13b5bae13c 100644
--- a/activerecord/test/cases/connection_specification/resolver_test.rb
+++ b/activerecord/test/cases/connection_specification/resolver_test.rb
@@ -4,17 +4,17 @@ module ActiveRecord
module ConnectionAdapters
class ConnectionSpecification
class ResolverTest < ActiveRecord::TestCase
- def resolve(spec, config={})
+ def resolve(spec, config = {})
Resolver.new(config).resolve(spec)
end
- def spec(spec, config={})
+ def spec(spec, config = {})
Resolver.new(config).spec(spec)
end
def test_url_invalid_adapter
error = assert_raises(LoadError) do
- spec 'ridiculous://foo?encoding=utf8'
+ spec "ridiculous://foo?encoding=utf8"
end
assert_match "Could not load 'active_record/connection_adapters/ridiculous_adapter'", error.message
@@ -24,36 +24,36 @@ module ActiveRecord
# checks that the adapter file can be required in.
def test_url_from_environment
- spec = resolve :production, 'production' => 'abstract://foo?encoding=utf8'
+ spec = resolve :production, "production" => "abstract://foo?encoding=utf8"
assert_equal({
"adapter" => "abstract",
"host" => "foo",
"encoding" => "utf8",
- "name" => "production"}, spec)
+ "name" => "production" }, spec)
end
def test_url_sub_key
- spec = resolve :production, 'production' => {"url" => 'abstract://foo?encoding=utf8'}
+ spec = resolve :production, "production" => { "url" => "abstract://foo?encoding=utf8" }
assert_equal({
"adapter" => "abstract",
"host" => "foo",
"encoding" => "utf8",
- "name" => "production"}, spec)
+ "name" => "production" }, spec)
end
def test_url_sub_key_merges_correctly
- hash = {"url" => 'abstract://foo?encoding=utf8&', "adapter" => "sqlite3", "host" => "bar", "pool" => "3"}
- spec = resolve :production, 'production' => hash
+ hash = { "url" => "abstract://foo?encoding=utf8&", "adapter" => "sqlite3", "host" => "bar", "pool" => "3" }
+ spec = resolve :production, "production" => hash
assert_equal({
"adapter" => "abstract",
"host" => "foo",
"encoding" => "utf8",
"pool" => "3",
- "name" => "production"}, spec)
+ "name" => "production" }, spec)
end
def test_url_host_no_db
- spec = resolve 'abstract://foo?encoding=utf8'
+ spec = resolve "abstract://foo?encoding=utf8"
assert_equal({
"adapter" => "abstract",
"host" => "foo",
@@ -61,13 +61,13 @@ module ActiveRecord
end
def test_url_missing_scheme
- spec = resolve 'foo'
+ spec = resolve "foo"
assert_equal({
"database" => "foo" }, spec)
end
def test_url_host_db
- spec = resolve 'abstract://foo/bar?encoding=utf8'
+ spec = resolve "abstract://foo/bar?encoding=utf8"
assert_equal({
"adapter" => "abstract",
"database" => "bar",
@@ -76,7 +76,7 @@ module ActiveRecord
end
def test_url_port
- spec = resolve 'abstract://foo:123?encoding=utf8'
+ spec = resolve "abstract://foo:123?encoding=utf8"
assert_equal({
"adapter" => "abstract",
"port" => 123,
@@ -85,48 +85,48 @@ module ActiveRecord
end
def test_encoded_password
- password = 'am@z1ng_p@ssw0rd#!'
+ password = "am@z1ng_p@ssw0rd#!"
encoded_password = URI.encode_www_form_component(password)
spec = resolve "abstract://foo:#{encoded_password}@localhost/bar"
assert_equal password, spec["password"]
end
def test_url_with_authority_for_sqlite3
- spec = resolve 'sqlite3:///foo_test'
- assert_equal('/foo_test', spec["database"])
+ spec = resolve "sqlite3:///foo_test"
+ assert_equal("/foo_test", spec["database"])
end
def test_url_absolute_path_for_sqlite3
- spec = resolve 'sqlite3:/foo_test'
- assert_equal('/foo_test', spec["database"])
+ spec = resolve "sqlite3:/foo_test"
+ assert_equal("/foo_test", spec["database"])
end
def test_url_relative_path_for_sqlite3
- spec = resolve 'sqlite3:foo_test'
- assert_equal('foo_test', spec["database"])
+ spec = resolve "sqlite3:foo_test"
+ assert_equal("foo_test", spec["database"])
end
def test_url_memory_db_for_sqlite3
- spec = resolve 'sqlite3::memory:'
- assert_equal(':memory:', spec["database"])
+ spec = resolve "sqlite3::memory:"
+ assert_equal(":memory:", spec["database"])
end
def test_url_sub_key_for_sqlite3
- spec = resolve :production, 'production' => {"url" => 'sqlite3:foo?encoding=utf8'}
+ spec = resolve :production, "production" => { "url" => "sqlite3:foo?encoding=utf8" }
assert_equal({
"adapter" => "sqlite3",
"database" => "foo",
"encoding" => "utf8",
- "name" => "production"}, spec)
+ "name" => "production" }, spec)
end
def test_spec_name_on_key_lookup
- spec = spec(:readonly, 'readonly' => {'adapter' => 'sqlite3'})
+ spec = spec(:readonly, "readonly" => { "adapter" => "sqlite3" })
assert_equal "readonly", spec.name
end
def test_spec_name_with_inline_config
- spec = spec({'adapter' => 'sqlite3'})
+ spec = spec("adapter" => "sqlite3")
assert_equal "primary", spec.name, "should default to primary id"
end
end
diff --git a/activerecord/test/cases/core_test.rb b/activerecord/test/cases/core_test.rb
index 3cb98832c5..3735572898 100644
--- a/activerecord/test/cases/core_test.rb
+++ b/activerecord/test/cases/core_test.rb
@@ -1,8 +1,8 @@
-require 'cases/helper'
-require 'models/person'
-require 'models/topic'
-require 'pp'
-require 'active_support/core_ext/string/strip'
+require "cases/helper"
+require "models/person"
+require "models/topic"
+require "pp"
+require "active_support/core_ext/string/strip"
class NonExistentTable < ActiveRecord::Base; end
@@ -10,8 +10,8 @@ class CoreTest < ActiveRecord::TestCase
fixtures :topics
def test_inspect_class
- assert_equal 'ActiveRecord::Base', ActiveRecord::Base.inspect
- assert_equal 'LoosePerson(abstract)', LoosePerson.inspect
+ assert_equal "ActiveRecord::Base", ActiveRecord::Base.inspect
+ assert_equal "LoosePerson(abstract)", LoosePerson.inspect
assert_match(/^Topic\(id: integer, title: string/, Topic.inspect)
end
@@ -25,8 +25,8 @@ class CoreTest < ActiveRecord::TestCase
end
def test_inspect_limited_select_instance
- assert_equal %(#<Topic id: 1>), Topic.all.merge!(:select => 'id', :where => 'id = 1').first.inspect
- assert_equal %(#<Topic id: 1, title: "The First Topic">), Topic.all.merge!(:select => 'id, title', :where => 'id = 1').first.inspect
+ assert_equal %(#<Topic id: 1>), Topic.all.merge!(select: "id", where: "id = 1").first.inspect
+ assert_equal %(#<Topic id: 1, title: "The First Topic">), Topic.all.merge!(select: "id, title", where: "id = 1").first.inspect
end
def test_inspect_class_without_table
@@ -35,7 +35,7 @@ class CoreTest < ActiveRecord::TestCase
def test_pretty_print_new
topic = Topic.new
- actual = ''
+ actual = ""
PP.pp(topic, StringIO.new(actual))
expected = <<-PRETTY.strip_heredoc
#<Topic:0xXXXXXX
@@ -58,13 +58,13 @@ class CoreTest < ActiveRecord::TestCase
created_at: nil,
updated_at: nil>
PRETTY
- assert actual.start_with?(expected.split('XXXXXX').first)
- assert actual.end_with?(expected.split('XXXXXX').last)
+ assert actual.start_with?(expected.split("XXXXXX").first)
+ assert actual.end_with?(expected.split("XXXXXX").last)
end
def test_pretty_print_persisted
topic = topics(:first)
- actual = ''
+ actual = ""
PP.pp(topic, StringIO.new(actual))
expected = <<-PRETTY.strip_heredoc
#<Topic:0x\\w+
@@ -92,11 +92,11 @@ class CoreTest < ActiveRecord::TestCase
def test_pretty_print_uninitialized
topic = Topic.allocate
- actual = ''
+ actual = ""
PP.pp(topic, StringIO.new(actual))
expected = "#<Topic:XXXXXX not initialized>\n"
- assert actual.start_with?(expected.split('XXXXXX').first)
- assert actual.end_with?(expected.split('XXXXXX').last)
+ assert actual.start_with?(expected.split("XXXXXX").first)
+ assert actual.end_with?(expected.split("XXXXXX").last)
end
def test_pretty_print_overridden_by_inspect
@@ -105,7 +105,7 @@ class CoreTest < ActiveRecord::TestCase
"inspecting topic"
end
end
- actual = ''
+ actual = ""
PP.pp(subtopic.new, StringIO.new(actual))
assert_equal "inspecting topic\n", actual
end
diff --git a/activerecord/test/cases/counter_cache_test.rb b/activerecord/test/cases/counter_cache_test.rb
index 66b4c3f1ff..c7d0ba32b4 100644
--- a/activerecord/test/cases/counter_cache_test.rb
+++ b/activerecord/test/cases/counter_cache_test.rb
@@ -1,30 +1,30 @@
-require 'cases/helper'
-require 'models/topic'
-require 'models/car'
-require 'models/aircraft'
-require 'models/wheel'
-require 'models/engine'
-require 'models/reply'
-require 'models/category'
-require 'models/categorization'
-require 'models/dog'
-require 'models/dog_lover'
-require 'models/person'
-require 'models/friendship'
-require 'models/subscriber'
-require 'models/subscription'
-require 'models/book'
+require "cases/helper"
+require "models/topic"
+require "models/car"
+require "models/aircraft"
+require "models/wheel"
+require "models/engine"
+require "models/reply"
+require "models/category"
+require "models/categorization"
+require "models/dog"
+require "models/dog_lover"
+require "models/person"
+require "models/friendship"
+require "models/subscriber"
+require "models/subscription"
+require "models/book"
class CounterCacheTest < ActiveRecord::TestCase
fixtures :topics, :categories, :categorizations, :cars, :dogs, :dog_lovers, :people, :friendships, :subscribers, :subscriptions, :books
class ::SpecialTopic < ::Topic
- has_many :special_replies, :foreign_key => 'parent_id'
- has_many :lightweight_special_replies, -> { select('topics.id, topics.title') }, :foreign_key => 'parent_id', :class_name => 'SpecialReply'
+ has_many :special_replies, foreign_key: "parent_id"
+ has_many :lightweight_special_replies, -> { select("topics.id, topics.title") }, foreign_key: "parent_id", class_name: "SpecialReply"
end
class ::SpecialReply < ::Reply
- belongs_to :special_topic, :foreign_key => 'parent_id', :counter_cache => 'replies_count'
+ belongs_to :special_topic, foreign_key: "parent_id", counter_cache: "replies_count"
end
setup do
@@ -32,13 +32,13 @@ class CounterCacheTest < ActiveRecord::TestCase
end
test "increment counter" do
- assert_difference '@topic.reload.replies_count' do
+ assert_difference "@topic.reload.replies_count" do
Topic.increment_counter(:replies_count, @topic.id)
end
end
test "decrement counter" do
- assert_difference '@topic.reload.replies_count', -1 do
+ assert_difference "@topic.reload.replies_count", -1 do
Topic.decrement_counter(:replies_count, @topic.id)
end
end
@@ -48,7 +48,7 @@ class CounterCacheTest < ActiveRecord::TestCase
Topic.increment_counter(:replies_count, @topic.id)
# check that it gets reset
- assert_difference '@topic.reload.replies_count', -1 do
+ assert_difference "@topic.reload.replies_count", -1 do
Topic.reset_counters(@topic.id, :replies)
end
end
@@ -58,31 +58,31 @@ class CounterCacheTest < ActiveRecord::TestCase
Topic.increment_counter(:replies_count, @topic.id)
# check that it gets reset
- assert_difference '@topic.reload.replies_count', -1 do
+ assert_difference "@topic.reload.replies_count", -1 do
Topic.reset_counters(@topic.id, :replies_count)
end
end
- test 'reset multiple counters' do
+ test "reset multiple counters" do
Topic.update_counters @topic.id, replies_count: 1, unique_replies_count: 1
- assert_difference ['@topic.reload.replies_count', '@topic.reload.unique_replies_count'], -1 do
+ assert_difference ["@topic.reload.replies_count", "@topic.reload.unique_replies_count"], -1 do
Topic.reset_counters(@topic.id, :replies, :unique_replies)
end
end
test "reset counters with string argument" do
- Topic.increment_counter('replies_count', @topic.id)
+ Topic.increment_counter("replies_count", @topic.id)
- assert_difference '@topic.reload.replies_count', -1 do
- Topic.reset_counters(@topic.id, 'replies')
+ assert_difference "@topic.reload.replies_count", -1 do
+ Topic.reset_counters(@topic.id, "replies")
end
end
test "reset counters with modularized and camelized classnames" do
- special = SpecialTopic.create!(:title => 'Special')
+ special = SpecialTopic.create!(title: "Special")
SpecialTopic.increment_counter(:replies_count, special.id)
- assert_difference 'special.reload.replies_count', -1 do
+ assert_difference "special.reload.replies_count", -1 do
SpecialTopic.reset_counters(special.id, :special_replies)
end
end
@@ -103,10 +103,10 @@ class CounterCacheTest < ActiveRecord::TestCase
DogLover.increment_counter(:bred_dogs_count, david.id)
DogLover.increment_counter(:trained_dogs_count, david.id)
- assert_difference 'david.reload.bred_dogs_count', -1 do
+ assert_difference "david.reload.bred_dogs_count", -1 do
DogLover.reset_counters(david.id, :bred_dogs)
end
- assert_difference 'david.reload.trained_dogs_count', -1 do
+ assert_difference "david.reload.trained_dogs_count", -1 do
DogLover.reset_counters(david.id, :trained_dogs)
end
end
@@ -116,26 +116,26 @@ class CounterCacheTest < ActiveRecord::TestCase
assert_equal 2, category.categorizations.count
assert_nil category.categorizations_count
- Category.update_counters(category.id, :categorizations_count => category.categorizations.count)
+ Category.update_counters(category.id, categorizations_count: category.categorizations.count)
assert_equal 2, category.reload.categorizations_count
end
test "update counter for decrement" do
- assert_difference '@topic.reload.replies_count', -3 do
- Topic.update_counters(@topic.id, :replies_count => -3)
+ assert_difference "@topic.reload.replies_count", -3 do
+ Topic.update_counters(@topic.id, replies_count: -3)
end
end
test "update counters of multiple records" do
t1, t2 = topics(:first, :second)
- assert_difference ['t1.reload.replies_count', 't2.reload.replies_count'], 2 do
- Topic.update_counters([t1.id, t2.id], :replies_count => 2)
+ assert_difference ["t1.reload.replies_count", "t2.reload.replies_count"], 2 do
+ Topic.update_counters([t1.id, t2.id], replies_count: 2)
end
end
- test 'update multiple counters' do
- assert_difference ['@topic.reload.replies_count', '@topic.reload.unique_replies_count'], 2 do
+ test "update multiple counters" do
+ assert_difference ["@topic.reload.replies_count", "@topic.reload.unique_replies_count"], 2 do
Topic.update_counters @topic.id, replies_count: 2, unique_replies_count: 2
end
end
@@ -144,7 +144,7 @@ class CounterCacheTest < ActiveRecord::TestCase
david, joanna = dog_lovers(:david, :joanna)
joanna = joanna # squelch a warning
- assert_difference 'joanna.reload.dogs_count', -1 do
+ assert_difference "joanna.reload.dogs_count", -1 do
david.destroy
end
end
@@ -157,12 +157,12 @@ class CounterCacheTest < ActiveRecord::TestCase
end
test "reset counter of has_many :through association" do
- subscriber = subscribers('second')
- Subscriber.reset_counters(subscriber.id, 'books')
- Subscriber.increment_counter('books_count', subscriber.id)
+ subscriber = subscribers("second")
+ Subscriber.reset_counters(subscriber.id, "books")
+ Subscriber.increment_counter("books_count", subscriber.id)
- assert_difference 'subscriber.reload.books_count', -1 do
- Subscriber.reset_counters(subscriber.id, 'books')
+ assert_difference "subscriber.reload.books_count", -1 do
+ Subscriber.reset_counters(subscriber.id, "books")
end
end
@@ -174,10 +174,10 @@ class CounterCacheTest < ActiveRecord::TestCase
end
test "reset counter works with select declared on association" do
- special = SpecialTopic.create!(:title => 'Special')
+ special = SpecialTopic.create!(title: "Special")
SpecialTopic.increment_counter(:replies_count, special.id)
- assert_difference 'special.reload.replies_count', -1 do
+ assert_difference "special.reload.replies_count", -1 do
SpecialTopic.reset_counters(special.id, :lightweight_special_replies)
end
end
@@ -203,12 +203,154 @@ class CounterCacheTest < ActiveRecord::TestCase
test "update counters in a polymorphic relationship" do
aircraft = Aircraft.create!
- assert_difference 'aircraft.reload.wheels_count' do
+ assert_difference "aircraft.reload.wheels_count" do
aircraft.wheels << Wheel.create!
end
- assert_difference 'aircraft.reload.wheels_count', -1 do
+ assert_difference "aircraft.reload.wheels_count", -1 do
aircraft.wheels.first.destroy
end
end
+
+ test "update counters doesn't touch timestamps by default" do
+ @topic.update_column :updated_at, 5.minutes.ago
+ previously_updated_at = @topic.updated_at
+
+ Topic.update_counters(@topic.id, replies_count: -1)
+
+ assert_equal previously_updated_at, @topic.updated_at
+ end
+
+ test "update counters with touch: true" do
+ assert_touching @topic, :updated_at do
+ Topic.update_counters(@topic.id, replies_count: -1, touch: true)
+ end
+ end
+
+ test "update counters of multiple records with touch: true" do
+ t1, t2 = topics(:first, :second)
+
+ assert_touching t1, :updated_at do
+ assert_difference ["t1.reload.replies_count", "t2.reload.replies_count"], 2 do
+ Topic.update_counters([t1.id, t2.id], replies_count: 2, touch: true)
+ end
+ end
+ end
+
+ test "update multiple counters with touch: true" do
+ assert_touching @topic, :updated_at do
+ Topic.update_counters(@topic.id, replies_count: 2, unique_replies_count: 2, touch: true)
+ end
+ end
+
+ test "reset counters with touch: true" do
+ assert_touching @topic, :updated_at do
+ Topic.reset_counters(@topic.id, :replies, touch: true)
+ end
+ end
+
+ test "reset multiple counters with touch: true" do
+ assert_touching @topic, :updated_at do
+ Topic.update_counters(@topic.id, replies_count: 1, unique_replies_count: 1)
+ Topic.reset_counters(@topic.id, :replies, :unique_replies, touch: true)
+ end
+ end
+
+ test "increment counters with touch: true" do
+ assert_touching @topic, :updated_at do
+ Topic.increment_counter(:replies_count, @topic.id, touch: true)
+ end
+ end
+
+ test "decrement counters with touch: true" do
+ assert_touching @topic, :updated_at do
+ Topic.decrement_counter(:replies_count, @topic.id, touch: true)
+ end
+ end
+
+ test "update counters with touch: :written_on" do
+ assert_touching @topic, :written_on do
+ Topic.update_counters(@topic.id, replies_count: -1, touch: :written_on)
+ end
+ end
+
+ test "update multiple counters with touch: :written_on" do
+ assert_touching @topic, :written_on do
+ Topic.update_counters(@topic.id, replies_count: 2, unique_replies_count: 2, touch: :written_on)
+ end
+ end
+
+ test "reset counters with touch: :written_on" do
+ assert_touching @topic, :written_on do
+ Topic.reset_counters(@topic.id, :replies, touch: :written_on)
+ end
+ end
+
+ test "reset multiple counters with touch: :written_on" do
+ assert_touching @topic, :written_on do
+ Topic.update_counters(@topic.id, replies_count: 1, unique_replies_count: 1)
+ Topic.reset_counters(@topic.id, :replies, :unique_replies, touch: :written_on)
+ end
+ end
+
+ test "increment counters with touch: :written_on" do
+ assert_touching @topic, :written_on do
+ Topic.increment_counter(:replies_count, @topic.id, touch: :written_on)
+ end
+ end
+
+ test "decrement counters with touch: :written_on" do
+ assert_touching @topic, :written_on do
+ Topic.decrement_counter(:replies_count, @topic.id, touch: :written_on)
+ end
+ end
+
+ test "update counters with touch: %i( updated_at written_on )" do
+ assert_touching @topic, :updated_at, :written_on do
+ Topic.update_counters(@topic.id, replies_count: -1, touch: %i( updated_at written_on ))
+ end
+ end
+
+ test "update multiple counters with touch: %i( updated_at written_on )" do
+ assert_touching @topic, :updated_at, :written_on do
+ Topic.update_counters(@topic.id, replies_count: 2, unique_replies_count: 2, touch: %i( updated_at written_on ))
+ end
+ end
+
+ test "reset counters with touch: %i( updated_at written_on )" do
+ assert_touching @topic, :updated_at, :written_on do
+ Topic.reset_counters(@topic.id, :replies, touch: %i( updated_at written_on ))
+ end
+ end
+
+ test "reset multiple counters with touch: %i( updated_at written_on )" do
+ assert_touching @topic, :updated_at, :written_on do
+ Topic.update_counters(@topic.id, replies_count: 1, unique_replies_count: 1)
+ Topic.reset_counters(@topic.id, :replies, :unique_replies, touch: %i( updated_at written_on ))
+ end
+ end
+
+ test "increment counters with touch: %i( updated_at written_on )" do
+ assert_touching @topic, :updated_at, :written_on do
+ Topic.increment_counter(:replies_count, @topic.id, touch: %i( updated_at written_on ))
+ end
+ end
+
+ test "decrement counters with touch: %i( updated_at written_on )" do
+ assert_touching @topic, :updated_at, :written_on do
+ Topic.decrement_counter(:replies_count, @topic.id, touch: %i( updated_at written_on ))
+ end
+ end
+
+ private
+ def assert_touching(record, *attributes)
+ record.update_columns attributes.map { |attr| [ attr, 5.minutes.ago ] }.to_h
+ touch_times = attributes.map { |attr| [ attr, record.public_send(attr) ] }.to_h
+
+ yield
+
+ touch_times.each do |attr, previous_touch_time|
+ assert_operator previous_touch_time, :<, record.reload.public_send(attr)
+ end
+ end
end
diff --git a/activerecord/test/cases/custom_locking_test.rb b/activerecord/test/cases/custom_locking_test.rb
index 26d015bf71..15c8b684e4 100644
--- a/activerecord/test/cases/custom_locking_test.rb
+++ b/activerecord/test/cases/custom_locking_test.rb
@@ -1,5 +1,5 @@
require "cases/helper"
-require 'models/person'
+require "models/person"
module ActiveRecord
class CustomLockingTest < ActiveRecord::TestCase
@@ -7,9 +7,9 @@ module ActiveRecord
def test_custom_lock
if current_adapter?(:Mysql2Adapter)
- assert_match 'SHARE MODE', Person.lock('LOCK IN SHARE MODE').to_sql
+ assert_match "SHARE MODE", Person.lock("LOCK IN SHARE MODE").to_sql
assert_sql(/LOCK IN SHARE MODE/) do
- Person.all.merge!(:lock => 'LOCK IN SHARE MODE').find(1)
+ Person.all.merge!(lock: "LOCK IN SHARE MODE").find(1)
end
end
end
diff --git a/activerecord/test/cases/database_statements_test.rb b/activerecord/test/cases/database_statements_test.rb
index 3169408ac0..66035865be 100644
--- a/activerecord/test/cases/database_statements_test.rb
+++ b/activerecord/test/cases/database_statements_test.rb
@@ -5,6 +5,13 @@ class DatabaseStatementsTest < ActiveRecord::TestCase
@connection = ActiveRecord::Base.connection
end
+ unless current_adapter?(:OracleAdapter)
+ def test_exec_insert
+ result = @connection.exec_insert("INSERT INTO accounts (firm_id,credit_limit) VALUES (42,5000)", nil, [])
+ assert_not_nil @connection.send(:last_inserted_id, result)
+ end
+ end
+
def test_insert_should_return_the_inserted_id
assert_not_nil return_the_inserted_id(method: :insert)
end
@@ -13,22 +20,16 @@ class DatabaseStatementsTest < ActiveRecord::TestCase
assert_not_nil return_the_inserted_id(method: :create)
end
- def test_insert_update_delete_sql_is_deprecated
- assert_deprecated { @connection.insert_sql("INSERT INTO accounts (firm_id,credit_limit) VALUES (42,5000)") }
- assert_deprecated { @connection.update_sql("UPDATE accounts SET credit_limit = 6000 WHERE firm_id = 42") }
- assert_deprecated { @connection.delete_sql("DELETE FROM accounts WHERE firm_id = 42") }
- end
-
private
- def return_the_inserted_id(method:)
- # Oracle adapter uses prefetched primary key values from sequence and passes them to connection adapter insert method
- if current_adapter?(:OracleAdapter)
- sequence_name = "accounts_seq"
- id_value = @connection.next_sequence_value(sequence_name)
- @connection.send(method, "INSERT INTO accounts (id, firm_id,credit_limit) VALUES (accounts_seq.nextval,42,5000)", nil, :id, id_value, sequence_name)
- else
- @connection.send(method, "INSERT INTO accounts (firm_id,credit_limit) VALUES (42,5000)")
+ def return_the_inserted_id(method:)
+ # Oracle adapter uses prefetched primary key values from sequence and passes them to connection adapter insert method
+ if current_adapter?(:OracleAdapter)
+ sequence_name = "accounts_seq"
+ id_value = @connection.next_sequence_value(sequence_name)
+ @connection.send(method, "INSERT INTO accounts (id, firm_id,credit_limit) VALUES (accounts_seq.nextval,42,5000)", nil, :id, id_value, sequence_name)
+ else
+ @connection.send(method, "INSERT INTO accounts (firm_id,credit_limit) VALUES (42,5000)")
+ end
end
- end
end
diff --git a/activerecord/test/cases/date_test.rb b/activerecord/test/cases/date_test.rb
index f112081c14..2edc0415cd 100644
--- a/activerecord/test/cases/date_test.rb
+++ b/activerecord/test/cases/date_test.rb
@@ -1,5 +1,5 @@
-require 'cases/helper'
-require 'models/topic'
+require "cases/helper"
+require "models/topic"
class DateTest < ActiveRecord::TestCase
def test_date_with_time_value
@@ -9,7 +9,7 @@ class DateTest < ActiveRecord::TestCase
end
def test_date_with_string_value
- string_value = '2016-05-11 19:00:00'
+ string_value = "2016-05-11 19:00:00"
topic = Topic.create(last_read: string_value)
assert_equal topic, Topic.find_by(last_read: string_value)
end
@@ -31,7 +31,7 @@ class DateTest < ActiveRecord::TestCase
invalid_dates.each do |date_src|
assert_nothing_raised do
- topic = Topic.new({"last_read(1i)" => date_src[0].to_s, "last_read(2i)" => date_src[1].to_s, "last_read(3i)" => date_src[2].to_s})
+ topic = Topic.new("last_read(1i)" => date_src[0].to_s, "last_read(2i)" => date_src[1].to_s, "last_read(3i)" => date_src[2].to_s)
# Oracle DATE columns are datetime columns and Oracle adapter returns Time value
if current_adapter?(:OracleAdapter)
assert_equal(topic.last_read.to_date, Time.local(*date_src).to_date, "The date should be modified according to the behavior of the Time object")
diff --git a/activerecord/test/cases/date_time_precision_test.rb b/activerecord/test/cases/date_time_precision_test.rb
index f8664d83bd..a1c3c5af9c 100644
--- a/activerecord/test/cases/date_time_precision_test.rb
+++ b/activerecord/test/cases/date_time_precision_test.rb
@@ -1,88 +1,87 @@
-require 'cases/helper'
-require 'support/schema_dumping_helper'
+require "cases/helper"
+require "support/schema_dumping_helper"
if subsecond_precision_supported?
-class DateTimePrecisionTest < ActiveRecord::TestCase
- include SchemaDumpingHelper
- self.use_transactional_tests = false
+ class DateTimePrecisionTest < ActiveRecord::TestCase
+ include SchemaDumpingHelper
+ self.use_transactional_tests = false
- class Foo < ActiveRecord::Base; end
+ class Foo < ActiveRecord::Base; end
- setup do
- @connection = ActiveRecord::Base.connection
- Foo.reset_column_information
- end
-
- teardown do
- @connection.drop_table :foos, if_exists: true
- end
+ setup do
+ @connection = ActiveRecord::Base.connection
+ Foo.reset_column_information
+ end
- def test_datetime_data_type_with_precision
- @connection.create_table(:foos, force: true)
- @connection.add_column :foos, :created_at, :datetime, precision: 0
- @connection.add_column :foos, :updated_at, :datetime, precision: 5
- assert_equal 0, Foo.columns_hash['created_at'].precision
- assert_equal 5, Foo.columns_hash['updated_at'].precision
- end
+ teardown do
+ @connection.drop_table :foos, if_exists: true
+ end
- def test_timestamps_helper_with_custom_precision
- @connection.create_table(:foos, force: true) do |t|
- t.timestamps precision: 4
+ def test_datetime_data_type_with_precision
+ @connection.create_table(:foos, force: true)
+ @connection.add_column :foos, :created_at, :datetime, precision: 0
+ @connection.add_column :foos, :updated_at, :datetime, precision: 5
+ assert_equal 0, Foo.columns_hash["created_at"].precision
+ assert_equal 5, Foo.columns_hash["updated_at"].precision
end
- assert_equal 4, Foo.columns_hash['created_at'].precision
- assert_equal 4, Foo.columns_hash['updated_at'].precision
- end
- def test_passing_precision_to_datetime_does_not_set_limit
- @connection.create_table(:foos, force: true) do |t|
- t.timestamps precision: 4
+ def test_timestamps_helper_with_custom_precision
+ @connection.create_table(:foos, force: true) do |t|
+ t.timestamps precision: 4
+ end
+ assert_equal 4, Foo.columns_hash["created_at"].precision
+ assert_equal 4, Foo.columns_hash["updated_at"].precision
end
- assert_nil Foo.columns_hash['created_at'].limit
- assert_nil Foo.columns_hash['updated_at'].limit
- end
- def test_invalid_datetime_precision_raises_error
- assert_raises ActiveRecord::ActiveRecordError do
+ def test_passing_precision_to_datetime_does_not_set_limit
@connection.create_table(:foos, force: true) do |t|
- t.timestamps precision: 7
+ t.timestamps precision: 4
end
+ assert_nil Foo.columns_hash["created_at"].limit
+ assert_nil Foo.columns_hash["updated_at"].limit
end
- end
- def test_formatting_datetime_according_to_precision
- @connection.create_table(:foos, force: true) do |t|
- t.datetime :created_at, precision: 0
- t.datetime :updated_at, precision: 4
+ def test_invalid_datetime_precision_raises_error
+ assert_raises ActiveRecord::ActiveRecordError do
+ @connection.create_table(:foos, force: true) do |t|
+ t.timestamps precision: 7
+ end
+ end
end
- date = ::Time.utc(2014, 8, 17, 12, 30, 0, 999999)
- Foo.create!(created_at: date, updated_at: date)
- assert foo = Foo.find_by(created_at: date)
- assert_equal 1, Foo.where(updated_at: date).count
- assert_equal date.to_s, foo.created_at.to_s
- assert_equal date.to_s, foo.updated_at.to_s
- assert_equal 000000, foo.created_at.usec
- assert_equal 999900, foo.updated_at.usec
- end
- def test_schema_dump_includes_datetime_precision
- @connection.create_table(:foos, force: true) do |t|
- t.timestamps precision: 6
+ def test_formatting_datetime_according_to_precision
+ @connection.create_table(:foos, force: true) do |t|
+ t.datetime :created_at, precision: 0
+ t.datetime :updated_at, precision: 4
+ end
+ date = ::Time.utc(2014, 8, 17, 12, 30, 0, 999999)
+ Foo.create!(created_at: date, updated_at: date)
+ assert foo = Foo.find_by(created_at: date)
+ assert_equal 1, Foo.where(updated_at: date).count
+ assert_equal date.to_s, foo.created_at.to_s
+ assert_equal date.to_s, foo.updated_at.to_s
+ assert_equal 000000, foo.created_at.usec
+ assert_equal 999900, foo.updated_at.usec
end
- output = dump_table_schema("foos")
- assert_match %r{t\.datetime\s+"created_at",\s+precision: 6,\s+null: false$}, output
- assert_match %r{t\.datetime\s+"updated_at",\s+precision: 6,\s+null: false$}, output
- end
- if current_adapter?(:PostgreSQLAdapter)
- def test_datetime_precision_with_zero_should_be_dumped
+ def test_schema_dump_includes_datetime_precision
@connection.create_table(:foos, force: true) do |t|
- t.timestamps precision: 0
+ t.timestamps precision: 6
end
output = dump_table_schema("foos")
- assert_match %r{t\.datetime\s+"created_at",\s+precision: 0,\s+null: false$}, output
- assert_match %r{t\.datetime\s+"updated_at",\s+precision: 0,\s+null: false$}, output
+ assert_match %r{t\.datetime\s+"created_at",\s+precision: 6,\s+null: false$}, output
+ assert_match %r{t\.datetime\s+"updated_at",\s+precision: 6,\s+null: false$}, output
end
- end
-end
+ if current_adapter?(:PostgreSQLAdapter)
+ def test_datetime_precision_with_zero_should_be_dumped
+ @connection.create_table(:foos, force: true) do |t|
+ t.timestamps precision: 0
+ end
+ output = dump_table_schema("foos")
+ assert_match %r{t\.datetime\s+"created_at",\s+precision: 0,\s+null: false$}, output
+ assert_match %r{t\.datetime\s+"updated_at",\s+precision: 0,\s+null: false$}, output
+ end
+ end
+ end
end
diff --git a/activerecord/test/cases/date_time_test.rb b/activerecord/test/cases/date_time_test.rb
index 4cbff564aa..3bc08f80ec 100644
--- a/activerecord/test/cases/date_time_test.rb
+++ b/activerecord/test/cases/date_time_test.rb
@@ -1,12 +1,12 @@
require "cases/helper"
-require 'models/topic'
-require 'models/task'
+require "models/topic"
+require "models/task"
class DateTimeTest < ActiveRecord::TestCase
include InTimeZone
def test_saves_both_date_and_time
- with_env_tz 'America/New_York' do
+ with_env_tz "America/New_York" do
with_timezone_config default: :utc do
time_values = [1807, 2, 10, 15, 30, 45]
# create DateTime value with local time zone offset
@@ -25,7 +25,7 @@ class DateTimeTest < ActiveRecord::TestCase
def test_assign_empty_date_time
task = Task.new
- task.starting = ''
+ task.starting = ""
task.ending = nil
assert_nil task.starting
assert_nil task.ending
@@ -34,20 +34,20 @@ class DateTimeTest < ActiveRecord::TestCase
def test_assign_bad_date_time_with_timezone
in_time_zone "Pacific Time (US & Canada)" do
task = Task.new
- task.starting = '2014-07-01T24:59:59GMT'
+ task.starting = "2014-07-01T24:59:59GMT"
assert_nil task.starting
end
end
def test_assign_empty_date
topic = Topic.new
- topic.last_read = ''
+ topic.last_read = ""
assert_nil topic.last_read
end
def test_assign_empty_time
topic = Topic.new
- topic.bonus_time = ''
+ topic.bonus_time = ""
assert_nil topic.bonus_time
end
diff --git a/activerecord/test/cases/defaults_test.rb b/activerecord/test/cases/defaults_test.rb
index 067513e24c..6532efcf22 100644
--- a/activerecord/test/cases/defaults_test.rb
+++ b/activerecord/test/cases/defaults_test.rb
@@ -1,7 +1,7 @@
require "cases/helper"
-require 'support/schema_dumping_helper'
-require 'models/default'
-require 'models/entrant'
+require "support/schema_dumping_helper"
+require "models/default"
+require "models/entrant"
class DefaultTest < ActiveRecord::TestCase
def test_nil_defaults_for_not_null_columns
@@ -99,7 +99,7 @@ if current_adapter?(:Mysql2Adapter)
class MysqlDefaultExpressionTest < ActiveRecord::TestCase
include SchemaDumpingHelper
- if ActiveRecord::Base.connection.version >= '5.6.0'
+ if ActiveRecord::Base.connection.version >= "5.6.0"
test "schema dump includes default expression" do
output = dump_table_schema("datetime_defaults")
assert_match %r/t\.datetime\s+"modified_datetime",\s+default: -> { "CURRENT_TIMESTAMP" }/, output
@@ -127,92 +127,66 @@ if current_adapter?(:Mysql2Adapter)
ActiveRecord::Base.establish_connection connection
end
- # MySQL cannot have defaults on text/blob columns. It reports the
- # default value as null.
+ # Strict mode controls how MySQL handles invalid or missing values
+ # in data-change statements such as INSERT or UPDATE. A value can be
+ # invalid for several reasons. For example, it might have the wrong
+ # data type for the column, or it might be out of range. A value is
+ # missing when a new row to be inserted does not contain a value for
+ # a non-NULL column that has no explicit DEFAULT clause in its definition.
+ # (For a NULL column, NULL is inserted if the value is missing.)
#
- # Despite this, in non-strict mode, MySQL will use an empty string
- # as the default value of the field, if no other value is
- # specified.
+ # If strict mode is not in effect, MySQL inserts adjusted values for
+ # invalid or missing values and produces warnings. In strict mode,
+ # you can produce this behavior by using INSERT IGNORE or UPDATE IGNORE.
#
- # Therefore, in non-strict mode, we want column.default to report
- # an empty string as its default, to be consistent with that.
- #
- # In strict mode, column.default should be nil.
- def test_mysql_text_not_null_defaults_non_strict
+ # https://dev.mysql.com/doc/refman/5.7/en/sql-mode.html#sql-mode-strict
+ def test_mysql_not_null_defaults_non_strict
using_strict(false) do
- with_text_blob_not_null_table do |klass|
+ with_mysql_not_null_table do |klass|
record = klass.new
- assert_equal '', record.non_null_blob
- assert_equal '', record.non_null_text
-
- assert_nil record.null_blob
- assert_nil record.null_text
+ assert_nil record.non_null_integer
+ assert_nil record.non_null_string
+ assert_nil record.non_null_text
+ assert_nil record.non_null_blob
record.save!
record.reload
- assert_equal '', record.non_null_text
- assert_equal '', record.non_null_blob
-
- assert_nil record.null_text
- assert_nil record.null_blob
+ assert_equal 0, record.non_null_integer
+ assert_equal "", record.non_null_string
+ assert_equal "", record.non_null_text
+ assert_equal "", record.non_null_blob
end
end
end
- def test_mysql_text_not_null_defaults_strict
+ def test_mysql_not_null_defaults_strict
using_strict(true) do
- with_text_blob_not_null_table do |klass|
+ with_mysql_not_null_table do |klass|
record = klass.new
- assert_nil record.non_null_blob
+ assert_nil record.non_null_integer
+ assert_nil record.non_null_string
assert_nil record.non_null_text
- assert_nil record.null_blob
- assert_nil record.null_text
+ assert_nil record.non_null_blob
- assert_raises(ActiveRecord::StatementInvalid) { klass.create }
+ assert_raises(ActiveRecord::NotNullViolation) { klass.create }
end
end
end
- def with_text_blob_not_null_table
+ def with_mysql_not_null_table
klass = Class.new(ActiveRecord::Base)
- klass.table_name = 'test_mysql_text_not_null_defaults'
+ klass.table_name = "test_mysql_not_null_defaults"
klass.connection.create_table klass.table_name do |t|
- t.column :non_null_text, :text, :null => false
- t.column :non_null_blob, :blob, :null => false
- t.column :null_text, :text, :null => true
- t.column :null_blob, :blob, :null => true
+ t.integer :non_null_integer, null: false
+ t.string :non_null_string, null: false
+ t.text :non_null_text, null: false
+ t.blob :non_null_blob, null: false
end
yield klass
ensure
klass.connection.drop_table(klass.table_name) rescue nil
end
-
- # MySQL uses an implicit default 0 rather than NULL unless in strict mode.
- # We use an implicit NULL so schema.rb is compatible with other databases.
- def test_mysql_integer_not_null_defaults
- klass = Class.new(ActiveRecord::Base)
- klass.table_name = 'test_integer_not_null_default_zero'
- klass.connection.create_table klass.table_name do |t|
- t.column :zero, :integer, :null => false, :default => 0
- t.column :omit, :integer, :null => false
- end
-
- assert_equal '0', klass.columns_hash['zero'].default
- assert !klass.columns_hash['zero'].null
- assert_equal nil, klass.columns_hash['omit'].default
- assert !klass.columns_hash['omit'].null
-
- assert_raise(ActiveRecord::StatementInvalid) { klass.create! }
-
- assert_nothing_raised do
- instance = klass.create!(:omit => 1)
- assert_equal 0, instance.zero
- assert_equal 1, instance.omit
- end
- ensure
- klass.connection.drop_table(klass.table_name) rescue nil
- end
end
end
diff --git a/activerecord/test/cases/dirty_test.rb b/activerecord/test/cases/dirty_test.rb
index f9794518c7..0e58e65a07 100644
--- a/activerecord/test/cases/dirty_test.rb
+++ b/activerecord/test/cases/dirty_test.rb
@@ -1,12 +1,12 @@
-require 'cases/helper'
-require 'models/topic' # For booleans
-require 'models/pirate' # For timestamps
-require 'models/parrot'
-require 'models/person' # For optimistic locking
-require 'models/aircraft'
+require "cases/helper"
+require "models/topic" # For booleans
+require "models/pirate" # For timestamps
+require "models/parrot"
+require "models/person" # For optimistic locking
+require "models/aircraft"
class NumericData < ActiveRecord::Base
- self.table_name = 'numeric_data'
+ self.table_name = "numeric_data"
end
class DirtyTest < ActiveRecord::TestCase
@@ -14,7 +14,7 @@ class DirtyTest < ActiveRecord::TestCase
# Dummy to force column loads so query counts are clean.
def setup
- Person.create :first_name => 'foo'
+ Person.create first_name: "foo"
end
def test_attribute_changes
@@ -24,10 +24,10 @@ class DirtyTest < ActiveRecord::TestCase
assert_equal false, pirate.non_validated_parrot_id_changed?
# Change catchphrase.
- pirate.catchphrase = 'arrr'
+ pirate.catchphrase = "arrr"
assert pirate.catchphrase_changed?
assert_nil pirate.catchphrase_was
- assert_equal [nil, 'arrr'], pirate.catchphrase_change
+ assert_equal [nil, "arrr"], pirate.catchphrase_change
# Saved - no changes.
pirate.save!
@@ -35,15 +35,15 @@ class DirtyTest < ActiveRecord::TestCase
assert_nil pirate.catchphrase_change
# Same value - no changes.
- pirate.catchphrase = 'arrr'
+ pirate.catchphrase = "arrr"
assert !pirate.catchphrase_changed?
assert_nil pirate.catchphrase_change
end
def test_time_attributes_changes_with_time_zone
- in_time_zone 'Paris' do
+ in_time_zone "Paris" do
target = Class.new(ActiveRecord::Base)
- target.table_name = 'pirates'
+ target.table_name = "pirates"
# New record - no changes.
pirate = target.new
@@ -51,7 +51,7 @@ class DirtyTest < ActiveRecord::TestCase
assert_nil pirate.created_on_change
# Saved - no changes.
- pirate.catchphrase = 'arrrr, time zone!!'
+ pirate.catchphrase = "arrrr, time zone!!"
pirate.save!
assert !pirate.created_on_changed?
assert_nil pirate.created_on_change
@@ -68,9 +68,9 @@ class DirtyTest < ActiveRecord::TestCase
end
def test_setting_time_attributes_with_time_zone_field_to_itself_should_not_be_marked_as_a_change
- in_time_zone 'Paris' do
+ in_time_zone "Paris" do
target = Class.new(ActiveRecord::Base)
- target.table_name = 'pirates'
+ target.table_name = "pirates"
pirate = target.create!
pirate.created_on = pirate.created_on
@@ -79,9 +79,9 @@ class DirtyTest < ActiveRecord::TestCase
end
def test_time_attributes_changes_without_time_zone_by_skip
- in_time_zone 'Paris' do
+ in_time_zone "Paris" do
target = Class.new(ActiveRecord::Base)
- target.table_name = 'pirates'
+ target.table_name = "pirates"
target.skip_time_zone_conversion_for_attributes = [:created_on]
@@ -91,7 +91,7 @@ class DirtyTest < ActiveRecord::TestCase
assert_nil pirate.created_on_change
# Saved - no changes.
- pirate.catchphrase = 'arrrr, time zone!!'
+ pirate.catchphrase = "arrrr, time zone!!"
pirate.save!
assert !pirate.created_on_changed?
assert_nil pirate.created_on_change
@@ -110,7 +110,7 @@ class DirtyTest < ActiveRecord::TestCase
def test_time_attributes_changes_without_time_zone
with_timezone_config aware_attributes: false do
target = Class.new(ActiveRecord::Base)
- target.table_name = 'pirates'
+ target.table_name = "pirates"
# New record - no changes.
pirate = target.new
@@ -118,7 +118,7 @@ class DirtyTest < ActiveRecord::TestCase
assert_nil pirate.created_on_change
# Saved - no changes.
- pirate.catchphrase = 'arrrr, time zone!!'
+ pirate.catchphrase = "arrrr, time zone!!"
pirate.save!
assert !pirate.created_on_changed?
assert_nil pirate.created_on_change
@@ -134,7 +134,6 @@ class DirtyTest < ActiveRecord::TestCase
end
end
-
def test_aliased_attribute_changes
# the actual attribute here is name, title is an
# alias setup via alias_attribute
@@ -142,15 +141,15 @@ class DirtyTest < ActiveRecord::TestCase
assert !parrot.title_changed?
assert_nil parrot.title_change
- parrot.name = 'Sam'
+ parrot.name = "Sam"
assert parrot.title_changed?
assert_nil parrot.title_was
assert_equal parrot.name_change, parrot.title_change
end
def test_restore_attribute!
- pirate = Pirate.create!(:catchphrase => 'Yar!')
- pirate.catchphrase = 'Ahoy!'
+ pirate = Pirate.create!(catchphrase: "Yar!")
+ pirate.catchphrase = "Ahoy!"
pirate.restore_catchphrase!
assert_equal "Yar!", pirate.catchphrase
@@ -189,9 +188,9 @@ class DirtyTest < ActiveRecord::TestCase
end
def test_nullable_datetime_not_marked_as_changed_if_new_value_is_blank
- in_time_zone 'Edinburgh' do
+ in_time_zone "Edinburgh" do
target = Class.new(ActiveRecord::Base)
- target.table_name = 'topics'
+ target.table_name = "topics"
topic = target.create
assert_nil topic.written_on
@@ -207,19 +206,19 @@ class DirtyTest < ActiveRecord::TestCase
def test_integer_zero_to_string_zero_not_marked_as_changed
pirate = Pirate.new
pirate.parrot_id = 0
- pirate.catchphrase = 'arrr'
+ pirate.catchphrase = "arrr"
assert pirate.save!
assert !pirate.changed?
- pirate.parrot_id = '0'
+ pirate.parrot_id = "0"
assert !pirate.changed?
end
def test_integer_zero_to_integer_zero_not_marked_as_changed
pirate = Pirate.new
pirate.parrot_id = 0
- pirate.catchphrase = 'arrr'
+ pirate.catchphrase = "arrr"
assert pirate.save!
assert !pirate.changed?
@@ -229,18 +228,18 @@ class DirtyTest < ActiveRecord::TestCase
end
def test_float_zero_to_string_zero_not_marked_as_changed
- data = NumericData.new :temperature => 0.0
+ data = NumericData.new temperature: 0.0
data.save!
assert_not data.changed?
- data.temperature = '0'
+ data.temperature = "0"
assert_empty data.changes
- data.temperature = '0.0'
+ data.temperature = "0.0"
assert_empty data.changes
- data.temperature = '0.00'
+ data.temperature = "0.00"
assert_empty data.changes
end
@@ -252,7 +251,7 @@ class DirtyTest < ActiveRecord::TestCase
# check the change from 1 to ''
pirate = Pirate.find_by_catchphrase("Yarrrr, me hearties")
- pirate.parrot_id = ''
+ pirate.parrot_id = ""
assert pirate.parrot_id_changed?
assert_equal([1, nil], pirate.parrot_id_change)
pirate.save
@@ -266,7 +265,7 @@ class DirtyTest < ActiveRecord::TestCase
# check the change from 0 to ''
pirate = Pirate.find_by_catchphrase("Yarrrr, me hearties")
- pirate.parrot_id = ''
+ pirate.parrot_id = ""
assert pirate.parrot_id_changed?
assert_equal([0, nil], pirate.parrot_id_change)
end
@@ -277,11 +276,11 @@ class DirtyTest < ActiveRecord::TestCase
assert_equal [], pirate.changed
assert_equal Hash.new, pirate.changes
- pirate.catchphrase = 'arrr'
+ pirate.catchphrase = "arrr"
assert pirate.changed?
assert_nil pirate.catchphrase_was
assert_equal %w(catchphrase), pirate.changed
- assert_equal({'catchphrase' => [nil, 'arrr']}, pirate.changes)
+ assert_equal({ "catchphrase" => [nil, "arrr"] }, pirate.changes)
pirate.save
assert !pirate.changed?
@@ -290,21 +289,21 @@ class DirtyTest < ActiveRecord::TestCase
end
def test_attribute_will_change!
- pirate = Pirate.create!(:catchphrase => 'arr')
+ pirate = Pirate.create!(catchphrase: "arr")
assert !pirate.catchphrase_changed?
assert pirate.catchphrase_will_change!
assert pirate.catchphrase_changed?
- assert_equal ['arr', 'arr'], pirate.catchphrase_change
+ assert_equal ["arr", "arr"], pirate.catchphrase_change
- pirate.catchphrase << ' matey!'
+ pirate.catchphrase << " matey!"
assert pirate.catchphrase_changed?
- assert_equal ['arr', 'arr matey!'], pirate.catchphrase_change
+ assert_equal ["arr", "arr matey!"], pirate.catchphrase_change
end
def test_association_assignment_changes_foreign_key
- pirate = Pirate.create!(:catchphrase => 'jarl')
- pirate.parrot = Parrot.create!(:name => 'Lorre')
+ pirate = Pirate.create!(catchphrase: "jarl")
+ pirate.parrot = Parrot.create!(name: "Lorre")
assert pirate.changed?
assert_equal %w(parrot_id), pirate.changed
end
@@ -315,7 +314,7 @@ class DirtyTest < ActiveRecord::TestCase
assert !topic.approved_changed?
# Coming from web form.
- params = {:topic => {:approved => 1}}
+ params = { topic: { approved: 1 } }
# In the controller.
topic.attributes = params[:topic]
assert topic.approved?
@@ -323,37 +322,37 @@ class DirtyTest < ActiveRecord::TestCase
end
def test_partial_update
- pirate = Pirate.new(:catchphrase => 'foo')
+ pirate = Pirate.new(catchphrase: "foo")
old_updated_on = 1.hour.ago.beginning_of_day
with_partial_writes Pirate, false do
assert_queries(2) { 2.times { pirate.save! } }
- Pirate.where(id: pirate.id).update_all(:updated_on => old_updated_on)
+ Pirate.where(id: pirate.id).update_all(updated_on: old_updated_on)
end
with_partial_writes Pirate, true do
assert_queries(0) { 2.times { pirate.save! } }
assert_equal old_updated_on, pirate.reload.updated_on
- assert_queries(1) { pirate.catchphrase = 'bar'; pirate.save! }
+ assert_queries(1) { pirate.catchphrase = "bar"; pirate.save! }
assert_not_equal old_updated_on, pirate.reload.updated_on
end
end
def test_partial_update_with_optimistic_locking
- person = Person.new(:first_name => 'foo')
- old_lock_version = 1
+ person = Person.new(first_name: "foo")
+ old_lock_version = person.lock_version
with_partial_writes Person, false do
assert_queries(2) { 2.times { person.save! } }
- Person.where(id: person.id).update_all(:first_name => 'baz')
+ Person.where(id: person.id).update_all(first_name: "baz")
end
with_partial_writes Person, true do
assert_queries(0) { 2.times { person.save! } }
assert_equal old_lock_version, person.reload.lock_version
- assert_queries(1) { person.first_name = 'bar'; person.save! }
+ assert_queries(1) { person.first_name = "bar"; person.save! }
assert_not_equal old_lock_version, person.reload.lock_version
end
end
@@ -371,7 +370,7 @@ class DirtyTest < ActiveRecord::TestCase
end
def test_reload_should_clear_changed_attributes
- pirate = Pirate.create!(:catchphrase => "shiver me timbers")
+ pirate = Pirate.create!(catchphrase: "shiver me timbers")
pirate.catchphrase = "*hic*"
assert pirate.changed?
pirate.reload
@@ -379,7 +378,7 @@ class DirtyTest < ActiveRecord::TestCase
end
def test_dup_objects_should_not_copy_dirty_flag_from_creator
- pirate = Pirate.create!(:catchphrase => "shiver me timbers")
+ pirate = Pirate.create!(catchphrase: "shiver me timbers")
pirate_dup = pirate.dup
pirate_dup.restore_catchphrase!
pirate.catchphrase = "I love Rum"
@@ -389,7 +388,7 @@ class DirtyTest < ActiveRecord::TestCase
def test_reverted_changes_are_not_dirty
phrase = "shiver me timbers"
- pirate = Pirate.create!(:catchphrase => phrase)
+ pirate = Pirate.create!(catchphrase: phrase)
pirate.catchphrase = "*hic*"
assert pirate.changed?
pirate.catchphrase = phrase
@@ -398,7 +397,7 @@ class DirtyTest < ActiveRecord::TestCase
def test_reverted_changes_are_not_dirty_after_multiple_changes
phrase = "shiver me timbers"
- pirate = Pirate.create!(:catchphrase => phrase)
+ pirate = Pirate.create!(catchphrase: phrase)
10.times do |i|
pirate.catchphrase = "*hic*" * i
assert pirate.changed?
@@ -408,9 +407,8 @@ class DirtyTest < ActiveRecord::TestCase
assert !pirate.changed?
end
-
def test_reverted_changes_are_not_dirty_going_from_nil_to_value_and_back
- pirate = Pirate.create!(:catchphrase => "Yar!")
+ pirate = Pirate.create!(catchphrase: "Yar!")
pirate.parrot_id = 1
assert pirate.changed?
@@ -425,7 +423,7 @@ class DirtyTest < ActiveRecord::TestCase
def test_save_should_store_serialized_attributes_even_with_partial_writes
with_partial_writes(Topic) do
- topic = Topic.create!(:content => {:a => "a"})
+ topic = Topic.create!(content: { a: "a" })
assert_not topic.changed?
@@ -446,25 +444,25 @@ class DirtyTest < ActiveRecord::TestCase
def test_save_always_should_update_timestamps_when_serialized_attributes_are_present
with_partial_writes(Topic) do
- topic = Topic.create!(:content => {:a => "a"})
+ topic = Topic.create!(content: { a: "a" })
topic.save!
updated_at = topic.updated_at
travel(1.second) do
- topic.content[:hello] = 'world'
+ topic.content[:hello] = "world"
topic.save!
end
assert_not_equal updated_at, topic.updated_at
- assert_equal 'world', topic.content[:hello]
+ assert_equal "world", topic.content[:hello]
end
end
def test_save_should_not_save_serialized_attribute_with_partial_writes_if_not_present
with_partial_writes(Topic) do
- Topic.create!(:author_name => 'Bill', :content => {:a => "a"})
- topic = Topic.select('id, author_name').first
- topic.update_columns author_name: 'John'
+ Topic.create!(author_name: "Bill", content: { a: "a" })
+ topic = Topic.select("id, author_name").first
+ topic.update_columns author_name: "John"
topic = Topic.first
assert_not_nil topic.content
end
@@ -479,13 +477,13 @@ class DirtyTest < ActiveRecord::TestCase
pirate.save!
assert_equal 4, pirate.previous_changes.size
- assert_equal [nil, "arrr"], pirate.previous_changes['catchphrase']
- assert_equal [nil, pirate.id], pirate.previous_changes['id']
- assert_nil pirate.previous_changes['updated_on'][0]
- assert_not_nil pirate.previous_changes['updated_on'][1]
- assert_nil pirate.previous_changes['created_on'][0]
- assert_not_nil pirate.previous_changes['created_on'][1]
- assert !pirate.previous_changes.key?('parrot_id')
+ assert_equal [nil, "arrr"], pirate.previous_changes["catchphrase"]
+ assert_equal [nil, pirate.id], pirate.previous_changes["id"]
+ assert_nil pirate.previous_changes["updated_on"][0]
+ assert_not_nil pirate.previous_changes["updated_on"][1]
+ assert_nil pirate.previous_changes["created_on"][0]
+ assert_not_nil pirate.previous_changes["created_on"][1]
+ assert !pirate.previous_changes.key?("parrot_id")
# original values should be in previous_changes
pirate = Pirate.new
@@ -495,11 +493,11 @@ class DirtyTest < ActiveRecord::TestCase
pirate.save
assert_equal 4, pirate.previous_changes.size
- assert_equal [nil, "arrr"], pirate.previous_changes['catchphrase']
- assert_equal [nil, pirate.id], pirate.previous_changes['id']
- assert pirate.previous_changes.include?('updated_on')
- assert pirate.previous_changes.include?('created_on')
- assert !pirate.previous_changes.key?('parrot_id')
+ assert_equal [nil, "arrr"], pirate.previous_changes["catchphrase"]
+ assert_equal [nil, pirate.id], pirate.previous_changes["id"]
+ assert_includes pirate.previous_changes, "updated_on"
+ assert_includes pirate.previous_changes, "created_on"
+ assert !pirate.previous_changes.key?("parrot_id")
pirate.catchphrase = "Yar!!"
pirate.reload
@@ -513,11 +511,11 @@ class DirtyTest < ActiveRecord::TestCase
pirate.save!
assert_equal 2, pirate.previous_changes.size
- assert_equal ["arrr", "Me Maties!"], pirate.previous_changes['catchphrase']
- assert_not_nil pirate.previous_changes['updated_on'][0]
- assert_not_nil pirate.previous_changes['updated_on'][1]
- assert !pirate.previous_changes.key?('parrot_id')
- assert !pirate.previous_changes.key?('created_on')
+ assert_equal ["arrr", "Me Maties!"], pirate.previous_changes["catchphrase"]
+ assert_not_nil pirate.previous_changes["updated_on"][0]
+ assert_not_nil pirate.previous_changes["updated_on"][1]
+ assert !pirate.previous_changes.key?("parrot_id")
+ assert !pirate.previous_changes.key?("created_on")
pirate = Pirate.find_by_catchphrase("Me Maties!")
@@ -527,11 +525,11 @@ class DirtyTest < ActiveRecord::TestCase
pirate.save
assert_equal 2, pirate.previous_changes.size
- assert_equal ["Me Maties!", "Thar She Blows!"], pirate.previous_changes['catchphrase']
- assert_not_nil pirate.previous_changes['updated_on'][0]
- assert_not_nil pirate.previous_changes['updated_on'][1]
- assert !pirate.previous_changes.key?('parrot_id')
- assert !pirate.previous_changes.key?('created_on')
+ assert_equal ["Me Maties!", "Thar She Blows!"], pirate.previous_changes["catchphrase"]
+ assert_not_nil pirate.previous_changes["updated_on"][0]
+ assert_not_nil pirate.previous_changes["updated_on"][1]
+ assert !pirate.previous_changes.key?("parrot_id")
+ assert !pirate.previous_changes.key?("created_on")
travel(1.second)
@@ -539,11 +537,11 @@ class DirtyTest < ActiveRecord::TestCase
pirate.update(catchphrase: "Ahoy!")
assert_equal 2, pirate.previous_changes.size
- assert_equal ["Thar She Blows!", "Ahoy!"], pirate.previous_changes['catchphrase']
- assert_not_nil pirate.previous_changes['updated_on'][0]
- assert_not_nil pirate.previous_changes['updated_on'][1]
- assert !pirate.previous_changes.key?('parrot_id')
- assert !pirate.previous_changes.key?('created_on')
+ assert_equal ["Thar She Blows!", "Ahoy!"], pirate.previous_changes["catchphrase"]
+ assert_not_nil pirate.previous_changes["updated_on"][0]
+ assert_not_nil pirate.previous_changes["updated_on"][1]
+ assert !pirate.previous_changes.key?("parrot_id")
+ assert !pirate.previous_changes.key?("created_on")
travel(1.second)
@@ -551,11 +549,11 @@ class DirtyTest < ActiveRecord::TestCase
pirate.update_attribute(:catchphrase, "Ninjas suck!")
assert_equal 2, pirate.previous_changes.size
- assert_equal ["Ahoy!", "Ninjas suck!"], pirate.previous_changes['catchphrase']
- assert_not_nil pirate.previous_changes['updated_on'][0]
- assert_not_nil pirate.previous_changes['updated_on'][1]
- assert !pirate.previous_changes.key?('parrot_id')
- assert !pirate.previous_changes.key?('created_on')
+ assert_equal ["Ahoy!", "Ninjas suck!"], pirate.previous_changes["catchphrase"]
+ assert_not_nil pirate.previous_changes["updated_on"][0]
+ assert_not_nil pirate.previous_changes["updated_on"][1]
+ assert !pirate.previous_changes.key?("parrot_id")
+ assert !pirate.previous_changes.key?("created_on")
ensure
travel_back
end
@@ -576,24 +574,24 @@ class DirtyTest < ActiveRecord::TestCase
def test_datetime_attribute_can_be_updated_with_fractional_seconds
skip "Fractional seconds are not supported" unless subsecond_precision_supported?
- in_time_zone 'Paris' do
+ in_time_zone "Paris" do
target = Class.new(ActiveRecord::Base)
- target.table_name = 'topics'
+ target.table_name = "topics"
- written_on = Time.utc(2012, 12, 1, 12, 0, 0).in_time_zone('Paris')
+ written_on = Time.utc(2012, 12, 1, 12, 0, 0).in_time_zone("Paris")
- topic = target.create(:written_on => written_on)
+ topic = target.create(written_on: written_on)
topic.written_on += 0.3
- assert topic.written_on_changed?, 'Fractional second update not detected'
+ assert topic.written_on_changed?, "Fractional second update not detected"
end
end
def test_datetime_attribute_doesnt_change_if_zone_is_modified_in_string
- time_in_paris = Time.utc(2014, 1, 1, 12, 0, 0).in_time_zone('Paris')
- pirate = Pirate.create!(:catchphrase => 'rrrr', :created_on => time_in_paris)
+ time_in_paris = Time.utc(2014, 1, 1, 12, 0, 0).in_time_zone("Paris")
+ pirate = Pirate.create!(catchphrase: "rrrr", created_on: time_in_paris)
- pirate.created_on = pirate.created_on.in_time_zone('Tokyo').to_s
+ pirate.created_on = pirate.created_on.in_time_zone("Tokyo").to_s
assert !pirate.created_on_changed?
end
@@ -601,13 +599,13 @@ class DirtyTest < ActiveRecord::TestCase
with_partial_writes Person do
jon = nil
assert_sql(/first_name/i) do
- jon = Person.create! first_name: 'Jon'
+ jon = Person.create! first_name: "Jon"
end
- assert ActiveRecord::SQLCounter.log_all.none? { |sql| sql =~ /followers_count/ }
+ assert ActiveRecord::SQLCounter.log_all.none? { |sql| sql.include?("followers_count") }
jon.reload
- assert_equal 'Jon', jon.first_name
+ assert_equal "Jon", jon.first_name
assert_equal 0, jon.followers_count
assert_not_nil jon.id
end
@@ -633,7 +631,7 @@ class DirtyTest < ActiveRecord::TestCase
assert_equal("arrrr", pirate.catchphrase_was)
assert pirate.catchphrase_changed?(from: "arrrr")
assert_not pirate.catchphrase_changed?(from: "anything else")
- assert pirate.changed_attributes.include?(:catchphrase)
+ assert_includes pirate.changed_attributes, :catchphrase
pirate.save!
pirate.reload
@@ -672,7 +670,7 @@ class DirtyTest < ActiveRecord::TestCase
end
end
klass = Class.new(ActiveRecord::Base) do
- self.table_name = 'people'
+ self.table_name = "people"
attribute :foo, test_type_class.new
end
@@ -682,7 +680,7 @@ class DirtyTest < ActiveRecord::TestCase
test "attribute_will_change! doesn't try to save non-persistable attributes" do
klass = Class.new(ActiveRecord::Base) do
- self.table_name = 'people'
+ self.table_name = "people"
attribute :non_persisted_attribute, :string
end
@@ -728,6 +726,89 @@ class DirtyTest < ActiveRecord::TestCase
assert person.changed?
end
+ test "saved_change_to_attribute? returns whether a change occurred in the last save" do
+ person = Person.create!(first_name: "Sean")
+
+ assert person.saved_change_to_first_name?
+ refute person.saved_change_to_gender?
+ assert person.saved_change_to_first_name?(from: nil, to: "Sean")
+ assert person.saved_change_to_first_name?(from: nil)
+ assert person.saved_change_to_first_name?(to: "Sean")
+ refute person.saved_change_to_first_name?(from: "Jim", to: "Sean")
+ refute person.saved_change_to_first_name?(from: "Jim")
+ refute person.saved_change_to_first_name?(to: "Jim")
+ end
+
+ test "saved_change_to_attribute returns the change that occurred in the last save" do
+ person = Person.create!(first_name: "Sean", gender: "M")
+
+ assert_equal [nil, "Sean"], person.saved_change_to_first_name
+ assert_equal [nil, "M"], person.saved_change_to_gender
+
+ person.update(first_name: "Jim")
+
+ assert_equal ["Sean", "Jim"], person.saved_change_to_first_name
+ assert_nil person.saved_change_to_gender
+ end
+
+ test "attribute_before_last_save returns the original value before saving" do
+ person = Person.create!(first_name: "Sean", gender: "M")
+
+ assert_nil person.first_name_before_last_save
+ assert_nil person.gender_before_last_save
+
+ person.first_name = "Jim"
+
+ assert_nil person.first_name_before_last_save
+ assert_nil person.gender_before_last_save
+
+ person.save
+
+ assert_equal "Sean", person.first_name_before_last_save
+ assert_equal "M", person.gender_before_last_save
+ end
+
+ test "saved_changes? returns whether the last call to save changed anything" do
+ person = Person.create!(first_name: "Sean")
+
+ assert person.saved_changes?
+
+ person.save
+
+ refute person.saved_changes?
+ end
+
+ test "saved_changes returns a hash of all the changes that occurred" do
+ person = Person.create!(first_name: "Sean", gender: "M")
+
+ assert_equal [nil, "Sean"], person.saved_changes[:first_name]
+ assert_equal [nil, "M"], person.saved_changes[:gender]
+ assert_equal %w(id first_name gender created_at updated_at).sort, person.saved_changes.keys.sort
+
+ travel(1.second) do
+ person.update(first_name: "Jim")
+ end
+
+ assert_equal ["Sean", "Jim"], person.saved_changes[:first_name]
+ assert_equal %w(first_name lock_version updated_at).sort, person.saved_changes.keys.sort
+ end
+
+ test "changed? in after callbacks returns true but is deprecated" do
+ klass = Class.new(ActiveRecord::Base) do
+ self.table_name = "people"
+
+ after_save do
+ ActiveSupport::Deprecation.silence do
+ raise "changed? should be true" unless changed?
+ end
+ raise "has_changes_to_save? should be false" if has_changes_to_save?
+ end
+ end
+
+ person = klass.create!(first_name: "Sean")
+ refute person.changed?
+ end
+
private
def with_partial_writes(klass, on = true)
old = klass.partial_writes?
diff --git a/activerecord/test/cases/dup_test.rb b/activerecord/test/cases/dup_test.rb
index 638cffe0e6..3821e0c949 100644
--- a/activerecord/test/cases/dup_test.rb
+++ b/activerecord/test/cases/dup_test.rb
@@ -1,6 +1,6 @@
require "cases/helper"
-require 'models/reply'
-require 'models/topic'
+require "models/reply"
+require "models/topic"
module ActiveRecord
class DupTest < ActiveRecord::TestCase
@@ -14,7 +14,7 @@ module ActiveRecord
topic = Topic.first
duped = topic.dup
- assert !duped.readonly?, 'should not be readonly'
+ assert !duped.readonly?, "should not be readonly"
end
def test_is_readonly
@@ -22,15 +22,15 @@ module ActiveRecord
topic.readonly!
duped = topic.dup
- assert duped.readonly?, 'should be readonly'
+ assert duped.readonly?, "should be readonly"
end
def test_dup_not_persisted
topic = Topic.first
duped = topic.dup
- assert !duped.persisted?, 'topic not persisted'
- assert duped.new_record?, 'topic is new'
+ assert !duped.persisted?, "topic not persisted"
+ assert duped.new_record?, "topic is new"
end
def test_dup_not_destroyed
@@ -49,9 +49,9 @@ module ActiveRecord
def test_dup_with_modified_attributes
topic = Topic.first
- topic.author_name = 'Aaron'
+ topic.author_name = "Aaron"
duped = topic.dup
- assert_equal 'Aaron', duped.author_name
+ assert_equal "Aaron", duped.author_name
end
def test_dup_with_changes
@@ -71,10 +71,10 @@ module ActiveRecord
def test_dup_topics_are_independent
topic = Topic.first
- topic.author_name = 'Aaron'
+ topic.author_name = "Aaron"
duped = topic.dup
- duped.author_name = 'meow'
+ duped.author_name = "meow"
assert_not_equal topic.changes, duped.changes
end
@@ -83,11 +83,11 @@ module ActiveRecord
topic = Topic.first
duped = topic.dup
- duped.author_name = 'meow'
- topic.author_name = 'Aaron'
+ duped.author_name = "meow"
+ topic.author_name = "Aaron"
- assert_equal 'Aaron', topic.author_name
- assert_equal 'meow', duped.author_name
+ assert_equal "Aaron", topic.author_name
+ assert_equal "meow", duped.author_name
end
def test_dup_timestamps_are_cleared
@@ -127,7 +127,7 @@ module ActiveRecord
assert duped.invalid?
topic.title = nil
- duped.title = 'Mathematics'
+ duped.title = "Mathematics"
assert topic.invalid?
assert duped.valid?
end
@@ -135,8 +135,8 @@ module ActiveRecord
def test_dup_with_default_scope
prev_default_scopes = Topic.default_scopes
- Topic.default_scopes = [proc { Topic.where(:approved => true) }]
- topic = Topic.new(:approved => false)
+ Topic.default_scopes = [proc { Topic.where(approved: true) }]
+ topic = Topic.new(approved: false)
assert !topic.dup.approved?, "should not be overridden by default scopes"
ensure
Topic.default_scopes = prev_default_scopes
@@ -144,7 +144,7 @@ module ActiveRecord
def test_dup_without_primary_key
klass = Class.new(ActiveRecord::Base) do
- self.table_name = 'parrots_pirates'
+ self.table_name = "parrots_pirates"
end
record = klass.create!
diff --git a/activerecord/test/cases/enum_test.rb b/activerecord/test/cases/enum_test.rb
index e781b60464..b7641fcf32 100644
--- a/activerecord/test/cases/enum_test.rb
+++ b/activerecord/test/cases/enum_test.rb
@@ -1,5 +1,5 @@
-require 'cases/helper'
-require 'models/book'
+require "cases/helper"
+require "models/book"
class EnumTest < ActiveRecord::TestCase
fixtures :books
@@ -18,6 +18,7 @@ class EnumTest < ActiveRecord::TestCase
assert @book.author_visibility_visible?
assert @book.illustrator_visibility_visible?
assert @book.with_medium_font_size?
+ assert @book.medium_to_read?
end
test "query state with strings" do
@@ -26,6 +27,7 @@ class EnumTest < ActiveRecord::TestCase
assert_equal "english", @book.language
assert_equal "visible", @book.author_visibility
assert_equal "visible", @book.illustrator_visibility
+ assert_equal "medium", @book.difficulty
end
test "find via scope" do
@@ -34,6 +36,7 @@ class EnumTest < ActiveRecord::TestCase
assert_equal @book, Book.in_english.first
assert_equal @book, Book.author_visibility_visible.first
assert_equal @book, Book.illustrator_visibility_visible.first
+ assert_equal @book, Book.medium_to_read.first
end
test "find via where with values" do
@@ -122,8 +125,8 @@ class EnumTest < ActiveRecord::TestCase
old_language = @book.language
@book.status = :proposed
@book.language = :spanish
- assert_equal [old_status, 'proposed'], @book.changes[:status]
- assert_equal [old_language, 'spanish'], @book.changes[:language]
+ assert_equal [old_status, "proposed"], @book.changes[:status]
+ assert_equal [old_language, "spanish"], @book.changes[:language]
end
test "enum attribute was" do
@@ -145,8 +148,8 @@ class EnumTest < ActiveRecord::TestCase
test "enum attribute changed to" do
@book.status = :proposed
@book.language = :french
- assert @book.attribute_changed?(:status, to: 'proposed')
- assert @book.attribute_changed?(:language, to: 'french')
+ assert @book.attribute_changed?(:status, to: "proposed")
+ assert @book.attribute_changed?(:language, to: "french")
end
test "enum attribute changed from" do
@@ -163,8 +166,8 @@ class EnumTest < ActiveRecord::TestCase
old_language = @book.language
@book.status = :proposed
@book.language = :french
- assert @book.attribute_changed?(:status, from: old_status, to: 'proposed')
- assert @book.attribute_changed?(:language, from: old_language, to: 'french')
+ assert @book.attribute_changed?(:status, from: old_status, to: "proposed")
+ assert @book.attribute_changed?(:language, from: old_language, to: "french")
end
test "enum didn't change" do
@@ -216,12 +219,12 @@ class EnumTest < ActiveRecord::TestCase
end
test "assign empty string value" do
- @book.status = ''
+ @book.status = ""
assert_nil @book.status
end
test "assign long empty string value" do
- @book.status = ' '
+ @book.status = " "
assert_nil @book.status
end
@@ -318,7 +321,7 @@ class EnumTest < ActiveRecord::TestCase
test "validate uniqueness" do
klass = Class.new(ActiveRecord::Base) do
- def self.name; 'Book'; end
+ def self.name; "Book"; end
enum status: [:proposed, :written]
validates_uniqueness_of :status
end
@@ -332,7 +335,7 @@ class EnumTest < ActiveRecord::TestCase
test "validate inclusion of value in array" do
klass = Class.new(ActiveRecord::Base) do
- def self.name; 'Book'; end
+ def self.name; "Book"; end
enum status: [:proposed, :written]
validates_inclusion_of :status, in: ["written"]
end
@@ -356,11 +359,11 @@ class EnumTest < ActiveRecord::TestCase
book1 = klass1.proposed.create!
book1.status = :written
- assert_equal ['proposed', 'written'], book1.status_change
+ assert_equal ["proposed", "written"], book1.status_change
book2 = klass2.drafted.create!
book2.status = :uploaded
- assert_equal ['drafted', 'uploaded'], book2.status_change
+ assert_equal ["drafted", "uploaded"], book2.status_change
end
test "enums are inheritable" do
@@ -372,11 +375,11 @@ class EnumTest < ActiveRecord::TestCase
book1 = subklass1.proposed.create!
book1.status = :written
- assert_equal ['proposed', 'written'], book1.status_change
+ assert_equal ["proposed", "written"], book1.status_change
book2 = subklass2.drafted.create!
book2.status = :uploaded
- assert_equal ['drafted', 'uploaded'], book2.status_change
+ assert_equal ["drafted", "uploaded"], book2.status_change
end
test "declare multiple enums at a time" do
@@ -393,6 +396,22 @@ class EnumTest < ActiveRecord::TestCase
assert book2.single?
end
+ test "enum with alias_attribute" do
+ klass = Class.new(ActiveRecord::Base) do
+ self.table_name = "books"
+ alias_attribute :aliased_status, :status
+ enum aliased_status: [:proposed, :written, :published]
+ end
+
+ book = klass.proposed.create!
+ assert book.proposed?
+ assert_equal "proposed", book.aliased_status
+
+ book = klass.find(book.id)
+ assert book.proposed?
+ assert_equal "proposed", book.aliased_status
+ end
+
test "query state by predicate with prefix" do
assert @book.author_visibility_visible?
assert_not @book.author_visibility_invisible?
@@ -406,6 +425,43 @@ class EnumTest < ActiveRecord::TestCase
assert_not @book.in_french?
end
+ test "query state by predicate with custom suffix" do
+ assert @book.medium_to_read?
+ assert_not @book.easy_to_read?
+ assert_not @book.hard_to_read?
+ end
+
+ test "enum methods with custom suffix defined" do
+ assert @book.class.respond_to?(:easy_to_read)
+ assert @book.class.respond_to?(:medium_to_read)
+ assert @book.class.respond_to?(:hard_to_read)
+
+ assert @book.respond_to?(:easy_to_read?)
+ assert @book.respond_to?(:medium_to_read?)
+ assert @book.respond_to?(:hard_to_read?)
+
+ assert @book.respond_to?(:easy_to_read!)
+ assert @book.respond_to?(:medium_to_read!)
+ assert @book.respond_to?(:hard_to_read!)
+ end
+
+ test "update enum attributes with custom suffix" do
+ @book.medium_to_read!
+ assert_not @book.easy_to_read?
+ assert @book.medium_to_read?
+ assert_not @book.hard_to_read?
+
+ @book.easy_to_read!
+ assert @book.easy_to_read?
+ assert_not @book.medium_to_read?
+ assert_not @book.hard_to_read?
+
+ @book.hard_to_read!
+ assert_not @book.easy_to_read?
+ assert_not @book.medium_to_read?
+ assert @book.hard_to_read?
+ end
+
test "uses default status when no status is provided in fixtures" do
book = books(:tlg)
assert book.proposed?, "expected fixture to default to proposed status"
@@ -423,6 +479,6 @@ class EnumTest < ActiveRecord::TestCase
end
test "data type of Enum type" do
- assert_equal :integer, Book.type_for_attribute('status').type
+ assert_equal :integer, Book.type_for_attribute("status").type
end
end
diff --git a/activerecord/test/cases/errors_test.rb b/activerecord/test/cases/errors_test.rb
index 0711a372f2..73feb831d0 100644
--- a/activerecord/test/cases/errors_test.rb
+++ b/activerecord/test/cases/errors_test.rb
@@ -5,7 +5,7 @@ class ErrorsTest < ActiveRecord::TestCase
base = ActiveRecord::ActiveRecordError
error_klasses = ObjectSpace.each_object(Class).select { |klass| klass < base }
- error_klasses.each do |error_klass|
+ (error_klasses - [ActiveRecord::AmbiguousSourceReflectionForThroughAssociation]).each do |error_klass|
begin
error_klass.new.inspect
rescue ArgumentError
diff --git a/activerecord/test/cases/explain_subscriber_test.rb b/activerecord/test/cases/explain_subscriber_test.rb
index 2dee8a26a5..ca87e04012 100644
--- a/activerecord/test/cases/explain_subscriber_test.rb
+++ b/activerecord/test/cases/explain_subscriber_test.rb
@@ -1,6 +1,6 @@
-require 'cases/helper'
-require 'active_record/explain_subscriber'
-require 'active_record/explain_registry'
+require "cases/helper"
+require "active_record/explain_subscriber"
+require "active_record/explain_registry"
if ActiveRecord::Base.connection.supports_explain?
class ExplainSubscriberTest < ActiveRecord::TestCase
@@ -25,31 +25,31 @@ if ActiveRecord::Base.connection.supports_explain?
def test_collects_nothing_if_collect_is_false
ActiveRecord::ExplainRegistry.collect = false
- SUBSCRIBER.finish(nil, nil, name: 'SQL', sql: 'select 1 from users', binds: [1, 2])
+ SUBSCRIBER.finish(nil, nil, name: "SQL", sql: "select 1 from users", binds: [1, 2])
assert queries.empty?
end
def test_collects_pairs_of_queries_and_binds
- sql = 'select 1 from users'
+ sql = "select 1 from users"
binds = [1, 2]
- SUBSCRIBER.finish(nil, nil, name: 'SQL', sql: sql, binds: binds)
+ SUBSCRIBER.finish(nil, nil, name: "SQL", sql: sql, binds: binds)
assert_equal 1, queries.size
assert_equal sql, queries[0][0]
assert_equal binds, queries[0][1]
end
def test_collects_nothing_if_the_statement_is_not_whitelisted
- SUBSCRIBER.finish(nil, nil, name: 'SQL', sql: 'SHOW max_identifier_length')
+ SUBSCRIBER.finish(nil, nil, name: "SQL", sql: "SHOW max_identifier_length")
assert queries.empty?
end
def test_collects_nothing_if_the_statement_is_only_partially_matched
- SUBSCRIBER.finish(nil, nil, name: 'SQL', sql: 'select_db yo_mama')
+ SUBSCRIBER.finish(nil, nil, name: "SQL", sql: "select_db yo_mama")
assert queries.empty?
end
def test_collects_cte_queries
- SUBSCRIBER.finish(nil, nil, name: 'SQL', sql: 'with s as (values(3)) select 1 from s')
+ SUBSCRIBER.finish(nil, nil, name: "SQL", sql: "with s as (values(3)) select 1 from s")
assert_equal 1, queries.size
end
diff --git a/activerecord/test/cases/explain_test.rb b/activerecord/test/cases/explain_test.rb
index 64dfd86ce2..86fe90ae51 100644
--- a/activerecord/test/cases/explain_test.rb
+++ b/activerecord/test/cases/explain_test.rb
@@ -1,6 +1,6 @@
-require 'cases/helper'
-require 'models/car'
-require 'active_support/core_ext/string/strip'
+require "cases/helper"
+require "models/car"
+require "active_support/core_ext/string/strip"
if ActiveRecord::Base.connection.supports_explain?
class ExplainTest < ActiveRecord::TestCase
@@ -15,13 +15,13 @@ if ActiveRecord::Base.connection.supports_explain?
end
def test_relation_explain
- message = Car.where(:name => 'honda').explain
+ message = Car.where(name: "honda").explain
assert_match(/^EXPLAIN for:/, message)
end
def test_collecting_queries_for_explain
queries = ActiveRecord::Base.collecting_queries_for_explain do
- Car.where(:name => 'honda').to_a
+ Car.where(name: "honda").to_a
end
sql, binds = queries[0]
@@ -30,7 +30,7 @@ if ActiveRecord::Base.connection.supports_explain?
assert_equal 1, binds.length
assert_equal "honda", binds.last.value
else
- assert_match 'honda', sql
+ assert_match "honda", sql
end
end
@@ -40,17 +40,14 @@ if ActiveRecord::Base.connection.supports_explain?
queries = sqls.zip(binds)
stub_explain_for_query_plans do
- expected = sqls.map {|sql| "EXPLAIN for: #{sql}\nquery plan #{sql}"}.join("\n")
+ expected = sqls.map { |sql| "EXPLAIN for: #{sql}\nquery plan #{sql}" }.join("\n")
assert_equal expected, base.exec_explain(queries)
end
end
def test_exec_explain_with_binds
- object = Struct.new(:name)
- cols = [object.new('wadus'), object.new('chaflan')]
-
sqls = %w(foo bar)
- binds = [[[cols[0], 1]], [[cols[1], 2]]]
+ binds = [[bind_param("wadus", 1)], [bind_param("chaflan", 2)]]
queries = sqls.zip(binds)
stub_explain_for_query_plans(["query plan foo\n", "query plan bar\n"]) do
@@ -68,20 +65,23 @@ if ActiveRecord::Base.connection.supports_explain?
def test_unsupported_connection_adapter
connection.stub(:supports_explain?, false) do
assert_not_called(base.logger, :warn) do
- Car.where(:name => 'honda').to_a
+ Car.where(name: "honda").to_a
end
end
end
private
- def stub_explain_for_query_plans(query_plans = ['query plan foo', 'query plan bar'])
+ def stub_explain_for_query_plans(query_plans = ["query plan foo", "query plan bar"])
explain_called = 0
- connection.stub(:explain, proc{ explain_called += 1; query_plans[explain_called - 1] }) do
+ connection.stub(:explain, proc { explain_called += 1; query_plans[explain_called - 1] }) do
yield
end
end
+ def bind_param(name, value)
+ ActiveRecord::Relation::QueryAttribute.new(name, value, ActiveRecord::Type::Value.new)
+ end
end
end
diff --git a/activerecord/test/cases/finder_respond_to_test.rb b/activerecord/test/cases/finder_respond_to_test.rb
index 6ab2657c44..3eaa993d45 100644
--- a/activerecord/test/cases/finder_respond_to_test.rb
+++ b/activerecord/test/cases/finder_respond_to_test.rb
@@ -1,8 +1,7 @@
require "cases/helper"
-require 'models/topic'
+require "models/topic"
class FinderRespondToTest < ActiveRecord::TestCase
-
fixtures :topics
def test_should_preserve_normal_respond_to_behaviour_on_base
@@ -11,7 +10,7 @@ class FinderRespondToTest < ActiveRecord::TestCase
end
def test_should_preserve_normal_respond_to_behaviour_and_respond_to_newly_added_method
- class << Topic; self; end.send(:define_method, :method_added_for_finder_respond_to_test) { }
+ class << Topic; self; end.send(:define_method, :method_added_for_finder_respond_to_test) {}
assert_respond_to Topic, :method_added_for_finder_respond_to_test
ensure
class << Topic; self; end.send(:remove_method, :method_added_for_finder_respond_to_test)
@@ -54,7 +53,7 @@ class FinderRespondToTest < ActiveRecord::TestCase
private
- def ensure_topic_method_is_not_cached(method_id)
- class << Topic; self; end.send(:remove_method, method_id) if Topic.public_methods.include? method_id
- end
+ def ensure_topic_method_is_not_cached(method_id)
+ class << Topic; self; end.send(:remove_method, method_id) if Topic.public_methods.include? method_id
+ end
end
diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb
index 6eaaa30cd0..f1459ae125 100644
--- a/activerecord/test/cases/finder_test.rb
+++ b/activerecord/test/cases/finder_test.rb
@@ -1,35 +1,35 @@
require "cases/helper"
-require 'models/post'
-require 'models/author'
-require 'models/categorization'
-require 'models/comment'
-require 'models/company'
-require 'models/tagging'
-require 'models/topic'
-require 'models/reply'
-require 'models/entrant'
-require 'models/project'
-require 'models/developer'
-require 'models/computer'
-require 'models/customer'
-require 'models/toy'
-require 'models/matey'
-require 'models/dog'
-require 'models/car'
-require 'models/tyre'
+require "models/post"
+require "models/author"
+require "models/categorization"
+require "models/comment"
+require "models/company"
+require "models/tagging"
+require "models/topic"
+require "models/reply"
+require "models/entrant"
+require "models/project"
+require "models/developer"
+require "models/computer"
+require "models/customer"
+require "models/toy"
+require "models/matey"
+require "models/dog"
+require "models/car"
+require "models/tyre"
class FinderTest < ActiveRecord::TestCase
fixtures :companies, :topics, :entrants, :developers, :developers_projects, :posts, :comments, :accounts, :authors, :author_addresses, :customers, :categories, :categorizations, :cars
def test_find_by_id_with_hash
- assert_raises(ActiveRecord::StatementInvalid) do
- Post.find_by_id(:limit => 1)
+ assert_nothing_raised do
+ Post.find_by_id(limit: 1)
end
end
def test_find_by_title_and_id_with_hash
- assert_raises(ActiveRecord::StatementInvalid) do
- Post.find_by_title_and_id('foo', :limit => 1)
+ assert_nothing_raised do
+ Post.find_by_title_and_id("foo", limit: 1)
end
end
@@ -49,72 +49,72 @@ class FinderTest < ActiveRecord::TestCase
end
def test_find_with_ids_returning_ordered
- records = Topic.find([4,2,5])
- assert_equal 'The Fourth Topic of the day', records[0].title
- assert_equal 'The Second Topic of the day', records[1].title
- assert_equal 'The Fifth Topic of the day', records[2].title
+ records = Topic.find([4, 2, 5])
+ assert_equal "The Fourth Topic of the day", records[0].title
+ assert_equal "The Second Topic of the day", records[1].title
+ assert_equal "The Fifth Topic of the day", records[2].title
- records = Topic.find(4,2,5)
- assert_equal 'The Fourth Topic of the day', records[0].title
- assert_equal 'The Second Topic of the day', records[1].title
- assert_equal 'The Fifth Topic of the day', records[2].title
+ records = Topic.find(4, 2, 5)
+ assert_equal "The Fourth Topic of the day", records[0].title
+ assert_equal "The Second Topic of the day", records[1].title
+ assert_equal "The Fifth Topic of the day", records[2].title
- records = Topic.find(['4','2','5'])
- assert_equal 'The Fourth Topic of the day', records[0].title
- assert_equal 'The Second Topic of the day', records[1].title
- assert_equal 'The Fifth Topic of the day', records[2].title
+ records = Topic.find(["4", "2", "5"])
+ assert_equal "The Fourth Topic of the day", records[0].title
+ assert_equal "The Second Topic of the day", records[1].title
+ assert_equal "The Fifth Topic of the day", records[2].title
- records = Topic.find('4','2','5')
- assert_equal 'The Fourth Topic of the day', records[0].title
- assert_equal 'The Second Topic of the day', records[1].title
- assert_equal 'The Fifth Topic of the day', records[2].title
+ records = Topic.find("4", "2", "5")
+ assert_equal "The Fourth Topic of the day", records[0].title
+ assert_equal "The Second Topic of the day", records[1].title
+ assert_equal "The Fifth Topic of the day", records[2].title
end
def test_find_with_ids_and_order_clause
# The order clause takes precedence over the informed ids
- records = Topic.order(:author_name).find([5,3,1])
- assert_equal 'The Third Topic of the day', records[0].title
- assert_equal 'The First Topic', records[1].title
- assert_equal 'The Fifth Topic of the day', records[2].title
+ records = Topic.order(:author_name).find([5, 3, 1])
+ assert_equal "The Third Topic of the day", records[0].title
+ assert_equal "The First Topic", records[1].title
+ assert_equal "The Fifth Topic of the day", records[2].title
- records = Topic.order(:id).find([5,3,1])
- assert_equal 'The First Topic', records[0].title
- assert_equal 'The Third Topic of the day', records[1].title
- assert_equal 'The Fifth Topic of the day', records[2].title
+ records = Topic.order(:id).find([5, 3, 1])
+ assert_equal "The First Topic", records[0].title
+ assert_equal "The Third Topic of the day", records[1].title
+ assert_equal "The Fifth Topic of the day", records[2].title
end
def test_find_with_ids_with_limit_and_order_clause
# The order clause takes precedence over the informed ids
- records = Topic.limit(2).order(:id).find([5,3,1])
+ records = Topic.limit(2).order(:id).find([5, 3, 1])
assert_equal 2, records.size
- assert_equal 'The First Topic', records[0].title
- assert_equal 'The Third Topic of the day', records[1].title
+ assert_equal "The First Topic", records[0].title
+ assert_equal "The Third Topic of the day", records[1].title
end
def test_find_with_ids_and_limit
- records = Topic.limit(3).find([3,2,5,1,4])
+ records = Topic.limit(3).find([3, 2, 5, 1, 4])
assert_equal 3, records.size
- assert_equal 'The Third Topic of the day', records[0].title
- assert_equal 'The Second Topic of the day', records[1].title
- assert_equal 'The Fifth Topic of the day', records[2].title
+ assert_equal "The Third Topic of the day", records[0].title
+ assert_equal "The Second Topic of the day", records[1].title
+ assert_equal "The Fifth Topic of the day", records[2].title
end
def test_find_with_ids_where_and_limit
# Please note that Topic 1 is the only not approved so
# if it were among the first 3 it would raise an ActiveRecord::RecordNotFound
- records = Topic.where(approved: true).limit(3).find([3,2,5,1,4])
+ records = Topic.where(approved: true).limit(3).find([3, 2, 5, 1, 4])
assert_equal 3, records.size
- assert_equal 'The Third Topic of the day', records[0].title
- assert_equal 'The Second Topic of the day', records[1].title
- assert_equal 'The Fifth Topic of the day', records[2].title
+ assert_equal "The Third Topic of the day", records[0].title
+ assert_equal "The Second Topic of the day", records[1].title
+ assert_equal "The Fifth Topic of the day", records[2].title
end
def test_find_with_ids_and_offset
- records = Topic.offset(2).find([3,2,5,1,4])
+ records = Topic.offset(2).find([3, 2, 5, 1, 4])
assert_equal 3, records.size
- assert_equal 'The Fifth Topic of the day', records[0].title
- assert_equal 'The First Topic', records[1].title
- assert_equal 'The Fourth Topic of the day', records[2].title
+ assert_equal "The Fifth Topic of the day", records[0].title
+ assert_equal "The First Topic", records[1].title
+ assert_equal "The Fourth Topic of the day", records[2].title
end
def test_find_passing_active_record_object_is_deprecated
@@ -127,7 +127,7 @@ class FinderTest < ActiveRecord::TestCase
gc_disabled = GC.disable
Post.where("author_id" => nil) # warm up
x = Symbol.all_symbols.count
- Post.where("title" => {"xxxqqqq" => "bar"})
+ Post.where("title" => { "xxxqqqq" => "bar" })
assert_equal x, Symbol.all_symbols.count
ensure
GC.enable if gc_disabled == false
@@ -136,7 +136,7 @@ class FinderTest < ActiveRecord::TestCase
# find should handle strings that come from URLs
# (example: Category.find(params[:id]))
def test_find_with_string
- assert_equal(Topic.find(1).title,Topic.find("1").title)
+ assert_equal(Topic.find(1).title, Topic.find("1").title)
end
def test_exists
@@ -144,22 +144,22 @@ class FinderTest < ActiveRecord::TestCase
assert_equal true, Topic.exists?("1")
assert_equal true, Topic.exists?(title: "The First Topic")
assert_equal true, Topic.exists?(heading: "The First Topic")
- assert_equal true, Topic.exists?(:author_name => "Mary", :approved => true)
+ assert_equal true, Topic.exists?(author_name: "Mary", approved: true)
assert_equal true, Topic.exists?(["parent_id = ?", 1])
assert_equal true, Topic.exists?(id: [1, 9999])
assert_equal false, Topic.exists?(45)
assert_equal false, Topic.exists?(Topic.new.id)
- assert_raise(NoMethodError) { Topic.exists?([1,2]) }
+ assert_raise(NoMethodError) { Topic.exists?([1, 2]) }
end
def test_exists_with_polymorphic_relation
- post = Post.create!(title: 'Post', body: 'default', taggings: [Tagging.new(comment: 'tagging comment')])
- relation = Post.tagged_with_comment('tagging comment')
+ post = Post.create!(title: "Post", body: "default", taggings: [Tagging.new(comment: "tagging comment")])
+ relation = Post.tagged_with_comment("tagging comment")
- assert_equal true, relation.exists?(title: ['Post'])
- assert_equal true, relation.exists?(['title LIKE ?', 'Post%'])
+ assert_equal true, relation.exists?(title: ["Post"])
+ assert_equal true, relation.exists?(["title LIKE ?", "Post%"])
assert_equal true, relation.exists?
assert_equal true, relation.exists?(post.id)
assert_equal true, relation.exists?(post.id.to_s)
@@ -175,7 +175,7 @@ class FinderTest < ActiveRecord::TestCase
def test_exists_returns_false_when_parameter_has_invalid_type
assert_equal false, Topic.exists?("foo")
- assert_equal false, Topic.exists?(("9"*53).to_i) # number that's bigger than int
+ assert_equal false, Topic.exists?(("9" * 53).to_i) # number that's bigger than int
end
def test_exists_does_not_select_columns_without_alias
@@ -209,7 +209,7 @@ class FinderTest < ActiveRecord::TestCase
def test_exists_with_includes_limit_and_empty_result
assert_equal false, Topic.includes(:replies).limit(0).exists?
- assert_equal false, Topic.includes(:replies).limit(1).where('0 = 1').exists?
+ assert_equal false, Topic.includes(:replies).limit(1).where("0 = 1").exists?
end
def test_exists_with_distinct_association_includes_and_limit
@@ -220,8 +220,8 @@ class FinderTest < ActiveRecord::TestCase
def test_exists_with_distinct_association_includes_limit_and_order
author = Author.first
- assert_equal false, author.unique_categorized_posts.includes(:special_comments).order('comments.tags_count DESC').limit(0).exists?
- assert_equal true, author.unique_categorized_posts.includes(:special_comments).order('comments.tags_count DESC').limit(1).exists?
+ assert_equal false, author.unique_categorized_posts.includes(:special_comments).order("comments.tags_count DESC").limit(0).exists?
+ assert_equal true, author.unique_categorized_posts.includes(:special_comments).order("comments.tags_count DESC").limit(1).exists?
end
def test_exists_with_empty_table_and_no_args_given
@@ -231,17 +231,14 @@ class FinderTest < ActiveRecord::TestCase
def test_exists_with_aggregate_having_three_mappings
existing_address = customers(:david).address
- assert_equal true, Customer.exists?(:address => existing_address)
+ assert_equal true, Customer.exists?(address: existing_address)
end
def test_exists_with_aggregate_having_three_mappings_with_one_difference
existing_address = customers(:david).address
- assert_equal false, Customer.exists?(:address =>
- Address.new(existing_address.street, existing_address.city, existing_address.country + "1"))
- assert_equal false, Customer.exists?(:address =>
- Address.new(existing_address.street, existing_address.city + "1", existing_address.country))
- assert_equal false, Customer.exists?(:address =>
- Address.new(existing_address.street + "1", existing_address.city, existing_address.country))
+ assert_equal false, Customer.exists?(address: Address.new(existing_address.street, existing_address.city, existing_address.country + "1"))
+ assert_equal false, Customer.exists?(address: Address.new(existing_address.street, existing_address.city + "1", existing_address.country))
+ assert_equal false, Customer.exists?(address: Address.new(existing_address.street + "1", existing_address.city, existing_address.country))
end
def test_exists_does_not_instantiate_records
@@ -261,10 +258,10 @@ class FinderTest < ActiveRecord::TestCase
end
def test_find_by_ids_with_limit_and_offset
- assert_equal 2, Entrant.limit(2).find([1,3,2]).size
- entrants = Entrant.limit(3).offset(2).find([1,3,2])
+ assert_equal 2, Entrant.limit(2).find([1, 3, 2]).size
+ entrants = Entrant.limit(3).offset(2).find([1, 3, 2])
assert_equal 1, entrants.size
- assert_equal 'Ruby Guru', entrants.first.name
+ assert_equal "Ruby Guru", entrants.first.name
# Also test an edge case: If you have 11 results, and you set a
# limit of 3 and offset of 9, then you should find that there
@@ -272,29 +269,29 @@ class FinderTest < ActiveRecord::TestCase
devs = Developer.all
last_devs = Developer.limit(3).offset(9).find devs.map(&:id)
assert_equal 2, last_devs.size
- assert_equal 'fixture_10', last_devs[0].name
- assert_equal 'Jamis', last_devs[1].name
+ assert_equal "fixture_10", last_devs[0].name
+ assert_equal "Jamis", last_devs[1].name
end
def test_find_with_large_number
- assert_raises(ActiveRecord::RecordNotFound) { Topic.find('9999999999999999999999999999999') }
+ assert_raises(ActiveRecord::RecordNotFound) { Topic.find("9999999999999999999999999999999") }
end
def test_find_by_with_large_number
- assert_nil Topic.find_by(id: '9999999999999999999999999999999')
+ assert_nil Topic.find_by(id: "9999999999999999999999999999999")
end
def test_find_by_id_with_large_number
- assert_nil Topic.find_by_id('9999999999999999999999999999999')
+ assert_nil Topic.find_by_id("9999999999999999999999999999999")
end
def test_find_on_relation_with_large_number
- assert_nil Topic.where('1=1').find_by(id: 9999999999999999999999999999999)
+ assert_nil Topic.where("1=1").find_by(id: 9999999999999999999999999999999)
end
def test_find_by_bang_on_relation_with_large_number
assert_raises(ActiveRecord::RecordNotFound) do
- Topic.where('1=1').find_by!(id: 9999999999999999999999999999999)
+ Topic.where("1=1").find_by!(id: 9999999999999999999999999999999)
end
end
@@ -311,7 +308,7 @@ class FinderTest < ActiveRecord::TestCase
end
def test_find_with_group_and_sanitized_having_method
- developers = Developer.group(:salary).having("sum(salary) > ?", 10000).select('salary').to_a
+ developers = Developer.group(:salary).having("sum(salary) > ?", 10000).select("salary").to_a
assert_equal 3, developers.size
assert_equal 3, developers.map(&:salary).uniq.size
assert developers.all? { |developer| developer.salary > 10000 }
@@ -491,12 +488,12 @@ class FinderTest < ActiveRecord::TestCase
assert_equal topics(:fourth), Topic.offset(1).second_to_last
assert_equal topics(:fourth), Topic.offset(2).second_to_last
assert_equal topics(:fourth), Topic.offset(3).second_to_last
- assert_equal nil, Topic.offset(4).second_to_last
- assert_equal nil, Topic.offset(5).second_to_last
+ assert_nil Topic.offset(4).second_to_last
+ assert_nil Topic.offset(5).second_to_last
#test with limit
- # assert_equal nil, Topic.limit(1).second # TODO: currently failing
- assert_equal nil, Topic.limit(1).second_to_last
+ # assert_nil Topic.limit(1).second # TODO: currently failing
+ assert_nil Topic.limit(1).second_to_last
end
def test_second_to_last_have_primary_key_order_by_default
@@ -519,15 +516,15 @@ class FinderTest < ActiveRecord::TestCase
# test with offset
assert_equal topics(:third), Topic.offset(1).third_to_last
assert_equal topics(:third), Topic.offset(2).third_to_last
- assert_equal nil, Topic.offset(3).third_to_last
- assert_equal nil, Topic.offset(4).third_to_last
- assert_equal nil, Topic.offset(5).third_to_last
+ assert_nil Topic.offset(3).third_to_last
+ assert_nil Topic.offset(4).third_to_last
+ assert_nil Topic.offset(5).third_to_last
# test with limit
- # assert_equal nil, Topic.limit(1).third # TODO: currently failing
- assert_equal nil, Topic.limit(1).third_to_last
- # assert_equal nil, Topic.limit(2).third # TODO: currently failing
- assert_equal nil, Topic.limit(2).third_to_last
+ # assert_nil Topic.limit(1).third # TODO: currently failing
+ assert_nil Topic.limit(1).third_to_last
+ # assert_nil Topic.limit(2).third # TODO: currently failing
+ assert_nil Topic.limit(2).third_to_last
end
def test_third_to_last_have_primary_key_order_by_default
@@ -565,9 +562,9 @@ class FinderTest < ActiveRecord::TestCase
end
def test_take_and_first_and_last_with_integer_should_use_sql_limit
- assert_sql(/LIMIT|ROWNUM <=/) { Topic.take(3).entries }
- assert_sql(/LIMIT|ROWNUM <=/) { Topic.first(2).entries }
- assert_sql(/LIMIT|ROWNUM <=/) { Topic.last(5).entries }
+ assert_sql(/LIMIT|ROWNUM <=|FETCH FIRST/) { Topic.take(3).entries }
+ assert_sql(/LIMIT|ROWNUM <=|FETCH FIRST/) { Topic.first(2).entries }
+ assert_sql(/LIMIT|ROWNUM <=|FETCH FIRST/) { Topic.last(5).entries }
end
def test_last_with_integer_and_order_should_keep_the_order
@@ -587,7 +584,7 @@ class FinderTest < ActiveRecord::TestCase
end
def test_last_on_loaded_relation_should_not_use_sql
- relation = Topic.limit(10).load
+ relation = Topic.limit(10).load
assert_no_queries do
relation.last
relation.last(2)
@@ -595,13 +592,13 @@ class FinderTest < ActiveRecord::TestCase
end
def test_last_with_irreversible_order
- assert_deprecated do
+ assert_raises(ActiveRecord::IrreversibleOrderError) do
Topic.order("coalesce(author_name, title)").last
end
end
def test_last_on_relation_with_limit_and_offset
- post = posts('sti_comments')
+ post = posts("sti_comments")
comments = post.comments.order(id: :asc)
assert_equal comments.limit(2).to_a.last, comments.limit(2).last
@@ -630,8 +627,8 @@ class FinderTest < ActiveRecord::TestCase
def test_find_only_some_columns
topic = Topic.select("author_name").find(1)
- assert_raise(ActiveModel::MissingAttributeError) {topic.title}
- assert_raise(ActiveModel::MissingAttributeError) {topic.title?}
+ assert_raise(ActiveModel::MissingAttributeError) { topic.title }
+ assert_raise(ActiveModel::MissingAttributeError) { topic.title? }
assert_nil topic.read_attribute("title")
assert_equal "David", topic.author_name
assert !topic.attribute_present?("title")
@@ -651,8 +648,8 @@ class FinderTest < ActiveRecord::TestCase
end
def test_find_on_hash_conditions_with_qualified_attribute_dot_notation_string
- assert Topic.where('topics.approved' => false).find(1)
- assert_raise(ActiveRecord::RecordNotFound) { Topic.where('topics.approved' => true).find(1) }
+ assert Topic.where("topics.approved" => false).find(1)
+ assert_raise(ActiveRecord::RecordNotFound) { Topic.where("topics.approved" => true).find(1) }
end
def test_find_on_hash_conditions_with_qualified_attribute_dot_notation_symbol
@@ -666,28 +663,28 @@ class FinderTest < ActiveRecord::TestCase
end
def test_find_on_combined_explicit_and_hashed_table_names
- assert Topic.where('topics.approved' => false, topics: { author_name: "David" }).find(1)
- assert_raise(ActiveRecord::RecordNotFound) { Topic.where('topics.approved' => true, topics: { author_name: "David" }).find(1) }
- assert_raise(ActiveRecord::RecordNotFound) { Topic.where('topics.approved' => false, topics: { author_name: "Melanie" }).find(1) }
+ assert Topic.where("topics.approved" => false, topics: { author_name: "David" }).find(1)
+ assert_raise(ActiveRecord::RecordNotFound) { Topic.where("topics.approved" => true, topics: { author_name: "David" }).find(1) }
+ assert_raise(ActiveRecord::RecordNotFound) { Topic.where("topics.approved" => false, topics: { author_name: "Melanie" }).find(1) }
end
def test_find_with_hash_conditions_on_joined_table
- firms = Firm.joins(:account).where(:accounts => { :credit_limit => 50 })
+ firms = Firm.joins(:account).where(accounts: { credit_limit: 50 })
assert_equal 1, firms.size
assert_equal companies(:first_firm), firms.first
end
def test_find_with_hash_conditions_on_joined_table_and_with_range
- firms = DependentFirm.joins(:account).where(name: 'RailsCore', accounts: { credit_limit: 55..60 })
+ firms = DependentFirm.joins(:account).where(name: "RailsCore", accounts: { credit_limit: 55..60 })
assert_equal 1, firms.size
assert_equal companies(:rails_core), firms.first
end
def test_find_on_hash_conditions_with_explicit_table_name_and_aggregate
david = customers(:david)
- assert Customer.where('customers.name' => david.name, :address => david.address).find(david.id)
+ assert Customer.where("customers.name" => david.name, :address => david.address).find(david.id)
assert_raise(ActiveRecord::RecordNotFound) {
- Customer.where('customers.name' => david.name + "1", :address => david.address).find(david.id)
+ Customer.where("customers.name" => david.name + "1", :address => david.address).find(david.id)
}
end
@@ -696,27 +693,27 @@ class FinderTest < ActiveRecord::TestCase
end
def test_find_on_hash_conditions_with_range
- assert_equal [1,2], Topic.where(id: 1..2).to_a.map(&:id).sort
+ assert_equal [1, 2], Topic.where(id: 1..2).to_a.map(&:id).sort
assert_raise(ActiveRecord::RecordNotFound) { Topic.where(id: 2..3).find(1) }
end
def test_find_on_hash_conditions_with_end_exclusive_range
- assert_equal [1,2,3], Topic.where(id: 1..3).to_a.map(&:id).sort
- assert_equal [1,2], Topic.where(id: 1...3).to_a.map(&:id).sort
+ assert_equal [1, 2, 3], Topic.where(id: 1..3).to_a.map(&:id).sort
+ assert_equal [1, 2], Topic.where(id: 1...3).to_a.map(&:id).sort
assert_raise(ActiveRecord::RecordNotFound) { Topic.where(id: 2...3).find(3) }
end
def test_find_on_hash_conditions_with_multiple_ranges
- assert_equal [1,2,3], Comment.where(id: 1..3, post_id: 1..2).to_a.map(&:id).sort
+ assert_equal [1, 2, 3], Comment.where(id: 1..3, post_id: 1..2).to_a.map(&:id).sort
assert_equal [1], Comment.where(id: 1..1, post_id: 1..10).to_a.map(&:id).sort
end
def test_find_on_hash_conditions_with_array_of_integers_and_ranges
- assert_equal [1,2,3,5,6,7,8,9], Comment.where(id: [1..2, 3, 5, 6..8, 9]).to_a.map(&:id).sort
+ assert_equal [1, 2, 3, 5, 6, 7, 8, 9], Comment.where(id: [1..2, 3, 5, 6..8, 9]).to_a.map(&:id).sort
end
def test_find_on_hash_conditions_with_array_of_ranges
- assert_equal [1,2,6,7,8], Comment.where(id: [1..2, 6..8]).to_a.map(&:id).sort
+ assert_equal [1, 2, 6, 7, 8], Comment.where(id: [1..2, 6..8]).to_a.map(&:id).sort
end
def test_find_on_multiple_hash_conditions
@@ -758,9 +755,9 @@ class FinderTest < ActiveRecord::TestCase
end
def test_hash_condition_find_with_array
- p1, p2 = Post.limit(2).order('id asc').to_a
- assert_equal [p1, p2], Post.where(id: [p1, p2]).order('id asc').to_a
- assert_equal [p1, p2], Post.where(id: [p1, p2.id]).order('id asc').to_a
+ p1, p2 = Post.limit(2).order("id asc").to_a
+ assert_equal [p1, p2], Post.where(id: [p1, p2]).order("id asc").to_a
+ assert_equal [p1, p2], Post.where(id: [p1, p2.id]).order("id asc").to_a
end
def test_hash_condition_find_with_nil
@@ -772,56 +769,56 @@ class FinderTest < ActiveRecord::TestCase
def test_hash_condition_find_with_aggregate_having_one_mapping
balance = customers(:david).balance
assert_kind_of Money, balance
- found_customer = Customer.where(:balance => balance).first
+ found_customer = Customer.where(balance: balance).first
assert_equal customers(:david), found_customer
end
def test_hash_condition_find_with_aggregate_attribute_having_same_name_as_field_and_key_value_being_aggregate
gps_location = customers(:david).gps_location
assert_kind_of GpsLocation, gps_location
- found_customer = Customer.where(:gps_location => gps_location).first
+ found_customer = Customer.where(gps_location: gps_location).first
assert_equal customers(:david), found_customer
end
def test_hash_condition_find_with_aggregate_having_one_mapping_and_key_value_being_attribute_value
balance = customers(:david).balance
assert_kind_of Money, balance
- found_customer = Customer.where(:balance => balance.amount).first
+ found_customer = Customer.where(balance: balance.amount).first
assert_equal customers(:david), found_customer
end
def test_hash_condition_find_with_aggregate_attribute_having_same_name_as_field_and_key_value_being_attribute_value
gps_location = customers(:david).gps_location
assert_kind_of GpsLocation, gps_location
- found_customer = Customer.where(:gps_location => gps_location.gps_location).first
+ found_customer = Customer.where(gps_location: gps_location.gps_location).first
assert_equal customers(:david), found_customer
end
def test_hash_condition_find_with_aggregate_having_three_mappings
address = customers(:david).address
assert_kind_of Address, address
- found_customer = Customer.where(:address => address).first
+ found_customer = Customer.where(address: address).first
assert_equal customers(:david), found_customer
end
def test_hash_condition_find_with_one_condition_being_aggregate_and_another_not
address = customers(:david).address
assert_kind_of Address, address
- found_customer = Customer.where(:address => address, :name => customers(:david).name).first
+ found_customer = Customer.where(address: address, name: customers(:david).name).first
assert_equal customers(:david), found_customer
end
def test_condition_utc_time_interpolation_with_default_timezone_local
- with_env_tz 'America/New_York' do
+ with_env_tz "America/New_York" do
with_timezone_config default: :local do
topic = Topic.first
- assert_equal topic, Topic.where(['written_on = ?', topic.written_on.getutc]).first
+ assert_equal topic, Topic.where(["written_on = ?", topic.written_on.getutc]).first
end
end
end
def test_hash_condition_utc_time_interpolation_with_default_timezone_local
- with_env_tz 'America/New_York' do
+ with_env_tz "America/New_York" do
with_timezone_config default: :local do
topic = Topic.first
assert_equal topic, Topic.where(written_on: topic.written_on.getutc).first
@@ -830,16 +827,16 @@ class FinderTest < ActiveRecord::TestCase
end
def test_condition_local_time_interpolation_with_default_timezone_utc
- with_env_tz 'America/New_York' do
+ with_env_tz "America/New_York" do
with_timezone_config default: :utc do
topic = Topic.first
- assert_equal topic, Topic.where(['written_on = ?', topic.written_on.getlocal]).first
+ assert_equal topic, Topic.where(["written_on = ?", topic.written_on.getlocal]).first
end
end
end
def test_hash_condition_local_time_interpolation_with_default_timezone_utc
- with_env_tz 'America/New_York' do
+ with_env_tz "America/New_York" do
with_timezone_config default: :utc do
topic = Topic.first
assert_equal topic, Topic.where(written_on: topic.written_on.getlocal).first
@@ -856,7 +853,7 @@ class FinderTest < ActiveRecord::TestCase
Company.where(["id=? AND name = ?", 2]).first
}
assert_raise(ActiveRecord::PreparedStatementInvalid) {
- Company.where(["id=?", 2, 3, 4]).first
+ Company.where(["id=?", 2, 3, 4]).first
}
end
@@ -867,7 +864,7 @@ class FinderTest < ActiveRecord::TestCase
def test_named_bind_variables_with_quotes
Company.create("name" => "37signals' go'es agains")
- assert Company.where(["name = :name", {name: "37signals' go'es agains"}]).first
+ assert Company.where(["name = :name", { name: "37signals' go'es agains" }]).first
end
def test_named_bind_variables
@@ -877,11 +874,6 @@ class FinderTest < ActiveRecord::TestCase
assert_kind_of Time, Topic.where(["id = :id", { id: 1 }]).first.written_on
end
- def test_string_sanitation
- assert_not_equal "'something ' 1=1'", ActiveRecord::Base.sanitize("something ' 1=1")
- assert_equal "'something; select table'", ActiveRecord::Base.sanitize("something; select table")
- end
-
def test_count_by_sql
assert_equal(0, Entrant.count_by_sql("SELECT COUNT(*) FROM entrants WHERE id > 3"))
assert_equal(1, Entrant.count_by_sql(["SELECT COUNT(*) FROM entrants WHERE id > ?", 2]))
@@ -901,7 +893,7 @@ class FinderTest < ActiveRecord::TestCase
end
def test_find_by_on_attribute_that_is_a_reserved_word
- dog_alias = 'Dog'
+ dog_alias = "Dog"
dog = Dog.create(alias: dog_alias)
assert_equal dog, Dog.find_by_alias(dog_alias)
@@ -918,7 +910,7 @@ class FinderTest < ActiveRecord::TestCase
end
def test_find_by_one_attribute_with_conditions
- assert_equal accounts(:rails_core_account), Account.where('firm_id = ?', 6).find_by_credit_limit(50)
+ assert_equal accounts(:rails_core_account), Account.where("firm_id = ?", 6).find_by_credit_limit(50)
end
def test_find_by_one_attribute_that_is_an_aggregate
@@ -958,12 +950,12 @@ class FinderTest < ActiveRecord::TestCase
def test_dynamic_finder_on_one_attribute_with_conditions_returns_same_results_after_caching
# ensure this test can run independently of order
class << Account; self; end.send(:remove_method, :find_by_credit_limit) if Account.public_methods.include?(:find_by_credit_limit)
- a = Account.where('firm_id = ?', 6).find_by_credit_limit(50)
- assert_equal a, Account.where('firm_id = ?', 6).find_by_credit_limit(50) # find_by_credit_limit has been cached
+ a = Account.where("firm_id = ?", 6).find_by_credit_limit(50)
+ assert_equal a, Account.where("firm_id = ?", 6).find_by_credit_limit(50) # find_by_credit_limit has been cached
end
def test_find_by_one_attribute_with_several_options
- assert_equal accounts(:unknown), Account.order('id DESC').where('id != ?', 3).find_by_credit_limit(50)
+ assert_equal accounts(:unknown), Account.order("id DESC").where("id != ?", 3).find_by_credit_limit(50)
end
def test_find_by_one_missing_attribute
@@ -987,12 +979,11 @@ class FinderTest < ActiveRecord::TestCase
end
def test_find_last_with_offset
- devs = Developer.order('id')
+ devs = Developer.order("id")
assert_equal devs[2], Developer.offset(2).first
assert_equal devs[-3], Developer.offset(2).last
- assert_equal devs[-3], Developer.offset(2).last
- assert_equal devs[-3], Developer.offset(2).order('id DESC').first
+ assert_equal devs[-3], Developer.offset(2).order("id DESC").first
end
def test_find_by_nil_attribute
@@ -1012,18 +1003,18 @@ class FinderTest < ActiveRecord::TestCase
def test_find_all_with_join
developers_on_project_one = Developer.
- joins('LEFT JOIN developers_projects ON developers.id = developers_projects.developer_id').
- where('project_id=1').to_a
+ joins("LEFT JOIN developers_projects ON developers.id = developers_projects.developer_id").
+ where("project_id=1").to_a
assert_equal 3, developers_on_project_one.length
developer_names = developers_on_project_one.map(&:name)
- assert developer_names.include?('David')
- assert developer_names.include?('Jamis')
+ assert_includes developer_names, "David"
+ assert_includes developer_names, "Jamis"
end
def test_joins_dont_clobber_id
first = Firm.
- joins('INNER JOIN companies clients ON clients.firm_id = companies.id').
- where('companies.id = 1').first
+ joins("INNER JOIN companies clients ON clients.firm_id = companies.id").
+ where("companies.id = 1").first
assert_equal 1, first.id
end
@@ -1037,12 +1028,12 @@ class FinderTest < ActiveRecord::TestCase
def test_find_by_id_with_conditions_with_or
assert_nothing_raised do
- Post.where("posts.id <= 3 OR posts.#{QUOTED_TYPE} = 'Post'").find([1,2,3])
+ Post.where("posts.id <= 3 OR posts.#{QUOTED_TYPE} = 'Post'").find([1, 2, 3])
end
end
def test_find_ignores_previously_inserted_record
- Post.create!(:title => 'test', :body => 'it out')
+ Post.create!(title: "test", body: "it out")
assert_equal [], Post.where(id: nil)
end
@@ -1051,13 +1042,13 @@ class FinderTest < ActiveRecord::TestCase
end
def test_find_by_empty_in_condition
- assert_equal [], Post.where('id in (?)', [])
+ assert_equal [], Post.where("id in (?)", [])
end
def test_find_by_records
- p1, p2 = Post.limit(2).order('id asc').to_a
- assert_equal [p1, p2], Post.where(['id in (?)', [p1, p2]]).order('id asc')
- assert_equal [p1, p2], Post.where(['id in (?)', [p1, p2.id]]).order('id asc')
+ p1, p2 = Post.limit(2).order("id asc").to_a
+ assert_equal [p1, p2], Post.where(["id in (?)", [p1, p2]]).order("id asc")
+ assert_equal [p1, p2], Post.where(["id in (?)", [p1, p2.id]]).order("id asc")
end
def test_select_value
@@ -1069,8 +1060,8 @@ class FinderTest < ActiveRecord::TestCase
end
def test_select_values
- assert_equal ["1","2","3","4","5","6","7","8","9", "10", "11"], Company.connection.select_values("SELECT id FROM companies ORDER BY id").map!(&:to_s)
- assert_equal ["37signals","Summit","Microsoft", "Flamboyant Software", "Ex Nihilo", "RailsCore", "Leetsoft", "Jadedpixel", "Odegy", "Ex Nihilo Part Deux", "Apex"], Company.connection.select_values("SELECT name FROM companies ORDER BY id")
+ assert_equal ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11"], Company.connection.select_values("SELECT id FROM companies ORDER BY id").map!(&:to_s)
+ assert_equal ["37signals", "Summit", "Microsoft", "Flamboyant Software", "Ex Nihilo", "RailsCore", "Leetsoft", "Jadedpixel", "Odegy", "Ex Nihilo Part Deux", "Apex"], Company.connection.select_values("SELECT name FROM companies ORDER BY id")
end
def test_select_rows
@@ -1078,36 +1069,36 @@ class FinderTest < ActiveRecord::TestCase
[["1", "1", nil, "37signals"],
["2", "1", "2", "Summit"],
["3", "1", "1", "Microsoft"]],
- Company.connection.select_rows("SELECT id, firm_id, client_of, name FROM companies WHERE id IN (1,2,3) ORDER BY id").map! {|i| i.map! {|j| j.to_s unless j.nil?}})
+ Company.connection.select_rows("SELECT id, firm_id, client_of, name FROM companies WHERE id IN (1,2,3) ORDER BY id").map! { |i| i.map! { |j| j.to_s unless j.nil? } })
assert_equal [["1", "37signals"], ["2", "Summit"], ["3", "Microsoft"]],
- Company.connection.select_rows("SELECT id, name FROM companies WHERE id IN (1,2,3) ORDER BY id").map! {|i| i.map! {|j| j.to_s unless j.nil?}}
+ Company.connection.select_rows("SELECT id, name FROM companies WHERE id IN (1,2,3) ORDER BY id").map! { |i| i.map! { |j| j.to_s unless j.nil? } }
end
def test_find_with_order_on_included_associations_with_construct_finder_sql_for_association_limiting_and_is_distinct
assert_equal 2, Post.includes(authors: :author_address).
where.not(author_addresses: { id: nil }).
- order('author_addresses.id DESC').limit(2).to_a.size
+ order("author_addresses.id DESC").limit(2).to_a.size
assert_equal 3, Post.includes(author: :author_address, authors: :author_address).
where.not(author_addresses_authors: { id: nil }).
- order('author_addresses_authors.id DESC').limit(3).to_a.size
+ order("author_addresses_authors.id DESC").limit(3).to_a.size
end
def test_find_with_nil_inside_set_passed_for_one_attribute
client_of = Company.
where(client_of: [2, 1, nil],
- name: ['37signals', 'Summit', 'Microsoft']).
- order('client_of DESC').
+ name: ["37signals", "Summit", "Microsoft"]).
+ order("client_of DESC").
map(&:client_of)
- assert client_of.include?(nil)
+ assert_includes client_of, nil
assert_equal [2, 1].sort, client_of.compact.sort
end
def test_find_with_nil_inside_set_passed_for_attribute
client_of = Company.
where(client_of: [nil]).
- order('client_of DESC').
+ order("client_of DESC").
map(&:client_of)
assert_equal [], client_of.compact
@@ -1115,20 +1106,30 @@ class FinderTest < ActiveRecord::TestCase
def test_with_limiting_with_custom_select
posts = Post.references(:authors).merge(
- :includes => :author, :select => 'posts.*, authors.id as "author_id"',
- :limit => 3, :order => 'posts.id'
+ includes: :author, select: 'posts.*, authors.id as "author_id"',
+ limit: 3, order: "posts.id"
).to_a
assert_equal 3, posts.size
assert_equal [0, 1, 1], posts.map(&:author_id).sort
end
+ def test_find_one_message_on_primary_key
+ e = assert_raises(ActiveRecord::RecordNotFound) do
+ Car.find(0)
+ end
+ assert_equal 0, e.id
+ assert_equal "id", e.primary_key
+ assert_equal "Car", e.model
+ assert_equal "Couldn't find Car with 'id'=0", e.message
+ end
+
def test_find_one_message_with_custom_primary_key
table_with_custom_primary_key do |model|
model.primary_key = :name
e = assert_raises(ActiveRecord::RecordNotFound) do
- model.find 'Hello World!'
+ model.find "Hello World!"
end
- assert_equal %Q{Couldn't find MercedesCar with 'name'=Hello World!}, e.message
+ assert_equal "Couldn't find MercedesCar with 'name'=Hello World!", e.message
end
end
@@ -1136,9 +1137,9 @@ class FinderTest < ActiveRecord::TestCase
table_with_custom_primary_key do |model|
model.primary_key = :name
e = assert_raises(ActiveRecord::RecordNotFound) do
- model.find 'Hello', 'World!'
+ model.find "Hello", "World!"
end
- assert_equal %Q{Couldn't find all MercedesCars with 'name': (Hello, World!) (found 0 results, but was looking for 2)}, e.message
+ assert_equal "Couldn't find all MercedesCars with 'name': (Hello, World!) (found 0 results, but was looking for 2)", e.message
end
end
@@ -1161,16 +1162,16 @@ class FinderTest < ActiveRecord::TestCase
end
test "find_by with multi-arg conditions returns the first matching record" do
- assert_equal posts(:eager_other), Post.find_by('id = ?', posts(:eager_other).id)
+ assert_equal posts(:eager_other), Post.find_by("id = ?", posts(:eager_other).id)
end
test "find_by returns nil if the record is missing" do
- assert_equal nil, Post.find_by("1 = 0")
+ assert_nil Post.find_by("1 = 0")
end
test "find_by with associations" do
assert_equal authors(:david), Post.find_by(author: authors(:david)).author
- assert_equal authors(:mary) , Post.find_by(author: authors(:mary) ).author
+ assert_equal authors(:mary) , Post.find_by(author: authors(:mary)).author
end
test "find_by doesn't have implicit ordering" do
@@ -1186,7 +1187,7 @@ class FinderTest < ActiveRecord::TestCase
end
test "find_by! with multi-arg conditions returns the first matching record" do
- assert_equal posts(:eager_other), Post.find_by!('id = ?', posts(:eager_other).id)
+ assert_equal posts(:eager_other), Post.find_by!("id = ?", posts(:eager_other).id)
end
test "find_by! doesn't have implicit ordering" do
@@ -1219,11 +1220,11 @@ class FinderTest < ActiveRecord::TestCase
assert_equal tyre2, zyke.tyres.custom_find_by(id: tyre2.id)
end
- protected
+ private
def table_with_custom_primary_key
yield(Class.new(Toy) do
def self.name
- 'MercedesCar'
+ "MercedesCar"
end
end)
end
@@ -1232,5 +1233,4 @@ class FinderTest < ActiveRecord::TestCase
err = assert_raises(exception_class) { block.call }
assert_match message, err.message
end
-
end
diff --git a/activerecord/test/cases/fixture_set/file_test.rb b/activerecord/test/cases/fixture_set/file_test.rb
index e64b90507e..533edcc2e0 100644
--- a/activerecord/test/cases/fixture_set/file_test.rb
+++ b/activerecord/test/cases/fixture_set/file_test.rb
@@ -1,5 +1,5 @@
-require 'cases/helper'
-require 'tempfile'
+require "cases/helper"
+require "tempfile"
module ActiveRecord
class FixtureSet
@@ -15,7 +15,7 @@ module ActiveRecord
called = true
assert_equal 6, fh.to_a.length
end
- assert called, 'block called'
+ assert called, "block called"
end
def test_names
@@ -31,8 +31,8 @@ module ActiveRecord
def test_values
File.open(::File.join(FIXTURES_ROOT, "accounts.yml")) do |fh|
- assert_equal [1,2,3,4,5,6].sort, fh.to_a.map(&:last).map { |x|
- x['id']
+ assert_equal [1, 2, 3, 4, 5, 6].sort, fh.to_a.map(&:last).map { |x|
+ x["id"]
}.sort
end
end
@@ -45,7 +45,7 @@ module ActiveRecord
end
def test_empty_file
- tmp_yaml ['empty', 'yml'], '' do |t|
+ tmp_yaml ["empty", "yml"], "" do |t|
assert_equal [], File.open(t.path) { |fh| fh.to_a }
end
end
@@ -53,7 +53,7 @@ module ActiveRecord
# A valid YAML file is not necessarily a value Fixture file. Make sure
# an exception is raised if the format is not valid Fixture format.
def test_wrong_fixture_format_string
- tmp_yaml ['empty', 'yml'], 'qwerty' do |t|
+ tmp_yaml ["empty", "yml"], "qwerty" do |t|
assert_raises(ActiveRecord::Fixture::FormatError) do
File.open(t.path) { |fh| fh.to_a }
end
@@ -61,7 +61,7 @@ module ActiveRecord
end
def test_wrong_fixture_format_nested
- tmp_yaml ['empty', 'yml'], 'one: two' do |t|
+ tmp_yaml ["empty", "yml"], "one: two" do |t|
assert_raises(ActiveRecord::Fixture::FormatError) do
File.open(t.path) { |fh| fh.to_a }
end
@@ -75,9 +75,9 @@ module ActiveRecord
end
end
yaml = "one:\n name: <%= fixture_helper %>\n"
- tmp_yaml ['curious', 'yml'], yaml do |t|
+ tmp_yaml ["curious", "yml"], yaml do |t|
golden =
- [["one", {"name" => "Fixture helper"}]]
+ [["one", { "name" => "Fixture helper" }]]
assert_equal golden, File.open(t.path) { |fh| fh.to_a }
end
ActiveRecord::FixtureSet.context_class.class_eval do
@@ -95,15 +95,15 @@ one:
File: <%= File.name %>
END
- golden = [['one', {
- 'ActiveRecord' => 'constant',
- 'ActiveRecord_FixtureSet' => 'constant',
- 'FixtureSet' => nil,
- 'ActiveRecord_FixtureSet_File' => 'constant',
- 'File' => 'File'
+ golden = [["one", {
+ "ActiveRecord" => "constant",
+ "ActiveRecord_FixtureSet" => "constant",
+ "FixtureSet" => nil,
+ "ActiveRecord_FixtureSet_File" => "constant",
+ "File" => "File"
}]]
- tmp_yaml ['curious', 'yml'], yaml do |t|
+ tmp_yaml ["curious", "yml"], yaml do |t|
assert_equal golden, File.open(t.path) { |fh| fh.to_a }
end
end
@@ -113,8 +113,8 @@ END
def test_independent_render_contexts
yaml1 = "<% def leaked_method; 'leak'; end %>\n"
yaml2 = "one:\n name: <%= leaked_method %>\n"
- tmp_yaml ['leaky', 'yml'], yaml1 do |t1|
- tmp_yaml ['curious', 'yml'], yaml2 do |t2|
+ tmp_yaml ["leaky", "yml"], yaml1 do |t1|
+ tmp_yaml ["curious", "yml"], yaml2 do |t2|
File.open(t1.path) { |fh| fh.to_a }
assert_raises(NameError) do
File.open(t2.path) { |fh| fh.to_a }
@@ -124,33 +124,33 @@ END
end
def test_removes_fixture_config_row
- File.open(::File.join(FIXTURES_ROOT, 'other_posts.yml')) do |fh|
- assert_equal(['second_welcome'], fh.each.map { |name, _| name })
+ File.open(::File.join(FIXTURES_ROOT, "other_posts.yml")) do |fh|
+ assert_equal(["second_welcome"], fh.each.map { |name, _| name })
end
end
def test_extracts_model_class_from_config_row
- File.open(::File.join(FIXTURES_ROOT, 'other_posts.yml')) do |fh|
- assert_equal 'Post', fh.model_class
+ File.open(::File.join(FIXTURES_ROOT, "other_posts.yml")) do |fh|
+ assert_equal "Post", fh.model_class
end
end
def test_erb_filename
- filename = 'filename.yaml'
+ filename = "filename.yaml"
erb = File.new(filename).send(:prepare_erb, "<% Rails.env %>\n")
assert_equal erb.filename, filename
end
private
- def tmp_yaml(name, contents)
- t = Tempfile.new name
- t.binmode
- t.write contents
- t.close
- yield t
- ensure
- t.close true
- end
+ def tmp_yaml(name, contents)
+ t = Tempfile.new name
+ t.binmode
+ t.write contents
+ t.close
+ yield t
+ ensure
+ t.close true
+ end
end
end
end
diff --git a/activerecord/test/cases/fixtures_test.rb b/activerecord/test/cases/fixtures_test.rb
index 9455d4886c..dd48053823 100644
--- a/activerecord/test/cases/fixtures_test.rb
+++ b/activerecord/test/cases/fixtures_test.rb
@@ -1,31 +1,33 @@
-require 'cases/helper'
-require 'models/admin'
-require 'models/admin/account'
-require 'models/admin/randomly_named_c1'
-require 'models/admin/user'
-require 'models/binary'
-require 'models/book'
-require 'models/bulb'
-require 'models/category'
-require 'models/comment'
-require 'models/company'
-require 'models/computer'
-require 'models/course'
-require 'models/developer'
-require 'models/doubloon'
-require 'models/joke'
-require 'models/matey'
-require 'models/parrot'
-require 'models/pirate'
-require 'models/post'
-require 'models/randomly_named_c1'
-require 'models/reply'
-require 'models/ship'
-require 'models/task'
-require 'models/topic'
-require 'models/traffic_light'
-require 'models/treasure'
-require 'tempfile'
+require "cases/helper"
+require "models/admin"
+require "models/admin/account"
+require "models/admin/randomly_named_c1"
+require "models/admin/user"
+require "models/binary"
+require "models/book"
+require "models/bulb"
+require "models/category"
+require "models/comment"
+require "models/company"
+require "models/computer"
+require "models/course"
+require "models/developer"
+require "models/dog"
+require "models/doubloon"
+require "models/joke"
+require "models/matey"
+require "models/other_dog"
+require "models/parrot"
+require "models/pirate"
+require "models/post"
+require "models/randomly_named_c1"
+require "models/reply"
+require "models/ship"
+require "models/task"
+require "models/topic"
+require "models/traffic_light"
+require "models/treasure"
+require "tempfile"
class FixturesTest < ActiveRecord::TestCase
self.use_instantiated_fixtures = true
@@ -53,12 +55,12 @@ class FixturesTest < ActiveRecord::TestCase
end
def test_broken_yaml_exception
- badyaml = Tempfile.new ['foo', '.yml']
- badyaml.write 'a: : '
+ badyaml = Tempfile.new ["foo", ".yml"]
+ badyaml.write "a: : "
badyaml.flush
dir = File.dirname badyaml.path
- name = File.basename badyaml.path, '.yml'
+ name = File.basename badyaml.path, ".yml"
assert_raises(ActiveRecord::Fixture::FormatError) do
ActiveRecord::FixtureSet.create_fixtures(dir, name)
end
@@ -69,8 +71,8 @@ class FixturesTest < ActiveRecord::TestCase
def test_create_fixtures
fixtures = ActiveRecord::FixtureSet.create_fixtures(FIXTURES_ROOT, "parrots")
- assert Parrot.find_by_name('Curious George'), 'George is not in the database'
- assert fixtures.detect { |f| f.name == 'parrots' }, "no fixtures named 'parrots' in #{fixtures.map(&:name).inspect}"
+ assert Parrot.find_by_name("Curious George"), "George is not in the database"
+ assert fixtures.detect { |f| f.name == "parrots" }, "no fixtures named 'parrots' in #{fixtures.map(&:name).inspect}"
end
def test_multiple_clean_fixtures
@@ -81,10 +83,10 @@ class FixturesTest < ActiveRecord::TestCase
end
def test_create_symbol_fixtures
- fixtures = ActiveRecord::FixtureSet.create_fixtures(FIXTURES_ROOT, :collections, :collections => Course) { Course.connection }
+ fixtures = ActiveRecord::FixtureSet.create_fixtures(FIXTURES_ROOT, :collections, collections: Course) { Course.connection }
- assert Course.find_by_name('Collection'), 'course is not in the database'
- assert fixtures.detect { |f| f.name == 'collections' }, "no fixtures named 'collections' in #{fixtures.map(&:name).inspect}"
+ assert Course.find_by_name("Collection"), "course is not in the database"
+ assert fixtures.detect { |f| f.name == "collections" }, "no fixtures named 'collections' in #{fixtures.map(&:name).inspect}"
end
def test_attributes
@@ -115,10 +117,10 @@ class FixturesTest < ActiveRecord::TestCase
t.column :bonus_time, :time
t.column :last_read, :date
t.column :content, :string
- t.column :approved, :boolean, :default => true
- t.column :replies_count, :integer, :default => 0
+ t.column :approved, :boolean, default: true
+ t.column :replies_count, :integer, default: 0
t.column :parent_id, :integer
- t.column :type, :string, :limit => 50
+ t.column :type, :string, limit: 50
end
# Store existing prefix/suffix
@@ -126,8 +128,8 @@ class FixturesTest < ActiveRecord::TestCase
old_suffix = ActiveRecord::Base.table_name_suffix
# Set a prefix/suffix we can test against
- ActiveRecord::Base.table_name_prefix = 'prefix_'
- ActiveRecord::Base.table_name_suffix = '_suffix'
+ ActiveRecord::Base.table_name_prefix = "prefix_"
+ ActiveRecord::Base.table_name_suffix = "_suffix"
other_topic_klass = Class.new(ActiveRecord::Base) do
def self.name
@@ -170,7 +172,7 @@ class FixturesTest < ActiveRecord::TestCase
def test_logger_level_invariant
level = ActiveRecord::Base.logger.level
- create_fixtures('topics')
+ create_fixtures("topics")
assert_equal level, ActiveRecord::Base.logger.level
end
@@ -192,28 +194,38 @@ class FixturesTest < ActiveRecord::TestCase
end
def test_empty_yaml_fixture
- assert_not_nil ActiveRecord::FixtureSet.new( Account.connection, "accounts", Account, FIXTURES_ROOT + "/naked/yml/accounts")
+ assert_not_nil ActiveRecord::FixtureSet.new(Account.connection, "accounts", Account, FIXTURES_ROOT + "/naked/yml/accounts")
end
def test_empty_yaml_fixture_with_a_comment_in_it
- assert_not_nil ActiveRecord::FixtureSet.new( Account.connection, "companies", Company, FIXTURES_ROOT + "/naked/yml/companies")
+ assert_not_nil ActiveRecord::FixtureSet.new(Account.connection, "companies", Company, FIXTURES_ROOT + "/naked/yml/companies")
end
def test_nonexistent_fixture_file
nonexistent_fixture_path = FIXTURES_ROOT + "/imnothere"
#sanity check to make sure that this file never exists
- assert Dir[nonexistent_fixture_path+"*"].empty?
+ assert Dir[nonexistent_fixture_path + "*"].empty?
assert_raise(Errno::ENOENT) do
- ActiveRecord::FixtureSet.new( Account.connection, "companies", Company, nonexistent_fixture_path)
+ ActiveRecord::FixtureSet.new(Account.connection, "companies", Company, nonexistent_fixture_path)
end
end
def test_dirty_dirty_yaml_file
- assert_raise(ActiveRecord::Fixture::FormatError) do
- ActiveRecord::FixtureSet.new( Account.connection, "courses", Course, FIXTURES_ROOT + "/naked/yml/courses")
+ fixture_path = FIXTURES_ROOT + "/naked/yml/courses"
+ error = assert_raise(ActiveRecord::Fixture::FormatError) do
+ ActiveRecord::FixtureSet.new(Account.connection, "courses", Course, fixture_path)
+ end
+ assert_equal "fixture is not a hash: #{fixture_path}.yml", error.to_s
+ end
+
+ def test_yaml_file_with_one_invalid_fixture
+ fixture_path = FIXTURES_ROOT + "/naked/yml/courses_with_invalid_key"
+ error = assert_raise(ActiveRecord::Fixture::FormatError) do
+ ActiveRecord::FixtureSet.new(Account.connection, "courses", Course, fixture_path)
end
+ assert_equal "fixture key is not a hash: #{fixture_path}.yml, keys: [\"two\"]", error.to_s
end
def test_yaml_file_with_invalid_column
@@ -229,11 +241,11 @@ class FixturesTest < ActiveRecord::TestCase
def test_omap_fixtures
assert_nothing_raised do
- fixtures = ActiveRecord::FixtureSet.new(Account.connection, 'categories', Category, FIXTURES_ROOT + "/categories_ordered")
+ fixtures = ActiveRecord::FixtureSet.new(Account.connection, "categories", Category, FIXTURES_ROOT + "/categories_ordered")
fixtures.each.with_index do |(name, fixture), i|
assert_equal "fixture_no_#{i}", name
- assert_equal "Category #{i}", fixture['name']
+ assert_equal "Category #{i}", fixture["name"]
end
end
end
@@ -249,8 +261,8 @@ class FixturesTest < ActiveRecord::TestCase
end
def test_binary_in_fixtures
- data = File.open(ASSETS_ROOT + "/flowers.jpg", 'rb') { |f| f.read }
- data.force_encoding('ASCII-8BIT')
+ data = File.open(ASSETS_ROOT + "/flowers.jpg", "rb") { |f| f.read }
+ data.force_encoding("ASCII-8BIT")
data.freeze
assert_equal data, @flowers.data
end
@@ -260,8 +272,8 @@ class FixturesTest < ActiveRecord::TestCase
end
def test_fixtures_are_set_up_with_database_env_variable
- db_url_tmp = ENV['DATABASE_URL']
- ENV['DATABASE_URL'] = "sqlite3::memory:"
+ db_url_tmp = ENV["DATABASE_URL"]
+ ENV["DATABASE_URL"] = "sqlite3::memory:"
ActiveRecord::Base.stub(:configurations, {}) do
test_case = Class.new(ActiveRecord::TestCase) do
fixtures :accounts
@@ -276,11 +288,11 @@ class FixturesTest < ActiveRecord::TestCase
assert result.passed?, "Expected #{result.name} to pass:\n#{result}"
end
ensure
- ENV['DATABASE_URL'] = db_url_tmp
+ ENV["DATABASE_URL"] = db_url_tmp
end
end
-class HasManyThroughFixture < ActiveSupport::TestCase
+class HasManyThroughFixture < ActiveRecord::TestCase
def make_model(name)
Class.new(ActiveRecord::Base) { define_singleton_method(:name) { name } }
end
@@ -291,17 +303,17 @@ class HasManyThroughFixture < ActiveSupport::TestCase
treasure = make_model "Treasure"
pt.table_name = "parrots_treasures"
- pt.belongs_to :parrot, :anonymous_class => parrot
- pt.belongs_to :treasure, :anonymous_class => treasure
+ pt.belongs_to :parrot, anonymous_class: parrot
+ pt.belongs_to :treasure, anonymous_class: treasure
- parrot.has_many :parrot_treasures, :anonymous_class => pt
- parrot.has_many :treasures, :through => :parrot_treasures
+ parrot.has_many :parrot_treasures, anonymous_class: pt
+ parrot.has_many :treasures, through: :parrot_treasures
- parrots = File.join FIXTURES_ROOT, 'parrots'
+ parrots = File.join FIXTURES_ROOT, "parrots"
fs = ActiveRecord::FixtureSet.new parrot.connection, "parrots", parrot, parrots
rows = fs.table_rows
- assert_equal load_has_and_belongs_to_many['parrots_treasures'], rows['parrots_treasures']
+ assert_equal load_has_and_belongs_to_many["parrots_treasures"], rows["parrots_treasures"]
end
def test_has_many_through_with_renamed_table
@@ -309,24 +321,24 @@ class HasManyThroughFixture < ActiveSupport::TestCase
parrot = make_model "Parrot"
treasure = make_model "Treasure"
- pt.belongs_to :parrot, :anonymous_class => parrot
- pt.belongs_to :treasure, :anonymous_class => treasure
+ pt.belongs_to :parrot, anonymous_class: parrot
+ pt.belongs_to :treasure, anonymous_class: treasure
- parrot.has_many :parrot_treasures, :anonymous_class => pt
- parrot.has_many :treasures, :through => :parrot_treasures
+ parrot.has_many :parrot_treasures, anonymous_class: pt
+ parrot.has_many :treasures, through: :parrot_treasures
- parrots = File.join FIXTURES_ROOT, 'parrots'
+ parrots = File.join FIXTURES_ROOT, "parrots"
fs = ActiveRecord::FixtureSet.new parrot.connection, "parrots", parrot, parrots
rows = fs.table_rows
- assert_equal load_has_and_belongs_to_many['parrots_treasures'], rows['parrot_treasures']
+ assert_equal load_has_and_belongs_to_many["parrots_treasures"], rows["parrot_treasures"]
end
def load_has_and_belongs_to_many
parrot = make_model "Parrot"
parrot.has_and_belongs_to_many :treasures
- parrots = File.join FIXTURES_ROOT, 'parrots'
+ parrots = File.join FIXTURES_ROOT, "parrots"
fs = ActiveRecord::FixtureSet.new parrot.connection, "parrots", parrot, parrots
fs.table_rows
@@ -339,7 +351,7 @@ if Account.connection.respond_to?(:reset_pk_sequence!)
fixtures :companies
def setup
- @instances = [Account.new(:credit_limit => 50), Company.new(:name => 'RoR Consulting'), Course.new(name: 'Test')]
+ @instances = [Account.new(credit_limit: 50), Company.new(name: "RoR Consulting"), Course.new(name: "Test")]
ActiveRecord::FixtureSet.reset_cache # make sure tables get reinitialized
end
@@ -368,7 +380,7 @@ if Account.connection.respond_to?(:reset_pk_sequence!)
def test_create_fixtures_resets_sequences_when_not_cached
@instances.each do |instance|
max_id = create_fixtures(instance.class.table_name).first.fixtures.inject(0) do |_max_id, (_, fixture)|
- fixture_id = fixture['id'].to_i
+ fixture_id = fixture["id"].to_i
fixture_id > _max_id ? fixture_id : _max_id
end
@@ -414,7 +426,7 @@ class FixturesWithoutInstantiationTest < ActiveRecord::TestCase
def test_reloading_fixtures_through_accessor_methods
topic = Struct.new(:title)
assert_equal "The First Topic", topics(:first).title
- assert_called(@loaded_fixtures['topics']['first'], :find, returns: topic.new("Fresh Topic!")) do
+ assert_called(@loaded_fixtures["topics"]["first"], :find, returns: topic.new("Fresh Topic!")) do
assert_equal "Fresh Topic!", topics(:first, true).title
end
end
@@ -479,7 +491,6 @@ class SetupSubclassTest < SetupTest
end
end
-
class OverlappingFixturesTest < ActiveRecord::TestCase
fixtures :topics, :developers
fixtures :developers, :accounts
@@ -510,13 +521,13 @@ class OverRideFixtureMethodTest < ActiveRecord::TestCase
def topics(name)
topic = super
- topic.title = 'omg'
+ topic.title = "omg"
topic
end
def test_fixture_methods_can_be_overridden
x = topics :first
- assert_equal 'omg', x.title
+ assert_equal "omg", x.title
end
end
@@ -553,7 +564,7 @@ class SetFixtureClassPrevailsTest < ActiveRecord::TestCase
end
class CheckSetTableNameFixturesTest < ActiveRecord::TestCase
- set_fixture_class :funny_jokes => Joke
+ set_fixture_class funny_jokes: Joke
fixtures :funny_jokes
# Set to false to blow away fixtures cache and ensure our fixtures are loaded
# and thus takes into account our set_fixture_class
@@ -565,7 +576,7 @@ class CheckSetTableNameFixturesTest < ActiveRecord::TestCase
end
class FixtureNameIsNotTableNameFixturesTest < ActiveRecord::TestCase
- set_fixture_class :items => Book
+ set_fixture_class items: Book
fixtures :items
# Set to false to blow away fixtures cache and ensure our fixtures are loaded
# and thus takes into account our set_fixture_class
@@ -577,7 +588,7 @@ class FixtureNameIsNotTableNameFixturesTest < ActiveRecord::TestCase
end
class FixtureNameIsNotTableNameMultipleFixturesTest < ActiveRecord::TestCase
- set_fixture_class :items => Book, :funny_jokes => Joke
+ set_fixture_class items: Book, funny_jokes: Joke
fixtures :items, :funny_jokes
# Set to false to blow away fixtures cache and ensure our fixtures are loaded
# and thus takes into account our set_fixture_class
@@ -593,7 +604,7 @@ class FixtureNameIsNotTableNameMultipleFixturesTest < ActiveRecord::TestCase
end
class CustomConnectionFixturesTest < ActiveRecord::TestCase
- set_fixture_class :courses => Course
+ set_fixture_class courses: Course
fixtures :courses
self.use_transactional_tests = false
@@ -608,7 +619,7 @@ class CustomConnectionFixturesTest < ActiveRecord::TestCase
end
class TransactionalFixturesOnCustomConnectionTest < ActiveRecord::TestCase
- set_fixture_class :courses => Course
+ set_fixture_class courses: Course
fixtures :courses
self.use_transactional_tests = true
@@ -622,6 +633,46 @@ class TransactionalFixturesOnCustomConnectionTest < ActiveRecord::TestCase
end
end
+class TransactionalFixturesOnConnectionNotification < ActiveRecord::TestCase
+ self.use_transactional_tests = true
+ self.use_instantiated_fixtures = false
+
+ def test_transaction_created_on_connection_notification
+ connection = stub(transaction_open?: false)
+ connection.expects(:begin_transaction).with(joinable: false)
+ fire_connection_notification(connection)
+ end
+
+ def test_notification_established_transactions_are_rolled_back
+ # Mocha is not thread-safe so define our own stub to test
+ connection = Class.new do
+ attr_accessor :rollback_transaction_called
+ def transaction_open?; true; end
+ def begin_transaction(*args); end
+ def rollback_transaction(*args)
+ @rollback_transaction_called = true
+ end
+ end.new
+ fire_connection_notification(connection)
+ teardown_fixtures
+ assert(connection.rollback_transaction_called, "Expected <mock connection>#rollback_transaction to be called but was not")
+ end
+
+ private
+
+ def fire_connection_notification(connection)
+ ActiveRecord::Base.connection_handler.stubs(:retrieve_connection).with("book").returns(connection)
+ message_bus = ActiveSupport::Notifications.instrumenter
+ payload = {
+ spec_name: "book",
+ config: nil,
+ connection_id: connection.object_id
+ }
+
+ message_bus.instrument("!connection.active_record", payload) {}
+ end
+end
+
class InvalidTableNameFixturesTest < ActiveRecord::TestCase
fixtures :funny_jokes
# Set to false to blow away fixtures cache and ensure our fixtures are loaded
@@ -636,7 +687,7 @@ class InvalidTableNameFixturesTest < ActiveRecord::TestCase
end
class CheckEscapedYamlFixturesTest < ActiveRecord::TestCase
- set_fixture_class :funny_jokes => Joke
+ set_fixture_class funny_jokes: Joke
fixtures :funny_jokes
# Set to false to blow away fixtures cache and ensure our fixtures are loaded
# and thus takes into account our set_fixture_class
@@ -679,7 +730,7 @@ class FixturesBrokenRollbackTest < ActiveRecord::TestCase
private
def load_fixtures(config)
- raise 'argh'
+ raise "argh"
end
end
@@ -689,7 +740,7 @@ class LoadAllFixturesTest < ActiveRecord::TestCase
self.class.fixtures :all
if File.symlink? FIXTURES_ROOT + "/all/admin"
- assert_equal %w(admin/accounts admin/users developers people tasks), fixture_table_names.sort
+ assert_equal %w(admin/accounts admin/users developers namespaced/accounts people tasks), fixture_table_names.sort
end
ensure
ActiveRecord::FixtureSet.reset_cache
@@ -698,11 +749,11 @@ end
class LoadAllFixturesWithPathnameTest < ActiveRecord::TestCase
def test_all_there
- self.class.fixture_path = Pathname.new(FIXTURES_ROOT).join('all')
+ self.class.fixture_path = Pathname.new(FIXTURES_ROOT).join("all")
self.class.fixtures :all
if File.symlink? FIXTURES_ROOT + "/all/admin"
- assert_equal %w(admin/accounts admin/users developers people tasks), fixture_table_names.sort
+ assert_equal %w(admin/accounts admin/users developers namespaced/accounts people tasks), fixture_table_names.sort
end
ensure
ActiveRecord::FixtureSet.reset_cache
@@ -720,18 +771,18 @@ class FasterFixturesTest < ActiveRecord::TestCase
end
def test_cache
- assert ActiveRecord::FixtureSet.fixture_is_cached?(ActiveRecord::Base.connection, 'categories')
- assert ActiveRecord::FixtureSet.fixture_is_cached?(ActiveRecord::Base.connection, 'authors')
+ assert ActiveRecord::FixtureSet.fixture_is_cached?(ActiveRecord::Base.connection, "categories")
+ assert ActiveRecord::FixtureSet.fixture_is_cached?(ActiveRecord::Base.connection, "authors")
assert_no_queries do
- create_fixtures('categories')
- create_fixtures('authors')
+ create_fixtures("categories")
+ create_fixtures("authors")
end
- load_extra_fixture('posts')
- assert ActiveRecord::FixtureSet.fixture_is_cached?(ActiveRecord::Base.connection, 'posts')
+ load_extra_fixture("posts")
+ assert ActiveRecord::FixtureSet.fixture_is_cached?(ActiveRecord::Base.connection, "posts")
self.class.setup_fixture_accessors :posts
- assert_equal 'Welcome to the weblog', posts(:welcome).title
+ assert_equal "Welcome to the weblog", posts(:welcome).title
end
end
@@ -739,9 +790,9 @@ class FoxyFixturesTest < ActiveRecord::TestCase
fixtures :parrots, :parrots_pirates, :pirates, :treasures, :mateys, :ships, :computers,
:developers, :"admin/accounts", :"admin/users", :live_parrots, :dead_parrots, :books
- if ActiveRecord::Base.connection.adapter_name == 'PostgreSQL'
- require 'models/uuid_parent'
- require 'models/uuid_child'
+ if ActiveRecord::Base.connection.adapter_name == "PostgreSQL"
+ require "models/uuid_parent"
+ require "models/uuid_child"
fixtures :uuid_parents, :uuid_children
end
@@ -758,8 +809,8 @@ class FoxyFixturesTest < ActiveRecord::TestCase
assert_equal 207281424, ActiveRecord::FixtureSet.identify(:ruby)
assert_equal 1066363776, ActiveRecord::FixtureSet.identify(:sapphire_2)
- assert_equal 'f92b6bda-0d0d-5fe1-9124-502b18badded', ActiveRecord::FixtureSet.identify(:daddy, :uuid)
- assert_equal 'b4b10018-ad47-595d-b42f-d8bdaa6d01bf', ActiveRecord::FixtureSet.identify(:sonny, :uuid)
+ assert_equal "f92b6bda-0d0d-5fe1-9124-502b18badded", ActiveRecord::FixtureSet.identify(:daddy, :uuid)
+ assert_equal "b4b10018-ad47-595d-b42f-d8bdaa6d01bf", ActiveRecord::FixtureSet.identify(:sonny, :uuid)
end
TIMESTAMP_COLUMNS = %w(created_at created_on updated_at updated_on)
@@ -884,7 +935,7 @@ class FoxyFixturesTest < ActiveRecord::TestCase
end
def test_namespaced_models
- assert admin_accounts(:signals37).users.include?(admin_users(:david))
+ assert_includes admin_accounts(:signals37).users, admin_users(:david)
assert_equal 2, admin_accounts(:signals37).users.size
end
@@ -909,14 +960,14 @@ end
class CustomNameForFixtureOrModelTest < ActiveRecord::TestCase
ActiveRecord::FixtureSet.reset_cache
- set_fixture_class :randomly_named_a9 =>
+ set_fixture_class :randomly_named_a9 =>
ClassNameThatDoesNotFollowCONVENTIONS,
:'admin/randomly_named_a9' =>
Admin::ClassNameThatDoesNotFollowCONVENTIONS1,
- 'admin/randomly_named_b0' =>
+ "admin/randomly_named_b0" =>
Admin::ClassNameThatDoesNotFollowCONVENTIONS2
- fixtures :randomly_named_a9, 'admin/randomly_named_a9',
+ fixtures :randomly_named_a9, "admin/randomly_named_a9",
:'admin/randomly_named_b0'
def test_named_accessor_for_randomly_named_fixture_and_class
@@ -932,8 +983,8 @@ class CustomNameForFixtureOrModelTest < ActiveRecord::TestCase
end
def test_table_name_is_defined_in_the_model
- assert_equal 'randomly_named_table2', ActiveRecord::FixtureSet::all_loaded_fixtures["admin/randomly_named_a9"].table_name
- assert_equal 'randomly_named_table2', Admin::ClassNameThatDoesNotFollowCONVENTIONS1.table_name
+ assert_equal "randomly_named_table2", ActiveRecord::FixtureSet::all_loaded_fixtures["admin/randomly_named_a9"].table_name
+ assert_equal "randomly_named_table2", Admin::ClassNameThatDoesNotFollowCONVENTIONS1.table_name
end
end
@@ -965,10 +1016,23 @@ class FixtureClassNamesTest < ActiveRecord::TestCase
end
def teardown
- self.fixture_class_names.replace(@saved_cache)
+ fixture_class_names.replace(@saved_cache)
end
test "fixture_class_names returns nil for unregistered identifier" do
- assert_nil self.fixture_class_names['unregistered_identifier']
+ assert_nil fixture_class_names["unregistered_identifier"]
+ end
+end
+
+class SameNameDifferentDatabaseFixturesTest < ActiveRecord::TestCase
+ fixtures :dogs, :other_dogs
+
+ test "fixtures are properly loaded" do
+ # Force loading the fixtures again to reproduce issue
+ ActiveRecord::FixtureSet.reset_cache
+ create_fixtures("dogs", "other_dogs")
+
+ assert_kind_of Dog, dogs(:sophie)
+ assert_kind_of OtherDog, other_dogs(:lassie)
end
end
diff --git a/activerecord/test/cases/forbidden_attributes_protection_test.rb b/activerecord/test/cases/forbidden_attributes_protection_test.rb
index 91921469b8..ffa3f63e0d 100644
--- a/activerecord/test/cases/forbidden_attributes_protection_test.rb
+++ b/activerecord/test/cases/forbidden_attributes_protection_test.rb
@@ -1,11 +1,11 @@
-require 'cases/helper'
-require 'active_support/core_ext/hash/indifferent_access'
+require "cases/helper"
+require "active_support/core_ext/hash/indifferent_access"
-require 'models/company'
-require 'models/person'
-require 'models/ship'
-require 'models/ship_part'
-require 'models/treasure'
+require "models/company"
+require "models/person"
+require "models/ship"
+require "models/ship_part"
+require "models/treasure"
class ProtectedParams
attr_accessor :permitted
@@ -44,40 +44,40 @@ end
class ForbiddenAttributesProtectionTest < ActiveRecord::TestCase
def test_forbidden_attributes_cannot_be_used_for_mass_assignment
- params = ProtectedParams.new(first_name: 'Guille', gender: 'm')
+ params = ProtectedParams.new(first_name: "Guille", gender: "m")
assert_raises(ActiveModel::ForbiddenAttributesError) do
Person.new(params)
end
end
def test_permitted_attributes_can_be_used_for_mass_assignment
- params = ProtectedParams.new(first_name: 'Guille', gender: 'm')
+ params = ProtectedParams.new(first_name: "Guille", gender: "m")
params.permit!
person = Person.new(params)
- assert_equal 'Guille', person.first_name
- assert_equal 'm', person.gender
+ assert_equal "Guille", person.first_name
+ assert_equal "m", person.gender
end
def test_forbidden_attributes_cannot_be_used_for_sti_inheritance_column
- params = ProtectedParams.new(type: 'Client')
+ params = ProtectedParams.new(type: "Client")
assert_raises(ActiveModel::ForbiddenAttributesError) do
Company.new(params)
end
end
def test_permitted_attributes_can_be_used_for_sti_inheritance_column
- params = ProtectedParams.new(type: 'Client')
+ params = ProtectedParams.new(type: "Client")
params.permit!
person = Company.new(params)
assert_equal person.class, Client
end
def test_regular_hash_should_still_be_used_for_mass_assignment
- person = Person.new(first_name: 'Guille', gender: 'm')
+ person = Person.new(first_name: "Guille", gender: "m")
- assert_equal 'Guille', person.first_name
- assert_equal 'm', person.gender
+ assert_equal "Guille", person.first_name
+ assert_equal "m", person.gender
end
def test_blank_attributes_should_not_raise
@@ -86,7 +86,7 @@ class ForbiddenAttributesProtectionTest < ActiveRecord::TestCase
end
def test_create_with_checks_permitted
- params = ProtectedParams.new(first_name: 'Guille', gender: 'm')
+ params = ProtectedParams.new(first_name: "Guille", gender: "m")
assert_raises(ActiveModel::ForbiddenAttributesError) do
Person.create_with(params).create!
@@ -94,21 +94,21 @@ class ForbiddenAttributesProtectionTest < ActiveRecord::TestCase
end
def test_create_with_works_with_permitted_params
- params = ProtectedParams.new(first_name: 'Guille').permit!
+ params = ProtectedParams.new(first_name: "Guille").permit!
person = Person.create_with(params).create!
- assert_equal 'Guille', person.first_name
+ assert_equal "Guille", person.first_name
end
def test_create_with_works_with_params_values
- params = ProtectedParams.new(first_name: 'Guille')
+ params = ProtectedParams.new(first_name: "Guille")
person = Person.create_with(first_name: params[:first_name]).create!
- assert_equal 'Guille', person.first_name
+ assert_equal "Guille", person.first_name
end
def test_where_checks_permitted
- params = ProtectedParams.new(first_name: 'Guille', gender: 'm')
+ params = ProtectedParams.new(first_name: "Guille", gender: "m")
assert_raises(ActiveModel::ForbiddenAttributesError) do
Person.where(params).create!
@@ -116,21 +116,21 @@ class ForbiddenAttributesProtectionTest < ActiveRecord::TestCase
end
def test_where_works_with_permitted_params
- params = ProtectedParams.new(first_name: 'Guille').permit!
+ params = ProtectedParams.new(first_name: "Guille").permit!
person = Person.where(params).create!
- assert_equal 'Guille', person.first_name
+ assert_equal "Guille", person.first_name
end
def test_where_works_with_params_values
- params = ProtectedParams.new(first_name: 'Guille')
+ params = ProtectedParams.new(first_name: "Guille")
person = Person.where(first_name: params[:first_name]).create!
- assert_equal 'Guille', person.first_name
+ assert_equal "Guille", person.first_name
end
def test_where_not_checks_permitted
- params = ProtectedParams.new(first_name: 'Guille', gender: 'm')
+ params = ProtectedParams.new(first_name: "Guille", gender: "m")
assert_raises(ActiveModel::ForbiddenAttributesError) do
Person.where().not(params)
@@ -138,13 +138,13 @@ class ForbiddenAttributesProtectionTest < ActiveRecord::TestCase
end
def test_where_not_works_with_permitted_params
- params = ProtectedParams.new(first_name: 'Guille').permit!
+ params = ProtectedParams.new(first_name: "Guille").permit!
Person.create!(params)
- assert_empty Person.where.not(params).select {|p| p.first_name == 'Guille' }
+ assert_empty Person.where.not(params).select { |p| p.first_name == "Guille" }
end
def test_strong_params_style_objects_work_with_singular_associations
- params = ProtectedParams.new( name: "Stern", ship_attributes: ProtectedParams.new(name: "The Black Rock").permit!).permit!
+ params = ProtectedParams.new(name: "Stern", ship_attributes: ProtectedParams.new(name: "The Black Rock").permit!).permit!
part = ShipPart.new(params)
assert_equal "Stern", part.name
@@ -155,11 +155,10 @@ class ForbiddenAttributesProtectionTest < ActiveRecord::TestCase
params = ProtectedParams.new(
trinkets_attributes: ProtectedParams.new(
"0" => ProtectedParams.new(name: "Necklace").permit!,
- "1" => ProtectedParams.new(name: "Spoon").permit! ) ).permit!
+ "1" => ProtectedParams.new(name: "Spoon").permit!)).permit!
part = ShipPart.new(params)
assert_equal "Necklace", part.trinkets[0].name
assert_equal "Spoon", part.trinkets[1].name
end
-
end
diff --git a/activerecord/test/cases/habtm_destroy_order_test.rb b/activerecord/test/cases/habtm_destroy_order_test.rb
index 2ce0de360e..365d4576dd 100644
--- a/activerecord/test/cases/habtm_destroy_order_test.rb
+++ b/activerecord/test/cases/habtm_destroy_order_test.rb
@@ -4,21 +4,21 @@ require "models/student"
class HabtmDestroyOrderTest < ActiveRecord::TestCase
test "may not delete a lesson with students" do
- sicp = Lesson.new(:name => "SICP")
- ben = Student.new(:name => "Ben Bitdiddle")
+ sicp = Lesson.new(name: "SICP")
+ ben = Student.new(name: "Ben Bitdiddle")
sicp.students << ben
sicp.save!
assert_raises LessonError do
- assert_no_difference('Lesson.count') do
+ assert_no_difference("Lesson.count") do
sicp.destroy
end
end
assert !sicp.destroyed?
end
- test 'should not raise error if have foreign key in the join table' do
- student = Student.new(:name => "Ben Bitdiddle")
- lesson = Lesson.new(:name => "SICP")
+ test "should not raise error if have foreign key in the join table" do
+ student = Student.new(name: "Ben Bitdiddle")
+ lesson = Lesson.new(name: "SICP")
lesson.students << student
lesson.save!
assert_nothing_raised do
@@ -29,8 +29,8 @@ class HabtmDestroyOrderTest < ActiveRecord::TestCase
test "not destroying a student with lessons leaves student<=>lesson association intact" do
# test a normal before_destroy doesn't destroy the habtm joins
begin
- sicp = Lesson.new(:name => "SICP")
- ben = Student.new(:name => "Ben Bitdiddle")
+ sicp = Lesson.new(name: "SICP")
+ ben = Student.new(name: "Ben Bitdiddle")
# add a before destroy to student
Student.class_eval do
before_destroy do
@@ -49,8 +49,8 @@ class HabtmDestroyOrderTest < ActiveRecord::TestCase
test "not destroying a lesson with students leaves student<=>lesson association intact" do
# test a more aggressive before_destroy doesn't destroy the habtm joins and still throws the exception
- sicp = Lesson.new(:name => "SICP")
- ben = Student.new(:name => "Ben Bitdiddle")
+ sicp = Lesson.new(name: "SICP")
+ ben = Student.new(name: "Ben Bitdiddle")
sicp.students << ben
sicp.save!
assert_raises LessonError do
diff --git a/activerecord/test/cases/helper.rb b/activerecord/test/cases/helper.rb
index d2fdf03e9d..1ddcbf0e4f 100644
--- a/activerecord/test/cases/helper.rb
+++ b/activerecord/test/cases/helper.rb
@@ -1,17 +1,15 @@
-require 'config'
+require "config"
-require 'active_support/testing/autorun'
-require 'active_support/testing/method_call_assertions'
-require 'stringio'
+require "stringio"
-require 'active_record'
-require 'cases/test_case'
-require 'active_support/dependencies'
-require 'active_support/logger'
-require 'active_support/core_ext/string/strip'
+require "active_record"
+require "cases/test_case"
+require "active_support/dependencies"
+require "active_support/logger"
+require "active_support/core_ext/string/strip"
-require 'support/config'
-require 'support/connection'
+require "support/config"
+require "support/connection"
# TODO: Move all these random hacks into the ARTest namespace and into the support/ dir
@@ -27,10 +25,7 @@ I18n.enforce_available_locales = false
ARTest.connect
# Quote "type" if it's a reserved word for the current connection.
-QUOTED_TYPE = ActiveRecord::Base.connection.quote_column_name('type')
-
-# FIXME: Remove this when the deprecation cycle on TZ aware types by default ends.
-ActiveRecord::Base.time_zone_aware_types << :time
+QUOTED_TYPE = ActiveRecord::Base.connection.quote_column_name("type")
def current_adapter?(*types)
types.any? do |type|
@@ -49,18 +44,18 @@ def subsecond_precision_supported?
end
def mysql_enforcing_gtid_consistency?
- current_adapter?(:Mysql2Adapter) && 'ON' == ActiveRecord::Base.connection.show_variable('enforce_gtid_consistency')
+ current_adapter?(:Mysql2Adapter) && "ON" == ActiveRecord::Base.connection.show_variable("enforce_gtid_consistency")
end
def supports_savepoints?
ActiveRecord::Base.connection.supports_savepoints?
end
-def with_env_tz(new_tz = 'US/Eastern')
- old_tz, ENV['TZ'] = ENV['TZ'], new_tz
+def with_env_tz(new_tz = "US/Eastern")
+ old_tz, ENV["TZ"] = ENV["TZ"], new_tz
yield
ensure
- old_tz ? ENV['TZ'] = old_tz : ENV.delete('TZ')
+ old_tz ? ENV["TZ"] = old_tz : ENV.delete("TZ")
end
def with_timezone_config(cfg)
@@ -134,21 +129,6 @@ def disable_extension!(extension, connection)
connection.reconnect!
end
-require "cases/validations_repair_helper"
-class ActiveSupport::TestCase
- include ActiveRecord::TestFixtures
- include ActiveRecord::ValidationsRepairHelper
- include ActiveSupport::Testing::MethodCallAssertions
-
- self.fixture_path = FIXTURES_ROOT
- self.use_instantiated_fixtures = false
- self.use_transactional_tests = true
-
- def create_fixtures(*fixture_set_names, &block)
- ActiveRecord::FixtureSet.create_fixtures(ActiveSupport::TestCase.fixture_path, fixture_set_names, fixture_class_names, &block)
- end
-end
-
def load_schema
# silence verbose schema loading
original_stdout = $stdout
@@ -188,17 +168,17 @@ end
module InTimeZone
private
- def in_time_zone(zone)
- old_zone = Time.zone
- old_tz = ActiveRecord::Base.time_zone_aware_attributes
-
- Time.zone = zone ? ActiveSupport::TimeZone[zone] : nil
- ActiveRecord::Base.time_zone_aware_attributes = !zone.nil?
- yield
- ensure
- Time.zone = old_zone
- ActiveRecord::Base.time_zone_aware_attributes = old_tz
- end
+ def in_time_zone(zone)
+ old_zone = Time.zone
+ old_tz = ActiveRecord::Base.time_zone_aware_attributes
+
+ Time.zone = zone ? ActiveSupport::TimeZone[zone] : nil
+ ActiveRecord::Base.time_zone_aware_attributes = !zone.nil?
+ yield
+ ensure
+ Time.zone = old_zone
+ ActiveRecord::Base.time_zone_aware_attributes = old_tz
+ end
end
-require 'mocha/setup' # FIXME: stop using mocha
+require "mocha/setup" # FIXME: stop using mocha
diff --git a/activerecord/test/cases/hot_compatibility_test.rb b/activerecord/test/cases/hot_compatibility_test.rb
index 9fc75b7377..e107ff2362 100644
--- a/activerecord/test/cases/hot_compatibility_test.rb
+++ b/activerecord/test/cases/hot_compatibility_test.rb
@@ -1,5 +1,5 @@
-require 'cases/helper'
-require 'support/connection_helper'
+require "cases/helper"
+require "support/connection_helper"
class HotCompatibilityTest < ActiveRecord::TestCase
self.use_transactional_tests = false
@@ -12,7 +12,7 @@ class HotCompatibilityTest < ActiveRecord::TestCase
t.string :bar
end
- def self.name; 'HotCompatibility'; end
+ def self.name; "HotCompatibility"; end
end
end
@@ -35,29 +35,29 @@ class HotCompatibilityTest < ActiveRecord::TestCase
# but we can successfully create a record so long as we don't
# reference the removed column
- record = @klass.create! foo: 'foo'
+ record = @klass.create! foo: "foo"
record.reload
- assert_equal 'foo', record.foo
+ assert_equal "foo", record.foo
end
test "update after remove_column" do
- record = @klass.create! foo: 'foo'
+ record = @klass.create! foo: "foo"
assert_equal 3, @klass.columns.length
@klass.connection.remove_column :hot_compatibilities, :bar
assert_equal 3, @klass.columns.length
record.reload
- assert_equal 'foo', record.foo
- record.foo = 'bar'
+ assert_equal "foo", record.foo
+ record.foo = "bar"
record.save!
record.reload
- assert_equal 'bar', record.foo
+ assert_equal "bar", record.foo
end
if current_adapter?(:PostgreSQLAdapter)
test "cleans up after prepared statement failure in a transaction" do
with_two_connections do |original_connection, ddl_connection|
- record = @klass.create! bar: 'bar'
+ record = @klass.create! bar: "bar"
# prepare the reload statement in a transaction
@klass.transaction do
@@ -83,7 +83,7 @@ class HotCompatibilityTest < ActiveRecord::TestCase
test "cleans up after prepared statement failure in nested transactions" do
with_two_connections do |original_connection, ddl_connection|
- record = @klass.create! bar: 'bar'
+ record = @klass.create! bar: "bar"
# prepare the reload statement in a transaction
@klass.transaction do
@@ -114,29 +114,29 @@ class HotCompatibilityTest < ActiveRecord::TestCase
private
- def get_prepared_statement_cache(connection)
- connection.instance_variable_get(:@statements)
- .instance_variable_get(:@cache)[Process.pid]
- end
+ def get_prepared_statement_cache(connection)
+ connection.instance_variable_get(:@statements)
+ .instance_variable_get(:@cache)[Process.pid]
+ end
- # Rails will automatically clear the prepared statements on the connection
- # that runs the migration, so we use two connections to simulate what would
- # actually happen on a production system; we'd have one connection running the
- # migration from the rake task ("ddl_connection" here), and we'd have another
- # connection in a web worker.
- def with_two_connections
- run_without_connection do |original_connection|
- ActiveRecord::Base.establish_connection(original_connection.merge(pool_size: 2))
- begin
- ddl_connection = ActiveRecord::Base.connection_pool.checkout
+ # Rails will automatically clear the prepared statements on the connection
+ # that runs the migration, so we use two connections to simulate what would
+ # actually happen on a production system; we'd have one connection running the
+ # migration from the rake task ("ddl_connection" here), and we'd have another
+ # connection in a web worker.
+ def with_two_connections
+ run_without_connection do |original_connection|
+ ActiveRecord::Base.establish_connection(original_connection.merge(pool_size: 2))
begin
- yield original_connection, ddl_connection
+ ddl_connection = ActiveRecord::Base.connection_pool.checkout
+ begin
+ yield original_connection, ddl_connection
+ ensure
+ ActiveRecord::Base.connection_pool.checkin ddl_connection
+ end
ensure
- ActiveRecord::Base.connection_pool.checkin ddl_connection
+ ActiveRecord::Base.clear_all_connections!
end
- ensure
- ActiveRecord::Base.clear_all_connections!
end
end
- end
end
diff --git a/activerecord/test/cases/i18n_test.rb b/activerecord/test/cases/i18n_test.rb
index a428f1d87b..7f03c5b23d 100644
--- a/activerecord/test/cases/i18n_test.rb
+++ b/activerecord/test/cases/i18n_test.rb
@@ -1,45 +1,44 @@
require "cases/helper"
-require 'models/topic'
-require 'models/reply'
+require "models/topic"
+require "models/reply"
class ActiveRecordI18nTests < ActiveRecord::TestCase
-
def setup
I18n.backend = I18n::Backend::Simple.new
end
def test_translated_model_attributes
- I18n.backend.store_translations 'en', :activerecord => {:attributes => {:topic => {:title => 'topic title attribute'} } }
- assert_equal 'topic title attribute', Topic.human_attribute_name('title')
+ I18n.backend.store_translations "en", activerecord: { attributes: { topic: { title: "topic title attribute" } } }
+ assert_equal "topic title attribute", Topic.human_attribute_name("title")
end
def test_translated_model_attributes_with_symbols
- I18n.backend.store_translations 'en', :activerecord => {:attributes => {:topic => {:title => 'topic title attribute'} } }
- assert_equal 'topic title attribute', Topic.human_attribute_name(:title)
+ I18n.backend.store_translations "en", activerecord: { attributes: { topic: { title: "topic title attribute" } } }
+ assert_equal "topic title attribute", Topic.human_attribute_name(:title)
end
def test_translated_model_attributes_with_sti
- I18n.backend.store_translations 'en', :activerecord => {:attributes => {:reply => {:title => 'reply title attribute'} } }
- assert_equal 'reply title attribute', Reply.human_attribute_name('title')
+ I18n.backend.store_translations "en", activerecord: { attributes: { reply: { title: "reply title attribute" } } }
+ assert_equal "reply title attribute", Reply.human_attribute_name("title")
end
def test_translated_model_attributes_with_sti_fallback
- I18n.backend.store_translations 'en', :activerecord => {:attributes => {:topic => {:title => 'topic title attribute'} } }
- assert_equal 'topic title attribute', Reply.human_attribute_name('title')
+ I18n.backend.store_translations "en", activerecord: { attributes: { topic: { title: "topic title attribute" } } }
+ assert_equal "topic title attribute", Reply.human_attribute_name("title")
end
def test_translated_model_names
- I18n.backend.store_translations 'en', :activerecord => {:models => {:topic => 'topic model'} }
- assert_equal 'topic model', Topic.model_name.human
+ I18n.backend.store_translations "en", activerecord: { models: { topic: "topic model" } }
+ assert_equal "topic model", Topic.model_name.human
end
def test_translated_model_names_with_sti
- I18n.backend.store_translations 'en', :activerecord => {:models => {:reply => 'reply model'} }
- assert_equal 'reply model', Reply.model_name.human
+ I18n.backend.store_translations "en", activerecord: { models: { reply: "reply model" } }
+ assert_equal "reply model", Reply.model_name.human
end
def test_translated_model_names_with_sti_fallback
- I18n.backend.store_translations 'en', :activerecord => {:models => {:topic => 'topic model'} }
- assert_equal 'topic model', Reply.model_name.human
+ I18n.backend.store_translations "en", activerecord: { models: { topic: "topic model" } }
+ assert_equal "topic model", Reply.model_name.human
end
end
diff --git a/activerecord/test/cases/inheritance_test.rb b/activerecord/test/cases/inheritance_test.rb
index e234b9a6a9..e570e9ac1d 100644
--- a/activerecord/test/cases/inheritance_test.rb
+++ b/activerecord/test/cases/inheritance_test.rb
@@ -1,13 +1,13 @@
-require 'cases/helper'
-require 'models/author'
-require 'models/company'
-require 'models/person'
-require 'models/post'
-require 'models/project'
-require 'models/subscriber'
-require 'models/vegetables'
-require 'models/shop'
-require 'models/sponsor'
+require "cases/helper"
+require "models/author"
+require "models/company"
+require "models/person"
+require "models/post"
+require "models/project"
+require "models/subscriber"
+require "models/vegetables"
+require "models/shop"
+require "models/sponsor"
module InheritanceTestHelper
def with_store_full_sti_class(&block)
@@ -33,7 +33,7 @@ class InheritanceTest < ActiveRecord::TestCase
def test_class_with_store_full_sti_class_returns_full_name
with_store_full_sti_class do
- assert_equal 'Namespaced::Company', Namespaced::Company.sti_name
+ assert_equal "Namespaced::Company", Namespaced::Company.sti_name
end
end
@@ -42,37 +42,37 @@ class InheritanceTest < ActiveRecord::TestCase
company = company.dup
company.extend(Module.new {
def _read_attribute(name)
- return ' ' if name == 'type'
+ return " " if name == "type"
super
end
})
company.save!
company = Company.all.to_a.find { |x| x.id == company.id }
- assert_equal ' ', company.type
+ assert_equal " ", company.type
end
def test_class_without_store_full_sti_class_returns_demodulized_name
without_store_full_sti_class do
- assert_equal 'Company', Namespaced::Company.sti_name
+ assert_equal "Company", Namespaced::Company.sti_name
end
end
def test_compute_type_success
- assert_equal Author, ActiveRecord::Base.send(:compute_type, 'Author')
+ assert_equal Author, Company.send(:compute_type, "Author")
end
def test_compute_type_nonexistent_constant
e = assert_raises NameError do
- ActiveRecord::Base.send :compute_type, 'NonexistentModel'
+ Company.send :compute_type, "NonexistentModel"
end
- assert_equal 'uninitialized constant ActiveRecord::Base::NonexistentModel', e.message
- assert_equal 'ActiveRecord::Base::NonexistentModel', e.name
+ assert_equal "uninitialized constant Company::NonexistentModel", e.message
+ assert_equal "Company::NonexistentModel", e.name
end
def test_compute_type_no_method_error
- ActiveSupport::Dependencies.stub(:safe_constantize, proc{ raise NoMethodError }) do
+ ActiveSupport::Dependencies.stub(:safe_constantize, proc { raise NoMethodError }) do
assert_raises NoMethodError do
- ActiveRecord::Base.send :compute_type, 'InvalidModel'
+ Company.send :compute_type, "InvalidModel"
end
end
end
@@ -87,19 +87,19 @@ class InheritanceTest < ActiveRecord::TestCase
error = e
end
- ActiveSupport::Dependencies.stub(:safe_constantize, proc{ raise e }) do
+ ActiveSupport::Dependencies.stub(:safe_constantize, proc { raise e }) do
exception = assert_raises NameError do
- ActiveRecord::Base.send :compute_type, 'InvalidModel'
+ Company.send :compute_type, "InvalidModel"
end
assert_equal error.message, exception.message
end
end
def test_compute_type_argument_error
- ActiveSupport::Dependencies.stub(:safe_constantize, proc{ raise ArgumentError }) do
+ ActiveSupport::Dependencies.stub(:safe_constantize, proc { raise ArgumentError }) do
assert_raises ArgumentError do
- ActiveRecord::Base.send :compute_type, 'InvalidModel'
+ Company.send :compute_type, "InvalidModel"
end
end
end
@@ -107,14 +107,14 @@ class InheritanceTest < ActiveRecord::TestCase
def test_should_store_demodulized_class_name_with_store_full_sti_class_option_disabled
without_store_full_sti_class do
item = Namespaced::Company.new
- assert_equal 'Company', item[:type]
+ assert_equal "Company", item[:type]
end
end
def test_should_store_full_class_name_with_store_full_sti_class_option_enabled
with_store_full_sti_class do
item = Namespaced::Company.new
- assert_equal 'Namespaced::Company', item[:type]
+ assert_equal "Namespaced::Company", item[:type]
end
end
@@ -154,9 +154,9 @@ class InheritanceTest < ActiveRecord::TestCase
def test_company_descends_from_active_record
assert !ActiveRecord::Base.descends_from_active_record?
- assert AbstractCompany.descends_from_active_record?, 'AbstractCompany should descend from ActiveRecord::Base'
- assert Company.descends_from_active_record?, 'Company should descend from ActiveRecord::Base'
- assert !Class.new(Company).descends_from_active_record?, 'Company subclass should not descend from ActiveRecord::Base'
+ assert AbstractCompany.descends_from_active_record?, "AbstractCompany should descend from ActiveRecord::Base"
+ assert Company.descends_from_active_record?, "Company should descend from ActiveRecord::Base"
+ assert !Class.new(Company).descends_from_active_record?, "Company subclass should not descend from ActiveRecord::Base"
end
def test_abstract_class
@@ -214,7 +214,7 @@ class InheritanceTest < ActiveRecord::TestCase
def test_becomes_and_change_tracking_for_inheritance_columns
cucumber = Vegetable.find(1)
cabbage = cucumber.becomes!(Cabbage)
- assert_equal ['Cucumber', 'Cabbage'], cabbage.custom_type_change
+ assert_equal ["Cucumber", "Cabbage"], cabbage.custom_type_change
end
def test_alt_becomes_bang_resets_inheritance_type_column
@@ -229,13 +229,13 @@ class InheritanceTest < ActiveRecord::TestCase
end
def test_inheritance_find_all
- companies = Company.all.merge!(:order => 'id').to_a
+ companies = Company.all.merge!(order: "id").to_a
assert_kind_of Firm, companies[0], "37signals should be a firm"
assert_kind_of Client, companies[1], "Summit should be a client"
end
def test_alt_inheritance_find_all
- companies = Vegetable.all.merge!(:order => 'id').to_a
+ companies = Vegetable.all.merge!(order: "id").to_a
assert_kind_of Cucumber, companies[0]
assert_kind_of Cabbage, companies[1]
end
@@ -250,7 +250,7 @@ class InheritanceTest < ActiveRecord::TestCase
end
def test_alt_inheritance_save
- cabbage = Cabbage.new(:name => 'Savoy')
+ cabbage = Cabbage.new(name: "Savoy")
cabbage.save!
savoy = Vegetable.find(cabbage.id)
@@ -263,12 +263,12 @@ class InheritanceTest < ActiveRecord::TestCase
end
def test_inheritance_new_with_base_class
- company = Company.new(:type => 'Company')
+ company = Company.new(type: "Company")
assert_equal Company, company.class
end
def test_inheritance_new_with_subclass
- firm = Company.new(:type => 'Firm')
+ firm = Company.new(type: "Firm")
assert_equal Firm, firm.class
end
@@ -287,17 +287,17 @@ class InheritanceTest < ActiveRecord::TestCase
end
def test_new_with_invalid_type
- assert_raise(ActiveRecord::SubclassNotFound) { Company.new(:type => 'InvalidType') }
+ assert_raise(ActiveRecord::SubclassNotFound) { Company.new(type: "InvalidType") }
end
def test_new_with_unrelated_type
- assert_raise(ActiveRecord::SubclassNotFound) { Company.new(:type => 'Account') }
+ assert_raise(ActiveRecord::SubclassNotFound) { Company.new(type: "Account") }
end
def test_new_with_unrelated_namespaced_type
without_store_full_sti_class do
e = assert_raises ActiveRecord::SubclassNotFound do
- Namespaced::Company.new(type: 'Firm')
+ Namespaced::Company.new(type: "Firm")
end
assert_equal "Invalid single-table inheritance type: Namespaced::Firm is not a subclass of Namespaced::Company", e.message
@@ -305,21 +305,21 @@ class InheritanceTest < ActiveRecord::TestCase
end
def test_new_with_complex_inheritance
- assert_nothing_raised { Client.new(type: 'VerySpecialClient') }
+ assert_nothing_raised { Client.new(type: "VerySpecialClient") }
end
def test_new_without_storing_full_sti_class
without_store_full_sti_class do
- item = Company.new(type: 'SpecialCo')
+ item = Company.new(type: "SpecialCo")
assert_instance_of Company::SpecialCo, item
end
end
def test_new_with_autoload_paths
- path = File.expand_path('../../models/autoloadable', __FILE__)
+ path = File.expand_path("../../models/autoloadable", __FILE__)
ActiveSupport::Dependencies.autoload_paths << path
- firm = Company.new(:type => 'ExtraFirm')
+ firm = Company.new(type: "ExtraFirm")
assert_equal ExtraFirm, firm.class
ensure
ActiveSupport::Dependencies.autoload_paths.reject! { |p| p == path }
@@ -352,7 +352,7 @@ class InheritanceTest < ActiveRecord::TestCase
Client.update_all "name = 'I am a client'"
assert_equal "I am a client", Client.first.name
# Order by added as otherwise Oracle tests were failing because of different order of results
- assert_equal "37signals", Firm.all.merge!(:order => "id").to_a.first.name
+ assert_equal "37signals", Firm.all.merge!(order: "id").to_a.first.name
end
def test_alt_update_all_within_inheritance
@@ -374,51 +374,51 @@ class InheritanceTest < ActiveRecord::TestCase
end
def test_find_first_within_inheritance
- assert_kind_of Firm, Company.all.merge!(:where => "name = '37signals'").first
- assert_kind_of Firm, Firm.all.merge!(:where => "name = '37signals'").first
- assert_nil Client.all.merge!(:where => "name = '37signals'").first
+ assert_kind_of Firm, Company.all.merge!(where: "name = '37signals'").first
+ assert_kind_of Firm, Firm.all.merge!(where: "name = '37signals'").first
+ assert_nil Client.all.merge!(where: "name = '37signals'").first
end
def test_alt_find_first_within_inheritance
- assert_kind_of Cabbage, Vegetable.all.merge!(:where => "name = 'his cabbage'").first
- assert_kind_of Cabbage, Cabbage.all.merge!(:where => "name = 'his cabbage'").first
- assert_nil Cucumber.all.merge!(:where => "name = 'his cabbage'").first
+ assert_kind_of Cabbage, Vegetable.all.merge!(where: "name = 'his cabbage'").first
+ assert_kind_of Cabbage, Cabbage.all.merge!(where: "name = 'his cabbage'").first
+ assert_nil Cucumber.all.merge!(where: "name = 'his cabbage'").first
end
def test_complex_inheritance
very_special_client = VerySpecialClient.create("name" => "veryspecial")
assert_equal very_special_client, VerySpecialClient.where("name = 'veryspecial'").first
- assert_equal very_special_client, SpecialClient.all.merge!(:where => "name = 'veryspecial'").first
- assert_equal very_special_client, Company.all.merge!(:where => "name = 'veryspecial'").first
- assert_equal very_special_client, Client.all.merge!(:where => "name = 'veryspecial'").first
- assert_equal 1, Client.all.merge!(:where => "name = 'Summit'").to_a.size
+ assert_equal very_special_client, SpecialClient.all.merge!(where: "name = 'veryspecial'").first
+ assert_equal very_special_client, Company.all.merge!(where: "name = 'veryspecial'").first
+ assert_equal very_special_client, Client.all.merge!(where: "name = 'veryspecial'").first
+ assert_equal 1, Client.all.merge!(where: "name = 'Summit'").to_a.size
assert_equal very_special_client, Client.find(very_special_client.id)
end
def test_alt_complex_inheritance
king_cole = KingCole.create("name" => "uniform heads")
assert_equal king_cole, KingCole.where("name = 'uniform heads'").first
- assert_equal king_cole, GreenCabbage.all.merge!(:where => "name = 'uniform heads'").first
- assert_equal king_cole, Cabbage.all.merge!(:where => "name = 'uniform heads'").first
- assert_equal king_cole, Vegetable.all.merge!(:where => "name = 'uniform heads'").first
- assert_equal 1, Cabbage.all.merge!(:where => "name = 'his cabbage'").to_a.size
+ assert_equal king_cole, GreenCabbage.all.merge!(where: "name = 'uniform heads'").first
+ assert_equal king_cole, Cabbage.all.merge!(where: "name = 'uniform heads'").first
+ assert_equal king_cole, Vegetable.all.merge!(where: "name = 'uniform heads'").first
+ assert_equal 1, Cabbage.all.merge!(where: "name = 'his cabbage'").to_a.size
assert_equal king_cole, Cabbage.find(king_cole.id)
end
def test_eager_load_belongs_to_something_inherited
- account = Account.all.merge!(:includes => :firm).find(1)
+ account = Account.all.merge!(includes: :firm).find(1)
assert account.association(:firm).loaded?, "association was not eager loaded"
end
def test_alt_eager_loading
- cabbage = RedCabbage.all.merge!(:includes => :seller).find(4)
+ cabbage = RedCabbage.all.merge!(includes: :seller).find(4)
assert cabbage.association(:seller).loaded?, "association was not eager loaded"
end
def test_eager_load_belongs_to_primary_key_quoting
con = Account.connection
assert_sql(/#{con.quote_table_name('companies')}.#{con.quote_column_name('id')} = 1/) do
- Account.all.merge!(:includes => :firm).find(1)
+ Account.all.merge!(includes: :firm).find(1)
end
end
@@ -428,7 +428,7 @@ class InheritanceTest < ActiveRecord::TestCase
def test_inheritance_without_mapping
assert_kind_of SpecialSubscriber, SpecialSubscriber.find("webster132")
- assert_nothing_raised { s = SpecialSubscriber.new("name" => "And breaaaaathe!"); s.id = 'roger'; s.save }
+ assert_nothing_raised { s = SpecialSubscriber.new("name" => "And breaaaaathe!"); s.id = "roger"; s.save }
end
def test_scope_inherited_properly
@@ -449,7 +449,7 @@ class InheritanceComputeTypeTest < ActiveRecord::TestCase
def test_instantiation_doesnt_try_to_require_corresponding_file
without_store_full_sti_class do
foo = Firm.first.clone
- foo.type = 'FirmOnTheFly'
+ foo.type = "FirmOnTheFly"
foo.save!
# Should fail without FirmOnTheFly in the type condition.
@@ -470,30 +470,30 @@ class InheritanceComputeTypeTest < ActiveRecord::TestCase
end
def test_sti_type_from_attributes_disabled_in_non_sti_class
- phone = Shop::Product::Type.new(name: 'Phone')
- product = Shop::Product.new(:type => phone)
+ phone = Shop::Product::Type.new(name: "Phone")
+ product = Shop::Product.new(type: phone)
assert product.save
end
def test_inheritance_new_with_subclass_as_default
original_type = Company.columns_hash["type"].default
- ActiveRecord::Base.connection.change_column_default :companies, :type, 'Firm'
+ ActiveRecord::Base.connection.change_column_default :companies, :type, "Firm"
Company.reset_column_information
firm = Company.new # without arguments
- assert_equal 'Firm', firm.type
+ assert_equal "Firm", firm.type
assert_instance_of Firm, firm
- firm = Company.new(firm_name: 'Shri Hans Plastic') # with arguments
- assert_equal 'Firm', firm.type
+ firm = Company.new(firm_name: "Shri Hans Plastic") # with arguments
+ assert_equal "Firm", firm.type
assert_instance_of Firm, firm
client = Client.new
- assert_equal 'Client', client.type
+ assert_equal "Client", client.type
assert_instance_of Client, client
- firm = Company.new(type: 'Client') # overwrite the default type
- assert_equal 'Client', firm.type
+ firm = Company.new(type: "Client") # overwrite the default type
+ assert_equal "Client", firm.type
assert_instance_of Client, firm
ensure
ActiveRecord::Base.connection.change_column_default :companies, :type, original_type
@@ -502,9 +502,8 @@ class InheritanceComputeTypeTest < ActiveRecord::TestCase
end
class InheritanceAttributeTest < ActiveRecord::TestCase
-
class Company < ActiveRecord::Base
- self.table_name = 'companies'
+ self.table_name = "companies"
attribute :type, :string, default: "InheritanceAttributeTest::Startup"
end
@@ -516,11 +515,11 @@ class InheritanceAttributeTest < ActiveRecord::TestCase
def test_inheritance_new_with_subclass_as_default
startup = Company.new # without arguments
- assert_equal 'InheritanceAttributeTest::Startup', startup.type
+ assert_equal "InheritanceAttributeTest::Startup", startup.type
assert_instance_of Startup, startup
- empire = Company.new(type: 'InheritanceAttributeTest::Empire') # without arguments
- assert_equal 'InheritanceAttributeTest::Empire', empire.type
+ empire = Company.new(type: "InheritanceAttributeTest::Empire") # without arguments
+ assert_equal "InheritanceAttributeTest::Empire", empire.type
assert_instance_of Empire, empire
end
end
@@ -555,7 +554,7 @@ class InheritanceAttributeMappingTest < ActiveRecord::TestCase
end
class Company < ActiveRecord::Base
- self.table_name = 'companies'
+ self.table_name = "companies"
attribute :type, :omg_sti
end
@@ -563,18 +562,18 @@ class InheritanceAttributeMappingTest < ActiveRecord::TestCase
class Empire < Company; end
class Sponsor < ActiveRecord::Base
- self.table_name = 'sponsors'
+ self.table_name = "sponsors"
attribute :sponsorable_type, :omg_sti
belongs_to :sponsorable, polymorphic: true
end
def test_sti_with_custom_type
- Startup.create! name: 'a Startup'
- Empire.create! name: 'an Empire'
+ Startup.create! name: "a Startup"
+ Empire.create! name: "an Empire"
assert_equal [["a Startup", "omg_inheritance_attribute_mapping_test/startup"],
- ["an Empire", "omg_inheritance_attribute_mapping_test/empire"]], ActiveRecord::Base.connection.select_rows('SELECT name, type FROM companies').sort
+ ["an Empire", "omg_inheritance_attribute_mapping_test/empire"]], ActiveRecord::Base.connection.select_rows("SELECT name, type FROM companies").sort
assert_equal [["a Startup", "InheritanceAttributeMappingTest::Startup"],
["an Empire", "InheritanceAttributeMappingTest::Empire"]], Company.all.map { |a| [a.name, a.type] }.sort
@@ -583,17 +582,17 @@ class InheritanceAttributeMappingTest < ActiveRecord::TestCase
startup.save!
assert_equal [["a Startup", "omg_inheritance_attribute_mapping_test/empire"],
- ["an Empire", "omg_inheritance_attribute_mapping_test/empire"]], ActiveRecord::Base.connection.select_rows('SELECT name, type FROM companies').sort
+ ["an Empire", "omg_inheritance_attribute_mapping_test/empire"]], ActiveRecord::Base.connection.select_rows("SELECT name, type FROM companies").sort
assert_equal [["a Startup", "InheritanceAttributeMappingTest::Empire"],
["an Empire", "InheritanceAttributeMappingTest::Empire"]], Company.all.map { |a| [a.name, a.type] }.sort
end
def test_polymorphic_associations_custom_type
- startup = Startup.create! name: 'a Startup'
+ startup = Startup.create! name: "a Startup"
sponsor = Sponsor.create! sponsorable: startup
- assert_equal ["omg_inheritance_attribute_mapping_test/company"], ActiveRecord::Base.connection.select_values('SELECT sponsorable_type FROM sponsors')
+ assert_equal ["omg_inheritance_attribute_mapping_test/company"], ActiveRecord::Base.connection.select_values("SELECT sponsorable_type FROM sponsors")
sponsor = Sponsor.first
assert_equal startup, sponsor.sponsorable
diff --git a/activerecord/test/cases/integration_test.rb b/activerecord/test/cases/integration_test.rb
index 08a186ae07..d7aa091623 100644
--- a/activerecord/test/cases/integration_test.rb
+++ b/activerecord/test/cases/integration_test.rb
@@ -1,10 +1,10 @@
-require 'cases/helper'
-require 'models/company'
-require 'models/developer'
-require 'models/computer'
-require 'models/owner'
-require 'models/pet'
+require "cases/helper"
+require "models/company"
+require "models/developer"
+require "models/computer"
+require "models/owner"
+require "models/pet"
class IntegrationTest < ActiveRecord::TestCase
fixtures :companies, :developers, :owners, :pets
@@ -15,59 +15,85 @@ class IntegrationTest < ActiveRecord::TestCase
def test_to_param_returns_nil_if_not_persisted
client = Client.new
- assert_equal nil, client.to_param
+ assert_nil client.to_param
end
def test_to_param_returns_id_if_not_persisted_but_id_is_set
client = Client.new
client.id = 1
- assert_equal '1', client.to_param
+ assert_equal "1", client.to_param
end
def test_to_param_class_method
firm = Firm.find(4)
- assert_equal '4-flamboyant-software', firm.to_param
+ assert_equal "4-flamboyant-software", firm.to_param
+ end
+
+ def test_to_param_class_method_truncates_words_properly
+ firm = Firm.find(4)
+ firm.name << ", Inc."
+ assert_equal "4-flamboyant-software", firm.to_param
+ end
+
+ def test_to_param_class_method_truncates_after_parameterize
+ firm = Firm.find(4)
+ firm.name = "Huey, Dewey, & Louie LLC"
+ # 123456789T123456789v
+ assert_equal "4-huey-dewey-louie-llc", firm.to_param
+ end
+
+ def test_to_param_class_method_truncates_after_parameterize_with_hyphens
+ firm = Firm.find(4)
+ firm.name = "Door-to-Door Wash-n-Fold Service"
+ # 123456789T123456789v
+ assert_equal "4-door-to-door-wash-n", firm.to_param
end
def test_to_param_class_method_truncates
firm = Firm.find(4)
- firm.name = 'a ' * 100
- assert_equal '4-a-a-a-a-a-a-a-a-a', firm.to_param
+ firm.name = "a " * 100
+ assert_equal "4-a-a-a-a-a-a-a-a-a-a", firm.to_param
end
def test_to_param_class_method_truncates_edge_case
firm = Firm.find(4)
- firm.name = 'David HeinemeierHansson'
- assert_equal '4-david', firm.to_param
+ firm.name = "David HeinemeierHansson"
+ assert_equal "4-david", firm.to_param
+ end
+
+ def test_to_param_class_method_truncates_case_shown_in_doc
+ firm = Firm.find(4)
+ firm.name = "David Heinemeier Hansson"
+ assert_equal "4-david-heinemeier", firm.to_param
end
def test_to_param_class_method_squishes
firm = Firm.find(4)
firm.name = "ab \n" * 100
- assert_equal '4-ab-ab-ab-ab-ab-ab', firm.to_param
+ assert_equal "4-ab-ab-ab-ab-ab-ab-ab", firm.to_param
end
def test_to_param_class_method_multibyte_character
firm = Firm.find(4)
firm.name = "戦場ヶ原 ひたぎ"
- assert_equal '4', firm.to_param
+ assert_equal "4", firm.to_param
end
def test_to_param_class_method_uses_default_if_blank
firm = Firm.find(4)
firm.name = nil
- assert_equal '4', firm.to_param
- firm.name = ' '
- assert_equal '4', firm.to_param
+ assert_equal "4", firm.to_param
+ firm.name = " "
+ assert_equal "4", firm.to_param
end
def test_to_param_class_method_uses_default_if_not_persisted
- firm = Firm.new(name: 'Fancy Shirts')
- assert_equal nil, firm.to_param
+ firm = Firm.new(name: "Fancy Shirts")
+ assert_nil firm.to_param
end
def test_to_param_with_no_arguments
- assert_equal 'Firm', Firm.to_param
+ assert_equal "Firm", Firm.to_param
end
def test_cache_key_for_existing_record_is_not_timezone_dependent
@@ -146,4 +172,10 @@ class IntegrationTest < ActiveRecord::TestCase
owner = owners(:blackbeard)
assert_equal "owners/#{owner.id}-#{owner.happy_at.utc.to_s(:usec)}", owner.cache_key(:updated_at, :happy_at)
end
+
+ def test_cache_key_when_named_timestamp_is_nil
+ owner = owners(:blackbeard)
+ owner.happy_at = nil
+ assert_equal "owners/#{owner.id}", owner.cache_key(:happy_at)
+ end
end
diff --git a/activerecord/test/cases/invalid_connection_test.rb b/activerecord/test/cases/invalid_connection_test.rb
index a16b52751a..1367af2859 100644
--- a/activerecord/test/cases/invalid_connection_test.rb
+++ b/activerecord/test/cases/invalid_connection_test.rb
@@ -1,24 +1,24 @@
require "cases/helper"
if current_adapter?(:Mysql2Adapter)
-class TestAdapterWithInvalidConnection < ActiveRecord::TestCase
- self.use_transactional_tests = false
+ class TestAdapterWithInvalidConnection < ActiveRecord::TestCase
+ self.use_transactional_tests = false
- class Bird < ActiveRecord::Base
- end
+ class Bird < ActiveRecord::Base
+ end
- def setup
- # Can't just use current adapter; sqlite3 will create a database
- # file on the fly.
- Bird.establish_connection adapter: 'mysql2', database: 'i_do_not_exist'
- end
+ def setup
+ # Can't just use current adapter; sqlite3 will create a database
+ # file on the fly.
+ Bird.establish_connection adapter: "mysql2", database: "i_do_not_exist"
+ end
- teardown do
- Bird.remove_connection
- end
+ teardown do
+ Bird.remove_connection
+ end
- test "inspect on Model class does not raise" do
- assert_equal "#{Bird.name} (call '#{Bird.name}.connection' to establish a connection)", Bird.inspect
+ test "inspect on Model class does not raise" do
+ assert_equal "#{Bird.name} (call '#{Bird.name}.connection' to establish a connection)", Bird.inspect
+ end
end
end
-end
diff --git a/activerecord/test/cases/invertible_migration_test.rb b/activerecord/test/cases/invertible_migration_test.rb
index aba854820b..cc3951e2ba 100644
--- a/activerecord/test/cases/invertible_migration_test.rb
+++ b/activerecord/test/cases/invertible_migration_test.rb
@@ -6,7 +6,7 @@ end
module ActiveRecord
class InvertibleMigrationTest < ActiveRecord::TestCase
class SilentMigration < ActiveRecord::Migration::Current
- def write(text = '')
+ def write(text = "")
# sssshhhhh!!
end
end
@@ -165,10 +165,8 @@ module ActiveRecord
teardown do
%w[horses new_horses].each do |table|
- ActiveSupport::Deprecation.silence do
- if ActiveRecord::Base.connection.table_exists?(table)
- ActiveRecord::Base.connection.drop_table(table)
- end
+ if ActiveRecord::Base.connection.table_exists?(table)
+ ActiveRecord::Base.connection.drop_table(table)
end
end
ActiveRecord::Migration.verbose = @verbose_was
@@ -199,14 +197,14 @@ module ActiveRecord
def test_migrate_up
migration = InvertibleMigration.new
migration.migrate(:up)
- ActiveSupport::Deprecation.silence { assert migration.connection.table_exists?("horses"), "horses should exist" }
+ assert migration.connection.table_exists?("horses"), "horses should exist"
end
def test_migrate_down
migration = InvertibleMigration.new
migration.migrate :up
migration.migrate :down
- ActiveSupport::Deprecation.silence { assert !migration.connection.table_exists?("horses") }
+ assert !migration.connection.table_exists?("horses")
end
def test_migrate_revert
@@ -214,36 +212,30 @@ module ActiveRecord
revert = InvertibleRevertMigration.new
migration.migrate :up
revert.migrate :up
- ActiveSupport::Deprecation.silence { assert !migration.connection.table_exists?("horses") }
+ assert !migration.connection.table_exists?("horses")
revert.migrate :down
- ActiveSupport::Deprecation.silence { assert migration.connection.table_exists?("horses") }
+ assert migration.connection.table_exists?("horses")
migration.migrate :down
- ActiveSupport::Deprecation.silence { assert !migration.connection.table_exists?("horses") }
+ assert !migration.connection.table_exists?("horses")
end
def test_migrate_revert_by_part
InvertibleMigration.new.migrate :up
received = []
migration = InvertibleByPartsMigration.new
- migration.test = ->(dir){
- ActiveSupport::Deprecation.silence do
- assert migration.connection.table_exists?("horses")
- assert migration.connection.table_exists?("new_horses")
- end
+ migration.test = ->(dir) {
+ assert migration.connection.table_exists?("horses")
+ assert migration.connection.table_exists?("new_horses")
received << dir
}
migration.migrate :up
assert_equal [:both, :up], received
- ActiveSupport::Deprecation.silence do
- assert !migration.connection.table_exists?("horses")
- assert migration.connection.table_exists?("new_horses")
- end
+ assert !migration.connection.table_exists?("horses")
+ assert migration.connection.table_exists?("new_horses")
migration.migrate :down
assert_equal [:both, :up, :both, :down], received
- ActiveSupport::Deprecation.silence do
- assert migration.connection.table_exists?("horses")
- assert !migration.connection.table_exists?("new_horses")
- end
+ assert migration.connection.table_exists?("horses")
+ assert !migration.connection.table_exists?("new_horses")
end
def test_migrate_revert_whole_migration
@@ -252,20 +244,20 @@ module ActiveRecord
revert = RevertWholeMigration.new(klass)
migration.migrate :up
revert.migrate :up
- ActiveSupport::Deprecation.silence { assert !migration.connection.table_exists?("horses") }
+ assert !migration.connection.table_exists?("horses")
revert.migrate :down
- ActiveSupport::Deprecation.silence { assert migration.connection.table_exists?("horses") }
+ assert migration.connection.table_exists?("horses")
migration.migrate :down
- ActiveSupport::Deprecation.silence { assert !migration.connection.table_exists?("horses") }
+ assert !migration.connection.table_exists?("horses")
end
end
def test_migrate_nested_revert_whole_migration
revert = NestedRevertWholeMigration.new(InvertibleRevertMigration)
revert.migrate :down
- ActiveSupport::Deprecation.silence { assert revert.connection.table_exists?("horses") }
+ assert revert.connection.table_exists?("horses")
revert.migrate :up
- ActiveSupport::Deprecation.silence { assert !revert.connection.table_exists?("horses") }
+ assert !revert.connection.table_exists?("horses")
end
def test_migrate_revert_change_column_default
@@ -291,21 +283,21 @@ module ActiveRecord
migration1.migrate(:up)
migration2.migrate(:up)
- assert_equal true, Horse.connection.extension_enabled?('hstore')
+ assert_equal true, Horse.connection.extension_enabled?("hstore")
migration3.migrate(:up)
- assert_equal false, Horse.connection.extension_enabled?('hstore')
+ assert_equal false, Horse.connection.extension_enabled?("hstore")
migration3.migrate(:down)
- assert_equal true, Horse.connection.extension_enabled?('hstore')
+ assert_equal true, Horse.connection.extension_enabled?("hstore")
migration2.migrate(:down)
- assert_equal false, Horse.connection.extension_enabled?('hstore')
+ assert_equal false, Horse.connection.extension_enabled?("hstore")
end
end
def test_revert_order
- block = Proc.new{|t| t.string :name }
+ block = Proc.new { |t| t.string :name }
recorder = ActiveRecord::Migration::CommandRecorder.new(ActiveRecord::Base.connection)
recorder.instance_eval do
create_table("apples", &block)
@@ -330,35 +322,35 @@ module ActiveRecord
def test_legacy_up
LegacyMigration.migrate :up
- ActiveSupport::Deprecation.silence { assert ActiveRecord::Base.connection.table_exists?("horses"), "horses should exist" }
+ assert ActiveRecord::Base.connection.table_exists?("horses"), "horses should exist"
end
def test_legacy_down
LegacyMigration.migrate :up
LegacyMigration.migrate :down
- ActiveSupport::Deprecation.silence { assert !ActiveRecord::Base.connection.table_exists?("horses"), "horses should not exist" }
+ assert !ActiveRecord::Base.connection.table_exists?("horses"), "horses should not exist"
end
def test_up
LegacyMigration.up
- ActiveSupport::Deprecation.silence { assert ActiveRecord::Base.connection.table_exists?("horses"), "horses should exist" }
+ assert ActiveRecord::Base.connection.table_exists?("horses"), "horses should exist"
end
def test_down
LegacyMigration.up
LegacyMigration.down
- ActiveSupport::Deprecation.silence { assert !ActiveRecord::Base.connection.table_exists?("horses"), "horses should not exist" }
+ assert !ActiveRecord::Base.connection.table_exists?("horses"), "horses should not exist"
end
def test_migrate_down_with_table_name_prefix
- ActiveRecord::Base.table_name_prefix = 'p_'
- ActiveRecord::Base.table_name_suffix = '_s'
+ ActiveRecord::Base.table_name_prefix = "p_"
+ ActiveRecord::Base.table_name_suffix = "_s"
migration = InvertibleMigration.new
migration.migrate(:up)
assert_nothing_raised { migration.migrate(:down) }
- ActiveSupport::Deprecation.silence { assert !ActiveRecord::Base.connection.table_exists?("p_horses_s"), "p_horses_s should not exist" }
+ assert !ActiveRecord::Base.connection.table_exists?("p_horses_s"), "p_horses_s should not exist"
ensure
- ActiveRecord::Base.table_name_prefix = ActiveRecord::Base.table_name_suffix = ''
+ ActiveRecord::Base.table_name_prefix = ActiveRecord::Base.table_name_suffix = ""
end
def test_migrations_can_handle_foreign_keys_to_specific_tables
@@ -382,6 +374,5 @@ module ActiveRecord
"horses_index_named index should not exist"
end
end
-
end
end
diff --git a/activerecord/test/cases/json_serialization_test.rb b/activerecord/test/cases/json_serialization_test.rb
index a222675918..155e858822 100644
--- a/activerecord/test/cases/json_serialization_test.rb
+++ b/activerecord/test/cases/json_serialization_test.rb
@@ -1,21 +1,21 @@
require "cases/helper"
-require 'models/contact'
-require 'models/post'
-require 'models/author'
-require 'models/tagging'
-require 'models/tag'
-require 'models/comment'
+require "models/contact"
+require "models/post"
+require "models/author"
+require "models/tagging"
+require "models/tag"
+require "models/comment"
module JsonSerializationHelpers
private
- def set_include_root_in_json(value)
- original_root_in_json = ActiveRecord::Base.include_root_in_json
- ActiveRecord::Base.include_root_in_json = value
- yield
- ensure
- ActiveRecord::Base.include_root_in_json = original_root_in_json
- end
+ def set_include_root_in_json(value)
+ original_root_in_json = ActiveRecord::Base.include_root_in_json
+ ActiveRecord::Base.include_root_in_json = value
+ yield
+ ensure
+ ActiveRecord::Base.include_root_in_json = original_root_in_json
+ end
end
class JsonSerializationTest < ActiveRecord::TestCase
@@ -27,18 +27,18 @@ class JsonSerializationTest < ActiveRecord::TestCase
def setup
@contact = Contact.new(
- :name => 'Konata Izumi',
- :age => 16,
- :avatar => 'binarydata',
- :created_at => Time.utc(2006, 8, 1),
- :awesome => true,
- :preferences => { :shows => 'anime' }
+ name: "Konata Izumi",
+ age: 16,
+ avatar: "binarydata",
+ created_at: Time.utc(2006, 8, 1),
+ awesome: true,
+ preferences: { shows: "anime" }
)
end
def test_should_demodulize_root_in_json
set_include_root_in_json(true) do
- @contact = NamespacedContact.new name: 'whatever'
+ @contact = NamespacedContact.new name: "whatever"
json = @contact.to_json
assert_match %r{^\{"namespaced_contact":\{}, json
end
@@ -51,7 +51,7 @@ class JsonSerializationTest < ActiveRecord::TestCase
assert_match %r{^\{"contact":\{}, json
assert_match %r{"name":"Konata Izumi"}, json
assert_match %r{"age":16}, json
- assert json.include?(%("created_at":#{ActiveSupport::JSON.encode(Time.utc(2006, 8, 1))}))
+ assert_includes json, %("created_at":#{ActiveSupport::JSON.encode(Time.utc(2006, 8, 1))})
assert_match %r{"awesome":true}, json
assert_match %r{"preferences":\{"shows":"anime"\}}, json
end
@@ -62,28 +62,28 @@ class JsonSerializationTest < ActiveRecord::TestCase
assert_match %r{"name":"Konata Izumi"}, json
assert_match %r{"age":16}, json
- assert json.include?(%("created_at":#{ActiveSupport::JSON.encode(Time.utc(2006, 8, 1))}))
+ assert_includes json, %("created_at":#{ActiveSupport::JSON.encode(Time.utc(2006, 8, 1))})
assert_match %r{"awesome":true}, json
assert_match %r{"preferences":\{"shows":"anime"\}}, json
end
def test_should_allow_attribute_filtering_with_only
- json = @contact.to_json(:only => [:name, :age])
+ json = @contact.to_json(only: [:name, :age])
assert_match %r{"name":"Konata Izumi"}, json
assert_match %r{"age":16}, json
assert_no_match %r{"awesome":true}, json
- assert !json.include?(%("created_at":#{ActiveSupport::JSON.encode(Time.utc(2006, 8, 1))}))
+ assert_not_includes json, %("created_at":#{ActiveSupport::JSON.encode(Time.utc(2006, 8, 1))})
assert_no_match %r{"preferences":\{"shows":"anime"\}}, json
end
def test_should_allow_attribute_filtering_with_except
- json = @contact.to_json(:except => [:name, :age])
+ json = @contact.to_json(except: [:name, :age])
assert_no_match %r{"name":"Konata Izumi"}, json
assert_no_match %r{"age":16}, json
assert_match %r{"awesome":true}, json
- assert json.include?(%("created_at":#{ActiveSupport::JSON.encode(Time.utc(2006, 8, 1))}))
+ assert_includes json, %("created_at":#{ActiveSupport::JSON.encode(Time.utc(2006, 8, 1))})
assert_match %r{"preferences":\{"shows":"anime"\}}, json
end
@@ -93,16 +93,16 @@ class JsonSerializationTest < ActiveRecord::TestCase
def @contact.favorite_quote; "Constraints are liberating"; end
# Single method.
- assert_match %r{"label":"Has cheezburger"}, @contact.to_json(:only => :name, :methods => :label)
+ assert_match %r{"label":"Has cheezburger"}, @contact.to_json(only: :name, methods: :label)
# Both methods.
- methods_json = @contact.to_json(:only => :name, :methods => [:label, :favorite_quote])
+ methods_json = @contact.to_json(only: :name, methods: [:label, :favorite_quote])
assert_match %r{"label":"Has cheezburger"}, methods_json
assert_match %r{"favorite_quote":"Constraints are liberating"}, methods_json
end
def test_uses_serializable_hash_with_only_option
- def @contact.serializable_hash(options=nil)
+ def @contact.serializable_hash(options = nil)
super(only: %w(name))
end
@@ -113,7 +113,7 @@ class JsonSerializationTest < ActiveRecord::TestCase
end
def test_uses_serializable_hash_with_except_option
- def @contact.serializable_hash(options=nil)
+ def @contact.serializable_hash(options = nil)
super(except: %w(age))
end
@@ -125,7 +125,7 @@ class JsonSerializationTest < ActiveRecord::TestCase
def test_does_not_include_inheritance_column_from_sti
@contact = ContactSti.new(@contact.attributes)
- assert_equal 'ContactSti', @contact.type
+ assert_equal "ContactSti", @contact.type
json = @contact.to_json
assert_match %r{"name":"Konata Izumi"}, json
@@ -135,9 +135,9 @@ class JsonSerializationTest < ActiveRecord::TestCase
def test_serializable_hash_with_default_except_option_and_excluding_inheritance_column_from_sti
@contact = ContactSti.new(@contact.attributes)
- assert_equal 'ContactSti', @contact.type
+ assert_equal "ContactSti", @contact.type
- def @contact.serializable_hash(options={})
+ def @contact.serializable_hash(options = {})
super({ except: %w(age) }.merge!(options))
end
@@ -149,7 +149,7 @@ class JsonSerializationTest < ActiveRecord::TestCase
end
def test_serializable_hash_should_not_modify_options_in_argument
- options = { :only => :name }
+ options = { only: :name }
@contact.serializable_hash(options)
assert_nil options[:except]
@@ -167,7 +167,7 @@ class DatabaseConnectedJsonEncodingTest < ActiveRecord::TestCase
end
def test_includes_uses_association_name
- json = @david.to_json(:include => :posts)
+ json = @david.to_json(include: :posts)
assert_match %r{"posts":\[}, json
@@ -183,7 +183,7 @@ class DatabaseConnectedJsonEncodingTest < ActiveRecord::TestCase
end
def test_includes_uses_association_name_and_applies_attribute_filters
- json = @david.to_json(:include => { :posts => { :only => :title } })
+ json = @david.to_json(include: { posts: { only: :title } })
assert_match %r{"name":"David"}, json
assert_match %r{"posts":\[}, json
@@ -196,7 +196,7 @@ class DatabaseConnectedJsonEncodingTest < ActiveRecord::TestCase
end
def test_includes_fetches_second_level_associations
- json = @david.to_json(:include => { :posts => { :include => { :comments => { :only => :body } } } })
+ json = @david.to_json(include: { posts: { include: { comments: { only: :body } } } })
assert_match %r{"name":"David"}, json
assert_match %r{"posts":\[}, json
@@ -209,12 +209,12 @@ class DatabaseConnectedJsonEncodingTest < ActiveRecord::TestCase
def test_includes_fetches_nth_level_associations
json = @david.to_json(
- :include => {
- :posts => {
- :include => {
- :taggings => {
- :include => {
- :tag => { :only => :name }
+ include: {
+ posts: {
+ include: {
+ taggings: {
+ include: {
+ tag: { only: :name }
}
}
}
@@ -230,8 +230,8 @@ class DatabaseConnectedJsonEncodingTest < ActiveRecord::TestCase
def test_includes_doesnt_merge_opts_from_base
json = @david.to_json(
- :only => :id,
- :include => :posts
+ only: :id,
+ include: :posts
)
assert_match %{"title":"Welcome to the weblog"}, json
@@ -239,11 +239,11 @@ class DatabaseConnectedJsonEncodingTest < ActiveRecord::TestCase
def test_should_not_call_methods_on_associations_that_dont_respond
def @david.favorite_quote; "Constraints are liberating"; end
- json = @david.to_json(:include => :posts, :methods => :favorite_quote)
+ json = @david.to_json(include: :posts, methods: :favorite_quote)
assert !@david.posts.first.respond_to?(:favorite_quote)
assert_match %r{"favorite_quote":"Constraints are liberating"}, json
- assert_equal %r{"favorite_quote":}.match(json).size, 1
+ assert_equal 1, %r{"favorite_quote":}.match(json).size
end
def test_should_allow_only_option_for_list_of_authors
@@ -267,15 +267,15 @@ class DatabaseConnectedJsonEncodingTest < ActiveRecord::TestCase
def test_should_allow_includes_for_list_of_authors
authors = [@david, @mary]
json = ActiveSupport::JSON.encode(authors,
- :only => :name,
- :include => {
- :posts => { :only => :id }
+ only: :name,
+ include: {
+ posts: { only: :id }
}
)
['"name":"David"', '"posts":[', '{"id":1}', '{"id":2}', '{"id":4}',
'{"id":5}', '{"id":6}', '"name":"Mary"', '"posts":[', '{"id":7}', '{"id":9}'].each do |fragment|
- assert json.include?(fragment), json
+ assert_includes json, fragment, json
end
end
diff --git a/activerecord/test/cases/locking_test.rb b/activerecord/test/cases/locking_test.rb
index 9fc0041892..95fb670dac 100644
--- a/activerecord/test/cases/locking_test.rb
+++ b/activerecord/test/cases/locking_test.rb
@@ -1,18 +1,18 @@
-require 'thread'
+require "thread"
require "cases/helper"
-require 'models/person'
-require 'models/job'
-require 'models/reader'
-require 'models/ship'
-require 'models/legacy_thing'
-require 'models/personal_legacy_thing'
-require 'models/reference'
-require 'models/string_key_object'
-require 'models/car'
-require 'models/bulb'
-require 'models/engine'
-require 'models/wheel'
-require 'models/treasure'
+require "models/person"
+require "models/job"
+require "models/reader"
+require "models/ship"
+require "models/legacy_thing"
+require "models/personal_legacy_thing"
+require "models/reference"
+require "models/string_key_object"
+require "models/car"
+require "models/bulb"
+require "models/engine"
+require "models/wheel"
+require "models/treasure"
class LockWithoutDefault < ActiveRecord::Base; end
@@ -33,7 +33,7 @@ class OptimisticLockingTest < ActiveRecord::TestCase
p1 = Person.find(1)
assert_equal 0, p1.lock_version
- p1.first_name = 'anika2'
+ p1.first_name = "anika2"
p1.save!
assert_equal 1, p1.lock_version
@@ -45,12 +45,12 @@ class OptimisticLockingTest < ActiveRecord::TestCase
assert_equal 0, s1.lock_version
assert_equal 0, s2.lock_version
- s1.name = 'updated record'
+ s1.name = "updated record"
s1.save!
assert_equal 1, s1.lock_version
assert_equal 0, s2.lock_version
- s2.name = 'doubly updated record'
+ s2.name = "doubly updated record"
assert_raise(ActiveRecord::StaleObjectError) { s2.save! }
end
@@ -60,7 +60,7 @@ class OptimisticLockingTest < ActiveRecord::TestCase
assert_equal 0, s1.lock_version
assert_equal 0, s2.lock_version
- s1.name = 'updated record'
+ s1.name = "updated record"
s1.save!
assert_equal 1, s1.lock_version
assert_equal 0, s2.lock_version
@@ -78,12 +78,12 @@ class OptimisticLockingTest < ActiveRecord::TestCase
assert_equal 0, p1.lock_version
assert_equal 0, p2.lock_version
- p1.first_name = 'stu'
+ p1.first_name = "stu"
p1.save!
assert_equal 1, p1.lock_version
assert_equal 0, p2.lock_version
- p2.first_name = 'sue'
+ p2.first_name = "sue"
assert_raise(ActiveRecord::StaleObjectError) { p2.save! }
end
@@ -94,7 +94,7 @@ class OptimisticLockingTest < ActiveRecord::TestCase
assert_equal 0, p1.lock_version
assert_equal 0, p2.lock_version
- p1.first_name = 'stu'
+ p1.first_name = "stu"
p1.save!
assert_equal 1, p1.lock_version
assert_equal 0, p2.lock_version
@@ -113,64 +113,56 @@ class OptimisticLockingTest < ActiveRecord::TestCase
assert_equal 0, p1.lock_version
assert_equal 0, p2.lock_version
- p1.first_name = 'stu'
+ p1.first_name = "stu"
p1.save!
assert_equal 1, p1.lock_version
assert_equal 0, p2.lock_version
- p2.first_name = 'sue'
+ p2.first_name = "sue"
assert_raise(ActiveRecord::StaleObjectError) { p2.save! }
- p2.first_name = 'sue2'
+ p2.first_name = "sue2"
assert_raise(ActiveRecord::StaleObjectError) { p2.save! }
end
def test_lock_new
- p1 = Person.new(:first_name => 'anika')
+ p1 = Person.new(first_name: "anika")
assert_equal 0, p1.lock_version
- p1.first_name = 'anika2'
+ p1.first_name = "anika2"
p1.save!
p2 = Person.find(p1.id)
assert_equal 0, p1.lock_version
assert_equal 0, p2.lock_version
- p1.first_name = 'anika3'
+ p1.first_name = "anika3"
p1.save!
assert_equal 1, p1.lock_version
assert_equal 0, p2.lock_version
- p2.first_name = 'sue'
+ p2.first_name = "sue"
assert_raise(ActiveRecord::StaleObjectError) { p2.save! }
end
def test_lock_exception_record
- p1 = Person.new(:first_name => 'mira')
+ p1 = Person.new(first_name: "mira")
assert_equal 0, p1.lock_version
- p1.first_name = 'mira2'
+ p1.first_name = "mira2"
p1.save!
p2 = Person.find(p1.id)
assert_equal 0, p1.lock_version
assert_equal 0, p2.lock_version
- p1.first_name = 'mira3'
+ p1.first_name = "mira3"
p1.save!
- p2.first_name = 'sue'
+ p2.first_name = "sue"
error = assert_raise(ActiveRecord::StaleObjectError) { p2.save! }
assert_equal(error.record.object_id, p2.object_id)
end
- def test_lock_new_with_nil
- p1 = Person.new(:first_name => 'anika')
- p1.save!
- p1.lock_version = nil # simulate bad fixture or column with no default
- p1.save!
- assert_equal 1, p1.lock_version
- end
-
def test_lock_new_when_explicitly_passing_nil
- p1 = Person.new(:first_name => 'anika', lock_version: nil)
+ p1 = Person.new(first_name: "anika", lock_version: nil)
p1.save!
assert_equal 0, p1.lock_version
end
@@ -181,12 +173,13 @@ class OptimisticLockingTest < ActiveRecord::TestCase
p1.touch
assert_equal 1, p1.lock_version
+ assert_not p1.changed?, "Changes should have been cleared"
end
def test_touch_stale_object
- person = Person.create!(first_name: 'Mehmet Emin')
+ person = Person.create!(first_name: "Mehmet Emin")
stale_person = Person.find(person.id)
- person.update_attribute(:gender, 'M')
+ person.update_attribute(:gender, "M")
assert_raises(ActiveRecord::StaleObjectError) do
stale_person.touch
@@ -209,11 +202,11 @@ class OptimisticLockingTest < ActiveRecord::TestCase
end
def test_lock_column_is_mass_assignable
- p1 = Person.create(:first_name => 'bianca')
+ p1 = Person.create(first_name: "bianca")
assert_equal 0, p1.lock_version
assert_equal p1.lock_version, Person.new(p1.attributes).lock_version
- p1.first_name = 'bianca2'
+ p1.first_name = "bianca2"
p1.save!
assert_equal 1, p1.lock_version
assert_equal p1.lock_version, Person.new(p1.attributes).lock_version
@@ -221,28 +214,155 @@ class OptimisticLockingTest < ActiveRecord::TestCase
def test_lock_without_default_sets_version_to_zero
t1 = LockWithoutDefault.new
+
+ assert_equal 0, t1.lock_version
+ assert_nil t1.lock_version_before_type_cast
+
+ t1.save!
+ t1.reload
+
assert_equal 0, t1.lock_version
+ assert_equal 0, t1.lock_version_before_type_cast
+ end
+
+ def test_lock_without_default_should_work_with_null_in_the_database
+ ActiveRecord::Base.connection.execute("INSERT INTO lock_without_defaults(title) VALUES('title1')")
+ t1 = LockWithoutDefault.last
+ t2 = LockWithoutDefault.last
- t1.save
- t1 = LockWithoutDefault.find(t1.id)
assert_equal 0, t1.lock_version
+ assert_nil t1.lock_version_before_type_cast
+ assert_equal 0, t2.lock_version
+ assert_nil t2.lock_version_before_type_cast
+
+ t1.title = "new title1"
+ t2.title = "new title2"
+
+ assert_nothing_raised { t1.save! }
+ assert_equal 1, t1.lock_version
+ assert_equal "new title1", t1.title
+
+ assert_raise(ActiveRecord::StaleObjectError) { t2.save! }
+ assert_equal 0, t2.lock_version
+ assert_equal "new title2", t2.title
+ end
+
+ def test_lock_without_default_should_update_with_lock_col
+ t1 = LockWithoutDefault.create(title: "title1", lock_version: 6)
+
+ assert_equal 6, t1.lock_version
+
+ t1.update(lock_version: 0)
+ t1.reload
+
+ assert_equal 0, t1.lock_version
+ end
+
+ def test_lock_without_default_queries_count
+ t1 = LockWithoutDefault.create(title: "title1")
+
+ assert_equal "title1", t1.title
+ assert_equal 0, t1.lock_version
+
+ assert_queries(1) { t1.update(title: "title2") }
+
+ t1.reload
+ assert_equal "title2", t1.title
+ assert_equal 1, t1.lock_version
+
+ assert_queries(1) { t1.update(title: "title3", lock_version: 6) }
+
+ t1.reload
+ assert_equal "title3", t1.title
+ assert_equal 6, t1.lock_version
+
+ t2 = LockWithoutDefault.new(title: "title1")
+
+ assert_queries(1) { t2.save! }
+
+ t2.reload
+ assert_equal "title1", t2.title
+ assert_equal 0, t2.lock_version
end
def test_lock_with_custom_column_without_default_sets_version_to_zero
t1 = LockWithCustomColumnWithoutDefault.new
+
assert_equal 0, t1.custom_lock_version
assert_nil t1.custom_lock_version_before_type_cast
t1.save!
t1.reload
+
+ assert_equal 0, t1.custom_lock_version
+ assert_equal 0, t1.custom_lock_version_before_type_cast
+ end
+
+ def test_lock_with_custom_column_without_default_should_work_with_null_in_the_database
+ ActiveRecord::Base.connection.execute("INSERT INTO lock_without_defaults_cust(title) VALUES('title1')")
+
+ t1 = LockWithCustomColumnWithoutDefault.last
+ t2 = LockWithCustomColumnWithoutDefault.last
+
+ assert_equal 0, t1.custom_lock_version
+ assert_nil t1.custom_lock_version_before_type_cast
+ assert_equal 0, t2.custom_lock_version
+ assert_nil t2.custom_lock_version_before_type_cast
+
+ t1.title = "new title1"
+ t2.title = "new title2"
+
+ assert_nothing_raised { t1.save! }
+ assert_equal 1, t1.custom_lock_version
+ assert_equal "new title1", t1.title
+
+ assert_raise(ActiveRecord::StaleObjectError) { t2.save! }
+ assert_equal 0, t2.custom_lock_version
+ assert_equal "new title2", t2.title
+ end
+
+ def test_lock_with_custom_column_without_default_should_update_with_lock_col
+ t1 = LockWithCustomColumnWithoutDefault.create(title: "title1", custom_lock_version: 6)
+
+ assert_equal 6, t1.custom_lock_version
+
+ t1.update(custom_lock_version: 0)
+ t1.reload
+
assert_equal 0, t1.custom_lock_version
- assert [0, "0"].include?(t1.custom_lock_version_before_type_cast)
+ end
+
+ def test_lock_with_custom_column_without_default_queries_count
+ t1 = LockWithCustomColumnWithoutDefault.create(title: "title1")
+
+ assert_equal "title1", t1.title
+ assert_equal 0, t1.custom_lock_version
+
+ assert_queries(1) { t1.update(title: "title2") }
+
+ t1.reload
+ assert_equal "title2", t1.title
+ assert_equal 1, t1.custom_lock_version
+
+ assert_queries(1) { t1.update(title: "title3", custom_lock_version: 6) }
+
+ t1.reload
+ assert_equal "title3", t1.title
+ assert_equal 6, t1.custom_lock_version
+
+ t2 = LockWithCustomColumnWithoutDefault.new(title: "title1")
+
+ assert_queries(1) { t2.save! }
+
+ t2.reload
+ assert_equal "title1", t2.title
+ assert_equal 0, t2.custom_lock_version
end
def test_readonly_attributes
- assert_equal Set.new([ 'name' ]), ReadonlyNameShip.readonly_attributes
+ assert_equal Set.new([ "name" ]), ReadonlyNameShip.readonly_attributes
- s = ReadonlyNameShip.create(:name => "unchangeable name")
+ s = ReadonlyNameShip.create(name: "unchangeable name")
s.reload
assert_equal "unchangeable name", s.name
@@ -261,7 +381,7 @@ class OptimisticLockingTest < ActiveRecord::TestCase
# is nothing else being updated.
def test_update_without_attributes_does_not_only_update_lock_version
assert_nothing_raised do
- p1 = Person.create!(:first_name => 'anika')
+ p1 = Person.create!(first_name: "anika")
lock_version = p1.lock_version
p1.save
p1.reload
@@ -272,17 +392,17 @@ class OptimisticLockingTest < ActiveRecord::TestCase
def test_polymorphic_destroy_with_dependencies_and_lock_version
car = Car.create!
- assert_difference 'car.wheels.count' do
+ assert_difference "car.wheels.count" do
car.wheels << Wheel.create!
end
- assert_difference 'car.wheels.count', -1 do
+ assert_difference "car.wheels.count", -1 do
car.reload.destroy
end
assert car.destroyed?
end
def test_removing_has_and_belongs_to_many_associations_upon_destroy
- p = RichPerson.create! first_name: 'Jon'
+ p = RichPerson.create! first_name: "Jon"
p.treasures.create!
assert !p.treasures.empty?
p.destroy
@@ -306,7 +426,7 @@ class OptimisticLockingWithSchemaChangeTest < ActiveRecord::TestCase
# of a test (see test_increment_counter_*).
self.use_transactional_tests = false
- { :lock_version => Person, :custom_lock_version => LegacyThing }.each do |name, model|
+ { lock_version: Person, custom_lock_version: LegacyThing }.each do |name, model|
define_method("test_increment_counter_updates_#{name}") do
counter_test model, 1 do |id|
model.increment_counter :test_count, id
@@ -321,7 +441,7 @@ class OptimisticLockingWithSchemaChangeTest < ActiveRecord::TestCase
define_method("test_update_counters_updates_#{name}") do
counter_test model, 1 do |id|
- model.update_counters id, :test_count => 1
+ model.update_counters id, test_count: 1
end
end
end
@@ -329,13 +449,13 @@ class OptimisticLockingWithSchemaChangeTest < ActiveRecord::TestCase
# See Lighthouse ticket #1966
def test_destroy_dependents
# Establish dependent relationship between Person and PersonalLegacyThing
- add_counter_column_to(Person, 'personal_legacy_things_count')
+ add_counter_column_to(Person, "personal_legacy_things_count")
PersonalLegacyThing.reset_column_information
# Make sure that counter incrementing doesn't cause problems
- p1 = Person.new(:first_name => 'fjord')
+ p1 = Person.new(first_name: "fjord")
p1.save!
- t = PersonalLegacyThing.new(:person => p1)
+ t = PersonalLegacyThing.new(person: p1)
t.save!
p1.reload
assert_equal 1, p1.personal_legacy_things_count
@@ -344,14 +464,14 @@ class OptimisticLockingWithSchemaChangeTest < ActiveRecord::TestCase
assert_raises(ActiveRecord::RecordNotFound) { Person.find(p1.id) }
assert_raises(ActiveRecord::RecordNotFound) { PersonalLegacyThing.find(t.id) }
ensure
- remove_counter_column_from(Person, 'personal_legacy_things_count')
+ remove_counter_column_from(Person, "personal_legacy_things_count")
PersonalLegacyThing.reset_column_information
end
private
- def add_counter_column_to(model, col='test_count')
- model.connection.add_column model.table_name, col, :integer, :null => false, :default => 0
+ def add_counter_column_to(model, col = "test_count")
+ model.connection.add_column model.table_name, col, :integer, null: false, default: 0
model.reset_column_information
end
@@ -374,7 +494,6 @@ class OptimisticLockingWithSchemaChangeTest < ActiveRecord::TestCase
end
end
-
# TODO: test against the generated SQL since testing locking behavior itself
# is so cumbersome. Will deadlock Ruby threads if the underlying db.execute
# blocks, so separate script called by Kernel#system is needed.
@@ -416,7 +535,7 @@ unless in_memory_db?
assert_nothing_raised do
Person.transaction do
person = Person.find 1
- old, person.first_name = person.first_name, 'fooman'
+ old, person.first_name = person.first_name, "fooman"
person.lock!
assert_equal old, person.first_name
end
@@ -426,19 +545,19 @@ unless in_memory_db?
def test_with_lock_commits_transaction
person = Person.find 1
person.with_lock do
- person.first_name = 'fooman'
+ person.first_name = "fooman"
person.save!
end
- assert_equal 'fooman', person.reload.first_name
+ assert_equal "fooman", person.reload.first_name
end
def test_with_lock_rolls_back_transaction
person = Person.find 1
old = person.first_name
person.with_lock do
- person.first_name = 'fooman'
+ person.first_name = "fooman"
person.save!
- raise 'oops'
+ raise "oops"
end rescue nil
assert_equal old, person.reload.first_name
end
@@ -448,7 +567,7 @@ unless in_memory_db?
Person.transaction do
person = Person.find(1)
assert_sql(/LIMIT \$?\d FOR SHARE NOWAIT/) do
- person.lock!('FOR SHARE NOWAIT')
+ person.lock!("FOR SHARE NOWAIT")
end
end
end
@@ -460,34 +579,35 @@ unless in_memory_db?
assert first.end > second.end
end
- protected
- def duel(zzz = 5)
- t0, t1, t2, t3 = nil, nil, nil, nil
-
- a = Thread.new do
- t0 = Time.now
- Person.transaction do
- yield
- sleep zzz # block thread 2 for zzz seconds
- end
- t1 = Time.now
- end
+ private
- b = Thread.new do
- sleep zzz / 2.0 # ensure thread 1 tx starts first
- t2 = Time.now
- Person.transaction { yield }
- t3 = Time.now
- end
+ def duel(zzz = 5)
+ t0, t1, t2, t3 = nil, nil, nil, nil
- a.join
- b.join
+ a = Thread.new do
+ t0 = Time.now
+ Person.transaction do
+ yield
+ sleep zzz # block thread 2 for zzz seconds
+ end
+ t1 = Time.now
+ end
- assert t1 > t0 + zzz
- assert t2 > t0
- assert t3 > t2
- [t0.to_f..t1.to_f, t2.to_f..t3.to_f]
+ b = Thread.new do
+ sleep zzz / 2.0 # ensure thread 1 tx starts first
+ t2 = Time.now
+ Person.transaction { yield }
+ t3 = Time.now
end
+
+ a.join
+ b.join
+
+ assert t1 > t0 + zzz
+ assert t2 > t0
+ assert t3 > t2
+ [t0.to_f..t1.to_f, t2.to_f..t3.to_f]
+ end
end
end
end
diff --git a/activerecord/test/cases/log_subscriber_test.rb b/activerecord/test/cases/log_subscriber_test.rb
index c97960a412..90ad970e16 100644
--- a/activerecord/test/cases/log_subscriber_test.rb
+++ b/activerecord/test/cases/log_subscriber_test.rb
@@ -30,7 +30,7 @@ class LogSubscriberTest < ActiveRecord::TestCase
super
end
- def debug message
+ def debug(message)
@debugs << message
end
end
@@ -60,20 +60,20 @@ class LogSubscriberTest < ActiveRecord::TestCase
logger = TestDebugLogSubscriber.new
assert_equal 0, logger.debugs.length
- logger.sql(event.new(0, sql: 'hi mom!'))
+ logger.sql(event.new(0, sql: "hi mom!"))
assert_equal 1, logger.debugs.length
- logger.sql(event.new(0, sql: 'hi mom!', name: 'foo'))
+ logger.sql(event.new(0, sql: "hi mom!", name: "foo"))
assert_equal 2, logger.debugs.length
- logger.sql(event.new(0, sql: 'hi mom!', name: 'SCHEMA'))
+ logger.sql(event.new(0, sql: "hi mom!", name: "SCHEMA"))
assert_equal 2, logger.debugs.length
end
def test_sql_statements_are_not_squeezed
event = Struct.new(:duration, :payload)
logger = TestDebugLogSubscriber.new
- logger.sql(event.new(0, sql: 'ruby rails'))
+ logger.sql(event.new(0, sql: "ruby rails"))
assert_match(/ruby rails/, logger.debugs.first)
end
@@ -103,7 +103,7 @@ class LogSubscriberTest < ActiveRecord::TestCase
logger.sql(event.new(0, sql: verb.to_s))
assert_match(/#{REGEXP_BOLD}#{REGEXP_MAGENTA} \(0.0ms\)#{REGEXP_CLEAR}/i, logger.debugs.last)
- logger.sql(event.new(0, {sql: verb.to_s, name: "SQL"}))
+ logger.sql(event.new(0, sql: verb.to_s, name: "SQL"))
assert_match(/#{REGEXP_BOLD}#{REGEXP_MAGENTA}SQL \(0.0ms\)#{REGEXP_CLEAR}/i, logger.debugs.last)
end
end
@@ -113,13 +113,13 @@ class LogSubscriberTest < ActiveRecord::TestCase
logger = TestDebugLogSubscriber.new
logger.colorize_logging = true
SQL_COLORINGS.each do |verb, _|
- logger.sql(event.new(0, {sql: verb.to_s, name: "Model Load"}))
+ logger.sql(event.new(0, sql: verb.to_s, name: "Model Load"))
assert_match(/#{REGEXP_BOLD}#{REGEXP_CYAN}Model Load \(0.0ms\)#{REGEXP_CLEAR}/i, logger.debugs.last)
- logger.sql(event.new(0, {sql: verb.to_s, name: "Model Exists"}))
+ logger.sql(event.new(0, sql: verb.to_s, name: "Model Exists"))
assert_match(/#{REGEXP_BOLD}#{REGEXP_CYAN}Model Exists \(0.0ms\)#{REGEXP_CLEAR}/i, logger.debugs.last)
- logger.sql(event.new(0, {sql: verb.to_s, name: "ANY SPECIFIC NAME"}))
+ logger.sql(event.new(0, sql: verb.to_s, name: "ANY SPECIFIC NAME"))
assert_match(/#{REGEXP_BOLD}#{REGEXP_CYAN}ANY SPECIFIC NAME \(0.0ms\)#{REGEXP_CLEAR}/i, logger.debugs.last)
end
end
@@ -211,7 +211,7 @@ class LogSubscriberTest < ActiveRecord::TestCase
if ActiveRecord::Base.connection.prepared_statements
def test_binary_data_is_not_logged
- Binary.create(data: 'some binary data')
+ Binary.create(data: "some binary data")
wait
assert_match(/<16 bytes of binary data>/, @logger.logged(:debug).join)
end
diff --git a/activerecord/test/cases/migration/change_schema_test.rb b/activerecord/test/cases/migration/change_schema_test.rb
index d6963b48d7..48cfe89882 100644
--- a/activerecord/test/cases/migration/change_schema_test.rb
+++ b/activerecord/test/cases/migration/change_schema_test.rb
@@ -1,4 +1,4 @@
-require 'cases/helper'
+require "cases/helper"
module ActiveRecord
class Migration
@@ -40,10 +40,10 @@ module ActiveRecord
def test_create_table_with_not_null_column
connection.create_table :testings do |t|
- t.column :foo, :string, :null => false
+ t.column :foo, :string, null: false
end
- assert_raises(ActiveRecord::StatementInvalid) do
+ assert_raises(ActiveRecord::NotNullViolation) do
connection.execute "insert into testings (foo) values (NULL)"
end
end
@@ -53,11 +53,11 @@ module ActiveRecord
mysql = current_adapter?(:Mysql2Adapter)
connection.create_table :testings do |t|
- t.column :one, :string, :default => "hello"
- t.column :two, :boolean, :default => true
- t.column :three, :boolean, :default => false
- t.column :four, :integer, :default => 1
- t.column :five, :text, :default => "hello" unless mysql
+ t.column :one, :string, default: "hello"
+ t.column :two, :boolean, default: true
+ t.column :three, :boolean, default: false
+ t.column :four, :integer, default: 1
+ t.column :five, :text, default: "hello" unless mysql
end
columns = connection.columns(:testings)
@@ -70,14 +70,14 @@ module ActiveRecord
assert_equal "hello", one.default
assert_equal true, connection.lookup_cast_type_from_column(two).deserialize(two.default)
assert_equal false, connection.lookup_cast_type_from_column(three).deserialize(three.default)
- assert_equal '1', four.default
+ assert_equal "1", four.default
assert_equal "hello", five.default unless mysql
end
if current_adapter?(:PostgreSQLAdapter)
def test_add_column_with_array
connection.create_table :testings
- connection.add_column :testings, :foo, :string, :array => true
+ connection.add_column :testings, :foo, :string, array: true
columns = connection.columns(:testings)
array_column = columns.detect { |c| c.name == "foo" }
@@ -87,7 +87,7 @@ module ActiveRecord
def test_create_table_with_array_column
connection.create_table :testings do |t|
- t.string :foo, :array => true
+ t.string :foo, array: true
end
columns = connection.columns(:testings)
@@ -105,9 +105,9 @@ module ActiveRecord
eight = columns.detect { |c| c.name == "eight_int" }
if current_adapter?(:OracleAdapter)
- assert_equal 'NUMBER(19)', eight.sql_type
+ assert_equal "NUMBER(19)", eight.sql_type
elsif current_adapter?(:SQLite3Adapter)
- assert_equal 'bigint', eight.sql_type
+ assert_equal "bigint", eight.sql_type
else
assert_equal :integer, eight.type
assert_equal 8, eight.limit
@@ -118,13 +118,13 @@ module ActiveRecord
def test_create_table_with_limits
connection.create_table :testings do |t|
- t.column :foo, :string, :limit => 255
+ t.column :foo, :string, limit: 255
t.column :default_int, :integer
- t.column :one_int, :integer, :limit => 1
- t.column :four_int, :integer, :limit => 4
- t.column :eight_int, :integer, :limit => 8
+ t.column :one_int, :integer, limit: 1
+ t.column :four_int, :integer, limit: 4
+ t.column :eight_int, :integer, limit: 8
end
columns = connection.columns(:testings)
@@ -137,20 +137,20 @@ module ActiveRecord
eight = columns.detect { |c| c.name == "eight_int" }
if current_adapter?(:PostgreSQLAdapter)
- assert_equal 'integer', default.sql_type
- assert_equal 'smallint', one.sql_type
- assert_equal 'integer', four.sql_type
- assert_equal 'bigint', eight.sql_type
+ assert_equal "integer", default.sql_type
+ assert_equal "smallint", one.sql_type
+ assert_equal "integer", four.sql_type
+ assert_equal "bigint", eight.sql_type
elsif current_adapter?(:Mysql2Adapter)
- assert_match 'int(11)', default.sql_type
- assert_match 'tinyint', one.sql_type
- assert_match 'int', four.sql_type
- assert_match 'bigint', eight.sql_type
+ assert_match "int(11)", default.sql_type
+ assert_match "tinyint", one.sql_type
+ assert_match "int", four.sql_type
+ assert_match "bigint", eight.sql_type
elsif current_adapter?(:OracleAdapter)
- assert_equal 'NUMBER(38)', default.sql_type
- assert_equal 'NUMBER(1)', one.sql_type
- assert_equal 'NUMBER(4)', four.sql_type
- assert_equal 'NUMBER(8)', eight.sql_type
+ assert_equal "NUMBER(38)", default.sql_type
+ assert_equal "NUMBER(1)", one.sql_type
+ assert_equal "NUMBER(4)", four.sql_type
+ assert_equal "NUMBER(8)", eight.sql_type
end
end
@@ -200,8 +200,8 @@ module ActiveRecord
end
created_columns = connection.columns(table_name)
- created_at_column = created_columns.detect {|c| c.name == 'created_at' }
- updated_at_column = created_columns.detect {|c| c.name == 'updated_at' }
+ created_at_column = created_columns.detect { |c| c.name == "created_at" }
+ updated_at_column = created_columns.detect { |c| c.name == "updated_at" }
assert !created_at_column.null
assert !updated_at_column.null
@@ -213,8 +213,8 @@ module ActiveRecord
end
created_columns = connection.columns(table_name)
- created_at_column = created_columns.detect {|c| c.name == 'created_at' }
- updated_at_column = created_columns.detect {|c| c.name == 'updated_at' }
+ created_at_column = created_columns.detect { |c| c.name == "created_at" }
+ updated_at_column = created_columns.detect { |c| c.name == "updated_at" }
assert created_at_column.null
assert updated_at_column.null
@@ -231,9 +231,9 @@ module ActiveRecord
connection.create_table :testings do |t|
t.column :foo, :string
end
- connection.add_column :testings, :bar, :string, :null => false
+ connection.add_column :testings, :bar, :string, null: false
- assert_raise(ActiveRecord::StatementInvalid) do
+ assert_raise(ActiveRecord::NotNullViolation) do
connection.execute "insert into testings (foo, bar) values ('hello', NULL)"
end
end
@@ -244,12 +244,16 @@ module ActiveRecord
t.column :foo, :string
end
- con = connection
- connection.execute "insert into testings (#{con.quote_column_name('id')}, #{con.quote_column_name('foo')}) values (1, 'hello')"
- assert_nothing_raised {connection.add_column :testings, :bar, :string, :null => false, :default => "default" }
+ quoted_id = connection.quote_column_name("id")
+ quoted_foo = connection.quote_column_name("foo")
+ quoted_bar = connection.quote_column_name("bar")
+ connection.execute("insert into testings (#{quoted_id}, #{quoted_foo}) values (1, 'hello')")
+ assert_nothing_raised do
+ connection.add_column :testings, :bar, :string, null: false, default: "default"
+ end
- assert_raises(ActiveRecord::StatementInvalid) do
- connection.execute "insert into testings (#{con.quote_column_name('id')}, #{con.quote_column_name('foo')}, #{con.quote_column_name('bar')}) values (2, 'hello', NULL)"
+ assert_raises(ActiveRecord::NotNullViolation) do
+ connection.execute("insert into testings (#{quoted_id}, #{quoted_foo}, #{quoted_bar}) values (2, 'hello', NULL)")
end
end
@@ -259,14 +263,14 @@ module ActiveRecord
end
klass = Class.new(ActiveRecord::Base)
- klass.table_name = 'testings'
+ klass.table_name = "testings"
- assert_equal :datetime, klass.columns_hash['foo'].type
+ assert_equal :datetime, klass.columns_hash["foo"].type
if current_adapter?(:PostgreSQLAdapter)
- assert_equal 'timestamp without time zone', klass.columns_hash['foo'].sql_type
+ assert_equal "timestamp without time zone", klass.columns_hash["foo"].sql_type
else
- assert_equal klass.connection.type_to_sql('datetime'), klass.columns_hash['foo'].sql_type
+ assert_equal klass.connection.type_to_sql("datetime"), klass.columns_hash["foo"].sql_type
end
end
@@ -275,7 +279,7 @@ module ActiveRecord
t.column :select, :string
end
- connection.change_column :testings, :select, :string, :limit => 10
+ connection.change_column :testings, :select, :string, limit: 10
# Oracle needs primary key value from sequence
if current_adapter?(:OracleAdapter)
@@ -290,17 +294,17 @@ module ActiveRecord
t.column :title, :string
end
person_klass = Class.new(ActiveRecord::Base)
- person_klass.table_name = 'testings'
+ person_klass.table_name = "testings"
- person_klass.connection.add_column "testings", "wealth", :integer, :null => false, :default => 99
+ person_klass.connection.add_column "testings", "wealth", :integer, null: false, default: 99
person_klass.reset_column_information
assert_equal 99, person_klass.column_defaults["wealth"]
assert_equal false, person_klass.columns_hash["wealth"].null
# Oracle needs primary key value from sequence
if current_adapter?(:OracleAdapter)
- assert_nothing_raised {person_klass.connection.execute("insert into testings (id, title) values (testings_seq.nextval, 'tester')")}
+ assert_nothing_raised { person_klass.connection.execute("insert into testings (id, title) values (testings_seq.nextval, 'tester')") }
else
- assert_nothing_raised {person_klass.connection.execute("insert into testings (title) values ('tester')")}
+ assert_nothing_raised { person_klass.connection.execute("insert into testings (title) values ('tester')") }
end
# change column default to see that column doesn't lose its not null definition
@@ -317,19 +321,19 @@ module ActiveRecord
assert_equal false, person_klass.columns_hash["money"].null
# change column
- person_klass.connection.change_column "testings", "money", :integer, :null => false, :default => 1000
+ person_klass.connection.change_column "testings", "money", :integer, null: false, default: 1000
person_klass.reset_column_information
assert_equal 1000, person_klass.column_defaults["money"]
assert_equal false, person_klass.columns_hash["money"].null
# change column, make it nullable and clear default
- person_klass.connection.change_column "testings", "money", :integer, :null => true, :default => nil
+ person_klass.connection.change_column "testings", "money", :integer, null: true, default: nil
person_klass.reset_column_information
assert_nil person_klass.columns_hash["money"].default
assert_equal true, person_klass.columns_hash["money"].null
# change_column_null, make it not nullable and set null values to a default value
- person_klass.connection.execute('UPDATE testings SET money = NULL')
+ person_klass.connection.execute("UPDATE testings SET money = NULL")
person_klass.connection.change_column_null "testings", "money", false, 2000
person_klass.reset_column_information
assert_nil person_klass.columns_hash["money"].default
@@ -346,9 +350,9 @@ module ActiveRecord
end
notnull_migration.new.suppress_messages do
notnull_migration.migrate(:up)
- assert_equal false, connection.columns(:testings).find{ |c| c.name == "foo"}.null
+ assert_equal false, connection.columns(:testings).find { |c| c.name == "foo" }.null
notnull_migration.migrate(:down)
- assert connection.columns(:testings).find{ |c| c.name == "foo"}.null
+ assert connection.columns(:testings).find { |c| c.name == "foo" }.null
end
end
end
@@ -365,7 +369,7 @@ module ActiveRecord
def test_column_exists_with_type
connection.create_table :testings do |t|
t.column :foo, :string
- t.column :bar, :decimal, :precision => 8, :scale => 2
+ t.column :bar, :decimal, precision: 8, scale: 2
end
assert connection.column_exists?(:testings, :foo, :string)
@@ -380,7 +384,7 @@ module ActiveRecord
t.column :foo, :string, limit: 100
t.column :bar, :decimal, precision: 8, scale: 2
t.column :taggable_id, :integer, null: false
- t.column :taggable_type, :string, default: 'Photo'
+ t.column :taggable_type, :string, default: "Photo"
end
assert connection.column_exists?(:testings, :foo, :string, limit: 100)
@@ -389,7 +393,7 @@ module ActiveRecord
assert_not connection.column_exists?(:testings, :bar, :decimal, precision: nil, scale: nil)
assert connection.column_exists?(:testings, :taggable_id, :integer, null: false)
assert_not connection.column_exists?(:testings, :taggable_id, :integer, null: true)
- assert connection.column_exists?(:testings, :taggable_type, :string, default: 'Photo')
+ assert connection.column_exists?(:testings, :taggable_type, :string, default: "Photo")
assert_not connection.column_exists?(:testings, :taggable_type, :string, default: nil)
end
@@ -405,9 +409,9 @@ module ActiveRecord
def test_drop_table_if_exists
connection.create_table(:testings)
- ActiveSupport::Deprecation.silence { assert connection.table_exists?(:testings) }
+ assert connection.table_exists?(:testings)
connection.drop_table(:testings, if_exists: true)
- ActiveSupport::Deprecation.silence { assert_not connection.table_exists?(:testings) }
+ assert_not connection.table_exists?(:testings)
end
def test_drop_table_if_exists_nothing_raised
@@ -415,13 +419,13 @@ module ActiveRecord
end
private
- def testing_table_with_only_foo_attribute
- connection.create_table :testings, :id => false do |t|
- t.column :foo, :string
- end
+ def testing_table_with_only_foo_attribute
+ connection.create_table :testings, id: false do |t|
+ t.column :foo, :string
+ end
- yield
- end
+ yield
+ end
end
if ActiveRecord::Base.connection.supports_foreign_keys?
diff --git a/activerecord/test/cases/migration/change_table_test.rb b/activerecord/test/cases/migration/change_table_test.rb
index 2f9c50141f..8a4242cf1d 100644
--- a/activerecord/test/cases/migration/change_table_test.rb
+++ b/activerecord/test/cases/migration/change_table_test.rb
@@ -95,13 +95,18 @@ module ActiveRecord
def test_remove_timestamps_creates_updated_at_and_created_at
with_change_table do |t|
@connection.expect :remove_timestamps, nil, [:delete_me, { null: true }]
- t.remove_timestamps({ null: true })
+ t.remove_timestamps(null: true)
end
end
def test_primary_key_creates_primary_key_column
with_change_table do |t|
- @connection.expect :add_column, nil, [:delete_me, :id, :primary_key, primary_key: true, first: true]
+ if current_adapter?(:Mysql2Adapter)
+ @connection.expect :add_column, nil, [:delete_me, :id, :primary_key, { first: true, auto_increment: true, limit: 8, primary_key: true }]
+ else
+ @connection.expect :add_column, nil, [:delete_me, :id, :primary_key, primary_key: true, first: true]
+ end
+
t.primary_key :id, first: true
end
end
@@ -157,8 +162,8 @@ module ActiveRecord
def test_column_creates_column_with_options
with_change_table do |t|
- @connection.expect :add_column, nil, [:delete_me, :bar, :integer, {:null => false}]
- t.column :bar, :integer, :null => false
+ @connection.expect :add_column, nil, [:delete_me, :bar, :integer, { null: false }]
+ t.column :bar, :integer, null: false
end
end
@@ -171,8 +176,8 @@ module ActiveRecord
def test_index_creates_index_with_options
with_change_table do |t|
- @connection.expect :add_index, nil, [:delete_me, :bar, {:unique => true}]
- t.index :bar, :unique => true
+ @connection.expect :add_index, nil, [:delete_me, :bar, { unique: true }]
+ t.index :bar, unique: true
end
end
@@ -185,8 +190,8 @@ module ActiveRecord
def test_index_exists_with_options
with_change_table do |t|
- @connection.expect :index_exists?, nil, [:delete_me, :bar, {:unique => true}]
- t.index_exists?(:bar, :unique => true)
+ @connection.expect :index_exists?, nil, [:delete_me, :bar, { unique: true }]
+ t.index_exists?(:bar, unique: true)
end
end
@@ -206,8 +211,8 @@ module ActiveRecord
def test_change_changes_column_with_options
with_change_table do |t|
- @connection.expect :change_column, nil, [:delete_me, :bar, :string, {:null => true}]
- t.change :bar, :string, :null => true
+ @connection.expect :change_column, nil, [:delete_me, :bar, :string, { null: true }]
+ t.change :bar, :string, null: true
end
end
@@ -234,8 +239,8 @@ module ActiveRecord
def test_remove_index_removes_index_with_options
with_change_table do |t|
- @connection.expect :remove_index, nil, [:delete_me, {:unique => true}]
- t.remove_index :unique => true
+ @connection.expect :remove_index, nil, [:delete_me, { unique: true }]
+ t.remove_index unique: true
end
end
diff --git a/activerecord/test/cases/migration/column_attributes_test.rb b/activerecord/test/cases/migration/column_attributes_test.rb
index 29546525f3..48df931543 100644
--- a/activerecord/test/cases/migration/column_attributes_test.rb
+++ b/activerecord/test/cases/migration/column_attributes_test.rb
@@ -9,7 +9,7 @@ module ActiveRecord
def test_add_column_newline_default
string = "foo\nbar"
- add_column 'test_models', 'command', :string, :default => string
+ add_column "test_models", "command", :string, default: string
TestModel.reset_column_information
assert_equal string, TestModel.new.command
@@ -18,10 +18,10 @@ module ActiveRecord
def test_add_remove_single_field_using_string_arguments
assert_no_column TestModel, :last_name
- add_column 'test_models', 'last_name', :string
+ add_column "test_models", "last_name", :string
assert_column TestModel, :last_name
- remove_column 'test_models', 'last_name'
+ remove_column "test_models", "last_name"
assert_no_column TestModel, :last_name
end
@@ -47,7 +47,7 @@ module ActiveRecord
def test_unabstracted_database_dependent_types
add_column :test_models, :intelligence_quotient, :tinyint
TestModel.reset_column_information
- assert_match(/tinyint/, TestModel.columns_hash['intelligence_quotient'].sql_type)
+ assert_match(/tinyint/, TestModel.columns_hash["intelligence_quotient"].sql_type)
end
end
@@ -56,15 +56,13 @@ module ActiveRecord
# functionality. This allows us to more easily catch INSERT being broken,
# but SELECT actually working fine.
def test_native_decimal_insert_manual_vs_automatic
- correct_value = '0012345678901234567890.0123456789'.to_d
+ correct_value = "0012345678901234567890.0123456789".to_d
- connection.add_column "test_models", "wealth", :decimal, :precision => '30', :scale => '10'
+ connection.add_column "test_models", "wealth", :decimal, precision: "30", scale: "10"
# Do a manual insertion
if current_adapter?(:OracleAdapter)
connection.execute "insert into test_models (id, wealth) values (people_seq.nextval, 12345678901234567890.0123456789)"
- elsif current_adapter?(:PostgreSQLAdapter)
- connection.execute "insert into test_models (wealth) values (12345678901234567890.0123456789)"
else
connection.execute "insert into test_models (wealth) values (12345678901234567890.0123456789)"
end
@@ -74,15 +72,13 @@ module ActiveRecord
assert_kind_of BigDecimal, row.wealth
# If this assert fails, that means the SELECT is broken!
- unless current_adapter?(:SQLite3Adapter)
- assert_equal correct_value, row.wealth
- end
+ assert_equal correct_value, row.wealth
# Reset to old state
TestModel.delete_all
# Now use the Rails insertion
- TestModel.create :wealth => BigDecimal.new("12345678901234567890.0123456789")
+ TestModel.create wealth: BigDecimal.new("12345678901234567890.0123456789")
# SELECT
row = TestModel.first
@@ -94,26 +90,26 @@ module ActiveRecord
end
def test_add_column_with_precision_and_scale
- connection.add_column 'test_models', 'wealth', :decimal, :precision => 9, :scale => 7
+ connection.add_column "test_models", "wealth", :decimal, precision: 9, scale: 7
- wealth_column = TestModel.columns_hash['wealth']
+ wealth_column = TestModel.columns_hash["wealth"]
assert_equal 9, wealth_column.precision
assert_equal 7, wealth_column.scale
end
if current_adapter?(:SQLite3Adapter)
def test_change_column_preserve_other_column_precision_and_scale
- connection.add_column 'test_models', 'last_name', :string
- connection.add_column 'test_models', 'wealth', :decimal, :precision => 9, :scale => 7
+ connection.add_column "test_models", "last_name", :string
+ connection.add_column "test_models", "wealth", :decimal, precision: 9, scale: 7
- wealth_column = TestModel.columns_hash['wealth']
+ wealth_column = TestModel.columns_hash["wealth"]
assert_equal 9, wealth_column.precision
assert_equal 7, wealth_column.scale
- connection.change_column 'test_models', 'last_name', :string, :null => false
+ connection.change_column "test_models", "last_name", :string, null: false
TestModel.reset_column_information
- wealth_column = TestModel.columns_hash['wealth']
+ wealth_column = TestModel.columns_hash["wealth"]
assert_equal 9, wealth_column.precision
assert_equal 7, wealth_column.scale
end
@@ -126,21 +122,21 @@ module ActiveRecord
add_column "test_models", "bio", :text
add_column "test_models", "age", :integer
add_column "test_models", "height", :float
- add_column "test_models", "wealth", :decimal, :precision => '30', :scale => '10'
+ add_column "test_models", "wealth", :decimal, precision: "30", scale: "10"
add_column "test_models", "birthday", :datetime
add_column "test_models", "favorite_day", :date
add_column "test_models", "moment_of_truth", :datetime
add_column "test_models", "male", :boolean
- TestModel.create :first_name => 'bob', :last_name => 'bobsen',
- :bio => "I was born ....", :age => 18, :height => 1.78,
- :wealth => BigDecimal.new("12345678901234567890.0123456789"),
- :birthday => 18.years.ago, :favorite_day => 10.days.ago,
- :moment_of_truth => "1782-10-10 21:40:18", :male => true
+ TestModel.create first_name: "bob", last_name: "bobsen",
+ bio: "I was born ....", age: 18, height: 1.78,
+ wealth: BigDecimal.new("12345678901234567890.0123456789"),
+ birthday: 18.years.ago, favorite_day: 10.days.ago,
+ moment_of_truth: "1782-10-10 21:40:18", male: true
bob = TestModel.first
- assert_equal 'bob', bob.first_name
- assert_equal 'bobsen', bob.last_name
+ assert_equal "bob", bob.first_name
+ assert_equal "bobsen", bob.last_name
assert_equal "I was born ....", bob.bio
assert_equal 18, bob.age
@@ -156,14 +152,7 @@ module ActiveRecord
assert_equal String, bob.bio.class
assert_kind_of Integer, bob.age
assert_equal Time, bob.birthday.class
-
- if current_adapter?(:OracleAdapter)
- # Oracle doesn't differentiate between date/time
- assert_equal Time, bob.favorite_day.class
- else
- assert_equal Date, bob.favorite_day.class
- end
-
+ assert_equal Date, bob.favorite_day.class
assert_instance_of TrueClass, bob.male?
assert_kind_of BigDecimal, bob.wealth
end
@@ -171,10 +160,10 @@ module ActiveRecord
if current_adapter?(:Mysql2Adapter, :PostgreSQLAdapter)
def test_out_of_range_limit_should_raise
- assert_raise(ActiveRecordError) { add_column :test_models, :integer_too_big, :integer, :limit => 10 }
+ assert_raise(ActiveRecordError) { add_column :test_models, :integer_too_big, :integer, limit: 10 }
unless current_adapter?(:PostgreSQLAdapter)
- assert_raise(ActiveRecordError) { add_column :test_models, :text_too_big, :integer, :limit => 0xfffffffff }
+ assert_raise(ActiveRecordError) { add_column :test_models, :text_too_big, :text, limit: 0xfffffffff }
end
end
end
diff --git a/activerecord/test/cases/migration/column_positioning_test.rb b/activerecord/test/cases/migration/column_positioning_test.rb
index 8294da0373..f2162d91b1 100644
--- a/activerecord/test/cases/migration/column_positioning_test.rb
+++ b/activerecord/test/cases/migration/column_positioning_test.rb
@@ -1,4 +1,4 @@
-require 'cases/helper'
+require "cases/helper"
module ActiveRecord
class Migration
@@ -11,7 +11,7 @@ module ActiveRecord
@connection = ActiveRecord::Base.connection
- connection.create_table :testings, :id => false do |t|
+ connection.create_table :testings, id: false do |t|
t.column :first, :integer
t.column :second, :integer
t.column :third, :integer
@@ -34,20 +34,20 @@ module ActiveRecord
end
def test_add_column_with_positioning_first
- conn.add_column :testings, :new_col, :integer, :first => true
+ conn.add_column :testings, :new_col, :integer, first: true
assert_equal %w(new_col first second third), conn.columns(:testings).map(&:name)
end
def test_add_column_with_positioning_after
- conn.add_column :testings, :new_col, :integer, :after => :first
+ conn.add_column :testings, :new_col, :integer, after: :first
assert_equal %w(first new_col second third), conn.columns(:testings).map(&:name)
end
def test_change_column_with_positioning
- conn.change_column :testings, :second, :integer, :first => true
+ conn.change_column :testings, :second, :integer, first: true
assert_equal %w(second first third), conn.columns(:testings).map(&:name)
- conn.change_column :testings, :second, :integer, :after => :third
+ conn.change_column :testings, :second, :integer, after: :third
assert_equal %w(first third second), conn.columns(:testings).map(&:name)
end
end
diff --git a/activerecord/test/cases/migration/columns_test.rb b/activerecord/test/cases/migration/columns_test.rb
index fca1cb7e97..55c06da411 100644
--- a/activerecord/test/cases/migration/columns_test.rb
+++ b/activerecord/test/cases/migration/columns_test.rb
@@ -13,7 +13,7 @@ module ActiveRecord
add_column "test_models", "girlfriend", :string
TestModel.reset_column_information
- TestModel.create :girlfriend => 'bobette'
+ TestModel.create girlfriend: "bobette"
rename_column "test_models", "girlfriend", "exgirlfriend"
@@ -28,12 +28,12 @@ module ActiveRecord
def test_rename_column_using_symbol_arguments
add_column :test_models, :first_name, :string
- TestModel.create :first_name => 'foo'
+ TestModel.create first_name: "foo"
rename_column :test_models, :first_name, :nick_name
TestModel.reset_column_information
- assert TestModel.column_names.include?("nick_name")
- assert_equal ['foo'], TestModel.all.map(&:nick_name)
+ assert_includes TestModel.column_names, "nick_name"
+ assert_equal ["foo"], TestModel.all.map(&:nick_name)
end
# FIXME: another integration test. We should decouple this from the
@@ -41,25 +41,25 @@ module ActiveRecord
def test_rename_column
add_column "test_models", "first_name", "string"
- TestModel.create :first_name => 'foo'
+ TestModel.create first_name: "foo"
rename_column "test_models", "first_name", "nick_name"
TestModel.reset_column_information
- assert TestModel.column_names.include?("nick_name")
- assert_equal ['foo'], TestModel.all.map(&:nick_name)
+ assert_includes TestModel.column_names, "nick_name"
+ assert_equal ["foo"], TestModel.all.map(&:nick_name)
end
def test_rename_column_preserves_default_value_not_null
- add_column 'test_models', 'salary', :integer, :default => 70000
+ add_column "test_models", "salary", :integer, default: 70000
default_before = connection.columns("test_models").find { |c| c.name == "salary" }.default
- assert_equal '70000', default_before
+ assert_equal "70000", default_before
rename_column "test_models", "salary", "annual_salary"
- assert TestModel.column_names.include?("annual_salary")
+ assert_includes TestModel.column_names, "annual_salary"
default_after = connection.columns("test_models").find { |c| c.name == "annual_salary" }.default
- assert_equal '70000', default_after
+ assert_equal "70000", default_after
end
if current_adapter?(:Mysql2Adapter)
@@ -74,30 +74,31 @@ module ActiveRecord
def test_rename_nonexistent_column
exception = if current_adapter?(:PostgreSQLAdapter, :OracleAdapter)
- ActiveRecord::StatementInvalid
- else
- ActiveRecord::ActiveRecordError
- end
+ ActiveRecord::StatementInvalid
+ else
+ ActiveRecord::ActiveRecordError
+ end
+
assert_raise(exception) do
rename_column "test_models", "nonexistent", "should_fail"
end
end
def test_rename_column_with_sql_reserved_word
- add_column 'test_models', 'first_name', :string
+ add_column "test_models", "first_name", :string
rename_column "test_models", "first_name", "group"
- assert TestModel.column_names.include?("group")
+ assert_includes TestModel.column_names, "group"
end
def test_rename_column_with_an_index
add_column "test_models", :hat_name, :string
add_index :test_models, :hat_name
- assert_equal 1, connection.indexes('test_models').size
+ assert_equal 1, connection.indexes("test_models").size
rename_column "test_models", "hat_name", "name"
- assert_equal ['index_test_models_on_name'], connection.indexes('test_models').map(&:name)
+ assert_equal ["index_test_models_on_name"], connection.indexes("test_models").map(&:name)
end
def test_rename_column_with_multi_column_index
@@ -105,153 +106,153 @@ module ActiveRecord
add_column "test_models", :hat_style, :string, limit: 100
add_index "test_models", ["hat_style", "hat_size"], unique: true
- rename_column "test_models", "hat_size", 'size'
+ rename_column "test_models", "hat_size", "size"
if current_adapter? :OracleAdapter
- assert_equal ['i_test_models_hat_style_size'], connection.indexes('test_models').map(&:name)
+ assert_equal ["i_test_models_hat_style_size"], connection.indexes("test_models").map(&:name)
else
- assert_equal ['index_test_models_on_hat_style_and_size'], connection.indexes('test_models').map(&:name)
+ assert_equal ["index_test_models_on_hat_style_and_size"], connection.indexes("test_models").map(&:name)
end
- rename_column "test_models", "hat_style", 'style'
+ rename_column "test_models", "hat_style", "style"
if current_adapter? :OracleAdapter
- assert_equal ['i_test_models_style_size'], connection.indexes('test_models').map(&:name)
+ assert_equal ["i_test_models_style_size"], connection.indexes("test_models").map(&:name)
else
- assert_equal ['index_test_models_on_style_and_size'], connection.indexes('test_models').map(&:name)
+ assert_equal ["index_test_models_on_style_and_size"], connection.indexes("test_models").map(&:name)
end
end
def test_rename_column_does_not_rename_custom_named_index
add_column "test_models", :hat_name, :string
- add_index :test_models, :hat_name, :name => 'idx_hat_name'
+ add_index :test_models, :hat_name, name: "idx_hat_name"
- assert_equal 1, connection.indexes('test_models').size
+ assert_equal 1, connection.indexes("test_models").size
rename_column "test_models", "hat_name", "name"
- assert_equal ['idx_hat_name'], connection.indexes('test_models').map(&:name)
+ assert_equal ["idx_hat_name"], connection.indexes("test_models").map(&:name)
end
def test_remove_column_with_index
add_column "test_models", :hat_name, :string
add_index :test_models, :hat_name
- assert_equal 1, connection.indexes('test_models').size
+ assert_equal 1, connection.indexes("test_models").size
remove_column("test_models", "hat_name")
- assert_equal 0, connection.indexes('test_models').size
+ assert_equal 0, connection.indexes("test_models").size
end
def test_remove_column_with_multi_column_index
add_column "test_models", :hat_size, :integer
- add_column "test_models", :hat_style, :string, :limit => 100
- add_index "test_models", ["hat_style", "hat_size"], :unique => true
+ add_column "test_models", :hat_style, :string, limit: 100
+ add_index "test_models", ["hat_style", "hat_size"], unique: true
- assert_equal 1, connection.indexes('test_models').size
+ assert_equal 1, connection.indexes("test_models").size
remove_column("test_models", "hat_size")
# Every database and/or database adapter has their own behavior
# if it drops the multi-column index when any of the indexed columns dropped by remove_column.
if current_adapter?(:PostgreSQLAdapter, :OracleAdapter)
- assert_equal [], connection.indexes('test_models').map(&:name)
+ assert_equal [], connection.indexes("test_models").map(&:name)
else
- assert_equal ['index_test_models_on_hat_style_and_hat_size'], connection.indexes('test_models').map(&:name)
+ assert_equal ["index_test_models_on_hat_style_and_hat_size"], connection.indexes("test_models").map(&:name)
end
end
def test_change_type_of_not_null_column
- change_column "test_models", "updated_at", :datetime, :null => false
- change_column "test_models", "updated_at", :datetime, :null => false
+ change_column "test_models", "updated_at", :datetime, null: false
+ change_column "test_models", "updated_at", :datetime, null: false
TestModel.reset_column_information
- assert_equal false, TestModel.columns_hash['updated_at'].null
+ assert_equal false, TestModel.columns_hash["updated_at"].null
ensure
- change_column "test_models", "updated_at", :datetime, :null => true
+ change_column "test_models", "updated_at", :datetime, null: true
end
def test_change_column_nullability
add_column "test_models", "funny", :boolean
assert TestModel.columns_hash["funny"].null, "Column 'funny' must initially allow nulls"
- change_column "test_models", "funny", :boolean, :null => false, :default => true
+ change_column "test_models", "funny", :boolean, null: false, default: true
TestModel.reset_column_information
assert_not TestModel.columns_hash["funny"].null, "Column 'funny' must *not* allow nulls at this point"
- change_column "test_models", "funny", :boolean, :null => true
+ change_column "test_models", "funny", :boolean, null: true
TestModel.reset_column_information
assert TestModel.columns_hash["funny"].null, "Column 'funny' must allow nulls again at this point"
end
def test_change_column
- add_column 'test_models', 'age', :integer
- add_column 'test_models', 'approved', :boolean, :default => true
+ add_column "test_models", "age", :integer
+ add_column "test_models", "approved", :boolean, default: true
old_columns = connection.columns(TestModel.table_name)
- assert old_columns.find { |c| c.name == 'age' && c.type == :integer }
+ assert old_columns.find { |c| c.name == "age" && c.type == :integer }
change_column "test_models", "age", :string
new_columns = connection.columns(TestModel.table_name)
- assert_not new_columns.find { |c| c.name == 'age' and c.type == :integer }
- assert new_columns.find { |c| c.name == 'age' and c.type == :string }
+ assert_not new_columns.find { |c| c.name == "age" && c.type == :integer }
+ assert new_columns.find { |c| c.name == "age" && c.type == :string }
old_columns = connection.columns(TestModel.table_name)
assert old_columns.find { |c|
default = connection.lookup_cast_type_from_column(c).deserialize(c.default)
- c.name == 'approved' && c.type == :boolean && default == true
+ c.name == "approved" && c.type == :boolean && default == true
}
- change_column :test_models, :approved, :boolean, :default => false
+ change_column :test_models, :approved, :boolean, default: false
new_columns = connection.columns(TestModel.table_name)
assert_not new_columns.find { |c|
default = connection.lookup_cast_type_from_column(c).deserialize(c.default)
- c.name == 'approved' and c.type == :boolean and default == true
+ c.name == "approved" && c.type == :boolean && default == true
}
assert new_columns.find { |c|
default = connection.lookup_cast_type_from_column(c).deserialize(c.default)
- c.name == 'approved' and c.type == :boolean and default == false
+ c.name == "approved" && c.type == :boolean && default == false
}
- change_column :test_models, :approved, :boolean, :default => true
+ change_column :test_models, :approved, :boolean, default: true
end
def test_change_column_with_nil_default
- add_column "test_models", "contributor", :boolean, :default => true
+ add_column "test_models", "contributor", :boolean, default: true
assert TestModel.new.contributor?
- change_column "test_models", "contributor", :boolean, :default => nil
+ change_column "test_models", "contributor", :boolean, default: nil
TestModel.reset_column_information
assert_not TestModel.new.contributor?
assert_nil TestModel.new.contributor
end
def test_change_column_with_new_default
- add_column "test_models", "administrator", :boolean, :default => true
+ add_column "test_models", "administrator", :boolean, default: true
assert TestModel.new.administrator?
- change_column "test_models", "administrator", :boolean, :default => false
+ change_column "test_models", "administrator", :boolean, default: false
TestModel.reset_column_information
assert_not TestModel.new.administrator?
end
def test_change_column_with_custom_index_name
add_column "test_models", "category", :string
- add_index :test_models, :category, name: 'test_models_categories_idx'
+ add_index :test_models, :category, name: "test_models_categories_idx"
- assert_equal ['test_models_categories_idx'], connection.indexes('test_models').map(&:name)
- change_column "test_models", "category", :string, null: false, default: 'article'
+ assert_equal ["test_models_categories_idx"], connection.indexes("test_models").map(&:name)
+ change_column "test_models", "category", :string, null: false, default: "article"
- assert_equal ['test_models_categories_idx'], connection.indexes('test_models').map(&:name)
+ assert_equal ["test_models_categories_idx"], connection.indexes("test_models").map(&:name)
end
def test_change_column_with_long_index_name
- table_name_prefix = 'test_models_'
- long_index_name = table_name_prefix + ('x' * (connection.allowed_index_name_length - table_name_prefix.length))
+ table_name_prefix = "test_models_"
+ long_index_name = table_name_prefix + ("x" * (connection.allowed_index_name_length - table_name_prefix.length))
add_column "test_models", "category", :string
add_index :test_models, :category, name: long_index_name
- change_column "test_models", "category", :string, null: false, default: 'article'
+ change_column "test_models", "category", :string, null: false, default: "article"
- assert_equal [long_index_name], connection.indexes('test_models').map(&:name)
+ assert_equal [long_index_name], connection.indexes("test_models").map(&:name)
end
def test_change_column_default
@@ -287,7 +288,7 @@ module ActiveRecord
remove_column("my_table", "col_two")
rename_column("my_table", "col_one", "col_three")
- assert_equal 'my_table_id', connection.primary_key('my_table')
+ assert_equal "my_table_id", connection.primary_key("my_table")
ensure
connection.drop_table(:my_table) rescue nil
end
diff --git a/activerecord/test/cases/migration/command_recorder_test.rb b/activerecord/test/cases/migration/command_recorder_test.rb
index 1e3529db54..802a969cb7 100644
--- a/activerecord/test/cases/migration/command_recorder_test.rb
+++ b/activerecord/test/cases/migration/command_recorder_test.rb
@@ -25,26 +25,26 @@ module ActiveRecord
recorder = CommandRecorder.new(Class.new {
def create_table(name); end
}.new)
- assert recorder.respond_to?(:create_table), 'respond_to? create_table'
+ assert recorder.respond_to?(:create_table), "respond_to? create_table"
recorder.send(:create_table, :horses)
assert_equal [[:create_table, [:horses], nil]], recorder.commands
end
def test_unknown_commands_delegate
recorder = Struct.new(:foo)
- recorder = CommandRecorder.new(recorder.new('bar'))
- assert_equal 'bar', recorder.foo
+ recorder = CommandRecorder.new(recorder.new("bar"))
+ assert_equal "bar", recorder.foo
end
def test_inverse_of_raise_exception_on_unknown_commands
assert_raises(ActiveRecord::IrreversibleMigration) do
- @recorder.inverse_of :execute, ['some sql']
+ @recorder.inverse_of :execute, ["some sql"]
end
end
def test_irreversible_commands_raise_exception
assert_raises(ActiveRecord::IrreversibleMigration) do
- @recorder.revert{ @recorder.execute 'some sql' }
+ @recorder.revert { @recorder.execute "some sql" }
end
end
@@ -58,12 +58,12 @@ module ActiveRecord
@recorder.record :create_table, [:hello]
@recorder.record :create_table, [:world]
end
- tables = @recorder.commands.map{|_cmd, args, _block| args}
+ tables = @recorder.commands.map { |_cmd, args, _block| args }
assert_equal [[:world], [:hello]], tables
end
def test_revert_order
- block = Proc.new{|t| t.string :name }
+ block = Proc.new { |t| t.string :name }
@recorder.instance_eval do
create_table("apples", &block)
revert do
@@ -115,13 +115,13 @@ module ActiveRecord
end
def test_invert_create_table_with_options_and_block
- block = Proc.new{}
+ block = Proc.new {}
drop_table = @recorder.inverse_of :create_table, [:people_reminders, id: false], &block
assert_equal [:drop_table, [:people_reminders, id: false], block], drop_table
end
def test_invert_drop_table
- block = Proc.new{}
+ block = Proc.new {}
create_table = @recorder.inverse_of :drop_table, [:people_reminders, id: false], &block
assert_equal [:create_table, [:people_reminders, id: false], block], create_table
end
@@ -143,7 +143,7 @@ module ActiveRecord
end
def test_invert_drop_join_table
- block = Proc.new{}
+ block = Proc.new {}
create_join_table = @recorder.inverse_of :drop_join_table, [:musics, :artists, table_name: :catalog], &block
assert_equal [:create_join_table, [:musics, :artists, table_name: :catalog], block], create_join_table
end
@@ -166,7 +166,7 @@ module ActiveRecord
def test_invert_change_column_default
assert_raises(ActiveRecord::IrreversibleMigration) do
- @recorder.inverse_of :change_column_default, [:table, :column, 'default_value']
+ @recorder.inverse_of :change_column_default, [:table, :column, "default_value"]
end
end
@@ -203,17 +203,17 @@ module ActiveRecord
def test_invert_add_index
remove = @recorder.inverse_of :add_index, [:table, [:one, :two]]
- assert_equal [:remove_index, [:table, {column: [:one, :two]}]], remove
+ assert_equal [:remove_index, [:table, { column: [:one, :two] }]], remove
end
def test_invert_add_index_with_name
remove = @recorder.inverse_of :add_index, [:table, [:one, :two], name: "new_index"]
- assert_equal [:remove_index, [:table, {name: "new_index"}]], remove
+ assert_equal [:remove_index, [:table, { name: "new_index" }]], remove
end
def test_invert_add_index_with_no_options
remove = @recorder.inverse_of :add_index, [:table, [:one, :two]]
- assert_equal [:remove_index, [:table, {column: [:one, :two]}]], remove
+ assert_equal [:remove_index, [:table, { column: [:one, :two] }]], remove
end
def test_invert_remove_index
@@ -222,17 +222,17 @@ module ActiveRecord
end
def test_invert_remove_index_with_column
- add = @recorder.inverse_of :remove_index, [:table, {column: [:one, :two], options: true}]
+ add = @recorder.inverse_of :remove_index, [:table, { column: [:one, :two], options: true }]
assert_equal [:add_index, [:table, [:one, :two], options: true]], add
end
def test_invert_remove_index_with_name
- add = @recorder.inverse_of :remove_index, [:table, {column: [:one, :two], name: "new_index"}]
+ add = @recorder.inverse_of :remove_index, [:table, { column: [:one, :two], name: "new_index" }]
assert_equal [:add_index, [:table, [:one, :two], name: "new_index"]], add
end
def test_invert_remove_index_with_no_special_options
- add = @recorder.inverse_of :remove_index, [:table, {column: [:one, :two]}]
+ add = @recorder.inverse_of :remove_index, [:table, { column: [:one, :two] }]
assert_equal [:add_index, [:table, [:one, :two], {}]], add
end
@@ -254,7 +254,7 @@ module ActiveRecord
def test_invert_remove_timestamps
add = @recorder.inverse_of :remove_timestamps, [:table, { null: true }]
- assert_equal [:add_timestamps, [:table, {null: true }], nil], add
+ assert_equal [:add_timestamps, [:table, { null: true }], nil], add
end
def test_invert_add_reference
@@ -283,13 +283,13 @@ module ActiveRecord
end
def test_invert_enable_extension
- disable = @recorder.inverse_of :enable_extension, ['uuid-ossp']
- assert_equal [:disable_extension, ['uuid-ossp'], nil], disable
+ disable = @recorder.inverse_of :enable_extension, ["uuid-ossp"]
+ assert_equal [:disable_extension, ["uuid-ossp"], nil], disable
end
def test_invert_disable_extension
- enable = @recorder.inverse_of :disable_extension, ['uuid-ossp']
- assert_equal [:enable_extension, ['uuid-ossp'], nil], enable
+ enable = @recorder.inverse_of :disable_extension, ["uuid-ossp"]
+ assert_equal [:enable_extension, ["uuid-ossp"], nil], enable
end
def test_invert_add_foreign_key
diff --git a/activerecord/test/cases/migration/compatibility_test.rb b/activerecord/test/cases/migration/compatibility_test.rb
index 60ca90464d..e5a7412bc3 100644
--- a/activerecord/test/cases/migration/compatibility_test.rb
+++ b/activerecord/test/cases/migration/compatibility_test.rb
@@ -1,4 +1,4 @@
-require 'cases/helper'
+require "cases/helper"
module ActiveRecord
class Migration
@@ -13,8 +13,8 @@ module ActiveRecord
ActiveRecord::Migration.verbose = false
connection.create_table :testings do |t|
- t.column :foo, :string, :limit => 100
- t.column :bar, :string, :limit => 100
+ t.column :foo, :string, limit: 100
+ t.column :bar, :string, limit: 100
end
end
@@ -25,7 +25,7 @@ module ActiveRecord
end
def test_migration_doesnt_remove_named_index
- connection.add_index :testings, :foo, :name => "custom_index_name"
+ connection.add_index :testings, :foo, name: "custom_index_name"
migration = Class.new(ActiveRecord::Migration[4.2]) {
def version; 101 end
@@ -55,7 +55,7 @@ module ActiveRecord
end
def test_references_does_not_add_index_by_default
- migration = Class.new(ActiveRecord::Migration) {
+ migration = Class.new(ActiveRecord::Migration[4.2]) {
def migrate(x)
create_table :more_testings do |t|
t.references :foo
@@ -73,7 +73,7 @@ module ActiveRecord
end
def test_timestamps_have_null_constraints_if_not_present_in_migration_of_create_table
- migration = Class.new(ActiveRecord::Migration) {
+ migration = Class.new(ActiveRecord::Migration[4.2]) {
def migrate(x)
create_table :more_testings do |t|
t.timestamps
@@ -83,14 +83,14 @@ module ActiveRecord
ActiveRecord::Migrator.new(:up, [migration]).migrate
- assert connection.columns(:more_testings).find { |c| c.name == 'created_at' }.null
- assert connection.columns(:more_testings).find { |c| c.name == 'updated_at' }.null
+ assert connection.columns(:more_testings).find { |c| c.name == "created_at" }.null
+ assert connection.columns(:more_testings).find { |c| c.name == "updated_at" }.null
ensure
connection.drop_table :more_testings rescue nil
end
def test_timestamps_have_null_constraints_if_not_present_in_migration_for_adding_timestamps_to_existing_table
- migration = Class.new(ActiveRecord::Migration) {
+ migration = Class.new(ActiveRecord::Migration[4.2]) {
def migrate(x)
add_timestamps :testings
end
@@ -98,19 +98,13 @@ module ActiveRecord
ActiveRecord::Migrator.new(:up, [migration]).migrate
- assert connection.columns(:testings).find { |c| c.name == 'created_at' }.null
- assert connection.columns(:testings).find { |c| c.name == 'updated_at' }.null
+ assert connection.columns(:testings).find { |c| c.name == "created_at" }.null
+ assert connection.columns(:testings).find { |c| c.name == "updated_at" }.null
end
- def test_legacy_migrations_get_deprecation_warning_when_run
- migration = Class.new(ActiveRecord::Migration) {
- def up
- add_column :testings, :baz, :string
- end
- }
-
- assert_deprecated do
- migration.migrate :up
+ def test_legacy_migrations_raises_exception_when_inherited
+ assert_raises(StandardError) do
+ Class.new(ActiveRecord::Migration)
end
end
end
diff --git a/activerecord/test/cases/migration/create_join_table_test.rb b/activerecord/test/cases/migration/create_join_table_test.rb
index 920c472c73..26b1bb4419 100644
--- a/activerecord/test/cases/migration/create_join_table_test.rb
+++ b/activerecord/test/cases/migration/create_join_table_test.rb
@@ -1,4 +1,4 @@
-require 'cases/helper'
+require "cases/helper"
module ActiveRecord
class Migration
@@ -12,9 +12,7 @@ module ActiveRecord
teardown do
%w(artists_musics musics_videos catalog).each do |table_name|
- ActiveSupport::Deprecation.silence do
- connection.drop_table table_name if connection.table_exists?(table_name)
- end
+ connection.drop_table table_name if connection.table_exists?(table_name)
end
end
@@ -31,13 +29,13 @@ module ActiveRecord
end
def test_create_join_table_with_strings
- connection.create_join_table 'artists', 'musics'
+ connection.create_join_table "artists", "musics"
assert_equal %w(artist_id music_id), connection.columns(:artists_musics).map(&:name).sort
end
def test_create_join_table_with_symbol_and_string
- connection.create_join_table :artists, 'musics'
+ connection.create_join_table :artists, "musics"
assert_equal %w(artist_id music_id), connection.columns(:artists_musics).map(&:name).sort
end
@@ -55,13 +53,13 @@ module ActiveRecord
end
def test_create_join_table_with_the_table_name_as_string
- connection.create_join_table :artists, :musics, table_name: 'catalog'
+ connection.create_join_table :artists, :musics, table_name: "catalog"
assert_equal %w(artist_id music_id), connection.columns(:catalog).map(&:name).sort
end
def test_create_join_table_with_column_options
- connection.create_join_table :artists, :musics, column_options: {null: true}
+ connection.create_join_table :artists, :musics, column_options: { null: true }
assert_equal [true, true], connection.columns(:artists_musics).map(&:null)
end
@@ -84,51 +82,51 @@ module ActiveRecord
connection.create_join_table :artists, :musics
connection.drop_join_table :artists, :musics
- ActiveSupport::Deprecation.silence { assert !connection.table_exists?('artists_musics') }
+ assert !connection.table_exists?("artists_musics")
end
def test_drop_join_table_with_strings
connection.create_join_table :artists, :musics
- connection.drop_join_table 'artists', 'musics'
+ connection.drop_join_table "artists", "musics"
- ActiveSupport::Deprecation.silence { assert !connection.table_exists?('artists_musics') }
+ assert !connection.table_exists?("artists_musics")
end
def test_drop_join_table_with_the_proper_order
connection.create_join_table :videos, :musics
connection.drop_join_table :videos, :musics
- ActiveSupport::Deprecation.silence { assert !connection.table_exists?('musics_videos') }
+ assert !connection.table_exists?("musics_videos")
end
def test_drop_join_table_with_the_table_name
connection.create_join_table :artists, :musics, table_name: :catalog
connection.drop_join_table :artists, :musics, table_name: :catalog
- ActiveSupport::Deprecation.silence { assert !connection.table_exists?('catalog') }
+ assert !connection.table_exists?("catalog")
end
def test_drop_join_table_with_the_table_name_as_string
- connection.create_join_table :artists, :musics, table_name: 'catalog'
- connection.drop_join_table :artists, :musics, table_name: 'catalog'
+ connection.create_join_table :artists, :musics, table_name: "catalog"
+ connection.drop_join_table :artists, :musics, table_name: "catalog"
- ActiveSupport::Deprecation.silence { assert !connection.table_exists?('catalog') }
+ assert !connection.table_exists?("catalog")
end
def test_drop_join_table_with_column_options
- connection.create_join_table :artists, :musics, column_options: {null: true}
- connection.drop_join_table :artists, :musics, column_options: {null: true}
+ connection.create_join_table :artists, :musics, column_options: { null: true }
+ connection.drop_join_table :artists, :musics, column_options: { null: true }
- ActiveSupport::Deprecation.silence { assert !connection.table_exists?('artists_musics') }
+ assert !connection.table_exists?("artists_musics")
end
def test_create_and_drop_join_table_with_common_prefix
with_table_cleanup do
- connection.create_join_table 'audio_artists', 'audio_musics'
- ActiveSupport::Deprecation.silence { assert connection.table_exists?('audio_artists_musics') }
+ connection.create_join_table "audio_artists", "audio_musics"
+ assert connection.table_exists?("audio_artists_musics")
- connection.drop_join_table 'audio_artists', 'audio_musics'
- ActiveSupport::Deprecation.silence { assert !connection.table_exists?('audio_artists_musics'), "Should have dropped join table, but didn't" }
+ connection.drop_join_table "audio_artists", "audio_musics"
+ assert !connection.table_exists?("audio_artists_musics"), "Should have dropped join table, but didn't"
end
end
diff --git a/activerecord/test/cases/migration/foreign_key_test.rb b/activerecord/test/cases/migration/foreign_key_test.rb
index 4237e89731..9be6667aa1 100644
--- a/activerecord/test/cases/migration/foreign_key_test.rb
+++ b/activerecord/test/cases/migration/foreign_key_test.rb
@@ -1,317 +1,316 @@
-require 'cases/helper'
-require 'support/ddl_helper'
-require 'support/schema_dumping_helper'
+require "cases/helper"
+require "support/ddl_helper"
+require "support/schema_dumping_helper"
if ActiveRecord::Base.connection.supports_foreign_keys?
-module ActiveRecord
- class Migration
- class ForeignKeyTest < ActiveRecord::TestCase
- include DdlHelper
- include SchemaDumpingHelper
- include ActiveSupport::Testing::Stream
-
- class Rocket < ActiveRecord::Base
- end
-
- class Astronaut < ActiveRecord::Base
- end
-
- setup do
- @connection = ActiveRecord::Base.connection
- @connection.create_table "rockets", force: true do |t|
- t.string :name
+ module ActiveRecord
+ class Migration
+ class ForeignKeyTest < ActiveRecord::TestCase
+ include DdlHelper
+ include SchemaDumpingHelper
+ include ActiveSupport::Testing::Stream
+
+ class Rocket < ActiveRecord::Base
end
- @connection.create_table "astronauts", force: true do |t|
- t.string :name
- t.references :rocket
+ class Astronaut < ActiveRecord::Base
end
- end
- teardown do
- if defined?(@connection)
- @connection.drop_table "astronauts", if_exists: true
- @connection.drop_table "rockets", if_exists: true
- end
- end
-
- def test_foreign_keys
- foreign_keys = @connection.foreign_keys("fk_test_has_fk")
- assert_equal 1, foreign_keys.size
+ setup do
+ @connection = ActiveRecord::Base.connection
+ @connection.create_table "rockets", force: true do |t|
+ t.string :name
+ end
- fk = foreign_keys.first
- assert_equal "fk_test_has_fk", fk.from_table
- assert_equal "fk_test_has_pk", fk.to_table
- assert_equal "fk_id", fk.column
- assert_equal "pk_id", fk.primary_key
- assert_equal "fk_name", fk.name
- end
+ @connection.create_table "astronauts", force: true do |t|
+ t.string :name
+ t.references :rocket
+ end
+ end
- def test_add_foreign_key_inferes_column
- @connection.add_foreign_key :astronauts, :rockets
+ teardown do
+ if defined?(@connection)
+ @connection.drop_table "astronauts", if_exists: true
+ @connection.drop_table "rockets", if_exists: true
+ end
+ end
- foreign_keys = @connection.foreign_keys("astronauts")
- assert_equal 1, foreign_keys.size
+ def test_foreign_keys
+ foreign_keys = @connection.foreign_keys("fk_test_has_fk")
+ assert_equal 1, foreign_keys.size
- fk = foreign_keys.first
- assert_equal "astronauts", fk.from_table
- assert_equal "rockets", fk.to_table
- assert_equal "rocket_id", fk.column
- assert_equal "id", fk.primary_key
- assert_equal("fk_rails_78146ddd2e", fk.name)
- end
+ fk = foreign_keys.first
+ assert_equal "fk_test_has_fk", fk.from_table
+ assert_equal "fk_test_has_pk", fk.to_table
+ assert_equal "fk_id", fk.column
+ assert_equal "pk_id", fk.primary_key
+ assert_equal "fk_name", fk.name
+ end
- def test_add_foreign_key_with_column
- @connection.add_foreign_key :astronauts, :rockets, column: "rocket_id"
+ def test_add_foreign_key_inferes_column
+ @connection.add_foreign_key :astronauts, :rockets
- foreign_keys = @connection.foreign_keys("astronauts")
- assert_equal 1, foreign_keys.size
+ foreign_keys = @connection.foreign_keys("astronauts")
+ assert_equal 1, foreign_keys.size
- fk = foreign_keys.first
- assert_equal "astronauts", fk.from_table
- assert_equal "rockets", fk.to_table
- assert_equal "rocket_id", fk.column
- assert_equal "id", fk.primary_key
- assert_equal("fk_rails_78146ddd2e", fk.name)
- end
+ fk = foreign_keys.first
+ assert_equal "astronauts", fk.from_table
+ assert_equal "rockets", fk.to_table
+ assert_equal "rocket_id", fk.column
+ assert_equal "id", fk.primary_key
+ assert_equal("fk_rails_78146ddd2e", fk.name)
+ end
- def test_add_foreign_key_with_non_standard_primary_key
- with_example_table @connection, "space_shuttles", "pk integer PRIMARY KEY" do
- @connection.add_foreign_key(:astronauts, :space_shuttles,
- column: "rocket_id", primary_key: "pk", name: "custom_pk")
+ def test_add_foreign_key_with_column
+ @connection.add_foreign_key :astronauts, :rockets, column: "rocket_id"
foreign_keys = @connection.foreign_keys("astronauts")
assert_equal 1, foreign_keys.size
fk = foreign_keys.first
assert_equal "astronauts", fk.from_table
- assert_equal "space_shuttles", fk.to_table
- assert_equal "pk", fk.primary_key
-
- @connection.remove_foreign_key :astronauts, name: "custom_pk"
+ assert_equal "rockets", fk.to_table
+ assert_equal "rocket_id", fk.column
+ assert_equal "id", fk.primary_key
+ assert_equal("fk_rails_78146ddd2e", fk.name)
end
- end
- def test_add_on_delete_restrict_foreign_key
- @connection.add_foreign_key :astronauts, :rockets, column: "rocket_id", on_delete: :restrict
+ def test_add_foreign_key_with_non_standard_primary_key
+ with_example_table @connection, "space_shuttles", "pk BIGINT PRIMARY KEY" do
+ @connection.add_foreign_key(:astronauts, :space_shuttles,
+ column: "rocket_id", primary_key: "pk", name: "custom_pk")
+
+ foreign_keys = @connection.foreign_keys("astronauts")
+ assert_equal 1, foreign_keys.size
- foreign_keys = @connection.foreign_keys("astronauts")
- assert_equal 1, foreign_keys.size
+ fk = foreign_keys.first
+ assert_equal "astronauts", fk.from_table
+ assert_equal "space_shuttles", fk.to_table
+ assert_equal "pk", fk.primary_key
- fk = foreign_keys.first
- if current_adapter?(:Mysql2Adapter)
- # ON DELETE RESTRICT is the default on MySQL
- assert_equal nil, fk.on_delete
- else
- assert_equal :restrict, fk.on_delete
+ @connection.remove_foreign_key :astronauts, name: "custom_pk"
+ end
end
- end
- def test_add_on_delete_cascade_foreign_key
- @connection.add_foreign_key :astronauts, :rockets, column: "rocket_id", on_delete: :cascade
+ def test_add_on_delete_restrict_foreign_key
+ @connection.add_foreign_key :astronauts, :rockets, column: "rocket_id", on_delete: :restrict
- foreign_keys = @connection.foreign_keys("astronauts")
- assert_equal 1, foreign_keys.size
+ foreign_keys = @connection.foreign_keys("astronauts")
+ assert_equal 1, foreign_keys.size
- fk = foreign_keys.first
- assert_equal :cascade, fk.on_delete
- end
+ fk = foreign_keys.first
+ if current_adapter?(:Mysql2Adapter)
+ # ON DELETE RESTRICT is the default on MySQL
+ assert_nil fk.on_delete
+ else
+ assert_equal :restrict, fk.on_delete
+ end
+ end
- def test_add_on_delete_nullify_foreign_key
- @connection.add_foreign_key :astronauts, :rockets, column: "rocket_id", on_delete: :nullify
+ def test_add_on_delete_cascade_foreign_key
+ @connection.add_foreign_key :astronauts, :rockets, column: "rocket_id", on_delete: :cascade
- foreign_keys = @connection.foreign_keys("astronauts")
- assert_equal 1, foreign_keys.size
+ foreign_keys = @connection.foreign_keys("astronauts")
+ assert_equal 1, foreign_keys.size
- fk = foreign_keys.first
- assert_equal :nullify, fk.on_delete
- end
+ fk = foreign_keys.first
+ assert_equal :cascade, fk.on_delete
+ end
+
+ def test_add_on_delete_nullify_foreign_key
+ @connection.add_foreign_key :astronauts, :rockets, column: "rocket_id", on_delete: :nullify
+
+ foreign_keys = @connection.foreign_keys("astronauts")
+ assert_equal 1, foreign_keys.size
- def test_on_update_and_on_delete_raises_with_invalid_values
- assert_raises ArgumentError do
- @connection.add_foreign_key :astronauts, :rockets, column: "rocket_id", on_delete: :invalid
+ fk = foreign_keys.first
+ assert_equal :nullify, fk.on_delete
end
- assert_raises ArgumentError do
- @connection.add_foreign_key :astronauts, :rockets, column: "rocket_id", on_update: :invalid
+ def test_on_update_and_on_delete_raises_with_invalid_values
+ assert_raises ArgumentError do
+ @connection.add_foreign_key :astronauts, :rockets, column: "rocket_id", on_delete: :invalid
+ end
+
+ assert_raises ArgumentError do
+ @connection.add_foreign_key :astronauts, :rockets, column: "rocket_id", on_update: :invalid
+ end
end
- end
- def test_add_foreign_key_with_on_update
- @connection.add_foreign_key :astronauts, :rockets, column: "rocket_id", on_update: :nullify
+ def test_add_foreign_key_with_on_update
+ @connection.add_foreign_key :astronauts, :rockets, column: "rocket_id", on_update: :nullify
- foreign_keys = @connection.foreign_keys("astronauts")
- assert_equal 1, foreign_keys.size
+ foreign_keys = @connection.foreign_keys("astronauts")
+ assert_equal 1, foreign_keys.size
- fk = foreign_keys.first
- assert_equal :nullify, fk.on_update
- end
+ fk = foreign_keys.first
+ assert_equal :nullify, fk.on_update
+ end
- def test_foreign_key_exists
- @connection.add_foreign_key :astronauts, :rockets
+ def test_foreign_key_exists
+ @connection.add_foreign_key :astronauts, :rockets
- assert @connection.foreign_key_exists?(:astronauts, :rockets)
- assert_not @connection.foreign_key_exists?(:astronauts, :stars)
- end
+ assert @connection.foreign_key_exists?(:astronauts, :rockets)
+ assert_not @connection.foreign_key_exists?(:astronauts, :stars)
+ end
- def test_foreign_key_exists_by_column
- @connection.add_foreign_key :astronauts, :rockets, column: "rocket_id"
+ def test_foreign_key_exists_by_column
+ @connection.add_foreign_key :astronauts, :rockets, column: "rocket_id"
- assert @connection.foreign_key_exists?(:astronauts, column: "rocket_id")
- assert_not @connection.foreign_key_exists?(:astronauts, column: "star_id")
- end
+ assert @connection.foreign_key_exists?(:astronauts, column: "rocket_id")
+ assert_not @connection.foreign_key_exists?(:astronauts, column: "star_id")
+ end
- def test_foreign_key_exists_by_name
- @connection.add_foreign_key :astronauts, :rockets, column: "rocket_id", name: "fancy_named_fk"
+ def test_foreign_key_exists_by_name
+ @connection.add_foreign_key :astronauts, :rockets, column: "rocket_id", name: "fancy_named_fk"
- assert @connection.foreign_key_exists?(:astronauts, name: "fancy_named_fk")
- assert_not @connection.foreign_key_exists?(:astronauts, name: "other_fancy_named_fk")
- end
+ assert @connection.foreign_key_exists?(:astronauts, name: "fancy_named_fk")
+ assert_not @connection.foreign_key_exists?(:astronauts, name: "other_fancy_named_fk")
+ end
- def test_remove_foreign_key_inferes_column
- @connection.add_foreign_key :astronauts, :rockets
+ def test_remove_foreign_key_inferes_column
+ @connection.add_foreign_key :astronauts, :rockets
- assert_equal 1, @connection.foreign_keys("astronauts").size
- @connection.remove_foreign_key :astronauts, :rockets
- assert_equal [], @connection.foreign_keys("astronauts")
- end
+ assert_equal 1, @connection.foreign_keys("astronauts").size
+ @connection.remove_foreign_key :astronauts, :rockets
+ assert_equal [], @connection.foreign_keys("astronauts")
+ end
- def test_remove_foreign_key_by_column
- @connection.add_foreign_key :astronauts, :rockets, column: "rocket_id"
+ def test_remove_foreign_key_by_column
+ @connection.add_foreign_key :astronauts, :rockets, column: "rocket_id"
- assert_equal 1, @connection.foreign_keys("astronauts").size
- @connection.remove_foreign_key :astronauts, column: "rocket_id"
- assert_equal [], @connection.foreign_keys("astronauts")
- end
+ assert_equal 1, @connection.foreign_keys("astronauts").size
+ @connection.remove_foreign_key :astronauts, column: "rocket_id"
+ assert_equal [], @connection.foreign_keys("astronauts")
+ end
- def test_remove_foreign_key_by_symbol_column
- @connection.add_foreign_key :astronauts, :rockets, column: :rocket_id
+ def test_remove_foreign_key_by_symbol_column
+ @connection.add_foreign_key :astronauts, :rockets, column: :rocket_id
- assert_equal 1, @connection.foreign_keys("astronauts").size
- @connection.remove_foreign_key :astronauts, column: :rocket_id
- assert_equal [], @connection.foreign_keys("astronauts")
- end
+ assert_equal 1, @connection.foreign_keys("astronauts").size
+ @connection.remove_foreign_key :astronauts, column: :rocket_id
+ assert_equal [], @connection.foreign_keys("astronauts")
+ end
- def test_remove_foreign_key_by_name
- @connection.add_foreign_key :astronauts, :rockets, column: "rocket_id", name: "fancy_named_fk"
+ def test_remove_foreign_key_by_name
+ @connection.add_foreign_key :astronauts, :rockets, column: "rocket_id", name: "fancy_named_fk"
- assert_equal 1, @connection.foreign_keys("astronauts").size
- @connection.remove_foreign_key :astronauts, name: "fancy_named_fk"
- assert_equal [], @connection.foreign_keys("astronauts")
- end
+ assert_equal 1, @connection.foreign_keys("astronauts").size
+ @connection.remove_foreign_key :astronauts, name: "fancy_named_fk"
+ assert_equal [], @connection.foreign_keys("astronauts")
+ end
- def test_remove_foreign_non_existing_foreign_key_raises
- assert_raises ArgumentError do
- @connection.remove_foreign_key :astronauts, :rockets
+ def test_remove_foreign_non_existing_foreign_key_raises
+ assert_raises ArgumentError do
+ @connection.remove_foreign_key :astronauts, :rockets
+ end
end
- end
- def test_schema_dumping
- @connection.add_foreign_key :astronauts, :rockets
- output = dump_table_schema "astronauts"
- assert_match %r{\s+add_foreign_key "astronauts", "rockets"$}, output
- end
+ def test_schema_dumping
+ @connection.add_foreign_key :astronauts, :rockets
+ output = dump_table_schema "astronauts"
+ assert_match %r{\s+add_foreign_key "astronauts", "rockets"$}, output
+ end
- def test_schema_dumping_with_options
- output = dump_table_schema "fk_test_has_fk"
- assert_match %r{\s+add_foreign_key "fk_test_has_fk", "fk_test_has_pk", column: "fk_id", primary_key: "pk_id", name: "fk_name"$}, output
- end
+ def test_schema_dumping_with_options
+ output = dump_table_schema "fk_test_has_fk"
+ assert_match %r{\s+add_foreign_key "fk_test_has_fk", "fk_test_has_pk", column: "fk_id", primary_key: "pk_id", name: "fk_name"$}, output
+ end
- def test_schema_dumping_on_delete_and_on_update_options
- @connection.add_foreign_key :astronauts, :rockets, column: "rocket_id", on_delete: :nullify, on_update: :cascade
+ def test_schema_dumping_on_delete_and_on_update_options
+ @connection.add_foreign_key :astronauts, :rockets, column: "rocket_id", on_delete: :nullify, on_update: :cascade
- output = dump_table_schema "astronauts"
- assert_match %r{\s+add_foreign_key "astronauts",.+on_update: :cascade,.+on_delete: :nullify$}, output
- end
+ output = dump_table_schema "astronauts"
+ assert_match %r{\s+add_foreign_key "astronauts",.+on_update: :cascade,.+on_delete: :nullify$}, output
+ end
- class CreateCitiesAndHousesMigration < ActiveRecord::Migration::Current
- def change
- create_table("cities") { |t| }
+ class CreateCitiesAndHousesMigration < ActiveRecord::Migration::Current
+ def change
+ create_table("cities") { |t| }
- create_table("houses") do |t|
- t.column :city_id, :integer
- end
- add_foreign_key :houses, :cities, column: "city_id"
+ create_table("houses") do |t|
+ t.column :city_id, :bigint
+ end
+ add_foreign_key :houses, :cities, column: "city_id"
- # remove and re-add to test that schema is updated and not accidentally cached
- remove_foreign_key :houses, :cities
- add_foreign_key :houses, :cities, column: "city_id", on_delete: :cascade
+ # remove and re-add to test that schema is updated and not accidentally cached
+ remove_foreign_key :houses, :cities
+ add_foreign_key :houses, :cities, column: "city_id", on_delete: :cascade
+ end
end
- end
- def test_add_foreign_key_is_reversible
- migration = CreateCitiesAndHousesMigration.new
- silence_stream($stdout) { migration.migrate(:up) }
- assert_equal 1, @connection.foreign_keys("houses").size
- ensure
- silence_stream($stdout) { migration.migrate(:down) }
- end
+ def test_add_foreign_key_is_reversible
+ migration = CreateCitiesAndHousesMigration.new
+ silence_stream($stdout) { migration.migrate(:up) }
+ assert_equal 1, @connection.foreign_keys("houses").size
+ ensure
+ silence_stream($stdout) { migration.migrate(:down) }
+ end
- def test_foreign_key_constraint_is_not_cached_incorrectly
- migration = CreateCitiesAndHousesMigration.new
- silence_stream($stdout) { migration.migrate(:up) }
- output = dump_table_schema "houses"
- assert_match %r{\s+add_foreign_key "houses",.+on_delete: :cascade$}, output
- ensure
- silence_stream($stdout) { migration.migrate(:down) }
- end
+ def test_foreign_key_constraint_is_not_cached_incorrectly
+ migration = CreateCitiesAndHousesMigration.new
+ silence_stream($stdout) { migration.migrate(:up) }
+ output = dump_table_schema "houses"
+ assert_match %r{\s+add_foreign_key "houses",.+on_delete: :cascade$}, output
+ ensure
+ silence_stream($stdout) { migration.migrate(:down) }
+ end
- class CreateSchoolsAndClassesMigration < ActiveRecord::Migration::Current
- def change
- create_table(:schools)
+ class CreateSchoolsAndClassesMigration < ActiveRecord::Migration::Current
+ def change
+ create_table(:schools)
- create_table(:classes) do |t|
- t.column :school_id, :integer
+ create_table(:classes) do |t|
+ t.column :school_id, :bigint
+ end
+ add_foreign_key :classes, :schools
end
- add_foreign_key :classes, :schools
end
- end
- def test_add_foreign_key_with_prefix
- ActiveRecord::Base.table_name_prefix = 'p_'
- migration = CreateSchoolsAndClassesMigration.new
- silence_stream($stdout) { migration.migrate(:up) }
- assert_equal 1, @connection.foreign_keys("p_classes").size
- ensure
- silence_stream($stdout) { migration.migrate(:down) }
- ActiveRecord::Base.table_name_prefix = nil
- end
+ def test_add_foreign_key_with_prefix
+ ActiveRecord::Base.table_name_prefix = "p_"
+ migration = CreateSchoolsAndClassesMigration.new
+ silence_stream($stdout) { migration.migrate(:up) }
+ assert_equal 1, @connection.foreign_keys("p_classes").size
+ ensure
+ silence_stream($stdout) { migration.migrate(:down) }
+ ActiveRecord::Base.table_name_prefix = nil
+ end
- def test_add_foreign_key_with_suffix
- ActiveRecord::Base.table_name_suffix = '_s'
- migration = CreateSchoolsAndClassesMigration.new
- silence_stream($stdout) { migration.migrate(:up) }
- assert_equal 1, @connection.foreign_keys("classes_s").size
- ensure
- silence_stream($stdout) { migration.migrate(:down) }
- ActiveRecord::Base.table_name_suffix = nil
+ def test_add_foreign_key_with_suffix
+ ActiveRecord::Base.table_name_suffix = "_s"
+ migration = CreateSchoolsAndClassesMigration.new
+ silence_stream($stdout) { migration.migrate(:up) }
+ assert_equal 1, @connection.foreign_keys("classes_s").size
+ ensure
+ silence_stream($stdout) { migration.migrate(:down) }
+ ActiveRecord::Base.table_name_suffix = nil
+ end
end
-
end
end
-end
else
-module ActiveRecord
- class Migration
- class NoForeignKeySupportTest < ActiveRecord::TestCase
- setup do
- @connection = ActiveRecord::Base.connection
- end
+ module ActiveRecord
+ class Migration
+ class NoForeignKeySupportTest < ActiveRecord::TestCase
+ setup do
+ @connection = ActiveRecord::Base.connection
+ end
- def test_add_foreign_key_should_be_noop
- @connection.add_foreign_key :clubs, :categories
- end
+ def test_add_foreign_key_should_be_noop
+ @connection.add_foreign_key :clubs, :categories
+ end
- def test_remove_foreign_key_should_be_noop
- @connection.remove_foreign_key :clubs, :categories
- end
+ def test_remove_foreign_key_should_be_noop
+ @connection.remove_foreign_key :clubs, :categories
+ end
- def test_foreign_keys_should_raise_not_implemented
- assert_raises NotImplementedError do
- @connection.foreign_keys("clubs")
+ def test_foreign_keys_should_raise_not_implemented
+ assert_raises NotImplementedError do
+ @connection.foreign_keys("clubs")
+ end
end
end
end
end
end
-end
diff --git a/activerecord/test/cases/migration/helper.rb b/activerecord/test/cases/migration/helper.rb
index ad85684c0b..9c0fa7339d 100644
--- a/activerecord/test/cases/migration/helper.rb
+++ b/activerecord/test/cases/migration/helper.rb
@@ -33,7 +33,7 @@ module ActiveRecord
private
- delegate(*CONNECTION_METHODS, to: :connection)
+ delegate(*CONNECTION_METHODS, to: :connection)
end
end
end
diff --git a/activerecord/test/cases/migration/index_test.rb b/activerecord/test/cases/migration/index_test.rb
index 5abd37bfa2..0f975026b8 100644
--- a/activerecord/test/cases/migration/index_test.rb
+++ b/activerecord/test/cases/migration/index_test.rb
@@ -1,4 +1,4 @@
-require 'cases/helper'
+require "cases/helper"
module ActiveRecord
class Migration
@@ -11,12 +11,12 @@ module ActiveRecord
@table_name = :testings
connection.create_table table_name do |t|
- t.column :foo, :string, :limit => 100
- t.column :bar, :string, :limit => 100
+ t.column :foo, :string, limit: 100
+ t.column :bar, :string, limit: 100
t.string :first_name
- t.string :last_name, :limit => 100
- t.string :key, :limit => 100
+ t.string :last_name, limit: 100
+ t.string :key, limit: 100
t.boolean :administrator
end
end
@@ -28,32 +28,31 @@ module ActiveRecord
def test_rename_index
# keep the names short to make Oracle and similar behave
- connection.add_index(table_name, [:foo], :name => 'old_idx')
- connection.rename_index(table_name, 'old_idx', 'new_idx')
+ connection.add_index(table_name, [:foo], name: "old_idx")
+ connection.rename_index(table_name, "old_idx", "new_idx")
# if the adapter doesn't support the indexes call, pick defaults that let the test pass
- assert_not connection.index_name_exists?(table_name, 'old_idx', false)
- assert connection.index_name_exists?(table_name, 'new_idx', true)
+ assert_not connection.index_name_exists?(table_name, "old_idx", false)
+ assert connection.index_name_exists?(table_name, "new_idx", true)
end
def test_rename_index_too_long
- too_long_index_name = good_index_name + 'x'
+ too_long_index_name = good_index_name + "x"
# keep the names short to make Oracle and similar behave
- connection.add_index(table_name, [:foo], :name => 'old_idx')
+ connection.add_index(table_name, [:foo], name: "old_idx")
e = assert_raises(ArgumentError) {
- connection.rename_index(table_name, 'old_idx', too_long_index_name)
+ connection.rename_index(table_name, "old_idx", too_long_index_name)
}
assert_match(/too long; the limit is #{connection.allowed_index_name_length} characters/, e.message)
# if the adapter doesn't support the indexes call, pick defaults that let the test pass
- assert connection.index_name_exists?(table_name, 'old_idx', false)
+ assert connection.index_name_exists?(table_name, "old_idx", false)
end
-
def test_double_add_index
- connection.add_index(table_name, [:foo], :name => 'some_idx')
+ connection.add_index(table_name, [:foo], name: "some_idx")
assert_raises(ArgumentError) {
- connection.add_index(table_name, [:foo], :name => 'some_idx')
+ connection.add_index(table_name, [:foo], name: "some_idx")
}
end
@@ -69,7 +68,7 @@ module ActiveRecord
end
def test_add_index_does_not_accept_too_long_index_names
- too_long_index_name = good_index_name + 'x'
+ too_long_index_name = good_index_name + "x"
e = assert_raises(ArgumentError) {
connection.add_index(table_name, "foo", name: too_long_index_name)
@@ -77,11 +76,11 @@ module ActiveRecord
assert_match(/too long; the limit is #{connection.allowed_index_name_length} characters/, e.message)
assert_not connection.index_name_exists?(table_name, too_long_index_name, false)
- connection.add_index(table_name, "foo", :name => good_index_name)
+ connection.add_index(table_name, "foo", name: good_index_name)
end
def test_internal_index_with_name_matching_database_limit
- good_index_name = 'x' * connection.index_name_length
+ good_index_name = "x" * connection.index_name_length
connection.add_index(table_name, "foo", name: good_index_name, internal: true)
assert connection.index_name_exists?(table_name, good_index_name, false)
@@ -89,11 +88,11 @@ module ActiveRecord
end
def test_index_symbol_names
- connection.add_index table_name, :foo, :name => :symbol_index_name
- assert connection.index_exists?(table_name, :foo, :name => :symbol_index_name)
+ connection.add_index table_name, :foo, name: :symbol_index_name
+ assert connection.index_exists?(table_name, :foo, name: :symbol_index_name)
- connection.remove_index table_name, :name => :symbol_index_name
- assert_not connection.index_exists?(table_name, :foo, :name => :symbol_index_name)
+ connection.remove_index table_name, name: :symbol_index_name
+ assert_not connection.index_exists?(table_name, :foo, name: :symbol_index_name)
end
def test_index_exists
@@ -122,21 +121,21 @@ module ActiveRecord
end
def test_unique_index_exists
- connection.add_index :testings, :foo, :unique => true
+ connection.add_index :testings, :foo, unique: true
- assert connection.index_exists?(:testings, :foo, :unique => true)
+ assert connection.index_exists?(:testings, :foo, unique: true)
end
def test_named_index_exists
- connection.add_index :testings, :foo, :name => "custom_index_name"
+ connection.add_index :testings, :foo, name: "custom_index_name"
assert connection.index_exists?(:testings, :foo)
- assert connection.index_exists?(:testings, :foo, :name => "custom_index_name")
- assert !connection.index_exists?(:testings, :foo, :name => "other_index_name")
+ assert connection.index_exists?(:testings, :foo, name: "custom_index_name")
+ assert !connection.index_exists?(:testings, :foo, name: "other_index_name")
end
def test_remove_named_index
- connection.add_index :testings, :foo, :name => "custom_index_name"
+ connection.add_index :testings, :foo, name: "custom_index_name"
assert connection.index_exists?(:testings, :foo)
connection.remove_index :testings, :foo
@@ -144,7 +143,7 @@ module ActiveRecord
end
def test_add_index_attribute_length_limit
- connection.add_index :testings, [:foo, :bar], :length => {:foo => 10, :bar => nil}
+ connection.add_index :testings, [:foo, :bar], length: { foo: 10, bar: nil }
assert connection.index_exists?(:testings, [:foo, :bar])
end
@@ -154,53 +153,53 @@ module ActiveRecord
connection.remove_index("testings", "last_name")
connection.add_index("testings", ["last_name", "first_name"])
- connection.remove_index("testings", :column => ["last_name", "first_name"])
+ connection.remove_index("testings", column: ["last_name", "first_name"])
# Oracle adapter cannot have specified index name larger than 30 characters
# Oracle adapter is shortening index name when just column list is given
unless current_adapter?(:OracleAdapter)
connection.add_index("testings", ["last_name", "first_name"])
- connection.remove_index("testings", :name => :index_testings_on_last_name_and_first_name)
+ connection.remove_index("testings", name: :index_testings_on_last_name_and_first_name)
connection.add_index("testings", ["last_name", "first_name"])
connection.remove_index("testings", "last_name_and_first_name")
end
connection.add_index("testings", ["last_name", "first_name"])
connection.remove_index("testings", ["last_name", "first_name"])
- connection.add_index("testings", ["last_name"], :length => 10)
+ connection.add_index("testings", ["last_name"], length: 10)
connection.remove_index("testings", "last_name")
- connection.add_index("testings", ["last_name"], :length => {:last_name => 10})
+ connection.add_index("testings", ["last_name"], length: { last_name: 10 })
connection.remove_index("testings", ["last_name"])
- connection.add_index("testings", ["last_name", "first_name"], :length => 10)
+ connection.add_index("testings", ["last_name", "first_name"], length: 10)
connection.remove_index("testings", ["last_name", "first_name"])
- connection.add_index("testings", ["last_name", "first_name"], :length => {:last_name => 10, :first_name => 20})
+ connection.add_index("testings", ["last_name", "first_name"], length: { last_name: 10, first_name: 20 })
connection.remove_index("testings", ["last_name", "first_name"])
- connection.add_index("testings", ["key"], :name => "key_idx", :unique => true)
- connection.remove_index("testings", :name => "key_idx", :unique => true)
+ connection.add_index("testings", ["key"], name: "key_idx", unique: true)
+ connection.remove_index("testings", name: "key_idx", unique: true)
- connection.add_index("testings", %w(last_name first_name administrator), :name => "named_admin")
- connection.remove_index("testings", :name => "named_admin")
+ connection.add_index("testings", %w(last_name first_name administrator), name: "named_admin")
+ connection.remove_index("testings", name: "named_admin")
# Selected adapters support index sort order
if current_adapter?(:SQLite3Adapter, :Mysql2Adapter, :PostgreSQLAdapter)
- connection.add_index("testings", ["last_name"], :order => {:last_name => :desc})
+ connection.add_index("testings", ["last_name"], order: { last_name: :desc })
connection.remove_index("testings", ["last_name"])
- connection.add_index("testings", ["last_name", "first_name"], :order => {:last_name => :desc})
+ connection.add_index("testings", ["last_name", "first_name"], order: { last_name: :desc })
connection.remove_index("testings", ["last_name", "first_name"])
- connection.add_index("testings", ["last_name", "first_name"], :order => {:last_name => :desc, :first_name => :asc})
+ connection.add_index("testings", ["last_name", "first_name"], order: { last_name: :desc, first_name: :asc })
connection.remove_index("testings", ["last_name", "first_name"])
- connection.add_index("testings", ["last_name", "first_name"], :order => :desc)
+ connection.add_index("testings", ["last_name", "first_name"], order: :desc)
connection.remove_index("testings", ["last_name", "first_name"])
end
end
if current_adapter?(:PostgreSQLAdapter)
def test_add_partial_index
- connection.add_index("testings", "last_name", :where => "first_name = 'john doe'")
+ connection.add_index("testings", "last_name", where: "first_name = 'john doe'")
assert connection.index_exists?("testings", "last_name")
connection.remove_index("testings", "last_name")
@@ -210,9 +209,8 @@ module ActiveRecord
private
def good_index_name
- 'x' * connection.allowed_index_name_length
+ "x" * connection.allowed_index_name_length
end
-
end
end
end
diff --git a/activerecord/test/cases/migration/logger_test.rb b/activerecord/test/cases/migration/logger_test.rb
index bf6e684887..3d7c7ad469 100644
--- a/activerecord/test/cases/migration/logger_test.rb
+++ b/activerecord/test/cases/migration/logger_test.rb
@@ -8,7 +8,7 @@ module ActiveRecord
Migration = Struct.new(:name, :version) do
def disable_ddl_transaction; false end
- def migrate direction
+ def migrate(direction)
# do nothing
end
end
@@ -26,7 +26,7 @@ module ActiveRecord
def test_migration_should_be_run_without_logger
previous_logger = ActiveRecord::Base.logger
ActiveRecord::Base.logger = nil
- migrations = [Migration.new('a', 1), Migration.new('b', 2), Migration.new('c', 3)]
+ migrations = [Migration.new("a", 1), Migration.new("b", 2), Migration.new("c", 3)]
ActiveRecord::Migrator.new(:up, migrations).migrate
ensure
ActiveRecord::Base.logger = previous_logger
diff --git a/activerecord/test/cases/migration/pending_migrations_test.rb b/activerecord/test/cases/migration/pending_migrations_test.rb
index 4f5589f32a..61f5a061b0 100644
--- a/activerecord/test/cases/migration/pending_migrations_test.rb
+++ b/activerecord/test/cases/migration/pending_migrations_test.rb
@@ -1,4 +1,4 @@
-require 'cases/helper'
+require "cases/helper"
module ActiveRecord
class Migration
diff --git a/activerecord/test/cases/migration/references_foreign_key_test.rb b/activerecord/test/cases/migration/references_foreign_key_test.rb
index 9e19eb9f73..4957ab8b3d 100644
--- a/activerecord/test/cases/migration/references_foreign_key_test.rb
+++ b/activerecord/test/cases/migration/references_foreign_key_test.rb
@@ -1,216 +1,216 @@
-require 'cases/helper'
+require "cases/helper"
if ActiveRecord::Base.connection.supports_foreign_keys?
-module ActiveRecord
- class Migration
- class ReferencesForeignKeyTest < ActiveRecord::TestCase
- setup do
- @connection = ActiveRecord::Base.connection
- @connection.create_table(:testing_parents, force: true)
- end
-
- teardown do
- @connection.drop_table "testings", if_exists: true
- @connection.drop_table "testing_parents", if_exists: true
- end
+ module ActiveRecord
+ class Migration
+ class ReferencesForeignKeyTest < ActiveRecord::TestCase
+ setup do
+ @connection = ActiveRecord::Base.connection
+ @connection.create_table(:testing_parents, force: true)
+ end
- test "foreign keys can be created with the table" do
- @connection.create_table :testings do |t|
- t.references :testing_parent, foreign_key: true
+ teardown do
+ @connection.drop_table "testings", if_exists: true
+ @connection.drop_table "testing_parents", if_exists: true
end
- fk = @connection.foreign_keys("testings").first
- assert_equal "testings", fk.from_table
- assert_equal "testing_parents", fk.to_table
- end
+ test "foreign keys can be created with the table" do
+ @connection.create_table :testings do |t|
+ t.references :testing_parent, foreign_key: true
+ end
- test "no foreign key is created by default" do
- @connection.create_table :testings do |t|
- t.references :testing_parent
+ fk = @connection.foreign_keys("testings").first
+ assert_equal "testings", fk.from_table
+ assert_equal "testing_parents", fk.to_table
end
- assert_equal [], @connection.foreign_keys("testings")
- end
-
- test "foreign keys can be created in one query when index is not added" do
- assert_queries(1) do
+ test "no foreign key is created by default" do
@connection.create_table :testings do |t|
- t.references :testing_parent, foreign_key: true, index: false
+ t.references :testing_parent
end
- end
- end
- test "options hash can be passed" do
- @connection.change_table :testing_parents do |t|
- t.integer :other_id
- t.index :other_id, unique: true
+ assert_equal [], @connection.foreign_keys("testings")
end
- @connection.create_table :testings do |t|
- t.references :testing_parent, foreign_key: { primary_key: :other_id }
+
+ test "foreign keys can be created in one query when index is not added" do
+ assert_queries(1) do
+ @connection.create_table :testings do |t|
+ t.references :testing_parent, foreign_key: true, index: false
+ end
+ end
end
- fk = @connection.foreign_keys("testings").find { |k| k.to_table == "testing_parents" }
- assert_equal "other_id", fk.primary_key
- end
+ test "options hash can be passed" do
+ @connection.change_table :testing_parents do |t|
+ t.bigint :other_id
+ t.index :other_id, unique: true
+ end
+ @connection.create_table :testings do |t|
+ t.references :testing_parent, foreign_key: { primary_key: :other_id }
+ end
- test "to_table option can be passed" do
- @connection.create_table :testings do |t|
- t.references :parent, foreign_key: { to_table: :testing_parents }
+ fk = @connection.foreign_keys("testings").find { |k| k.to_table == "testing_parents" }
+ assert_equal "other_id", fk.primary_key
end
- fks = @connection.foreign_keys("testings")
- assert_equal([["testings", "testing_parents", "parent_id"]],
- fks.map {|fk| [fk.from_table, fk.to_table, fk.column] })
- end
- test "foreign keys cannot be added to polymorphic relations when creating the table" do
- @connection.create_table :testings do |t|
- assert_raises(ArgumentError) do
- t.references :testing_parent, polymorphic: true, foreign_key: true
+ test "to_table option can be passed" do
+ @connection.create_table :testings do |t|
+ t.references :parent, foreign_key: { to_table: :testing_parents }
end
+ fks = @connection.foreign_keys("testings")
+ assert_equal([["testings", "testing_parents", "parent_id"]],
+ fks.map { |fk| [fk.from_table, fk.to_table, fk.column] })
end
- end
- test "foreign keys can be created while changing the table" do
- @connection.create_table :testings
- @connection.change_table :testings do |t|
- t.references :testing_parent, foreign_key: true
+ test "foreign keys cannot be added to polymorphic relations when creating the table" do
+ @connection.create_table :testings do |t|
+ assert_raises(ArgumentError) do
+ t.references :testing_parent, polymorphic: true, foreign_key: true
+ end
+ end
end
- fk = @connection.foreign_keys("testings").first
- assert_equal "testings", fk.from_table
- assert_equal "testing_parents", fk.to_table
- end
+ test "foreign keys can be created while changing the table" do
+ @connection.create_table :testings
+ @connection.change_table :testings do |t|
+ t.references :testing_parent, foreign_key: true
+ end
- test "foreign keys are not added by default when changing the table" do
- @connection.create_table :testings
- @connection.change_table :testings do |t|
- t.references :testing_parent
+ fk = @connection.foreign_keys("testings").first
+ assert_equal "testings", fk.from_table
+ assert_equal "testing_parents", fk.to_table
end
- assert_equal [], @connection.foreign_keys("testings")
- end
+ test "foreign keys are not added by default when changing the table" do
+ @connection.create_table :testings
+ @connection.change_table :testings do |t|
+ t.references :testing_parent
+ end
- test "foreign keys accept options when changing the table" do
- @connection.change_table :testing_parents do |t|
- t.integer :other_id
- t.index :other_id, unique: true
- end
- @connection.create_table :testings
- @connection.change_table :testings do |t|
- t.references :testing_parent, foreign_key: { primary_key: :other_id }
+ assert_equal [], @connection.foreign_keys("testings")
end
- fk = @connection.foreign_keys("testings").find { |k| k.to_table == "testing_parents" }
- assert_equal "other_id", fk.primary_key
- end
-
- test "foreign keys cannot be added to polymorphic relations when changing the table" do
- @connection.create_table :testings
- @connection.change_table :testings do |t|
- assert_raises(ArgumentError) do
- t.references :testing_parent, polymorphic: true, foreign_key: true
+ test "foreign keys accept options when changing the table" do
+ @connection.change_table :testing_parents do |t|
+ t.bigint :other_id
+ t.index :other_id, unique: true
+ end
+ @connection.create_table :testings
+ @connection.change_table :testings do |t|
+ t.references :testing_parent, foreign_key: { primary_key: :other_id }
end
- end
- end
- test "foreign key column can be removed" do
- @connection.create_table :testings do |t|
- t.references :testing_parent, index: true, foreign_key: true
+ fk = @connection.foreign_keys("testings").find { |k| k.to_table == "testing_parents" }
+ assert_equal "other_id", fk.primary_key
end
- assert_difference "@connection.foreign_keys('testings').size", -1 do
- @connection.remove_reference :testings, :testing_parent, foreign_key: true
+ test "foreign keys cannot be added to polymorphic relations when changing the table" do
+ @connection.create_table :testings
+ @connection.change_table :testings do |t|
+ assert_raises(ArgumentError) do
+ t.references :testing_parent, polymorphic: true, foreign_key: true
+ end
+ end
end
- end
- test "foreign key methods respect pluralize_table_names" do
- begin
- original_pluralize_table_names = ActiveRecord::Base.pluralize_table_names
- ActiveRecord::Base.pluralize_table_names = false
- @connection.create_table :testing
- @connection.change_table :testing_parents do |t|
- t.references :testing, foreign_key: true
+ test "foreign key column can be removed" do
+ @connection.create_table :testings do |t|
+ t.references :testing_parent, index: true, foreign_key: true
end
- fk = @connection.foreign_keys("testing_parents").first
- assert_equal "testing_parents", fk.from_table
- assert_equal "testing", fk.to_table
+ assert_difference "@connection.foreign_keys('testings').size", -1 do
+ @connection.remove_reference :testings, :testing_parent, foreign_key: true
+ end
+ end
- assert_difference "@connection.foreign_keys('testing_parents').size", -1 do
- @connection.remove_reference :testing_parents, :testing, foreign_key: true
+ test "foreign key methods respect pluralize_table_names" do
+ begin
+ original_pluralize_table_names = ActiveRecord::Base.pluralize_table_names
+ ActiveRecord::Base.pluralize_table_names = false
+ @connection.create_table :testing
+ @connection.change_table :testing_parents do |t|
+ t.references :testing, foreign_key: true
+ end
+
+ fk = @connection.foreign_keys("testing_parents").first
+ assert_equal "testing_parents", fk.from_table
+ assert_equal "testing", fk.to_table
+
+ assert_difference "@connection.foreign_keys('testing_parents').size", -1 do
+ @connection.remove_reference :testing_parents, :testing, foreign_key: true
+ end
+ ensure
+ ActiveRecord::Base.pluralize_table_names = original_pluralize_table_names
+ @connection.drop_table "testing", if_exists: true
end
- ensure
- ActiveRecord::Base.pluralize_table_names = original_pluralize_table_names
- @connection.drop_table "testing", if_exists: true
end
- end
- class CreateDogsMigration < ActiveRecord::Migration::Current
- def change
- create_table :dog_owners
+ class CreateDogsMigration < ActiveRecord::Migration::Current
+ def change
+ create_table :dog_owners
- create_table :dogs do |t|
- t.references :dog_owner, foreign_key: true
+ create_table :dogs do |t|
+ t.references :dog_owner, foreign_key: true
+ end
end
end
- end
- def test_references_foreign_key_with_prefix
- ActiveRecord::Base.table_name_prefix = 'p_'
- migration = CreateDogsMigration.new
- silence_stream($stdout) { migration.migrate(:up) }
- assert_equal 1, @connection.foreign_keys("p_dogs").size
- ensure
- silence_stream($stdout) { migration.migrate(:down) }
- ActiveRecord::Base.table_name_prefix = nil
- end
+ def test_references_foreign_key_with_prefix
+ ActiveRecord::Base.table_name_prefix = "p_"
+ migration = CreateDogsMigration.new
+ silence_stream($stdout) { migration.migrate(:up) }
+ assert_equal 1, @connection.foreign_keys("p_dogs").size
+ ensure
+ silence_stream($stdout) { migration.migrate(:down) }
+ ActiveRecord::Base.table_name_prefix = nil
+ end
- def test_references_foreign_key_with_suffix
- ActiveRecord::Base.table_name_suffix = '_s'
- migration = CreateDogsMigration.new
- silence_stream($stdout) { migration.migrate(:up) }
- assert_equal 1, @connection.foreign_keys("dogs_s").size
- ensure
- silence_stream($stdout) { migration.migrate(:down) }
- ActiveRecord::Base.table_name_suffix = nil
- end
+ def test_references_foreign_key_with_suffix
+ ActiveRecord::Base.table_name_suffix = "_s"
+ migration = CreateDogsMigration.new
+ silence_stream($stdout) { migration.migrate(:up) }
+ assert_equal 1, @connection.foreign_keys("dogs_s").size
+ ensure
+ silence_stream($stdout) { migration.migrate(:down) }
+ ActiveRecord::Base.table_name_suffix = nil
+ end
- test "multiple foreign keys can be added to the same table" do
- @connection.create_table :testings do |t|
- t.integer :col_1
- t.integer :col_2
+ test "multiple foreign keys can be added to the same table" do
+ @connection.create_table :testings do |t|
+ t.bigint :col_1
+ t.bigint :col_2
- t.foreign_key :testing_parents, column: :col_1
- t.foreign_key :testing_parents, column: :col_2
- end
+ t.foreign_key :testing_parents, column: :col_1
+ t.foreign_key :testing_parents, column: :col_2
+ end
- fks = @connection.foreign_keys("testings")
+ fks = @connection.foreign_keys("testings")
- fk_definitions = fks.map {|fk| [fk.from_table, fk.to_table, fk.column] }
- assert_equal([["testings", "testing_parents", "col_1"],
- ["testings", "testing_parents", "col_2"]], fk_definitions)
+ fk_definitions = fks.map { |fk| [fk.from_table, fk.to_table, fk.column] }
+ assert_equal([["testings", "testing_parents", "col_1"],
+ ["testings", "testing_parents", "col_2"]], fk_definitions)
+ end
end
end
end
-end
else
-class ReferencesWithoutForeignKeySupportTest < ActiveRecord::TestCase
- setup do
- @connection = ActiveRecord::Base.connection
- @connection.create_table(:testing_parents, force: true)
- end
-
- teardown do
- @connection.drop_table("testings", if_exists: true)
- @connection.drop_table("testing_parents", if_exists: true)
- end
+ class ReferencesWithoutForeignKeySupportTest < ActiveRecord::TestCase
+ setup do
+ @connection = ActiveRecord::Base.connection
+ @connection.create_table(:testing_parents, force: true)
+ end
- test "ignores foreign keys defined with the table" do
- @connection.create_table :testings do |t|
- t.references :testing_parent, foreign_key: true
+ teardown do
+ @connection.drop_table("testings", if_exists: true)
+ @connection.drop_table("testing_parents", if_exists: true)
end
- assert_includes @connection.data_sources, "testings"
+ test "ignores foreign keys defined with the table" do
+ @connection.create_table :testings do |t|
+ t.references :testing_parent, foreign_key: true
+ end
+
+ assert_includes @connection.data_sources, "testings"
+ end
end
end
-end
diff --git a/activerecord/test/cases/migration/references_index_test.rb b/activerecord/test/cases/migration/references_index_test.rb
index a9a7f0f4c4..2866cabab6 100644
--- a/activerecord/test/cases/migration/references_index_test.rb
+++ b/activerecord/test/cases/migration/references_index_test.rb
@@ -1,4 +1,4 @@
-require 'cases/helper'
+require "cases/helper"
module ActiveRecord
class Migration
@@ -17,10 +17,10 @@ module ActiveRecord
def test_creates_index
connection.create_table table_name do |t|
- t.references :foo, :index => true
+ t.references :foo, index: true
end
- assert connection.index_exists?(table_name, :foo_id, :name => :index_testings_on_foo_id)
+ assert connection.index_exists?(table_name, :foo_id, name: :index_testings_on_foo_id)
end
def test_creates_index_by_default_even_if_index_option_is_not_passed
@@ -28,31 +28,31 @@ module ActiveRecord
t.references :foo
end
- assert connection.index_exists?(table_name, :foo_id, :name => :index_testings_on_foo_id)
+ assert connection.index_exists?(table_name, :foo_id, name: :index_testings_on_foo_id)
end
def test_does_not_create_index_explicit
connection.create_table table_name do |t|
- t.references :foo, :index => false
+ t.references :foo, index: false
end
- assert_not connection.index_exists?(table_name, :foo_id, :name => :index_testings_on_foo_id)
+ assert_not connection.index_exists?(table_name, :foo_id, name: :index_testings_on_foo_id)
end
def test_creates_index_with_options
connection.create_table table_name do |t|
- t.references :foo, :index => {:name => :index_testings_on_yo_momma}
- t.references :bar, :index => {:unique => true}
+ t.references :foo, index: { name: :index_testings_on_yo_momma }
+ t.references :bar, index: { unique: true }
end
- assert connection.index_exists?(table_name, :foo_id, :name => :index_testings_on_yo_momma)
- assert connection.index_exists?(table_name, :bar_id, :name => :index_testings_on_bar_id, :unique => true)
+ assert connection.index_exists?(table_name, :foo_id, name: :index_testings_on_yo_momma)
+ assert connection.index_exists?(table_name, :bar_id, name: :index_testings_on_bar_id, unique: true)
end
unless current_adapter? :OracleAdapter
def test_creates_polymorphic_index
connection.create_table table_name do |t|
- t.references :foo, :polymorphic => true, :index => true
+ t.references :foo, polymorphic: true, index: true
end
assert connection.index_exists?(table_name, [:foo_type, :foo_id], name: :index_testings_on_foo_type_and_foo_id)
@@ -62,10 +62,10 @@ module ActiveRecord
def test_creates_index_for_existing_table
connection.create_table table_name
connection.change_table table_name do |t|
- t.references :foo, :index => true
+ t.references :foo, index: true
end
- assert connection.index_exists?(table_name, :foo_id, :name => :index_testings_on_foo_id)
+ assert connection.index_exists?(table_name, :foo_id, name: :index_testings_on_foo_id)
end
def test_creates_index_for_existing_table_even_if_index_option_is_not_passed
@@ -74,23 +74,23 @@ module ActiveRecord
t.references :foo
end
- assert connection.index_exists?(table_name, :foo_id, :name => :index_testings_on_foo_id)
+ assert connection.index_exists?(table_name, :foo_id, name: :index_testings_on_foo_id)
end
def test_does_not_create_index_for_existing_table_explicit
connection.create_table table_name
connection.change_table table_name do |t|
- t.references :foo, :index => false
+ t.references :foo, index: false
end
- assert_not connection.index_exists?(table_name, :foo_id, :name => :index_testings_on_foo_id)
+ assert_not connection.index_exists?(table_name, :foo_id, name: :index_testings_on_foo_id)
end
unless current_adapter? :OracleAdapter
def test_creates_polymorphic_index_for_existing_table
connection.create_table table_name
connection.change_table table_name do |t|
- t.references :foo, :polymorphic => true, :index => true
+ t.references :foo, polymorphic: true, index: true
end
assert connection.index_exists?(table_name, [:foo_type, :foo_id], name: :index_testings_on_foo_type_and_foo_id)
diff --git a/activerecord/test/cases/migration/references_statements_test.rb b/activerecord/test/cases/migration/references_statements_test.rb
index 70c64f3e71..df15d7cb45 100644
--- a/activerecord/test/cases/migration/references_statements_test.rb
+++ b/activerecord/test/cases/migration/references_statements_test.rb
@@ -35,7 +35,7 @@ module ActiveRecord
assert_not index_exists?(table_name, :user_id)
end
- def test_create_reference_id_index_even_if_index_option_is_passed
+ def test_create_reference_id_index_even_if_index_option_is_not_passed
add_reference table_name, :user
assert index_exists?(table_name, :user_id)
end
@@ -46,18 +46,18 @@ module ActiveRecord
end
def test_creates_reference_type_column_with_default
- add_reference table_name, :taggable, polymorphic: { default: 'Photo' }, index: true
- assert column_exists?(table_name, :taggable_type, :string, default: 'Photo')
+ add_reference table_name, :taggable, polymorphic: { default: "Photo" }, index: true
+ assert column_exists?(table_name, :taggable_type, :string, default: "Photo")
end
def test_creates_named_index
- add_reference table_name, :tag, index: { name: 'index_taggings_on_tag_id' }
- assert index_exists?(table_name, :tag_id, name: 'index_taggings_on_tag_id')
+ add_reference table_name, :tag, index: { name: "index_taggings_on_tag_id" }
+ assert index_exists?(table_name, :tag_id, name: "index_taggings_on_tag_id")
end
def test_creates_named_unique_index
- add_reference table_name, :tag, index: { name: 'index_taggings_on_tag_id', unique: true }
- assert index_exists?(table_name, :tag_id, name: 'index_taggings_on_tag_id', unique: true )
+ add_reference table_name, :tag, index: { name: "index_taggings_on_tag_id", unique: true }
+ assert index_exists?(table_name, :tag_id, name: "index_taggings_on_tag_id", unique: true)
end
def test_creates_reference_id_with_specified_type
@@ -110,12 +110,12 @@ module ActiveRecord
private
- def with_polymorphic_column
- add_column table_name, :supplier_type, :string
- add_index table_name, [:supplier_id, :supplier_type]
+ def with_polymorphic_column
+ add_column table_name, :supplier_type, :string
+ add_index table_name, [:supplier_id, :supplier_type]
- yield
- end
+ yield
+ end
end
end
end
diff --git a/activerecord/test/cases/migration/rename_table_test.rb b/activerecord/test/cases/migration/rename_table_test.rb
index b926a92849..19588d28a2 100644
--- a/activerecord/test/cases/migration/rename_table_test.rb
+++ b/activerecord/test/cases/migration/rename_table_test.rb
@@ -9,13 +9,13 @@ module ActiveRecord
def setup
super
- add_column 'test_models', :url, :string
- remove_column 'test_models', :created_at
- remove_column 'test_models', :updated_at
+ add_column "test_models", :url, :string
+ remove_column "test_models", :created_at
+ remove_column "test_models", :updated_at
end
def teardown
- ActiveSupport::Deprecation.silence { rename_table :octopi, :test_models if connection.table_exists? :octopi }
+ rename_table :octopi, :test_models if connection.table_exists? :octopi
super
end
@@ -31,7 +31,7 @@ module ActiveRecord
# Using explicit id in insert for compatibility across all databases
connection.execute "INSERT INTO 'references' (url, created_at, updated_at) VALUES ('http://rubyonrails.com', 0, 0)"
- assert_equal 'http://rubyonrails.com', connection.select_value("SELECT url FROM 'references' WHERE id=1")
+ assert_equal "http://rubyonrails.com", connection.select_value("SELECT url FROM 'references' WHERE id=1")
ensure
return unless renamed
connection.rename_table :references, :test_models
@@ -45,7 +45,7 @@ module ActiveRecord
connection.execute "INSERT INTO octopi (#{connection.quote_column_name('id')}, #{connection.quote_column_name('url')}) VALUES (1, 'http://www.foreverflying.com/octopus-black7.jpg')"
- assert_equal 'http://www.foreverflying.com/octopus-black7.jpg', connection.select_value("SELECT url FROM octopi WHERE id=1")
+ assert_equal "http://www.foreverflying.com/octopus-black7.jpg", connection.select_value("SELECT url FROM octopi WHERE id=1")
end
def test_rename_table_with_an_index
@@ -55,18 +55,18 @@ module ActiveRecord
connection.execute "INSERT INTO octopi (#{connection.quote_column_name('id')}, #{connection.quote_column_name('url')}) VALUES (1, 'http://www.foreverflying.com/octopus-black7.jpg')"
- assert_equal 'http://www.foreverflying.com/octopus-black7.jpg', connection.select_value("SELECT url FROM octopi WHERE id=1")
+ assert_equal "http://www.foreverflying.com/octopus-black7.jpg", connection.select_value("SELECT url FROM octopi WHERE id=1")
index = connection.indexes(:octopi).first
- assert index.columns.include?("url")
- assert_equal 'index_octopi_on_url', index.name
+ assert_includes index.columns, "url"
+ assert_equal "index_octopi_on_url", index.name
end
def test_rename_table_does_not_rename_custom_named_index
- add_index :test_models, :url, name: 'special_url_idx'
+ add_index :test_models, :url, name: "special_url_idx"
rename_table :test_models, :octopi
- assert_equal ['special_url_idx'], connection.indexes(:octopi).map(&:name)
+ assert_equal ["special_url_idx"], connection.indexes(:octopi).map(&:name)
end
end
@@ -74,7 +74,7 @@ module ActiveRecord
def test_rename_table_for_postgresql_should_also_rename_default_sequence
rename_table :test_models, :octopi
- pk, seq = connection.pk_and_sequence_for('octopi')
+ pk, seq = connection.pk_and_sequence_for("octopi")
assert_equal ConnectionAdapters::PostgreSQL::Name.new("public", "octopi_#{pk}_seq"), seq
end
@@ -82,7 +82,7 @@ module ActiveRecord
def test_renaming_table_doesnt_attempt_to_rename_non_existent_sequences
connection.create_table :cats, id: :uuid
assert_nothing_raised { rename_table :cats, :felines }
- ActiveSupport::Deprecation.silence { assert connection.table_exists? :felines }
+ assert connection.table_exists? :felines
ensure
connection.drop_table :cats, if_exists: true
connection.drop_table :felines, if_exists: true
diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb
index d499aadbae..082cfd3242 100644
--- a/activerecord/test/cases/migration_test.rb
+++ b/activerecord/test/cases/migration_test.rb
@@ -1,12 +1,12 @@
-require 'cases/helper'
-require 'cases/migration/helper'
-require 'bigdecimal/util'
-require 'concurrent/atomic/count_down_latch'
+require "cases/helper"
+require "cases/migration/helper"
+require "bigdecimal/util"
+require "concurrent/atomic/count_down_latch"
-require 'models/person'
-require 'models/topic'
-require 'models/developer'
-require 'models/computer'
+require "models/person"
+require "models/topic"
+require "models/developer"
+require "models/computer"
require MIGRATIONS_ROOT + "/valid/2_we_need_reminders"
require MIGRATIONS_ROOT + "/rename/1_we_need_things"
@@ -59,7 +59,7 @@ class MigrationTest < ActiveRecord::TestCase
%w(last_name key bio age height wealth birthday favorite_day
moment_of_truth male administrator funny).each do |column|
- Person.connection.remove_column('people', column) rescue nil
+ Person.connection.remove_column("people", column) rescue nil
end
Person.connection.remove_column("people", "first_name") rescue nil
Person.connection.remove_column("people", "middle_name") rescue nil
@@ -93,7 +93,7 @@ class MigrationTest < ActiveRecord::TestCase
end
def test_migration_detection_without_schema_migration_table
- ActiveRecord::Base.connection.drop_table 'schema_migrations', if_exists: true
+ ActiveRecord::Base.connection.drop_table "schema_migrations", if_exists: true
migrations_path = MIGRATIONS_ROOT + "/valid"
old_path = ActiveRecord::Migrator.migrations_paths
@@ -128,7 +128,7 @@ class MigrationTest < ActiveRecord::TestCase
assert_not_equal temp_conn, Person.connection
- temp_conn.create_table :testings2, :force => true do |t|
+ temp_conn.create_table :testings2, force: true do |t|
t.column :foo, :string
end
ensure
@@ -160,11 +160,11 @@ class MigrationTest < ActiveRecord::TestCase
BigNumber.reset_column_information
assert BigNumber.create(
- :bank_balance => 1586.43,
- :big_bank_balance => BigDecimal("1000234000567.95"),
- :world_population => 6000000000,
- :my_house_population => 3,
- :value_of_e => BigDecimal("2.7182818284590452353602875")
+ bank_balance: 1586.43,
+ big_bank_balance: BigDecimal("1000234000567.95"),
+ world_population: 6000000000,
+ my_house_population: 3,
+ value_of_e: BigDecimal("2.7182818284590452353602875")
)
b = BigNumber.first
@@ -248,22 +248,22 @@ class MigrationTest < ActiveRecord::TestCase
def test_instance_based_migration_up
migration = MockMigration.new
- assert !migration.went_up, 'have not gone up'
- assert !migration.went_down, 'have not gone down'
+ assert !migration.went_up, "have not gone up"
+ assert !migration.went_down, "have not gone down"
migration.migrate :up
- assert migration.went_up, 'have gone up'
- assert !migration.went_down, 'have not gone down'
+ assert migration.went_up, "have gone up"
+ assert !migration.went_down, "have not gone down"
end
def test_instance_based_migration_down
migration = MockMigration.new
- assert !migration.went_up, 'have not gone up'
- assert !migration.went_down, 'have not gone down'
+ assert !migration.went_up, "have not gone up"
+ assert !migration.went_down, "have not gone down"
migration.migrate :down
- assert !migration.went_up, 'have gone up'
- assert migration.went_down, 'have not gone down'
+ assert !migration.went_up, "have gone up"
+ assert migration.went_down, "have not gone down"
end
if ActiveRecord::Base.connection.supports_ddl_transactions?
@@ -274,7 +274,7 @@ class MigrationTest < ActiveRecord::TestCase
def version; 100 end
def migrate(x)
add_column "people", "last_name", :string
- raise 'Something broke'
+ raise "Something broke"
end
}.new
@@ -295,7 +295,7 @@ class MigrationTest < ActiveRecord::TestCase
def version; 100 end
def migrate(x)
add_column "people", "last_name", :string
- raise 'Something broke'
+ raise "Something broke"
end
}.new
@@ -313,12 +313,12 @@ class MigrationTest < ActiveRecord::TestCase
assert_no_column Person, :last_name
migration = Class.new(ActiveRecord::Migration::Current) {
- self.disable_ddl_transaction!
+ disable_ddl_transaction!
def version; 101 end
def migrate(x)
add_column "people", "last_name", :string
- raise 'Something broke'
+ raise "Something broke"
end
}.new
@@ -330,8 +330,8 @@ class MigrationTest < ActiveRecord::TestCase
"without ddl transactions, the Migrator should not rollback on error but it did."
ensure
Person.reset_column_information
- if Person.column_names.include?('last_name')
- Person.connection.remove_column('people', 'last_name')
+ if Person.column_names.include?("last_name")
+ Person.connection.remove_column("people", "last_name")
end
end
end
@@ -388,7 +388,7 @@ class MigrationTest < ActiveRecord::TestCase
original_rails_env = ENV["RAILS_ENV"]
original_rack_env = ENV["RACK_ENV"]
ENV["RAILS_ENV"] = ENV["RACK_ENV"] = "foofoo"
- new_env = ActiveRecord::ConnectionHandling::DEFAULT_ENV.call
+ new_env = ActiveRecord::ConnectionHandling::DEFAULT_ENV.call
refute_equal current_env, new_env
@@ -401,7 +401,6 @@ class MigrationTest < ActiveRecord::TestCase
ENV["RACK_ENV"] = original_rack_env
end
-
def test_migration_sets_internal_metadata_even_when_fully_migrated
current_env = ActiveRecord::ConnectionHandling::DEFAULT_ENV.call
migrations_path = MIGRATIONS_ROOT + "/valid"
@@ -414,7 +413,7 @@ class MigrationTest < ActiveRecord::TestCase
original_rails_env = ENV["RAILS_ENV"]
original_rack_env = ENV["RACK_ENV"]
ENV["RAILS_ENV"] = ENV["RACK_ENV"] = "foofoo"
- new_env = ActiveRecord::ConnectionHandling::DEFAULT_ENV.call
+ new_env = ActiveRecord::ConnectionHandling::DEFAULT_ENV.call
refute_equal current_env, new_env
@@ -430,17 +429,17 @@ class MigrationTest < ActiveRecord::TestCase
def test_internal_metadata_stores_environment_when_other_data_exists
ActiveRecord::InternalMetadata.delete_all
- ActiveRecord::InternalMetadata[:foo] = 'bar'
+ ActiveRecord::InternalMetadata[:foo] = "bar"
current_env = ActiveRecord::ConnectionHandling::DEFAULT_ENV.call
migrations_path = MIGRATIONS_ROOT + "/valid"
old_path = ActiveRecord::Migrator.migrations_paths
ActiveRecord::Migrator.migrations_paths = migrations_path
- current_env = ActiveRecord::ConnectionHandling::DEFAULT_ENV.call
+ current_env = ActiveRecord::ConnectionHandling::DEFAULT_ENV.call
ActiveRecord::Migrator.up(migrations_path)
assert_equal current_env, ActiveRecord::InternalMetadata[:environment]
- assert_equal 'bar', ActiveRecord::InternalMetadata[:foo]
+ assert_equal "bar", ActiveRecord::InternalMetadata[:foo]
ensure
ActiveRecord::Migrator.migrations_paths = old_path
end
@@ -448,7 +447,7 @@ class MigrationTest < ActiveRecord::TestCase
def test_proper_table_name_on_migration
reminder_class = new_isolated_reminder_class
migration = ActiveRecord::Migration.new
- assert_equal "table", migration.proper_table_name('table')
+ assert_equal "table", migration.proper_table_name("table")
assert_equal "table", migration.proper_table_name(:table)
assert_equal "reminders", migration.proper_table_name(reminder_class)
reminder_class.reset_table_name
@@ -457,26 +456,26 @@ class MigrationTest < ActiveRecord::TestCase
# Use the model's own prefix/suffix if a model is given
ActiveRecord::Base.table_name_prefix = "ARprefix_"
ActiveRecord::Base.table_name_suffix = "_ARsuffix"
- reminder_class.table_name_prefix = 'prefix_'
- reminder_class.table_name_suffix = '_suffix'
+ reminder_class.table_name_prefix = "prefix_"
+ reminder_class.table_name_suffix = "_suffix"
reminder_class.reset_table_name
assert_equal "prefix_reminders_suffix", migration.proper_table_name(reminder_class)
- reminder_class.table_name_prefix = ''
- reminder_class.table_name_suffix = ''
+ reminder_class.table_name_prefix = ""
+ reminder_class.table_name_suffix = ""
reminder_class.reset_table_name
# Use AR::Base's prefix/suffix if string or symbol is given
ActiveRecord::Base.table_name_prefix = "prefix_"
ActiveRecord::Base.table_name_suffix = "_suffix"
reminder_class.reset_table_name
- assert_equal "prefix_table_suffix", migration.proper_table_name('table', migration.table_name_options)
+ assert_equal "prefix_table_suffix", migration.proper_table_name("table", migration.table_name_options)
assert_equal "prefix_table_suffix", migration.proper_table_name(:table, migration.table_name_options)
end
def test_rename_table_with_prefix_and_suffix
assert !Thing.table_exists?
- ActiveRecord::Base.table_name_prefix = 'p_'
- ActiveRecord::Base.table_name_suffix = '_s'
+ ActiveRecord::Base.table_name_prefix = "p_"
+ ActiveRecord::Base.table_name_suffix = "_s"
Thing.reset_table_name
Thing.reset_sequence_name
WeNeedThings.up
@@ -496,8 +495,8 @@ class MigrationTest < ActiveRecord::TestCase
def test_add_drop_table_with_prefix_and_suffix
assert !Reminder.table_exists?
- ActiveRecord::Base.table_name_prefix = 'prefix_'
- ActiveRecord::Base.table_name_suffix = '_suffix'
+ ActiveRecord::Base.table_name_prefix = "prefix_"
+ ActiveRecord::Base.table_name_suffix = "_suffix"
Reminder.reset_table_name
Reminder.reset_sequence_name
Reminder.reset_column_information
@@ -514,7 +513,7 @@ class MigrationTest < ActiveRecord::TestCase
def test_create_table_with_binary_column
assert_nothing_raised {
Person.connection.create_table :binary_testings do |t|
- t.column "data", :binary, :null => false
+ t.column "data", :binary, null: false
end
}
@@ -552,6 +551,23 @@ class MigrationTest < ActiveRecord::TestCase
end
end
+ if current_adapter?(:SQLite3Adapter)
+ def test_allows_sqlite3_rollback_on_invalid_column_type
+ Person.connection.create_table :something, force: true do |t|
+ t.column :number, :integer
+ t.column :name, :string
+ t.column :foo, :bar
+ end
+ assert Person.connection.column_exists?(:something, :foo)
+ assert_nothing_raised { Person.connection.remove_column :something, :foo, :bar }
+ assert !Person.connection.column_exists?(:something, :foo)
+ assert Person.connection.column_exists?(:something, :name)
+ assert Person.connection.column_exists?(:something, :number)
+ ensure
+ Person.connection.drop_table :something, if_exists: true
+ end
+ end
+
if current_adapter? :OracleAdapter
def test_create_table_with_custom_sequence_name
# table name is 29 chars, the standard sequence name will
@@ -559,7 +575,7 @@ class MigrationTest < ActiveRecord::TestCase
assert_nothing_raised do
begin
Person.connection.create_table :table_with_name_thats_just_ok do |t|
- t.column :foo, :string, :null => false
+ t.column :foo, :string, null: false
end
ensure
Person.connection.drop_table :table_with_name_thats_just_ok rescue nil
@@ -570,15 +586,15 @@ class MigrationTest < ActiveRecord::TestCase
assert_nothing_raised do
begin
Person.connection.create_table :table_with_name_thats_just_ok,
- :sequence_name => 'suitably_short_seq' do |t|
- t.column :foo, :string, :null => false
+ sequence_name: "suitably_short_seq" do |t|
+ t.column :foo, :string, null: false
end
Person.connection.execute("select suitably_short_seq.nextval from dual")
ensure
Person.connection.drop_table :table_with_name_thats_just_ok,
- :sequence_name => 'suitably_short_seq' rescue nil
+ sequence_name: "suitably_short_seq" rescue nil
end
end
@@ -592,8 +608,8 @@ class MigrationTest < ActiveRecord::TestCase
if current_adapter?(:Mysql2Adapter, :PostgreSQLAdapter)
def test_out_of_range_integer_limit_should_raise
e = assert_raise(ActiveRecord::ActiveRecordError, "integer limit didn't raise") do
- Person.connection.create_table :test_integer_limits, :force => true do |t|
- t.column :bigone, :integer, :limit => 10
+ Person.connection.create_table :test_integer_limits, force: true do |t|
+ t.column :bigone, :integer, limit: 10
end
end
@@ -689,7 +705,7 @@ class MigrationTest < ActiveRecord::TestCase
end
end
- protected
+ private
# This is needed to isolate class_attribute assignments like `table_name_prefix`
# for each test case.
def new_isolated_reminder_class
@@ -727,13 +743,13 @@ end
class ReservedWordsMigrationTest < ActiveRecord::TestCase
def test_drop_index_from_table_named_values
connection = Person.connection
- connection.create_table :values, :force => true do |t|
+ connection.create_table :values, force: true do |t|
t.integer :value
end
assert_nothing_raised do
connection.add_index :values, :value
- connection.remove_index :values, :column => :value
+ connection.remove_index :values, column: :value
end
ensure
connection.drop_table :values rescue nil
@@ -748,8 +764,8 @@ class ExplicitlyNamedIndexMigrationTest < ActiveRecord::TestCase
end
assert_nothing_raised do
- connection.add_index :values, :value, name: 'a_different_name'
- connection.remove_index :values, column: :value, name: 'a_different_name'
+ connection.add_index :values, :value, name: "a_different_name"
+ connection.remove_index :values, column: :value, name: "a_different_name"
end
ensure
connection.drop_table :values rescue nil
@@ -760,7 +776,7 @@ if ActiveRecord::Base.connection.supports_bulk_alter?
class BulkAlterTableMigrationsTest < ActiveRecord::TestCase
def setup
@connection = Person.connection
- @connection.create_table(:delete_me, :force => true) {|t| }
+ @connection.create_table(:delete_me, force: true) { |t| }
Person.reset_column_information
Person.reset_sequence_name
end
@@ -774,15 +790,15 @@ if ActiveRecord::Base.connection.supports_bulk_alter?
with_bulk_change_table do |t|
t.column :name, :string
t.string :qualification, :experience
- t.integer :age, :default => 0
+ t.integer :age, default: 0
t.date :birthdate
t.timestamps null: true
end
end
assert_equal 8, columns.size
- [:name, :qualification, :experience].each {|s| assert_equal :string, column(s).type }
- assert_equal '0', column(:age).default
+ [:name, :qualification, :experience].each { |s| assert_equal :string, column(s).type }
+ assert_equal "0", column(:age).default
end
def test_removing_columns
@@ -790,7 +806,7 @@ if ActiveRecord::Base.connection.supports_bulk_alter?
t.string :qualification, :experience
end
- [:qualification, :experience].each {|c| assert column(c) }
+ [:qualification, :experience].each { |c| assert column(c) }
assert_queries(1) do
with_bulk_change_table do |t|
@@ -799,7 +815,7 @@ if ActiveRecord::Base.connection.supports_bulk_alter?
end
end
- [:qualification, :experience].each {|c| assert ! column(c) }
+ [:qualification, :experience].each { |c| assert ! column(c) }
assert column(:qualification_experience)
end
@@ -813,7 +829,7 @@ if ActiveRecord::Base.connection.supports_bulk_alter?
# Adding an index fires a query every time to check if an index already exists or not
assert_queries(3) do
with_bulk_change_table do |t|
- t.index :username, :unique => true, :name => :awesome_username_index
+ t.index :username, unique: true, name: :awesome_username_index
t.index [:name, :age]
end
end
@@ -821,7 +837,7 @@ if ActiveRecord::Base.connection.supports_bulk_alter?
assert_equal 2, indexes.size
name_age_index = index(:index_delete_me_on_name_and_age)
- assert_equal ['name', 'age'].sort, name_age_index.columns.sort
+ assert_equal ["name", "age"].sort, name_age_index.columns.sort
assert ! name_age_index.unique
assert index(:awesome_username_index).unique
@@ -838,7 +854,7 @@ if ActiveRecord::Base.connection.supports_bulk_alter?
assert_queries(3) do
with_bulk_change_table do |t|
t.remove_index :name
- t.index :name, :name => :new_name_index, :unique => true
+ t.index :name, name: :new_name_index, unique: true
end
end
@@ -860,43 +876,43 @@ if ActiveRecord::Base.connection.supports_bulk_alter?
# One query for columns (delete_me table)
# One query for primary key (delete_me table)
# One query to do the bulk change
- assert_queries(3, :ignore_none => true) do
+ assert_queries(3, ignore_none: true) do
with_bulk_change_table do |t|
- t.change :name, :string, :default => 'NONAME'
+ t.change :name, :string, default: "NONAME"
t.change :birthdate, :datetime
end
end
- assert_equal 'NONAME', column(:name).default
+ assert_equal "NONAME", column(:name).default
assert_equal :datetime, column(:birthdate).type
end
protected
- def with_bulk_change_table
- # Reset columns/indexes cache as we're changing the table
- @columns = @indexes = nil
+ def with_bulk_change_table
+ # Reset columns/indexes cache as we're changing the table
+ @columns = @indexes = nil
- Person.connection.change_table(:delete_me, :bulk => true) do |t|
- yield t
+ Person.connection.change_table(:delete_me, bulk: true) do |t|
+ yield t
+ end
end
- end
- def column(name)
- columns.detect {|c| c.name == name.to_s }
- end
+ def column(name)
+ columns.detect { |c| c.name == name.to_s }
+ end
- def columns
- @columns ||= Person.connection.columns('delete_me')
- end
+ def columns
+ @columns ||= Person.connection.columns("delete_me")
+ end
- def index(name)
- indexes.detect {|i| i.name == name.to_s }
- end
+ def index(name)
+ indexes.detect { |i| i.name == name.to_s }
+ end
- def indexes
- @indexes ||= Person.connection.indexes('delete_me')
- end
+ def indexes
+ @indexes ||= Person.connection.indexes("delete_me")
+ end
end # AlterTableMigrationsTest
end
@@ -918,7 +934,7 @@ class CopyMigrationsTest < ActiveRecord::TestCase
@migrations_path = MIGRATIONS_ROOT + "/valid"
@existing_migrations = Dir[@migrations_path + "/*.rb"]
- copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy"})
+ copied = ActiveRecord::Migration.copy(@migrations_path, bukkits: MIGRATIONS_ROOT + "/to_copy")
assert File.exist?(@migrations_path + "/4_people_have_hobbies.bukkits.rb")
assert File.exist?(@migrations_path + "/5_people_have_descriptions.bukkits.rb")
assert_equal [@migrations_path + "/4_people_have_hobbies.bukkits.rb", @migrations_path + "/5_people_have_descriptions.bukkits.rb"], copied.map(&:filename)
@@ -927,7 +943,7 @@ class CopyMigrationsTest < ActiveRecord::TestCase
assert_equal expected, IO.readlines(@migrations_path + "/4_people_have_hobbies.bukkits.rb")[0].chomp
files_count = Dir[@migrations_path + "/*.rb"].length
- copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy"})
+ copied = ActiveRecord::Migration.copy(@migrations_path, bukkits: MIGRATIONS_ROOT + "/to_copy")
assert_equal files_count, Dir[@migrations_path + "/*.rb"].length
assert copied.empty?
ensure
@@ -960,7 +976,7 @@ class CopyMigrationsTest < ActiveRecord::TestCase
@existing_migrations = Dir[@migrations_path + "/*.rb"]
travel_to(Time.utc(2010, 7, 26, 10, 10, 10)) do
- copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy_with_timestamps"})
+ copied = ActiveRecord::Migration.copy(@migrations_path, bukkits: MIGRATIONS_ROOT + "/to_copy_with_timestamps")
assert File.exist?(@migrations_path + "/20100726101010_people_have_hobbies.bukkits.rb")
assert File.exist?(@migrations_path + "/20100726101011_people_have_descriptions.bukkits.rb")
expected = [@migrations_path + "/20100726101010_people_have_hobbies.bukkits.rb",
@@ -968,7 +984,7 @@ class CopyMigrationsTest < ActiveRecord::TestCase
assert_equal expected, copied.map(&:filename)
files_count = Dir[@migrations_path + "/*.rb"].length
- copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy_with_timestamps"})
+ copied = ActiveRecord::Migration.copy(@migrations_path, bukkits: MIGRATIONS_ROOT + "/to_copy_with_timestamps")
assert_equal files_count, Dir[@migrations_path + "/*.rb"].length
assert copied.empty?
end
@@ -1005,12 +1021,12 @@ class CopyMigrationsTest < ActiveRecord::TestCase
@existing_migrations = Dir[@migrations_path + "/*.rb"]
travel_to(Time.utc(2010, 2, 20, 10, 10, 10)) do
- ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy_with_timestamps"})
+ ActiveRecord::Migration.copy(@migrations_path, bukkits: MIGRATIONS_ROOT + "/to_copy_with_timestamps")
assert File.exist?(@migrations_path + "/20100301010102_people_have_hobbies.bukkits.rb")
assert File.exist?(@migrations_path + "/20100301010103_people_have_descriptions.bukkits.rb")
files_count = Dir[@migrations_path + "/*.rb"].length
- copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy_with_timestamps"})
+ copied = ActiveRecord::Migration.copy(@migrations_path, bukkits: MIGRATIONS_ROOT + "/to_copy_with_timestamps")
assert_equal files_count, Dir[@migrations_path + "/*.rb"].length
assert copied.empty?
end
@@ -1023,7 +1039,7 @@ class CopyMigrationsTest < ActiveRecord::TestCase
@migrations_path = MIGRATIONS_ROOT + "/valid"
@existing_migrations = Dir[@migrations_path + "/*.rb"]
- copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/magic"})
+ copied = ActiveRecord::Migration.copy(@migrations_path, bukkits: MIGRATIONS_ROOT + "/magic")
assert File.exist?(@migrations_path + "/4_currencies_have_symbols.bukkits.rb")
assert_equal [@migrations_path + "/4_currencies_have_symbols.bukkits.rb"], copied.map(&:filename)
@@ -1031,7 +1047,7 @@ class CopyMigrationsTest < ActiveRecord::TestCase
assert_equal expected, IO.readlines(@migrations_path + "/4_currencies_have_symbols.bukkits.rb")[0..1].join.chomp
files_count = Dir[@migrations_path + "/*.rb"].length
- copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/magic"})
+ copied = ActiveRecord::Migration.copy(@migrations_path, bukkits: MIGRATIONS_ROOT + "/magic")
assert_equal files_count, Dir[@migrations_path + "/*.rb"].length
assert copied.empty?
ensure
@@ -1048,7 +1064,7 @@ class CopyMigrationsTest < ActiveRecord::TestCase
skipped = []
on_skip = Proc.new { |name, migration| skipped << "#{name} #{migration.name}" }
- copied = ActiveRecord::Migration.copy(@migrations_path, sources, :on_skip => on_skip)
+ copied = ActiveRecord::Migration.copy(@migrations_path, sources, on_skip: on_skip)
assert_equal 2, copied.length
assert_equal 1, skipped.length
@@ -1066,8 +1082,8 @@ class CopyMigrationsTest < ActiveRecord::TestCase
skipped = []
on_skip = Proc.new { |name, migration| skipped << "#{name} #{migration.name}" }
- copied = ActiveRecord::Migration.copy(@migrations_path, sources, :on_skip => on_skip)
- ActiveRecord::Migration.copy(@migrations_path, sources, :on_skip => on_skip)
+ copied = ActiveRecord::Migration.copy(@migrations_path, sources, on_skip: on_skip)
+ ActiveRecord::Migration.copy(@migrations_path, sources, on_skip: on_skip)
assert_equal 2, copied.length
assert_equal 0, skipped.length
@@ -1080,7 +1096,7 @@ class CopyMigrationsTest < ActiveRecord::TestCase
@existing_migrations = []
travel_to(Time.utc(2010, 7, 26, 10, 10, 10)) do
- copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy_with_timestamps"})
+ copied = ActiveRecord::Migration.copy(@migrations_path, bukkits: MIGRATIONS_ROOT + "/to_copy_with_timestamps")
assert File.exist?(@migrations_path + "/20100726101010_people_have_hobbies.bukkits.rb")
assert File.exist?(@migrations_path + "/20100726101011_people_have_descriptions.bukkits.rb")
assert_equal 2, copied.length
@@ -1095,7 +1111,7 @@ class CopyMigrationsTest < ActiveRecord::TestCase
@existing_migrations = []
travel_to(Time.utc(2010, 7, 26, 10, 10, 10)) do
- copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy_with_timestamps"})
+ copied = ActiveRecord::Migration.copy(@migrations_path, bukkits: MIGRATIONS_ROOT + "/to_copy_with_timestamps")
assert File.exist?(@migrations_path + "/20100726101010_people_have_hobbies.bukkits.rb")
assert File.exist?(@migrations_path + "/20100726101011_people_have_descriptions.bukkits.rb")
assert_equal 2, copied.length
diff --git a/activerecord/test/cases/migrator_test.rb b/activerecord/test/cases/migrator_test.rb
index 86eca53141..bb9835394b 100644
--- a/activerecord/test/cases/migrator_test.rb
+++ b/activerecord/test/cases/migrator_test.rb
@@ -9,9 +9,9 @@ class MigratorTest < ActiveRecord::TestCase
class Sensor < ActiveRecord::Migration::Current
attr_reader :went_up, :went_down
- def initialize name = self.class.name, version = nil
+ def initialize(name = self.class.name, version = nil)
super
- @went_up = false
+ @went_up = false
@went_down = false
end
@@ -46,21 +46,21 @@ class MigratorTest < ActiveRecord::TestCase
def test_migrator_with_duplicate_names
assert_raises(ActiveRecord::DuplicateMigrationNameError, "Multiple migrations have the name Chunky") do
- list = [ActiveRecord::Migration.new('Chunky'), ActiveRecord::Migration.new('Chunky')]
+ list = [ActiveRecord::Migration.new("Chunky"), ActiveRecord::Migration.new("Chunky")]
ActiveRecord::Migrator.new(:up, list)
end
end
def test_migrator_with_duplicate_versions
assert_raises(ActiveRecord::DuplicateMigrationVersionError) do
- list = [ActiveRecord::Migration.new('Foo', 1), ActiveRecord::Migration.new('Bar', 1)]
+ list = [ActiveRecord::Migration.new("Foo", 1), ActiveRecord::Migration.new("Bar", 1)]
ActiveRecord::Migrator.new(:up, list)
end
end
def test_migrator_with_missing_version_numbers
assert_raises(ActiveRecord::UnknownMigrationVersionError) do
- list = [ActiveRecord::Migration.new('Foo', 1), ActiveRecord::Migration.new('Bar', 2)]
+ list = [ActiveRecord::Migration.new("Foo", 1), ActiveRecord::Migration.new("Bar", 2)]
ActiveRecord::Migrator.new(:up, list, 3).run
end
end
@@ -68,7 +68,7 @@ class MigratorTest < ActiveRecord::TestCase
def test_finds_migrations
migrations = ActiveRecord::Migrator.migrations(MIGRATIONS_ROOT + "/valid")
- [[1, 'ValidPeopleHaveLastNames'], [2, 'WeNeedReminders'], [3, 'InnocentJointable']].each_with_index do |pair, i|
+ [[1, "ValidPeopleHaveLastNames"], [2, "WeNeedReminders"], [3, "InnocentJointable"]].each_with_index do |pair, i|
assert_equal migrations[i].version, pair.first
assert_equal migrations[i].name, pair.last
end
@@ -77,14 +77,14 @@ class MigratorTest < ActiveRecord::TestCase
def test_finds_migrations_in_subdirectories
migrations = ActiveRecord::Migrator.migrations(MIGRATIONS_ROOT + "/valid_with_subdirectories")
- [[1, 'ValidPeopleHaveLastNames'], [2, 'WeNeedReminders'], [3, 'InnocentJointable']].each_with_index do |pair, i|
+ [[1, "ValidPeopleHaveLastNames"], [2, "WeNeedReminders"], [3, "InnocentJointable"]].each_with_index do |pair, i|
assert_equal migrations[i].version, pair.first
assert_equal migrations[i].name, pair.last
end
end
def test_finds_migrations_from_two_directories
- directories = [MIGRATIONS_ROOT + '/valid_with_timestamps', MIGRATIONS_ROOT + '/to_copy_with_timestamps']
+ directories = [MIGRATIONS_ROOT + "/valid_with_timestamps", MIGRATIONS_ROOT + "/to_copy_with_timestamps"]
migrations = ActiveRecord::Migrator.migrations directories
[[20090101010101, "PeopleHaveHobbies"],
@@ -92,15 +92,15 @@ class MigratorTest < ActiveRecord::TestCase
[20100101010101, "ValidWithTimestampsPeopleHaveLastNames"],
[20100201010101, "ValidWithTimestampsWeNeedReminders"],
[20100301010101, "ValidWithTimestampsInnocentJointable"]].each_with_index do |pair, i|
- assert_equal pair.first, migrations[i].version
- assert_equal pair.last, migrations[i].name
+ assert_equal pair.first, migrations[i].version
+ assert_equal pair.last, migrations[i].name
end
end
def test_finds_migrations_in_numbered_directory
- migrations = ActiveRecord::Migrator.migrations [MIGRATIONS_ROOT + '/10_urban']
+ migrations = ActiveRecord::Migrator.migrations [MIGRATIONS_ROOT + "/10_urban"]
assert_equal 9, migrations[0].version
- assert_equal 'AddExpressions', migrations[0].name
+ assert_equal "AddExpressions", migrations[0].name
end
def test_relative_migrations
@@ -109,14 +109,14 @@ class MigratorTest < ActiveRecord::TestCase
end
migration_proxy = list.find { |item|
- item.name == 'ValidPeopleHaveLastNames'
+ item.name == "ValidPeopleHaveLastNames"
}
- assert migration_proxy, 'should find pending migration'
+ assert migration_proxy, "should find pending migration"
end
def test_finds_pending_migrations
- ActiveRecord::SchemaMigration.create!(:version => '1')
- migration_list = [ActiveRecord::Migration.new('foo', 1), ActiveRecord::Migration.new('bar', 3)]
+ ActiveRecord::SchemaMigration.create!(version: "1")
+ migration_list = [ActiveRecord::Migration.new("foo", 1), ActiveRecord::Migration.new("bar", 3)]
migrations = ActiveRecord::Migrator.new(:up, migration_list).pending_migrations
assert_equal 1, migrations.size
@@ -124,21 +124,21 @@ class MigratorTest < ActiveRecord::TestCase
end
def test_migrator_interleaved_migrations
- pass_one = [Sensor.new('One', 1)]
+ pass_one = [Sensor.new("One", 1)]
ActiveRecord::Migrator.new(:up, pass_one).migrate
assert pass_one.first.went_up
assert_not pass_one.first.went_down
- pass_two = [Sensor.new('One', 1), Sensor.new('Three', 3)]
+ pass_two = [Sensor.new("One", 1), Sensor.new("Three", 3)]
ActiveRecord::Migrator.new(:up, pass_two).migrate
assert_not pass_two[0].went_up
assert pass_two[1].went_up
assert pass_two.all? { |x| !x.went_down }
- pass_three = [Sensor.new('One', 1),
- Sensor.new('Two', 2),
- Sensor.new('Three', 3)]
+ pass_three = [Sensor.new("One", 1),
+ Sensor.new("Two", 2),
+ Sensor.new("Three", 3)]
ActiveRecord::Migrator.new(:down, pass_three).migrate
assert pass_three[0].went_down
@@ -165,7 +165,7 @@ class MigratorTest < ActiveRecord::TestCase
end
def test_current_version
- ActiveRecord::SchemaMigration.create!(:version => '1000')
+ ActiveRecord::SchemaMigration.create!(version: "1000")
assert_equal 1000, ActiveRecord::Migrator.current_version
end
@@ -313,9 +313,9 @@ class MigratorTest < ActiveRecord::TestCase
_, migrator = migrator_class(3)
ActiveRecord::Base.connection.drop_table "schema_migrations", if_exists: true
- ActiveSupport::Deprecation.silence { assert_not ActiveRecord::Base.connection.table_exists?('schema_migrations') }
+ assert_not ActiveRecord::Base.connection.table_exists?("schema_migrations")
migrator.migrate("valid", 1)
- ActiveSupport::Deprecation.silence { assert ActiveRecord::Base.connection.table_exists?('schema_migrations') }
+ assert ActiveRecord::Base.connection.table_exists?("schema_migrations")
end
def test_migrator_forward
@@ -332,7 +332,7 @@ class MigratorTest < ActiveRecord::TestCase
def test_only_loads_pending_migrations
# migrate up to 1
- ActiveRecord::SchemaMigration.create!(:version => '1')
+ ActiveRecord::SchemaMigration.create!(version: "1")
calls, migrator = migrator_class(3)
migrator.migrate("valid", nil)
@@ -344,10 +344,10 @@ class MigratorTest < ActiveRecord::TestCase
_, migrator = migrator_class(3)
migrator.migrate("valid")
- assert_equal([1,2,3], ActiveRecord::Migrator.get_all_versions)
+ assert_equal([1, 2, 3], ActiveRecord::Migrator.get_all_versions)
migrator.rollback("valid")
- assert_equal([1,2], ActiveRecord::Migrator.get_all_versions)
+ assert_equal([1, 2], ActiveRecord::Migrator.get_all_versions)
migrator.rollback("valid")
assert_equal([1], ActiveRecord::Migrator.get_all_versions)
@@ -357,32 +357,32 @@ class MigratorTest < ActiveRecord::TestCase
end
private
- def m(name, version)
- x = Sensor.new name, version
- x.extend(Module.new {
- define_method(:up) { yield(:up, x); super() }
- define_method(:down) { yield(:down, x); super() }
- }) if block_given?
- end
+ def m(name, version)
+ x = Sensor.new name, version
+ x.extend(Module.new {
+ define_method(:up) { yield(:up, x); super() }
+ define_method(:down) { yield(:down, x); super() }
+ }) if block_given?
+ end
- def sensors(count)
- calls = []
- migrations = count.times.map { |i|
- m(nil, i + 1) { |c,migration|
- calls << [c, migration.version]
+ def sensors(count)
+ calls = []
+ migrations = count.times.map { |i|
+ m(nil, i + 1) { |c, migration|
+ calls << [c, migration.version]
+ }
}
- }
- [calls, migrations]
- end
+ [calls, migrations]
+ end
- def migrator_class(count)
- calls, migrations = sensors(count)
+ def migrator_class(count)
+ calls, migrations = sensors(count)
- migrator = Class.new(ActiveRecord::Migrator).extend(Module.new {
- define_method(:migrations) { |paths|
- migrations
- }
- })
- [calls, migrator]
- end
+ migrator = Class.new(ActiveRecord::Migrator).extend(Module.new {
+ define_method(:migrations) { |paths|
+ migrations
+ }
+ })
+ [calls, migrator]
+ end
end
diff --git a/activerecord/test/cases/mixin_test.rb b/activerecord/test/cases/mixin_test.rb
index 06af75af0f..a8af8e30f7 100644
--- a/activerecord/test/cases/mixin_test.rb
+++ b/activerecord/test/cases/mixin_test.rb
@@ -63,5 +63,4 @@ class TouchTest < ActiveRecord::TestCase
ensure
Mixin.record_timestamps = true
end
-
end
diff --git a/activerecord/test/cases/modules_test.rb b/activerecord/test/cases/modules_test.rb
index 486bcc22df..f8a7bab35f 100644
--- a/activerecord/test/cases/modules_test.rb
+++ b/activerecord/test/cases/modules_test.rb
@@ -1,8 +1,8 @@
require "cases/helper"
-require 'models/company_in_module'
-require 'models/shop'
-require 'models/developer'
-require 'models/computer'
+require "models/company_in_module"
+require "models/shop"
+require "models/developer"
+require "models/computer"
class ModulesTest < ActiveRecord::TestCase
fixtures :accounts, :companies, :projects, :developers, :collections, :products, :variants
@@ -31,7 +31,7 @@ class ModulesTest < ActiveRecord::TestCase
def test_module_spanning_associations
firm = MyApplication::Business::Firm.first
assert !firm.clients.empty?, "Firm should have clients"
- assert_nil firm.class.table_name.match('::'), "Firm shouldn't have the module appear in its table name"
+ assert_nil firm.class.table_name.match("::"), "Firm shouldn't have the module appear in its table name"
end
def test_module_spanning_has_and_belongs_to_many_associations
@@ -41,7 +41,7 @@ class ModulesTest < ActiveRecord::TestCase
end
def test_associations_spanning_cross_modules
- account = MyApplication::Billing::Account.all.merge!(:order => 'id').first
+ account = MyApplication::Billing::Account.all.merge!(order: "id").first
assert_kind_of MyApplication::Business::Firm, account.firm
assert_kind_of MyApplication::Billing::Firm, account.qualified_billing_firm
assert_kind_of MyApplication::Billing::Firm, account.unqualified_billing_firm
@@ -50,14 +50,14 @@ class ModulesTest < ActiveRecord::TestCase
end
def test_find_account_and_include_company
- account = MyApplication::Billing::Account.all.merge!(:includes => :firm).find(1)
+ account = MyApplication::Billing::Account.all.merge!(includes: :firm).find(1)
assert_kind_of MyApplication::Business::Firm, account.firm
end
def test_table_name
- assert_equal 'accounts', MyApplication::Billing::Account.table_name, 'table_name for ActiveRecord model in module'
- assert_equal 'companies', MyApplication::Business::Client.table_name, 'table_name for ActiveRecord model subclass'
- assert_equal 'company_contacts', MyApplication::Business::Client::Contact.table_name, 'table_name for ActiveRecord model enclosed by another ActiveRecord model'
+ assert_equal "accounts", MyApplication::Billing::Account.table_name, "table_name for ActiveRecord model in module"
+ assert_equal "companies", MyApplication::Business::Client.table_name, "table_name for ActiveRecord model subclass"
+ assert_equal "company_contacts", MyApplication::Business::Client::Contact.table_name, "table_name for ActiveRecord model enclosed by another ActiveRecord model"
end
def test_assign_ids
@@ -73,8 +73,8 @@ class ModulesTest < ActiveRecord::TestCase
clients = []
assert_nothing_raised do
- clients << MyApplication::Business::Client.references(:accounts).merge!(:includes => {:firm => :account}, :where => 'accounts.id IS NOT NULL').find(3)
- clients << MyApplication::Business::Client.includes(:firm => :account).find(3)
+ clients << MyApplication::Business::Client.references(:accounts).merge!(includes: { firm: :account }, where: "accounts.id IS NOT NULL").find(3)
+ clients << MyApplication::Business::Client.includes(firm: :account).find(3)
end
clients.each do |client|
@@ -85,9 +85,9 @@ class ModulesTest < ActiveRecord::TestCase
end
def test_module_table_name_prefix
- assert_equal 'prefixed_companies', MyApplication::Business::Prefixed::Company.table_name, 'inferred table_name for ActiveRecord model in module with table_name_prefix'
- assert_equal 'prefixed_companies', MyApplication::Business::Prefixed::Nested::Company.table_name, 'table_name for ActiveRecord model in nested module with a parent table_name_prefix'
- assert_equal 'companies', MyApplication::Business::Prefixed::Firm.table_name, 'explicit table_name for ActiveRecord model in module with table_name_prefix should not be prefixed'
+ assert_equal "prefixed_companies", MyApplication::Business::Prefixed::Company.table_name, "inferred table_name for ActiveRecord model in module with table_name_prefix"
+ assert_equal "prefixed_companies", MyApplication::Business::Prefixed::Nested::Company.table_name, "table_name for ActiveRecord model in nested module with a parent table_name_prefix"
+ assert_equal "companies", MyApplication::Business::Prefixed::Firm.table_name, "explicit table_name for ActiveRecord model in module with table_name_prefix should not be prefixed"
end
def test_module_table_name_prefix_with_global_prefix
@@ -101,21 +101,21 @@ class ModulesTest < ActiveRecord::TestCase
MyApplication::Business::Prefixed::Nested::Company,
MyApplication::Billing::Account ]
- ActiveRecord::Base.table_name_prefix = 'global_'
+ ActiveRecord::Base.table_name_prefix = "global_"
classes.each(&:reset_table_name)
- assert_equal 'global_companies', MyApplication::Business::Company.table_name, 'inferred table_name for ActiveRecord model in module without table_name_prefix'
- assert_equal 'prefixed_companies', MyApplication::Business::Prefixed::Company.table_name, 'inferred table_name for ActiveRecord model in module with table_name_prefix'
- assert_equal 'prefixed_companies', MyApplication::Business::Prefixed::Nested::Company.table_name, 'table_name for ActiveRecord model in nested module with a parent table_name_prefix'
- assert_equal 'companies', MyApplication::Business::Prefixed::Firm.table_name, 'explicit table_name for ActiveRecord model in module with table_name_prefix should not be prefixed'
+ assert_equal "global_companies", MyApplication::Business::Company.table_name, "inferred table_name for ActiveRecord model in module without table_name_prefix"
+ assert_equal "prefixed_companies", MyApplication::Business::Prefixed::Company.table_name, "inferred table_name for ActiveRecord model in module with table_name_prefix"
+ assert_equal "prefixed_companies", MyApplication::Business::Prefixed::Nested::Company.table_name, "table_name for ActiveRecord model in nested module with a parent table_name_prefix"
+ assert_equal "companies", MyApplication::Business::Prefixed::Firm.table_name, "explicit table_name for ActiveRecord model in module with table_name_prefix should not be prefixed"
ensure
- ActiveRecord::Base.table_name_prefix = ''
+ ActiveRecord::Base.table_name_prefix = ""
classes.each(&:reset_table_name)
end
def test_module_table_name_suffix
- assert_equal 'companies_suffixed', MyApplication::Business::Suffixed::Company.table_name, 'inferred table_name for ActiveRecord model in module with table_name_suffix'
- assert_equal 'companies_suffixed', MyApplication::Business::Suffixed::Nested::Company.table_name, 'table_name for ActiveRecord model in nested module with a parent table_name_suffix'
- assert_equal 'companies', MyApplication::Business::Suffixed::Firm.table_name, 'explicit table_name for ActiveRecord model in module with table_name_suffix should not be suffixed'
+ assert_equal "companies_suffixed", MyApplication::Business::Suffixed::Company.table_name, "inferred table_name for ActiveRecord model in module with table_name_suffix"
+ assert_equal "companies_suffixed", MyApplication::Business::Suffixed::Nested::Company.table_name, "table_name for ActiveRecord model in nested module with a parent table_name_suffix"
+ assert_equal "companies", MyApplication::Business::Suffixed::Firm.table_name, "explicit table_name for ActiveRecord model in module with table_name_suffix should not be suffixed"
end
def test_module_table_name_suffix_with_global_suffix
@@ -129,14 +129,14 @@ class ModulesTest < ActiveRecord::TestCase
MyApplication::Business::Suffixed::Nested::Company,
MyApplication::Billing::Account ]
- ActiveRecord::Base.table_name_suffix = '_global'
+ ActiveRecord::Base.table_name_suffix = "_global"
classes.each(&:reset_table_name)
- assert_equal 'companies_global', MyApplication::Business::Company.table_name, 'inferred table_name for ActiveRecord model in module without table_name_suffix'
- assert_equal 'companies_suffixed', MyApplication::Business::Suffixed::Company.table_name, 'inferred table_name for ActiveRecord model in module with table_name_suffix'
- assert_equal 'companies_suffixed', MyApplication::Business::Suffixed::Nested::Company.table_name, 'table_name for ActiveRecord model in nested module with a parent table_name_suffix'
- assert_equal 'companies', MyApplication::Business::Suffixed::Firm.table_name, 'explicit table_name for ActiveRecord model in module with table_name_suffix should not be suffixed'
+ assert_equal "companies_global", MyApplication::Business::Company.table_name, "inferred table_name for ActiveRecord model in module without table_name_suffix"
+ assert_equal "companies_suffixed", MyApplication::Business::Suffixed::Company.table_name, "inferred table_name for ActiveRecord model in module with table_name_suffix"
+ assert_equal "companies_suffixed", MyApplication::Business::Suffixed::Nested::Company.table_name, "table_name for ActiveRecord model in nested module with a parent table_name_suffix"
+ assert_equal "companies", MyApplication::Business::Suffixed::Firm.table_name, "explicit table_name for ActiveRecord model in module with table_name_suffix should not be suffixed"
ensure
- ActiveRecord::Base.table_name_suffix = ''
+ ActiveRecord::Base.table_name_suffix = ""
classes.each(&:reset_table_name)
end
diff --git a/activerecord/test/cases/multiparameter_attributes_test.rb b/activerecord/test/cases/multiparameter_attributes_test.rb
index d05cb22740..ceb5724377 100644
--- a/activerecord/test/cases/multiparameter_attributes_test.rb
+++ b/activerecord/test/cases/multiparameter_attributes_test.rb
@@ -1,6 +1,6 @@
require "cases/helper"
-require 'models/topic'
-require 'models/customer'
+require "models/topic"
+require "models/customer"
class MultiParameterAttributeTest < ActiveRecord::TestCase
fixtures :topics
@@ -202,6 +202,20 @@ class MultiParameterAttributeTest < ActiveRecord::TestCase
Topic.reset_column_information
end
+ def test_multiparameter_attributes_on_time_with_time_zone_aware_attributes_and_invalid_time_params
+ with_timezone_config aware_attributes: true do
+ Topic.reset_column_information
+ attributes = {
+ "written_on(1i)" => "2004", "written_on(2i)" => "", "written_on(3i)" => ""
+ }
+ topic = Topic.find(1)
+ topic.attributes = attributes
+ assert_nil topic.written_on
+ end
+ ensure
+ Topic.reset_column_information
+ end
+
def test_multiparameter_attributes_on_time_with_time_zone_aware_attributes_false
with_timezone_config default: :local, aware_attributes: false, zone: -28800 do
attributes = {
@@ -246,6 +260,13 @@ class MultiParameterAttributeTest < ActiveRecord::TestCase
topic.attributes = attributes
assert_equal Time.zone.local(2000, 1, 1, 16, 24, 0), topic.bonus_time
assert_not topic.bonus_time.utc?
+
+ attributes = {
+ "written_on(1i)" => "2000", "written_on(2i)" => "", "written_on(3i)" => "",
+ "written_on(4i)" => "", "written_on(5i)" => ""
+ }
+ topic.attributes = attributes
+ assert_nil topic.written_on
end
ensure
Topic.reset_column_information
@@ -266,14 +287,14 @@ class MultiParameterAttributeTest < ActiveRecord::TestCase
unless current_adapter? :OracleAdapter
def test_multiparameter_attributes_setting_time_attribute
- topic = Topic.new( "bonus_time(4i)"=> "01", "bonus_time(5i)" => "05" )
+ topic = Topic.new("bonus_time(4i)" => "01", "bonus_time(5i)" => "05")
assert_equal 1, topic.bonus_time.hour
assert_equal 5, topic.bonus_time.min
end
end
def test_multiparameter_attributes_setting_date_attribute
- topic = Topic.new( "written_on(1i)" => "1952", "written_on(2i)" => "3", "written_on(3i)" => "11" )
+ topic = Topic.new("written_on(1i)" => "1952", "written_on(2i)" => "3", "written_on(3i)" => "11")
assert_equal 1952, topic.written_on.year
assert_equal 3, topic.written_on.month
assert_equal 11, topic.written_on.day
@@ -294,8 +315,8 @@ class MultiParameterAttributeTest < ActiveRecord::TestCase
end
def test_multiparameter_attributes_setting_time_but_not_date_on_date_field
- assert_raise( ActiveRecord::MultiparameterAssignmentErrors ) do
- Topic.new( "written_on(4i)" => "13", "written_on(5i)" => "55" )
+ assert_raise(ActiveRecord::MultiparameterAssignmentErrors) do
+ Topic.new("written_on(4i)" => "13", "written_on(5i)" => "55")
end
end
diff --git a/activerecord/test/cases/multiple_db_test.rb b/activerecord/test/cases/multiple_db_test.rb
index a4fbf579a1..e3bb51bd77 100644
--- a/activerecord/test/cases/multiple_db_test.rb
+++ b/activerecord/test/cases/multiple_db_test.rb
@@ -1,7 +1,7 @@
require "cases/helper"
-require 'models/entrant'
-require 'models/bird'
-require 'models/course'
+require "models/entrant"
+require "models/bird"
+require "models/course"
class MultipleDbTest < ActiveRecord::TestCase
self.use_transactional_tests = false
@@ -60,7 +60,7 @@ class MultipleDbTest < ActiveRecord::TestCase
ActiveSupport::Dependencies.clear
Object.send(:remove_const, :Course)
- require_dependency 'models/course'
+ require_dependency "models/course"
assert Course.connection
end
diff --git a/activerecord/test/cases/nested_attributes_test.rb b/activerecord/test/cases/nested_attributes_test.rb
index 11fb164d50..b87419d203 100644
--- a/activerecord/test/cases/nested_attributes_test.rb
+++ b/activerecord/test/cases/nested_attributes_test.rb
@@ -9,11 +9,11 @@ require "models/man"
require "models/interest"
require "models/owner"
require "models/pet"
-require 'active_support/hash_with_indifferent_access'
+require "active_support/hash_with_indifferent_access"
class TestNestedAttributesInGeneral < ActiveRecord::TestCase
teardown do
- Pirate.accepts_nested_attributes_for :ship, :allow_destroy => true, :reject_if => proc(&:empty?)
+ Pirate.accepts_nested_attributes_for :ship, allow_destroy: true, reject_if: proc(&:empty?)
end
def test_base_should_have_an_empty_nested_attributes_options
@@ -30,28 +30,28 @@ class TestNestedAttributesInGeneral < ActiveRecord::TestCase
end
def test_should_not_build_a_new_record_using_reject_all_even_if_destroy_is_given
- pirate = Pirate.create!(:catchphrase => "Don' botharrr talkin' like one, savvy?")
- pirate.birds_with_reject_all_blank_attributes = [{:name => '', :color => '', :_destroy => '0'}]
+ pirate = Pirate.create!(catchphrase: "Don' botharrr talkin' like one, savvy?")
+ pirate.birds_with_reject_all_blank_attributes = [{ name: "", color: "", _destroy: "0" }]
pirate.save!
assert pirate.birds_with_reject_all_blank.empty?
end
def test_should_not_build_a_new_record_if_reject_all_blank_returns_false
- pirate = Pirate.create!(:catchphrase => "Don' botharrr talkin' like one, savvy?")
- pirate.birds_with_reject_all_blank_attributes = [{:name => '', :color => ''}]
+ pirate = Pirate.create!(catchphrase: "Don' botharrr talkin' like one, savvy?")
+ pirate.birds_with_reject_all_blank_attributes = [{ name: "", color: "" }]
pirate.save!
assert pirate.birds_with_reject_all_blank.empty?
end
def test_should_build_a_new_record_if_reject_all_blank_does_not_return_false
- pirate = Pirate.create!(:catchphrase => "Don' botharrr talkin' like one, savvy?")
- pirate.birds_with_reject_all_blank_attributes = [{:name => 'Tweetie', :color => ''}]
+ pirate = Pirate.create!(catchphrase: "Don' botharrr talkin' like one, savvy?")
+ pirate.birds_with_reject_all_blank_attributes = [{ name: "Tweetie", color: "" }]
pirate.save!
assert_equal 1, pirate.birds_with_reject_all_blank.count
- assert_equal 'Tweetie', pirate.birds_with_reject_all_blank.first.name
+ assert_equal "Tweetie", pirate.birds_with_reject_all_blank.first.name
end
def test_should_raise_an_ArgumentError_for_non_existing_associations
@@ -63,7 +63,7 @@ class TestNestedAttributesInGeneral < ActiveRecord::TestCase
def test_should_raise_an_UnknownAttributeError_for_non_existing_nested_attributes
exception = assert_raise ActiveModel::UnknownAttributeError do
- Pirate.new(:ship_attributes => { :sail => true })
+ Pirate.new(ship_attributes: { sail: true })
end
assert_equal "unknown attribute 'sail' for Ship.", exception.message
end
@@ -72,84 +72,84 @@ class TestNestedAttributesInGeneral < ActiveRecord::TestCase
Pirate.accepts_nested_attributes_for :ship
pirate = Pirate.create!(catchphrase: "Don' botharrr talkin' like one, savvy?")
- ship = pirate.create_ship(name: 'Nights Dirty Lightning')
+ ship = pirate.create_ship(name: "Nights Dirty Lightning")
- pirate.update(ship_attributes: { '_destroy' => true, :id => ship.id })
+ pirate.update(ship_attributes: { "_destroy" => true, :id => ship.id })
assert_nothing_raised { pirate.ship.reload }
end
def test_a_model_should_respond_to_underscore_destroy_and_return_if_it_is_marked_for_destruction
- ship = Ship.create!(:name => 'Nights Dirty Lightning')
+ ship = Ship.create!(name: "Nights Dirty Lightning")
assert !ship._destroy
ship.mark_for_destruction
assert ship._destroy
end
def test_reject_if_method_without_arguments
- Pirate.accepts_nested_attributes_for :ship, :reject_if => :new_record?
+ Pirate.accepts_nested_attributes_for :ship, reject_if: :new_record?
- pirate = Pirate.new(:catchphrase => "Stop wastin' me time")
- pirate.ship_attributes = { :name => 'Black Pearl' }
- assert_no_difference('Ship.count') { pirate.save! }
+ pirate = Pirate.new(catchphrase: "Stop wastin' me time")
+ pirate.ship_attributes = { name: "Black Pearl" }
+ assert_no_difference("Ship.count") { pirate.save! }
end
def test_reject_if_method_with_arguments
- Pirate.accepts_nested_attributes_for :ship, :reject_if => :reject_empty_ships_on_create
+ Pirate.accepts_nested_attributes_for :ship, reject_if: :reject_empty_ships_on_create
- pirate = Pirate.new(:catchphrase => "Stop wastin' me time")
- pirate.ship_attributes = { :name => 'Red Pearl', :_reject_me_if_new => true }
- assert_no_difference('Ship.count') { pirate.save! }
+ pirate = Pirate.new(catchphrase: "Stop wastin' me time")
+ pirate.ship_attributes = { name: "Red Pearl", _reject_me_if_new: true }
+ assert_no_difference("Ship.count") { pirate.save! }
# pirate.reject_empty_ships_on_create returns false for saved pirate records
# in the previous step note that pirate gets saved but ship fails
- pirate.ship_attributes = { :name => 'Red Pearl', :_reject_me_if_new => true }
- assert_difference('Ship.count') { pirate.save! }
+ pirate.ship_attributes = { name: "Red Pearl", _reject_me_if_new: true }
+ assert_difference("Ship.count") { pirate.save! }
end
def test_reject_if_with_indifferent_keys
- Pirate.accepts_nested_attributes_for :ship, :reject_if => proc {|attributes| attributes[:name].blank? }
+ Pirate.accepts_nested_attributes_for :ship, reject_if: proc { |attributes| attributes[:name].blank? }
- pirate = Pirate.new(:catchphrase => "Stop wastin' me time")
- pirate.ship_attributes = { :name => 'Hello Pearl' }
- assert_difference('Ship.count') { pirate.save! }
+ pirate = Pirate.new(catchphrase: "Stop wastin' me time")
+ pirate.ship_attributes = { name: "Hello Pearl" }
+ assert_difference("Ship.count") { pirate.save! }
end
def test_reject_if_with_a_proc_which_returns_true_always_for_has_one
- Pirate.accepts_nested_attributes_for :ship, :reject_if => proc {|attributes| true }
+ Pirate.accepts_nested_attributes_for :ship, reject_if: proc { |attributes| true }
pirate = Pirate.new(catchphrase: "Stop wastin' me time")
- ship = pirate.create_ship(name: 's1')
- pirate.update({ship_attributes: { name: 's2', id: ship.id } })
- assert_equal 's1', ship.reload.name
+ ship = pirate.create_ship(name: "s1")
+ pirate.update(ship_attributes: { name: "s2", id: ship.id })
+ assert_equal "s1", ship.reload.name
end
def test_reuse_already_built_new_record
pirate = Pirate.new
ship_built_first = pirate.build_ship
- pirate.ship_attributes = { name: 'Ship 1' }
+ pirate.ship_attributes = { name: "Ship 1" }
assert_equal ship_built_first.object_id, pirate.ship.object_id
end
def test_do_not_allow_assigning_foreign_key_when_reusing_existing_new_record
pirate = Pirate.create!(catchphrase: "Don' botharrr talkin' like one, savvy?")
pirate.build_ship
- pirate.ship_attributes = { name: 'Ship 1', pirate_id: pirate.id + 1 }
+ pirate.ship_attributes = { name: "Ship 1", pirate_id: pirate.id + 1 }
assert_equal pirate.id, pirate.ship.pirate_id
end
def test_reject_if_with_a_proc_which_returns_true_always_for_has_many
- Man.accepts_nested_attributes_for :interests, :reject_if => proc {|attributes| true }
+ Man.accepts_nested_attributes_for :interests, reject_if: proc { |attributes| true }
man = Man.create(name: "John")
- interest = man.interests.create(topic: 'photography')
- man.update({interests_attributes: { topic: 'gardening', id: interest.id } })
- assert_equal 'photography', interest.reload.topic
+ interest = man.interests.create(topic: "photography")
+ man.update(interests_attributes: { topic: "gardening", id: interest.id })
+ assert_equal "photography", interest.reload.topic
end
def test_destroy_works_independent_of_reject_if
- Man.accepts_nested_attributes_for :interests, :reject_if => proc {|attributes| true }, :allow_destroy => true
+ Man.accepts_nested_attributes_for :interests, reject_if: proc { |attributes| true }, allow_destroy: true
man = Man.create(name: "Jon")
- interest = man.interests.create(topic: 'the ladies')
- man.update({interests_attributes: { _destroy: "1", id: interest.id } })
+ interest = man.interests.create(topic: "the ladies")
+ man.update(interests_attributes: { _destroy: "1", id: interest.id })
assert man.reload.interests.empty?
end
@@ -168,27 +168,27 @@ class TestNestedAttributesInGeneral < ActiveRecord::TestCase
def test_has_many_association_updating_a_single_record
Man.accepts_nested_attributes_for(:interests)
- man = Man.create(name: 'John')
- interest = man.interests.create(topic: 'photography')
- man.update({interests_attributes: {topic: 'gardening', id: interest.id}})
- assert_equal 'gardening', interest.reload.topic
+ man = Man.create(name: "John")
+ interest = man.interests.create(topic: "photography")
+ man.update(interests_attributes: { topic: "gardening", id: interest.id })
+ assert_equal "gardening", interest.reload.topic
end
def test_reject_if_with_blank_nested_attributes_id
# When using a select list to choose an existing 'ship' id, with include_blank: true
- Pirate.accepts_nested_attributes_for :ship, :reject_if => proc {|attributes| attributes[:id].blank? }
+ Pirate.accepts_nested_attributes_for :ship, reject_if: proc { |attributes| attributes[:id].blank? }
- pirate = Pirate.new(:catchphrase => "Stop wastin' me time")
- pirate.ship_attributes = { :id => "" }
+ pirate = Pirate.new(catchphrase: "Stop wastin' me time")
+ pirate.ship_attributes = { id: "" }
assert_nothing_raised { pirate.save! }
end
def test_first_and_array_index_zero_methods_return_the_same_value_when_nested_attributes_are_set_to_update_existing_record
Man.accepts_nested_attributes_for(:interests)
- man = Man.create(:name => "John")
- interest = man.interests.create :topic => 'gardening'
+ man = Man.create(name: "John")
+ interest = man.interests.create topic: "gardening"
man = Man.find man.id
- man.interests_attributes = [{:id => interest.id, :topic => 'gardening'}]
+ man.interests_attributes = [{ id: interest.id, topic: "gardening" }]
assert_equal man.interests.first.topic, man.interests[0].topic
end
@@ -196,11 +196,11 @@ class TestNestedAttributesInGeneral < ActiveRecord::TestCase
mean_pirate_class = Class.new(Pirate) do
accepts_nested_attributes_for :parrot
def parrot_attributes=(attrs)
- super(attrs.merge(:color => "blue"))
+ super(attrs.merge(color: "blue"))
end
end
mean_pirate = mean_pirate_class.new
- mean_pirate.parrot_attributes = { :name => "James" }
+ mean_pirate.parrot_attributes = { name: "James" }
assert_equal "James", mean_pirate.parrot.name
assert_equal "blue", mean_pirate.parrot.color
end
@@ -212,20 +212,20 @@ class TestNestedAttributesInGeneral < ActiveRecord::TestCase
accepts_nested_attributes_for :parrot
end
mean_pirate = mean_pirate_class.new
- mean_pirate.parrot_attributes = { :name => "James" }
+ mean_pirate.parrot_attributes = { name: "James" }
assert_equal "James", mean_pirate.parrot.name
end
end
class TestNestedAttributesOnAHasOneAssociation < ActiveRecord::TestCase
def setup
- @pirate = Pirate.create!(:catchphrase => "Don' botharrr talkin' like one, savvy?")
- @ship = @pirate.create_ship(:name => 'Nights Dirty Lightning')
+ @pirate = Pirate.create!(catchphrase: "Don' botharrr talkin' like one, savvy?")
+ @ship = @pirate.create_ship(name: "Nights Dirty Lightning")
end
def test_should_raise_argument_error_if_trying_to_build_polymorphic_belongs_to
exception = assert_raise ArgumentError do
- Treasure.new(:name => 'pearl', :looter_attributes => {:catchphrase => "Arrr"})
+ Treasure.new(name: "pearl", looter_attributes: { catchphrase: "Arrr" })
end
assert_equal "Cannot build association `looter'. Are you trying to build a polymorphic one-to-one association?", exception.message
end
@@ -236,15 +236,15 @@ class TestNestedAttributesOnAHasOneAssociation < ActiveRecord::TestCase
def test_should_build_a_new_record_if_there_is_no_id
@ship.destroy
- @pirate.reload.ship_attributes = { :name => 'Davy Jones Gold Dagger' }
+ @pirate.reload.ship_attributes = { name: "Davy Jones Gold Dagger" }
assert !@pirate.ship.persisted?
- assert_equal 'Davy Jones Gold Dagger', @pirate.ship.name
+ assert_equal "Davy Jones Gold Dagger", @pirate.ship.name
end
def test_should_not_build_a_new_record_if_there_is_no_id_and_destroy_is_truthy
@ship.destroy
- @pirate.reload.ship_attributes = { :name => 'Davy Jones Gold Dagger', :_destroy => '1' }
+ @pirate.reload.ship_attributes = { name: "Davy Jones Gold Dagger", _destroy: "1" }
assert_nil @pirate.ship
end
@@ -257,54 +257,54 @@ class TestNestedAttributesOnAHasOneAssociation < ActiveRecord::TestCase
end
def test_should_replace_an_existing_record_if_there_is_no_id
- @pirate.reload.ship_attributes = { :name => 'Davy Jones Gold Dagger' }
+ @pirate.reload.ship_attributes = { name: "Davy Jones Gold Dagger" }
assert !@pirate.ship.persisted?
- assert_equal 'Davy Jones Gold Dagger', @pirate.ship.name
- assert_equal 'Nights Dirty Lightning', @ship.name
+ assert_equal "Davy Jones Gold Dagger", @pirate.ship.name
+ assert_equal "Nights Dirty Lightning", @ship.name
end
def test_should_not_replace_an_existing_record_if_there_is_no_id_and_destroy_is_truthy
- @pirate.reload.ship_attributes = { :name => 'Davy Jones Gold Dagger', :_destroy => '1' }
+ @pirate.reload.ship_attributes = { name: "Davy Jones Gold Dagger", _destroy: "1" }
assert_equal @ship, @pirate.ship
- assert_equal 'Nights Dirty Lightning', @pirate.ship.name
+ assert_equal "Nights Dirty Lightning", @pirate.ship.name
end
def test_should_modify_an_existing_record_if_there_is_a_matching_id
- @pirate.reload.ship_attributes = { :id => @ship.id, :name => 'Davy Jones Gold Dagger' }
+ @pirate.reload.ship_attributes = { id: @ship.id, name: "Davy Jones Gold Dagger" }
assert_equal @ship, @pirate.ship
- assert_equal 'Davy Jones Gold Dagger', @pirate.ship.name
+ assert_equal "Davy Jones Gold Dagger", @pirate.ship.name
end
def test_should_raise_RecordNotFound_if_an_id_is_given_but_doesnt_return_a_record
exception = assert_raise ActiveRecord::RecordNotFound do
- @pirate.ship_attributes = { :id => 1234567890 }
+ @pirate.ship_attributes = { id: 1234567890 }
end
assert_equal "Couldn't find Ship with ID=1234567890 for Pirate with ID=#{@pirate.id}", exception.message
end
def test_should_take_a_hash_with_string_keys_and_update_the_associated_model
- @pirate.reload.ship_attributes = { 'id' => @ship.id, 'name' => 'Davy Jones Gold Dagger' }
+ @pirate.reload.ship_attributes = { "id" => @ship.id, "name" => "Davy Jones Gold Dagger" }
assert_equal @ship, @pirate.ship
- assert_equal 'Davy Jones Gold Dagger', @pirate.ship.name
+ assert_equal "Davy Jones Gold Dagger", @pirate.ship.name
end
def test_should_modify_an_existing_record_if_there_is_a_matching_composite_id
- @ship.stub(:id, 'ABC1X') do
- @pirate.ship_attributes = { :id => @ship.id, :name => 'Davy Jones Gold Dagger' }
+ @ship.stub(:id, "ABC1X") do
+ @pirate.ship_attributes = { id: @ship.id, name: "Davy Jones Gold Dagger" }
- assert_equal 'Davy Jones Gold Dagger', @pirate.ship.name
+ assert_equal "Davy Jones Gold Dagger", @pirate.ship.name
end
end
def test_should_destroy_an_existing_record_if_there_is_a_matching_id_and_destroy_is_truthy
@pirate.ship.destroy
- [1, '1', true, 'true'].each do |truth|
- ship = @pirate.reload.create_ship(name: 'Mister Pablo')
+ [1, "1", true, "true"].each do |truth|
+ ship = @pirate.reload.create_ship(name: "Mister Pablo")
@pirate.update(ship_attributes: { id: ship.id, _destroy: truth })
assert_nil @pirate.reload.ship
@@ -313,7 +313,7 @@ class TestNestedAttributesOnAHasOneAssociation < ActiveRecord::TestCase
end
def test_should_not_destroy_an_existing_record_if_destroy_is_not_truthy
- [nil, '0', 0, 'false', false].each do |not_truth|
+ [nil, "0", 0, "false", false].each do |not_truth|
@pirate.update(ship_attributes: { id: @pirate.ship.id, _destroy: not_truth })
assert_equal @ship, @pirate.reload.ship
@@ -321,32 +321,32 @@ class TestNestedAttributesOnAHasOneAssociation < ActiveRecord::TestCase
end
def test_should_not_destroy_an_existing_record_if_allow_destroy_is_false
- Pirate.accepts_nested_attributes_for :ship, :allow_destroy => false, :reject_if => proc(&:empty?)
+ Pirate.accepts_nested_attributes_for :ship, allow_destroy: false, reject_if: proc(&:empty?)
- @pirate.update(ship_attributes: { id: @pirate.ship.id, _destroy: '1' })
+ @pirate.update(ship_attributes: { id: @pirate.ship.id, _destroy: "1" })
assert_equal @ship, @pirate.reload.ship
- Pirate.accepts_nested_attributes_for :ship, :allow_destroy => true, :reject_if => proc(&:empty?)
+ Pirate.accepts_nested_attributes_for :ship, allow_destroy: true, reject_if: proc(&:empty?)
end
def test_should_also_work_with_a_HashWithIndifferentAccess
- @pirate.ship_attributes = ActiveSupport::HashWithIndifferentAccess.new(:id => @ship.id, :name => 'Davy Jones Gold Dagger')
+ @pirate.ship_attributes = ActiveSupport::HashWithIndifferentAccess.new(id: @ship.id, name: "Davy Jones Gold Dagger")
assert @pirate.ship.persisted?
- assert_equal 'Davy Jones Gold Dagger', @pirate.ship.name
+ assert_equal "Davy Jones Gold Dagger", @pirate.ship.name
end
def test_should_work_with_update_as_well
- @pirate.update({ catchphrase: 'Arr', ship_attributes: { id: @ship.id, name: 'Mister Pablo' } })
+ @pirate.update(catchphrase: "Arr", ship_attributes: { id: @ship.id, name: "Mister Pablo" })
@pirate.reload
- assert_equal 'Arr', @pirate.catchphrase
- assert_equal 'Mister Pablo', @pirate.ship.name
+ assert_equal "Arr", @pirate.catchphrase
+ assert_equal "Mister Pablo", @pirate.ship.name
end
def test_should_not_destroy_the_associated_model_until_the_parent_is_saved
- @pirate.attributes = { :ship_attributes => { :id => @ship.id, :_destroy => '1' } }
+ @pirate.attributes = { ship_attributes: { id: @ship.id, _destroy: "1" } }
assert !@pirate.ship.destroyed?
assert @pirate.ship.marked_for_destruction?
@@ -362,56 +362,55 @@ class TestNestedAttributesOnAHasOneAssociation < ActiveRecord::TestCase
end
def test_should_accept_update_only_option
- @pirate.update(update_only_ship_attributes: { id: @pirate.ship.id, name: 'Mayflower' })
+ @pirate.update(update_only_ship_attributes: { id: @pirate.ship.id, name: "Mayflower" })
end
def test_should_create_new_model_when_nothing_is_there_and_update_only_is_true
@ship.delete
- @pirate.reload.update(update_only_ship_attributes: { name: 'Mayflower' })
+ @pirate.reload.update(update_only_ship_attributes: { name: "Mayflower" })
assert_not_nil @pirate.ship
end
def test_should_update_existing_when_update_only_is_true_and_no_id_is_given
@ship.delete
- @ship = @pirate.create_update_only_ship(name: 'Nights Dirty Lightning')
+ @ship = @pirate.create_update_only_ship(name: "Nights Dirty Lightning")
- @pirate.update(update_only_ship_attributes: { name: 'Mayflower' })
+ @pirate.update(update_only_ship_attributes: { name: "Mayflower" })
- assert_equal 'Mayflower', @ship.reload.name
+ assert_equal "Mayflower", @ship.reload.name
assert_equal @ship, @pirate.reload.ship
end
def test_should_update_existing_when_update_only_is_true_and_id_is_given
@ship.delete
- @ship = @pirate.create_update_only_ship(name: 'Nights Dirty Lightning')
+ @ship = @pirate.create_update_only_ship(name: "Nights Dirty Lightning")
- @pirate.update(update_only_ship_attributes: { name: 'Mayflower', id: @ship.id })
+ @pirate.update(update_only_ship_attributes: { name: "Mayflower", id: @ship.id })
- assert_equal 'Mayflower', @ship.reload.name
+ assert_equal "Mayflower", @ship.reload.name
assert_equal @ship, @pirate.reload.ship
end
def test_should_destroy_existing_when_update_only_is_true_and_id_is_given_and_is_marked_for_destruction
- Pirate.accepts_nested_attributes_for :update_only_ship, :update_only => true, :allow_destroy => true
+ Pirate.accepts_nested_attributes_for :update_only_ship, update_only: true, allow_destroy: true
@ship.delete
- @ship = @pirate.create_update_only_ship(name: 'Nights Dirty Lightning')
+ @ship = @pirate.create_update_only_ship(name: "Nights Dirty Lightning")
- @pirate.update(update_only_ship_attributes: { name: 'Mayflower', id: @ship.id, _destroy: true })
+ @pirate.update(update_only_ship_attributes: { name: "Mayflower", id: @ship.id, _destroy: true })
assert_nil @pirate.reload.ship
assert_raise(ActiveRecord::RecordNotFound) { Ship.find(@ship.id) }
- Pirate.accepts_nested_attributes_for :update_only_ship, :update_only => true, :allow_destroy => false
+ Pirate.accepts_nested_attributes_for :update_only_ship, update_only: true, allow_destroy: false
end
-
end
class TestNestedAttributesOnABelongsToAssociation < ActiveRecord::TestCase
def setup
- @ship = Ship.new(:name => 'Nights Dirty Lightning')
- @pirate = @ship.build_pirate(:catchphrase => 'Aye')
+ @ship = Ship.new(name: "Nights Dirty Lightning")
+ @pirate = @ship.build_pirate(catchphrase: "Aye")
@ship.save!
end
@@ -421,15 +420,15 @@ class TestNestedAttributesOnABelongsToAssociation < ActiveRecord::TestCase
def test_should_build_a_new_record_if_there_is_no_id
@pirate.destroy
- @ship.reload.pirate_attributes = { :catchphrase => 'Arr' }
+ @ship.reload.pirate_attributes = { catchphrase: "Arr" }
assert !@ship.pirate.persisted?
- assert_equal 'Arr', @ship.pirate.catchphrase
+ assert_equal "Arr", @ship.pirate.catchphrase
end
def test_should_not_build_a_new_record_if_there_is_no_id_and_destroy_is_truthy
@pirate.destroy
- @ship.reload.pirate_attributes = { :catchphrase => 'Arr', :_destroy => '1' }
+ @ship.reload.pirate_attributes = { catchphrase: "Arr", _destroy: "1" }
assert_nil @ship.pirate
end
@@ -442,53 +441,53 @@ class TestNestedAttributesOnABelongsToAssociation < ActiveRecord::TestCase
end
def test_should_replace_an_existing_record_if_there_is_no_id
- @ship.reload.pirate_attributes = { :catchphrase => 'Arr' }
+ @ship.reload.pirate_attributes = { catchphrase: "Arr" }
assert !@ship.pirate.persisted?
- assert_equal 'Arr', @ship.pirate.catchphrase
- assert_equal 'Aye', @pirate.catchphrase
+ assert_equal "Arr", @ship.pirate.catchphrase
+ assert_equal "Aye", @pirate.catchphrase
end
def test_should_not_replace_an_existing_record_if_there_is_no_id_and_destroy_is_truthy
- @ship.reload.pirate_attributes = { :catchphrase => 'Arr', :_destroy => '1' }
+ @ship.reload.pirate_attributes = { catchphrase: "Arr", _destroy: "1" }
assert_equal @pirate, @ship.pirate
- assert_equal 'Aye', @ship.pirate.catchphrase
+ assert_equal "Aye", @ship.pirate.catchphrase
end
def test_should_modify_an_existing_record_if_there_is_a_matching_id
- @ship.reload.pirate_attributes = { :id => @pirate.id, :catchphrase => 'Arr' }
+ @ship.reload.pirate_attributes = { id: @pirate.id, catchphrase: "Arr" }
assert_equal @pirate, @ship.pirate
- assert_equal 'Arr', @ship.pirate.catchphrase
+ assert_equal "Arr", @ship.pirate.catchphrase
end
def test_should_raise_RecordNotFound_if_an_id_is_given_but_doesnt_return_a_record
exception = assert_raise ActiveRecord::RecordNotFound do
- @ship.pirate_attributes = { :id => 1234567890 }
+ @ship.pirate_attributes = { id: 1234567890 }
end
assert_equal "Couldn't find Pirate with ID=1234567890 for Ship with ID=#{@ship.id}", exception.message
end
def test_should_take_a_hash_with_string_keys_and_update_the_associated_model
- @ship.reload.pirate_attributes = { 'id' => @pirate.id, 'catchphrase' => 'Arr' }
+ @ship.reload.pirate_attributes = { "id" => @pirate.id, "catchphrase" => "Arr" }
assert_equal @pirate, @ship.pirate
- assert_equal 'Arr', @ship.pirate.catchphrase
+ assert_equal "Arr", @ship.pirate.catchphrase
end
def test_should_modify_an_existing_record_if_there_is_a_matching_composite_id
- @pirate.stub(:id, 'ABC1X') do
- @ship.pirate_attributes = { :id => @pirate.id, :catchphrase => 'Arr' }
+ @pirate.stub(:id, "ABC1X") do
+ @ship.pirate_attributes = { id: @pirate.id, catchphrase: "Arr" }
- assert_equal 'Arr', @ship.pirate.catchphrase
+ assert_equal "Arr", @ship.pirate.catchphrase
end
end
def test_should_destroy_an_existing_record_if_there_is_a_matching_id_and_destroy_is_truthy
@ship.pirate.destroy
- [1, '1', true, 'true'].each do |truth|
- pirate = @ship.reload.create_pirate(catchphrase: 'Arr')
+ [1, "1", true, "true"].each do |truth|
+ pirate = @ship.reload.create_pirate(catchphrase: "Arr")
@ship.update(pirate_attributes: { id: pirate.id, _destroy: truth })
assert_raise(ActiveRecord::RecordNotFound) { pirate.reload }
end
@@ -509,33 +508,33 @@ class TestNestedAttributesOnABelongsToAssociation < ActiveRecord::TestCase
end
def test_should_not_destroy_an_existing_record_if_destroy_is_not_truthy
- [nil, '0', 0, 'false', false].each do |not_truth|
+ [nil, "0", 0, "false", false].each do |not_truth|
@ship.update(pirate_attributes: { id: @ship.pirate.id, _destroy: not_truth })
assert_nothing_raised { @ship.pirate.reload }
end
end
def test_should_not_destroy_an_existing_record_if_allow_destroy_is_false
- Ship.accepts_nested_attributes_for :pirate, :allow_destroy => false, :reject_if => proc(&:empty?)
+ Ship.accepts_nested_attributes_for :pirate, allow_destroy: false, reject_if: proc(&:empty?)
- @ship.update(pirate_attributes: { id: @ship.pirate.id, _destroy: '1' })
+ @ship.update(pirate_attributes: { id: @ship.pirate.id, _destroy: "1" })
assert_nothing_raised { @ship.pirate.reload }
ensure
- Ship.accepts_nested_attributes_for :pirate, :allow_destroy => true, :reject_if => proc(&:empty?)
+ Ship.accepts_nested_attributes_for :pirate, allow_destroy: true, reject_if: proc(&:empty?)
end
def test_should_work_with_update_as_well
- @ship.update({ name: 'Mister Pablo', pirate_attributes: { catchphrase: 'Arr' } })
+ @ship.update(name: "Mister Pablo", pirate_attributes: { catchphrase: "Arr" })
@ship.reload
- assert_equal 'Mister Pablo', @ship.name
- assert_equal 'Arr', @ship.pirate.catchphrase
+ assert_equal "Mister Pablo", @ship.name
+ assert_equal "Arr", @ship.pirate.catchphrase
end
def test_should_not_destroy_the_associated_model_until_the_parent_is_saved
pirate = @ship.pirate
- @ship.attributes = { :pirate_attributes => { :id => pirate.id, '_destroy' => true } }
+ @ship.attributes = { pirate_attributes: { :id => pirate.id, "_destroy" => true } }
assert_nothing_raised { Pirate.find(pirate.id) }
@ship.save
assert_raise(ActiveRecord::RecordNotFound) { Pirate.find(pirate.id) }
@@ -547,40 +546,40 @@ class TestNestedAttributesOnABelongsToAssociation < ActiveRecord::TestCase
def test_should_create_new_model_when_nothing_is_there_and_update_only_is_true
@pirate.delete
- @ship.reload.attributes = { :update_only_pirate_attributes => { :catchphrase => 'Arr' } }
+ @ship.reload.attributes = { update_only_pirate_attributes: { catchphrase: "Arr" } }
assert !@ship.update_only_pirate.persisted?
end
def test_should_update_existing_when_update_only_is_true_and_no_id_is_given
@pirate.delete
- @pirate = @ship.create_update_only_pirate(catchphrase: 'Aye')
+ @pirate = @ship.create_update_only_pirate(catchphrase: "Aye")
- @ship.update(update_only_pirate_attributes: { catchphrase: 'Arr' })
- assert_equal 'Arr', @pirate.reload.catchphrase
+ @ship.update(update_only_pirate_attributes: { catchphrase: "Arr" })
+ assert_equal "Arr", @pirate.reload.catchphrase
assert_equal @pirate, @ship.reload.update_only_pirate
end
def test_should_update_existing_when_update_only_is_true_and_id_is_given
@pirate.delete
- @pirate = @ship.create_update_only_pirate(catchphrase: 'Aye')
+ @pirate = @ship.create_update_only_pirate(catchphrase: "Aye")
- @ship.update(update_only_pirate_attributes: { catchphrase: 'Arr', id: @pirate.id })
+ @ship.update(update_only_pirate_attributes: { catchphrase: "Arr", id: @pirate.id })
- assert_equal 'Arr', @pirate.reload.catchphrase
+ assert_equal "Arr", @pirate.reload.catchphrase
assert_equal @pirate, @ship.reload.update_only_pirate
end
def test_should_destroy_existing_when_update_only_is_true_and_id_is_given_and_is_marked_for_destruction
- Ship.accepts_nested_attributes_for :update_only_pirate, :update_only => true, :allow_destroy => true
+ Ship.accepts_nested_attributes_for :update_only_pirate, update_only: true, allow_destroy: true
@pirate.delete
- @pirate = @ship.create_update_only_pirate(catchphrase: 'Aye')
+ @pirate = @ship.create_update_only_pirate(catchphrase: "Aye")
- @ship.update(update_only_pirate_attributes: { catchphrase: 'Arr', id: @pirate.id, _destroy: true })
+ @ship.update(update_only_pirate_attributes: { catchphrase: "Arr", id: @pirate.id, _destroy: true })
assert_raise(ActiveRecord::RecordNotFound) { @pirate.reload }
- Ship.accepts_nested_attributes_for :update_only_pirate, :update_only => true, :allow_destroy => false
+ Ship.accepts_nested_attributes_for :update_only_pirate, update_only: true, allow_destroy: false
end
end
@@ -597,10 +596,9 @@ module NestedAttributesOnACollectionAssociationTests
end
def test_should_save_only_one_association_on_create
- pirate = Pirate.create!({
- :catchphrase => 'Arr',
- association_getter => { 'foo' => { :name => 'Grace OMalley' } }
- })
+ pirate = Pirate.create!(
+ :catchphrase => "Arr",
+ association_getter => { "foo" => { name: "Grace OMalley" } })
assert_equal 1, pirate.reload.send(@association_name).count
end
@@ -608,90 +606,90 @@ module NestedAttributesOnACollectionAssociationTests
def test_should_take_a_hash_with_string_keys_and_assign_the_attributes_to_the_associated_models
@alternate_params[association_getter].stringify_keys!
@pirate.update @alternate_params
- assert_equal ['Grace OMalley', 'Privateers Greed'], [@child_1.reload.name, @child_2.reload.name]
+ assert_equal ["Grace OMalley", "Privateers Greed"], [@child_1.reload.name, @child_2.reload.name]
end
def test_should_take_an_array_and_assign_the_attributes_to_the_associated_models
@pirate.send(association_setter, @alternate_params[association_getter].values)
@pirate.save
- assert_equal ['Grace OMalley', 'Privateers Greed'], [@child_1.reload.name, @child_2.reload.name]
+ assert_equal ["Grace OMalley", "Privateers Greed"], [@child_1.reload.name, @child_2.reload.name]
end
def test_should_also_work_with_a_HashWithIndifferentAccess
- @pirate.send(association_setter, ActiveSupport::HashWithIndifferentAccess.new('foo' => ActiveSupport::HashWithIndifferentAccess.new(:id => @child_1.id, :name => 'Grace OMalley')))
+ @pirate.send(association_setter, ActiveSupport::HashWithIndifferentAccess.new("foo" => ActiveSupport::HashWithIndifferentAccess.new(id: @child_1.id, name: "Grace OMalley")))
@pirate.save
- assert_equal 'Grace OMalley', @child_1.reload.name
+ assert_equal "Grace OMalley", @child_1.reload.name
end
def test_should_take_a_hash_and_assign_the_attributes_to_the_associated_models
@pirate.attributes = @alternate_params
- assert_equal 'Grace OMalley', @pirate.send(@association_name).first.name
- assert_equal 'Privateers Greed', @pirate.send(@association_name).last.name
+ assert_equal "Grace OMalley", @pirate.send(@association_name).first.name
+ assert_equal "Privateers Greed", @pirate.send(@association_name).last.name
end
def test_should_not_load_association_when_updating_existing_records
@pirate.reload
- @pirate.send(association_setter, [{ :id => @child_1.id, :name => 'Grace OMalley' }])
+ @pirate.send(association_setter, [{ id: @child_1.id, name: "Grace OMalley" }])
assert ! @pirate.send(@association_name).loaded?
@pirate.save
assert ! @pirate.send(@association_name).loaded?
- assert_equal 'Grace OMalley', @child_1.reload.name
+ assert_equal "Grace OMalley", @child_1.reload.name
end
def test_should_not_overwrite_unsaved_updates_when_loading_association
@pirate.reload
- @pirate.send(association_setter, [{ :id => @child_1.id, :name => 'Grace OMalley' }])
- assert_equal 'Grace OMalley', @pirate.send(@association_name).send(:load_target).find { |r| r.id == @child_1.id }.name
+ @pirate.send(association_setter, [{ id: @child_1.id, name: "Grace OMalley" }])
+ assert_equal "Grace OMalley", @pirate.send(@association_name).load_target.find { |r| r.id == @child_1.id }.name
end
def test_should_preserve_order_when_not_overwriting_unsaved_updates
@pirate.reload
- @pirate.send(association_setter, [{ :id => @child_1.id, :name => 'Grace OMalley' }])
- assert_equal @child_1.id, @pirate.send(@association_name).send(:load_target).first.id
+ @pirate.send(association_setter, [{ id: @child_1.id, name: "Grace OMalley" }])
+ assert_equal @child_1.id, @pirate.send(@association_name).load_target.first.id
end
def test_should_refresh_saved_records_when_not_overwriting_unsaved_updates
@pirate.reload
- record = @pirate.class.reflect_on_association(@association_name).klass.new(name: 'Grace OMalley')
+ record = @pirate.class.reflect_on_association(@association_name).klass.new(name: "Grace OMalley")
@pirate.send(@association_name) << record
record.save!
- @pirate.send(@association_name).last.update!(name: 'Polly')
- assert_equal 'Polly', @pirate.send(@association_name).send(:load_target).last.name
+ @pirate.send(@association_name).last.update!(name: "Polly")
+ assert_equal "Polly", @pirate.send(@association_name).load_target.last.name
end
def test_should_not_remove_scheduled_destroys_when_loading_association
@pirate.reload
- @pirate.send(association_setter, [{ :id => @child_1.id, :_destroy => '1' }])
- assert @pirate.send(@association_name).send(:load_target).find { |r| r.id == @child_1.id }.marked_for_destruction?
+ @pirate.send(association_setter, [{ id: @child_1.id, _destroy: "1" }])
+ assert @pirate.send(@association_name).load_target.find { |r| r.id == @child_1.id }.marked_for_destruction?
end
def test_should_take_a_hash_with_composite_id_keys_and_assign_the_attributes_to_the_associated_models
- @child_1.stub(:id, 'ABC1X') do
- @child_2.stub(:id, 'ABC2X') do
+ @child_1.stub(:id, "ABC1X") do
+ @child_2.stub(:id, "ABC2X") do
@pirate.attributes = {
association_getter => [
- { :id => @child_1.id, :name => 'Grace OMalley' },
- { :id => @child_2.id, :name => 'Privateers Greed' }
+ { id: @child_1.id, name: "Grace OMalley" },
+ { id: @child_2.id, name: "Privateers Greed" }
]
}
- assert_equal ['Grace OMalley', 'Privateers Greed'], [@child_1.name, @child_2.name]
+ assert_equal ["Grace OMalley", "Privateers Greed"], [@child_1.name, @child_2.name]
end
end
end
def test_should_raise_RecordNotFound_if_an_id_is_given_but_doesnt_return_a_record
exception = assert_raise ActiveRecord::RecordNotFound do
- @pirate.attributes = { association_getter => [{ :id => 1234567890 }] }
+ @pirate.attributes = { association_getter => [{ id: 1234567890 }] }
end
assert_equal "Couldn't find #{@child_1.class.name} with ID=1234567890 for Pirate with ID=#{@pirate.id}", exception.message
end
def test_should_raise_RecordNotFound_if_an_id_belonging_to_a_different_record_is_given
- other_pirate = Pirate.create! catchphrase: 'Ahoy!'
- other_child = other_pirate.send(@association_name).create! name: 'Buccaneers Servant'
+ other_pirate = Pirate.create! catchphrase: "Ahoy!"
+ other_child = other_pirate.send(@association_name).create! name: "Buccaneers Servant"
exception = assert_raise ActiveRecord::RecordNotFound do
@pirate.attributes = { association_getter => [{ id: other_child.id }] }
@@ -702,19 +700,19 @@ module NestedAttributesOnACollectionAssociationTests
def test_should_automatically_build_new_associated_models_for_each_entry_in_a_hash_where_the_id_is_missing
@pirate.send(@association_name).destroy_all
@pirate.reload.attributes = {
- association_getter => { 'foo' => { :name => 'Grace OMalley' }, 'bar' => { :name => 'Privateers Greed' }}
+ association_getter => { "foo" => { name: "Grace OMalley" }, "bar" => { name: "Privateers Greed" } }
}
assert !@pirate.send(@association_name).first.persisted?
- assert_equal 'Grace OMalley', @pirate.send(@association_name).first.name
+ assert_equal "Grace OMalley", @pirate.send(@association_name).first.name
assert !@pirate.send(@association_name).last.persisted?
- assert_equal 'Privateers Greed', @pirate.send(@association_name).last.name
+ assert_equal "Privateers Greed", @pirate.send(@association_name).last.name
end
def test_should_not_assign_destroy_key_to_a_record
assert_nothing_raised do
- @pirate.send(association_setter, { 'foo' => { '_destroy' => '0' }})
+ @pirate.send(association_setter, "foo" => { "_destroy" => "0" })
end
end
@@ -722,17 +720,17 @@ module NestedAttributesOnACollectionAssociationTests
@pirate.send(@association_name).destroy_all
@pirate.reload.attributes = {
association_getter => {
- 'foo' => { :name => 'Grace OMalley' },
- 'bar' => { :name => 'Privateers Greed', '_destroy' => '1' }
+ "foo" => { name: "Grace OMalley" },
+ "bar" => { :name => "Privateers Greed", "_destroy" => "1" }
}
}
assert_equal 1, @pirate.send(@association_name).length
- assert_equal 'Grace OMalley', @pirate.send(@association_name).first.name
+ assert_equal "Grace OMalley", @pirate.send(@association_name).first.name
end
def test_should_ignore_new_associated_records_if_a_reject_if_proc_returns_false
- @alternate_params[association_getter]['baz'] = {}
+ @alternate_params[association_getter]["baz"] = {}
assert_no_difference("@pirate.send(@association_name).count") do
@pirate.attributes = @alternate_params
end
@@ -740,11 +738,11 @@ module NestedAttributesOnACollectionAssociationTests
def test_should_sort_the_hash_by_the_keys_before_building_new_associated_models
attributes = {}
- attributes['123726353'] = { :name => 'Grace OMalley' }
- attributes['2'] = { :name => 'Privateers Greed' } # 2 is lower then 123726353
+ attributes["123726353"] = { name: "Grace OMalley" }
+ attributes["2"] = { name: "Privateers Greed" } # 2 is lower then 123726353
@pirate.send(association_setter, attributes)
- assert_equal ['Posideons Killer', 'Killer bandita Dionne', 'Privateers Greed', 'Grace OMalley'].to_set, @pirate.send(@association_name).map(&:name).to_set
+ assert_equal ["Posideons Killer", "Killer bandita Dionne", "Privateers Greed", "Grace OMalley"].to_set, @pirate.send(@association_name).map(&:name).to_set
end
def test_should_raise_an_argument_error_if_something_else_than_a_hash_is_passed
@@ -758,47 +756,47 @@ module NestedAttributesOnACollectionAssociationTests
end
def test_should_work_with_update_as_well
- @pirate.update(catchphrase: 'Arr',
- association_getter => { 'foo' => { :id => @child_1.id, :name => 'Grace OMalley' }})
+ @pirate.update(catchphrase: "Arr",
+ association_getter => { "foo" => { id: @child_1.id, name: "Grace OMalley" } })
- assert_equal 'Grace OMalley', @child_1.reload.name
+ assert_equal "Grace OMalley", @child_1.reload.name
end
def test_should_update_existing_records_and_add_new_ones_that_have_no_id
- @alternate_params[association_getter]['baz'] = { name: 'Buccaneers Servant' }
- assert_difference('@pirate.send(@association_name).count', +1) do
+ @alternate_params[association_getter]["baz"] = { name: "Buccaneers Servant" }
+ assert_difference("@pirate.send(@association_name).count", +1) do
@pirate.update @alternate_params
end
- assert_equal ['Grace OMalley', 'Privateers Greed', 'Buccaneers Servant'].to_set, @pirate.reload.send(@association_name).map(&:name).to_set
+ assert_equal ["Grace OMalley", "Privateers Greed", "Buccaneers Servant"].to_set, @pirate.reload.send(@association_name).map(&:name).to_set
end
def test_should_be_possible_to_destroy_a_record
- ['1', 1, 'true', true].each do |true_variable|
- record = @pirate.reload.send(@association_name).create!(:name => 'Grace OMalley')
+ ["1", 1, "true", true].each do |true_variable|
+ record = @pirate.reload.send(@association_name).create!(name: "Grace OMalley")
@pirate.send(association_setter,
- @alternate_params[association_getter].merge('baz' => { :id => record.id, '_destroy' => true_variable })
+ @alternate_params[association_getter].merge("baz" => { :id => record.id, "_destroy" => true_variable })
)
- assert_difference('@pirate.send(@association_name).count', -1) do
+ assert_difference("@pirate.send(@association_name).count", -1) do
@pirate.save
end
end
end
def test_should_not_destroy_the_associated_model_with_a_non_truthy_argument
- [nil, '', '0', 0, 'false', false].each do |false_variable|
- @alternate_params[association_getter]['foo']['_destroy'] = false_variable
- assert_no_difference('@pirate.send(@association_name).count') do
+ [nil, "", "0", 0, "false", false].each do |false_variable|
+ @alternate_params[association_getter]["foo"]["_destroy"] = false_variable
+ assert_no_difference("@pirate.send(@association_name).count") do
@pirate.update(@alternate_params)
end
end
end
def test_should_not_destroy_the_associated_model_until_the_parent_is_saved
- assert_no_difference('@pirate.send(@association_name).count') do
- @pirate.send(association_setter, @alternate_params[association_getter].merge('baz' => { :id => @child_1.id, '_destroy' => true }))
+ assert_no_difference("@pirate.send(@association_name).count") do
+ @pirate.send(association_setter, @alternate_params[association_getter].merge("baz" => { :id => @child_1.id, "_destroy" => true }))
end
- assert_difference('@pirate.send(@association_name).count', -1) { @pirate.save }
+ assert_difference("@pirate.send(@association_name).count", -1) { @pirate.save }
end
def test_should_automatically_enable_autosave_on_the_association
@@ -812,10 +810,10 @@ module NestedAttributesOnACollectionAssociationTests
repair_validations(Interest) do
Interest.validates_presence_of(:man)
- assert_difference 'Man.count' do
- assert_difference 'Interest.count', 2 do
- man = Man.create!(:name => 'John',
- :interests_attributes => [{:topic=>'Cars'}, {:topic=>'Sports'}])
+ assert_difference "Man.count" do
+ assert_difference "Interest.count", 2 do
+ man = Man.create!(name: "John",
+ interests_attributes: [{ topic: "Cars" }, { topic: "Sports" }])
assert_equal 2, man.interests.count
end
end
@@ -823,7 +821,7 @@ module NestedAttributesOnACollectionAssociationTests
end
def test_can_use_symbols_as_object_identifier
- @pirate.attributes = { :parrots_attributes => { :foo => { :name => 'Lovely Day' }, :bar => { :name => 'Blown Away' } } }
+ @pirate.attributes = { parrots_attributes: { foo: { name: "Lovely Day" }, bar: { name: "Blown Away" } } }
assert_nothing_raised { @pirate.save! }
end
@@ -832,22 +830,22 @@ module NestedAttributesOnACollectionAssociationTests
repair_validations(Interest) do
Interest.validates_numericality_of(:zine_id)
- man = Man.create(name: 'John')
- interest = man.interests.create(topic: 'bar', zine_id: 0)
+ man = Man.create(name: "John")
+ interest = man.interests.create(topic: "bar", zine_id: 0)
assert interest.save
- assert !man.update({interests_attributes: { id: interest.id, zine_id: 'foo' }})
+ assert !man.update(interests_attributes: { id: interest.id, zine_id: "foo" })
end
end
private
- def association_setter
- @association_setter ||= "#{@association_name}_attributes=".to_sym
- end
+ def association_setter
+ @association_setter ||= "#{@association_name}_attributes=".to_sym
+ end
- def association_getter
- @association_getter ||= "#{@association_name}_attributes".to_sym
- end
+ def association_getter
+ @association_getter ||= "#{@association_name}_attributes".to_sym
+ end
end
class TestNestedAttributesOnAHasManyAssociation < ActiveRecord::TestCase
@@ -855,16 +853,16 @@ class TestNestedAttributesOnAHasManyAssociation < ActiveRecord::TestCase
@association_type = :has_many
@association_name = :birds
- @pirate = Pirate.create!(:catchphrase => "Don' botharrr talkin' like one, savvy?")
- @pirate.birds.create!(:name => 'Posideons Killer')
- @pirate.birds.create!(:name => 'Killer bandita Dionne')
+ @pirate = Pirate.create!(catchphrase: "Don' botharrr talkin' like one, savvy?")
+ @pirate.birds.create!(name: "Posideons Killer")
+ @pirate.birds.create!(name: "Killer bandita Dionne")
@child_1, @child_2 = @pirate.birds
@alternate_params = {
- :birds_attributes => {
- 'foo' => { :id => @child_1.id, :name => 'Grace OMalley' },
- 'bar' => { :id => @child_2.id, :name => 'Privateers Greed' }
+ birds_attributes: {
+ "foo" => { id: @child_1.id, name: "Grace OMalley" },
+ "bar" => { id: @child_2.id, name: "Privateers Greed" }
}
}
end
@@ -877,16 +875,16 @@ class TestNestedAttributesOnAHasAndBelongsToManyAssociation < ActiveRecord::Test
@association_type = :has_and_belongs_to_many
@association_name = :parrots
- @pirate = Pirate.create!(:catchphrase => "Don' botharrr talkin' like one, savvy?")
- @pirate.parrots.create!(:name => 'Posideons Killer')
- @pirate.parrots.create!(:name => 'Killer bandita Dionne')
+ @pirate = Pirate.create!(catchphrase: "Don' botharrr talkin' like one, savvy?")
+ @pirate.parrots.create!(name: "Posideons Killer")
+ @pirate.parrots.create!(name: "Killer bandita Dionne")
@child_1, @child_2 = @pirate.parrots
@alternate_params = {
- :parrots_attributes => {
- 'foo' => { :id => @child_1.id, :name => 'Grace OMalley' },
- 'bar' => { :id => @child_2.id, :name => 'Privateers Greed' }
+ parrots_attributes: {
+ "foo" => { id: @child_1.id, name: "Grace OMalley" },
+ "bar" => { id: @child_2.id, name: "Privateers Greed" }
}
}
end
@@ -896,33 +894,33 @@ end
module NestedAttributesLimitTests
def teardown
- Pirate.accepts_nested_attributes_for :parrots, :allow_destroy => true, :reject_if => proc(&:empty?)
+ Pirate.accepts_nested_attributes_for :parrots, allow_destroy: true, reject_if: proc(&:empty?)
end
def test_limit_with_less_records
- @pirate.attributes = { :parrots_attributes => { 'foo' => { :name => 'Big Big Love' } } }
- assert_difference('Parrot.count') { @pirate.save! }
+ @pirate.attributes = { parrots_attributes: { "foo" => { name: "Big Big Love" } } }
+ assert_difference("Parrot.count") { @pirate.save! }
end
def test_limit_with_number_exact_records
- @pirate.attributes = { :parrots_attributes => { 'foo' => { :name => 'Lovely Day' }, 'bar' => { :name => 'Blown Away' } } }
- assert_difference('Parrot.count', 2) { @pirate.save! }
+ @pirate.attributes = { parrots_attributes: { "foo" => { name: "Lovely Day" }, "bar" => { name: "Blown Away" } } }
+ assert_difference("Parrot.count", 2) { @pirate.save! }
end
def test_limit_with_exceeding_records
assert_raises(ActiveRecord::NestedAttributes::TooManyRecords) do
- @pirate.attributes = { :parrots_attributes => { 'foo' => { :name => 'Lovely Day' },
- 'bar' => { :name => 'Blown Away' },
- 'car' => { :name => 'The Happening' }} }
+ @pirate.attributes = { parrots_attributes: { "foo" => { name: "Lovely Day" },
+ "bar" => { name: "Blown Away" },
+ "car" => { name: "The Happening" } } }
end
end
end
class TestNestedAttributesLimitNumeric < ActiveRecord::TestCase
def setup
- Pirate.accepts_nested_attributes_for :parrots, :limit => 2
+ Pirate.accepts_nested_attributes_for :parrots, limit: 2
- @pirate = Pirate.create!(:catchphrase => "Don' botharrr talkin' like one, savvy?")
+ @pirate = Pirate.create!(catchphrase: "Don' botharrr talkin' like one, savvy?")
end
include NestedAttributesLimitTests
@@ -930,9 +928,9 @@ end
class TestNestedAttributesLimitSymbol < ActiveRecord::TestCase
def setup
- Pirate.accepts_nested_attributes_for :parrots, :limit => :parrots_limit
+ Pirate.accepts_nested_attributes_for :parrots, limit: :parrots_limit
- @pirate = Pirate.create!(:catchphrase => "Don' botharrr talkin' like one, savvy?", :parrots_limit => 2)
+ @pirate = Pirate.create!(catchphrase: "Don' botharrr talkin' like one, savvy?", parrots_limit: 2)
end
include NestedAttributesLimitTests
@@ -940,9 +938,9 @@ end
class TestNestedAttributesLimitProc < ActiveRecord::TestCase
def setup
- Pirate.accepts_nested_attributes_for :parrots, :limit => proc { 2 }
+ Pirate.accepts_nested_attributes_for :parrots, limit: proc { 2 }
- @pirate = Pirate.create!(:catchphrase => "Don' botharrr talkin' like one, savvy?")
+ @pirate = Pirate.create!(catchphrase: "Don' botharrr talkin' like one, savvy?")
end
include NestedAttributesLimitTests
@@ -952,45 +950,44 @@ class TestNestedAttributesWithNonStandardPrimaryKeys < ActiveRecord::TestCase
fixtures :owners, :pets
def setup
- Owner.accepts_nested_attributes_for :pets, :allow_destroy => true
+ Owner.accepts_nested_attributes_for :pets, allow_destroy: true
@owner = owners(:ashley)
@pet1, @pet2 = pets(:chew), pets(:mochi)
@params = {
- :pets_attributes => {
- '0' => { :id => @pet1.id, :name => 'Foo' },
- '1' => { :id => @pet2.id, :name => 'Bar' }
+ pets_attributes: {
+ "0" => { id: @pet1.id, name: "Foo" },
+ "1" => { id: @pet2.id, name: "Bar" }
}
}
end
def test_should_update_existing_records_with_non_standard_primary_key
@owner.update(@params)
- assert_equal ['Foo', 'Bar'], @owner.pets.map(&:name)
+ assert_equal ["Foo", "Bar"], @owner.pets.map(&:name)
end
def test_attr_accessor_of_child_should_be_value_provided_during_update
@owner = owners(:ashley)
@pet1 = pets(:chew)
- attributes = {:pets_attributes => { "1"=> { :id => @pet1.id,
- :name => "Foo2",
- :current_user => "John",
- :_destroy=>true }}}
+ attributes = { pets_attributes: { "1" => { id: @pet1.id,
+ name: "Foo2",
+ current_user: "John",
+ _destroy: true } } }
@owner.update(attributes)
- assert_equal 'John', Pet.after_destroy_output
+ assert_equal "John", Pet.after_destroy_output
end
-
end
class TestHasOneAutosaveAssociationWhichItselfHasAutosaveAssociations < ActiveRecord::TestCase
self.use_transactional_tests = false unless supports_savepoints?
def setup
- @pirate = Pirate.create!(:catchphrase => "My baby takes tha mornin' train!")
- @ship = @pirate.create_ship(:name => "The good ship Dollypop")
- @part = @ship.parts.create!(:name => "Mast")
- @trinket = @part.trinkets.create!(:name => "Necklace")
+ @pirate = Pirate.create!(catchphrase: "My baby takes tha mornin' train!")
+ @ship = @pirate.create_ship(name: "The good ship Dollypop")
+ @part = @ship.parts.create!(name: "Mast")
+ @trinket = @part.trinkets.create!(name: "Necklace")
end
test "when great-grandchild changed in memory, saving parent should save great-grandchild" do
@@ -1000,25 +997,25 @@ class TestHasOneAutosaveAssociationWhichItselfHasAutosaveAssociations < ActiveRe
end
test "when great-grandchild changed via attributes, saving parent should save great-grandchild" do
- @pirate.attributes = {:ship_attributes => {:id => @ship.id, :parts_attributes => [{:id => @part.id, :trinkets_attributes => [{:id => @trinket.id, :name => "changed"}]}]}}
+ @pirate.attributes = { ship_attributes: { id: @ship.id, parts_attributes: [{ id: @part.id, trinkets_attributes: [{ id: @trinket.id, name: "changed" }] }] } }
@pirate.save
assert_equal "changed", @trinket.reload.name
end
test "when great-grandchild marked_for_destruction via attributes, saving parent should destroy great-grandchild" do
- @pirate.attributes = {:ship_attributes => {:id => @ship.id, :parts_attributes => [{:id => @part.id, :trinkets_attributes => [{:id => @trinket.id, :_destroy => true}]}]}}
- assert_difference('@part.trinkets.count', -1) { @pirate.save }
+ @pirate.attributes = { ship_attributes: { id: @ship.id, parts_attributes: [{ id: @part.id, trinkets_attributes: [{ id: @trinket.id, _destroy: true }] }] } }
+ assert_difference("@part.trinkets.count", -1) { @pirate.save }
end
test "when great-grandchild added via attributes, saving parent should create great-grandchild" do
- @pirate.attributes = {:ship_attributes => {:id => @ship.id, :parts_attributes => [{:id => @part.id, :trinkets_attributes => [{:name => "created"}]}]}}
- assert_difference('@part.trinkets.count', 1) { @pirate.save }
+ @pirate.attributes = { ship_attributes: { id: @ship.id, parts_attributes: [{ id: @part.id, trinkets_attributes: [{ name: "created" }] }] } }
+ assert_difference("@part.trinkets.count", 1) { @pirate.save }
end
test "when extra records exist for associations, validate (which calls nested_records_changed_for_autosave?) should not load them up" do
@trinket.name = "changed"
- Ship.create!(:pirate => @pirate, :name => "The Black Rock")
- ShipPart.create!(:ship => @ship, :name => "Stern")
+ Ship.create!(pirate: @pirate, name: "The Black Rock")
+ ShipPart.create!(ship: @ship, name: "Stern")
assert_no_queries { @pirate.valid? }
end
end
@@ -1027,27 +1024,27 @@ class TestHasManyAutosaveAssociationWhichItselfHasAutosaveAssociations < ActiveR
self.use_transactional_tests = false unless supports_savepoints?
def setup
- @ship = Ship.create!(:name => "The good ship Dollypop")
- @part = @ship.parts.create!(:name => "Mast")
- @trinket = @part.trinkets.create!(:name => "Necklace")
+ @ship = Ship.create!(name: "The good ship Dollypop")
+ @part = @ship.parts.create!(name: "Mast")
+ @trinket = @part.trinkets.create!(name: "Necklace")
end
test "if association is not loaded and association record is saved and then in memory record attributes should be saved" do
- @ship.parts_attributes=[{:id => @part.id,:name =>'Deck'}]
+ @ship.parts_attributes = [{ id: @part.id, name: "Deck" }]
assert_equal 1, @ship.association(:parts).target.size
- assert_equal 'Deck', @ship.parts[0].name
+ assert_equal "Deck", @ship.parts[0].name
end
test "if association is not loaded and child doesn't change and I am saving a grandchild then in memory record should be used" do
- @ship.parts_attributes=[{:id => @part.id,:trinkets_attributes =>[{:id => @trinket.id, :name => 'Ruby'}]}]
+ @ship.parts_attributes = [{ id: @part.id, trinkets_attributes: [{ id: @trinket.id, name: "Ruby" }] }]
assert_equal 1, @ship.association(:parts).target.size
- assert_equal 'Mast', @ship.parts[0].name
+ assert_equal "Mast", @ship.parts[0].name
assert_no_difference("@ship.parts[0].association(:trinkets).target.size") do
@ship.parts[0].association(:trinkets).target.size
end
- assert_equal 'Ruby', @ship.parts[0].trinkets[0].name
+ assert_equal "Ruby", @ship.parts[0].trinkets[0].name
@ship.save
- assert_equal 'Ruby', @ship.parts[0].trinkets[0].name
+ assert_equal "Ruby", @ship.parts[0].trinkets[0].name
end
test "when grandchild changed in memory, saving parent should save grandchild" do
@@ -1057,25 +1054,25 @@ class TestHasManyAutosaveAssociationWhichItselfHasAutosaveAssociations < ActiveR
end
test "when grandchild changed via attributes, saving parent should save grandchild" do
- @ship.attributes = {:parts_attributes => [{:id => @part.id, :trinkets_attributes => [{:id => @trinket.id, :name => "changed"}]}]}
+ @ship.attributes = { parts_attributes: [{ id: @part.id, trinkets_attributes: [{ id: @trinket.id, name: "changed" }] }] }
@ship.save
assert_equal "changed", @trinket.reload.name
end
test "when grandchild marked_for_destruction via attributes, saving parent should destroy grandchild" do
- @ship.attributes = {:parts_attributes => [{:id => @part.id, :trinkets_attributes => [{:id => @trinket.id, :_destroy => true}]}]}
- assert_difference('@part.trinkets.count', -1) { @ship.save }
+ @ship.attributes = { parts_attributes: [{ id: @part.id, trinkets_attributes: [{ id: @trinket.id, _destroy: true }] }] }
+ assert_difference("@part.trinkets.count", -1) { @ship.save }
end
test "when grandchild added via attributes, saving parent should create grandchild" do
- @ship.attributes = {:parts_attributes => [{:id => @part.id, :trinkets_attributes => [{:name => "created"}]}]}
- assert_difference('@part.trinkets.count', 1) { @ship.save }
+ @ship.attributes = { parts_attributes: [{ id: @part.id, trinkets_attributes: [{ name: "created" }] }] }
+ assert_difference("@part.trinkets.count", 1) { @ship.save }
end
test "when extra records exist for associations, validate (which calls nested_records_changed_for_autosave?) should not load them up" do
@trinket.name = "changed"
- Ship.create!(:name => "The Black Rock")
- ShipPart.create!(:ship => @ship, :name => "Stern")
+ Ship.create!(name: "The Black Rock")
+ ShipPart.create!(ship: @ship, name: "Stern")
assert_no_queries { @ship.valid? }
end
diff --git a/activerecord/test/cases/nested_attributes_with_callbacks_test.rb b/activerecord/test/cases/nested_attributes_with_callbacks_test.rb
index 43a69928b6..350a966d40 100644
--- a/activerecord/test/cases/nested_attributes_with_callbacks_test.rb
+++ b/activerecord/test/cases/nested_attributes_with_callbacks_test.rb
@@ -4,24 +4,24 @@ require "models/bird"
class NestedAttributesWithCallbacksTest < ActiveRecord::TestCase
Pirate.has_many(:birds_with_add_load,
- :class_name => "Bird",
- :before_add => proc { |p,b|
+ class_name: "Bird",
+ before_add: proc { |p, b|
@@add_callback_called << b
p.birds_with_add_load.to_a
})
Pirate.has_many(:birds_with_add,
- :class_name => "Bird",
- :before_add => proc { |p,b| @@add_callback_called << b })
+ class_name: "Bird",
+ before_add: proc { |p, b| @@add_callback_called << b })
Pirate.accepts_nested_attributes_for(:birds_with_add_load,
:birds_with_add,
- :allow_destroy => true)
+ allow_destroy: true)
def setup
@@add_callback_called = []
@pirate = Pirate.new.tap do |pirate|
pirate.catchphrase = "Don't call me!"
- pirate.birds_attributes = [{:name => 'Bird1'},{:name => 'Bird2'}]
+ pirate.birds_attributes = [{ name: "Bird1" }, { name: "Bird2" }]
pirate.save!
end
@birds = @pirate.birds.to_a
@@ -37,7 +37,7 @@ class NestedAttributesWithCallbacksTest < ActiveRecord::TestCase
def existing_birds_attributes
@birds.map do |bird|
- bird.attributes.slice("id","name")
+ bird.attributes.slice("id", "name")
end
end
@@ -46,17 +46,17 @@ class NestedAttributesWithCallbacksTest < ActiveRecord::TestCase
end
def new_bird_attributes
- [{'name' => "New Bird"}]
+ [{ "name" => "New Bird" }]
end
def destroy_bird_attributes
- [{'id' => bird_to_destroy.id.to_s, "_destroy" => true}]
+ [{ "id" => bird_to_destroy.id.to_s, "_destroy" => true }]
end
def update_new_and_destroy_bird_attributes
- [{'id' => @birds[0].id.to_s, 'name' => 'New Name'},
- {'name' => "New Bird"},
- {'id' => bird_to_destroy.id.to_s, "_destroy" => true}]
+ [{ "id" => @birds[0].id.to_s, "name" => "New Name" },
+ { "name" => "New Bird" },
+ { "id" => bird_to_destroy.id.to_s, "_destroy" => true }]
end
# Characterizing when :before_add callback is called
@@ -136,9 +136,9 @@ class NestedAttributesWithCallbacksTest < ActiveRecord::TestCase
def assert_assignment_affects_records_in_target(association_name)
association = @pirate.send(association_name)
- assert association.detect {|b| b == bird_to_update }.name_changed?,
- 'Update record not updated'
- assert association.detect {|b| b == bird_to_destroy }.marked_for_destruction?,
- 'Destroy record not marked for destruction'
+ assert association.detect { |b| b == bird_to_update }.name_changed?,
+ "Update record not updated"
+ assert association.detect { |b| b == bird_to_destroy }.marked_for_destruction?,
+ "Destroy record not marked for destruction"
end
end
diff --git a/activerecord/test/cases/persistence_test.rb b/activerecord/test/cases/persistence_test.rb
index 56092aaa0c..3f1da82cb4 100644
--- a/activerecord/test/cases/persistence_test.rb
+++ b/activerecord/test/cases/persistence_test.rb
@@ -1,30 +1,30 @@
require "cases/helper"
-require 'models/aircraft'
-require 'models/post'
-require 'models/comment'
-require 'models/author'
-require 'models/topic'
-require 'models/reply'
-require 'models/category'
-require 'models/company'
-require 'models/developer'
-require 'models/computer'
-require 'models/project'
-require 'models/minimalistic'
-require 'models/warehouse_thing'
-require 'models/parrot'
-require 'models/minivan'
-require 'models/owner'
-require 'models/person'
-require 'models/pet'
-require 'models/ship'
-require 'models/toy'
-require 'models/admin'
-require 'models/admin/user'
-require 'rexml/document'
+require "models/aircraft"
+require "models/post"
+require "models/comment"
+require "models/author"
+require "models/topic"
+require "models/reply"
+require "models/category"
+require "models/company"
+require "models/developer"
+require "models/computer"
+require "models/project"
+require "models/minimalistic"
+require "models/warehouse_thing"
+require "models/parrot"
+require "models/minivan"
+require "models/owner"
+require "models/person"
+require "models/pet"
+require "models/ship"
+require "models/toy"
+require "models/admin"
+require "models/admin/user"
+require "rexml/document"
class PersistenceTest < ActiveRecord::TestCase
- fixtures :topics, :companies, :developers, :projects, :computers, :accounts, :minimalistics, 'warehouse-things', :authors, :author_addresses, :categorizations, :categories, :posts, :minivans, :pets, :toys
+ fixtures :topics, :companies, :developers, :projects, :computers, :accounts, :minimalistics, "warehouse-things", :authors, :author_addresses, :categorizations, :categories, :posts, :minivans, :pets, :toys
# Oracle UPDATE does not support ORDER BY
unless current_adapter?(:OracleAdapter)
@@ -39,18 +39,18 @@ class PersistenceTest < ActiveRecord::TestCase
assert_equal authors(:david).id + 1, authors(:mary).id # make sure there is going to be a duplicate PK error
test_update_with_order_succeeds = lambda do |order|
begin
- Author.order(order).update_all('id = id + 1')
+ Author.order(order).update_all("id = id + 1")
rescue ActiveRecord::ActiveRecordError
false
end
end
- if test_update_with_order_succeeds.call('id DESC')
- assert !test_update_with_order_succeeds.call('id ASC') # test that this wasn't a fluke and using an incorrect order results in an exception
+ if test_update_with_order_succeeds.call("id DESC")
+ assert !test_update_with_order_succeeds.call("id ASC") # test that this wasn't a fluke and using an incorrect order results in an exception
else
# test that we're failing because the current Arel's engine doesn't support UPDATE ORDER BY queries is using subselects instead
assert_sql(/\AUPDATE .+ \(SELECT .* ORDER BY id DESC\)\Z/i) do
- test_update_with_order_succeeds.call('id DESC')
+ test_update_with_order_succeeds.call("id DESC")
end
end
end
@@ -83,15 +83,23 @@ class PersistenceTest < ActiveRecord::TestCase
end
def test_delete_all_with_joins_and_where_part_is_hash
- where_args = {:toys => {:name => 'Bone'}}
+ where_args = { toys: { name: "Bone" } }
count = Pet.joins(:toys).where(where_args).count
assert_equal count, 1
assert_equal count, Pet.joins(:toys).where(where_args).delete_all
end
+ def test_delete_all_with_left_joins
+ where_args = { toys: { name: "Bone" } }
+ count = Pet.left_joins(:toys).where(where_args).count
+
+ assert_equal count, 1
+ assert_equal count, Pet.left_joins(:toys).where(where_args).delete_all
+ end
+
def test_delete_all_with_joins_and_where_part_is_not_hash
- where_args = ['toys.name = ?', 'Bone']
+ where_args = ["toys.name = ?", "Bone"]
count = Pet.joins(:toys).where(where_args).count
assert_equal count, 1
@@ -133,10 +141,10 @@ class PersistenceTest < ActiveRecord::TestCase
def test_destroy_all
conditions = "author_name = 'Mary'"
- topics_by_mary = Topic.all.merge!(:where => conditions, :order => 'id').to_a
+ topics_by_mary = Topic.all.merge!(where: conditions, order: "id").to_a
assert ! topics_by_mary.empty?
- assert_difference('Topic.count', -topics_by_mary.size) do
+ assert_difference("Topic.count", -topics_by_mary.size) do
destroyed = Topic.where(conditions).destroy_all.sort_by(&:id)
assert_equal topics_by_mary, destroyed
assert destroyed.all?(&:frozen?), "destroyed topics should be frozen"
@@ -144,9 +152,9 @@ class PersistenceTest < ActiveRecord::TestCase
end
def test_destroy_many
- clients = Client.all.merge!(:order => 'id').find([2, 3])
+ clients = Client.all.merge!(order: "id").find([2, 3])
- assert_difference('Client.count', -2) do
+ assert_difference("Client.count", -2) do
destroyed = Client.destroy([2, 3]).sort_by(&:id)
assert_equal clients, destroyed
assert destroyed.all?(&:frozen?), "destroyed clients should be frozen"
@@ -159,7 +167,7 @@ class PersistenceTest < ActiveRecord::TestCase
end
def test_becomes_includes_errors
- company = Company.new(:name => nil)
+ company = Company.new(name: nil)
assert !company.valid?
original_errors = company.errors
client = company.becomes(Client)
@@ -170,7 +178,7 @@ class PersistenceTest < ActiveRecord::TestCase
child_class = Class.new(Admin::User) do
store_accessor :settings, :foo
- def self.name; 'Admin::ChildUser'; end
+ def self.name; "Admin::ChildUser"; end
end
admin = Admin::User.new
@@ -231,7 +239,7 @@ class PersistenceTest < ActiveRecord::TestCase
end
def test_save!
- topic = Topic.new(:title => "New Topic")
+ topic = Topic.new(title: "New Topic")
assert topic.save!
reply = WrongReply.new
@@ -261,7 +269,7 @@ class PersistenceTest < ActiveRecord::TestCase
end
def test_save_for_record_with_only_primary_key_that_is_provided
- assert_nothing_raised { Minimalistic.create!(:id => 2) }
+ assert_nothing_raised { Minimalistic.create!(id: 2) }
end
def test_save_with_duping_of_destroyed_object
@@ -281,9 +289,9 @@ class PersistenceTest < ActiveRecord::TestCase
def test_create_columns_not_equal_attributes
topic = Topic.instantiate(
- 'attributes' => {
- 'title' => 'Another New Topic',
- 'does_not_exist' => 'test'
+ "attributes" => {
+ "title" => "Another New Topic",
+ "does_not_exist" => "test"
}
)
assert_nothing_raised { topic.save }
@@ -330,8 +338,8 @@ class PersistenceTest < ActiveRecord::TestCase
topic.title = "Still another topic"
topic.save
- topic_reloaded = Topic.instantiate(topic.attributes.merge('does_not_exist' => 'test'))
- topic_reloaded.title = 'A New Topic'
+ topic_reloaded = Topic.instantiate(topic.attributes.merge("does_not_exist" => "test"))
+ topic_reloaded.title = "A New Topic"
assert_nothing_raised { topic_reloaded.save }
end
@@ -371,7 +379,7 @@ class PersistenceTest < ActiveRecord::TestCase
def test_update_after_create
klass = Class.new(Topic) do
- def self.name; 'Topic'; end
+ def self.name; "Topic"; end
after_create do
update_attribute("author_name", "David")
end
@@ -387,24 +395,24 @@ class PersistenceTest < ActiveRecord::TestCase
def test_update_attribute_does_not_run_sql_if_attribute_is_not_changed
klass = Class.new(Topic) do
- def self.name; 'Topic'; end
+ def self.name; "Topic"; end
end
- topic = klass.create(title: 'Another New Topic')
+ topic = klass.create(title: "Another New Topic")
assert_queries(0) do
- topic.update_attribute(:title, 'Another New Topic')
+ assert topic.update_attribute(:title, "Another New Topic")
end
end
def test_update_does_not_run_sql_if_record_has_not_changed
- topic = Topic.create(title: 'Another New Topic')
- assert_queries(0) { topic.update(title: 'Another New Topic') }
- assert_queries(0) { topic.update_attributes(title: 'Another New Topic') }
+ topic = Topic.create(title: "Another New Topic")
+ assert_queries(0) { assert topic.update(title: "Another New Topic") }
+ assert_queries(0) { assert topic.update_attributes(title: "Another New Topic") }
end
def test_delete
topic = Topic.find(1)
- assert_equal topic, topic.delete, 'topic.delete did not return self'
- assert topic.frozen?, 'topic not frozen after delete'
+ assert_equal topic, topic.delete, "topic.delete did not return self"
+ assert topic.frozen?, "topic not frozen after delete"
assert_raise(ActiveRecord::RecordNotFound) { Topic.find(topic.id) }
end
@@ -415,15 +423,15 @@ class PersistenceTest < ActiveRecord::TestCase
def test_destroy
topic = Topic.find(1)
- assert_equal topic, topic.destroy, 'topic.destroy did not return self'
- assert topic.frozen?, 'topic not frozen after destroy'
+ assert_equal topic, topic.destroy, "topic.destroy did not return self"
+ assert topic.frozen?, "topic not frozen after destroy"
assert_raise(ActiveRecord::RecordNotFound) { Topic.find(topic.id) }
end
def test_destroy!
topic = Topic.find(1)
- assert_equal topic, topic.destroy!, 'topic.destroy! did not return self'
- assert topic.frozen?, 'topic not frozen after destroy!'
+ assert_equal topic, topic.destroy!, "topic.destroy! did not return self"
+ assert topic.frozen?, "topic not frozen after destroy!"
assert_raise(ActiveRecord::RecordNotFound) { Topic.find(topic.id) }
end
@@ -436,25 +444,39 @@ class PersistenceTest < ActiveRecord::TestCase
assert_equal "bulk updated!", Topic.find(1).content
assert_equal "bulk updated!", Topic.find(2).content
- assert_equal Topic.count, Topic.update_all(['content = ?', 'bulk updated again!'])
+ assert_equal Topic.count, Topic.update_all(["content = ?", "bulk updated again!"])
assert_equal "bulk updated again!", Topic.find(1).content
assert_equal "bulk updated again!", Topic.find(2).content
- assert_equal Topic.count, Topic.update_all(['content = ?', nil])
+ assert_equal Topic.count, Topic.update_all(["content = ?", nil])
assert_nil Topic.find(1).content
end
def test_update_all_with_hash
assert_not_nil Topic.find(1).last_read
- assert_equal Topic.count, Topic.update_all(:content => 'bulk updated with hash!', :last_read => nil)
+ assert_equal Topic.count, Topic.update_all(content: "bulk updated with hash!", last_read: nil)
assert_equal "bulk updated with hash!", Topic.find(1).content
assert_equal "bulk updated with hash!", Topic.find(2).content
assert_nil Topic.find(1).last_read
assert_nil Topic.find(2).last_read
end
+ def test_update_all_with_joins
+ where_args = { toys: { name: "Bone" } }
+ count = Pet.left_joins(:toys).where(where_args).count
+
+ assert_equal count, Pet.joins(:toys).where(where_args).update_all(name: "Bob")
+ end
+
+ def test_update_all_with_left_joins
+ where_args = { toys: { name: "Bone" } }
+ count = Pet.left_joins(:toys).where(where_args).count
+
+ assert_equal count, Pet.left_joins(:toys).where(where_args).update_all(name: "Bob")
+ end
+
def test_update_all_with_non_standard_table_name
- assert_equal 1, WarehouseThing.where(id: 1).update_all(['value = ?', 0])
+ assert_equal 1, WarehouseThing.where(id: 1).update_all(["value = ?", 0])
assert_equal 0, WarehouseThing.find(1).value
end
@@ -496,20 +518,20 @@ class PersistenceTest < ActiveRecord::TestCase
end
def test_update_attribute_for_readonly_attribute
- minivan = Minivan.find('m1')
- assert_raises(ActiveRecord::ActiveRecordError) { minivan.update_attribute(:color, 'black') }
+ minivan = Minivan.find("m1")
+ assert_raises(ActiveRecord::ActiveRecordError) { minivan.update_attribute(:color, "black") }
end
def test_update_attribute_with_one_updated
t = Topic.first
- t.update_attribute(:title, 'super_title')
- assert_equal 'super_title', t.title
+ t.update_attribute(:title, "super_title")
+ assert_equal "super_title", t.title
assert !t.changed?, "topic should not have changed"
assert !t.title_changed?, "title should not have changed"
- assert_nil t.title_change, 'title change should be nil'
+ assert_nil t.title_change, "title change should be nil"
t.reload
- assert_equal 'super_title', t.title
+ assert_equal "super_title", t.title
end
def test_update_attribute_for_updated_at_on
@@ -569,17 +591,17 @@ class PersistenceTest < ActiveRecord::TestCase
end
def test_update_column_with_model_having_primary_key_other_than_id
- minivan = Minivan.find('m1')
- new_name = 'sebavan'
+ minivan = Minivan.find("m1")
+ new_name = "sebavan"
minivan.update_column(:name, new_name)
assert_equal new_name, minivan.name
end
def test_update_column_for_readonly_attribute
- minivan = Minivan.find('m1')
+ minivan = Minivan.find("m1")
prev_color = minivan.color
- assert_raises(ActiveRecord::ActiveRecordError) { minivan.update_column(:color, 'black') }
+ assert_raises(ActiveRecord::ActiveRecordError) { minivan.update_column(:color, "black") }
assert_equal prev_color, minivan.color
end
@@ -598,31 +620,31 @@ class PersistenceTest < ActiveRecord::TestCase
end
def test_update_column_with_one_changed_and_one_updated
- t = Topic.order('id').limit(1).first
+ t = Topic.order("id").limit(1).first
author_name = t.author_name
- t.author_name = 'John'
- t.update_column(:title, 'super_title')
- assert_equal 'John', t.author_name
- assert_equal 'super_title', t.title
+ t.author_name = "John"
+ t.update_column(:title, "super_title")
+ assert_equal "John", t.author_name
+ assert_equal "super_title", t.title
assert t.changed?, "topic should have changed"
assert t.author_name_changed?, "author_name should have changed"
t.reload
assert_equal author_name, t.author_name
- assert_equal 'super_title', t.title
+ assert_equal "super_title", t.title
end
def test_update_column_with_default_scope
developer = DeveloperCalledDavid.first
- developer.name = 'John'
+ developer.name = "John"
developer.save!
- assert developer.update_column(:name, 'Will'), 'did not update record due to default scope'
+ assert developer.update_column(:name, "Will"), "did not update record due to default scope"
end
def test_update_columns
topic = Topic.find(1)
- topic.update_columns({ "approved" => true, title: "Sebastian Topic" })
+ topic.update_columns("approved" => true, title: "Sebastian Topic")
assert topic.approved?
assert_equal "Sebastian Topic", topic.title
topic.reload
@@ -643,35 +665,35 @@ class PersistenceTest < ActiveRecord::TestCase
def test_update_columns_should_raise_exception_if_new_record
topic = Topic.new
- assert_raises(ActiveRecord::ActiveRecordError) { topic.update_columns({ approved: false }) }
+ assert_raises(ActiveRecord::ActiveRecordError) { topic.update_columns(approved: false) }
end
def test_update_columns_should_not_leave_the_object_dirty
topic = Topic.find(1)
- topic.update({ "content" => "--- Have a nice day\n...\n", :author_name => "Jose" })
+ topic.update("content" => "--- Have a nice day\n...\n", :author_name => "Jose")
topic.reload
- topic.update_columns({ content: "--- You too\n...\n", "author_name" => "Sebastian" })
+ topic.update_columns(content: "--- You too\n...\n", "author_name" => "Sebastian")
assert_equal [], topic.changed
topic.reload
- topic.update_columns({ content: "--- Have a nice day\n...\n", author_name: "Jose" })
+ topic.update_columns(content: "--- Have a nice day\n...\n", author_name: "Jose")
assert_equal [], topic.changed
end
def test_update_columns_with_model_having_primary_key_other_than_id
- minivan = Minivan.find('m1')
- new_name = 'sebavan'
+ minivan = Minivan.find("m1")
+ new_name = "sebavan"
minivan.update_columns(name: new_name)
assert_equal new_name, minivan.name
end
def test_update_columns_with_one_readonly_attribute
- minivan = Minivan.find('m1')
+ minivan = Minivan.find("m1")
prev_color = minivan.color
prev_name = minivan.name
- assert_raises(ActiveRecord::ActiveRecordError) { minivan.update_columns({ name: "My old minivan", color: 'black' }) }
+ assert_raises(ActiveRecord::ActiveRecordError) { minivan.update_columns(name: "My old minivan", color: "black") }
assert_equal prev_color, minivan.color
assert_equal prev_name, minivan.name
@@ -697,18 +719,18 @@ class PersistenceTest < ActiveRecord::TestCase
end
def test_update_columns_with_one_changed_and_one_updated
- t = Topic.order('id').limit(1).first
+ t = Topic.order("id").limit(1).first
author_name = t.author_name
- t.author_name = 'John'
- t.update_columns(title: 'super_title')
- assert_equal 'John', t.author_name
- assert_equal 'super_title', t.title
+ t.author_name = "John"
+ t.update_columns(title: "super_title")
+ assert_equal "John", t.author_name
+ assert_equal "super_title", t.title
assert t.changed?, "topic should have changed"
assert t.author_name_changed?, "author_name should have changed"
t.reload
assert_equal author_name, t.author_name
- assert_equal 'super_title', t.title
+ assert_equal "super_title", t.title
end
def test_update_columns_changing_id
@@ -726,10 +748,10 @@ class PersistenceTest < ActiveRecord::TestCase
def test_update_columns_with_default_scope
developer = DeveloperCalledDavid.first
- developer.name = 'John'
+ developer.name = "John"
developer.save!
- assert developer.update_columns(name: 'Will'), 'did not update record due to default scope'
+ assert developer.update_columns(name: "Will"), "did not update record due to default scope"
end
def test_update
@@ -840,7 +862,7 @@ class PersistenceTest < ActiveRecord::TestCase
end
def test_persisted_returns_boolean
- developer = Developer.new(:name => "Jose")
+ developer = Developer.new(name: "Jose")
assert_equal false, developer.persisted?
developer.save!
assert_equal true, developer.persisted?
@@ -909,7 +931,7 @@ class PersistenceTest < ActiveRecord::TestCase
end
def test_reload_removes_custom_selects
- post = Post.select('posts.*, 1 as wibble').last!
+ post = Post.select("posts.*, 1 as wibble").last!
assert_equal 1, post[:wibble]
assert_nil post.reload[:wibble]
@@ -930,8 +952,8 @@ class PersistenceTest < ActiveRecord::TestCase
def test_reload_via_querycache
ActiveRecord::Base.connection.enable_query_cache!
ActiveRecord::Base.connection.clear_query_cache
- assert ActiveRecord::Base.connection.query_cache_enabled, 'cache should be on'
- parrot = Parrot.create(:name => 'Shane')
+ assert ActiveRecord::Base.connection.query_cache_enabled, "cache should be on"
+ parrot = Parrot.create(name: "Shane")
# populate the cache with the SELECT result
found_parrot = Parrot.find(parrot.id)
@@ -940,16 +962,16 @@ class PersistenceTest < ActiveRecord::TestCase
# Manually update the 'name' attribute in the DB directly
assert_equal 1, ActiveRecord::Base.connection.query_cache.length
ActiveRecord::Base.uncached do
- found_parrot.name = 'Mary'
+ found_parrot.name = "Mary"
found_parrot.save
end
# Now reload, and verify that it gets the DB version, and not the querycache version
found_parrot.reload
- assert_equal 'Mary', found_parrot.name
+ assert_equal "Mary", found_parrot.name
found_parrot = Parrot.find(parrot.id)
- assert_equal 'Mary', found_parrot.name
+ assert_equal "Mary", found_parrot.name
ensure
ActiveRecord::Base.connection.disable_query_cache!
end
@@ -967,16 +989,15 @@ class PersistenceTest < ActiveRecord::TestCase
self.table_name = :widgets
end
- instance = widget.create!({
- name: 'Bob',
+ instance = widget.create!(
+ name: "Bob",
created_at: 1.day.ago,
- updated_at: 1.day.ago
- })
+ updated_at: 1.day.ago)
created_at = instance.created_at
updated_at = instance.updated_at
- instance.name = 'Barb'
+ instance.name = "Barb"
instance.save!(touch: false)
assert_equal instance.created_at, created_at
assert_equal instance.updated_at, updated_at
diff --git a/activerecord/test/cases/pooled_connections_test.rb b/activerecord/test/cases/pooled_connections_test.rb
index bca50dd008..f1b0d08765 100644
--- a/activerecord/test/cases/pooled_connections_test.rb
+++ b/activerecord/test/cases/pooled_connections_test.rb
@@ -18,7 +18,7 @@ class PooledConnectionsTest < ActiveRecord::TestCase
# Will deadlock due to lack of Monitor timeouts in 1.9
def checkout_checkin_connections(pool_size, threads)
- ActiveRecord::Base.establish_connection(@connection.merge({:pool => pool_size, :checkout_timeout => 0.5}))
+ ActiveRecord::Base.establish_connection(@connection.merge(pool: pool_size, checkout_timeout: 0.5))
@connection_count = 0
@timed_out = 0
threads.times do
@@ -36,7 +36,7 @@ class PooledConnectionsTest < ActiveRecord::TestCase
end
def checkout_checkin_connections_loop(pool_size, loops)
- ActiveRecord::Base.establish_connection(@connection.merge({:pool => pool_size, :checkout_timeout => 0.5}))
+ ActiveRecord::Base.establish_connection(@connection.merge(pool: pool_size, checkout_timeout: 0.5))
@connection_count = 0
@timed_out = 0
loops.times do
@@ -66,7 +66,7 @@ class PooledConnectionsTest < ActiveRecord::TestCase
end
def test_pooled_connection_remove
- ActiveRecord::Base.establish_connection(@connection.merge({:pool => 2, :checkout_timeout => 0.5}))
+ ActiveRecord::Base.establish_connection(@connection.merge(pool: 2, checkout_timeout: 0.5))
old_connection = ActiveRecord::Base.connection
extra_connection = ActiveRecord::Base.connection_pool.checkout
ActiveRecord::Base.connection_pool.remove(extra_connection)
@@ -75,7 +75,7 @@ class PooledConnectionsTest < ActiveRecord::TestCase
private
- def add_record(name)
- ActiveRecord::Base.connection_pool.with_connection { Project.create! :name => name }
- end
+ def add_record(name)
+ ActiveRecord::Base.connection_pool.with_connection { Project.create! name: name }
+ end
end unless in_memory_db?
diff --git a/activerecord/test/cases/primary_keys_test.rb b/activerecord/test/cases/primary_keys_test.rb
index 4267ad4a24..1d72899102 100644
--- a/activerecord/test/cases/primary_keys_test.rb
+++ b/activerecord/test/cases/primary_keys_test.rb
@@ -1,12 +1,12 @@
require "cases/helper"
-require 'support/schema_dumping_helper'
-require 'models/topic'
-require 'models/reply'
-require 'models/subscriber'
-require 'models/movie'
-require 'models/keyboard'
-require 'models/mixed_case_monkey'
-require 'models/dashboard'
+require "support/schema_dumping_helper"
+require "models/topic"
+require "models/reply"
+require "models/subscriber"
+require "models/movie"
+require "models/keyboard"
+require "models/mixed_case_monkey"
+require "models/dashboard"
class PrimaryKeysTest < ActiveRecord::TestCase
fixtures :topics, :subscribers, :movies, :mixed_case_monkeys
@@ -54,9 +54,9 @@ class PrimaryKeysTest < ActiveRecord::TestCase
def test_customized_primary_key_auto_assigns_on_save
Keyboard.delete_all
- keyboard = Keyboard.new(:name => 'HHKB')
+ keyboard = Keyboard.new(name: "HHKB")
assert_nothing_raised { keyboard.save! }
- assert_equal keyboard.id, Keyboard.find_by_name('HHKB').id
+ assert_equal keyboard.id, Keyboard.find_by_name("HHKB").id
end
def test_customized_primary_key_can_be_get_before_saving
@@ -67,9 +67,9 @@ class PrimaryKeysTest < ActiveRecord::TestCase
def test_customized_string_primary_key_settable_before_save
subscriber = Subscriber.new
- assert_nothing_raised { subscriber.id = 'webster123' }
- assert_equal 'webster123', subscriber.id
- assert_equal 'webster123', subscriber.nick
+ assert_nothing_raised { subscriber.id = "webster123" }
+ assert_equal "webster123", subscriber.id
+ assert_equal "webster123", subscriber.nick
end
def test_string_key
@@ -114,13 +114,13 @@ class PrimaryKeysTest < ActiveRecord::TestCase
assert_nothing_raised { MixedCaseMonkey.delete(1) }
end
def test_update_counters_should_quote_pkey_and_quote_counter_columns
- assert_nothing_raised { MixedCaseMonkey.update_counters(1, :fleaCount => 99) }
+ assert_nothing_raised { MixedCaseMonkey.update_counters(1, fleaCount: 99) }
end
def test_find_with_one_id_should_quote_pkey
assert_nothing_raised { MixedCaseMonkey.find(1) }
end
def test_find_with_multiple_ids_should_quote_pkey
- assert_nothing_raised { MixedCaseMonkey.find([1,2]) }
+ assert_nothing_raised { MixedCaseMonkey.find([1, 2]) }
end
def test_instance_update_should_quote_pkey
assert_nothing_raised { MixedCaseMonkey.find(1).save }
@@ -129,24 +129,18 @@ class PrimaryKeysTest < ActiveRecord::TestCase
assert_nothing_raised { MixedCaseMonkey.find(1).destroy }
end
- def test_supports_primary_key
- assert_nothing_raised do
- ActiveRecord::Base.connection.supports_primary_key?
- end
- end
-
if ActiveRecord::Base.connection.supports_primary_key?
def test_primary_key_returns_value_if_it_exists
klass = Class.new(ActiveRecord::Base) do
- self.table_name = 'developers'
+ self.table_name = "developers"
end
- assert_equal 'id', klass.primary_key
+ assert_equal "id", klass.primary_key
end
def test_primary_key_returns_nil_if_it_does_not_exist
klass = Class.new(ActiveRecord::Base) do
- self.table_name = 'developers_projects'
+ self.table_name = "developers_projects"
end
assert_nil klass.primary_key
@@ -154,7 +148,7 @@ class PrimaryKeysTest < ActiveRecord::TestCase
end
def test_quoted_primary_key_after_set_primary_key
- k = Class.new( ActiveRecord::Base )
+ k = Class.new(ActiveRecord::Base)
assert_equal k.connection.quote_column_name("id"), k.quoted_primary_key
k.primary_key = "foo"
assert_equal k.connection.quote_column_name("foo"), k.quoted_primary_key
@@ -166,17 +160,17 @@ class PrimaryKeysTest < ActiveRecord::TestCase
end
def test_primary_key_update_with_custom_key_name
- dashboard = Dashboard.create!(dashboard_id: '1')
- dashboard.id = '2'
+ dashboard = Dashboard.create!(dashboard_id: "1")
+ dashboard.id = "2"
dashboard.save!
dashboard = Dashboard.first
- assert_equal '2', dashboard.id
+ assert_equal "2", dashboard.id
end
def test_create_without_primary_key_no_extra_query
klass = Class.new(ActiveRecord::Base) do
- self.table_name = 'dashboards'
+ self.table_name = "dashboards"
end
klass.create! # warmup schema cache
assert_queries(3, ignore_none: true) { klass.create! }
@@ -205,17 +199,54 @@ class PrimaryKeyWithNoConnectionTest < ActiveRecord::TestCase
connection = ActiveRecord::Base.remove_connection
model = Class.new(ActiveRecord::Base)
- model.primary_key = 'foo'
+ model.primary_key = "foo"
- assert_equal 'foo', model.primary_key
+ assert_equal "foo", model.primary_key
ActiveRecord::Base.establish_connection(connection)
- assert_equal 'foo', model.primary_key
+ assert_equal "foo", model.primary_key
end
end
end
+class PrimaryKeyWithAutoIncrementTest < ActiveRecord::TestCase
+ self.use_transactional_tests = false
+
+ class AutoIncrement < ActiveRecord::Base
+ end
+
+ def setup
+ @connection = ActiveRecord::Base.connection
+ end
+
+ def teardown
+ @connection.drop_table(:auto_increments, if_exists: true)
+ end
+
+ def test_primary_key_with_auto_increment
+ @connection.create_table(:auto_increments, id: :integer, auto_increment: true, force: true)
+ assert_auto_incremented
+ end
+
+ def test_primary_key_with_auto_increment_and_bigint
+ @connection.create_table(:auto_increments, id: :bigint, auto_increment: true, force: true)
+ assert_auto_incremented
+ end
+
+ private
+ def assert_auto_incremented
+ record1 = AutoIncrement.create!
+ assert_not_nil record1.id
+
+ record1.destroy
+
+ record2 = AutoIncrement.create!
+ assert_not_nil record2.id
+ assert_operator record2.id, :>, record1.id
+ end
+end
+
class PrimaryKeyAnyTypeTest < ActiveRecord::TestCase
include SchemaDumpingHelper
@@ -255,6 +286,7 @@ class CompositePrimaryKeyTest < ActiveRecord::TestCase
def setup
@connection = ActiveRecord::Base.connection
+ @connection.schema_cache.clear!
@connection.create_table(:barcodes, primary_key: ["region", "code"], force: true) do |t|
t.string :region
t.integer :code
@@ -270,10 +302,15 @@ class CompositePrimaryKeyTest < ActiveRecord::TestCase
end
def test_primary_key_issues_warning
+ model = Class.new(ActiveRecord::Base) do
+ def self.table_name
+ "barcodes"
+ end
+ end
warning = capture(:stderr) do
- assert_nil @connection.primary_key("barcodes")
+ assert_nil model.primary_key
end
- assert_match(/WARNING: Rails does not support composite primary key\./, warning)
+ assert_match(/WARNING: Active Record does not support composite primary key\./, warning)
end
def test_collectly_dump_composite_primary_key
@@ -283,85 +320,69 @@ class CompositePrimaryKeyTest < ActiveRecord::TestCase
end
if current_adapter?(:Mysql2Adapter)
- class PrimaryKeyBigintNilDefaultTest < ActiveRecord::TestCase
+ class PrimaryKeyIntegerNilDefaultTest < ActiveRecord::TestCase
include SchemaDumpingHelper
self.use_transactional_tests = false
def setup
@connection = ActiveRecord::Base.connection
- @connection.create_table(:bigint_defaults, id: :bigint, default: nil, force: true)
+ @connection.create_table(:int_defaults, id: :integer, default: nil, force: true)
end
def teardown
- @connection.drop_table :bigint_defaults, if_exists: true
+ @connection.drop_table :int_defaults, if_exists: true
end
- test "primary key with bigint allows default override via nil" do
- column = @connection.columns(:bigint_defaults).find { |c| c.name == 'id' }
- assert column.bigint?
+ test "primary key with integer allows default override via nil" do
+ column = @connection.columns(:int_defaults).find { |c| c.name == "id" }
+ assert_equal :integer, column.type
assert_not column.auto_increment?
end
- test "schema dump primary key with bigint default nil" do
- schema = dump_table_schema "bigint_defaults"
- assert_match %r{create_table "bigint_defaults", id: :bigint, default: nil}, schema
+ test "schema dump primary key with int default nil" do
+ schema = dump_table_schema "int_defaults"
+ assert_match %r{create_table "int_defaults", id: :integer, default: nil}, schema
end
end
end
-if current_adapter?(:PostgreSQLAdapter, :Mysql2Adapter)
- class PrimaryKeyBigSerialTest < ActiveRecord::TestCase
- include SchemaDumpingHelper
+class PrimaryKeyIntegerTest < ActiveRecord::TestCase
+ include SchemaDumpingHelper
- self.use_transactional_tests = false
+ self.use_transactional_tests = false
- class Widget < ActiveRecord::Base
- end
+ class Widget < ActiveRecord::Base
+ end
- setup do
- @connection = ActiveRecord::Base.connection
- if current_adapter?(:PostgreSQLAdapter)
- @connection.create_table(:widgets, id: :bigserial, force: true)
- else
- @connection.create_table(:widgets, id: :bigint, force: true)
- end
- end
+ setup do
+ @connection = ActiveRecord::Base.connection
+ @connection.create_table(:widgets, force: true)
+ end
- teardown do
- @connection.drop_table :widgets, if_exists: true
- Widget.reset_column_information
- end
+ teardown do
+ @connection.drop_table :widgets, if_exists: true
+ Widget.reset_column_information
+ end
- test "primary key column type with bigserial" do
- column_type = Widget.type_for_attribute(Widget.primary_key)
- assert_equal :integer, column_type.type
- assert_equal 8, column_type.limit
+ if current_adapter?(:PostgreSQLAdapter, :Mysql2Adapter)
+ test "schema dump primary key with bigserial" do
+ schema = dump_table_schema "widgets"
+ assert_match %r{create_table "widgets", force: :cascade}, schema
end
+ end
- test "primary key with bigserial are automatically numbered" do
- widget = Widget.create!
- assert_not_nil widget.id
- end
+ test "primary key column type" do
+ column_type = Widget.type_for_attribute(Widget.primary_key)
+ assert_equal :integer, column_type.type
- test "schema dump primary key with bigserial" do
- schema = dump_table_schema "widgets"
- if current_adapter?(:PostgreSQLAdapter)
- assert_match %r{create_table "widgets", id: :bigserial, force: :cascade}, schema
- else
- assert_match %r{create_table "widgets", id: :bigint, force: :cascade}, schema
- end
+ if current_adapter?(:PostgreSQLAdapter, :Mysql2Adapter)
+ assert_equal 8, column_type.limit
end
if current_adapter?(:Mysql2Adapter)
- test "primary key column type with options" do
- @connection.create_table(:widgets, id: :primary_key, limit: 8, unsigned: true, force: true)
- column = @connection.columns(:widgets).find { |c| c.name == 'id' }
- assert column.auto_increment?
- assert_equal :integer, column.type
- assert_equal 8, column.limit
- assert column.unsigned?
- end
+ column = @connection.columns(:widgets).find { |c| c.name == "id" }
+ assert column.auto_increment?
end
end
end
diff --git a/activerecord/test/cases/query_cache_test.rb b/activerecord/test/cases/query_cache_test.rb
index 03ec063671..4a49bfe9b1 100644
--- a/activerecord/test/cases/query_cache_test.rb
+++ b/activerecord/test/cases/query_cache_test.rb
@@ -1,20 +1,43 @@
require "cases/helper"
-require 'models/topic'
-require 'models/task'
-require 'models/category'
-require 'models/post'
-require 'rack'
+require "models/topic"
+require "models/task"
+require "models/category"
+require "models/post"
+require "rack"
class QueryCacheTest < ActiveRecord::TestCase
+ self.use_transactional_tests = false
+
fixtures :tasks, :topics, :categories, :posts, :categories_posts
- teardown do
+ class ShouldNotHaveExceptionsLogger < ActiveRecord::LogSubscriber
+ attr_reader :logger
+
+ def initialize
+ super
+ @logger = ::Logger.new File::NULL
+ @exception = false
+ end
+
+ def exception?
+ @exception
+ end
+
+ def sql(event)
+ super
+ rescue
+ @exception = true
+ end
+ end
+
+ def teardown
Task.connection.clear_query_cache
ActiveRecord::Base.connection.disable_query_cache!
+ super
end
def test_exceptional_middleware_clears_and_disables_cache_on_error
- assert !ActiveRecord::Base.connection.query_cache_enabled, 'cache off'
+ assert_cache :off
mw = middleware { |env|
Task.find 1
@@ -24,19 +47,66 @@ class QueryCacheTest < ActiveRecord::TestCase
}
assert_raises(RuntimeError) { mw.call({}) }
- assert_equal 0, ActiveRecord::Base.connection.query_cache.length
- assert !ActiveRecord::Base.connection.query_cache_enabled, 'cache off'
+ assert_cache :off
end
- def test_exceptional_middleware_leaves_enabled_cache_alone
- ActiveRecord::Base.connection.enable_query_cache!
+ def test_query_cache_across_threads
+ ActiveRecord::Base.connection_pool.connections.each do |conn|
+ assert_cache :off, conn
+ end
- mw = middleware { |env|
- raise "lol borked"
- }
- assert_raises(RuntimeError) { mw.call({}) }
+ assert !ActiveRecord::Base.connection.nil?
+ assert_cache :off
+
+ middleware {
+ assert_cache :clean
+
+ Task.find 1
+ assert_cache :dirty
- assert ActiveRecord::Base.connection.query_cache_enabled, 'cache on'
+ thread_1_connection = ActiveRecord::Base.connection
+ ActiveRecord::Base.clear_active_connections!
+ assert_cache :off, thread_1_connection
+
+ started = Concurrent::Event.new
+ checked = Concurrent::Event.new
+
+ thread_2_connection = nil
+ thread = Thread.new {
+ thread_2_connection = ActiveRecord::Base.connection
+
+ assert_equal thread_2_connection, thread_1_connection
+ assert_cache :off
+
+ middleware {
+ assert_cache :clean
+
+ Task.find 1
+ assert_cache :dirty
+
+ started.set
+ checked.wait
+
+ ActiveRecord::Base.clear_active_connections!
+ }.call({})
+ }
+
+ started.wait
+
+ thread_1_connection = ActiveRecord::Base.connection
+ assert_not_equal thread_1_connection, thread_2_connection
+ assert_cache :dirty, thread_2_connection
+ checked.set
+ thread.join
+
+ assert_cache :off, thread_2_connection
+ }.call({})
+
+ ActiveRecord::Base.connection_pool.connections.each do |conn|
+ assert_cache :off, conn
+ end
+ ensure
+ ActiveRecord::Base.clear_all_connections!
end
def test_middleware_delegates
@@ -46,7 +116,7 @@ class QueryCacheTest < ActiveRecord::TestCase
[200, {}, nil]
}
mw.call({})
- assert called, 'middleware should delegate'
+ assert called, "middleware should delegate"
end
def test_middleware_caches
@@ -60,10 +130,10 @@ class QueryCacheTest < ActiveRecord::TestCase
end
def test_cache_enabled_during_call
- assert !ActiveRecord::Base.connection.query_cache_enabled, 'cache off'
+ assert_cache :off
mw = middleware { |env|
- assert ActiveRecord::Base.connection.query_cache_enabled, 'cache on'
+ assert_cache :clean
[200, {}, nil]
}
mw.call({})
@@ -119,6 +189,33 @@ class QueryCacheTest < ActiveRecord::TestCase
end
end
+ def test_cache_does_not_raise_exceptions
+ logger = ShouldNotHaveExceptionsLogger.new
+ subscriber = ActiveSupport::Notifications.subscribe "sql.active_record", logger
+
+ ActiveRecord::Base.cache do
+ assert_queries(1) { Task.find(1); Task.find(1) }
+ end
+
+ assert_not_predicate logger, :exception?
+ ensure
+ ActiveSupport::Notifications.unsubscribe subscriber
+ end
+
+ def test_query_cache_does_not_allow_sql_key_mutation
+ subscriber = ActiveSupport::Notifications.subscribe("sql.active_record") do |_, _, _, _, payload|
+ payload[:sql].downcase!
+ end
+
+ assert_raises RuntimeError do
+ ActiveRecord::Base.cache do
+ assert_queries(1) { Task.find(1); Task.find(1) }
+ end
+ end
+ ensure
+ ActiveSupport::Notifications.unsubscribe subscriber
+ end
+
def test_cache_is_flat
Task.cache do
assert_queries(1) { Topic.find(1); Topic.find(1); }
@@ -136,7 +233,7 @@ class QueryCacheTest < ActiveRecord::TestCase
assert_kind_of Numeric, Task.connection.select_value("SELECT count(*) AS count_all FROM tasks")
elsif current_adapter?(:SQLite3Adapter, :Mysql2Adapter, :PostgreSQLAdapter)
# Future versions of the sqlite3 adapter will return numeric
- assert_instance_of Fixnum, Task.connection.select_value("SELECT count(*) AS count_all FROM tasks")
+ assert_instance_of 0.class, Task.connection.select_value("SELECT count(*) AS count_all FROM tasks")
else
assert_instance_of String, Task.connection.select_value("SELECT count(*) AS count_all FROM tasks")
end
@@ -164,7 +261,7 @@ class QueryCacheTest < ActiveRecord::TestCase
def test_cache_is_not_available_when_using_a_not_connected_connection
spec_name = Task.connection_specification_name
- conf = ActiveRecord::Base.configurations['arunit'].merge('name' => 'test2')
+ conf = ActiveRecord::Base.configurations["arunit"].merge("name" => "test2")
ActiveRecord::Base.connection_handler.establish_connection(conf)
Task.connection_specification_name = "test2"
refute Task.connected?
@@ -183,39 +280,109 @@ class QueryCacheTest < ActiveRecord::TestCase
post = Post.first
Post.transaction do
- post.update_attributes(title: 'rollback')
- assert_equal 1, Post.where(title: 'rollback').to_a.count
+ post.update_attributes(title: "rollback")
+ assert_equal 1, Post.where(title: "rollback").to_a.count
raise ActiveRecord::Rollback
end
- assert_equal 0, Post.where(title: 'rollback').to_a.count
+ assert_equal 0, Post.where(title: "rollback").to_a.count
ActiveRecord::Base.connection.uncached do
- assert_equal 0, Post.where(title: 'rollback').to_a.count
+ assert_equal 0, Post.where(title: "rollback").to_a.count
end
begin
Post.transaction do
- post.update_attributes(title: 'rollback')
- assert_equal 1, Post.where(title: 'rollback').to_a.count
- raise 'broken'
+ post.update_attributes(title: "rollback")
+ assert_equal 1, Post.where(title: "rollback").to_a.count
+ raise "broken"
end
rescue Exception
end
- assert_equal 0, Post.where(title: 'rollback').to_a.count
+ assert_equal 0, Post.where(title: "rollback").to_a.count
ActiveRecord::Base.connection.uncached do
- assert_equal 0, Post.where(title: 'rollback').to_a.count
+ assert_equal 0, Post.where(title: "rollback").to_a.count
+ end
+ end
+
+ def test_query_cached_even_when_types_are_reset
+ Task.cache do
+ # Warm the cache
+ Task.find(1)
+
+ Task.connection.type_map.clear
+
+ # Preload the type cache again (so we don't have those queries issued during our assertions)
+ Task.connection.send(:initialize_type_map, Task.connection.type_map)
+
+ # Clear places where type information is cached
+ Task.reset_column_information
+ Task.initialize_find_by_cache
+
+ assert_queries(0) do
+ Task.find(1)
+ end
end
end
+ def test_query_cache_does_not_establish_connection_if_unconnected
+ ActiveRecord::Base.clear_active_connections!
+ refute ActiveRecord::Base.connection_handler.active_connections? # sanity check
+
+ middleware {
+ refute ActiveRecord::Base.connection_handler.active_connections?, "QueryCache forced ActiveRecord::Base to establish a connection in setup"
+ }.call({})
+
+ refute ActiveRecord::Base.connection_handler.active_connections?, "QueryCache forced ActiveRecord::Base to establish a connection in cleanup"
+ end
+
+ def test_query_cache_is_enabled_on_connections_established_after_middleware_runs
+ ActiveRecord::Base.clear_active_connections!
+ refute ActiveRecord::Base.connection_handler.active_connections? # sanity check
+
+ middleware {
+ assert ActiveRecord::Base.connection.query_cache_enabled, "QueryCache did not get lazily enabled"
+ }.call({})
+ end
+
+ def test_query_caching_is_local_to_the_current_thread
+ ActiveRecord::Base.clear_active_connections!
+
+ middleware {
+ assert ActiveRecord::Base.connection_pool.query_cache_enabled
+ assert ActiveRecord::Base.connection.query_cache_enabled
+
+ Thread.new {
+ refute ActiveRecord::Base.connection_pool.query_cache_enabled
+ refute ActiveRecord::Base.connection.query_cache_enabled
+ }.join
+ }.call({})
+ end
+
private
def middleware(&app)
executor = Class.new(ActiveSupport::Executor)
ActiveRecord::QueryCache.install_executor_hooks executor
lambda { |env| executor.wrap { app.call(env) } }
end
+
+ def assert_cache(state, connection = ActiveRecord::Base.connection)
+ case state
+ when :off
+ assert !connection.query_cache_enabled, "cache should be off"
+ assert connection.query_cache.empty?, "cache should be empty"
+ when :clean
+ assert connection.query_cache_enabled, "cache should be on"
+ assert connection.query_cache.empty?, "cache should be empty"
+ when :dirty
+ assert connection.query_cache_enabled, "cache should be on"
+ assert !connection.query_cache.empty?, "cache should be dirty"
+ else
+ raise "unknown state"
+ end
+ end
end
class QueryCacheExpiryTest < ActiveRecord::TestCase
diff --git a/activerecord/test/cases/quoting_test.rb b/activerecord/test/cases/quoting_test.rb
index c01c82f4f5..5ff5e3c735 100644
--- a/activerecord/test/cases/quoting_test.rb
+++ b/activerecord/test/cases/quoting_test.rb
@@ -16,20 +16,20 @@ module ActiveRecord
end
def test_quote_column_name
- assert_equal "foo", @quoter.quote_column_name('foo')
+ assert_equal "foo", @quoter.quote_column_name("foo")
end
def test_quote_table_name
- assert_equal "foo", @quoter.quote_table_name('foo')
+ assert_equal "foo", @quoter.quote_table_name("foo")
end
def test_quote_table_name_calls_quote_column_name
@quoter.extend(Module.new {
def quote_column_name(string)
- 'lol'
+ "lol"
end
})
- assert_equal 'lol', @quoter.quote_table_name('foo')
+ assert_equal "lol", @quoter.quote_table_name("foo")
end
def test_quote_string
@@ -82,72 +82,88 @@ module ActiveRecord
end
def test_quote_with_quoted_id
- assert_equal 1, @quoter.quote(Struct.new(:quoted_id).new(1), nil)
+ assert_equal 1, @quoter.quote(Struct.new(:quoted_id).new(1))
end
def test_quote_nil
- assert_equal 'NULL', @quoter.quote(nil, nil)
+ assert_equal "NULL", @quoter.quote(nil)
end
def test_quote_true
- assert_equal @quoter.quoted_true, @quoter.quote(true, nil)
+ assert_equal @quoter.quoted_true, @quoter.quote(true)
end
def test_quote_false
- assert_equal @quoter.quoted_false, @quoter.quote(false, nil)
+ assert_equal @quoter.quoted_false, @quoter.quote(false)
end
def test_quote_float
float = 1.2
- assert_equal float.to_s, @quoter.quote(float, nil)
+ assert_equal float.to_s, @quoter.quote(float)
end
def test_quote_integer
integer = 1
- assert_equal integer.to_s, @quoter.quote(integer, nil)
+ assert_equal integer.to_s, @quoter.quote(integer)
end
def test_quote_bignum
bignum = 1 << 100
- assert_equal bignum.to_s, @quoter.quote(bignum, nil)
+ assert_equal bignum.to_s, @quoter.quote(bignum)
end
def test_quote_bigdecimal
bigdec = BigDecimal.new((1 << 100).to_s)
- assert_equal bigdec.to_s('F'), @quoter.quote(bigdec, nil)
+ assert_equal bigdec.to_s("F"), @quoter.quote(bigdec)
end
def test_dates_and_times
- @quoter.extend(Module.new { def quoted_date(value) 'lol' end })
- assert_equal "'lol'", @quoter.quote(Date.today, nil)
- assert_equal "'lol'", @quoter.quote(Time.now, nil)
- assert_equal "'lol'", @quoter.quote(DateTime.now, nil)
+ @quoter.extend(Module.new { def quoted_date(value) "lol" end })
+ assert_equal "'lol'", @quoter.quote(Date.today)
+ assert_equal "'lol'", @quoter.quote(Time.now)
+ assert_equal "'lol'", @quoter.quote(DateTime.now)
+ end
+
+ def test_quoting_classes
+ assert_equal "'Object'", @quoter.quote(Object)
end
def test_crazy_object
crazy = Object.new
e = assert_raises(TypeError) do
- @quoter.quote(crazy, nil)
+ @quoter.quote(crazy)
end
assert_equal "can't quote Object", e.message
end
def test_quote_string_no_column
- assert_equal "'lo\\\\l'", @quoter.quote('lo\l', nil)
+ assert_equal "'lo\\\\l'", @quoter.quote('lo\l')
end
def test_quote_as_mb_chars_no_column
string = ActiveSupport::Multibyte::Chars.new('lo\l')
- assert_equal "'lo\\\\l'", @quoter.quote(string, nil)
- end
-
- def test_string_with_crazy_column
- assert_equal "'lo\\\\l'", @quoter.quote('lo\l')
+ assert_equal "'lo\\\\l'", @quoter.quote(string)
end
def test_quote_duration
assert_equal "1800", @quoter.quote(30.minutes)
end
end
+
+ class QuoteBooleanTest < ActiveRecord::TestCase
+ def setup
+ @connection = ActiveRecord::Base.connection
+ end
+
+ def test_quote_returns_frozen_string
+ assert_predicate @connection.quote(true), :frozen?
+ assert_predicate @connection.quote(false), :frozen?
+ end
+
+ def test_type_cast_returns_frozen_value
+ assert_predicate @connection.type_cast(true), :frozen?
+ assert_predicate @connection.type_cast(false), :frozen?
+ end
+ end
end
end
diff --git a/activerecord/test/cases/readonly_test.rb b/activerecord/test/cases/readonly_test.rb
index 5f6eb41240..a93061b516 100644
--- a/activerecord/test/cases/readonly_test.rb
+++ b/activerecord/test/cases/readonly_test.rb
@@ -1,13 +1,13 @@
require "cases/helper"
-require 'models/author'
-require 'models/post'
-require 'models/comment'
-require 'models/developer'
-require 'models/computer'
-require 'models/project'
-require 'models/reader'
-require 'models/person'
-require 'models/ship'
+require "models/author"
+require "models/post"
+require "models/comment"
+require "models/developer"
+require "models/computer"
+require "models/project"
+require "models/reader"
+require "models/person"
+require "models/ship"
class ReadOnlyTest < ActiveRecord::TestCase
fixtures :authors, :posts, :comments, :developers, :projects, :developers_projects, :people, :readers
@@ -20,9 +20,9 @@ class ReadOnlyTest < ActiveRecord::TestCase
assert dev.readonly?
assert_nothing_raised do
- dev.name = 'Luscious forbidden fruit.'
+ dev.name = "Luscious forbidden fruit."
assert !dev.save
- dev.name = 'Forbidden.'
+ dev.name = "Forbidden."
end
e = assert_raise(ActiveRecord::ReadOnlyRecord) { dev.save }
@@ -35,7 +35,6 @@ class ReadOnlyTest < ActiveRecord::TestCase
assert_equal "Developer is marked as readonly", e.message
end
-
def test_find_with_readonly_option
Developer.all.each { |d| assert !d.readonly? }
Developer.readonly(false).each { |d| assert !d.readonly? }
@@ -44,11 +43,11 @@ class ReadOnlyTest < ActiveRecord::TestCase
end
def test_find_with_joins_option_does_not_imply_readonly
- Developer.joins(' ').each { |d| assert_not d.readonly? }
- Developer.joins(' ').readonly(true).each { |d| assert d.readonly? }
+ Developer.joins(" ").each { |d| assert_not d.readonly? }
+ Developer.joins(" ").readonly(true).each { |d| assert d.readonly? }
- Developer.joins(', projects').each { |d| assert_not d.readonly? }
- Developer.joins(', projects').readonly(true).each { |d| assert d.readonly? }
+ Developer.joins(", projects").each { |d| assert_not d.readonly? }
+ Developer.joins(", projects").readonly(true).each { |d| assert d.readonly? }
end
def test_has_many_find_readonly
@@ -77,13 +76,13 @@ class ReadOnlyTest < ActiveRecord::TestCase
end
def test_readonly_scoping
- Post.where('1=1').scoping do
+ Post.where("1=1").scoping do
assert !Post.find(1).readonly?
assert Post.readonly(true).find(1).readonly?
assert !Post.readonly(false).find(1).readonly?
end
- Post.joins(' ').scoping do
+ Post.joins(" ").scoping do
assert !Post.find(1).readonly?
assert Post.readonly.find(1).readonly?
assert !Post.readonly(false).find(1).readonly?
@@ -92,7 +91,7 @@ class ReadOnlyTest < ActiveRecord::TestCase
# Oracle barfs on this because the join includes unqualified and
# conflicting column names
unless current_adapter?(:OracleAdapter)
- Post.joins(', developers').scoping do
+ Post.joins(", developers").scoping do
assert_not Post.find(1).readonly?
assert Post.readonly.find(1).readonly?
assert !Post.readonly(false).find(1).readonly?
diff --git a/activerecord/test/cases/reaper_test.rb b/activerecord/test/cases/reaper_test.rb
index cccfc6774e..249878b67d 100644
--- a/activerecord/test/cases/reaper_test.rb
+++ b/activerecord/test/cases/reaper_test.rb
@@ -60,7 +60,7 @@ module ActiveRecord
def test_connection_pool_starts_reaper
spec = ActiveRecord::Base.connection_pool.spec.dup
- spec.config[:reaping_frequency] = '0.0001'
+ spec.config[:reaping_frequency] = "0.0001"
pool = ConnectionPool.new spec
diff --git a/activerecord/test/cases/reflection_test.rb b/activerecord/test/cases/reflection_test.rb
index 710c86b151..0ef51272b9 100644
--- a/activerecord/test/cases/reflection_test.rb
+++ b/activerecord/test/cases/reflection_test.rb
@@ -1,30 +1,30 @@
require "cases/helper"
-require 'models/topic'
-require 'models/customer'
-require 'models/company'
-require 'models/company_in_module'
-require 'models/ship'
-require 'models/pirate'
-require 'models/price_estimate'
-require 'models/essay'
-require 'models/author'
-require 'models/organization'
-require 'models/post'
-require 'models/tagging'
-require 'models/category'
-require 'models/book'
-require 'models/subscriber'
-require 'models/subscription'
-require 'models/tag'
-require 'models/sponsor'
-require 'models/edge'
-require 'models/hotel'
-require 'models/chef'
-require 'models/department'
-require 'models/cake_designer'
-require 'models/drink_designer'
-require 'models/mocktail_designer'
-require 'models/recipe'
+require "models/topic"
+require "models/customer"
+require "models/company"
+require "models/company_in_module"
+require "models/ship"
+require "models/pirate"
+require "models/price_estimate"
+require "models/essay"
+require "models/author"
+require "models/organization"
+require "models/post"
+require "models/tagging"
+require "models/category"
+require "models/book"
+require "models/subscriber"
+require "models/subscription"
+require "models/tag"
+require "models/sponsor"
+require "models/edge"
+require "models/hotel"
+require "models/chef"
+require "models/department"
+require "models/cake_designer"
+require "models/drink_designer"
+require "models/mocktail_designer"
+require "models/recipe"
class ReflectionTest < ActiveRecord::TestCase
include ActiveRecord::Reflection
@@ -86,8 +86,8 @@ class ReflectionTest < ActiveRecord::TestCase
column = @first.column_for_attribute("attribute_that_doesnt_exist")
assert_instance_of ActiveRecord::ConnectionAdapters::NullColumn, column
assert_equal "attribute_that_doesnt_exist", column.name
- assert_equal nil, column.sql_type
- assert_equal nil, column.type
+ assert_nil column.sql_type
+ assert_nil column.type
end
def test_non_existent_types_are_identity_types
@@ -100,7 +100,13 @@ class ReflectionTest < ActiveRecord::TestCase
end
def test_reflection_klass_for_nested_class_name
- reflection = ActiveRecord::Reflection.create(:has_many, nil, nil, { :class_name => 'MyApplication::Business::Company' }, ActiveRecord::Base)
+ reflection = ActiveRecord::Reflection.create(
+ :has_many,
+ nil,
+ nil,
+ { class_name: "MyApplication::Business::Company" },
+ Customer
+ )
assert_nothing_raised do
assert_equal MyApplication::Business::Company, reflection.klass
end
@@ -108,28 +114,28 @@ class ReflectionTest < ActiveRecord::TestCase
def test_irregular_reflection_class_name
ActiveSupport::Inflector.inflections do |inflect|
- inflect.irregular 'plural_irregular', 'plurales_irregulares'
+ inflect.irregular "plural_irregular", "plurales_irregulares"
end
- reflection = ActiveRecord::Reflection.create(:has_many, 'plurales_irregulares', nil, {}, ActiveRecord::Base)
- assert_equal 'PluralIrregular', reflection.class_name
+ reflection = ActiveRecord::Reflection.create(:has_many, "plurales_irregulares", nil, {}, ActiveRecord::Base)
+ assert_equal "PluralIrregular", reflection.class_name
end
def test_aggregation_reflection
reflection_for_address = AggregateReflection.new(
- :address, nil, { :mapping => [ %w(address_street street), %w(address_city city), %w(address_country country) ] }, Customer
+ :address, nil, { mapping: [ %w(address_street street), %w(address_city city), %w(address_country country) ] }, Customer
)
reflection_for_balance = AggregateReflection.new(
- :balance, nil, { :class_name => "Money", :mapping => %w(balance amount) }, Customer
+ :balance, nil, { class_name: "Money", mapping: %w(balance amount) }, Customer
)
reflection_for_gps_location = AggregateReflection.new(
- :gps_location, nil, { }, Customer
+ :gps_location, nil, {}, Customer
)
- assert Customer.reflect_on_all_aggregations.include?(reflection_for_gps_location)
- assert Customer.reflect_on_all_aggregations.include?(reflection_for_balance)
- assert Customer.reflect_on_all_aggregations.include?(reflection_for_address)
+ assert_includes Customer.reflect_on_all_aggregations, reflection_for_gps_location
+ assert_includes Customer.reflect_on_all_aggregations, reflection_for_balance
+ assert_includes Customer.reflect_on_all_aggregations, reflection_for_address
assert_equal reflection_for_address, Customer.reflect_on_aggregation(:address)
@@ -148,31 +154,31 @@ class ReflectionTest < ActiveRecord::TestCase
end
def test_has_many_reflection
- reflection_for_clients = ActiveRecord::Reflection.create(:has_many, :clients, nil, { :order => "id", :dependent => :destroy }, Firm)
+ reflection_for_clients = ActiveRecord::Reflection.create(:has_many, :clients, nil, { order: "id", dependent: :destroy }, Firm)
assert_equal reflection_for_clients, Firm.reflect_on_association(:clients)
assert_equal Client, Firm.reflect_on_association(:clients).klass
- assert_equal 'companies', Firm.reflect_on_association(:clients).table_name
+ assert_equal "companies", Firm.reflect_on_association(:clients).table_name
assert_equal Client, Firm.reflect_on_association(:clients_of_firm).klass
- assert_equal 'companies', Firm.reflect_on_association(:clients_of_firm).table_name
+ assert_equal "companies", Firm.reflect_on_association(:clients_of_firm).table_name
end
def test_has_one_reflection
- reflection_for_account = ActiveRecord::Reflection.create(:has_one, :account, nil, { :foreign_key => "firm_id", :dependent => :destroy }, Firm)
+ reflection_for_account = ActiveRecord::Reflection.create(:has_one, :account, nil, { foreign_key: "firm_id", dependent: :destroy }, Firm)
assert_equal reflection_for_account, Firm.reflect_on_association(:account)
assert_equal Account, Firm.reflect_on_association(:account).klass
- assert_equal 'accounts', Firm.reflect_on_association(:account).table_name
+ assert_equal "accounts", Firm.reflect_on_association(:account).table_name
end
def test_belongs_to_inferred_foreign_key_from_assoc_name
Company.belongs_to :foo
assert_equal "foo_id", Company.reflect_on_association(:foo).foreign_key
- Company.belongs_to :bar, :class_name => "Xyzzy"
+ Company.belongs_to :bar, class_name: "Xyzzy"
assert_equal "bar_id", Company.reflect_on_association(:bar).foreign_key
- Company.belongs_to :baz, :class_name => "Xyzzy", :foreign_key => "xyzzy_id"
+ Company.belongs_to :baz, class_name: "Xyzzy", foreign_key: "xyzzy_id"
assert_equal "xyzzy_id", Company.reflect_on_association(:baz).foreign_key
end
@@ -181,45 +187,45 @@ class ReflectionTest < ActiveRecord::TestCase
assert_reflection MyApplication::Business::Firm,
:clients_of_firm,
- :klass => MyApplication::Business::Client,
- :class_name => 'Client',
- :table_name => 'companies'
+ klass: MyApplication::Business::Client,
+ class_name: "Client",
+ table_name: "companies"
assert_reflection MyApplication::Billing::Account,
:firm,
- :klass => MyApplication::Business::Firm,
- :class_name => 'MyApplication::Business::Firm',
- :table_name => 'companies'
+ klass: MyApplication::Business::Firm,
+ class_name: "MyApplication::Business::Firm",
+ table_name: "companies"
assert_reflection MyApplication::Billing::Account,
:qualified_billing_firm,
- :klass => MyApplication::Billing::Firm,
- :class_name => 'MyApplication::Billing::Firm',
- :table_name => 'companies'
+ klass: MyApplication::Billing::Firm,
+ class_name: "MyApplication::Billing::Firm",
+ table_name: "companies"
assert_reflection MyApplication::Billing::Account,
:unqualified_billing_firm,
- :klass => MyApplication::Billing::Firm,
- :class_name => 'Firm',
- :table_name => 'companies'
+ klass: MyApplication::Billing::Firm,
+ class_name: "Firm",
+ table_name: "companies"
assert_reflection MyApplication::Billing::Account,
:nested_qualified_billing_firm,
- :klass => MyApplication::Billing::Nested::Firm,
- :class_name => 'MyApplication::Billing::Nested::Firm',
- :table_name => 'companies'
+ klass: MyApplication::Billing::Nested::Firm,
+ class_name: "MyApplication::Billing::Nested::Firm",
+ table_name: "companies"
assert_reflection MyApplication::Billing::Account,
:nested_unqualified_billing_firm,
- :klass => MyApplication::Billing::Nested::Firm,
- :class_name => 'Nested::Firm',
- :table_name => 'companies'
+ klass: MyApplication::Billing::Nested::Firm,
+ class_name: "Nested::Firm",
+ table_name: "companies"
ensure
ActiveRecord::Base.store_full_sti_class = true
end
def test_reflection_should_not_raise_error_when_compared_to_other_object
- assert_not_equal Object.new, Firm._reflections['clients']
+ assert_not_equal Object.new, Firm._reflections["clients"]
end
def test_reflections_should_return_keys_as_strings
@@ -227,7 +233,7 @@ class ReflectionTest < ActiveRecord::TestCase
end
def test_has_and_belongs_to_many_reflection
- assert_equal :has_and_belongs_to_many, Category.reflections['posts'].macro
+ assert_equal :has_and_belongs_to_many, Category.reflections["posts"].macro
assert_equal :posts, Category.reflect_on_all_associations(:has_and_belongs_to_many).first.name
end
@@ -366,21 +372,21 @@ class ReflectionTest < ActiveRecord::TestCase
end
def test_always_validate_association_if_explicit
- assert ActiveRecord::Reflection.create(:has_one, :client, nil, { :validate => true }, Firm).validate?
- assert ActiveRecord::Reflection.create(:belongs_to, :client, nil, { :validate => true }, Firm).validate?
- assert ActiveRecord::Reflection.create(:has_many, :clients, nil, { :validate => true }, Firm).validate?
+ assert ActiveRecord::Reflection.create(:has_one, :client, nil, { validate: true }, Firm).validate?
+ assert ActiveRecord::Reflection.create(:belongs_to, :client, nil, { validate: true }, Firm).validate?
+ assert ActiveRecord::Reflection.create(:has_many, :clients, nil, { validate: true }, Firm).validate?
end
def test_validate_association_if_autosave
- assert ActiveRecord::Reflection.create(:has_one, :client, nil, { :autosave => true }, Firm).validate?
- assert ActiveRecord::Reflection.create(:belongs_to, :client, nil, { :autosave => true }, Firm).validate?
- assert ActiveRecord::Reflection.create(:has_many, :clients, nil, { :autosave => true }, Firm).validate?
+ assert ActiveRecord::Reflection.create(:has_one, :client, nil, { autosave: true }, Firm).validate?
+ assert ActiveRecord::Reflection.create(:belongs_to, :client, nil, { autosave: true }, Firm).validate?
+ assert ActiveRecord::Reflection.create(:has_many, :clients, nil, { autosave: true }, Firm).validate?
end
def test_never_validate_association_if_explicit
- assert !ActiveRecord::Reflection.create(:has_one, :client, nil, { :autosave => true, :validate => false }, Firm).validate?
- assert !ActiveRecord::Reflection.create(:belongs_to, :client, nil, { :autosave => true, :validate => false }, Firm).validate?
- assert !ActiveRecord::Reflection.create(:has_many, :clients, nil, { :autosave => true, :validate => false }, Firm).validate?
+ assert !ActiveRecord::Reflection.create(:has_one, :client, nil, { autosave: true, validate: false }, Firm).validate?
+ assert !ActiveRecord::Reflection.create(:belongs_to, :client, nil, { autosave: true, validate: false }, Firm).validate?
+ assert !ActiveRecord::Reflection.create(:has_many, :clients, nil, { autosave: true, validate: false }, Firm).validate?
end
def test_foreign_key
@@ -399,62 +405,62 @@ class ReflectionTest < ActiveRecord::TestCase
end
def test_join_table
- category = Struct.new(:table_name, :pluralize_table_names).new('categories', true)
- product = Struct.new(:table_name, :pluralize_table_names).new('products', true)
+ category = Struct.new(:table_name, :pluralize_table_names).new("categories", true)
+ product = Struct.new(:table_name, :pluralize_table_names).new("products", true)
reflection = ActiveRecord::Reflection.create(:has_many, :categories, nil, {}, product)
reflection.stub(:klass, category) do
- assert_equal 'categories_products', reflection.join_table
+ assert_equal "categories_products", reflection.join_table
end
reflection = ActiveRecord::Reflection.create(:has_many, :products, nil, {}, category)
reflection.stub(:klass, product) do
- assert_equal 'categories_products', reflection.join_table
+ assert_equal "categories_products", reflection.join_table
end
end
def test_join_table_with_common_prefix
- category = Struct.new(:table_name, :pluralize_table_names).new('catalog_categories', true)
- product = Struct.new(:table_name, :pluralize_table_names).new('catalog_products', true)
+ category = Struct.new(:table_name, :pluralize_table_names).new("catalog_categories", true)
+ product = Struct.new(:table_name, :pluralize_table_names).new("catalog_products", true)
reflection = ActiveRecord::Reflection.create(:has_many, :categories, nil, {}, product)
reflection.stub(:klass, category) do
- assert_equal 'catalog_categories_products', reflection.join_table
+ assert_equal "catalog_categories_products", reflection.join_table
end
reflection = ActiveRecord::Reflection.create(:has_many, :products, nil, {}, category)
reflection.stub(:klass, product) do
- assert_equal 'catalog_categories_products', reflection.join_table
+ assert_equal "catalog_categories_products", reflection.join_table
end
end
def test_join_table_with_different_prefix
- category = Struct.new(:table_name, :pluralize_table_names).new('catalog_categories', true)
- page = Struct.new(:table_name, :pluralize_table_names).new('content_pages', true)
+ category = Struct.new(:table_name, :pluralize_table_names).new("catalog_categories", true)
+ page = Struct.new(:table_name, :pluralize_table_names).new("content_pages", true)
reflection = ActiveRecord::Reflection.create(:has_many, :categories, nil, {}, page)
reflection.stub(:klass, category) do
- assert_equal 'catalog_categories_content_pages', reflection.join_table
+ assert_equal "catalog_categories_content_pages", reflection.join_table
end
reflection = ActiveRecord::Reflection.create(:has_many, :pages, nil, {}, category)
reflection.stub(:klass, page) do
- assert_equal 'catalog_categories_content_pages', reflection.join_table
+ assert_equal "catalog_categories_content_pages", reflection.join_table
end
end
def test_join_table_can_be_overridden
- category = Struct.new(:table_name, :pluralize_table_names).new('categories', true)
- product = Struct.new(:table_name, :pluralize_table_names).new('products', true)
+ category = Struct.new(:table_name, :pluralize_table_names).new("categories", true)
+ product = Struct.new(:table_name, :pluralize_table_names).new("products", true)
- reflection = ActiveRecord::Reflection.create(:has_many, :categories, nil, { :join_table => 'product_categories' }, product)
+ reflection = ActiveRecord::Reflection.create(:has_many, :categories, nil, { join_table: "product_categories" }, product)
reflection.stub(:klass, category) do
- assert_equal 'product_categories', reflection.join_table
+ assert_equal "product_categories", reflection.join_table
end
- reflection = ActiveRecord::Reflection.create(:has_many, :products, nil, { :join_table => 'product_categories' }, category)
+ reflection = ActiveRecord::Reflection.create(:has_many, :products, nil, { join_table: "product_categories" }, category)
reflection.stub(:klass, product) do
- assert_equal 'product_categories', reflection.join_table
+ assert_equal "product_categories", reflection.join_table
end
end
@@ -474,7 +480,7 @@ class ReflectionTest < ActiveRecord::TestCase
department.chefs.create!
assert_nothing_raised do
- assert_equal department.chefs, Hotel.includes(['departments' => 'chefs']).first.chefs
+ assert_equal department.chefs, Hotel.includes(["departments" => "chefs"]).first.chefs
end
end
diff --git a/activerecord/test/cases/relation/delegation_test.rb b/activerecord/test/cases/relation/delegation_test.rb
index f0e07e0731..d2382b9bb2 100644
--- a/activerecord/test/cases/relation/delegation_test.rb
+++ b/activerecord/test/cases/relation/delegation_test.rb
@@ -1,6 +1,6 @@
-require 'cases/helper'
-require 'models/post'
-require 'models/comment'
+require "cases/helper"
+require "models/post"
+require "models/comment"
module ActiveRecord
class DelegationTest < ActiveRecord::TestCase
@@ -18,7 +18,7 @@ module ActiveRecord
target.public_send(method, 1)
end
elsif method_arity == 1
- target.public_send(method, 1)
+ target.public_send(method, 1)
else
raise NotImplementedError
end
diff --git a/activerecord/test/cases/relation/merging_test.rb b/activerecord/test/cases/relation/merging_test.rb
index 60a806c05a..278dac8171 100644
--- a/activerecord/test/cases/relation/merging_test.rb
+++ b/activerecord/test/cases/relation/merging_test.rb
@@ -1,20 +1,20 @@
-require 'cases/helper'
-require 'models/author'
-require 'models/comment'
-require 'models/developer'
-require 'models/computer'
-require 'models/post'
-require 'models/project'
-require 'models/rating'
+require "cases/helper"
+require "models/author"
+require "models/comment"
+require "models/developer"
+require "models/computer"
+require "models/post"
+require "models/project"
+require "models/rating"
class RelationMergingTest < ActiveRecord::TestCase
fixtures :developers, :comments, :authors, :posts
def test_relation_merging
- devs = Developer.where("salary >= 80000").merge(Developer.limit(2)).merge(Developer.order('id ASC').where("id < 3"))
+ devs = Developer.where("salary >= 80000").merge(Developer.limit(2)).merge(Developer.order("id ASC").where("id < 3"))
assert_equal [developers(:david), developers(:jamis)], devs.to_a
- dev_with_count = Developer.limit(1).merge(Developer.order('id DESC')).merge(Developer.select('developers.*'))
+ dev_with_count = Developer.limit(1).merge(Developer.order("id DESC")).merge(Developer.select("developers.*"))
assert_equal [developers(:poor_jamis)], dev_with_count.to_a
end
@@ -34,10 +34,10 @@ class RelationMergingTest < ActiveRecord::TestCase
def test_relation_merging_with_arel_equalities_keeps_last_equality_with_non_attribute_left_hand
salary_attr = Developer.arel_table[:salary]
devs = Developer.where(
- Arel::Nodes::NamedFunction.new('abs', [salary_attr]).eq(80000)
+ Arel::Nodes::NamedFunction.new("abs", [salary_attr]).eq(80000)
).merge(
Developer.where(
- Arel::Nodes::NamedFunction.new('abs', [salary_attr]).eq(9000)
+ Arel::Nodes::NamedFunction.new("abs", [salary_attr]).eq(9000)
)
)
assert_equal [developers(:poor_jamis)], devs.to_a
@@ -45,8 +45,8 @@ class RelationMergingTest < ActiveRecord::TestCase
def test_relation_merging_with_eager_load
relations = []
- relations << Post.order('comments.id DESC').merge(Post.eager_load(:last_comment)).merge(Post.all)
- relations << Post.eager_load(:last_comment).merge(Post.order('comments.id DESC')).merge(Post.all)
+ relations << Post.order("comments.id DESC").merge(Post.eager_load(:last_comment)).merge(Post.all)
+ relations << Post.eager_load(:last_comment).merge(Post.order("comments.id DESC")).merge(Post.all)
relations.each do |posts|
post = posts.find { |p| p.id == 1 }
@@ -66,14 +66,14 @@ class RelationMergingTest < ActiveRecord::TestCase
end
def test_relation_merging_with_joins
- comments = Comment.joins(:post).where(:body => 'Thank you for the welcome').merge(Post.where(:body => 'Such a lovely day'))
+ comments = Comment.joins(:post).where(body: "Thank you for the welcome").merge(Post.where(body: "Such a lovely day"))
assert_equal 1, comments.count
end
def test_relation_merging_with_association
assert_queries(2) do # one for loading post, and another one merged query
- post = Post.where(:body => 'Such a lovely day').first
- comments = Comment.where(:body => 'Thank you for the welcome').merge(post.comments)
+ post = Post.where(body: "Such a lovely day").first
+ comments = Comment.where(body: "Thank you for the welcome").merge(post.comments)
assert_equal 1, comments.count
end
end
@@ -86,9 +86,9 @@ class RelationMergingTest < ActiveRecord::TestCase
merged = left.merge(right)
assert_equal expected, merged.bound_attributes
- assert !merged.to_sql.include?("omg")
- assert merged.to_sql.include?("wtf")
- assert merged.to_sql.include?("bbq")
+ assert_not_includes merged.to_sql, "omg"
+ assert_includes merged.to_sql, "wtf"
+ assert_includes merged.to_sql, "bbq"
end
def test_merging_reorders_bind_params
diff --git a/activerecord/test/cases/relation/mutation_test.rb b/activerecord/test/cases/relation/mutation_test.rb
index ffb2da7a26..2cbbc775ce 100644
--- a/activerecord/test/cases/relation/mutation_test.rb
+++ b/activerecord/test/cases/relation/mutation_test.rb
@@ -1,8 +1,8 @@
-require 'cases/helper'
-require 'models/post'
+require "cases/helper"
+require "models/post"
module ActiveRecord
- class RelationMutationTest < ActiveSupport::TestCase
+ class RelationMutationTest < ActiveRecord::TestCase
class FakeKlass < Struct.new(:table_name, :name)
extend ActiveRecord::Delegation::DelegateCache
inherited self
@@ -33,7 +33,7 @@ module ActiveRecord
end
def relation
- @relation ||= Relation.new FakeKlass.new('posts'), Post.arel_table, Post.predicate_builder
+ @relation ||= Relation.new FakeKlass.new("posts"), Post.arel_table, Post.predicate_builder
end
(Relation::MULTI_VALUE_METHODS - [:references, :extending, :order, :unscope, :select, :left_joins]).each do |method|
@@ -44,16 +44,16 @@ module ActiveRecord
end
test "#_select!" do
- assert relation.public_send("_select!", :foo).equal?(relation)
- assert_equal [:foo], relation.public_send("select_values")
+ assert relation._select!(:foo).equal?(relation)
+ assert_equal [:foo], relation.select_values
end
- test '#order!' do
- assert relation.order!('name ASC').equal?(relation)
- assert_equal ['name ASC'], relation.order_values
+ test "#order!" do
+ assert relation.order!("name ASC").equal?(relation)
+ assert_equal ["name ASC"], relation.order_values
end
- test '#order! with symbol prepends the table name' do
+ test "#order! with symbol prepends the table name" do
assert relation.order!(:name).equal?(relation)
node = relation.order_values.first
assert node.ascending?
@@ -61,7 +61,7 @@ module ActiveRecord
assert_equal "posts", node.expr.relation.name
end
- test '#order! on non-string does not attempt regexp match for references' do
+ test "#order! on non-string does not attempt regexp match for references" do
obj = Object.new
assert_not_called(obj, :=~) do
assert relation.order!(obj)
@@ -69,12 +69,12 @@ module ActiveRecord
end
end
- test '#references!' do
+ test "#references!" do
assert relation.references!(:foo).equal?(relation)
- assert relation.references_values.include?('foo')
+ assert_includes relation.references_values, "foo"
end
- test 'extending!' do
+ test "extending!" do
mod, mod2 = Module.new, Module.new
assert relation.extending!(mod).equal?(relation)
@@ -85,37 +85,37 @@ module ActiveRecord
assert_equal [mod, mod2], relation.extending_values
end
- test 'extending! with empty args' do
+ test "extending! with empty args" do
relation.extending!
assert_equal [], relation.extending_values
end
- (Relation::SINGLE_VALUE_METHODS - [:lock, :reordering, :reverse_order, :create_with, :uniq]).each do |method|
+ (Relation::SINGLE_VALUE_METHODS - [:lock, :reordering, :reverse_order, :create_with]).each do |method|
test "##{method}!" do
assert relation.public_send("#{method}!", :foo).equal?(relation)
assert_equal :foo, relation.public_send("#{method}_value")
end
end
- test '#from!' do
- assert relation.from!('foo').equal?(relation)
- assert_equal 'foo', relation.from_clause.value
+ test "#from!" do
+ assert relation.from!("foo").equal?(relation)
+ assert_equal "foo", relation.from_clause.value
end
- test '#lock!' do
- assert relation.lock!('foo').equal?(relation)
- assert_equal 'foo', relation.lock_value
+ test "#lock!" do
+ assert relation.lock!("foo").equal?(relation)
+ assert_equal "foo", relation.lock_value
end
- test '#reorder!' do
- @relation = self.relation.order('foo')
+ test "#reorder!" do
+ @relation = self.relation.order("foo")
- assert relation.reorder!('bar').equal?(relation)
- assert_equal ['bar'], relation.order_values
+ assert relation.reorder!("bar").equal?(relation)
+ assert_equal ["bar"], relation.order_values
assert relation.reordering_value
end
- test '#reorder! with symbol prepends the table name' do
+ test "#reorder! with symbol prepends the table name" do
assert relation.reorder!(:name).equal?(relation)
node = relation.order_values.first
@@ -124,60 +124,43 @@ module ActiveRecord
assert_equal "posts", node.expr.relation.name
end
- test 'reverse_order!' do
- @relation = Post.order('title ASC, comments_count DESC')
+ test "reverse_order!" do
+ @relation = Post.order("title ASC, comments_count DESC")
relation.reverse_order!
- assert_equal 'title DESC', relation.order_values.first
- assert_equal 'comments_count ASC', relation.order_values.last
-
+ assert_equal "title DESC", relation.order_values.first
+ assert_equal "comments_count ASC", relation.order_values.last
relation.reverse_order!
- assert_equal 'title ASC', relation.order_values.first
- assert_equal 'comments_count DESC', relation.order_values.last
+ assert_equal "title ASC", relation.order_values.first
+ assert_equal "comments_count DESC", relation.order_values.last
end
- test 'create_with!' do
- assert relation.create_with!(foo: 'bar').equal?(relation)
- assert_equal({foo: 'bar'}, relation.create_with_value)
+ test "create_with!" do
+ assert relation.create_with!(foo: "bar").equal?(relation)
+ assert_equal({ foo: "bar" }, relation.create_with_value)
end
- test 'test_merge!' do
+ test "test_merge!" do
assert relation.merge!(select: :foo).equal?(relation)
assert_equal [:foo], relation.select_values
end
- test 'merge with a proc' do
+ test "merge with a proc" do
assert_equal [:foo], relation.merge(-> { select(:foo) }).select_values
end
- test 'none!' do
+ test "none!" do
assert relation.none!.equal?(relation)
assert_equal [NullRelation], relation.extending_values
assert relation.is_a?(NullRelation)
end
- test 'distinct!' do
+ test "distinct!" do
relation.distinct! :foo
assert_equal :foo, relation.distinct_value
-
- assert_deprecated do
- assert_equal :foo, relation.uniq_value # deprecated access
- end
- end
-
- test 'uniq! was replaced by distinct!' do
- assert_deprecated(/use distinct! instead/) do
- relation.uniq! :foo
- end
-
- assert_deprecated(/use distinct_value instead/) do
- assert_equal :foo, relation.uniq_value # deprecated access
- end
-
- assert_equal :foo, relation.distinct_value
end
end
end
diff --git a/activerecord/test/cases/relation/or_test.rb b/activerecord/test/cases/relation/or_test.rb
index ce8c5ca489..abb7ca72dd 100644
--- a/activerecord/test/cases/relation/or_test.rb
+++ b/activerecord/test/cases/relation/or_test.rb
@@ -1,28 +1,28 @@
require "cases/helper"
-require 'models/post'
+require "models/post"
module ActiveRecord
class OrTest < ActiveRecord::TestCase
fixtures :posts
def test_or_with_relation
- expected = Post.where('id = 1 or id = 2').to_a
- assert_equal expected, Post.where('id = 1').or(Post.where('id = 2')).to_a
+ expected = Post.where("id = 1 or id = 2").to_a
+ assert_equal expected, Post.where("id = 1").or(Post.where("id = 2")).to_a
end
def test_or_identity
- expected = Post.where('id = 1').to_a
- assert_equal expected, Post.where('id = 1').or(Post.where('id = 1')).to_a
+ expected = Post.where("id = 1").to_a
+ assert_equal expected, Post.where("id = 1").or(Post.where("id = 1")).to_a
end
def test_or_with_null_left
- expected = Post.where('id = 1').to_a
- assert_equal expected, Post.none.or(Post.where('id = 1')).to_a
+ expected = Post.where("id = 1").to_a
+ assert_equal expected, Post.none.or(Post.where("id = 1")).to_a
end
def test_or_with_null_right
- expected = Post.where('id = 1').to_a
- assert_equal expected, Post.where('id = 1').or(Post.none).to_a
+ expected = Post.where("id = 1").to_a
+ assert_equal expected, Post.where("id = 1").or(Post.none).to_a
end
def test_or_with_bind_params
@@ -36,56 +36,56 @@ module ActiveRecord
def test_or_without_left_where
expected = Post.all
- assert_equal expected, Post.or(Post.where('id = 1')).to_a
+ assert_equal expected, Post.or(Post.where("id = 1")).to_a
end
def test_or_without_right_where
expected = Post.all
- assert_equal expected, Post.where('id = 1').or(Post.all).to_a
+ assert_equal expected, Post.where("id = 1").or(Post.all).to_a
end
def test_or_preserves_other_querying_methods
- expected = Post.where('id = 1 or id = 2 or id = 3').order('body asc').to_a
- partial = Post.order('body asc')
- assert_equal expected, partial.where('id = 1').or(partial.where(:id => [2, 3])).to_a
- assert_equal expected, Post.order('body asc').where('id = 1').or(Post.order('body asc').where(:id => [2, 3])).to_a
+ expected = Post.where("id = 1 or id = 2 or id = 3").order("body asc").to_a
+ partial = Post.order("body asc")
+ assert_equal expected, partial.where("id = 1").or(partial.where(id: [2, 3])).to_a
+ assert_equal expected, Post.order("body asc").where("id = 1").or(Post.order("body asc").where(id: [2, 3])).to_a
end
def test_or_with_incompatible_relations
error = assert_raises ArgumentError do
- Post.order('body asc').where('id = 1').or(Post.order('id desc').where(:id => [2, 3])).to_a
+ Post.order("body asc").where("id = 1").or(Post.order("id desc").where(id: [2, 3])).to_a
end
assert_equal "Relation passed to #or must be structurally compatible. Incompatible values: [:order]", error.message
end
def test_or_when_grouping
- groups = Post.where('id < 10').group('body').select('body, COUNT(*) AS c')
- expected = groups.having("COUNT(*) > 1 OR body like 'Such%'").to_a.map {|o| [o.body, o.c] }
- assert_equal expected, groups.having('COUNT(*) > 1').or(groups.having("body like 'Such%'")).to_a.map {|o| [o.body, o.c] }
+ groups = Post.where("id < 10").group("body").select("body, COUNT(*) AS c")
+ expected = groups.having("COUNT(*) > 1 OR body like 'Such%'").to_a.map { |o| [o.body, o.c] }
+ assert_equal expected, groups.having("COUNT(*) > 1").or(groups.having("body like 'Such%'")).to_a.map { |o| [o.body, o.c] }
end
def test_or_with_named_scope
expected = Post.where("id = 1 or body LIKE '\%a\%'").to_a
- assert_equal expected, Post.where('id = 1').or(Post.containing_the_letter_a)
+ assert_equal expected, Post.where("id = 1").or(Post.containing_the_letter_a)
end
def test_or_inside_named_scope
- expected = Post.where("body LIKE '\%a\%' OR title LIKE ?", "%'%").order('id DESC').to_a
+ expected = Post.where("body LIKE '\%a\%' OR title LIKE ?", "%'%").order("id DESC").to_a
assert_equal expected, Post.order(id: :desc).typographically_interesting
end
def test_or_on_loaded_relation
- expected = Post.where('id = 1 or id = 2').to_a
- p = Post.where('id = 1')
+ expected = Post.where("id = 1 or id = 2").to_a
+ p = Post.where("id = 1")
p.load
- assert_equal p.loaded?, true
- assert_equal expected, p.or(Post.where('id = 2')).to_a
+ assert_equal true, p.loaded?
+ assert_equal expected, p.or(Post.where("id = 2")).to_a
end
def test_or_with_non_relation_object_raises_error
assert_raises ArgumentError do
- Post.where(id: [1, 2, 3]).or(title: 'Rails')
+ Post.where(id: [1, 2, 3]).or(title: "Rails")
end
end
end
diff --git a/activerecord/test/cases/relation/predicate_builder_test.rb b/activerecord/test/cases/relation/predicate_builder_test.rb
index 8f62014622..48758dc148 100644
--- a/activerecord/test/cases/relation/predicate_builder_test.rb
+++ b/activerecord/test/cases/relation/predicate_builder_test.rb
@@ -1,11 +1,11 @@
require "cases/helper"
-require 'models/topic'
+require "models/topic"
module ActiveRecord
class PredicateBuilderTest < ActiveRecord::TestCase
def test_registering_new_handlers
Topic.predicate_builder.register_handler(Regexp, proc do |column, value|
- Arel::Nodes::InfixOperation.new('~', column, Arel.sql(value.source))
+ Arel::Nodes::InfixOperation.new("~", column, Arel.sql(value.source))
end)
assert_match %r{["`]topics["`]\.["`]title["`] ~ rails}i, Topic.where(title: /rails/).to_sql
diff --git a/activerecord/test/cases/relation/record_fetch_warning_test.rb b/activerecord/test/cases/relation/record_fetch_warning_test.rb
index 0e0e23b24b..62ca051431 100644
--- a/activerecord/test/cases/relation/record_fetch_warning_test.rb
+++ b/activerecord/test/cases/relation/record_fetch_warning_test.rb
@@ -1,6 +1,6 @@
-require 'cases/helper'
-require 'models/post'
-require 'active_record/relation/record_fetch_warning'
+require "cases/helper"
+require "models/post"
+require "active_record/relation/record_fetch_warning"
module ActiveRecord
class RecordFetchWarningTest < ActiveRecord::TestCase
diff --git a/activerecord/test/cases/relation/where_chain_test.rb b/activerecord/test/cases/relation/where_chain_test.rb
index 27bbd80f79..a96d1ae5b5 100644
--- a/activerecord/test/cases/relation/where_chain_test.rb
+++ b/activerecord/test/cases/relation/where_chain_test.rb
@@ -1,6 +1,6 @@
-require 'cases/helper'
-require 'models/post'
-require 'models/comment'
+require "cases/helper"
+require "models/post"
+require "models/comment"
module ActiveRecord
class WhereChainTest < ActiveRecord::TestCase
@@ -8,12 +8,12 @@ module ActiveRecord
def setup
super
- @name = 'title'
+ @name = "title"
end
def test_not_inverts_where_clause
- relation = Post.where.not(title: 'hello')
- expected_where_clause = Post.where(title: 'hello').where_clause.invert
+ relation = Post.where.not(title: "hello")
+ expected_where_clause = Post.where(title: "hello").where_clause.invert
assert_equal expected_where_clause, relation.where_clause
end
@@ -26,54 +26,54 @@ module ActiveRecord
def test_association_not_eq
expected = Arel::Nodes::Grouping.new(Comment.arel_table[@name].not_eq(Arel::Nodes::BindParam.new))
- relation = Post.joins(:comments).where.not(comments: {title: 'hello'})
+ relation = Post.joins(:comments).where.not(comments: { title: "hello" })
assert_equal(expected.to_sql, relation.where_clause.ast.to_sql)
end
def test_not_eq_with_preceding_where
- relation = Post.where(title: 'hello').where.not(title: 'world')
+ relation = Post.where(title: "hello").where.not(title: "world")
expected_where_clause =
- Post.where(title: 'hello').where_clause +
- Post.where(title: 'world').where_clause.invert
+ Post.where(title: "hello").where_clause +
+ Post.where(title: "world").where_clause.invert
assert_equal expected_where_clause, relation.where_clause
end
def test_not_eq_with_succeeding_where
- relation = Post.where.not(title: 'hello').where(title: 'world')
+ relation = Post.where.not(title: "hello").where(title: "world")
expected_where_clause =
- Post.where(title: 'hello').where_clause.invert +
- Post.where(title: 'world').where_clause
+ Post.where(title: "hello").where_clause.invert +
+ Post.where(title: "world").where_clause
assert_equal expected_where_clause, relation.where_clause
end
def test_chaining_multiple
- relation = Post.where.not(author_id: [1, 2]).where.not(title: 'ruby on rails')
+ relation = Post.where.not(author_id: [1, 2]).where.not(title: "ruby on rails")
expected_where_clause =
Post.where(author_id: [1, 2]).where_clause.invert +
- Post.where(title: 'ruby on rails').where_clause.invert
+ Post.where(title: "ruby on rails").where_clause.invert
assert_equal expected_where_clause, relation.where_clause
end
def test_rewhere_with_one_condition
- relation = Post.where(title: 'hello').where(title: 'world').rewhere(title: 'alone')
- expected = Post.where(title: 'alone')
+ relation = Post.where(title: "hello").where(title: "world").rewhere(title: "alone")
+ expected = Post.where(title: "alone")
assert_equal expected.where_clause, relation.where_clause
end
def test_rewhere_with_multiple_overwriting_conditions
- relation = Post.where(title: 'hello').where(body: 'world').rewhere(title: 'alone', body: 'again')
- expected = Post.where(title: 'alone', body: 'again')
+ relation = Post.where(title: "hello").where(body: "world").rewhere(title: "alone", body: "again")
+ expected = Post.where(title: "alone", body: "again")
assert_equal expected.where_clause, relation.where_clause
end
def test_rewhere_with_one_overwriting_condition_and_one_unrelated
- relation = Post.where(title: 'hello').where(body: 'world').rewhere(title: 'alone')
- expected = Post.where(body: 'world', title: 'alone')
+ relation = Post.where(title: "hello").where(body: "world").rewhere(title: "alone")
+ expected = Post.where(body: "world", title: "alone")
assert_equal expected.where_clause, relation.where_clause
end
diff --git a/activerecord/test/cases/relation/where_clause_test.rb b/activerecord/test/cases/relation/where_clause_test.rb
index c20ed94d90..d8e4c304f0 100644
--- a/activerecord/test/cases/relation/where_clause_test.rb
+++ b/activerecord/test/cases/relation/where_clause_test.rb
@@ -140,7 +140,7 @@ class ActiveRecord::Relation
test "ast removes any empty strings" do
where_clause = WhereClause.new([table["id"].in([1, 2, 3])], [])
- where_clause_with_empty = WhereClause.new([table["id"].in([1, 2, 3]), ''], [])
+ where_clause_with_empty = WhereClause.new([table["id"].in([1, 2, 3]), ""], [])
assert_equal where_clause.ast, where_clause_with_empty.ast
end
@@ -167,16 +167,16 @@ class ActiveRecord::Relation
private
- def table
- Arel::Table.new("table")
- end
+ def table
+ Arel::Table.new("table")
+ end
- def bind_param
- Arel::Nodes::BindParam.new
- end
+ def bind_param
+ Arel::Nodes::BindParam.new
+ end
- def attribute(name, value)
- ActiveRecord::Attribute.with_cast_value(name, value, ActiveRecord::Type::Value.new)
- end
+ def attribute(name, value)
+ ActiveRecord::Attribute.with_cast_value(name, value, ActiveRecord::Type::Value.new)
+ end
end
end
diff --git a/activerecord/test/cases/relation/where_test.rb b/activerecord/test/cases/relation/where_test.rb
index 56a2b5b8c6..ed8ffddcf5 100644
--- a/activerecord/test/cases/relation/where_test.rb
+++ b/activerecord/test/cases/relation/where_test.rb
@@ -4,10 +4,10 @@ require "models/binary"
require "models/cake_designer"
require "models/car"
require "models/chef"
+require "models/post"
require "models/comment"
require "models/edge"
require "models/essay"
-require "models/post"
require "models/price_estimate"
require "models/topic"
require "models/treasure"
@@ -19,7 +19,7 @@ module ActiveRecord
def test_where_copies_bind_params
author = authors(:david)
- posts = author.posts.where('posts.id != 1')
+ posts = author.posts.where("posts.id != 1")
joined = Post.where(id: posts)
assert_operator joined.length, :>, 0
@@ -49,7 +49,7 @@ module ActiveRecord
end
def test_rewhere_on_root
- assert_equal posts(:welcome), Post.rewhere(title: 'Welcome to the weblog').first
+ assert_equal posts(:welcome), Post.rewhere(title: "Welcome to the weblog").first
end
def test_belongs_to_shallow_where
@@ -64,12 +64,12 @@ module ActiveRecord
end
def test_belongs_to_array_value_where
- assert_equal Post.where(author_id: [1,2]).to_sql, Post.where(author: [1,2]).to_sql
+ assert_equal Post.where(author_id: [1, 2]).to_sql, Post.where(author: [1, 2]).to_sql
end
def test_belongs_to_nested_relation_where
- expected = Post.where(author_id: Author.where(id: [1,2])).to_sql
- actual = Post.where(author: Author.where(id: [1,2])).to_sql
+ expected = Post.where(author_id: Author.where(id: [1, 2])).to_sql
+ actual = Post.where(author: Author.where(id: [1, 2])).to_sql
assert_equal expected, actual
end
@@ -87,7 +87,7 @@ module ActiveRecord
def test_belongs_to_nested_where_with_relation
author = authors(:david)
- expected = Author.where(id: author ).joins(:posts)
+ expected = Author.where(id: author).joins(:posts)
actual = Author.where(posts: { author_id: Author.where(id: author.id) }).joins(:posts)
assert_equal expected.to_a, actual.to_a
@@ -97,7 +97,7 @@ module ActiveRecord
treasure = Treasure.new
treasure.id = 1
- expected = PriceEstimate.where(estimate_of_type: 'Treasure', estimate_of_id: 1)
+ expected = PriceEstimate.where(estimate_of_type: "Treasure", estimate_of_id: 1)
actual = PriceEstimate.where(estimate_of: treasure)
assert_equal expected.to_sql, actual.to_sql
@@ -109,7 +109,7 @@ module ActiveRecord
hidden = HiddenTreasure.new
hidden.id = 2
- expected = PriceEstimate.where(estimate_of_type: 'Treasure', estimate_of_id: [treasure, hidden])
+ expected = PriceEstimate.where(estimate_of_type: "Treasure", estimate_of_id: [treasure, hidden])
actual = PriceEstimate.where(estimate_of: [treasure, hidden])
assert_equal expected.to_sql, actual.to_sql
@@ -127,8 +127,8 @@ module ActiveRecord
end
def test_polymorphic_nested_relation_where
- expected = PriceEstimate.where(estimate_of_type: 'Treasure', estimate_of_id: Treasure.where(id: [1,2]))
- actual = PriceEstimate.where(estimate_of: Treasure.where(id: [1,2]))
+ expected = PriceEstimate.where(estimate_of_type: "Treasure", estimate_of_id: Treasure.where(id: [1, 2]))
+ actual = PriceEstimate.where(estimate_of: Treasure.where(id: [1, 2]))
assert_equal expected.to_sql, actual.to_sql
end
@@ -137,7 +137,7 @@ module ActiveRecord
treasure = HiddenTreasure.new
treasure.id = 1
- expected = PriceEstimate.where(estimate_of_type: 'Treasure', estimate_of_id: 1)
+ expected = PriceEstimate.where(estimate_of_type: "Treasure", estimate_of_id: 1)
actual = PriceEstimate.where(estimate_of: treasure)
assert_equal expected.to_sql, actual.to_sql
@@ -147,7 +147,7 @@ module ActiveRecord
thing = Post.new
thing.id = 1
- expected = Treasure.where(price_estimates: { thing_type: 'Post', thing_id: 1 }).joins(:price_estimates)
+ expected = Treasure.where(price_estimates: { thing_type: "Post", thing_id: 1 }).joins(:price_estimates)
actual = Treasure.where(price_estimates: { thing: thing }).joins(:price_estimates)
assert_equal expected.to_sql, actual.to_sql
@@ -157,7 +157,7 @@ module ActiveRecord
treasure = HiddenTreasure.new
treasure.id = 1
- expected = Treasure.where(price_estimates: { estimate_of_type: 'Treasure', estimate_of_id: 1 }).joins(:price_estimates)
+ expected = Treasure.where(price_estimates: { estimate_of_type: "Treasure", estimate_of_id: 1 }).joins(:price_estimates)
actual = Treasure.where(price_estimates: { estimate_of: treasure }).joins(:price_estimates)
assert_equal expected.to_sql, actual.to_sql
@@ -182,40 +182,40 @@ module ActiveRecord
treasure.id = 1
decorated_treasure = treasure_decorator.new(treasure)
- expected = PriceEstimate.where(estimate_of_type: 'Treasure', estimate_of_id: 1)
+ expected = PriceEstimate.where(estimate_of_type: "Treasure", estimate_of_id: 1)
actual = PriceEstimate.where(estimate_of: decorated_treasure)
assert_equal expected.to_sql, actual.to_sql
end
def test_aliased_attribute
- expected = Topic.where(heading: 'The First Topic')
- actual = Topic.where(title: 'The First Topic')
+ expected = Topic.where(heading: "The First Topic")
+ actual = Topic.where(title: "The First Topic")
assert_equal expected.to_sql, actual.to_sql
end
def test_where_error
- assert_raises(ActiveRecord::StatementInvalid) do
- Post.where(:id => { 'posts.author_id' => 10 }).first
+ assert_nothing_raised do
+ Post.where(id: { "posts.author_id" => 10 }).first
end
end
def test_where_with_table_name
post = Post.first
- assert_equal post, Post.where(:posts => { 'id' => post.id }).first
+ assert_equal post, Post.where(posts: { "id" => post.id }).first
end
def test_where_with_table_name_and_empty_hash
- assert_equal 0, Post.where(:posts => {}).count
+ assert_equal 0, Post.where(posts: {}).count
end
def test_where_with_table_name_and_empty_array
- assert_equal 0, Post.where(:id => []).count
+ assert_equal 0, Post.where(id: []).count
end
def test_where_with_empty_hash_and_no_foreign_key
- assert_equal 0, Edge.where(:sink => {}).count
+ assert_equal 0, Edge.where(sink: {}).count
end
def test_where_with_blank_conditions
@@ -225,32 +225,32 @@ module ActiveRecord
end
def test_where_with_integer_for_string_column
- count = Post.where(:title => 0).count
+ count = Post.where(title: 0).count
assert_equal 0, count
end
def test_where_with_float_for_string_column
- count = Post.where(:title => 0.0).count
+ count = Post.where(title: 0.0).count
assert_equal 0, count
end
def test_where_with_boolean_for_string_column
- count = Post.where(:title => false).count
+ count = Post.where(title: false).count
assert_equal 0, count
end
def test_where_with_decimal_for_string_column
- count = Post.where(:title => BigDecimal.new(0)).count
+ count = Post.where(title: BigDecimal.new(0)).count
assert_equal 0, count
end
def test_where_with_duration_for_string_column
- count = Post.where(:title => 0.seconds).count
+ count = Post.where(title: 0.seconds).count
assert_equal 0, count
end
def test_where_with_integer_for_binary_column
- count = Binary.where(:data => 0).count
+ count = Binary.where(data: 0).count
assert_equal 0, count
end
diff --git a/activerecord/test/cases/relation_test.rb b/activerecord/test/cases/relation_test.rb
index 03583344a8..d5af0cc9a5 100644
--- a/activerecord/test/cases/relation_test.rb
+++ b/activerecord/test/cases/relation_test.rb
@@ -1,8 +1,8 @@
require "cases/helper"
-require 'models/post'
-require 'models/comment'
-require 'models/author'
-require 'models/rating'
+require "models/post"
+require "models/comment"
+require "models/author"
+require "models/rating"
module ActiveRecord
class RelationTest < ActiveRecord::TestCase
@@ -18,7 +18,7 @@ module ActiveRecord
end
def self.table_name
- 'fake_table'
+ "fake_table"
end
def self.sanitize_sql_for_order(sql)
@@ -30,7 +30,7 @@ module ActiveRecord
relation = Relation.new(FakeKlass, :b, nil)
assert_equal FakeKlass, relation.klass
assert_equal :b, relation.table
- assert !relation.loaded, 'relation is not loaded'
+ assert !relation.loaded, "relation is not loaded"
end
def test_responds_to_model_and_returns_klass
@@ -40,9 +40,10 @@ module ActiveRecord
def test_initialize_single_values
relation = Relation.new(FakeKlass, :b, nil)
- (Relation::SINGLE_VALUE_METHODS - [:create_with]).each do |method|
+ (Relation::SINGLE_VALUE_METHODS - [:create_with, :readonly]).each do |method|
assert_nil relation.send("#{method}_value"), method.to_s
end
+ assert_equal false, relation.readonly_value
value = relation.create_with_value
assert_equal({}, value)
assert_predicate value, :frozen?
@@ -70,7 +71,7 @@ module ActiveRecord
def test_has_values
relation = Relation.new(Post, Post.arel_table, Post.predicate_builder)
relation.where! relation.table[:id].eq(10)
- assert_equal({:id => 10}, relation.where_values_hash)
+ assert_equal({ id: 10 }, relation.where_values_hash)
end
def test_values_wrong_table
@@ -89,8 +90,8 @@ module ActiveRecord
end
def test_table_name_delegates_to_klass
- relation = Relation.new(FakeKlass.new('posts'), :b, Post.predicate_builder)
- assert_equal 'posts', relation.table_name
+ relation = Relation.new(FakeKlass.new("posts"), :b, Post.predicate_builder)
+ assert_equal "posts", relation.table_name
end
def test_scope_for_create
@@ -100,7 +101,7 @@ module ActiveRecord
def test_create_with_value
relation = Relation.new(Post, Post.arel_table, Post.predicate_builder)
- hash = { :hello => 'world' }
+ hash = { hello: "world" }
relation.create_with_value = hash
assert_equal hash, relation.scope_for_create
end
@@ -108,8 +109,8 @@ module ActiveRecord
def test_create_with_value_with_wheres
relation = Relation.new(Post, Post.arel_table, Post.predicate_builder)
relation.where! relation.table[:id].eq(10)
- relation.create_with_value = {:hello => 'world'}
- assert_equal({:hello => 'world', :id => 10}, relation.scope_for_create)
+ relation.create_with_value = { hello: "world" }
+ assert_equal({ hello: "world", id: 10 }, relation.scope_for_create)
end
# FIXME: is this really wanted or expected behavior?
@@ -120,7 +121,7 @@ module ActiveRecord
relation.where! relation.table[:id].eq(10)
assert_equal({}, relation.scope_for_create)
- relation.create_with_value = {:hello => 'world'}
+ relation.create_with_value = { hello: "world" }
assert_equal({}, relation.scope_for_create)
end
@@ -145,48 +146,48 @@ module ActiveRecord
relation = Relation.new(FakeKlass, :b, nil)
assert_equal [], relation.references_values
relation = relation.references(:foo).references(:omg, :lol)
- assert_equal ['foo', 'omg', 'lol'], relation.references_values
+ assert_equal ["foo", "omg", "lol"], relation.references_values
end
def test_references_values_dont_duplicate
relation = Relation.new(FakeKlass, :b, nil)
relation = relation.references(:foo).references(:foo)
- assert_equal ['foo'], relation.references_values
+ assert_equal ["foo"], relation.references_values
end
- test 'merging a hash into a relation' do
+ test "merging a hash into a relation" do
relation = Relation.new(Post, Post.arel_table, Post.predicate_builder)
- relation = relation.merge where: {name: :lol}, readonly: true
+ relation = relation.merge where: { name: :lol }, readonly: true
- assert_equal({"name"=>:lol}, relation.where_clause.to_h)
+ assert_equal({ "name" => :lol }, relation.where_clause.to_h)
assert_equal true, relation.readonly_value
end
- test 'merging an empty hash into a relation' do
+ test "merging an empty hash into a relation" do
assert_equal Relation::WhereClause.empty, Relation.new(FakeKlass, :b, nil).merge({}).where_clause
end
- test 'merging a hash with unknown keys raises' do
- assert_raises(ArgumentError) { Relation::HashMerger.new(nil, omg: 'lol') }
+ test "merging a hash with unknown keys raises" do
+ assert_raises(ArgumentError) { Relation::HashMerger.new(nil, omg: "lol") }
end
- test 'merging nil or false raises' do
+ test "merging nil or false raises" do
relation = Relation.new(FakeKlass, :b, nil)
e = assert_raises(ArgumentError) do
relation = relation.merge nil
end
- assert_equal 'invalid argument: nil.', e.message
+ assert_equal "invalid argument: nil.", e.message
e = assert_raises(ArgumentError) do
relation = relation.merge false
end
- assert_equal 'invalid argument: false.', e.message
+ assert_equal "invalid argument: false.", e.message
end
- test '#values returns a dup of the values' do
+ test "#values returns a dup of the values" do
relation = Relation.new(Post, Post.arel_table, Post.predicate_builder).where!(name: :foo)
values = relation.values
@@ -194,22 +195,22 @@ module ActiveRecord
assert_not_nil relation.where_clause
end
- test 'relations can be created with a values hash' do
+ test "relations can be created with a values hash" do
relation = Relation.new(FakeKlass, :b, nil, select: [:foo])
assert_equal [:foo], relation.select_values
end
- test 'merging a hash interpolates conditions' do
+ test "merging a hash interpolates conditions" do
klass = Class.new(FakeKlass) do
def self.sanitize_sql(args)
- raise unless args == ['foo = ?', 'bar']
- 'foo = bar'
+ raise unless args == ["foo = ?", "bar"]
+ "foo = bar"
end
end
relation = Relation.new(klass, :b, nil)
- relation.merge!(where: ['foo = ?', 'bar'])
- assert_equal Relation::WhereClause.new(['foo = bar'], []), relation.where_clause
+ relation.merge!(where: ["foo = ?", "bar"])
+ assert_equal Relation::WhereClause.new(["foo = bar"], []), relation.where_clause
end
def test_merging_readonly_false
@@ -223,7 +224,7 @@ module ActiveRecord
def test_relation_merging_with_merged_joins_as_symbols
special_comments_with_ratings = SpecialComment.joins(:ratings)
posts_with_special_comments_with_ratings = Post.group("posts.id").joins(:special_comments).merge(special_comments_with_ratings)
- assert_equal 3, authors(:david).posts.merge(posts_with_special_comments_with_ratings).count.length
+ assert_equal({ 2 => 1, 4 => 3, 5 => 1 }, authors(:david).posts.merge(posts_with_special_comments_with_ratings).count)
end
def test_relation_merging_with_joins_as_join_dependency_pick_proper_parent
@@ -273,7 +274,7 @@ module ActiveRecord
join_string = "LEFT OUTER JOIN #{Rating.quoted_table_name} ON #{SpecialComment.quoted_table_name}.id = #{Rating.quoted_table_name}.comment_id"
special_comments_with_ratings = SpecialComment.joins join_string
posts_with_special_comments_with_ratings = Post.group("posts.id").joins(:special_comments).merge(special_comments_with_ratings)
- assert_equal 3, authors(:david).posts.merge(posts_with_special_comments_with_ratings).count.length
+ assert_equal({ 2 => 1, 4 => 3, 5 => 1 }, authors(:david).posts.merge(posts_with_special_comments_with_ratings).count)
end
class EnsureRoundTripTypeCasting < ActiveRecord::Type::Value
@@ -293,7 +294,7 @@ module ActiveRecord
end
class UpdateAllTestModel < ActiveRecord::Base
- self.table_name = 'posts'
+ self.table_name = "posts"
attribute :body, EnsureRoundTripTypeCasting.new
end
@@ -306,23 +307,23 @@ module ActiveRecord
private
- def skip_if_sqlite3_version_includes_quoting_bug
- if sqlite3_version_includes_quoting_bug?
- skip <<-ERROR.squish
- You are using an outdated version of SQLite3 which has a bug in
- quoted column names. Please update SQLite3 and rebuild the sqlite3
- ruby gem
- ERROR
+ def skip_if_sqlite3_version_includes_quoting_bug
+ if sqlite3_version_includes_quoting_bug?
+ skip <<-ERROR.squish
+ You are using an outdated version of SQLite3 which has a bug in
+ quoted column names. Please update SQLite3 and rebuild the sqlite3
+ ruby gem
+ ERROR
+ end
end
- end
- def sqlite3_version_includes_quoting_bug?
- if current_adapter?(:SQLite3Adapter)
- selected_quoted_column_names = ActiveRecord::Base.connection.exec_query(
- 'SELECT "join" FROM (SELECT id AS "join" FROM posts) subquery'
- ).columns
- ["join"] != selected_quoted_column_names
+ def sqlite3_version_includes_quoting_bug?
+ if current_adapter?(:SQLite3Adapter)
+ selected_quoted_column_names = ActiveRecord::Base.connection.exec_query(
+ 'SELECT "join" FROM (SELECT id AS "join" FROM posts) subquery'
+ ).columns
+ ["join"] != selected_quoted_column_names
+ end
end
- end
end
end
diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb
index 5604124bb3..9519fec0c4 100644
--- a/activerecord/test/cases/relations_test.rb
+++ b/activerecord/test/cases/relations_test.rb
@@ -1,21 +1,21 @@
require "cases/helper"
-require 'models/tag'
-require 'models/tagging'
-require 'models/post'
-require 'models/topic'
-require 'models/comment'
-require 'models/author'
-require 'models/entrant'
-require 'models/developer'
-require 'models/computer'
-require 'models/reply'
-require 'models/company'
-require 'models/bird'
-require 'models/car'
-require 'models/engine'
-require 'models/tyre'
-require 'models/minivan'
-require 'models/aircraft'
+require "models/tag"
+require "models/tagging"
+require "models/post"
+require "models/topic"
+require "models/comment"
+require "models/author"
+require "models/entrant"
+require "models/developer"
+require "models/computer"
+require "models/reply"
+require "models/company"
+require "models/bird"
+require "models/car"
+require "models/engine"
+require "models/tyre"
+require "models/minivan"
+require "models/aircraft"
require "models/possession"
require "models/reader"
require "models/categorization"
@@ -27,19 +27,19 @@ class RelationTest < ActiveRecord::TestCase
class TopicWithCallbacks < ActiveRecord::Base
self.table_name = :topics
- before_update { |topic| topic.author_name = 'David' if topic.author_name.blank? }
+ before_update { |topic| topic.author_name = "David" if topic.author_name.blank? }
end
def test_do_not_double_quote_string_id
van = Minivan.last
assert van
- assert_equal van.id, Minivan.where(:minivan_id => van).to_a.first.minivan_id
+ assert_equal van.id, Minivan.where(minivan_id: van).to_a.first.minivan_id
end
def test_do_not_double_quote_string_id_with_array
van = Minivan.last
assert van
- assert_equal van, Minivan.where(:minivan_id => [van]).to_a.first
+ assert_equal van, Minivan.where(minivan_id: [van]).to_a.first
end
def test_two_scopes_with_includes_should_not_drop_any_include
@@ -54,12 +54,12 @@ class RelationTest < ActiveRecord::TestCase
end
def test_dynamic_finder
- x = Post.where('author_id = ?', 1)
- assert x.klass.respond_to?(:find_by_id), '@klass should handle dynamic finders'
+ x = Post.where("author_id = ?", 1)
+ assert x.klass.respond_to?(:find_by_id), "@klass should handle dynamic finders"
end
def test_multivalue_where
- posts = Post.where('author_id = ? AND id = ?', 1, 1)
+ posts = Post.where("author_id = ? AND id = ?", 1, 1)
assert_equal 1, posts.to_a.size
end
@@ -101,7 +101,7 @@ class RelationTest < ActiveRecord::TestCase
end
def test_scoped_first
- topics = Topic.all.order('id ASC')
+ topics = Topic.all.order("id ASC")
assert_queries(1) do
2.times { assert_equal "The First Topic", topics.first.title }
@@ -111,7 +111,7 @@ class RelationTest < ActiveRecord::TestCase
end
def test_loaded_first
- topics = Topic.all.order('id ASC')
+ topics = Topic.all.order("id ASC")
topics.to_a # force load
assert_no_queries do
@@ -122,7 +122,7 @@ class RelationTest < ActiveRecord::TestCase
end
def test_loaded_first_with_limit
- topics = Topic.all.order('id ASC')
+ topics = Topic.all.order("id ASC")
topics.to_a # force load
assert_no_queries do
@@ -134,7 +134,7 @@ class RelationTest < ActiveRecord::TestCase
end
def test_first_get_more_than_available
- topics = Topic.all.order('id ASC')
+ topics = Topic.all.order("id ASC")
unloaded_first = topics.first(10)
topics.to_a # force load
@@ -154,7 +154,7 @@ class RelationTest < ActiveRecord::TestCase
assert topics.loaded?
original_size = topics.to_a.size
- Topic.create! :title => 'fake'
+ Topic.create! title: "fake"
assert_queries(1) { topics.reload }
assert_equal original_size + 1, topics.size
@@ -162,17 +162,17 @@ class RelationTest < ActiveRecord::TestCase
end
def test_finding_with_subquery
- relation = Topic.where(:approved => true)
- assert_equal relation.to_a, Topic.select('*').from(relation).to_a
- assert_equal relation.to_a, Topic.select('subquery.*').from(relation).to_a
- assert_equal relation.to_a, Topic.select('a.*').from(relation, :a).to_a
+ relation = Topic.where(approved: true)
+ assert_equal relation.to_a, Topic.select("*").from(relation).to_a
+ assert_equal relation.to_a, Topic.select("subquery.*").from(relation).to_a
+ assert_equal relation.to_a, Topic.select("a.*").from(relation, :a).to_a
end
def test_finding_with_subquery_with_binds
relation = Post.first.comments
- assert_equal relation.to_a, Comment.select('*').from(relation).to_a
- assert_equal relation.to_a, Comment.select('subquery.*').from(relation).to_a
- assert_equal relation.to_a, Comment.select('a.*').from(relation, :a).to_a
+ assert_equal relation.to_a, Comment.select("*").from(relation).to_a
+ assert_equal relation.to_a, Comment.select("subquery.*").from(relation).to_a
+ assert_equal relation.to_a, Comment.select("a.*").from(relation, :a).to_a
end
def test_finding_with_subquery_without_select_does_not_change_the_select
@@ -183,25 +183,25 @@ class RelationTest < ActiveRecord::TestCase
end
def test_select_with_subquery_in_from_does_not_use_original_table_name
- relation = Comment.group(:type).select('COUNT(post_id) AS post_count, type')
- subquery = Comment.from(relation).select('type','post_count')
- assert_equal(relation.map(&:post_count).sort,subquery.map(&:post_count).sort)
+ relation = Comment.group(:type).select("COUNT(post_id) AS post_count, type")
+ subquery = Comment.from(relation).select("type", "post_count")
+ assert_equal(relation.map(&:post_count).sort, subquery.map(&:post_count).sort)
end
def test_group_with_subquery_in_from_does_not_use_original_table_name
- relation = Comment.group(:type).select('COUNT(post_id) AS post_count,type')
- subquery = Comment.from(relation).group('type').average("post_count")
- assert_equal(relation.map(&:post_count).sort,subquery.values.sort)
+ relation = Comment.group(:type).select("COUNT(post_id) AS post_count,type")
+ subquery = Comment.from(relation).group("type").average("post_count")
+ assert_equal(relation.map(&:post_count).sort, subquery.values.sort)
end
def test_finding_with_conditions
- assert_equal ["David"], Author.where(:name => 'David').map(&:name)
- assert_equal ['Mary'], Author.where(["name = ?", 'Mary']).map(&:name)
- assert_equal ['Mary'], Author.where("name = ?", 'Mary').map(&:name)
+ assert_equal ["David"], Author.where(name: "David").map(&:name)
+ assert_equal ["Mary"], Author.where(["name = ?", "Mary"]).map(&:name)
+ assert_equal ["Mary"], Author.where("name = ?", "Mary").map(&:name)
end
def test_finding_with_order
- topics = Topic.order('id')
+ topics = Topic.order("id")
assert_equal 5, topics.to_a.size
assert_equal topics(:first).title, topics.first.title
end
@@ -213,13 +213,13 @@ class RelationTest < ActiveRecord::TestCase
end
def test_finding_with_assoc_order
- topics = Topic.order(:id => :desc)
+ topics = Topic.order(id: :desc)
assert_equal 5, topics.to_a.size
assert_equal topics(:fifth).title, topics.first.title
end
def test_finding_with_reverted_assoc_order
- topics = Topic.order(:id => :asc).reverse_order
+ topics = Topic.order(id: :asc).reverse_order
assert_equal 5, topics.to_a.size
assert_equal topics(:fifth).title, topics.first.title
end
@@ -240,6 +240,15 @@ class RelationTest < ActiveRecord::TestCase
assert_raises(ActiveRecord::IrreversibleOrderError) do
Topic.order("concat(author_name, title)").reverse_order
end
+ assert_raises(ActiveRecord::IrreversibleOrderError) do
+ Topic.order("concat(lower(author_name), title)").reverse_order
+ end
+ assert_raises(ActiveRecord::IrreversibleOrderError) do
+ Topic.order("concat(author_name, lower(title))").reverse_order
+ end
+ assert_raises(ActiveRecord::IrreversibleOrderError) do
+ Topic.order("concat(lower(author_name), title, length(title)").reverse_order
+ end
end
def test_reverse_order_with_nulls_first_or_last
@@ -258,7 +267,7 @@ class RelationTest < ActiveRecord::TestCase
end
def test_order_with_hash_and_symbol_generates_the_same_sql
- assert_equal Topic.order(:id).to_sql, Topic.order(:id => :asc).to_sql
+ assert_equal Topic.order(:id).to_sql, Topic.order(id: :asc).to_sql
end
def test_finding_with_desc_order_with_string
@@ -268,7 +277,7 @@ class RelationTest < ActiveRecord::TestCase
end
def test_finding_with_asc_order_with_string
- topics = Topic.order(id: 'asc')
+ topics = Topic.order(id: "asc")
assert_equal 5, topics.to_a.size
assert_equal [topics(:first), topics(:second), topics(:third), topics(:fourth), topics(:fifth)], topics.to_a
end
@@ -282,7 +291,7 @@ class RelationTest < ActiveRecord::TestCase
assert_includes Topic.order(id: "DESC").to_sql, "DESC"
assert_includes Topic.order(id: "desc").to_sql, "DESC"
assert_includes Topic.order(id: :DESC).to_sql, "DESC"
- assert_includes Topic.order(id: :desc).to_sql,"DESC"
+ assert_includes Topic.order(id: :desc).to_sql, "DESC"
end
def test_raising_exception_on_invalid_hash_params
@@ -296,7 +305,7 @@ class RelationTest < ActiveRecord::TestCase
end
def test_finding_with_order_concatenated
- topics = Topic.order('author_name').order('title')
+ topics = Topic.order("author_name").order("title")
assert_equal 5, topics.to_a.size
assert_equal topics(:fourth).title, topics.first.title
end
@@ -314,19 +323,19 @@ class RelationTest < ActiveRecord::TestCase
end
def test_finding_with_reorder
- topics = Topic.order('author_name').order('title').reorder('id').to_a
+ topics = Topic.order("author_name").order("title").reorder("id").to_a
topics_titles = topics.map(&:title)
- assert_equal ['The First Topic', 'The Second Topic of the day', 'The Third Topic of the day', 'The Fourth Topic of the day', 'The Fifth Topic of the day'], topics_titles
+ assert_equal ["The First Topic", "The Second Topic of the day", "The Third Topic of the day", "The Fourth Topic of the day", "The Fifth Topic of the day"], topics_titles
end
def test_finding_with_reorder_by_aliased_attributes
- topics = Topic.order('author_name').reorder(:heading)
+ topics = Topic.order("author_name").reorder(:heading)
assert_equal 5, topics.to_a.size
assert_equal topics(:fifth).title, topics.first.title
end
def test_finding_with_assoc_reorder_by_aliased_attributes
- topics = Topic.order('author_name').reorder(heading: :desc)
+ topics = Topic.order("author_name").reorder(heading: :desc)
assert_equal 5, topics.to_a.size
assert_equal topics(:third).title, topics.first.title
end
@@ -356,7 +365,7 @@ class RelationTest < ActiveRecord::TestCase
end
def test_finding_with_sanitized_order
- query = Tag.order(["field(id, ?)", [1,3,2]]).to_sql
+ query = Tag.order(["field(id, ?)", [1, 3, 2]]).to_sql
assert_match(/field\(id, 1,3,2\)/, query)
query = Tag.order(["field(id, ?)", []]).to_sql
@@ -384,7 +393,7 @@ class RelationTest < ActiveRecord::TestCase
end
def test_select_with_block
- even_ids = Developer.all.select {|d| d.id % 2 == 0 }.map(&:id)
+ even_ids = Developer.all.select { |d| d.id % 2 == 0 }.map(&:id)
assert_equal [2, 4, 6, 8, 10], even_ids.sort
end
@@ -397,7 +406,7 @@ class RelationTest < ActiveRecord::TestCase
def test_none_chainable
assert_no_queries(ignore_none: false) do
- assert_equal [], Developer.none.where(:name => 'David')
+ assert_equal [], Developer.none.where(name: "David")
end
end
@@ -411,7 +420,7 @@ class RelationTest < ActiveRecord::TestCase
assert_no_queries(ignore_none: false) do
assert_equal [], Developer.none.pluck(:id, :name)
assert_equal 0, Developer.none.delete_all
- assert_equal 0, Developer.none.update_all(:name => 'David')
+ assert_equal 0, Developer.none.update_all(name: "David")
assert_equal 0, Developer.none.delete(1)
assert_equal false, Developer.none.exists?(1)
end
@@ -433,7 +442,7 @@ class RelationTest < ActiveRecord::TestCase
assert_no_queries(ignore_none: false) do
assert_equal 0, Developer.none.count
assert_equal 0, Developer.none.calculate(:count, nil)
- assert_equal nil, Developer.none.calculate(:average, 'salary')
+ assert_nil Developer.none.calculate(:average, "salary")
end
end
@@ -443,61 +452,61 @@ class RelationTest < ActiveRecord::TestCase
end
def test_null_relation_where_values_hash
- assert_equal({ 'salary' => 100_000 }, Developer.none.where(salary: 100_000).where_values_hash)
+ assert_equal({ "salary" => 100_000 }, Developer.none.where(salary: 100_000).where_values_hash)
end
def test_null_relation_sum
ac = Aircraft.new
assert_equal Hash.new, ac.engines.group(:id).sum(:id)
- assert_equal 0, ac.engines.count
+ assert_equal 0, ac.engines.count
ac.save
assert_equal Hash.new, ac.engines.group(:id).sum(:id)
- assert_equal 0, ac.engines.count
+ assert_equal 0, ac.engines.count
end
def test_null_relation_count
ac = Aircraft.new
assert_equal Hash.new, ac.engines.group(:id).count
- assert_equal 0, ac.engines.count
+ assert_equal 0, ac.engines.count
ac.save
assert_equal Hash.new, ac.engines.group(:id).count
- assert_equal 0, ac.engines.count
+ assert_equal 0, ac.engines.count
end
def test_null_relation_size
ac = Aircraft.new
assert_equal Hash.new, ac.engines.group(:id).size
- assert_equal 0, ac.engines.size
+ assert_equal 0, ac.engines.size
ac.save
assert_equal Hash.new, ac.engines.group(:id).size
- assert_equal 0, ac.engines.size
+ assert_equal 0, ac.engines.size
end
def test_null_relation_average
ac = Aircraft.new
assert_equal Hash.new, ac.engines.group(:car_id).average(:id)
- assert_equal nil, ac.engines.average(:id)
+ assert_nil ac.engines.average(:id)
ac.save
assert_equal Hash.new, ac.engines.group(:car_id).average(:id)
- assert_equal nil, ac.engines.average(:id)
+ assert_nil ac.engines.average(:id)
end
def test_null_relation_minimum
ac = Aircraft.new
assert_equal Hash.new, ac.engines.group(:car_id).minimum(:id)
- assert_equal nil, ac.engines.minimum(:id)
+ assert_nil ac.engines.minimum(:id)
ac.save
assert_equal Hash.new, ac.engines.group(:car_id).minimum(:id)
- assert_equal nil, ac.engines.minimum(:id)
+ assert_nil ac.engines.minimum(:id)
end
def test_null_relation_maximum
ac = Aircraft.new
assert_equal Hash.new, ac.engines.group(:car_id).maximum(:id)
- assert_equal nil, ac.engines.maximum(:id)
+ assert_nil ac.engines.maximum(:id)
ac.save
assert_equal Hash.new, ac.engines.group(:car_id).maximum(:id)
- assert_equal nil, ac.engines.maximum(:id)
+ assert_nil ac.engines.maximum(:id)
end
def test_null_relation_in_where_condition
@@ -510,23 +519,23 @@ class RelationTest < ActiveRecord::TestCase
end
def test_finding_with_hash_conditions_on_joined_table
- firms = DependentFirm.joins(:account).where({:name => 'RailsCore', :accounts => { :credit_limit => 55..60 }}).to_a
+ firms = DependentFirm.joins(:account).where(name: "RailsCore", accounts: { credit_limit: 55..60 }).to_a
assert_equal 1, firms.size
assert_equal companies(:rails_core), firms.first
end
def test_find_all_with_join
- developers_on_project_one = Developer.joins('LEFT JOIN developers_projects ON developers.id = developers_projects.developer_id').
- where('project_id=1').to_a
+ developers_on_project_one = Developer.joins("LEFT JOIN developers_projects ON developers.id = developers_projects.developer_id").
+ where("project_id=1").to_a
assert_equal 3, developers_on_project_one.length
developer_names = developers_on_project_one.map(&:name)
- assert developer_names.include?('David')
- assert developer_names.include?('Jamis')
+ assert_includes developer_names, "David"
+ assert_includes developer_names, "Jamis"
end
def test_find_on_hash_conditions
- assert_equal Topic.all.merge!(:where => {:approved => false}).to_a, Topic.where({ :approved => false }).to_a
+ assert_equal Topic.all.merge!(where: { approved: false }).to_a, Topic.where(approved: false).to_a
end
def test_joins_with_string_array
@@ -565,7 +574,7 @@ class RelationTest < ActiveRecord::TestCase
def test_respond_to_delegates_to_relation
relation = Topic.all
fake_arel = Struct.new(:responds) {
- def respond_to? method, access = false
+ def respond_to?(method, access = false)
responds << [method, access]
end
}.new []
@@ -599,8 +608,8 @@ class RelationTest < ActiveRecord::TestCase
end
def test_eager_association_loading_of_stis_with_multiple_references
- authors = Author.eager_load(:posts => { :special_comments => { :post => [ :special_comments, :very_special_comment ] } }).
- order('comments.body, very_special_comments_posts.body').where('posts.id = 4').to_a
+ authors = Author.eager_load(posts: { special_comments: { post: [ :special_comments, :very_special_comment ] } }).
+ order("comments.body, very_special_comments_posts.body").where("posts.id = 4").to_a
assert_equal [authors(:david)], authors
assert_no_queries do
@@ -611,27 +620,27 @@ class RelationTest < ActiveRecord::TestCase
def test_find_with_preloaded_associations
assert_queries(2) do
- posts = Post.preload(:comments).order('posts.id')
+ posts = Post.preload(:comments).order("posts.id")
assert posts.first.comments.first
end
assert_queries(2) do
- posts = Post.preload(:comments).order('posts.id')
+ posts = Post.preload(:comments).order("posts.id")
assert posts.first.comments.first
end
assert_queries(2) do
- posts = Post.preload(:author).order('posts.id')
+ posts = Post.preload(:author).order("posts.id")
assert posts.first.author
end
assert_queries(2) do
- posts = Post.preload(:author).order('posts.id')
+ posts = Post.preload(:author).order("posts.id")
assert posts.first.author
end
assert_queries(3) do
- posts = Post.preload(:author, :comments).order('posts.id')
+ posts = Post.preload(:author, :comments).order("posts.id")
assert posts.first.author
assert posts.first.comments.first
end
@@ -646,58 +655,58 @@ class RelationTest < ActiveRecord::TestCase
def test_find_with_included_associations
assert_queries(2) do
- posts = Post.includes(:comments).order('posts.id')
+ posts = Post.includes(:comments).order("posts.id")
assert posts.first.comments.first
end
assert_queries(2) do
- posts = Post.all.includes(:comments).order('posts.id')
+ posts = Post.all.includes(:comments).order("posts.id")
assert posts.first.comments.first
end
assert_queries(2) do
- posts = Post.includes(:author).order('posts.id')
+ posts = Post.includes(:author).order("posts.id")
assert posts.first.author
end
assert_queries(3) do
- posts = Post.includes(:author, :comments).order('posts.id')
+ posts = Post.includes(:author, :comments).order("posts.id")
assert posts.first.author
assert posts.first.comments.first
end
end
def test_default_scope_with_conditions_string
- assert_equal Developer.where(name: 'David').map(&:id).sort, DeveloperCalledDavid.all.map(&:id).sort
+ assert_equal Developer.where(name: "David").map(&:id).sort, DeveloperCalledDavid.all.map(&:id).sort
assert_nil DeveloperCalledDavid.create!.name
end
def test_default_scope_with_conditions_hash
- assert_equal Developer.where(name: 'Jamis').map(&:id).sort, DeveloperCalledJamis.all.map(&:id).sort
- assert_equal 'Jamis', DeveloperCalledJamis.create!.name
+ assert_equal Developer.where(name: "Jamis").map(&:id).sort, DeveloperCalledJamis.all.map(&:id).sort
+ assert_equal "Jamis", DeveloperCalledJamis.create!.name
end
def test_default_scoping_finder_methods
- developers = DeveloperCalledDavid.order('id').map(&:id).sort
- assert_equal Developer.where(name: 'David').map(&:id).sort, developers
+ developers = DeveloperCalledDavid.order("id").map(&:id).sort
+ assert_equal Developer.where(name: "David").map(&:id).sort, developers
end
def test_includes_with_select
- query = Post.select('comments_count AS ranking').order('ranking').includes(:comments)
+ query = Post.select("comments_count AS ranking").order("ranking").includes(:comments)
.where(comments: { id: 1 })
- assert_equal ['comments_count AS ranking'], query.select_values
+ assert_equal ["comments_count AS ranking"], query.select_values
assert_equal 1, query.to_a.size
end
def test_preloading_with_associations_and_merges
- post = Post.create! title: 'Uhuu', body: 'body'
+ post = Post.create! title: "Uhuu", body: "body"
reader = Reader.create! post_id: post.id, person_id: 1
- comment = Comment.create! post_id: post.id, body: 'body'
+ comment = Comment.create! post_id: post.id, body: "body"
assert !comment.respond_to?(:readers)
- post_rel = Post.preload(:readers).joins(:readers).where(title: 'Uhuu')
+ post_rel = Post.preload(:readers).joins(:readers).where(title: "Uhuu")
result_comment = Comment.joins(:post).merge(post_rel).to_a.first
assert_equal comment, result_comment
@@ -706,7 +715,7 @@ class RelationTest < ActiveRecord::TestCase
assert_equal [reader], result_comment.post.readers.to_a
end
- post_rel = Post.includes(:readers).where(title: 'Uhuu')
+ post_rel = Post.includes(:readers).where(title: "Uhuu")
result_comment = Comment.joins(:post).merge(post_rel).first
assert_equal comment, result_comment
@@ -717,17 +726,17 @@ class RelationTest < ActiveRecord::TestCase
end
def test_preloading_with_associations_default_scopes_and_merges
- post = Post.create! title: 'Uhuu', body: 'body'
+ post = Post.create! title: "Uhuu", body: "body"
reader = Reader.create! post_id: post.id, person_id: 1
- post_rel = PostWithPreloadDefaultScope.preload(:readers).joins(:readers).where(title: 'Uhuu')
+ post_rel = PostWithPreloadDefaultScope.preload(:readers).joins(:readers).where(title: "Uhuu")
result_post = PostWithPreloadDefaultScope.all.merge(post_rel).to_a.first
assert_no_queries do
assert_equal [reader], result_post.readers.to_a
end
- post_rel = PostWithIncludesDefaultScope.includes(:readers).where(title: 'Uhuu')
+ post_rel = PostWithIncludesDefaultScope.includes(:readers).where(title: "Uhuu")
result_post = PostWithIncludesDefaultScope.all.merge(post_rel).to_a.first
assert_no_queries do
@@ -739,11 +748,11 @@ class RelationTest < ActiveRecord::TestCase
posts = Post.preload(:comments)
post = posts.find { |p| p.id == 1 }
assert_equal 2, post.comments.size
- assert post.comments.include?(comments(:greetings))
+ assert_includes post.comments, comments(:greetings)
post = Post.where("posts.title = 'Welcome to the weblog'").preload(:comments).first
assert_equal 2, post.comments.size
- assert post.comments.include?(comments(:greetings))
+ assert_includes post.comments, comments(:greetings)
posts = Post.preload(:last_comment)
post = posts.find { |p| p.id == 1 }
@@ -752,9 +761,9 @@ class RelationTest < ActiveRecord::TestCase
def test_to_sql_on_eager_join
expected = assert_sql {
- Post.eager_load(:last_comment).order('comments.id DESC').to_a
+ Post.eager_load(:last_comment).order("comments.id DESC").to_a
}.first
- actual = Post.eager_load(:last_comment).order('comments.id DESC').to_sql
+ actual = Post.eager_load(:last_comment).order("comments.id DESC").to_sql
assert_equal expected, actual
end
@@ -765,7 +774,7 @@ class RelationTest < ActiveRecord::TestCase
end
def test_loading_with_one_association_with_non_preload
- posts = Post.eager_load(:last_comment).order('comments.id DESC')
+ posts = Post.eager_load(:last_comment).order("comments.id DESC")
post = posts.find { |p| p.id == 1 }
assert_equal Post.find(1).last_comment, post.last_comment
end
@@ -776,7 +785,6 @@ class RelationTest < ActiveRecord::TestCase
expected_taggings = taggings(:welcome_general, :thinking_general)
assert_no_queries do
- assert_equal expected_taggings, author.taggings.distinct.sort_by(&:id)
assert_equal expected_taggings, author.taggings.uniq.sort_by(&:id)
end
@@ -789,40 +797,40 @@ class RelationTest < ActiveRecord::TestCase
author = Author.all.find_by_id!(authors(:david).id)
assert_equal "David", author.name
- assert_raises(ActiveRecord::RecordNotFound) { Author.all.find_by_id_and_name!(20, 'invalid') }
+ assert_raises(ActiveRecord::RecordNotFound) { Author.all.find_by_id_and_name!(20, "invalid") }
end
def test_find_id
authors = Author.all
david = authors.find(authors(:david).id)
- assert_equal 'David', david.name
+ assert_equal "David", david.name
- assert_raises(ActiveRecord::RecordNotFound) { authors.where(:name => 'lifo').find('42') }
+ assert_raises(ActiveRecord::RecordNotFound) { authors.where(name: "lifo").find("42") }
end
def test_find_ids
- authors = Author.order('id ASC')
+ authors = Author.order("id ASC")
results = authors.find(authors(:david).id, authors(:mary).id)
assert_kind_of Array, results
assert_equal 2, results.size
- assert_equal 'David', results[0].name
- assert_equal 'Mary', results[1].name
+ assert_equal "David", results[0].name
+ assert_equal "Mary", results[1].name
assert_equal results, authors.find([authors(:david).id, authors(:mary).id])
- assert_raises(ActiveRecord::RecordNotFound) { authors.where(:name => 'lifo').find(authors(:david).id, '42') }
- assert_raises(ActiveRecord::RecordNotFound) { authors.find(['42', 43]) }
+ assert_raises(ActiveRecord::RecordNotFound) { authors.where(name: "lifo").find(authors(:david).id, "42") }
+ assert_raises(ActiveRecord::RecordNotFound) { authors.find(["42", 43]) }
end
def test_find_in_empty_array
- authors = Author.all.where(:id => [])
+ authors = Author.all.where(id: [])
assert authors.to_a.blank?
end
def test_where_with_ar_object
author = Author.first
- authors = Author.all.where(:id => author)
+ authors = Author.all.where(id: author)
assert_equal 1, authors.to_a.length
end
@@ -832,15 +840,6 @@ class RelationTest < ActiveRecord::TestCase
assert_equal author, authors.first
end
- class Mary < Author; end
-
- def test_find_by_classname
- Author.create!(:name => Mary.name)
- assert_deprecated do
- assert_equal 1, Author.where(:name => Mary).size
- end
- end
-
def test_find_by_id_with_list_of_ar
author = Author.first
authors = Author.find_by_id([author])
@@ -850,25 +849,25 @@ class RelationTest < ActiveRecord::TestCase
def test_find_all_using_where_twice_should_or_the_relation
david = authors(:david)
relation = Author.unscoped
- relation = relation.where(:name => david.name)
- relation = relation.where(:name => 'Santiago')
- relation = relation.where(:id => david.id)
+ relation = relation.where(name: david.name)
+ relation = relation.where(name: "Santiago")
+ relation = relation.where(id: david.id)
assert_equal [], relation.to_a
end
def test_multi_where_ands_queries
relation = Author.unscoped
david = authors(:david)
- sql = relation.where(:name => david.name).where(:name => 'Santiago').to_sql
- assert_match('AND', sql)
+ sql = relation.where(name: david.name).where(name: "Santiago").to_sql
+ assert_match("AND", sql)
end
def test_find_all_with_multiple_should_use_and
david = authors(:david)
relation = [
- { :name => david.name },
- { :name => 'Santiago' },
- { :name => 'tenderlove' },
+ { name: david.name },
+ { name: "Santiago" },
+ { name: "tenderlove" },
].inject(Author.unscoped) do |memo, param|
memo.where(param)
end
@@ -887,17 +886,17 @@ class RelationTest < ActiveRecord::TestCase
# switching the lines below would succeed in current rails
# assert_queries(2) {
assert_queries(1) {
- relation = Author.where(:id => Author.where(:id => david.id))
+ relation = Author.where(id: Author.where(id: david.id))
assert_equal [david], relation.to_a
}
assert_queries(1) {
- relation = Author.where('id in (?)', Author.where(id: david).select(:id))
+ relation = Author.where("id in (?)", Author.where(id: david).select(:id))
assert_equal [david], relation.to_a
}
assert_queries(1) do
- relation = Author.where('id in (:author_ids)', author_ids: Author.where(id: david).select(:id))
+ relation = Author.where("id in (:author_ids)", author_ids: Author.where(id: david).select(:id))
assert_equal [david], relation.to_a
end
end
@@ -912,13 +911,13 @@ class RelationTest < ActiveRecord::TestCase
end
assert_queries(1) do
- relation = Post.where('id in (?)', david.posts.select(:id))
- assert_equal davids_posts, relation.order(:id).to_a, 'should process Relation as bind variables'
+ relation = Post.where("id in (?)", david.posts.select(:id))
+ assert_equal davids_posts, relation.order(:id).to_a, "should process Relation as bind variables"
end
assert_queries(1) do
- relation = Post.where('id in (:post_ids)', post_ids: david.posts.select(:id))
- assert_equal davids_posts, relation.order(:id).to_a, 'should process Relation as named bind variables'
+ relation = Post.where("id in (:post_ids)", post_ids: david.posts.select(:id))
+ assert_equal davids_posts, relation.order(:id).to_a, "should process Relation as named bind variables"
end
end
@@ -927,7 +926,7 @@ class RelationTest < ActiveRecord::TestCase
# switching the lines below would succeed in current rails
# assert_queries(2) {
assert_queries(1) {
- relation = Minivan.where(:minivan_id => Minivan.where(:name => cool_first.name))
+ relation = Minivan.where(minivan_id: Minivan.where(name: cool_first.name))
assert_equal [cool_first], relation.to_a
}
end
@@ -935,10 +934,10 @@ class RelationTest < ActiveRecord::TestCase
def test_find_all_using_where_with_relation_does_not_alter_select_values
david = authors(:david)
- subquery = Author.where(:id => david.id)
+ subquery = Author.where(id: david.id)
assert_queries(1) {
- relation = Author.where(:id => subquery)
+ relation = Author.where(id: subquery)
assert_equal [david], relation.to_a
}
@@ -948,22 +947,21 @@ class RelationTest < ActiveRecord::TestCase
def test_find_all_using_where_with_relation_with_joins
david = authors(:david)
assert_queries(1) {
- relation = Author.where(:id => Author.joins(:posts).where(:id => david.id))
+ relation = Author.where(id: Author.joins(:posts).where(id: david.id))
assert_equal [david], relation.to_a
}
end
-
def test_find_all_using_where_with_relation_with_select_to_build_subquery
david = authors(:david)
assert_queries(1) {
- relation = Author.where(:name => Author.where(:id => david.id).select(:name))
+ relation = Author.where(name: Author.where(id: david.id).select(:name))
assert_equal [david], relation.to_a
}
end
def test_exists
- davids = Author.where(:name => 'David')
+ davids = Author.where(name: "David")
assert davids.exists?
assert davids.exists?(authors(:david).id)
assert ! davids.exists?(authors(:mary).id)
@@ -971,7 +969,7 @@ class RelationTest < ActiveRecord::TestCase
assert ! davids.exists?(42)
assert ! davids.exists?(davids.new.id)
- fake = Author.where(:name => 'fake author')
+ fake = Author.where(name: "fake author")
assert ! fake.exists?
assert ! fake.exists?(authors(:david).id)
end
@@ -994,45 +992,33 @@ class RelationTest < ActiveRecord::TestCase
end
def test_destroy_all
- davids = Author.where(:name => 'David')
+ davids = Author.where(name: "David")
# Force load
assert_equal [authors(:david)], davids.to_a
assert davids.loaded?
- assert_difference('Author.count', -1) { davids.destroy_all }
+ assert_difference("Author.count", -1) { davids.destroy_all }
assert_equal [], davids.to_a
assert davids.loaded?
end
- def test_destroy_all_with_conditions_is_deprecated
- assert_deprecated do
- assert_difference('Author.count', -1) { Author.destroy_all(name: 'David') }
- end
- end
-
def test_delete_all
- davids = Author.where(:name => 'David')
+ davids = Author.where(name: "David")
- assert_difference('Author.count', -1) { davids.delete_all }
+ assert_difference("Author.count", -1) { davids.delete_all }
assert ! davids.loaded?
end
- def test_delete_all_with_conditions_is_deprecated
- assert_deprecated do
- assert_difference('Author.count', -1) { Author.delete_all(name: 'David') }
- end
- end
-
def test_delete_all_loaded
- davids = Author.where(:name => 'David')
+ davids = Author.where(name: "David")
# Force load
assert_equal [authors(:david)], davids.to_a
assert davids.loaded?
- assert_difference('Author.count', -1) { davids.delete_all }
+ assert_difference("Author.count", -1) { davids.delete_all }
assert_equal [], davids.to_a
assert davids.loaded?
@@ -1042,7 +1028,7 @@ class RelationTest < ActiveRecord::TestCase
assert_raises(ActiveRecord::ActiveRecordError) { Author.limit(10).delete_all }
assert_raises(ActiveRecord::ActiveRecordError) { Author.distinct.delete_all }
assert_raises(ActiveRecord::ActiveRecordError) { Author.group(:name).delete_all }
- assert_raises(ActiveRecord::ActiveRecordError) { Author.having('SUM(id) < 3').delete_all }
+ assert_raises(ActiveRecord::ActiveRecordError) { Author.having("SUM(id) < 3").delete_all }
assert_raises(ActiveRecord::ActiveRecordError) { Author.offset(10).delete_all }
end
@@ -1082,8 +1068,8 @@ class RelationTest < ActiveRecord::TestCase
assert_equal 11, posts.count(:all)
assert_equal 11, posts.count(:id)
- assert_equal 1, posts.where('comments_count > 1').count
- assert_equal 9, posts.where(:comments_count => 0).count
+ assert_equal 1, posts.where("comments_count > 1").count
+ assert_equal 9, posts.where(comments_count: 0).count
end
def test_count_with_block
@@ -1117,24 +1103,24 @@ class RelationTest < ActiveRecord::TestCase
Post.tagged_with(tag.id).update_all title: "rofl"
list = Post.tagged_with(tag.id).all.to_a
assert_operator list.length, :>, 0
- list.each { |post| assert_equal 'rofl', post.title }
+ list.each { |post| assert_equal "rofl", post.title }
end
def test_count_explicit_columns
- Post.update_all(:comments_count => nil)
+ Post.update_all(comments_count: nil)
posts = Post.all
- assert_equal [0], posts.select('comments_count').where('id is not null').group('id').order('id').count.values.uniq
- assert_equal 0, posts.where('id is not null').select('comments_count').count
+ assert_equal [0], posts.select("comments_count").where("id is not null").group("id").order("id").count.values.uniq
+ assert_equal 0, posts.where("id is not null").select("comments_count").count
- assert_equal 11, posts.select('comments_count').count('id')
- assert_equal 0, posts.select('comments_count').count
+ assert_equal 11, posts.select("comments_count").count("id")
+ assert_equal 0, posts.select("comments_count").count
assert_equal 0, posts.count(:comments_count)
- assert_equal 0, posts.count('comments_count')
+ assert_equal 0, posts.count("comments_count")
end
def test_multiple_selects
- post = Post.all.select('comments_count').select('title').order("id ASC").first
+ post = Post.all.select("comments_count").select("title").order("id ASC").first
assert_equal "Welcome to the weblog", post.title
assert_equal 2, post.comments_count
end
@@ -1145,7 +1131,7 @@ class RelationTest < ActiveRecord::TestCase
assert_queries(1) { assert_equal 11, posts.size }
assert ! posts.loaded?
- best_posts = posts.where(:comments_count => 0)
+ best_posts = posts.where(comments_count: 0)
best_posts.to_a # force load
assert_no_queries { assert_equal 9, best_posts.size }
end
@@ -1156,7 +1142,7 @@ class RelationTest < ActiveRecord::TestCase
assert_queries(1) { assert_equal 10, posts.size }
assert ! posts.loaded?
- best_posts = posts.where(:comments_count => 0)
+ best_posts = posts.where(comments_count: 0)
best_posts.to_a # force load
assert_no_queries { assert_equal 9, best_posts.size }
end
@@ -1179,7 +1165,7 @@ class RelationTest < ActiveRecord::TestCase
end
def test_count_complex_chained_relations
- posts = Post.select('comments_count').where('id is not null').group("author_id").where("comments_count > 0")
+ posts = Post.select("comments_count").where("id is not null").group("author_id").where("comments_count > 0")
expected = { 1 => 2 }
assert_equal expected, posts.count
@@ -1191,11 +1177,11 @@ class RelationTest < ActiveRecord::TestCase
assert_queries(1) { assert_equal false, posts.empty? }
assert ! posts.loaded?
- no_posts = posts.where(:title => "")
+ no_posts = posts.where(title: "")
assert_queries(1) { assert_equal true, no_posts.empty? }
assert ! no_posts.loaded?
- best_posts = posts.where(:comments_count => 0)
+ best_posts = posts.where(comments_count: 0)
best_posts.to_a # force load
assert_no_queries { assert_equal false, best_posts.empty? }
end
@@ -1206,7 +1192,7 @@ class RelationTest < ActiveRecord::TestCase
assert_queries(1) { assert_equal false, posts.empty? }
assert ! posts.loaded?
- no_posts = posts.where(:title => "")
+ no_posts = posts.where(title: "")
assert_queries(1) { assert_equal true, no_posts.empty? }
assert ! no_posts.loaded?
end
@@ -1220,14 +1206,14 @@ class RelationTest < ActiveRecord::TestCase
# the SHOW TABLES result to be cached so we don't have to do it again in the block.
#
# This is obviously a rubbish fix but it's the best I can come up with for now...
- posts.where(:id => nil).any?
+ posts.where(id: nil).any?
assert_queries(3) do
assert posts.any? # Uses COUNT()
- assert ! posts.where(:id => nil).any?
+ assert ! posts.where(id: nil).any?
- assert posts.any? {|p| p.id > 0 }
- assert ! posts.any? {|p| p.id <= 0 }
+ assert posts.any? { |p| p.id > 0 }
+ assert ! posts.any? { |p| p.id <= 0 }
end
assert posts.loaded?
@@ -1238,8 +1224,8 @@ class RelationTest < ActiveRecord::TestCase
assert_queries(2) do
assert posts.many? # Uses COUNT()
- assert posts.many? {|p| p.id > 0 }
- assert ! posts.many? {|p| p.id < 2 }
+ assert posts.many? { |p| p.id > 0 }
+ assert ! posts.many? { |p| p.id < 2 }
end
assert posts.loaded?
@@ -1261,8 +1247,8 @@ class RelationTest < ActiveRecord::TestCase
assert ! posts.loaded?
assert_queries(1) do
- assert posts.none? {|p| p.id < 0 }
- assert ! posts.none? {|p| p.id == 1 }
+ assert posts.none? { |p| p.id < 0 }
+ assert ! posts.none? { |p| p.id == 1 }
end
assert posts.loaded?
@@ -1277,8 +1263,8 @@ class RelationTest < ActiveRecord::TestCase
assert ! posts.loaded?
assert_queries(1) do
- assert ! posts.one? {|p| p.id < 3 }
- assert posts.one? {|p| p.id == 1 }
+ assert ! posts.one? { |p| p.id < 3 }
+ assert posts.one? { |p| p.id == 1 }
end
assert posts.loaded?
@@ -1302,11 +1288,11 @@ class RelationTest < ActiveRecord::TestCase
end
def test_scoped_build
- posts = Post.where(:title => 'You told a lie')
+ posts = Post.where(title: "You told a lie")
post = posts.new
assert_kind_of Post, post
- assert_equal 'You told a lie', post.title
+ assert_equal "You told a lie", post.title
end
def test_create
@@ -1316,9 +1302,9 @@ class RelationTest < ActiveRecord::TestCase
assert_kind_of Bird, sparrow
assert !sparrow.persisted?
- hen = birds.where(:name => 'hen').create
+ hen = birds.where(name: "hen").create
assert hen.persisted?
- assert_equal 'hen', hen.name
+ assert_equal "hen", hen.name
end
def test_create_bang
@@ -1326,201 +1312,201 @@ class RelationTest < ActiveRecord::TestCase
assert_raises(ActiveRecord::RecordInvalid) { birds.create! }
- hen = birds.where(:name => 'hen').create!
+ hen = birds.where(name: "hen").create!
assert_kind_of Bird, hen
assert hen.persisted?
- assert_equal 'hen', hen.name
+ assert_equal "hen", hen.name
end
def test_first_or_create
- parrot = Bird.where(:color => 'green').first_or_create(:name => 'parrot')
+ parrot = Bird.where(color: "green").first_or_create(name: "parrot")
assert_kind_of Bird, parrot
assert parrot.persisted?
- assert_equal 'parrot', parrot.name
- assert_equal 'green', parrot.color
+ assert_equal "parrot", parrot.name
+ assert_equal "green", parrot.color
- same_parrot = Bird.where(:color => 'green').first_or_create(:name => 'parakeet')
+ same_parrot = Bird.where(color: "green").first_or_create(name: "parakeet")
assert_kind_of Bird, same_parrot
assert same_parrot.persisted?
assert_equal parrot, same_parrot
end
def test_first_or_create_with_no_parameters
- parrot = Bird.where(:color => 'green').first_or_create
+ parrot = Bird.where(color: "green").first_or_create
assert_kind_of Bird, parrot
assert !parrot.persisted?
- assert_equal 'green', parrot.color
+ assert_equal "green", parrot.color
end
def test_first_or_create_with_block
- parrot = Bird.where(:color => 'green').first_or_create { |bird| bird.name = 'parrot' }
+ parrot = Bird.where(color: "green").first_or_create { |bird| bird.name = "parrot" }
assert_kind_of Bird, parrot
assert parrot.persisted?
- assert_equal 'green', parrot.color
- assert_equal 'parrot', parrot.name
+ assert_equal "green", parrot.color
+ assert_equal "parrot", parrot.name
- same_parrot = Bird.where(:color => 'green').first_or_create { |bird| bird.name = 'parakeet' }
+ same_parrot = Bird.where(color: "green").first_or_create { |bird| bird.name = "parakeet" }
assert_equal parrot, same_parrot
end
def test_first_or_create_with_array
- several_green_birds = Bird.where(:color => 'green').first_or_create([{:name => 'parrot'}, {:name => 'parakeet'}])
+ several_green_birds = Bird.where(color: "green").first_or_create([{ name: "parrot" }, { name: "parakeet" }])
assert_kind_of Array, several_green_birds
several_green_birds.each { |bird| assert bird.persisted? }
- same_parrot = Bird.where(:color => 'green').first_or_create([{:name => 'hummingbird'}, {:name => 'macaw'}])
+ same_parrot = Bird.where(color: "green").first_or_create([{ name: "hummingbird" }, { name: "macaw" }])
assert_kind_of Bird, same_parrot
assert_equal several_green_birds.first, same_parrot
end
def test_first_or_create_bang_with_valid_options
- parrot = Bird.where(:color => 'green').first_or_create!(:name => 'parrot')
+ parrot = Bird.where(color: "green").first_or_create!(name: "parrot")
assert_kind_of Bird, parrot
assert parrot.persisted?
- assert_equal 'parrot', parrot.name
- assert_equal 'green', parrot.color
+ assert_equal "parrot", parrot.name
+ assert_equal "green", parrot.color
- same_parrot = Bird.where(:color => 'green').first_or_create!(:name => 'parakeet')
+ same_parrot = Bird.where(color: "green").first_or_create!(name: "parakeet")
assert_kind_of Bird, same_parrot
assert same_parrot.persisted?
assert_equal parrot, same_parrot
end
def test_first_or_create_bang_with_invalid_options
- assert_raises(ActiveRecord::RecordInvalid) { Bird.where(:color => 'green').first_or_create!(:pirate_id => 1) }
+ assert_raises(ActiveRecord::RecordInvalid) { Bird.where(color: "green").first_or_create!(pirate_id: 1) }
end
def test_first_or_create_bang_with_no_parameters
- assert_raises(ActiveRecord::RecordInvalid) { Bird.where(:color => 'green').first_or_create! }
+ assert_raises(ActiveRecord::RecordInvalid) { Bird.where(color: "green").first_or_create! }
end
def test_first_or_create_bang_with_valid_block
- parrot = Bird.where(:color => 'green').first_or_create! { |bird| bird.name = 'parrot' }
+ parrot = Bird.where(color: "green").first_or_create! { |bird| bird.name = "parrot" }
assert_kind_of Bird, parrot
assert parrot.persisted?
- assert_equal 'green', parrot.color
- assert_equal 'parrot', parrot.name
+ assert_equal "green", parrot.color
+ assert_equal "parrot", parrot.name
- same_parrot = Bird.where(:color => 'green').first_or_create! { |bird| bird.name = 'parakeet' }
+ same_parrot = Bird.where(color: "green").first_or_create! { |bird| bird.name = "parakeet" }
assert_equal parrot, same_parrot
end
def test_first_or_create_bang_with_invalid_block
assert_raise(ActiveRecord::RecordInvalid) do
- Bird.where(:color => 'green').first_or_create! { |bird| bird.pirate_id = 1 }
+ Bird.where(color: "green").first_or_create! { |bird| bird.pirate_id = 1 }
end
end
def test_first_or_create_with_valid_array
- several_green_birds = Bird.where(:color => 'green').first_or_create!([{:name => 'parrot'}, {:name => 'parakeet'}])
+ several_green_birds = Bird.where(color: "green").first_or_create!([{ name: "parrot" }, { name: "parakeet" }])
assert_kind_of Array, several_green_birds
several_green_birds.each { |bird| assert bird.persisted? }
- same_parrot = Bird.where(:color => 'green').first_or_create!([{:name => 'hummingbird'}, {:name => 'macaw'}])
+ same_parrot = Bird.where(color: "green").first_or_create!([{ name: "hummingbird" }, { name: "macaw" }])
assert_kind_of Bird, same_parrot
assert_equal several_green_birds.first, same_parrot
end
def test_first_or_create_with_invalid_array
- assert_raises(ActiveRecord::RecordInvalid) { Bird.where(:color => 'green').first_or_create!([ {:name => 'parrot'}, {:pirate_id => 1} ]) }
+ assert_raises(ActiveRecord::RecordInvalid) { Bird.where(color: "green").first_or_create!([ { name: "parrot" }, { pirate_id: 1 } ]) }
end
def test_first_or_initialize
- parrot = Bird.where(:color => 'green').first_or_initialize(:name => 'parrot')
+ parrot = Bird.where(color: "green").first_or_initialize(name: "parrot")
assert_kind_of Bird, parrot
assert !parrot.persisted?
assert parrot.valid?
assert parrot.new_record?
- assert_equal 'parrot', parrot.name
- assert_equal 'green', parrot.color
+ assert_equal "parrot", parrot.name
+ assert_equal "green", parrot.color
end
def test_first_or_initialize_with_no_parameters
- parrot = Bird.where(:color => 'green').first_or_initialize
+ parrot = Bird.where(color: "green").first_or_initialize
assert_kind_of Bird, parrot
assert !parrot.persisted?
assert !parrot.valid?
assert parrot.new_record?
- assert_equal 'green', parrot.color
+ assert_equal "green", parrot.color
end
def test_first_or_initialize_with_block
- parrot = Bird.where(:color => 'green').first_or_initialize { |bird| bird.name = 'parrot' }
+ parrot = Bird.where(color: "green").first_or_initialize { |bird| bird.name = "parrot" }
assert_kind_of Bird, parrot
assert !parrot.persisted?
assert parrot.valid?
assert parrot.new_record?
- assert_equal 'green', parrot.color
- assert_equal 'parrot', parrot.name
+ assert_equal "green", parrot.color
+ assert_equal "parrot", parrot.name
end
def test_find_or_create_by
- assert_nil Bird.find_by(name: 'bob')
+ assert_nil Bird.find_by(name: "bob")
- bird = Bird.find_or_create_by(name: 'bob')
+ bird = Bird.find_or_create_by(name: "bob")
assert bird.persisted?
- assert_equal bird, Bird.find_or_create_by(name: 'bob')
+ assert_equal bird, Bird.find_or_create_by(name: "bob")
end
def test_find_or_create_by_with_create_with
- assert_nil Bird.find_by(name: 'bob')
+ assert_nil Bird.find_by(name: "bob")
- bird = Bird.create_with(color: 'green').find_or_create_by(name: 'bob')
+ bird = Bird.create_with(color: "green").find_or_create_by(name: "bob")
assert bird.persisted?
- assert_equal 'green', bird.color
+ assert_equal "green", bird.color
- assert_equal bird, Bird.create_with(color: 'blue').find_or_create_by(name: 'bob')
+ assert_equal bird, Bird.create_with(color: "blue").find_or_create_by(name: "bob")
end
def test_find_or_create_by!
- assert_raises(ActiveRecord::RecordInvalid) { Bird.find_or_create_by!(color: 'green') }
+ assert_raises(ActiveRecord::RecordInvalid) { Bird.find_or_create_by!(color: "green") }
end
def test_find_or_initialize_by
- assert_nil Bird.find_by(name: 'bob')
+ assert_nil Bird.find_by(name: "bob")
- bird = Bird.find_or_initialize_by(name: 'bob')
+ bird = Bird.find_or_initialize_by(name: "bob")
assert bird.new_record?
bird.save!
- assert_equal bird, Bird.find_or_initialize_by(name: 'bob')
+ assert_equal bird, Bird.find_or_initialize_by(name: "bob")
end
def test_explicit_create_scope
- hens = Bird.where(:name => 'hen')
- assert_equal 'hen', hens.new.name
+ hens = Bird.where(name: "hen")
+ assert_equal "hen", hens.new.name
- hens = hens.create_with(:name => 'cock')
- assert_equal 'cock', hens.new.name
+ hens = hens.create_with(name: "cock")
+ assert_equal "cock", hens.new.name
end
def test_except
- relation = Post.where(:author_id => 1).order('id ASC').limit(1)
+ relation = Post.where(author_id: 1).order("id ASC").limit(1)
assert_equal [posts(:welcome)], relation.to_a
author_posts = relation.except(:order, :limit)
- assert_equal Post.where(:author_id => 1).to_a, author_posts.to_a
+ assert_equal Post.where(author_id: 1).to_a, author_posts.to_a
all_posts = relation.except(:where, :order, :limit)
assert_equal Post.all, all_posts
end
def test_only
- relation = Post.where(:author_id => 1).order('id ASC').limit(1)
+ relation = Post.where(author_id: 1).order("id ASC").limit(1)
assert_equal [posts(:welcome)], relation.to_a
author_posts = relation.only(:where)
- assert_equal Post.where(:author_id => 1).to_a, author_posts.to_a
+ assert_equal Post.where(author_id: 1).to_a, author_posts.to_a
all_posts = relation.only(:limit)
- assert_equal Post.limit(1).to_a.first, all_posts.first
+ assert_equal Post.limit(1).to_a, all_posts.to_a
end
def test_anonymous_extension
- relation = Post.where(:author_id => 1).order('id ASC').extending do
+ relation = Post.where(author_id: 1).order("id ASC").extending do
def author
- 'lifo'
+ "lifo"
end
end
@@ -1529,7 +1515,7 @@ class RelationTest < ActiveRecord::TestCase
end
def test_named_extension
- relation = Post.where(:author_id => 1).order('id ASC').extending(Post::NamedExtension)
+ relation = Post.where(author_id: 1).order("id ASC").extending(Post::NamedExtension)
assert_equal "lifo", relation.author
assert_equal "lifo", relation.limit(1).author
end
@@ -1539,29 +1525,29 @@ class RelationTest < ActiveRecord::TestCase
end
def test_default_scope_order_with_scope_order
- assert_equal 'zyke', CoolCar.order_using_new_style.limit(1).first.name
- assert_equal 'zyke', FastCar.order_using_new_style.limit(1).first.name
+ assert_equal "zyke", CoolCar.order_using_new_style.limit(1).first.name
+ assert_equal "zyke", FastCar.order_using_new_style.limit(1).first.name
end
def test_order_using_scoping
- car1 = CoolCar.order('id DESC').scoping do
- CoolCar.all.merge!(order: 'id asc').first
+ car1 = CoolCar.order("id DESC").scoping do
+ CoolCar.all.merge!(order: "id asc").first
end
- assert_equal 'zyke', car1.name
+ assert_equal "zyke", car1.name
- car2 = FastCar.order('id DESC').scoping do
- FastCar.all.merge!(order: 'id asc').first
+ car2 = FastCar.order("id DESC").scoping do
+ FastCar.all.merge!(order: "id asc").first
end
- assert_equal 'zyke', car2.name
+ assert_equal "zyke", car2.name
end
def test_unscoped_block_style
- assert_equal 'honda', CoolCar.unscoped { CoolCar.order_using_new_style.limit(1).first.name}
- assert_equal 'honda', FastCar.unscoped { FastCar.order_using_new_style.limit(1).first.name}
+ assert_equal "honda", CoolCar.unscoped { CoolCar.order_using_new_style.limit(1).first.name }
+ assert_equal "honda", FastCar.unscoped { FastCar.order_using_new_style.limit(1).first.name }
end
def test_intersection_with_array
- relation = Author.where(:name => "David")
+ relation = Author.where(name: "David")
rails_author = relation.first
assert_equal [rails_author], [rails_author] & relation
@@ -1573,7 +1559,7 @@ class RelationTest < ActiveRecord::TestCase
end
def test_ordering_with_extra_spaces
- assert_equal authors(:david), Author.order('id DESC , name DESC').last
+ assert_equal authors(:david), Author.order("id DESC , name DESC").last
end
def test_update_all_with_blank_argument
@@ -1581,87 +1567,81 @@ class RelationTest < ActiveRecord::TestCase
end
def test_update_all_with_joins
- comments = Comment.joins(:post).where('posts.id' => posts(:welcome).id)
+ comments = Comment.joins(:post).where("posts.id" => posts(:welcome).id)
count = comments.count
- assert_equal count, comments.update_all(:post_id => posts(:thinking).id)
+ assert_equal count, comments.update_all(post_id: posts(:thinking).id)
assert_equal posts(:thinking), comments(:greetings).post
end
def test_update_all_with_joins_and_limit
- comments = Comment.joins(:post).where('posts.id' => posts(:welcome).id).limit(1)
- assert_equal 1, comments.update_all(:post_id => posts(:thinking).id)
+ comments = Comment.joins(:post).where("posts.id" => posts(:welcome).id).limit(1)
+ assert_equal 1, comments.update_all(post_id: posts(:thinking).id)
end
def test_update_all_with_joins_and_limit_and_order
- comments = Comment.joins(:post).where('posts.id' => posts(:welcome).id).order('comments.id').limit(1)
- assert_equal 1, comments.update_all(:post_id => posts(:thinking).id)
+ comments = Comment.joins(:post).where("posts.id" => posts(:welcome).id).order("comments.id").limit(1)
+ assert_equal 1, comments.update_all(post_id: posts(:thinking).id)
assert_equal posts(:thinking), comments(:greetings).post
assert_equal posts(:welcome), comments(:more_greetings).post
end
def test_update_all_with_joins_and_offset
- all_comments = Comment.joins(:post).where('posts.id' => posts(:welcome).id)
+ all_comments = Comment.joins(:post).where("posts.id" => posts(:welcome).id)
count = all_comments.count
comments = all_comments.offset(1)
- assert_equal count - 1, comments.update_all(:post_id => posts(:thinking).id)
+ assert_equal count - 1, comments.update_all(post_id: posts(:thinking).id)
end
def test_update_all_with_joins_and_offset_and_order
- all_comments = Comment.joins(:post).where('posts.id' => posts(:welcome).id).order('posts.id', 'comments.id')
+ all_comments = Comment.joins(:post).where("posts.id" => posts(:welcome).id).order("posts.id", "comments.id")
count = all_comments.count
comments = all_comments.offset(1)
- assert_equal count - 1, comments.update_all(:post_id => posts(:thinking).id)
+ assert_equal count - 1, comments.update_all(post_id: posts(:thinking).id)
assert_equal posts(:thinking), comments(:more_greetings).post
assert_equal posts(:welcome), comments(:greetings).post
end
def test_update_on_relation
- topic1 = TopicWithCallbacks.create! title: 'arel', author_name: nil
- topic2 = TopicWithCallbacks.create! title: 'activerecord', author_name: nil
+ topic1 = TopicWithCallbacks.create! title: "arel", author_name: nil
+ topic2 = TopicWithCallbacks.create! title: "activerecord", author_name: nil
topics = TopicWithCallbacks.where(id: [topic1.id, topic2.id])
- topics.update(title: 'adequaterecord')
+ topics.update(title: "adequaterecord")
- assert_equal 'adequaterecord', topic1.reload.title
- assert_equal 'adequaterecord', topic2.reload.title
+ assert_equal "adequaterecord", topic1.reload.title
+ assert_equal "adequaterecord", topic2.reload.title
# Testing that the before_update callbacks have run
- assert_equal 'David', topic1.reload.author_name
- assert_equal 'David', topic2.reload.author_name
+ assert_equal "David", topic1.reload.author_name
+ assert_equal "David", topic2.reload.author_name
end
def test_update_on_relation_passing_active_record_object_is_deprecated
- topic = Topic.create!(title: 'Foo', author_name: nil)
+ topic = Topic.create!(title: "Foo", author_name: nil)
assert_deprecated(/update/) do
- Topic.where(id: topic.id).update(topic, title: 'Bar')
+ Topic.where(id: topic.id).update(topic, title: "Bar")
end
end
def test_distinct
- tag1 = Tag.create(:name => 'Foo')
- tag2 = Tag.create(:name => 'Foo')
+ tag1 = Tag.create(name: "Foo")
+ tag2 = Tag.create(name: "Foo")
- query = Tag.select(:name).where(:id => [tag1.id, tag2.id])
+ query = Tag.select(:name).where(id: [tag1.id, tag2.id])
- assert_equal ['Foo', 'Foo'], query.map(&:name)
+ assert_equal ["Foo", "Foo"], query.map(&:name)
assert_sql(/DISTINCT/) do
- assert_equal ['Foo'], query.distinct.map(&:name)
- assert_deprecated { assert_equal ['Foo'], query.uniq.map(&:name) }
+ assert_equal ["Foo"], query.distinct.map(&:name)
end
assert_sql(/DISTINCT/) do
- assert_equal ['Foo'], query.distinct(true).map(&:name)
- assert_deprecated { assert_equal ['Foo'], query.uniq(true).map(&:name) }
- end
- assert_equal ['Foo', 'Foo'], query.distinct(true).distinct(false).map(&:name)
-
- assert_deprecated do
- assert_equal ['Foo', 'Foo'], query.uniq(true).uniq(false).map(&:name)
+ assert_equal ["Foo"], query.distinct(true).map(&:name)
end
+ assert_equal ["Foo", "Foo"], query.distinct(true).distinct(false).map(&:name)
end
def test_doesnt_add_having_values_if_options_are_blank
- scope = Post.having('')
+ scope = Post.having("")
assert scope.having_clause.empty?
scope = Post.having([])
@@ -1701,62 +1681,62 @@ class RelationTest < ActiveRecord::TestCase
end
def test_automatically_added_where_references
- scope = Post.where(:comments => { :body => "Bla" })
- assert_equal ['comments'], scope.references_values
+ scope = Post.where(comments: { body: "Bla" })
+ assert_equal ["comments"], scope.references_values
- scope = Post.where('comments.body' => 'Bla')
- assert_equal ['comments'], scope.references_values
+ scope = Post.where("comments.body" => "Bla")
+ assert_equal ["comments"], scope.references_values
end
def test_automatically_added_where_not_references
scope = Post.where.not(comments: { body: "Bla" })
- assert_equal ['comments'], scope.references_values
+ assert_equal ["comments"], scope.references_values
- scope = Post.where.not('comments.body' => 'Bla')
- assert_equal ['comments'], scope.references_values
+ scope = Post.where.not("comments.body" => "Bla")
+ assert_equal ["comments"], scope.references_values
end
def test_automatically_added_having_references
- scope = Post.having(:comments => { :body => "Bla" })
- assert_equal ['comments'], scope.references_values
+ scope = Post.having(comments: { body: "Bla" })
+ assert_equal ["comments"], scope.references_values
- scope = Post.having('comments.body' => 'Bla')
- assert_equal ['comments'], scope.references_values
+ scope = Post.having("comments.body" => "Bla")
+ assert_equal ["comments"], scope.references_values
end
def test_automatically_added_order_references
- scope = Post.order('comments.body')
- assert_equal ['comments'], scope.references_values
+ scope = Post.order("comments.body")
+ assert_equal ["comments"], scope.references_values
- scope = Post.order('comments.body', 'yaks.body')
- assert_equal ['comments', 'yaks'], scope.references_values
+ scope = Post.order("comments.body", "yaks.body")
+ assert_equal ["comments", "yaks"], scope.references_values
# Don't infer yaks, let's not go down that road again...
- scope = Post.order('comments.body, yaks.body')
- assert_equal ['comments'], scope.references_values
+ scope = Post.order("comments.body, yaks.body")
+ assert_equal ["comments"], scope.references_values
- scope = Post.order('comments.body asc')
- assert_equal ['comments'], scope.references_values
+ scope = Post.order("comments.body asc")
+ assert_equal ["comments"], scope.references_values
- scope = Post.order('foo(comments.body)')
+ scope = Post.order("foo(comments.body)")
assert_equal [], scope.references_values
end
def test_automatically_added_reorder_references
- scope = Post.reorder('comments.body')
+ scope = Post.reorder("comments.body")
assert_equal %w(comments), scope.references_values
- scope = Post.reorder('comments.body', 'yaks.body')
+ scope = Post.reorder("comments.body", "yaks.body")
assert_equal %w(comments yaks), scope.references_values
# Don't infer yaks, let's not go down that road again...
- scope = Post.reorder('comments.body, yaks.body')
+ scope = Post.reorder("comments.body, yaks.body")
assert_equal %w(comments), scope.references_values
- scope = Post.reorder('comments.body asc')
+ scope = Post.reorder("comments.body asc")
assert_equal %w(comments), scope.references_values
- scope = Post.reorder('foo(comments.body)')
+ scope = Post.reorder("foo(comments.body)")
assert_equal [], scope.references_values
end
@@ -1803,11 +1783,11 @@ class RelationTest < ActiveRecord::TestCase
end
test "find_by with multi-arg conditions returns the first matching record" do
- assert_equal posts(:eager_other), Post.order(:id).find_by('author_id = ?', 2)
+ assert_equal posts(:eager_other), Post.order(:id).find_by("author_id = ?", 2)
end
test "find_by returns nil if the record is missing" do
- assert_equal nil, Post.all.find_by("1 = 0")
+ assert_nil Post.all.find_by("1 = 0")
end
test "find_by doesn't have implicit ordering" do
@@ -1827,7 +1807,7 @@ class RelationTest < ActiveRecord::TestCase
end
test "find_by! with multi-arg conditions returns the first matching record" do
- assert_equal posts(:eager_other), Post.order(:id).find_by!('author_id = ?', 2)
+ assert_equal posts(:eager_other), Post.order(:id).find_by!("author_id = ?", 2)
end
test "find_by! doesn't have implicit ordering" do
@@ -1849,7 +1829,7 @@ class RelationTest < ActiveRecord::TestCase
relation.to_a
assert_raises(ActiveRecord::ImmutableRelation) do
- relation.where! 'foo'
+ relation.where! "foo"
end
end
@@ -1867,7 +1847,7 @@ class RelationTest < ActiveRecord::TestCase
relation.to_a
assert_raises(ActiveRecord::ImmutableRelation) do
- relation.merge! where: 'foo'
+ relation.merge! where: "foo"
end
end
@@ -1909,19 +1889,19 @@ class RelationTest < ActiveRecord::TestCase
end
end
- test 'using a custom table affects the wheres' do
- table_alias = Post.arel_table.alias('omg_posts')
+ test "using a custom table affects the wheres" do
+ table_alias = Post.arel_table.alias("omg_posts")
table_metadata = ActiveRecord::TableMetadata.new(Post, table_alias)
predicate_builder = ActiveRecord::PredicateBuilder.new(table_metadata)
relation = ActiveRecord::Relation.new(Post, table_alias, predicate_builder)
- relation.where!(:foo => "bar")
+ relation.where!(foo: "bar")
node = relation.arel.constraints.first.grep(Arel::Attributes::Attribute).first
assert_equal table_alias, node.relation
end
- test '#load' do
+ test "#load" do
relation = Post.all
assert_queries(1) do
assert_equal relation, relation.load
@@ -1929,9 +1909,9 @@ class RelationTest < ActiveRecord::TestCase
assert_no_queries { relation.to_a }
end
- test 'group with select and includes' do
- authors_count = Post.select('author_id, COUNT(author_id) AS num_posts').
- group('author_id').order('author_id').includes(:author).to_a
+ test "group with select and includes" do
+ authors_count = Post.select("author_id, COUNT(author_id) AS num_posts").
+ group("author_id").order("author_id").includes(:author).to_a
assert_no_queries do
result = authors_count.map do |post|
@@ -1956,8 +1936,8 @@ class RelationTest < ActiveRecord::TestCase
end
def test_unscope_removes_binds
- left = Post.where(id: Arel::Nodes::BindParam.new)
- column = Post.columns_hash['id']
+ left = Post.where(id: Arel::Nodes::BindParam.new)
+ column = Post.columns_hash["id"]
left.bind_values += [[column, 20]]
relation = left.unscope(where: :id)
@@ -1965,8 +1945,8 @@ class RelationTest < ActiveRecord::TestCase
end
def test_merging_removes_rhs_bind_parameters
- left = Post.where(id: 20)
- right = Post.where(id: [1,2,3,4])
+ left = Post.where(id: 20)
+ right = Post.where(id: [1, 2, 3, 4])
merged = left.merge(right)
assert_equal [], merged.bind_values
@@ -1992,7 +1972,7 @@ class RelationTest < ActiveRecord::TestCase
end
def test_relation_join_method
- assert_equal 'Thank you for the welcome,Thank you again for the welcome', Post.first.comments.join(",")
+ assert_equal "Thank you for the welcome,Thank you again for the welcome", Post.first.comments.join(",")
end
def test_connection_adapters_can_reorder_binds
diff --git a/activerecord/test/cases/reload_models_test.rb b/activerecord/test/cases/reload_models_test.rb
index 431fbf1297..5dc9d6d8b7 100644
--- a/activerecord/test/cases/reload_models_test.rb
+++ b/activerecord/test/cases/reload_models_test.rb
@@ -1,13 +1,13 @@
require "cases/helper"
-require 'models/owner'
-require 'models/pet'
+require "models/owner"
+require "models/pet"
class ReloadModelsTest < ActiveRecord::TestCase
fixtures :pets, :owners
def test_has_one_with_reload
- pet = Pet.find_by_name('parrot')
- pet.owner = Owner.find_by_name('ashley')
+ pet = Pet.find_by_name("parrot")
+ pet.owner = Owner.find_by_name("ashley")
# Reload the class Owner, simulating auto-reloading of model classes in a
# development environment. Note that meanwhile the class Pet is not
@@ -15,8 +15,8 @@ class ReloadModelsTest < ActiveRecord::TestCase
Object.class_eval { remove_const :Owner }
Kernel.load(File.expand_path(File.join(File.dirname(__FILE__), "../models/owner.rb")))
- pet = Pet.find_by_name('parrot')
- pet.owner = Owner.find_by_name('ashley')
- assert_equal pet.owner, Owner.find_by_name('ashley')
+ pet = Pet.find_by_name("parrot")
+ pet.owner = Owner.find_by_name("ashley")
+ assert_equal pet.owner, Owner.find_by_name("ashley")
end
end
diff --git a/activerecord/test/cases/result_test.rb b/activerecord/test/cases/result_test.rb
index 83dc5347e0..949086fda0 100644
--- a/activerecord/test/cases/result_test.rb
+++ b/activerecord/test/cases/result_test.rb
@@ -3,10 +3,10 @@ require "cases/helper"
module ActiveRecord
class ResultTest < ActiveRecord::TestCase
def result
- Result.new(['col_1', 'col_2'], [
- ['row 1 col 1', 'row 1 col 2'],
- ['row 2 col 1', 'row 2 col 2'],
- ['row 3 col 1', 'row 3 col 2'],
+ Result.new(["col_1", "col_2"], [
+ ["row 1 col 1", "row 1 col 2"],
+ ["row 2 col 1", "row 2 col 2"],
+ ["row 3 col 1", "row 3 col 2"],
])
end
@@ -16,31 +16,31 @@ module ActiveRecord
test "to_hash returns row_hashes" do
assert_equal [
- {'col_1' => 'row 1 col 1', 'col_2' => 'row 1 col 2'},
- {'col_1' => 'row 2 col 1', 'col_2' => 'row 2 col 2'},
- {'col_1' => 'row 3 col 1', 'col_2' => 'row 3 col 2'},
+ { "col_1" => "row 1 col 1", "col_2" => "row 1 col 2" },
+ { "col_1" => "row 2 col 1", "col_2" => "row 2 col 2" },
+ { "col_1" => "row 3 col 1", "col_2" => "row 3 col 2" },
], result.to_hash
end
test "first returns first row as a hash" do
assert_equal(
- {'col_1' => 'row 1 col 1', 'col_2' => 'row 1 col 2'}, result.first)
+ { "col_1" => "row 1 col 1", "col_2" => "row 1 col 2" }, result.first)
end
test "last returns last row as a hash" do
assert_equal(
- {'col_1' => 'row 3 col 1', 'col_2' => 'row 3 col 2'}, result.last)
+ { "col_1" => "row 3 col 1", "col_2" => "row 3 col 2" }, result.last)
end
test "each with block returns row hashes" do
result.each do |row|
- assert_equal ['col_1', 'col_2'], row.keys
+ assert_equal ["col_1", "col_2"], row.keys
end
end
test "each without block returns an enumerator" do
result.each.with_index do |row, index|
- assert_equal ['col_1', 'col_2'], row.keys
+ assert_equal ["col_1", "col_2"], row.keys
assert_kind_of Integer, index
end
end
diff --git a/activerecord/test/cases/sanitize_test.rb b/activerecord/test/cases/sanitize_test.rb
index 239f63d27b..23bcb0af1e 100644
--- a/activerecord/test/cases/sanitize_test.rb
+++ b/activerecord/test/cases/sanitize_test.rb
@@ -1,7 +1,7 @@
require "cases/helper"
-require 'models/binary'
-require 'models/author'
-require 'models/post'
+require "models/binary"
+require "models/author"
+require "models/post"
class SanitizeTest < ActiveRecord::TestCase
def setup
@@ -12,8 +12,8 @@ class SanitizeTest < ActiveRecord::TestCase
assert_equal "name='#{quoted_bambi}'", Binary.send(:sanitize_sql_array, ["name='%s'", "Bambi"])
assert_equal "name='#{quoted_bambi}'", Binary.send(:sanitize_sql_array, ["name='%s'", "Bambi".mb_chars])
quoted_bambi_and_thumper = ActiveRecord::Base.connection.quote_string("Bambi\nand\nThumper")
- assert_equal "name='#{quoted_bambi_and_thumper}'",Binary.send(:sanitize_sql_array, ["name='%s'", "Bambi\nand\nThumper"])
- assert_equal "name='#{quoted_bambi_and_thumper}'",Binary.send(:sanitize_sql_array, ["name='%s'", "Bambi\nand\nThumper".mb_chars])
+ assert_equal "name='#{quoted_bambi_and_thumper}'", Binary.send(:sanitize_sql_array, ["name='%s'", "Bambi\nand\nThumper"])
+ assert_equal "name='#{quoted_bambi_and_thumper}'", Binary.send(:sanitize_sql_array, ["name='%s'", "Bambi\nand\nThumper".mb_chars])
end
def test_sanitize_sql_array_handles_bind_variables
@@ -36,42 +36,42 @@ class SanitizeTest < ActiveRecord::TestCase
end
def test_sanitize_sql_array_handles_relations
- david = Author.create!(name: 'David')
+ david = Author.create!(name: "David")
david_posts = david.posts.select(:id)
sub_query_pattern = /\(\bselect\b.*?\bwhere\b.*?\)/i
- select_author_sql = Post.send(:sanitize_sql_array, ['id in (?)', david_posts])
- assert_match(sub_query_pattern, select_author_sql, 'should sanitize `Relation` as subquery for bind variables')
+ select_author_sql = Post.send(:sanitize_sql_array, ["id in (?)", david_posts])
+ assert_match(sub_query_pattern, select_author_sql, "should sanitize `Relation` as subquery for bind variables")
- select_author_sql = Post.send(:sanitize_sql_array, ['id in (:post_ids)', post_ids: david_posts])
- assert_match(sub_query_pattern, select_author_sql, 'should sanitize `Relation` as subquery for named bind variables')
+ select_author_sql = Post.send(:sanitize_sql_array, ["id in (:post_ids)", post_ids: david_posts])
+ assert_match(sub_query_pattern, select_author_sql, "should sanitize `Relation` as subquery for named bind variables")
end
def test_sanitize_sql_array_handles_empty_statement
- select_author_sql = Post.send(:sanitize_sql_array, [''])
- assert_equal('', select_author_sql)
+ select_author_sql = Post.send(:sanitize_sql_array, [""])
+ assert_equal("", select_author_sql)
end
def test_sanitize_sql_like
- assert_equal '100\%', Binary.send(:sanitize_sql_like, '100%')
- assert_equal 'snake\_cased\_string', Binary.send(:sanitize_sql_like, 'snake_cased_string')
+ assert_equal '100\%', Binary.send(:sanitize_sql_like, "100%")
+ assert_equal 'snake\_cased\_string', Binary.send(:sanitize_sql_like, "snake_cased_string")
assert_equal 'C:\\\\Programs\\\\MsPaint', Binary.send(:sanitize_sql_like, 'C:\\Programs\\MsPaint')
- assert_equal 'normal string 42', Binary.send(:sanitize_sql_like, 'normal string 42')
+ assert_equal "normal string 42", Binary.send(:sanitize_sql_like, "normal string 42")
end
def test_sanitize_sql_like_with_custom_escape_character
- assert_equal '100!%', Binary.send(:sanitize_sql_like, '100%', '!')
- assert_equal 'snake!_cased!_string', Binary.send(:sanitize_sql_like, 'snake_cased_string', '!')
- assert_equal 'great!!', Binary.send(:sanitize_sql_like, 'great!', '!')
- assert_equal 'C:\\Programs\\MsPaint', Binary.send(:sanitize_sql_like, 'C:\\Programs\\MsPaint', '!')
- assert_equal 'normal string 42', Binary.send(:sanitize_sql_like, 'normal string 42', '!')
+ assert_equal "100!%", Binary.send(:sanitize_sql_like, "100%", "!")
+ assert_equal "snake!_cased!_string", Binary.send(:sanitize_sql_like, "snake_cased_string", "!")
+ assert_equal "great!!", Binary.send(:sanitize_sql_like, "great!", "!")
+ assert_equal 'C:\\Programs\\MsPaint', Binary.send(:sanitize_sql_like, 'C:\\Programs\\MsPaint', "!")
+ assert_equal "normal string 42", Binary.send(:sanitize_sql_like, "normal string 42", "!")
end
def test_sanitize_sql_like_example_use_case
searchable_post = Class.new(Post) do
def self.search(term)
- where("title LIKE ?", sanitize_sql_like(term, '!'))
+ where("title LIKE ?", sanitize_sql_like(term, "!"))
end
end
@@ -81,25 +81,25 @@ class SanitizeTest < ActiveRecord::TestCase
end
def test_bind_arity
- assert_nothing_raised { bind '' }
- assert_raise(ActiveRecord::PreparedStatementInvalid) { bind '', 1 }
+ assert_nothing_raised { bind "" }
+ assert_raise(ActiveRecord::PreparedStatementInvalid) { bind "", 1 }
- assert_raise(ActiveRecord::PreparedStatementInvalid) { bind '?' }
- assert_nothing_raised { bind '?', 1 }
- assert_raise(ActiveRecord::PreparedStatementInvalid) { bind '?', 1, 1 }
+ assert_raise(ActiveRecord::PreparedStatementInvalid) { bind "?" }
+ assert_nothing_raised { bind "?", 1 }
+ assert_raise(ActiveRecord::PreparedStatementInvalid) { bind "?", 1, 1 }
end
def test_named_bind_variables
- assert_equal '1', bind(':a', :a => 1) # ' ruby-mode
- assert_equal '1 1', bind(':a :a', :a => 1) # ' ruby-mode
+ assert_equal "1", bind(":a", a: 1) # ' ruby-mode
+ assert_equal "1 1", bind(":a :a", a: 1) # ' ruby-mode
- assert_nothing_raised { bind("'+00:00'", :foo => "bar") }
+ assert_nothing_raised { bind("'+00:00'", foo: "bar") }
end
def test_named_bind_arity
- assert_nothing_raised { bind "name = :name", { name: "37signals" } }
- assert_nothing_raised { bind "name = :name", { name: "37signals", id: 1 } }
- assert_raise(ActiveRecord::PreparedStatementInvalid) { bind "name = :name", { id: 1 } }
+ assert_nothing_raised { bind "name = :name", name: "37signals" }
+ assert_nothing_raised { bind "name = :name", name: "37signals", id: 1 }
+ assert_raise(ActiveRecord::PreparedStatementInvalid) { bind "name = :name", id: 1 }
end
class SimpleEnumerable
@@ -117,50 +117,50 @@ class SanitizeTest < ActiveRecord::TestCase
def test_bind_enumerable
quoted_abc = %(#{ActiveRecord::Base.connection.quote('a')},#{ActiveRecord::Base.connection.quote('b')},#{ActiveRecord::Base.connection.quote('c')})
- assert_equal '1,2,3', bind('?', [1, 2, 3])
- assert_equal quoted_abc, bind('?', %w(a b c))
+ assert_equal "1,2,3", bind("?", [1, 2, 3])
+ assert_equal quoted_abc, bind("?", %w(a b c))
- assert_equal '1,2,3', bind(':a', :a => [1, 2, 3])
- assert_equal quoted_abc, bind(':a', :a => %w(a b c)) # '
+ assert_equal "1,2,3", bind(":a", a: [1, 2, 3])
+ assert_equal quoted_abc, bind(":a", a: %w(a b c)) # '
- assert_equal '1,2,3', bind('?', SimpleEnumerable.new([1, 2, 3]))
- assert_equal quoted_abc, bind('?', SimpleEnumerable.new(%w(a b c)))
+ assert_equal "1,2,3", bind("?", SimpleEnumerable.new([1, 2, 3]))
+ assert_equal quoted_abc, bind("?", SimpleEnumerable.new(%w(a b c)))
- assert_equal '1,2,3', bind(':a', :a => SimpleEnumerable.new([1, 2, 3]))
- assert_equal quoted_abc, bind(':a', :a => SimpleEnumerable.new(%w(a b c))) # '
+ assert_equal "1,2,3", bind(":a", a: SimpleEnumerable.new([1, 2, 3]))
+ assert_equal quoted_abc, bind(":a", a: SimpleEnumerable.new(%w(a b c))) # '
end
def test_bind_empty_enumerable
quoted_nil = ActiveRecord::Base.connection.quote(nil)
- assert_equal quoted_nil, bind('?', [])
- assert_equal " in (#{quoted_nil})", bind(' in (?)', [])
- assert_equal "foo in (#{quoted_nil})", bind('foo in (?)', [])
+ assert_equal quoted_nil, bind("?", [])
+ assert_equal " in (#{quoted_nil})", bind(" in (?)", [])
+ assert_equal "foo in (#{quoted_nil})", bind("foo in (?)", [])
end
def test_bind_empty_string
- quoted_empty = ActiveRecord::Base.connection.quote('')
- assert_equal quoted_empty, bind('?', '')
+ quoted_empty = ActiveRecord::Base.connection.quote("")
+ assert_equal quoted_empty, bind("?", "")
end
def test_bind_chars
quoted_bambi = ActiveRecord::Base.connection.quote("Bambi")
quoted_bambi_and_thumper = ActiveRecord::Base.connection.quote("Bambi\nand\nThumper")
- assert_equal "name=#{quoted_bambi}", bind('name=?', "Bambi")
- assert_equal "name=#{quoted_bambi_and_thumper}", bind('name=?', "Bambi\nand\nThumper")
- assert_equal "name=#{quoted_bambi}", bind('name=?', "Bambi".mb_chars)
- assert_equal "name=#{quoted_bambi_and_thumper}", bind('name=?', "Bambi\nand\nThumper".mb_chars)
+ assert_equal "name=#{quoted_bambi}", bind("name=?", "Bambi")
+ assert_equal "name=#{quoted_bambi_and_thumper}", bind("name=?", "Bambi\nand\nThumper")
+ assert_equal "name=#{quoted_bambi}", bind("name=?", "Bambi".mb_chars)
+ assert_equal "name=#{quoted_bambi_and_thumper}", bind("name=?", "Bambi\nand\nThumper".mb_chars)
end
def test_bind_record
o = Struct.new(:quoted_id).new(1)
- assert_equal '1', bind('?', o)
+ assert_equal "1", bind("?", o)
os = [o] * 3
- assert_equal '1,1,1', bind('?', os)
+ assert_equal "1,1,1", bind("?", os)
end
def test_named_bind_with_postgresql_type_casts
- l = Proc.new { bind(":a::integer '2009-01-01'::date", :a => '10') }
+ l = Proc.new { bind(":a::integer '2009-01-01'::date", a: "10") }
assert_nothing_raised(&l)
assert_equal "#{ActiveRecord::Base.connection.quote('10')}::integer '2009-01-01'::date", l.call
end
diff --git a/activerecord/test/cases/schema_dumper_test.rb b/activerecord/test/cases/schema_dumper_test.rb
index 12f4196724..bea78d2a95 100644
--- a/activerecord/test/cases/schema_dumper_test.rb
+++ b/activerecord/test/cases/schema_dumper_test.rb
@@ -1,5 +1,5 @@
require "cases/helper"
-require 'support/schema_dumping_helper'
+require "support/schema_dumping_helper"
class SchemaDumperTest < ActiveRecord::TestCase
include SchemaDumpingHelper
@@ -20,7 +20,7 @@ class SchemaDumperTest < ActiveRecord::TestCase
def test_dump_schema_information_outputs_lexically_ordered_versions
versions = %w{ 20100101010101 20100201010101 20100301010101 }
versions.reverse_each do |v|
- ActiveRecord::SchemaMigration.create!(:version => v)
+ ActiveRecord::SchemaMigration.create!(version: v)
end
schema_info = ActiveRecord::Base.connection.dump_schema_information
@@ -37,7 +37,7 @@ class SchemaDumperTest < ActiveRecord::TestCase
versions = %w{ 20100101010101 20100201010101 20100301010101 }
versions.reverse_each do |v|
- ActiveRecord::SchemaMigration.create!(:version => v)
+ ActiveRecord::SchemaMigration.create!(version: v)
end
schema_info = ActiveRecord::Base.connection.dump_schema_information
@@ -51,6 +51,7 @@ class SchemaDumperTest < ActiveRecord::TestCase
output = standard_dump
assert_match %r{create_table "accounts"}, output
assert_match %r{create_table "authors"}, output
+ assert_no_match %r{(?<=, ) do \|t\|}, output
assert_no_match %r{create_table "schema_migrations"}, output
assert_no_match %r{create_table "ar_internal_metadata"}, output
end
@@ -70,38 +71,35 @@ class SchemaDumperTest < ActiveRecord::TestCase
assert_match %r{create_table "CamelCase"}, output
end
- def assert_line_up(lines, pattern, required = false)
+ def assert_no_line_up(lines, pattern)
return assert(true) if lines.empty?
matches = lines.map { |line| line.match(pattern) }
- assert matches.all? if required
matches.compact!
return assert(true) if matches.empty?
- assert_equal 1, matches.map{ |match| match.offset(0).first }.uniq.length
+ line_matches = lines.map { |line| [line, line.match(pattern)] }.select { |line, match| match }
+ assert line_matches.all? { |line, match|
+ start = match.offset(0).first
+ line[start - 2..start - 1] == ", "
+ }
end
def column_definition_lines(output = standard_dump)
- output.scan(/^( *)create_table.*?\n(.*?)^\1end/m).map{ |m| m.last.split(/\n/) }
+ output.scan(/^( *)create_table.*?\n(.*?)^\1end/m).map { |m| m.last.split(/\n/) }
end
- def test_types_line_up
+ def test_types_no_line_up
column_definition_lines.each do |column_set|
next if column_set.empty?
- lengths = column_set.map do |column|
- if match = column.match(/\bt\.\w+\s+(?="\w+?")/)
- match[0].length
- end
- end.compact
-
- assert_equal 1, lengths.uniq.length
+ assert column_set.all? { |column| !column.match(/\bt\.\w+\s{2,}/) }
end
end
- def test_arguments_line_up
+ def test_arguments_no_line_up
column_definition_lines.each do |column_set|
- assert_line_up(column_set, /default: /)
- assert_line_up(column_set, /limit: /)
- assert_line_up(column_set, /null: /)
+ assert_no_line_up(column_set, /default: /)
+ assert_no_line_up(column_set, /limit: /)
+ assert_no_line_up(column_set, /null: /)
end
end
@@ -150,12 +148,7 @@ class SchemaDumperTest < ActiveRecord::TestCase
assert_match %r{c_int_4.*limit: 4}, output
end
- if current_adapter?(:SQLite3Adapter)
- assert_match %r{c_int_5.*limit: 5}, output
- assert_match %r{c_int_6.*limit: 6}, output
- assert_match %r{c_int_7.*limit: 7}, output
- assert_match %r{c_int_8.*limit: 8}, output
- elsif current_adapter?(:OracleAdapter)
+ if current_adapter?(:SQLite3Adapter, :OracleAdapter)
assert_match %r{c_int_5.*limit: 5}, output
assert_match %r{c_int_6.*limit: 6}, output
assert_match %r{c_int_7.*limit: 7}, output
@@ -169,7 +162,7 @@ class SchemaDumperTest < ActiveRecord::TestCase
end
def test_schema_dump_with_string_ignored_table
- output = dump_all_table_schema(['accounts'])
+ output = dump_all_table_schema(["accounts"])
assert_no_match %r{create_table "accounts"}, output
assert_match %r{create_table "authors"}, output
assert_no_match %r{create_table "schema_migrations"}, output
@@ -186,8 +179,10 @@ class SchemaDumperTest < ActiveRecord::TestCase
def test_schema_dumps_index_columns_in_right_order
index_definition = standard_dump.split(/\n/).grep(/t\.index.*company_index/).first.strip
- if current_adapter?(:Mysql2Adapter, :PostgreSQLAdapter)
- assert_equal 't.index ["firm_id", "type", "rating"], name: "company_index", using: :btree', index_definition
+ if current_adapter?(:PostgreSQLAdapter)
+ assert_equal 't.index ["firm_id", "type", "rating"], name: "company_index", order: { rating: :desc }, using: :btree', index_definition
+ elsif current_adapter?(:Mysql2Adapter)
+ assert_equal 't.index ["firm_id", "type", "rating"], name: "company_index", length: { type: 10 }, using: :btree', index_definition
else
assert_equal 't.index ["firm_id", "type", "rating"], name: "company_index"', index_definition
end
@@ -289,7 +284,7 @@ class SchemaDumperTest < ActiveRecord::TestCase
def test_schema_dump_includes_extensions
connection = ActiveRecord::Base.connection
- connection.stubs(:extensions).returns(['hstore'])
+ connection.stubs(:extensions).returns(["hstore"])
output = perform_schema_dump
assert_match "# These are extensions that must be enabled", output
assert_match %r{enable_extension "hstore"}, output
@@ -346,7 +341,7 @@ class SchemaDumperTest < ActiveRecord::TestCase
create_table("dogs") do |t|
t.column :name, :string
- t.column :owner_id, :integer
+ t.column :owner_id, :bigint
t.index [:name]
t.foreign_key :dog_owners, column: "owner_id" if supports_foreign_keys?
end
@@ -359,8 +354,8 @@ class SchemaDumperTest < ActiveRecord::TestCase
def test_schema_dump_with_table_name_prefix_and_suffix
original, $stdout = $stdout, StringIO.new
- ActiveRecord::Base.table_name_prefix = 'foo_'
- ActiveRecord::Base.table_name_suffix = '_bar'
+ ActiveRecord::Base.table_name_prefix = "foo_"
+ ActiveRecord::Base.table_name_suffix = "_bar"
migration = CreateDogMigration.new
migration.migrate(:up)
@@ -378,7 +373,7 @@ class SchemaDumperTest < ActiveRecord::TestCase
ensure
migration.migrate(:down)
- ActiveRecord::Base.table_name_suffix = ActiveRecord::Base.table_name_prefix = ''
+ ActiveRecord::Base.table_name_suffix = ActiveRecord::Base.table_name_prefix = ""
$stdout = original
end
@@ -396,7 +391,7 @@ class SchemaDumperTest < ActiveRecord::TestCase
original_table_name_prefix = ActiveRecord::Base.table_name_prefix
original_schema_dumper_ignore_tables = ActiveRecord::SchemaDumper.ignore_tables
- ActiveRecord::Base.table_name_prefix = 'omg_'
+ ActiveRecord::Base.table_name_prefix = "omg_"
ActiveRecord::SchemaDumper.ignore_tables = ["cats"]
migration = create_cat_migration.new
migration.migrate(:up)
@@ -422,23 +417,37 @@ class SchemaDumperDefaultsTest < ActiveRecord::TestCase
@connection = ActiveRecord::Base.connection
@connection.create_table :defaults, force: true do |t|
t.string :string_with_default, default: "Hello!"
- t.date :date_with_default, default: '2014-06-05'
+ t.date :date_with_default, default: "2014-06-05"
t.datetime :datetime_with_default, default: "2014-06-05 07:17:04"
t.time :time_with_default, default: "07:17:04"
end
+
+ if current_adapter?(:PostgreSQLAdapter)
+ @connection.create_table :infinity_defaults, force: true do |t|
+ t.float :float_with_inf_default, default: Float::INFINITY
+ t.float :float_with_nan_default, default: Float::NAN
+ end
+ end
end
teardown do
return unless @connection
- @connection.drop_table 'defaults', if_exists: true
+ @connection.drop_table "defaults", if_exists: true
end
def test_schema_dump_defaults_with_universally_supported_types
- output = dump_table_schema('defaults')
+ output = dump_table_schema("defaults")
assert_match %r{t\.string\s+"string_with_default",.*?default: "Hello!"}, output
assert_match %r{t\.date\s+"date_with_default",\s+default: '2014-06-05'}, output
assert_match %r{t\.datetime\s+"datetime_with_default",\s+default: '2014-06-05 07:17:04'}, output
assert_match %r{t\.time\s+"time_with_default",\s+default: '2000-01-01 07:17:04'}, output
end
+
+ def test_schema_dump_with_float_column_infinity_default
+ skip unless current_adapter?(:PostgreSQLAdapter)
+ output = dump_table_schema("infinity_defaults")
+ assert_match %r{t\.float\s+"float_with_inf_default",\s+default: ::Float::INFINITY}, output
+ assert_match %r{t\.float\s+"float_with_nan_default",\s+default: ::Float::NAN}, output
+ end
end
diff --git a/activerecord/test/cases/schema_loading_test.rb b/activerecord/test/cases/schema_loading_test.rb
index 3d92a5e104..362370ac61 100644
--- a/activerecord/test/cases/schema_loading_test.rb
+++ b/activerecord/test/cases/schema_loading_test.rb
@@ -8,7 +8,7 @@ module SchemaLoadCounter
def load_schema!
self.load_schema_calls ||= 0
- self.load_schema_calls +=1
+ self.load_schema_calls += 1
super
end
end
diff --git a/activerecord/test/cases/scoping/default_scoping_test.rb b/activerecord/test/cases/scoping/default_scoping_test.rb
index dcd09b6973..3a04f4bf7d 100644
--- a/activerecord/test/cases/scoping/default_scoping_test.rb
+++ b/activerecord/test/cases/scoping/default_scoping_test.rb
@@ -1,16 +1,17 @@
-require 'cases/helper'
-require 'models/post'
-require 'models/comment'
-require 'models/developer'
-require 'models/computer'
-require 'models/vehicle'
-require 'models/cat'
+require "cases/helper"
+require "models/post"
+require "models/comment"
+require "models/developer"
+require "models/computer"
+require "models/vehicle"
+require "models/cat"
+require "concurrent/atomic/cyclic_barrier"
class DefaultScopingTest < ActiveRecord::TestCase
fixtures :developers, :posts, :comments
def test_default_scope
- expected = Developer.all.merge!(:order => 'salary DESC').to_a.collect(&:salary)
+ expected = Developer.all.merge!(order: "salary DESC").to_a.collect(&:salary)
received = DeveloperOrderedBySalary.all.collect(&:salary)
assert_equal expected, received
end
@@ -49,20 +50,20 @@ class DefaultScopingTest < ActiveRecord::TestCase
end
def test_default_scope_with_conditions_string
- assert_equal Developer.where(name: 'David').map(&:id).sort, DeveloperCalledDavid.all.map(&:id).sort
- assert_equal nil, DeveloperCalledDavid.create!.name
+ assert_equal Developer.where(name: "David").map(&:id).sort, DeveloperCalledDavid.all.map(&:id).sort
+ assert_nil DeveloperCalledDavid.create!.name
end
def test_default_scope_with_conditions_hash
- assert_equal Developer.where(name: 'Jamis').map(&:id).sort, DeveloperCalledJamis.all.map(&:id).sort
- assert_equal 'Jamis', DeveloperCalledJamis.create!.name
+ assert_equal Developer.where(name: "Jamis").map(&:id).sort, DeveloperCalledJamis.all.map(&:id).sort
+ assert_equal "Jamis", DeveloperCalledJamis.create!.name
end
unless in_memory_db?
def test_default_scoping_with_threads
2.times do
Thread.new {
- assert DeveloperOrderedBySalary.all.to_sql.include?('salary DESC')
+ assert_includes DeveloperOrderedBySalary.all.to_sql, "salary DESC"
DeveloperOrderedBySalary.connection.close
}.join
end
@@ -71,37 +72,37 @@ class DefaultScopingTest < ActiveRecord::TestCase
def test_default_scope_with_inheritance
wheres = InheritedPoorDeveloperCalledJamis.all.where_values_hash
- assert_equal "Jamis", wheres['name']
- assert_equal 50000, wheres['salary']
+ assert_equal "Jamis", wheres["name"]
+ assert_equal 50000, wheres["salary"]
end
def test_default_scope_with_module_includes
wheres = ModuleIncludedPoorDeveloperCalledJamis.all.where_values_hash
- assert_equal "Jamis", wheres['name']
- assert_equal 50000, wheres['salary']
+ assert_equal "Jamis", wheres["name"]
+ assert_equal 50000, wheres["salary"]
end
def test_default_scope_with_multiple_calls
wheres = MultiplePoorDeveloperCalledJamis.all.where_values_hash
- assert_equal "Jamis", wheres['name']
- assert_equal 50000, wheres['salary']
+ assert_equal "Jamis", wheres["name"]
+ assert_equal 50000, wheres["salary"]
end
def test_scope_overwrites_default
- expected = Developer.all.merge!(order: 'salary DESC, name DESC').to_a.collect(&:name)
+ expected = Developer.all.merge!(order: "salary DESC, name DESC").to_a.collect(&:name)
received = DeveloperOrderedBySalary.by_name.to_a.collect(&:name)
assert_equal expected, received
end
def test_reorder_overrides_default_scope_order
- expected = Developer.order('name DESC').collect(&:name)
- received = DeveloperOrderedBySalary.reorder('name DESC').collect(&:name)
+ expected = Developer.order("name DESC").collect(&:name)
+ received = DeveloperOrderedBySalary.reorder("name DESC").collect(&:name)
assert_equal expected, received
end
def test_order_after_reorder_combines_orders
- expected = Developer.order('name DESC, id DESC').collect { |dev| [dev.name, dev.id] }
- received = Developer.order('name ASC').reorder('name DESC').order('id DESC').collect { |dev| [dev.name, dev.id] }
+ expected = Developer.order("name DESC, id DESC").collect { |dev| [dev.name, dev.id] }
+ received = Developer.order("name ASC").reorder("name DESC").order("id DESC").collect { |dev| [dev.name, dev.id] }
assert_equal expected, received
end
@@ -112,107 +113,107 @@ class DefaultScopingTest < ActiveRecord::TestCase
end
def test_unscope_after_reordering_and_combining
- expected = Developer.order('id DESC, name DESC').collect { |dev| [dev.name, dev.id] }
- received = DeveloperOrderedBySalary.reorder('name DESC').unscope(:order).order('id DESC, name DESC').collect { |dev| [dev.name, dev.id] }
+ expected = Developer.order("id DESC, name DESC").collect { |dev| [dev.name, dev.id] }
+ received = DeveloperOrderedBySalary.reorder("name DESC").unscope(:order).order("id DESC, name DESC").collect { |dev| [dev.name, dev.id] }
assert_equal expected, received
expected_2 = Developer.all.collect { |dev| [dev.name, dev.id] }
- received_2 = Developer.order('id DESC, name DESC').unscope(:order).collect { |dev| [dev.name, dev.id] }
+ received_2 = Developer.order("id DESC, name DESC").unscope(:order).collect { |dev| [dev.name, dev.id] }
assert_equal expected_2, received_2
expected_3 = Developer.all.collect { |dev| [dev.name, dev.id] }
- received_3 = Developer.reorder('name DESC').unscope(:order).collect { |dev| [dev.name, dev.id] }
+ received_3 = Developer.reorder("name DESC").unscope(:order).collect { |dev| [dev.name, dev.id] }
assert_equal expected_3, received_3
end
def test_unscope_with_where_attributes
- expected = Developer.order('salary DESC').collect(&:name)
- received = DeveloperOrderedBySalary.where(name: 'David').unscope(where: :name).collect(&:name)
+ expected = Developer.order("salary DESC").collect(&:name)
+ received = DeveloperOrderedBySalary.where(name: "David").unscope(where: :name).collect(&:name)
assert_equal expected, received
- expected_2 = Developer.order('salary DESC').collect(&:name)
- received_2 = DeveloperOrderedBySalary.select("id").where("name" => "Jamis").unscope({:where => :name}, :select).collect(&:name)
+ expected_2 = Developer.order("salary DESC").collect(&:name)
+ received_2 = DeveloperOrderedBySalary.select("id").where("name" => "Jamis").unscope({ where: :name }, :select).collect(&:name)
assert_equal expected_2, received_2
- expected_3 = Developer.order('salary DESC').collect(&:name)
+ expected_3 = Developer.order("salary DESC").collect(&:name)
received_3 = DeveloperOrderedBySalary.select("id").where("name" => "Jamis").unscope(:select, :where).collect(&:name)
assert_equal expected_3, received_3
- expected_4 = Developer.order('salary DESC').collect(&:name)
+ expected_4 = Developer.order("salary DESC").collect(&:name)
received_4 = DeveloperOrderedBySalary.where.not("name" => "Jamis").unscope(where: :name).collect(&:name)
assert_equal expected_4, received_4
- expected_5 = Developer.order('salary DESC').collect(&:name)
+ expected_5 = Developer.order("salary DESC").collect(&:name)
received_5 = DeveloperOrderedBySalary.where.not("name" => ["Jamis", "David"]).unscope(where: :name).collect(&:name)
assert_equal expected_5, received_5
- expected_6 = Developer.order('salary DESC').collect(&:name)
- received_6 = DeveloperOrderedBySalary.where(Developer.arel_table['name'].eq('David')).unscope(where: :name).collect(&:name)
+ expected_6 = Developer.order("salary DESC").collect(&:name)
+ received_6 = DeveloperOrderedBySalary.where(Developer.arel_table["name"].eq("David")).unscope(where: :name).collect(&:name)
assert_equal expected_6, received_6
- expected_7 = Developer.order('salary DESC').collect(&:name)
- received_7 = DeveloperOrderedBySalary.where(Developer.arel_table[:name].eq('David')).unscope(where: :name).collect(&:name)
+ expected_7 = Developer.order("salary DESC").collect(&:name)
+ received_7 = DeveloperOrderedBySalary.where(Developer.arel_table[:name].eq("David")).unscope(where: :name).collect(&:name)
assert_equal expected_7, received_7
end
def test_unscope_comparison_where_clauses
# unscoped for WHERE (`developers`.`id` <= 2)
- expected = Developer.order('salary DESC').collect(&:name)
+ expected = Developer.order("salary DESC").collect(&:name)
received = DeveloperOrderedBySalary.where(id: -Float::INFINITY..2).unscope(where: :id).collect { |dev| dev.name }
assert_equal expected, received
# unscoped for WHERE (`developers`.`id` < 2)
- expected = Developer.order('salary DESC').collect(&:name)
+ expected = Developer.order("salary DESC").collect(&:name)
received = DeveloperOrderedBySalary.where(id: -Float::INFINITY...2).unscope(where: :id).collect { |dev| dev.name }
assert_equal expected, received
end
def test_unscope_multiple_where_clauses
- expected = Developer.order('salary DESC').collect(&:name)
- received = DeveloperOrderedBySalary.where(name: 'Jamis').where(id: 1).unscope(where: [:name, :id]).collect(&:name)
+ expected = Developer.order("salary DESC").collect(&:name)
+ received = DeveloperOrderedBySalary.where(name: "Jamis").where(id: 1).unscope(where: [:name, :id]).collect(&:name)
assert_equal expected, received
end
def test_unscope_string_where_clauses_involved
- dev_relation = Developer.order('salary DESC').where("created_at > ?", 1.year.ago)
+ dev_relation = Developer.order("salary DESC").where("created_at > ?", 1.year.ago)
expected = dev_relation.collect(&:name)
- dev_ordered_relation = DeveloperOrderedBySalary.where(name: 'Jamis').where("created_at > ?", 1.year.ago)
+ dev_ordered_relation = DeveloperOrderedBySalary.where(name: "Jamis").where("created_at > ?", 1.year.ago)
received = dev_ordered_relation.unscope(where: [:name]).collect(&:name)
assert_equal expected, received
end
def test_unscope_with_grouping_attributes
- expected = Developer.order('salary DESC').collect(&:name)
+ expected = Developer.order("salary DESC").collect(&:name)
received = DeveloperOrderedBySalary.group(:name).unscope(:group).collect(&:name)
assert_equal expected, received
- expected_2 = Developer.order('salary DESC').collect(&:name)
+ expected_2 = Developer.order("salary DESC").collect(&:name)
received_2 = DeveloperOrderedBySalary.group("name").unscope(:group).collect(&:name)
assert_equal expected_2, received_2
end
def test_unscope_with_limit_in_query
- expected = Developer.order('salary DESC').collect(&:name)
+ expected = Developer.order("salary DESC").collect(&:name)
received = DeveloperOrderedBySalary.limit(1).unscope(:limit).collect(&:name)
assert_equal expected, received
end
def test_order_to_unscope_reordering
- scope = DeveloperOrderedBySalary.order('salary DESC, name ASC').reverse_order.unscope(:order)
- assert !(scope.to_sql =~ /order/i)
+ scope = DeveloperOrderedBySalary.order("salary DESC, name ASC").reverse_order.unscope(:order)
+ assert !/order/i.match?(scope.to_sql)
end
def test_unscope_reverse_order
expected = Developer.all.collect(&:name)
- received = Developer.order('salary DESC').reverse_order.unscope(:order).collect(&:name)
+ received = Developer.order("salary DESC").reverse_order.unscope(:order).collect(&:name)
assert_equal expected, received
end
def test_unscope_select
- expected = Developer.order('salary ASC').collect(&:name)
- received = Developer.order('salary DESC').reverse_order.select(:name).unscope(:select).collect(&:name)
+ expected = Developer.order("salary ASC").collect(&:name)
+ received = Developer.order("salary DESC").reverse_order.select(:name).unscope(:select).collect(&:name)
assert_equal expected, received
expected_2 = Developer.all.collect(&:id)
@@ -228,7 +229,7 @@ class DefaultScopingTest < ActiveRecord::TestCase
def test_unscope_joins_and_select_on_developers_projects
expected = Developer.all.collect(&:name)
- received = Developer.joins('JOIN developers_projects ON id = developer_id').select(:id).unscope(:joins, :select).collect(&:name)
+ received = Developer.joins("JOIN developers_projects ON id = developer_id").select(:id).unscope(:joins, :select).collect(&:name)
assert_equal expected, received
end
@@ -249,8 +250,8 @@ class DefaultScopingTest < ActiveRecord::TestCase
scope :by_name, -> name { unscope(where: :name).where(name: name) }
end
- expected = developer_klass.where(name: 'Jamis').collect { |dev| [dev.name, dev.id] }
- received = developer_klass.where(name: 'David').by_name('Jamis').collect { |dev| [dev.name, dev.id] }
+ expected = developer_klass.where(name: "Jamis").collect { |dev| [dev.name, dev.id] }
+ received = developer_klass.where(name: "David").by_name("Jamis").collect { |dev| [dev.name, dev.id] }
assert_equal expected, received
end
@@ -264,11 +265,11 @@ class DefaultScopingTest < ActiveRecord::TestCase
end
assert_raises(ArgumentError) do
- Developer.order('name DESC').reverse_order.unscope(:reverse_order)
+ Developer.order("name DESC").reverse_order.unscope(:reverse_order)
end
assert_raises(ArgumentError) do
- Developer.order('name DESC').where(name: "Jamis").unscope()
+ Developer.order("name DESC").where(name: "Jamis").unscope()
end
end
@@ -303,35 +304,35 @@ class DefaultScopingTest < ActiveRecord::TestCase
end
def test_order_in_default_scope_should_not_prevail
- expected = Developer.all.merge!(order: 'salary desc').to_a.collect(&:salary)
- received = DeveloperOrderedBySalary.all.merge!(order: 'salary').to_a.collect(&:salary)
+ expected = Developer.all.merge!(order: "salary desc").to_a.collect(&:salary)
+ received = DeveloperOrderedBySalary.all.merge!(order: "salary").to_a.collect(&:salary)
assert_equal expected, received
end
def test_create_attribute_overwrites_default_scoping
- assert_equal 'David', PoorDeveloperCalledJamis.create!(:name => 'David').name
- assert_equal 200000, PoorDeveloperCalledJamis.create!(:name => 'David', :salary => 200000).salary
+ assert_equal "David", PoorDeveloperCalledJamis.create!(name: "David").name
+ assert_equal 200000, PoorDeveloperCalledJamis.create!(name: "David", salary: 200000).salary
end
def test_create_attribute_overwrites_default_values
- assert_equal nil, PoorDeveloperCalledJamis.create!(:salary => nil).salary
- assert_equal 50000, PoorDeveloperCalledJamis.create!(:name => 'David').salary
+ assert_nil PoorDeveloperCalledJamis.create!(salary: nil).salary
+ assert_equal 50000, PoorDeveloperCalledJamis.create!(name: "David").salary
end
def test_default_scope_attribute
- jamis = PoorDeveloperCalledJamis.new(:name => 'David')
+ jamis = PoorDeveloperCalledJamis.new(name: "David")
assert_equal 50000, jamis.salary
end
def test_where_attribute
- aaron = PoorDeveloperCalledJamis.where(:salary => 20).new(:name => 'Aaron')
+ aaron = PoorDeveloperCalledJamis.where(salary: 20).new(name: "Aaron")
assert_equal 20, aaron.salary
- assert_equal 'Aaron', aaron.name
+ assert_equal "Aaron", aaron.name
end
def test_where_attribute_merge
- aaron = PoorDeveloperCalledJamis.where(:name => 'foo').new(:name => 'Aaron')
- assert_equal 'Aaron', aaron.name
+ aaron = PoorDeveloperCalledJamis.where(name: "foo").new(name: "Aaron")
+ assert_equal "Aaron", aaron.name
end
def test_scope_composed_by_limit_and_then_offset_is_equal_to_scope_composed_by_offset_and_then_limit
@@ -341,33 +342,33 @@ class DefaultScopingTest < ActiveRecord::TestCase
end
def test_create_with_merge
- aaron = PoorDeveloperCalledJamis.create_with(:name => 'foo', :salary => 20).merge(
- PoorDeveloperCalledJamis.create_with(:name => 'Aaron')).new
+ aaron = PoorDeveloperCalledJamis.create_with(name: "foo", salary: 20).merge(
+ PoorDeveloperCalledJamis.create_with(name: "Aaron")).new
assert_equal 20, aaron.salary
- assert_equal 'Aaron', aaron.name
+ assert_equal "Aaron", aaron.name
- aaron = PoorDeveloperCalledJamis.create_with(:name => 'foo', :salary => 20).
- create_with(:name => 'Aaron').new
+ aaron = PoorDeveloperCalledJamis.create_with(name: "foo", salary: 20).
+ create_with(name: "Aaron").new
assert_equal 20, aaron.salary
- assert_equal 'Aaron', aaron.name
+ assert_equal "Aaron", aaron.name
end
def test_create_with_reset
- jamis = PoorDeveloperCalledJamis.create_with(:name => 'Aaron').create_with(nil).new
- assert_equal 'Jamis', jamis.name
+ jamis = PoorDeveloperCalledJamis.create_with(name: "Aaron").create_with(nil).new
+ assert_equal "Jamis", jamis.name
end
# FIXME: I don't know if this is *desired* behavior, but it is *today's*
# behavior.
def test_create_with_empty_hash_will_not_reset
- jamis = PoorDeveloperCalledJamis.create_with(:name => 'Aaron').create_with({}).new
- assert_equal 'Aaron', jamis.name
+ jamis = PoorDeveloperCalledJamis.create_with(name: "Aaron").create_with({}).new
+ assert_equal "Aaron", jamis.name
end
def test_unscoped_with_named_scope_should_not_have_default_scope
assert_equal [DeveloperCalledJamis.find(developers(:poor_jamis).id)], DeveloperCalledJamis.poor
- assert DeveloperCalledJamis.unscoped.poor.include?(developers(:david).becomes(DeveloperCalledJamis))
+ assert_includes DeveloperCalledJamis.unscoped.poor, developers(:david).becomes(DeveloperCalledJamis)
assert_equal 11, DeveloperCalledJamis.unscoped.length
assert_equal 1, DeveloperCalledJamis.poor.length
@@ -409,9 +410,9 @@ class DefaultScopingTest < ActiveRecord::TestCase
def test_default_scope_include_with_count
d = DeveloperWithIncludes.create!
- d.audit_logs.create! :message => 'foo'
+ d.audit_logs.create! message: "foo"
- assert_equal 1, DeveloperWithIncludes.where(:audit_logs => { :message => 'foo' }).count
+ assert_equal 1, DeveloperWithIncludes.where(audit_logs: { message: "foo" }).count
end
def test_default_scope_with_references_works_through_collection_association
@@ -437,12 +438,17 @@ class DefaultScopingTest < ActiveRecord::TestCase
threads = []
assert_not_equal 1, ThreadsafeDeveloper.unscoped.count
+ barrier_1 = Concurrent::CyclicBarrier.new(2)
+ barrier_2 = Concurrent::CyclicBarrier.new(2)
+
threads << Thread.new do
- Thread.current[:long_default_scope] = true
+ Thread.current[:default_scope_delay] = -> { barrier_1.wait; barrier_2.wait }
assert_equal 1, ThreadsafeDeveloper.all.to_a.count
ThreadsafeDeveloper.connection.close
end
threads << Thread.new do
+ Thread.current[:default_scope_delay] = -> { barrier_2.wait }
+ barrier_1.wait
assert_equal 1, ThreadsafeDeveloper.all.to_a.count
ThreadsafeDeveloper.connection.close
end
@@ -474,9 +480,9 @@ class DefaultScopingTest < ActiveRecord::TestCase
end
def test_sti_conditions_are_not_carried_in_default_scope
- ConditionalStiPost.create! body: ''
- SubConditionalStiPost.create! body: ''
- SubConditionalStiPost.create! title: 'Hello world', body: ''
+ ConditionalStiPost.create! body: ""
+ SubConditionalStiPost.create! body: ""
+ SubConditionalStiPost.create! title: "Hello world", body: ""
assert_equal 2, ConditionalStiPost.count
assert_equal 2, ConditionalStiPost.all.to_a.size
diff --git a/activerecord/test/cases/scoping/named_scoping_test.rb b/activerecord/test/cases/scoping/named_scoping_test.rb
index 0e277ed235..995ff4dfc5 100644
--- a/activerecord/test/cases/scoping/named_scoping_test.rb
+++ b/activerecord/test/cases/scoping/named_scoping_test.rb
@@ -1,11 +1,11 @@
require "cases/helper"
-require 'models/post'
-require 'models/topic'
-require 'models/comment'
-require 'models/reply'
-require 'models/author'
-require 'models/developer'
-require 'models/computer'
+require "models/post"
+require "models/topic"
+require "models/comment"
+require "models/reply"
+require "models/author"
+require "models/developer"
+require "models/computer"
class NamedScopingTest < ActiveRecord::TestCase
fixtures :posts, :authors, :topics, :comments, :author_addresses
@@ -33,8 +33,8 @@ class NamedScopingTest < ActiveRecord::TestCase
all_posts.to_a
new_post = Topic.create!
- assert !all_posts.include?(new_post)
- assert all_posts.reload.include?(new_post)
+ assert_not_includes all_posts, new_post
+ assert_includes all_posts.reload, new_post
end
def test_delegates_finds_and_calculations_to_the_base_class
@@ -46,11 +46,18 @@ class NamedScopingTest < ActiveRecord::TestCase
assert_equal Topic.average(:replies_count), Topic.base.average(:replies_count)
end
+ def test_calling_merge_at_first_in_scope
+ Topic.class_eval do
+ scope :calling_merge_at_first_in_scope, Proc.new { merge(Topic.replied) }
+ end
+ assert_equal Topic.calling_merge_at_first_in_scope.to_a, Topic.replied.to_a
+ end
+
def test_method_missing_priority_when_delegating
klazz = Class.new(ActiveRecord::Base) do
self.table_name = "topics"
- scope :since, Proc.new { where('written_on >= ?', Time.now - 1.day) }
- scope :to, Proc.new { where('written_on <= ?', Time.now) }
+ scope :since, Proc.new { where("written_on >= ?", Time.now - 1.day) }
+ scope :to, Proc.new { where("written_on <= ?", Time.now) }
end
assert_equal klazz.to.since.to_a, klazz.since.to.to_a
end
@@ -62,10 +69,10 @@ class NamedScopingTest < ActiveRecord::TestCase
end
def test_scopes_with_options_limit_finds_to_those_matching_the_criteria_specified
- assert !Topic.all.merge!(:where => {:approved => true}).to_a.empty?
+ assert !Topic.all.merge!(where: { approved: true }).to_a.empty?
- assert_equal Topic.all.merge!(:where => {:approved => true}).to_a, Topic.approved
- assert_equal Topic.where(:approved => true).count, Topic.approved.count
+ assert_equal Topic.all.merge!(where: { approved: true }).to_a, Topic.approved
+ assert_equal Topic.where(approved: true).count, Topic.approved.count
end
def test_scopes_with_string_name_can_be_composed
@@ -75,8 +82,8 @@ class NamedScopingTest < ActiveRecord::TestCase
end
def test_scopes_are_composable
- assert_equal((approved = Topic.all.merge!(:where => {:approved => true}).to_a), Topic.approved)
- assert_equal((replied = Topic.all.merge!(:where => 'replies_count > 0').to_a), Topic.replied)
+ assert_equal((approved = Topic.all.merge!(where: { approved: true }).to_a), Topic.approved)
+ assert_equal((replied = Topic.all.merge!(where: "replies_count > 0").to_a), Topic.replied)
assert !(approved == replied)
assert !(approved & replied).empty?
@@ -84,8 +91,8 @@ class NamedScopingTest < ActiveRecord::TestCase
end
def test_procedural_scopes
- topics_written_before_the_third = Topic.where('written_on < ?', topics(:third).written_on)
- topics_written_before_the_second = Topic.where('written_on < ?', topics(:second).written_on)
+ topics_written_before_the_third = Topic.where("written_on < ?", topics(:third).written_on)
+ topics_written_before_the_second = Topic.where("written_on < ?", topics(:second).written_on)
assert_not_equal topics_written_before_the_second, topics_written_before_the_third
assert_equal topics_written_before_the_third, Topic.written_before(topics(:third).written_on)
@@ -101,7 +108,7 @@ class NamedScopingTest < ActiveRecord::TestCase
def test_scope_with_object
objects = Topic.with_object
assert_operator objects.length, :>, 0
- assert objects.all?(&:approved?), 'all objects should be approved'
+ assert objects.all?(&:approved?), "all objects should be approved"
end
def test_has_many_associations_have_access_to_scopes
@@ -112,8 +119,8 @@ class NamedScopingTest < ActiveRecord::TestCase
end
def test_scope_with_STI
- assert_equal 3,Post.containing_the_letter_a.count
- assert_equal 1,SpecialPost.containing_the_letter_a.count
+ assert_equal 3, Post.containing_the_letter_a.count
+ assert_equal 1, SpecialPost.containing_the_letter_a.count
end
def test_has_many_through_associations_have_access_to_scopes
@@ -231,9 +238,9 @@ class NamedScopingTest < ActiveRecord::TestCase
end
def test_many_should_return_false_if_none_or_one
- topics = Topic.base.where(:id => 0)
+ topics = Topic.base.where(id: 0)
assert !topics.many?
- topics = Topic.base.where(:id => 1)
+ topics = Topic.base.where(id: 1)
assert !topics.many?
end
@@ -273,7 +280,7 @@ class NamedScopingTest < ActiveRecord::TestCase
def test_should_build_on_top_of_chained_scopes
topic = Topic.approved.by_lifo.build({})
assert topic.approved
- assert_equal 'lifo', topic.author_name
+ assert_equal "lifo", topic.author_name
end
def test_reserved_scope_names
@@ -320,12 +327,12 @@ class NamedScopingTest < ActiveRecord::TestCase
conflicts.each do |name|
e = assert_raises(ArgumentError, "scope `#{name}` should not be allowed") do
- klass.class_eval { scope name, ->{ where(approved: true) } }
+ klass.class_eval { scope name, -> { where(approved: true) } }
end
assert_match(/You tried to define a scope named \"#{name}\" on the model/, e.message)
e = assert_raises(ArgumentError, "scope `#{name}` should not be allowed") do
- subklass.class_eval { scope name, ->{ where(approved: true) } }
+ subklass.class_eval { scope name, -> { where(approved: true) } }
end
assert_match(/You tried to define a scope named \"#{name}\" on the model/, e.message)
end
@@ -333,12 +340,12 @@ class NamedScopingTest < ActiveRecord::TestCase
non_conflicts.each do |name|
assert_nothing_raised do
silence_warnings do
- klass.class_eval { scope name, ->{ where(approved: true) } }
+ klass.class_eval { scope name, -> { where(approved: true) } }
end
end
assert_nothing_raised do
- subklass.class_eval { scope name, ->{ where(approved: true) } }
+ subklass.class_eval { scope name, -> { where(approved: true) } }
end
end
end
@@ -350,7 +357,7 @@ class NamedScopingTest < ActiveRecord::TestCase
klass = Class.new(ActiveRecord::Base) do
self.table_name = "topics"
scope :"title containing space", -> { where("title LIKE '% %'") }
- scope :approved, -> { where(:approved => true) }
+ scope :approved, -> { where(approved: true) }
end
assert_equal klass.send(:"title containing space"), klass.where("title LIKE '% %'")
assert_equal klass.approved.send(:"title containing space"), klass.approved.where("title LIKE '% %'")
@@ -365,7 +372,7 @@ class NamedScopingTest < ActiveRecord::TestCase
end
def test_should_use_where_in_query_for_scope
- assert_equal Developer.where(name: 'Jamis').to_set, Developer.where(id: Developer.jamises).to_set
+ assert_equal Developer.where(name: "Jamis").to_set, Developer.where(id: Developer.jamises).to_set
end
def test_size_should_use_count_when_results_are_not_loaded
@@ -424,12 +431,12 @@ class NamedScopingTest < ActiveRecord::TestCase
assert_equal 4, Topic.approved.count
assert_queries(5) do
- Topic.approved.find_each(:batch_size => 1) {|t| assert t.approved? }
+ Topic.approved.find_each(batch_size: 1) { |t| assert t.approved? }
end
assert_queries(3) do
- Topic.approved.find_in_batches(:batch_size => 2) do |group|
- group.each {|t| assert t.approved? }
+ Topic.approved.find_in_batches(batch_size: 2) do |group|
+ group.each { |t| assert t.approved? }
end
end
end
@@ -455,13 +462,13 @@ class NamedScopingTest < ActiveRecord::TestCase
[:public_method, :protected_method, :private_method].each do |reserved_method|
assert Topic.respond_to?(reserved_method, true)
ActiveRecord::Base.logger.expects(:warn)
- silence_warnings { Topic.scope(reserved_method, -> { }) }
+ silence_warnings { Topic.scope(reserved_method, -> {}) }
end
end
def test_scopes_on_relations
# Topic.replied
- approved_topics = Topic.all.approved.order('id DESC')
+ approved_topics = Topic.all.approved.order("id DESC")
assert_equal topics(:fifth), approved_topics.first
replied_approved_topics = approved_topics.replied
@@ -469,7 +476,7 @@ class NamedScopingTest < ActiveRecord::TestCase
end
def test_index_on_scope
- approved = Topic.approved.order('id ASC')
+ approved = Topic.approved.order("id ASC")
assert_equal topics(:second), approved[0]
assert approved.loaded?
end
@@ -510,7 +517,7 @@ class NamedScopingTest < ActiveRecord::TestCase
def test_scopes_to_get_newest
post = posts(:welcome)
old_last_comment = post.comments.newest
- new_comment = post.comments.create(:body => "My new comment")
+ new_comment = post.comments.create(body: "My new comment")
assert_equal new_comment, post.comments.newest
assert_not_equal old_last_comment, post.comments.newest
end
@@ -533,15 +540,15 @@ class NamedScopingTest < ActiveRecord::TestCase
def test_eager_default_scope_relations_are_remove
klass = Class.new(ActiveRecord::Base)
- klass.table_name = 'posts'
+ klass.table_name = "posts"
assert_raises(ArgumentError) do
- klass.send(:default_scope, klass.where(:id => posts(:welcome).id))
+ klass.send(:default_scope, klass.where(id: posts(:welcome).id))
end
end
def test_subclass_merges_scopes_properly
- assert_equal 1, SpecialComment.where(body: 'go crazy').created.count
+ assert_equal 1, SpecialComment.where(body: "go crazy").created.count
end
def test_model_class_should_respond_to_none
@@ -557,5 +564,4 @@ class NamedScopingTest < ActiveRecord::TestCase
Topic.create!
assert Topic.one?
end
-
end
diff --git a/activerecord/test/cases/scoping/relation_scoping_test.rb b/activerecord/test/cases/scoping/relation_scoping_test.rb
index c15d57460b..a1ae57fdbb 100644
--- a/activerecord/test/cases/scoping/relation_scoping_test.rb
+++ b/activerecord/test/cases/scoping/relation_scoping_test.rb
@@ -1,13 +1,13 @@
require "cases/helper"
-require 'models/post'
-require 'models/author'
-require 'models/developer'
-require 'models/computer'
-require 'models/project'
-require 'models/comment'
-require 'models/category'
-require 'models/person'
-require 'models/reference'
+require "models/post"
+require "models/author"
+require "models/developer"
+require "models/computer"
+require "models/project"
+require "models/comment"
+require "models/category"
+require "models/person"
+require "models/reference"
class RelationScopingTest < ActiveRecord::TestCase
fixtures :authors, :developers, :projects, :comments, :posts, :developers_projects
@@ -28,7 +28,7 @@ class RelationScopingTest < ActiveRecord::TestCase
def test_scope_breaks_caching_on_collections
author = authors :david
ids = author.reload.special_posts_with_default_scope.map(&:id)
- assert_equal [1,5,6], ids.sort
+ assert_equal [1, 5, 6], ids.sort
scoped_posts = SpecialPostWithDefaultScope.unscoped do
author = authors :david
author.reload.special_posts_with_default_scope.to_a
@@ -109,7 +109,7 @@ class RelationScopingTest < ActiveRecord::TestCase
def test_scope_select_concatenates
Developer.select("id, name").scoping do
- developer = Developer.select('salary').where("name = 'David'").first
+ developer = Developer.select("salary").where("name = 'David'").first
assert_equal 80000, developer.salary
assert developer.has_attribute?(:id)
assert developer.has_attribute?(:name)
@@ -122,7 +122,7 @@ class RelationScopingTest < ActiveRecord::TestCase
assert_equal 1, Developer.count
end
- Developer.where('salary = 100000').scoping do
+ Developer.where("salary = 100000").scoping do
assert_equal 8, Developer.count
assert_equal 1, Developer.where("name LIKE 'fixture_1%'").count
end
@@ -131,49 +131,49 @@ class RelationScopingTest < ActiveRecord::TestCase
def test_scoped_find_include
# with the include, will retrieve only developers for the given project
scoped_developers = Developer.includes(:projects).scoping do
- Developer.where('projects.id' => 2).to_a
+ Developer.where("projects.id" => 2).to_a
end
- assert scoped_developers.include?(developers(:david))
- assert !scoped_developers.include?(developers(:jamis))
+ assert_includes scoped_developers, developers(:david)
+ assert_not_includes scoped_developers, developers(:jamis)
assert_equal 1, scoped_developers.size
end
def test_scoped_find_joins
- scoped_developers = Developer.joins('JOIN developers_projects ON id = developer_id').scoping do
- Developer.where('developers_projects.project_id = 2').to_a
+ scoped_developers = Developer.joins("JOIN developers_projects ON id = developer_id").scoping do
+ Developer.where("developers_projects.project_id = 2").to_a
end
- assert scoped_developers.include?(developers(:david))
- assert !scoped_developers.include?(developers(:jamis))
+ assert_includes scoped_developers, developers(:david)
+ assert_not_includes scoped_developers, developers(:jamis)
assert_equal 1, scoped_developers.size
assert_equal developers(:david).attributes, scoped_developers.first.attributes
end
def test_scoped_create_with_where
- new_comment = VerySpecialComment.where(:post_id => 1).scoping do
- VerySpecialComment.create :body => "Wonderful world"
+ new_comment = VerySpecialComment.where(post_id: 1).scoping do
+ VerySpecialComment.create body: "Wonderful world"
end
assert_equal 1, new_comment.post_id
- assert Post.find(1).comments.include?(new_comment)
+ assert_includes Post.find(1).comments, new_comment
end
def test_scoped_create_with_create_with
- new_comment = VerySpecialComment.create_with(:post_id => 1).scoping do
- VerySpecialComment.create :body => "Wonderful world"
+ new_comment = VerySpecialComment.create_with(post_id: 1).scoping do
+ VerySpecialComment.create body: "Wonderful world"
end
assert_equal 1, new_comment.post_id
- assert Post.find(1).comments.include?(new_comment)
+ assert_includes Post.find(1).comments, new_comment
end
def test_scoped_create_with_create_with_has_higher_priority
- new_comment = VerySpecialComment.where(:post_id => 2).create_with(:post_id => 1).scoping do
- VerySpecialComment.create :body => "Wonderful world"
+ new_comment = VerySpecialComment.where(post_id: 2).create_with(post_id: 1).scoping do
+ VerySpecialComment.create body: "Wonderful world"
end
assert_equal 1, new_comment.post_id
- assert Post.find(1).comments.include?(new_comment)
+ assert_includes Post.find(1).comments, new_comment
end
def test_ensure_that_method_scoping_is_correctly_restored
@@ -193,7 +193,7 @@ class RelationScopingTest < ActiveRecord::TestCase
end
def test_update_all_default_scope_filters_on_joins
- DeveloperFilteredOnJoins.update_all(:salary => 65000)
+ DeveloperFilteredOnJoins.update_all(salary: 65000)
assert_equal 65000, Developer.find(developers(:david).id).salary
# has not changed jamis
@@ -228,18 +228,25 @@ class RelationScopingTest < ActiveRecord::TestCase
assert SpecialComment.all.any?
end
end
+
+ def test_circular_joins_with_current_scope_does_not_crash
+ posts = Post.joins(comments: :post).scoping do
+ Post.current_scope.first(10)
+ end
+ assert_equal posts, Post.joins(comments: :post).first(10)
+ end
end
class NestedRelationScopingTest < ActiveRecord::TestCase
fixtures :authors, :developers, :projects, :comments, :posts
def test_merge_options
- Developer.where('salary = 80000').scoping do
+ Developer.where("salary = 80000").scoping do
Developer.limit(10).scoping do
devs = Developer.all
sql = devs.to_sql
- assert_match '(salary = 80000)', sql
- assert_match 'LIMIT 10', sql
+ assert_match "(salary = 80000)", sql
+ assert_match(/LIMIT 10|ROWNUM <= 10|FETCH FIRST 10 ROWS ONLY/, sql)
end
end
end
@@ -253,39 +260,39 @@ class NestedRelationScopingTest < ActiveRecord::TestCase
end
def test_replace_options
- Developer.where(:name => 'David').scoping do
+ Developer.where(name: "David").scoping do
Developer.unscoped do
- assert_equal 'Jamis', Developer.where(:name => 'Jamis').first[:name]
+ assert_equal "Jamis", Developer.where(name: "Jamis").first[:name]
end
- assert_equal 'David', Developer.first[:name]
+ assert_equal "David", Developer.first[:name]
end
end
def test_three_level_nested_exclusive_scoped_find
Developer.where("name = 'Jamis'").scoping do
- assert_equal 'Jamis', Developer.first.name
+ assert_equal "Jamis", Developer.first.name
Developer.unscoped.where("name = 'David'") do
- assert_equal 'David', Developer.first.name
+ assert_equal "David", Developer.first.name
Developer.unscoped.where("name = 'Maiha'") do
- assert_equal nil, Developer.first
+ assert_nil Developer.first
end
# ensure that scoping is restored
- assert_equal 'David', Developer.first.name
+ assert_equal "David", Developer.first.name
end
# ensure that scoping is restored
- assert_equal 'Jamis', Developer.first.name
+ assert_equal "Jamis", Developer.first.name
end
end
def test_nested_scoped_create
- comment = Comment.create_with(:post_id => 1).scoping do
- Comment.create_with(:post_id => 2).scoping do
- Comment.create :body => "Hey guys, nested scopes are broken. Please fix!"
+ comment = Comment.create_with(post_id: 1).scoping do
+ Comment.create_with(post_id: 2).scoping do
+ Comment.create body: "Hey guys, nested scopes are broken. Please fix!"
end
end
@@ -293,15 +300,15 @@ class NestedRelationScopingTest < ActiveRecord::TestCase
end
def test_nested_exclusive_scope_for_create
- comment = Comment.create_with(:body => "Hey guys, nested scopes are broken. Please fix!").scoping do
- Comment.unscoped.create_with(:post_id => 1).scoping do
+ comment = Comment.create_with(body: "Hey guys, nested scopes are broken. Please fix!").scoping do
+ Comment.unscoped.create_with(post_id: 1).scoping do
assert Comment.new.body.blank?
- Comment.create :body => "Hey guys"
+ Comment.create body: "Hey guys"
end
end
assert_equal 1, comment.post_id
- assert_equal 'Hey guys', comment.body
+ assert_equal "Hey guys", comment.body
end
end
@@ -313,24 +320,24 @@ class HasManyScopingTest < ActiveRecord::TestCase
end
def test_forwarding_of_static_methods
- assert_equal 'a comment...', Comment.what_are_you
- assert_equal 'a comment...', @welcome.comments.what_are_you
+ assert_equal "a comment...", Comment.what_are_you
+ assert_equal "a comment...", @welcome.comments.what_are_you
end
def test_forwarding_to_scoped
- assert_equal 4, Comment.search_by_type('Comment').size
- assert_equal 2, @welcome.comments.search_by_type('Comment').size
+ assert_equal 4, Comment.search_by_type("Comment").size
+ assert_equal 2, @welcome.comments.search_by_type("Comment").size
end
def test_nested_scope_finder
- Comment.where('1=0').scoping do
+ Comment.where("1=0").scoping do
assert_equal 0, @welcome.comments.count
- assert_equal 'a comment...', @welcome.comments.what_are_you
+ assert_equal "a comment...", @welcome.comments.what_are_you
end
- Comment.where('1=1').scoping do
+ Comment.where("1=1").scoping do
assert_equal 2, @welcome.comments.count
- assert_equal 'a comment...', @welcome.comments.what_are_you
+ assert_equal "a comment...", @welcome.comments.what_are_you
end
end
@@ -345,7 +352,7 @@ class HasManyScopingTest < ActiveRecord::TestCase
end
def test_should_maintain_default_scope_on_eager_loaded_associations
- michael = Person.where(:id => people(:michael).id).includes(:bad_references).first
+ michael = Person.where(id: people(:michael).id).includes(:bad_references).first
magician = BadReference.find(1)
assert_equal [magician], michael.bad_references
end
@@ -359,19 +366,19 @@ class HasAndBelongsToManyScopingTest < ActiveRecord::TestCase
end
def test_forwarding_of_static_methods
- assert_equal 'a category...', Category.what_are_you
- assert_equal 'a category...', @welcome.categories.what_are_you
+ assert_equal "a category...", Category.what_are_you
+ assert_equal "a category...", @welcome.categories.what_are_you
end
def test_nested_scope_finder
- Category.where('1=0').scoping do
+ Category.where("1=0").scoping do
assert_equal 0, @welcome.categories.count
- assert_equal 'a category...', @welcome.categories.what_are_you
+ assert_equal "a category...", @welcome.categories.what_are_you
end
- Category.where('1=1').scoping do
+ Category.where("1=1").scoping do
assert_equal 2, @welcome.categories.count
- assert_equal 'a category...', @welcome.categories.what_are_you
+ assert_equal "a category...", @welcome.categories.what_are_you
end
end
end
diff --git a/activerecord/test/cases/secure_token_test.rb b/activerecord/test/cases/secure_token_test.rb
index e731443fc2..7b9cbee40a 100644
--- a/activerecord/test/cases/secure_token_test.rb
+++ b/activerecord/test/cases/secure_token_test.rb
@@ -1,5 +1,5 @@
-require 'cases/helper'
-require 'models/user'
+require "cases/helper"
+require "models/user"
class SecureTokenTest < ActiveRecord::TestCase
setup do
@@ -27,6 +27,6 @@ class SecureTokenTest < ActiveRecord::TestCase
@user.token = "custom-secure-token"
@user.save
- assert_equal @user.token, "custom-secure-token"
+ assert_equal "custom-secure-token", @user.token
end
end
diff --git a/activerecord/test/cases/serialization_test.rb b/activerecord/test/cases/serialization_test.rb
index 14b80f4df4..ec33ad38f2 100644
--- a/activerecord/test/cases/serialization_test.rb
+++ b/activerecord/test/cases/serialization_test.rb
@@ -1,9 +1,9 @@
require "cases/helper"
-require 'models/contact'
-require 'models/topic'
-require 'models/book'
-require 'models/author'
-require 'models/post'
+require "models/contact"
+require "models/topic"
+require "models/book"
+require "models/author"
+require "models/post"
class SerializationTest < ActiveRecord::TestCase
fixtures :books
@@ -12,14 +12,14 @@ class SerializationTest < ActiveRecord::TestCase
def setup
@contact_attributes = {
- :name => 'aaron stack',
- :age => 25,
- :avatar => 'binarydata',
- :created_at => Time.utc(2006, 8, 1),
- :awesome => false,
- :preferences => { :gem => '<strong>ruby</strong>' },
- :alternative_id => nil,
- :id => nil
+ name: "aaron stack",
+ age: 25,
+ avatar: "binarydata",
+ created_at: Time.utc(2006, 8, 1),
+ awesome: false,
+ preferences: { gem: "<strong>ruby</strong>" },
+ alternative_id: nil,
+ id: nil
}
end
@@ -38,7 +38,7 @@ class SerializationTest < ActiveRecord::TestCase
def test_serialize_should_allow_attribute_only_filtering
FORMATS.each do |format|
- @serialized = Contact.new(@contact_attributes).send("to_#{format}", :only => [ :age, :name ])
+ @serialized = Contact.new(@contact_attributes).send("to_#{format}", only: [ :age, :name ])
contact = Contact.new.send("from_#{format}", @serialized)
assert_equal @contact_attributes[:name], contact.name, "For #{format}"
assert_nil contact.avatar, "For #{format}"
@@ -47,7 +47,7 @@ class SerializationTest < ActiveRecord::TestCase
def test_serialize_should_allow_attribute_except_filtering
FORMATS.each do |format|
- @serialized = Contact.new(@contact_attributes).send("to_#{format}", :except => [ :age, :name ])
+ @serialized = Contact.new(@contact_attributes).send("to_#{format}", except: [ :age, :name ])
contact = Contact.new.send("from_#{format}", @serialized)
assert_nil contact.name, "For #{format}"
assert_nil contact.age, "For #{format}"
@@ -60,7 +60,7 @@ class SerializationTest < ActiveRecord::TestCase
ActiveRecord::Base.include_root_in_json = true
klazz = Class.new(ActiveRecord::Base)
- klazz.table_name = 'topics'
+ klazz.table_name = "topics"
assert klazz.include_root_in_json
klazz.include_root_in_json = false
@@ -73,7 +73,7 @@ class SerializationTest < ActiveRecord::TestCase
def test_read_attribute_for_serialization_with_format_without_method_missing
klazz = Class.new(ActiveRecord::Base)
- klazz.table_name = 'books'
+ klazz.table_name = "books"
book = klazz.new
assert_nil book.read_attribute_for_serialization(:format)
@@ -81,18 +81,18 @@ class SerializationTest < ActiveRecord::TestCase
def test_read_attribute_for_serialization_with_format_after_init
klazz = Class.new(ActiveRecord::Base)
- klazz.table_name = 'books'
+ klazz.table_name = "books"
- book = klazz.new(format: 'paperback')
- assert_equal 'paperback', book.read_attribute_for_serialization(:format)
+ book = klazz.new(format: "paperback")
+ assert_equal "paperback", book.read_attribute_for_serialization(:format)
end
def test_read_attribute_for_serialization_with_format_after_find
klazz = Class.new(ActiveRecord::Base)
- klazz.table_name = 'books'
+ klazz.table_name = "books"
book = klazz.find(books(:awdr).id)
- assert_equal 'paperback', book.read_attribute_for_serialization(:format)
+ assert_equal "paperback", book.read_attribute_for_serialization(:format)
end
def test_find_records_by_serialized_attributes_through_join
diff --git a/activerecord/test/cases/serialized_attribute_test.rb b/activerecord/test/cases/serialized_attribute_test.rb
index 846be857d0..a469da0a5b 100644
--- a/activerecord/test/cases/serialized_attribute_test.rb
+++ b/activerecord/test/cases/serialized_attribute_test.rb
@@ -1,10 +1,10 @@
-require 'cases/helper'
-require 'models/topic'
-require 'models/reply'
-require 'models/person'
-require 'models/traffic_light'
-require 'models/post'
-require 'bcrypt'
+require "cases/helper"
+require "models/topic"
+require "models/reply"
+require "models/person"
+require "models/traffic_light"
+require "models/post"
+require "bcrypt"
class SerializedAttributeTest < ActiveRecord::TestCase
fixtures :topics, :posts
@@ -25,7 +25,7 @@ class SerializedAttributeTest < ActiveRecord::TestCase
def test_serialized_attribute
Topic.serialize("content", MyObject)
- myobj = MyObject.new('value1', 'value2')
+ myobj = MyObject.new("value1", "value2")
topic = Topic.create("content" => myobj)
assert_equal(myobj, topic.content)
@@ -36,7 +36,7 @@ class SerializedAttributeTest < ActiveRecord::TestCase
def test_serialized_attribute_in_base_class
Topic.serialize("content", Hash)
- hash = { 'content1' => 'value1', 'content2' => 'value2' }
+ hash = { "content1" => "value1", "content2" => "value2" }
important_topic = ImportantTopic.create("content" => hash)
assert_equal(hash, important_topic.content)
@@ -97,7 +97,7 @@ class SerializedAttributeTest < ActiveRecord::TestCase
end
def test_serialized_attribute_declared_in_subclass
- hash = { 'important1' => 'value1', 'important2' => 'value2' }
+ hash = { "important1" => "value1", "important2" => "value2" }
important_topic = ImportantTopic.create("important" => hash)
assert_equal(hash, important_topic.important)
@@ -107,7 +107,7 @@ class SerializedAttributeTest < ActiveRecord::TestCase
end
def test_serialized_time_attribute
- myobj = Time.local(2008,1,1,1,0)
+ myobj = Time.local(2008, 1, 1, 1, 0)
topic = Topic.create("content" => myobj).reload
assert_equal(myobj, topic.content)
end
@@ -124,26 +124,26 @@ class SerializedAttributeTest < ActiveRecord::TestCase
end
def test_nil_not_serialized_without_class_constraint
- assert Topic.new(:content => nil).save
- assert_equal 1, Topic.where(:content => nil).count
+ assert Topic.new(content: nil).save
+ assert_equal 1, Topic.where(content: nil).count
end
def test_nil_not_serialized_with_class_constraint
Topic.serialize :content, Hash
- assert Topic.new(:content => nil).save
- assert_equal 1, Topic.where(:content => nil).count
+ assert Topic.new(content: nil).save
+ assert_equal 1, Topic.where(content: nil).count
end
def test_serialized_attribute_should_raise_exception_on_assignment_with_wrong_type
Topic.serialize(:content, Hash)
assert_raise(ActiveRecord::SerializationTypeMismatch) do
- Topic.new(content: 'string')
+ Topic.new(content: "string")
end
end
def test_should_raise_exception_on_serialized_attribute_with_type_mismatch
- myobj = MyObject.new('value1', 'value2')
- topic = Topic.new(:content => myobj)
+ myobj = MyObject.new("value1", "value2")
+ topic = Topic.new(content: myobj)
assert topic.save
Topic.serialize(:content, Hash)
assert_raise(ActiveRecord::SerializationTypeMismatch) { Topic.find(topic.id).content }
@@ -152,11 +152,18 @@ class SerializedAttributeTest < ActiveRecord::TestCase
def test_serialized_attribute_with_class_constraint
settings = { "color" => "blue" }
Topic.serialize(:content, Hash)
- topic = Topic.new(:content => settings)
+ topic = Topic.new(content: settings)
assert topic.save
assert_equal(settings, Topic.find(topic.id).content)
end
+ def test_where_by_serialized_attribute_with_hash
+ settings = { "color" => "green" }
+ Topic.serialize(:content, Hash)
+ topic = Topic.create!(content: settings)
+ assert_equal topic, Topic.where(content: settings).take
+ end
+
def test_serialized_default_class
Topic.serialize(:content, Hash)
topic = Topic.new
@@ -175,17 +182,17 @@ class SerializedAttributeTest < ActiveRecord::TestCase
end
def test_serialized_boolean_value_true
- topic = Topic.new(:content => true)
+ topic = Topic.new(content: true)
assert topic.save
topic = topic.reload
- assert_equal topic.content, true
+ assert_equal true, topic.content
end
def test_serialized_boolean_value_false
- topic = Topic.new(:content => false)
+ topic = Topic.new(content: false)
assert topic.save
topic = topic.reload
- assert_equal topic.content, false
+ assert_equal false, topic.content
end
def test_serialize_with_coder
@@ -200,18 +207,18 @@ class SerializedAttributeTest < ActiveRecord::TestCase
end
Topic.serialize(:content, some_class)
- topic = Topic.new(:content => some_class.new('my value'))
+ topic = Topic.new(content: some_class.new("my value"))
topic.save!
topic.reload
assert_kind_of some_class, topic.content
- assert_equal topic.content, some_class.new('my value')
+ assert_equal some_class.new("my value"), topic.content
end
def test_serialize_attribute_via_select_method_when_time_zone_available
with_timezone_config aware_attributes: true do
Topic.serialize(:content, MyObject)
- myobj = MyObject.new('value1', 'value2')
+ myobj = MyObject.new("value1", "value2")
topic = Topic.create(content: myobj)
assert_equal(myobj, Topic.select(:content).find(topic.id).content)
@@ -220,8 +227,8 @@ class SerializedAttributeTest < ActiveRecord::TestCase
end
def test_serialize_attribute_can_be_serialized_in_an_integer_column
- insures = ['life']
- person = SerializedPerson.new(first_name: 'David', insures: insures)
+ insures = ["life"]
+ person = SerializedPerson.new(first_name: "David", insures: insures)
assert person.save
person = person.reload
assert_equal(insures, person.insures)
diff --git a/activerecord/test/cases/statement_cache_test.rb b/activerecord/test/cases/statement_cache_test.rb
index 104226010a..f45f63c68e 100644
--- a/activerecord/test/cases/statement_cache_test.rb
+++ b/activerecord/test/cases/statement_cache_test.rb
@@ -1,8 +1,8 @@
-require 'cases/helper'
-require 'models/book'
-require 'models/liquid'
-require 'models/molecule'
-require 'models/electron'
+require "cases/helper"
+require "models/book"
+require "models/liquid"
+require "models/molecule"
+require "models/electron"
module ActiveRecord
class StatementCacheTest < ActiveRecord::TestCase
@@ -16,7 +16,7 @@ module ActiveRecord
Book.create(name: "my other book")
cache = StatementCache.create(Book.connection) do |params|
- Book.where(:name => params.bind)
+ Book.where(name: params.bind)
end
b = cache.execute([ "my book" ], Book, Book.connection)
@@ -25,7 +25,6 @@ module ActiveRecord
assert_equal "my other book", b[0].name
end
-
def test_statement_cache_id
b1 = Book.create(name: "my book")
b2 = Book.create(name: "my other book")
@@ -65,12 +64,12 @@ module ActiveRecord
def test_statement_cache_with_complex_statement
cache = ActiveRecord::StatementCache.create(Book.connection) do |params|
- Liquid.joins(:molecules => :electrons).where('molecules.name' => 'dioxane', 'electrons.name' => 'lepton')
+ Liquid.joins(molecules: :electrons).where("molecules.name" => "dioxane", "electrons.name" => "lepton")
end
- salty = Liquid.create(name: 'salty')
- molecule = salty.molecules.create(name: 'dioxane')
- molecule.electrons.create(name: 'lepton')
+ salty = Liquid.create(name: "salty")
+ molecule = salty.molecules.create(name: "dioxane")
+ molecule.electrons.create(name: "lepton")
liquids = cache.execute([], Book, Book.connection)
assert_equal "salty", liquids[0].name
diff --git a/activerecord/test/cases/store_test.rb b/activerecord/test/cases/store_test.rb
index bce86875e1..633a8a0ebc 100644
--- a/activerecord/test/cases/store_test.rb
+++ b/activerecord/test/cases/store_test.rb
@@ -1,55 +1,55 @@
-require 'cases/helper'
-require 'models/admin'
-require 'models/admin/user'
+require "cases/helper"
+require "models/admin"
+require "models/admin/user"
class StoreTest < ActiveRecord::TestCase
fixtures :'admin/users'
setup do
- @john = Admin::User.create!(:name => 'John Doe', :color => 'black', :remember_login => true, :height => 'tall', :is_a_good_guy => true)
+ @john = Admin::User.create!(name: "John Doe", color: "black", remember_login: true, height: "tall", is_a_good_guy: true)
end
test "reading store attributes through accessors" do
- assert_equal 'black', @john.color
+ assert_equal "black", @john.color
assert_nil @john.homepage
end
test "writing store attributes through accessors" do
- @john.color = 'red'
- @john.homepage = '37signals.com'
+ @john.color = "red"
+ @john.homepage = "37signals.com"
- assert_equal 'red', @john.color
- assert_equal '37signals.com', @john.homepage
+ assert_equal "red", @john.color
+ assert_equal "37signals.com", @john.homepage
end
test "accessing attributes not exposed by accessors" do
- @john.settings[:icecream] = 'graeters'
+ @john.settings[:icecream] = "graeters"
@john.save
- assert_equal 'graeters', @john.reload.settings[:icecream]
+ assert_equal "graeters", @john.reload.settings[:icecream]
end
test "overriding a read accessor" do
- @john.settings[:phone_number] = '1234567890'
+ @john.settings[:phone_number] = "1234567890"
- assert_equal '(123) 456-7890', @john.phone_number
+ assert_equal "(123) 456-7890", @john.phone_number
end
test "overriding a read accessor using super" do
@john.settings[:color] = nil
- assert_equal 'red', @john.color
+ assert_equal "red", @john.color
end
test "updating the store will mark it as changed" do
- @john.color = 'red'
+ @john.color = "red"
assert @john.settings_changed?
end
test "updating the store populates the changed array correctly" do
- @john.color = 'red'
- assert_equal 'black', @john.settings_change[0]['color']
- assert_equal 'red', @john.settings_change[1]['color']
+ @john.color = "red"
+ assert_equal "black", @john.settings_change[0]["color"]
+ assert_equal "red", @john.settings_change[1]["color"]
end
test "updating the store won't mark it as changed if an attribute isn't changed" do
@@ -67,74 +67,74 @@ class StoreTest < ActiveRecord::TestCase
end
test "overriding a write accessor" do
- @john.phone_number = '(123) 456-7890'
+ @john.phone_number = "(123) 456-7890"
- assert_equal '1234567890', @john.settings[:phone_number]
+ assert_equal "1234567890", @john.settings[:phone_number]
end
test "overriding a write accessor using super" do
- @john.color = 'yellow'
+ @john.color = "yellow"
- assert_equal 'blue', @john.color
+ assert_equal "blue", @john.color
end
test "preserve store attributes data in HashWithIndifferentAccess format without any conversion" do
- @john.json_data = ActiveSupport::HashWithIndifferentAccess.new(:height => 'tall', 'weight' => 'heavy')
- @john.height = 'low'
+ @john.json_data = ActiveSupport::HashWithIndifferentAccess.new(:height => "tall", "weight" => "heavy")
+ @john.height = "low"
assert_equal true, @john.json_data.instance_of?(ActiveSupport::HashWithIndifferentAccess)
- assert_equal 'low', @john.json_data[:height]
- assert_equal 'low', @john.json_data['height']
- assert_equal 'heavy', @john.json_data[:weight]
- assert_equal 'heavy', @john.json_data['weight']
+ assert_equal "low", @john.json_data[:height]
+ assert_equal "low", @john.json_data["height"]
+ assert_equal "heavy", @john.json_data[:weight]
+ assert_equal "heavy", @john.json_data["weight"]
end
test "convert store attributes from Hash to HashWithIndifferentAccess saving the data and access attributes indifferently" do
- user = Admin::User.find_by_name('Jamis')
- assert_equal 'symbol', user.settings[:symbol]
- assert_equal 'symbol', user.settings['symbol']
- assert_equal 'string', user.settings[:string]
- assert_equal 'string', user.settings['string']
+ user = Admin::User.find_by_name("Jamis")
+ assert_equal "symbol", user.settings[:symbol]
+ assert_equal "symbol", user.settings["symbol"]
+ assert_equal "string", user.settings[:string]
+ assert_equal "string", user.settings["string"]
assert_equal true, user.settings.instance_of?(ActiveSupport::HashWithIndifferentAccess)
- user.height = 'low'
- assert_equal 'symbol', user.settings[:symbol]
- assert_equal 'symbol', user.settings['symbol']
- assert_equal 'string', user.settings[:string]
- assert_equal 'string', user.settings['string']
+ user.height = "low"
+ assert_equal "symbol", user.settings[:symbol]
+ assert_equal "symbol", user.settings["symbol"]
+ assert_equal "string", user.settings[:string]
+ assert_equal "string", user.settings["string"]
assert_equal true, user.settings.instance_of?(ActiveSupport::HashWithIndifferentAccess)
end
test "convert store attributes from any format other than Hash or HashWithIndifferentAccess losing the data" do
@john.json_data = "somedata"
- @john.height = 'low'
+ @john.height = "low"
assert_equal true, @john.json_data.instance_of?(ActiveSupport::HashWithIndifferentAccess)
- assert_equal 'low', @john.json_data[:height]
- assert_equal 'low', @john.json_data['height']
- assert_equal false, @john.json_data.delete_if { |k, v| k == 'height' }.any?
+ assert_equal "low", @john.json_data[:height]
+ assert_equal "low", @john.json_data["height"]
+ assert_equal false, @john.json_data.delete_if { |k, v| k == "height" }.any?
end
test "reading store attributes through accessors encoded with JSON" do
- assert_equal 'tall', @john.height
+ assert_equal "tall", @john.height
assert_nil @john.weight
end
test "writing store attributes through accessors encoded with JSON" do
- @john.height = 'short'
- @john.weight = 'heavy'
+ @john.height = "short"
+ @john.weight = "heavy"
- assert_equal 'short', @john.height
- assert_equal 'heavy', @john.weight
+ assert_equal "short", @john.height
+ assert_equal "heavy", @john.weight
end
test "accessing attributes not exposed by accessors encoded with JSON" do
- @john.json_data['somestuff'] = 'somecoolstuff'
+ @john.json_data["somestuff"] = "somecoolstuff"
@john.save
- assert_equal 'somecoolstuff', @john.reload.json_data['somestuff']
+ assert_equal "somecoolstuff", @john.reload.json_data["somestuff"]
end
test "updating the store will mark it as changed encoded with JSON" do
- @john.height = 'short'
+ @john.height = "short"
assert @john.json_data_changed?
end
diff --git a/activerecord/test/cases/suppressor_test.rb b/activerecord/test/cases/suppressor_test.rb
index 2f00241de2..a7d16b7cdb 100644
--- a/activerecord/test/cases/suppressor_test.rb
+++ b/activerecord/test/cases/suppressor_test.rb
@@ -1,6 +1,6 @@
-require 'cases/helper'
-require 'models/notification'
-require 'models/user'
+require "cases/helper"
+require "models/notification"
+require "models/user"
class SuppressorTest < ActiveRecord::TestCase
def test_suppresses_create
@@ -15,22 +15,22 @@ class SuppressorTest < ActiveRecord::TestCase
end
def test_suppresses_update
- user = User.create! token: 'asdf'
+ user = User.create! token: "asdf"
User.suppress do
- user.update token: 'ghjkl'
- assert_equal 'asdf', user.reload.token
+ user.update token: "ghjkl"
+ assert_equal "asdf", user.reload.token
- user.update! token: 'zxcvbnm'
- assert_equal 'asdf', user.reload.token
+ user.update! token: "zxcvbnm"
+ assert_equal "asdf", user.reload.token
- user.token = 'qwerty'
+ user.token = "qwerty"
user.save
- assert_equal 'asdf', user.reload.token
+ assert_equal "asdf", user.reload.token
- user.token = 'uiop'
+ user.token = "uiop"
user.save!
- assert_equal 'asdf', user.reload.token
+ assert_equal "asdf", user.reload.token
end
end
@@ -64,7 +64,7 @@ class SuppressorTest < ActiveRecord::TestCase
def test_suppresses_when_nested_multiple_times
assert_no_difference -> { Notification.count } do
Notification.suppress do
- Notification.suppress { }
+ Notification.suppress {}
Notification.create
Notification.create!
Notification.new.save
diff --git a/activerecord/test/cases/tasks/database_tasks_test.rb b/activerecord/test/cases/tasks/database_tasks_test.rb
index 510bb088c8..f7c53b5801 100644
--- a/activerecord/test/cases/tasks/database_tasks_test.rb
+++ b/activerecord/test/cases/tasks/database_tasks_test.rb
@@ -1,5 +1,5 @@
-require 'cases/helper'
-require 'active_record/tasks/database_tasks'
+require "cases/helper"
+require "active_record/tasks/database_tasks"
module ActiveRecord
module DatabaseTasksSetupper
@@ -24,13 +24,13 @@ module ActiveRecord
sqlite3: :sqlite_tasks
}
- class DatabaseTasksUtilsTask< ActiveRecord::TestCase
+ class DatabaseTasksUtilsTask < ActiveRecord::TestCase
def test_raises_an_error_when_called_with_protected_environment
ActiveRecord::Migrator.stubs(:current_version).returns(1)
protected_environments = ActiveRecord::Base.protected_environments.dup
current_env = ActiveRecord::Migrator.current_environment
- assert !protected_environments.include?(current_env)
+ assert_not_includes protected_environments, current_env
# Assert no error
ActiveRecord::Tasks::DatabaseTasks.check_protected_environments!
@@ -61,15 +61,15 @@ module ActiveRecord
instance = klazz.new
klazz.stubs(:new).returns instance
- instance.expects(:structure_dump).with("awesome-file.sql")
+ instance.expects(:structure_dump).with("awesome-file.sql", nil)
ActiveRecord::Tasks::DatabaseTasks.register_task(/foo/, klazz)
- ActiveRecord::Tasks::DatabaseTasks.structure_dump({'adapter' => :foo}, "awesome-file.sql")
+ ActiveRecord::Tasks::DatabaseTasks.structure_dump({ "adapter" => :foo }, "awesome-file.sql")
end
def test_unregistered_task
assert_raise(ActiveRecord::Tasks::DatabaseNotSupported) do
- ActiveRecord::Tasks::DatabaseTasks.structure_dump({'adapter' => :bar}, "awesome-file.sql")
+ ActiveRecord::Tasks::DatabaseTasks.structure_dump({ "adapter" => :bar }, "awesome-file.sql")
end
end
end
@@ -80,20 +80,30 @@ module ActiveRecord
ADAPTERS_TASKS.each do |k, v|
define_method("test_#{k}_create") do
eval("@#{v}").expects(:create)
- ActiveRecord::Tasks::DatabaseTasks.create 'adapter' => k
+ ActiveRecord::Tasks::DatabaseTasks.create "adapter" => k
end
end
end
+ class DatabaseTasksDumpSchemaCacheTest < ActiveRecord::TestCase
+ def test_dump_schema_cache
+ path = "/tmp/my_schema_cache.yml"
+ ActiveRecord::Tasks::DatabaseTasks.dump_schema_cache(ActiveRecord::Base.connection, path)
+ assert File.file?(path)
+ ensure
+ FileUtils.rm_rf(path)
+ end
+ end
+
class DatabaseTasksCreateAllTest < ActiveRecord::TestCase
def setup
- @configurations = {'development' => {'database' => 'my-db'}}
+ @configurations = { "development" => { "database" => "my-db" } }
ActiveRecord::Base.stubs(:configurations).returns(@configurations)
end
def test_ignores_configurations_without_databases
- @configurations['development'].merge!('database' => nil)
+ @configurations["development"].merge!("database" => nil)
ActiveRecord::Tasks::DatabaseTasks.expects(:create).never
@@ -101,7 +111,7 @@ module ActiveRecord
end
def test_ignores_remote_databases
- @configurations['development'].merge!('host' => 'my.server.tld')
+ @configurations["development"].merge!("host" => "my.server.tld")
$stderr.stubs(:puts).returns(nil)
ActiveRecord::Tasks::DatabaseTasks.expects(:create).never
@@ -110,15 +120,15 @@ module ActiveRecord
end
def test_warning_for_remote_databases
- @configurations['development'].merge!('host' => 'my.server.tld')
+ @configurations["development"].merge!("host" => "my.server.tld")
- $stderr.expects(:puts).with('This task only modifies local databases. my-db is on a remote host.')
+ $stderr.expects(:puts).with("This task only modifies local databases. my-db is on a remote host.")
ActiveRecord::Tasks::DatabaseTasks.create_all
end
def test_creates_configurations_with_local_ip
- @configurations['development'].merge!('host' => '127.0.0.1')
+ @configurations["development"].merge!("host" => "127.0.0.1")
ActiveRecord::Tasks::DatabaseTasks.expects(:create)
@@ -126,7 +136,7 @@ module ActiveRecord
end
def test_creates_configurations_with_local_host
- @configurations['development'].merge!('host' => 'localhost')
+ @configurations["development"].merge!("host" => "localhost")
ActiveRecord::Tasks::DatabaseTasks.expects(:create)
@@ -134,7 +144,7 @@ module ActiveRecord
end
def test_creates_configurations_with_blank_hosts
- @configurations['development'].merge!('host' => nil)
+ @configurations["development"].merge!("host" => nil)
ActiveRecord::Tasks::DatabaseTasks.expects(:create)
@@ -145,9 +155,9 @@ module ActiveRecord
class DatabaseTasksCreateCurrentTest < ActiveRecord::TestCase
def setup
@configurations = {
- 'development' => {'database' => 'dev-db'},
- 'test' => {'database' => 'test-db'},
- 'production' => {'database' => 'prod-db'}
+ "development" => { "database" => "dev-db" },
+ "test" => { "database" => "test-db" },
+ "production" => { "database" => "prod-db" }
}
ActiveRecord::Base.stubs(:configurations).returns(@configurations)
@@ -156,37 +166,37 @@ module ActiveRecord
def test_creates_current_environment_database
ActiveRecord::Tasks::DatabaseTasks.expects(:create).
- with('database' => 'prod-db')
+ with("database" => "prod-db")
ActiveRecord::Tasks::DatabaseTasks.create_current(
- ActiveSupport::StringInquirer.new('production')
+ ActiveSupport::StringInquirer.new("production")
)
end
def test_creates_test_and_development_databases_when_env_was_not_specified
ActiveRecord::Tasks::DatabaseTasks.expects(:create).
- with('database' => 'dev-db')
+ with("database" => "dev-db")
ActiveRecord::Tasks::DatabaseTasks.expects(:create).
- with('database' => 'test-db')
+ with("database" => "test-db")
ActiveRecord::Tasks::DatabaseTasks.create_current(
- ActiveSupport::StringInquirer.new('development')
+ ActiveSupport::StringInquirer.new("development")
)
end
def test_creates_test_and_development_databases_when_rails_env_is_development
- old_env = ENV['RAILS_ENV']
- ENV['RAILS_ENV'] = 'development'
+ old_env = ENV["RAILS_ENV"]
+ ENV["RAILS_ENV"] = "development"
ActiveRecord::Tasks::DatabaseTasks.expects(:create).
- with('database' => 'dev-db')
+ with("database" => "dev-db")
ActiveRecord::Tasks::DatabaseTasks.expects(:create).
- with('database' => 'test-db')
+ with("database" => "test-db")
ActiveRecord::Tasks::DatabaseTasks.create_current(
- ActiveSupport::StringInquirer.new('development')
+ ActiveSupport::StringInquirer.new("development")
)
ensure
- ENV['RAILS_ENV'] = old_env
+ ENV["RAILS_ENV"] = old_env
end
def test_establishes_connection_for_the_given_environment
@@ -195,7 +205,7 @@ module ActiveRecord
ActiveRecord::Base.expects(:establish_connection).with(:development)
ActiveRecord::Tasks::DatabaseTasks.create_current(
- ActiveSupport::StringInquirer.new('development')
+ ActiveSupport::StringInquirer.new("development")
)
end
end
@@ -206,20 +216,20 @@ module ActiveRecord
ADAPTERS_TASKS.each do |k, v|
define_method("test_#{k}_drop") do
eval("@#{v}").expects(:drop)
- ActiveRecord::Tasks::DatabaseTasks.drop 'adapter' => k
+ ActiveRecord::Tasks::DatabaseTasks.drop "adapter" => k
end
end
end
class DatabaseTasksDropAllTest < ActiveRecord::TestCase
def setup
- @configurations = {:development => {'database' => 'my-db'}}
+ @configurations = { development: { "database" => "my-db" } }
ActiveRecord::Base.stubs(:configurations).returns(@configurations)
end
def test_ignores_configurations_without_databases
- @configurations[:development].merge!('database' => nil)
+ @configurations[:development].merge!("database" => nil)
ActiveRecord::Tasks::DatabaseTasks.expects(:drop).never
@@ -227,7 +237,7 @@ module ActiveRecord
end
def test_ignores_remote_databases
- @configurations[:development].merge!('host' => 'my.server.tld')
+ @configurations[:development].merge!("host" => "my.server.tld")
$stderr.stubs(:puts).returns(nil)
ActiveRecord::Tasks::DatabaseTasks.expects(:drop).never
@@ -236,15 +246,15 @@ module ActiveRecord
end
def test_warning_for_remote_databases
- @configurations[:development].merge!('host' => 'my.server.tld')
+ @configurations[:development].merge!("host" => "my.server.tld")
- $stderr.expects(:puts).with('This task only modifies local databases. my-db is on a remote host.')
+ $stderr.expects(:puts).with("This task only modifies local databases. my-db is on a remote host.")
ActiveRecord::Tasks::DatabaseTasks.drop_all
end
def test_drops_configurations_with_local_ip
- @configurations[:development].merge!('host' => '127.0.0.1')
+ @configurations[:development].merge!("host" => "127.0.0.1")
ActiveRecord::Tasks::DatabaseTasks.expects(:drop)
@@ -252,7 +262,7 @@ module ActiveRecord
end
def test_drops_configurations_with_local_host
- @configurations[:development].merge!('host' => 'localhost')
+ @configurations[:development].merge!("host" => "localhost")
ActiveRecord::Tasks::DatabaseTasks.expects(:drop)
@@ -260,7 +270,7 @@ module ActiveRecord
end
def test_drops_configurations_with_blank_hosts
- @configurations[:development].merge!('host' => nil)
+ @configurations[:development].merge!("host" => nil)
ActiveRecord::Tasks::DatabaseTasks.expects(:drop)
@@ -271,9 +281,9 @@ module ActiveRecord
class DatabaseTasksDropCurrentTest < ActiveRecord::TestCase
def setup
@configurations = {
- 'development' => {'database' => 'dev-db'},
- 'test' => {'database' => 'test-db'},
- 'production' => {'database' => 'prod-db'}
+ "development" => { "database" => "dev-db" },
+ "test" => { "database" => "test-db" },
+ "production" => { "database" => "prod-db" }
}
ActiveRecord::Base.stubs(:configurations).returns(@configurations)
@@ -281,37 +291,37 @@ module ActiveRecord
def test_drops_current_environment_database
ActiveRecord::Tasks::DatabaseTasks.expects(:drop).
- with('database' => 'prod-db')
+ with("database" => "prod-db")
ActiveRecord::Tasks::DatabaseTasks.drop_current(
- ActiveSupport::StringInquirer.new('production')
+ ActiveSupport::StringInquirer.new("production")
)
end
def test_drops_test_and_development_databases_when_env_was_not_specified
ActiveRecord::Tasks::DatabaseTasks.expects(:drop).
- with('database' => 'dev-db')
+ with("database" => "dev-db")
ActiveRecord::Tasks::DatabaseTasks.expects(:drop).
- with('database' => 'test-db')
+ with("database" => "test-db")
ActiveRecord::Tasks::DatabaseTasks.drop_current(
- ActiveSupport::StringInquirer.new('development')
+ ActiveSupport::StringInquirer.new("development")
)
end
def test_drops_testand_development_databases_when_rails_env_is_development
- old_env = ENV['RAILS_ENV']
- ENV['RAILS_ENV'] = 'development'
+ old_env = ENV["RAILS_ENV"]
+ ENV["RAILS_ENV"] = "development"
ActiveRecord::Tasks::DatabaseTasks.expects(:drop).
- with('database' => 'dev-db')
+ with("database" => "dev-db")
ActiveRecord::Tasks::DatabaseTasks.expects(:drop).
- with('database' => 'test-db')
+ with("database" => "test-db")
ActiveRecord::Tasks::DatabaseTasks.drop_current(
- ActiveSupport::StringInquirer.new('development')
+ ActiveSupport::StringInquirer.new("development")
)
ensure
- ENV['RAILS_ENV'] = old_env
+ ENV["RAILS_ENV"] = old_env
end
end
@@ -319,7 +329,7 @@ module ActiveRecord
self.use_transactional_tests = false
def setup
- ActiveRecord::Tasks::DatabaseTasks.migrations_paths = 'custom/path'
+ ActiveRecord::Tasks::DatabaseTasks.migrations_paths = "custom/path"
end
def teardown
@@ -327,15 +337,15 @@ module ActiveRecord
end
def test_migrate_receives_correct_env_vars
- verbose, version = ENV['VERBOSE'], ENV['VERSION']
+ verbose, version = ENV["VERBOSE"], ENV["VERSION"]
- ENV['VERBOSE'] = 'false'
- ENV['VERSION'] = '4'
+ ENV["VERBOSE"] = "false"
+ ENV["VERSION"] = "4"
- ActiveRecord::Migrator.expects(:migrate).with('custom/path', 4)
+ ActiveRecord::Migrator.expects(:migrate).with("custom/path", 4)
ActiveRecord::Tasks::DatabaseTasks.migrate
ensure
- ENV['VERBOSE'], ENV['VERSION'] = verbose, version
+ ENV["VERBOSE"], ENV["VERSION"] = verbose, version
end
def test_migrate_clears_schema_cache_afterward
@@ -350,7 +360,7 @@ module ActiveRecord
ADAPTERS_TASKS.each do |k, v|
define_method("test_#{k}_purge") do
eval("@#{v}").expects(:purge)
- ActiveRecord::Tasks::DatabaseTasks.purge 'adapter' => k
+ ActiveRecord::Tasks::DatabaseTasks.purge "adapter" => k
end
end
end
@@ -358,27 +368,27 @@ module ActiveRecord
class DatabaseTasksPurgeCurrentTest < ActiveRecord::TestCase
def test_purges_current_environment_database
configurations = {
- 'development' => {'database' => 'dev-db'},
- 'test' => {'database' => 'test-db'},
- 'production' => {'database' => 'prod-db'}
+ "development" => { "database" => "dev-db" },
+ "test" => { "database" => "test-db" },
+ "production" => { "database" => "prod-db" }
}
ActiveRecord::Base.stubs(:configurations).returns(configurations)
ActiveRecord::Tasks::DatabaseTasks.expects(:purge).
- with('database' => 'prod-db')
+ with("database" => "prod-db")
ActiveRecord::Base.expects(:establish_connection).with(:production)
- ActiveRecord::Tasks::DatabaseTasks.purge_current('production')
+ ActiveRecord::Tasks::DatabaseTasks.purge_current("production")
end
end
class DatabaseTasksPurgeAllTest < ActiveRecord::TestCase
def test_purge_all_local_configurations
- configurations = {:development => {'database' => 'my-db'}}
+ configurations = { development: { "database" => "my-db" } }
ActiveRecord::Base.stubs(:configurations).returns(configurations)
ActiveRecord::Tasks::DatabaseTasks.expects(:purge).
- with('database' => 'my-db')
+ with("database" => "my-db")
ActiveRecord::Tasks::DatabaseTasks.purge_all
end
@@ -390,7 +400,7 @@ module ActiveRecord
ADAPTERS_TASKS.each do |k, v|
define_method("test_#{k}_charset") do
eval("@#{v}").expects(:charset)
- ActiveRecord::Tasks::DatabaseTasks.charset 'adapter' => k
+ ActiveRecord::Tasks::DatabaseTasks.charset "adapter" => k
end
end
end
@@ -401,7 +411,7 @@ module ActiveRecord
ADAPTERS_TASKS.each do |k, v|
define_method("test_#{k}_collation") do
eval("@#{v}").expects(:collation)
- ActiveRecord::Tasks::DatabaseTasks.collation 'adapter' => k
+ ActiveRecord::Tasks::DatabaseTasks.collation "adapter" => k
end
end
end
@@ -411,8 +421,8 @@ module ActiveRecord
ADAPTERS_TASKS.each do |k, v|
define_method("test_#{k}_structure_dump") do
- eval("@#{v}").expects(:structure_dump).with("awesome-file.sql")
- ActiveRecord::Tasks::DatabaseTasks.structure_dump({'adapter' => k}, "awesome-file.sql")
+ eval("@#{v}").expects(:structure_dump).with("awesome-file.sql", nil)
+ ActiveRecord::Tasks::DatabaseTasks.structure_dump({ "adapter" => k }, "awesome-file.sql")
end
end
end
@@ -422,8 +432,8 @@ module ActiveRecord
ADAPTERS_TASKS.each do |k, v|
define_method("test_#{k}_structure_load") do
- eval("@#{v}").expects(:structure_load).with("awesome-file.sql")
- ActiveRecord::Tasks::DatabaseTasks.structure_load({'adapter' => k}, "awesome-file.sql")
+ eval("@#{v}").expects(:structure_load).with("awesome-file.sql", nil)
+ ActiveRecord::Tasks::DatabaseTasks.structure_load({ "adapter" => k }, "awesome-file.sql")
end
end
end
@@ -437,15 +447,15 @@ module ActiveRecord
class DatabaseTasksCheckSchemaFileDefaultsTest < ActiveRecord::TestCase
def test_check_schema_file_defaults
- ActiveRecord::Tasks::DatabaseTasks.stubs(:db_dir).returns('/tmp')
- assert_equal '/tmp/schema.rb', ActiveRecord::Tasks::DatabaseTasks.schema_file
+ ActiveRecord::Tasks::DatabaseTasks.stubs(:db_dir).returns("/tmp")
+ assert_equal "/tmp/schema.rb", ActiveRecord::Tasks::DatabaseTasks.schema_file
end
end
class DatabaseTasksCheckSchemaFileSpecifiedFormatsTest < ActiveRecord::TestCase
- {ruby: 'schema.rb', sql: 'structure.sql'}.each_pair do |fmt, filename|
+ { ruby: "schema.rb", sql: "structure.sql" }.each_pair do |fmt, filename|
define_method("test_check_schema_file_for_#{fmt}_format") do
- ActiveRecord::Tasks::DatabaseTasks.stubs(:db_dir).returns('/tmp')
+ ActiveRecord::Tasks::DatabaseTasks.stubs(:db_dir).returns("/tmp")
assert_equal "/tmp/#{filename}", ActiveRecord::Tasks::DatabaseTasks.schema_file(fmt)
end
end
diff --git a/activerecord/test/cases/tasks/mysql_rake_test.rb b/activerecord/test/cases/tasks/mysql_rake_test.rb
index 70e406038f..f30e0958c3 100644
--- a/activerecord/test/cases/tasks/mysql_rake_test.rb
+++ b/activerecord/test/cases/tasks/mysql_rake_test.rb
@@ -1,345 +1,377 @@
-require 'cases/helper'
-require 'active_record/tasks/database_tasks'
+require "cases/helper"
+require "active_record/tasks/database_tasks"
if current_adapter?(:Mysql2Adapter)
-module ActiveRecord
- class MysqlDBCreateTest < ActiveRecord::TestCase
- def setup
- @connection = stub(:create_database => true)
- @configuration = {
- 'adapter' => 'mysql2',
- 'database' => 'my-app-db'
- }
-
- ActiveRecord::Base.stubs(:connection).returns(@connection)
- ActiveRecord::Base.stubs(:establish_connection).returns(true)
-
- $stdout, @original_stdout = StringIO.new, $stdout
- $stderr, @original_stderr = StringIO.new, $stderr
- end
+ module ActiveRecord
+ class MysqlDBCreateTest < ActiveRecord::TestCase
+ def setup
+ @connection = stub(create_database: true)
+ @configuration = {
+ "adapter" => "mysql2",
+ "database" => "my-app-db"
+ }
+
+ ActiveRecord::Base.stubs(:connection).returns(@connection)
+ ActiveRecord::Base.stubs(:establish_connection).returns(true)
+
+ $stdout, @original_stdout = StringIO.new, $stdout
+ $stderr, @original_stderr = StringIO.new, $stderr
+ end
- def teardown
- $stdout, $stderr = @original_stdout, @original_stderr
- end
+ def teardown
+ $stdout, $stderr = @original_stdout, @original_stderr
+ end
- def test_establishes_connection_without_database
- ActiveRecord::Base.expects(:establish_connection).
- with('adapter' => 'mysql2', 'database' => nil)
+ def test_establishes_connection_without_database
+ ActiveRecord::Base.expects(:establish_connection).
+ with("adapter" => "mysql2", "database" => nil)
- ActiveRecord::Tasks::DatabaseTasks.create @configuration
- end
+ ActiveRecord::Tasks::DatabaseTasks.create @configuration
+ end
- def test_creates_database_with_no_default_options
- @connection.expects(:create_database).
- with('my-app-db', {})
+ def test_creates_database_with_no_default_options
+ @connection.expects(:create_database).
+ with("my-app-db", {})
- ActiveRecord::Tasks::DatabaseTasks.create @configuration
- end
+ ActiveRecord::Tasks::DatabaseTasks.create @configuration
+ end
- def test_creates_database_with_given_encoding
- @connection.expects(:create_database).
- with('my-app-db', charset: 'latin1')
+ def test_creates_database_with_given_encoding
+ @connection.expects(:create_database).
+ with("my-app-db", charset: "latin1")
- ActiveRecord::Tasks::DatabaseTasks.create @configuration.merge('encoding' => 'latin1')
- end
+ ActiveRecord::Tasks::DatabaseTasks.create @configuration.merge("encoding" => "latin1")
+ end
- def test_creates_database_with_given_collation
- @connection.expects(:create_database).
- with('my-app-db', collation: 'latin1_swedish_ci')
+ def test_creates_database_with_given_collation
+ @connection.expects(:create_database).
+ with("my-app-db", collation: "latin1_swedish_ci")
- ActiveRecord::Tasks::DatabaseTasks.create @configuration.merge('collation' => 'latin1_swedish_ci')
- end
+ ActiveRecord::Tasks::DatabaseTasks.create @configuration.merge("collation" => "latin1_swedish_ci")
+ end
- def test_establishes_connection_to_database
- ActiveRecord::Base.expects(:establish_connection).with(@configuration)
+ def test_establishes_connection_to_database
+ ActiveRecord::Base.expects(:establish_connection).with(@configuration)
- ActiveRecord::Tasks::DatabaseTasks.create @configuration
- end
+ ActiveRecord::Tasks::DatabaseTasks.create @configuration
+ end
- def test_when_database_created_successfully_outputs_info_to_stdout
- ActiveRecord::Tasks::DatabaseTasks.create @configuration
+ def test_when_database_created_successfully_outputs_info_to_stdout
+ ActiveRecord::Tasks::DatabaseTasks.create @configuration
- assert_equal $stdout.string, "Created database 'my-app-db'\n"
- end
+ assert_equal "Created database 'my-app-db'\n", $stdout.string
+ end
- def test_create_when_database_exists_outputs_info_to_stderr
- ActiveRecord::Base.connection.stubs(:create_database).raises(
- ActiveRecord::Tasks::DatabaseAlreadyExists
- )
+ def test_create_when_database_exists_outputs_info_to_stderr
+ ActiveRecord::Base.connection.stubs(:create_database).raises(
+ ActiveRecord::Tasks::DatabaseAlreadyExists
+ )
- ActiveRecord::Tasks::DatabaseTasks.create @configuration
+ ActiveRecord::Tasks::DatabaseTasks.create @configuration
- assert_equal $stderr.string, "Database 'my-app-db' already exists\n"
+ assert_equal "Database 'my-app-db' already exists\n", $stderr.string
+ end
end
- end
- class MysqlDBCreateAsRootTest < ActiveRecord::TestCase
- def setup
- @connection = stub("Connection", create_database: true)
- @error = Mysql2::Error.new("Invalid permissions")
- @configuration = {
- 'adapter' => 'mysql2',
- 'database' => 'my-app-db',
- 'username' => 'pat',
- 'password' => 'wossname'
- }
-
- $stdin.stubs(:gets).returns("secret\n")
- $stdout.stubs(:print).returns(nil)
- @error.stubs(:errno).returns(1045)
- ActiveRecord::Base.stubs(:connection).returns(@connection)
- ActiveRecord::Base.stubs(:establish_connection).
- raises(@error).
- then.returns(true)
-
- $stdout, @original_stdout = StringIO.new, $stdout
- $stderr, @original_stderr = StringIO.new, $stderr
- end
+ class MysqlDBCreateAsRootTest < ActiveRecord::TestCase
+ def setup
+ @connection = stub("Connection", create_database: true)
+ @error = Mysql2::Error.new("Invalid permissions")
+ @configuration = {
+ "adapter" => "mysql2",
+ "database" => "my-app-db",
+ "username" => "pat",
+ "password" => "wossname"
+ }
+
+ $stdin.stubs(:gets).returns("secret\n")
+ $stdout.stubs(:print).returns(nil)
+ @error.stubs(:errno).returns(1045)
+ ActiveRecord::Base.stubs(:connection).returns(@connection)
+ ActiveRecord::Base.stubs(:establish_connection).
+ raises(@error).
+ then.returns(true)
+
+ $stdout, @original_stdout = StringIO.new, $stdout
+ $stderr, @original_stderr = StringIO.new, $stderr
+ end
- def teardown
- $stdout, $stderr = @original_stdout, @original_stderr
- end
+ def teardown
+ $stdout, $stderr = @original_stdout, @original_stderr
+ end
- def test_root_password_is_requested
- assert_permissions_granted_for("pat")
- $stdin.expects(:gets).returns("secret\n")
+ def test_root_password_is_requested
+ assert_permissions_granted_for("pat")
+ $stdin.expects(:gets).returns("secret\n")
- ActiveRecord::Tasks::DatabaseTasks.create @configuration
- end
+ ActiveRecord::Tasks::DatabaseTasks.create @configuration
+ end
- def test_connection_established_as_root
- assert_permissions_granted_for("pat")
- ActiveRecord::Base.expects(:establish_connection).with(
- 'adapter' => 'mysql2',
- 'database' => nil,
- 'username' => 'root',
- 'password' => 'secret'
- )
+ def test_connection_established_as_root
+ assert_permissions_granted_for("pat")
+ ActiveRecord::Base.expects(:establish_connection).with(
+ "adapter" => "mysql2",
+ "database" => nil,
+ "username" => "root",
+ "password" => "secret"
+ )
- ActiveRecord::Tasks::DatabaseTasks.create @configuration
- end
+ ActiveRecord::Tasks::DatabaseTasks.create @configuration
+ end
- def test_database_created_by_root
- assert_permissions_granted_for("pat")
- @connection.expects(:create_database).
- with('my-app-db', {})
+ def test_database_created_by_root
+ assert_permissions_granted_for("pat")
+ @connection.expects(:create_database).
+ with("my-app-db", {})
- ActiveRecord::Tasks::DatabaseTasks.create @configuration
- end
+ ActiveRecord::Tasks::DatabaseTasks.create @configuration
+ end
- def test_grant_privileges_for_normal_user
- assert_permissions_granted_for("pat")
- ActiveRecord::Tasks::DatabaseTasks.create @configuration
- end
+ def test_grant_privileges_for_normal_user
+ assert_permissions_granted_for("pat")
+ ActiveRecord::Tasks::DatabaseTasks.create @configuration
+ end
- def test_do_not_grant_privileges_for_root_user
- @configuration['username'] = 'root'
- @configuration['password'] = ''
- ActiveRecord::Tasks::DatabaseTasks.create @configuration
- end
+ def test_do_not_grant_privileges_for_root_user
+ @configuration["username"] = "root"
+ @configuration["password"] = ""
+ ActiveRecord::Tasks::DatabaseTasks.create @configuration
+ end
- def test_connection_established_as_normal_user
- assert_permissions_granted_for("pat")
- ActiveRecord::Base.expects(:establish_connection).returns do
- ActiveRecord::Base.expects(:establish_connection).with(
- 'adapter' => 'mysql2',
- 'database' => 'my-app-db',
- 'username' => 'pat',
- 'password' => 'secret'
- )
+ def test_connection_established_as_normal_user
+ assert_permissions_granted_for("pat")
+ ActiveRecord::Base.expects(:establish_connection).returns do
+ ActiveRecord::Base.expects(:establish_connection).with(
+ "adapter" => "mysql2",
+ "database" => "my-app-db",
+ "username" => "pat",
+ "password" => "secret"
+ )
+
+ raise @error
+ end
- raise @error
+ ActiveRecord::Tasks::DatabaseTasks.create @configuration
end
- ActiveRecord::Tasks::DatabaseTasks.create @configuration
- end
+ def test_sends_output_to_stderr_when_other_errors
+ @error.stubs(:errno).returns(42)
- def test_sends_output_to_stderr_when_other_errors
- @error.stubs(:errno).returns(42)
+ $stderr.expects(:puts).at_least_once.returns(nil)
- $stderr.expects(:puts).at_least_once.returns(nil)
+ ActiveRecord::Tasks::DatabaseTasks.create @configuration
+ end
+
+ private
- ActiveRecord::Tasks::DatabaseTasks.create @configuration
+ def assert_permissions_granted_for(db_user)
+ db_name = @configuration["database"]
+ db_password = @configuration["password"]
+ @connection.expects(:execute).with("GRANT ALL PRIVILEGES ON #{db_name}.* TO '#{db_user}'@'localhost' IDENTIFIED BY '#{db_password}' WITH GRANT OPTION;")
+ end
end
- private
+ class MySQLDBDropTest < ActiveRecord::TestCase
+ def setup
+ @connection = stub(drop_database: true)
+ @configuration = {
+ "adapter" => "mysql2",
+ "database" => "my-app-db"
+ }
- def assert_permissions_granted_for(db_user)
- db_name = @configuration['database']
- db_password = @configuration['password']
- @connection.expects(:execute).with("GRANT ALL PRIVILEGES ON #{db_name}.* TO '#{db_user}'@'localhost' IDENTIFIED BY '#{db_password}' WITH GRANT OPTION;")
- end
- end
+ ActiveRecord::Base.stubs(:connection).returns(@connection)
+ ActiveRecord::Base.stubs(:establish_connection).returns(true)
- class MySQLDBDropTest < ActiveRecord::TestCase
- def setup
- @connection = stub(:drop_database => true)
- @configuration = {
- 'adapter' => 'mysql2',
- 'database' => 'my-app-db'
- }
+ $stdout, @original_stdout = StringIO.new, $stdout
+ $stderr, @original_stderr = StringIO.new, $stderr
+ end
- ActiveRecord::Base.stubs(:connection).returns(@connection)
- ActiveRecord::Base.stubs(:establish_connection).returns(true)
+ def teardown
+ $stdout, $stderr = @original_stdout, @original_stderr
+ end
- $stdout, @original_stdout = StringIO.new, $stdout
- $stderr, @original_stderr = StringIO.new, $stderr
- end
+ def test_establishes_connection_to_mysql_database
+ ActiveRecord::Base.expects(:establish_connection).with @configuration
- def teardown
- $stdout, $stderr = @original_stdout, @original_stderr
- end
+ ActiveRecord::Tasks::DatabaseTasks.drop @configuration
+ end
- def test_establishes_connection_to_mysql_database
- ActiveRecord::Base.expects(:establish_connection).with @configuration
+ def test_drops_database
+ @connection.expects(:drop_database).with("my-app-db")
- ActiveRecord::Tasks::DatabaseTasks.drop @configuration
- end
+ ActiveRecord::Tasks::DatabaseTasks.drop @configuration
+ end
- def test_drops_database
- @connection.expects(:drop_database).with('my-app-db')
+ def test_when_database_dropped_successfully_outputs_info_to_stdout
+ ActiveRecord::Tasks::DatabaseTasks.drop @configuration
- ActiveRecord::Tasks::DatabaseTasks.drop @configuration
+ assert_equal "Dropped database 'my-app-db'\n", $stdout.string
+ end
end
- def test_when_database_dropped_successfully_outputs_info_to_stdout
- ActiveRecord::Tasks::DatabaseTasks.drop @configuration
+ class MySQLPurgeTest < ActiveRecord::TestCase
+ def setup
+ @connection = stub(recreate_database: true)
+ @configuration = {
+ "adapter" => "mysql2",
+ "database" => "test-db"
+ }
- assert_equal $stdout.string, "Dropped database 'my-app-db'\n"
- end
- end
+ ActiveRecord::Base.stubs(:connection).returns(@connection)
+ ActiveRecord::Base.stubs(:establish_connection).returns(true)
+ end
- class MySQLPurgeTest < ActiveRecord::TestCase
- def setup
- @connection = stub(:recreate_database => true)
- @configuration = {
- 'adapter' => 'mysql2',
- 'database' => 'test-db'
- }
+ def test_establishes_connection_to_the_appropriate_database
+ ActiveRecord::Base.expects(:establish_connection).with(@configuration)
- ActiveRecord::Base.stubs(:connection).returns(@connection)
- ActiveRecord::Base.stubs(:establish_connection).returns(true)
- end
+ ActiveRecord::Tasks::DatabaseTasks.purge @configuration
+ end
- def test_establishes_connection_to_the_appropriate_database
- ActiveRecord::Base.expects(:establish_connection).with(@configuration)
+ def test_recreates_database_with_no_default_options
+ @connection.expects(:recreate_database).
+ with("test-db", {})
- ActiveRecord::Tasks::DatabaseTasks.purge @configuration
- end
+ ActiveRecord::Tasks::DatabaseTasks.purge @configuration
+ end
- def test_recreates_database_with_no_default_options
- @connection.expects(:recreate_database).
- with('test-db', {})
+ def test_recreates_database_with_the_given_options
+ @connection.expects(:recreate_database).
+ with("test-db", charset: "latin", collation: "latin1_swedish_ci")
- ActiveRecord::Tasks::DatabaseTasks.purge @configuration
+ ActiveRecord::Tasks::DatabaseTasks.purge @configuration.merge(
+ "encoding" => "latin", "collation" => "latin1_swedish_ci")
+ end
end
- def test_recreates_database_with_the_given_options
- @connection.expects(:recreate_database).
- with('test-db', charset: 'latin', collation: 'latin1_swedish_ci')
+ class MysqlDBCharsetTest < ActiveRecord::TestCase
+ def setup
+ @connection = stub(create_database: true)
+ @configuration = {
+ "adapter" => "mysql2",
+ "database" => "my-app-db"
+ }
+
+ ActiveRecord::Base.stubs(:connection).returns(@connection)
+ ActiveRecord::Base.stubs(:establish_connection).returns(true)
+ end
- ActiveRecord::Tasks::DatabaseTasks.purge @configuration.merge(
- 'encoding' => 'latin', 'collation' => 'latin1_swedish_ci')
+ def test_db_retrieves_charset
+ @connection.expects(:charset)
+ ActiveRecord::Tasks::DatabaseTasks.charset @configuration
+ end
end
- end
- class MysqlDBCharsetTest < ActiveRecord::TestCase
- def setup
- @connection = stub(:create_database => true)
- @configuration = {
- 'adapter' => 'mysql2',
- 'database' => 'my-app-db'
- }
+ class MysqlDBCollationTest < ActiveRecord::TestCase
+ def setup
+ @connection = stub(create_database: true)
+ @configuration = {
+ "adapter" => "mysql2",
+ "database" => "my-app-db"
+ }
- ActiveRecord::Base.stubs(:connection).returns(@connection)
- ActiveRecord::Base.stubs(:establish_connection).returns(true)
- end
+ ActiveRecord::Base.stubs(:connection).returns(@connection)
+ ActiveRecord::Base.stubs(:establish_connection).returns(true)
+ end
- def test_db_retrieves_charset
- @connection.expects(:charset)
- ActiveRecord::Tasks::DatabaseTasks.charset @configuration
+ def test_db_retrieves_collation
+ @connection.expects(:collation)
+ ActiveRecord::Tasks::DatabaseTasks.collation @configuration
+ end
end
- end
- class MysqlDBCollationTest < ActiveRecord::TestCase
- def setup
- @connection = stub(:create_database => true)
- @configuration = {
- 'adapter' => 'mysql2',
- 'database' => 'my-app-db'
- }
+ class MySQLStructureDumpTest < ActiveRecord::TestCase
+ def setup
+ @configuration = {
+ "adapter" => "mysql2",
+ "database" => "test-db"
+ }
+ end
- ActiveRecord::Base.stubs(:connection).returns(@connection)
- ActiveRecord::Base.stubs(:establish_connection).returns(true)
- end
+ def test_structure_dump
+ filename = "awesome-file.sql"
+ Kernel.expects(:system).with("mysqldump", "--result-file", filename, "--no-data", "--routines", "--skip-comments", "test-db").returns(true)
- def test_db_retrieves_collation
- @connection.expects(:collation)
- ActiveRecord::Tasks::DatabaseTasks.collation @configuration
- end
- end
+ ActiveRecord::Tasks::DatabaseTasks.structure_dump(@configuration, filename)
+ end
- class MySQLStructureDumpTest < ActiveRecord::TestCase
- def setup
- @configuration = {
- 'adapter' => 'mysql2',
- 'database' => 'test-db'
- }
- end
+ def test_structure_dump_with_extra_flags
+ filename = "awesome-file.sql"
+ expected_command = ["mysqldump", "--result-file", filename, "--no-data", "--routines", "--skip-comments", "--noop", "test-db"]
- def test_structure_dump
- filename = "awesome-file.sql"
- Kernel.expects(:system).with("mysqldump", "--result-file", filename, "--no-data", "--routines", "--skip-comments", "test-db").returns(true)
+ assert_called_with(Kernel, :system, expected_command, returns: true) do
+ with_structure_dump_flags(["--noop"]) do
+ ActiveRecord::Tasks::DatabaseTasks.structure_dump(@configuration, filename)
+ end
+ end
+ end
- ActiveRecord::Tasks::DatabaseTasks.structure_dump(@configuration, filename)
- end
+ def test_warn_when_external_structure_dump_command_execution_fails
+ filename = "awesome-file.sql"
+ Kernel.expects(:system)
+ .with("mysqldump", "--result-file", filename, "--no-data", "--routines", "--skip-comments", "test-db")
+ .returns(false)
- def test_warn_when_external_structure_dump_command_execution_fails
- filename = "awesome-file.sql"
- Kernel.expects(:system)
- .with("mysqldump", "--result-file", filename, "--no-data", "--routines", "--skip-comments", "test-db")
- .returns(false)
+ e = assert_raise(RuntimeError) {
+ ActiveRecord::Tasks::DatabaseTasks.structure_dump(@configuration, filename)
+ }
+ assert_match(/^failed to execute: `mysqldump`$/, e.message)
+ end
- e = assert_raise(RuntimeError) {
- ActiveRecord::Tasks::DatabaseTasks.structure_dump(@configuration, filename)
- }
- assert_match(/^failed to execute: `mysqldump`$/, e.message)
- end
+ def test_structure_dump_with_port_number
+ filename = "awesome-file.sql"
+ Kernel.expects(:system).with("mysqldump", "--port=10000", "--result-file", filename, "--no-data", "--routines", "--skip-comments", "test-db").returns(true)
- def test_structure_dump_with_port_number
- filename = "awesome-file.sql"
- Kernel.expects(:system).with("mysqldump", "--port=10000", "--result-file", filename, "--no-data", "--routines", "--skip-comments", "test-db").returns(true)
+ ActiveRecord::Tasks::DatabaseTasks.structure_dump(
+ @configuration.merge("port" => 10000),
+ filename)
+ end
- ActiveRecord::Tasks::DatabaseTasks.structure_dump(
- @configuration.merge('port' => 10000),
- filename)
- end
+ def test_structure_dump_with_ssl
+ filename = "awesome-file.sql"
+ Kernel.expects(:system).with("mysqldump", "--ssl-ca=ca.crt", "--result-file", filename, "--no-data", "--routines", "--skip-comments", "test-db").returns(true)
- def test_structure_dump_with_ssl
- filename = "awesome-file.sql"
- Kernel.expects(:system).with("mysqldump", "--ssl-ca=ca.crt", "--result-file", filename, "--no-data", "--routines", "--skip-comments", "test-db").returns(true)
+ ActiveRecord::Tasks::DatabaseTasks.structure_dump(
+ @configuration.merge("sslca" => "ca.crt"),
+ filename)
+ end
- ActiveRecord::Tasks::DatabaseTasks.structure_dump(
- @configuration.merge("sslca" => "ca.crt"),
- filename)
- end
- end
+ private
+ def with_structure_dump_flags(flags)
+ old = ActiveRecord::Tasks::DatabaseTasks.structure_dump_flags
+ ActiveRecord::Tasks::DatabaseTasks.structure_dump_flags = flags
+ yield
+ ensure
+ ActiveRecord::Tasks::DatabaseTasks.structure_dump_flags = old
+ end
+ end
+
+ class MySQLStructureLoadTest < ActiveRecord::TestCase
+ def setup
+ @configuration = {
+ "adapter" => "mysql2",
+ "database" => "test-db"
+ }
+ end
- class MySQLStructureLoadTest < ActiveRecord::TestCase
- def setup
- @configuration = {
- 'adapter' => 'mysql2',
- 'database' => 'test-db'
- }
- end
+ def test_structure_load
+ filename = "awesome-file.sql"
+ expected_command = ["mysql", "--execute", %{SET FOREIGN_KEY_CHECKS = 0; SOURCE #{filename}; SET FOREIGN_KEY_CHECKS = 1}, "--database", "test-db", "--noop"]
- def test_structure_load
- filename = "awesome-file.sql"
- Kernel.expects(:system).with('mysql', '--execute', %{SET FOREIGN_KEY_CHECKS = 0; SOURCE #{filename}; SET FOREIGN_KEY_CHECKS = 1}, "--database", "test-db")
- .returns(true)
+ assert_called_with(Kernel, :system, expected_command, returns: true) do
+ with_structure_load_flags(["--noop"]) do
+ ActiveRecord::Tasks::DatabaseTasks.structure_load(@configuration, filename)
+ end
+ end
+ end
- ActiveRecord::Tasks::DatabaseTasks.structure_load(@configuration, filename)
+ private
+ def with_structure_load_flags(flags)
+ old = ActiveRecord::Tasks::DatabaseTasks.structure_load_flags
+ ActiveRecord::Tasks::DatabaseTasks.structure_load_flags = flags
+ yield
+ ensure
+ ActiveRecord::Tasks::DatabaseTasks.structure_load_flags = old
+ end
end
end
end
-end
diff --git a/activerecord/test/cases/tasks/postgresql_rake_test.rb b/activerecord/test/cases/tasks/postgresql_rake_test.rb
index 99d73e91a4..a23100c32a 100644
--- a/activerecord/test/cases/tasks/postgresql_rake_test.rb
+++ b/activerecord/test/cases/tasks/postgresql_rake_test.rb
@@ -1,304 +1,341 @@
-require 'cases/helper'
-require 'active_record/tasks/database_tasks'
+require "cases/helper"
+require "active_record/tasks/database_tasks"
if current_adapter?(:PostgreSQLAdapter)
-module ActiveRecord
- class PostgreSQLDBCreateTest < ActiveRecord::TestCase
- def setup
- @connection = stub(:create_database => true)
- @configuration = {
- 'adapter' => 'postgresql',
- 'database' => 'my-app-db'
- }
-
- ActiveRecord::Base.stubs(:connection).returns(@connection)
- ActiveRecord::Base.stubs(:establish_connection).returns(true)
-
- $stdout, @original_stdout = StringIO.new, $stdout
- $stderr, @original_stderr = StringIO.new, $stderr
- end
+ module ActiveRecord
+ class PostgreSQLDBCreateTest < ActiveRecord::TestCase
+ def setup
+ @connection = stub(create_database: true)
+ @configuration = {
+ "adapter" => "postgresql",
+ "database" => "my-app-db"
+ }
+
+ ActiveRecord::Base.stubs(:connection).returns(@connection)
+ ActiveRecord::Base.stubs(:establish_connection).returns(true)
+
+ $stdout, @original_stdout = StringIO.new, $stdout
+ $stderr, @original_stderr = StringIO.new, $stderr
+ end
- def teardown
- $stdout, $stderr = @original_stdout, @original_stderr
- end
+ def teardown
+ $stdout, $stderr = @original_stdout, @original_stderr
+ end
- def test_establishes_connection_to_postgresql_database
- ActiveRecord::Base.expects(:establish_connection).with(
- 'adapter' => 'postgresql',
- 'database' => 'postgres',
- 'schema_search_path' => 'public'
- )
+ def test_establishes_connection_to_postgresql_database
+ ActiveRecord::Base.expects(:establish_connection).with(
+ "adapter" => "postgresql",
+ "database" => "postgres",
+ "schema_search_path" => "public"
+ )
- ActiveRecord::Tasks::DatabaseTasks.create @configuration
- end
+ ActiveRecord::Tasks::DatabaseTasks.create @configuration
+ end
- def test_creates_database_with_default_encoding
- @connection.expects(:create_database).
- with('my-app-db', @configuration.merge('encoding' => 'utf8'))
+ def test_creates_database_with_default_encoding
+ @connection.expects(:create_database).
+ with("my-app-db", @configuration.merge("encoding" => "utf8"))
- ActiveRecord::Tasks::DatabaseTasks.create @configuration
- end
+ ActiveRecord::Tasks::DatabaseTasks.create @configuration
+ end
- def test_creates_database_with_given_encoding
- @connection.expects(:create_database).
- with('my-app-db', @configuration.merge('encoding' => 'latin'))
+ def test_creates_database_with_given_encoding
+ @connection.expects(:create_database).
+ with("my-app-db", @configuration.merge("encoding" => "latin"))
- ActiveRecord::Tasks::DatabaseTasks.create @configuration.
- merge('encoding' => 'latin')
- end
+ ActiveRecord::Tasks::DatabaseTasks.create @configuration.
+ merge("encoding" => "latin")
+ end
- def test_creates_database_with_given_collation_and_ctype
- @connection.expects(:create_database).
- with('my-app-db', @configuration.merge('encoding' => 'utf8', 'collation' => 'ja_JP.UTF8', 'ctype' => 'ja_JP.UTF8'))
+ def test_creates_database_with_given_collation_and_ctype
+ @connection.expects(:create_database).
+ with("my-app-db", @configuration.merge("encoding" => "utf8", "collation" => "ja_JP.UTF8", "ctype" => "ja_JP.UTF8"))
- ActiveRecord::Tasks::DatabaseTasks.create @configuration.
- merge('collation' => 'ja_JP.UTF8', 'ctype' => 'ja_JP.UTF8')
- end
+ ActiveRecord::Tasks::DatabaseTasks.create @configuration.
+ merge("collation" => "ja_JP.UTF8", "ctype" => "ja_JP.UTF8")
+ end
- def test_establishes_connection_to_new_database
- ActiveRecord::Base.expects(:establish_connection).with(@configuration)
+ def test_establishes_connection_to_new_database
+ ActiveRecord::Base.expects(:establish_connection).with(@configuration)
- ActiveRecord::Tasks::DatabaseTasks.create @configuration
- end
+ ActiveRecord::Tasks::DatabaseTasks.create @configuration
+ end
- def test_db_create_with_error_prints_message
- ActiveRecord::Base.stubs(:establish_connection).raises(Exception)
+ def test_db_create_with_error_prints_message
+ ActiveRecord::Base.stubs(:establish_connection).raises(Exception)
- $stderr.stubs(:puts).returns(true)
- $stderr.expects(:puts).
- with("Couldn't create database for #{@configuration.inspect}")
+ $stderr.stubs(:puts).returns(true)
+ $stderr.expects(:puts).
+ with("Couldn't create database for #{@configuration.inspect}")
- assert_raises(Exception) { ActiveRecord::Tasks::DatabaseTasks.create @configuration }
- end
+ assert_raises(Exception) { ActiveRecord::Tasks::DatabaseTasks.create @configuration }
+ end
- def test_when_database_created_successfully_outputs_info_to_stdout
- ActiveRecord::Tasks::DatabaseTasks.create @configuration
+ def test_when_database_created_successfully_outputs_info_to_stdout
+ ActiveRecord::Tasks::DatabaseTasks.create @configuration
- assert_equal $stdout.string, "Created database 'my-app-db'\n"
- end
+ assert_equal "Created database 'my-app-db'\n", $stdout.string
+ end
- def test_create_when_database_exists_outputs_info_to_stderr
- ActiveRecord::Base.connection.stubs(:create_database).raises(
- ActiveRecord::Tasks::DatabaseAlreadyExists
- )
+ def test_create_when_database_exists_outputs_info_to_stderr
+ ActiveRecord::Base.connection.stubs(:create_database).raises(
+ ActiveRecord::Tasks::DatabaseAlreadyExists
+ )
- ActiveRecord::Tasks::DatabaseTasks.create @configuration
+ ActiveRecord::Tasks::DatabaseTasks.create @configuration
- assert_equal $stderr.string, "Database 'my-app-db' already exists\n"
+ assert_equal "Database 'my-app-db' already exists\n", $stderr.string
+ end
end
- end
- class PostgreSQLDBDropTest < ActiveRecord::TestCase
- def setup
- @connection = stub(:drop_database => true)
- @configuration = {
- 'adapter' => 'postgresql',
- 'database' => 'my-app-db'
- }
+ class PostgreSQLDBDropTest < ActiveRecord::TestCase
+ def setup
+ @connection = stub(drop_database: true)
+ @configuration = {
+ "adapter" => "postgresql",
+ "database" => "my-app-db"
+ }
- ActiveRecord::Base.stubs(:connection).returns(@connection)
- ActiveRecord::Base.stubs(:establish_connection).returns(true)
+ ActiveRecord::Base.stubs(:connection).returns(@connection)
+ ActiveRecord::Base.stubs(:establish_connection).returns(true)
- $stdout, @original_stdout = StringIO.new, $stdout
- $stderr, @original_stderr = StringIO.new, $stderr
- end
+ $stdout, @original_stdout = StringIO.new, $stdout
+ $stderr, @original_stderr = StringIO.new, $stderr
+ end
- def teardown
- $stdout, $stderr = @original_stdout, @original_stderr
- end
+ def teardown
+ $stdout, $stderr = @original_stdout, @original_stderr
+ end
- def test_establishes_connection_to_postgresql_database
- ActiveRecord::Base.expects(:establish_connection).with(
- 'adapter' => 'postgresql',
- 'database' => 'postgres',
- 'schema_search_path' => 'public'
- )
+ def test_establishes_connection_to_postgresql_database
+ ActiveRecord::Base.expects(:establish_connection).with(
+ "adapter" => "postgresql",
+ "database" => "postgres",
+ "schema_search_path" => "public"
+ )
- ActiveRecord::Tasks::DatabaseTasks.drop @configuration
- end
+ ActiveRecord::Tasks::DatabaseTasks.drop @configuration
+ end
- def test_drops_database
- @connection.expects(:drop_database).with('my-app-db')
+ def test_drops_database
+ @connection.expects(:drop_database).with("my-app-db")
- ActiveRecord::Tasks::DatabaseTasks.drop @configuration
- end
+ ActiveRecord::Tasks::DatabaseTasks.drop @configuration
+ end
- def test_when_database_dropped_successfully_outputs_info_to_stdout
- ActiveRecord::Tasks::DatabaseTasks.drop @configuration
+ def test_when_database_dropped_successfully_outputs_info_to_stdout
+ ActiveRecord::Tasks::DatabaseTasks.drop @configuration
- assert_equal $stdout.string, "Dropped database 'my-app-db'\n"
+ assert_equal "Dropped database 'my-app-db'\n", $stdout.string
+ end
end
- end
- class PostgreSQLPurgeTest < ActiveRecord::TestCase
- def setup
- @connection = stub(:create_database => true, :drop_database => true)
- @configuration = {
- 'adapter' => 'postgresql',
- 'database' => 'my-app-db'
- }
-
- ActiveRecord::Base.stubs(:connection).returns(@connection)
- ActiveRecord::Base.stubs(:clear_active_connections!).returns(true)
- ActiveRecord::Base.stubs(:establish_connection).returns(true)
- end
+ class PostgreSQLPurgeTest < ActiveRecord::TestCase
+ def setup
+ @connection = stub(create_database: true, drop_database: true)
+ @configuration = {
+ "adapter" => "postgresql",
+ "database" => "my-app-db"
+ }
- def test_clears_active_connections
- ActiveRecord::Base.expects(:clear_active_connections!)
+ ActiveRecord::Base.stubs(:connection).returns(@connection)
+ ActiveRecord::Base.stubs(:clear_active_connections!).returns(true)
+ ActiveRecord::Base.stubs(:establish_connection).returns(true)
+ end
- ActiveRecord::Tasks::DatabaseTasks.purge @configuration
- end
+ def test_clears_active_connections
+ ActiveRecord::Base.expects(:clear_active_connections!)
- def test_establishes_connection_to_postgresql_database
- ActiveRecord::Base.expects(:establish_connection).with(
- 'adapter' => 'postgresql',
- 'database' => 'postgres',
- 'schema_search_path' => 'public'
- )
+ ActiveRecord::Tasks::DatabaseTasks.purge @configuration
+ end
- ActiveRecord::Tasks::DatabaseTasks.purge @configuration
- end
+ def test_establishes_connection_to_postgresql_database
+ ActiveRecord::Base.expects(:establish_connection).with(
+ "adapter" => "postgresql",
+ "database" => "postgres",
+ "schema_search_path" => "public"
+ )
- def test_drops_database
- @connection.expects(:drop_database).with('my-app-db')
+ ActiveRecord::Tasks::DatabaseTasks.purge @configuration
+ end
- ActiveRecord::Tasks::DatabaseTasks.purge @configuration
- end
+ def test_drops_database
+ @connection.expects(:drop_database).with("my-app-db")
- def test_creates_database
- @connection.expects(:create_database).
- with('my-app-db', @configuration.merge('encoding' => 'utf8'))
+ ActiveRecord::Tasks::DatabaseTasks.purge @configuration
+ end
- ActiveRecord::Tasks::DatabaseTasks.purge @configuration
- end
+ def test_creates_database
+ @connection.expects(:create_database).
+ with("my-app-db", @configuration.merge("encoding" => "utf8"))
+
+ ActiveRecord::Tasks::DatabaseTasks.purge @configuration
+ end
- def test_establishes_connection
- ActiveRecord::Base.expects(:establish_connection).with(@configuration)
+ def test_establishes_connection
+ ActiveRecord::Base.expects(:establish_connection).with(@configuration)
- ActiveRecord::Tasks::DatabaseTasks.purge @configuration
+ ActiveRecord::Tasks::DatabaseTasks.purge @configuration
+ end
end
- end
- class PostgreSQLDBCharsetTest < ActiveRecord::TestCase
- def setup
- @connection = stub(:create_database => true)
- @configuration = {
- 'adapter' => 'postgresql',
- 'database' => 'my-app-db'
- }
+ class PostgreSQLDBCharsetTest < ActiveRecord::TestCase
+ def setup
+ @connection = stub(create_database: true)
+ @configuration = {
+ "adapter" => "postgresql",
+ "database" => "my-app-db"
+ }
- ActiveRecord::Base.stubs(:connection).returns(@connection)
- ActiveRecord::Base.stubs(:establish_connection).returns(true)
- end
+ ActiveRecord::Base.stubs(:connection).returns(@connection)
+ ActiveRecord::Base.stubs(:establish_connection).returns(true)
+ end
- def test_db_retrieves_charset
- @connection.expects(:encoding)
- ActiveRecord::Tasks::DatabaseTasks.charset @configuration
+ def test_db_retrieves_charset
+ @connection.expects(:encoding)
+ ActiveRecord::Tasks::DatabaseTasks.charset @configuration
+ end
end
- end
- class PostgreSQLDBCollationTest < ActiveRecord::TestCase
- def setup
- @connection = stub(:create_database => true)
- @configuration = {
- 'adapter' => 'postgresql',
- 'database' => 'my-app-db'
- }
+ class PostgreSQLDBCollationTest < ActiveRecord::TestCase
+ def setup
+ @connection = stub(create_database: true)
+ @configuration = {
+ "adapter" => "postgresql",
+ "database" => "my-app-db"
+ }
- ActiveRecord::Base.stubs(:connection).returns(@connection)
- ActiveRecord::Base.stubs(:establish_connection).returns(true)
- end
+ ActiveRecord::Base.stubs(:connection).returns(@connection)
+ ActiveRecord::Base.stubs(:establish_connection).returns(true)
+ end
- def test_db_retrieves_collation
- @connection.expects(:collation)
- ActiveRecord::Tasks::DatabaseTasks.collation @configuration
+ def test_db_retrieves_collation
+ @connection.expects(:collation)
+ ActiveRecord::Tasks::DatabaseTasks.collation @configuration
+ end
end
- end
- class PostgreSQLStructureDumpTest < ActiveRecord::TestCase
- def setup
- @connection = stub(:structure_dump => true)
- @configuration = {
- 'adapter' => 'postgresql',
- 'database' => 'my-app-db'
- }
- @filename = "awesome-file.sql"
-
- ActiveRecord::Base.stubs(:connection).returns(@connection)
- ActiveRecord::Base.stubs(:establish_connection).returns(true)
- Kernel.stubs(:system)
- File.stubs(:open)
- end
+ class PostgreSQLStructureDumpTest < ActiveRecord::TestCase
+ def setup
+ @connection = stub(structure_dump: true)
+ @configuration = {
+ "adapter" => "postgresql",
+ "database" => "my-app-db"
+ }
+ @filename = "awesome-file.sql"
- def test_structure_dump
- Kernel.expects(:system).with('pg_dump', '-s', '-x', '-O', '-f', @filename, 'my-app-db').returns(true)
+ ActiveRecord::Base.stubs(:connection).returns(@connection)
+ ActiveRecord::Base.stubs(:establish_connection).returns(true)
+ Kernel.stubs(:system)
+ File.stubs(:open)
+ end
- ActiveRecord::Tasks::DatabaseTasks.structure_dump(@configuration, @filename)
- end
+ def test_structure_dump
+ Kernel.expects(:system).with("pg_dump", "-s", "-x", "-O", "-f", @filename, "my-app-db").returns(true)
- def test_structure_dump_with_schema_search_path
- @configuration['schema_search_path'] = 'foo,bar'
+ ActiveRecord::Tasks::DatabaseTasks.structure_dump(@configuration, @filename)
+ end
- Kernel.expects(:system).with('pg_dump', '-s', '-x', '-O', '-f', @filename, '--schema=foo', '--schema=bar', 'my-app-db').returns(true)
+ def test_structure_dump_with_extra_flags
+ expected_command = ["pg_dump", "-s", "-x", "-O", "-f", @filename, "--noop", "my-app-db"]
- ActiveRecord::Tasks::DatabaseTasks.structure_dump(@configuration, @filename)
- end
+ assert_called_with(Kernel, :system, expected_command, returns: true) do
+ with_structure_dump_flags(["--noop"]) do
+ ActiveRecord::Tasks::DatabaseTasks.structure_dump(@configuration, @filename)
+ end
+ end
+ end
- def test_structure_dump_with_schema_search_path_and_dump_schemas_all
- @configuration['schema_search_path'] = 'foo,bar'
+ def test_structure_dump_with_schema_search_path
+ @configuration["schema_search_path"] = "foo,bar"
- Kernel.expects(:system).with("pg_dump", '-s', '-x', '-O', '-f', @filename, 'my-app-db').returns(true)
+ Kernel.expects(:system).with("pg_dump", "-s", "-x", "-O", "-f", @filename, "--schema=foo", "--schema=bar", "my-app-db").returns(true)
- with_dump_schemas(:all) do
ActiveRecord::Tasks::DatabaseTasks.structure_dump(@configuration, @filename)
end
- end
- def test_structure_dump_with_dump_schemas_string
- Kernel.expects(:system).with("pg_dump", '-s', '-x', '-O', '-f', @filename, '--schema=foo', '--schema=bar', "my-app-db").returns(true)
+ def test_structure_dump_with_schema_search_path_and_dump_schemas_all
+ @configuration["schema_search_path"] = "foo,bar"
- with_dump_schemas('foo,bar') do
- ActiveRecord::Tasks::DatabaseTasks.structure_dump(@configuration, @filename)
+ Kernel.expects(:system).with("pg_dump", "-s", "-x", "-O", "-f", @filename, "my-app-db").returns(true)
+
+ with_dump_schemas(:all) do
+ ActiveRecord::Tasks::DatabaseTasks.structure_dump(@configuration, @filename)
+ end
end
- end
- private
+ def test_structure_dump_with_dump_schemas_string
+ Kernel.expects(:system).with("pg_dump", "-s", "-x", "-O", "-f", @filename, "--schema=foo", "--schema=bar", "my-app-db").returns(true)
- def with_dump_schemas(value, &block)
- old_dump_schemas = ActiveRecord::Base.dump_schemas
- ActiveRecord::Base.dump_schemas = value
- yield
- ensure
- ActiveRecord::Base.dump_schemas = old_dump_schemas
- end
- end
+ with_dump_schemas("foo,bar") do
+ ActiveRecord::Tasks::DatabaseTasks.structure_dump(@configuration, @filename)
+ end
+ end
- class PostgreSQLStructureLoadTest < ActiveRecord::TestCase
- def setup
- @connection = stub
- @configuration = {
- 'adapter' => 'postgresql',
- 'database' => 'my-app-db'
- }
-
- ActiveRecord::Base.stubs(:connection).returns(@connection)
- ActiveRecord::Base.stubs(:establish_connection).returns(true)
- Kernel.stubs(:system)
- end
+ private
+ def with_dump_schemas(value, &block)
+ old_dump_schemas = ActiveRecord::Base.dump_schemas
+ ActiveRecord::Base.dump_schemas = value
+ yield
+ ensure
+ ActiveRecord::Base.dump_schemas = old_dump_schemas
+ end
+
+ def with_structure_dump_flags(flags)
+ old = ActiveRecord::Tasks::DatabaseTasks.structure_dump_flags
+ ActiveRecord::Tasks::DatabaseTasks.structure_dump_flags = flags
+ yield
+ ensure
+ ActiveRecord::Tasks::DatabaseTasks.structure_dump_flags = old
+ end
+ end
+
+ class PostgreSQLStructureLoadTest < ActiveRecord::TestCase
+ def setup
+ @connection = stub
+ @configuration = {
+ "adapter" => "postgresql",
+ "database" => "my-app-db"
+ }
+
+ ActiveRecord::Base.stubs(:connection).returns(@connection)
+ ActiveRecord::Base.stubs(:establish_connection).returns(true)
+ Kernel.stubs(:system)
+ end
- def test_structure_load
- filename = "awesome-file.sql"
- Kernel.expects(:system).with('psql', '-v', 'ON_ERROR_STOP=1', '-q', '-f', filename, @configuration['database']).returns(true)
+ def test_structure_load
+ filename = "awesome-file.sql"
+ Kernel.expects(:system).with("psql", "-v", "ON_ERROR_STOP=1", "-q", "-f", filename, @configuration["database"]).returns(true)
- ActiveRecord::Tasks::DatabaseTasks.structure_load(@configuration, filename)
- end
+ ActiveRecord::Tasks::DatabaseTasks.structure_load(@configuration, filename)
+ end
- def test_structure_load_accepts_path_with_spaces
- filename = "awesome file.sql"
- Kernel.expects(:system).with('psql', '-v', 'ON_ERROR_STOP=1', '-q', '-f', filename, @configuration['database']).returns(true)
+ def test_structure_load_with_extra_flags
+ filename = "awesome-file.sql"
+ expected_command = ["psql", "-v", "ON_ERROR_STOP=1", "-q", "-f", filename, "--noop", @configuration["database"]]
+
+ assert_called_with(Kernel, :system, expected_command, returns: true) do
+ with_structure_load_flags(["--noop"]) do
+ ActiveRecord::Tasks::DatabaseTasks.structure_load(@configuration, filename)
+ end
+ end
+ end
- ActiveRecord::Tasks::DatabaseTasks.structure_load(@configuration, filename)
+ def test_structure_load_accepts_path_with_spaces
+ filename = "awesome file.sql"
+ Kernel.expects(:system).with("psql", "-v", "ON_ERROR_STOP=1", "-q", "-f", filename, @configuration["database"]).returns(true)
+
+ ActiveRecord::Tasks::DatabaseTasks.structure_load(@configuration, filename)
+ end
+
+ private
+ def with_structure_load_flags(flags)
+ old = ActiveRecord::Tasks::DatabaseTasks.structure_load_flags
+ ActiveRecord::Tasks::DatabaseTasks.structure_load_flags = flags
+ yield
+ ensure
+ ActiveRecord::Tasks::DatabaseTasks.structure_load_flags = old
+ end
end
end
end
-end
diff --git a/activerecord/test/cases/tasks/sqlite_rake_test.rb b/activerecord/test/cases/tasks/sqlite_rake_test.rb
index 4be03c7f61..0d917f3f6c 100644
--- a/activerecord/test/cases/tasks/sqlite_rake_test.rb
+++ b/activerecord/test/cases/tasks/sqlite_rake_test.rb
@@ -1,220 +1,220 @@
-require 'cases/helper'
-require 'active_record/tasks/database_tasks'
-require 'pathname'
+require "cases/helper"
+require "active_record/tasks/database_tasks"
+require "pathname"
if current_adapter?(:SQLite3Adapter)
-module ActiveRecord
- class SqliteDBCreateTest < ActiveRecord::TestCase
- def setup
- @database = 'db_create.sqlite3'
- @connection = stub :connection
- @configuration = {
- 'adapter' => 'sqlite3',
- 'database' => @database
- }
-
- File.stubs(:exist?).returns(false)
- ActiveRecord::Base.stubs(:connection).returns(@connection)
- ActiveRecord::Base.stubs(:establish_connection).returns(true)
-
- $stdout, @original_stdout = StringIO.new, $stdout
- $stderr, @original_stderr = StringIO.new, $stderr
- end
+ module ActiveRecord
+ class SqliteDBCreateTest < ActiveRecord::TestCase
+ def setup
+ @database = "db_create.sqlite3"
+ @connection = stub :connection
+ @configuration = {
+ "adapter" => "sqlite3",
+ "database" => @database
+ }
+
+ File.stubs(:exist?).returns(false)
+ ActiveRecord::Base.stubs(:connection).returns(@connection)
+ ActiveRecord::Base.stubs(:establish_connection).returns(true)
+
+ $stdout, @original_stdout = StringIO.new, $stdout
+ $stderr, @original_stderr = StringIO.new, $stderr
+ end
- def teardown
- $stdout, $stderr = @original_stdout, @original_stderr
- end
+ def teardown
+ $stdout, $stderr = @original_stdout, @original_stderr
+ end
- def test_db_checks_database_exists
- File.expects(:exist?).with(@database).returns(false)
+ def test_db_checks_database_exists
+ File.expects(:exist?).with(@database).returns(false)
- ActiveRecord::Tasks::DatabaseTasks.create @configuration, '/rails/root'
- end
+ ActiveRecord::Tasks::DatabaseTasks.create @configuration, "/rails/root"
+ end
- def test_when_db_created_successfully_outputs_info_to_stdout
- ActiveRecord::Tasks::DatabaseTasks.create @configuration, '/rails/root'
+ def test_when_db_created_successfully_outputs_info_to_stdout
+ ActiveRecord::Tasks::DatabaseTasks.create @configuration, "/rails/root"
- assert_equal $stdout.string, "Created database '#{@database}'\n"
- end
+ assert_equal "Created database '#{@database}'\n", $stdout.string
+ end
- def test_db_create_when_file_exists
- File.stubs(:exist?).returns(true)
+ def test_db_create_when_file_exists
+ File.stubs(:exist?).returns(true)
- ActiveRecord::Tasks::DatabaseTasks.create @configuration, '/rails/root'
+ ActiveRecord::Tasks::DatabaseTasks.create @configuration, "/rails/root"
- assert_equal $stderr.string, "Database '#{@database}' already exists\n"
- end
+ assert_equal "Database '#{@database}' already exists\n", $stderr.string
+ end
- def test_db_create_with_file_does_nothing
- File.stubs(:exist?).returns(true)
- $stderr.stubs(:puts).returns(nil)
+ def test_db_create_with_file_does_nothing
+ File.stubs(:exist?).returns(true)
+ $stderr.stubs(:puts).returns(nil)
- ActiveRecord::Base.expects(:establish_connection).never
+ ActiveRecord::Base.expects(:establish_connection).never
- ActiveRecord::Tasks::DatabaseTasks.create @configuration, '/rails/root'
- end
+ ActiveRecord::Tasks::DatabaseTasks.create @configuration, "/rails/root"
+ end
- def test_db_create_establishes_a_connection
- ActiveRecord::Base.expects(:establish_connection).with(@configuration)
+ def test_db_create_establishes_a_connection
+ ActiveRecord::Base.expects(:establish_connection).with(@configuration)
- ActiveRecord::Tasks::DatabaseTasks.create @configuration, '/rails/root'
- end
+ ActiveRecord::Tasks::DatabaseTasks.create @configuration, "/rails/root"
+ end
- def test_db_create_with_error_prints_message
- ActiveRecord::Base.stubs(:establish_connection).raises(Exception)
+ def test_db_create_with_error_prints_message
+ ActiveRecord::Base.stubs(:establish_connection).raises(Exception)
- $stderr.stubs(:puts).returns(true)
- $stderr.expects(:puts).
- with("Couldn't create database for #{@configuration.inspect}")
+ $stderr.stubs(:puts).returns(true)
+ $stderr.expects(:puts).
+ with("Couldn't create database for #{@configuration.inspect}")
- assert_raises(Exception) { ActiveRecord::Tasks::DatabaseTasks.create @configuration, '/rails/root' }
+ assert_raises(Exception) { ActiveRecord::Tasks::DatabaseTasks.create @configuration, "/rails/root" }
+ end
end
- end
- class SqliteDBDropTest < ActiveRecord::TestCase
- def setup
- @database = "db_create.sqlite3"
- @path = stub(:to_s => '/absolute/path', :absolute? => true)
- @configuration = {
- 'adapter' => 'sqlite3',
- 'database' => @database
- }
-
- Pathname.stubs(:new).returns(@path)
- File.stubs(:join).returns('/former/relative/path')
- FileUtils.stubs(:rm).returns(true)
-
- $stdout, @original_stdout = StringIO.new, $stdout
- $stderr, @original_stderr = StringIO.new, $stderr
- end
+ class SqliteDBDropTest < ActiveRecord::TestCase
+ def setup
+ @database = "db_create.sqlite3"
+ @path = stub(to_s: "/absolute/path", absolute?: true)
+ @configuration = {
+ "adapter" => "sqlite3",
+ "database" => @database
+ }
- def teardown
- $stdout, $stderr = @original_stdout, @original_stderr
- end
+ Pathname.stubs(:new).returns(@path)
+ File.stubs(:join).returns("/former/relative/path")
+ FileUtils.stubs(:rm).returns(true)
- def test_creates_path_from_database
- Pathname.expects(:new).with(@database).returns(@path)
+ $stdout, @original_stdout = StringIO.new, $stdout
+ $stderr, @original_stderr = StringIO.new, $stderr
+ end
- ActiveRecord::Tasks::DatabaseTasks.drop @configuration, '/rails/root'
- end
+ def teardown
+ $stdout, $stderr = @original_stdout, @original_stderr
+ end
- def test_removes_file_with_absolute_path
- File.stubs(:exist?).returns(true)
- @path.stubs(:absolute?).returns(true)
+ def test_creates_path_from_database
+ Pathname.expects(:new).with(@database).returns(@path)
- FileUtils.expects(:rm).with('/absolute/path')
+ ActiveRecord::Tasks::DatabaseTasks.drop @configuration, "/rails/root"
+ end
- ActiveRecord::Tasks::DatabaseTasks.drop @configuration, '/rails/root'
- end
+ def test_removes_file_with_absolute_path
+ File.stubs(:exist?).returns(true)
+ @path.stubs(:absolute?).returns(true)
- def test_generates_absolute_path_with_given_root
- @path.stubs(:absolute?).returns(false)
+ FileUtils.expects(:rm).with("/absolute/path")
- File.expects(:join).with('/rails/root', @path).
- returns('/former/relative/path')
+ ActiveRecord::Tasks::DatabaseTasks.drop @configuration, "/rails/root"
+ end
- ActiveRecord::Tasks::DatabaseTasks.drop @configuration, '/rails/root'
- end
+ def test_generates_absolute_path_with_given_root
+ @path.stubs(:absolute?).returns(false)
- def test_removes_file_with_relative_path
- File.stubs(:exist?).returns(true)
- @path.stubs(:absolute?).returns(false)
+ File.expects(:join).with("/rails/root", @path).
+ returns("/former/relative/path")
- FileUtils.expects(:rm).with('/former/relative/path')
+ ActiveRecord::Tasks::DatabaseTasks.drop @configuration, "/rails/root"
+ end
- ActiveRecord::Tasks::DatabaseTasks.drop @configuration, '/rails/root'
- end
+ def test_removes_file_with_relative_path
+ File.stubs(:exist?).returns(true)
+ @path.stubs(:absolute?).returns(false)
- def test_when_db_dropped_successfully_outputs_info_to_stdout
- ActiveRecord::Tasks::DatabaseTasks.drop @configuration, '/rails/root'
+ FileUtils.expects(:rm).with("/former/relative/path")
- assert_equal $stdout.string, "Dropped database '#{@database}'\n"
- end
- end
+ ActiveRecord::Tasks::DatabaseTasks.drop @configuration, "/rails/root"
+ end
- class SqliteDBCharsetTest < ActiveRecord::TestCase
- def setup
- @database = 'db_create.sqlite3'
- @connection = stub :connection
- @configuration = {
- 'adapter' => 'sqlite3',
- 'database' => @database
- }
-
- File.stubs(:exist?).returns(false)
- ActiveRecord::Base.stubs(:connection).returns(@connection)
- ActiveRecord::Base.stubs(:establish_connection).returns(true)
- end
+ def test_when_db_dropped_successfully_outputs_info_to_stdout
+ ActiveRecord::Tasks::DatabaseTasks.drop @configuration, "/rails/root"
- def test_db_retrieves_charset
- @connection.expects(:encoding)
- ActiveRecord::Tasks::DatabaseTasks.charset @configuration, '/rails/root'
+ assert_equal "Dropped database '#{@database}'\n", $stdout.string
+ end
end
- end
- class SqliteDBCollationTest < ActiveRecord::TestCase
- def setup
- @database = 'db_create.sqlite3'
- @connection = stub :connection
- @configuration = {
- 'adapter' => 'sqlite3',
- 'database' => @database
- }
-
- File.stubs(:exist?).returns(false)
- ActiveRecord::Base.stubs(:connection).returns(@connection)
- ActiveRecord::Base.stubs(:establish_connection).returns(true)
- end
+ class SqliteDBCharsetTest < ActiveRecord::TestCase
+ def setup
+ @database = "db_create.sqlite3"
+ @connection = stub :connection
+ @configuration = {
+ "adapter" => "sqlite3",
+ "database" => @database
+ }
- def test_db_retrieves_collation
- assert_raise NoMethodError do
- ActiveRecord::Tasks::DatabaseTasks.collation @configuration, '/rails/root'
+ File.stubs(:exist?).returns(false)
+ ActiveRecord::Base.stubs(:connection).returns(@connection)
+ ActiveRecord::Base.stubs(:establish_connection).returns(true)
end
- end
- end
- class SqliteStructureDumpTest < ActiveRecord::TestCase
- def setup
- @database = "db_create.sqlite3"
- @configuration = {
- 'adapter' => 'sqlite3',
- 'database' => @database
- }
+ def test_db_retrieves_charset
+ @connection.expects(:encoding)
+ ActiveRecord::Tasks::DatabaseTasks.charset @configuration, "/rails/root"
+ end
end
- def test_structure_dump
- dbfile = @database
- filename = "awesome-file.sql"
+ class SqliteDBCollationTest < ActiveRecord::TestCase
+ def setup
+ @database = "db_create.sqlite3"
+ @connection = stub :connection
+ @configuration = {
+ "adapter" => "sqlite3",
+ "database" => @database
+ }
- ActiveRecord::Tasks::DatabaseTasks.structure_dump @configuration, filename, '/rails/root'
- assert File.exist?(dbfile)
- assert File.exist?(filename)
- ensure
- FileUtils.rm_f(filename)
- FileUtils.rm_f(dbfile)
+ File.stubs(:exist?).returns(false)
+ ActiveRecord::Base.stubs(:connection).returns(@connection)
+ ActiveRecord::Base.stubs(:establish_connection).returns(true)
+ end
+
+ def test_db_retrieves_collation
+ assert_raise NoMethodError do
+ ActiveRecord::Tasks::DatabaseTasks.collation @configuration, "/rails/root"
+ end
+ end
end
- end
- class SqliteStructureLoadTest < ActiveRecord::TestCase
- def setup
- @database = "db_create.sqlite3"
- @configuration = {
- 'adapter' => 'sqlite3',
- 'database' => @database
- }
+ class SqliteStructureDumpTest < ActiveRecord::TestCase
+ def setup
+ @database = "db_create.sqlite3"
+ @configuration = {
+ "adapter" => "sqlite3",
+ "database" => @database
+ }
+ end
+
+ def test_structure_dump
+ dbfile = @database
+ filename = "awesome-file.sql"
+
+ ActiveRecord::Tasks::DatabaseTasks.structure_dump @configuration, filename, "/rails/root"
+ assert File.exist?(dbfile)
+ assert File.exist?(filename)
+ ensure
+ FileUtils.rm_f(filename)
+ FileUtils.rm_f(dbfile)
+ end
end
- def test_structure_load
- dbfile = @database
- filename = "awesome-file.sql"
+ class SqliteStructureLoadTest < ActiveRecord::TestCase
+ def setup
+ @database = "db_create.sqlite3"
+ @configuration = {
+ "adapter" => "sqlite3",
+ "database" => @database
+ }
+ end
+
+ def test_structure_load
+ dbfile = @database
+ filename = "awesome-file.sql"
- open(filename, 'w') { |f| f.puts("select datetime('now', 'localtime');") }
- ActiveRecord::Tasks::DatabaseTasks.structure_load @configuration, filename, '/rails/root'
- assert File.exist?(dbfile)
- ensure
- FileUtils.rm_f(filename)
- FileUtils.rm_f(dbfile)
+ open(filename, "w") { |f| f.puts("select datetime('now', 'localtime');") }
+ ActiveRecord::Tasks::DatabaseTasks.structure_load @configuration, filename, "/rails/root"
+ assert File.exist?(dbfile)
+ ensure
+ FileUtils.rm_f(filename)
+ FileUtils.rm_f(dbfile)
+ end
end
end
end
-end
diff --git a/activerecord/test/cases/test_case.rb b/activerecord/test/cases/test_case.rb
index c8adc21bbc..31b11c19f7 100644
--- a/activerecord/test/cases/test_case.rb
+++ b/activerecord/test/cases/test_case.rb
@@ -1,12 +1,28 @@
-require 'active_support/test_case'
-require 'active_support/testing/stream'
+require "active_support/test_case"
+require "active_support/testing/autorun"
+require "active_support/testing/method_call_assertions"
+require "active_support/testing/stream"
+require "active_record/fixtures"
+
+require "cases/validations_repair_helper"
module ActiveRecord
# = Active Record Test Case
#
# Defines some test assertions to test against SQL queries.
class TestCase < ActiveSupport::TestCase #:nodoc:
+ include ActiveSupport::Testing::MethodCallAssertions
include ActiveSupport::Testing::Stream
+ include ActiveRecord::TestFixtures
+ include ActiveRecord::ValidationsRepairHelper
+
+ self.fixture_path = FIXTURES_ROOT
+ self.use_instantiated_fixtures = false
+ self.use_transactional_tests = true
+
+ def create_fixtures(*fixture_set_names, &block)
+ ActiveRecord::FixtureSet.create_fixtures(ActiveRecord::TestCase.fixture_path, fixture_set_names, fixture_class_names, &block)
+ end
def teardown
SQLCounter.clear_log
@@ -23,7 +39,7 @@ module ActiveRecord
ensure
failed_patterns = []
patterns_to_match.each do |pattern|
- failed_patterns << pattern unless SQLCounter.log_all.any?{ |sql| pattern === sql }
+ failed_patterns << pattern unless SQLCounter.log_all.any? { |sql| pattern === sql }
end
assert failed_patterns.empty?, "Query pattern(s) #{failed_patterns.map(&:inspect).join(', ')} not found.#{SQLCounter.log.size == 0 ? '' : "\nQueries:\n#{SQLCounter.log.join("\n")}"}"
end
@@ -47,11 +63,11 @@ module ActiveRecord
assert_queries(0, options, &block)
end
- def assert_column(model, column_name, msg=nil)
+ def assert_column(model, column_name, msg = nil)
assert has_column?(model, column_name), msg
end
- def assert_no_column(model, column_name, msg=nil)
+ def assert_no_column(model, column_name, msg = nil)
assert_not has_column?(model, column_name), msg
end
@@ -85,7 +101,7 @@ module ActiveRecord
def clear_log; self.log = []; self.log_all = []; end
end
- self.clear_log
+ clear_log
self.ignored_sql = [/^PRAGMA/, /^SELECT currval/, /^SELECT CAST/, /^SELECT @@IDENTITY/, /^SELECT @@ROWCOUNT/, /^SAVEPOINT/, /^ROLLBACK TO SAVEPOINT/, /^RELEASE SAVEPOINT/, /^SHOW max_identifier_length/, /^BEGIN/, /^COMMIT/]
@@ -108,16 +124,13 @@ module ActiveRecord
end
def call(name, start, finish, message_id, values)
- sql = values[:sql]
-
- # FIXME: this seems bad. we should probably have a better way to indicate
- # the query was cached
- return if 'CACHE' == values[:name]
+ return if values[:cached]
+ sql = values[:sql]
self.class.log_all << sql
- self.class.log << sql unless ignore =~ sql
+ self.class.log << sql unless ignore.match?(sql)
end
end
- ActiveSupport::Notifications.subscribe('sql.active_record', SQLCounter.new)
+ ActiveSupport::Notifications.subscribe("sql.active_record", SQLCounter.new)
end
diff --git a/activerecord/test/cases/test_fixtures_test.rb b/activerecord/test/cases/test_fixtures_test.rb
index 1970fe82d0..58d3bea3a2 100644
--- a/activerecord/test/cases/test_fixtures_test.rb
+++ b/activerecord/test/cases/test_fixtures_test.rb
@@ -1,30 +1,12 @@
-require 'cases/helper'
+require "cases/helper"
class TestFixturesTest < ActiveRecord::TestCase
setup do
@klass = Class.new
- @klass.send(:include, ActiveRecord::TestFixtures)
- end
-
- def test_deprecated_use_transactional_fixtures=
- assert_deprecated 'use use_transactional_tests= instead' do
- @klass.use_transactional_fixtures = true
- end
- end
-
- def test_use_transactional_tests_prefers_use_transactional_fixtures
- ActiveSupport::Deprecation.silence do
- @klass.use_transactional_fixtures = false
- end
-
- assert_equal false, @klass.use_transactional_tests
+ @klass.include(ActiveRecord::TestFixtures)
end
def test_use_transactional_tests_defaults_to_true
- ActiveSupport::Deprecation.silence do
- @klass.use_transactional_fixtures = nil
- end
-
assert_equal true, @klass.use_transactional_tests
end
diff --git a/activerecord/test/cases/time_precision_test.rb b/activerecord/test/cases/time_precision_test.rb
index 628a8eb771..03f6c234e8 100644
--- a/activerecord/test/cases/time_precision_test.rb
+++ b/activerecord/test/cases/time_precision_test.rb
@@ -1,84 +1,83 @@
-require 'cases/helper'
-require 'support/schema_dumping_helper'
+require "cases/helper"
+require "support/schema_dumping_helper"
if subsecond_precision_supported?
-class TimePrecisionTest < ActiveRecord::TestCase
- include SchemaDumpingHelper
- self.use_transactional_tests = false
+ class TimePrecisionTest < ActiveRecord::TestCase
+ include SchemaDumpingHelper
+ self.use_transactional_tests = false
- class Foo < ActiveRecord::Base; end
+ class Foo < ActiveRecord::Base; end
- setup do
- @connection = ActiveRecord::Base.connection
- Foo.reset_column_information
- end
-
- teardown do
- @connection.drop_table :foos, if_exists: true
- end
+ setup do
+ @connection = ActiveRecord::Base.connection
+ Foo.reset_column_information
+ end
- def test_time_data_type_with_precision
- @connection.create_table(:foos, force: true)
- @connection.add_column :foos, :start, :time, precision: 3
- @connection.add_column :foos, :finish, :time, precision: 6
- assert_equal 3, Foo.columns_hash['start'].precision
- assert_equal 6, Foo.columns_hash['finish'].precision
- end
+ teardown do
+ @connection.drop_table :foos, if_exists: true
+ end
- def test_passing_precision_to_time_does_not_set_limit
- @connection.create_table(:foos, force: true) do |t|
- t.time :start, precision: 3
- t.time :finish, precision: 6
+ def test_time_data_type_with_precision
+ @connection.create_table(:foos, force: true)
+ @connection.add_column :foos, :start, :time, precision: 3
+ @connection.add_column :foos, :finish, :time, precision: 6
+ assert_equal 3, Foo.columns_hash["start"].precision
+ assert_equal 6, Foo.columns_hash["finish"].precision
end
- assert_nil Foo.columns_hash['start'].limit
- assert_nil Foo.columns_hash['finish'].limit
- end
- def test_invalid_time_precision_raises_error
- assert_raises ActiveRecord::ActiveRecordError do
+ def test_passing_precision_to_time_does_not_set_limit
@connection.create_table(:foos, force: true) do |t|
- t.time :start, precision: 7
- t.time :finish, precision: 7
+ t.time :start, precision: 3
+ t.time :finish, precision: 6
end
+ assert_nil Foo.columns_hash["start"].limit
+ assert_nil Foo.columns_hash["finish"].limit
end
- end
- def test_formatting_time_according_to_precision
- @connection.create_table(:foos, force: true) do |t|
- t.time :start, precision: 0
- t.time :finish, precision: 4
+ def test_invalid_time_precision_raises_error
+ assert_raises ActiveRecord::ActiveRecordError do
+ @connection.create_table(:foos, force: true) do |t|
+ t.time :start, precision: 7
+ t.time :finish, precision: 7
+ end
+ end
end
- time = ::Time.utc(2000, 1, 1, 12, 30, 0, 999999)
- Foo.create!(start: time, finish: time)
- assert foo = Foo.find_by(start: time)
- assert_equal 1, Foo.where(finish: time).count
- assert_equal time.to_s, foo.start.to_s
- assert_equal time.to_s, foo.finish.to_s
- assert_equal 000000, foo.start.usec
- assert_equal 999900, foo.finish.usec
- end
- def test_schema_dump_includes_time_precision
- @connection.create_table(:foos, force: true) do |t|
- t.time :start, precision: 4
- t.time :finish, precision: 6
+ def test_formatting_time_according_to_precision
+ @connection.create_table(:foos, force: true) do |t|
+ t.time :start, precision: 0
+ t.time :finish, precision: 4
+ end
+ time = ::Time.utc(2000, 1, 1, 12, 30, 0, 999999)
+ Foo.create!(start: time, finish: time)
+ assert foo = Foo.find_by(start: time)
+ assert_equal 1, Foo.where(finish: time).count
+ assert_equal time.to_s, foo.start.to_s
+ assert_equal time.to_s, foo.finish.to_s
+ assert_equal 000000, foo.start.usec
+ assert_equal 999900, foo.finish.usec
end
- output = dump_table_schema("foos")
- assert_match %r{t\.time\s+"start",\s+precision: 4$}, output
- assert_match %r{t\.time\s+"finish",\s+precision: 6$}, output
- end
- if current_adapter?(:PostgreSQLAdapter)
- def test_time_precision_with_zero_should_be_dumped
+ def test_schema_dump_includes_time_precision
@connection.create_table(:foos, force: true) do |t|
- t.time :start, precision: 0
- t.time :finish, precision: 0
+ t.time :start, precision: 4
+ t.time :finish, precision: 6
end
output = dump_table_schema("foos")
- assert_match %r{t\.time\s+"start",\s+precision: 0$}, output
- assert_match %r{t\.time\s+"finish",\s+precision: 0$}, output
+ assert_match %r{t\.time\s+"start",\s+precision: 4$}, output
+ assert_match %r{t\.time\s+"finish",\s+precision: 6$}, output
end
- end
-end
+ if current_adapter?(:PostgreSQLAdapter)
+ def test_time_precision_with_zero_should_be_dumped
+ @connection.create_table(:foos, force: true) do |t|
+ t.time :start, precision: 0
+ t.time :finish, precision: 0
+ end
+ output = dump_table_schema("foos")
+ assert_match %r{t\.time\s+"start",\s+precision: 0$}, output
+ assert_match %r{t\.time\s+"finish",\s+precision: 0$}, output
+ end
+ end
+ end
end
diff --git a/activerecord/test/cases/timestamp_test.rb b/activerecord/test/cases/timestamp_test.rb
index 937b84bccc..7766a74612 100644
--- a/activerecord/test/cases/timestamp_test.rb
+++ b/activerecord/test/cases/timestamp_test.rb
@@ -1,12 +1,12 @@
-require 'cases/helper'
-require 'support/ddl_helper'
-require 'models/developer'
-require 'models/computer'
-require 'models/owner'
-require 'models/pet'
-require 'models/toy'
-require 'models/car'
-require 'models/task'
+require "cases/helper"
+require "support/ddl_helper"
+require "models/developer"
+require "models/computer"
+require "models/owner"
+require "models/pet"
+require "models/toy"
+require "models/car"
+require "models/task"
class TimestampTest < ActiveRecord::TestCase
fixtures :developers, :owners, :pets, :toys, :cars, :tasks
@@ -38,8 +38,8 @@ class TimestampTest < ActiveRecord::TestCase
assert_not_equal @previously_updated_at, @developer.updated_at
assert_equal previous_salary + 10000, @developer.salary
- assert @developer.salary_changed?, 'developer salary should have changed'
- assert @developer.changed?, 'developer should be marked as changed'
+ assert @developer.salary_changed?, "developer salary should have changed"
+ assert @developer.changed?, "developer should be marked as changed"
@developer.reload
assert_equal previous_salary, @developer.salary
end
@@ -74,12 +74,12 @@ class TimestampTest < ActiveRecord::TestCase
end
def test_touching_updates_timestamp_with_given_time
- previously_updated_at = @developer.updated_at
- new_time = Time.utc(2015, 2, 16, 0, 0, 0)
- @developer.touch(time: new_time)
+ previously_updated_at = @developer.updated_at
+ new_time = Time.utc(2015, 2, 16, 0, 0, 0)
+ @developer.touch(time: new_time)
- assert_not_equal previously_updated_at, @developer.updated_at
- assert_equal new_time, @developer.updated_at
+ assert_not_equal previously_updated_at, @developer.updated_at
+ assert_equal new_time, @developer.updated_at
end
def test_touching_an_attribute_updates_timestamp
@@ -88,8 +88,8 @@ class TimestampTest < ActiveRecord::TestCase
@developer.touch(:created_at)
end
- assert !@developer.created_at_changed? , 'created_at should not be changed'
- assert !@developer.changed?, 'record should not be changed'
+ assert !@developer.created_at_changed? , "created_at should not be changed"
+ assert !@developer.changed?, "record should not be changed"
assert_not_equal previously_created_at, @developer.created_at
assert_not_equal @previously_updated_at, @developer.updated_at
end
@@ -106,9 +106,9 @@ class TimestampTest < ActiveRecord::TestCase
end
def test_touching_an_attribute_updates_timestamp_with_given_time
- previously_updated_at = @developer.updated_at
+ previously_updated_at = @developer.updated_at
previously_created_at = @developer.created_at
- new_time = Time.utc(2015, 2, 16, 4, 54, 0)
+ new_time = Time.utc(2015, 2, 16, 4, 54, 0)
@developer.touch(:created_at, time: new_time)
assert_not_equal previously_created_at, @developer.created_at
@@ -228,7 +228,7 @@ class TimestampTest < ActiveRecord::TestCase
def test_saving_a_new_record_belonging_to_invalid_parent_with_touch_should_not_raise_exception
klass = Class.new(Owner) do
- def self.name; 'Owner'; end
+ def self.name; "Owner"; end
validate { errors.add(:base, :invalid) }
end
@@ -240,8 +240,8 @@ class TimestampTest < ActiveRecord::TestCase
def test_saving_a_record_with_a_belongs_to_that_specifies_touching_a_specific_attribute_the_parent_should_update_that_attribute
klass = Class.new(ActiveRecord::Base) do
- def self.name; 'Pet'; end
- belongs_to :owner, :touch => :happy_at
+ def self.name; "Pet"; end
+ belongs_to :owner, touch: :happy_at
end
pet = klass.first
@@ -256,8 +256,8 @@ class TimestampTest < ActiveRecord::TestCase
def test_touching_a_record_with_a_belongs_to_that_uses_a_counter_cache_should_update_the_parent
klass = Class.new(ActiveRecord::Base) do
- def self.name; 'Pet'; end
- belongs_to :owner, :counter_cache => :use_count, :touch => true
+ def self.name; "Pet"; end
+ belongs_to :owner, counter_cache: :use_count, touch: true
end
pet = klass.first
@@ -275,8 +275,8 @@ class TimestampTest < ActiveRecord::TestCase
def test_touching_a_record_touches_parent_record_and_grandparent_record
klass = Class.new(ActiveRecord::Base) do
- def self.name; 'Toy'; end
- belongs_to :pet, :touch => true
+ def self.name; "Toy"; end
+ belongs_to :pet, touch: true
end
toy = klass.first
@@ -293,12 +293,12 @@ class TimestampTest < ActiveRecord::TestCase
def test_touching_a_record_touches_polymorphic_record
klass = Class.new(ActiveRecord::Base) do
- def self.name; 'Toy'; end
+ def self.name; "Toy"; end
end
wheel_klass = Class.new(ActiveRecord::Base) do
- def self.name; 'Wheel'; end
- belongs_to :wheelable, :polymorphic => true, :touch => true
+ def self.name; "Wheel"; end
+ belongs_to :wheelable, polymorphic: true, touch: true
end
toy = klass.first
@@ -315,7 +315,7 @@ class TimestampTest < ActiveRecord::TestCase
def test_changing_parent_of_a_record_touches_both_new_and_old_parent_record
klass = Class.new(ActiveRecord::Base) do
- def self.name; 'Toy'; end
+ def self.name; "Toy"; end
belongs_to :pet, touch: true
end
@@ -341,12 +341,12 @@ class TimestampTest < ActiveRecord::TestCase
def test_changing_parent_of_a_record_touches_both_new_and_old_polymorphic_parent_record_changes_within_same_class
car_class = Class.new(ActiveRecord::Base) do
- def self.name; 'Car'; end
+ def self.name; "Car"; end
end
wheel_class = Class.new(ActiveRecord::Base) do
- def self.name; 'Wheel'; end
- belongs_to :wheelable, :polymorphic => true, :touch => true
+ def self.name; "Wheel"; end
+ belongs_to :wheelable, polymorphic: true, touch: true
end
car1 = car_class.find(1)
@@ -368,16 +368,16 @@ class TimestampTest < ActiveRecord::TestCase
def test_changing_parent_of_a_record_touches_both_new_and_old_polymorphic_parent_record_changes_with_other_class
car_class = Class.new(ActiveRecord::Base) do
- def self.name; 'Car'; end
+ def self.name; "Car"; end
end
toy_class = Class.new(ActiveRecord::Base) do
- def self.name; 'Toy'; end
+ def self.name; "Toy"; end
end
wheel_class = Class.new(ActiveRecord::Base) do
- def self.name; 'Wheel'; end
- belongs_to :wheelable, :polymorphic => true, :touch => true
+ def self.name; "Wheel"; end
+ belongs_to :wheelable, polymorphic: true, touch: true
end
car = car_class.find(1)
@@ -399,7 +399,7 @@ class TimestampTest < ActiveRecord::TestCase
def test_clearing_association_touches_the_old_record
klass = Class.new(ActiveRecord::Base) do
- def self.name; 'Toy'; end
+ def self.name; "Toy"; end
belongs_to :pet, touch: true
end
@@ -419,54 +419,39 @@ class TimestampTest < ActiveRecord::TestCase
def test_timestamp_column_values_are_present_in_the_callbacks
klass = Class.new(ActiveRecord::Base) do
- self.table_name = 'people'
+ self.table_name = "people"
before_create do
self.born_at = self.created_at
end
end
- person = klass.create first_name: 'David'
+ person = klass.create first_name: "David"
assert_not_equal person.born_at, nil
end
- def test_timestamp_attributes_for_create
- toy = Toy.first
- assert_equal [:created_at, :created_on], toy.send(:timestamp_attributes_for_create)
- end
-
- def test_timestamp_attributes_for_update
- toy = Toy.first
- assert_equal [:updated_at, :updated_on], toy.send(:timestamp_attributes_for_update)
- end
-
- def test_all_timestamp_attributes
- toy = Toy.first
- assert_equal [:created_at, :created_on, :updated_at, :updated_on], toy.send(:all_timestamp_attributes)
- end
-
def test_timestamp_attributes_for_create_in_model
toy = Toy.first
- assert_equal [:created_at], toy.send(:timestamp_attributes_for_create_in_model)
+ assert_equal ["created_at"], toy.send(:timestamp_attributes_for_create_in_model)
end
def test_timestamp_attributes_for_update_in_model
toy = Toy.first
- assert_equal [:updated_at], toy.send(:timestamp_attributes_for_update_in_model)
+ assert_equal ["updated_at"], toy.send(:timestamp_attributes_for_update_in_model)
end
def test_all_timestamp_attributes_in_model
toy = Toy.first
- assert_equal [:created_at, :updated_at], toy.send(:all_timestamp_attributes_in_model)
+ assert_equal ["created_at", "updated_at"], toy.send(:all_timestamp_attributes_in_model)
end
def test_index_is_created_for_both_timestamps
ActiveRecord::Base.connection.create_table(:foos, force: true) do |t|
- t.timestamps(:foos, null: true, index: true)
+ t.timestamps null: true, index: true
end
- indexes = ActiveRecord::Base.connection.indexes('foos')
- assert_equal ['created_at', 'updated_at'], indexes.flat_map(&:columns).sort
+ indexes = ActiveRecord::Base.connection.indexes("foos")
+ assert_equal ["created_at", "updated_at"], indexes.flat_map(&:columns).sort
ensure
ActiveRecord::Base.connection.drop_table(:foos)
end
diff --git a/activerecord/test/cases/touch_later_test.rb b/activerecord/test/cases/touch_later_test.rb
index b47769eed7..d1e8c649d9 100644
--- a/activerecord/test/cases/touch_later_test.rb
+++ b/activerecord/test/cases/touch_later_test.rb
@@ -1,9 +1,9 @@
-require 'cases/helper'
-require 'models/invoice'
-require 'models/line_item'
-require 'models/topic'
-require 'models/node'
-require 'models/tree'
+require "cases/helper"
+require "models/invoice"
+require "models/line_item"
+require "models/topic"
+require "models/node"
+require "models/tree"
class TouchLaterTest < ActiveRecord::TestCase
fixtures :nodes, :trees
@@ -24,6 +24,15 @@ class TouchLaterTest < ActiveRecord::TestCase
assert_not invoice.changed?
end
+ def test_touch_later_respects_no_touching_policy
+ time = Time.now.utc - 25.days
+ topic = Topic.create!(updated_at: time, created_at: time)
+ Topic.no_touching do
+ topic.touch_later
+ end
+ assert_equal time.to_i, topic.updated_at.to_i
+ end
+
def test_touch_later_update_the_attributes
time = Time.now.utc - 25.days
topic = Topic.create!(updated_at: time, created_at: time)
diff --git a/activerecord/test/cases/transaction_callbacks_test.rb b/activerecord/test/cases/transaction_callbacks_test.rb
index 8a7f19293d..391bbe8877 100644
--- a/activerecord/test/cases/transaction_callbacks_test.rb
+++ b/activerecord/test/cases/transaction_callbacks_test.rb
@@ -1,7 +1,7 @@
require "cases/helper"
-require 'models/owner'
-require 'models/pet'
-require 'models/topic'
+require "models/owner"
+require "models/pet"
+require "models/topic"
class TransactionCallbacksTest < ActiveRecord::TestCase
fixtures :topics, :owners, :pets
@@ -17,7 +17,7 @@ class TransactionCallbacksTest < ActiveRecord::TestCase
attr_accessor :save_on_after_create
after_create do
- self.save! if save_on_after_create
+ save! if save_on_after_create
end
def history
@@ -68,17 +68,17 @@ class TransactionCallbacksTest < ActiveRecord::TestCase
def do_before_commit(on)
blocks = @before_commit[on] if defined?(@before_commit)
- blocks.each{|b| b.call(self)} if blocks
+ blocks.each { |b| b.call(self) } if blocks
end
def do_after_commit(on)
blocks = @after_commit[on] if defined?(@after_commit)
- blocks.each{|b| b.call(self)} if blocks
+ blocks.each { |b| b.call(self) } if blocks
end
def do_after_rollback(on)
blocks = @after_rollback[on] if defined?(@after_rollback)
- blocks.each{|b| b.call(self)} if blocks
+ blocks.each { |b| b.call(self) } if blocks
end
end
@@ -88,7 +88,7 @@ class TransactionCallbacksTest < ActiveRecord::TestCase
# FIXME: Test behavior, not implementation.
def test_before_commit_exception_should_pop_transaction_stack
- @first.before_commit_block { raise 'better pop this txn from the stack!' }
+ @first.before_commit_block { raise "better pop this txn from the stack!" }
original_txn = @first.class.connection.current_transaction
@@ -101,8 +101,8 @@ class TransactionCallbacksTest < ActiveRecord::TestCase
end
def test_call_after_commit_after_transaction_commits
- @first.after_commit_block{|r| r.history << :after_commit}
- @first.after_rollback_block{|r| r.history << :after_rollback}
+ @first.after_commit_block { |r| r.history << :after_commit }
+ @first.after_rollback_block { |r| r.history << :after_rollback }
@first.save!
assert_equal [:after_commit], @first.history
@@ -123,7 +123,7 @@ class TransactionCallbacksTest < ActiveRecord::TestCase
end
def test_only_call_after_commit_on_create_after_transaction_commits_for_new_record
- new_record = TopicWithCallbacks.new(:title => "New topic", :written_on => Date.today)
+ new_record = TopicWithCallbacks.new(title: "New topic", written_on: Date.today)
add_transaction_execution_blocks new_record
new_record.save!
@@ -131,17 +131,17 @@ class TransactionCallbacksTest < ActiveRecord::TestCase
end
def test_only_call_after_commit_on_create_after_transaction_commits_for_new_record_if_create_succeeds_creating_through_association
- topic = TopicWithCallbacks.create!(:title => "New topic", :written_on => Date.today)
+ topic = TopicWithCallbacks.create!(title: "New topic", written_on: Date.today)
reply = topic.replies.create
assert_equal [], reply.history
end
def test_only_call_after_commit_on_create_and_doesnt_leaky
- r = ReplyWithCallbacks.new(content: 'foo')
+ r = ReplyWithCallbacks.new(content: "foo")
r.save_on_after_create = true
r.save!
- r.content = 'bar'
+ r.content = "bar"
r.save!
r.save!
assert_equal [:commit_on_create], r.history
@@ -155,7 +155,7 @@ class TransactionCallbacksTest < ActiveRecord::TestCase
end
def test_only_call_after_commit_on_top_level_transactions
- @first.after_commit_block{|r| r.history << :after_commit}
+ @first.after_commit_block { |r| r.history << :after_commit }
assert @first.history.empty?
@first.transaction do
@@ -168,8 +168,8 @@ class TransactionCallbacksTest < ActiveRecord::TestCase
end
def test_call_after_rollback_after_transaction_rollsback
- @first.after_commit_block{|r| r.history << :after_commit}
- @first.after_rollback_block{|r| r.history << :after_rollback}
+ @first.after_commit_block { |r| r.history << :after_commit }
+ @first.after_rollback_block { |r| r.history << :after_rollback }
Topic.transaction do
@first.save!
@@ -213,7 +213,7 @@ class TransactionCallbacksTest < ActiveRecord::TestCase
end
def test_only_call_after_rollback_on_create_after_transaction_rollsback_for_new_record
- new_record = TopicWithCallbacks.new(:title => "New topic", :written_on => Date.today)
+ new_record = TopicWithCallbacks.new(title: "New topic", written_on: Date.today)
add_transaction_execution_blocks new_record
Topic.transaction do
@@ -243,20 +243,20 @@ class TransactionCallbacksTest < ActiveRecord::TestCase
end
def test_only_call_after_rollback_on_records_rolled_back_to_a_savepoint
- def @first.rollbacks(i=0); @rollbacks ||= 0; @rollbacks += i if i; end
- def @first.commits(i=0); @commits ||= 0; @commits += i if i; end
- @first.after_rollback_block{|r| r.rollbacks(1)}
- @first.after_commit_block{|r| r.commits(1)}
+ def @first.rollbacks(i = 0); @rollbacks ||= 0; @rollbacks += i if i; end
+ def @first.commits(i = 0); @commits ||= 0; @commits += i if i; end
+ @first.after_rollback_block { |r| r.rollbacks(1) }
+ @first.after_commit_block { |r| r.commits(1) }
second = TopicWithCallbacks.find(3)
- def second.rollbacks(i=0); @rollbacks ||= 0; @rollbacks += i if i; end
- def second.commits(i=0); @commits ||= 0; @commits += i if i; end
- second.after_rollback_block{|r| r.rollbacks(1)}
- second.after_commit_block{|r| r.commits(1)}
+ def second.rollbacks(i = 0); @rollbacks ||= 0; @rollbacks += i if i; end
+ def second.commits(i = 0); @commits ||= 0; @commits += i if i; end
+ second.after_rollback_block { |r| r.rollbacks(1) }
+ second.after_commit_block { |r| r.commits(1) }
Topic.transaction do
@first.save!
- Topic.transaction(:requires_new => true) do
+ Topic.transaction(requires_new: true) do
second.save!
raise ActiveRecord::Rollback
end
@@ -269,19 +269,19 @@ class TransactionCallbacksTest < ActiveRecord::TestCase
end
def test_only_call_after_rollback_on_records_rolled_back_to_a_savepoint_when_release_savepoint_fails
- def @first.rollbacks(i=0); @rollbacks ||= 0; @rollbacks += i if i; end
- def @first.commits(i=0); @commits ||= 0; @commits += i if i; end
+ def @first.rollbacks(i = 0); @rollbacks ||= 0; @rollbacks += i if i; end
+ def @first.commits(i = 0); @commits ||= 0; @commits += i if i; end
- @first.after_rollback_block{|r| r.rollbacks(1)}
- @first.after_commit_block{|r| r.commits(1)}
+ @first.after_rollback_block { |r| r.rollbacks(1) }
+ @first.after_commit_block { |r| r.commits(1) }
Topic.transaction do
@first.save
- Topic.transaction(:requires_new => true) do
+ Topic.transaction(requires_new: true) do
@first.save!
raise ActiveRecord::Rollback
end
- Topic.transaction(:requires_new => true) do
+ Topic.transaction(requires_new: true) do
@first.save!
raise ActiveRecord::Rollback
end
@@ -292,7 +292,7 @@ class TransactionCallbacksTest < ActiveRecord::TestCase
end
def test_after_commit_callback_should_not_swallow_errors
- @first.after_commit_block{ fail "boom" }
+ @first.after_commit_block { fail "boom" }
assert_raises(RuntimeError) do
Topic.transaction do
@first.save!
@@ -303,8 +303,8 @@ class TransactionCallbacksTest < ActiveRecord::TestCase
def test_after_commit_callback_when_raise_should_not_restore_state
first = TopicWithCallbacks.new
second = TopicWithCallbacks.new
- first.after_commit_block{ fail "boom" }
- second.after_commit_block{ fail "boom" }
+ first.after_commit_block { fail "boom" }
+ second.after_commit_block { fail "boom" }
begin
Topic.transaction do
@@ -322,7 +322,7 @@ class TransactionCallbacksTest < ActiveRecord::TestCase
def test_after_rollback_callback_should_not_swallow_errors_when_set_to_raise
error_class = Class.new(StandardError)
- @first.after_rollback_block{ raise error_class }
+ @first.after_rollback_block { raise error_class }
assert_raises(error_class) do
Topic.transaction do
@first.save!
@@ -336,8 +336,8 @@ class TransactionCallbacksTest < ActiveRecord::TestCase
first = TopicWithCallbacks.new
second = TopicWithCallbacks.new
- first.after_rollback_block{ raise error_class }
- second.after_rollback_block{ raise error_class }
+ first.after_rollback_block { raise error_class }
+ second.after_rollback_block { raise error_class }
begin
Topic.transaction do
@@ -355,13 +355,13 @@ class TransactionCallbacksTest < ActiveRecord::TestCase
def test_after_rollback_callbacks_should_validate_on_condition
assert_raise(ArgumentError) { Topic.after_rollback(on: :save) }
- e = assert_raise(ArgumentError) { Topic.after_rollback(on: 'create') }
+ e = assert_raise(ArgumentError) { Topic.after_rollback(on: "create") }
assert_match(/:on conditions for after_commit and after_rollback callbacks have to be one of \[:create, :destroy, :update\]/, e.message)
end
def test_after_commit_callbacks_should_validate_on_condition
assert_raise(ArgumentError) { Topic.after_commit(on: :save) }
- e = assert_raise(ArgumentError) { Topic.after_commit(on: 'create') }
+ e = assert_raise(ArgumentError) { Topic.after_commit(on: "create") }
assert_match(/:on conditions for after_commit and after_rollback callbacks have to be one of \[:create, :destroy, :update\]/, e.message)
end
@@ -449,9 +449,52 @@ class CallbacksOnMultipleActionsTest < ActiveRecord::TestCase
end
end
+class CallbacksOnDestroyUpdateActionRaceTest < ActiveRecord::TestCase
+ class TopicWithHistory < ActiveRecord::Base
+ self.table_name = :topics
-class TransactionEnrollmentCallbacksTest < ActiveRecord::TestCase
+ def self.clear_history
+ @@history = []
+ end
+
+ def self.history
+ @@history ||= []
+ end
+ end
+ class TopicWithCallbacksOnDestroy < TopicWithHistory
+ after_commit(on: :destroy) { |record| record.class.history << :destroy }
+ end
+
+ class TopicWithCallbacksOnUpdate < TopicWithHistory
+ after_commit(on: :update) { |record| record.class.history << :update }
+ end
+
+ def test_trigger_once_on_multiple_deletions
+ TopicWithCallbacksOnDestroy.clear_history
+ topic = TopicWithCallbacksOnDestroy.new
+ topic.save
+ topic_clone = TopicWithCallbacksOnDestroy.find(topic.id)
+ topic.destroy
+ topic_clone.destroy
+
+ assert_equal [:destroy], TopicWithCallbacksOnDestroy.history
+ end
+
+ def test_trigger_on_update_where_row_was_deleted
+ TopicWithCallbacksOnUpdate.clear_history
+ topic = TopicWithCallbacksOnUpdate.new
+ topic.save
+ topic_clone = TopicWithCallbacksOnUpdate.find(topic.id)
+ topic.destroy
+ topic_clone.author_name = "Test Author"
+ topic_clone.save
+
+ assert_equal [], TopicWithCallbacksOnUpdate.history
+ end
+end
+
+class TransactionEnrollmentCallbacksTest < ActiveRecord::TestCase
class TopicWithoutTransactionalEnrollmentCallbacks < ActiveRecord::Base
self.table_name = :topics
@@ -470,7 +513,7 @@ class TransactionEnrollmentCallbacksTest < ActiveRecord::TestCase
def test_commit_does_not_run_transactions_callbacks_without_enrollment
@topic.transaction do
- @topic.content = 'foo'
+ @topic.content = "foo"
@topic.save!
end
assert @topic.history.empty?
@@ -479,7 +522,7 @@ class TransactionEnrollmentCallbacksTest < ActiveRecord::TestCase
def test_commit_run_transactions_callbacks_with_explicit_enrollment
@topic.transaction do
2.times do
- @topic.content = 'foo'
+ @topic.content = "foo"
@topic.save!
end
@topic.class.connection.add_transaction_record(@topic)
@@ -489,7 +532,7 @@ class TransactionEnrollmentCallbacksTest < ActiveRecord::TestCase
def test_rollback_does_not_run_transactions_callbacks_without_enrollment
@topic.transaction do
- @topic.content = 'foo'
+ @topic.content = "foo"
@topic.save!
raise ActiveRecord::Rollback
end
@@ -499,7 +542,7 @@ class TransactionEnrollmentCallbacksTest < ActiveRecord::TestCase
def test_rollback_run_transactions_callbacks_with_explicit_enrollment
@topic.transaction do
2.times do
- @topic.content = 'foo'
+ @topic.content = "foo"
@topic.save!
end
@topic.class.connection.add_transaction_record(@topic)
diff --git a/activerecord/test/cases/transaction_isolation_test.rb b/activerecord/test/cases/transaction_isolation_test.rb
index 2f7d208ed2..58abfadaf4 100644
--- a/activerecord/test/cases/transaction_isolation_test.rb
+++ b/activerecord/test/cases/transaction_isolation_test.rb
@@ -1,4 +1,4 @@
-require 'cases/helper'
+require "cases/helper"
unless ActiveRecord::Base.connection.supports_transaction_isolation?
class TransactionIsolationUnsupportedTest < ActiveRecord::TestCase
@@ -9,7 +9,7 @@ unless ActiveRecord::Base.connection.supports_transaction_isolation?
test "setting the isolation level raises an error" do
assert_raises(ActiveRecord::TransactionIsolationError) do
- Tag.transaction(isolation: :serializable) { }
+ Tag.transaction(isolation: :serializable) {}
end
end
end
@@ -20,11 +20,11 @@ if ActiveRecord::Base.connection.supports_transaction_isolation?
self.use_transactional_tests = false
class Tag < ActiveRecord::Base
- self.table_name = 'tags'
+ self.table_name = "tags"
end
class Tag2 < ActiveRecord::Base
- self.table_name = 'tags'
+ self.table_name = "tags"
end
setup do
@@ -63,18 +63,18 @@ if ActiveRecord::Base.connection.supports_transaction_isolation?
# We are testing that a nonrepeatable read does not happen
if ActiveRecord::Base.connection.transaction_isolation_levels.include?(:repeatable_read)
test "repeatable read" do
- tag = Tag.create(name: 'jon')
+ tag = Tag.create(name: "jon")
Tag.transaction(isolation: :repeatable_read) do
tag.reload
- Tag2.find(tag.id).update(name: 'emily')
+ Tag2.find(tag.id).update(name: "emily")
tag.reload
- assert_equal 'jon', tag.name
+ assert_equal "jon", tag.name
end
tag.reload
- assert_equal 'emily', tag.name
+ assert_equal "emily", tag.name
end
end
@@ -90,7 +90,7 @@ if ActiveRecord::Base.connection.supports_transaction_isolation?
test "setting isolation when joining a transaction raises an error" do
Tag.transaction do
assert_raises(ActiveRecord::TransactionIsolationError) do
- Tag.transaction(isolation: :serializable) { }
+ Tag.transaction(isolation: :serializable) {}
end
end
end
@@ -98,7 +98,7 @@ if ActiveRecord::Base.connection.supports_transaction_isolation?
test "setting isolation when starting a nested transaction raises error" do
Tag.transaction do
assert_raises(ActiveRecord::TransactionIsolationError) do
- Tag.transaction(requires_new: true, isolation: :serializable) { }
+ Tag.transaction(requires_new: true, isolation: :serializable) {}
end
end
end
diff --git a/activerecord/test/cases/transactions_test.rb b/activerecord/test/cases/transactions_test.rb
index 791b895d02..9b1cca8583 100644
--- a/activerecord/test/cases/transactions_test.rb
+++ b/activerecord/test/cases/transactions_test.rb
@@ -1,12 +1,12 @@
require "cases/helper"
-require 'models/topic'
-require 'models/reply'
-require 'models/developer'
-require 'models/computer'
-require 'models/book'
-require 'models/author'
-require 'models/post'
-require 'models/movie'
+require "models/topic"
+require "models/reply"
+require "models/developer"
+require "models/computer"
+require "models/book"
+require "models/author"
+require "models/post"
+require "models/movie"
class TransactionTest < ActiveRecord::TestCase
self.use_transactional_tests = false
@@ -98,7 +98,7 @@ class TransactionTest < ActiveRecord::TestCase
end
Topic.transaction do
- @first.approved = true
+ @first.approved = true
@first.save!
end
@@ -144,7 +144,7 @@ class TransactionTest < ActiveRecord::TestCase
def test_raising_exception_in_callback_rollbacks_in_save
def @first.after_save_for_transaction
- raise 'Make the transaction rollback'
+ raise "Make the transaction rollback"
end
@first.approved = true
@@ -160,7 +160,7 @@ class TransactionTest < ActiveRecord::TestCase
assert !@first.approved
Topic.transaction do
- @first.approved = true
+ @first.approved = true
@first.save!
end
assert !Topic.find(@first.id).approved?, "Should not commit the approved flag"
@@ -170,7 +170,7 @@ class TransactionTest < ActiveRecord::TestCase
topic = Topic.new
def topic.after_save_for_transaction
- raise 'Make the transaction rollback'
+ raise "Make the transaction rollback"
end
assert_raises(RuntimeError) do
@@ -181,7 +181,7 @@ class TransactionTest < ActiveRecord::TestCase
end
def test_transaction_state_is_cleared_when_record_is_persisted
- author = Author.create! name: 'foo'
+ author = Author.create! name: "foo"
author.name = nil
assert_not author.save
assert_not author.new_record?
@@ -230,7 +230,7 @@ class TransactionTest < ActiveRecord::TestCase
send("add_cancelling_before_#{filter}_with_db_side_effect_to_topic", @first)
nbooks_before_save = Book.count
original_author_name = @first.author_name
- @first.author_name += '_this_should_not_end_up_in_the_db'
+ @first.author_name += "_this_should_not_end_up_in_the_db"
status = @first.save
assert !status
assert_equal original_author_name, @first.reload.author_name
@@ -241,7 +241,7 @@ class TransactionTest < ActiveRecord::TestCase
send("add_cancelling_before_#{filter}_with_db_side_effect_to_topic", @first)
nbooks_before_save = Book.count
original_author_name = @first.author_name
- @first.author_name += '_this_should_not_end_up_in_the_db'
+ @first.author_name += "_this_should_not_end_up_in_the_db"
begin
@first.save!
@@ -256,18 +256,18 @@ class TransactionTest < ActiveRecord::TestCase
def test_callback_rollback_in_create
topic = Class.new(Topic) {
def after_create_for_transaction
- raise 'Make the transaction rollback'
+ raise "Make the transaction rollback"
end
}
- new_topic = topic.new(:title => "A new topic",
- :author_name => "Ben",
- :author_email_address => "ben@example.com",
- :written_on => "2003-07-16t15:28:11.2233+01:00",
- :last_read => "2004-04-15",
- :bonus_time => "2005-01-30t15:28:00.00+01:00",
- :content => "Have a nice day",
- :approved => false)
+ new_topic = topic.new(title: "A new topic",
+ author_name: "Ben",
+ author_email_address: "ben@example.com",
+ written_on: "2003-07-16t15:28:11.2233+01:00",
+ last_read: "2004-04-15",
+ bonus_time: "2005-01-30t15:28:00.00+01:00",
+ content: "Have a nice day",
+ approved: false)
new_record_snapshot = !new_topic.persisted?
id_present = new_topic.has_attribute?(Topic.primary_key)
@@ -291,7 +291,7 @@ class TransactionTest < ActiveRecord::TestCase
end
}
- new_topic = topic.create(:title => "A new topic")
+ new_topic = topic.create(title: "A new topic")
assert !new_topic.persisted?, "The topic should not be persisted"
assert_nil new_topic.id, "The topic should not have an ID"
end
@@ -329,7 +329,7 @@ class TransactionTest < ActiveRecord::TestCase
def test_invalid_keys_for_transaction
assert_raise ArgumentError do
- Topic.transaction :nested => true do
+ Topic.transaction nested: true do
end
end
end
@@ -342,7 +342,7 @@ class TransactionTest < ActiveRecord::TestCase
@second.save!
begin
- Topic.transaction :requires_new => true do
+ Topic.transaction requires_new: true do
@first.happy = false
@first.save!
raise
@@ -363,7 +363,7 @@ class TransactionTest < ActiveRecord::TestCase
@second.save!
begin
- @second.transaction :requires_new => true do
+ @second.transaction requires_new: true do
@first.happy = false
@first.save!
raise
@@ -403,17 +403,17 @@ class TransactionTest < ActiveRecord::TestCase
@first.save!
begin
- Topic.transaction :requires_new => true do
+ Topic.transaction requires_new: true do
@first.content = "Two"
@first.save!
begin
- Topic.transaction :requires_new => true do
+ Topic.transaction requires_new: true do
@first.content = "Three"
@first.save!
begin
- Topic.transaction :requires_new => true do
+ Topic.transaction requires_new: true do
@first.content = "Four"
@first.save!
raise
@@ -443,16 +443,16 @@ class TransactionTest < ActiveRecord::TestCase
def test_using_named_savepoints
Topic.transaction do
- @first.approved = true
+ @first.approved = true
@first.save!
Topic.connection.create_savepoint("first")
- @first.approved = false
+ @first.approved = false
@first.save!
Topic.connection.rollback_to_savepoint("first")
assert @first.reload.approved?
- @first.approved = false
+ @first.approved = false
@first.save!
Topic.connection.release_savepoint("first")
assert_not @first.reload.approved?
@@ -493,7 +493,7 @@ class TransactionTest < ActiveRecord::TestCase
def test_rollback_when_commit_raises
assert_called(Topic.connection, :begin_db_transaction) do
- Topic.connection.stub(:commit_db_transaction, ->{ raise('OH NOES') }) do
+ Topic.connection.stub(:commit_db_transaction, -> { raise("OH NOES") }) do
assert_called(Topic.connection, :rollback_db_transaction) do
e = assert_raise RuntimeError do
@@ -501,22 +501,23 @@ class TransactionTest < ActiveRecord::TestCase
# do nothing
end
end
- assert_equal 'OH NOES', e.message
+ assert_equal "OH NOES", e.message
end
end
end
end
def test_rollback_when_saving_a_frozen_record
- topic = Topic.new(:title => 'test')
+ topic = Topic.new(title: "test")
topic.freeze
e = assert_raise(RuntimeError) { topic.save }
- assert_match(/frozen/i, e.message) # Not good enough, but we can't do much
- # about it since there is no specific error
- # for frozen objects.
- assert !topic.persisted?, 'not persisted'
+ # Not good enough, but we can't do much
+ # about it since there is no specific error
+ # for frozen objects.
+ assert_match(/frozen/i, e.message)
+ assert !topic.persisted?, "not persisted"
assert_nil topic.id
- assert topic.frozen?, 'not frozen'
+ assert topic.frozen?, "not frozen"
end
def test_rollback_when_thread_killed
@@ -549,12 +550,12 @@ class TransactionTest < ActiveRecord::TestCase
def test_restore_active_record_state_for_all_records_in_a_transaction
topic_without_callbacks = Class.new(ActiveRecord::Base) do
- self.table_name = 'topics'
+ self.table_name = "topics"
end
- topic_1 = Topic.new(:title => 'test_1')
- topic_2 = Topic.new(:title => 'test_2')
- topic_3 = topic_without_callbacks.new(:title => 'test_3')
+ topic_1 = Topic.new(title: "test_1")
+ topic_2 = Topic.new(title: "test_2")
+ topic_3 = topic_without_callbacks.new(title: "test_3")
Topic.transaction do
assert topic_1.save
@@ -562,27 +563,27 @@ class TransactionTest < ActiveRecord::TestCase
assert topic_3.save
@first.save
@second.destroy
- assert topic_1.persisted?, 'persisted'
+ assert topic_1.persisted?, "persisted"
assert_not_nil topic_1.id
- assert topic_2.persisted?, 'persisted'
+ assert topic_2.persisted?, "persisted"
assert_not_nil topic_2.id
- assert topic_3.persisted?, 'persisted'
+ assert topic_3.persisted?, "persisted"
assert_not_nil topic_3.id
- assert @first.persisted?, 'persisted'
+ assert @first.persisted?, "persisted"
assert_not_nil @first.id
- assert @second.destroyed?, 'destroyed'
+ assert @second.destroyed?, "destroyed"
raise ActiveRecord::Rollback
end
- assert !topic_1.persisted?, 'not persisted'
+ assert !topic_1.persisted?, "not persisted"
assert_nil topic_1.id
- assert !topic_2.persisted?, 'not persisted'
+ assert !topic_2.persisted?, "not persisted"
assert_nil topic_2.id
- assert !topic_3.persisted?, 'not persisted'
+ assert !topic_3.persisted?, "not persisted"
assert_nil topic_3.id
- assert @first.persisted?, 'persisted'
+ assert @first.persisted?, "persisted"
assert_not_nil @first.id
- assert !@second.destroyed?, 'not destroyed'
+ assert !@second.destroyed?, "not destroyed"
end
def test_restore_frozen_state_after_double_destroy
@@ -606,7 +607,7 @@ class TransactionTest < ActiveRecord::TestCase
topic.destroy
raise ActiveRecord::Rollback
end
- assert topic.frozen?, 'frozen'
+ assert topic.frozen?, "frozen"
end
def test_rollback_for_freshly_persisted_records
@@ -615,7 +616,7 @@ class TransactionTest < ActiveRecord::TestCase
topic.destroy
raise ActiveRecord::Rollback
end
- assert topic.persisted?, 'persisted'
+ assert topic.persisted?, "persisted"
end
def test_sqlite_add_column_in_transaction
@@ -629,27 +630,27 @@ class TransactionTest < ActiveRecord::TestCase
assert_nothing_raised do
Topic.reset_column_information
- Topic.connection.add_column('topics', 'stuff', :string)
- assert Topic.column_names.include?('stuff')
+ Topic.connection.add_column("topics", "stuff", :string)
+ assert_includes Topic.column_names, "stuff"
Topic.reset_column_information
- Topic.connection.remove_column('topics', 'stuff')
- assert !Topic.column_names.include?('stuff')
+ Topic.connection.remove_column("topics", "stuff")
+ assert_not_includes Topic.column_names, "stuff"
end
if Topic.connection.supports_ddl_transactions?
assert_nothing_raised do
- Topic.transaction { Topic.connection.add_column('topics', 'stuff', :string) }
+ Topic.transaction { Topic.connection.add_column("topics", "stuff", :string) }
end
else
Topic.transaction do
- assert_raise(ActiveRecord::StatementInvalid) { Topic.connection.add_column('topics', 'stuff', :string) }
+ assert_raise(ActiveRecord::StatementInvalid) { Topic.connection.add_column("topics", "stuff", :string) }
raise ActiveRecord::Rollback
end
end
ensure
begin
- Topic.connection.remove_column('topics', 'stuff')
+ Topic.connection.remove_column("topics", "stuff")
rescue
ensure
Topic.reset_column_information
@@ -687,12 +688,12 @@ class TransactionTest < ActiveRecord::TestCase
def test_transaction_rollback_with_primarykeyless_tables
connection = ActiveRecord::Base.connection
connection.create_table(:transaction_without_primary_keys, force: true, id: false) do |t|
- t.integer :thing_id
+ t.integer :thing_id
end
klass = Class.new(ActiveRecord::Base) do
- self.table_name = 'transaction_without_primary_keys'
- after_commit { } # necessary to trigger the has_transactional_callbacks branch
+ self.table_name = "transaction_without_primary_keys"
+ after_commit {} # necessary to trigger the has_transactional_callbacks branch
end
assert_no_difference(-> { klass.count }) do
@@ -702,20 +703,20 @@ class TransactionTest < ActiveRecord::TestCase
end
end
ensure
- connection.drop_table 'transaction_without_primary_keys', if_exists: true
+ connection.drop_table "transaction_without_primary_keys", if_exists: true
end
private
- %w(validation save destroy).each do |filter|
- define_method("add_cancelling_before_#{filter}_with_db_side_effect_to_topic") do |topic|
- meta = class << topic; self; end
- meta.send("define_method", "before_#{filter}_for_transaction") do
- Book.create
- throw(:abort)
+ %w(validation save destroy).each do |filter|
+ define_method("add_cancelling_before_#{filter}_with_db_side_effect_to_topic") do |topic|
+ meta = class << topic; self; end
+ meta.send("define_method", "before_#{filter}_for_transaction") do
+ Book.create
+ throw(:abort)
+ end
end
end
- end
end
class TransactionsWithTransactionalFixturesTest < ActiveRecord::TestCase
diff --git a/activerecord/test/cases/type/date_time_test.rb b/activerecord/test/cases/type/date_time_test.rb
index bc4900e1c2..6848619ece 100644
--- a/activerecord/test/cases/type/date_time_test.rb
+++ b/activerecord/test/cases/type/date_time_test.rb
@@ -3,7 +3,7 @@ require "models/task"
module ActiveRecord
module Type
- class IntegerTest < ActiveRecord::TestCase
+ class DateTimeTest < ActiveRecord::TestCase
def test_datetime_seconds_precision_applied_to_timestamp
skip "This test is invalid if subsecond precision isn't supported" unless subsecond_precision_supported?
p = Task.create!(starting: ::Time.now)
diff --git a/activerecord/test/cases/type/integer_test.rb b/activerecord/test/cases/type/integer_test.rb
index c0932d5357..368b6d7199 100644
--- a/activerecord/test/cases/type/integer_test.rb
+++ b/activerecord/test/cases/type/integer_test.rb
@@ -6,13 +6,13 @@ module ActiveRecord
class IntegerTest < ActiveRecord::TestCase
test "casting ActiveRecord models" do
type = Type::Integer.new
- firm = Firm.create(:name => 'Apple')
+ firm = Firm.create(name: "Apple")
assert_nil type.cast(firm)
end
test "values which are out of range can be re-assigned" do
klass = Class.new(ActiveRecord::Base) do
- self.table_name = 'posts'
+ self.table_name = "posts"
attribute :foo, :integer
end
model = klass.new
diff --git a/activerecord/test/cases/type/string_test.rb b/activerecord/test/cases/type/string_test.rb
index 6fe6d46711..a95da864fa 100644
--- a/activerecord/test/cases/type/string_test.rb
+++ b/activerecord/test/cases/type/string_test.rb
@@ -1,21 +1,21 @@
-require 'cases/helper'
+require "cases/helper"
module ActiveRecord
class StringTypeTest < ActiveRecord::TestCase
test "string mutations are detected" do
klass = Class.new(Base)
- klass.table_name = 'authors'
+ klass.table_name = "authors"
- author = klass.create!(name: 'Sean')
+ author = klass.create!(name: "Sean")
assert_not author.changed?
- author.name << ' Griffin'
+ author.name << " Griffin"
assert author.name_changed?
author.save!
author.reload
- assert_equal 'Sean Griffin', author.name
+ assert_equal "Sean Griffin", author.name
assert_not author.changed?
end
end
diff --git a/activerecord/test/cases/type/type_map_test.rb b/activerecord/test/cases/type/type_map_test.rb
index 172c6dfc4c..2959d36466 100644
--- a/activerecord/test/cases/type/type_map_test.rb
+++ b/activerecord/test/cases/type/type_map_test.rb
@@ -15,7 +15,7 @@ module ActiveRecord
mapping.register_type(/boolean/i, boolean)
- assert_equal mapping.lookup('boolean'), boolean
+ assert_equal mapping.lookup("boolean"), boolean
end
def test_overriding_registered_types
@@ -26,7 +26,7 @@ module ActiveRecord
mapping.register_type(/time/i, time)
mapping.register_type(/time/i, timestamp)
- assert_equal mapping.lookup('time'), timestamp
+ assert_equal mapping.lookup("time"), timestamp
end
def test_fuzzy_lookup
@@ -35,7 +35,7 @@ module ActiveRecord
mapping.register_type(/varchar/i, string)
- assert_equal mapping.lookup('varchar(20)'), string
+ assert_equal mapping.lookup("varchar(20)"), string
end
def test_aliasing_types
@@ -43,9 +43,9 @@ module ActiveRecord
mapping = TypeMap.new
mapping.register_type(/string/i, string)
- mapping.alias_type(/varchar/i, 'string')
+ mapping.alias_type(/varchar/i, "string")
- assert_equal mapping.lookup('varchar'), string
+ assert_equal mapping.lookup("varchar"), string
end
def test_changing_type_changes_aliases
@@ -54,20 +54,20 @@ module ActiveRecord
mapping = TypeMap.new
mapping.register_type(/timestamp/i, time)
- mapping.alias_type(/datetime/i, 'timestamp')
+ mapping.alias_type(/datetime/i, "timestamp")
mapping.register_type(/timestamp/i, timestamp)
- assert_equal mapping.lookup('datetime'), timestamp
+ assert_equal mapping.lookup("datetime"), timestamp
end
def test_aliases_keep_metadata
mapping = TypeMap.new
mapping.register_type(/decimal/i) { |sql_type| sql_type }
- mapping.alias_type(/number/i, 'decimal')
+ mapping.alias_type(/number/i, "decimal")
- assert_equal mapping.lookup('number(20)'), 'decimal(20)'
- assert_equal mapping.lookup('number'), 'decimal'
+ assert_equal mapping.lookup("number(20)"), "decimal(20)"
+ assert_equal mapping.lookup("number"), "decimal"
end
def test_register_proc
@@ -76,15 +76,15 @@ module ActiveRecord
mapping = TypeMap.new
mapping.register_type(/varchar/i) do |type|
- if type.include?('(')
+ if type.include?("(")
string
else
binary
end
end
- assert_equal mapping.lookup('varchar(20)'), string
- assert_equal mapping.lookup('varchar'), binary
+ assert_equal mapping.lookup("varchar(20)"), string
+ assert_equal mapping.lookup("varchar"), binary
end
def test_additional_lookup_args
@@ -92,16 +92,16 @@ module ActiveRecord
mapping.register_type(/varchar/i) do |type, limit|
if limit > 255
- 'text'
+ "text"
else
- 'string'
+ "string"
end
end
- mapping.alias_type(/string/i, 'varchar')
+ mapping.alias_type(/string/i, "varchar")
- assert_equal mapping.lookup('varchar', 200), 'string'
- assert_equal mapping.lookup('varchar', 400), 'text'
- assert_equal mapping.lookup('string', 400), 'text'
+ assert_equal mapping.lookup("varchar", 200), "string"
+ assert_equal mapping.lookup("varchar", 400), "text"
+ assert_equal mapping.lookup("string", 400), "text"
end
def test_requires_value_or_block
@@ -115,13 +115,13 @@ module ActiveRecord
def test_lookup_non_strings
mapping = HashLookupTypeMap.new
- mapping.register_type(1, 'string')
- mapping.register_type(2, 'int')
+ mapping.register_type(1, "string")
+ mapping.register_type(2, "int")
mapping.alias_type(3, 1)
- assert_equal mapping.lookup(1), 'string'
- assert_equal mapping.lookup(2), 'int'
- assert_equal mapping.lookup(3), 'string'
+ assert_equal mapping.lookup(1), "string"
+ assert_equal mapping.lookup(2), "int"
+ assert_equal mapping.lookup(3), "string"
assert_kind_of Type::Value, mapping.lookup(4)
end
@@ -174,4 +174,3 @@ module ActiveRecord
end
end
end
-
diff --git a/activerecord/test/cases/type/unsigned_integer_test.rb b/activerecord/test/cases/type/unsigned_integer_test.rb
new file mode 100644
index 0000000000..1cd4dbc2c5
--- /dev/null
+++ b/activerecord/test/cases/type/unsigned_integer_test.rb
@@ -0,0 +1,17 @@
+require "cases/helper"
+
+module ActiveRecord
+ module Type
+ class UnsignedIntegerTest < ActiveRecord::TestCase
+ test "unsigned int max value is in range" do
+ assert_equal(4294967295, UnsignedInteger.new.serialize(4294967295))
+ end
+
+ test "minus value is out of range" do
+ assert_raises(ActiveModel::RangeError) do
+ UnsignedInteger.new.serialize(-1)
+ end
+ end
+ end
+ end
+end
diff --git a/activerecord/test/cases/types_test.rb b/activerecord/test/cases/types_test.rb
index 81fcf04a27..11476ea0ef 100644
--- a/activerecord/test/cases/types_test.rb
+++ b/activerecord/test/cases/types_test.rb
@@ -9,7 +9,7 @@ module ActiveRecord
raise
end
klass = Class.new(ActiveRecord::Base) do
- self.table_name = 'posts'
+ self.table_name = "posts"
attribute :foo, type_which_cannot_go_to_the_database
end
model = klass.new
diff --git a/activerecord/test/cases/validations/absence_validation_test.rb b/activerecord/test/cases/validations/absence_validation_test.rb
index c0b3750bcc..870619e4e7 100644
--- a/activerecord/test/cases/validations/absence_validation_test.rb
+++ b/activerecord/test/cases/validations/absence_validation_test.rb
@@ -1,8 +1,8 @@
require "cases/helper"
-require 'models/face'
-require 'models/interest'
-require 'models/man'
-require 'models/topic'
+require "models/face"
+require "models/interest"
+require "models/man"
+require "models/topic"
class AbsenceValidationTest < ActiveRecord::TestCase
def test_non_association
@@ -52,7 +52,7 @@ class AbsenceValidationTest < ActiveRecord::TestCase
end
face_with_to_a = Face.new
- def face_with_to_a.to_a; ['(/)', '(\)']; end
+ def face_with_to_a.to_a; ["(/)", '(\)']; end
assert_nothing_raised { boy_klass.new(face: face_with_to_a).valid? }
end
@@ -62,10 +62,10 @@ class AbsenceValidationTest < ActiveRecord::TestCase
Interest.send(:attr_accessor, :token)
Interest.validates_absence_of(:token)
- interest = Interest.create!(topic: 'Thought Leadering')
+ interest = Interest.create!(topic: "Thought Leadering")
assert interest.valid?
- interest.token = 'tl'
+ interest.token = "tl"
assert interest.invalid?
end
diff --git a/activerecord/test/cases/validations/association_validation_test.rb b/activerecord/test/cases/validations/association_validation_test.rb
index 584a3dc0d8..f5ceb27d97 100644
--- a/activerecord/test/cases/validations/association_validation_test.rb
+++ b/activerecord/test/cases/validations/association_validation_test.rb
@@ -1,8 +1,8 @@
require "cases/helper"
-require 'models/topic'
-require 'models/reply'
-require 'models/man'
-require 'models/interest'
+require "models/topic"
+require "models/reply"
+require "models/man"
+require "models/interest"
class AssociationValidationTest < ActiveRecord::TestCase
fixtures :topics
@@ -25,8 +25,8 @@ class AssociationValidationTest < ActiveRecord::TestCase
end
def test_validates_associated_one
- Reply.validates :topic, :associated => true
- Topic.validates_presence_of( :content )
+ Reply.validates :topic, associated: true
+ Topic.validates_presence_of(:content)
r = Reply.new("title" => "A reply", "content" => "with content!")
r.topic = Topic.create("title" => "uhohuhoh")
assert !r.valid?
@@ -58,7 +58,7 @@ class AssociationValidationTest < ActiveRecord::TestCase
end
def test_validates_associated_with_custom_message_using_quotes
- Reply.validates_associated :topic, :message=> "This string contains 'single' and \"double\" quotes"
+ Reply.validates_associated :topic, message: "This string contains 'single' and \"double\" quotes"
Topic.validates_presence_of :content
r = Reply.create("title" => "A reply", "content" => "with content!")
r.topic = Topic.create("title" => "uhohuhoh")
@@ -80,8 +80,8 @@ class AssociationValidationTest < ActiveRecord::TestCase
repair_validations(Interest) do
# Note that Interest and Man have the :inverse_of option set
Interest.validates_presence_of(:man)
- man = Man.new(:name => 'John')
- interest = man.interests.build(:topic => 'Airplanes')
+ man = Man.new(name: "John")
+ interest = man.interests.build(topic: "Airplanes")
assert interest.valid?, "Expected interest to be valid, but was not. Interest should have a man object associated"
end
end
@@ -89,8 +89,8 @@ class AssociationValidationTest < ActiveRecord::TestCase
def test_validates_presence_of_belongs_to_association__existing_parent
repair_validations(Interest) do
Interest.validates_presence_of(:man)
- man = Man.create!(:name => 'John')
- interest = man.interests.build(:topic => 'Airplanes')
+ man = Man.create!(name: "John")
+ interest = man.interests.build(topic: "Airplanes")
assert interest.valid?, "Expected interest to be valid, but was not. Interest should have a man object associated"
end
end
diff --git a/activerecord/test/cases/validations/i18n_generate_message_validation_test.rb b/activerecord/test/cases/validations/i18n_generate_message_validation_test.rb
index 13d4d85afa..a57065ba75 100644
--- a/activerecord/test/cases/validations/i18n_generate_message_validation_test.rb
+++ b/activerecord/test/cases/validations/i18n_generate_message_validation_test.rb
@@ -1,5 +1,5 @@
require "cases/helper"
-require 'models/topic'
+require "models/topic"
class I18nGenerateMessageValidationTest < ActiveRecord::TestCase
def setup
@@ -20,20 +20,20 @@ class I18nGenerateMessageValidationTest < ActiveRecord::TestCase
# validates_associated: generate_message(attr_name, :invalid, :message => custom_message, :value => value)
def test_generate_message_invalid_with_default_message
- assert_equal 'is invalid', @topic.errors.generate_message(:title, :invalid, :value => 'title')
+ assert_equal "is invalid", @topic.errors.generate_message(:title, :invalid, value: "title")
end
def test_generate_message_invalid_with_custom_message
- assert_equal 'custom message title', @topic.errors.generate_message(:title, :invalid, :message => 'custom message %{value}', :value => 'title')
+ assert_equal "custom message title", @topic.errors.generate_message(:title, :invalid, message: "custom message %{value}", value: "title")
end
# validates_uniqueness_of: generate_message(attr_name, :taken, :message => custom_message)
def test_generate_message_taken_with_default_message
- assert_equal "has already been taken", @topic.errors.generate_message(:title, :taken, :value => 'title')
+ assert_equal "has already been taken", @topic.errors.generate_message(:title, :taken, value: "title")
end
def test_generate_message_taken_with_custom_message
- assert_equal 'custom message title', @topic.errors.generate_message(:title, :taken, :message => 'custom message %{value}', :value => 'title')
+ assert_equal "custom message title", @topic.errors.generate_message(:title, :taken, message: "custom message %{value}", value: "title")
end
# ActiveRecord#RecordInvalid exception
@@ -47,7 +47,7 @@ class I18nGenerateMessageValidationTest < ActiveRecord::TestCase
test "RecordInvalid exception translation falls back to the :errors namespace" do
reset_i18n_load_path do
- I18n.backend.store_translations 'en', :errors => {:messages => {:record_invalid => 'fallback message'}}
+ I18n.backend.store_translations "en", errors: { messages: { record_invalid: "fallback message" } }
topic = Topic.new
topic.errors.add(:title, :blank)
assert_equal "fallback message", ActiveRecord::RecordInvalid.new(topic).message
@@ -56,29 +56,29 @@ class I18nGenerateMessageValidationTest < ActiveRecord::TestCase
test "translation for 'taken' can be overridden" do
reset_i18n_load_path do
- I18n.backend.store_translations "en", {errors: {attributes: {title: {taken: "Custom taken message" }}}}
- assert_equal "Custom taken message", @topic.errors.generate_message(:title, :taken, :value => 'title')
+ I18n.backend.store_translations "en", errors: { attributes: { title: { taken: "Custom taken message" } } }
+ assert_equal "Custom taken message", @topic.errors.generate_message(:title, :taken, value: "title")
end
end
test "translation for 'taken' can be overridden in activerecord scope" do
reset_i18n_load_path do
- I18n.backend.store_translations "en", {activerecord: {errors: {messages: {taken: "Custom taken message" }}}}
- assert_equal "Custom taken message", @topic.errors.generate_message(:title, :taken, :value => 'title')
+ I18n.backend.store_translations "en", activerecord: { errors: { messages: { taken: "Custom taken message" } } }
+ assert_equal "Custom taken message", @topic.errors.generate_message(:title, :taken, value: "title")
end
end
test "translation for 'taken' can be overridden in activerecord model scope" do
reset_i18n_load_path do
- I18n.backend.store_translations "en", {activerecord: {errors: {models: {topic: {taken: "Custom taken message" }}}}}
- assert_equal "Custom taken message", @topic.errors.generate_message(:title, :taken, :value => 'title')
+ I18n.backend.store_translations "en", activerecord: { errors: { models: { topic: { taken: "Custom taken message" } } } }
+ assert_equal "Custom taken message", @topic.errors.generate_message(:title, :taken, value: "title")
end
end
test "translation for 'taken' can be overridden in activerecord attributes scope" do
reset_i18n_load_path do
- I18n.backend.store_translations "en", {activerecord: {errors: {models: {topic: {attributes: {title: {taken: "Custom taken message" }}}}}}}
- assert_equal "Custom taken message", @topic.errors.generate_message(:title, :taken, :value => 'title')
+ I18n.backend.store_translations "en", activerecord: { errors: { models: { topic: { attributes: { title: { taken: "Custom taken message" } } } } } }
+ assert_equal "Custom taken message", @topic.errors.generate_message(:title, :taken, value: "title")
end
end
end
diff --git a/activerecord/test/cases/validations/i18n_validation_test.rb b/activerecord/test/cases/validations/i18n_validation_test.rb
index 5b5307489a..fd88a3ea67 100644
--- a/activerecord/test/cases/validations/i18n_validation_test.rb
+++ b/activerecord/test/cases/validations/i18n_validation_test.rb
@@ -1,6 +1,6 @@
require "cases/helper"
-require 'models/topic'
-require 'models/reply'
+require "models/topic"
+require "models/reply"
class I18nValidationTest < ActiveRecord::TestCase
repair_validations(Topic, Reply)
@@ -12,7 +12,7 @@ class I18nValidationTest < ActiveRecord::TestCase
@old_load_path, @old_backend = I18n.load_path.dup, I18n.backend
I18n.load_path.clear
I18n.backend = I18n::Backend::Simple.new
- I18n.backend.store_translations('en', :errors => {:messages => {:custom => nil}})
+ I18n.backend.store_translations("en", errors: { messages: { custom: nil } })
end
teardown do
@@ -21,12 +21,12 @@ class I18nValidationTest < ActiveRecord::TestCase
end
def unique_topic
- @unique ||= Topic.create :title => 'unique!'
+ @unique ||= Topic.create title: "unique!"
end
def replied_topic
@replied_topic ||= begin
- topic = Topic.create(:title => "topic")
+ topic = Topic.create(title: "topic")
topic.replies << Reply.new
topic
end
@@ -36,20 +36,20 @@ class I18nValidationTest < ActiveRecord::TestCase
# are used to generate tests to keep things DRY
#
COMMON_CASES = [
- # [ case, validation_options, generate_message_options]
+ # [ case, validation_options, generate_message_options]
[ "given no options", {}, {}],
- [ "given custom message", {:message => "custom"}, {:message => "custom"}],
- [ "given if condition", {:if => lambda { true }}, {}],
- [ "given unless condition", {:unless => lambda { false }}, {}],
- [ "given option that is not reserved", {:format => "jpg"}, {:format => "jpg" }],
- [ "given on condition", {on: [:create, :update] }, {}]
+ [ "given custom message", { message: "custom" }, { message: "custom" }],
+ [ "given if condition", { if: lambda { true } }, {}],
+ [ "given unless condition", { unless: lambda { false } }, {}],
+ [ "given option that is not reserved", { format: "jpg" }, { format: "jpg" }],
+ [ "given on condition", { on: [:create, :update] }, {}]
]
COMMON_CASES.each do |name, validation_options, generate_message_options|
test "validates_uniqueness_of on generated message #{name}" do
Topic.validates_uniqueness_of :title, validation_options
@topic.title = unique_topic.title
- assert_called_with(@topic.errors, :generate_message, [:title, :taken, generate_message_options.merge(:value => 'unique!')]) do
+ assert_called_with(@topic.errors, :generate_message, [:title, :taken, generate_message_options.merge(value: "unique!")]) do
@topic.valid?
end
end
@@ -58,27 +58,26 @@ class I18nValidationTest < ActiveRecord::TestCase
COMMON_CASES.each do |name, validation_options, generate_message_options|
test "validates_associated on generated message #{name}" do
Topic.validates_associated :replies, validation_options
- assert_called_with(replied_topic.errors, :generate_message, [:replies, :invalid, generate_message_options.merge(:value => replied_topic.replies)]) do
+ assert_called_with(replied_topic.errors, :generate_message, [:replies, :invalid, generate_message_options.merge(value: replied_topic.replies)]) do
replied_topic.save
end
end
end
def test_validates_associated_finds_custom_model_key_translation
- I18n.backend.store_translations 'en', :activerecord => {:errors => {:models => {:topic => {:attributes => {:replies => {:invalid => 'custom message'}}}}}}
- I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:invalid => 'global message'}}}
+ I18n.backend.store_translations "en", activerecord: { errors: { models: { topic: { attributes: { replies: { invalid: "custom message" } } } } } }
+ I18n.backend.store_translations "en", activerecord: { errors: { messages: { invalid: "global message" } } }
Topic.validates_associated :replies
replied_topic.valid?
- assert_equal ['custom message'], replied_topic.errors[:replies].uniq
+ assert_equal ["custom message"], replied_topic.errors[:replies].uniq
end
def test_validates_associated_finds_global_default_translation
- I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:invalid => 'global message'}}}
+ I18n.backend.store_translations "en", activerecord: { errors: { messages: { invalid: "global message" } } }
Topic.validates_associated :replies
replied_topic.valid?
- assert_equal ['global message'], replied_topic.errors[:replies]
+ assert_equal ["global message"], replied_topic.errors[:replies]
end
-
end
diff --git a/activerecord/test/cases/validations/length_validation_test.rb b/activerecord/test/cases/validations/length_validation_test.rb
index 78263fd955..ba45c6dcc1 100644
--- a/activerecord/test/cases/validations/length_validation_test.rb
+++ b/activerecord/test/cases/validations/length_validation_test.rb
@@ -1,47 +1,46 @@
require "cases/helper"
-require 'models/owner'
-require 'models/pet'
-require 'models/person'
+require "models/owner"
+require "models/pet"
+require "models/person"
class LengthValidationTest < ActiveRecord::TestCase
fixtures :owners
setup do
@owner = Class.new(Owner) do
- def self.name; 'Owner'; end
+ def self.name; "Owner"; end
end
end
-
def test_validates_size_of_association
assert_nothing_raised { @owner.validates_size_of :pets, minimum: 1 }
- o = @owner.new('name' => 'nopets')
+ o = @owner.new("name" => "nopets")
assert !o.save
assert o.errors[:pets].any?
- o.pets.build('name' => 'apet')
+ o.pets.build("name" => "apet")
assert o.valid?
end
def test_validates_size_of_association_using_within
assert_nothing_raised { @owner.validates_size_of :pets, within: 1..2 }
- o = @owner.new('name' => 'nopets')
+ o = @owner.new("name" => "nopets")
assert !o.save
assert o.errors[:pets].any?
- o.pets.build('name' => 'apet')
+ o.pets.build("name" => "apet")
assert o.valid?
- 2.times { o.pets.build('name' => 'apet') }
+ 2.times { o.pets.build("name" => "apet") }
assert !o.save
assert o.errors[:pets].any?
end
def test_validates_size_of_association_utf8
@owner.validates_size_of :pets, minimum: 1
- o = @owner.new('name' => 'あいうえおかきくけこ')
+ o = @owner.new("name" => "あいうえおかきくけこ")
assert !o.save
assert o.errors[:pets].any?
- o.pets.build('name' => 'あいうえおかきくけこ')
+ o.pets.build("name" => "あいうえおかきくけこ")
assert o.valid?
end
@@ -55,7 +54,7 @@ class LengthValidationTest < ActiveRecord::TestCase
assert owner.save
pet_count = Pet.count
- assert_not owner.update_attributes pets_attributes: [ {_destroy: 1, id: pet.id} ]
+ assert_not owner.update_attributes pets_attributes: [ { _destroy: 1, id: pet.id } ]
assert_not owner.valid?
assert owner.errors[:pets].any?
assert_equal pet_count, Pet.count
@@ -67,11 +66,11 @@ class LengthValidationTest < ActiveRecord::TestCase
Pet.validates_length_of(:name, minimum: 1)
Pet.validates_length_of(:nickname, minimum: 1)
- pet = Pet.create!(name: 'Fancy Pants', nickname: 'Fancy')
+ pet = Pet.create!(name: "Fancy Pants", nickname: "Fancy")
assert pet.valid?
- pet.nickname = ''
+ pet.nickname = ""
assert pet.invalid?
end
diff --git a/activerecord/test/cases/validations/presence_validation_test.rb b/activerecord/test/cases/validations/presence_validation_test.rb
index 868d111b8c..13956e26ec 100644
--- a/activerecord/test/cases/validations/presence_validation_test.rb
+++ b/activerecord/test/cases/validations/presence_validation_test.rb
@@ -1,9 +1,9 @@
require "cases/helper"
-require 'models/man'
-require 'models/face'
-require 'models/interest'
-require 'models/speedometer'
-require 'models/dashboard'
+require "models/man"
+require "models/face"
+require "models/interest"
+require "models/speedometer"
+require "models/dashboard"
class PresenceValidationTest < ActiveRecord::TestCase
class Boy < Man; end
@@ -57,7 +57,7 @@ class PresenceValidationTest < ActiveRecord::TestCase
dash = Dashboard.new
# dashboard has to_a method
- def dash.to_a; ['(/)', '(\)']; end
+ def dash.to_a; ["(/)", '(\)']; end
s = speedometer.new
s.dashboard = dash
@@ -71,10 +71,10 @@ class PresenceValidationTest < ActiveRecord::TestCase
Interest.validates_presence_of(:topic)
Interest.validates_presence_of(:abbreviation)
- interest = Interest.create!(topic: 'Thought Leadering', abbreviation: 'tl')
+ interest = Interest.create!(topic: "Thought Leadering", abbreviation: "tl")
assert interest.valid?
- interest.abbreviation = ''
+ interest.abbreviation = ""
assert interest.invalid?
end
diff --git a/activerecord/test/cases/validations/uniqueness_validation_test.rb b/activerecord/test/cases/validations/uniqueness_validation_test.rb
index 4b0a590adb..6d22638592 100644
--- a/activerecord/test/cases/validations/uniqueness_validation_test.rb
+++ b/activerecord/test/cases/validations/uniqueness_validation_test.rb
@@ -1,11 +1,14 @@
require "cases/helper"
-require 'models/topic'
-require 'models/reply'
-require 'models/warehouse_thing'
-require 'models/guid'
-require 'models/event'
-require 'models/dashboard'
-require 'models/uuid_item'
+require "models/topic"
+require "models/reply"
+require "models/warehouse_thing"
+require "models/guid"
+require "models/event"
+require "models/dashboard"
+require "models/uuid_item"
+require "models/author"
+require "models/person"
+require "models/essay"
class Wizard < ActiveRecord::Base
self.abstract_class = true
@@ -26,7 +29,7 @@ end
class ReplyTitle; end
class ReplyWithTitleObject < Reply
- validates_uniqueness_of :content, :scope => :title
+ validates_uniqueness_of :content, scope: :title
def title; ReplyTitle.new; end
end
@@ -38,13 +41,13 @@ end
class BigIntTest < ActiveRecord::Base
INT_MAX_VALUE = 2147483647
- self.table_name = 'cars'
+ self.table_name = "cars"
validates :engines_count, uniqueness: true, inclusion: { in: 0..INT_MAX_VALUE }
end
class BigIntReverseTest < ActiveRecord::Base
INT_MAX_VALUE = 2147483647
- self.table_name = 'cars'
+ self.table_name = "cars"
validates :engines_count, inclusion: { in: 0..INT_MAX_VALUE }
validates :engines_count, uniqueness: true
end
@@ -57,14 +60,14 @@ class TopicWithAfterCreate < Topic
after_create :set_author
def set_author
- update_attributes!(:author_name => "#{title} #{id}")
+ update_attributes!(author_name: "#{title} #{id}")
end
end
class UniquenessValidationTest < ActiveRecord::TestCase
INT_MAX_VALUE = 2147483647
- fixtures :topics, 'warehouse-things'
+ fixtures :topics, "warehouse-things"
repair_validations(Topic, Reply)
@@ -90,7 +93,7 @@ class UniquenessValidationTest < ActiveRecord::TestCase
Topic.alias_attribute :new_title, :title
Topic.validates_uniqueness_of(:new_title)
- topic = Topic.new(new_title: 'abc')
+ topic = Topic.new(new_title: "abc")
assert topic.valid?
end
@@ -107,33 +110,33 @@ class UniquenessValidationTest < ActiveRecord::TestCase
end
def test_validates_uniqueness_with_validates
- Topic.validates :title, :uniqueness => true
- Topic.create!('title' => 'abc')
+ Topic.validates :title, uniqueness: true
+ Topic.create!("title" => "abc")
- t2 = Topic.new('title' => 'abc')
+ t2 = Topic.new("title" => "abc")
assert !t2.valid?
assert t2.errors[:title]
end
def test_validate_uniqueness_when_integer_out_of_range
entry = BigIntTest.create(engines_count: INT_MAX_VALUE + 1)
- assert_equal entry.errors[:engines_count], ['is not included in the list']
+ assert_equal entry.errors[:engines_count], ["is not included in the list"]
end
def test_validate_uniqueness_when_integer_out_of_range_show_order_does_not_matter
entry = BigIntReverseTest.create(engines_count: INT_MAX_VALUE + 1)
- assert_equal entry.errors[:engines_count], ['is not included in the list']
+ assert_equal entry.errors[:engines_count], ["is not included in the list"]
end
def test_validates_uniqueness_with_newline_chars
- Topic.validates_uniqueness_of(:title, :case_sensitive => false)
+ Topic.validates_uniqueness_of(:title, case_sensitive: false)
t = Topic.new("title" => "new\nline")
assert t.save, "Should save t as unique"
end
def test_validate_uniqueness_with_scope
- Reply.validates_uniqueness_of(:content, :scope => "parent_id")
+ Reply.validates_uniqueness_of(:content, scope: "parent_id")
t = Topic.create("title" => "I'm unique!")
@@ -152,7 +155,7 @@ class UniquenessValidationTest < ActiveRecord::TestCase
end
def test_validate_uniqueness_with_object_scope
- Reply.validates_uniqueness_of(:content, :scope => :topic)
+ Reply.validates_uniqueness_of(:content, scope: :topic)
t = Topic.create("title" => "I'm unique!")
@@ -163,6 +166,19 @@ class UniquenessValidationTest < ActiveRecord::TestCase
assert !r2.valid?, "Saving r2 first time"
end
+ def test_validate_uniqueness_with_polymorphic_object_scope
+ Essay.validates_uniqueness_of(:name, scope: :writer)
+
+ a = Author.create(name: "Sergey")
+ p = Person.create(first_name: "Sergey")
+
+ e1 = a.essays.create(name: "Essay")
+ assert e1.valid?, "Saving e1"
+
+ e2 = p.essays.create(name: "Essay")
+ assert e2.valid?, "Saving e2"
+ end
+
def test_validate_uniqueness_with_composed_attribute_scope
r1 = ReplyWithTitleObject.create "title" => "r1", "content" => "hello world"
assert r1.valid?, "Saving r1"
@@ -199,7 +215,7 @@ class UniquenessValidationTest < ActiveRecord::TestCase
end
def test_validate_uniqueness_with_scope_array
- Reply.validates_uniqueness_of(:author_name, :scope => [:author_email_address, :parent_id])
+ Reply.validates_uniqueness_of(:author_name, scope: [:author_email_address, :parent_id])
t = Topic.create("title" => "The earth is actually flat!")
@@ -223,7 +239,7 @@ class UniquenessValidationTest < ActiveRecord::TestCase
end
def test_validate_case_insensitive_uniqueness
- Topic.validates_uniqueness_of(:title, :parent_id, :case_sensitive => false, :allow_nil => true)
+ Topic.validates_uniqueness_of(:title, :parent_id, case_sensitive: false, allow_nil: true)
t = Topic.new("title" => "I'm unique!", :parent_id => 2)
assert t.save, "Should save t as unique"
@@ -256,7 +272,7 @@ class UniquenessValidationTest < ActiveRecord::TestCase
assert t_utf8.save, "Should save t_utf8 as unique"
# If database hasn't UTF-8 character set, this test fails
- if Topic.all.merge!(:select => 'LOWER(title) AS title').find(t_utf8.id).title == "я тоже уникальный!"
+ if Topic.all.merge!(select: "LOWER(title) AS title").find(t_utf8.id).title == "я тоже уникальный!"
t2_utf8 = Topic.new("title" => "я тоже УНИКАЛЬНЫЙ!")
assert !t2_utf8.valid?, "Shouldn't be valid"
assert !t2_utf8.save, "Shouldn't save t2_utf8 as unique"
@@ -264,7 +280,7 @@ class UniquenessValidationTest < ActiveRecord::TestCase
end
def test_validate_case_sensitive_uniqueness_with_special_sql_like_chars
- Topic.validates_uniqueness_of(:title, :case_sensitive => true)
+ Topic.validates_uniqueness_of(:title, case_sensitive: true)
t = Topic.new("title" => "I'm unique!")
assert t.save, "Should save t as unique"
@@ -277,7 +293,7 @@ class UniquenessValidationTest < ActiveRecord::TestCase
end
def test_validate_case_insensitive_uniqueness_with_special_sql_like_chars
- Topic.validates_uniqueness_of(:title, :case_sensitive => false)
+ Topic.validates_uniqueness_of(:title, case_sensitive: false)
t = Topic.new("title" => "I'm unique!")
assert t.save, "Should save t as unique"
@@ -290,7 +306,7 @@ class UniquenessValidationTest < ActiveRecord::TestCase
end
def test_validate_case_sensitive_uniqueness
- Topic.validates_uniqueness_of(:title, :case_sensitive => true, :allow_nil => true)
+ Topic.validates_uniqueness_of(:title, case_sensitive: true, allow_nil: true)
t = Topic.new("title" => "I'm unique!")
assert t.save, "Should save t as unique"
@@ -314,16 +330,16 @@ class UniquenessValidationTest < ActiveRecord::TestCase
end
def test_validate_case_sensitive_uniqueness_with_attribute_passed_as_integer
- Topic.validates_uniqueness_of(:title, :case_sensitive => true)
- Topic.create!('title' => 101)
+ Topic.validates_uniqueness_of(:title, case_sensitive: true)
+ Topic.create!("title" => 101)
- t2 = Topic.new('title' => 101)
+ t2 = Topic.new("title" => 101)
assert !t2.valid?
assert t2.errors[:title]
end
def test_validate_uniqueness_with_non_standard_table_names
- i1 = WarehouseThing.create(:value => 1000)
+ i1 = WarehouseThing.create(value: 1000)
assert !i1.valid?, "i1 should not be valid"
assert i1.errors[:value].any?, "Should not be empty"
end
@@ -331,7 +347,7 @@ class UniquenessValidationTest < ActiveRecord::TestCase
def test_validates_uniqueness_inside_scoping
Topic.validates_uniqueness_of(:title)
- Topic.where(:author_name => "David").scoping do
+ Topic.where(author_name: "David").scoping do
t1 = Topic.new("title" => "I'm unique!", "author_name" => "Mary")
assert t1.save
t2 = Topic.new("title" => "I'm unique!", "author_name" => "David")
@@ -387,29 +403,29 @@ class UniquenessValidationTest < ActiveRecord::TestCase
end
def test_validate_straight_inheritance_uniqueness
- w1 = IneptWizard.create(:name => "Rincewind", :city => "Ankh-Morpork")
+ w1 = IneptWizard.create(name: "Rincewind", city: "Ankh-Morpork")
assert w1.valid?, "Saving w1"
# Should use validation from base class (which is abstract)
- w2 = IneptWizard.new(:name => "Rincewind", :city => "Quirm")
+ w2 = IneptWizard.new(name: "Rincewind", city: "Quirm")
assert !w2.valid?, "w2 shouldn't be valid"
assert w2.errors[:name].any?, "Should have errors for name"
assert_equal ["has already been taken"], w2.errors[:name], "Should have uniqueness message for name"
- w3 = Conjurer.new(:name => "Rincewind", :city => "Quirm")
+ w3 = Conjurer.new(name: "Rincewind", city: "Quirm")
assert !w3.valid?, "w3 shouldn't be valid"
assert w3.errors[:name].any?, "Should have errors for name"
assert_equal ["has already been taken"], w3.errors[:name], "Should have uniqueness message for name"
- w4 = Conjurer.create(:name => "The Amazing Bonko", :city => "Quirm")
+ w4 = Conjurer.create(name: "The Amazing Bonko", city: "Quirm")
assert w4.valid?, "Saving w4"
- w5 = Thaumaturgist.new(:name => "The Amazing Bonko", :city => "Lancre")
+ w5 = Thaumaturgist.new(name: "The Amazing Bonko", city: "Lancre")
assert !w5.valid?, "w5 shouldn't be valid"
assert w5.errors[:name].any?, "Should have errors for name"
assert_equal ["has already been taken"], w5.errors[:name], "Should have uniqueness message for name"
- w6 = Thaumaturgist.new(:name => "Mustrum Ridcully", :city => "Quirm")
+ w6 = Thaumaturgist.new(name: "Mustrum Ridcully", city: "Quirm")
assert !w6.valid?, "w6 shouldn't be valid"
assert w6.errors[:city].any?, "Should have errors for city"
assert_equal ["has already been taken"], w6.errors[:city], "Should have uniqueness message for city"
@@ -439,7 +455,7 @@ class UniquenessValidationTest < ActiveRecord::TestCase
topic = TopicWithUniqEvent.new(event: event)
assert_not topic.valid?
- assert_equal ['has already been taken'], topic.errors[:event]
+ assert_equal ["has already been taken"], topic.errors[:event]
end
def test_validate_uniqueness_on_empty_relation
@@ -501,30 +517,30 @@ class UniquenessValidationTest < ActiveRecord::TestCase
def test_validate_uniqueness_with_after_create_performing_save
TopicWithAfterCreate.validates_uniqueness_of(:title)
- topic = TopicWithAfterCreate.create!(:title => "Title1")
+ topic = TopicWithAfterCreate.create!(title: "Title1")
assert topic.author_name.start_with?("Title1")
- topic2 = TopicWithAfterCreate.new(:title => "Title1")
+ topic2 = TopicWithAfterCreate.new(title: "Title1")
refute topic2.valid?
assert_equal(["has already been taken"], topic2.errors[:title])
end
def test_validate_uniqueness_uuid
skip unless current_adapter?(:PostgreSQLAdapter)
- item = UuidItem.create!(uuid: SecureRandom.uuid, title: 'item1')
- item.update(title: 'item1-title2')
+ item = UuidItem.create!(uuid: SecureRandom.uuid, title: "item1")
+ item.update(title: "item1-title2")
assert_empty item.errors
- item2 = UuidValidatingItem.create!(uuid: SecureRandom.uuid, title: 'item2')
- item2.update(title: 'item2-title2')
+ item2 = UuidValidatingItem.create!(uuid: SecureRandom.uuid, title: "item2")
+ item2.update(title: "item2-title2")
assert_empty item2.errors
end
def test_validate_uniqueness_regular_id
- item = CoolTopic.create!(title: 'MyItem')
+ item = CoolTopic.create!(title: "MyItem")
assert_empty item.errors
- item2 = CoolTopic.new(id: item.id, title: 'MyItem2')
+ item2 = CoolTopic.new(id: item.id, title: "MyItem2")
refute item2.valid?
assert_equal(["has already been taken"], item2.errors[:id])
diff --git a/activerecord/test/cases/validations_test.rb b/activerecord/test/cases/validations_test.rb
index 85e33d2218..5d9aa99497 100644
--- a/activerecord/test/cases/validations_test.rb
+++ b/activerecord/test/cases/validations_test.rb
@@ -1,11 +1,11 @@
require "cases/helper"
-require 'models/topic'
-require 'models/reply'
-require 'models/person'
-require 'models/developer'
-require 'models/computer'
-require 'models/parrot'
-require 'models/company'
+require "models/topic"
+require "models/reply"
+require "models/person"
+require "models/developer"
+require "models/computer"
+require "models/parrot"
+require "models/company"
class ValidationsTest < ActiveRecord::TestCase
fixtures :topics, :developers
@@ -36,7 +36,7 @@ class ValidationsTest < ActiveRecord::TestCase
end
def test_valid_using_special_context
- r = WrongReply.new(:title => "Valid title")
+ r = WrongReply.new(title: "Valid title")
assert !r.valid?(:special_case)
assert_equal "Invalid", r.errors[:author_name].join
@@ -53,7 +53,7 @@ class ValidationsTest < ActiveRecord::TestCase
end
def test_invalid_using_multiple_contexts
- r = WrongReply.new(:title => 'Wrong Create')
+ r = WrongReply.new(title: "Wrong Create")
assert r.invalid?([:special_case, :create])
assert_equal "Invalid", r.errors[:author_name].join
assert_equal "is Wrong Create", r.errors[:title].join
@@ -95,7 +95,7 @@ class ValidationsTest < ActiveRecord::TestCase
assert_raise(ActiveRecord::RecordInvalid) do
WrongReply.new.validate!(:special_case)
end
- r = WrongReply.new(:title => "Valid title", :author_name => "secret", :content => "Good")
+ r = WrongReply.new(title: "Valid title", author_name: "secret", content: "Good")
assert r.validate!(:special_case)
end
@@ -107,7 +107,7 @@ class ValidationsTest < ActiveRecord::TestCase
def test_exception_on_create_bang_with_block
assert_raise(ActiveRecord::RecordInvalid) do
- WrongReply.create!({ "title" => "OK" }) do |r|
+ WrongReply.create!("title" => "OK") do |r|
r.content = nil
end
end
@@ -124,7 +124,7 @@ class ValidationsTest < ActiveRecord::TestCase
def test_save_without_validation
reply = WrongReply.new
assert !reply.save
- assert reply.save(:validate => false)
+ assert reply.save(validate: false)
end
def test_validates_acceptance_of_with_non_existent_table
@@ -156,22 +156,20 @@ class ValidationsTest < ActiveRecord::TestCase
end
def test_numericality_validation_with_mutation
- Topic.class_eval do
+ klass = Class.new(Topic) do
attribute :wibble, :string
validates_numericality_of :wibble, only_integer: true
end
- topic = Topic.new(wibble: '123-4567')
- topic.wibble.gsub!('-', '')
+ topic = klass.new(wibble: "123-4567")
+ topic.wibble.gsub!("-", "")
assert topic.valid?
- ensure
- Topic.reset_column_information
end
def test_acceptance_validator_doesnt_require_db_connection
klass = Class.new(ActiveRecord::Base) do
- self.table_name = 'posts'
+ self.table_name = "posts"
end
klass.reset_column_information
diff --git a/activerecord/test/cases/view_test.rb b/activerecord/test/cases/view_test.rb
index f3c2d2f30e..d055968a56 100644
--- a/activerecord/test/cases/view_test.rb
+++ b/activerecord/test/cases/view_test.rb
@@ -11,20 +11,21 @@ module ViewBehavior
end
class Ebook < ActiveRecord::Base
+ self.table_name = "ebooks'"
self.primary_key = "id"
end
def setup
super
@connection = ActiveRecord::Base.connection
- create_view "ebooks", <<-SQL
+ create_view "ebooks'", <<-SQL
SELECT id, name, status FROM books WHERE format = 'ebook'
SQL
end
def teardown
super
- drop_view "ebooks"
+ drop_view "ebooks'"
end
def test_reading
@@ -44,8 +45,7 @@ module ViewBehavior
def test_table_exists
view_name = Ebook.table_name
- # TODO: switch this assertion around once we changed #tables to not return views.
- ActiveSupport::Deprecation.silence { assert @connection.table_exists?(view_name), "'#{view_name}' table should exist" }
+ assert_not @connection.table_exists?(view_name), "'#{view_name}' table should not exist"
end
def test_views_ara_valid_data_sources
@@ -60,157 +60,161 @@ module ViewBehavior
end
def test_attributes
- assert_equal({"id" => 2, "name" => "Ruby for Rails", "status" => 0},
+ assert_equal({ "id" => 2, "name" => "Ruby for Rails", "status" => 0 },
Ebook.first.attributes)
end
def test_does_not_assume_id_column_as_primary_key
model = Class.new(ActiveRecord::Base) do
- self.table_name = "ebooks"
+ self.table_name = "ebooks'"
end
assert_nil model.primary_key
end
def test_does_not_dump_view_as_table
- schema = dump_table_schema "ebooks"
- assert_no_match %r{create_table "ebooks"}, schema
+ schema = dump_table_schema "ebooks'"
+ assert_no_match %r{create_table "ebooks'"}, schema
end
-end
-
-if ActiveRecord::Base.connection.supports_views?
-class ViewWithPrimaryKeyTest < ActiveRecord::TestCase
- include ViewBehavior
private
- def create_view(name, query)
- @connection.execute "CREATE VIEW #{name} AS #{query}"
- end
-
- def drop_view(name)
- @connection.execute "DROP VIEW #{name}" if @connection.view_exists? name
- end
+ def quote_table_name(name)
+ @connection.quote_table_name(name)
+ end
end
-class ViewWithoutPrimaryKeyTest < ActiveRecord::TestCase
- include SchemaDumpingHelper
- fixtures :books
-
- class Paperback < ActiveRecord::Base; end
-
- setup do
- @connection = ActiveRecord::Base.connection
- @connection.execute <<-SQL
- CREATE VIEW paperbacks
- AS SELECT name, status FROM books WHERE format = 'paperback'
- SQL
- end
-
- teardown do
- @connection.execute "DROP VIEW paperbacks" if @connection.view_exists? "paperbacks"
- end
+if ActiveRecord::Base.connection.supports_views?
+ class ViewWithPrimaryKeyTest < ActiveRecord::TestCase
+ include ViewBehavior
- def test_reading
- books = Paperback.all
- assert_equal ["Agile Web Development with Rails"], books.map(&:name)
- end
+ private
+ def create_view(name, query)
+ @connection.execute "CREATE VIEW #{quote_table_name(name)} AS #{query}"
+ end
- def test_views
- assert_equal [Paperback.table_name], @connection.views
- end
-
- def test_view_exists
- view_name = Paperback.table_name
- assert @connection.view_exists?(view_name), "'#{view_name}' view should exist"
+ def drop_view(name)
+ @connection.execute "DROP VIEW #{quote_table_name(name)}" if @connection.view_exists? name
+ end
end
- def test_table_exists
- view_name = Paperback.table_name
- # TODO: switch this assertion around once we changed #tables to not return views.
- ActiveSupport::Deprecation.silence { assert @connection.table_exists?(view_name), "'#{view_name}' table should exist" }
- end
+ class ViewWithoutPrimaryKeyTest < ActiveRecord::TestCase
+ include SchemaDumpingHelper
+ fixtures :books
- def test_column_definitions
- assert_equal([["name", :string],
- ["status", :integer]], Paperback.columns.map { |c| [c.name, c.type] })
- end
+ class Paperback < ActiveRecord::Base; end
- def test_attributes
- assert_equal({"name" => "Agile Web Development with Rails", "status" => 2},
- Paperback.first.attributes)
- end
+ setup do
+ @connection = ActiveRecord::Base.connection
+ @connection.execute <<-SQL
+ CREATE VIEW paperbacks
+ AS SELECT name, status FROM books WHERE format = 'paperback'
+ SQL
+ end
- def test_does_not_have_a_primary_key
- assert_nil Paperback.primary_key
- end
+ teardown do
+ @connection.execute "DROP VIEW paperbacks" if @connection.view_exists? "paperbacks"
+ end
- def test_does_not_dump_view_as_table
- schema = dump_table_schema "paperbacks"
- assert_no_match %r{create_table "paperbacks"}, schema
- end
-end
+ def test_reading
+ books = Paperback.all
+ assert_equal ["Agile Web Development with Rails"], books.map(&:name)
+ end
-# sqlite dose not support CREATE, INSERT, and DELETE for VIEW
-if current_adapter?(:Mysql2Adapter, :PostgreSQLAdapter)
-class UpdateableViewTest < ActiveRecord::TestCase
- self.use_transactional_tests = false
- fixtures :books
+ def test_views
+ assert_equal [Paperback.table_name], @connection.views
+ end
- class PrintedBook < ActiveRecord::Base
- self.primary_key = "id"
- end
+ def test_view_exists
+ view_name = Paperback.table_name
+ assert @connection.view_exists?(view_name), "'#{view_name}' view should exist"
+ end
- setup do
- @connection = ActiveRecord::Base.connection
- @connection.execute <<-SQL
- CREATE VIEW printed_books
- AS SELECT id, name, status, format FROM books WHERE format = 'paperback'
- SQL
- end
+ def test_table_exists
+ view_name = Paperback.table_name
+ assert_not @connection.table_exists?(view_name), "'#{view_name}' table should not exist"
+ end
- teardown do
- @connection.execute "DROP VIEW printed_books" if @connection.view_exists? "printed_books"
- end
+ def test_column_definitions
+ assert_equal([["name", :string],
+ ["status", :integer]], Paperback.columns.map { |c| [c.name, c.type] })
+ end
- def test_update_record
- book = PrintedBook.first
- book.name = "AWDwR"
- book.save!
- book.reload
- assert_equal "AWDwR", book.name
- end
+ def test_attributes
+ assert_equal({ "name" => "Agile Web Development with Rails", "status" => 2 },
+ Paperback.first.attributes)
+ end
- def test_insert_record
- PrintedBook.create! name: "Rails in Action", status: 0, format: "paperback"
+ def test_does_not_have_a_primary_key
+ assert_nil Paperback.primary_key
+ end
- new_book = PrintedBook.last
- assert_equal "Rails in Action", new_book.name
+ def test_does_not_dump_view_as_table
+ schema = dump_table_schema "paperbacks"
+ assert_no_match %r{create_table "paperbacks"}, schema
+ end
end
- def test_update_record_to_fail_view_conditions
- book = PrintedBook.first
- book.format = "ebook"
- book.save!
-
- assert_raises ActiveRecord::RecordNotFound do
- book.reload
+ # sqlite dose not support CREATE, INSERT, and DELETE for VIEW
+ if current_adapter?(:Mysql2Adapter, :PostgreSQLAdapter)
+ class UpdateableViewTest < ActiveRecord::TestCase
+ self.use_transactional_tests = false
+ fixtures :books
+
+ class PrintedBook < ActiveRecord::Base
+ self.primary_key = "id"
+ end
+
+ setup do
+ @connection = ActiveRecord::Base.connection
+ @connection.execute <<-SQL
+ CREATE VIEW printed_books
+ AS SELECT id, name, status, format FROM books WHERE format = 'paperback'
+ SQL
+ end
+
+ teardown do
+ @connection.execute "DROP VIEW printed_books" if @connection.view_exists? "printed_books"
+ end
+
+ def test_update_record
+ book = PrintedBook.first
+ book.name = "AWDwR"
+ book.save!
+ book.reload
+ assert_equal "AWDwR", book.name
+ end
+
+ def test_insert_record
+ PrintedBook.create! name: "Rails in Action", status: 0, format: "paperback"
+
+ new_book = PrintedBook.last
+ assert_equal "Rails in Action", new_book.name
+ end
+
+ def test_update_record_to_fail_view_conditions
+ book = PrintedBook.first
+ book.format = "ebook"
+ book.save!
+
+ assert_raises ActiveRecord::RecordNotFound do
+ book.reload
+ end
+ end
end
- end
-end
-end # end fo `if current_adapter?(:Mysql2Adapter, :PostgreSQLAdapter)`
-end # end fo `if ActiveRecord::Base.connection.supports_views?`
+ end # end of `if current_adapter?(:Mysql2Adapter, :PostgreSQLAdapter)`
+end # end of `if ActiveRecord::Base.connection.supports_views?`
if ActiveRecord::Base.connection.respond_to?(:supports_materialized_views?) &&
ActiveRecord::Base.connection.supports_materialized_views?
-class MaterializedViewTest < ActiveRecord::PostgreSQLTestCase
- include ViewBehavior
+ class MaterializedViewTest < ActiveRecord::PostgreSQLTestCase
+ include ViewBehavior
- private
- def create_view(name, query)
- @connection.execute "CREATE MATERIALIZED VIEW #{name} AS #{query}"
- end
+ private
+ def create_view(name, query)
+ @connection.execute "CREATE MATERIALIZED VIEW #{quote_table_name(name)} AS #{query}"
+ end
- def drop_view(name)
- @connection.execute "DROP MATERIALIZED VIEW #{name}" if @connection.view_exists? name
+ def drop_view(name)
+ @connection.execute "DROP MATERIALIZED VIEW #{quote_table_name(name)}" if @connection.view_exists? name
+ end
end
end
-end
diff --git a/activerecord/test/cases/yaml_serialization_test.rb b/activerecord/test/cases/yaml_serialization_test.rb
index d1c9a00786..1571b31329 100644
--- a/activerecord/test/cases/yaml_serialization_test.rb
+++ b/activerecord/test/cases/yaml_serialization_test.rb
@@ -1,15 +1,15 @@
-require 'cases/helper'
-require 'models/topic'
-require 'models/reply'
-require 'models/post'
-require 'models/author'
+require "cases/helper"
+require "models/topic"
+require "models/reply"
+require "models/post"
+require "models/author"
class YamlSerializationTest < ActiveRecord::TestCase
fixtures :topics, :authors, :posts
def test_to_yaml_with_time_with_zone_should_not_raise_exception
with_timezone_config aware_attributes: true, zone: "Pacific Time (US & Canada)" do
- topic = Topic.new(:written_on => DateTime.now)
+ topic = Topic.new(written_on: DateTime.now)
assert_nothing_raised { topic.to_yaml }
end
end
@@ -22,8 +22,8 @@ class YamlSerializationTest < ActiveRecord::TestCase
end
def test_roundtrip_serialized_column
- topic = Topic.new(:content => {:omg=>:lol})
- assert_equal({:omg=>:lol}, YAML.load(YAML.dump(topic)).content)
+ topic = Topic.new(content: { omg: :lol })
+ assert_equal({ omg: :lol }, YAML.load(YAML.dump(topic)).content)
end
def test_psych_roundtrip
@@ -67,16 +67,16 @@ class YamlSerializationTest < ActiveRecord::TestCase
assert_not topic.new_record?, "Saved records are not new"
assert_not YAML.load(YAML.dump(topic)).new_record?, "Saved record should not be new after deserialization"
- topic = Topic.select('title').last
+ topic = Topic.select("title").last
assert_not topic.new_record?, "Loaded records without ID are not new"
assert_not YAML.load(YAML.dump(topic)).new_record?, "Record should not be new after deserialization"
end
def test_types_of_virtual_columns_are_not_changed_on_round_trip
- author = Author.select('authors.*, count(posts.id) as posts_count')
+ author = Author.select("authors.*, count(posts.id) as posts_count")
.joins(:posts)
- .group('authors.id')
+ .group("authors.id")
.first
dumped = YAML.load(YAML.dump(author))
@@ -88,14 +88,14 @@ class YamlSerializationTest < ActiveRecord::TestCase
coder = {}
Topic.first.encode_with(coder)
- assert coder['active_record_yaml_version']
+ assert coder["active_record_yaml_version"]
end
def test_deserializing_rails_41_yaml
topic = YAML.load(yaml_fixture("rails_4_1"))
assert topic.new_record?
- assert_equal nil, topic.id
+ assert_nil topic.id
assert_equal "The First Topic", topic.title
assert_equal({ omg: :lol }, topic.content)
end
@@ -121,11 +121,11 @@ class YamlSerializationTest < ActiveRecord::TestCase
private
- def yaml_fixture(file_name)
- path = File.expand_path(
- "../../support/yaml_compatibility_fixtures/#{file_name}.yml",
- __FILE__
- )
- File.read(path)
- end
+ def yaml_fixture(file_name)
+ path = File.expand_path(
+ "../../support/yaml_compatibility_fixtures/#{file_name}.yml",
+ __FILE__
+ )
+ File.read(path)
+ end
end
diff --git a/activerecord/test/config.example.yml b/activerecord/test/config.example.yml
index 58e2d45748..4bcb2aeea6 100644
--- a/activerecord/test/config.example.yml
+++ b/activerecord/test/config.example.yml
@@ -77,6 +77,9 @@ connections:
postgresql:
arunit:
min_messages: warning
+ arunit_without_prepared_statements:
+ min_messages: warning
+ prepared_statements: false
arunit2:
min_messages: warning
diff --git a/activerecord/test/fixtures/all/namespaced/accounts.yml b/activerecord/test/fixtures/all/namespaced/accounts.yml
new file mode 100644
index 0000000000..9e341a15af
--- /dev/null
+++ b/activerecord/test/fixtures/all/namespaced/accounts.yml
@@ -0,0 +1,2 @@
+signals37:
+ name: 37signals
diff --git a/activerecord/test/fixtures/books.yml b/activerecord/test/fixtures/books.yml
index a304fba399..b3625ee72e 100644
--- a/activerecord/test/fixtures/books.yml
+++ b/activerecord/test/fixtures/books.yml
@@ -9,6 +9,7 @@ awdr:
author_visibility: :visible
illustrator_visibility: :visible
font_size: :medium
+ difficulty: :medium
rfr:
author_id: 1
diff --git a/activerecord/test/fixtures/naked/yml/courses_with_invalid_key.yml b/activerecord/test/fixtures/naked/yml/courses_with_invalid_key.yml
new file mode 100644
index 0000000000..6f9da79b45
--- /dev/null
+++ b/activerecord/test/fixtures/naked/yml/courses_with_invalid_key.yml
@@ -0,0 +1,3 @@
+one:
+ id: 1
+two: ['not a hash']
diff --git a/activerecord/test/fixtures/other_dogs.yml b/activerecord/test/fixtures/other_dogs.yml
new file mode 100644
index 0000000000..b576861929
--- /dev/null
+++ b/activerecord/test/fixtures/other_dogs.yml
@@ -0,0 +1,2 @@
+lassie:
+ id: 1
diff --git a/activerecord/test/migrations/decimal/1_give_me_big_numbers.rb b/activerecord/test/migrations/decimal/1_give_me_big_numbers.rb
index 549647de86..43c79bc20b 100644
--- a/activerecord/test/migrations/decimal/1_give_me_big_numbers.rb
+++ b/activerecord/test/migrations/decimal/1_give_me_big_numbers.rb
@@ -1,10 +1,10 @@
class GiveMeBigNumbers < ActiveRecord::Migration::Current
def self.up
create_table :big_numbers do |table|
- table.column :bank_balance, :decimal, :precision => 10, :scale => 2
- table.column :big_bank_balance, :decimal, :precision => 15, :scale => 2
- table.column :world_population, :decimal, :precision => 10
- table.column :my_house_population, :decimal, :precision => 2
+ table.column :bank_balance, :decimal, precision: 10, scale: 2
+ table.column :big_bank_balance, :decimal, precision: 15, scale: 2
+ table.column :world_population, :decimal, precision: 10
+ table.column :my_house_population, :decimal, precision: 2
table.column :value_of_e, :decimal
end
end
diff --git a/activerecord/test/migrations/magic/1_currencies_have_symbols.rb b/activerecord/test/migrations/magic/1_currencies_have_symbols.rb
index 53b263bf55..d4b0e6cd95 100644
--- a/activerecord/test/migrations/magic/1_currencies_have_symbols.rb
+++ b/activerecord/test/migrations/magic/1_currencies_have_symbols.rb
@@ -2,8 +2,8 @@
class CurrenciesHaveSymbols < ActiveRecord::Migration::Current
def self.up
- # We use for default currency symbol
- add_column "currencies", "symbol", :string, :default => ""
+ # We use € for default currency symbol
+ add_column "currencies", "symbol", :string, default: "€"
end
def self.down
diff --git a/activerecord/test/migrations/missing/4_innocent_jointable.rb b/activerecord/test/migrations/missing/4_innocent_jointable.rb
index 20fe183777..bd3bf21576 100644
--- a/activerecord/test/migrations/missing/4_innocent_jointable.rb
+++ b/activerecord/test/migrations/missing/4_innocent_jointable.rb
@@ -1,6 +1,6 @@
class InnocentJointable < ActiveRecord::Migration::Current
def self.up
- create_table("people_reminders", :id => false) do |t|
+ create_table("people_reminders", id: false) do |t|
t.column :reminder_id, :integer
t.column :person_id, :integer
end
diff --git a/activerecord/test/migrations/valid/3_innocent_jointable.rb b/activerecord/test/migrations/valid/3_innocent_jointable.rb
index 20fe183777..bd3bf21576 100644
--- a/activerecord/test/migrations/valid/3_innocent_jointable.rb
+++ b/activerecord/test/migrations/valid/3_innocent_jointable.rb
@@ -1,6 +1,6 @@
class InnocentJointable < ActiveRecord::Migration::Current
def self.up
- create_table("people_reminders", :id => false) do |t|
+ create_table("people_reminders", id: false) do |t|
t.column :reminder_id, :integer
t.column :person_id, :integer
end
diff --git a/activerecord/test/migrations/valid_with_subdirectories/sub1/3_innocent_jointable.rb b/activerecord/test/migrations/valid_with_subdirectories/sub1/3_innocent_jointable.rb
index 20fe183777..bd3bf21576 100644
--- a/activerecord/test/migrations/valid_with_subdirectories/sub1/3_innocent_jointable.rb
+++ b/activerecord/test/migrations/valid_with_subdirectories/sub1/3_innocent_jointable.rb
@@ -1,6 +1,6 @@
class InnocentJointable < ActiveRecord::Migration::Current
def self.up
- create_table("people_reminders", :id => false) do |t|
+ create_table("people_reminders", id: false) do |t|
t.column :reminder_id, :integer
t.column :person_id, :integer
end
diff --git a/activerecord/test/migrations/valid_with_timestamps/20100301010101_valid_with_timestamps_innocent_jointable.rb b/activerecord/test/migrations/valid_with_timestamps/20100301010101_valid_with_timestamps_innocent_jointable.rb
index bf934576c9..be24de6d70 100644
--- a/activerecord/test/migrations/valid_with_timestamps/20100301010101_valid_with_timestamps_innocent_jointable.rb
+++ b/activerecord/test/migrations/valid_with_timestamps/20100301010101_valid_with_timestamps_innocent_jointable.rb
@@ -1,6 +1,6 @@
class ValidWithTimestampsInnocentJointable < ActiveRecord::Migration::Current
def self.up
- create_table("people_reminders", :id => false) do |t|
+ create_table("people_reminders", id: false) do |t|
t.column :reminder_id, :integer
t.column :person_id, :integer
end
diff --git a/activerecord/test/models/admin.rb b/activerecord/test/models/admin.rb
index a38e3f4846..bc3ce23447 100644
--- a/activerecord/test/models/admin.rb
+++ b/activerecord/test/models/admin.rb
@@ -1,5 +1,5 @@
module Admin
def self.table_name_prefix
- 'admin_'
+ "admin_"
end
end
diff --git a/activerecord/test/models/admin/user.rb b/activerecord/test/models/admin/user.rb
index 48a110bd23..a76e4b6795 100644
--- a/activerecord/test/models/admin/user.rb
+++ b/activerecord/test/models/admin/user.rb
@@ -15,26 +15,26 @@ class Admin::User < ActiveRecord::Base
belongs_to :account
store :params, accessors: [ :token ], coder: YAML
- store :settings, :accessors => [ :color, :homepage ]
+ store :settings, accessors: [ :color, :homepage ]
store_accessor :settings, :favorite_food
- store :preferences, :accessors => [ :remember_login ]
- store :json_data, :accessors => [ :height, :weight ], :coder => Coder.new
- store :json_data_empty, :accessors => [ :is_a_good_guy ], :coder => Coder.new
+ store :preferences, accessors: [ :remember_login ]
+ store :json_data, accessors: [ :height, :weight ], coder: Coder.new
+ store :json_data_empty, accessors: [ :is_a_good_guy ], coder: Coder.new
def phone_number
- read_store_attribute(:settings, :phone_number).gsub(/(\d{3})(\d{3})(\d{4})/,'(\1) \2-\3')
+ read_store_attribute(:settings, :phone_number).gsub(/(\d{3})(\d{3})(\d{4})/, '(\1) \2-\3')
end
def phone_number=(value)
- write_store_attribute(:settings, :phone_number, value && value.gsub(/[^\d]/,''))
+ write_store_attribute(:settings, :phone_number, value && value.gsub(/[^\d]/, ""))
end
def color
- super || 'red'
+ super || "red"
end
def color=(value)
- value = 'blue' unless %w(black red green blue).include?(value)
+ value = "blue" unless %w(black red green blue).include?(value)
super
end
end
diff --git a/activerecord/test/models/aircraft.rb b/activerecord/test/models/aircraft.rb
index c4404a8094..ebd42ff824 100644
--- a/activerecord/test/models/aircraft.rb
+++ b/activerecord/test/models/aircraft.rb
@@ -1,5 +1,5 @@
class Aircraft < ActiveRecord::Base
self.pluralize_table_names = false
- has_many :engines, :foreign_key => "car_id"
+ has_many :engines, foreign_key: "car_id"
has_many :wheels, as: :wheelable
end
diff --git a/activerecord/test/models/author.rb b/activerecord/test/models/author.rb
index 38b983eda0..fab613afd1 100644
--- a/activerecord/test/models/author.rb
+++ b/activerecord/test/models/author.rb
@@ -2,145 +2,144 @@ class Author < ActiveRecord::Base
has_many :posts
has_many :serialized_posts
has_one :post
- has_many :very_special_comments, :through => :posts
- has_many :posts_with_comments, -> { includes(:comments) }, :class_name => "Post"
- has_many :popular_grouped_posts, -> { includes(:comments).group("type").having("SUM(comments_count) > 1").select("type") }, :class_name => "Post"
- has_many :posts_with_comments_sorted_by_comment_id, -> { includes(:comments).order('comments.id') }, :class_name => "Post"
- has_many :posts_sorted_by_id_limited, -> { order('posts.id').limit(1) }, :class_name => "Post"
- has_many :posts_with_categories, -> { includes(:categories) }, :class_name => "Post"
- has_many :posts_with_comments_and_categories, -> { includes(:comments, :categories).order("posts.id") }, :class_name => "Post"
- has_many :posts_with_special_categorizations, :class_name => 'PostWithSpecialCategorization'
- has_one :post_about_thinking, -> { where("posts.title like '%thinking%'") }, :class_name => 'Post'
- has_one :post_about_thinking_with_last_comment, -> { where("posts.title like '%thinking%'").includes(:last_comment) }, :class_name => 'Post'
+ has_many :very_special_comments, through: :posts
+ has_many :posts_with_comments, -> { includes(:comments) }, class_name: "Post"
+ has_many :popular_grouped_posts, -> { includes(:comments).group("type").having("SUM(comments_count) > 1").select("type") }, class_name: "Post"
+ has_many :posts_with_comments_sorted_by_comment_id, -> { includes(:comments).order("comments.id") }, class_name: "Post"
+ has_many :posts_sorted_by_id_limited, -> { order("posts.id").limit(1) }, class_name: "Post"
+ has_many :posts_with_categories, -> { includes(:categories) }, class_name: "Post"
+ has_many :posts_with_comments_and_categories, -> { includes(:comments, :categories).order("posts.id") }, class_name: "Post"
+ has_many :posts_with_special_categorizations, class_name: "PostWithSpecialCategorization"
+ has_one :post_about_thinking, -> { where("posts.title like '%thinking%'") }, class_name: "Post"
+ has_one :post_about_thinking_with_last_comment, -> { where("posts.title like '%thinking%'").includes(:last_comment) }, class_name: "Post"
has_many :comments, through: :posts do
def ratings
Rating.joins(:comment).merge(self)
end
end
- has_many :comments_containing_the_letter_e, :through => :posts, :source => :comments
- has_many :comments_with_order_and_conditions, -> { order('comments.body').where("comments.body like 'Thank%'") }, :through => :posts, :source => :comments
- has_many :comments_with_include, -> { includes(:post) }, :through => :posts, :source => :comments
+ has_many :comments_containing_the_letter_e, through: :posts, source: :comments
+ has_many :comments_with_order_and_conditions, -> { order("comments.body").where("comments.body like 'Thank%'") }, through: :posts, source: :comments
+ has_many :comments_with_include, -> { includes(:post) }, through: :posts, source: :comments
has_many :first_posts
- has_many :comments_on_first_posts, -> { order('posts.id desc, comments.id asc') }, :through => :first_posts, :source => :comments
+ has_many :comments_on_first_posts, -> { order("posts.id desc, comments.id asc") }, through: :first_posts, source: :comments
has_one :first_post
- has_one :comment_on_first_post, -> { order('posts.id desc, comments.id asc') }, :through => :first_post, :source => :comments
+ has_one :comment_on_first_post, -> { order("posts.id desc, comments.id asc") }, through: :first_post, source: :comments
- has_many :thinking_posts, -> { where(:title => 'So I was thinking') }, :dependent => :delete_all, :class_name => 'Post'
- has_many :welcome_posts, -> { where(:title => 'Welcome to the weblog') }, :class_name => 'Post'
+ has_many :thinking_posts, -> { where(title: "So I was thinking") }, dependent: :delete_all, class_name: "Post"
+ has_many :welcome_posts, -> { where(title: "Welcome to the weblog") }, class_name: "Post"
has_many :welcome_posts_with_one_comment,
- -> { where(title: 'Welcome to the weblog').where('comments_count = ?', 1) },
- class_name: 'Post'
+ -> { where(title: "Welcome to the weblog").where("comments_count = ?", 1) },
+ class_name: "Post"
has_many :welcome_posts_with_comments,
- -> { where(title: 'Welcome to the weblog').where(Post.arel_table[:comments_count].gt(0)) },
- class_name: 'Post'
+ -> { where(title: "Welcome to the weblog").where(Post.arel_table[:comments_count].gt(0)) },
+ class_name: "Post"
- has_many :comments_desc, -> { order('comments.id DESC') }, :through => :posts, :source => :comments
- has_many :funky_comments, :through => :posts, :source => :comments
- has_many :ordered_uniq_comments, -> { distinct.order('comments.id') }, :through => :posts, :source => :comments
- has_many :ordered_uniq_comments_desc, -> { distinct.order('comments.id DESC') }, :through => :posts, :source => :comments
- has_many :readonly_comments, -> { readonly }, :through => :posts, :source => :comments
+ has_many :comments_desc, -> { order("comments.id DESC") }, through: :posts, source: :comments
+ has_many :funky_comments, through: :posts, source: :comments
+ has_many :ordered_uniq_comments, -> { distinct.order("comments.id") }, through: :posts, source: :comments
+ has_many :ordered_uniq_comments_desc, -> { distinct.order("comments.id DESC") }, through: :posts, source: :comments
+ has_many :readonly_comments, -> { readonly }, through: :posts, source: :comments
has_many :special_posts
- has_many :special_post_comments, :through => :special_posts, :source => :comments
- has_many :special_posts_with_default_scope, :class_name => 'SpecialPostWithDefaultScope'
-
- has_many :sti_posts, :class_name => 'StiPost'
- has_many :sti_post_comments, :through => :sti_posts, :source => :comments
-
- has_many :special_nonexistent_posts, -> { where("posts.body = 'nonexistent'") }, :class_name => "SpecialPost"
- has_many :special_nonexistent_post_comments, -> { where('comments.post_id' => 0) }, :through => :special_nonexistent_posts, :source => :comments
- has_many :nonexistent_comments, :through => :posts
-
- has_many :hello_posts, -> { where "posts.body = 'hello'" }, :class_name => "Post"
- has_many :hello_post_comments, :through => :hello_posts, :source => :comments
- has_many :posts_with_no_comments, -> { where('comments.id' => nil).includes(:comments) }, :class_name => 'Post'
-
- has_many :hello_posts_with_hash_conditions, -> { where(:body => 'hello') }, :class_name => "Post"
- has_many :hello_post_comments_with_hash_conditions, :through =>
-:hello_posts_with_hash_conditions, :source => :comments
-
- has_many :other_posts, :class_name => "Post"
- has_many :posts_with_callbacks, :class_name => "Post", :before_add => :log_before_adding,
- :after_add => :log_after_adding,
- :before_remove => :log_before_removing,
- :after_remove => :log_after_removing
- has_many :posts_with_proc_callbacks, :class_name => "Post",
- :before_add => Proc.new {|o, r| o.post_log << "before_adding#{r.id || '<new>'}"},
- :after_add => Proc.new {|o, r| o.post_log << "after_adding#{r.id || '<new>'}"},
- :before_remove => Proc.new {|o, r| o.post_log << "before_removing#{r.id}"},
- :after_remove => Proc.new {|o, r| o.post_log << "after_removing#{r.id}"}
- has_many :posts_with_multiple_callbacks, :class_name => "Post",
- :before_add => [:log_before_adding, Proc.new {|o, r| o.post_log << "before_adding_proc#{r.id || '<new>'}"}],
- :after_add => [:log_after_adding, Proc.new {|o, r| o.post_log << "after_adding_proc#{r.id || '<new>'}"}]
- has_many :unchangeable_posts, :class_name => "Post", :before_add => :raise_exception, :after_add => :log_after_adding
+ has_many :special_post_comments, through: :special_posts, source: :comments
+ has_many :special_posts_with_default_scope, class_name: "SpecialPostWithDefaultScope"
+
+ has_many :sti_posts, class_name: "StiPost"
+ has_many :sti_post_comments, through: :sti_posts, source: :comments
+
+ has_many :special_nonexistent_posts, -> { where("posts.body = 'nonexistent'") }, class_name: "SpecialPost"
+ has_many :special_nonexistent_post_comments, -> { where("comments.post_id" => 0) }, through: :special_nonexistent_posts, source: :comments
+ has_many :nonexistent_comments, through: :posts
+
+ has_many :hello_posts, -> { where "posts.body = 'hello'" }, class_name: "Post"
+ has_many :hello_post_comments, through: :hello_posts, source: :comments
+ has_many :posts_with_no_comments, -> { where("comments.id" => nil).includes(:comments) }, class_name: "Post"
+
+ has_many :hello_posts_with_hash_conditions, -> { where(body: "hello") }, class_name: "Post"
+ has_many :hello_post_comments_with_hash_conditions, through: :hello_posts_with_hash_conditions, source: :comments
+
+ has_many :other_posts, class_name: "Post"
+ has_many :posts_with_callbacks, class_name: "Post", before_add: :log_before_adding,
+ after_add: :log_after_adding,
+ before_remove: :log_before_removing,
+ after_remove: :log_after_removing
+ has_many :posts_with_proc_callbacks, class_name: "Post",
+ before_add: Proc.new { |o, r| o.post_log << "before_adding#{r.id || '<new>'}" },
+ after_add: Proc.new { |o, r| o.post_log << "after_adding#{r.id || '<new>'}" },
+ before_remove: Proc.new { |o, r| o.post_log << "before_removing#{r.id}" },
+ after_remove: Proc.new { |o, r| o.post_log << "after_removing#{r.id}" }
+ has_many :posts_with_multiple_callbacks, class_name: "Post",
+ before_add: [:log_before_adding, Proc.new { |o, r| o.post_log << "before_adding_proc#{r.id || '<new>'}" }],
+ after_add: [:log_after_adding, Proc.new { |o, r| o.post_log << "after_adding_proc#{r.id || '<new>'}" }]
+ has_many :unchangeable_posts, class_name: "Post", before_add: :raise_exception, after_add: :log_after_adding
has_many :categorizations
- has_many :categories, :through => :categorizations
- has_many :named_categories, :through => :categorizations
+ has_many :categories, through: :categorizations
+ has_many :named_categories, through: :categorizations
has_many :special_categorizations
- has_many :special_categories, :through => :special_categorizations, :source => :category
- has_one :special_category, :through => :special_categorizations, :source => :category
+ has_many :special_categories, through: :special_categorizations, source: :category
+ has_one :special_category, through: :special_categorizations, source: :category
- has_many :categories_like_general, -> { where(:name => 'General') }, :through => :categorizations, :source => :category, :class_name => 'Category'
+ has_many :categories_like_general, -> { where(name: "General") }, through: :categorizations, source: :category, class_name: "Category"
- has_many :categorized_posts, :through => :categorizations, :source => :post
- has_many :unique_categorized_posts, -> { distinct }, :through => :categorizations, :source => :post
+ has_many :categorized_posts, through: :categorizations, source: :post
+ has_many :unique_categorized_posts, -> { distinct }, through: :categorizations, source: :post
- has_many :nothings, :through => :kateggorisatons, :class_name => 'Category'
+ has_many :nothings, through: :kateggorisatons, class_name: "Category"
has_many :author_favorites
- has_many :favorite_authors, -> { order('name') }, :through => :author_favorites
+ has_many :favorite_authors, -> { order("name") }, through: :author_favorites
- has_many :taggings, :through => :posts, :source => :taggings
- has_many :taggings_2, :through => :posts, :source => :tagging
- has_many :tags, :through => :posts
- has_many :post_categories, :through => :posts, :source => :categories
- has_many :tagging_tags, :through => :taggings, :source => :tag
+ has_many :taggings, through: :posts, source: :taggings
+ has_many :taggings_2, through: :posts, source: :tagging
+ has_many :tags, through: :posts
+ has_many :post_categories, through: :posts, source: :categories
+ has_many :tagging_tags, through: :taggings, source: :tag
- has_many :similar_posts, -> { distinct }, :through => :tags, :source => :tagged_posts
- has_many :distinct_tags, -> { select("DISTINCT tags.*").order("tags.name") }, :through => :posts, :source => :tags
+ has_many :similar_posts, -> { distinct }, through: :tags, source: :tagged_posts
+ has_many :distinct_tags, -> { select("DISTINCT tags.*").order("tags.name") }, through: :posts, source: :tags
- has_many :tags_with_primary_key, :through => :posts
+ has_many :tags_with_primary_key, through: :posts
has_many :books
- has_many :subscriptions, :through => :books
- has_many :subscribers, -> { order("subscribers.nick") }, :through => :subscriptions
- has_many :distinct_subscribers, -> { select("DISTINCT subscribers.*").order("subscribers.nick") }, :through => :subscriptions, :source => :subscriber
+ has_many :subscriptions, through: :books
+ has_many :subscribers, -> { order("subscribers.nick") }, through: :subscriptions
+ has_many :distinct_subscribers, -> { select("DISTINCT subscribers.*").order("subscribers.nick") }, through: :subscriptions, source: :subscriber
- has_one :essay, :primary_key => :name, :as => :writer
- has_one :essay_category, :through => :essay, :source => :category
- has_one :essay_owner, :through => :essay, :source => :owner
+ has_one :essay, primary_key: :name, as: :writer
+ has_one :essay_category, through: :essay, source: :category
+ has_one :essay_owner, through: :essay, source: :owner
- has_one :essay_2, :primary_key => :name, :class_name => 'Essay', :foreign_key => :author_id
- has_one :essay_category_2, :through => :essay_2, :source => :category
+ has_one :essay_2, primary_key: :name, class_name: "Essay", foreign_key: :author_id
+ has_one :essay_category_2, through: :essay_2, source: :category
- has_many :essays, :primary_key => :name, :as => :writer
- has_many :essay_categories, :through => :essays, :source => :category
- has_many :essay_owners, :through => :essays, :source => :owner
+ has_many :essays, primary_key: :name, as: :writer
+ has_many :essay_categories, through: :essays, source: :category
+ has_many :essay_owners, through: :essays, source: :owner
- has_many :essays_2, :primary_key => :name, :class_name => 'Essay', :foreign_key => :author_id
- has_many :essay_categories_2, :through => :essays_2, :source => :category
+ has_many :essays_2, primary_key: :name, class_name: "Essay", foreign_key: :author_id
+ has_many :essay_categories_2, through: :essays_2, source: :category
- belongs_to :owned_essay, :primary_key => :name, :class_name => 'Essay'
- has_one :owned_essay_category, :through => :owned_essay, :source => :category
+ belongs_to :owned_essay, primary_key: :name, class_name: "Essay"
+ has_one :owned_essay_category, through: :owned_essay, source: :category
- belongs_to :author_address, :dependent => :destroy
- belongs_to :author_address_extra, :dependent => :delete, :class_name => "AuthorAddress"
+ belongs_to :author_address, dependent: :destroy
+ belongs_to :author_address_extra, dependent: :delete, class_name: "AuthorAddress"
- has_many :category_post_comments, :through => :categories, :source => :post_comments
+ has_many :category_post_comments, through: :categories, source: :post_comments
- has_many :misc_posts, -> { where(:posts => { :title => ['misc post by bob', 'misc post by mary'] }) }, :class_name => 'Post'
- has_many :misc_post_first_blue_tags, :through => :misc_posts, :source => :first_blue_tags
+ has_many :misc_posts, -> { where(posts: { title: ["misc post by bob", "misc post by mary"] }) }, class_name: "Post"
+ has_many :misc_post_first_blue_tags, through: :misc_posts, source: :first_blue_tags
- has_many :misc_post_first_blue_tags_2, -> { where(:posts => { :title => ['misc post by bob', 'misc post by mary'] }) },
- :through => :posts, :source => :first_blue_tags_2
+ has_many :misc_post_first_blue_tags_2, -> { where(posts: { title: ["misc post by bob", "misc post by mary"] }) },
+ through: :posts, source: :first_blue_tags_2
- has_many :posts_with_default_include, :class_name => 'PostWithDefaultInclude'
- has_many :comments_on_posts_with_default_include, :through => :posts_with_default_include, :source => :comments
+ has_many :posts_with_default_include, class_name: "PostWithDefaultInclude"
+ has_many :comments_on_posts_with_default_include, through: :posts_with_default_include, source: :comments
has_many :posts_with_signature, ->(record) { where("posts.title LIKE ?", "%by #{record.name.downcase}%") }, class_name: "Post"
@@ -205,5 +204,5 @@ end
class AuthorFavorite < ActiveRecord::Base
belongs_to :author
- belongs_to :favorite_author, :class_name => "Author"
+ belongs_to :favorite_author, class_name: "Author"
end
diff --git a/activerecord/test/models/bird.rb b/activerecord/test/models/bird.rb
index 2a51d903b8..24b839135d 100644
--- a/activerecord/test/models/bird.rb
+++ b/activerecord/test/models/bird.rb
@@ -5,7 +5,7 @@ class Bird < ActiveRecord::Base
accepts_nested_attributes_for :pirate
attr_accessor :cancel_save_from_callback
- before_save :cancel_save_callback_method, :if => :cancel_save_from_callback
+ before_save :cancel_save_callback_method, if: :cancel_save_from_callback
def cancel_save_callback_method
throw(:abort)
end
diff --git a/activerecord/test/models/book.rb b/activerecord/test/models/book.rb
index e43e5c3901..17bf3fbcb4 100644
--- a/activerecord/test/models/book.rb
+++ b/activerecord/test/models/book.rb
@@ -1,20 +1,21 @@
class Book < ActiveRecord::Base
has_many :authors
- has_many :citations, :foreign_key => 'book1_id'
+ has_many :citations, foreign_key: "book1_id"
has_many :references, -> { distinct }, through: :citations, source: :reference_of
has_many :subscriptions
has_many :subscribers, through: :subscriptions
enum status: [:proposed, :written, :published]
- enum read_status: {unread: 0, reading: 2, read: 3}
+ enum read_status: { unread: 0, reading: 2, read: 3 }
enum nullable_status: [:single, :married]
enum language: [:english, :spanish, :french], _prefix: :in
enum author_visibility: [:visible, :invisible], _prefix: true
enum illustrator_visibility: [:visible, :invisible], _prefix: true
enum font_size: [:small, :medium, :large], _prefix: :with, _suffix: true
- enum cover: { hard: 'hard', soft: 'soft' }
+ enum difficulty: [:easy, :medium, :hard], _suffix: :to_read
+ enum cover: { hard: "hard", soft: "soft" }
def published!
super
diff --git a/activerecord/test/models/boolean.rb b/activerecord/test/models/boolean.rb
index 7bae22e5f9..0da228aac2 100644
--- a/activerecord/test/models/boolean.rb
+++ b/activerecord/test/models/boolean.rb
@@ -1,2 +1,5 @@
class Boolean < ActiveRecord::Base
+ def has_fun
+ super
+ end
end
diff --git a/activerecord/test/models/bulb.rb b/activerecord/test/models/bulb.rb
index dc0296305a..113d21cb84 100644
--- a/activerecord/test/models/bulb.rb
+++ b/activerecord/test/models/bulb.rb
@@ -1,6 +1,6 @@
class Bulb < ActiveRecord::Base
- default_scope { where(:name => 'defaulty') }
- belongs_to :car, :touch => true
+ default_scope { where(name: "defaulty") }
+ belongs_to :car, touch: true
scope :awesome, -> { where(frickinawesome: true) }
attr_reader :scope_after_initialize, :attributes_after_initialize
@@ -35,7 +35,7 @@ class CustomBulb < Bulb
after_initialize :set_awesomeness
def set_awesomeness
- self.frickinawesome = true if name == 'Dude'
+ self.frickinawesome = true if name == "Dude"
end
end
@@ -50,9 +50,3 @@ class FailedBulb < Bulb
throw(:abort)
end
end
-
-class TrickyBulb < Bulb
- after_create do |record|
- record.car.bulbs.to_a
- end
-end
diff --git a/activerecord/test/models/car.rb b/activerecord/test/models/car.rb
index 0f37e9a289..92bff7ff96 100644
--- a/activerecord/test/models/car.rb
+++ b/activerecord/test/models/car.rb
@@ -1,29 +1,29 @@
class Car < ActiveRecord::Base
has_many :bulbs
has_many :all_bulbs, -> { unscope where: :name }, class_name: "Bulb"
- has_many :funky_bulbs, class_name: 'FunkyBulb', dependent: :destroy
- has_many :failed_bulbs, class_name: 'FailedBulb', dependent: :destroy
- has_many :foo_bulbs, -> { where(:name => 'foo') }, :class_name => "Bulb"
+ has_many :funky_bulbs, class_name: "FunkyBulb", dependent: :destroy
+ has_many :failed_bulbs, class_name: "FailedBulb", dependent: :destroy
+ has_many :foo_bulbs, -> { where(name: "foo") }, class_name: "Bulb"
has_many :awesome_bulbs, -> { awesome }, class_name: "Bulb"
has_one :bulb
has_many :tyres
- has_many :engines, :dependent => :destroy, inverse_of: :my_car
- has_many :wheels, :as => :wheelable, :dependent => :destroy
+ has_many :engines, dependent: :destroy, inverse_of: :my_car
+ has_many :wheels, as: :wheelable, dependent: :destroy
- has_many :price_estimates, :as => :estimate_of
+ has_many :price_estimates, as: :estimate_of
scope :incl_tyres, -> { includes(:tyres) }
scope :incl_engines, -> { includes(:engines) }
- scope :order_using_new_style, -> { order('name asc') }
+ scope :order_using_new_style, -> { order("name asc") }
end
class CoolCar < Car
- default_scope { order('name desc') }
+ default_scope { order("name desc") }
end
class FastCar < Car
- default_scope { order('name desc') }
+ default_scope { order("name desc") }
end
diff --git a/activerecord/test/models/categorization.rb b/activerecord/test/models/categorization.rb
index 4cd67c970a..b99383d0b1 100644
--- a/activerecord/test/models/categorization.rb
+++ b/activerecord/test/models/categorization.rb
@@ -1,18 +1,18 @@
class Categorization < ActiveRecord::Base
belongs_to :post
belongs_to :category, counter_cache: true
- belongs_to :named_category, :class_name => 'Category', :foreign_key => :named_category_name, :primary_key => :name
+ belongs_to :named_category, class_name: "Category", foreign_key: :named_category_name, primary_key: :name
belongs_to :author
- has_many :post_taggings, :through => :author, :source => :taggings
+ has_many :post_taggings, through: :author, source: :taggings
- belongs_to :author_using_custom_pk, :class_name => 'Author', :foreign_key => :author_id, :primary_key => :author_address_extra_id
- has_many :authors_using_custom_pk, :class_name => 'Author', :foreign_key => :id, :primary_key => :category_id
+ belongs_to :author_using_custom_pk, class_name: "Author", foreign_key: :author_id, primary_key: :author_address_extra_id
+ has_many :authors_using_custom_pk, class_name: "Author", foreign_key: :id, primary_key: :category_id
end
class SpecialCategorization < ActiveRecord::Base
- self.table_name = 'categorizations'
- default_scope { where(:special => true) }
+ self.table_name = "categorizations"
+ default_scope { where(special: true) }
belongs_to :author
belongs_to :category
diff --git a/activerecord/test/models/category.rb b/activerecord/test/models/category.rb
index 272223e1d8..e8654dca01 100644
--- a/activerecord/test/models/category.rb
+++ b/activerecord/test/models/category.rb
@@ -1,34 +1,34 @@
class Category < ActiveRecord::Base
has_and_belongs_to_many :posts
- has_and_belongs_to_many :special_posts, :class_name => "Post"
- has_and_belongs_to_many :other_posts, :class_name => "Post"
- has_and_belongs_to_many :posts_with_authors_sorted_by_author_id, -> { includes(:authors).order("authors.id") }, :class_name => "Post"
+ has_and_belongs_to_many :special_posts, class_name: "Post"
+ has_and_belongs_to_many :other_posts, class_name: "Post"
+ has_and_belongs_to_many :posts_with_authors_sorted_by_author_id, -> { includes(:authors).order("authors.id") }, class_name: "Post"
has_and_belongs_to_many :select_testing_posts,
- -> { select 'posts.*, 1 as correctness_marker' },
- :class_name => 'Post',
- :foreign_key => 'category_id',
- :association_foreign_key => 'post_id'
+ -> { select "posts.*, 1 as correctness_marker" },
+ class_name: "Post",
+ foreign_key: "category_id",
+ association_foreign_key: "post_id"
has_and_belongs_to_many :post_with_conditions,
- -> { where :title => 'Yet Another Testing Title' },
- :class_name => 'Post'
+ -> { where title: "Yet Another Testing Title" },
+ class_name: "Post"
- has_and_belongs_to_many :popular_grouped_posts, -> { group("posts.type").having("sum(comments.post_id) > 2").includes(:comments) }, :class_name => "Post"
- has_and_belongs_to_many :posts_grouped_by_title, -> { group("title").select("title") }, :class_name => "Post"
+ has_and_belongs_to_many :popular_grouped_posts, -> { group("posts.type").having("sum(comments.post_id) > 2").includes(:comments) }, class_name: "Post"
+ has_and_belongs_to_many :posts_grouped_by_title, -> { group("title").select("title") }, class_name: "Post"
def self.what_are_you
- 'a category...'
+ "a category..."
end
has_many :categorizations
has_many :special_categorizations
- has_many :post_comments, :through => :posts, :source => :comments
+ has_many :post_comments, through: :posts, source: :comments
- has_many :authors, :through => :categorizations
- has_many :authors_with_select, -> { select 'authors.*, categorizations.post_id' }, :through => :categorizations, :source => :author
+ has_many :authors, through: :categorizations
+ has_many :authors_with_select, -> { select "authors.*, categorizations.post_id" }, through: :categorizations, source: :author
- scope :general, -> { where(:name => 'General') }
+ scope :general, -> { where(name: "General") }
end
class SpecialCategory < Category
diff --git a/activerecord/test/models/citation.rb b/activerecord/test/models/citation.rb
index 3d87eb795c..7d06387f56 100644
--- a/activerecord/test/models/citation.rb
+++ b/activerecord/test/models/citation.rb
@@ -1,3 +1,3 @@
class Citation < ActiveRecord::Base
- belongs_to :reference_of, :class_name => "Book", :foreign_key => :book2_id
+ belongs_to :reference_of, class_name: "Book", foreign_key: :book2_id
end
diff --git a/activerecord/test/models/club.rb b/activerecord/test/models/club.rb
index 6ceafe5858..49d7b24a3b 100644
--- a/activerecord/test/models/club.rb
+++ b/activerecord/test/models/club.rb
@@ -1,23 +1,23 @@
class Club < ActiveRecord::Base
has_one :membership
- has_many :memberships, :inverse_of => false
- has_many :members, :through => :memberships
+ has_many :memberships, inverse_of: false
+ has_many :members, through: :memberships
has_one :sponsor
- has_one :sponsored_member, :through => :sponsor, :source => :sponsorable, :source_type => "Member"
+ has_one :sponsored_member, through: :sponsor, source: :sponsorable, source_type: "Member"
belongs_to :category
has_many :favourites, -> { where(memberships: { favourite: true }) }, through: :memberships, source: :member
private
- def private_method
- "I'm sorry sir, this is a *private* club, not a *pirate* club"
- end
+ def private_method
+ "I'm sorry sir, this is a *private* club, not a *pirate* club"
+ end
end
class SuperClub < ActiveRecord::Base
self.table_name = "clubs"
- has_many :memberships, class_name: 'SuperMembership', foreign_key: 'club_id'
+ has_many :memberships, class_name: "SuperMembership", foreign_key: "club_id"
has_many :members, through: :memberships
end
diff --git a/activerecord/test/models/college.rb b/activerecord/test/models/college.rb
index 501af4a8dd..c9dbe1ecc2 100644
--- a/activerecord/test/models/college.rb
+++ b/activerecord/test/models/college.rb
@@ -1,5 +1,5 @@
-require_dependency 'models/arunit2_model'
-require 'active_support/core_ext/object/with_options'
+require_dependency "models/arunit2_model"
+require "active_support/core_ext/object/with_options"
class College < ARUnit2Model
has_many :courses
diff --git a/activerecord/test/models/comment.rb b/activerecord/test/models/comment.rb
index dcc5c5a310..a4b81d56e0 100644
--- a/activerecord/test/models/comment.rb
+++ b/activerecord/test/models/comment.rb
@@ -1,26 +1,26 @@
class Comment < ActiveRecord::Base
- scope :limit_by, lambda {|l| limit(l) }
+ scope :limit_by, lambda { |l| limit(l) }
scope :containing_the_letter_e, -> { where("comments.body LIKE '%e%'") }
scope :not_again, -> { where("comments.body NOT LIKE '%again%'") }
- scope :for_first_post, -> { where(:post_id => 1) }
+ scope :for_first_post, -> { where(post_id: 1) }
scope :for_first_author, -> { joins(:post).where("posts.author_id" => 1) }
scope :created, -> { all }
- belongs_to :post, :counter_cache => true
+ belongs_to :post, counter_cache: true
belongs_to :author, polymorphic: true
belongs_to :resource, polymorphic: true
belongs_to :developer
has_many :ratings
- belongs_to :first_post, :foreign_key => :post_id
+ belongs_to :first_post, foreign_key: :post_id
belongs_to :special_post_with_default_scope, foreign_key: :post_id
- has_many :children, :class_name => 'Comment', :foreign_key => :parent_id
- belongs_to :parent, :class_name => 'Comment', :counter_cache => :children_count
+ has_many :children, class_name: "Comment", foreign_key: :parent_id
+ belongs_to :parent, class_name: "Comment", counter_cache: :children_count
def self.what_are_you
- 'a comment...'
+ "a comment..."
end
def self.search_by_type(q)
@@ -55,6 +55,6 @@ class CommentThatAutomaticallyAltersPostBody < Comment
end
class CommentWithDefaultScopeReferencesAssociation < Comment
- default_scope ->{ includes(:developer).order('developers.name').references(:developer) }
+ default_scope -> { includes(:developer).order("developers.name").references(:developer) }
belongs_to :developer
end
diff --git a/activerecord/test/models/company.rb b/activerecord/test/models/company.rb
index 1dcd9fc21e..4561b3132b 100644
--- a/activerecord/test/models/company.rb
+++ b/activerecord/test/models/company.rb
@@ -7,13 +7,13 @@ class Company < AbstractCompany
validates_presence_of :name
- has_one :dummy_account, :foreign_key => "firm_id", :class_name => "Account"
+ has_one :dummy_account, foreign_key: "firm_id", class_name: "Account"
has_many :contracts
- has_many :developers, :through => :contracts
+ has_many :developers, through: :contracts
scope :of_first_firm, lambda {
- joins(:account => :firm).
- where('firms.id' => 1)
+ joins(account: :firm).
+ where("firms.id" => 1)
}
def arbitrary_method
@@ -22,12 +22,12 @@ class Company < AbstractCompany
private
- def private_method
- "I am Jack's innermost fears and aspirations"
- end
+ def private_method
+ "I am Jack's innermost fears and aspirations"
+ end
- class SpecialCo < Company
- end
+ class SpecialCo < Company
+ end
end
module Namespaced
@@ -35,7 +35,7 @@ module Namespaced
end
class Firm < ::Company
- has_many :clients, :class_name => 'Namespaced::Client'
+ has_many :clients, class_name: "Namespaced::Client"
end
class Client < ::Company
@@ -45,45 +45,45 @@ end
class Firm < Company
to_param :name
- has_many :clients, -> { order "id" }, :dependent => :destroy, :before_remove => :log_before_remove, :after_remove => :log_after_remove
- has_many :unsorted_clients, :class_name => "Client"
- has_many :unsorted_clients_with_symbol, :class_name => :Client
- has_many :clients_sorted_desc, -> { order "id DESC" }, :class_name => "Client"
- has_many :clients_of_firm, -> { order "id" }, :foreign_key => "client_of", :class_name => "Client", :inverse_of => :firm
- has_many :clients_ordered_by_name, -> { order "name" }, :class_name => "Client"
- has_many :unvalidated_clients_of_firm, :foreign_key => "client_of", :class_name => "Client", :validate => false
- has_many :dependent_clients_of_firm, -> { order "id" }, :foreign_key => "client_of", :class_name => "Client", :dependent => :destroy
- has_many :exclusively_dependent_clients_of_firm, -> { order "id" }, :foreign_key => "client_of", :class_name => "Client", :dependent => :delete_all
- has_many :limited_clients, -> { limit 1 }, :class_name => "Client"
- has_many :clients_with_interpolated_conditions, ->(firm) { where "rating > #{firm.rating}" }, :class_name => "Client"
- has_many :clients_like_ms, -> { where("name = 'Microsoft'").order("id") }, :class_name => "Client"
- has_many :clients_like_ms_with_hash_conditions, -> { where(:name => 'Microsoft').order("id") }, :class_name => "Client"
- has_many :plain_clients, :class_name => 'Client'
- has_many :clients_using_primary_key, :class_name => 'Client',
- :primary_key => 'name', :foreign_key => 'firm_name'
- has_many :clients_using_primary_key_with_delete_all, :class_name => 'Client',
- :primary_key => 'name', :foreign_key => 'firm_name', :dependent => :delete_all
- has_many :clients_grouped_by_firm_id, -> { group("firm_id").select("firm_id") }, :class_name => "Client"
- has_many :clients_grouped_by_name, -> { group("name").select("name") }, :class_name => "Client"
-
- has_one :account, :foreign_key => "firm_id", :dependent => :destroy, :validate => true
- has_one :unvalidated_account, :foreign_key => "firm_id", :class_name => 'Account', :validate => false
- has_one :account_with_select, -> { select("id, firm_id") }, :foreign_key => "firm_id", :class_name=>'Account'
- has_one :readonly_account, -> { readonly }, :foreign_key => "firm_id", :class_name => "Account"
+ has_many :clients, -> { order "id" }, dependent: :destroy, before_remove: :log_before_remove, after_remove: :log_after_remove
+ has_many :unsorted_clients, class_name: "Client"
+ has_many :unsorted_clients_with_symbol, class_name: :Client
+ has_many :clients_sorted_desc, -> { order "id DESC" }, class_name: "Client"
+ has_many :clients_of_firm, -> { order "id" }, foreign_key: "client_of", class_name: "Client", inverse_of: :firm
+ has_many :clients_ordered_by_name, -> { order "name" }, class_name: "Client"
+ has_many :unvalidated_clients_of_firm, foreign_key: "client_of", class_name: "Client", validate: false
+ has_many :dependent_clients_of_firm, -> { order "id" }, foreign_key: "client_of", class_name: "Client", dependent: :destroy
+ has_many :exclusively_dependent_clients_of_firm, -> { order "id" }, foreign_key: "client_of", class_name: "Client", dependent: :delete_all
+ has_many :limited_clients, -> { limit 1 }, class_name: "Client"
+ has_many :clients_with_interpolated_conditions, ->(firm) { where "rating > #{firm.rating}" }, class_name: "Client"
+ has_many :clients_like_ms, -> { where("name = 'Microsoft'").order("id") }, class_name: "Client"
+ has_many :clients_like_ms_with_hash_conditions, -> { where(name: "Microsoft").order("id") }, class_name: "Client"
+ has_many :plain_clients, class_name: "Client"
+ has_many :clients_using_primary_key, class_name: "Client",
+ primary_key: "name", foreign_key: "firm_name"
+ has_many :clients_using_primary_key_with_delete_all, class_name: "Client",
+ primary_key: "name", foreign_key: "firm_name", dependent: :delete_all
+ has_many :clients_grouped_by_firm_id, -> { group("firm_id").select("firm_id") }, class_name: "Client"
+ has_many :clients_grouped_by_name, -> { group("name").select("name") }, class_name: "Client"
+
+ has_one :account, foreign_key: "firm_id", dependent: :destroy, validate: true
+ has_one :unvalidated_account, foreign_key: "firm_id", class_name: "Account", validate: false
+ has_one :account_with_select, -> { select("id, firm_id") }, foreign_key: "firm_id", class_name: "Account"
+ has_one :readonly_account, -> { readonly }, foreign_key: "firm_id", class_name: "Account"
# added order by id as in fixtures there are two accounts for Rails Core
# Oracle tests were failing because of that as the second fixture was selected
- has_one :account_using_primary_key, -> { order('id') }, :primary_key => "firm_id", :class_name => "Account"
- has_one :account_using_foreign_and_primary_keys, :foreign_key => "firm_name", :primary_key => "name", :class_name => "Account"
- has_one :account_with_inexistent_foreign_key, class_name: 'Account', foreign_key: "inexistent"
- has_one :deletable_account, :foreign_key => "firm_id", :class_name => "Account", :dependent => :delete
+ has_one :account_using_primary_key, -> { order("id") }, primary_key: "firm_id", class_name: "Account"
+ has_one :account_using_foreign_and_primary_keys, foreign_key: "firm_name", primary_key: "name", class_name: "Account"
+ has_one :account_with_inexistent_foreign_key, class_name: "Account", foreign_key: "inexistent"
+ has_one :deletable_account, foreign_key: "firm_id", class_name: "Account", dependent: :delete
- has_one :account_limit_500_with_hash_conditions, -> { where :credit_limit => 500 }, :foreign_key => "firm_id", :class_name => "Account"
+ has_one :account_limit_500_with_hash_conditions, -> { where credit_limit: 500 }, foreign_key: "firm_id", class_name: "Account"
- has_one :unautosaved_account, :foreign_key => "firm_id", :class_name => 'Account', :autosave => false
+ has_one :unautosaved_account, foreign_key: "firm_id", class_name: "Account", autosave: false
has_many :accounts
- has_many :unautosaved_accounts, :foreign_key => "firm_id", :class_name => 'Account', :autosave => false
+ has_many :unautosaved_accounts, foreign_key: "firm_id", class_name: "Account", autosave: false
- has_many :association_with_references, -> { references(:foo) }, :class_name => 'Client'
+ has_many :association_with_references, -> { references(:foo) }, class_name: "Client"
has_one :lead_developer, class_name: "Developer"
has_many :projects
@@ -103,32 +103,32 @@ class Firm < Company
end
class DependentFirm < Company
- has_one :account, :foreign_key => "firm_id", :dependent => :nullify
- has_many :companies, :foreign_key => 'client_of', :dependent => :nullify
- has_one :company, :foreign_key => 'client_of', :dependent => :nullify
+ has_one :account, foreign_key: "firm_id", dependent: :nullify
+ has_many :companies, foreign_key: "client_of", dependent: :nullify
+ has_one :company, foreign_key: "client_of", dependent: :nullify
end
class RestrictedWithExceptionFirm < Company
- has_one :account, -> { order("id") }, :foreign_key => "firm_id", :dependent => :restrict_with_exception
- has_many :companies, -> { order("id") }, :foreign_key => 'client_of', :dependent => :restrict_with_exception
+ has_one :account, -> { order("id") }, foreign_key: "firm_id", dependent: :restrict_with_exception
+ has_many :companies, -> { order("id") }, foreign_key: "client_of", dependent: :restrict_with_exception
end
class RestrictedWithErrorFirm < Company
- has_one :account, -> { order("id") }, :foreign_key => "firm_id", :dependent => :restrict_with_error
- has_many :companies, -> { order("id") }, :foreign_key => 'client_of', :dependent => :restrict_with_error
+ has_one :account, -> { order("id") }, foreign_key: "firm_id", dependent: :restrict_with_error
+ has_many :companies, -> { order("id") }, foreign_key: "client_of", dependent: :restrict_with_error
end
class Client < Company
- belongs_to :firm, :foreign_key => "client_of"
- belongs_to :firm_with_basic_id, :class_name => "Firm", :foreign_key => "firm_id"
- belongs_to :firm_with_select, -> { select("id") }, :class_name => "Firm", :foreign_key => "firm_id"
- belongs_to :firm_with_other_name, :class_name => "Firm", :foreign_key => "client_of"
- belongs_to :firm_with_condition, -> { where "1 = ?", 1 }, :class_name => "Firm", :foreign_key => "client_of"
- belongs_to :firm_with_primary_key, :class_name => "Firm", :primary_key => "name", :foreign_key => "firm_name"
- belongs_to :firm_with_primary_key_symbols, :class_name => "Firm", :primary_key => :name, :foreign_key => :firm_name
- belongs_to :readonly_firm, -> { readonly }, :class_name => "Firm", :foreign_key => "firm_id"
- belongs_to :bob_firm, -> { where :name => "Bob" }, :class_name => "Firm", :foreign_key => "client_of"
- has_many :accounts, :through => :firm, :source => :accounts
+ belongs_to :firm, foreign_key: "client_of"
+ belongs_to :firm_with_basic_id, class_name: "Firm", foreign_key: "firm_id"
+ belongs_to :firm_with_select, -> { select("id") }, class_name: "Firm", foreign_key: "firm_id"
+ belongs_to :firm_with_other_name, class_name: "Firm", foreign_key: "client_of"
+ belongs_to :firm_with_condition, -> { where "1 = ?", 1 }, class_name: "Firm", foreign_key: "client_of"
+ belongs_to :firm_with_primary_key, class_name: "Firm", primary_key: "name", foreign_key: "firm_name"
+ belongs_to :firm_with_primary_key_symbols, class_name: "Firm", primary_key: :name, foreign_key: :firm_name
+ belongs_to :readonly_firm, -> { readonly }, class_name: "Firm", foreign_key: "firm_id"
+ belongs_to :bob_firm, -> { where name: "Bob" }, class_name: "Firm", foreign_key: "client_of"
+ has_many :accounts, through: :firm, source: :accounts
belongs_to :account
validate do
@@ -151,7 +151,7 @@ class Client < Company
# is calling client.destroy, deleting from the database, or setting
# foreign keys to NULL.
def self.destroyed_client_ids
- @destroyed_client_ids ||= Hash.new { |h,k| h[k] = [] }
+ @destroyed_client_ids ||= Hash.new { |h, k| h[k] = [] }
end
before_destroy do |client|
@@ -181,9 +181,9 @@ class Client < Company
end
class ExclusivelyDependentFirm < Company
- has_one :account, :foreign_key => "firm_id", :dependent => :delete
- has_many :dependent_sanitized_conditional_clients_of_firm, -> { order("id").where("name = 'BigShot Inc.'") }, :foreign_key => "client_of", :class_name => "Client", :dependent => :delete_all
- has_many :dependent_conditional_clients_of_firm, -> { order("id").where("name = ?", 'BigShot Inc.') }, :foreign_key => "client_of", :class_name => "Client", :dependent => :delete_all
+ has_one :account, foreign_key: "firm_id", dependent: :delete
+ has_many :dependent_sanitized_conditional_clients_of_firm, -> { order("id").where("name = 'BigShot Inc.'") }, foreign_key: "client_of", class_name: "Client", dependent: :delete_all
+ has_many :dependent_conditional_clients_of_firm, -> { order("id").where("name = ?", "BigShot Inc.") }, foreign_key: "client_of", class_name: "Client", dependent: :delete_all
end
class SpecialClient < Client
@@ -193,18 +193,18 @@ class VerySpecialClient < SpecialClient
end
class Account < ActiveRecord::Base
- belongs_to :firm, :class_name => 'Company'
- belongs_to :unautosaved_firm, :foreign_key => "firm_id", :class_name => "Firm", :autosave => false
+ belongs_to :firm, class_name: "Company"
+ belongs_to :unautosaved_firm, foreign_key: "firm_id", class_name: "Firm", autosave: false
alias_attribute :available_credit, :credit_limit
def self.destroyed_account_ids
- @destroyed_account_ids ||= Hash.new { |h,k| h[k] = [] }
+ @destroyed_account_ids ||= Hash.new { |h, k| h[k] = [] }
end
# Test private kernel method through collection proxy using has_many.
def self.open
- where('firm_name = ?', '37signals')
+ where("firm_name = ?", "37signals")
end
before_destroy do |account|
@@ -218,13 +218,13 @@ class Account < ActiveRecord::Base
protected
- def check_empty_credit_limit
- errors.add("credit_limit", :blank) if credit_limit.blank?
- end
+ def check_empty_credit_limit
+ errors.add("credit_limit", :blank) if credit_limit.blank?
+ end
private
- def private_method
- "Sir, yes sir!"
- end
+ def private_method
+ "Sir, yes sir!"
+ end
end
diff --git a/activerecord/test/models/company_in_module.rb b/activerecord/test/models/company_in_module.rb
index bf0a0d1c3e..0782c1eff4 100644
--- a/activerecord/test/models/company_in_module.rb
+++ b/activerecord/test/models/company_in_module.rb
@@ -1,4 +1,4 @@
-require 'active_support/core_ext/object/with_options'
+require "active_support/core_ext/object/with_options"
module MyApplication
module Business
@@ -6,23 +6,23 @@ module MyApplication
end
class Firm < Company
- has_many :clients, -> { order("id") }, :dependent => :destroy
- has_many :clients_sorted_desc, -> { order("id DESC") }, :class_name => "Client"
- has_many :clients_of_firm, -> { order "id" }, :foreign_key => "client_of", :class_name => "Client"
- has_many :clients_like_ms, -> { where("name = 'Microsoft'").order("id") }, :class_name => "Client"
- has_one :account, :class_name => 'MyApplication::Billing::Account', :dependent => :destroy
+ has_many :clients, -> { order("id") }, dependent: :destroy
+ has_many :clients_sorted_desc, -> { order("id DESC") }, class_name: "Client"
+ has_many :clients_of_firm, -> { order "id" }, foreign_key: "client_of", class_name: "Client"
+ has_many :clients_like_ms, -> { where("name = 'Microsoft'").order("id") }, class_name: "Client"
+ has_one :account, class_name: "MyApplication::Billing::Account", dependent: :destroy
end
class Client < Company
- belongs_to :firm, :foreign_key => "client_of"
- belongs_to :firm_with_other_name, :class_name => "Firm", :foreign_key => "client_of"
+ belongs_to :firm, foreign_key: "client_of"
+ belongs_to :firm_with_other_name, class_name: "Firm", foreign_key: "client_of"
class Contact < ActiveRecord::Base; end
end
class Developer < ActiveRecord::Base
has_and_belongs_to_many :projects
- validates_length_of :name, :within => (3..20)
+ validates_length_of :name, within: (3..20)
end
class Project < ActiveRecord::Base
@@ -31,14 +31,14 @@ module MyApplication
module Prefixed
def self.table_name_prefix
- 'prefixed_'
+ "prefixed_"
end
class Company < ActiveRecord::Base
end
class Firm < Company
- self.table_name = 'companies'
+ self.table_name = "companies"
end
module Nested
@@ -49,14 +49,14 @@ module MyApplication
module Suffixed
def self.table_name_suffix
- '_suffixed'
+ "_suffixed"
end
class Company < ActiveRecord::Base
end
class Firm < Company
- self.table_name = 'companies'
+ self.table_name = "companies"
end
module Nested
@@ -68,31 +68,31 @@ module MyApplication
module Billing
class Firm < ActiveRecord::Base
- self.table_name = 'companies'
+ self.table_name = "companies"
end
module Nested
class Firm < ActiveRecord::Base
- self.table_name = 'companies'
+ self.table_name = "companies"
end
end
class Account < ActiveRecord::Base
- with_options(:foreign_key => :firm_id) do |i|
- i.belongs_to :firm, :class_name => 'MyApplication::Business::Firm'
- i.belongs_to :qualified_billing_firm, :class_name => 'MyApplication::Billing::Firm'
- i.belongs_to :unqualified_billing_firm, :class_name => 'Firm'
- i.belongs_to :nested_qualified_billing_firm, :class_name => 'MyApplication::Billing::Nested::Firm'
- i.belongs_to :nested_unqualified_billing_firm, :class_name => 'Nested::Firm'
+ with_options(foreign_key: :firm_id) do |i|
+ i.belongs_to :firm, class_name: "MyApplication::Business::Firm"
+ i.belongs_to :qualified_billing_firm, class_name: "MyApplication::Billing::Firm"
+ i.belongs_to :unqualified_billing_firm, class_name: "Firm"
+ i.belongs_to :nested_qualified_billing_firm, class_name: "MyApplication::Billing::Nested::Firm"
+ i.belongs_to :nested_unqualified_billing_firm, class_name: "Nested::Firm"
end
validate :check_empty_credit_limit
- protected
+ private
- def check_empty_credit_limit
- errors.add("credit_card", :blank) if credit_card.blank?
- end
+ def check_empty_credit_limit
+ errors.add("credit_card", :blank) if credit_card.blank?
+ end
end
end
end
diff --git a/activerecord/test/models/computer.rb b/activerecord/test/models/computer.rb
index cc8deb1b2b..1c9856e1af 100644
--- a/activerecord/test/models/computer.rb
+++ b/activerecord/test/models/computer.rb
@@ -1,3 +1,3 @@
class Computer < ActiveRecord::Base
- belongs_to :developer, :foreign_key=>'developer'
+ belongs_to :developer, foreign_key: "developer"
end
diff --git a/activerecord/test/models/contact.rb b/activerecord/test/models/contact.rb
index 9f2f69e1ee..47bbbbfd8b 100644
--- a/activerecord/test/models/contact.rb
+++ b/activerecord/test/models/contact.rb
@@ -1,11 +1,11 @@
module ContactFakeColumns
def self.extended(base)
base.class_eval do
- establish_connection(:adapter => 'fake')
+ establish_connection(adapter: "fake")
connection.data_sources = [table_name]
connection.primary_keys = {
- table_name => 'id'
+ table_name => "id"
}
column :id, :integer
@@ -19,7 +19,7 @@ module ContactFakeColumns
serialize :preferences
- belongs_to :alternative, :class_name => 'Contact'
+ belongs_to :alternative, class_name: "Contact"
end
end
@@ -37,5 +37,5 @@ class ContactSti < ActiveRecord::Base
extend ContactFakeColumns
column :type, :string
- def type; 'ContactSti' end
+ def type; "ContactSti" end
end
diff --git a/activerecord/test/models/content.rb b/activerecord/test/models/content.rb
index 140e1dfc78..68db2127d8 100644
--- a/activerecord/test/models/content.rb
+++ b/activerecord/test/models/content.rb
@@ -1,5 +1,5 @@
class Content < ActiveRecord::Base
- self.table_name = 'content'
+ self.table_name = "content"
has_one :content_position, dependent: :destroy
def self.destroyed_ids
@@ -12,8 +12,8 @@ class Content < ActiveRecord::Base
end
class ContentWhichRequiresTwoDestroyCalls < ActiveRecord::Base
- self.table_name = 'content'
- has_one :content_position, foreign_key: 'content_id', dependent: :destroy
+ self.table_name = "content"
+ has_one :content_position, foreign_key: "content_id", dependent: :destroy
after_initialize do
@destroy_count = 0
diff --git a/activerecord/test/models/contract.rb b/activerecord/test/models/contract.rb
index cdf7b267b5..32bd581377 100644
--- a/activerecord/test/models/contract.rb
+++ b/activerecord/test/models/contract.rb
@@ -1,7 +1,7 @@
class Contract < ActiveRecord::Base
belongs_to :company
belongs_to :developer
- belongs_to :firm, :foreign_key => 'company_id'
+ belongs_to :firm, foreign_key: "company_id"
before_save :hi
after_save :bye
diff --git a/activerecord/test/models/country.rb b/activerecord/test/models/country.rb
index 7db9a4e731..7912719ddd 100644
--- a/activerecord/test/models/country.rb
+++ b/activerecord/test/models/country.rb
@@ -1,7 +1,5 @@
class Country < ActiveRecord::Base
-
self.primary_key = :country_id
has_and_belongs_to_many :treaties
-
end
diff --git a/activerecord/test/models/course.rb b/activerecord/test/models/course.rb
index f3d0e05ff7..348f2bf1e0 100644
--- a/activerecord/test/models/course.rb
+++ b/activerecord/test/models/course.rb
@@ -1,4 +1,4 @@
-require_dependency 'models/arunit2_model'
+require_dependency "models/arunit2_model"
class Course < ARUnit2Model
belongs_to :college
diff --git a/activerecord/test/models/customer.rb b/activerecord/test/models/customer.rb
index 3338aaf7e1..60af7c2247 100644
--- a/activerecord/test/models/customer.rb
+++ b/activerecord/test/models/customer.rb
@@ -1,12 +1,13 @@
class Customer < ActiveRecord::Base
cattr_accessor :gps_conversion_was_run
- composed_of :address, :mapping => [ %w(address_street street), %w(address_city city), %w(address_country country) ], :allow_nil => true
- composed_of :balance, :class_name => "Money", :mapping => %w(balance amount), :converter => Proc.new(&:to_money)
- composed_of :gps_location, :allow_nil => true
- composed_of :non_blank_gps_location, :class_name => "GpsLocation", :allow_nil => true, :mapping => %w(gps_location gps_location),
- :converter => lambda { |gps| self.gps_conversion_was_run = true; gps.blank? ? nil : GpsLocation.new(gps)}
- composed_of :fullname, :mapping => %w(name to_s), :constructor => Proc.new { |name| Fullname.parse(name) }, :converter => :parse
+ composed_of :address, mapping: [ %w(address_street street), %w(address_city city), %w(address_country country) ], allow_nil: true
+ composed_of :balance, class_name: "Money", mapping: %w(balance amount), converter: Proc.new(&:to_money)
+ composed_of :gps_location, allow_nil: true
+ composed_of :non_blank_gps_location, class_name: "GpsLocation", allow_nil: true, mapping: %w(gps_location gps_location),
+ converter: lambda { |gps| self.gps_conversion_was_run = true; gps.blank? ? nil : GpsLocation.new(gps) }
+ composed_of :fullname, mapping: %w(name to_s), constructor: Proc.new { |name| Fullname.parse(name) }, converter: :parse
+ composed_of :fullname_no_converter, mapping: %w(name to_s), class_name: "Fullname"
end
class Address
diff --git a/activerecord/test/models/developer.rb b/activerecord/test/models/developer.rb
index 9a907273f8..ea4f719517 100644
--- a/activerecord/test/models/developer.rb
+++ b/activerecord/test/models/developer.rb
@@ -1,4 +1,4 @@
-require 'ostruct'
+require "ostruct"
module DeveloperProjectsAssociationExtension2
def find_least_recent
@@ -23,35 +23,35 @@ class Developer < ActiveRecord::Base
has_and_belongs_to_many :projects_extended_by_name,
-> { extending(DeveloperProjectsAssociationExtension) },
- :class_name => "Project",
- :join_table => "developers_projects",
- :association_foreign_key => "project_id"
+ class_name: "Project",
+ join_table: "developers_projects",
+ association_foreign_key: "project_id"
has_and_belongs_to_many :projects_extended_by_name_twice,
-> { extending(DeveloperProjectsAssociationExtension, DeveloperProjectsAssociationExtension2) },
- :class_name => "Project",
- :join_table => "developers_projects",
- :association_foreign_key => "project_id"
+ class_name: "Project",
+ join_table: "developers_projects",
+ association_foreign_key: "project_id"
has_and_belongs_to_many :projects_extended_by_name_and_block,
-> { extending(DeveloperProjectsAssociationExtension) },
- :class_name => "Project",
- :join_table => "developers_projects",
- :association_foreign_key => "project_id" do
+ class_name: "Project",
+ join_table: "developers_projects",
+ association_foreign_key: "project_id" do
def find_least_recent
order("id ASC").first
end
end
- has_and_belongs_to_many :special_projects, :join_table => 'developers_projects', :association_foreign_key => 'project_id'
+ has_and_belongs_to_many :special_projects, join_table: "developers_projects", association_foreign_key: "project_id"
has_and_belongs_to_many :sym_special_projects,
- :join_table => :developers_projects,
- :association_foreign_key => 'project_id',
- :class_name => 'SpecialProject'
+ join_table: :developers_projects,
+ association_foreign_key: "project_id",
+ class_name: "SpecialProject"
has_many :audit_logs
has_many :contracts
- has_many :firms, :through => :contracts, :source => :firm
+ has_many :firms, through: :contracts, source: :firm
has_many :comments, ->(developer) { where(body: "I'm #{developer.name}") }
has_many :ratings, through: :comments
has_one :ship, dependent: :nullify
@@ -59,20 +59,20 @@ class Developer < ActiveRecord::Base
belongs_to :firm
has_many :contracted_projects, class_name: "Project"
- scope :jamises, -> { where(:name => 'Jamis') }
+ scope :jamises, -> { where(name: "Jamis") }
- validates_inclusion_of :salary, :in => 50000..200000
- validates_length_of :name, :within => 3..20
+ validates_inclusion_of :salary, in: 50000..200000
+ validates_length_of :name, within: 3..20
before_create do |developer|
- developer.audit_logs.build :message => "Computer created"
+ developer.audit_logs.build message: "Computer created"
end
attr_accessor :last_name
- define_attribute_method 'last_name'
+ define_attribute_method "last_name"
def log=(message)
- audit_logs.build :message => message
+ audit_logs.build message: message
end
after_find :track_instance_count
@@ -83,17 +83,16 @@ class Developer < ActiveRecord::Base
self.class.instance_count += 1
end
private :track_instance_count
-
end
class AuditLog < ActiveRecord::Base
- belongs_to :developer, :validate => true
- belongs_to :unvalidated_developer, :class_name => 'Developer'
+ belongs_to :developer, validate: true
+ belongs_to :unvalidated_developer, class_name: "Developer"
end
class DeveloperWithBeforeDestroyRaise < ActiveRecord::Base
- self.table_name = 'developers'
- has_and_belongs_to_many :projects, :join_table => 'developers_projects', :foreign_key => 'developer_id'
+ self.table_name = "developers"
+ has_and_belongs_to_many :projects, join_table: "developers_projects", foreign_key: "developer_id"
before_destroy :raise_if_projects_empty!
def raise_if_projects_empty!
@@ -102,63 +101,63 @@ class DeveloperWithBeforeDestroyRaise < ActiveRecord::Base
end
class DeveloperWithSelect < ActiveRecord::Base
- self.table_name = 'developers'
- default_scope { select('name') }
+ self.table_name = "developers"
+ default_scope { select("name") }
end
class DeveloperWithIncludes < ActiveRecord::Base
- self.table_name = 'developers'
- has_many :audit_logs, :foreign_key => :developer_id
+ self.table_name = "developers"
+ has_many :audit_logs, foreign_key: :developer_id
default_scope { includes(:audit_logs) }
end
class DeveloperFilteredOnJoins < ActiveRecord::Base
- self.table_name = 'developers'
- has_and_belongs_to_many :projects, -> { order('projects.id') }, :foreign_key => 'developer_id', :join_table => 'developers_projects'
+ self.table_name = "developers"
+ has_and_belongs_to_many :projects, -> { order("projects.id") }, foreign_key: "developer_id", join_table: "developers_projects"
def self.default_scope
- joins(:projects).where(:projects => { :name => 'Active Controller' })
+ joins(:projects).where(projects: { name: "Active Controller" })
end
end
class DeveloperOrderedBySalary < ActiveRecord::Base
- self.table_name = 'developers'
- default_scope { order('salary DESC') }
+ self.table_name = "developers"
+ default_scope { order("salary DESC") }
- scope :by_name, -> { order('name DESC') }
+ scope :by_name, -> { order("name DESC") }
end
class DeveloperCalledDavid < ActiveRecord::Base
- self.table_name = 'developers'
+ self.table_name = "developers"
default_scope { where("name = 'David'") }
end
class LazyLambdaDeveloperCalledDavid < ActiveRecord::Base
- self.table_name = 'developers'
- default_scope lambda { where(:name => 'David') }
+ self.table_name = "developers"
+ default_scope lambda { where(name: "David") }
end
class LazyBlockDeveloperCalledDavid < ActiveRecord::Base
- self.table_name = 'developers'
- default_scope { where(:name => 'David') }
+ self.table_name = "developers"
+ default_scope { where(name: "David") }
end
class CallableDeveloperCalledDavid < ActiveRecord::Base
- self.table_name = 'developers'
- default_scope OpenStruct.new(:call => where(:name => 'David'))
+ self.table_name = "developers"
+ default_scope OpenStruct.new(call: where(name: "David"))
end
class ClassMethodDeveloperCalledDavid < ActiveRecord::Base
- self.table_name = 'developers'
+ self.table_name = "developers"
def self.default_scope
- where(:name => 'David')
+ where(name: "David")
end
end
class ClassMethodReferencingScopeDeveloperCalledDavid < ActiveRecord::Base
- self.table_name = 'developers'
- scope :david, -> { where(:name => 'David') }
+ self.table_name = "developers"
+ scope :david, -> { where(name: "David") }
def self.default_scope
david
@@ -166,61 +165,61 @@ class ClassMethodReferencingScopeDeveloperCalledDavid < ActiveRecord::Base
end
class LazyBlockReferencingScopeDeveloperCalledDavid < ActiveRecord::Base
- self.table_name = 'developers'
- scope :david, -> { where(:name => 'David') }
+ self.table_name = "developers"
+ scope :david, -> { where(name: "David") }
default_scope { david }
end
class DeveloperCalledJamis < ActiveRecord::Base
- self.table_name = 'developers'
+ self.table_name = "developers"
- default_scope { where(:name => 'Jamis') }
- scope :poor, -> { where('salary < 150000') }
+ default_scope { where(name: "Jamis") }
+ scope :poor, -> { where("salary < 150000") }
scope :david, -> { where name: "David" }
scope :david2, -> { unscoped.where name: "David" }
end
class PoorDeveloperCalledJamis < ActiveRecord::Base
- self.table_name = 'developers'
+ self.table_name = "developers"
- default_scope -> { where(:name => 'Jamis', :salary => 50000) }
+ default_scope -> { where(name: "Jamis", salary: 50000) }
end
class InheritedPoorDeveloperCalledJamis < DeveloperCalledJamis
- self.table_name = 'developers'
+ self.table_name = "developers"
- default_scope -> { where(:salary => 50000) }
+ default_scope -> { where(salary: 50000) }
end
class MultiplePoorDeveloperCalledJamis < ActiveRecord::Base
- self.table_name = 'developers'
+ self.table_name = "developers"
- default_scope -> { where(:name => 'Jamis') }
- default_scope -> { where(:salary => 50000) }
+ default_scope -> { where(name: "Jamis") }
+ default_scope -> { where(salary: 50000) }
end
module SalaryDefaultScope
extend ActiveSupport::Concern
- included { default_scope { where(:salary => 50000) } }
+ included { default_scope { where(salary: 50000) } }
end
class ModuleIncludedPoorDeveloperCalledJamis < DeveloperCalledJamis
- self.table_name = 'developers'
+ self.table_name = "developers"
include SalaryDefaultScope
end
class EagerDeveloperWithDefaultScope < ActiveRecord::Base
- self.table_name = 'developers'
- has_and_belongs_to_many :projects, -> { order('projects.id') }, :foreign_key => 'developer_id', :join_table => 'developers_projects'
+ self.table_name = "developers"
+ has_and_belongs_to_many :projects, -> { order("projects.id") }, foreign_key: "developer_id", join_table: "developers_projects"
default_scope { includes(:projects) }
end
class EagerDeveloperWithClassMethodDefaultScope < ActiveRecord::Base
- self.table_name = 'developers'
- has_and_belongs_to_many :projects, -> { order('projects.id') }, :foreign_key => 'developer_id', :join_table => 'developers_projects'
+ self.table_name = "developers"
+ has_and_belongs_to_many :projects, -> { order("projects.id") }, foreign_key: "developer_id", join_table: "developers_projects"
def self.default_scope
includes(:projects)
@@ -228,31 +227,31 @@ class EagerDeveloperWithClassMethodDefaultScope < ActiveRecord::Base
end
class EagerDeveloperWithLambdaDefaultScope < ActiveRecord::Base
- self.table_name = 'developers'
- has_and_belongs_to_many :projects, -> { order('projects.id') }, :foreign_key => 'developer_id', :join_table => 'developers_projects'
+ self.table_name = "developers"
+ has_and_belongs_to_many :projects, -> { order("projects.id") }, foreign_key: "developer_id", join_table: "developers_projects"
default_scope lambda { includes(:projects) }
end
class EagerDeveloperWithBlockDefaultScope < ActiveRecord::Base
- self.table_name = 'developers'
- has_and_belongs_to_many :projects, -> { order('projects.id') }, :foreign_key => 'developer_id', :join_table => 'developers_projects'
+ self.table_name = "developers"
+ has_and_belongs_to_many :projects, -> { order("projects.id") }, foreign_key: "developer_id", join_table: "developers_projects"
default_scope { includes(:projects) }
end
class EagerDeveloperWithCallableDefaultScope < ActiveRecord::Base
- self.table_name = 'developers'
- has_and_belongs_to_many :projects, -> { order('projects.id') }, :foreign_key => 'developer_id', :join_table => 'developers_projects'
+ self.table_name = "developers"
+ has_and_belongs_to_many :projects, -> { order("projects.id") }, foreign_key: "developer_id", join_table: "developers_projects"
- default_scope OpenStruct.new(:call => includes(:projects))
+ default_scope OpenStruct.new(call: includes(:projects))
end
class ThreadsafeDeveloper < ActiveRecord::Base
- self.table_name = 'developers'
+ self.table_name = "developers"
def self.default_scope
- sleep 0.05 if Thread.current[:long_default_scope]
+ Thread.current[:default_scope_delay].call
limit(1)
end
end
diff --git a/activerecord/test/models/doubloon.rb b/activerecord/test/models/doubloon.rb
index 2b11d128e2..7272504666 100644
--- a/activerecord/test/models/doubloon.rb
+++ b/activerecord/test/models/doubloon.rb
@@ -8,5 +8,5 @@ end
class Doubloon < AbstractDoubloon
# This uses an abstract class that defines attributes and associations.
- self.table_name = 'doubloons'
+ self.table_name = "doubloons"
end
diff --git a/activerecord/test/models/edge.rb b/activerecord/test/models/edge.rb
index 55e0c31fcb..e61d25c9bc 100644
--- a/activerecord/test/models/edge.rb
+++ b/activerecord/test/models/edge.rb
@@ -1,5 +1,5 @@
# This class models an edge in a directed graph.
class Edge < ActiveRecord::Base
- belongs_to :source, :class_name => 'Vertex', :foreign_key => 'source_id'
- belongs_to :sink, :class_name => 'Vertex', :foreign_key => 'sink_id'
+ belongs_to :source, class_name: "Vertex", foreign_key: "source_id"
+ belongs_to :sink, class_name: "Vertex", foreign_key: "sink_id"
end
diff --git a/activerecord/test/models/engine.rb b/activerecord/test/models/engine.rb
index 851ff8c22b..eada171f6a 100644
--- a/activerecord/test/models/engine.rb
+++ b/activerecord/test/models/engine.rb
@@ -1,4 +1,3 @@
class Engine < ActiveRecord::Base
- belongs_to :my_car, :class_name => 'Car', :foreign_key => 'car_id', :counter_cache => :engines_count
+ belongs_to :my_car, class_name: "Car", foreign_key: "car_id", counter_cache: :engines_count
end
-
diff --git a/activerecord/test/models/essay.rb b/activerecord/test/models/essay.rb
index ec4b982b5b..13267fbc21 100644
--- a/activerecord/test/models/essay.rb
+++ b/activerecord/test/models/essay.rb
@@ -1,5 +1,5 @@
class Essay < ActiveRecord::Base
- belongs_to :writer, :primary_key => :name, :polymorphic => true
- belongs_to :category, :primary_key => :name
- has_one :owner, :primary_key => :name
+ belongs_to :writer, primary_key: :name, polymorphic: true
+ belongs_to :category, primary_key: :name
+ has_one :owner, primary_key: :name
end
diff --git a/activerecord/test/models/eye.rb b/activerecord/test/models/eye.rb
index dc8ae2b3f6..f53c34e4b1 100644
--- a/activerecord/test/models/eye.rb
+++ b/activerecord/test/models/eye.rb
@@ -15,19 +15,19 @@ class Eye < ActiveRecord::Base
after_create :trace_after_create2
after_update :trace_after_update2
after_save :trace_after_save2
-
+
def trace_after_create
(@after_create_callbacks_stack ||= []) << !iris.persisted?
end
alias trace_after_create2 trace_after_create
def trace_after_update
- (@after_update_callbacks_stack ||= []) << iris.changed?
+ (@after_update_callbacks_stack ||= []) << iris.has_changes_to_save?
end
alias trace_after_update2 trace_after_update
def trace_after_save
- (@after_save_callbacks_stack ||= []) << iris.changed?
+ (@after_save_callbacks_stack ||= []) << iris.has_changes_to_save?
end
alias trace_after_save2 trace_after_save
end
diff --git a/activerecord/test/models/face.rb b/activerecord/test/models/face.rb
index af76fea52c..5913bfa969 100644
--- a/activerecord/test/models/face.rb
+++ b/activerecord/test/models/face.rb
@@ -1,9 +1,9 @@
class Face < ActiveRecord::Base
- belongs_to :man, :inverse_of => :face
- belongs_to :polymorphic_man, :polymorphic => true, :inverse_of => :polymorphic_face
+ belongs_to :man, inverse_of: :face
+ belongs_to :polymorphic_man, polymorphic: true, inverse_of: :polymorphic_face
# Oracle identifier length is limited to 30 bytes or less, `polymorphic` renamed `poly`
- belongs_to :poly_man_without_inverse, :polymorphic => true
+ belongs_to :poly_man_without_inverse, polymorphic: true
# These is a "broken" inverse_of for the purposes of testing
- belongs_to :horrible_man, :class_name => 'Man', :inverse_of => :horrible_face
- belongs_to :horrible_polymorphic_man, :polymorphic => true, :inverse_of => :horrible_polymorphic_face
+ belongs_to :horrible_man, class_name: "Man", inverse_of: :horrible_face
+ belongs_to :horrible_polymorphic_man, polymorphic: true, inverse_of: :horrible_polymorphic_face
end
diff --git a/activerecord/test/models/friendship.rb b/activerecord/test/models/friendship.rb
index 4b411ca8e0..578382b494 100644
--- a/activerecord/test/models/friendship.rb
+++ b/activerecord/test/models/friendship.rb
@@ -1,6 +1,6 @@
class Friendship < ActiveRecord::Base
- belongs_to :friend, class_name: 'Person'
+ belongs_to :friend, class_name: "Person"
# friend_too exists to test a bug, and probably shouldn't be used elsewhere
- belongs_to :friend_too, foreign_key: 'friend_id', class_name: 'Person', counter_cache: :friends_too_count
- belongs_to :follower, class_name: 'Person'
+ belongs_to :friend_too, foreign_key: "friend_id", class_name: "Person", counter_cache: :friends_too_count
+ belongs_to :follower, class_name: "Person"
end
diff --git a/activerecord/test/models/hotel.rb b/activerecord/test/models/hotel.rb
index 9c90ffcff4..7bc717c891 100644
--- a/activerecord/test/models/hotel.rb
+++ b/activerecord/test/models/hotel.rb
@@ -1,11 +1,11 @@
class Hotel < ActiveRecord::Base
has_many :departments
has_many :chefs, through: :departments
- has_many :cake_designers, source_type: 'CakeDesigner', source: :employable, through: :chefs
- has_many :drink_designers, source_type: 'DrinkDesigner', source: :employable, through: :chefs
+ has_many :cake_designers, source_type: "CakeDesigner", source: :employable, through: :chefs
+ has_many :drink_designers, source_type: "DrinkDesigner", source: :employable, through: :chefs
has_many :chef_lists, as: :employable_list
- has_many :mocktail_designers, through: :chef_lists, source: :employable, :source_type => "MocktailDesigner"
+ has_many :mocktail_designers, through: :chef_lists, source: :employable, source_type: "MocktailDesigner"
has_many :recipes, through: :chefs
end
diff --git a/activerecord/test/models/interest.rb b/activerecord/test/models/interest.rb
index d5d9226204..ec79416ee7 100644
--- a/activerecord/test/models/interest.rb
+++ b/activerecord/test/models/interest.rb
@@ -1,5 +1,5 @@
class Interest < ActiveRecord::Base
- belongs_to :man, :inverse_of => :interests
- belongs_to :polymorphic_man, :polymorphic => true, :inverse_of => :polymorphic_interests
- belongs_to :zine, :inverse_of => :interests
+ belongs_to :man, inverse_of: :interests
+ belongs_to :polymorphic_man, polymorphic: true, inverse_of: :polymorphic_interests
+ belongs_to :zine, inverse_of: :interests
end
diff --git a/activerecord/test/models/invoice.rb b/activerecord/test/models/invoice.rb
index fc6ef0230e..4be5a00193 100644
--- a/activerecord/test/models/invoice.rb
+++ b/activerecord/test/models/invoice.rb
@@ -1,4 +1,4 @@
class Invoice < ActiveRecord::Base
- has_many :line_items, :autosave => true
- before_save {|record| record.balance = record.line_items.map(&:amount).sum }
+ has_many :line_items, autosave: true
+ before_save { |record| record.balance = record.line_items.map(&:amount).sum }
end
diff --git a/activerecord/test/models/item.rb b/activerecord/test/models/item.rb
index c2571dd7fb..336fb1769a 100644
--- a/activerecord/test/models/item.rb
+++ b/activerecord/test/models/item.rb
@@ -1,6 +1,6 @@
class AbstractItem < ActiveRecord::Base
self.abstract_class = true
- has_one :tagging, :as => :taggable
+ has_one :tagging, as: :taggable
end
class Item < AbstractItem
diff --git a/activerecord/test/models/job.rb b/activerecord/test/models/job.rb
index f7b0e787b1..bbaef2792c 100644
--- a/activerecord/test/models/job.rb
+++ b/activerecord/test/models/job.rb
@@ -1,7 +1,7 @@
class Job < ActiveRecord::Base
has_many :references
- has_many :people, :through => :references
- belongs_to :ideal_reference, :class_name => 'Reference'
+ has_many :people, through: :references
+ belongs_to :ideal_reference, class_name: "Reference"
- has_many :agents, :through => :people
+ has_many :agents, through: :people
end
diff --git a/activerecord/test/models/joke.rb b/activerecord/test/models/joke.rb
index edda4655dc..eeb5818a1f 100644
--- a/activerecord/test/models/joke.rb
+++ b/activerecord/test/models/joke.rb
@@ -1,7 +1,7 @@
class Joke < ActiveRecord::Base
- self.table_name = 'funny_jokes'
+ self.table_name = "funny_jokes"
end
class GoodJoke < ActiveRecord::Base
- self.table_name = 'funny_jokes'
+ self.table_name = "funny_jokes"
end
diff --git a/activerecord/test/models/keyboard.rb b/activerecord/test/models/keyboard.rb
index 39347e274e..bcede53ec9 100644
--- a/activerecord/test/models/keyboard.rb
+++ b/activerecord/test/models/keyboard.rb
@@ -1,3 +1,3 @@
class Keyboard < ActiveRecord::Base
- self.primary_key = 'key_number'
+ self.primary_key = "key_number"
end
diff --git a/activerecord/test/models/line_item.rb b/activerecord/test/models/line_item.rb
index 0dd921a300..93f7cceb13 100644
--- a/activerecord/test/models/line_item.rb
+++ b/activerecord/test/models/line_item.rb
@@ -1,3 +1,3 @@
class LineItem < ActiveRecord::Base
- belongs_to :invoice, :touch => true
+ belongs_to :invoice, touch: true
end
diff --git a/activerecord/test/models/man.rb b/activerecord/test/models/man.rb
index 4fbb6b226b..d2436a735c 100644
--- a/activerecord/test/models/man.rb
+++ b/activerecord/test/models/man.rb
@@ -1,11 +1,11 @@
class Man < ActiveRecord::Base
- has_one :face, :inverse_of => :man
- has_one :polymorphic_face, :class_name => 'Face', :as => :polymorphic_man, :inverse_of => :polymorphic_man
- has_one :polymorphic_face_without_inverse, :class_name => 'Face', :as => :poly_man_without_inverse
- has_many :interests, :inverse_of => :man
- has_many :polymorphic_interests, :class_name => 'Interest', :as => :polymorphic_man, :inverse_of => :polymorphic_man
+ has_one :face, inverse_of: :man
+ has_one :polymorphic_face, class_name: "Face", as: :polymorphic_man, inverse_of: :polymorphic_man
+ has_one :polymorphic_face_without_inverse, class_name: "Face", as: :poly_man_without_inverse
+ has_many :interests, inverse_of: :man
+ has_many :polymorphic_interests, class_name: "Interest", as: :polymorphic_man, inverse_of: :polymorphic_man
# These are "broken" inverse_of associations for the purposes of testing
- has_one :dirty_face, :class_name => 'Face', :inverse_of => :dirty_man
- has_many :secret_interests, :class_name => 'Interest', :inverse_of => :secret_man
+ has_one :dirty_face, class_name: "Face", inverse_of: :dirty_man
+ has_many :secret_interests, class_name: "Interest", inverse_of: :secret_man
has_one :mixed_case_monkey
end
diff --git a/activerecord/test/models/matey.rb b/activerecord/test/models/matey.rb
index 47b0baa974..80ee5f47c5 100644
--- a/activerecord/test/models/matey.rb
+++ b/activerecord/test/models/matey.rb
@@ -1,4 +1,4 @@
class Matey < ActiveRecord::Base
belongs_to :pirate
- belongs_to :target, :class_name => 'Pirate'
+ belongs_to :target, class_name: "Pirate"
end
diff --git a/activerecord/test/models/member.rb b/activerecord/test/models/member.rb
index 7693c6e515..36f2461b84 100644
--- a/activerecord/test/models/member.rb
+++ b/activerecord/test/models/member.rb
@@ -2,34 +2,34 @@ class Member < ActiveRecord::Base
has_one :current_membership
has_one :selected_membership
has_one :membership
- has_one :club, :through => :current_membership
- has_one :selected_club, :through => :selected_membership, :source => :club
- has_one :favourite_club, -> { where "memberships.favourite = ?", true }, :through => :membership, :source => :club
- has_one :hairy_club, -> { where :clubs => {:name => "Moustache and Eyebrow Fancier Club"} }, :through => :membership, :source => :club
- has_one :sponsor, :as => :sponsorable
- has_one :sponsor_club, :through => :sponsor
- has_one :member_detail, :inverse_of => false
- has_one :organization, :through => :member_detail
+ has_one :club, through: :current_membership
+ has_one :selected_club, through: :selected_membership, source: :club
+ has_one :favourite_club, -> { where "memberships.favourite = ?", true }, through: :membership, source: :club
+ has_one :hairy_club, -> { where clubs: { name: "Moustache and Eyebrow Fancier Club" } }, through: :membership, source: :club
+ has_one :sponsor, as: :sponsorable
+ has_one :sponsor_club, through: :sponsor
+ has_one :member_detail, inverse_of: false
+ has_one :organization, through: :member_detail
belongs_to :member_type
- has_many :nested_member_types, :through => :member_detail, :source => :member_type
- has_one :nested_member_type, :through => :member_detail, :source => :member_type
+ has_many :nested_member_types, through: :member_detail, source: :member_type
+ has_one :nested_member_type, through: :member_detail, source: :member_type
- has_many :nested_sponsors, :through => :sponsor_club, :source => :sponsor
- has_one :nested_sponsor, :through => :sponsor_club, :source => :sponsor
+ has_many :nested_sponsors, through: :sponsor_club, source: :sponsor
+ has_one :nested_sponsor, through: :sponsor_club, source: :sponsor
- has_many :organization_member_details, :through => :member_detail
- has_many :organization_member_details_2, :through => :organization, :source => :member_details
+ has_many :organization_member_details, through: :member_detail
+ has_many :organization_member_details_2, through: :organization, source: :member_details
- has_one :club_category, :through => :club, :source => :category
+ has_one :club_category, through: :club, source: :category
- has_many :current_memberships, -> { where :favourite => true }
- has_many :clubs, :through => :current_memberships
+ has_many :current_memberships, -> { where favourite: true }
+ has_many :clubs, through: :current_memberships
has_many :tenant_memberships
- has_many :tenant_clubs, through: :tenant_memberships, class_name: 'Club', source: :club
+ has_many :tenant_clubs, through: :tenant_memberships, class_name: "Club", source: :club
- has_one :club_through_many, :through => :current_memberships, :source => :club
+ has_one :club_through_many, through: :current_memberships, source: :club
belongs_to :admittable, polymorphic: true
has_one :premium_club, through: :admittable
@@ -37,5 +37,5 @@ end
class SelfMember < ActiveRecord::Base
self.table_name = "members"
- has_and_belongs_to_many :friends, :class_name => "SelfMember", :join_table => "member_friends"
+ has_and_belongs_to_many :friends, class_name: "SelfMember", join_table: "member_friends"
end
diff --git a/activerecord/test/models/membership.rb b/activerecord/test/models/membership.rb
index e181ba1f11..2c3ad230a7 100644
--- a/activerecord/test/models/membership.rb
+++ b/activerecord/test/models/membership.rb
@@ -9,7 +9,7 @@ class CurrentMembership < Membership
end
class SuperMembership < Membership
- belongs_to :member, -> { order('members.id DESC') }
+ belongs_to :member, -> { order("members.id DESC") }
belongs_to :club
end
diff --git a/activerecord/test/models/mentor.rb b/activerecord/test/models/mentor.rb
index 11f1e4bff8..66504b4e91 100644
--- a/activerecord/test/models/mentor.rb
+++ b/activerecord/test/models/mentor.rb
@@ -1,3 +1,3 @@
class Mentor < ActiveRecord::Base
has_many :developers
-end \ No newline at end of file
+end
diff --git a/activerecord/test/models/minivan.rb b/activerecord/test/models/minivan.rb
index 4fe79720ad..e9b05dadf2 100644
--- a/activerecord/test/models/minivan.rb
+++ b/activerecord/test/models/minivan.rb
@@ -2,8 +2,7 @@ class Minivan < ActiveRecord::Base
self.primary_key = :minivan_id
belongs_to :speedometer
- has_one :dashboard, :through => :speedometer
+ has_one :dashboard, through: :speedometer
attr_readonly :color
-
end
diff --git a/activerecord/test/models/node.rb b/activerecord/test/models/node.rb
index 07dd2dbccb..459ea8cf95 100644
--- a/activerecord/test/models/node.rb
+++ b/activerecord/test/models/node.rb
@@ -1,5 +1,5 @@
class Node < ActiveRecord::Base
belongs_to :tree, touch: true
- belongs_to :parent, class_name: 'Node', touch: true, optional: true
- has_many :children, class_name: 'Node', foreign_key: :parent_id, dependent: :destroy
+ belongs_to :parent, class_name: "Node", touch: true, optional: true
+ has_many :children, class_name: "Node", foreign_key: :parent_id, dependent: :destroy
end
diff --git a/activerecord/test/models/order.rb b/activerecord/test/models/order.rb
index e838c0b70d..699be53959 100644
--- a/activerecord/test/models/order.rb
+++ b/activerecord/test/models/order.rb
@@ -1,4 +1,4 @@
class Order < ActiveRecord::Base
- belongs_to :billing, :class_name => 'Customer', :foreign_key => 'billing_customer_id'
- belongs_to :shipping, :class_name => 'Customer', :foreign_key => 'shipping_customer_id'
+ belongs_to :billing, class_name: "Customer", foreign_key: "billing_customer_id"
+ belongs_to :shipping, class_name: "Customer", foreign_key: "shipping_customer_id"
end
diff --git a/activerecord/test/models/organization.rb b/activerecord/test/models/organization.rb
index f3e92f3067..462830dadc 100644
--- a/activerecord/test/models/organization.rb
+++ b/activerecord/test/models/organization.rb
@@ -1,14 +1,14 @@
class Organization < ActiveRecord::Base
has_many :member_details
- has_many :members, :through => :member_details
+ has_many :members, through: :member_details
- has_many :authors, :primary_key => :name
- has_many :author_essay_categories, :through => :authors, :source => :essay_categories
+ has_many :authors, primary_key: :name
+ has_many :author_essay_categories, through: :authors, source: :essay_categories
- has_one :author, :primary_key => :name
- has_one :author_owned_essay_category, :through => :author, :source => :owned_essay_category
+ has_one :author, primary_key: :name
+ has_one :author_owned_essay_category, through: :author, source: :owned_essay_category
- has_many :posts, :through => :author, :source => :posts
+ has_many :posts, through: :author, source: :posts
- scope :clubs, -> { from('clubs') }
+ scope :clubs, -> { from("clubs") }
end
diff --git a/activerecord/test/models/other_dog.rb b/activerecord/test/models/other_dog.rb
new file mode 100644
index 0000000000..418caf34be
--- /dev/null
+++ b/activerecord/test/models/other_dog.rb
@@ -0,0 +1,5 @@
+require_dependency "models/arunit2_model"
+
+class OtherDog < ARUnit2Model
+ self.table_name = "dogs"
+end
diff --git a/activerecord/test/models/owner.rb b/activerecord/test/models/owner.rb
index ce8242cf2f..21fc9b6eb8 100644
--- a/activerecord/test/models/owner.rb
+++ b/activerecord/test/models/owner.rb
@@ -1,19 +1,19 @@
class Owner < ActiveRecord::Base
self.primary_key = :owner_id
- has_many :pets, -> { order 'pets.name desc' }
+ has_many :pets, -> { order "pets.name desc" }
has_many :toys, through: :pets
has_many :persons, through: :pets
- belongs_to :last_pet, class_name: 'Pet'
+ belongs_to :last_pet, class_name: "Pet"
scope :including_last_pet, -> {
- select(%q[
+ select('
owners.*, (
select p.pet_id from pets p
where p.owner_id = owners.owner_id
order by p.name desc
limit 1
) as last_pet_id
- ]).includes(:last_pet)
+ ').includes(:last_pet)
}
after_commit :execute_blocks
diff --git a/activerecord/test/models/parrot.rb b/activerecord/test/models/parrot.rb
index ddc9dcaf29..1e5f9285a8 100644
--- a/activerecord/test/models/parrot.rb
+++ b/activerecord/test/models/parrot.rb
@@ -3,21 +3,26 @@ class Parrot < ActiveRecord::Base
has_and_belongs_to_many :pirates
has_and_belongs_to_many :treasures
- has_many :loots, :as => :looter
+ has_many :loots, as: :looter
alias_attribute :title, :name
validates_presence_of :name
attr_accessor :cancel_save_from_callback
- before_save :cancel_save_callback_method, :if => :cancel_save_from_callback
+ before_save :cancel_save_callback_method, if: :cancel_save_from_callback
def cancel_save_callback_method
throw(:abort)
end
+
+ before_update :increment_updated_count
+ def increment_updated_count
+ self.updated_count += 1
+ end
end
class LiveParrot < Parrot
end
class DeadParrot < Parrot
- belongs_to :killer, :class_name => 'Pirate', foreign_key: :killer_id
+ belongs_to :killer, class_name: "Pirate", foreign_key: :killer_id
end
diff --git a/activerecord/test/models/person.rb b/activerecord/test/models/person.rb
index a4a9c6b0d4..18994d6f18 100644
--- a/activerecord/test/models/person.rb
+++ b/activerecord/test/models/person.rb
@@ -3,71 +3,70 @@ class Person < ActiveRecord::Base
has_many :secure_readers
has_one :reader
- has_many :posts, :through => :readers
- has_many :secure_posts, :through => :secure_readers
- has_many :posts_with_no_comments, -> { includes(:comments).where('comments.id is null').references(:comments) },
- :through => :readers, :source => :post
+ has_many :posts, through: :readers
+ has_many :secure_posts, through: :secure_readers
+ has_many :posts_with_no_comments, -> { includes(:comments).where("comments.id is null").references(:comments) },
+ through: :readers, source: :post
- has_many :friendships, foreign_key: 'friend_id'
+ has_many :friendships, foreign_key: "friend_id"
# friends_too exists to test a bug, and probably shouldn't be used elsewhere
- has_many :friends_too, foreign_key: 'friend_id', class_name: 'Friendship'
+ has_many :friends_too, foreign_key: "friend_id", class_name: "Friendship"
has_many :followers, through: :friendships
has_many :references
has_many :bad_references
- has_many :fixed_bad_references, -> { where :favourite => true }, :class_name => 'BadReference'
- has_one :favourite_reference, -> { where 'favourite=?', true }, :class_name => 'Reference'
- has_many :posts_with_comments_sorted_by_comment_id, -> { includes(:comments).order('comments.id') }, :through => :readers, :source => :post
+ has_many :fixed_bad_references, -> { where favourite: true }, class_name: "BadReference"
+ has_one :favourite_reference, -> { where "favourite=?", true }, class_name: "Reference"
+ has_many :posts_with_comments_sorted_by_comment_id, -> { includes(:comments).order("comments.id") }, through: :readers, source: :post
has_many :first_posts, -> { where(id: [1, 2]) }, through: :readers
- has_many :jobs, :through => :references
- has_many :jobs_with_dependent_destroy, :source => :job, :through => :references, :dependent => :destroy
- has_many :jobs_with_dependent_delete_all, :source => :job, :through => :references, :dependent => :delete_all
- has_many :jobs_with_dependent_nullify, :source => :job, :through => :references, :dependent => :nullify
+ has_many :jobs, through: :references
+ has_many :jobs_with_dependent_destroy, source: :job, through: :references, dependent: :destroy
+ has_many :jobs_with_dependent_delete_all, source: :job, through: :references, dependent: :delete_all
+ has_many :jobs_with_dependent_nullify, source: :job, through: :references, dependent: :nullify
- belongs_to :primary_contact, :class_name => 'Person'
- has_many :agents, :class_name => 'Person', :foreign_key => 'primary_contact_id'
- has_many :agents_of_agents, :through => :agents, :source => :agents
- belongs_to :number1_fan, :class_name => 'Person'
+ belongs_to :primary_contact, class_name: "Person"
+ has_many :agents, class_name: "Person", foreign_key: "primary_contact_id"
+ has_many :agents_of_agents, through: :agents, source: :agents
+ belongs_to :number1_fan, class_name: "Person"
- has_many :personal_legacy_things, :dependent => :destroy
+ has_many :personal_legacy_things, dependent: :destroy
- has_many :agents_posts, :through => :agents, :source => :posts
- has_many :agents_posts_authors, :through => :agents_posts, :source => :author
+ has_many :agents_posts, through: :agents, source: :posts
+ has_many :agents_posts_authors, through: :agents_posts, source: :author
has_many :essays, primary_key: "first_name", foreign_key: "writer_id"
- scope :males, -> { where(:gender => 'M') }
+ scope :males, -> { where(gender: "M") }
end
class PersonWithDependentDestroyJobs < ActiveRecord::Base
- self.table_name = 'people'
+ self.table_name = "people"
- has_many :references, :foreign_key => :person_id
- has_many :jobs, :source => :job, :through => :references, :dependent => :destroy
+ has_many :references, foreign_key: :person_id
+ has_many :jobs, source: :job, through: :references, dependent: :destroy
end
class PersonWithDependentDeleteAllJobs < ActiveRecord::Base
- self.table_name = 'people'
+ self.table_name = "people"
- has_many :references, :foreign_key => :person_id
- has_many :jobs, :source => :job, :through => :references, :dependent => :delete_all
+ has_many :references, foreign_key: :person_id
+ has_many :jobs, source: :job, through: :references, dependent: :delete_all
end
class PersonWithDependentNullifyJobs < ActiveRecord::Base
- self.table_name = 'people'
+ self.table_name = "people"
- has_many :references, :foreign_key => :person_id
- has_many :jobs, :source => :job, :through => :references, :dependent => :nullify
+ has_many :references, foreign_key: :person_id
+ has_many :jobs, source: :job, through: :references, dependent: :nullify
end
-
class LoosePerson < ActiveRecord::Base
- self.table_name = 'people'
+ self.table_name = "people"
self.abstract_class = true
- has_one :best_friend, :class_name => 'LoosePerson', :foreign_key => :best_friend_id
- belongs_to :best_friend_of, :class_name => 'LoosePerson', :foreign_key => :best_friend_of_id
- has_many :best_friends, :class_name => 'LoosePerson', :foreign_key => :best_friend_id
+ has_one :best_friend, class_name: "LoosePerson", foreign_key: :best_friend_id
+ belongs_to :best_friend_of, class_name: "LoosePerson", foreign_key: :best_friend_of_id
+ has_many :best_friends, class_name: "LoosePerson", foreign_key: :best_friend_id
accepts_nested_attributes_for :best_friend, :best_friend_of, :best_friends
end
@@ -75,11 +74,11 @@ end
class LooseDescendant < LoosePerson; end
class TightPerson < ActiveRecord::Base
- self.table_name = 'people'
+ self.table_name = "people"
- has_one :best_friend, :class_name => 'TightPerson', :foreign_key => :best_friend_id
- belongs_to :best_friend_of, :class_name => 'TightPerson', :foreign_key => :best_friend_of_id
- has_many :best_friends, :class_name => 'TightPerson', :foreign_key => :best_friend_id
+ has_one :best_friend, class_name: "TightPerson", foreign_key: :best_friend_id
+ belongs_to :best_friend_of, class_name: "TightPerson", foreign_key: :best_friend_of_id
+ has_many :best_friends, class_name: "TightPerson", foreign_key: :best_friend_id
accepts_nested_attributes_for :best_friend, :best_friend_of, :best_friends
end
@@ -87,56 +86,56 @@ end
class TightDescendant < TightPerson; end
class RichPerson < ActiveRecord::Base
- self.table_name = 'people'
+ self.table_name = "people"
- has_and_belongs_to_many :treasures, :join_table => 'peoples_treasures'
+ has_and_belongs_to_many :treasures, join_table: "peoples_treasures"
before_validation :run_before_create, on: :create
before_validation :run_before_validation
private
- def run_before_create
- self.first_name = first_name.to_s + 'run_before_create'
- end
+ def run_before_create
+ self.first_name = first_name.to_s + "run_before_create"
+ end
- def run_before_validation
- self.first_name = first_name.to_s + 'run_before_validation'
- end
+ def run_before_validation
+ self.first_name = first_name.to_s + "run_before_validation"
+ end
end
class NestedPerson < ActiveRecord::Base
- self.table_name = 'people'
+ self.table_name = "people"
- has_one :best_friend, :class_name => 'NestedPerson', :foreign_key => :best_friend_id
- accepts_nested_attributes_for :best_friend, :update_only => true
+ has_one :best_friend, class_name: "NestedPerson", foreign_key: :best_friend_id
+ accepts_nested_attributes_for :best_friend, update_only: true
def comments=(new_comments)
raise RuntimeError
end
def best_friend_first_name=(new_name)
- assign_attributes({ :best_friend_attributes => { :first_name => new_name } })
+ assign_attributes(best_friend_attributes: { first_name: new_name })
end
end
class Insure
INSURES = %W{life annuality}
- def self.load mask
+ def self.load(mask)
INSURES.select do |insure|
(1 << INSURES.index(insure)) & mask.to_i > 0
end
end
- def self.dump insures
+ def self.dump(insures)
numbers = insures.map { |insure| INSURES.index(insure) }
numbers.inject(0) { |sum, n| sum + (1 << n) }
end
end
class SerializedPerson < ActiveRecord::Base
- self.table_name = 'people'
+ self.table_name = "people"
serialize :insures, Insure
end
diff --git a/activerecord/test/models/personal_legacy_thing.rb b/activerecord/test/models/personal_legacy_thing.rb
index a7ee3a0bca..adde7a504a 100644
--- a/activerecord/test/models/personal_legacy_thing.rb
+++ b/activerecord/test/models/personal_legacy_thing.rb
@@ -1,4 +1,4 @@
class PersonalLegacyThing < ActiveRecord::Base
self.locking_column = :version
- belongs_to :person, :counter_cache => true
+ belongs_to :person, counter_cache: true
end
diff --git a/activerecord/test/models/pet.rb b/activerecord/test/models/pet.rb
index 53489fa1b3..51a3e42815 100644
--- a/activerecord/test/models/pet.rb
+++ b/activerecord/test/models/pet.rb
@@ -2,11 +2,11 @@ class Pet < ActiveRecord::Base
attr_accessor :current_user
self.primary_key = :pet_id
- belongs_to :owner, :touch => true
+ belongs_to :owner, touch: true
has_many :toys
has_many :pet_treasures
has_many :treasures, through: :pet_treasures
- has_many :persons, through: :treasures, source: :looter, source_type: 'Person'
+ has_many :persons, through: :treasures, source: :looter, source_type: "Person"
class << self
attr_accessor :after_destroy_output
diff --git a/activerecord/test/models/pirate.rb b/activerecord/test/models/pirate.rb
index 30545bdcd7..c532ab426e 100644
--- a/activerecord/test/models/pirate.rb
+++ b/activerecord/test/models/pirate.rb
@@ -1,47 +1,47 @@
class Pirate < ActiveRecord::Base
- belongs_to :parrot, :validate => true
- belongs_to :non_validated_parrot, :class_name => 'Parrot'
- has_and_belongs_to_many :parrots, -> { order('parrots.id ASC') }, :validate => true
- has_and_belongs_to_many :non_validated_parrots, :class_name => 'Parrot'
- has_and_belongs_to_many :parrots_with_method_callbacks, :class_name => "Parrot",
- :before_add => :log_before_add,
- :after_add => :log_after_add,
- :before_remove => :log_before_remove,
- :after_remove => :log_after_remove
- has_and_belongs_to_many :parrots_with_proc_callbacks, :class_name => "Parrot",
- :before_add => proc {|p,pa| p.ship_log << "before_adding_proc_parrot_#{pa.id || '<new>'}"},
- :after_add => proc {|p,pa| p.ship_log << "after_adding_proc_parrot_#{pa.id || '<new>'}"},
- :before_remove => proc {|p,pa| p.ship_log << "before_removing_proc_parrot_#{pa.id}"},
- :after_remove => proc {|p,pa| p.ship_log << "after_removing_proc_parrot_#{pa.id}"}
+ belongs_to :parrot, validate: true
+ belongs_to :non_validated_parrot, class_name: "Parrot"
+ has_and_belongs_to_many :parrots, -> { order("parrots.id ASC") }, validate: true
+ has_and_belongs_to_many :non_validated_parrots, class_name: "Parrot"
+ has_and_belongs_to_many :parrots_with_method_callbacks, class_name: "Parrot",
+ before_add: :log_before_add,
+ after_add: :log_after_add,
+ before_remove: :log_before_remove,
+ after_remove: :log_after_remove
+ has_and_belongs_to_many :parrots_with_proc_callbacks, class_name: "Parrot",
+ before_add: proc { |p, pa| p.ship_log << "before_adding_proc_parrot_#{pa.id || '<new>'}" },
+ after_add: proc { |p, pa| p.ship_log << "after_adding_proc_parrot_#{pa.id || '<new>'}" },
+ before_remove: proc { |p, pa| p.ship_log << "before_removing_proc_parrot_#{pa.id}" },
+ after_remove: proc { |p, pa| p.ship_log << "after_removing_proc_parrot_#{pa.id}" }
has_and_belongs_to_many :autosaved_parrots, class_name: "Parrot", autosave: true
- has_many :treasures, :as => :looter
- has_many :treasure_estimates, :through => :treasures, :source => :price_estimates
+ has_many :treasures, as: :looter
+ has_many :treasure_estimates, through: :treasures, source: :price_estimates
has_one :ship
- has_one :update_only_ship, :class_name => 'Ship'
- has_one :non_validated_ship, :class_name => 'Ship'
- has_many :birds, -> { order('birds.id ASC') }
- has_many :birds_with_method_callbacks, :class_name => "Bird",
- :before_add => :log_before_add,
- :after_add => :log_after_add,
- :before_remove => :log_before_remove,
- :after_remove => :log_after_remove
- has_many :birds_with_proc_callbacks, :class_name => "Bird",
- :before_add => proc {|p,b| p.ship_log << "before_adding_proc_bird_#{b.id || '<new>'}"},
- :after_add => proc {|p,b| p.ship_log << "after_adding_proc_bird_#{b.id || '<new>'}"},
- :before_remove => proc {|p,b| p.ship_log << "before_removing_proc_bird_#{b.id}"},
- :after_remove => proc {|p,b| p.ship_log << "after_removing_proc_bird_#{b.id}"}
- has_many :birds_with_reject_all_blank, :class_name => "Bird"
+ has_one :update_only_ship, class_name: "Ship"
+ has_one :non_validated_ship, class_name: "Ship"
+ has_many :birds, -> { order("birds.id ASC") }
+ has_many :birds_with_method_callbacks, class_name: "Bird",
+ before_add: :log_before_add,
+ after_add: :log_after_add,
+ before_remove: :log_before_remove,
+ after_remove: :log_after_remove
+ has_many :birds_with_proc_callbacks, class_name: "Bird",
+ before_add: proc { |p, b| p.ship_log << "before_adding_proc_bird_#{b.id || '<new>'}" },
+ after_add: proc { |p, b| p.ship_log << "after_adding_proc_bird_#{b.id || '<new>'}" },
+ before_remove: proc { |p, b| p.ship_log << "before_removing_proc_bird_#{b.id}" },
+ after_remove: proc { |p, b| p.ship_log << "after_removing_proc_bird_#{b.id}" }
+ has_many :birds_with_reject_all_blank, class_name: "Bird"
- has_one :foo_bulb, -> { where :name => 'foo' }, :foreign_key => :car_id, :class_name => "Bulb"
+ has_one :foo_bulb, -> { where name: "foo" }, foreign_key: :car_id, class_name: "Bulb"
- accepts_nested_attributes_for :parrots, :birds, :allow_destroy => true, :reject_if => proc(&:empty?)
- accepts_nested_attributes_for :ship, :allow_destroy => true, :reject_if => proc(&:empty?)
- accepts_nested_attributes_for :update_only_ship, :update_only => true
+ accepts_nested_attributes_for :parrots, :birds, allow_destroy: true, reject_if: proc(&:empty?)
+ accepts_nested_attributes_for :ship, allow_destroy: true, reject_if: proc(&:empty?)
+ accepts_nested_attributes_for :update_only_ship, update_only: true
accepts_nested_attributes_for :parrots_with_method_callbacks, :parrots_with_proc_callbacks,
- :birds_with_method_callbacks, :birds_with_proc_callbacks, :allow_destroy => true
- accepts_nested_attributes_for :birds_with_reject_all_blank, :reject_if => :all_blank
+ :birds_with_method_callbacks, :birds_with_proc_callbacks, allow_destroy: true
+ accepts_nested_attributes_for :birds_with_reject_all_blank, reject_if: :all_blank
validates_presence_of :catchphrase
@@ -50,11 +50,11 @@ class Pirate < ActiveRecord::Base
end
def reject_empty_ships_on_create(attributes)
- attributes.delete('_reject_me_if_new').present? && !persisted?
+ attributes.delete("_reject_me_if_new").present? && !persisted?
end
attr_accessor :cancel_save_from_callback, :parrots_limit
- before_save :cancel_save_callback_method, :if => :cancel_save_from_callback
+ before_save :cancel_save_callback_method, if: :cancel_save_from_callback
def cancel_save_callback_method
throw(:abort)
end
@@ -82,11 +82,11 @@ class Pirate < ActiveRecord::Base
end
class DestructivePirate < Pirate
- has_one :dependent_ship, :class_name => 'Ship', :foreign_key => :pirate_id, :dependent => :destroy
+ has_one :dependent_ship, class_name: "Ship", foreign_key: :pirate_id, dependent: :destroy
end
class FamousPirate < ActiveRecord::Base
- self.table_name = 'pirates'
+ self.table_name = "pirates"
has_many :famous_ships
validates_presence_of :catchphrase, on: :conference
end
diff --git a/activerecord/test/models/possession.rb b/activerecord/test/models/possession.rb
index ddf759113b..0226336c16 100644
--- a/activerecord/test/models/possession.rb
+++ b/activerecord/test/models/possession.rb
@@ -1,3 +1,3 @@
class Possession < ActiveRecord::Base
- self.table_name = 'having'
+ self.table_name = "having"
end
diff --git a/activerecord/test/models/post.rb b/activerecord/test/models/post.rb
index bf3079a1df..e74aedb814 100644
--- a/activerecord/test/models/post.rb
+++ b/activerecord/test/models/post.rb
@@ -7,7 +7,7 @@ class Post < ActiveRecord::Base
module NamedExtension
def author
- 'lifo'
+ "lifo"
end
end
@@ -21,22 +21,23 @@ class Post < ActiveRecord::Base
scope :titled_with_an_apostrophe, -> { where("title LIKE '%''%'") }
scope :ranked_by_comments, -> { order("comments_count DESC") }
- scope :limit_by, lambda {|l| limit(l) }
+ scope :limit_by, lambda { |l| limit(l) }
belongs_to :author
+ belongs_to :readonly_author, -> { readonly }, class_name: "Author", foreign_key: :author_id
- belongs_to :author_with_posts, -> { includes(:posts) }, :class_name => "Author", :foreign_key => :author_id
- belongs_to :author_with_address, -> { includes(:author_address) }, :class_name => "Author", :foreign_key => :author_id
+ belongs_to :author_with_posts, -> { includes(:posts) }, class_name: "Author", foreign_key: :author_id
+ belongs_to :author_with_address, -> { includes(:author_address) }, class_name: "Author", foreign_key: :author_id
def first_comment
super.body
end
- has_one :first_comment, -> { order('id ASC') }, :class_name => 'Comment'
- has_one :last_comment, -> { order('id desc') }, :class_name => 'Comment'
+ has_one :first_comment, -> { order("id ASC") }, class_name: "Comment"
+ has_one :last_comment, -> { order("id desc") }, class_name: "Comment"
- scope :with_special_comments, -> { joins(:comments).where(:comments => {:type => 'SpecialComment'}) }
- scope :with_very_special_comments, -> { joins(:comments).where(:comments => {:type => 'VerySpecialComment'}) }
- scope :with_post, ->(post_id) { joins(:comments).where(:comments => { :post_id => post_id }) }
+ scope :with_special_comments, -> { joins(:comments).where(comments: { type: "SpecialComment" }) }
+ scope :with_very_special_comments, -> { joins(:comments).where(comments: { type: "VerySpecialComment" }) }
+ scope :with_post, ->(post_id) { joins(:comments).where(comments: { post_id: post_id }) }
scope :with_comments, -> { preload(:comments) }
scope :with_tags, -> { preload(:taggings) }
@@ -46,7 +47,7 @@ class Post < ActiveRecord::Base
scope :typographically_interesting, -> { containing_the_letter_a.or(titled_with_an_apostrophe) }
- has_many :comments do
+ has_many :comments do
def find_most_recent
order("id DESC").first
end
@@ -68,92 +69,92 @@ class Post < ActiveRecord::Base
has_many :comments_with_extend_2, extend: [NamedExtension, NamedExtension2], class_name: "Comment", foreign_key: "post_id"
- has_many :author_favorites, :through => :author
- has_many :author_categorizations, :through => :author, :source => :categorizations
- has_many :author_addresses, :through => :author
+ has_many :author_favorites, through: :author
+ has_many :author_categorizations, through: :author, source: :categorizations
+ has_many :author_addresses, through: :author
has_many :author_address_extra_with_address,
through: :author_with_address,
source: :author_address_extra
has_one :very_special_comment
- has_one :very_special_comment_with_post, -> { includes(:post) }, :class_name => "VerySpecialComment"
- has_one :very_special_comment_with_post_with_joins, -> { joins(:post).order('posts.id') }, class_name: "VerySpecialComment"
+ has_one :very_special_comment_with_post, -> { includes(:post) }, class_name: "VerySpecialComment"
+ has_one :very_special_comment_with_post_with_joins, -> { joins(:post).order("posts.id") }, class_name: "VerySpecialComment"
has_many :special_comments
- has_many :nonexistent_comments, -> { where 'comments.id < 0' }, :class_name => 'Comment'
+ has_many :nonexistent_comments, -> { where "comments.id < 0" }, class_name: "Comment"
- has_many :special_comments_ratings, :through => :special_comments, :source => :ratings
- has_many :special_comments_ratings_taggings, :through => :special_comments_ratings, :source => :taggings
+ has_many :special_comments_ratings, through: :special_comments, source: :ratings
+ has_many :special_comments_ratings_taggings, through: :special_comments_ratings, source: :taggings
- has_many :category_posts, :class_name => 'CategoryPost'
+ has_many :category_posts, class_name: "CategoryPost"
has_many :scategories, through: :category_posts, source: :category
has_and_belongs_to_many :categories
- has_and_belongs_to_many :special_categories, :join_table => "categories_posts", :association_foreign_key => 'category_id'
+ has_and_belongs_to_many :special_categories, join_table: "categories_posts", association_foreign_key: "category_id"
- has_many :taggings, :as => :taggable, :counter_cache => :tags_count
- has_many :tags, :through => :taggings do
+ has_many :taggings, as: :taggable, counter_cache: :tags_count
+ has_many :tags, through: :taggings do
def add_joins_and_select
- select('tags.*, authors.id as author_id')
- .joins('left outer join posts on taggings.taggable_id = posts.id left outer join authors on posts.author_id = authors.id')
+ select("tags.*, authors.id as author_id")
+ .joins("left outer join posts on taggings.taggable_id = posts.id left outer join authors on posts.author_id = authors.id")
.to_a
end
end
- has_many :taggings_with_delete_all, :class_name => 'Tagging', :as => :taggable, :dependent => :delete_all, counter_cache: :taggings_with_delete_all_count
- has_many :taggings_with_destroy, :class_name => 'Tagging', :as => :taggable, :dependent => :destroy, counter_cache: :taggings_with_destroy_count
+ has_many :taggings_with_delete_all, class_name: "Tagging", as: :taggable, dependent: :delete_all, counter_cache: :taggings_with_delete_all_count
+ has_many :taggings_with_destroy, class_name: "Tagging", as: :taggable, dependent: :destroy, counter_cache: :taggings_with_destroy_count
- has_many :tags_with_destroy, :through => :taggings, :source => :tag, :dependent => :destroy, counter_cache: :tags_with_destroy_count
- has_many :tags_with_nullify, :through => :taggings, :source => :tag, :dependent => :nullify, counter_cache: :tags_with_nullify_count
+ has_many :tags_with_destroy, through: :taggings, source: :tag, dependent: :destroy, counter_cache: :tags_with_destroy_count
+ has_many :tags_with_nullify, through: :taggings, source: :tag, dependent: :nullify, counter_cache: :tags_with_nullify_count
- has_many :misc_tags, -> { where :tags => { :name => 'Misc' } }, :through => :taggings, :source => :tag
- has_many :funky_tags, :through => :taggings, :source => :tag
- has_many :super_tags, :through => :taggings
- has_many :tags_with_primary_key, :through => :taggings, :source => :tag_with_primary_key
- has_one :tagging, :as => :taggable
+ has_many :misc_tags, -> { where tags: { name: "Misc" } }, through: :taggings, source: :tag
+ has_many :funky_tags, through: :taggings, source: :tag
+ has_many :super_tags, through: :taggings
+ has_many :tags_with_primary_key, through: :taggings, source: :tag_with_primary_key
+ has_one :tagging, as: :taggable
- has_many :first_taggings, -> { where :taggings => { :comment => 'first' } }, :as => :taggable, :class_name => 'Tagging'
- has_many :first_blue_tags, -> { where :tags => { :name => 'Blue' } }, :through => :first_taggings, :source => :tag
+ has_many :first_taggings, -> { where taggings: { comment: "first" } }, as: :taggable, class_name: "Tagging"
+ has_many :first_blue_tags, -> { where tags: { name: "Blue" } }, through: :first_taggings, source: :tag
- has_many :first_blue_tags_2, -> { where :taggings => { :comment => 'first' } }, :through => :taggings, :source => :blue_tag
+ has_many :first_blue_tags_2, -> { where taggings: { comment: "first" } }, through: :taggings, source: :blue_tag
- has_many :invalid_taggings, -> { where 'taggings.id < 0' }, :as => :taggable, :class_name => "Tagging"
- has_many :invalid_tags, :through => :invalid_taggings, :source => :tag
+ has_many :invalid_taggings, -> { where "taggings.id < 0" }, as: :taggable, class_name: "Tagging"
+ has_many :invalid_tags, through: :invalid_taggings, source: :tag
- has_many :categorizations, :foreign_key => :category_id
- has_many :authors, :through => :categorizations
+ has_many :categorizations, foreign_key: :category_id
+ has_many :authors, through: :categorizations
- has_many :categorizations_using_author_id, :primary_key => :author_id, :foreign_key => :post_id, :class_name => 'Categorization'
- has_many :authors_using_author_id, :through => :categorizations_using_author_id, :source => :author
+ has_many :categorizations_using_author_id, primary_key: :author_id, foreign_key: :post_id, class_name: "Categorization"
+ has_many :authors_using_author_id, through: :categorizations_using_author_id, source: :author
- has_many :taggings_using_author_id, :primary_key => :author_id, :as => :taggable, :class_name => 'Tagging'
- has_many :tags_using_author_id, :through => :taggings_using_author_id, :source => :tag
+ has_many :taggings_using_author_id, primary_key: :author_id, as: :taggable, class_name: "Tagging"
+ has_many :tags_using_author_id, through: :taggings_using_author_id, source: :tag
- has_many :images, :as => :imageable, :foreign_key => :imageable_identifier, :foreign_type => :imageable_class
- has_one :main_image, :as => :imageable, :foreign_key => :imageable_identifier, :foreign_type => :imageable_class, :class_name => 'Image'
+ has_many :images, as: :imageable, foreign_key: :imageable_identifier, foreign_type: :imageable_class
+ has_one :main_image, as: :imageable, foreign_key: :imageable_identifier, foreign_type: :imageable_class, class_name: "Image"
- has_many :standard_categorizations, :class_name => 'Categorization', :foreign_key => :post_id
- has_many :author_using_custom_pk, :through => :standard_categorizations
- has_many :authors_using_custom_pk, :through => :standard_categorizations
- has_many :named_categories, :through => :standard_categorizations
+ has_many :standard_categorizations, class_name: "Categorization", foreign_key: :post_id
+ has_many :author_using_custom_pk, through: :standard_categorizations
+ has_many :authors_using_custom_pk, through: :standard_categorizations
+ has_many :named_categories, through: :standard_categorizations
has_many :readers
has_many :secure_readers
- has_many :readers_with_person, -> { includes(:person) }, :class_name => "Reader"
- has_many :people, :through => :readers
- has_many :single_people, :through => :readers
- has_many :people_with_callbacks, :source=>:person, :through => :readers,
- :before_add => lambda {|owner, reader| log(:added, :before, reader.first_name) },
- :after_add => lambda {|owner, reader| log(:added, :after, reader.first_name) },
- :before_remove => lambda {|owner, reader| log(:removed, :before, reader.first_name) },
- :after_remove => lambda {|owner, reader| log(:removed, :after, reader.first_name) }
- has_many :skimmers, -> { where :skimmer => true }, :class_name => 'Reader'
- has_many :impatient_people, :through => :skimmers, :source => :person
+ has_many :readers_with_person, -> { includes(:person) }, class_name: "Reader"
+ has_many :people, through: :readers
+ has_many :single_people, through: :readers
+ has_many :people_with_callbacks, source: :person, through: :readers,
+ before_add: lambda { |owner, reader| log(:added, :before, reader.first_name) },
+ after_add: lambda { |owner, reader| log(:added, :after, reader.first_name) },
+ before_remove: lambda { |owner, reader| log(:removed, :before, reader.first_name) },
+ after_remove: lambda { |owner, reader| log(:removed, :after, reader.first_name) }
+ has_many :skimmers, -> { where skimmer: true }, class_name: "Reader"
+ has_many :impatient_people, through: :skimmers, source: :person
has_many :lazy_readers
- has_many :lazy_readers_skimmers_or_not, -> { where(skimmer: [ true, false ]) }, :class_name => 'LazyReader'
+ has_many :lazy_readers_skimmers_or_not, -> { where(skimmer: [ true, false ]) }, class_name: "LazyReader"
- has_many :lazy_people, :through => :lazy_readers, :source => :person
- has_many :lazy_readers_unscope_skimmers, -> { skimmers_or_not }, :class_name => 'LazyReader'
- has_many :lazy_people_unscope_skimmers, :through => :lazy_readers_unscope_skimmers, :source => :person
+ has_many :lazy_people, through: :lazy_readers, source: :person
+ has_many :lazy_readers_unscope_skimmers, -> { skimmers_or_not }, class_name: "LazyReader"
+ has_many :lazy_people_unscope_skimmers, through: :lazy_readers_unscope_skimmers, source: :person
def self.top(limit)
ranked_by_comments.limit_by(limit)
@@ -167,7 +168,7 @@ class Post < ActiveRecord::Base
@log = []
end
- def self.log(message=nil, side=nil, new_record=nil)
+ def self.log(message = nil, side = nil, new_record = nil)
return @log if message.nil?
@log << [message, side, new_record]
end
@@ -177,7 +178,7 @@ class SpecialPost < Post; end
class StiPost < Post
self.abstract_class = true
- has_one :special_comment, :class_name => "SpecialComment"
+ has_one :special_comment, class_name: "SpecialComment"
end
class SubStiPost < StiPost
@@ -186,56 +187,56 @@ end
class FirstPost < ActiveRecord::Base
self.inheritance_column = :disabled
- self.table_name = 'posts'
- default_scope { where(:id => 1) }
+ self.table_name = "posts"
+ default_scope { where(id: 1) }
- has_many :comments, :foreign_key => :post_id
- has_one :comment, :foreign_key => :post_id
+ has_many :comments, foreign_key: :post_id
+ has_one :comment, foreign_key: :post_id
end
class PostWithDefaultInclude < ActiveRecord::Base
self.inheritance_column = :disabled
- self.table_name = 'posts'
+ self.table_name = "posts"
default_scope { includes(:comments) }
- has_many :comments, :foreign_key => :post_id
+ has_many :comments, foreign_key: :post_id
end
class PostWithSpecialCategorization < Post
- has_many :categorizations, :foreign_key => :post_id
- default_scope { where(:type => 'PostWithSpecialCategorization').joins(:categorizations).where(:categorizations => { :special => true }) }
+ has_many :categorizations, foreign_key: :post_id
+ default_scope { where(type: "PostWithSpecialCategorization").joins(:categorizations).where(categorizations: { special: true }) }
end
class PostWithDefaultScope < ActiveRecord::Base
self.inheritance_column = :disabled
- self.table_name = 'posts'
+ self.table_name = "posts"
default_scope { order(:title) }
end
class PostWithPreloadDefaultScope < ActiveRecord::Base
- self.table_name = 'posts'
+ self.table_name = "posts"
- has_many :readers, foreign_key: 'post_id'
+ has_many :readers, foreign_key: "post_id"
default_scope { preload(:readers) }
end
class PostWithIncludesDefaultScope < ActiveRecord::Base
- self.table_name = 'posts'
+ self.table_name = "posts"
- has_many :readers, foreign_key: 'post_id'
+ has_many :readers, foreign_key: "post_id"
default_scope { includes(:readers) }
end
class SpecialPostWithDefaultScope < ActiveRecord::Base
self.inheritance_column = :disabled
- self.table_name = 'posts'
- default_scope { where(:id => [1, 5,6]) }
+ self.table_name = "posts"
+ default_scope { where(id: [1, 5, 6]) }
end
class PostThatLoadsCommentsInAnAfterSaveHook < ActiveRecord::Base
self.inheritance_column = :disabled
- self.table_name = 'posts'
+ self.table_name = "posts"
has_many :comments, class_name: "CommentThatAutomaticallyAltersPostBody", foreign_key: :post_id
after_save do |post|
@@ -245,7 +246,7 @@ end
class PostWithAfterCreateCallback < ActiveRecord::Base
self.inheritance_column = :disabled
- self.table_name = 'posts'
+ self.table_name = "posts"
has_many :comments, foreign_key: :post_id
after_create do |post|
@@ -255,7 +256,7 @@ end
class PostWithCommentWithDefaultScopeReferencesAssociation < ActiveRecord::Base
self.inheritance_column = :disabled
- self.table_name = 'posts'
+ self.table_name = "posts"
has_many :comment_with_default_scope_references_associations, foreign_key: :post_id
has_one :first_comment, class_name: "CommentWithDefaultScopeReferencesAssociation", foreign_key: :post_id
end
@@ -265,7 +266,7 @@ class SerializedPost < ActiveRecord::Base
end
class ConditionalStiPost < Post
- default_scope { where(title: 'Untitled') }
+ default_scope { where(title: "Untitled") }
end
class SubConditionalStiPost < ConditionalStiPost
diff --git a/activerecord/test/models/price_estimate.rb b/activerecord/test/models/price_estimate.rb
index d09e2a88a3..ce086e40a3 100644
--- a/activerecord/test/models/price_estimate.rb
+++ b/activerecord/test/models/price_estimate.rb
@@ -1,4 +1,4 @@
class PriceEstimate < ActiveRecord::Base
- belongs_to :estimate_of, :polymorphic => true
+ belongs_to :estimate_of, polymorphic: true
belongs_to :thing, polymorphic: true
end
diff --git a/activerecord/test/models/professor.rb b/activerecord/test/models/professor.rb
index 7654eda0ef..4dfb1a9602 100644
--- a/activerecord/test/models/professor.rb
+++ b/activerecord/test/models/professor.rb
@@ -1,4 +1,4 @@
-require_dependency 'models/arunit2_model'
+require_dependency "models/arunit2_model"
class Professor < ARUnit2Model
has_and_belongs_to_many :courses
diff --git a/activerecord/test/models/project.rb b/activerecord/test/models/project.rb
index efa8246f1e..5009f8f54b 100644
--- a/activerecord/test/models/project.rb
+++ b/activerecord/test/models/project.rb
@@ -1,17 +1,17 @@
class Project < ActiveRecord::Base
belongs_to :mentor
- has_and_belongs_to_many :developers, -> { distinct.order 'developers.name desc, developers.id desc' }
- has_and_belongs_to_many :readonly_developers, -> { readonly }, :class_name => "Developer"
- has_and_belongs_to_many :non_unique_developers, -> { order 'developers.name desc, developers.id desc' }, :class_name => 'Developer'
- has_and_belongs_to_many :limited_developers, -> { limit 1 }, :class_name => "Developer"
- has_and_belongs_to_many :developers_named_david, -> { where("name = 'David'").distinct }, :class_name => "Developer"
- has_and_belongs_to_many :developers_named_david_with_hash_conditions, -> { where(:name => 'David').distinct }, :class_name => "Developer"
- has_and_belongs_to_many :salaried_developers, -> { where "salary > 0" }, :class_name => "Developer"
- has_and_belongs_to_many :developers_with_callbacks, :class_name => "Developer", :before_add => Proc.new {|o, r| o.developers_log << "before_adding#{r.id || '<new>'}"},
- :after_add => Proc.new {|o, r| o.developers_log << "after_adding#{r.id || '<new>'}"},
- :before_remove => Proc.new {|o, r| o.developers_log << "before_removing#{r.id}"},
- :after_remove => Proc.new {|o, r| o.developers_log << "after_removing#{r.id}"}
- has_and_belongs_to_many :well_payed_salary_groups, -> { group("developers.salary").having("SUM(salary) > 10000").select("SUM(salary) as salary") }, :class_name => "Developer"
+ has_and_belongs_to_many :developers, -> { distinct.order "developers.name desc, developers.id desc" }
+ has_and_belongs_to_many :readonly_developers, -> { readonly }, class_name: "Developer"
+ has_and_belongs_to_many :non_unique_developers, -> { order "developers.name desc, developers.id desc" }, class_name: "Developer"
+ has_and_belongs_to_many :limited_developers, -> { limit 1 }, class_name: "Developer"
+ has_and_belongs_to_many :developers_named_david, -> { where("name = 'David'").distinct }, class_name: "Developer"
+ has_and_belongs_to_many :developers_named_david_with_hash_conditions, -> { where(name: "David").distinct }, class_name: "Developer"
+ has_and_belongs_to_many :salaried_developers, -> { where "salary > 0" }, class_name: "Developer"
+ has_and_belongs_to_many :developers_with_callbacks, class_name: "Developer", before_add: Proc.new { |o, r| o.developers_log << "before_adding#{r.id || '<new>'}" },
+ after_add: Proc.new { |o, r| o.developers_log << "after_adding#{r.id || '<new>'}" },
+ before_remove: Proc.new { |o, r| o.developers_log << "before_removing#{r.id}" },
+ after_remove: Proc.new { |o, r| o.developers_log << "after_removing#{r.id}" }
+ has_and_belongs_to_many :well_payed_salary_groups, -> { group("developers.salary").having("SUM(salary) > 10000").select("SUM(salary) as salary") }, class_name: "Developer"
belongs_to :firm
has_one :lead_developer, through: :firm, inverse_of: :contracted_projects
diff --git a/activerecord/test/models/rating.rb b/activerecord/test/models/rating.rb
index 25a52c4ad7..7420821db0 100644
--- a/activerecord/test/models/rating.rb
+++ b/activerecord/test/models/rating.rb
@@ -1,4 +1,4 @@
class Rating < ActiveRecord::Base
belongs_to :comment
- has_many :taggings, :as => :taggable
+ has_many :taggings, as: :taggable
end
diff --git a/activerecord/test/models/reader.rb b/activerecord/test/models/reader.rb
index 91afc1898c..7c5a159fe0 100644
--- a/activerecord/test/models/reader.rb
+++ b/activerecord/test/models/reader.rb
@@ -1,22 +1,22 @@
class Reader < ActiveRecord::Base
belongs_to :post
- belongs_to :person, :inverse_of => :readers
- belongs_to :single_person, :class_name => 'Person', :foreign_key => :person_id, :inverse_of => :reader
+ belongs_to :person, inverse_of: :readers
+ belongs_to :single_person, class_name: "Person", foreign_key: :person_id, inverse_of: :reader
belongs_to :first_post, -> { where(id: [2, 3]) }
end
class SecureReader < ActiveRecord::Base
self.table_name = "readers"
- belongs_to :secure_post, :class_name => "Post", :foreign_key => "post_id"
- belongs_to :secure_person, :inverse_of => :secure_readers, :class_name => "Person", :foreign_key => "person_id"
+ belongs_to :secure_post, class_name: "Post", foreign_key: "post_id"
+ belongs_to :secure_person, inverse_of: :secure_readers, class_name: "Person", foreign_key: "person_id"
end
class LazyReader < ActiveRecord::Base
self.table_name = "readers"
default_scope -> { where(skimmer: true) }
- scope :skimmers_or_not, -> { unscope(:where => :skimmer) }
+ scope :skimmers_or_not, -> { unscope(where: :skimmer) }
belongs_to :post
belongs_to :person
diff --git a/activerecord/test/models/reference.rb b/activerecord/test/models/reference.rb
index c2f9068f57..e2bb980fed 100644
--- a/activerecord/test/models/reference.rb
+++ b/activerecord/test/models/reference.rb
@@ -2,7 +2,7 @@ class Reference < ActiveRecord::Base
belongs_to :person
belongs_to :job
- has_many :agents_posts_authors, :through => :person
+ has_many :agents_posts_authors, through: :person
class << self; attr_accessor :make_comments; end
self.make_comments = false
@@ -17,6 +17,6 @@ class Reference < ActiveRecord::Base
end
class BadReference < ActiveRecord::Base
- self.table_name = 'references'
- default_scope { where(:favourite => false) }
+ self.table_name = "references"
+ default_scope { where(favourite: false) }
end
diff --git a/activerecord/test/models/reply.rb b/activerecord/test/models/reply.rb
index 3e82e55d89..a2d169292a 100644
--- a/activerecord/test/models/reply.rb
+++ b/activerecord/test/models/reply.rb
@@ -1,14 +1,14 @@
-require 'models/topic'
+require "models/topic"
class Reply < Topic
- belongs_to :topic, :foreign_key => "parent_id", :counter_cache => true
- belongs_to :topic_with_primary_key, :class_name => "Topic", :primary_key => "title", :foreign_key => "parent_title", :counter_cache => "replies_count"
- has_many :replies, :class_name => "SillyReply", :dependent => :destroy, :foreign_key => "parent_id"
+ belongs_to :topic, foreign_key: "parent_id", counter_cache: true
+ belongs_to :topic_with_primary_key, class_name: "Topic", primary_key: "title", foreign_key: "parent_title", counter_cache: "replies_count"
+ has_many :replies, class_name: "SillyReply", dependent: :destroy, foreign_key: "parent_id"
end
class UniqueReply < Reply
- belongs_to :topic, :foreign_key => 'parent_id', :counter_cache => true
- validates_uniqueness_of :content, :scope => 'parent_id'
+ belongs_to :topic, foreign_key: "parent_id", counter_cache: true
+ validates_uniqueness_of :content, scope: "parent_id"
end
class SillyUniqueReply < UniqueReply
@@ -16,12 +16,12 @@ end
class WrongReply < Reply
validate :errors_on_empty_content
- validate :title_is_wrong_create, :on => :create
+ validate :title_is_wrong_create, on: :create
validate :check_empty_title
- validate :check_content_mismatch, :on => :create
- validate :check_wrong_update, :on => :update
- validate :check_author_name_is_secret, :on => :special_case
+ validate :check_content_mismatch, on: :create
+ validate :check_wrong_update, on: :update
+ validate :check_author_name_is_secret, on: :special_case
def check_empty_title
errors[:title] << "Empty" unless attribute_present?("title")
@@ -51,11 +51,11 @@ class WrongReply < Reply
end
class SillyReply < Reply
- belongs_to :reply, :foreign_key => "parent_id", :counter_cache => :replies_count
+ belongs_to :reply, foreign_key: "parent_id", counter_cache: :replies_count
end
module Web
class Reply < Web::Topic
- belongs_to :topic, :foreign_key => "parent_id", :counter_cache => true, :class_name => 'Web::Topic'
+ belongs_to :topic, foreign_key: "parent_id", counter_cache: true, class_name: "Web::Topic"
end
end
diff --git a/activerecord/test/models/ship.rb b/activerecord/test/models/ship.rb
index e333b964ab..77a7b22315 100644
--- a/activerecord/test/models/ship.rb
+++ b/activerecord/test/models/ship.rb
@@ -2,19 +2,19 @@ class Ship < ActiveRecord::Base
self.record_timestamps = false
belongs_to :pirate
- belongs_to :update_only_pirate, :class_name => 'Pirate'
+ belongs_to :update_only_pirate, class_name: "Pirate"
belongs_to :developer, dependent: :destroy
- has_many :parts, :class_name => 'ShipPart'
+ has_many :parts, class_name: "ShipPart"
has_many :treasures
- accepts_nested_attributes_for :parts, :allow_destroy => true
- accepts_nested_attributes_for :pirate, :allow_destroy => true, :reject_if => proc(&:empty?)
- accepts_nested_attributes_for :update_only_pirate, :update_only => true
+ accepts_nested_attributes_for :parts, allow_destroy: true
+ accepts_nested_attributes_for :pirate, allow_destroy: true, reject_if: proc(&:empty?)
+ accepts_nested_attributes_for :update_only_pirate, update_only: true
validates_presence_of :name
attr_accessor :cancel_save_from_callback
- before_save :cancel_save_callback_method, :if => :cancel_save_from_callback
+ before_save :cancel_save_callback_method, if: :cancel_save_from_callback
def cancel_save_callback_method
throw(:abort)
end
@@ -33,7 +33,7 @@ class Prisoner < ActiveRecord::Base
end
class FamousShip < ActiveRecord::Base
- self.table_name = 'ships'
+ self.table_name = "ships"
belongs_to :famous_pirate
validates_presence_of :name, on: :conference
end
diff --git a/activerecord/test/models/ship_part.rb b/activerecord/test/models/ship_part.rb
index 05c65f8a4a..1a633b8d77 100644
--- a/activerecord/test/models/ship_part.rb
+++ b/activerecord/test/models/ship_part.rb
@@ -1,7 +1,7 @@
class ShipPart < ActiveRecord::Base
belongs_to :ship
- has_many :trinkets, :class_name => "Treasure", :as => :looter
- accepts_nested_attributes_for :trinkets, :allow_destroy => true
+ has_many :trinkets, class_name: "Treasure", as: :looter
+ accepts_nested_attributes_for :trinkets, allow_destroy: true
accepts_nested_attributes_for :ship
validates_presence_of :name
diff --git a/activerecord/test/models/shop.rb b/activerecord/test/models/shop.rb
index 607a0a5b41..f9d23d13b0 100644
--- a/activerecord/test/models/shop.rb
+++ b/activerecord/test/models/shop.rb
@@ -1,10 +1,10 @@
module Shop
class Collection < ActiveRecord::Base
- has_many :products, :dependent => :nullify
+ has_many :products, dependent: :nullify
end
class Product < ActiveRecord::Base
- has_many :variants, :dependent => :delete_all
+ has_many :variants, dependent: :delete_all
belongs_to :type
class Type < ActiveRecord::Base
diff --git a/activerecord/test/models/sponsor.rb b/activerecord/test/models/sponsor.rb
index ec3dcf8a97..3f142b25fe 100644
--- a/activerecord/test/models/sponsor.rb
+++ b/activerecord/test/models/sponsor.rb
@@ -1,7 +1,7 @@
class Sponsor < ActiveRecord::Base
- belongs_to :sponsor_club, :class_name => "Club", :foreign_key => "club_id"
- belongs_to :sponsorable, :polymorphic => true
- belongs_to :thing, :polymorphic => true, :foreign_type => :sponsorable_type, :foreign_key => :sponsorable_id
- belongs_to :sponsorable_with_conditions, -> { where :name => 'Ernie'}, :polymorphic => true,
- :foreign_type => 'sponsorable_type', :foreign_key => 'sponsorable_id'
+ belongs_to :sponsor_club, class_name: "Club", foreign_key: "club_id"
+ belongs_to :sponsorable, polymorphic: true
+ belongs_to :thing, polymorphic: true, foreign_type: :sponsorable_type, foreign_key: :sponsorable_id
+ belongs_to :sponsorable_with_conditions, -> { where name: "Ernie" }, polymorphic: true,
+ foreign_type: "sponsorable_type", foreign_key: "sponsorable_id"
end
diff --git a/activerecord/test/models/subject.rb b/activerecord/test/models/subject.rb
index 8e28f8b86b..504f68a296 100644
--- a/activerecord/test/models/subject.rb
+++ b/activerecord/test/models/subject.rb
@@ -1,16 +1,14 @@
# used for OracleSynonymTest, see test/synonym_test_oracle.rb
#
class Subject < ActiveRecord::Base
-
# added initialization of author_email_address in the same way as in Topic class
# as otherwise synonym test was failing
after_initialize :set_email_address
- protected
+ private
def set_email_address
- unless self.persisted?
- self.author_email_address = 'test@test.com'
+ unless persisted?
+ self.author_email_address = "test@test.com"
end
end
-
end
diff --git a/activerecord/test/models/subscriber.rb b/activerecord/test/models/subscriber.rb
index 76e85a0cd3..a820329003 100644
--- a/activerecord/test/models/subscriber.rb
+++ b/activerecord/test/models/subscriber.rb
@@ -1,7 +1,7 @@
class Subscriber < ActiveRecord::Base
- self.primary_key = 'nick'
+ self.primary_key = "nick"
has_many :subscriptions
- has_many :books, :through => :subscriptions
+ has_many :books, through: :subscriptions
end
class SpecialSubscriber < Subscriber
diff --git a/activerecord/test/models/subscription.rb b/activerecord/test/models/subscription.rb
index bcac4738a3..1cedf6deae 100644
--- a/activerecord/test/models/subscription.rb
+++ b/activerecord/test/models/subscription.rb
@@ -1,4 +1,4 @@
class Subscription < ActiveRecord::Base
- belongs_to :subscriber, :counter_cache => :books_count
+ belongs_to :subscriber, counter_cache: :books_count
belongs_to :book
end
diff --git a/activerecord/test/models/tag.rb b/activerecord/test/models/tag.rb
index b48b9a2155..c907aea10f 100644
--- a/activerecord/test/models/tag.rb
+++ b/activerecord/test/models/tag.rb
@@ -1,13 +1,13 @@
class Tag < ActiveRecord::Base
has_many :taggings
- has_many :taggables, :through => :taggings
+ has_many :taggables, through: :taggings
has_one :tagging
- has_many :tagged_posts, :through => :taggings, :source => 'taggable', :source_type => 'Post'
+ has_many :tagged_posts, through: :taggings, source: "taggable", source_type: "Post"
end
class OrderedTag < Tag
self.table_name = "tags"
- has_many :taggings, -> { order('taggings.id DESC') }, foreign_key: 'tag_id'
+ has_many :taggings, -> { order("taggings.id DESC") }, foreign_key: "tag_id"
end
diff --git a/activerecord/test/models/tagging.rb b/activerecord/test/models/tagging.rb
index a6c05da26a..f739b4a197 100644
--- a/activerecord/test/models/tagging.rb
+++ b/activerecord/test/models/tagging.rb
@@ -4,10 +4,10 @@ end
class Tagging < ActiveRecord::Base
belongs_to :tag, -> { includes(:tagging) }
- belongs_to :super_tag, :class_name => 'Tag', :foreign_key => 'super_tag_id'
- belongs_to :invalid_tag, :class_name => 'Tag', :foreign_key => 'tag_id'
- belongs_to :blue_tag, -> { where :tags => { :name => 'Blue' } }, :class_name => 'Tag', :foreign_key => :tag_id
- belongs_to :tag_with_primary_key, :class_name => 'Tag', :foreign_key => :tag_id, :primary_key => :custom_primary_key
- belongs_to :taggable, :polymorphic => true, :counter_cache => :tags_count
- has_many :things, :through => :taggable
+ belongs_to :super_tag, class_name: "Tag", foreign_key: "super_tag_id"
+ belongs_to :invalid_tag, class_name: "Tag", foreign_key: "tag_id"
+ belongs_to :blue_tag, -> { where tags: { name: "Blue" } }, class_name: "Tag", foreign_key: :tag_id
+ belongs_to :tag_with_primary_key, class_name: "Tag", foreign_key: :tag_id, primary_key: :custom_primary_key
+ belongs_to :taggable, polymorphic: true, counter_cache: :tags_count
+ has_many :things, through: :taggable
end
diff --git a/activerecord/test/models/topic.rb b/activerecord/test/models/topic.rb
index 176bc79dc7..0420e2d15c 100644
--- a/activerecord/test/models/topic.rb
+++ b/activerecord/test/models/topic.rb
@@ -2,18 +2,18 @@ class Topic < ActiveRecord::Base
scope :base, -> { all }
scope :written_before, lambda { |time|
if time
- where 'written_on < ?', time
+ where "written_on < ?", time
end
}
- scope :approved, -> { where(:approved => true) }
- scope :rejected, -> { where(:approved => false) }
+ scope :approved, -> { where(approved: true) }
+ scope :rejected, -> { where(approved: false) }
scope :scope_with_lambda, lambda { all }
- scope :by_lifo, -> { where(:author_name => 'lifo') }
- scope :replied, -> { where 'replies_count > 0' }
+ scope :by_lifo, -> { where(author_name: "lifo") }
+ scope :replied, -> { where "replies_count > 0" }
- scope 'approved_as_string', -> { where(:approved => true) }
+ scope "approved_as_string", -> { where(approved: true) }
scope :anonymous_extension, -> { all } do
def one
1
@@ -22,7 +22,7 @@ class Topic < ActiveRecord::Base
scope :with_object, Class.new(Struct.new(:klass)) {
def call
- klass.where(:approved => true)
+ klass.where(approved: true)
end
}.new(self)
@@ -33,22 +33,16 @@ class Topic < ActiveRecord::Base
end
has_many :replies, dependent: :destroy, foreign_key: "parent_id", autosave: true
- has_many :approved_replies, -> { approved }, class_name: 'Reply', foreign_key: "parent_id", counter_cache: 'replies_count'
+ has_many :approved_replies, -> { approved }, class_name: "Reply", foreign_key: "parent_id", counter_cache: "replies_count"
- has_many :unique_replies, :dependent => :destroy, :foreign_key => "parent_id"
- has_many :silly_unique_replies, :dependent => :destroy, :foreign_key => "parent_id"
+ has_many :unique_replies, dependent: :destroy, foreign_key: "parent_id"
+ has_many :silly_unique_replies, dependent: :destroy, foreign_key: "parent_id"
serialize :content
before_create :default_written_on
before_destroy :destroy_children
- # Explicitly define as :date column so that returned Oracle DATE values would be typecasted to Date and not Time.
- # Some tests depend on assumption that this attribute will have Date values.
- if current_adapter?(:OracleEnhancedAdapter)
- set_date_columns :last_read
- end
-
def parent
Topic.find(parent_id)
end
@@ -79,7 +73,7 @@ class Topic < ActiveRecord::Base
write_attribute(:approved, val)
end
- protected
+ private
def default_written_on
self.written_on = Time.now unless attribute_present?("written_on")
@@ -90,8 +84,8 @@ class Topic < ActiveRecord::Base
end
def set_email_address
- unless self.persisted?
- self.author_email_address = 'test@test.com'
+ unless persisted?
+ self.author_email_address = "test@test.com"
end
end
@@ -119,6 +113,6 @@ end
module Web
class Topic < ActiveRecord::Base
- has_many :replies, :dependent => :destroy, :foreign_key => "parent_id", :class_name => 'Web::Reply'
+ has_many :replies, dependent: :destroy, foreign_key: "parent_id", class_name: "Web::Reply"
end
end
diff --git a/activerecord/test/models/treasure.rb b/activerecord/test/models/treasure.rb
index 63ff0c23ec..fb2a5d44e2 100644
--- a/activerecord/test/models/treasure.rb
+++ b/activerecord/test/models/treasure.rb
@@ -1,11 +1,11 @@
class Treasure < ActiveRecord::Base
has_and_belongs_to_many :parrots
- belongs_to :looter, :polymorphic => true
+ belongs_to :looter, polymorphic: true
# No counter_cache option given
belongs_to :ship
- has_many :price_estimates, :as => :estimate_of
- has_and_belongs_to_many :rich_people, join_table: 'peoples_treasures', validate: false
+ has_many :price_estimates, as: :estimate_of
+ has_and_belongs_to_many :rich_people, join_table: "peoples_treasures", validate: false
accepts_nested_attributes_for :looter
end
diff --git a/activerecord/test/models/treaty.rb b/activerecord/test/models/treaty.rb
index 41fd1350f3..373cd48f71 100644
--- a/activerecord/test/models/treaty.rb
+++ b/activerecord/test/models/treaty.rb
@@ -1,7 +1,5 @@
class Treaty < ActiveRecord::Base
-
self.primary_key = :treaty_id
has_and_belongs_to_many :countries
-
end
diff --git a/activerecord/test/models/user.rb b/activerecord/test/models/user.rb
index f5dc93e994..47649e0a77 100644
--- a/activerecord/test/models/user.rb
+++ b/activerecord/test/models/user.rb
@@ -1,6 +1,12 @@
+require "models/job"
+
class User < ActiveRecord::Base
has_secure_token
has_secure_token :auth_token
+
+ has_and_belongs_to_many :jobs_pool,
+ class_name: Job,
+ join_table: "jobs_pool"
end
class UserWithNotification < User
diff --git a/activerecord/test/models/vegetables.rb b/activerecord/test/models/vegetables.rb
index 1f41cde3a5..a4590d06e0 100644
--- a/activerecord/test/models/vegetables.rb
+++ b/activerecord/test/models/vegetables.rb
@@ -1,9 +1,8 @@
class Vegetable < ActiveRecord::Base
-
validates_presence_of :name
def self.inheritance_column
- 'custom_type'
+ "custom_type"
end
end
@@ -20,5 +19,5 @@ class KingCole < GreenCabbage
end
class RedCabbage < Cabbage
- belongs_to :seller, :class_name => 'Company'
+ belongs_to :seller, class_name: "Company"
end
diff --git a/activerecord/test/models/vehicle.rb b/activerecord/test/models/vehicle.rb
index ef26170f1f..855bc4e325 100644
--- a/activerecord/test/models/vehicle.rb
+++ b/activerecord/test/models/vehicle.rb
@@ -4,4 +4,4 @@ class Vehicle < ActiveRecord::Base
end
class Bus < Vehicle
-end \ No newline at end of file
+end
diff --git a/activerecord/test/models/vertex.rb b/activerecord/test/models/vertex.rb
index 48bb851e62..3d19433b6f 100644
--- a/activerecord/test/models/vertex.rb
+++ b/activerecord/test/models/vertex.rb
@@ -1,9 +1,9 @@
# This class models a vertex in a directed graph.
class Vertex < ActiveRecord::Base
- has_many :sink_edges, :class_name => 'Edge', :foreign_key => 'source_id'
- has_many :sinks, :through => :sink_edges
+ has_many :sink_edges, class_name: "Edge", foreign_key: "source_id"
+ has_many :sinks, through: :sink_edges
has_and_belongs_to_many :sources,
- :class_name => 'Vertex', :join_table => 'edges',
- :foreign_key => 'sink_id', :association_foreign_key => 'source_id'
+ class_name: "Vertex", join_table: "edges",
+ foreign_key: "sink_id", association_foreign_key: "source_id"
end
diff --git a/activerecord/test/models/wheel.rb b/activerecord/test/models/wheel.rb
index 26868bce5e..cba2b3e518 100644
--- a/activerecord/test/models/wheel.rb
+++ b/activerecord/test/models/wheel.rb
@@ -1,3 +1,3 @@
class Wheel < ActiveRecord::Base
- belongs_to :wheelable, :polymorphic => true, :counter_cache => true
+ belongs_to :wheelable, polymorphic: true, counter_cache: true
end
diff --git a/activerecord/test/models/without_table.rb b/activerecord/test/models/without_table.rb
index 50c824e4ac..7c0fc286e1 100644
--- a/activerecord/test/models/without_table.rb
+++ b/activerecord/test/models/without_table.rb
@@ -1,3 +1,3 @@
class WithoutTable < ActiveRecord::Base
- default_scope -> { where(:published => true) }
+ default_scope -> { where(published: true) }
end
diff --git a/activerecord/test/models/zine.rb b/activerecord/test/models/zine.rb
index c2d0fdaf25..3f2b348b46 100644
--- a/activerecord/test/models/zine.rb
+++ b/activerecord/test/models/zine.rb
@@ -1,3 +1,3 @@
class Zine < ActiveRecord::Base
- has_many :interests, :inverse_of => :zine
+ has_many :interests, inverse_of: :zine
end
diff --git a/activerecord/test/schema/mysql2_specific_schema.rb b/activerecord/test/schema/mysql2_specific_schema.rb
index 5a49b38457..9a203a7293 100644
--- a/activerecord/test/schema/mysql2_specific_schema.rb
+++ b/activerecord/test/schema/mysql2_specific_schema.rb
@@ -1,8 +1,8 @@
ActiveRecord::Schema.define do
- if ActiveRecord::Base.connection.version >= '5.6.0'
+ if ActiveRecord::Base.connection.version >= "5.6.0"
create_table :datetime_defaults, force: true do |t|
- t.datetime :modified_datetime, default: -> { 'CURRENT_TIMESTAMP' }
+ t.datetime :modified_datetime, default: -> { "CURRENT_TIMESTAMP" }
end
end
@@ -21,18 +21,18 @@ ActiveRecord::Schema.define do
t.index :var_binary
end
- create_table :key_tests, force: true, options: 'ENGINE=MyISAM' do |t|
+ create_table :key_tests, force: true, options: "ENGINE=MyISAM" do |t|
t.string :awesome
t.string :pizza
t.string :snacks
- t.index :awesome, type: :fulltext, name: 'index_key_tests_on_awesome'
- t.index :pizza, using: :btree, name: 'index_key_tests_on_pizza'
- t.index :snacks, name: 'index_key_tests_on_snack'
+ t.index :awesome, type: :fulltext, name: "index_key_tests_on_awesome"
+ t.index :pizza, using: :btree, name: "index_key_tests_on_pizza"
+ t.index :snacks, name: "index_key_tests_on_snack"
end
create_table :collation_tests, id: false, force: true do |t|
- t.string :string_cs_column, limit: 1, collation: 'utf8_bin'
- t.string :string_ci_column, limit: 1, collation: 'utf8_general_ci'
+ t.string :string_cs_column, limit: 1, collation: "utf8_bin"
+ t.string :string_ci_column, limit: 1, collation: "utf8_general_ci"
t.binary :binary_column, limit: 1
end
diff --git a/activerecord/test/schema/postgresql_specific_schema.rb b/activerecord/test/schema/postgresql_specific_schema.rb
index 24713f722a..15ba2d67ab 100644
--- a/activerecord/test/schema/postgresql_specific_schema.rb
+++ b/activerecord/test/schema/postgresql_specific_schema.rb
@@ -1,6 +1,7 @@
ActiveRecord::Schema.define do
- enable_extension!('uuid-ossp', ActiveRecord::Base.connection)
+ enable_extension!("uuid-ossp", ActiveRecord::Base.connection)
+ enable_extension!("pgcrypto", ActiveRecord::Base.connection) if ActiveRecord::Base.connection.supports_pgcrypto_uuid?
create_table :uuid_parents, id: :uuid, force: true do |t|
t.string :name
@@ -12,16 +13,16 @@ ActiveRecord::Schema.define do
end
create_table :defaults, force: true do |t|
- t.date :modified_date, default: -> { 'CURRENT_DATE' }
- t.date :modified_date_function, default: -> { 'now()' }
- t.date :fixed_date, default: '2004-01-01'
- t.datetime :modified_time, default: -> { 'CURRENT_TIMESTAMP' }
- t.datetime :modified_time_function, default: -> { 'now()' }
- t.datetime :fixed_time, default: '2004-01-01 00:00:00.000000-00'
- t.column :char1, 'char(1)', default: 'Y'
- t.string :char2, limit: 50, default: 'a varchar field'
- t.text :char3, default: 'a text field'
- t.bigint :bigint_default, default: -> { '0::bigint' }
+ t.date :modified_date, default: -> { "CURRENT_DATE" }
+ t.date :modified_date_function, default: -> { "now()" }
+ t.date :fixed_date, default: "2004-01-01"
+ t.datetime :modified_time, default: -> { "CURRENT_TIMESTAMP" }
+ t.datetime :modified_time_function, default: -> { "now()" }
+ t.datetime :fixed_time, default: "2004-01-01 00:00:00.000000-00"
+ t.column :char1, "char(1)", default: "Y"
+ t.string :char2, limit: 50, default: "a varchar field"
+ t.text :char3, default: "a text field"
+ t.bigint :bigint_default, default: -> { "0::bigint" }
t.text :multiline_default, default: '--- []
'
@@ -32,12 +33,12 @@ ActiveRecord::Schema.define do
drop_table table_name, if_exists: true
end
- execute 'DROP SEQUENCE IF EXISTS companies_nonstd_seq CASCADE'
- execute 'CREATE SEQUENCE companies_nonstd_seq START 101 OWNED BY companies.id'
+ execute "DROP SEQUENCE IF EXISTS companies_nonstd_seq CASCADE"
+ execute "CREATE SEQUENCE companies_nonstd_seq START 101 OWNED BY companies.id"
execute "ALTER TABLE companies ALTER COLUMN id SET DEFAULT nextval('companies_nonstd_seq')"
- execute 'DROP SEQUENCE IF EXISTS companies_id_seq'
+ execute "DROP SEQUENCE IF EXISTS companies_id_seq"
- execute 'DROP FUNCTION IF EXISTS partitioned_insert_trigger()'
+ execute "DROP FUNCTION IF EXISTS partitioned_insert_trigger()"
%w(accounts_id_seq developers_id_seq projects_id_seq topics_id_seq customers_id_seq orders_id_seq).each do |seq_name|
execute "SELECT setval('#{seq_name}', 100)"
@@ -88,7 +89,7 @@ _SQL
FOR EACH ROW EXECUTE PROCEDURE partitioned_insert_trigger();
_SQL
rescue ActiveRecord::StatementInvalid => e
- if e.message =~ /language "plpgsql" does not exist/
+ if e.message.include?('language "plpgsql" does not exist')
execute "CREATE LANGUAGE 'plpgsql';"
retry
else
diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb
index 2f2993ce18..ba6f5de894 100644
--- a/activerecord/test/schema/schema.rb
+++ b/activerecord/test/schema/schema.rb
@@ -21,7 +21,7 @@ ActiveRecord::Schema.define do
t.string :settings, null: true, limit: 1024
# MySQL does not allow default values for blobs. Fake it out with a
# big varchar below.
- t.string :preferences, null: true, default: '', limit: 1024
+ t.string :preferences, null: true, default: "", limit: 1024
t.string :json_data, null: true, limit: 1024
t.string :json_data_empty, null: true, default: "", limit: 1024
t.text :params
@@ -54,7 +54,7 @@ ActiveRecord::Schema.define do
create_table :authors, force: true do |t|
t.string :name, null: false
- t.integer :author_address_id
+ t.bigint :author_address_id
t.integer :author_address_extra_id
t.string :organization_id
t.string :owned_essay_id
@@ -98,7 +98,8 @@ ActiveRecord::Schema.define do
t.column :author_visibility, :integer, default: 0
t.column :illustrator_visibility, :integer, default: 0
t.column :font_size, :integer, default: 0
- t.column :cover, :string, default: 'hard'
+ t.column :difficulty, :integer, default: 0
+ t.column :cover, :string, default: "hard"
end
create_table :booleans, force: true do |t|
@@ -125,6 +126,9 @@ ActiveRecord::Schema.define do
t.timestamps null: false
end
+ create_table :old_cars, id: :integer, force: true do |t|
+ end
+
create_table :carriers, force: true
create_table :categories, force: true do |t|
@@ -196,10 +200,10 @@ ActiveRecord::Schema.define do
t.integer :rating, default: 1
t.integer :account_id
t.string :description, default: ""
- t.index [:firm_id, :type, :rating], name: "company_index"
+ t.index [:firm_id, :type, :rating], name: "company_index", length: { type: 10 }, order: { rating: :desc }
t.index [:firm_id, :type], name: "company_partial_index", where: "rating > 10"
- t.index :name, name: 'company_name_index', using: :btree
- t.index 'lower(name)', name: "company_expression_index" if supports_expression_index?
+ t.index :name, name: "company_name_index", using: :btree
+ t.index "lower(name)", name: "company_expression_index" if supports_expression_index?
end
create_table :content, force: true do |t|
@@ -298,11 +302,11 @@ ActiveRecord::Schema.define do
create_table :edges, force: true, id: false do |t|
t.column :source_id, :integer, null: false
t.column :sink_id, :integer, null: false
- t.index [:source_id, :sink_id], unique: true, name: 'unique_edge_index'
+ t.index [:source_id, :sink_id], unique: true, name: "unique_edge_index"
end
create_table :engines, force: true do |t|
- t.integer :car_id
+ t.bigint :car_id
end
create_table :entrants, force: true do |t|
@@ -390,6 +394,11 @@ ActiveRecord::Schema.define do
t.integer :ideal_reference_id
end
+ create_table :jobs_pool, force: true, id: false do |t|
+ t.references :job, null: false, index: true
+ t.references :user, null: false, index: true
+ end
+
create_table :keyboards, force: true, id: false do |t|
t.primary_key :key_number
t.string :name
@@ -430,10 +439,12 @@ ActiveRecord::Schema.define do
end
create_table :lock_without_defaults, force: true do |t|
+ t.column :title, :string
t.column :lock_version, :integer
end
create_table :lock_without_defaults_cust, force: true do |t|
+ t.column :title, :string
t.column :custom_lock_version, :integer
end
@@ -563,6 +574,7 @@ ActiveRecord::Schema.define do
t.column :color, :string
t.column :parrot_sti_class, :string
t.column :killer_id, :integer
+ t.column :updated_count, :integer, default: 0
if subsecond_precision_supported?
t.column :created_at, :datetime, precision: 0
t.column :created_on, :datetime, precision: 0
@@ -889,15 +901,14 @@ ActiveRecord::Schema.define do
t.column :label, :string
end
- create_table 'warehouse-things', force: true do |t|
+ create_table "warehouse-things", force: true do |t|
t.integer :value
end
[:circles, :squares, :triangles, :non_poly_ones, :non_poly_twos].each do |t|
- create_table(t, force: true) { }
+ create_table(t, force: true) {}
end
- # NOTE - the following 4 tables are used by models that have :inverse_of options on the associations
create_table :men, force: true do |t|
t.string :name
end
@@ -921,19 +932,19 @@ ActiveRecord::Schema.define do
t.integer :zine_id
end
- create_table :wheels, force: true do |t|
- t.references :wheelable, polymorphic: true
- end
-
create_table :zines, force: true do |t|
t.string :title
end
- create_table :countries, force: true, id: false, primary_key: 'country_id' do |t|
+ create_table :wheels, force: true do |t|
+ t.references :wheelable, polymorphic: true
+ end
+
+ create_table :countries, force: true, id: false, primary_key: "country_id" do |t|
t.string :country_id
t.string :name
end
- create_table :treaties, force: true, id: false, primary_key: 'treaty_id' do |t|
+ create_table :treaties, force: true, id: false, primary_key: "treaty_id" do |t|
t.string :treaty_id
t.string :name
end
@@ -954,9 +965,9 @@ ActiveRecord::Schema.define do
t.string :name
end
create_table :weirds, force: true do |t|
- t.string 'a$b'
- t.string 'なまえ'
- t.string 'from'
+ t.string "a$b"
+ t.string "なまえ"
+ t.string "from"
end
create_table :nodes, force: true do |t|
@@ -997,7 +1008,7 @@ ActiveRecord::Schema.define do
if supports_foreign_keys?
# fk_test_has_fk should be before fk_test_has_pk
create_table :fk_test_has_fk, force: true do |t|
- t.integer :fk_id, null: false
+ t.bigint :fk_id, null: false
end
create_table :fk_test_has_pk, force: true, primary_key: "pk_id" do |t|
@@ -1010,7 +1021,7 @@ ActiveRecord::Schema.define do
t.float :overloaded_float, default: 500
t.float :unoverloaded_float
t.string :overloaded_string_with_limit, limit: 255
- t.string :string_with_default, default: 'the original default'
+ t.string :string_with_default, default: "the original default"
end
create_table :users, force: true do |t|
@@ -1040,3 +1051,5 @@ Professor.connection.create_table :courses_professors, id: false, force: true do
t.references :course
t.references :professor
end
+
+OtherDog.connection.create_table :dogs, force: true
diff --git a/activerecord/test/support/config.rb b/activerecord/test/support/config.rb
index 6d123688a3..aaff408b41 100644
--- a/activerecord/test/support/config.rb
+++ b/activerecord/test/support/config.rb
@@ -1,7 +1,7 @@
-require 'yaml'
-require 'erubis'
-require 'fileutils'
-require 'pathname'
+require "yaml"
+require "erb"
+require "fileutils"
+require "pathname"
module ARTest
class << self
@@ -12,28 +12,29 @@ module ARTest
private
def config_file
- Pathname.new(ENV['ARCONFIG'] || TEST_ROOT + '/config.yml')
+ Pathname.new(ENV["ARCONFIG"] || TEST_ROOT + "/config.yml")
end
def read_config
unless config_file.exist?
- FileUtils.cp TEST_ROOT + '/config.example.yml', config_file
+ FileUtils.cp TEST_ROOT + "/config.example.yml", config_file
end
- erb = Erubis::Eruby.new(config_file.read)
+ erb = ERB.new(config_file.read)
expand_config(YAML.parse(erb.result(binding)).transform)
end
def expand_config(config)
- config['connections'].each do |adapter, connection|
- dbs = [['arunit', 'activerecord_unittest'], ['arunit2', 'activerecord_unittest2']]
+ config["connections"].each do |adapter, connection|
+ dbs = [["arunit", "activerecord_unittest"], ["arunit2", "activerecord_unittest2"],
+ ["arunit_without_prepared_statements", "activerecord_unittest"]]
dbs.each do |name, dbname|
unless connection[name].is_a?(Hash)
- connection[name] = { 'database' => connection[name] }
+ connection[name] = { "database" => connection[name] }
end
- connection[name]['database'] ||= dbname
- connection[name]['adapter'] ||= adapter
+ connection[name]["database"] ||= dbname
+ connection[name]["adapter"] ||= adapter
end
end
diff --git a/activerecord/test/support/connection.rb b/activerecord/test/support/connection.rb
index c5334e8596..bc5af36a28 100644
--- a/activerecord/test/support/connection.rb
+++ b/activerecord/test/support/connection.rb
@@ -1,15 +1,16 @@
-require 'active_support/logger'
-require 'models/college'
-require 'models/course'
-require 'models/professor'
+require "active_support/logger"
+require "models/college"
+require "models/course"
+require "models/professor"
+require "models/other_dog"
module ARTest
def self.connection_name
- ENV['ARCONN'] || config['default_connection']
+ ENV["ARCONN"] || config["default_connection"]
end
def self.connection_config
- config['connections'][connection_name]
+ config["connections"][connection_name]
end
def self.connect