aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.github/autolabeler.yml24
-rw-r--r--.github/no-response.yml13
-rw-r--r--.gitignore2
-rw-r--r--.rubocop.yml11
-rw-r--r--.travis.yml53
-rw-r--r--.yarnrc2
-rw-r--r--Gemfile3
-rw-r--r--Gemfile.lock34
-rw-r--r--actioncable/lib/action_cable.rb12
-rw-r--r--actioncable/lib/action_cable/channel.rb1
-rw-r--r--actioncable/lib/action_cable/channel/base.rb2
-rw-r--r--actioncable/lib/action_cable/channel/test_case.rb271
-rw-r--r--actioncable/lib/rails/generators/channel/USAGE8
-rw-r--r--actioncable/lib/rails/generators/channel/channel_generator.rb5
-rw-r--r--actioncable/lib/rails/generators/channel/templates/assets/channel.coffee.tt14
-rw-r--r--actioncable/lib/rails/generators/channel/templates/javascript/channel.js.tt (renamed from actioncable/lib/rails/generators/channel/templates/assets/channel.js.tt)4
-rw-r--r--actioncable/lib/rails/generators/channel/templates/javascript/consumer.js.tt (renamed from actioncable/lib/rails/generators/channel/templates/assets/cable.js.tt)11
-rw-r--r--actioncable/lib/rails/generators/channel/templates/javascript/index.js.tt5
-rw-r--r--actioncable/test/channel/stream_test.rb21
-rw-r--r--actioncable/test/channel/test_case_test.rb188
-rw-r--r--actionmailer/CHANGELOG.md11
-rw-r--r--actionmailer/lib/action_mailer/base.rb3
-rw-r--r--actionmailer/lib/action_mailer/message_delivery.rb2
-rw-r--r--actionmailer/lib/action_mailer/parameterized.rb3
-rw-r--r--actionmailer/test/abstract_unit.rb18
-rw-r--r--actionmailer/test/assert_select_email_test.rb2
-rw-r--r--actionmailer/test/base_test.rb4
-rw-r--r--actionmailer/test/parameterized_test.rb13
-rw-r--r--actionmailer/test/url_test.rb24
-rw-r--r--actionpack/CHANGELOG.md16
-rw-r--r--actionpack/lib/abstract_controller/base.rb2
-rw-r--r--actionpack/lib/abstract_controller/helpers.rb2
-rw-r--r--actionpack/lib/abstract_controller/railties/routes_helpers.rb2
-rw-r--r--actionpack/lib/action_controller/caching.rb2
-rw-r--r--actionpack/lib/action_controller/log_subscriber.rb4
-rw-r--r--actionpack/lib/action_controller/metal/data_streaming.rb11
-rw-r--r--actionpack/lib/action_controller/metal/http_authentication.rb6
-rw-r--r--actionpack/lib/action_controller/metal/live.rb4
-rw-r--r--actionpack/lib/action_controller/metal/rendering.rb2
-rw-r--r--actionpack/lib/action_controller/metal/strong_parameters.rb8
-rw-r--r--actionpack/lib/action_controller/metal/url_for.rb2
-rw-r--r--actionpack/lib/action_controller/railties/helpers.rb2
-rw-r--r--actionpack/lib/action_dispatch/http/cache.rb18
-rw-r--r--actionpack/lib/action_dispatch/http/content_disposition.rb45
-rw-r--r--actionpack/lib/action_dispatch/http/content_security_policy.rb16
-rw-r--r--actionpack/lib/action_dispatch/http/filter_redirect.rb2
-rw-r--r--actionpack/lib/action_dispatch/http/mime_type.rb2
-rw-r--r--actionpack/lib/action_dispatch/http/parameter_filter.rb10
-rw-r--r--actionpack/lib/action_dispatch/http/request.rb20
-rw-r--r--actionpack/lib/action_dispatch/http/response.rb8
-rw-r--r--actionpack/lib/action_dispatch/http/url.rb6
-rw-r--r--actionpack/lib/action_dispatch/journey/formatter.rb2
-rw-r--r--actionpack/lib/action_dispatch/journey/router/utils.rb20
-rw-r--r--actionpack/lib/action_dispatch/journey/visitors.rb2
-rw-r--r--actionpack/lib/action_dispatch/middleware/cookies.rb38
-rw-r--r--actionpack/lib/action_dispatch/middleware/debug_exceptions.rb2
-rw-r--r--actionpack/lib/action_dispatch/middleware/debug_locks.rb10
-rw-r--r--actionpack/lib/action_dispatch/middleware/flash.rb2
-rw-r--r--actionpack/lib/action_dispatch/middleware/request_id.rb4
-rw-r--r--actionpack/lib/action_dispatch/middleware/ssl.rb14
-rw-r--r--actionpack/lib/action_dispatch/middleware/static.rb4
-rw-r--r--actionpack/lib/action_dispatch/routing/mapper.rb20
-rw-r--r--actionpack/lib/action_dispatch/routing/polymorphic_routes.rb4
-rw-r--r--actionpack/lib/action_dispatch/routing/route_set.rb31
-rw-r--r--actionpack/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb2
-rw-r--r--actionpack/lib/action_dispatch/testing/assertions/response.rb5
-rw-r--r--actionpack/lib/action_dispatch/testing/assertions/routing.rb5
-rw-r--r--actionpack/test/abstract_unit.rb19
-rw-r--r--actionpack/test/controller/base_test.rb4
-rw-r--r--actionpack/test/controller/http_token_authentication_test.rb2
-rw-r--r--actionpack/test/controller/live_stream_test.rb2
-rw-r--r--actionpack/test/controller/metal_test.rb2
-rw-r--r--actionpack/test/controller/mime/respond_to_test.rb2
-rw-r--r--actionpack/test/controller/new_base/bare_metal_test.rb2
-rw-r--r--actionpack/test/controller/new_base/render_context_test.rb9
-rw-r--r--actionpack/test/controller/parameters/accessors_test.rb18
-rw-r--r--actionpack/test/controller/redirect_test.rb7
-rw-r--r--actionpack/test/controller/routing_test.rb8
-rw-r--r--actionpack/test/controller/send_file_test.rb4
-rw-r--r--actionpack/test/controller/test_case_test.rb2
-rw-r--r--actionpack/test/dispatch/content_disposition_test.rb37
-rw-r--r--actionpack/test/dispatch/content_security_policy_test.rb15
-rw-r--r--actionpack/test/dispatch/debug_exceptions_test.rb2
-rw-r--r--actionpack/test/dispatch/middleware_stack_test.rb2
-rw-r--r--actionpack/test/dispatch/prefix_generation_test.rb2
-rw-r--r--actionpack/test/dispatch/request_test.rb2
-rw-r--r--actionpack/test/dispatch/routing/inspector_test.rb4
-rw-r--r--actionpack/test/dispatch/routing_test.rb31
-rw-r--r--actionpack/test/dispatch/static_test.rb4
-rw-r--r--actionpack/test/fixtures/alternate_helpers/foo_helper.rb2
-rw-r--r--actionpack/test/journey/router/utils_test.rb2
-rw-r--r--actionview/app/assets/javascripts/rails-ujs/features/disable.coffee11
-rw-r--r--actionview/lib/action_view/helpers/asset_tag_helper.rb4
-rw-r--r--actionview/lib/action_view/helpers/asset_url_helper.rb2
-rw-r--r--actionview/lib/action_view/helpers/date_helper.rb20
-rw-r--r--actionview/lib/action_view/helpers/form_options_helper.rb8
-rw-r--r--actionview/lib/action_view/helpers/form_tag_helper.rb8
-rw-r--r--actionview/lib/action_view/helpers/javascript_helper.rb15
-rw-r--r--actionview/lib/action_view/helpers/tag_helper.rb6
-rw-r--r--actionview/lib/action_view/helpers/translation_helper.rb2
-rw-r--r--actionview/lib/action_view/helpers/url_helper.rb22
-rw-r--r--actionview/lib/action_view/log_subscriber.rb6
-rw-r--r--actionview/lib/action_view/lookup_context.rb6
-rw-r--r--actionview/lib/action_view/record_identifier.rb4
-rw-r--r--actionview/lib/action_view/renderer/partial_renderer.rb2
-rw-r--r--actionview/lib/action_view/renderer/partial_renderer/collection_caching.rb35
-rw-r--r--actionview/lib/action_view/renderer/streaming_template_renderer.rb2
-rw-r--r--actionview/lib/action_view/template.rb27
-rw-r--r--actionview/lib/action_view/template/resolver.rb6
-rw-r--r--actionview/lib/action_view/test_case.rb2
-rw-r--r--actionview/lib/action_view/testing/resolvers.rb2
-rw-r--r--actionview/test/abstract_unit.rb97
-rw-r--r--actionview/test/actionpack/controller/capture_test.rb9
-rw-r--r--actionview/test/actionpack/controller/layout_test.rb21
-rw-r--r--actionview/test/actionpack/controller/render_test.rb118
-rw-r--r--actionview/test/actionpack/controller/view_paths_test.rb10
-rw-r--r--actionview/test/active_record_unit.rb12
-rw-r--r--actionview/test/activerecord/controller_runtime_test.rb8
-rw-r--r--actionview/test/activerecord/debug_helper_test.rb2
-rw-r--r--actionview/test/activerecord/form_helper_activerecord_test.rb7
-rw-r--r--actionview/test/activerecord/polymorphic_routes_test.rb8
-rw-r--r--actionview/test/activerecord/relation_cache_test.rb1
-rw-r--r--actionview/test/activerecord/render_partial_with_record_identification_test.rb20
-rw-r--r--actionview/test/fixtures/ruby_template.ruby2
-rw-r--r--actionview/test/template/capture_helper_test.rb4
-rw-r--r--actionview/test/template/date_helper_test.rb374
-rw-r--r--actionview/test/template/erb/form_for_test.rb6
-rw-r--r--actionview/test/template/erb/helper.rb11
-rw-r--r--actionview/test/template/form_helper/form_with_test.rb45
-rw-r--r--actionview/test/template/form_helper_test.rb21
-rw-r--r--actionview/test/template/form_options_helper_test.rb2
-rw-r--r--actionview/test/template/form_tag_helper_test.rb4
-rw-r--r--actionview/test/template/javascript_helper_test.rb8
-rw-r--r--actionview/test/template/render_test.rb2
-rw-r--r--actionview/test/template/streaming_render_test.rb2
-rw-r--r--actionview/test/template/template_test.rb7
-rw-r--r--actionview/test/template/test_case_test.rb14
-rw-r--r--actionview/test/template/text_helper_test.rb12
-rw-r--r--actionview/test/template/url_helper_test.rb46
-rw-r--r--actionview/test/ujs/public/test/data-disable.js17
-rw-r--r--actionview/test/ujs/public/test/settings.js4
-rw-r--r--actionview/test/ujs/server.rb7
-rw-r--r--activejob/CHANGELOG.md6
-rw-r--r--activejob/lib/active_job/arguments.rb6
-rw-r--r--activejob/lib/active_job/core.rb40
-rw-r--r--activejob/lib/active_job/exceptions.rb37
-rw-r--r--activejob/lib/active_job/logging.rb2
-rw-r--r--activejob/lib/active_job/queue_adapter.rb2
-rw-r--r--activejob/lib/active_job/queue_adapters.rb2
-rw-r--r--activejob/lib/active_job/test_helper.rb87
-rw-r--r--activejob/test/cases/logging_test.rb5
-rw-r--r--activejob/test/cases/test_helper_test.rb61
-rw-r--r--activejob/test/jobs/multiple_kwargs_job.rb9
-rw-r--r--activejob/test/support/integration/adapters/que.rb4
-rw-r--r--activejob/test/support/integration/adapters/queue_classic.rb4
-rw-r--r--activejob/test/support/integration/helper.rb2
-rw-r--r--activemodel/lib/active_model/attribute_assignment.rb2
-rw-r--r--activemodel/lib/active_model/attribute_methods.rb2
-rw-r--r--activemodel/lib/active_model/attributes.rb2
-rw-r--r--activemodel/lib/active_model/conversion.rb2
-rw-r--r--activemodel/lib/active_model/naming.rb4
-rw-r--r--activemodel/lib/active_model/type/string.rb4
-rw-r--r--activemodel/lib/active_model/validations/validates.rb2
-rw-r--r--activemodel/test/cases/attribute_assignment_test.rb4
-rw-r--r--activemodel/test/cases/attribute_set_test.rb2
-rw-r--r--activemodel/test/cases/attribute_test.rb4
-rw-r--r--activemodel/test/cases/callbacks_test.rb2
-rw-r--r--activemodel/test/cases/dirty_test.rb2
-rw-r--r--activemodel/test/cases/helper.rb18
-rw-r--r--activemodel/test/cases/type/string_test.rb4
-rw-r--r--activerecord/CHANGELOG.md37
-rw-r--r--activerecord/examples/performance.rb2
-rw-r--r--activerecord/lib/active_record/associations.rb3
-rw-r--r--activerecord/lib/active_record/associations/belongs_to_association.rb52
-rw-r--r--activerecord/lib/active_record/associations/belongs_to_polymorphic_association.rb4
-rw-r--r--activerecord/lib/active_record/associations/builder/belongs_to.rb62
-rw-r--r--activerecord/lib/active_record/associations/builder/collection_association.rb2
-rw-r--r--activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb2
-rw-r--r--activerecord/lib/active_record/associations/preloader.rb50
-rw-r--r--activerecord/lib/active_record/attribute_methods.rb8
-rw-r--r--activerecord/lib/active_record/attribute_methods/read.rb4
-rw-r--r--activerecord/lib/active_record/attribute_methods/write.rb4
-rw-r--r--activerecord/lib/active_record/callbacks.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb43
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/quoting.rb6
-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.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb6
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_adapter.rb4
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb40
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql/quoting.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql/schema_creation.rb4
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql/schema_statements.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb4
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid/array.rb8
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb8
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb4
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/type_metadata.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb22
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite3/quoting.rb10
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite3/schema_statements.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb4
-rw-r--r--activerecord/lib/active_record/counter_cache.rb8
-rw-r--r--activerecord/lib/active_record/database_configurations.rb4
-rw-r--r--activerecord/lib/active_record/database_configurations/database_config.rb4
-rw-r--r--activerecord/lib/active_record/database_configurations/hash_config.rb7
-rw-r--r--activerecord/lib/active_record/database_configurations/url_config.rb7
-rw-r--r--activerecord/lib/active_record/explain.rb2
-rw-r--r--activerecord/lib/active_record/fixture_set/render_context.rb17
-rw-r--r--activerecord/lib/active_record/fixtures.rb215
-rw-r--r--activerecord/lib/active_record/inheritance.rb2
-rw-r--r--activerecord/lib/active_record/migration.rb24
-rw-r--r--activerecord/lib/active_record/migration/command_recorder.rb19
-rw-r--r--activerecord/lib/active_record/migration/compatibility.rb15
-rw-r--r--activerecord/lib/active_record/model_schema.rb12
-rw-r--r--activerecord/lib/active_record/railtie.rb49
-rw-r--r--activerecord/lib/active_record/relation.rb70
-rw-r--r--activerecord/lib/active_record/relation/delegation.rb2
-rw-r--r--activerecord/lib/active_record/relation/finder_methods.rb6
-rw-r--r--activerecord/lib/active_record/relation/merger.rb9
-rw-r--r--activerecord/lib/active_record/relation/predicate_builder.rb6
-rw-r--r--activerecord/lib/active_record/relation/query_methods.rb4
-rw-r--r--activerecord/lib/active_record/result.rb11
-rw-r--r--activerecord/lib/active_record/statement_cache.rb28
-rw-r--r--activerecord/lib/active_record/tasks/database_tasks.rb2
-rw-r--r--activerecord/lib/active_record/tasks/mysql_database_tasks.rb2
-rw-r--r--activerecord/lib/active_record/tasks/postgresql_database_tasks.rb8
-rw-r--r--activerecord/lib/active_record/tasks/sqlite_database_tasks.rb2
-rw-r--r--activerecord/lib/active_record/test_fixtures.rb201
-rw-r--r--activerecord/lib/arel.rb5
-rw-r--r--activerecord/lib/arel/collectors/composite.rb3
-rw-r--r--activerecord/lib/arel/collectors/plain_string.rb2
-rw-r--r--activerecord/lib/arel/collectors/sql_string.rb4
-rw-r--r--activerecord/lib/arel/collectors/substitute_binds.rb3
-rw-r--r--activerecord/lib/arel/delete_manager.rb11
-rw-r--r--activerecord/lib/arel/factory_methods.rb4
-rw-r--r--activerecord/lib/arel/nodes/delete_statement.rb15
-rw-r--r--activerecord/lib/arel/nodes/node.rb10
-rw-r--r--activerecord/lib/arel/nodes/select_core.rb6
-rw-r--r--activerecord/lib/arel/nodes/unary.rb1
-rw-r--r--activerecord/lib/arel/nodes/update_statement.rb7
-rw-r--r--activerecord/lib/arel/select_manager.rb2
-rw-r--r--activerecord/lib/arel/table.rb3
-rw-r--r--activerecord/lib/arel/tree_manager.rb34
-rw-r--r--activerecord/lib/arel/update_manager.rb29
-rw-r--r--activerecord/lib/arel/visitors/depth_first.rb3
-rw-r--r--activerecord/lib/arel/visitors/dot.rb2
-rw-r--r--activerecord/lib/arel/visitors/mssql.rb7
-rw-r--r--activerecord/lib/arel/visitors/mysql.rb64
-rw-r--r--activerecord/lib/arel/visitors/to_sql.rb101
-rw-r--r--activerecord/lib/rails/generators/active_record/migration.rb19
-rw-r--r--activerecord/lib/rails/generators/active_record/migration/migration_generator.rb2
-rw-r--r--activerecord/lib/rails/generators/active_record/model/model_generator.rb1
-rw-r--r--activerecord/test/cases/adapter_test.rb4
-rw-r--r--activerecord/test/cases/adapters/mysql2/case_sensitivity_test.rb4
-rw-r--r--activerecord/test/cases/adapters/mysql2/charset_collation_test.rb10
-rw-r--r--activerecord/test/cases/adapters/postgresql/bytea_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/money_test.rb10
-rw-r--r--activerecord/test/cases/adapters/postgresql/referential_integrity_test.rb2
-rw-r--r--activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb6
-rw-r--r--activerecord/test/cases/arel/collectors/sql_string_test.rb16
-rw-r--r--activerecord/test/cases/arel/delete_manager_test.rb1
-rw-r--r--activerecord/test/cases/arel/select_manager_test.rb4
-rw-r--r--activerecord/test/cases/arel/visitors/depth_first_test.rb1
-rw-r--r--activerecord/test/cases/arel/visitors/dot_test.rb1
-rw-r--r--activerecord/test/cases/arel/visitors/to_sql_test.rb2
-rw-r--r--activerecord/test/cases/associations/belongs_to_associations_test.rb63
-rw-r--r--activerecord/test/cases/associations/cascaded_eager_loading_test.rb15
-rw-r--r--activerecord/test/cases/associations/eager_load_nested_include_test.rb2
-rw-r--r--activerecord/test/cases/associations/eager_test.rb28
-rw-r--r--activerecord/test/cases/associations/extension_test.rb2
-rw-r--r--activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb10
-rw-r--r--activerecord/test/cases/associations/has_many_associations_test.rb68
-rw-r--r--activerecord/test/cases/associations/has_many_through_associations_test.rb23
-rw-r--r--activerecord/test/cases/associations/has_one_associations_test.rb8
-rw-r--r--activerecord/test/cases/associations/nested_through_associations_test.rb4
-rw-r--r--activerecord/test/cases/associations_test.rb2
-rw-r--r--activerecord/test/cases/attributes_test.rb14
-rw-r--r--activerecord/test/cases/autosave_association_test.rb14
-rw-r--r--activerecord/test/cases/base_test.rb10
-rw-r--r--activerecord/test/cases/batches_test.rb32
-rw-r--r--activerecord/test/cases/binary_test.rb2
-rw-r--r--activerecord/test/cases/callbacks_test.rb6
-rw-r--r--activerecord/test/cases/collection_cache_key_test.rb4
-rw-r--r--activerecord/test/cases/connection_adapters/connection_handler_test.rb29
-rw-r--r--activerecord/test/cases/connection_management_test.rb2
-rw-r--r--activerecord/test/cases/connection_pool_test.rb2
-rw-r--r--activerecord/test/cases/core_test.rb8
-rw-r--r--activerecord/test/cases/dirty_test.rb4
-rw-r--r--activerecord/test/cases/finder_respond_to_test.rb2
-rw-r--r--activerecord/test/cases/finder_test.rb5
-rw-r--r--activerecord/test/cases/fixtures_test.rb18
-rw-r--r--activerecord/test/cases/integration_test.rb86
-rw-r--r--activerecord/test/cases/invertible_migration_test.rb16
-rw-r--r--activerecord/test/cases/migration/change_table_test.rb8
-rw-r--r--activerecord/test/cases/migration/command_recorder_test.rb16
-rw-r--r--activerecord/test/cases/migration/compatibility_test.rb14
-rw-r--r--activerecord/test/cases/migration/pending_migrations_test.rb4
-rw-r--r--activerecord/test/cases/migration_test.rb17
-rw-r--r--activerecord/test/cases/migrator_test.rb1
-rw-r--r--activerecord/test/cases/null_relation_test.rb14
-rw-r--r--activerecord/test/cases/persistence_test.rb180
-rw-r--r--activerecord/test/cases/query_cache_test.rb28
-rw-r--r--activerecord/test/cases/relation/delete_all_test.rb104
-rw-r--r--activerecord/test/cases/relation/merging_test.rb4
-rw-r--r--activerecord/test/cases/relation/update_all_test.rb239
-rw-r--r--activerecord/test/cases/relation_test.rb2
-rw-r--r--activerecord/test/cases/relations_test.rb180
-rw-r--r--activerecord/test/cases/result_test.rb14
-rw-r--r--activerecord/test/cases/scoping/named_scoping_test.rb2
-rw-r--r--activerecord/test/cases/suppressor_test.rb2
-rw-r--r--activerecord/test/cases/tasks/postgresql_rake_test.rb6
-rw-r--r--activerecord/test/cases/touch_later_test.rb2
-rw-r--r--activerecord/test/cases/transaction_isolation_test.rb4
-rw-r--r--activerecord/test/cases/transactions_test.rb10
-rw-r--r--activerecord/test/cases/type/type_map_test.rb6
-rw-r--r--activerecord/test/models/author.rb2
-rw-r--r--activerecord/test/models/reply.rb8
-rw-r--r--activerecord/test/models/topic.rb2
-rw-r--r--activerecord/test/schema/mysql2_specific_schema.rb4
-rw-r--r--activerecord/test/schema/schema.rb2
-rw-r--r--activestorage/app/models/active_storage/blob.rb4
-rw-r--r--activestorage/app/models/active_storage/filename.rb6
-rw-r--r--activestorage/app/models/active_storage/filename/parameters.rb36
-rw-r--r--activestorage/lib/active_storage/service.rb5
-rw-r--r--activestorage/test/dummy/app/assets/config/manifest.js2
-rw-r--r--activestorage/test/dummy/config/environments/production.rb3
-rw-r--r--activestorage/test/dummy/config/webpacker.yml72
-rw-r--r--activestorage/test/models/blob_test.rb2
-rw-r--r--activestorage/test/models/filename/parameters_test.rb32
-rw-r--r--activestorage/test/models/filename_test.rb4
-rw-r--r--activestorage/test/service/shared_service_tests.rb4
-rw-r--r--activesupport/CHANGELOG.md23
-rw-r--r--activesupport/lib/active_support/backtrace_cleaner.rb2
-rw-r--r--activesupport/lib/active_support/cache.rb12
-rw-r--r--activesupport/lib/active_support/cache/file_store.rb5
-rw-r--r--activesupport/lib/active_support/cache/mem_cache_store.rb5
-rw-r--r--activesupport/lib/active_support/cache/memory_store.rb5
-rw-r--r--activesupport/lib/active_support/cache/null_store.rb5
-rw-r--r--activesupport/lib/active_support/cache/redis_cache_store.rb5
-rw-r--r--activesupport/lib/active_support/configurable.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/class/subclasses.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/load_error.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/module/introspection.rb50
-rw-r--r--activesupport/lib/active_support/core_ext/object/try.rb4
-rw-r--r--activesupport/lib/active_support/core_ext/string/access.rb8
-rw-r--r--activesupport/lib/active_support/core_ext/string/output_safety.rb17
-rw-r--r--activesupport/lib/active_support/core_ext/string/strip.rb2
-rw-r--r--activesupport/lib/active_support/dependencies.rb10
-rw-r--r--activesupport/lib/active_support/deprecation/behaviors.rb2
-rw-r--r--activesupport/lib/active_support/duration/iso8601_parser.rb4
-rw-r--r--activesupport/lib/active_support/duration/iso8601_serializer.rb6
-rw-r--r--activesupport/lib/active_support/encrypted_configuration.rb2
-rw-r--r--activesupport/lib/active_support/hash_with_indifferent_access.rb3
-rw-r--r--activesupport/lib/active_support/inflector/methods.rb26
-rw-r--r--activesupport/lib/active_support/inflector/transliterate.rb6
-rw-r--r--activesupport/lib/active_support/logger.rb15
-rw-r--r--activesupport/lib/active_support/logger_silence.rb40
-rw-r--r--activesupport/lib/active_support/logger_thread_safe_level.rb30
-rw-r--r--activesupport/lib/active_support/message_encryptor.rb2
-rw-r--r--activesupport/lib/active_support/message_verifier.rb4
-rw-r--r--activesupport/lib/active_support/number_helper/number_to_currency_converter.rb2
-rw-r--r--activesupport/lib/active_support/number_helper/number_to_delimited_converter.rb2
-rw-r--r--activesupport/lib/active_support/number_helper/number_to_human_converter.rb2
-rw-r--r--activesupport/lib/active_support/number_helper/number_to_human_size_converter.rb2
-rw-r--r--activesupport/lib/active_support/number_helper/number_to_percentage_converter.rb2
-rw-r--r--activesupport/lib/active_support/number_helper/number_to_rounded_converter.rb6
-rw-r--r--activesupport/lib/active_support/subscriber.rb2
-rw-r--r--activesupport/lib/active_support/tagged_logging.rb2
-rw-r--r--activesupport/lib/active_support/time_with_zone.rb6
-rw-r--r--activesupport/lib/active_support/xml_mini/jdom.rb4
-rw-r--r--activesupport/lib/active_support/xml_mini/libxml.rb4
-rw-r--r--activesupport/lib/active_support/xml_mini/libxmlsax.rb8
-rw-r--r--activesupport/lib/active_support/xml_mini/nokogiri.rb4
-rw-r--r--activesupport/lib/active_support/xml_mini/nokogirisax.rb6
-rw-r--r--activesupport/lib/active_support/xml_mini/rexml.rb4
-rw-r--r--activesupport/test/abstract_unit.rb27
-rw-r--r--activesupport/test/benchmarkable_test.rb4
-rw-r--r--activesupport/test/broadcast_logger_test.rb11
-rw-r--r--activesupport/test/cache/behaviors/cache_store_behavior.rb55
-rw-r--r--activesupport/test/cache/behaviors/encoded_key_cache_behavior.rb6
-rw-r--r--activesupport/test/cache/cache_key_test.rb4
-rw-r--r--activesupport/test/cache/local_cache_middleware_test.rb2
-rw-r--r--activesupport/test/callbacks_test.rb4
-rw-r--r--activesupport/test/core_ext/array/extract_test.rb2
-rw-r--r--activesupport/test/core_ext/date_and_time_compatibility_test.rb4
-rw-r--r--activesupport/test/core_ext/module/concerning_test.rb2
-rw-r--r--activesupport/test/core_ext/module/introspection_test.rb40
-rw-r--r--activesupport/test/core_ext/object/instance_variables_test.rb6
-rw-r--r--activesupport/test/core_ext/range_ext_test.rb8
-rw-r--r--activesupport/test/core_ext/string_ext_test.rb86
-rw-r--r--activesupport/test/core_ext/time_ext_test.rb38
-rw-r--r--activesupport/test/core_ext/time_with_zone_test.rb2
-rw-r--r--activesupport/test/dependencies_test.rb4
-rw-r--r--activesupport/test/encrypted_configuration_test.rb6
-rw-r--r--activesupport/test/evented_file_update_checker_test.rb2
-rw-r--r--activesupport/test/executor_test.rb18
-rw-r--r--activesupport/test/hash_with_indifferent_access_test.rb11
-rw-r--r--activesupport/test/logger_test.rb4
-rw-r--r--activesupport/test/multibyte_chars_test.rb24
-rw-r--r--activesupport/test/multibyte_test_helpers.rb6
-rw-r--r--activesupport/test/notifications_test.rb2
-rw-r--r--activesupport/test/reloader_test.rb8
-rw-r--r--activesupport/test/safe_buffer_test.rb43
-rw-r--r--activesupport/test/share_lock_test.rb54
-rw-r--r--activesupport/test/silence_logger_test.rb35
-rw-r--r--activesupport/test/xml_mini/rexml_engine_test.rb2
-rw-r--r--activesupport/test/xml_mini/xml_mini_engine_test.rb2
-rw-r--r--ci/qunit-selenium-runner.rb2
-rwxr-xr-xci/travis.rb8
-rw-r--r--guides/rails_guides/markdown.rb2
-rw-r--r--guides/rails_guides/markdown/renderer.rb7
-rw-r--r--guides/source/3_0_release_notes.md2
-rw-r--r--guides/source/3_1_release_notes.md2
-rw-r--r--guides/source/4_2_release_notes.md2
-rw-r--r--guides/source/active_record_callbacks.md4
-rw-r--r--guides/source/active_record_querying.md2
-rw-r--r--guides/source/active_record_validations.md36
-rw-r--r--guides/source/active_storage_overview.md10
-rw-r--r--guides/source/active_support_core_extensions.md32
-rw-r--r--guides/source/active_support_instrumentation.md1
-rw-r--r--guides/source/asset_pipeline.md5
-rw-r--r--guides/source/association_basics.md26
-rw-r--r--guides/source/caching_with_rails.md4
-rw-r--r--guides/source/command_line.md5
-rw-r--r--guides/source/configuring.md8
-rw-r--r--guides/source/contributing_to_ruby_on_rails.md74
-rw-r--r--guides/source/form_helpers.md4
-rw-r--r--guides/source/generators.md5
-rw-r--r--guides/source/getting_started.md3
-rw-r--r--guides/source/testing.md2
-rw-r--r--guides/source/upgrading_ruby_on_rails.md2
-rw-r--r--guides/source/working_with_javascript_in_rails.md4
-rw-r--r--package.json14
-rw-r--r--railties/CHANGELOG.md55
-rw-r--r--railties/Rakefile3
-rw-r--r--railties/lib/rails/application.rb6
-rw-r--r--railties/lib/rails/application/configuration.rb26
-rw-r--r--railties/lib/rails/backtrace_cleaner.rb8
-rw-r--r--railties/lib/rails/commands/credentials/USAGE9
-rw-r--r--railties/lib/rails/commands/credentials/credentials_command.rb63
-rw-r--r--railties/lib/rails/commands/dbconsole/dbconsole_command.rb2
-rw-r--r--railties/lib/rails/commands/server/server_command.rb2
-rw-r--r--railties/lib/rails/generators.rb2
-rw-r--r--railties/lib/rails/generators/app_base.rb58
-rw-r--r--railties/lib/rails/generators/generated_attribute.rb2
-rw-r--r--railties/lib/rails/generators/js/assets/assets_generator.rb15
-rw-r--r--railties/lib/rails/generators/js/assets/templates/javascript.js2
-rw-r--r--railties/lib/rails/generators/rails/app/app_generator.rb8
-rw-r--r--railties/lib/rails/generators/rails/app/templates/app/assets/config/manifest.js.tt3
-rw-r--r--railties/lib/rails/generators/rails/app/templates/app/assets/javascripts/application.js.tt22
-rw-r--r--railties/lib/rails/generators/rails/app/templates/app/javascript/channels/consumer.js (renamed from railties/lib/rails/generators/rails/app/templates/app/assets/javascripts/cable.js.tt)11
-rw-r--r--railties/lib/rails/generators/rails/app/templates/app/javascript/channels/index.js5
-rw-r--r--railties/lib/rails/generators/rails/app/templates/app/javascript/packs/application.js.tt21
-rw-r--r--railties/lib/rails/generators/rails/app/templates/app/jobs/application_job.rb.tt5
-rw-r--r--railties/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb.tt8
-rw-r--r--railties/lib/rails/generators/rails/app/templates/bin/setup.tt3
-rw-r--r--railties/lib/rails/generators/rails/app/templates/bin/update.tt3
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt7
-rw-r--r--railties/lib/rails/generators/rails/app/templates/package.json.tt8
-rw-r--r--railties/lib/rails/generators/rails/assets/USAGE5
-rw-r--r--railties/lib/rails/generators/rails/assets/assets_generator.rb8
-rw-r--r--railties/lib/rails/generators/rails/assets/templates/javascript.js2
-rw-r--r--railties/lib/rails/generators/rails/plugin/plugin_generator.rb2
-rw-r--r--railties/lib/rails/generators/testing/behaviour.rb3
-rw-r--r--railties/lib/rails/info.rb2
-rw-r--r--railties/lib/rails/railtie.rb2
-rw-r--r--railties/lib/rails/source_annotation_extractor.rb2
-rw-r--r--railties/lib/rails/tasks/statistics.rake1
-rw-r--r--railties/lib/rails/test_unit/runner.rb4
-rw-r--r--railties/test/abstract_unit.rb18
-rw-r--r--railties/test/application/assets_test.rb20
-rw-r--r--railties/test/application/bin_setup_test.rb18
-rw-r--r--railties/test/application/configuration_test.rb59
-rw-r--r--railties/test/application/middleware_test.rb15
-rw-r--r--railties/test/application/rake/dbs_test.rb18
-rw-r--r--railties/test/application/rake_test.rb3
-rw-r--r--railties/test/commands/credentials_test.rb26
-rw-r--r--railties/test/console_helpers.rb2
-rw-r--r--railties/test/credentials_test.rb49
-rw-r--r--railties/test/engine/commands_test.rb3
-rw-r--r--railties/test/generators/actions_test.rb2
-rw-r--r--railties/test/generators/api_app_generator_test.rb1
-rw-r--r--railties/test/generators/app_generator_test.rb75
-rw-r--r--railties/test/generators/assets_generator_test.rb4
-rw-r--r--railties/test/generators/channel_generator_test.rb27
-rw-r--r--railties/test/generators/controller_generator_test.rb5
-rw-r--r--railties/test/generators/generators_test_helper.rb15
-rw-r--r--railties/test/generators/migration_generator_test.rb12
-rw-r--r--railties/test/generators/model_generator_test.rb11
-rw-r--r--railties/test/generators/plugin_generator_test.rb49
-rw-r--r--railties/test/generators/resource_generator_test.rb4
-rw-r--r--railties/test/generators/scaffold_generator_test.rb27
-rw-r--r--railties/test/generators/shared_generator_tests.rb29
-rw-r--r--railties/test/isolation/abstract_unit.rb21
-rw-r--r--railties/test/rack_logger_test.rb2
497 files changed, 4971 insertions, 2866 deletions
diff --git a/.github/autolabeler.yml b/.github/autolabeler.yml
new file mode 100644
index 0000000000..d73b2e3362
--- /dev/null
+++ b/.github/autolabeler.yml
@@ -0,0 +1,24 @@
+actioncable:
+ - "actioncable/**/*"
+actionmailer:
+ - "actionmailer/**/*"
+actionpack:
+ - "actionpack/**/*"
+actionview:
+ - "actionview/**/*"
+activejob:
+ - "activejob/**/*"
+activemodel:
+ - "activemodel/**/*"
+activerecord:
+ - "activerecord/**/*"
+activestorage:
+ - "activestorage/**/*"
+activesupport:
+ - "activesupport/**/*"
+rails-ujs:
+ - "actionview/app/assets/javascripts/rails-ujs*/*"
+railties:
+ - "railties/**/*"
+docs:
+ - "guides/**/*"
diff --git a/.github/no-response.yml b/.github/no-response.yml
new file mode 100644
index 0000000000..7193eaa3b2
--- /dev/null
+++ b/.github/no-response.yml
@@ -0,0 +1,13 @@
+# Configuration for probot-no-response - https://github.com/probot/no-response
+
+# Number of days of inactivity before an Issue is closed for lack of response
+daysUntilClose: 14
+# Label requiring a response
+responseRequiredLabel: more-information-needed
+# Comment to post when closing an Issue for lack of response. Set to `false` to disable
+closeComment: >
+ This issue has been automatically closed because there has been no response
+ to our request for more information from the original author. With only the
+ information that is currently in the issue, we don't have enough information
+ to take action. Please reach out if you have or find the answers we need so
+ that we can investigate further.
diff --git a/.gitignore b/.gitignore
index ab9ca8a6ac..d44875ed9a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -14,3 +14,5 @@ debug.log
node_modules/
package-lock.json
pkg/
+/tmp/
+/yarn.lock \ No newline at end of file
diff --git a/.rubocop.yml b/.rubocop.yml
index a673e6ba83..615a229432 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -8,6 +8,7 @@ AllCops:
- '**/vendor/**/*'
- 'actionpack/lib/action_dispatch/journey/parser.rb'
- 'railties/test/fixtures/tmp/**/*'
+ - 'node_modules/**/*'
Performance:
Exclude:
@@ -130,6 +131,12 @@ Style/FrozenStringLiteralComment:
- 'actionpack/test/**/*.ruby'
- 'activestorage/db/migrate/**/*.rb'
+Style/RedundantFreeze:
+ Enabled: true
+ Exclude:
+ - 'actionpack/lib/action_dispatch/journey/router/utils.rb'
+ - 'activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb'
+
# Use `foo {}` not `foo{}`.
Layout/SpaceBeforeBlockBraces:
Enabled: true
@@ -137,6 +144,7 @@ Layout/SpaceBeforeBlockBraces:
# Use `foo { bar }` not `foo {bar}`.
Layout/SpaceInsideBlockBraces:
Enabled: true
+ EnforcedStyleForEmptyBraces: space
# Use `{ a: 1 }` not `{a:1}`.
Layout/SpaceInsideHashLiteralBraces:
@@ -208,3 +216,6 @@ Performance/EndWith:
Performance/RegexpMatch:
Enabled: true
+
+Performance/UnfreezeString:
+ Enabled: true
diff --git a/.travis.yml b/.travis.yml
index 6943a67ffe..a4f5fa045d 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -14,7 +14,7 @@ services:
- redis-server
addons:
- postgresql: "9.6"
+ postgresql: 10
chrome: stable
apt:
sources:
@@ -28,6 +28,8 @@ addons:
- poppler-utils
- mysql-server
- mysql-client
+ - postgresql-10
+ - postgresql-client-10
bundler_args: --without test --jobs 3 --retry 3
before_install:
@@ -40,6 +42,8 @@ before_install:
- "[[ $GEM != 'av:ujs' ]] || nvm install node"
- "[[ $GEM != 'av:ujs' ]] || node --version"
- "[[ $GEM != 'av:ujs' ]] || (cd actionview && npm install)"
+ - "[[ $GEM != 'railties' ]] || (curl -o- -L https://yarnpkg.com/install.sh | bash)"
+ - "[[ $GEM != 'railties' ]] || export PATH=$HOME/.yarn/bin:$PATH"
before_script:
# Set Sauce Labs username and access key. Obfuscated, purposefully not encrypted.
@@ -53,12 +57,10 @@ env:
global:
- "JRUBY_OPTS='--dev -J-Xmx1024M'"
matrix:
- - "GEM=railties"
- "GEM=ap,ac"
- "GEM=am,amo,as,av,aj,ast"
- "GEM=as PRESERVE_TIMEZONES=1"
- "GEM=ar:sqlite3"
- - "GEM=ar:postgresql"
- "GEM=guides"
- "GEM=ac:integration"
@@ -69,6 +71,33 @@ rvm:
matrix:
include:
+ - rvm: 2.4.4
+ env: "GEM=railties"
+ sudo: required
+ before_install:
+ - "rm ${BUNDLE_GEMFILE}.lock"
+ - "travis_retry gem update --system"
+ - "travis_retry gem install bundler"
+ - "sudo sed -i 's/port = 5433/port = 5432/' /etc/postgresql/10/main/postgresql.conf"
+ - "sudo service postgresql restart 10"
+ - rvm: 2.5.1
+ env: "GEM=railties"
+ sudo: required
+ before_install:
+ - "rm ${BUNDLE_GEMFILE}.lock"
+ - "travis_retry gem update --system"
+ - "travis_retry gem install bundler"
+ - "sudo sed -i 's/port = 5433/port = 5432/' /etc/postgresql/10/main/postgresql.conf"
+ - "sudo service postgresql restart 10"
+ - rvm: ruby-head
+ env: "GEM=railties"
+ sudo: required
+ before_install:
+ - "rm ${BUNDLE_GEMFILE}.lock"
+ - "travis_retry gem update --system"
+ - "travis_retry gem install bundler"
+ - "sudo sed -i 's/port = 5433/port = 5432/' /etc/postgresql/10/main/postgresql.conf"
+ - "sudo service postgresql restart 10"
- rvm: 2.5.1
env: "GEM=av:ujs"
- rvm: 2.4.4
@@ -118,6 +147,24 @@ matrix:
- rvm: 2.5.1
env:
- "GEM=ar:sqlite3_mem"
+ - rvm: 2.4.4
+ env: "GEM=ar:postgresql"
+ sudo: required
+ before_install:
+ - "sudo sed -i 's/port = 5433/port = 5432/' /etc/postgresql/10/main/postgresql.conf"
+ - "sudo service postgresql restart 10"
+ - rvm: 2.5.1
+ env: "GEM=ar:postgresql"
+ sudo: required
+ before_install:
+ - "sudo sed -i 's/port = 5433/port = 5432/' /etc/postgresql/10/main/postgresql.conf"
+ - "sudo service postgresql restart 10"
+ - rvm: ruby-head
+ env: "GEM=ar:postgresql"
+ sudo: required
+ before_install:
+ - "sudo sed -i 's/port = 5433/port = 5432/' /etc/postgresql/10/main/postgresql.conf"
+ - "sudo service postgresql restart 10"
- rvm: 2.5.1
env:
- "GEM=ar:postgresql POSTGRES=9.2"
diff --git a/.yarnrc b/.yarnrc
new file mode 100644
index 0000000000..6be74e145d
--- /dev/null
+++ b/.yarnrc
@@ -0,0 +1,2 @@
+workspaces-experimental true
+--add.prefer-offline true \ No newline at end of file
diff --git a/Gemfile b/Gemfile
index 5bf443b7c8..c630f95482 100644
--- a/Gemfile
+++ b/Gemfile
@@ -12,10 +12,9 @@ gem "rake", ">= 11.1"
gem "capybara", ">= 2.15"
gem "rack-cache", "~> 1.2"
-gem "coffee-rails"
gem "sass-rails"
gem "turbolinks", "~> 5"
-
+gem "webpacker", github: "rails/webpacker", require: ENV["SKIP_REQUIRE_WEBPACKER"] != "true"
# require: false so bcrypt is loaded only when has_secure_password is used.
# This is to avoid Active Model (and by extension the entire framework)
# being dependent on a binary library.
diff --git a/Gemfile.lock b/Gemfile.lock
index 80563bd316..b4f46698bc 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -30,6 +30,15 @@ GIT
queue_classic (3.2.0.RC1)
pg (>= 0.17, < 2.0)
+GIT
+ remote: https://github.com/rails/webpacker.git
+ revision: 48d9fd52c3e9637dbb21e551b48c84c0f2dbb2ed
+ specs:
+ webpacker (4.0.0.pre.pre.2)
+ activesupport (>= 4.2)
+ rack-proxy (>= 0.6.1)
+ railties (>= 4.2)
+
PATH
remote: .
specs:
@@ -137,9 +146,9 @@ GEM
faraday (~> 0.9)
faraday_middleware (~> 0.10)
nokogiri (~> 1.6, >= 1.6.8)
- backburner (1.4.1)
+ backburner (1.5.0)
beaneater (~> 1.0)
- concurrent-ruby (~> 1.0.1)
+ concurrent-ruby (~> 1.0, >= 1.0.1)
dante (> 0.1.5)
bcrypt (3.1.12)
bcrypt (3.1.12-java)
@@ -181,12 +190,9 @@ GEM
xpath (~> 3.1)
childprocess (0.9.0)
ffi (~> 1.0, >= 1.0.11)
- chromedriver-helper (1.2.0)
+ chromedriver-helper (2.0.0)
archive-zip (~> 0.10)
nokogiri (~> 1.8)
- coffee-rails (4.2.2)
- coffee-script (>= 2.2.0)
- railties (>= 4.0.0)
coffee-script (2.4.1)
coffee-script-source
execjs
@@ -310,7 +316,7 @@ GEM
mime-types-data (~> 3.2015)
mime-types-data (3.2018.0812)
mimemagic (0.3.2)
- mini_magick (4.8.0)
+ mini_magick (4.9.2)
mini_mime (1.0.1)
mini_portile2 (2.3.0)
minitest (5.11.3)
@@ -333,12 +339,12 @@ GEM
mysql2 (0.5.2-x86-mingw32)
nio4r (2.3.1)
nio4r (2.3.1-java)
- nokogiri (1.8.4)
+ nokogiri (1.8.5)
mini_portile2 (~> 2.3.0)
- nokogiri (1.8.4-java)
- nokogiri (1.8.4-x64-mingw32)
+ nokogiri (1.8.5-java)
+ nokogiri (1.8.5-x64-mingw32)
mini_portile2 (~> 2.3.0)
- nokogiri (1.8.4-x86-mingw32)
+ nokogiri (1.8.5-x86-mingw32)
mini_portile2 (~> 2.3.0)
os (1.0.0)
parallel (1.12.1)
@@ -364,6 +370,8 @@ GEM
rack (>= 0.4)
rack-protection (2.0.3)
rack
+ rack-proxy (0.6.5)
+ rack
rack-test (1.1.0)
rack (>= 1.0, < 3)
rails-dom-testing (2.0.3)
@@ -518,7 +526,6 @@ DEPENDENCIES
byebug
capybara (>= 2.15)
chromedriver-helper
- coffee-rails
connection_pool
dalli
delayed_job
@@ -565,7 +572,8 @@ DEPENDENCIES
uglifier (>= 1.3.0)
w3c_validators
wdm (>= 0.1.0)
+ webpacker!
websocket-client-simple!
BUNDLED WITH
- 1.16.4
+ 1.16.5
diff --git a/actioncable/lib/action_cable.rb b/actioncable/lib/action_cable.rb
index 35eacc2f4f..d261d4112e 100644
--- a/actioncable/lib/action_cable.rb
+++ b/actioncable/lib/action_cable.rb
@@ -32,13 +32,13 @@ module ActionCable
INTERNAL = {
message_types: {
- welcome: "welcome".freeze,
- ping: "ping".freeze,
- confirmation: "confirm_subscription".freeze,
- rejection: "reject_subscription".freeze
+ welcome: "welcome",
+ ping: "ping",
+ confirmation: "confirm_subscription",
+ rejection: "reject_subscription"
},
- default_mount_path: "/cable".freeze,
- protocols: ["actioncable-v1-json".freeze, "actioncable-unsupported".freeze].freeze
+ default_mount_path: "/cable",
+ protocols: ["actioncable-v1-json", "actioncable-unsupported"].freeze
}
# Singleton instance of the server
diff --git a/actioncable/lib/action_cable/channel.rb b/actioncable/lib/action_cable/channel.rb
index d2f6fbbbc7..d5118b9dc9 100644
--- a/actioncable/lib/action_cable/channel.rb
+++ b/actioncable/lib/action_cable/channel.rb
@@ -11,6 +11,7 @@ module ActionCable
autoload :Naming
autoload :PeriodicTimers
autoload :Streams
+ autoload :TestCase
end
end
end
diff --git a/actioncable/lib/action_cable/channel/base.rb b/actioncable/lib/action_cable/channel/base.rb
index c5ad749bfe..70c93ec0f3 100644
--- a/actioncable/lib/action_cable/channel/base.rb
+++ b/actioncable/lib/action_cable/channel/base.rb
@@ -270,7 +270,7 @@ module ActionCable
end
def action_signature(action, data)
- "#{self.class.name}##{action}".dup.tap do |signature|
+ (+"#{self.class.name}##{action}").tap do |signature|
if (arguments = data.except("action")).any?
signature << "(#{arguments.inspect})"
end
diff --git a/actioncable/lib/action_cable/channel/test_case.rb b/actioncable/lib/action_cable/channel/test_case.rb
new file mode 100644
index 0000000000..dd2762ccd0
--- /dev/null
+++ b/actioncable/lib/action_cable/channel/test_case.rb
@@ -0,0 +1,271 @@
+# frozen_string_literal: true
+
+require "active_support"
+require "active_support/test_case"
+require "active_support/core_ext/hash/indifferent_access"
+require "json"
+
+module ActionCable
+ module Channel
+ class NonInferrableChannelError < ::StandardError
+ def initialize(name)
+ super "Unable to determine the channel to test from #{name}. " +
+ "You'll need to specify it using `tests YourChannel` in your " +
+ "test case definition."
+ end
+ end
+
+ # Stub `stream_from` to track streams for the channel.
+ # Add public aliases for `subscription_confirmation_sent?` and
+ # `subscription_rejected?`.
+ module ChannelStub
+ def confirmed?
+ subscription_confirmation_sent?
+ end
+
+ def rejected?
+ subscription_rejected?
+ end
+
+ def stream_from(broadcasting, *)
+ streams << broadcasting
+ end
+
+ def stop_all_streams
+ @_streams = []
+ end
+
+ def streams
+ @_streams ||= []
+ end
+
+ # Make periodic timers no-op
+ def start_periodic_timers; end
+ alias stop_periodic_timers start_periodic_timers
+ end
+
+ class ConnectionStub
+ attr_reader :transmissions, :identifiers, :subscriptions, :logger
+
+ def initialize(identifiers = {})
+ @transmissions = []
+
+ identifiers.each do |identifier, val|
+ define_singleton_method(identifier) { val }
+ end
+
+ @subscriptions = ActionCable::Connection::Subscriptions.new(self)
+ @identifiers = identifiers.keys
+ @logger = ActiveSupport::TaggedLogging.new ActiveSupport::Logger.new(StringIO.new)
+ end
+
+ def transmit(cable_message)
+ transmissions << cable_message.with_indifferent_access
+ end
+ end
+
+ # Superclass for Action Cable channel functional tests.
+ #
+ # == Basic example
+ #
+ # Functional tests are written as follows:
+ # 1. First, one uses the +subscribe+ method to simulate subscription creation.
+ # 2. Then, one asserts whether the current state is as expected. "State" can be anything:
+ # transmitted messages, subscribed streams, etc.
+ #
+ # For example:
+ #
+ # class ChatChannelTest < ActionCable::Channel::TestCase
+ # def test_subscribed_with_room_number
+ # # Simulate a subscription creation
+ # subscribe room_number: 1
+ #
+ # # Asserts that the subscription was successfully created
+ # assert subscription.confirmed?
+ #
+ # # Asserts that the channel subscribes connection to a stream
+ # assert_equal "chat_1", streams.last
+ # end
+ #
+ # def test_does_not_subscribe_without_room_number
+ # subscribe
+ #
+ # # Asserts that the subscription was rejected
+ # assert subscription.rejected?
+ # end
+ # end
+ #
+ # You can also perform actions:
+ # def test_perform_speak
+ # subscribe room_number: 1
+ #
+ # perform :speak, message: "Hello, Rails!"
+ #
+ # assert_equal "Hello, Rails!", transmissions.last["text"]
+ # end
+ #
+ # == Special methods
+ #
+ # ActionCable::Channel::TestCase will also automatically provide the following instance
+ # methods for use in the tests:
+ #
+ # <b>connection</b>::
+ # An ActionCable::Channel::ConnectionStub, representing the current HTTP connection.
+ # <b>subscription</b>::
+ # An instance of the current channel, created when you call `subscribe`.
+ # <b>transmissions</b>::
+ # A list of all messages that have been transmitted into the channel.
+ # <b>streams</b>::
+ # A list of all created streams subscriptions (as identifiers) for the subscription.
+ #
+ #
+ # == Channel is automatically inferred
+ #
+ # ActionCable::Channel::TestCase will automatically infer the channel under test
+ # from the test class name. If the channel cannot be inferred from the test
+ # class name, you can explicitly set it with +tests+.
+ #
+ # class SpecialEdgeCaseChannelTest < ActionCable::Channel::TestCase
+ # tests SpecialChannel
+ # end
+ #
+ # == Specifying connection identifiers
+ #
+ # You need to set up your connection manually to provide values for the identifiers.
+ # To do this just use:
+ #
+ # stub_connection(user: users[:john])
+ #
+ # == Testing broadcasting
+ #
+ # ActionCable::Channel::TestCase enhances ActionCable::TestHelper assertions (e.g.
+ # +assert_broadcasts+) to handle broadcasting to models:
+ #
+ #
+ # # in your channel
+ # def speak(data)
+ # broadcast_to room, text: data["message"]
+ # end
+ #
+ # def test_speak
+ # subscribe room_id: rooms[:chat].id
+ #
+ # assert_broadcasts_on(rooms[:chat], text: "Hello, Rails!") do
+ # perform :speak, message: "Hello, Rails!"
+ # end
+ # end
+ class TestCase < ActiveSupport::TestCase
+ module Behavior
+ extend ActiveSupport::Concern
+
+ include ActiveSupport::Testing::ConstantLookup
+ include ActionCable::TestHelper
+
+ CHANNEL_IDENTIFIER = "test_stub"
+
+ included do
+ class_attribute :_channel_class
+
+ attr_reader :connection, :subscription
+ delegate :streams, to: :subscription
+
+ ActiveSupport.run_load_hooks(:action_cable_channel_test_case, self)
+ end
+
+ module ClassMethods
+ def tests(channel)
+ case channel
+ when String, Symbol
+ self._channel_class = channel.to_s.camelize.constantize
+ when Module
+ self._channel_class = channel
+ else
+ raise NonInferrableChannelError.new(channel)
+ end
+ end
+
+ def channel_class
+ if channel = self._channel_class
+ channel
+ else
+ tests determine_default_channel(name)
+ end
+ end
+
+ def determine_default_channel(name)
+ channel = determine_constant_from_test_name(name) do |constant|
+ Class === constant && constant < ActionCable::Channel::Base
+ end
+ raise NonInferrableChannelError.new(name) if channel.nil?
+ channel
+ end
+ end
+
+ # Setup test connection with the specified identifiers:
+ #
+ # class ApplicationCable < ActionCable::Connection::Base
+ # identified_by :user, :token
+ # end
+ #
+ # stub_connection(user: users[:john], token: 'my-secret-token')
+ def stub_connection(identifiers = {})
+ @connection = ConnectionStub.new(identifiers)
+ end
+
+ # Subscribe to the channel under test. Optionally pass subscription parameters as a Hash.
+ def subscribe(params = {})
+ @connection ||= stub_connection
+ @subscription = self.class.channel_class.new(connection, CHANNEL_IDENTIFIER, params.with_indifferent_access)
+ @subscription.singleton_class.include(ChannelStub)
+ @subscription.subscribe_to_channel
+ @subscription
+ end
+
+ # Unsubscribe the subscription under test.
+ def unsubscribe
+ check_subscribed!
+ subscription.unsubscribe_from_channel
+ end
+
+ # Perform action on a channel.
+ #
+ # NOTE: Must be subscribed.
+ def perform(action, data = {})
+ check_subscribed!
+ subscription.perform_action(data.stringify_keys.merge("action" => action.to_s))
+ end
+
+ # Returns messages transmitted into channel
+ def transmissions
+ # Return only directly sent message (via #transmit)
+ connection.transmissions.map { |data| data["message"] }.compact
+ end
+
+ # Enhance TestHelper assertions to handle non-String
+ # broadcastings
+ def assert_broadcasts(stream_or_object, *args)
+ super(broadcasting_for(stream_or_object), *args)
+ end
+
+ def assert_broadcast_on(stream_or_object, *args)
+ super(broadcasting_for(stream_or_object), *args)
+ end
+
+ private
+ def check_subscribed!
+ raise "Must be subscribed!" if subscription.nil? || subscription.rejected?
+ end
+
+ def broadcasting_for(stream_or_object)
+ return stream_or_object if stream_or_object.is_a?(String)
+
+ self.class.channel_class.broadcasting_for(
+ [self.class.channel_class.channel_name, stream_or_object]
+ )
+ end
+ end
+
+ include Behavior
+ end
+ end
+end
diff --git a/actioncable/lib/rails/generators/channel/USAGE b/actioncable/lib/rails/generators/channel/USAGE
index dd109fda80..ea9662436c 100644
--- a/actioncable/lib/rails/generators/channel/USAGE
+++ b/actioncable/lib/rails/generators/channel/USAGE
@@ -1,14 +1,12 @@
Description:
============
- Stubs out a new cable channel for the server (in Ruby) and client (in CoffeeScript).
+ Stubs out a new cable channel for the server (in Ruby) and client (in JavaScript).
Pass the channel name, either CamelCased or under_scored, and an optional list of channel actions as arguments.
- Note: Turn on the cable connection in app/assets/javascripts/cable.js after generating any channels.
-
Example:
========
rails generate channel Chat speak
- creates a Chat channel class and CoffeeScript asset:
+ creates a Chat channel class and JavaScript asset:
Channel: app/channels/chat_channel.rb
- Assets: app/assets/javascripts/channels/chat.coffee
+ Assets: app/javascript/channels/chat_channel.js
diff --git a/actioncable/lib/rails/generators/channel/channel_generator.rb b/actioncable/lib/rails/generators/channel/channel_generator.rb
index 427eef1f55..ef51981e89 100644
--- a/actioncable/lib/rails/generators/channel/channel_generator.rb
+++ b/actioncable/lib/rails/generators/channel/channel_generator.rb
@@ -16,10 +16,11 @@ module Rails
if options[:assets]
if behavior == :invoke
- template "assets/cable.js", "app/assets/javascripts/cable.js"
+ template "javascript/index.js", "app/javascript/channels/index.js"
+ template "javascript/consumer.js", "app/javascript/channels/consumer.js"
end
- js_template "assets/channel", File.join("app/assets/javascripts/channels", class_path, "#{file_name}")
+ js_template "javascript/channel", File.join("app/javascript/channels", class_path, "#{file_name}_channel")
end
generate_application_cable_files
diff --git a/actioncable/lib/rails/generators/channel/templates/assets/channel.coffee.tt b/actioncable/lib/rails/generators/channel/templates/assets/channel.coffee.tt
deleted file mode 100644
index 5467811aba..0000000000
--- a/actioncable/lib/rails/generators/channel/templates/assets/channel.coffee.tt
+++ /dev/null
@@ -1,14 +0,0 @@
-App.<%= class_name.underscore %> = App.cable.subscriptions.create "<%= class_name %>Channel",
- connected: ->
- # Called when the subscription is ready for use on the server
-
- disconnected: ->
- # Called when the subscription has been terminated by the server
-
- received: (data) ->
- # Called when there's incoming data on the websocket for this channel
-<% actions.each do |action| -%>
-
- <%= action %>: ->
- @perform '<%= action %>'
-<% end -%>
diff --git a/actioncable/lib/rails/generators/channel/templates/assets/channel.js.tt b/actioncable/lib/rails/generators/channel/templates/javascript/channel.js.tt
index ab0e68b11a..33baaa5a22 100644
--- a/actioncable/lib/rails/generators/channel/templates/assets/channel.js.tt
+++ b/actioncable/lib/rails/generators/channel/templates/javascript/channel.js.tt
@@ -1,4 +1,6 @@
-App.<%= class_name.underscore %> = App.cable.subscriptions.create("<%= class_name %>Channel", {
+import consumer from "./consumer"
+
+consumer.subscriptions.create("<%= class_name %>Channel", {
connected: function() {
// Called when the subscription is ready for use on the server
},
diff --git a/actioncable/lib/rails/generators/channel/templates/assets/cable.js.tt b/actioncable/lib/rails/generators/channel/templates/javascript/consumer.js.tt
index 739aa5f022..76ca3d0f2f 100644
--- a/actioncable/lib/rails/generators/channel/templates/assets/cable.js.tt
+++ b/actioncable/lib/rails/generators/channel/templates/javascript/consumer.js.tt
@@ -1,13 +1,6 @@
// Action Cable provides the framework to deal with WebSockets in Rails.
// You can generate new channels where WebSocket features live using the `rails generate channel` command.
-//
-//= require action_cable
-//= require_self
-//= require_tree ./channels
-(function() {
- this.App || (this.App = {});
+import ActionCable from "actioncable"
- App.cable = ActionCable.createConsumer();
-
-}).call(this);
+export default ActionCable.createConsumer()
diff --git a/actioncable/lib/rails/generators/channel/templates/javascript/index.js.tt b/actioncable/lib/rails/generators/channel/templates/javascript/index.js.tt
new file mode 100644
index 0000000000..5da1ce2dce
--- /dev/null
+++ b/actioncable/lib/rails/generators/channel/templates/javascript/index.js.tt
@@ -0,0 +1,5 @@
+// Load all the channels within this directory and all subdirectories.
+// Channel files must be named *_channel.js.
+
+const channels = require.context('.', true, /\_channel\.js$/)
+channels.keys().forEach(channels)
diff --git a/actioncable/test/channel/stream_test.rb b/actioncable/test/channel/stream_test.rb
index bfe1f92946..9ad2213d47 100644
--- a/actioncable/test/channel/stream_test.rb
+++ b/actioncable/test/channel/stream_test.rb
@@ -26,16 +26,17 @@ module ActionCable::StreamTests
transmit_subscription_confirmation
end
- private def pick_coder(coder)
- case coder
- when nil, "json"
- ActiveSupport::JSON
- when "custom"
- DummyEncoder
- when "none"
- nil
+ private
+ def pick_coder(coder)
+ case coder
+ when nil, "json"
+ ActiveSupport::JSON
+ when "custom"
+ DummyEncoder
+ when "none"
+ nil
+ end
end
- end
end
module DummyEncoder
@@ -192,7 +193,7 @@ module ActionCable::StreamTests
end
end
- test "subscription confirmation should only be sent out once with muptiple stream_from" do
+ test "subscription confirmation should only be sent out once with multiple stream_from" do
run_in_eventmachine do
connection = open_connection
expected = { "identifier" => { "channel" => MultiChatChannel.name }.to_json, "type" => "confirm_subscription" }
diff --git a/actioncable/test/channel/test_case_test.rb b/actioncable/test/channel/test_case_test.rb
new file mode 100644
index 0000000000..63d0d6207e
--- /dev/null
+++ b/actioncable/test/channel/test_case_test.rb
@@ -0,0 +1,188 @@
+# frozen_string_literal: true
+
+require "test_helper"
+
+class TestTestChannel < ActionCable::Channel::Base
+end
+
+class NonInferrableExplicitClassChannelTest < ActionCable::Channel::TestCase
+ tests TestTestChannel
+
+ def test_set_channel_class_manual
+ assert_equal TestTestChannel, self.class.channel_class
+ end
+end
+
+class NonInferrableSymbolNameChannelTest < ActionCable::Channel::TestCase
+ tests :test_test_channel
+
+ def test_set_channel_class_manual_using_symbol
+ assert_equal TestTestChannel, self.class.channel_class
+ end
+end
+
+class NonInferrableStringNameChannelTest < ActionCable::Channel::TestCase
+ tests "test_test_channel"
+
+ def test_set_channel_class_manual_using_string
+ assert_equal TestTestChannel, self.class.channel_class
+ end
+end
+
+class SubscriptionsTestChannel < ActionCable::Channel::Base
+end
+
+class SubscriptionsTestChannelTest < ActionCable::Channel::TestCase
+ def setup
+ stub_connection
+ end
+
+ def test_no_subscribe
+ assert_nil subscription
+ end
+
+ def test_subscribe
+ subscribe
+
+ assert subscription.confirmed?
+ assert_not subscription.rejected?
+ assert_equal 1, connection.transmissions.size
+ assert_equal ActionCable::INTERNAL[:message_types][:confirmation],
+ connection.transmissions.last["type"]
+ end
+end
+
+class StubConnectionTest < ActionCable::Channel::TestCase
+ tests SubscriptionsTestChannel
+
+ def test_connection_identifiers
+ stub_connection username: "John", admin: true
+
+ subscribe
+
+ assert_equal "John", subscription.username
+ assert subscription.admin
+ end
+end
+
+class RejectionTestChannel < ActionCable::Channel::Base
+ def subscribed
+ reject
+ end
+end
+
+class RejectionTestChannelTest < ActionCable::Channel::TestCase
+ def test_rejection
+ subscribe
+
+ assert_not subscription.confirmed?
+ assert subscription.rejected?
+ assert_equal 1, connection.transmissions.size
+ assert_equal ActionCable::INTERNAL[:message_types][:rejection],
+ connection.transmissions.last["type"]
+ end
+end
+
+class StreamsTestChannel < ActionCable::Channel::Base
+ def subscribed
+ stream_from "test_#{params[:id] || 0}"
+ end
+end
+
+class StreamsTestChannelTest < ActionCable::Channel::TestCase
+ def test_stream_without_params
+ subscribe
+
+ assert_equal "test_0", streams.last
+ end
+
+ def test_stream_with_params
+ subscribe id: 42
+
+ assert_equal "test_42", streams.last
+ end
+end
+
+class PerformTestChannel < ActionCable::Channel::Base
+ def echo(data)
+ data.delete("action")
+ transmit data
+ end
+
+ def ping
+ transmit type: "pong"
+ end
+end
+
+class PerformTestChannelTest < ActionCable::Channel::TestCase
+ def setup
+ stub_connection user_id: 2016
+ subscribe id: 5
+ end
+
+ def test_perform_with_params
+ perform :echo, text: "You are man!"
+
+ assert_equal({ "text" => "You are man!" }, transmissions.last)
+ end
+
+ def test_perform_and_transmit
+ perform :ping
+
+ assert_equal "pong", transmissions.last["type"]
+ end
+end
+
+class PerformUnsubscribedTestChannelTest < ActionCable::Channel::TestCase
+ tests PerformTestChannel
+
+ def test_perform_when_unsubscribed
+ assert_raises do
+ perform :echo
+ end
+ end
+end
+
+class BroadcastsTestChannel < ActionCable::Channel::Base
+ def broadcast(data)
+ ActionCable.server.broadcast(
+ "broadcast_#{params[:id]}",
+ text: data["message"], user_id: user_id
+ )
+ end
+
+ def broadcast_to_user(data)
+ user = User.new user_id
+
+ self.class.broadcast_to user, text: data["message"]
+ end
+end
+
+class BroadcastsTestChannelTest < ActionCable::Channel::TestCase
+ def setup
+ stub_connection user_id: 2017
+ subscribe id: 5
+ end
+
+ def test_broadcast_matchers_included
+ assert_broadcast_on("broadcast_5", user_id: 2017, text: "SOS") do
+ perform :broadcast, message: "SOS"
+ end
+ end
+
+ def test_broadcast_to_object
+ user = User.new(2017)
+
+ assert_broadcasts(user, 1) do
+ perform :broadcast_to_user, text: "SOS"
+ end
+ end
+
+ def test_broadcast_to_object_with_data
+ user = User.new(2017)
+
+ assert_broadcast_on(user, text: "SOS") do
+ perform :broadcast_to_user, message: "SOS"
+ end
+ end
+end
diff --git a/actionmailer/CHANGELOG.md b/actionmailer/CHANGELOG.md
index 1aa7485d3e..db6fc7ee9c 100644
--- a/actionmailer/CHANGELOG.md
+++ b/actionmailer/CHANGELOG.md
@@ -1,3 +1,14 @@
+* Allow ActionMailer classes to configure the parameterized delivery job
+ Example:
+ ```
+ class MyMailer < ApplicationMailer
+ self.parameterized_delivery_job = MyCustomDeliveryJob
+ ...
+ end
+ ```
+
+ *Luke Pearce*
+
* `ActionDispatch::IntegrationTest` includes `ActionMailer::TestHelper` module by default.
*Ricardo Díaz*
diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb
index 55f701b18e..509d859ac3 100644
--- a/actionmailer/lib/action_mailer/base.rb
+++ b/actionmailer/lib/action_mailer/base.rb
@@ -462,6 +462,7 @@ module ActionMailer
helper ActionMailer::MailHelper
class_attribute :delivery_job, default: ::ActionMailer::DeliveryJob
+ class_attribute :parameterized_delivery_job, default: ::ActionMailer::Parameterized::DeliveryJob
class_attribute :default_params, default: {
mime_version: "1.0",
charset: "UTF-8",
@@ -1008,7 +1009,7 @@ module ActionMailer
end
def instrument_name
- "action_mailer".freeze
+ "action_mailer"
end
ActiveSupport.run_load_hooks(:action_mailer, self)
diff --git a/actionmailer/lib/action_mailer/message_delivery.rb b/actionmailer/lib/action_mailer/message_delivery.rb
index 2377aeb9a5..a57980c322 100644
--- a/actionmailer/lib/action_mailer/message_delivery.rb
+++ b/actionmailer/lib/action_mailer/message_delivery.rb
@@ -29,7 +29,7 @@ module ActionMailer
@mail_message ||= processed_mailer.message
end
- # Unused except for delegator internals (dup, marshaling).
+ # Unused except for delegator internals (dup, marshalling).
def __setobj__(mail_message) #:nodoc:
@mail_message = mail_message
end
diff --git a/actionmailer/lib/action_mailer/parameterized.rb b/actionmailer/lib/action_mailer/parameterized.rb
index 5e768e7106..0fe417affe 100644
--- a/actionmailer/lib/action_mailer/parameterized.rb
+++ b/actionmailer/lib/action_mailer/parameterized.rb
@@ -140,7 +140,8 @@ module ActionMailer
super
else
args = @mailer_class.name, @action.to_s, delivery_method.to_s, @params, *@args
- ActionMailer::Parameterized::DeliveryJob.set(options).perform_later(*args)
+ job = @mailer_class.parameterized_delivery_job
+ job.set(options).perform_later(*args)
end
end
end
diff --git a/actionmailer/test/abstract_unit.rb b/actionmailer/test/abstract_unit.rb
index 45f69d5375..f647896374 100644
--- a/actionmailer/test/abstract_unit.rb
+++ b/actionmailer/test/abstract_unit.rb
@@ -36,12 +36,14 @@ ActionMailer::Base.view_paths = FIXTURE_LOAD_PATH
class ActiveSupport::TestCase
include ActiveSupport::Testing::MethodCallAssertions
- # Skips the current run on Rubinius using Minitest::Assertions#skip
- private def rubinius_skip(message = "")
- skip message if RUBY_ENGINE == "rbx"
- end
- # Skips the current run on JRuby using Minitest::Assertions#skip
- private def jruby_skip(message = "")
- skip message if defined?(JRUBY_VERSION)
- end
+ private
+ # Skips the current run on Rubinius using Minitest::Assertions#skip
+ def rubinius_skip(message = "")
+ skip message if RUBY_ENGINE == "rbx"
+ end
+
+ # Skips the current run on JRuby using Minitest::Assertions#skip
+ def jruby_skip(message = "")
+ skip message if defined?(JRUBY_VERSION)
+ end
end
diff --git a/actionmailer/test/assert_select_email_test.rb b/actionmailer/test/assert_select_email_test.rb
index eb58ddd9c9..9699fe4000 100644
--- a/actionmailer/test/assert_select_email_test.rb
+++ b/actionmailer/test/assert_select_email_test.rb
@@ -25,7 +25,7 @@ class AssertSelectEmailTest < ActionMailer::TestCase
def test_assert_select_email
assert_raise ActiveSupport::TestCase::Assertion do
- assert_select_email {}
+ assert_select_email { }
end
AssertSelectMailer.test("<div><p>foo</p><p>bar</p></div>").deliver_now
diff --git a/actionmailer/test/base_test.rb b/actionmailer/test/base_test.rb
index 7898996c30..7a1a505398 100644
--- a/actionmailer/test/base_test.rb
+++ b/actionmailer/test/base_test.rb
@@ -122,7 +122,7 @@ class BaseTest < ActiveSupport::TestCase
email = BaseMailer.attachment_with_hash
assert_equal(1, email.attachments.length)
assert_equal("invoice.jpg", email.attachments[0].filename)
- expected = "\312\213\254\232)b".dup
+ expected = +"\312\213\254\232)b"
expected.force_encoding(Encoding::BINARY)
assert_equal expected, email.attachments["invoice.jpg"].decoded
end
@@ -131,7 +131,7 @@ class BaseTest < ActiveSupport::TestCase
email = BaseMailer.attachment_with_hash_default_encoding
assert_equal(1, email.attachments.length)
assert_equal("invoice.jpg", email.attachments[0].filename)
- expected = "\312\213\254\232)b".dup
+ expected = +"\312\213\254\232)b"
expected.force_encoding(Encoding::BINARY)
assert_equal expected, email.attachments["invoice.jpg"].decoded
end
diff --git a/actionmailer/test/parameterized_test.rb b/actionmailer/test/parameterized_test.rb
index ec6c5e9e67..d3b34fc3e3 100644
--- a/actionmailer/test/parameterized_test.rb
+++ b/actionmailer/test/parameterized_test.rb
@@ -53,4 +53,17 @@ class ParameterizedTest < ActiveSupport::TestCase
invitation = mailer.method(:anything)
end
end
+
+ test "should enqueue a parameterized request with the correct delivery job" do
+ old_delivery_job = ParamsMailer.parameterized_delivery_job
+ ParamsMailer.parameterized_delivery_job = ParameterizedDummyJob
+
+ assert_performed_with(job: ParameterizedDummyJob, args: ["ParamsMailer", "invitation", "deliver_now", { inviter: "david@basecamp.com", invitee: "jason@basecamp.com" } ]) do
+ @mail.deliver_later
+ end
+
+ ParamsMailer.parameterized_delivery_job = old_delivery_job
+ end
+
+ class ParameterizedDummyJob < ActionMailer::Parameterized::DeliveryJob; end
end
diff --git a/actionmailer/test/url_test.rb b/actionmailer/test/url_test.rb
index 3c940bc969..a926663a9f 100644
--- a/actionmailer/test/url_test.rb
+++ b/actionmailer/test/url_test.rb
@@ -8,11 +8,16 @@ end
AppRoutes = ActionDispatch::Routing::RouteSet.new
-class ActionMailer::Base
- include AppRoutes.url_helpers
+AppRoutes.draw do
+ get "/welcome" => "foo#bar", as: "welcome"
+ get "/dummy_model" => "foo#baz", as: "dummy_model"
+ get "/welcome/greeting", to: "welcome#greeting"
+ get "/a/b(/:id)", to: "a#b"
end
class UrlTestMailer < ActionMailer::Base
+ include AppRoutes.url_helpers
+
default_url_options[:host] = "www.basecamphq.com"
configure do |c|
@@ -80,14 +85,6 @@ class ActionMailerUrlTest < ActionMailer::TestCase
def test_url_for
UrlTestMailer.delivery_method = :test
- AppRoutes.draw do
- ActiveSupport::Deprecation.silence do
- get ":controller(/:action(/:id))"
- get "/welcome" => "foo#bar", as: "welcome"
- get "/dummy_model" => "foo#baz", as: "dummy_model"
- end
- end
-
# string
assert_url_for "http://foo/", "http://foo/"
@@ -111,13 +108,6 @@ class ActionMailerUrlTest < ActionMailer::TestCase
def test_signed_up_with_url
UrlTestMailer.delivery_method = :test
- AppRoutes.draw do
- ActiveSupport::Deprecation.silence do
- get ":controller(/:action(/:id))"
- get "/welcome" => "foo#bar", as: "welcome"
- end
- end
-
expected = new_mail
expected.to = @recipient
expected.subject = "[Signed up] Welcome #{@recipient}"
diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md
index 7781980cab..c972c64766 100644
--- a/actionpack/CHANGELOG.md
+++ b/actionpack/CHANGELOG.md
@@ -1,3 +1,19 @@
+* Remove undocumented `params` option from `url_for` helper.
+
+ *Ilkka Oksanen*
+
+* Encode Content-Disposition filenames on `send_data` and `send_file`.
+ Previously, `send_data 'data', filename: "\u{3042}.txt"` sends
+ `"filename=\"\u{3042}.txt\""` as Content-Disposition and it can be
+ garbled.
+ Now it follows [RFC 2231](https://tools.ietf.org/html/rfc2231) and
+ [RFC 5987](https://tools.ietf.org/html/rfc5987) and sends
+ `"filename=\"%3F.txt\"; filename*=UTF-8''%E3%81%82.txt"`.
+ Most browsers can find filename correctly and old browsers fallback to ASCII
+ converted name.
+
+ *Fumiaki Matsushima*
+
* Expose `ActionController::Parameters#each_key` which allows iterating over
keys without allocating an array.
diff --git a/actionpack/lib/abstract_controller/base.rb b/actionpack/lib/abstract_controller/base.rb
index 6e6786d0be..bb42f2e119 100644
--- a/actionpack/lib/abstract_controller/base.rb
+++ b/actionpack/lib/abstract_controller/base.rb
@@ -104,7 +104,7 @@ module AbstractController
# ==== Returns
# * <tt>String</tt>
def controller_path
- @controller_path ||= name.sub(/Controller$/, "".freeze).underscore unless anonymous?
+ @controller_path ||= name.sub(/Controller$/, "").underscore unless anonymous?
end
# Refresh the cached action_methods when a new action_method is added.
diff --git a/actionpack/lib/abstract_controller/helpers.rb b/actionpack/lib/abstract_controller/helpers.rb
index 3191584770..3913259ecc 100644
--- a/actionpack/lib/abstract_controller/helpers.rb
+++ b/actionpack/lib/abstract_controller/helpers.rb
@@ -181,7 +181,7 @@ module AbstractController
end
def default_helper_module!
- module_name = name.sub(/Controller$/, "".freeze)
+ module_name = name.sub(/Controller$/, "")
module_path = module_name.underscore
helper module_path
rescue LoadError => e
diff --git a/actionpack/lib/abstract_controller/railties/routes_helpers.rb b/actionpack/lib/abstract_controller/railties/routes_helpers.rb
index b6e5631a4e..fbd93705ed 100644
--- a/actionpack/lib/abstract_controller/railties/routes_helpers.rb
+++ b/actionpack/lib/abstract_controller/railties/routes_helpers.rb
@@ -7,7 +7,7 @@ module AbstractController
Module.new do
define_method(:inherited) do |klass|
super(klass)
- if namespace = klass.parents.detect { |m| m.respond_to?(:railtie_routes_url_helpers) }
+ if namespace = klass.module_parents.detect { |m| m.respond_to?(:railtie_routes_url_helpers) }
klass.include(namespace.railtie_routes_url_helpers(include_path_helpers))
else
klass.include(routes.url_helpers(include_path_helpers))
diff --git a/actionpack/lib/action_controller/caching.rb b/actionpack/lib/action_controller/caching.rb
index 97775d1dc8..bf3b00a7b7 100644
--- a/actionpack/lib/action_controller/caching.rb
+++ b/actionpack/lib/action_controller/caching.rb
@@ -40,7 +40,7 @@ module ActionController
end
def instrument_name
- "action_controller".freeze
+ "action_controller"
end
end
end
diff --git a/actionpack/lib/action_controller/log_subscriber.rb b/actionpack/lib/action_controller/log_subscriber.rb
index 14f41eb55f..6de1fb2c19 100644
--- a/actionpack/lib/action_controller/log_subscriber.rb
+++ b/actionpack/lib/action_controller/log_subscriber.rb
@@ -26,8 +26,8 @@ module ActionController
exception_class_name = payload[:exception].first
status = ActionDispatch::ExceptionWrapper.status_code_for_exception(exception_class_name)
end
- message = "Completed #{status} #{Rack::Utils::HTTP_STATUS_CODES[status]} in #{event.duration.round}ms".dup
- message << " (#{additions.join(" | ".freeze)})" unless additions.empty?
+ message = +"Completed #{status} #{Rack::Utils::HTTP_STATUS_CODES[status]} in #{event.duration.round}ms"
+ message << " (#{additions.join(" | ")})" unless additions.empty?
message << "\n\n" if defined?(Rails.env) && Rails.env.development?
message
diff --git a/actionpack/lib/action_controller/metal/data_streaming.rb b/actionpack/lib/action_controller/metal/data_streaming.rb
index 5a82ccf668..9ef4f50df1 100644
--- a/actionpack/lib/action_controller/metal/data_streaming.rb
+++ b/actionpack/lib/action_controller/metal/data_streaming.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true
require "action_controller/metal/exceptions"
+require "action_dispatch/http/content_disposition"
module ActionController #:nodoc:
# Methods for sending arbitrary data and for streaming files to the browser,
@@ -10,8 +11,8 @@ module ActionController #:nodoc:
include ActionController::Rendering
- DEFAULT_SEND_FILE_TYPE = "application/octet-stream".freeze #:nodoc:
- DEFAULT_SEND_FILE_DISPOSITION = "attachment".freeze #:nodoc:
+ DEFAULT_SEND_FILE_TYPE = "application/octet-stream" #:nodoc:
+ DEFAULT_SEND_FILE_DISPOSITION = "attachment" #:nodoc:
private
# Sends the file. This uses a server-appropriate method (such as X-Sendfile)
@@ -132,10 +133,8 @@ module ActionController #:nodoc:
end
disposition = options.fetch(:disposition, DEFAULT_SEND_FILE_DISPOSITION)
- unless disposition.nil?
- disposition = disposition.to_s
- disposition += %(; filename="#{options[:filename]}") if options[:filename]
- headers["Content-Disposition"] = disposition
+ if disposition
+ headers["Content-Disposition"] = ActionDispatch::Http::ContentDisposition.format(disposition: disposition, filename: options[:filename])
end
headers["Content-Transfer-Encoding"] = "binary"
diff --git a/actionpack/lib/action_controller/metal/http_authentication.rb b/actionpack/lib/action_controller/metal/http_authentication.rb
index a871ccd533..7036123d5d 100644
--- a/actionpack/lib/action_controller/metal/http_authentication.rb
+++ b/actionpack/lib/action_controller/metal/http_authentication.rb
@@ -127,7 +127,7 @@ module ActionController
def authentication_request(controller, realm, message)
message ||= "HTTP Basic: Access denied.\n"
- controller.headers["WWW-Authenticate"] = %(Basic realm="#{realm.tr('"'.freeze, "".freeze)}")
+ controller.headers["WWW-Authenticate"] = %(Basic realm="#{realm.tr('"', "")}")
controller.status = 401
controller.response_body = message
end
@@ -474,7 +474,7 @@ module ActionController
# This removes the <tt>"</tt> characters wrapping the value.
def rewrite_param_values(array_params)
- array_params.each { |param| (param[1] || "".dup).gsub! %r/^"|"$/, "" }
+ array_params.each { |param| (param[1] || +"").gsub! %r/^"|"$/, "" }
end
# This method takes an authorization body and splits up the key-value
@@ -511,7 +511,7 @@ module ActionController
# Returns nothing.
def authentication_request(controller, realm, message = nil)
message ||= "HTTP Token: Access denied.\n"
- controller.headers["WWW-Authenticate"] = %(Token realm="#{realm.tr('"'.freeze, "".freeze)}")
+ controller.headers["WWW-Authenticate"] = %(Token realm="#{realm.tr('"', "")}")
controller.__send__ :render, plain: message, status: :unauthorized
end
end
diff --git a/actionpack/lib/action_controller/metal/live.rb b/actionpack/lib/action_controller/metal/live.rb
index b1c2391afe..1482b2999a 100644
--- a/actionpack/lib/action_controller/metal/live.rb
+++ b/actionpack/lib/action_controller/metal/live.rb
@@ -117,7 +117,7 @@ module ActionController
end
end
- message = json.gsub("\n".freeze, "\ndata: ".freeze)
+ message = json.gsub("\n", "\ndata: ")
@stream.write "data: #{message}\n\n"
end
end
@@ -297,7 +297,7 @@ module ActionController
return unless logger
logger.fatal do
- message = "\n#{exception.class} (#{exception.message}):\n".dup
+ message = +"\n#{exception.class} (#{exception.message}):\n"
message << exception.annoted_source_code.to_s if exception.respond_to?(:annoted_source_code)
message << " " << exception.backtrace.join("\n ")
"#{message}\n\n"
diff --git a/actionpack/lib/action_controller/metal/rendering.rb b/actionpack/lib/action_controller/metal/rendering.rb
index 6d181e6456..7d0a944381 100644
--- a/actionpack/lib/action_controller/metal/rendering.rb
+++ b/actionpack/lib/action_controller/metal/rendering.rb
@@ -40,7 +40,7 @@ module ActionController
def render_to_string(*)
result = super
if result.respond_to?(:each)
- string = "".dup
+ string = +""
result.each { |r| string << r }
string
else
diff --git a/actionpack/lib/action_controller/metal/strong_parameters.rb b/actionpack/lib/action_controller/metal/strong_parameters.rb
index a37f08d944..c1272ce667 100644
--- a/actionpack/lib/action_controller/metal/strong_parameters.rb
+++ b/actionpack/lib/action_controller/metal/strong_parameters.rb
@@ -348,6 +348,14 @@ module ActionController
end
alias_method :each, :each_pair
+ # Convert all hashes in values into parameters, then yield each value in
+ # the same way as <tt>Hash#each_value</tt>.
+ def each_value(&block)
+ @parameters.each_pair do |key, value|
+ yield [convert_hashes_to_parameters(key, value)]
+ end
+ end
+
# Attribute that keeps track of converted arrays, if any, to avoid double
# looping in the common use case permit + mass-assignment. Defined in a
# method to instantiate it only if needed.
diff --git a/actionpack/lib/action_controller/metal/url_for.rb b/actionpack/lib/action_controller/metal/url_for.rb
index 84dbb59a63..f077e765ab 100644
--- a/actionpack/lib/action_controller/metal/url_for.rb
+++ b/actionpack/lib/action_controller/metal/url_for.rb
@@ -44,7 +44,7 @@ module ActionController
options[:original_script_name] = original_script_name
else
if same_origin
- options[:script_name] = request.script_name.empty? ? "".freeze : request.script_name.dup
+ options[:script_name] = request.script_name.empty? ? "" : request.script_name.dup
else
options[:script_name] = script_name
end
diff --git a/actionpack/lib/action_controller/railties/helpers.rb b/actionpack/lib/action_controller/railties/helpers.rb
index fa746fa9e8..75938108d6 100644
--- a/actionpack/lib/action_controller/railties/helpers.rb
+++ b/actionpack/lib/action_controller/railties/helpers.rb
@@ -7,7 +7,7 @@ module ActionController
super
return unless klass.respond_to?(:helpers_path=)
- if namespace = klass.parents.detect { |m| m.respond_to?(:railtie_helpers_paths) }
+ if namespace = klass.module_parents.detect { |m| m.respond_to?(:railtie_helpers_paths) }
paths = namespace.railtie_helpers_paths
else
paths = ActionController::Helpers.helpers_path
diff --git a/actionpack/lib/action_dispatch/http/cache.rb b/actionpack/lib/action_dispatch/http/cache.rb
index a7c7cfc1e5..f67b13f657 100644
--- a/actionpack/lib/action_dispatch/http/cache.rb
+++ b/actionpack/lib/action_dispatch/http/cache.rb
@@ -4,8 +4,8 @@ module ActionDispatch
module Http
module Cache
module Request
- HTTP_IF_MODIFIED_SINCE = "HTTP_IF_MODIFIED_SINCE".freeze
- HTTP_IF_NONE_MATCH = "HTTP_IF_NONE_MATCH".freeze
+ HTTP_IF_MODIFIED_SINCE = "HTTP_IF_MODIFIED_SINCE"
+ HTTP_IF_NONE_MATCH = "HTTP_IF_NONE_MATCH"
def if_modified_since
if since = get_header(HTTP_IF_MODIFIED_SINCE)
@@ -124,8 +124,8 @@ module ActionDispatch
private
- DATE = "Date".freeze
- LAST_MODIFIED = "Last-Modified".freeze
+ DATE = "Date"
+ LAST_MODIFIED = "Last-Modified"
SPECIAL_KEYS = Set.new(%w[extras no-cache max-age public private must-revalidate])
def generate_weak_etag(validators)
@@ -166,11 +166,11 @@ module ActionDispatch
@cache_control = cache_control_headers
end
- DEFAULT_CACHE_CONTROL = "max-age=0, private, must-revalidate".freeze
- NO_CACHE = "no-cache".freeze
- PUBLIC = "public".freeze
- PRIVATE = "private".freeze
- MUST_REVALIDATE = "must-revalidate".freeze
+ DEFAULT_CACHE_CONTROL = "max-age=0, private, must-revalidate"
+ NO_CACHE = "no-cache"
+ PUBLIC = "public"
+ PRIVATE = "private"
+ MUST_REVALIDATE = "must-revalidate"
def handle_conditional_get!
# Normally default cache control setting is handled by ETag
diff --git a/actionpack/lib/action_dispatch/http/content_disposition.rb b/actionpack/lib/action_dispatch/http/content_disposition.rb
new file mode 100644
index 0000000000..58164c1522
--- /dev/null
+++ b/actionpack/lib/action_dispatch/http/content_disposition.rb
@@ -0,0 +1,45 @@
+# frozen_string_literal: true
+
+module ActionDispatch
+ module Http
+ class ContentDisposition # :nodoc:
+ def self.format(disposition:, filename:)
+ new(disposition: disposition, filename: filename).to_s
+ end
+
+ attr_reader :disposition, :filename
+
+ def initialize(disposition:, filename:)
+ @disposition = disposition
+ @filename = filename
+ end
+
+ TRADITIONAL_ESCAPED_CHAR = /[^ A-Za-z0-9!#$+.^_`|~-]/
+
+ def ascii_filename
+ 'filename="' + percent_escape(I18n.transliterate(filename), TRADITIONAL_ESCAPED_CHAR) + '"'
+ end
+
+ RFC_5987_ESCAPED_CHAR = /[^A-Za-z0-9!#$&+.^_`|~-]/
+
+ def utf8_filename
+ "filename*=UTF-8''" + percent_escape(filename, RFC_5987_ESCAPED_CHAR)
+ end
+
+ def to_s
+ if filename
+ "#{disposition}; #{ascii_filename}; #{utf8_filename}"
+ else
+ "#{disposition}"
+ end
+ end
+
+ private
+ def percent_escape(string, pattern)
+ string.gsub(pattern) do |char|
+ char.bytes.map { |byte| "%%%02X" % byte }.join
+ end
+ end
+ end
+ end
+end
diff --git a/actionpack/lib/action_dispatch/http/content_security_policy.rb b/actionpack/lib/action_dispatch/http/content_security_policy.rb
index 35041fd072..50953e32b5 100644
--- a/actionpack/lib/action_dispatch/http/content_security_policy.rb
+++ b/actionpack/lib/action_dispatch/http/content_security_policy.rb
@@ -5,9 +5,9 @@ require "active_support/core_ext/object/deep_dup"
module ActionDispatch #:nodoc:
class ContentSecurityPolicy
class Middleware
- CONTENT_TYPE = "Content-Type".freeze
- POLICY = "Content-Security-Policy".freeze
- POLICY_REPORT_ONLY = "Content-Security-Policy-Report-Only".freeze
+ CONTENT_TYPE = "Content-Type"
+ POLICY = "Content-Security-Policy"
+ POLICY_REPORT_ONLY = "Content-Security-Policy-Report-Only"
def initialize(app)
@app = app
@@ -50,10 +50,10 @@ module ActionDispatch #:nodoc:
end
module Request
- POLICY = "action_dispatch.content_security_policy".freeze
- POLICY_REPORT_ONLY = "action_dispatch.content_security_policy_report_only".freeze
- NONCE_GENERATOR = "action_dispatch.content_security_policy_nonce_generator".freeze
- NONCE = "action_dispatch.content_security_policy_nonce".freeze
+ POLICY = "action_dispatch.content_security_policy"
+ POLICY_REPORT_ONLY = "action_dispatch.content_security_policy_report_only"
+ NONCE_GENERATOR = "action_dispatch.content_security_policy_nonce_generator"
+ NONCE = "action_dispatch.content_security_policy_nonce"
def content_security_policy
get_header(POLICY)
@@ -132,7 +132,7 @@ module ActionDispatch #:nodoc:
worker_src: "worker-src"
}.freeze
- NONCE_DIRECTIVES = %w[script-src].freeze
+ NONCE_DIRECTIVES = %w[script-src style-src].freeze
private_constant :MAPPINGS, :DIRECTIVES, :NONCE_DIRECTIVES
diff --git a/actionpack/lib/action_dispatch/http/filter_redirect.rb b/actionpack/lib/action_dispatch/http/filter_redirect.rb
index 25394fe5dd..8c4e852235 100644
--- a/actionpack/lib/action_dispatch/http/filter_redirect.rb
+++ b/actionpack/lib/action_dispatch/http/filter_redirect.rb
@@ -3,7 +3,7 @@
module ActionDispatch
module Http
module FilterRedirect
- FILTERED = "[FILTERED]".freeze # :nodoc:
+ FILTERED = "[FILTERED]" # :nodoc:
def filtered_location # :nodoc:
if location_filter_match?
diff --git a/actionpack/lib/action_dispatch/http/mime_type.rb b/actionpack/lib/action_dispatch/http/mime_type.rb
index 295539281f..dd74695229 100644
--- a/actionpack/lib/action_dispatch/http/mime_type.rb
+++ b/actionpack/lib/action_dispatch/http/mime_type.rb
@@ -74,7 +74,7 @@ module Mime
def initialize(index, name, q = nil)
@index = index
@name = name
- q ||= 0.0 if @name == "*/*".freeze # Default wildcard match to end of list.
+ q ||= 0.0 if @name == "*/*" # Default wildcard match to end of list.
@q = ((q || 1.0).to_f * 100).to_i
end
diff --git a/actionpack/lib/action_dispatch/http/parameter_filter.rb b/actionpack/lib/action_dispatch/http/parameter_filter.rb
index 09aab631ed..6689092859 100644
--- a/actionpack/lib/action_dispatch/http/parameter_filter.rb
+++ b/actionpack/lib/action_dispatch/http/parameter_filter.rb
@@ -6,7 +6,7 @@ require "active_support/core_ext/array/extract"
module ActionDispatch
module Http
class ParameterFilter
- FILTERED = "[FILTERED]".freeze # :nodoc:
+ FILTERED = "[FILTERED]" # :nodoc:
def initialize(filters = [])
@filters = filters
@@ -39,11 +39,11 @@ module ActionDispatch
end
end
- deep_regexps = regexps.extract! { |r| r.to_s.include?("\\.".freeze) }
- deep_strings = strings.extract! { |s| s.include?("\\.".freeze) }
+ deep_regexps = regexps.extract! { |r| r.to_s.include?("\\.") }
+ deep_strings = strings.extract! { |s| s.include?("\\.") }
- regexps << Regexp.new(strings.join("|".freeze), true) unless strings.empty?
- deep_regexps << Regexp.new(deep_strings.join("|".freeze), true) unless deep_strings.empty?
+ regexps << Regexp.new(strings.join("|"), true) unless strings.empty?
+ deep_regexps << Regexp.new(deep_strings.join("|"), true) unless deep_strings.empty?
new regexps, deep_regexps, blocks
end
diff --git a/actionpack/lib/action_dispatch/http/request.rb b/actionpack/lib/action_dispatch/http/request.rb
index 3838b84a7a..7bc364d370 100644
--- a/actionpack/lib/action_dispatch/http/request.rb
+++ b/actionpack/lib/action_dispatch/http/request.rb
@@ -136,11 +136,11 @@ module ActionDispatch
end
def routes # :nodoc:
- get_header("action_dispatch.routes".freeze)
+ get_header("action_dispatch.routes")
end
def routes=(routes) # :nodoc:
- set_header("action_dispatch.routes".freeze, routes)
+ set_header("action_dispatch.routes", routes)
end
def engine_script_name(_routes) # :nodoc:
@@ -158,11 +158,11 @@ module ActionDispatch
end
def controller_instance # :nodoc:
- get_header("action_controller.instance".freeze)
+ get_header("action_controller.instance")
end
def controller_instance=(controller) # :nodoc:
- set_header("action_controller.instance".freeze, controller)
+ set_header("action_controller.instance", controller)
end
def http_auth_salt
@@ -173,7 +173,7 @@ module ActionDispatch
# We're treating `nil` as "unset", and we want the default setting to be
# `true`. This logic should be extracted to `env_config` and calculated
# once.
- !(get_header("action_dispatch.show_exceptions".freeze) == false)
+ !(get_header("action_dispatch.show_exceptions") == false)
end
# Returns a symbol form of the #request_method.
@@ -280,10 +280,10 @@ module ActionDispatch
end
def remote_ip=(remote_ip)
- set_header "action_dispatch.remote_ip".freeze, remote_ip
+ set_header "action_dispatch.remote_ip", remote_ip
end
- ACTION_DISPATCH_REQUEST_ID = "action_dispatch.request_id".freeze # :nodoc:
+ ACTION_DISPATCH_REQUEST_ID = "action_dispatch.request_id" # :nodoc:
# Returns the unique request id, which is based on either the X-Request-Id header that can
# be generated by a firewall, load balancer, or web server or by the RequestId middleware
@@ -407,18 +407,18 @@ module ActionDispatch
def request_parameters=(params)
raise if params.nil?
- set_header("action_dispatch.request.request_parameters".freeze, params)
+ set_header("action_dispatch.request.request_parameters", params)
end
def logger
- get_header("action_dispatch.logger".freeze)
+ get_header("action_dispatch.logger")
end
def commit_flash
end
def ssl?
- super || scheme == "wss".freeze
+ super || scheme == "wss"
end
private
diff --git a/actionpack/lib/action_dispatch/http/response.rb b/actionpack/lib/action_dispatch/http/response.rb
index 885dc0ecba..1d38942a31 100644
--- a/actionpack/lib/action_dispatch/http/response.rb
+++ b/actionpack/lib/action_dispatch/http/response.rb
@@ -78,9 +78,9 @@ module ActionDispatch # :nodoc:
x
end
- CONTENT_TYPE = "Content-Type".freeze
- SET_COOKIE = "Set-Cookie".freeze
- LOCATION = "Location".freeze
+ CONTENT_TYPE = "Content-Type"
+ SET_COOKIE = "Set-Cookie"
+ LOCATION = "Location"
NO_CONTENT_CODES = [100, 101, 102, 204, 205, 304]
cattr_accessor :default_charset, default: "utf-8"
@@ -105,7 +105,7 @@ module ActionDispatch # :nodoc:
def body
@str_body ||= begin
- buf = "".dup
+ buf = +""
each { |chunk| buf << chunk }
buf
end
diff --git a/actionpack/lib/action_dispatch/http/url.rb b/actionpack/lib/action_dispatch/http/url.rb
index 35ba44005a..3af4c176a7 100644
--- a/actionpack/lib/action_dispatch/http/url.rb
+++ b/actionpack/lib/action_dispatch/http/url.rb
@@ -67,7 +67,7 @@ module ActionDispatch
end
def path_for(options)
- path = options[:script_name].to_s.chomp("/".freeze)
+ path = options[:script_name].to_s.chomp("/")
path << options[:path] if options.key?(:path)
add_trailing_slash(path) if options[:trailing_slash]
@@ -157,7 +157,7 @@ module ActionDispatch
subdomain = options.fetch :subdomain, true
domain = options[:domain]
- host = "".dup
+ host = +""
if subdomain == true
return _host if domain.nil?
@@ -231,7 +231,7 @@ module ActionDispatch
# req = ActionDispatch::Request.new 'HTTP_HOST' => 'example.com:8080'
# req.host # => "example.com"
def host
- raw_host_with_port.sub(/:\d+$/, "".freeze)
+ raw_host_with_port.sub(/:\d+$/, "")
end
# Returns a \host:\port string for this request, such as "example.com" or
diff --git a/actionpack/lib/action_dispatch/journey/formatter.rb b/actionpack/lib/action_dispatch/journey/formatter.rb
index 0f04839d9b..52396ec901 100644
--- a/actionpack/lib/action_dispatch/journey/formatter.rb
+++ b/actionpack/lib/action_dispatch/journey/formatter.rb
@@ -50,7 +50,7 @@ module ActionDispatch
unmatched_keys = (missing_keys || []) & constraints.keys
missing_keys = (missing_keys || []) - unmatched_keys
- message = "No route matches #{Hash[constraints.sort_by { |k, v| k.to_s }].inspect}".dup
+ message = +"No route matches #{Hash[constraints.sort_by { |k, v| k.to_s }].inspect}"
message << ", missing required keys: #{missing_keys.sort.inspect}" if missing_keys && !missing_keys.empty?
message << ", possible unmatched constraints: #{unmatched_keys.sort.inspect}" if unmatched_keys && !unmatched_keys.empty?
diff --git a/actionpack/lib/action_dispatch/journey/router/utils.rb b/actionpack/lib/action_dispatch/journey/router/utils.rb
index df3f79a407..3c8b9a6eaa 100644
--- a/actionpack/lib/action_dispatch/journey/router/utils.rb
+++ b/actionpack/lib/action_dispatch/journey/router/utils.rb
@@ -17,11 +17,11 @@ module ActionDispatch
def self.normalize_path(path)
path ||= ""
encoding = path.encoding
- path = "/#{path}".dup
- path.squeeze!("/".freeze)
- path.sub!(%r{/+\Z}, "".freeze)
+ path = +"/#{path}"
+ path.squeeze!("/")
+ path.sub!(%r{/+\Z}, "")
path.gsub!(/(%[a-f0-9]{2})/) { $1.upcase }
- path = "/".dup if path == "".freeze
+ path = +"/" if path == ""
path.force_encoding(encoding)
path
end
@@ -29,16 +29,16 @@ module ActionDispatch
# URI path and fragment escaping
# https://tools.ietf.org/html/rfc3986
class UriEncoder # :nodoc:
- ENCODE = "%%%02X".freeze
+ ENCODE = "%%%02X"
US_ASCII = Encoding::US_ASCII
UTF_8 = Encoding::UTF_8
- EMPTY = "".dup.force_encoding(US_ASCII).freeze
+ EMPTY = (+"").force_encoding(US_ASCII).freeze
DEC2HEX = (0..255).to_a.map { |i| ENCODE % i }.map { |s| s.force_encoding(US_ASCII) }
- ALPHA = "a-zA-Z".freeze
- DIGIT = "0-9".freeze
- UNRESERVED = "#{ALPHA}#{DIGIT}\\-\\._~".freeze
- SUB_DELIMS = "!\\$&'\\(\\)\\*\\+,;=".freeze
+ ALPHA = "a-zA-Z"
+ DIGIT = "0-9"
+ UNRESERVED = "#{ALPHA}#{DIGIT}\\-\\._~"
+ SUB_DELIMS = "!\\$&'\\(\\)\\*\\+,;="
ESCAPED = /%[a-zA-Z0-9]{2}/.freeze
diff --git a/actionpack/lib/action_dispatch/journey/visitors.rb b/actionpack/lib/action_dispatch/journey/visitors.rb
index 3395471a85..d2619cbf3a 100644
--- a/actionpack/lib/action_dispatch/journey/visitors.rb
+++ b/actionpack/lib/action_dispatch/journey/visitors.rb
@@ -40,7 +40,7 @@ module ActionDispatch
@parameters.each do |index|
param = parts[index]
value = hash[param.name]
- return "".freeze unless value
+ return "" unless value
parts[index] = param.escape value
end
diff --git a/actionpack/lib/action_dispatch/middleware/cookies.rb b/actionpack/lib/action_dispatch/middleware/cookies.rb
index 34331b7e4b..26d3fd936f 100644
--- a/actionpack/lib/action_dispatch/middleware/cookies.rb
+++ b/actionpack/lib/action_dispatch/middleware/cookies.rb
@@ -9,7 +9,7 @@ require "rack/utils"
module ActionDispatch
class Request
def cookie_jar
- fetch_header("action_dispatch.cookies".freeze) do
+ fetch_header("action_dispatch.cookies") do
self.cookie_jar = Cookies::CookieJar.build(self, cookies)
end
end
@@ -22,11 +22,11 @@ module ActionDispatch
}
def have_cookie_jar?
- has_header? "action_dispatch.cookies".freeze
+ has_header? "action_dispatch.cookies"
end
def cookie_jar=(jar)
- set_header "action_dispatch.cookies".freeze, jar
+ set_header "action_dispatch.cookies", jar
end
def key_generator
@@ -172,21 +172,21 @@ module ActionDispatch
# * <tt>:httponly</tt> - Whether this cookie is accessible via scripting or
# only HTTP. Defaults to +false+.
class Cookies
- HTTP_HEADER = "Set-Cookie".freeze
- GENERATOR_KEY = "action_dispatch.key_generator".freeze
- SIGNED_COOKIE_SALT = "action_dispatch.signed_cookie_salt".freeze
- ENCRYPTED_COOKIE_SALT = "action_dispatch.encrypted_cookie_salt".freeze
- ENCRYPTED_SIGNED_COOKIE_SALT = "action_dispatch.encrypted_signed_cookie_salt".freeze
- AUTHENTICATED_ENCRYPTED_COOKIE_SALT = "action_dispatch.authenticated_encrypted_cookie_salt".freeze
- USE_AUTHENTICATED_COOKIE_ENCRYPTION = "action_dispatch.use_authenticated_cookie_encryption".freeze
- ENCRYPTED_COOKIE_CIPHER = "action_dispatch.encrypted_cookie_cipher".freeze
- SIGNED_COOKIE_DIGEST = "action_dispatch.signed_cookie_digest".freeze
- SECRET_TOKEN = "action_dispatch.secret_token".freeze
- SECRET_KEY_BASE = "action_dispatch.secret_key_base".freeze
- COOKIES_SERIALIZER = "action_dispatch.cookies_serializer".freeze
- COOKIES_DIGEST = "action_dispatch.cookies_digest".freeze
- COOKIES_ROTATIONS = "action_dispatch.cookies_rotations".freeze
- USE_COOKIES_WITH_METADATA = "action_dispatch.use_cookies_with_metadata".freeze
+ HTTP_HEADER = "Set-Cookie"
+ GENERATOR_KEY = "action_dispatch.key_generator"
+ SIGNED_COOKIE_SALT = "action_dispatch.signed_cookie_salt"
+ ENCRYPTED_COOKIE_SALT = "action_dispatch.encrypted_cookie_salt"
+ ENCRYPTED_SIGNED_COOKIE_SALT = "action_dispatch.encrypted_signed_cookie_salt"
+ AUTHENTICATED_ENCRYPTED_COOKIE_SALT = "action_dispatch.authenticated_encrypted_cookie_salt"
+ USE_AUTHENTICATED_COOKIE_ENCRYPTION = "action_dispatch.use_authenticated_cookie_encryption"
+ ENCRYPTED_COOKIE_CIPHER = "action_dispatch.encrypted_cookie_cipher"
+ SIGNED_COOKIE_DIGEST = "action_dispatch.signed_cookie_digest"
+ SECRET_TOKEN = "action_dispatch.secret_token"
+ SECRET_KEY_BASE = "action_dispatch.secret_key_base"
+ COOKIES_SERIALIZER = "action_dispatch.cookies_serializer"
+ COOKIES_DIGEST = "action_dispatch.cookies_digest"
+ COOKIES_ROTATIONS = "action_dispatch.cookies_rotations"
+ USE_COOKIES_WITH_METADATA = "action_dispatch.use_cookies_with_metadata"
# Cookies can typically store 4096 bytes.
MAX_COOKIE_SIZE = 4096
@@ -535,7 +535,7 @@ module ActionDispatch
end
module SerializedCookieJars # :nodoc:
- MARSHAL_SIGNATURE = "\x04\x08".freeze
+ MARSHAL_SIGNATURE = "\x04\x08"
SERIALIZER = ActiveSupport::MessageEncryptor::NullSerializer
protected
diff --git a/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb b/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb
index 077a83b112..5f5fdbc66a 100644
--- a/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb
+++ b/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb
@@ -23,7 +23,7 @@ module ActionDispatch
if clean_params.empty?
"None"
else
- PP.pp(clean_params, "".dup, 200)
+ PP.pp(clean_params, +"", 200)
end
end
diff --git a/actionpack/lib/action_dispatch/middleware/debug_locks.rb b/actionpack/lib/action_dispatch/middleware/debug_locks.rb
index 03760438f7..93c6c85a71 100644
--- a/actionpack/lib/action_dispatch/middleware/debug_locks.rb
+++ b/actionpack/lib/action_dispatch/middleware/debug_locks.rb
@@ -32,7 +32,7 @@ module ActionDispatch
req = ActionDispatch::Request.new env
if req.get?
- path = req.path_info.chomp("/".freeze)
+ path = req.path_info.chomp("/")
if path == @path
return render_details(req)
end
@@ -63,19 +63,19 @@ module ActionDispatch
str = threads.map do |thread, info|
if info[:exclusive]
- lock_state = "Exclusive".dup
+ lock_state = +"Exclusive"
elsif info[:sharing] > 0
- lock_state = "Sharing".dup
+ lock_state = +"Sharing"
lock_state << " x#{info[:sharing]}" if info[:sharing] > 1
else
- lock_state = "No lock".dup
+ lock_state = +"No lock"
end
if info[:waiting]
lock_state << " (yielded share)"
end
- msg = "Thread #{info[:index]} [0x#{thread.__id__.to_s(16)} #{thread.status || 'dead'}] #{lock_state}\n".dup
+ msg = +"Thread #{info[:index]} [0x#{thread.__id__.to_s(16)} #{thread.status || 'dead'}] #{lock_state}\n"
if info[:sleeper]
msg << " Waiting in #{info[:sleeper]}"
diff --git a/actionpack/lib/action_dispatch/middleware/flash.rb b/actionpack/lib/action_dispatch/middleware/flash.rb
index fd05eec172..cf9165d008 100644
--- a/actionpack/lib/action_dispatch/middleware/flash.rb
+++ b/actionpack/lib/action_dispatch/middleware/flash.rb
@@ -38,7 +38,7 @@ module ActionDispatch
#
# See docs on the FlashHash class for more details about the flash.
class Flash
- KEY = "action_dispatch.request.flash_hash".freeze
+ KEY = "action_dispatch.request.flash_hash"
module RequestMethods
# Access the contents of the flash. Use <tt>flash["notice"]</tt> to
diff --git a/actionpack/lib/action_dispatch/middleware/request_id.rb b/actionpack/lib/action_dispatch/middleware/request_id.rb
index da2871b551..fcc0c72240 100644
--- a/actionpack/lib/action_dispatch/middleware/request_id.rb
+++ b/actionpack/lib/action_dispatch/middleware/request_id.rb
@@ -15,7 +15,7 @@ module ActionDispatch
# The unique request id can be used to trace a request end-to-end and would typically end up being part of log files
# from multiple pieces of the stack.
class RequestId
- X_REQUEST_ID = "X-Request-Id".freeze #:nodoc:
+ X_REQUEST_ID = "X-Request-Id" #:nodoc:
def initialize(app)
@app = app
@@ -30,7 +30,7 @@ module ActionDispatch
private
def make_request_id(request_id)
if request_id.presence
- request_id.gsub(/[^\w\-@]/, "".freeze).first(255)
+ request_id.gsub(/[^\w\-@]/, "").first(255)
else
internal_request_id
end
diff --git a/actionpack/lib/action_dispatch/middleware/ssl.rb b/actionpack/lib/action_dispatch/middleware/ssl.rb
index 190e54223e..00902ede21 100644
--- a/actionpack/lib/action_dispatch/middleware/ssl.rb
+++ b/actionpack/lib/action_dispatch/middleware/ssl.rb
@@ -83,7 +83,7 @@ module ActionDispatch
private
def set_hsts_header!(headers)
- headers["Strict-Transport-Security".freeze] ||= @hsts_header
+ headers["Strict-Transport-Security"] ||= @hsts_header
end
def normalize_hsts_options(options)
@@ -102,23 +102,23 @@ module ActionDispatch
# https://tools.ietf.org/html/rfc6797#section-6.1
def build_hsts_header(hsts)
- value = "max-age=#{hsts[:expires].to_i}".dup
+ value = +"max-age=#{hsts[:expires].to_i}"
value << "; includeSubDomains" if hsts[:subdomains]
value << "; preload" if hsts[:preload]
value
end
def flag_cookies_as_secure!(headers)
- if cookies = headers["Set-Cookie".freeze]
- cookies = cookies.split("\n".freeze)
+ if cookies = headers["Set-Cookie"]
+ cookies = cookies.split("\n")
- headers["Set-Cookie".freeze] = cookies.map { |cookie|
+ headers["Set-Cookie"] = cookies.map { |cookie|
if !/;\s*secure\s*(;|$)/i.match?(cookie)
"#{cookie}; secure"
else
cookie
end
- }.join("\n".freeze)
+ }.join("\n")
end
end
@@ -141,7 +141,7 @@ module ActionDispatch
host = @redirect[:host] || request.host
port = @redirect[:port] || request.port
- location = "https://#{host}".dup
+ location = +"https://#{host}"
location << ":#{port}" if port != 80 && port != 443
location << request.fullpath
location
diff --git a/actionpack/lib/action_dispatch/middleware/static.rb b/actionpack/lib/action_dispatch/middleware/static.rb
index 277074f216..1f2f7757a3 100644
--- a/actionpack/lib/action_dispatch/middleware/static.rb
+++ b/actionpack/lib/action_dispatch/middleware/static.rb
@@ -79,7 +79,7 @@ module ActionDispatch
end
def content_type(path)
- ::Rack::Mime.mime_type(::File.extname(path), "text/plain".freeze)
+ ::Rack::Mime.mime_type(::File.extname(path), "text/plain")
end
def gzip_encoding_accepted?(request)
@@ -116,7 +116,7 @@ module ActionDispatch
req = Rack::Request.new env
if req.get? || req.head?
- path = req.path_info.chomp("/".freeze)
+ path = req.path_info.chomp("/")
if match = @file_handler.match?(path)
req.path_info = match
return @file_handler.serve(req)
diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb
index 07e3be4db8..99f3b4c2cd 100644
--- a/actionpack/lib/action_dispatch/routing/mapper.rb
+++ b/actionpack/lib/action_dispatch/routing/mapper.rb
@@ -50,7 +50,19 @@ module ActionDispatch
private
def constraint_args(constraint, request)
- constraint.arity == 1 ? [request] : [request.path_parameters, request]
+ arity = if constraint.respond_to?(:arity)
+ constraint.arity
+ else
+ constraint.method(:call).arity
+ end
+
+ if arity < 1
+ []
+ elsif arity == 1
+ [request]
+ else
+ [request.path_parameters, request]
+ end
end
end
@@ -308,7 +320,7 @@ module ActionDispatch
def check_controller_and_action(path_params, controller, action)
hash = check_part(:controller, controller, path_params, {}) do |part|
translate_controller(part) {
- message = "'#{part}' is not a supported controller name. This can lead to potential routing problems.".dup
+ message = +"'#{part}' is not a supported controller name. This can lead to potential routing problems."
message << " See https://guides.rubyonrails.org/routing.html#specifying-a-controller-to-use"
raise ArgumentError, message
@@ -390,7 +402,7 @@ module ActionDispatch
# for root cases, where the latter is the correct one.
def self.normalize_path(path)
path = Journey::Router::Utils.normalize_path(path)
- path.gsub!(%r{/(\(+)/?}, '\1/') unless path =~ %r{^/\(+[^)]+\)$}
+ path.gsub!(%r{/(\(+)/?}, '\1/') unless path =~ %r{^/(\(+[^)]+\)){1,}$}
path
end
@@ -668,7 +680,7 @@ module ActionDispatch
script_namer = ->(options) do
prefix_options = options.slice(*_route.segment_keys)
- prefix_options[:relative_url_root] = "".freeze
+ prefix_options[:relative_url_root] = ""
if options[:_recall]
prefix_options.reverse_merge!(options[:_recall].slice(*_route.segment_keys))
diff --git a/actionpack/lib/action_dispatch/routing/polymorphic_routes.rb b/actionpack/lib/action_dispatch/routing/polymorphic_routes.rb
index e17ccaf986..4de5f9e2f7 100644
--- a/actionpack/lib/action_dispatch/routing/polymorphic_routes.rb
+++ b/actionpack/lib/action_dispatch/routing/polymorphic_routes.rb
@@ -181,8 +181,8 @@ module ActionDispatch
CACHE[type].fetch(action) { build action, type }
end
- def self.url; CACHE["url".freeze][nil]; end
- def self.path; CACHE["path".freeze][nil]; end
+ def self.url; CACHE["url"][nil]; end
+ def self.path; CACHE["path"][nil]; end
def self.build(action, type)
prefix = action ? "#{action}_" : ""
diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb
index 07d3a41173..fedf622e90 100644
--- a/actionpack/lib/action_dispatch/routing/route_set.rb
+++ b/actionpack/lib/action_dispatch/routing/route_set.rb
@@ -245,7 +245,7 @@ module ActionDispatch
missing_keys << missing_key
}
constraints = Hash[@route.requirements.merge(params).sort_by { |k, v| k.to_s }]
- message = "No route matches #{constraints.inspect}".dup
+ message = +"No route matches #{constraints.inspect}"
message << ", missing required keys: #{missing_keys.sort.inspect}"
raise ActionController::UrlGenerationError, message
@@ -377,7 +377,9 @@ module ActionDispatch
@prepend = []
@disable_clear_and_finalize = false
@finalized = false
- @env_key = "ROUTES_#{object_id}_SCRIPT_NAME".freeze
+ @env_key = "ROUTES_#{object_id}_SCRIPT_NAME"
+ @url_helpers = nil
+ @deferred_classes = []
@set = Journey::Routes.new
@router = Journey::Router.new @set
@@ -433,6 +435,25 @@ module ActionDispatch
end
private :eval_block
+ def include_helpers(klass, include_path_helpers)
+ if @finalized
+ include_helpers_now klass, include_path_helpers
+ else
+ @deferred_classes << [klass, include_path_helpers]
+ end
+ end
+
+ def include_helpers_now(klass, include_path_helpers)
+ namespace = klass.module_parents.detect { |m| m.respond_to?(:railtie_include_helpers) }
+
+ if namespace && namespace.railtie_namespace.routes != self
+ namespace.railtie_include_helpers(klass, include_path_helpers)
+ else
+ klass.include(url_helpers(include_path_helpers))
+ end
+ end
+ private :include_helpers_now
+
def finalize!
return if @finalized
@append.each { |blk| eval_block(blk) }
@@ -729,7 +750,7 @@ module ActionDispatch
# Remove leading slashes from controllers
def normalize_controller!
if controller
- if controller.start_with?("/".freeze)
+ if controller.start_with?("/")
@options[:controller] = controller[1..-1]
else
@options[:controller] = controller
@@ -820,10 +841,6 @@ module ActionDispatch
path, params = generate(route_name, path_options, recall)
- if options.key? :params
- params.merge! options[:params]
- end
-
options[:path] = path
options[:script_name] = script_name
options[:params] = params
diff --git a/actionpack/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb b/actionpack/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb
index d2685e0452..884fb51d18 100644
--- a/actionpack/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb
+++ b/actionpack/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb
@@ -65,7 +65,7 @@ module ActionDispatch
end
def display_image
- message = "[Screenshot]: #{image_path}\n".dup
+ message = +"[Screenshot]: #{image_path}\n"
case output_type
when "artifact"
diff --git a/actionpack/lib/action_dispatch/testing/assertions/response.rb b/actionpack/lib/action_dispatch/testing/assertions/response.rb
index 98b1965d22..8595ea03cf 100644
--- a/actionpack/lib/action_dispatch/testing/assertions/response.rb
+++ b/actionpack/lib/action_dispatch/testing/assertions/response.rb
@@ -79,9 +79,8 @@ module ActionDispatch
end
def generate_response_message(expected, actual = @response.response_code)
- "Expected response to be a <#{code_with_name(expected)}>,"\
- " but was a <#{code_with_name(actual)}>"
- .dup.concat(location_if_redirected).concat(response_body_if_short)
+ (+"Expected response to be a <#{code_with_name(expected)}>,"\
+ " but was a <#{code_with_name(actual)}>").concat(location_if_redirected).concat(response_body_if_short)
end
def response_body_if_short
diff --git a/actionpack/lib/action_dispatch/testing/assertions/routing.rb b/actionpack/lib/action_dispatch/testing/assertions/routing.rb
index 77cb311630..af41521c5c 100644
--- a/actionpack/lib/action_dispatch/testing/assertions/routing.rb
+++ b/actionpack/lib/action_dispatch/testing/assertions/routing.rb
@@ -9,6 +9,11 @@ module ActionDispatch
module Assertions
# Suite of assertions to test routes generated by \Rails and the handling of requests made to them.
module RoutingAssertions
+ def setup # :nodoc:
+ @routes ||= nil
+ super
+ end
+
# Asserts that the routing of the given +path+ was handled correctly and that the parsed options (given in the +expected_options+ hash)
# match +path+. Basically, it asserts that \Rails recognizes the route given by +expected_options+.
#
diff --git a/actionpack/test/abstract_unit.rb b/actionpack/test/abstract_unit.rb
index f4787ed27a..65dd28b3d7 100644
--- a/actionpack/test/abstract_unit.rb
+++ b/actionpack/test/abstract_unit.rb
@@ -232,6 +232,7 @@ module ActionController
routes = ActionDispatch::Routing::RouteSet.new
routes.draw(&block)
include routes.url_helpers
+ routes
end
end
@@ -430,14 +431,16 @@ end
class ActiveSupport::TestCase
include ActiveSupport::Testing::MethodCallAssertions
- # Skips the current run on Rubinius using Minitest::Assertions#skip
- private def rubinius_skip(message = "")
- skip message if RUBY_ENGINE == "rbx"
- end
- # Skips the current run on JRuby using Minitest::Assertions#skip
- private def jruby_skip(message = "")
- skip message if defined?(JRUBY_VERSION)
- end
+ private
+ # Skips the current run on Rubinius using Minitest::Assertions#skip
+ def rubinius_skip(message = "")
+ skip message if RUBY_ENGINE == "rbx"
+ end
+
+ # Skips the current run on JRuby using Minitest::Assertions#skip
+ def jruby_skip(message = "")
+ skip message if defined?(JRUBY_VERSION)
+ end
end
class DrivenByRackTest < ActionDispatch::SystemTestCase
diff --git a/actionpack/test/controller/base_test.rb b/actionpack/test/controller/base_test.rb
index a672ede1a9..558e710df9 100644
--- a/actionpack/test/controller/base_test.rb
+++ b/actionpack/test/controller/base_test.rb
@@ -138,7 +138,7 @@ class ControllerInstanceTests < ActiveSupport::TestCase
response_headers = SimpleController.action("hello").call(
"REQUEST_METHOD" => "GET",
- "rack.input" => -> {}
+ "rack.input" => -> { }
)[1]
assert response_headers.key?("X-Frame-Options")
@@ -193,7 +193,7 @@ class UrlOptionsTest < ActionController::TestCase
action: "home",
controller: "pages",
only_path: true,
- params: { "token" => "secret" }
+ token: "secret"
}
assert_equal "/home?token=secret", rs.url_for(options)
diff --git a/actionpack/test/controller/http_token_authentication_test.rb b/actionpack/test/controller/http_token_authentication_test.rb
index 672aa1351c..103123f98c 100644
--- a/actionpack/test/controller/http_token_authentication_test.rb
+++ b/actionpack/test/controller/http_token_authentication_test.rb
@@ -150,7 +150,7 @@ class HttpTokenAuthenticationTest < ActionController::TestCase
end
test "token_and_options returns empty string with empty token" do
- token = "".dup
+ token = +""
actual = ActionController::HttpAuthentication::Token.token_and_options(sample_request(token)).first
expected = token
assert_equal(expected, actual)
diff --git a/actionpack/test/controller/live_stream_test.rb b/actionpack/test/controller/live_stream_test.rb
index 431fe90b23..d81c43b87d 100644
--- a/actionpack/test/controller/live_stream_test.rb
+++ b/actionpack/test/controller/live_stream_test.rb
@@ -304,7 +304,7 @@ module ActionController
# Simulate InterlockHook
ActiveSupport::Dependencies.interlock.start_running
res = get :write_sleep_autoload
- res.each {}
+ res.each { }
ActiveSupport::Dependencies.interlock.done_running
end
diff --git a/actionpack/test/controller/metal_test.rb b/actionpack/test/controller/metal_test.rb
index 248ef36b7c..7b53092266 100644
--- a/actionpack/test/controller/metal_test.rb
+++ b/actionpack/test/controller/metal_test.rb
@@ -20,7 +20,7 @@ class MetalControllerInstanceTests < ActiveSupport::TestCase
response_headers = SimpleController.action("hello").call(
"REQUEST_METHOD" => "GET",
- "rack.input" => -> {}
+ "rack.input" => -> { }
)[1]
assert_not response_headers.key?("X-Frame-Options")
diff --git a/actionpack/test/controller/mime/respond_to_test.rb b/actionpack/test/controller/mime/respond_to_test.rb
index 1163775d3c..00e1d5f3b3 100644
--- a/actionpack/test/controller/mime/respond_to_test.rb
+++ b/actionpack/test/controller/mime/respond_to_test.rb
@@ -78,7 +78,7 @@ class RespondToController < ActionController::Base
def missing_templates
respond_to do |type|
# This test requires a block that is empty
- type.json {}
+ type.json { }
type.xml
end
end
diff --git a/actionpack/test/controller/new_base/bare_metal_test.rb b/actionpack/test/controller/new_base/bare_metal_test.rb
index b049022a06..7572d514fb 100644
--- a/actionpack/test/controller/new_base/bare_metal_test.rb
+++ b/actionpack/test/controller/new_base/bare_metal_test.rb
@@ -13,7 +13,7 @@ module BareMetalTest
test "response body is a Rack-compatible response" do
status, headers, body = BareController.action(:index).call(Rack::MockRequest.env_for("/"))
assert_equal 200, status
- string = "".dup
+ string = +""
body.each do |part|
assert part.is_a?(String), "Each part of the body must be a String"
diff --git a/actionpack/test/controller/new_base/render_context_test.rb b/actionpack/test/controller/new_base/render_context_test.rb
index 07fbadae9f..5e570a1d79 100644
--- a/actionpack/test/controller/new_base/render_context_test.rb
+++ b/actionpack/test/controller/new_base/render_context_test.rb
@@ -32,10 +32,11 @@ module RenderContext
"controller context!"
end
- # 3) Set view_context to self
- private def view_context
- self
- end
+ private
+ # 3) Set view_context to self
+ def view_context
+ self
+ end
end
class RenderContextTest < Rack::TestCase
diff --git a/actionpack/test/controller/parameters/accessors_test.rb b/actionpack/test/controller/parameters/accessors_test.rb
index 68c7f2d9ea..9f1fb3d042 100644
--- a/actionpack/test/controller/parameters/accessors_test.rb
+++ b/actionpack/test/controller/parameters/accessors_test.rb
@@ -75,6 +75,24 @@ class ParametersAccessorsTest < ActiveSupport::TestCase
end
end
+ test "each_value carries permitted status" do
+ @params.permit!
+ @params["person"].each_value { |value| assert(value.permitted?) if value == 32 }
+ end
+
+ test "each_value carries unpermitted status" do
+ @params["person"].each_value { |value| assert_not(value.permitted?) if value == 32 }
+ end
+
+ test "each_key converts to hash for permitted" do
+ @params.permit!
+ @params.each_key { |key| assert_kind_of(String, key) if key == "person" }
+ end
+
+ test "each_key converts to hash for unpermitted" do
+ @params.each_key { |key| assert_kind_of(String, key) if key == "person" }
+ end
+
test "empty? returns true when params contains no key/value pairs" do
params = ActionController::Parameters.new
assert_empty params
diff --git a/actionpack/test/controller/redirect_test.rb b/actionpack/test/controller/redirect_test.rb
index 461e627154..998498e1b2 100644
--- a/actionpack/test/controller/redirect_test.rb
+++ b/actionpack/test/controller/redirect_test.rb
@@ -214,6 +214,13 @@ class RedirectTest < ActionController::TestCase
assert_equal "http://test.host/things/stuff", redirect_to_url
end
+ def test_relative_url_redirect_host_with_port
+ request.host = "test.host:1234"
+ get :relative_url_redirect_with_status
+ assert_response 302
+ assert_equal "http://test.host:1234/things/stuff", redirect_to_url
+ end
+
def test_simple_redirect_using_options
get :host_redirect
assert_response :redirect
diff --git a/actionpack/test/controller/routing_test.rb b/actionpack/test/controller/routing_test.rb
index a7033b2d30..30f2a23b33 100644
--- a/actionpack/test/controller/routing_test.rb
+++ b/actionpack/test/controller/routing_test.rb
@@ -25,8 +25,8 @@ class UriReservedCharactersRoutingTest < ActiveSupport::TestCase
safe, unsafe = %w(: @ & = + $ , ;), %w(^ ? # [ ])
hex = unsafe.map { |char| "%" + char.unpack1("H2").upcase }
- @segment = "#{safe.join}#{unsafe.join}".freeze
- @escaped = "#{safe.join}#{hex.join}".freeze
+ @segment = "#{safe.join}#{unsafe.join}"
+ @escaped = "#{safe.join}#{hex.join}"
end
def test_route_generation_escapes_unsafe_path_characters
@@ -309,7 +309,7 @@ class LegacyRouteSetTests < ActiveSupport::TestCase
def test_specific_controller_action_failure
rs.draw do
- mount lambda {} => "/foo"
+ mount lambda { } => "/foo"
end
assert_raises(ActionController::UrlGenerationError) do
@@ -674,7 +674,7 @@ class LegacyRouteSetTests < ActiveSupport::TestCase
assert_equal "/page/foo", url_for(rs, controller: "content", action: "show_page", id: "foo")
assert_equal({ controller: "content", action: "show_page", id: "foo" }, rs.recognize_path("/page/foo"))
- token = "\321\202\320\265\320\272\321\201\321\202".dup # 'text' in Russian
+ token = +"\321\202\320\265\320\272\321\201\321\202" # 'text' in Russian
token.force_encoding(Encoding::BINARY)
escaped_token = CGI.escape(token)
diff --git a/actionpack/test/controller/send_file_test.rb b/actionpack/test/controller/send_file_test.rb
index 7b1a52b277..c917cdf761 100644
--- a/actionpack/test/controller/send_file_test.rb
+++ b/actionpack/test/controller/send_file_test.rb
@@ -144,7 +144,7 @@ class SendFileTest < ActionController::TestCase
get :test_send_file_headers_bang
assert_equal "image/png", response.content_type
- assert_equal 'disposition; filename="filename"', response.get_header("Content-Disposition")
+ assert_equal %(disposition; filename="filename"; filename*=UTF-8''filename), response.get_header("Content-Disposition")
assert_equal "binary", response.get_header("Content-Transfer-Encoding")
assert_equal "private", response.get_header("Cache-Control")
end
@@ -153,7 +153,7 @@ class SendFileTest < ActionController::TestCase
def test_send_file_headers_with_disposition_as_a_symbol
get :test_send_file_headers_with_disposition_as_a_symbol
- assert_equal 'disposition; filename="filename"', response.get_header("Content-Disposition")
+ assert_equal %(disposition; filename="filename"; filename*=UTF-8''filename), response.get_header("Content-Disposition")
end
def test_send_file_headers_with_mime_lookup_with_symbol
diff --git a/actionpack/test/controller/test_case_test.rb b/actionpack/test/controller/test_case_test.rb
index dda2686a9b..6fc70d6248 100644
--- a/actionpack/test/controller/test_case_test.rb
+++ b/actionpack/test/controller/test_case_test.rb
@@ -542,7 +542,7 @@ XML
def test_params_passing_with_frozen_values
assert_nothing_raised do
get :test_params, params: {
- frozen: "icy".freeze, frozens: ["icy".freeze].freeze, deepfreeze: { frozen: "icy".freeze }.freeze
+ frozen: -"icy", frozens: [-"icy"].freeze, deepfreeze: { frozen: -"icy" }.freeze
}
end
parsed_params = ::JSON.parse(@response.body)
diff --git a/actionpack/test/dispatch/content_disposition_test.rb b/actionpack/test/dispatch/content_disposition_test.rb
new file mode 100644
index 0000000000..3f5959da6e
--- /dev/null
+++ b/actionpack/test/dispatch/content_disposition_test.rb
@@ -0,0 +1,37 @@
+# frozen_string_literal: true
+
+require "abstract_unit"
+
+module ActionDispatch
+ class ContentDispositionTest < ActiveSupport::TestCase
+ test "encoding a Latin filename" do
+ disposition = Http::ContentDisposition.new(disposition: :inline, filename: "racecar.jpg")
+
+ assert_equal %(filename="racecar.jpg"), disposition.ascii_filename
+ assert_equal "filename*=UTF-8''racecar.jpg", disposition.utf8_filename
+ assert_equal "inline; #{disposition.ascii_filename}; #{disposition.utf8_filename}", disposition.to_s
+ end
+
+ test "encoding a Latin filename with accented characters" do
+ disposition = Http::ContentDisposition.new(disposition: :inline, filename: "råcëçâr.jpg")
+
+ assert_equal %(filename="racecar.jpg"), disposition.ascii_filename
+ assert_equal "filename*=UTF-8''r%C3%A5c%C3%AB%C3%A7%C3%A2r.jpg", disposition.utf8_filename
+ assert_equal "inline; #{disposition.ascii_filename}; #{disposition.utf8_filename}", disposition.to_s
+ end
+
+ test "encoding a non-Latin filename" do
+ disposition = Http::ContentDisposition.new(disposition: :inline, filename: "автомобиль.jpg")
+
+ assert_equal %(filename="%3F%3F%3F%3F%3F%3F%3F%3F%3F%3F.jpg"), disposition.ascii_filename
+ assert_equal "filename*=UTF-8''%D0%B0%D0%B2%D1%82%D0%BE%D0%BC%D0%BE%D0%B1%D0%B8%D0%BB%D1%8C.jpg", disposition.utf8_filename
+ assert_equal "inline; #{disposition.ascii_filename}; #{disposition.utf8_filename}", disposition.to_s
+ end
+
+ test "without filename" do
+ disposition = Http::ContentDisposition.new(disposition: :inline, filename: nil)
+
+ assert_equal "inline", disposition.to_s
+ end
+ end
+end
diff --git a/actionpack/test/dispatch/content_security_policy_test.rb b/actionpack/test/dispatch/content_security_policy_test.rb
index 4f9a4ff2bd..13ad22b5c5 100644
--- a/actionpack/test/dispatch/content_security_policy_test.rb
+++ b/actionpack/test/dispatch/content_security_policy_test.rb
@@ -339,6 +339,11 @@ class ContentSecurityPolicyIntegrationTest < ActionDispatch::IntegrationTest
p.script_src :self
end
+ content_security_policy only: :style_src do |p|
+ p.default_src false
+ p.style_src :self
+ end
+
content_security_policy(false, only: :no_policy)
content_security_policy_report_only only: :report_only
@@ -363,6 +368,10 @@ class ContentSecurityPolicyIntegrationTest < ActionDispatch::IntegrationTest
head :ok
end
+ def style_src
+ head :ok
+ end
+
def no_policy
head :ok
end
@@ -381,6 +390,7 @@ class ContentSecurityPolicyIntegrationTest < ActionDispatch::IntegrationTest
get "/conditional", to: "policy#conditional"
get "/report-only", to: "policy#report_only"
get "/script-src", to: "policy#script_src"
+ get "/style-src", to: "policy#style_src"
get "/no-policy", to: "policy#no_policy"
end
end
@@ -441,6 +451,11 @@ class ContentSecurityPolicyIntegrationTest < ActionDispatch::IntegrationTest
assert_policy "script-src 'self' 'nonce-iyhD0Yc0W+c='"
end
+ def test_adds_nonce_to_style_src_content_security_policy
+ get "/style-src"
+ assert_policy "style-src 'self' 'nonce-iyhD0Yc0W+c='"
+ end
+
def test_generates_no_content_security_policy
get "/no-policy"
diff --git a/actionpack/test/dispatch/debug_exceptions_test.rb b/actionpack/test/dispatch/debug_exceptions_test.rb
index 44b79c0e5d..37399cfd07 100644
--- a/actionpack/test/dispatch/debug_exceptions_test.rb
+++ b/actionpack/test/dispatch/debug_exceptions_test.rb
@@ -368,7 +368,7 @@ class DebugExceptionsTest < ActionDispatch::IntegrationTest
})
assert_response 500
- assert_includes(body, CGI.escapeHTML(PP.pp(params, "".dup, 200)))
+ assert_includes(body, CGI.escapeHTML(PP.pp(params, +"", 200)))
end
test "sets the HTTP charset parameter" do
diff --git a/actionpack/test/dispatch/middleware_stack_test.rb b/actionpack/test/dispatch/middleware_stack_test.rb
index e9f7ad41dd..5f43e5a3c5 100644
--- a/actionpack/test/dispatch/middleware_stack_test.rb
+++ b/actionpack/test/dispatch/middleware_stack_test.rb
@@ -42,7 +42,7 @@ class MiddlewareStackTest < ActiveSupport::TestCase
end
test "use should push middleware class with block arguments onto the stack" do
- proc = Proc.new {}
+ proc = Proc.new { }
assert_difference "@stack.size" do
@stack.use(BlockMiddleware, &proc)
end
diff --git a/actionpack/test/dispatch/prefix_generation_test.rb b/actionpack/test/dispatch/prefix_generation_test.rb
index 85ea04356a..7a7a201b11 100644
--- a/actionpack/test/dispatch/prefix_generation_test.rb
+++ b/actionpack/test/dispatch/prefix_generation_test.rb
@@ -13,7 +13,7 @@ module TestGenerationPrefix
end
def self.model_name
- klass = "Post".dup
+ klass = +"Post"
def klass.name; self end
ActiveModel::Name.new(klass)
diff --git a/actionpack/test/dispatch/request_test.rb b/actionpack/test/dispatch/request_test.rb
index 0ac8713527..c7b68e5266 100644
--- a/actionpack/test/dispatch/request_test.rb
+++ b/actionpack/test/dispatch/request_test.rb
@@ -24,7 +24,7 @@ class BaseRequestTest < ActiveSupport::TestCase
def stub_request(env = {})
ip_spoofing_check = env.key?(:ip_spoofing_check) ? env.delete(:ip_spoofing_check) : true
@trusted_proxies ||= nil
- ip_app = ActionDispatch::RemoteIp.new(Proc.new {}, ip_spoofing_check, @trusted_proxies)
+ ip_app = ActionDispatch::RemoteIp.new(Proc.new { }, ip_spoofing_check, @trusted_proxies)
ActionDispatch::Http::URL.tld_length = env.delete(:tld_length) if env.key?(:tld_length)
ip_app.call(env)
diff --git a/actionpack/test/dispatch/routing/inspector_test.rb b/actionpack/test/dispatch/routing/inspector_test.rb
index f1f6547889..fe1f1995d8 100644
--- a/actionpack/test/dispatch/routing/inspector_test.rb
+++ b/actionpack/test/dispatch/routing/inspector_test.rb
@@ -373,7 +373,7 @@ module ActionDispatch
end
def test_not_routes_when_expanded
- output = draw(grep: "rails/dummy", formatter: ActionDispatch::Routing::ConsoleFormatter::Expanded.new) {}
+ output = draw(grep: "rails/dummy", formatter: ActionDispatch::Routing::ConsoleFormatter::Expanded.new) { }
assert_equal [
"You don't have any routes defined!",
@@ -450,7 +450,7 @@ module ActionDispatch
end
def test_no_routes_were_defined
- output = draw(grep: "Rails::DummyController") {}
+ output = draw(grep: "Rails::DummyController") { }
assert_equal [
"You don't have any routes defined!",
diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb
index 5efbe5b553..affc2d8497 100644
--- a/actionpack/test/dispatch/routing_test.rb
+++ b/actionpack/test/dispatch/routing_test.rb
@@ -115,6 +115,21 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
assert_equal 301, status
end
+ def test_accepts_a_constraint_object_responding_to_call
+ constraint = Class.new do
+ def call(*); true; end
+ def matches?(*); false; end
+ end
+
+ draw do
+ get "/", to: "home#show", constraints: constraint.new
+ end
+
+ assert_nothing_raised do
+ get "/"
+ end
+ end
+
def test_namespace_with_controller_segment
assert_raise(ArgumentError) do
draw do
@@ -1367,6 +1382,22 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
assert_equal "projects#index", @response.body
end
+ def test_optionally_scoped_root_unscoped_access
+ draw do
+ scope "(:locale)" do
+ scope "(:platform)" do
+ scope "(:browser)" do
+ root to: "projects#index"
+ end
+ end
+ end
+ end
+
+ assert_equal "/", root_path
+ get "/"
+ assert_equal "projects#index", @response.body
+ end
+
def test_scope_with_format_option
draw do
get "direct/index", as: :no_format_direct, format: false
diff --git a/actionpack/test/dispatch/static_test.rb b/actionpack/test/dispatch/static_test.rb
index 6b69cd9999..d44aa00122 100644
--- a/actionpack/test/dispatch/static_test.rb
+++ b/actionpack/test/dispatch/static_test.rb
@@ -31,7 +31,7 @@ module StaticTests
end
def test_handles_urls_with_ascii_8bit
- assert_equal "Hello, World!", get("/doorkeeper%E3E4".dup.force_encoding("ASCII-8BIT")).body
+ assert_equal "Hello, World!", get((+"/doorkeeper%E3E4").force_encoding("ASCII-8BIT")).body
end
def test_handles_urls_with_ascii_8bit_on_win_31j
@@ -39,7 +39,7 @@ module StaticTests
Encoding.default_internal = "Windows-31J"
Encoding.default_external = "Windows-31J"
end
- assert_equal "Hello, World!", get("/doorkeeper%E3E4".dup.force_encoding("ASCII-8BIT")).body
+ assert_equal "Hello, World!", get((+"/doorkeeper%E3E4").force_encoding("ASCII-8BIT")).body
end
def test_handles_urls_with_null_byte
diff --git a/actionpack/test/fixtures/alternate_helpers/foo_helper.rb b/actionpack/test/fixtures/alternate_helpers/foo_helper.rb
index 3aadb6145e..c1a995af5f 100644
--- a/actionpack/test/fixtures/alternate_helpers/foo_helper.rb
+++ b/actionpack/test/fixtures/alternate_helpers/foo_helper.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
module FooHelper
- redefine_method(:baz) {}
+ redefine_method(:baz) { }
end
diff --git a/actionpack/test/journey/router/utils_test.rb b/actionpack/test/journey/router/utils_test.rb
index 2d09098f11..472f1bf35e 100644
--- a/actionpack/test/journey/router/utils_test.rb
+++ b/actionpack/test/journey/router/utils_test.rb
@@ -23,7 +23,7 @@ module ActionDispatch
end
def test_uri_unescape_with_utf8_string
- assert_equal "Šašinková", Utils.unescape_uri("%C5%A0a%C5%A1inkov%C3%A1".dup.force_encoding(Encoding::US_ASCII))
+ assert_equal "Šašinková", Utils.unescape_uri((+"%C5%A0a%C5%A1inkov%C3%A1").force_encoding(Encoding::US_ASCII))
end
def test_normalize_path_not_greedy
diff --git a/actionview/app/assets/javascripts/rails-ujs/features/disable.coffee b/actionview/app/assets/javascripts/rails-ujs/features/disable.coffee
index 90aa3bdf0e..a8c692ee62 100644
--- a/actionview/app/assets/javascripts/rails-ujs/features/disable.coffee
+++ b/actionview/app/assets/javascripts/rails-ujs/features/disable.coffee
@@ -8,7 +8,12 @@ Rails.handleDisabledElement = (e) ->
# Unified function to enable an element (link, button and form)
Rails.enableElement = (e) ->
- element = if e instanceof Event then e.target else e
+ if e instanceof Event
+ return if isXhrRedirect(e)
+ element = e.target
+ else
+ element = e
+
if matches(element, Rails.linkDisableSelector)
enableLinkElement(element)
else if matches(element, Rails.buttonDisableSelector) or matches(element, Rails.formEnableSelector)
@@ -80,3 +85,7 @@ enableFormElement = (element) ->
setData(element, 'ujs:enable-with', null) # clean up cache
element.disabled = false
setData(element, 'ujs:disabled', null)
+
+isXhrRedirect = (event) ->
+ xhr = event.detail?[0]
+ xhr?.getResponseHeader("X-Xhr-Redirect")?
diff --git a/actionview/lib/action_view/helpers/asset_tag_helper.rb b/actionview/lib/action_view/helpers/asset_tag_helper.rb
index cbcce4a4dc..3d7c8dae75 100644
--- a/actionview/lib/action_view/helpers/asset_tag_helper.rb
+++ b/actionview/lib/action_view/helpers/asset_tag_helper.rb
@@ -98,7 +98,7 @@ module ActionView
if tag_options["nonce"] == true
tag_options["nonce"] = content_security_policy_nonce
end
- content_tag("script".freeze, "", tag_options)
+ content_tag("script", "", tag_options)
}.join("\n").html_safe
request.send_early_hints("Link" => early_hints_links.join("\n")) if respond_to?(:request) && request
@@ -375,7 +375,7 @@ module ActionView
def image_alt(src)
ActiveSupport::Deprecation.warn("image_alt is deprecated and will be removed from Rails 6.0. You must explicitly set alt text on images.")
- File.basename(src, ".*".freeze).sub(/-[[:xdigit:]]{32,64}\z/, "".freeze).tr("-_".freeze, " ".freeze).capitalize
+ File.basename(src, ".*").sub(/-[[:xdigit:]]{32,64}\z/, "").tr("-_", " ").capitalize
end
# Returns an HTML video tag for the +sources+. If +sources+ is a string,
diff --git a/actionview/lib/action_view/helpers/asset_url_helper.rb b/actionview/lib/action_view/helpers/asset_url_helper.rb
index 1808765666..cc62783d60 100644
--- a/actionview/lib/action_view/helpers/asset_url_helper.rb
+++ b/actionview/lib/action_view/helpers/asset_url_helper.rb
@@ -188,7 +188,7 @@ module ActionView
return "" if source.blank?
return source if URI_REGEXP.match?(source)
- tail, source = source[/([\?#].+)$/], source.sub(/([\?#].+)$/, "".freeze)
+ tail, source = source[/([\?#].+)$/], source.sub(/([\?#].+)$/, "")
if extname = compute_asset_extname(source, options)
source = "#{source}#{extname}"
diff --git a/actionview/lib/action_view/helpers/date_helper.rb b/actionview/lib/action_view/helpers/date_helper.rb
index 4de4fafde0..9d5e5eaba3 100644
--- a/actionview/lib/action_view/helpers/date_helper.rb
+++ b/actionview/lib/action_view/helpers/date_helper.rb
@@ -684,7 +684,7 @@ module ActionView
format = options.delete(:format) || :long
content = args.first || I18n.l(date_or_time, format: format)
- content_tag("time".freeze, content, options.reverse_merge(datetime: date_or_time.iso8601), &block)
+ content_tag("time", content, options.reverse_merge(datetime: date_or_time.iso8601), &block)
end
private
@@ -703,7 +703,7 @@ module ActionView
class DateTimeSelector #:nodoc:
include ActionView::Helpers::TagHelper
- DEFAULT_PREFIX = "date".freeze
+ DEFAULT_PREFIX = "date"
POSITION = {
year: 1, month: 2, day: 3, hour: 4, minute: 5, second: 6
}.freeze
@@ -824,7 +824,7 @@ module ActionView
1.upto(12) do |month_number|
options = { value: month_number }
options[:selected] = "selected" if month == month_number
- month_options << content_tag("option".freeze, month_name(month_number), options) + "\n"
+ month_options << content_tag("option", month_name(month_number), options) + "\n"
end
build_select(:month, month_options.join)
end
@@ -1006,7 +1006,7 @@ module ActionView
tag_options[:selected] = "selected" if selected == i
text = options[:use_two_digit_numbers] ? sprintf("%02d", i) : value
text = options[:ampm] ? AMPM_TRANSLATION[i] : text
- select_options << content_tag("option".freeze, text, tag_options)
+ select_options << content_tag("option", text, tag_options)
end
(select_options.join("\n") + "\n").html_safe
@@ -1034,7 +1034,7 @@ module ActionView
tag_options = { value: value }
tag_options[:selected] = "selected" if selected == value
text = year_name(value)
- select_options << content_tag("option".freeze, text, tag_options)
+ select_options << content_tag("option", text, tag_options)
end
(select_options.join("\n") + "\n").html_safe
@@ -1053,12 +1053,12 @@ module ActionView
select_options[:disabled] = "disabled" if @options[:disabled]
select_options[:class] = css_class_attribute(type, select_options[:class], @options[:with_css_classes]) if @options[:with_css_classes]
- select_html = "\n".dup
- select_html << content_tag("option".freeze, "", value: "") + "\n" if @options[:include_blank]
+ select_html = +"\n"
+ select_html << content_tag("option", "", value: "") + "\n" if @options[:include_blank]
select_html << prompt_option_tag(type, @options[:prompt]) + "\n" if @options[:prompt]
select_html << select_options_as_html
- (content_tag("select".freeze, select_html.html_safe, select_options) + "\n").html_safe
+ (content_tag("select", select_html.html_safe, select_options) + "\n").html_safe
end
# Builds the css class value for the select element
@@ -1091,7 +1091,7 @@ module ActionView
I18n.translate(:"datetime.prompts.#{type}", locale: @options[:locale])
end
- prompt ? content_tag("option".freeze, prompt, value: "") : ""
+ prompt ? content_tag("option", prompt, value: "") : ""
end
# Builds hidden input tag for date part and value.
@@ -1135,7 +1135,7 @@ module ActionView
# Given an ordering of datetime components, create the selection HTML
# and join them with their appropriate separators.
def build_selects_from_types(order)
- select = "".dup
+ select = +""
first_visible = order.find { |type| !@options[:"discard_#{type}"] }
order.reverse_each do |type|
separator = separator(type) unless type == first_visible # don't add before first visible field
diff --git a/actionview/lib/action_view/helpers/form_options_helper.rb b/actionview/lib/action_view/helpers/form_options_helper.rb
index 2b9d55a019..ebdd96f570 100644
--- a/actionview/lib/action_view/helpers/form_options_helper.rb
+++ b/actionview/lib/action_view/helpers/form_options_helper.rb
@@ -463,7 +463,7 @@ module ActionView
option_tags = options_from_collection_for_select(
value_for_collection(group, group_method), option_key_method, option_value_method, selected_key)
- content_tag("optgroup".freeze, option_tags, label: value_for_collection(group, group_label_method))
+ content_tag("optgroup", option_tags, label: value_for_collection(group, group_label_method))
end.join.html_safe
end
@@ -535,7 +535,7 @@ module ActionView
body = "".html_safe
if prompt
- body.safe_concat content_tag("option".freeze, prompt_text(prompt), value: "")
+ body.safe_concat content_tag("option", prompt_text(prompt), value: "")
end
grouped_options.each do |container|
@@ -548,7 +548,7 @@ module ActionView
end
html_attributes = { label: label }.merge!(html_attributes)
- body.safe_concat content_tag("optgroup".freeze, options_for_select(container, selected_key), html_attributes)
+ body.safe_concat content_tag("optgroup", options_for_select(container, selected_key), html_attributes)
end
body
@@ -584,7 +584,7 @@ module ActionView
end
zone_options.safe_concat options_for_select(convert_zones[priority_zones], selected)
- zone_options.safe_concat content_tag("option".freeze, "-------------", value: "", disabled: true)
+ zone_options.safe_concat content_tag("option", "-------------", value: "", disabled: true)
zone_options.safe_concat "\n"
zones = zones - priority_zones
diff --git a/actionview/lib/action_view/helpers/form_tag_helper.rb b/actionview/lib/action_view/helpers/form_tag_helper.rb
index ba09738beb..c0996049f0 100644
--- a/actionview/lib/action_view/helpers/form_tag_helper.rb
+++ b/actionview/lib/action_view/helpers/form_tag_helper.rb
@@ -146,15 +146,15 @@ module ActionView
end
if include_blank
- option_tags = content_tag("option".freeze, include_blank, options_for_blank_options_tag).safe_concat(option_tags)
+ option_tags = content_tag("option", include_blank, options_for_blank_options_tag).safe_concat(option_tags)
end
end
if prompt = options.delete(:prompt)
- option_tags = content_tag("option".freeze, prompt, value: "").safe_concat(option_tags)
+ option_tags = content_tag("option", prompt, value: "").safe_concat(option_tags)
end
- content_tag "select".freeze, option_tags, { "name" => html_name, "id" => sanitize_to_id(name) }.update(options.stringify_keys)
+ content_tag "select", option_tags, { "name" => html_name, "id" => sanitize_to_id(name) }.update(options.stringify_keys)
end
# Creates a standard text field; use these text fields to input smaller chunks of text like a username
@@ -577,7 +577,7 @@ module ActionView
# # => <fieldset class="format"><p><input id="name" name="name" type="text" /></p></fieldset>
def field_set_tag(legend = nil, options = nil, &block)
output = tag(:fieldset, options, true)
- output.safe_concat(content_tag("legend".freeze, legend)) unless legend.blank?
+ output.safe_concat(content_tag("legend", legend)) unless legend.blank?
output.concat(capture(&block)) if block_given?
output.safe_concat("</fieldset>")
end
diff --git a/actionview/lib/action_view/helpers/javascript_helper.rb b/actionview/lib/action_view/helpers/javascript_helper.rb
index 830088bea3..b680cb1bd3 100644
--- a/actionview/lib/action_view/helpers/javascript_helper.rb
+++ b/actionview/lib/action_view/helpers/javascript_helper.rb
@@ -15,8 +15,8 @@ module ActionView
"'" => "\\'"
}
- JS_ESCAPE_MAP["\342\200\250".dup.force_encoding(Encoding::UTF_8).encode!] = "&#x2028;"
- JS_ESCAPE_MAP["\342\200\251".dup.force_encoding(Encoding::UTF_8).encode!] = "&#x2029;"
+ JS_ESCAPE_MAP[(+"\342\200\250").force_encoding(Encoding::UTF_8).encode!] = "&#x2028;"
+ JS_ESCAPE_MAP[(+"\342\200\251").force_encoding(Encoding::UTF_8).encode!] = "&#x2029;"
# Escapes carriage returns and single and double quotes for JavaScript segments.
#
@@ -25,12 +25,13 @@ module ActionView
#
# $('some_element').replaceWith('<%= j render 'some/element_template' %>');
def escape_javascript(javascript)
- if javascript
- result = javascript.gsub(/(\\|<\/|\r\n|\342\200\250|\342\200\251|[\n\r"'])/u) { |match| JS_ESCAPE_MAP[match] }
- javascript.html_safe? ? result.html_safe : result
+ javascript = javascript.to_s
+ if javascript.empty?
+ result = ""
else
- ""
+ result = javascript.gsub(/(\\|<\/|\r\n|\342\200\250|\342\200\251|[\n\r"'])/u) { |match| JS_ESCAPE_MAP[match] }
end
+ javascript.html_safe? ? result.html_safe : result
end
alias_method :j, :escape_javascript
@@ -83,7 +84,7 @@ module ActionView
html_options[:nonce] = content_security_policy_nonce
end
- content_tag("script".freeze, javascript_cdata_section(content), html_options)
+ content_tag("script", javascript_cdata_section(content), html_options)
end
def javascript_cdata_section(content) #:nodoc:
diff --git a/actionview/lib/action_view/helpers/tag_helper.rb b/actionview/lib/action_view/helpers/tag_helper.rb
index d12989ea64..3979721d34 100644
--- a/actionview/lib/action_view/helpers/tag_helper.rb
+++ b/actionview/lib/action_view/helpers/tag_helper.rb
@@ -58,7 +58,7 @@ module ActionView
def tag_options(options, escape = true)
return if options.blank?
- output = "".dup
+ output = +""
sep = " "
options.each_pair do |key, value|
if TAG_PREFIXES.include?(key) && value.is_a?(Hash)
@@ -86,11 +86,11 @@ module ActionView
def tag_option(key, value, escape)
if value.is_a?(Array)
- value = escape ? safe_join(value, " ".freeze) : value.join(" ".freeze)
+ value = escape ? safe_join(value, " ") : value.join(" ")
else
value = escape ? ERB::Util.unwrapped_html_escape(value) : value.to_s.dup
end
- value.gsub!('"'.freeze, "&quot;".freeze)
+ value.gsub!('"', "&quot;")
%(#{key}="#{value}")
end
diff --git a/actionview/lib/action_view/helpers/translation_helper.rb b/actionview/lib/action_view/helpers/translation_helper.rb
index ba82dcab3e..ae1c93e12f 100644
--- a/actionview/lib/action_view/helpers/translation_helper.rb
+++ b/actionview/lib/action_view/helpers/translation_helper.rb
@@ -98,7 +98,7 @@ module ActionView
raise e if raise_error
keys = I18n.normalize_keys(e.locale, e.key, e.options[:scope])
- title = "translation missing: #{keys.join('.')}".dup
+ title = +"translation missing: #{keys.join('.')}"
interpolations = options.except(:default, :scope)
if interpolations.any?
diff --git a/actionview/lib/action_view/helpers/url_helper.rb b/actionview/lib/action_view/helpers/url_helper.rb
index 52bffaab84..948dd1551f 100644
--- a/actionview/lib/action_view/helpers/url_helper.rb
+++ b/actionview/lib/action_view/helpers/url_helper.rb
@@ -200,9 +200,9 @@ module ActionView
html_options = convert_options_to_data_attributes(options, html_options)
url = url_for(options)
- html_options["href".freeze] ||= url
+ html_options["href"] ||= url
- content_tag("a".freeze, name || url, html_options, &block)
+ content_tag("a", name || url, html_options, &block)
end
# Generates a form containing a single button that submits to the URL created
@@ -308,7 +308,7 @@ module ActionView
params = html_options.delete("params")
method = html_options.delete("method").to_s
- method_tag = BUTTON_TAG_METHOD_VERBS.include?(method) ? method_tag(method) : "".freeze.html_safe
+ method_tag = BUTTON_TAG_METHOD_VERBS.include?(method) ? method_tag(method) : "".html_safe
form_method = method == "get" ? "get" : "post"
form_options = html_options.delete("form") || {}
@@ -321,7 +321,7 @@ module ActionView
request_method = method.empty? ? "post" : method
token_tag(nil, form_options: { action: url, method: request_method })
else
- "".freeze
+ ""
end
html_options = convert_options_to_data_attributes(options, html_options)
@@ -487,12 +487,12 @@ module ActionView
option = html_options.delete(item).presence || next
"#{item.dasherize}=#{ERB::Util.url_encode(option)}"
}.compact
- extras = extras.empty? ? "".freeze : "?" + extras.join("&")
+ extras = extras.empty? ? "" : "?" + extras.join("&")
encoded_email_address = ERB::Util.url_encode(email_address).gsub("%40", "@")
html_options["href"] = "mailto:#{encoded_email_address}#{extras}"
- content_tag("a".freeze, name || email_address, html_options, &block)
+ content_tag("a", name || email_address, html_options, &block)
end
# True if the current request URI was generated by the given +options+.
@@ -575,21 +575,21 @@ module ActionView
def convert_options_to_data_attributes(options, html_options)
if html_options
html_options = html_options.stringify_keys
- html_options["data-remote"] = "true".freeze if link_to_remote_options?(options) || link_to_remote_options?(html_options)
+ html_options["data-remote"] = "true" if link_to_remote_options?(options) || link_to_remote_options?(html_options)
- method = html_options.delete("method".freeze)
+ method = html_options.delete("method")
add_method_to_attributes!(html_options, method) if method
html_options
else
- link_to_remote_options?(options) ? { "data-remote" => "true".freeze } : {}
+ link_to_remote_options?(options) ? { "data-remote" => "true" } : {}
end
end
def link_to_remote_options?(options)
if options.is_a?(Hash)
- options.delete("remote".freeze) || options.delete(:remote)
+ options.delete("remote") || options.delete(:remote)
end
end
@@ -622,7 +622,7 @@ module ActionView
token ||= form_authenticity_token(form_options: form_options)
tag(:input, type: "hidden", name: request_forgery_protection_token.to_s, value: token)
else
- "".freeze
+ ""
end
end
diff --git a/actionview/lib/action_view/log_subscriber.rb b/actionview/lib/action_view/log_subscriber.rb
index d4ac77e10f..db07b9d7fb 100644
--- a/actionview/lib/action_view/log_subscriber.rb
+++ b/actionview/lib/action_view/log_subscriber.rb
@@ -16,7 +16,7 @@ module ActionView
def render_template(event)
info do
- message = " Rendered #{from_rails_root(event.payload[:identifier])}".dup
+ message = +" Rendered #{from_rails_root(event.payload[:identifier])}"
message << " within #{from_rails_root(event.payload[:layout])}" if event.payload[:layout]
message << " (#{event.duration.round(1)}ms)"
end
@@ -24,7 +24,7 @@ module ActionView
def render_partial(event)
info do
- message = " Rendered #{from_rails_root(event.payload[:identifier])}".dup
+ message = +" Rendered #{from_rails_root(event.payload[:identifier])}"
message << " within #{from_rails_root(event.payload[:layout])}" if event.payload[:layout]
message << " (#{event.duration.round(1)}ms)"
message << " #{cache_message(event.payload)}" unless event.payload[:cache_hit].nil?
@@ -85,7 +85,7 @@ module ActionView
def log_rendering_start(payload)
info do
- message = " Rendering #{from_rails_root(payload[:identifier])}".dup
+ message = +" Rendering #{from_rails_root(payload[:identifier])}"
message << " within #{from_rails_root(payload[:layout])}" if payload[:layout]
message
end
diff --git a/actionview/lib/action_view/lookup_context.rb b/actionview/lib/action_view/lookup_context.rb
index 0e56eca35c..af67ffa12d 100644
--- a/actionview/lib/action_view/lookup_context.rb
+++ b/actionview/lib/action_view/lookup_context.rb
@@ -202,13 +202,13 @@ module ActionView
# name instead of the prefix.
def normalize_name(name, prefixes)
prefixes = prefixes.presence
- parts = name.to_s.split("/".freeze)
+ parts = name.to_s.split("/")
parts.shift if parts.first.empty?
name = parts.pop
return name, prefixes || [""] if parts.empty?
- parts = parts.join("/".freeze)
+ parts = parts.join("/")
prefixes = prefixes ? prefixes.map { |p| "#{p}/#{parts}" } : [parts]
return name, prefixes
@@ -245,7 +245,7 @@ module ActionView
# add :html as fallback to :js.
def formats=(values)
if values
- values.concat(default_formats) if values.delete "*/*".freeze
+ values.concat(default_formats) if values.delete "*/*"
if values == [:js]
values << :html
@html_fallback_for_js = true
diff --git a/actionview/lib/action_view/record_identifier.rb b/actionview/lib/action_view/record_identifier.rb
index 1310a1ce0a..ee39b6050d 100644
--- a/actionview/lib/action_view/record_identifier.rb
+++ b/actionview/lib/action_view/record_identifier.rb
@@ -59,8 +59,8 @@ module ActionView
include ModelNaming
- JOIN = "_".freeze
- NEW = "new".freeze
+ JOIN = "_"
+ NEW = "new"
# The DOM class convention is to use the singular form of an object or class.
#
diff --git a/actionview/lib/action_view/renderer/partial_renderer.rb b/actionview/lib/action_view/renderer/partial_renderer.rb
index d7f97c3b50..cb850d75ee 100644
--- a/actionview/lib/action_view/renderer/partial_renderer.rb
+++ b/actionview/lib/action_view/renderer/partial_renderer.rb
@@ -523,7 +523,7 @@ module ActionView
def retrieve_variable(path, as)
variable = as || begin
- base = path[-1] == "/".freeze ? "".freeze : File.basename(path)
+ base = path[-1] == "/" ? "" : File.basename(path)
raise_invalid_identifier(path) unless base =~ /\A_?(.*?)(?:\.\w+)*\z/
$1.to_sym
end
diff --git a/actionview/lib/action_view/renderer/partial_renderer/collection_caching.rb b/actionview/lib/action_view/renderer/partial_renderer/collection_caching.rb
index 3c10e0452f..5aa6f77902 100644
--- a/actionview/lib/action_view/renderer/partial_renderer/collection_caching.rb
+++ b/actionview/lib/action_view/renderer/partial_renderer/collection_caching.rb
@@ -14,15 +14,35 @@ module ActionView
def cache_collection_render(instrumentation_payload)
return yield unless @options[:cached]
+ # Result is a hash with the key represents the
+ # key used for cache lookup and the value is the item
+ # on which the partial is being rendered
keyed_collection = collection_by_cache_keys
+
+ # Pull all partials from cache
+ # Result is a hash, key matches the entry in
+ # `keyed_collection` where the cache was retrieved and the
+ # value is the value that was present in the cache
cached_partials = collection_cache.read_multi(*keyed_collection.keys)
instrumentation_payload[:cache_hits] = cached_partials.size
+ # Extract the items for the keys that are not found
+ # Set the uncached values to instance variable @collection
+ # which is used by the caller
@collection = keyed_collection.reject { |key, _| cached_partials.key?(key) }.values
+
+ # If all elements are already in cache then
+ # rendered partials will be an empty array
+ #
+ # If the cache is missing elements then
+ # the block will be called against the remaining items
+ # in the @collection.
rendered_partials = @collection.empty? ? [] : yield
index = 0
fetch_or_cache_partial(cached_partials, order_by: keyed_collection.each_key) do
+ # This block is called once
+ # for every cache miss while preserving order.
rendered_partials[index].tap { index += 1 }
end
end
@@ -48,6 +68,21 @@ module ActionView
@digest_path ||= @view.digest_path_from_virtual(@template.virtual_path)
end
+ # `order_by` is an enumerable object containing keys of the cache,
+ # all keys are passed in whether found already or not.
+ #
+ # `cached_partials` is a hash. If the value exists
+ # it represents the rendered partial from the cache
+ # otherwise `Hash#fetch` will take the value of its block.
+ #
+ # This method expects a block that will return the rendered
+ # partial. An example is to render all results
+ # for each element that was not found in the cache and store it as an array.
+ # Order it so that the first empty cache element in `cached_partials`
+ # corresponds to the first element in `rendered_partials`.
+ #
+ # If the partial is not already cached it will also be
+ # written back to the underlying cache store.
def fetch_or_cache_partial(cached_partials, order_by:)
order_by.map do |cache_key|
cached_partials.fetch(cache_key) do
diff --git a/actionview/lib/action_view/renderer/streaming_template_renderer.rb b/actionview/lib/action_view/renderer/streaming_template_renderer.rb
index 276a28ce07..bb9db21e32 100644
--- a/actionview/lib/action_view/renderer/streaming_template_renderer.rb
+++ b/actionview/lib/action_view/renderer/streaming_template_renderer.rb
@@ -33,7 +33,7 @@ module ActionView
logger = ActionView::Base.logger
return unless logger
- message = "\n#{exception.class} (#{exception.message}):\n".dup
+ message = +"\n#{exception.class} (#{exception.message}):\n"
message << exception.annoted_source_code.to_s if exception.respond_to?(:annoted_source_code)
message << " " << exception.backtrace.join("\n ")
logger.fatal("#{message}\n\n")
diff --git a/actionview/lib/action_view/template.rb b/actionview/lib/action_view/template.rb
index ee1cd61f12..070d82cf17 100644
--- a/actionview/lib/action_view/template.rb
+++ b/actionview/lib/action_view/template.rb
@@ -188,7 +188,7 @@ module ActionView
end
def inspect
- @inspect ||= defined?(Rails.root) ? identifier.sub("#{Rails.root}/", "".freeze) : identifier
+ @inspect ||= defined?(Rails.root) ? identifier.sub("#{Rails.root}/", "") : identifier
end
# This method is responsible for properly setting the encoding of the
@@ -235,6 +235,19 @@ module ActionView
end
end
+
+ # Exceptions are marshalled when using the parallel test runner with DRb, so we need
+ # to ensure that references to the template object can be marshalled as well. This means forgoing
+ # the marshalling of the compiler mutex and instantiating that again on unmarshalling.
+ def marshal_dump # :nodoc:
+ [ @source, @identifier, @handler, @compiled, @original_encoding, @locals, @virtual_path, @updated_at, @formats, @variants ]
+ end
+
+ def marshal_load(array) # :nodoc:
+ @source, @identifier, @handler, @compiled, @original_encoding, @locals, @virtual_path, @updated_at, @formats, @variants = *array
+ @compile_mutex = Mutex.new
+ end
+
private
# Compile a template. This method ensures a template is compiled
@@ -286,7 +299,7 @@ module ActionView
# Make sure that the resulting String to be eval'd is in the
# encoding of the code
- source = <<-end_src.dup
+ source = +<<-end_src
def #{method_name}(local_assigns, output_buffer)
_old_virtual_path, @virtual_path = @virtual_path, #{@virtual_path.inspect};_old_output_buffer = @output_buffer;#{locals_code};#{code}
ensure
@@ -335,19 +348,19 @@ module ActionView
locals = locals.grep(/\A@?(?![A-Z0-9])(?:[[:alnum:]_]|[^\0-\177])+\z/)
# Assign for the same variable is to suppress unused variable warning
- locals.each_with_object("".dup) { |key, code| code << "#{key} = local_assigns[:#{key}]; #{key} = #{key};" }
+ locals.each_with_object(+"") { |key, code| code << "#{key} = local_assigns[:#{key}]; #{key} = #{key};" }
end
def method_name
@method_name ||= begin
- m = "_#{identifier_method_name}__#{@identifier.hash}_#{__id__}".dup
- m.tr!("-".freeze, "_".freeze)
+ m = +"_#{identifier_method_name}__#{@identifier.hash}_#{__id__}"
+ m.tr!("-", "_")
m
end
end
def identifier_method_name
- inspect.tr("^a-z_".freeze, "_".freeze)
+ inspect.tr("^a-z_", "_")
end
def instrument(action, &block) # :doc:
@@ -355,7 +368,7 @@ module ActionView
end
def instrument_render_template(&block)
- ActiveSupport::Notifications.instrument("!render_template.action_view".freeze, instrument_payload, &block)
+ ActiveSupport::Notifications.instrument("!render_template.action_view", instrument_payload, &block)
end
def instrument_payload
diff --git a/actionview/lib/action_view/template/resolver.rb b/actionview/lib/action_view/template/resolver.rb
index db3cff71df..08dd6fb510 100644
--- a/actionview/lib/action_view/template/resolver.rb
+++ b/actionview/lib/action_view/template/resolver.rb
@@ -16,7 +16,7 @@ module ActionView
alias_method :partial?, :partial
def self.build(name, prefix, partial)
- virtual = "".dup
+ virtual = +""
virtual << "#{prefix}/" unless prefix.empty?
virtual << (partial ? "_#{name}" : name)
new name, prefix, partial, virtual
@@ -282,7 +282,7 @@ module ActionView
end
def escape_entry(entry)
- entry.gsub(/[*?{}\[\]]/, '\\\\\\&'.freeze)
+ entry.gsub(/[*?{}\[\]]/, '\\\\\\&')
end
# Returns the file mtime from the filesystem.
@@ -294,7 +294,7 @@ module ActionView
# from the path, or the handler, we should return the array of formats given
# to the resolver.
def extract_handler_and_format_and_variant(path)
- pieces = File.basename(path).split(".".freeze)
+ pieces = File.basename(path).split(".")
pieces.shift
extension = pieces.pop
diff --git a/actionview/lib/action_view/test_case.rb b/actionview/lib/action_view/test_case.rb
index e1cbae5845..e14f7aaec7 100644
--- a/actionview/lib/action_view/test_case.rb
+++ b/actionview/lib/action_view/test_case.rb
@@ -107,7 +107,7 @@ module ActionView
# empty string ensures buffer has UTF-8 encoding as
# new without arguments returns ASCII-8BIT encoded buffer like String#new
@output_buffer = ActiveSupport::SafeBuffer.new ""
- @rendered = "".dup
+ @rendered = +""
make_test_case_available_to_view!
say_no_to_protect_against_forgery!
diff --git a/actionview/lib/action_view/testing/resolvers.rb b/actionview/lib/action_view/testing/resolvers.rb
index 68186c3bf8..1fad08a689 100644
--- a/actionview/lib/action_view/testing/resolvers.rb
+++ b/actionview/lib/action_view/testing/resolvers.rb
@@ -22,7 +22,7 @@ module ActionView #:nodoc:
private
def query(path, exts, _, _)
- query = "".dup
+ query = +""
EXTENSIONS.each_key do |ext|
query << "(" << exts[ext].map { |e| e && Regexp.escape(".#{e}") }.join("|") << "|)"
end
diff --git a/actionview/test/abstract_unit.rb b/actionview/test/abstract_unit.rb
index f20a66c2d2..f90626ad9e 100644
--- a/actionview/test/abstract_unit.rb
+++ b/actionview/test/abstract_unit.rb
@@ -65,43 +65,6 @@ module RenderERBUtils
end
end
-SharedTestRoutes = ActionDispatch::Routing::RouteSet.new
-
-module ActionDispatch
- module SharedRoutes
- def before_setup
- @routes = SharedTestRoutes
- super
- end
- end
-
- # Hold off drawing routes until all the possible controller classes
- # have been loaded.
- module DrawOnce
- class << self
- attr_accessor :drew
- end
- self.drew = false
-
- def before_setup
- super
- return if DrawOnce.drew
-
- ActiveSupport::Deprecation.silence do
- SharedTestRoutes.draw do
- get ":controller(/:action)"
- end
-
- ActionDispatch::IntegrationTest.app.routes.draw do
- get ":controller(/:action)"
- end
- end
-
- DrawOnce.drew = true
- end
- end
-end
-
class RoutedRackApp
attr_reader :routes
@@ -132,10 +95,11 @@ class BasicController
end
class ActionDispatch::IntegrationTest < ActiveSupport::TestCase
- include ActionDispatch::SharedRoutes
-
def self.build_app(routes = nil)
- RoutedRackApp.new(routes || ActionDispatch::Routing::RouteSet.new) do |middleware|
+ routes ||= ActionDispatch::Routing::RouteSet.new.tap { |rs|
+ rs.draw { }
+ }
+ RoutedRackApp.new(routes) do |middleware|
middleware.use ActionDispatch::ShowExceptions, ActionDispatch::PublicExceptions.new("#{FIXTURE_LOAD_PATH}/public")
middleware.use ActionDispatch::DebugExceptions
middleware.use ActionDispatch::Callbacks
@@ -151,13 +115,10 @@ class ActionDispatch::IntegrationTest < ActiveSupport::TestCase
def with_routing(&block)
temporary_routes = ActionDispatch::Routing::RouteSet.new
old_app, self.class.app = self.class.app, self.class.build_app(temporary_routes)
- old_routes = SharedTestRoutes
- silence_warnings { Object.const_set(:SharedTestRoutes, temporary_routes) }
yield temporary_routes
ensure
self.class.app = old_app
- silence_warnings { Object.const_set(:SharedTestRoutes, old_routes) }
end
end
@@ -165,30 +126,37 @@ ActionView::RoutingUrlFor.include(ActionDispatch::Routing::UrlFor)
module ActionController
class Base
- # This stub emulates the Railtie including the URL helpers from a Rails application
- include SharedTestRoutes.url_helpers
- include SharedTestRoutes.mounted_helpers
-
self.view_paths = FIXTURE_LOAD_PATH
def self.test_routes(&block)
routes = ActionDispatch::Routing::RouteSet.new
routes.draw(&block)
include routes.url_helpers
+ routes
end
end
class TestCase
include ActionDispatch::TestProcess
- include ActionDispatch::SharedRoutes
- end
-end
-module ActionView
- class TestCase
- # Must repeat the setup because AV::TestCase is a duplication
- # of AC::TestCase
- include ActionDispatch::SharedRoutes
+ def self.with_routes(&block)
+ routes = ActionDispatch::Routing::RouteSet.new
+ routes.draw(&block)
+ include Module.new {
+ define_method(:setup) do
+ super()
+ @routes = routes
+ @controller.singleton_class.include @routes.url_helpers
+ end
+ }
+ routes
+ end
+
+ def with_routes(&block)
+ @routes = ActionDispatch::Routing::RouteSet.new
+ @routes.draw(&block)
+ @routes
+ end
end
end
@@ -222,15 +190,16 @@ module ActionDispatch
end
class ActiveSupport::TestCase
- include ActionDispatch::DrawOnce
include ActiveSupport::Testing::MethodCallAssertions
- # Skips the current run on Rubinius using Minitest::Assertions#skip
- private def rubinius_skip(message = "")
- skip message if RUBY_ENGINE == "rbx"
- end
- # Skips the current run on JRuby using Minitest::Assertions#skip
- private def jruby_skip(message = "")
- skip message if defined?(JRUBY_VERSION)
- end
+ private
+ # Skips the current run on Rubinius using Minitest::Assertions#skip
+ def rubinius_skip(message = "")
+ skip message if RUBY_ENGINE == "rbx"
+ end
+
+ # Skips the current run on JRuby using Minitest::Assertions#skip
+ def jruby_skip(message = "")
+ skip message if defined?(JRUBY_VERSION)
+ end
end
diff --git a/actionview/test/actionpack/controller/capture_test.rb b/actionview/test/actionpack/controller/capture_test.rb
index 09309e5b6d..d02125bafa 100644
--- a/actionview/test/actionpack/controller/capture_test.rb
+++ b/actionview/test/actionpack/controller/capture_test.rb
@@ -37,6 +37,15 @@ end
class CaptureTest < ActionController::TestCase
tests CaptureController
+ with_routes do
+ get :content_for, to: "test#content_for"
+ get :capturing, to: "test#capturing"
+ get :proper_block_detection, to: "test#proper_block_detection"
+ get :non_erb_block_content_for, to: "test#non_erb_block_content_for"
+ get :content_for_concatenated, to: "test#content_for_concatenated"
+ get :content_for_with_parameter, to: "test#content_for_with_parameter"
+ end
+
def setup
super
# enable a logger so that (e.g.) the benchmarking stuff runs, so we can get
diff --git a/actionview/test/actionpack/controller/layout_test.rb b/actionview/test/actionpack/controller/layout_test.rb
index ff66ff2a1a..6d5c97b7fd 100644
--- a/actionview/test/actionpack/controller/layout_test.rb
+++ b/actionview/test/actionpack/controller/layout_test.rb
@@ -48,6 +48,10 @@ end
class LayoutAutoDiscoveryTest < ActionController::TestCase
include TemplateHandlerHelper
+ with_routes do
+ get :hello, to: "views#hello"
+ end
+
def setup
super
@request.host = "www.nextangle.com"
@@ -148,6 +152,11 @@ class LayoutSetInResponseTest < ActionController::TestCase
include ActionView::Template::Handlers
include TemplateHandlerHelper
+ with_routes do
+ get :hello, to: "views#hello"
+ get :hello, to: "views#goodbye"
+ end
+
def test_layout_set_when_using_default_layout
@controller = DefaultLayoutController.new
get :hello
@@ -234,6 +243,10 @@ class SetsNonExistentLayoutFile < LayoutTest
end
class LayoutExceptionRaisedTest < ActionController::TestCase
+ with_routes do
+ get :hello, to: "views#hello"
+ end
+
def test_exception_raised_when_layout_file_not_found
@controller = SetsNonExistentLayoutFile.new
assert_raise(ActionView::MissingTemplate) { get :hello }
@@ -247,6 +260,10 @@ class LayoutStatusIsRendered < LayoutTest
end
class LayoutStatusIsRenderedTest < ActionController::TestCase
+ with_routes do
+ get :hello, to: "views#hello"
+ end
+
def test_layout_status_is_rendered
@controller = LayoutStatusIsRendered.new
get :hello
@@ -260,6 +277,10 @@ unless Gem.win_platform?
end
class LayoutSymlinkedIsRenderedTest < ActionController::TestCase
+ with_routes do
+ get :hello, to: "views#hello"
+ end
+
def test_symlinked_layout_is_rendered
@controller = LayoutSymlinkedTest.new
get :hello
diff --git a/actionview/test/actionpack/controller/render_test.rb b/actionview/test/actionpack/controller/render_test.rb
index 3e6b55a87e..204903c60c 100644
--- a/actionview/test/actionpack/controller/render_test.rb
+++ b/actionview/test/actionpack/controller/render_test.rb
@@ -632,6 +632,124 @@ end
class RenderTest < ActionController::TestCase
tests TestController
+ with_routes do
+ get :"hyphen-ated", to: "test#hyphen-ated"
+ get :accessing_action_name_in_template, to: "test#accessing_action_name_in_template"
+ get :accessing_controller_name_in_template, to: "test#accessing_controller_name_in_template"
+ get :accessing_local_assigns_in_inline_template, to: "test#accessing_local_assigns_in_inline_template"
+ get :accessing_logger_in_template, to: "test#accessing_logger_in_template"
+ get :accessing_params_in_template, to: "test#accessing_params_in_template"
+ get :accessing_params_in_template_with_layout, to: "test#accessing_params_in_template_with_layout"
+ get :accessing_request_in_template, to: "test#accessing_request_in_template"
+ get :action_talk_to_layout, to: "test#action_talk_to_layout"
+ get :builder_layout_test, to: "test#builder_layout_test"
+ get :builder_partial_test, to: "test#builder_partial_test"
+ get :clone, to: "test#clone"
+ get :determine_layout, to: "test#determine_layout"
+ get :double_redirect, to: "test#double_redirect"
+ get :double_render, to: "test#double_render"
+ get :empty_partial_collection, to: "test#empty_partial_collection"
+ get :formatted_html_erb, to: "test#formatted_html_erb"
+ get :formatted_xml_erb, to: "test#formatted_xml_erb"
+ get :greeting, to: "test#greeting"
+ get :hello_in_a_string, to: "test#hello_in_a_string"
+ get :hello_world, to: "fun/games#hello_world"
+ get :hello_world, to: "test#hello_world"
+ get :hello_world_file, to: "test#hello_world_file"
+ get :hello_world_from_rxml_using_action, to: "test#hello_world_from_rxml_using_action"
+ get :hello_world_from_rxml_using_template, to: "test#hello_world_from_rxml_using_template"
+ get :hello_world_with_layout_false, to: "test#hello_world_with_layout_false"
+ get :layout_overriding_layout, to: "test#layout_overriding_layout"
+ get :layout_test, to: "test#layout_test"
+ get :layout_test_with_different_layout, to: "test#layout_test_with_different_layout"
+ get :layout_test_with_different_layout_and_string_action, to: "test#layout_test_with_different_layout_and_string_action"
+ get :layout_test_with_different_layout_and_symbol_action, to: "test#layout_test_with_different_layout_and_symbol_action"
+ get :missing_partial, to: "test#missing_partial"
+ get :nested_partial_with_form_builder, to: "fun/games#nested_partial_with_form_builder"
+ get :new, to: "quiz/questions#new"
+ get :partial, to: "test#partial"
+ get :partial_collection, to: "test#partial_collection"
+ get :partial_collection_shorthand_with_different_types_of_records, to: "test#partial_collection_shorthand_with_different_types_of_records"
+ get :partial_collection_shorthand_with_locals, to: "test#partial_collection_shorthand_with_locals"
+ get :partial_collection_with_as, to: "test#partial_collection_with_as"
+ get :partial_collection_with_as_and_counter, to: "test#partial_collection_with_as_and_counter"
+ get :partial_collection_with_as_and_iteration, to: "test#partial_collection_with_as_and_iteration"
+ get :partial_collection_with_counter, to: "test#partial_collection_with_counter"
+ get :partial_collection_with_iteration, to: "test#partial_collection_with_iteration"
+ get :partial_collection_with_locals, to: "test#partial_collection_with_locals"
+ get :partial_collection_with_spacer, to: "test#partial_collection_with_spacer"
+ get :partial_collection_with_spacer_which_uses_render, to: "test#partial_collection_with_spacer_which_uses_render"
+ get :partial_formats_html, to: "test#partial_formats_html"
+ get :partial_hash_collection, to: "test#partial_hash_collection"
+ get :partial_hash_collection_with_locals, to: "test#partial_hash_collection_with_locals"
+ get :partial_html_erb, to: "test#partial_html_erb"
+ get :partial_only, to: "test#partial_only"
+ get :partial_with_counter, to: "test#partial_with_counter"
+ get :partial_with_form_builder, to: "test#partial_with_form_builder"
+ get :partial_with_form_builder_subclass, to: "test#partial_with_form_builder_subclass"
+ get :partial_with_hash_object, to: "test#partial_with_hash_object"
+ get :partial_with_locals, to: "test#partial_with_locals"
+ get :partial_with_nested_object, to: "test#partial_with_nested_object"
+ get :partial_with_nested_object_shorthand, to: "test#partial_with_nested_object_shorthand"
+ get :partial_with_string_locals, to: "test#partial_with_string_locals"
+ get :partials_list, to: "test#partials_list"
+ get :render_action_hello_world, to: "test#render_action_hello_world"
+ get :render_action_hello_world_as_string, to: "test#render_action_hello_world_as_string"
+ get :render_action_hello_world_with_symbol, to: "test#render_action_hello_world_with_symbol"
+ get :render_action_upcased_hello_world, to: "test#render_action_upcased_hello_world"
+ get :render_and_redirect, to: "test#render_and_redirect"
+ get :render_call_to_partial_with_layout, to: "test#render_call_to_partial_with_layout"
+ get :render_call_to_partial_with_layout_in_main_layout_and_within_content_for_layout, to: "test#render_call_to_partial_with_layout_in_main_layout_and_within_content_for_layout"
+ get :render_custom_code, to: "test#render_custom_code"
+ get :render_file_as_string_with_locals, to: "test#render_file_as_string_with_locals"
+ get :render_file_from_template, to: "test#render_file_from_template"
+ get :render_file_not_using_full_path, to: "test#render_file_not_using_full_path"
+ get :render_file_not_using_full_path_with_dot_in_path, to: "test#render_file_not_using_full_path_with_dot_in_path"
+ get :render_file_using_pathname, to: "test#render_file_using_pathname"
+ get :render_file_with_instance_variables, to: "test#render_file_with_instance_variables"
+ get :render_file_with_locals, to: "test#render_file_with_locals"
+ get :render_hello_world, to: "test#render_hello_world"
+ get :render_hello_world_from_variable, to: "test#render_hello_world_from_variable"
+ get :render_hello_world_with_forward_slash, to: "test#render_hello_world_with_forward_slash"
+ get :render_implicit_html_template_from_xhr_request, to: "test#render_implicit_html_template_from_xhr_request"
+ get :render_implicit_js_template_without_layout, to: "test#render_implicit_js_template_without_layout"
+ get :render_line_offset, to: "test#render_line_offset"
+ get :render_nothing_with_appendix, to: "test#render_nothing_with_appendix"
+ get :render_template_in_top_directory, to: "test#render_template_in_top_directory"
+ get :render_template_in_top_directory_with_slash, to: "test#render_template_in_top_directory_with_slash"
+ get :render_template_within_a_template_with_other_format, to: "test#render_template_within_a_template_with_other_format"
+ get :render_text_hello_world, to: "test#render_text_hello_world"
+ get :render_text_hello_world_with_layout, to: "test#render_text_hello_world_with_layout"
+ get :render_text_with_assigns, to: "test#render_text_with_assigns"
+ get :render_text_with_false, to: "test#render_text_with_false"
+ get :render_text_with_nil, to: "test#render_text_with_nil"
+ get :render_text_with_resource, to: "test#render_text_with_resource"
+ get :render_to_string_and_render, to: "test#render_to_string_and_render"
+ get :render_to_string_and_render_with_different_formats, to: "test#render_to_string_and_render_with_different_formats"
+ get :render_to_string_test, to: "test#render_to_string_test"
+ get :render_to_string_with_assigns, to: "test#render_to_string_with_assigns"
+ get :render_to_string_with_caught_exception, to: "test#render_to_string_with_caught_exception"
+ get :render_to_string_with_exception, to: "test#render_to_string_with_exception"
+ get :render_to_string_with_inline_and_render, to: "test#render_to_string_with_inline_and_render"
+ get :render_to_string_with_partial, to: "test#render_to_string_with_partial"
+ get :render_to_string_with_template_and_html_partial, to: "test#render_to_string_with_template_and_html_partial"
+ get :render_using_layout_around_block, to: "test#render_using_layout_around_block"
+ get :render_using_layout_around_block_in_main_layout_and_within_content_for_layout, to: "test#render_using_layout_around_block_in_main_layout_and_within_content_for_layout"
+ get :render_with_assigns_option, to: "test#render_with_assigns_option"
+ get :render_with_explicit_escaped_template, to: "test#render_with_explicit_escaped_template"
+ get :render_with_explicit_string_template, to: "test#render_with_explicit_string_template"
+ get :render_with_explicit_template, to: "test#render_with_explicit_template"
+ get :render_with_explicit_template_with_locals, to: "test#render_with_explicit_template_with_locals"
+ get :render_with_explicit_unescaped_template, to: "test#render_with_explicit_unescaped_template"
+ get :render_with_filters, to: "test#render_with_filters"
+ get :render_xml_hello, to: "test#render_xml_hello"
+ get :render_xml_hello_as_string_template, to: "test#render_xml_hello_as_string_template"
+ get :rendering_nothing_on_layout, to: "test#rendering_nothing_on_layout"
+ get :rendering_with_conflicting_local_vars, to: "test#rendering_with_conflicting_local_vars"
+ get :rendering_without_layout, to: "test#rendering_without_layout"
+ get :yield_content_for, to: "test#yield_content_for"
+ end
+
def setup
# enable a logger so that (e.g.) the benchmarking stuff runs, so we can get
# a more accurate simulation of what happens in "real life".
diff --git a/actionview/test/actionpack/controller/view_paths_test.rb b/actionview/test/actionpack/controller/view_paths_test.rb
index 45c662f0ce..7f3fe0fa08 100644
--- a/actionview/test/actionpack/controller/view_paths_test.rb
+++ b/actionview/test/actionpack/controller/view_paths_test.rb
@@ -24,11 +24,17 @@ class ViewLoadPathsTest < ActionController::TestCase
end
end
+ with_routes do
+ get :hello_world, to: "test#hello_world"
+ get :hello_world_at_request_time, to: "test#hello_world_at_request_time"
+ end
+
def setup
@controller = TestController.new
@request = ActionController::TestRequest.create(@controller.class)
@response = ActionDispatch::TestResponse.new
@paths = TestController.view_paths
+ super
end
def teardown
@@ -109,6 +115,10 @@ class ViewLoadPathsTest < ActionController::TestCase
def test_view_paths_override_for_layouts_in_controllers_with_a_module
@controller = Test::SubController.new
+ with_routes do
+ get :hello_world, to: "view_load_paths_test/test/sub#hello_world"
+ end
+
Test::SubController.view_paths = [ "#{FIXTURE_LOAD_PATH}/override", FIXTURE_LOAD_PATH, "#{FIXTURE_LOAD_PATH}/override2" ]
get :hello_world
assert_response :success
diff --git a/actionview/test/active_record_unit.rb b/actionview/test/active_record_unit.rb
index 7f48b515a0..e4ea6a426d 100644
--- a/actionview/test/active_record_unit.rb
+++ b/actionview/test/active_record_unit.rb
@@ -74,6 +74,18 @@ end
class ActiveRecordTestCase < ActionController::TestCase
include ActiveRecord::TestFixtures
+ def self.tests(controller)
+ super
+ if defined? controller::ROUTES
+ include Module.new {
+ define_method(:setup) do
+ super()
+ @routes = controller::ROUTES
+ end
+ }
+ end
+ end
+
# Set our fixture path
if ActiveRecordTestConnector.able_to_connect
self.fixture_path = [FIXTURE_LOAD_PATH]
diff --git a/actionview/test/activerecord/controller_runtime_test.rb b/actionview/test/activerecord/controller_runtime_test.rb
index 42b171ea07..7cbd3aaf89 100644
--- a/actionview/test/activerecord/controller_runtime_test.rb
+++ b/actionview/test/activerecord/controller_runtime_test.rb
@@ -39,6 +39,14 @@ class ControllerRuntimeLogSubscriberTest < ActionController::TestCase
include ActiveSupport::LogSubscriber::TestHelper
tests LogSubscriberController
+ with_routes do
+ get :show, to: "#{LogSubscriberController.controller_path}#show"
+ get :zero, to: "#{LogSubscriberController.controller_path}#zero"
+ get :db_after_render, to: "#{LogSubscriberController.controller_path}#db_after_render"
+ get :redirect, to: "#{LogSubscriberController.controller_path}#redirect"
+ post :create, to: "#{LogSubscriberController.controller_path}#create"
+ end
+
def setup
@old_logger = ActionController::Base.logger
super
diff --git a/actionview/test/activerecord/debug_helper_test.rb b/actionview/test/activerecord/debug_helper_test.rb
index 4be1023733..87a1791573 100644
--- a/actionview/test/activerecord/debug_helper_test.rb
+++ b/actionview/test/activerecord/debug_helper_test.rb
@@ -13,7 +13,7 @@ class DebugHelperTest < ActionView::TestCase
end
def test_debug_with_marshal_error
- obj = -> {}
+ obj = -> { }
assert_match obj.inspect, Nokogiri.XML(debug(obj)).content
end
end
diff --git a/actionview/test/activerecord/form_helper_activerecord_test.rb b/actionview/test/activerecord/form_helper_activerecord_test.rb
index 1472ee8def..34655bfe23 100644
--- a/actionview/test/activerecord/form_helper_activerecord_test.rb
+++ b/actionview/test/activerecord/form_helper_activerecord_test.rb
@@ -23,9 +23,12 @@ class FormHelperActiveRecordTest < ActionView::TestCase
@developer.projects << @project
@developer.save
+ super
+ @controller.singleton_class.include Routes.url_helpers
end
def teardown
+ super
Project.delete(321)
Developer.delete(123)
end
@@ -57,7 +60,7 @@ class FormHelperActiveRecordTest < ActionView::TestCase
private
def hidden_fields(method = nil)
- txt = %{<input name="utf8" type="hidden" value="&#x2713;" />}.dup
+ txt = +%{<input name="utf8" type="hidden" value="&#x2713;" />}
if method && !%w(get post).include?(method.to_s)
txt << %{<input name="_method" type="hidden" value="#{method}" />}
@@ -67,7 +70,7 @@ class FormHelperActiveRecordTest < ActionView::TestCase
end
def form_text(action = "/", id = nil, html_class = nil, remote = nil, multipart = nil, method = nil)
- txt = %{<form accept-charset="UTF-8" action="#{action}"}.dup
+ txt = +%{<form accept-charset="UTF-8" action="#{action}"}
txt << %{ enctype="multipart/form-data"} if multipart
txt << %{ data-remote="true"} if remote
txt << %{ class="#{html_class}"} if html_class
diff --git a/actionview/test/activerecord/polymorphic_routes_test.rb b/actionview/test/activerecord/polymorphic_routes_test.rb
index 4b931f793f..724129a7d9 100644
--- a/actionview/test/activerecord/polymorphic_routes_test.rb
+++ b/actionview/test/activerecord/polymorphic_routes_test.rb
@@ -62,10 +62,14 @@ module Weblog
end
class PolymorphicRoutesTest < ActionController::TestCase
- include SharedTestRoutes.url_helpers
+ Routes = ActionDispatch::Routing::RouteSet.new
+ Routes.draw { }
+ include Routes.url_helpers
+
default_url_options[:host] = "example.com"
def setup
+ super
@project = Project.new
@task = Task.new
@step = Step.new
@@ -763,9 +767,11 @@ class DirectRoutesTest < ActionView::TestCase
include Routes.url_helpers
def setup
+ super
@category = Category.new("1")
@collection = Collection.new("2")
@product = Product.new("3")
+ @controller.singleton_class.include Routes.url_helpers
end
def test_direct_routes
diff --git a/actionview/test/activerecord/relation_cache_test.rb b/actionview/test/activerecord/relation_cache_test.rb
index 56e17e67a8..a6befc3ee5 100644
--- a/actionview/test/activerecord/relation_cache_test.rb
+++ b/actionview/test/activerecord/relation_cache_test.rb
@@ -6,6 +6,7 @@ class RelationCacheTest < ActionView::TestCase
tests ActionView::Helpers::CacheHelper
def setup
+ super
view_paths = ActionController::Base.view_paths
lookup_context = ActionView::LookupContext.new(view_paths, {}, ["test"])
@view_renderer = ActionView::Renderer.new(lookup_context)
diff --git a/actionview/test/activerecord/render_partial_with_record_identification_test.rb b/actionview/test/activerecord/render_partial_with_record_identification_test.rb
index 3a698fa42e..2bb3cfeb5b 100644
--- a/actionview/test/activerecord/render_partial_with_record_identification_test.rb
+++ b/actionview/test/activerecord/render_partial_with_record_identification_test.rb
@@ -3,6 +3,16 @@
require "active_record_unit"
class RenderPartialWithRecordIdentificationController < ActionController::Base
+ ROUTES = test_routes do
+ get :render_with_record_collection, to: "render_partial_with_record_identification#render_with_record_collection"
+ get :render_with_scope, to: "render_partial_with_record_identification#render_with_scope"
+ get :render_with_record, to: "render_partial_with_record_identification#render_with_record"
+ get :render_with_has_many_association, to: "render_partial_with_record_identification#render_with_has_many_association"
+ get :render_with_has_many_and_belongs_to_association, to: "render_partial_with_record_identification#render_with_has_many_and_belongs_to_association"
+ get :render_with_has_one_association, to: "render_partial_with_record_identification#render_with_has_one_association"
+ get :render_with_record_collection_and_spacer_template, to: "render_partial_with_record_identification#render_with_record_collection_and_spacer_template"
+ end
+
def render_with_has_many_and_belongs_to_association
@developer = Developer.find(1)
render partial: @developer.projects
@@ -89,6 +99,11 @@ end
module Fun
class NestedController < ActionController::Base
+ ROUTES = test_routes do
+ get :render_with_record_in_nested_controller, to: "fun/nested#render_with_record_in_nested_controller"
+ get :render_with_record_collection_in_nested_controller, to: "fun/nested#render_with_record_collection_in_nested_controller"
+ end
+
def render_with_record_in_nested_controller
render partial: Game.new("Pong")
end
@@ -100,6 +115,11 @@ module Fun
module Serious
class NestedDeeperController < ActionController::Base
+ ROUTES = test_routes do
+ get :render_with_record_in_deeper_nested_controller, to: "fun/serious/nested_deeper#render_with_record_in_deeper_nested_controller"
+ get :render_with_record_collection_in_deeper_nested_controller, to: "fun/serious/nested_deeper#render_with_record_collection_in_deeper_nested_controller"
+ end
+
def render_with_record_in_deeper_nested_controller
render partial: Game.new("Chess")
end
diff --git a/actionview/test/fixtures/ruby_template.ruby b/actionview/test/fixtures/ruby_template.ruby
index 93334610a8..3e0bc445a2 100644
--- a/actionview/test/fixtures/ruby_template.ruby
+++ b/actionview/test/fixtures/ruby_template.ruby
@@ -1,2 +1,2 @@
-body = "".dup
+body = +""
body << ["Hello", "from", "Ruby", "code"].join(" ")
diff --git a/actionview/test/template/capture_helper_test.rb b/actionview/test/template/capture_helper_test.rb
index 131e49327e..e172497c88 100644
--- a/actionview/test/template/capture_helper_test.rb
+++ b/actionview/test/template/capture_helper_test.rb
@@ -40,7 +40,7 @@ class CaptureHelperTest < ActionView::TestCase
assert_equal "&lt;em&gt;bar&lt;/em&gt;", string
end
- def test_capture_used_for_read
+ def test_content_for_used_for_read
content_for :foo, "foo"
assert_equal "foo", content_for(:foo)
@@ -219,7 +219,7 @@ class CaptureHelperTest < ActionView::TestCase
def test_with_output_buffer_does_not_assume_there_is_an_output_buffer
assert_nil @av.output_buffer
- assert_equal "", @av.with_output_buffer {}
+ assert_equal "", @av.with_output_buffer { }
end
def alt_encoding(output_buffer)
diff --git a/actionview/test/template/date_helper_test.rb b/actionview/test/template/date_helper_test.rb
index 701e6c86fd..0a294ec674 100644
--- a/actionview/test/template/date_helper_test.rb
+++ b/actionview/test/template/date_helper_test.rb
@@ -214,7 +214,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_day
- expected = %(<select id="date_day" name="date[day]">\n).dup
+ expected = +%(<select id="date_day" name="date[day]">\n)
expected << %(<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16" selected="selected">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n)
expected << "</select>\n"
@@ -223,7 +223,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_day_with_blank
- expected = %(<select id="date_day" name="date[day]">\n).dup
+ expected = +%(<select id="date_day" name="date[day]">\n)
expected << %(<option value=""></option>\n<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16" selected="selected">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n)
expected << "</select>\n"
@@ -232,7 +232,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_day_nil_with_blank
- expected = %(<select id="date_day" name="date[day]">\n).dup
+ expected = +%(<select id="date_day" name="date[day]">\n)
expected << %(<option value=""></option>\n<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n)
expected << "</select>\n"
@@ -240,7 +240,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_day_with_two_digit_numbers
- expected = %(<select id="date_day" name="date[day]">\n).dup
+ expected = +%(<select id="date_day" name="date[day]">\n)
expected << %(<option value="1">01</option>\n<option selected="selected" value="2">02</option>\n<option value="3">03</option>\n<option value="4">04</option>\n<option value="5">05</option>\n<option value="6">06</option>\n<option value="7">07</option>\n<option value="8">08</option>\n<option value="9">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n)
expected << "</select>\n"
@@ -249,7 +249,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_day_with_html_options
- expected = %(<select id="date_day" name="date[day]" class="selector">\n).dup
+ expected = +%(<select id="date_day" name="date[day]" class="selector">\n)
expected << %(<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16" selected="selected">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n)
expected << "</select>\n"
@@ -258,7 +258,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_day_with_default_prompt
- expected = %(<select id="date_day" name="date[day]">\n).dup
+ expected = +%(<select id="date_day" name="date[day]">\n)
expected << %(<option value="">Day</option>\n<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16" selected="selected">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n)
expected << "</select>\n"
@@ -266,7 +266,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_day_with_custom_prompt
- expected = %(<select id="date_day" name="date[day]">\n).dup
+ expected = +%(<select id="date_day" name="date[day]">\n)
expected << %(<option value="">Choose day</option>\n<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16" selected="selected">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n)
expected << "</select>\n"
@@ -274,7 +274,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_day_with_generic_with_css_classes
- expected = %(<select id="date_day" name="date[day]" class="day">\n).dup
+ expected = +%(<select id="date_day" name="date[day]" class="day">\n)
expected << %(<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16" selected="selected">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n)
expected << "</select>\n"
@@ -282,7 +282,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_day_with_custom_with_css_classes
- expected = %(<select id="date_day" name="date[day]" class="my-day">\n).dup
+ expected = +%(<select id="date_day" name="date[day]" class="my-day">\n)
expected << %(<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16" selected="selected">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n)
expected << "</select>\n"
@@ -290,7 +290,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_month
- expected = %(<select id="date_month" name="date[month]">\n).dup
+ expected = +%(<select id="date_month" name="date[month]">\n)
expected << %(<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6">June</option>\n<option value="7">July</option>\n<option value="8" selected="selected">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n)
expected << "</select>\n"
@@ -299,7 +299,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_month_with_two_digit_numbers
- expected = %(<select id="date_month" name="date[month]">\n).dup
+ expected = +%(<select id="date_month" name="date[month]">\n)
expected << %(<option value="1">01</option>\n<option value="2">02</option>\n<option value="3">03</option>\n<option value="4">04</option>\n<option value="5">05</option>\n<option value="6">06</option>\n<option value="7">07</option>\n<option value="8" selected="selected">08</option>\n<option value="9">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n)
expected << "</select>\n"
@@ -308,7 +308,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_month_with_disabled
- expected = %(<select id="date_month" name="date[month]" disabled="disabled">\n).dup
+ expected = +%(<select id="date_month" name="date[month]" disabled="disabled">\n)
expected << %(<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6">June</option>\n<option value="7">July</option>\n<option value="8" selected="selected">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n)
expected << "</select>\n"
@@ -317,7 +317,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_month_with_field_name_override
- expected = %(<select id="date_mois" name="date[mois]">\n).dup
+ expected = +%(<select id="date_mois" name="date[mois]">\n)
expected << %(<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6">June</option>\n<option value="7">July</option>\n<option value="8" selected="selected">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n)
expected << "</select>\n"
@@ -326,7 +326,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_month_with_blank
- expected = %(<select id="date_month" name="date[month]">\n).dup
+ expected = +%(<select id="date_month" name="date[month]">\n)
expected << %(<option value=""></option>\n<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6">June</option>\n<option value="7">July</option>\n<option value="8" selected="selected">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n)
expected << "</select>\n"
@@ -335,7 +335,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_month_nil_with_blank
- expected = %(<select id="date_month" name="date[month]">\n).dup
+ expected = +%(<select id="date_month" name="date[month]">\n)
expected << %(<option value=""></option>\n<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6">June</option>\n<option value="7">July</option>\n<option value="8">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n)
expected << "</select>\n"
@@ -343,7 +343,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_month_with_numbers
- expected = %(<select id="date_month" name="date[month]">\n).dup
+ expected = +%(<select id="date_month" name="date[month]">\n)
expected << %(<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8" selected="selected">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n)
expected << "</select>\n"
@@ -352,7 +352,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_month_with_numbers_and_names
- expected = %(<select id="date_month" name="date[month]">\n).dup
+ expected = +%(<select id="date_month" name="date[month]">\n)
expected << %(<option value="1">1 - January</option>\n<option value="2">2 - February</option>\n<option value="3">3 - March</option>\n<option value="4">4 - April</option>\n<option value="5">5 - May</option>\n<option value="6">6 - June</option>\n<option value="7">7 - July</option>\n<option value="8" selected="selected">8 - August</option>\n<option value="9">9 - September</option>\n<option value="10">10 - October</option>\n<option value="11">11 - November</option>\n<option value="12">12 - December</option>\n)
expected << "</select>\n"
@@ -361,7 +361,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_month_with_format_string
- expected = %(<select id="date_month" name="date[month]">\n).dup
+ expected = +%(<select id="date_month" name="date[month]">\n)
expected << %(<option value="1">January (01)</option>\n<option value="2">February (02)</option>\n<option value="3">March (03)</option>\n<option value="4">April (04)</option>\n<option value="5">May (05)</option>\n<option value="6">June (06)</option>\n<option value="7">July (07)</option>\n<option value="8" selected="selected">August (08)</option>\n<option value="9">September (09)</option>\n<option value="10">October (10)</option>\n<option value="11">November (11)</option>\n<option value="12">December (12)</option>\n)
expected << "</select>\n"
@@ -371,7 +371,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_month_with_numbers_and_names_with_abbv
- expected = %(<select id="date_month" name="date[month]">\n).dup
+ expected = +%(<select id="date_month" name="date[month]">\n)
expected << %(<option value="1">1 - Jan</option>\n<option value="2">2 - Feb</option>\n<option value="3">3 - Mar</option>\n<option value="4">4 - Apr</option>\n<option value="5">5 - May</option>\n<option value="6">6 - Jun</option>\n<option value="7">7 - Jul</option>\n<option value="8" selected="selected">8 - Aug</option>\n<option value="9">9 - Sep</option>\n<option value="10">10 - Oct</option>\n<option value="11">11 - Nov</option>\n<option value="12">12 - Dec</option>\n)
expected << "</select>\n"
@@ -380,7 +380,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_month_with_abbv
- expected = %(<select id="date_month" name="date[month]">\n).dup
+ expected = +%(<select id="date_month" name="date[month]">\n)
expected << %(<option value="1">Jan</option>\n<option value="2">Feb</option>\n<option value="3">Mar</option>\n<option value="4">Apr</option>\n<option value="5">May</option>\n<option value="6">Jun</option>\n<option value="7">Jul</option>\n<option value="8" selected="selected">Aug</option>\n<option value="9">Sep</option>\n<option value="10">Oct</option>\n<option value="11">Nov</option>\n<option value="12">Dec</option>\n)
expected << "</select>\n"
@@ -391,7 +391,7 @@ class DateHelperTest < ActionView::TestCase
def test_select_month_with_custom_names
month_names = %w(nil Januar Februar Marts April Maj Juni Juli August September Oktober November December)
- expected = %(<select id="date_month" name="date[month]">\n).dup
+ expected = +%(<select id="date_month" name="date[month]">\n)
1.upto(12) { |month| expected << %(<option value="#{month}"#{' selected="selected"' if month == 8}>#{month_names[month]}</option>\n) }
expected << "</select>\n"
@@ -402,7 +402,7 @@ class DateHelperTest < ActionView::TestCase
def test_select_month_with_zero_indexed_custom_names
month_names = %w(Januar Februar Marts April Maj Juni Juli August September Oktober November December)
- expected = %(<select id="date_month" name="date[month]">\n).dup
+ expected = +%(<select id="date_month" name="date[month]">\n)
1.upto(12) { |month| expected << %(<option value="#{month}"#{' selected="selected"' if month == 8}>#{month_names[month - 1]}</option>\n) }
expected << "</select>\n"
@@ -419,7 +419,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_month_with_html_options
- expected = %(<select id="date_month" name="date[month]" class="selector" accesskey="M">\n).dup
+ expected = +%(<select id="date_month" name="date[month]" class="selector" accesskey="M">\n)
expected << %(<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6">June</option>\n<option value="7">July</option>\n<option value="8" selected="selected">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n)
expected << "</select>\n"
@@ -427,7 +427,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_month_with_default_prompt
- expected = %(<select id="date_month" name="date[month]">\n).dup
+ expected = +%(<select id="date_month" name="date[month]">\n)
expected << %(<option value="">Month</option>\n<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6">June</option>\n<option value="7">July</option>\n<option value="8" selected="selected">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n)
expected << "</select>\n"
@@ -435,7 +435,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_month_with_custom_prompt
- expected = %(<select id="date_month" name="date[month]">\n).dup
+ expected = +%(<select id="date_month" name="date[month]">\n)
expected << %(<option value="">Choose month</option>\n<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6">June</option>\n<option value="7">July</option>\n<option value="8" selected="selected">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n)
expected << "</select>\n"
@@ -443,7 +443,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_month_with_generic_with_css_classes
- expected = %(<select id="date_month" name="date[month]" class="month">\n).dup
+ expected = +%(<select id="date_month" name="date[month]" class="month">\n)
expected << %(<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6">June</option>\n<option value="7">July</option>\n<option value="8" selected="selected">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n)
expected << "</select>\n"
@@ -451,7 +451,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_month_with_custom_with_css_classes
- expected = %(<select id="date_month" name="date[month]" class="my-month">\n).dup
+ expected = +%(<select id="date_month" name="date[month]" class="my-month">\n)
expected << %(<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6">June</option>\n<option value="7">July</option>\n<option value="8" selected="selected">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n)
expected << "</select>\n"
@@ -459,7 +459,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_year
- expected = %(<select id="date_year" name="date[year]">\n).dup
+ expected = +%(<select id="date_year" name="date[year]">\n)
expected << %(<option value="2003" selected="selected">2003</option>\n<option value="2004">2004</option>\n<option value="2005">2005</option>\n)
expected << "</select>\n"
@@ -468,7 +468,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_year_with_disabled
- expected = %(<select id="date_year" name="date[year]" disabled="disabled">\n).dup
+ expected = +%(<select id="date_year" name="date[year]" disabled="disabled">\n)
expected << %(<option value="2003" selected="selected">2003</option>\n<option value="2004">2004</option>\n<option value="2005">2005</option>\n)
expected << "</select>\n"
@@ -477,7 +477,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_year_with_field_name_override
- expected = %(<select id="date_annee" name="date[annee]">\n).dup
+ expected = +%(<select id="date_annee" name="date[annee]">\n)
expected << %(<option value="2003" selected="selected">2003</option>\n<option value="2004">2004</option>\n<option value="2005">2005</option>\n)
expected << "</select>\n"
@@ -486,7 +486,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_year_with_type_discarding
- expected = %(<select id="date_year" name="date_year">\n).dup
+ expected = +%(<select id="date_year" name="date_year">\n)
expected << %(<option value="2003" selected="selected">2003</option>\n<option value="2004">2004</option>\n<option value="2005">2005</option>\n)
expected << "</select>\n"
@@ -497,7 +497,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_year_descending
- expected = %(<select id="date_year" name="date[year]">\n).dup
+ expected = +%(<select id="date_year" name="date[year]">\n)
expected << %(<option value="2005" selected="selected">2005</option>\n<option value="2004">2004</option>\n<option value="2003">2003</option>\n)
expected << "</select>\n"
@@ -514,7 +514,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_year_with_html_options
- expected = %(<select id="date_year" name="date[year]" class="selector" accesskey="M">\n).dup
+ expected = +%(<select id="date_year" name="date[year]" class="selector" accesskey="M">\n)
expected << %(<option value="2003" selected="selected">2003</option>\n<option value="2004">2004</option>\n<option value="2005">2005</option>\n)
expected << "</select>\n"
@@ -522,7 +522,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_year_with_default_prompt
- expected = %(<select id="date_year" name="date[year]">\n).dup
+ expected = +%(<select id="date_year" name="date[year]">\n)
expected << %(<option value="">Year</option>\n<option value="2003">2003</option>\n<option value="2004">2004</option>\n<option value="2005">2005</option>\n)
expected << "</select>\n"
@@ -530,7 +530,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_year_with_custom_prompt
- expected = %(<select id="date_year" name="date[year]">\n).dup
+ expected = +%(<select id="date_year" name="date[year]">\n)
expected << %(<option value="">Choose year</option>\n<option value="2003">2003</option>\n<option value="2004">2004</option>\n<option value="2005">2005</option>\n)
expected << "</select>\n"
@@ -538,7 +538,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_year_with_generic_with_css_classes
- expected = %(<select id="date_year" name="date[year]" class="year">\n).dup
+ expected = +%(<select id="date_year" name="date[year]" class="year">\n)
expected << %(<option value="2003">2003</option>\n<option value="2004">2004</option>\n<option value="2005">2005</option>\n)
expected << "</select>\n"
@@ -546,7 +546,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_year_with_custom_with_css_classes
- expected = %(<select id="date_year" name="date[year]" class="my-year">\n).dup
+ expected = +%(<select id="date_year" name="date[year]" class="my-year">\n)
expected << %(<option value="2003">2003</option>\n<option value="2004">2004</option>\n<option value="2005">2005</option>\n)
expected << "</select>\n"
@@ -554,7 +554,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_year_with_position
- expected = %(<select id="date_year_1i" name="date[year(1i)]">\n).dup
+ expected = +%(<select id="date_year_1i" name="date[year(1i)]">\n)
expected << %(<option value="2003">2003</option>\n<option value="2004">2004</option>\n<option value="2005">2005</option>\n)
expected << "</select>\n"
assert_dom_equal expected, select_year(Date.current, include_position: true, start_year: 2003, end_year: 2005)
@@ -570,7 +570,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_hour
- expected = %(<select id="date_hour" name="date[hour]">\n).dup
+ expected = +%(<select id="date_hour" name="date[hour]">\n)
expected << %(<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08" selected="selected">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n)
expected << "</select>\n"
@@ -578,7 +578,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_hour_with_ampm
- expected = %(<select id="date_hour" name="date[hour]">\n).dup
+ expected = +%(<select id="date_hour" name="date[hour]">\n)
expected << %(<option value="00">12 AM</option>\n<option value="01">01 AM</option>\n<option value="02">02 AM</option>\n<option value="03">03 AM</option>\n<option value="04">04 AM</option>\n<option value="05">05 AM</option>\n<option value="06">06 AM</option>\n<option value="07">07 AM</option>\n<option value="08" selected="selected">08 AM</option>\n<option value="09">09 AM</option>\n<option value="10">10 AM</option>\n<option value="11">11 AM</option>\n<option value="12">12 PM</option>\n<option value="13">01 PM</option>\n<option value="14">02 PM</option>\n<option value="15">03 PM</option>\n<option value="16">04 PM</option>\n<option value="17">05 PM</option>\n<option value="18">06 PM</option>\n<option value="19">07 PM</option>\n<option value="20">08 PM</option>\n<option value="21">09 PM</option>\n<option value="22">10 PM</option>\n<option value="23">11 PM</option>\n)
expected << "</select>\n"
@@ -586,7 +586,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_hour_with_disabled
- expected = %(<select id="date_hour" name="date[hour]" disabled="disabled">\n).dup
+ expected = +%(<select id="date_hour" name="date[hour]" disabled="disabled">\n)
expected << %(<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08" selected="selected">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n)
expected << "</select>\n"
@@ -594,7 +594,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_hour_with_field_name_override
- expected = %(<select id="date_heure" name="date[heure]">\n).dup
+ expected = +%(<select id="date_heure" name="date[heure]">\n)
expected << %(<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08" selected="selected">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n)
expected << "</select>\n"
@@ -602,7 +602,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_hour_with_blank
- expected = %(<select id="date_hour" name="date[hour]">\n).dup
+ expected = +%(<select id="date_hour" name="date[hour]">\n)
expected << %(<option value=""></option>\n<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08" selected="selected">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n)
expected << "</select>\n"
@@ -610,7 +610,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_hour_nil_with_blank
- expected = %(<select id="date_hour" name="date[hour]">\n).dup
+ expected = +%(<select id="date_hour" name="date[hour]">\n)
expected << %(<option value=""></option>\n<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n)
expected << "</select>\n"
@@ -618,7 +618,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_hour_with_html_options
- expected = %(<select id="date_hour" name="date[hour]" class="selector" accesskey="M">\n).dup
+ expected = +%(<select id="date_hour" name="date[hour]" class="selector" accesskey="M">\n)
expected << %(<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08" selected="selected">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n)
expected << "</select>\n"
@@ -626,7 +626,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_hour_with_default_prompt
- expected = %(<select id="date_hour" name="date[hour]">\n).dup
+ expected = +%(<select id="date_hour" name="date[hour]">\n)
expected << %(<option value="">Hour</option>\n<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08" selected="selected">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n)
expected << "</select>\n"
@@ -634,7 +634,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_hour_with_custom_prompt
- expected = %(<select id="date_hour" name="date[hour]">\n).dup
+ expected = +%(<select id="date_hour" name="date[hour]">\n)
expected << %(<option value="">Choose hour</option>\n<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08" selected="selected">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n)
expected << "</select>\n"
@@ -642,7 +642,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_hour_with_generic_with_css_classes
- expected = %(<select id="date_hour" name="date[hour]" class="hour">\n).dup
+ expected = +%(<select id="date_hour" name="date[hour]" class="hour">\n)
expected << %(<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08" selected="selected">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n)
expected << "</select>\n"
@@ -650,7 +650,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_hour_with_custom_with_css_classes
- expected = %(<select id="date_hour" name="date[hour]" class="my-hour">\n).dup
+ expected = +%(<select id="date_hour" name="date[hour]" class="my-hour">\n)
expected << %(<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08" selected="selected">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n)
expected << "</select>\n"
@@ -658,7 +658,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_minute
- expected = %(<select id="date_minute" name="date[minute]">\n).dup
+ expected = +%(<select id="date_minute" name="date[minute]">\n)
expected << %(<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04" selected="selected">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n<option value="32">32</option>\n<option value="33">33</option>\n<option value="34">34</option>\n<option value="35">35</option>\n<option value="36">36</option>\n<option value="37">37</option>\n<option value="38">38</option>\n<option value="39">39</option>\n<option value="40">40</option>\n<option value="41">41</option>\n<option value="42">42</option>\n<option value="43">43</option>\n<option value="44">44</option>\n<option value="45">45</option>\n<option value="46">46</option>\n<option value="47">47</option>\n<option value="48">48</option>\n<option value="49">49</option>\n<option value="50">50</option>\n<option value="51">51</option>\n<option value="52">52</option>\n<option value="53">53</option>\n<option value="54">54</option>\n<option value="55">55</option>\n<option value="56">56</option>\n<option value="57">57</option>\n<option value="58">58</option>\n<option value="59">59</option>\n)
expected << "</select>\n"
@@ -666,7 +666,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_minute_with_disabled
- expected = %(<select id="date_minute" name="date[minute]" disabled="disabled">\n).dup
+ expected = +%(<select id="date_minute" name="date[minute]" disabled="disabled">\n)
expected << %(<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04" selected="selected">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n<option value="32">32</option>\n<option value="33">33</option>\n<option value="34">34</option>\n<option value="35">35</option>\n<option value="36">36</option>\n<option value="37">37</option>\n<option value="38">38</option>\n<option value="39">39</option>\n<option value="40">40</option>\n<option value="41">41</option>\n<option value="42">42</option>\n<option value="43">43</option>\n<option value="44">44</option>\n<option value="45">45</option>\n<option value="46">46</option>\n<option value="47">47</option>\n<option value="48">48</option>\n<option value="49">49</option>\n<option value="50">50</option>\n<option value="51">51</option>\n<option value="52">52</option>\n<option value="53">53</option>\n<option value="54">54</option>\n<option value="55">55</option>\n<option value="56">56</option>\n<option value="57">57</option>\n<option value="58">58</option>\n<option value="59">59</option>\n)
expected << "</select>\n"
@@ -674,7 +674,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_minute_with_field_name_override
- expected = %(<select id="date_minuto" name="date[minuto]">\n).dup
+ expected = +%(<select id="date_minuto" name="date[minuto]">\n)
expected << %(<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04" selected="selected">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n<option value="32">32</option>\n<option value="33">33</option>\n<option value="34">34</option>\n<option value="35">35</option>\n<option value="36">36</option>\n<option value="37">37</option>\n<option value="38">38</option>\n<option value="39">39</option>\n<option value="40">40</option>\n<option value="41">41</option>\n<option value="42">42</option>\n<option value="43">43</option>\n<option value="44">44</option>\n<option value="45">45</option>\n<option value="46">46</option>\n<option value="47">47</option>\n<option value="48">48</option>\n<option value="49">49</option>\n<option value="50">50</option>\n<option value="51">51</option>\n<option value="52">52</option>\n<option value="53">53</option>\n<option value="54">54</option>\n<option value="55">55</option>\n<option value="56">56</option>\n<option value="57">57</option>\n<option value="58">58</option>\n<option value="59">59</option>\n)
expected << "</select>\n"
@@ -682,7 +682,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_minute_with_blank
- expected = %(<select id="date_minute" name="date[minute]">\n).dup
+ expected = +%(<select id="date_minute" name="date[minute]">\n)
expected << %(<option value=""></option>\n<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04" selected="selected">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n<option value="32">32</option>\n<option value="33">33</option>\n<option value="34">34</option>\n<option value="35">35</option>\n<option value="36">36</option>\n<option value="37">37</option>\n<option value="38">38</option>\n<option value="39">39</option>\n<option value="40">40</option>\n<option value="41">41</option>\n<option value="42">42</option>\n<option value="43">43</option>\n<option value="44">44</option>\n<option value="45">45</option>\n<option value="46">46</option>\n<option value="47">47</option>\n<option value="48">48</option>\n<option value="49">49</option>\n<option value="50">50</option>\n<option value="51">51</option>\n<option value="52">52</option>\n<option value="53">53</option>\n<option value="54">54</option>\n<option value="55">55</option>\n<option value="56">56</option>\n<option value="57">57</option>\n<option value="58">58</option>\n<option value="59">59</option>\n)
expected << "</select>\n"
@@ -690,7 +690,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_minute_with_blank_and_step
- expected = %(<select id="date_minute" name="date[minute]">\n).dup
+ expected = +%(<select id="date_minute" name="date[minute]">\n)
expected << %(<option value=""></option>\n<option value="00">00</option>\n<option value="15">15</option>\n<option value="30">30</option>\n<option value="45">45</option>\n)
expected << "</select>\n"
@@ -698,7 +698,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_minute_nil_with_blank
- expected = %(<select id="date_minute" name="date[minute]">\n).dup
+ expected = +%(<select id="date_minute" name="date[minute]">\n)
expected << %(<option value=""></option>\n<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n<option value="32">32</option>\n<option value="33">33</option>\n<option value="34">34</option>\n<option value="35">35</option>\n<option value="36">36</option>\n<option value="37">37</option>\n<option value="38">38</option>\n<option value="39">39</option>\n<option value="40">40</option>\n<option value="41">41</option>\n<option value="42">42</option>\n<option value="43">43</option>\n<option value="44">44</option>\n<option value="45">45</option>\n<option value="46">46</option>\n<option value="47">47</option>\n<option value="48">48</option>\n<option value="49">49</option>\n<option value="50">50</option>\n<option value="51">51</option>\n<option value="52">52</option>\n<option value="53">53</option>\n<option value="54">54</option>\n<option value="55">55</option>\n<option value="56">56</option>\n<option value="57">57</option>\n<option value="58">58</option>\n<option value="59">59</option>\n)
expected << "</select>\n"
@@ -706,7 +706,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_minute_nil_with_blank_and_step
- expected = %(<select id="date_minute" name="date[minute]">\n).dup
+ expected = +%(<select id="date_minute" name="date[minute]">\n)
expected << %(<option value=""></option>\n<option value="00">00</option>\n<option value="15">15</option>\n<option value="30">30</option>\n<option value="45">45</option>\n)
expected << "</select>\n"
@@ -722,7 +722,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_minute_with_html_options
- expected = %(<select id="date_minute" name="date[minute]" class="selector" accesskey="M">\n).dup
+ expected = +%(<select id="date_minute" name="date[minute]" class="selector" accesskey="M">\n)
expected << %(<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04" selected="selected">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n<option value="32">32</option>\n<option value="33">33</option>\n<option value="34">34</option>\n<option value="35">35</option>\n<option value="36">36</option>\n<option value="37">37</option>\n<option value="38">38</option>\n<option value="39">39</option>\n<option value="40">40</option>\n<option value="41">41</option>\n<option value="42">42</option>\n<option value="43">43</option>\n<option value="44">44</option>\n<option value="45">45</option>\n<option value="46">46</option>\n<option value="47">47</option>\n<option value="48">48</option>\n<option value="49">49</option>\n<option value="50">50</option>\n<option value="51">51</option>\n<option value="52">52</option>\n<option value="53">53</option>\n<option value="54">54</option>\n<option value="55">55</option>\n<option value="56">56</option>\n<option value="57">57</option>\n<option value="58">58</option>\n<option value="59">59</option>\n)
expected << "</select>\n"
@@ -730,7 +730,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_minute_with_default_prompt
- expected = %(<select id="date_minute" name="date[minute]">\n).dup
+ expected = +%(<select id="date_minute" name="date[minute]">\n)
expected << %(<option value="">Minute</option>\n<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04" selected="selected">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n<option value="32">32</option>\n<option value="33">33</option>\n<option value="34">34</option>\n<option value="35">35</option>\n<option value="36">36</option>\n<option value="37">37</option>\n<option value="38">38</option>\n<option value="39">39</option>\n<option value="40">40</option>\n<option value="41">41</option>\n<option value="42">42</option>\n<option value="43">43</option>\n<option value="44">44</option>\n<option value="45">45</option>\n<option value="46">46</option>\n<option value="47">47</option>\n<option value="48">48</option>\n<option value="49">49</option>\n<option value="50">50</option>\n<option value="51">51</option>\n<option value="52">52</option>\n<option value="53">53</option>\n<option value="54">54</option>\n<option value="55">55</option>\n<option value="56">56</option>\n<option value="57">57</option>\n<option value="58">58</option>\n<option value="59">59</option>\n)
expected << "</select>\n"
@@ -738,7 +738,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_minute_with_custom_prompt
- expected = %(<select id="date_minute" name="date[minute]">\n).dup
+ expected = +%(<select id="date_minute" name="date[minute]">\n)
expected << %(<option value="">Choose minute</option>\n<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04" selected="selected">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n<option value="32">32</option>\n<option value="33">33</option>\n<option value="34">34</option>\n<option value="35">35</option>\n<option value="36">36</option>\n<option value="37">37</option>\n<option value="38">38</option>\n<option value="39">39</option>\n<option value="40">40</option>\n<option value="41">41</option>\n<option value="42">42</option>\n<option value="43">43</option>\n<option value="44">44</option>\n<option value="45">45</option>\n<option value="46">46</option>\n<option value="47">47</option>\n<option value="48">48</option>\n<option value="49">49</option>\n<option value="50">50</option>\n<option value="51">51</option>\n<option value="52">52</option>\n<option value="53">53</option>\n<option value="54">54</option>\n<option value="55">55</option>\n<option value="56">56</option>\n<option value="57">57</option>\n<option value="58">58</option>\n<option value="59">59</option>\n)
expected << "</select>\n"
@@ -746,7 +746,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_minute_with_generic_with_css_classes
- expected = %(<select id="date_minute" name="date[minute]" class="minute">\n).dup
+ expected = +%(<select id="date_minute" name="date[minute]" class="minute">\n)
expected << %(<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04" selected="selected">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n<option value="32">32</option>\n<option value="33">33</option>\n<option value="34">34</option>\n<option value="35">35</option>\n<option value="36">36</option>\n<option value="37">37</option>\n<option value="38">38</option>\n<option value="39">39</option>\n<option value="40">40</option>\n<option value="41">41</option>\n<option value="42">42</option>\n<option value="43">43</option>\n<option value="44">44</option>\n<option value="45">45</option>\n<option value="46">46</option>\n<option value="47">47</option>\n<option value="48">48</option>\n<option value="49">49</option>\n<option value="50">50</option>\n<option value="51">51</option>\n<option value="52">52</option>\n<option value="53">53</option>\n<option value="54">54</option>\n<option value="55">55</option>\n<option value="56">56</option>\n<option value="57">57</option>\n<option value="58">58</option>\n<option value="59">59</option>\n)
expected << "</select>\n"
@@ -754,7 +754,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_minute_with_custom_with_css_classes
- expected = %(<select id="date_minute" name="date[minute]" class="my-minute">\n).dup
+ expected = +%(<select id="date_minute" name="date[minute]" class="my-minute">\n)
expected << %(<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04" selected="selected">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n<option value="32">32</option>\n<option value="33">33</option>\n<option value="34">34</option>\n<option value="35">35</option>\n<option value="36">36</option>\n<option value="37">37</option>\n<option value="38">38</option>\n<option value="39">39</option>\n<option value="40">40</option>\n<option value="41">41</option>\n<option value="42">42</option>\n<option value="43">43</option>\n<option value="44">44</option>\n<option value="45">45</option>\n<option value="46">46</option>\n<option value="47">47</option>\n<option value="48">48</option>\n<option value="49">49</option>\n<option value="50">50</option>\n<option value="51">51</option>\n<option value="52">52</option>\n<option value="53">53</option>\n<option value="54">54</option>\n<option value="55">55</option>\n<option value="56">56</option>\n<option value="57">57</option>\n<option value="58">58</option>\n<option value="59">59</option>\n)
expected << "</select>\n"
@@ -762,7 +762,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_second
- expected = %(<select id="date_second" name="date[second]">\n).dup
+ expected = +%(<select id="date_second" name="date[second]">\n)
expected << %(<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18" selected="selected">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n<option value="32">32</option>\n<option value="33">33</option>\n<option value="34">34</option>\n<option value="35">35</option>\n<option value="36">36</option>\n<option value="37">37</option>\n<option value="38">38</option>\n<option value="39">39</option>\n<option value="40">40</option>\n<option value="41">41</option>\n<option value="42">42</option>\n<option value="43">43</option>\n<option value="44">44</option>\n<option value="45">45</option>\n<option value="46">46</option>\n<option value="47">47</option>\n<option value="48">48</option>\n<option value="49">49</option>\n<option value="50">50</option>\n<option value="51">51</option>\n<option value="52">52</option>\n<option value="53">53</option>\n<option value="54">54</option>\n<option value="55">55</option>\n<option value="56">56</option>\n<option value="57">57</option>\n<option value="58">58</option>\n<option value="59">59</option>\n)
expected << "</select>\n"
@@ -770,7 +770,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_second_with_disabled
- expected = %(<select id="date_second" name="date[second]" disabled="disabled">\n).dup
+ expected = +%(<select id="date_second" name="date[second]" disabled="disabled">\n)
expected << %(<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18" selected="selected">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n<option value="32">32</option>\n<option value="33">33</option>\n<option value="34">34</option>\n<option value="35">35</option>\n<option value="36">36</option>\n<option value="37">37</option>\n<option value="38">38</option>\n<option value="39">39</option>\n<option value="40">40</option>\n<option value="41">41</option>\n<option value="42">42</option>\n<option value="43">43</option>\n<option value="44">44</option>\n<option value="45">45</option>\n<option value="46">46</option>\n<option value="47">47</option>\n<option value="48">48</option>\n<option value="49">49</option>\n<option value="50">50</option>\n<option value="51">51</option>\n<option value="52">52</option>\n<option value="53">53</option>\n<option value="54">54</option>\n<option value="55">55</option>\n<option value="56">56</option>\n<option value="57">57</option>\n<option value="58">58</option>\n<option value="59">59</option>\n)
expected << "</select>\n"
@@ -778,7 +778,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_second_with_field_name_override
- expected = %(<select id="date_segundo" name="date[segundo]">\n).dup
+ expected = +%(<select id="date_segundo" name="date[segundo]">\n)
expected << %(<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18" selected="selected">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n<option value="32">32</option>\n<option value="33">33</option>\n<option value="34">34</option>\n<option value="35">35</option>\n<option value="36">36</option>\n<option value="37">37</option>\n<option value="38">38</option>\n<option value="39">39</option>\n<option value="40">40</option>\n<option value="41">41</option>\n<option value="42">42</option>\n<option value="43">43</option>\n<option value="44">44</option>\n<option value="45">45</option>\n<option value="46">46</option>\n<option value="47">47</option>\n<option value="48">48</option>\n<option value="49">49</option>\n<option value="50">50</option>\n<option value="51">51</option>\n<option value="52">52</option>\n<option value="53">53</option>\n<option value="54">54</option>\n<option value="55">55</option>\n<option value="56">56</option>\n<option value="57">57</option>\n<option value="58">58</option>\n<option value="59">59</option>\n)
expected << "</select>\n"
@@ -786,7 +786,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_second_with_blank
- expected = %(<select id="date_second" name="date[second]">\n).dup
+ expected = +%(<select id="date_second" name="date[second]">\n)
expected << %(<option value=""></option>\n<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18" selected="selected">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n<option value="32">32</option>\n<option value="33">33</option>\n<option value="34">34</option>\n<option value="35">35</option>\n<option value="36">36</option>\n<option value="37">37</option>\n<option value="38">38</option>\n<option value="39">39</option>\n<option value="40">40</option>\n<option value="41">41</option>\n<option value="42">42</option>\n<option value="43">43</option>\n<option value="44">44</option>\n<option value="45">45</option>\n<option value="46">46</option>\n<option value="47">47</option>\n<option value="48">48</option>\n<option value="49">49</option>\n<option value="50">50</option>\n<option value="51">51</option>\n<option value="52">52</option>\n<option value="53">53</option>\n<option value="54">54</option>\n<option value="55">55</option>\n<option value="56">56</option>\n<option value="57">57</option>\n<option value="58">58</option>\n<option value="59">59</option>\n)
expected << "</select>\n"
@@ -794,7 +794,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_second_nil_with_blank
- expected = %(<select id="date_second" name="date[second]">\n).dup
+ expected = +%(<select id="date_second" name="date[second]">\n)
expected << %(<option value=""></option>\n<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n<option value="32">32</option>\n<option value="33">33</option>\n<option value="34">34</option>\n<option value="35">35</option>\n<option value="36">36</option>\n<option value="37">37</option>\n<option value="38">38</option>\n<option value="39">39</option>\n<option value="40">40</option>\n<option value="41">41</option>\n<option value="42">42</option>\n<option value="43">43</option>\n<option value="44">44</option>\n<option value="45">45</option>\n<option value="46">46</option>\n<option value="47">47</option>\n<option value="48">48</option>\n<option value="49">49</option>\n<option value="50">50</option>\n<option value="51">51</option>\n<option value="52">52</option>\n<option value="53">53</option>\n<option value="54">54</option>\n<option value="55">55</option>\n<option value="56">56</option>\n<option value="57">57</option>\n<option value="58">58</option>\n<option value="59">59</option>\n)
expected << "</select>\n"
@@ -802,7 +802,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_second_with_html_options
- expected = %(<select id="date_second" name="date[second]" class="selector" accesskey="M">\n).dup
+ expected = +%(<select id="date_second" name="date[second]" class="selector" accesskey="M">\n)
expected << %(<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18" selected="selected">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n<option value="32">32</option>\n<option value="33">33</option>\n<option value="34">34</option>\n<option value="35">35</option>\n<option value="36">36</option>\n<option value="37">37</option>\n<option value="38">38</option>\n<option value="39">39</option>\n<option value="40">40</option>\n<option value="41">41</option>\n<option value="42">42</option>\n<option value="43">43</option>\n<option value="44">44</option>\n<option value="45">45</option>\n<option value="46">46</option>\n<option value="47">47</option>\n<option value="48">48</option>\n<option value="49">49</option>\n<option value="50">50</option>\n<option value="51">51</option>\n<option value="52">52</option>\n<option value="53">53</option>\n<option value="54">54</option>\n<option value="55">55</option>\n<option value="56">56</option>\n<option value="57">57</option>\n<option value="58">58</option>\n<option value="59">59</option>\n)
expected << "</select>\n"
@@ -810,7 +810,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_second_with_default_prompt
- expected = %(<select id="date_second" name="date[second]">\n).dup
+ expected = +%(<select id="date_second" name="date[second]">\n)
expected << %(<option value="">Seconds</option>\n<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18" selected="selected">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n<option value="32">32</option>\n<option value="33">33</option>\n<option value="34">34</option>\n<option value="35">35</option>\n<option value="36">36</option>\n<option value="37">37</option>\n<option value="38">38</option>\n<option value="39">39</option>\n<option value="40">40</option>\n<option value="41">41</option>\n<option value="42">42</option>\n<option value="43">43</option>\n<option value="44">44</option>\n<option value="45">45</option>\n<option value="46">46</option>\n<option value="47">47</option>\n<option value="48">48</option>\n<option value="49">49</option>\n<option value="50">50</option>\n<option value="51">51</option>\n<option value="52">52</option>\n<option value="53">53</option>\n<option value="54">54</option>\n<option value="55">55</option>\n<option value="56">56</option>\n<option value="57">57</option>\n<option value="58">58</option>\n<option value="59">59</option>\n)
expected << "</select>\n"
@@ -818,7 +818,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_second_with_custom_prompt
- expected = %(<select id="date_second" name="date[second]">\n).dup
+ expected = +%(<select id="date_second" name="date[second]">\n)
expected << %(<option value="">Choose seconds</option>\n<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18" selected="selected">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n<option value="32">32</option>\n<option value="33">33</option>\n<option value="34">34</option>\n<option value="35">35</option>\n<option value="36">36</option>\n<option value="37">37</option>\n<option value="38">38</option>\n<option value="39">39</option>\n<option value="40">40</option>\n<option value="41">41</option>\n<option value="42">42</option>\n<option value="43">43</option>\n<option value="44">44</option>\n<option value="45">45</option>\n<option value="46">46</option>\n<option value="47">47</option>\n<option value="48">48</option>\n<option value="49">49</option>\n<option value="50">50</option>\n<option value="51">51</option>\n<option value="52">52</option>\n<option value="53">53</option>\n<option value="54">54</option>\n<option value="55">55</option>\n<option value="56">56</option>\n<option value="57">57</option>\n<option value="58">58</option>\n<option value="59">59</option>\n)
expected << "</select>\n"
@@ -826,7 +826,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_second_with_generic_with_css_classes
- expected = %(<select id="date_second" name="date[second]" class="second">\n).dup
+ expected = +%(<select id="date_second" name="date[second]" class="second">\n)
expected << %(<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18" selected="selected">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n<option value="32">32</option>\n<option value="33">33</option>\n<option value="34">34</option>\n<option value="35">35</option>\n<option value="36">36</option>\n<option value="37">37</option>\n<option value="38">38</option>\n<option value="39">39</option>\n<option value="40">40</option>\n<option value="41">41</option>\n<option value="42">42</option>\n<option value="43">43</option>\n<option value="44">44</option>\n<option value="45">45</option>\n<option value="46">46</option>\n<option value="47">47</option>\n<option value="48">48</option>\n<option value="49">49</option>\n<option value="50">50</option>\n<option value="51">51</option>\n<option value="52">52</option>\n<option value="53">53</option>\n<option value="54">54</option>\n<option value="55">55</option>\n<option value="56">56</option>\n<option value="57">57</option>\n<option value="58">58</option>\n<option value="59">59</option>\n)
expected << "</select>\n"
@@ -834,7 +834,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_second_with_custom_with_css_classes
- expected = %(<select id="date_second" name="date[second]" class="my-second">\n).dup
+ expected = +%(<select id="date_second" name="date[second]" class="my-second">\n)
expected << %(<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18" selected="selected">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n<option value="32">32</option>\n<option value="33">33</option>\n<option value="34">34</option>\n<option value="35">35</option>\n<option value="36">36</option>\n<option value="37">37</option>\n<option value="38">38</option>\n<option value="39">39</option>\n<option value="40">40</option>\n<option value="41">41</option>\n<option value="42">42</option>\n<option value="43">43</option>\n<option value="44">44</option>\n<option value="45">45</option>\n<option value="46">46</option>\n<option value="47">47</option>\n<option value="48">48</option>\n<option value="49">49</option>\n<option value="50">50</option>\n<option value="51">51</option>\n<option value="52">52</option>\n<option value="53">53</option>\n<option value="54">54</option>\n<option value="55">55</option>\n<option value="56">56</option>\n<option value="57">57</option>\n<option value="58">58</option>\n<option value="59">59</option>\n)
expected << "</select>\n"
@@ -842,7 +842,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_date
- expected = %(<select id="date_first_year" name="date[first][year]">\n).dup
+ expected = +%(<select id="date_first_year" name="date[first][year]">\n)
expected << %(<option value="2003" selected="selected">2003</option>\n<option value="2004">2004</option>\n<option value="2005">2005</option>\n)
expected << "</select>\n"
@@ -867,7 +867,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_date_with_order
- expected = %(<select id="date_first_month" name="date[first][month]">\n).dup
+ expected = +%(<select id="date_first_month" name="date[first][month]">\n)
expected << %(<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6">June</option>\n<option value="7">July</option>\n<option value="8" selected="selected">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n)
expected << "</select>\n"
@@ -884,7 +884,7 @@ class DateHelperTest < ActionView::TestCase
def test_select_date_with_incomplete_order
# Since the order is incomplete nothing will be shown
- expected = %(<input id="date_first_year" name="date[first][year]" type="hidden" value="2003" />\n).dup
+ expected = +%(<input id="date_first_year" name="date[first][year]" type="hidden" value="2003" />\n)
expected << %(<input id="date_first_month" name="date[first][month]" type="hidden" value="8" />\n)
expected << %(<input id="date_first_day" name="date[first][day]" type="hidden" value="1" />\n)
@@ -892,7 +892,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_date_with_disabled
- expected = %(<select id="date_first_year" name="date[first][year]" disabled="disabled">\n).dup
+ expected = +%(<select id="date_first_year" name="date[first][year]" disabled="disabled">\n)
expected << %(<option value="2003" selected="selected">2003</option>\n<option value="2004">2004</option>\n<option value="2005">2005</option>\n)
expected << "</select>\n"
@@ -908,7 +908,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_date_with_no_start_year
- expected = %(<select id="date_first_year" name="date[first][year]">\n).dup
+ expected = +%(<select id="date_first_year" name="date[first][year]">\n)
(Date.today.year - 5).upto(Date.today.year + 1) do |y|
if y == Date.today.year
expected << %(<option value="#{y}" selected="selected">#{y}</option>\n)
@@ -932,7 +932,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_date_with_no_end_year
- expected = %(<select id="date_first_year" name="date[first][year]">\n).dup
+ expected = +%(<select id="date_first_year" name="date[first][year]">\n)
2003.upto(2008) do |y|
if y == 2003
expected << %(<option value="#{y}" selected="selected">#{y}</option>\n)
@@ -956,7 +956,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_date_with_no_start_or_end_year
- expected = %(<select id="date_first_year" name="date[first][year]">\n).dup
+ expected = +%(<select id="date_first_year" name="date[first][year]">\n)
(Date.today.year - 5).upto(Date.today.year + 5) do |y|
if y == Date.today.year
expected << %(<option value="#{y}" selected="selected">#{y}</option>\n)
@@ -980,7 +980,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_date_with_zero_value
- expected = %(<select id="date_first_year" name="date[first][year]">\n).dup
+ expected = +%(<select id="date_first_year" name="date[first][year]">\n)
expected << %(<option value="2003">2003</option>\n<option value="2004">2004</option>\n<option value="2005">2005</option>\n)
expected << "</select>\n"
@@ -996,7 +996,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_date_with_zero_value_and_no_start_year
- expected = %(<select id="date_first_year" name="date[first][year]">\n).dup
+ expected = +%(<select id="date_first_year" name="date[first][year]">\n)
(Date.today.year - 5).upto(Date.today.year + 1) { |y| expected << %(<option value="#{y}">#{y}</option>\n) }
expected << "</select>\n"
@@ -1012,7 +1012,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_date_with_zero_value_and_no_end_year
- expected = %(<select id="date_first_year" name="date[first][year]">\n).dup
+ expected = +%(<select id="date_first_year" name="date[first][year]">\n)
last_year = Time.now.year + 5
2003.upto(last_year) { |y| expected << %(<option value="#{y}">#{y}</option>\n) }
expected << "</select>\n"
@@ -1029,7 +1029,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_date_with_zero_value_and_no_start_and_end_year
- expected = %(<select id="date_first_year" name="date[first][year]">\n).dup
+ expected = +%(<select id="date_first_year" name="date[first][year]">\n)
(Date.today.year - 5).upto(Date.today.year + 5) { |y| expected << %(<option value="#{y}">#{y}</option>\n) }
expected << "</select>\n"
@@ -1045,7 +1045,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_date_with_nil_value_and_no_start_and_end_year
- expected = %(<select id="date_first_year" name="date[first][year]">\n).dup
+ expected = +%(<select id="date_first_year" name="date[first][year]">\n)
(Date.today.year - 5).upto(Date.today.year + 5) { |y| expected << %(<option value="#{y}">#{y}</option>\n) }
expected << "</select>\n"
@@ -1061,7 +1061,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_date_with_html_options
- expected = %(<select id="date_first_year" name="date[first][year]" class="selector">\n).dup
+ expected = +%(<select id="date_first_year" name="date[first][year]" class="selector">\n)
expected << %(<option value="2003" selected="selected">2003</option>\n<option value="2004">2004</option>\n<option value="2005">2005</option>\n)
expected << "</select>\n"
@@ -1077,7 +1077,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_date_with_separator
- expected = %(<select id="date_first_year" name="date[first][year]">\n).dup
+ expected = +%(<select id="date_first_year" name="date[first][year]">\n)
expected << %(<option value="2003" selected="selected">2003</option>\n<option value="2004">2004</option>\n<option value="2005">2005</option>\n)
expected << "</select>\n"
@@ -1097,7 +1097,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_date_with_separator_and_discard_day
- expected = %(<select id="date_first_year" name="date[first][year]">\n).dup
+ expected = +%(<select id="date_first_year" name="date[first][year]">\n)
expected << %(<option value="2003" selected="selected">2003</option>\n<option value="2004">2004</option>\n<option value="2005">2005</option>\n)
expected << "</select>\n"
@@ -1113,7 +1113,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_date_with_separator_discard_month_and_day
- expected = %(<select id="date_first_year" name="date[first][year]">\n).dup
+ expected = +%(<select id="date_first_year" name="date[first][year]">\n)
expected << %(<option value="2003" selected="selected">2003</option>\n<option value="2004">2004</option>\n<option value="2005">2005</option>\n)
expected << "</select>\n"
@@ -1124,7 +1124,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_date_with_hidden
- expected = %(<input id="date_first_year" name="date[first][year]" type="hidden" value="2003"/>\n).dup
+ expected = +%(<input id="date_first_year" name="date[first][year]" type="hidden" value="2003"/>\n)
expected << %(<input id="date_first_month" name="date[first][month]" type="hidden" value="8" />\n)
expected << %(<input id="date_first_day" name="date[first][day]" type="hidden" value="16" />\n)
@@ -1133,7 +1133,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_date_with_css_classes_option
- expected = %(<select id="date_first_year" name="date[first][year]" class="year">\n).dup
+ expected = +%(<select id="date_first_year" name="date[first][year]" class="year">\n)
expected << %(<option value="2003" selected="selected">2003</option>\n<option value="2004">2004</option>\n<option value="2005">2005</option>\n)
expected << "</select>\n"
@@ -1149,7 +1149,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_date_with_custom_with_css_classes
- expected = %(<select id="date_year" name="date[year]" class="my-year">\n).dup
+ expected = +%(<select id="date_year" name="date[year]" class="my-year">\n)
expected << %(<option value="2003" selected="selected">2003</option>\n<option value="2004">2004</option>\n<option value="2005">2005</option>\n)
expected << "</select>\n"
@@ -1165,7 +1165,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_date_with_css_classes_option_and_html_class_option
- expected = %(<select id="date_first_year" name="date[first][year]" class="datetime optional year">\n).dup
+ expected = +%(<select id="date_first_year" name="date[first][year]" class="datetime optional year">\n)
expected << %(<option value="2003" selected="selected">2003</option>\n<option value="2004">2004</option>\n<option value="2005">2005</option>\n)
expected << "</select>\n"
@@ -1181,7 +1181,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_date_with_custom_with_css_classes_and_html_class_option
- expected = %(<select id="date_year" name="date[year]" class="date optional my-year">\n).dup
+ expected = +%(<select id="date_year" name="date[year]" class="date optional my-year">\n)
expected << %(<option value="2003" selected="selected">2003</option>\n<option value="2004">2004</option>\n<option value="2005">2005</option>\n)
expected << "</select>\n"
@@ -1197,7 +1197,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_date_with_partial_with_css_classes_and_html_class_option
- expected = %(<select id="date_year" name="date[year]" class="date optional">\n).dup
+ expected = +%(<select id="date_year" name="date[year]" class="date optional">\n)
expected << %(<option value="2003" selected="selected">2003</option>\n<option value="2004">2004</option>\n<option value="2005">2005</option>\n)
expected << "</select>\n"
@@ -1213,7 +1213,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_date_with_html_class_option
- expected = %(<select id="date_year" name="date[year]" class="date optional custom-grid">\n).dup
+ expected = +%(<select id="date_year" name="date[year]" class="date optional custom-grid">\n)
expected << %(<option value="2003" selected="selected">2003</option>\n<option value="2004">2004</option>\n<option value="2005">2005</option>\n)
expected << "</select>\n"
@@ -1229,7 +1229,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_datetime
- expected = %(<select id="date_first_year" name="date[first][year]">\n).dup
+ expected = +%(<select id="date_first_year" name="date[first][year]">\n)
expected << %(<option value="2003" selected="selected">2003</option>\n<option value="2004">2004</option>\n<option value="2005">2005</option>\n)
expected << "</select>\n"
@@ -1257,7 +1257,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_datetime_with_ampm
- expected = %(<select id="date_first_year" name="date[first][year]">\n).dup
+ expected = +%(<select id="date_first_year" name="date[first][year]">\n)
expected << %(<option value="2003" selected="selected">2003</option>\n<option value="2004">2004</option>\n<option value="2005">2005</option>\n)
expected << "</select>\n"
@@ -1285,7 +1285,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_datetime_with_separators
- expected = %(<select id="date_first_year" name="date[first][year]">\n).dup
+ expected = +%(<select id="date_first_year" name="date[first][year]">\n)
expected << %(<option value="2003" selected="selected">2003</option>\n<option value="2004">2004</option>\n<option value="2005">2005</option>\n)
expected << "</select>\n"
@@ -1313,7 +1313,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_datetime_with_nil_value_and_no_start_and_end_year
- expected = %(<select id="date_first_year" name="date[first][year]">\n).dup
+ expected = +%(<select id="date_first_year" name="date[first][year]">\n)
(Date.today.year - 5).upto(Date.today.year + 5) { |y| expected << %(<option value="#{y}">#{y}</option>\n) }
expected << "</select>\n"
@@ -1341,7 +1341,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_datetime_with_html_options
- expected = %(<select id="date_first_year" name="date[first][year]" class="selector">\n).dup
+ expected = +%(<select id="date_first_year" name="date[first][year]" class="selector">\n)
expected << %(<option value="2003" selected="selected">2003</option>\n<option value="2004">2004</option>\n<option value="2005">2005</option>\n)
expected << "</select>\n"
@@ -1369,7 +1369,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_datetime_with_all_separators
- expected = %(<select id="date_first_year" name="date[first][year]" class="selector">\n).dup
+ expected = +%(<select id="date_first_year" name="date[first][year]" class="selector">\n)
expected << %(<option value="2003" selected="selected">2003</option>\n<option value="2004">2004</option>\n<option value="2005">2005</option>\n)
expected << "</select>\n"
@@ -1405,7 +1405,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_datetime_with_default_prompt
- expected = %(<select id="date_first_year" name="date[first][year]">\n).dup
+ expected = +%(<select id="date_first_year" name="date[first][year]">\n)
expected << %(<option value="">Year</option>\n<option value="2003" selected="selected">2003</option>\n<option value="2004">2004</option>\n<option value="2005">2005</option>\n)
expected << "</select>\n"
@@ -1434,7 +1434,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_datetime_with_custom_prompt
- expected = %(<select id="date_first_year" name="date[first][year]">\n).dup
+ expected = +%(<select id="date_first_year" name="date[first][year]">\n)
expected << %(<option value="">Choose year</option>\n<option value="2003" selected="selected">2003</option>\n<option value="2004">2004</option>\n<option value="2005">2005</option>\n)
expected << "</select>\n"
@@ -1463,7 +1463,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_datetime_with_generic_with_css_classes
- expected = %(<select id="date_year" name="date[year]" class="year">\n).dup
+ expected = +%(<select id="date_year" name="date[year]" class="year">\n)
expected << %(<option value="2003" selected="selected">2003</option>\n<option value="2004">2004</option>\n<option value="2005">2005</option>\n)
expected << "</select>\n"
@@ -1491,7 +1491,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_datetime_with_custom_with_css_classes
- expected = %(<select id="date_year" name="date[year]" class="my-year">\n).dup
+ expected = +%(<select id="date_year" name="date[year]" class="my-year">\n)
expected << %(<option value="2003" selected="selected">2003</option>\n<option value="2004">2004</option>\n<option value="2005">2005</option>\n)
expected << "</select>\n"
@@ -1519,7 +1519,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_datetime_with_custom_hours
- expected = %(<select id="date_first_year" name="date[first][year]">\n).dup
+ expected = +%(<select id="date_first_year" name="date[first][year]">\n)
expected << %(<option value="">Choose year</option>\n<option value="2003" selected="selected">2003</option>\n<option value="2004">2004</option>\n<option value="2005">2005</option>\n)
expected << "</select>\n"
@@ -1548,7 +1548,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_datetime_with_hidden
- expected = %(<input id="date_first_year" name="date[first][year]" type="hidden" value="2003" />\n).dup
+ expected = +%(<input id="date_first_year" name="date[first][year]" type="hidden" value="2003" />\n)
expected << %(<input id="date_first_month" name="date[first][month]" type="hidden" value="8" />\n)
expected << %(<input id="date_first_day" name="date[first][day]" type="hidden" value="16" />\n)
expected << %(<input id="date_first_hour" name="date[first][hour]" type="hidden" value="8" />\n)
@@ -1560,7 +1560,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_time
- expected = %(<input name="date[year]" id="date_year" value="2003" type="hidden" />\n).dup
+ expected = +%(<input name="date[year]" id="date_year" value="2003" type="hidden" />\n)
expected << %(<input name="date[month]" id="date_month" value="8" type="hidden" />\n)
expected << %(<input name="date[day]" id="date_day" value="16" type="hidden" />\n)
@@ -1579,7 +1579,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_time_with_ampm
- expected = %(<input name="date[year]" id="date_year" value="2003" type="hidden" />\n).dup
+ expected = +%(<input name="date[year]" id="date_year" value="2003" type="hidden" />\n)
expected << %(<input name="date[month]" id="date_month" value="8" type="hidden" />\n)
expected << %(<input name="date[day]" id="date_day" value="16" type="hidden" />\n)
@@ -1597,7 +1597,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_time_with_separator
- expected = %(<input name="date[year]" id="date_year" value="2003" type="hidden" />\n).dup
+ expected = +%(<input name="date[year]" id="date_year" value="2003" type="hidden" />\n)
expected << %(<input name="date[month]" id="date_month" value="8" type="hidden" />\n)
expected << %(<input name="date[day]" id="date_day" value="16" type="hidden" />\n)
expected << %(<select id="date_hour" name="date[hour]">\n)
@@ -1615,7 +1615,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_time_with_seconds
- expected = %(<input name="date[year]" id="date_year" value="2003" type="hidden" />\n).dup
+ expected = +%(<input name="date[year]" id="date_year" value="2003" type="hidden" />\n)
expected << %(<input name="date[month]" id="date_month" value="8" type="hidden" />\n)
expected << %(<input name="date[day]" id="date_day" value="16" type="hidden" />\n)
@@ -1639,7 +1639,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_time_with_seconds_and_separator
- expected = %(<input name="date[year]" id="date_year" value="2003" type="hidden" />\n).dup
+ expected = +%(<input name="date[year]" id="date_year" value="2003" type="hidden" />\n)
expected << %(<input name="date[month]" id="date_month" value="8" type="hidden" />\n)
expected << %(<input name="date[day]" id="date_day" value="16" type="hidden" />\n)
@@ -1663,7 +1663,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_time_with_html_options
- expected = %(<input name="date[year]" id="date_year" value="2003" type="hidden" />\n).dup
+ expected = +%(<input name="date[year]" id="date_year" value="2003" type="hidden" />\n)
expected << %(<input name="date[month]" id="date_month" value="8" type="hidden" />\n)
expected << %(<input name="date[day]" id="date_day" value="16" type="hidden" />\n)
@@ -1686,7 +1686,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_time_with_default_prompt
- expected = %(<input name="date[year]" id="date_year" value="2003" type="hidden" />\n).dup
+ expected = +%(<input name="date[year]" id="date_year" value="2003" type="hidden" />\n)
expected << %(<input name="date[month]" id="date_month" value="8" type="hidden" />\n)
expected << %(<input name="date[day]" id="date_day" value="16" type="hidden" />\n)
@@ -1710,7 +1710,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_time_with_custom_prompt
- expected = %(<input name="date[year]" id="date_year" value="2003" type="hidden" />\n).dup
+ expected = +%(<input name="date[year]" id="date_year" value="2003" type="hidden" />\n)
expected << %(<input name="date[month]" id="date_month" value="8" type="hidden" />\n)
expected << %(<input name="date[day]" id="date_day" value="16" type="hidden" />\n)
@@ -1735,7 +1735,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_time_with_generic_with_css_classes
- expected = %(<input name="date[year]" id="date_year" value="2003" type="hidden" />\n).dup
+ expected = +%(<input name="date[year]" id="date_year" value="2003" type="hidden" />\n)
expected << %(<input name="date[month]" id="date_month" value="8" type="hidden" />\n)
expected << %(<input name="date[day]" id="date_day" value="16" type="hidden" />\n)
@@ -1759,7 +1759,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_time_with_custom_with_css_classes
- expected = %(<input name="date[year]" id="date_year" value="2003" type="hidden" />\n).dup
+ expected = +%(<input name="date[year]" id="date_year" value="2003" type="hidden" />\n)
expected << %(<input name="date[month]" id="date_month" value="8" type="hidden" />\n)
expected << %(<input name="date[day]" id="date_day" value="16" type="hidden" />\n)
@@ -1783,7 +1783,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_time_with_hidden
- expected = %(<input id="date_first_year" name="date[first][year]" type="hidden" value="2003" />\n).dup
+ expected = +%(<input id="date_first_year" name="date[first][year]" type="hidden" value="2003" />\n)
expected << %(<input id="date_first_month" name="date[first][month]" type="hidden" value="8" />\n)
expected << %(<input id="date_first_day" name="date[first][day]" type="hidden" value="16" />\n)
expected << %(<input id="date_first_hour" name="date[first][hour]" type="hidden" value="8" />\n)
@@ -1797,7 +1797,7 @@ class DateHelperTest < ActionView::TestCase
@post = Post.new
@post.written_on = Date.new(2004, 6, 15)
- expected = %{<select id="post_written_on_1i" name="post[written_on(1i)]">\n}.dup
+ expected = +%{<select id="post_written_on_1i" name="post[written_on(1i)]">\n}
expected << %{<option value="1999">1999</option>\n<option value="2000">2000</option>\n<option value="2001">2001</option>\n<option value="2002">2002</option>\n<option value="2003">2003</option>\n<option value="2004" selected="selected">2004</option>\n<option value="2005">2005</option>\n<option value="2006">2006</option>\n<option value="2007">2007</option>\n<option value="2008">2008</option>\n<option value="2009">2009</option>\n}
expected << "</select>\n"
@@ -1817,7 +1817,7 @@ class DateHelperTest < ActionView::TestCase
@post = Post.new
@post.written_on = Date.new(2004, 6, 15)
- expected = %{<select id="post_written_on_1i" name="post[written_on(1i)]">\n}.dup
+ expected = +%{<select id="post_written_on_1i" name="post[written_on(1i)]">\n}
expected << %{<option value="1999">1999</option>\n<option value="2000">2000</option>\n<option value="2001">2001</option>\n<option value="2002">2002</option>\n<option value="2003">2003</option>\n<option selected="selected" value="2004">2004</option>\n<option value="2005">2005</option>\n<option value="2006">2006</option>\n<option value="2007">2007</option>\n<option value="2008">2008</option>\n<option value="2009">2009</option>\n}
expected << "</select>\n"
@@ -1837,7 +1837,7 @@ class DateHelperTest < ActionView::TestCase
@post = Post.new
@post.written_on = Date.new(2004, 6, 15)
- expected = %{<select id="post_written_on_1i" name="post[written_on(1i)]">\n}.dup
+ expected = +%{<select id="post_written_on_1i" name="post[written_on(1i)]">\n}
expected << %{<option value="1999">1999</option>\n<option value="2000">2000</option>\n<option value="2001">2001</option>\n<option value="2002">2002</option>\n<option value="2003">2003</option>\n<option selected="selected" value="2004">2004</option>\n<option value="2005">2005</option>\n<option value="2006">2006</option>\n<option value="2007">2007</option>\n<option value="2008">2008</option>\n<option value="2009">2009</option>\n}
expected << "</select>\n"
@@ -1875,7 +1875,7 @@ class DateHelperTest < ActionView::TestCase
@post = Post.new
@post.written_on = Date.new(2004, 6, 15)
- expected = "<input type=\"hidden\" id=\"post_written_on_3i\" name=\"post[written_on(3i)]\" value=\"1\" />\n".dup
+ expected = +"<input type=\"hidden\" id=\"post_written_on_3i\" name=\"post[written_on(3i)]\" value=\"1\" />\n"
expected << %{<select id="post_written_on_2i" name="post[written_on(2i)]">\n}
expected << %{<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6" selected="selected">June</option>\n<option value="7">July</option>\n<option value="8">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n}
@@ -1892,7 +1892,7 @@ class DateHelperTest < ActionView::TestCase
@post = Post.new
@post.written_on = Date.new(2004, 2, 29)
- expected = "<input type=\"hidden\" id=\"post_written_on_2i\" name=\"post[written_on(2i)]\" value=\"2\" />\n".dup
+ expected = +"<input type=\"hidden\" id=\"post_written_on_2i\" name=\"post[written_on(2i)]\" value=\"2\" />\n"
expected << "<input type=\"hidden\" id=\"post_written_on_3i\" name=\"post[written_on(3i)]\" value=\"1\" />\n"
expected << %{<select id="post_written_on_1i" name="post[written_on(1i)]">\n}
@@ -1906,7 +1906,7 @@ class DateHelperTest < ActionView::TestCase
@post = Post.new
@post.written_on = Date.new(2004, 6, 15)
- expected = "<input type=\"hidden\" id=\"post_written_on_3i\" name=\"post[written_on(3i)]\" value=\"1\" />\n".dup
+ expected = +"<input type=\"hidden\" id=\"post_written_on_3i\" name=\"post[written_on(3i)]\" value=\"1\" />\n"
expected << %{<select id="post_written_on_2i" name="post[written_on(2i)]">\n}
expected << %{<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6" selected="selected">June</option>\n<option value="7">July</option>\n<option value="8">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n}
@@ -1925,7 +1925,7 @@ class DateHelperTest < ActionView::TestCase
@post = Post.new
@post.written_on = Date.new(2004, 6, 15)
- expected = "<input type=\"hidden\" id=\"post_written_on_3i\" disabled=\"disabled\" name=\"post[written_on(3i)]\" value=\"1\" />\n".dup
+ expected = +"<input type=\"hidden\" id=\"post_written_on_3i\" disabled=\"disabled\" name=\"post[written_on(3i)]\" value=\"1\" />\n"
expected << %{<select id="post_written_on_2i" disabled="disabled" name="post[written_on(2i)]">\n}
expected << %{<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6" selected="selected">June</option>\n<option value="7">July</option>\n<option value="8">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n}
@@ -1946,7 +1946,7 @@ class DateHelperTest < ActionView::TestCase
concat f.date_select(:written_on)
end
- expected = %{<select id="post_written_on_1i" name="post[written_on(1i)]">\n<option value="1999">1999</option>\n<option value="2000">2000</option>\n<option value="2001">2001</option>\n<option value="2002">2002</option>\n<option value="2003">2003</option>\n<option selected="selected" value="2004">2004</option>\n<option value="2005">2005</option>\n<option value="2006">2006</option>\n<option value="2007">2007</option>\n<option value="2008">2008</option>\n<option value="2009">2009</option>\n</select>\n}.dup
+ expected = +%{<select id="post_written_on_1i" name="post[written_on(1i)]">\n<option value="1999">1999</option>\n<option value="2000">2000</option>\n<option value="2001">2001</option>\n<option value="2002">2002</option>\n<option value="2003">2003</option>\n<option selected="selected" value="2004">2004</option>\n<option value="2005">2005</option>\n<option value="2006">2006</option>\n<option value="2007">2007</option>\n<option value="2008">2008</option>\n<option value="2009">2009</option>\n</select>\n}
expected << %{<select id="post_written_on_2i" name="post[written_on(2i)]">\n<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option selected="selected" value="6">June</option>\n<option value="7">July</option>\n<option value="8">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n</select>\n}
expected << %{<select id="post_written_on_3i" name="post[written_on(3i)]">\n<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option selected="selected" value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n</select>\n}
@@ -1962,7 +1962,7 @@ class DateHelperTest < ActionView::TestCase
concat f.date_select(:written_on)
end
- expected = %{<select id="post_#{id}_written_on_1i" name="post[#{id}][written_on(1i)]">\n<option value="1999">1999</option>\n<option value="2000">2000</option>\n<option value="2001">2001</option>\n<option value="2002">2002</option>\n<option value="2003">2003</option>\n<option selected="selected" value="2004">2004</option>\n<option value="2005">2005</option>\n<option value="2006">2006</option>\n<option value="2007">2007</option>\n<option value="2008">2008</option>\n<option value="2009">2009</option>\n</select>\n}.dup
+ expected = +%{<select id="post_#{id}_written_on_1i" name="post[#{id}][written_on(1i)]">\n<option value="1999">1999</option>\n<option value="2000">2000</option>\n<option value="2001">2001</option>\n<option value="2002">2002</option>\n<option value="2003">2003</option>\n<option selected="selected" value="2004">2004</option>\n<option value="2005">2005</option>\n<option value="2006">2006</option>\n<option value="2007">2007</option>\n<option value="2008">2008</option>\n<option value="2009">2009</option>\n</select>\n}
expected << %{<select id="post_#{id}_written_on_2i" name="post[#{id}][written_on(2i)]">\n<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option selected="selected" value="6">June</option>\n<option value="7">July</option>\n<option value="8">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n</select>\n}
expected << %{<select id="post_#{id}_written_on_3i" name="post[#{id}][written_on(3i)]">\n<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option selected="selected" value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n</select>\n}
@@ -1978,7 +1978,7 @@ class DateHelperTest < ActionView::TestCase
concat f.date_select(:written_on)
end
- expected = %{<select id="post_#{id}_written_on_1i" name="post[#{id}][written_on(1i)]">\n<option value="1999">1999</option>\n<option value="2000">2000</option>\n<option value="2001">2001</option>\n<option value="2002">2002</option>\n<option value="2003">2003</option>\n<option selected="selected" value="2004">2004</option>\n<option value="2005">2005</option>\n<option value="2006">2006</option>\n<option value="2007">2007</option>\n<option value="2008">2008</option>\n<option value="2009">2009</option>\n</select>\n}.dup
+ expected = +%{<select id="post_#{id}_written_on_1i" name="post[#{id}][written_on(1i)]">\n<option value="1999">1999</option>\n<option value="2000">2000</option>\n<option value="2001">2001</option>\n<option value="2002">2002</option>\n<option value="2003">2003</option>\n<option selected="selected" value="2004">2004</option>\n<option value="2005">2005</option>\n<option value="2006">2006</option>\n<option value="2007">2007</option>\n<option value="2008">2008</option>\n<option value="2009">2009</option>\n</select>\n}
expected << %{<select id="post_#{id}_written_on_2i" name="post[#{id}][written_on(2i)]">\n<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option selected="selected" value="6">June</option>\n<option value="7">July</option>\n<option value="8">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n</select>\n}
expected << %{<select id="post_#{id}_written_on_3i" name="post[#{id}][written_on(3i)]">\n<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option selected="selected" value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n</select>\n}
@@ -1990,7 +1990,7 @@ class DateHelperTest < ActionView::TestCase
@post.written_on = Date.new(2004, 6, 15)
id = 456
- expected = %{<select id="post_456_written_on_1i" name="post[#{id}][written_on(1i)]">\n}.dup
+ expected = +%{<select id="post_456_written_on_1i" name="post[#{id}][written_on(1i)]">\n}
expected << %{<option value="1999">1999</option>\n<option value="2000">2000</option>\n<option value="2001">2001</option>\n<option value="2002">2002</option>\n<option value="2003">2003</option>\n<option value="2004" selected="selected">2004</option>\n<option value="2005">2005</option>\n<option value="2006">2006</option>\n<option value="2007">2007</option>\n<option value="2008">2008</option>\n<option value="2009">2009</option>\n}
expected << "</select>\n"
@@ -2010,7 +2010,7 @@ class DateHelperTest < ActionView::TestCase
@post.written_on = Date.new(2004, 6, 15)
id = 123
- expected = %{<select id="post_123_written_on_1i" name="post[#{id}][written_on(1i)]">\n}.dup
+ expected = +%{<select id="post_123_written_on_1i" name="post[#{id}][written_on(1i)]">\n}
expected << %{<option value="1999">1999</option>\n<option value="2000">2000</option>\n<option value="2001">2001</option>\n<option value="2002">2002</option>\n<option value="2003">2003</option>\n<option value="2004" selected="selected">2004</option>\n<option value="2005">2005</option>\n<option value="2006">2006</option>\n<option value="2007">2007</option>\n<option value="2008">2008</option>\n<option value="2009">2009</option>\n}
expected << "</select>\n"
@@ -2029,7 +2029,7 @@ class DateHelperTest < ActionView::TestCase
@post = Post.new
@post.written_on = Date.new(2004, 6, 15)
- expected = %{<select id="post_written_on_3i" name="post[written_on(3i)]">\n}.dup
+ expected = +%{<select id="post_written_on_3i" name="post[written_on(3i)]">\n}
1.upto(31) { |i| expected << %(<option value="#{i}"#{' selected="selected"' if i == 15}>#{i}</option>\n) }
expected << "</select>\n"
@@ -2049,7 +2049,7 @@ class DateHelperTest < ActionView::TestCase
start_year = Time.now.year - 5
end_year = Time.now.year + 5
- expected = %{<select id="post_written_on_1i" name="post[written_on(1i)]">\n}.dup
+ expected = +%{<select id="post_written_on_1i" name="post[written_on(1i)]">\n}
start_year.upto(end_year) { |i| expected << %(<option value="#{i}"#{' selected="selected"' if i == Time.now.year}>#{i}</option>\n) }
expected << "</select>\n"
@@ -2069,7 +2069,7 @@ class DateHelperTest < ActionView::TestCase
start_year = Time.now.year - 5
end_year = Time.now.year + 5
- expected = %{<select id="post_written_on_1i" name="post[written_on(1i)]">\n}.dup
+ expected = +%{<select id="post_written_on_1i" name="post[written_on(1i)]">\n}
expected << "<option value=\"\"></option>\n"
start_year.upto(end_year) { |i| expected << %(<option value="#{i}">#{i}</option>\n) }
expected << "</select>\n"
@@ -2113,7 +2113,7 @@ class DateHelperTest < ActionView::TestCase
start_year = Time.now.year - 5
end_year = Time.now.year + 5
- expected = %{<select id="post_written_on_1i" name="post[written_on(1i)]">\n}.dup
+ expected = +%{<select id="post_written_on_1i" name="post[written_on(1i)]">\n}
expected << "<option value=\"\"></option>\n"
start_year.upto(end_year) { |i| expected << %(<option value="#{i}">#{i}</option>\n) }
expected << "</select>\n"
@@ -2145,7 +2145,7 @@ class DateHelperTest < ActionView::TestCase
@post = Post.new
@post.written_on = Date.new(2004, 6, 15)
- expected = %{<select id="post_written_on_1i" name="post[written_on(1i)]">\n}.dup
+ expected = +%{<select id="post_written_on_1i" name="post[written_on(1i)]">\n}
expected << %{<option value="1999">1999</option>\n<option value="2000">2000</option>\n<option value="2001">2001</option>\n<option value="2002">2002</option>\n<option value="2003">2003</option>\n<option value="2004" selected="selected">2004</option>\n<option value="2005">2005</option>\n<option value="2006">2006</option>\n<option value="2007">2007</option>\n<option value="2008">2008</option>\n<option value="2009">2009</option>\n}
expected << "</select>\n"
@@ -2164,7 +2164,7 @@ class DateHelperTest < ActionView::TestCase
@post = Post.new
@post.written_on = Date.new(2004, 6, 15)
- expected = %{<select id="post_written_on_1i" name="post[written_on(1i)]" class="selector">\n}.dup
+ expected = +%{<select id="post_written_on_1i" name="post[written_on(1i)]" class="selector">\n}
expected << %{<option value="1999">1999</option>\n<option value="2000">2000</option>\n<option value="2001">2001</option>\n<option value="2002">2002</option>\n<option value="2003">2003</option>\n<option value="2004" selected="selected">2004</option>\n<option value="2005">2005</option>\n<option value="2006">2006</option>\n<option value="2007">2007</option>\n<option value="2008">2008</option>\n<option value="2009">2009</option>\n}
expected << "</select>\n"
@@ -2188,7 +2188,7 @@ class DateHelperTest < ActionView::TestCase
concat f.date_select(:written_on, {}, { class: "selector" })
end
- expected = %{<select id="post_written_on_1i" name="post[written_on(1i)]" class="selector">\n}.dup
+ expected = +%{<select id="post_written_on_1i" name="post[written_on(1i)]" class="selector">\n}
expected << %{<option value="1999">1999</option>\n<option value="2000">2000</option>\n<option value="2001">2001</option>\n<option value="2002">2002</option>\n<option value="2003">2003</option>\n<option value="2004" selected="selected">2004</option>\n<option value="2005">2005</option>\n<option value="2006">2006</option>\n<option value="2007">2007</option>\n<option value="2008">2008</option>\n<option value="2009">2009</option>\n}
expected << "</select>\n"
@@ -2208,7 +2208,7 @@ class DateHelperTest < ActionView::TestCase
@post = Post.new
@post.written_on = Date.new(2004, 6, 15)
- expected = %{<select id="post_written_on_1i" name="post[written_on(1i)]">\n}.dup
+ expected = +%{<select id="post_written_on_1i" name="post[written_on(1i)]">\n}
expected << %{<option value="1999">1999</option>\n<option value="2000">2000</option>\n<option value="2001">2001</option>\n<option value="2002">2002</option>\n<option value="2003">2003</option>\n<option value="2004" selected="selected">2004</option>\n<option value="2005">2005</option>\n<option value="2006">2006</option>\n<option value="2007">2007</option>\n<option value="2008">2008</option>\n<option value="2009">2009</option>\n}
expected << "</select>\n"
@@ -2232,7 +2232,7 @@ class DateHelperTest < ActionView::TestCase
@post = Post.new
@post.written_on = Date.new(2004, 6, 15)
- expected = %{<select id="post_written_on_3i" name="post[written_on(3i)]">\n}.dup
+ expected = +%{<select id="post_written_on_3i" name="post[written_on(3i)]">\n}
expected << %{<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15" selected="selected">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n}
expected << "</select>\n"
@@ -2255,7 +2255,7 @@ class DateHelperTest < ActionView::TestCase
@post = Post.new
@post.written_on = Date.new(2004, 6, 15)
- expected = %{<select id="post_written_on_3i" name="post[written_on(3i)]">\n}.dup
+ expected = +%{<select id="post_written_on_3i" name="post[written_on(3i)]">\n}
expected << %{<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15" selected="selected">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n}
expected << "</select>\n"
@@ -2273,7 +2273,7 @@ class DateHelperTest < ActionView::TestCase
@post = Post.new
@post.written_on = Date.new(2004, 6, 15)
- expected = %{<select id="post_written_on_1i" name="post[written_on(1i)]">\n}.dup
+ expected = +%{<select id="post_written_on_1i" name="post[written_on(1i)]">\n}
expected << %{<option value="">Year</option>\n<option value="1999">1999</option>\n<option value="2000">2000</option>\n<option value="2001">2001</option>\n<option value="2002">2002</option>\n<option value="2003">2003</option>\n<option value="2004" selected="selected">2004</option>\n<option value="2005">2005</option>\n<option value="2006">2006</option>\n<option value="2007">2007</option>\n<option value="2008">2008</option>\n<option value="2009">2009</option>\n}
expected << "</select>\n"
@@ -2293,7 +2293,7 @@ class DateHelperTest < ActionView::TestCase
@post = Post.new
@post.written_on = Date.new(2004, 6, 15)
- expected = %{<select id="post_written_on_1i" name="post[written_on(1i)]">\n}.dup
+ expected = +%{<select id="post_written_on_1i" name="post[written_on(1i)]">\n}
expected << %{<option value="">Choose year</option>\n<option value="1999">1999</option>\n<option value="2000">2000</option>\n<option value="2001">2001</option>\n<option value="2002">2002</option>\n<option value="2003">2003</option>\n<option value="2004" selected="selected">2004</option>\n<option value="2005">2005</option>\n<option value="2006">2006</option>\n<option value="2007">2007</option>\n<option value="2008">2008</option>\n<option value="2009">2009</option>\n}
expected << "</select>\n"
@@ -2313,7 +2313,7 @@ class DateHelperTest < ActionView::TestCase
@post = Post.new
@post.written_on = Date.new(2004, 6, 15)
- expected = %{<select id="post_written_on_1i" name="post[written_on(1i)]" class="year">\n}.dup
+ expected = +%{<select id="post_written_on_1i" name="post[written_on(1i)]" class="year">\n}
expected << %{<option value="1999">1999</option>\n<option value="2000">2000</option>\n<option value="2001">2001</option>\n<option value="2002">2002</option>\n<option value="2003">2003</option>\n<option value="2004" selected="selected">2004</option>\n<option value="2005">2005</option>\n<option value="2006">2006</option>\n<option value="2007">2007</option>\n<option value="2008">2008</option>\n<option value="2009">2009</option>\n}
expected << "</select>\n"
@@ -2333,7 +2333,7 @@ class DateHelperTest < ActionView::TestCase
@post = Post.new
@post.written_on = Date.new(2004, 6, 15)
- expected = %{<select id="post_written_on_1i" name="post[written_on(1i)]" class="my-year">\n}.dup
+ expected = +%{<select id="post_written_on_1i" name="post[written_on(1i)]" class="my-year">\n}
expected << %{<option value="1999">1999</option>\n<option value="2000">2000</option>\n<option value="2001">2001</option>\n<option value="2002">2002</option>\n<option value="2003">2003</option>\n<option value="2004" selected="selected">2004</option>\n<option value="2005">2005</option>\n<option value="2006">2006</option>\n<option value="2007">2007</option>\n<option value="2008">2008</option>\n<option value="2009">2009</option>\n}
expected << "</select>\n"
@@ -2353,7 +2353,7 @@ class DateHelperTest < ActionView::TestCase
@post = Post.new
@post.written_on = Time.local(2004, 6, 15, 15, 16, 35)
- expected = %{<input type="hidden" id="post_written_on_1i" name="post[written_on(1i)]" value="2004" />\n}.dup
+ expected = +%{<input type="hidden" id="post_written_on_1i" name="post[written_on(1i)]" value="2004" />\n}
expected << %{<input type="hidden" id="post_written_on_2i" name="post[written_on(2i)]" value="6" />\n}
expected << %{<input type="hidden" id="post_written_on_3i" name="post[written_on(3i)]" value="15" />\n}
@@ -2372,7 +2372,7 @@ class DateHelperTest < ActionView::TestCase
@post = Post.new
@post.written_on = Time.local(2004, 6, 15, 15, 16, 35)
- expected = %{<input type="hidden" id="post_written_on_1i" name="post[written_on(1i)]" value="2004" />\n}.dup
+ expected = +%{<input type="hidden" id="post_written_on_1i" name="post[written_on(1i)]" value="2004" />\n}
expected << %{<input type="hidden" id="post_written_on_2i" name="post[written_on(2i)]" value="6" />\n}
expected << %{<input type="hidden" id="post_written_on_3i" name="post[written_on(3i)]" value="15" />\n}
@@ -2391,7 +2391,7 @@ class DateHelperTest < ActionView::TestCase
@post = Post.new
@post.written_on = Time.local(2004, 6, 15, 15, 16, 35)
- expected = %{<input type="hidden" id="post_written_on_1i" name="post[written_on(1i)]" value="1" />\n}.dup
+ expected = +%{<input type="hidden" id="post_written_on_1i" name="post[written_on(1i)]" value="1" />\n}
expected << %{<input type="hidden" id="post_written_on_2i" name="post[written_on(2i)]" value="1" />\n}
expected << %{<input type="hidden" id="post_written_on_3i" name="post[written_on(3i)]" value="1" />\n}
@@ -2410,7 +2410,7 @@ class DateHelperTest < ActionView::TestCase
@post = Post.new
@post.written_on = Time.local(2004, 6, 15, 15, 16, 35)
- expected = %(<select id="post_written_on_4i" name="post[written_on(4i)]">\n).dup
+ expected = +%(<select id="post_written_on_4i" name="post[written_on(4i)]">\n)
0.upto(23) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 15}>#{sprintf("%02d", i)}</option>\n) }
expected << "</select>\n"
expected << " : "
@@ -2425,7 +2425,7 @@ class DateHelperTest < ActionView::TestCase
@post = Post.new
@post.written_on = Time.local(2004, 6, 15, 15, 16, 35)
- expected = %{<input type="hidden" id="post_written_on_1i" name="post[written_on(1i)]" value="2004" />\n}.dup
+ expected = +%{<input type="hidden" id="post_written_on_1i" name="post[written_on(1i)]" value="2004" />\n}
expected << %{<input type="hidden" id="post_written_on_2i" name="post[written_on(2i)]" value="6" />\n}
expected << %{<input type="hidden" id="post_written_on_3i" name="post[written_on(3i)]" value="15" />\n}
@@ -2448,7 +2448,7 @@ class DateHelperTest < ActionView::TestCase
@post = Post.new
@post.written_on = Time.local(2004, 6, 15, 15, 16, 35)
- expected = %{<input type="hidden" id="post_written_on_1i" name="post[written_on(1i)]" value="2004" />\n}.dup
+ expected = +%{<input type="hidden" id="post_written_on_1i" name="post[written_on(1i)]" value="2004" />\n}
expected << %{<input type="hidden" id="post_written_on_2i" name="post[written_on(2i)]" value="6" />\n}
expected << %{<input type="hidden" id="post_written_on_3i" name="post[written_on(3i)]" value="15" />\n}
@@ -2471,7 +2471,7 @@ class DateHelperTest < ActionView::TestCase
concat f.time_select(:written_on, {}, { class: "selector" })
end
- expected = %{<input type="hidden" id="post_written_on_1i" name="post[written_on(1i)]" value="2004" />\n}.dup
+ expected = +%{<input type="hidden" id="post_written_on_1i" name="post[written_on(1i)]" value="2004" />\n}
expected << %{<input type="hidden" id="post_written_on_2i" name="post[written_on(2i)]" value="6" />\n}
expected << %{<input type="hidden" id="post_written_on_3i" name="post[written_on(3i)]" value="15" />\n}
@@ -2490,7 +2490,7 @@ class DateHelperTest < ActionView::TestCase
@post = Post.new
@post.written_on = Time.local(2004, 6, 15, 15, 16, 35)
- expected = %{<input type="hidden" id="post_written_on_1i" name="post[written_on(1i)]" value="2004" />\n}.dup
+ expected = +%{<input type="hidden" id="post_written_on_1i" name="post[written_on(1i)]" value="2004" />\n}
expected << %{<input type="hidden" id="post_written_on_2i" name="post[written_on(2i)]" value="6" />\n}
expected << %{<input type="hidden" id="post_written_on_3i" name="post[written_on(3i)]" value="15" />\n}
@@ -2517,7 +2517,7 @@ class DateHelperTest < ActionView::TestCase
@post = Post.new
@post.written_on = Time.local(2004, 6, 15, 15, 16, 35)
- expected = %{<input type="hidden" id="post_written_on_1i" name="post[written_on(1i)]" value="2004" />\n}.dup
+ expected = +%{<input type="hidden" id="post_written_on_1i" name="post[written_on(1i)]" value="2004" />\n}
expected << %{<input type="hidden" id="post_written_on_2i" name="post[written_on(2i)]" value="6" />\n}
expected << %{<input type="hidden" id="post_written_on_3i" name="post[written_on(3i)]" value="15" />\n}
@@ -2538,7 +2538,7 @@ class DateHelperTest < ActionView::TestCase
@post = Post.new
@post.written_on = Time.local(2004, 6, 15, 15, 16, 35)
- expected = %{<input type="hidden" id="post_written_on_1i" name="post[written_on(1i)]" value="2004" />\n}.dup
+ expected = +%{<input type="hidden" id="post_written_on_1i" name="post[written_on(1i)]" value="2004" />\n}
expected << %{<input type="hidden" id="post_written_on_2i" name="post[written_on(2i)]" value="6" />\n}
expected << %{<input type="hidden" id="post_written_on_3i" name="post[written_on(3i)]" value="15" />\n}
@@ -2559,7 +2559,7 @@ class DateHelperTest < ActionView::TestCase
@post = Post.new
@post.written_on = Time.local(2004, 6, 15, 15, 16, 35)
- expected = %{<input type="hidden" id="post_written_on_1i" name="post[written_on(1i)]" value="2004" />\n}.dup
+ expected = +%{<input type="hidden" id="post_written_on_1i" name="post[written_on(1i)]" value="2004" />\n}
expected << %{<input type="hidden" id="post_written_on_2i" name="post[written_on(2i)]" value="6" />\n}
expected << %{<input type="hidden" id="post_written_on_3i" name="post[written_on(3i)]" value="15" />\n}
@@ -2580,7 +2580,7 @@ class DateHelperTest < ActionView::TestCase
@post = Post.new
@post.written_on = Time.local(2004, 6, 15, 15, 16, 35)
- expected = %{<input type="hidden" id="post_written_on_1i" name="post[written_on(1i)]" value="2004" />\n}.dup
+ expected = +%{<input type="hidden" id="post_written_on_1i" name="post[written_on(1i)]" value="2004" />\n}
expected << %{<input type="hidden" id="post_written_on_2i" name="post[written_on(2i)]" value="6" />\n}
expected << %{<input type="hidden" id="post_written_on_3i" name="post[written_on(3i)]" value="15" />\n}
@@ -2601,7 +2601,7 @@ class DateHelperTest < ActionView::TestCase
@post = Post.new
@post.written_on = Time.local(2004, 6, 15, 15, 16, 35)
- expected = %{<input type="hidden" id="post_written_on_1i" disabled="disabled" name="post[written_on(1i)]" value="2004" />\n}.dup
+ expected = +%{<input type="hidden" id="post_written_on_1i" disabled="disabled" name="post[written_on(1i)]" value="2004" />\n}
expected << %{<input type="hidden" id="post_written_on_2i" disabled="disabled" name="post[written_on(2i)]" value="6" />\n}
expected << %{<input type="hidden" id="post_written_on_3i" disabled="disabled" name="post[written_on(3i)]" value="15" />\n}
@@ -2620,7 +2620,7 @@ class DateHelperTest < ActionView::TestCase
@post = Post.new
@post.updated_at = Time.local(2004, 6, 15, 16, 35)
- expected = %{<select id="post_updated_at_1i" name="post[updated_at(1i)]">\n}.dup
+ expected = +%{<select id="post_updated_at_1i" name="post[updated_at(1i)]">\n}
expected << %{<option value="1999">1999</option>\n<option value="2000">2000</option>\n<option value="2001">2001</option>\n<option value="2002">2002</option>\n<option value="2003">2003</option>\n<option value="2004" selected="selected">2004</option>\n<option value="2005">2005</option>\n<option value="2006">2006</option>\n<option value="2007">2007</option>\n<option value="2008">2008</option>\n<option value="2009">2009</option>\n}
expected << "</select>\n"
@@ -2649,7 +2649,7 @@ class DateHelperTest < ActionView::TestCase
@post = Post.new
@post.updated_at = Time.local(2004, 6, 15, 16, 35)
- expected = %{<select id="post_updated_at_1i" name="post[updated_at(1i)]">\n}.dup
+ expected = +%{<select id="post_updated_at_1i" name="post[updated_at(1i)]">\n}
expected << %{<option value="1999">1999</option>\n<option value="2000">2000</option>\n<option value="2001">2001</option>\n<option value="2002">2002</option>\n<option value="2003">2003</option>\n<option value="2004" selected="selected">2004</option>\n<option value="2005">2005</option>\n<option value="2006">2006</option>\n<option value="2007">2007</option>\n<option value="2008">2008</option>\n<option value="2009">2009</option>\n}
expected << "</select>\n"
@@ -2715,7 +2715,7 @@ class DateHelperTest < ActionView::TestCase
@post = Post.new
- expected = %{<select id="post_updated_at_1i" name="post[updated_at(1i)]">\n}.dup
+ expected = +%{<select id="post_updated_at_1i" name="post[updated_at(1i)]">\n}
expected << %{<option value="1999">1999</option>\n<option value="2000">2000</option>\n<option value="2001">2001</option>\n<option value="2002">2002</option>\n<option value="2003">2003</option>\n<option value="2004" selected="selected">2004</option>\n<option value="2005">2005</option>\n<option value="2006">2006</option>\n<option value="2007">2007</option>\n<option value="2008">2008</option>\n<option value="2009">2009</option>\n}
expected << "</select>\n"
@@ -2750,7 +2750,7 @@ class DateHelperTest < ActionView::TestCase
concat f.datetime_select(:updated_at, {}, { class: "selector" })
end
- expected = %{<select id="post_updated_at_1i" name="post[updated_at(1i)]" class="selector">\n<option value="1999">1999</option>\n<option value="2000">2000</option>\n<option value="2001">2001</option>\n<option value="2002">2002</option>\n<option value="2003">2003</option>\n<option selected="selected" value="2004">2004</option>\n<option value="2005">2005</option>\n<option value="2006">2006</option>\n<option value="2007">2007</option>\n<option value="2008">2008</option>\n<option value="2009">2009</option>\n</select>\n}.dup
+ expected = +%{<select id="post_updated_at_1i" name="post[updated_at(1i)]" class="selector">\n<option value="1999">1999</option>\n<option value="2000">2000</option>\n<option value="2001">2001</option>\n<option value="2002">2002</option>\n<option value="2003">2003</option>\n<option selected="selected" value="2004">2004</option>\n<option value="2005">2005</option>\n<option value="2006">2006</option>\n<option value="2007">2007</option>\n<option value="2008">2008</option>\n<option value="2009">2009</option>\n</select>\n}
expected << %{<select id="post_updated_at_2i" name="post[updated_at(2i)]" class="selector">\n<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option selected="selected" value="6">June</option>\n<option value="7">July</option>\n<option value="8">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n</select>\n}
expected << %{<select id="post_updated_at_3i" name="post[updated_at(3i)]" class="selector">\n<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option selected="selected" value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n</select>\n}
expected << %{ &mdash; <select id="post_updated_at_4i" name="post[updated_at(4i)]" class="selector">\n<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option selected="selected" value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n</select>\n}
@@ -2763,7 +2763,7 @@ class DateHelperTest < ActionView::TestCase
@post = Post.new
@post.updated_at = Time.local(2004, 6, 15, 15, 16, 35)
- expected = %{<select id="post_updated_at_1i" name="post[updated_at(1i)]">\n}.dup
+ expected = +%{<select id="post_updated_at_1i" name="post[updated_at(1i)]">\n}
expected << %{<option value="1999">1999</option>\n<option value="2000">2000</option>\n<option value="2001">2001</option>\n<option value="2002">2002</option>\n<option value="2003">2003</option>\n<option value="2004" selected="selected">2004</option>\n<option value="2005">2005</option>\n<option value="2006">2006</option>\n<option value="2007">2007</option>\n<option value="2008">2008</option>\n<option value="2009">2009</option>\n}
expected << "</select>\n"
@@ -2816,7 +2816,7 @@ class DateHelperTest < ActionView::TestCase
@post = Post.new
@post.updated_at = nil
- expected = %{<select id="post_updated_at_1i" name="post[updated_at(1i)]">\n}.dup
+ expected = +%{<select id="post_updated_at_1i" name="post[updated_at(1i)]">\n}
expected << %{<option value="">Year</option>\n<option value="1999">1999</option>\n<option value="2000">2000</option>\n<option value="2001">2001</option>\n<option value="2002">2002</option>\n<option value="2003">2003</option>\n<option value="2004">2004</option>\n<option value="2005">2005</option>\n<option value="2006">2006</option>\n<option value="2007">2007</option>\n<option value="2008">2008</option>\n<option value="2009">2009</option>\n}
expected << "</select>\n"
@@ -2845,7 +2845,7 @@ class DateHelperTest < ActionView::TestCase
@post = Post.new
@post.updated_at = nil
- expected = %{<select id="post_updated_at_1i" name="post[updated_at(1i)]">\n}.dup
+ expected = +%{<select id="post_updated_at_1i" name="post[updated_at(1i)]">\n}
expected << %{<option value="">Choose year</option>\n<option value="1999">1999</option>\n<option value="2000">2000</option>\n<option value="2001">2001</option>\n<option value="2002">2002</option>\n<option value="2003">2003</option>\n<option value="2004">2004</option>\n<option value="2005">2005</option>\n<option value="2006">2006</option>\n<option value="2007">2007</option>\n<option value="2008">2008</option>\n<option value="2009">2009</option>\n}
expected << "</select>\n"
@@ -2874,7 +2874,7 @@ class DateHelperTest < ActionView::TestCase
@post = Post.new
@post.written_on = Time.local(2004, 6, 15, 15, 16, 35)
- expected = %{<select id="post_written_on_1i" name="post[written_on(1i)]" class="year">\n}.dup
+ expected = +%{<select id="post_written_on_1i" name="post[written_on(1i)]" class="year">\n}
expected << %{<option value="1999">1999</option>\n<option value="2000">2000</option>\n<option value="2001">2001</option>\n<option value="2002">2002</option>\n<option value="2003">2003</option>\n<option value="2004" selected="selected">2004</option>\n<option value="2005">2005</option>\n<option value="2006">2006</option>\n<option value="2007">2007</option>\n<option value="2008">2008</option>\n<option value="2009">2009</option>\n}
expected << "</select>\n"
@@ -2903,7 +2903,7 @@ class DateHelperTest < ActionView::TestCase
@post = Post.new
@post.written_on = Time.local(2004, 6, 15, 15, 16, 35)
- expected = %{<select id="post_written_on_1i" name="post[written_on(1i)]" class="my-year">\n}.dup
+ expected = +%{<select id="post_written_on_1i" name="post[written_on(1i)]" class="my-year">\n}
expected << %{<option value="1999">1999</option>\n<option value="2000">2000</option>\n<option value="2001">2001</option>\n<option value="2002">2002</option>\n<option value="2003">2003</option>\n<option value="2004" selected="selected">2004</option>\n<option value="2005">2005</option>\n<option value="2006">2006</option>\n<option value="2007">2007</option>\n<option value="2008">2008</option>\n<option value="2009">2009</option>\n}
expected << "</select>\n"
@@ -2929,7 +2929,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_date_select_with_zero_value_and_no_start_year
- expected = %(<select id="date_first_year" name="date[first][year]">\n).dup
+ expected = +%(<select id="date_first_year" name="date[first][year]">\n)
(Date.today.year - 5).upto(Date.today.year + 1) { |y| expected << %(<option value="#{y}">#{y}</option>\n) }
expected << "</select>\n"
@@ -2945,7 +2945,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_date_select_with_zero_value_and_no_end_year
- expected = %(<select id="date_first_year" name="date[first][year]">\n).dup
+ expected = +%(<select id="date_first_year" name="date[first][year]">\n)
last_year = Time.now.year + 5
2003.upto(last_year) { |y| expected << %(<option value="#{y}">#{y}</option>\n) }
expected << "</select>\n"
@@ -2962,7 +2962,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_date_select_with_zero_value_and_no_start_and_end_year
- expected = %(<select id="date_first_year" name="date[first][year]">\n).dup
+ expected = +%(<select id="date_first_year" name="date[first][year]">\n)
(Date.today.year - 5).upto(Date.today.year + 5) { |y| expected << %(<option value="#{y}">#{y}</option>\n) }
expected << "</select>\n"
@@ -2978,7 +2978,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_date_select_with_nil_value_and_no_start_and_end_year
- expected = %(<select id="date_first_year" name="date[first][year]">\n).dup
+ expected = +%(<select id="date_first_year" name="date[first][year]">\n)
(Date.today.year - 5).upto(Date.today.year + 5) { |y| expected << %(<option value="#{y}">#{y}</option>\n) }
expected << "</select>\n"
@@ -2994,7 +2994,7 @@ class DateHelperTest < ActionView::TestCase
end
def test_datetime_select_with_nil_value_and_no_start_and_end_year
- expected = %(<select id="date_first_year" name="date[first][year]">\n).dup
+ expected = +%(<select id="date_first_year" name="date[first][year]">\n)
(Date.today.year - 5).upto(Date.today.year + 5) { |y| expected << %(<option value="#{y}">#{y}</option>\n) }
expected << "</select>\n"
@@ -3026,7 +3026,7 @@ class DateHelperTest < ActionView::TestCase
@post.updated_at = Time.local(2004, 6, 15, 16, 35)
id = 456
- expected = %{<select id="post_456_updated_at_1i" name="post[#{id}][updated_at(1i)]">\n}.dup
+ expected = +%{<select id="post_456_updated_at_1i" name="post[#{id}][updated_at(1i)]">\n}
expected << %{<option value="1999">1999</option>\n<option value="2000">2000</option>\n<option value="2001">2001</option>\n<option value="2002">2002</option>\n<option value="2003">2003</option>\n<option value="2004" selected="selected">2004</option>\n<option value="2005">2005</option>\n<option value="2006">2006</option>\n<option value="2007">2007</option>\n<option value="2008">2008</option>\n<option value="2009">2009</option>\n}
expected << "</select>\n"
@@ -3060,7 +3060,7 @@ class DateHelperTest < ActionView::TestCase
concat f.datetime_select(:updated_at)
end
- expected = %{<select id="post_456_updated_at_1i" name="post[#{id}][updated_at(1i)]">\n}.dup
+ expected = +%{<select id="post_456_updated_at_1i" name="post[#{id}][updated_at(1i)]">\n}
expected << %{<option value="1999">1999</option>\n<option value="2000">2000</option>\n<option value="2001">2001</option>\n<option value="2002">2002</option>\n<option value="2003">2003</option>\n<option value="2004" selected="selected">2004</option>\n<option value="2005">2005</option>\n<option value="2006">2006</option>\n<option value="2007">2007</option>\n<option value="2008">2008</option>\n<option value="2009">2009</option>\n}
expected << "</select>\n"
@@ -3090,7 +3090,7 @@ class DateHelperTest < ActionView::TestCase
@post.updated_at = Time.local(2004, 6, 15, 16, 35)
id = @post.id
- expected = %{<select id="post_123_updated_at_1i" name="post[#{id}][updated_at(1i)]">\n}.dup
+ expected = +%{<select id="post_123_updated_at_1i" name="post[#{id}][updated_at(1i)]">\n}
expected << %{<option value="1999">1999</option>\n<option value="2000">2000</option>\n<option value="2001">2001</option>\n<option value="2002">2002</option>\n<option value="2003">2003</option>\n<option value="2004" selected="selected">2004</option>\n<option value="2005">2005</option>\n<option value="2006">2006</option>\n<option value="2007">2007</option>\n<option value="2008">2008</option>\n<option value="2009">2009</option>\n}
expected << "</select>\n"
@@ -3119,7 +3119,7 @@ class DateHelperTest < ActionView::TestCase
@post = Post.new
@post.updated_at = Time.local(2004, 6, 15, 15, 16, 35)
- expected = %{<select id="post_updated_at_1i" name="post[updated_at(1i)]">\n}.dup
+ expected = +%{<select id="post_updated_at_1i" name="post[updated_at(1i)]">\n}
1999.upto(2009) { |i| expected << %(<option value="#{i}"#{' selected="selected"' if i == 2004}>#{i}</option>\n) }
expected << "</select>\n"
expected << %{<select id="post_updated_at_2i" name="post[updated_at(2i)]">\n}
@@ -3150,7 +3150,7 @@ class DateHelperTest < ActionView::TestCase
@post = Post.new
@post.updated_at = Time.local(2004, 6, 15, 15, 16, 35)
- expected = %{<input type="hidden" id="post_updated_at_1i" name="post[updated_at(1i)]" value="2004" />\n}.dup
+ expected = +%{<input type="hidden" id="post_updated_at_1i" name="post[updated_at(1i)]" value="2004" />\n}
expected << %{<select id="post_updated_at_2i" name="post[updated_at(2i)]">\n}
1.upto(12) { |i| expected << %(<option value="#{i}"#{' selected="selected"' if i == 6}>#{Date::MONTHNAMES[i]}</option>\n) }
expected << "</select>\n"
@@ -3175,7 +3175,7 @@ class DateHelperTest < ActionView::TestCase
@post = Post.new
@post.updated_at = Time.local(2004, 6, 15, 15, 16, 35)
- expected = %{<select id="post_updated_at_1i" name="post[updated_at(1i)]">\n}.dup
+ expected = +%{<select id="post_updated_at_1i" name="post[updated_at(1i)]">\n}
1999.upto(2009) { |i| expected << %(<option value="#{i}"#{' selected="selected"' if i == 2004}>#{i}</option>\n) }
expected << "</select>\n"
expected << %{<input type="hidden" id="post_updated_at_2i" name="post[updated_at(2i)]" value="6" />\n}
@@ -3198,7 +3198,7 @@ class DateHelperTest < ActionView::TestCase
@post = Post.new
@post.updated_at = Time.local(2004, 6, 15, 15, 16, 35)
- expected = %{<input type="hidden" id="post_updated_at_1i" name="post[updated_at(1i)]" value="2004" />\n}.dup
+ expected = +%{<input type="hidden" id="post_updated_at_1i" name="post[updated_at(1i)]" value="2004" />\n}
expected << %{<input type="hidden" id="post_updated_at_2i" name="post[updated_at(2i)]" value="6" />\n}
expected << %{<input type="hidden" id="post_updated_at_3i" name="post[updated_at(3i)]" value="1" />\n}
@@ -3217,7 +3217,7 @@ class DateHelperTest < ActionView::TestCase
@post = Post.new
@post.updated_at = Time.local(2004, 6, 15, 15, 16, 35)
- expected = %{<input type="hidden" id="post_updated_at_1i" disabled="disabled" name="post[updated_at(1i)]" value="2004" />\n}.dup
+ expected = +%{<input type="hidden" id="post_updated_at_1i" disabled="disabled" name="post[updated_at(1i)]" value="2004" />\n}
expected << %{<input type="hidden" id="post_updated_at_2i" disabled="disabled" name="post[updated_at(2i)]" value="6" />\n}
expected << %{<input type="hidden" id="post_updated_at_3i" disabled="disabled" name="post[updated_at(3i)]" value="1" />\n}
@@ -3236,7 +3236,7 @@ class DateHelperTest < ActionView::TestCase
@post = Post.new
@post.updated_at = Time.local(2004, 6, 15, 15, 16, 35)
- expected = %{<select id="post_updated_at_1i" name="post[updated_at(1i)]">\n}.dup
+ expected = +%{<select id="post_updated_at_1i" name="post[updated_at(1i)]">\n}
1999.upto(2009) { |i| expected << %(<option value="#{i}"#{' selected="selected"' if i == 2004}>#{i}</option>\n) }
expected << "</select>\n"
expected << %{<select id="post_updated_at_2i" name="post[updated_at(2i)]">\n}
@@ -3253,7 +3253,7 @@ class DateHelperTest < ActionView::TestCase
@post = Post.new
@post.updated_at = Time.local(2004, 6, 15, 15, 16, 35)
- expected = %{<select id="post_updated_at_1i" name="post[updated_at(1i)]">\n}.dup
+ expected = +%{<select id="post_updated_at_1i" name="post[updated_at(1i)]">\n}
1999.upto(2009) { |i| expected << %(<option value="#{i}"#{' selected="selected"' if i == 2004}>#{i}</option>\n) }
expected << "</select>\n"
expected << %{<select id="post_updated_at_2i" name="post[updated_at(2i)]">\n}
@@ -3277,7 +3277,7 @@ class DateHelperTest < ActionView::TestCase
@post = Post.new
@post.updated_at = Time.local(2004, 6, 15, 15, 16, 35)
- expected = %{<select id="post_updated_at_1i" disabled="disabled" name="post[updated_at(1i)]">\n}.dup
+ expected = +%{<select id="post_updated_at_1i" disabled="disabled" name="post[updated_at(1i)]">\n}
1999.upto(2009) { |i| expected << %(<option value="#{i}"#{' selected="selected"' if i == 2004}>#{i}</option>\n) }
expected << "</select>\n"
expected << %{<select id="post_updated_at_2i" disabled="disabled" name="post[updated_at(2i)]">\n}
@@ -3301,7 +3301,7 @@ class DateHelperTest < ActionView::TestCase
@post = Post.new
@post.updated_at = Time.local(2004, 6, 15, 15, 16, 35)
- expected = %{<select id="post_updated_at_3i" name="post[updated_at(3i)]">\n}.dup
+ expected = +%{<select id="post_updated_at_3i" name="post[updated_at(3i)]">\n}
1.upto(31) { |i| expected << %(<option value="#{i}"#{' selected="selected"' if i == 15}>#{i}</option>\n) }
expected << "</select>\n"
expected << %{<select id="post_updated_at_2i" name="post[updated_at(2i)]">\n}
@@ -3328,7 +3328,7 @@ class DateHelperTest < ActionView::TestCase
@post = Post.new
@post.updated_at = Time.local(2004, 6, 15, 15, 16, 35)
- expected = %{<input type="hidden" id="post_updated_at_1i" name="post[updated_at(1i)]" value="2004" />\n}.dup
+ expected = +%{<input type="hidden" id="post_updated_at_1i" name="post[updated_at(1i)]" value="2004" />\n}
expected << %{<select id="post_updated_at_3i" name="post[updated_at(3i)]">\n}
1.upto(31) { |i| expected << %(<option value="#{i}"#{' selected="selected"' if i == 15}>#{i}</option>\n) }
expected << "</select>\n"
@@ -3353,7 +3353,7 @@ class DateHelperTest < ActionView::TestCase
@post = Post.new
@post.updated_at = nil
- expected = %{<select id="post_updated_at_1i" name="post[updated_at(1i)]">\n}.dup
+ expected = +%{<select id="post_updated_at_1i" name="post[updated_at(1i)]">\n}
2001.upto(2011) { |i| expected << %(<option value="#{i}"#{' selected="selected"' if i == 2006}>#{i}</option>\n) }
expected << "</select>\n"
expected << %{<select id="post_updated_at_2i" name="post[updated_at(2i)]">\n}
@@ -3380,7 +3380,7 @@ class DateHelperTest < ActionView::TestCase
@post = Post.new
@post.updated_at = nil
- expected = %{<select id="post_updated_at_1i" name="post[updated_at(1i)]">\n}.dup
+ expected = +%{<select id="post_updated_at_1i" name="post[updated_at(1i)]">\n}
expected << %(<option value=""></option>\n)
(Time.now.year - 5).upto(Time.now.year + 5) { |i| expected << %(<option value="#{i}">#{i}</option>\n) }
expected << "</select>\n"
@@ -3400,7 +3400,7 @@ class DateHelperTest < ActionView::TestCase
@post = Post.new
@post.updated_at = nil
- expected = %{<select id="post_updated_at_1i" name="post[updated_at(1i)]">\n}.dup
+ expected = +%{<select id="post_updated_at_1i" name="post[updated_at(1i)]">\n}
(Time.now.year - 5).upto(Time.now.year + 5) { |i| expected << %(<option value="#{i}"#{' selected="selected"' if i == Time.now.year}>#{i}</option>\n) }
expected << "</select>\n"
expected << %{<select id="post_updated_at_2i" name="post[updated_at(2i)]">\n}
@@ -3427,7 +3427,7 @@ class DateHelperTest < ActionView::TestCase
@post = Post.new
@post.updated_at = Time.local(2004, 6, 15, 16, 35)
- expected = %{<select id="post_updated_at_1i" name="post[updated_at(1i)]" class="selector">\n}.dup
+ expected = +%{<select id="post_updated_at_1i" name="post[updated_at(1i)]" class="selector">\n}
expected << %{<option value="1999">1999</option>\n<option value="2000">2000</option>\n<option value="2001">2001</option>\n<option value="2002">2002</option>\n<option value="2003">2003</option>\n<option value="2004" selected="selected">2004</option>\n<option value="2005">2005</option>\n<option value="2006">2006</option>\n<option value="2007">2007</option>\n<option value="2008">2008</option>\n<option value="2009">2009</option>\n}
expected << "</select>\n"
diff --git a/actionview/test/template/erb/form_for_test.rb b/actionview/test/template/erb/form_for_test.rb
index b6ecf003a5..b3a47e17a4 100644
--- a/actionview/test/template/erb/form_for_test.rb
+++ b/actionview/test/template/erb/form_for_test.rb
@@ -6,7 +6,11 @@ require "template/erb/helper"
module ERBTest
class TagHelperTest < BlockTestCase
test "form_for works" do
- output = render_content "form_for(:staticpage, :url => {:controller => 'blah', :action => 'update'})", ""
+ routes = ActionDispatch::Routing::RouteSet.new
+ routes.draw do
+ get "/blah/update", to: "blah#update"
+ end
+ output = render_content "form_for(:staticpage, :url => {:controller => 'blah', :action => 'update'})", "", routes
assert_match %r{<form.*action="/blah/update".*method="post">.*</form>}, output
end
end
diff --git a/actionview/test/template/erb/helper.rb b/actionview/test/template/erb/helper.rb
index 57d6cb1be3..727cc3dcf2 100644
--- a/actionview/test/template/erb/helper.rb
+++ b/actionview/test/template/erb/helper.rb
@@ -3,7 +3,6 @@
module ERBTest
class ViewContext
include ActionView::Helpers::UrlHelper
- include SharedTestRoutes.url_helpers
include ActionView::Helpers::TagHelper
include ActionView::Helpers::JavaScriptHelper
include ActionView::Helpers::FormHelper
@@ -14,9 +13,15 @@ module ERBTest
end
class BlockTestCase < ActiveSupport::TestCase
- def render_content(start, inside)
+ def render_content(start, inside, routes = nil)
+ routes ||= ActionDispatch::Routing::RouteSet.new.tap do |rs|
+ rs.draw { }
+ end
+ context = Class.new(ViewContext) {
+ include routes.url_helpers
+ }.new
template = block_helper(start, inside)
- ActionView::Template::Handlers::ERB.erb_implementation.new(template).evaluate(ViewContext.new)
+ ActionView::Template::Handlers::ERB.erb_implementation.new(template).evaluate(context)
end
def block_helper(str, rest)
diff --git a/actionview/test/template/form_helper/form_with_test.rb b/actionview/test/template/form_helper/form_with_test.rb
index c30176349c..f84c9b2b73 100644
--- a/actionview/test/template/form_helper/form_with_test.rb
+++ b/actionview/test/template/form_helper/form_with_test.rb
@@ -37,7 +37,7 @@ class FormWithActsLikeFormTagTest < FormWithTest
method = options[:method]
skip_enforcing_utf8 = options.fetch(:skip_enforcing_utf8, false)
- "".dup.tap do |txt|
+ (+"").tap do |txt|
unless skip_enforcing_utf8
txt << %{<input name="utf8" type="hidden" value="&#x2713;" />}
end
@@ -53,7 +53,7 @@ class FormWithActsLikeFormTagTest < FormWithTest
method = method.to_s == "get" ? "get" : "post"
- txt = %{<form accept-charset="UTF-8" action="#{action}"}.dup
+ txt = +%{<form accept-charset="UTF-8" action="#{action}"}
txt << %{ enctype="multipart/form-data"} if enctype
txt << %{ data-remote="true"} unless local
txt << %{ class="#{html_class}"} if html_class
@@ -290,6 +290,7 @@ class FormWithActsLikeFormForTest < FormWithTest
@post_delegator.title = "Hello World"
@car = Car.new("#000FFF")
+ @controller.singleton_class.include Routes.url_helpers
end
Routes = ActionDispatch::Routing::RouteSet.new
@@ -308,10 +309,6 @@ class FormWithActsLikeFormForTest < FormWithTest
root to: "main#index"
end
- def _routes
- Routes
- end
-
include Routes.url_helpers
def url_for(object)
@@ -450,13 +447,15 @@ class FormWithActsLikeFormForTest < FormWithTest
def test_form_with_doesnt_call_private_or_protected_properties_on_form_object_skipping_value
obj = Class.new do
- private def private_property
- "That would be great."
- end
+ private
+ def private_property
+ "That would be great."
+ end
- protected def protected_property
- "I believe you have my stapler."
- end
+ protected
+ def protected_property
+ "I believe you have my stapler."
+ end
end.new
form_with(model: obj, scope: "other_name", url: "/", id: "edit-other-name") do |f|
@@ -2247,7 +2246,7 @@ class FormWithActsLikeFormForTest < FormWithTest
post.persisted = false
def post.to_key; nil; end
- form_with(model: post) {}
+ form_with(model: post) { }
expected = whole_form("/posts")
assert_dom_equal expected, output_buffer
@@ -2255,14 +2254,14 @@ class FormWithActsLikeFormForTest < FormWithTest
def test_form_with_with_existing_object_in_list
@comment.save
- form_with(model: [@post, @comment]) {}
+ form_with(model: [@post, @comment]) { }
expected = whole_form(post_comment_path(@post, @comment), method: "patch")
assert_dom_equal expected, output_buffer
end
def test_form_with_with_new_object_in_list
- form_with(model: [@post, @comment]) {}
+ form_with(model: [@post, @comment]) { }
expected = whole_form(post_comments_path(@post))
assert_dom_equal expected, output_buffer
@@ -2270,14 +2269,14 @@ class FormWithActsLikeFormForTest < FormWithTest
def test_form_with_with_existing_object_and_namespace_in_list
@comment.save
- form_with(model: [:admin, @post, @comment]) {}
+ form_with(model: [:admin, @post, @comment]) { }
expected = whole_form(admin_post_comment_path(@post, @comment), method: "patch")
assert_dom_equal expected, output_buffer
end
def test_form_with_with_new_object_and_namespace_in_list
- form_with(model: [:admin, @post, @comment]) {}
+ form_with(model: [:admin, @post, @comment]) { }
expected = whole_form(admin_post_comments_path(@post))
assert_dom_equal expected, output_buffer
@@ -2291,13 +2290,13 @@ class FormWithActsLikeFormForTest < FormWithTest
end
def test_form_with_with_default_method_as_patch
- form_with(model: @post) {}
+ form_with(model: @post) { }
expected = whole_form("/posts/123", method: "patch")
assert_dom_equal expected, output_buffer
end
def test_form_with_with_data_attributes
- form_with(model: @post, data: { behavior: "stuff" }) {}
+ form_with(model: @post, data: { behavior: "stuff" }) { }
assert_match %r|data-behavior="stuff"|, output_buffer
assert_match %r|data-remote="true"|, output_buffer
end
@@ -2316,7 +2315,7 @@ class FormWithActsLikeFormForTest < FormWithTest
end
end
- form_with(model: @post, builder: builder_class) {}
+ form_with(model: @post, builder: builder_class) { }
assert_equal 1, initialization_count, "form builder instantiated more than once"
end
@@ -2325,9 +2324,9 @@ class FormWithActsLikeFormForTest < FormWithTest
method = options[:method]
if options.fetch(:skip_enforcing_utf8, false)
- txt = "".dup
+ txt = +""
else
- txt = %{<input name="utf8" type="hidden" value="&#x2713;" />}.dup
+ txt = +%{<input name="utf8" type="hidden" value="&#x2713;" />}
end
if method && !%w(get post).include?(method.to_s)
@@ -2338,7 +2337,7 @@ class FormWithActsLikeFormForTest < FormWithTest
end
def form_text(action = "/", id = nil, html_class = nil, local = nil, multipart = nil, method = nil)
- txt = %{<form accept-charset="UTF-8" action="#{action}"}.dup
+ txt = +%{<form accept-charset="UTF-8" action="#{action}"}
txt << %{ enctype="multipart/form-data"} if multipart
txt << %{ data-remote="true"} unless local
txt << %{ class="#{html_class}"} if html_class
diff --git a/actionview/test/template/form_helper_test.rb b/actionview/test/template/form_helper_test.rb
index 38bb1e1d24..5972946074 100644
--- a/actionview/test/template/form_helper_test.rb
+++ b/actionview/test/template/form_helper_test.rb
@@ -140,6 +140,7 @@ class FormHelperTest < ActionView::TestCase
@post_delegator.title = "Hello World"
@car = Car.new("#000FFF")
+ @controller.singleton_class.include Routes.url_helpers
end
Routes = ActionDispatch::Routing::RouteSet.new
@@ -3487,14 +3488,14 @@ class FormHelperTest < ActionView::TestCase
def test_form_for_with_existing_object_in_list
@comment.save
- form_for([@post, @comment]) {}
+ form_for([@post, @comment]) { }
expected = whole_form(post_comment_path(@post, @comment), "edit_comment_1", "edit_comment", method: "patch")
assert_dom_equal expected, output_buffer
end
def test_form_for_with_new_object_in_list
- form_for([@post, @comment]) {}
+ form_for([@post, @comment]) { }
expected = whole_form(post_comments_path(@post), "new_comment", "new_comment")
assert_dom_equal expected, output_buffer
@@ -3502,14 +3503,14 @@ class FormHelperTest < ActionView::TestCase
def test_form_for_with_existing_object_and_namespace_in_list
@comment.save
- form_for([:admin, @post, @comment]) {}
+ form_for([:admin, @post, @comment]) { }
expected = whole_form(admin_post_comment_path(@post, @comment), "edit_comment_1", "edit_comment", method: "patch")
assert_dom_equal expected, output_buffer
end
def test_form_for_with_new_object_and_namespace_in_list
- form_for([:admin, @post, @comment]) {}
+ form_for([:admin, @post, @comment]) { }
expected = whole_form(admin_post_comments_path(@post), "new_comment", "new_comment")
assert_dom_equal expected, output_buffer
@@ -3523,13 +3524,13 @@ class FormHelperTest < ActionView::TestCase
end
def test_form_for_with_default_method_as_patch
- form_for(@post) {}
+ form_for(@post) { }
expected = whole_form("/posts/123", "edit_post_123", "edit_post", method: "patch")
assert_dom_equal expected, output_buffer
end
def test_form_for_with_data_attributes
- form_for(@post, data: { behavior: "stuff" }, remote: true) {}
+ form_for(@post, data: { behavior: "stuff" }, remote: true) { }
assert_match %r|data-behavior="stuff"|, output_buffer
assert_match %r|data-remote="true"|, output_buffer
end
@@ -3548,7 +3549,7 @@ class FormHelperTest < ActionView::TestCase
end
end
- form_for(@post, builder: builder_class) {}
+ form_for(@post, builder: builder_class) { }
assert_equal 1, initialization_count, "form builder instantiated more than once"
end
@@ -3558,9 +3559,9 @@ class FormHelperTest < ActionView::TestCase
method = options[:method]
if options.fetch(:enforce_utf8, true)
- txt = %{<input name="utf8" type="hidden" value="&#x2713;" />}.dup
+ txt = +%{<input name="utf8" type="hidden" value="&#x2713;" />}
else
- txt = "".dup
+ txt = +""
end
if method && !%w(get post).include?(method.to_s)
@@ -3571,7 +3572,7 @@ class FormHelperTest < ActionView::TestCase
end
def form_text(action = "/", id = nil, html_class = nil, remote = nil, multipart = nil, method = nil)
- txt = %{<form accept-charset="UTF-8" action="#{action}"}.dup
+ txt = +%{<form accept-charset="UTF-8" action="#{action}"}
txt << %{ enctype="multipart/form-data"} if multipart
txt << %{ data-remote="true"} if remote
txt << %{ class="#{html_class}"} if html_class
diff --git a/actionview/test/template/form_options_helper_test.rb b/actionview/test/template/form_options_helper_test.rb
index 8a0a706fef..a2d1474a94 100644
--- a/actionview/test/template/form_options_helper_test.rb
+++ b/actionview/test/template/form_options_helper_test.rb
@@ -668,7 +668,7 @@ class FormOptionsHelperTest < ActionView::TestCase
@post = Post.new
output_buffer = fields_for :post, @post do |f|
- concat(f.select(:category) {})
+ concat(f.select(:category) { })
end
assert_dom_equal(
diff --git a/actionview/test/template/form_tag_helper_test.rb b/actionview/test/template/form_tag_helper_test.rb
index a3500a7eb3..9ece9f3ad1 100644
--- a/actionview/test/template/form_tag_helper_test.rb
+++ b/actionview/test/template/form_tag_helper_test.rb
@@ -26,7 +26,7 @@ class FormTagHelperTest < ActionView::TestCase
method = options[:method]
enforce_utf8 = options.fetch(:enforce_utf8, true)
- "".dup.tap do |txt|
+ (+"").tap do |txt|
if enforce_utf8
txt << %{<input name="utf8" type="hidden" value="&#x2713;" />}
end
@@ -42,7 +42,7 @@ class FormTagHelperTest < ActionView::TestCase
method = method.to_s == "get" ? "get" : "post"
- txt = %{<form accept-charset="UTF-8" action="#{action}"}.dup
+ txt = +%{<form accept-charset="UTF-8" action="#{action}"}
txt << %{ enctype="multipart/form-data"} if enctype
txt << %{ data-remote="true"} if remote
txt << %{ class="#{html_class}"} if html_class
diff --git a/actionview/test/template/javascript_helper_test.rb b/actionview/test/template/javascript_helper_test.rb
index a72bc6c2fe..4c28aeaee1 100644
--- a/actionview/test/template/javascript_helper_test.rb
+++ b/actionview/test/template/javascript_helper_test.rb
@@ -23,11 +23,15 @@ class JavaScriptHelperTest < ActionView::TestCase
def test_escape_javascript
assert_equal "", escape_javascript(nil)
+ assert_equal "123", escape_javascript(123)
+ assert_equal "en", escape_javascript(:en)
+ assert_equal "false", escape_javascript(false)
+ assert_equal "true", escape_javascript(true)
assert_equal %(This \\"thing\\" is really\\n netos\\'), escape_javascript(%(This "thing" is really\n netos'))
assert_equal %(backslash\\\\test), escape_javascript(%(backslash\\test))
assert_equal %(dont <\\/close> tags), escape_javascript(%(dont </close> tags))
- assert_equal %(unicode &#x2028; newline), escape_javascript(%(unicode \342\200\250 newline).dup.force_encoding(Encoding::UTF_8).encode!)
- assert_equal %(unicode &#x2029; newline), escape_javascript(%(unicode \342\200\251 newline).dup.force_encoding(Encoding::UTF_8).encode!)
+ assert_equal %(unicode &#x2028; newline), escape_javascript((+%(unicode \342\200\250 newline)).force_encoding(Encoding::UTF_8).encode!)
+ assert_equal %(unicode &#x2029; newline), escape_javascript((+%(unicode \342\200\251 newline)).force_encoding(Encoding::UTF_8).encode!)
assert_equal %(dont <\\/close> tags), j(%(dont </close> tags))
end
diff --git a/actionview/test/template/render_test.rb b/actionview/test/template/render_test.rb
index 6ff7ddba0f..afe68b7ff0 100644
--- a/actionview/test/template/render_test.rb
+++ b/actionview/test/template/render_test.rb
@@ -585,7 +585,7 @@ module RenderTestCases
def test_render_with_passing_couple_extensions_to_one_register_template_handler_function_call
ActionView::Template.register_template_handler :foo1, :foo2, CustomHandler
- assert_equal @view.render(inline: "Hello, World!".dup, type: :foo1), @view.render(inline: "Hello, World!".dup, type: :foo2)
+ assert_equal @view.render(inline: +"Hello, World!", type: :foo1), @view.render(inline: +"Hello, World!", type: :foo2)
ensure
ActionView::Template.unregister_template_handler :foo1, :foo2
end
diff --git a/actionview/test/template/streaming_render_test.rb b/actionview/test/template/streaming_render_test.rb
index ef000300cc..f196c42c4f 100644
--- a/actionview/test/template/streaming_render_test.rb
+++ b/actionview/test/template/streaming_render_test.rb
@@ -19,7 +19,7 @@ class SetupFiberedBase < ActiveSupport::TestCase
def buffered_render(options)
body = render_body(options)
- string = "".dup
+ string = +""
body.each do |piece|
string << piece
end
diff --git a/actionview/test/template/template_test.rb b/actionview/test/template/template_test.rb
index 3dc14e36e0..b348d1f17b 100644
--- a/actionview/test/template/template_test.rb
+++ b/actionview/test/template/template_test.rb
@@ -196,6 +196,13 @@ class TestERBTemplate < ActiveSupport::TestCase
assert_match(Regexp.new("\xFC"), e.message)
end
+ def test_template_is_marshalable
+ template = new_template
+ serialized = Marshal.load(Marshal.dump(template))
+ assert_equal template.identifier, serialized.identifier
+ assert_equal template.source, serialized.source
+ end
+
def with_external_encoding(encoding)
old = Encoding.default_external
Encoding::Converter.new old, encoding if old != encoding
diff --git a/actionview/test/template/test_case_test.rb b/actionview/test/template/test_case_test.rb
index d98fd4f9a2..976b6bc77e 100644
--- a/actionview/test/template/test_case_test.rb
+++ b/actionview/test/template/test_case_test.rb
@@ -217,8 +217,14 @@ module ActionView
test "is able to use routes" do
controller.request.assign_parameters(@routes, "foo", "index", {}, "/foo", [])
- assert_equal "/foo", url_for
- assert_equal "/bar", url_for(controller: "bar")
+ with_routing do |set|
+ set.draw {
+ get :foo, to: "foo#index"
+ get :bar, to: "bar#index"
+ }
+ assert_equal "/foo", url_for
+ assert_equal "/bar", url_for(controller: "bar")
+ end
end
test "is able to use named routes" do
@@ -236,7 +242,7 @@ module ActionView
@routes ||= ActionDispatch::Routing::RouteSet.new
end
- routes.draw { get "bar", to: lambda {} }
+ routes.draw { get "bar", to: lambda { } }
def self.call(*)
end
@@ -244,6 +250,8 @@ module ActionView
set.draw { mount app => "/foo", :as => "foo_app" }
+ singleton_class.include set.mounted_helpers
+
assert_equal "/foo/bar", foo_app.bar_path
end
end
diff --git a/actionview/test/template/text_helper_test.rb b/actionview/test/template/text_helper_test.rb
index 4d47706bda..2925489f5d 100644
--- a/actionview/test/template/text_helper_test.rb
+++ b/actionview/test/template/text_helper_test.rb
@@ -13,7 +13,7 @@ class TextHelperTest < ActionView::TestCase
end
def test_concat
- self.output_buffer = "foo".dup
+ self.output_buffer = +"foo"
assert_equal "foobar", concat("bar")
assert_equal "foobar", output_buffer
end
@@ -34,10 +34,10 @@ class TextHelperTest < ActionView::TestCase
assert_equal "<p>A paragraph</p>\n\n<p>and another one!</p>", simple_format("A paragraph\n\nand another one!")
assert_equal "<p>A paragraph\n<br /> With a newline</p>", simple_format("A paragraph\n With a newline")
- text = "A\nB\nC\nD".freeze
+ text = "A\nB\nC\nD"
assert_equal "<p>A\n<br />B\n<br />C\n<br />D</p>", simple_format(text)
- text = "A\r\n \nB\n\n\r\n\t\nC\nD".freeze
+ text = "A\r\n \nB\n\n\r\n\t\nC\nD"
assert_equal "<p>A\n<br /> \n<br />B</p>\n\n<p>\t\n<br />C\n<br />D</p>", simple_format(text)
assert_equal '<p class="test">This is a classy test</p>', simple_format("This is a classy test", class: "test")
@@ -106,8 +106,8 @@ class TextHelperTest < ActionView::TestCase
end
def test_truncate_multibyte
- assert_equal "\354\225\204\353\246\254\353\236\221 \354\225\204\353\246\254 ...".dup.force_encoding(Encoding::UTF_8),
- truncate("\354\225\204\353\246\254\353\236\221 \354\225\204\353\246\254 \354\225\204\353\235\274\353\246\254\354\230\244".dup.force_encoding(Encoding::UTF_8), length: 10)
+ assert_equal (+"\354\225\204\353\246\254\353\236\221 \354\225\204\353\246\254 ...").force_encoding(Encoding::UTF_8),
+ truncate((+"\354\225\204\353\246\254\353\236\221 \354\225\204\353\246\254 \354\225\204\353\235\274\353\246\254\354\230\244").force_encoding(Encoding::UTF_8), length: 10)
end
def test_truncate_does_not_modify_the_options_hash
@@ -327,7 +327,7 @@ class TextHelperTest < ActionView::TestCase
end
def test_excerpt_with_utf8
- assert_equal("...\357\254\203ciency could not be...".dup.force_encoding(Encoding::UTF_8), excerpt("That's why e\357\254\203ciency could not be helped".dup.force_encoding(Encoding::UTF_8), "could", radius: 8))
+ assert_equal((+"...\357\254\203ciency could not be...").force_encoding(Encoding::UTF_8), excerpt((+"That's why e\357\254\203ciency could not be helped").force_encoding(Encoding::UTF_8), "could", radius: 8))
end
def test_excerpt_does_not_modify_the_options_hash
diff --git a/actionview/test/template/url_helper_test.rb b/actionview/test/template/url_helper_test.rb
index 9d91dbb72b..6db9eb3be1 100644
--- a/actionview/test/template/url_helper_test.rb
+++ b/actionview/test/template/url_helper_test.rb
@@ -548,7 +548,7 @@ class UrlHelperTest < ActiveSupport::TestCase
def test_current_page_with_escaped_params_with_different_encoding
@request = request_for_url("/")
- @request.stub(:path, "/category/administra%c3%a7%c3%a3o".dup.force_encoding(Encoding::ASCII_8BIT)) do
+ @request.stub(:path, (+"/category/administra%c3%a7%c3%a3o").force_encoding(Encoding::ASCII_8BIT)) do
assert current_page?(controller: "foo", action: "category", category: "administração")
assert current_page?("http://www.example.com/category/administra%c3%a7%c3%a3o")
end
@@ -704,7 +704,7 @@ end
class UrlHelperControllerTest < ActionController::TestCase
class UrlHelperController < ActionController::Base
- test_routes do
+ ROUTES = test_routes do
get "url_helper_controller_test/url_helper/show/:id",
to: "url_helper_controller_test/url_helper#show",
as: :show
@@ -768,6 +768,11 @@ class UrlHelperControllerTest < ActionController::TestCase
helper_method :override_url_helper_path
end
+ def setup
+ super
+ @routes = UrlHelperController::ROUTES
+ end
+
tests UrlHelperController
def test_url_for_shows_only_path
@@ -828,7 +833,7 @@ class UrlHelperControllerTest < ActionController::TestCase
end
class TasksController < ActionController::Base
- test_routes do
+ ROUTES = test_routes do
resources :tasks
end
@@ -850,6 +855,11 @@ end
class LinkToUnlessCurrentWithControllerTest < ActionController::TestCase
tests TasksController
+ def setup
+ super
+ @routes = TasksController::ROUTES
+ end
+
def test_link_to_unless_current_to_current
get :index
assert_equal "tasks\ntasks", @response.body
@@ -882,7 +892,7 @@ class Session
end
class WorkshopsController < ActionController::Base
- test_routes do
+ ROUTES = test_routes do
resources :workshops do
resources :sessions
end
@@ -905,7 +915,7 @@ class WorkshopsController < ActionController::Base
end
class SessionsController < ActionController::Base
- test_routes do
+ ROUTES = test_routes do
resources :workshops do
resources :sessions
end
@@ -932,6 +942,11 @@ class SessionsController < ActionController::Base
end
class PolymorphicControllerTest < ActionController::TestCase
+ def setup
+ super
+ @routes = WorkshopsController::ROUTES
+ end
+
def test_new_resource
@controller = WorkshopsController.new
@@ -946,6 +961,20 @@ class PolymorphicControllerTest < ActionController::TestCase
assert_equal %{/workshops/1\n<a href="/workshops/1">Workshop</a>}, @response.body
end
+ def test_current_page_when_options_does_not_respond_to_to_hash
+ @controller = WorkshopsController.new
+
+ get :edit, params: { id: 1 }
+ assert_equal "false", @response.body
+ end
+end
+
+class PolymorphicSessionsControllerTest < ActionController::TestCase
+ def setup
+ super
+ @routes = SessionsController::ROUTES
+ end
+
def test_new_nested_resource
@controller = SessionsController.new
@@ -966,11 +995,4 @@ class PolymorphicControllerTest < ActionController::TestCase
get :edit, params: { workshop_id: 1, id: 1, format: "json" }
assert_equal %{/workshops/1/sessions/1.json\n<a href="/workshops/1/sessions/1.json">Session</a>}, @response.body
end
-
- def test_current_page_when_options_does_not_respond_to_to_hash
- @controller = WorkshopsController.new
-
- get :edit, params: { id: 1 }
- assert_equal "false", @response.body
- end
end
diff --git a/actionview/test/ujs/public/test/data-disable.js b/actionview/test/ujs/public/test/data-disable.js
index e9919764b6..88dc801b2f 100644
--- a/actionview/test/ujs/public/test/data-disable.js
+++ b/actionview/test/ujs/public/test/data-disable.js
@@ -320,3 +320,20 @@ asyncTest('button[data-remote][data-disable] re-enables when `ajax:error` event
start()
}, 30)
})
+
+asyncTest('do not enable elements for XHR redirects', 6, function() {
+ var link = $('a[data-disable]').attr('data-remote', true).attr('href', '/echo?with_xhr_redirect=true')
+
+ App.checkEnabledState(link, 'Click me')
+
+ link
+ .bindNative('ajax:send', function() {
+ App.checkDisabledState(link, 'Click me')
+ })
+ .triggerNative('click')
+
+ setTimeout(function() {
+ App.checkDisabledState(link, 'Click me')
+ start()
+ }, 30)
+})
diff --git a/actionview/test/ujs/public/test/settings.js b/actionview/test/ujs/public/test/settings.js
index b1ce3b8c64..05677f2595 100644
--- a/actionview/test/ujs/public/test/settings.js
+++ b/actionview/test/ujs/public/test/settings.js
@@ -1,4 +1,5 @@
var App = App || {}
+var Turbolinks = Turbolinks || {}
App.assertCallbackInvoked = function(callbackName) {
ok(true, callbackName + ' callback should have been invoked')
@@ -116,3 +117,6 @@ $.fn.extend({
return this
}
})
+
+Turbolinks.clearCache = function() {}
+Turbolinks.visit = function() {}
diff --git a/actionview/test/ujs/server.rb b/actionview/test/ujs/server.rb
index 48e9bcb65f..56f436c8b8 100644
--- a/actionview/test/ujs/server.rb
+++ b/actionview/test/ujs/server.rb
@@ -64,7 +64,12 @@ class TestsController < ActionController::Base
if params[:content_type] && params[:content]
render inline: params[:content], content_type: params[:content_type]
elsif request.xhr?
- render json: JSON.generate(data)
+ if params[:with_xhr_redirect]
+ response.set_header("X-Xhr-Redirect", "http://example.com/")
+ render inline: %{Turbolinks.clearCache()\nTurbolinks.visit("http://example.com/", {"action":"replace"})}
+ else
+ render json: JSON.generate(data)
+ end
elsif params[:iframe]
payload = JSON.generate(data).gsub("<", "&lt;").gsub(">", "&gt;")
html = <<-HTML
diff --git a/activejob/CHANGELOG.md b/activejob/CHANGELOG.md
index 57a62e963d..af5c197bac 100644
--- a/activejob/CHANGELOG.md
+++ b/activejob/CHANGELOG.md
@@ -1,3 +1,9 @@
+* Allow `assert_enqueued_with`/`assert_performed_with` methods to accept
+ a proc for the `args` argument. This is useful to check if only a subset of arguments
+ matches your expectations.
+
+ *Edouard Chin*
+
* `ActionDispatch::IntegrationTest` includes `ActiveJob::TestHelper` module by default.
*Ricardo Díaz*
diff --git a/activejob/lib/active_job/arguments.rb b/activejob/lib/active_job/arguments.rb
index b344c44aef..8dcf588f35 100644
--- a/activejob/lib/active_job/arguments.rb
+++ b/activejob/lib/active_job/arguments.rb
@@ -47,11 +47,11 @@ module ActiveJob
private
# :nodoc:
- GLOBALID_KEY = "_aj_globalid".freeze
+ GLOBALID_KEY = "_aj_globalid"
# :nodoc:
- SYMBOL_KEYS_KEY = "_aj_symbol_keys".freeze
+ SYMBOL_KEYS_KEY = "_aj_symbol_keys"
# :nodoc:
- WITH_INDIFFERENT_ACCESS_KEY = "_aj_hash_with_indifferent_access".freeze
+ WITH_INDIFFERENT_ACCESS_KEY = "_aj_hash_with_indifferent_access"
# :nodoc:
OBJECT_SERIALIZER_KEY = "_aj_serialized"
diff --git a/activejob/lib/active_job/core.rb b/activejob/lib/active_job/core.rb
index 61d402cfca..62bb5861bb 100644
--- a/activejob/lib/active_job/core.rb
+++ b/activejob/lib/active_job/core.rb
@@ -6,35 +6,33 @@ module ActiveJob
module Core
extend ActiveSupport::Concern
- included do
- # Job arguments
- attr_accessor :arguments
- attr_writer :serialized_arguments
+ # Job arguments
+ attr_accessor :arguments
+ attr_writer :serialized_arguments
- # Timestamp when the job should be performed
- attr_accessor :scheduled_at
+ # Timestamp when the job should be performed
+ attr_accessor :scheduled_at
- # Job Identifier
- attr_accessor :job_id
+ # Job Identifier
+ attr_accessor :job_id
- # Queue in which the job will reside.
- attr_writer :queue_name
+ # Queue in which the job will reside.
+ attr_writer :queue_name
- # Priority that the job will have (lower is more priority).
- attr_writer :priority
+ # Priority that the job will have (lower is more priority).
+ attr_writer :priority
- # ID optionally provided by adapter
- attr_accessor :provider_job_id
+ # ID optionally provided by adapter
+ attr_accessor :provider_job_id
- # Number of times this job has been executed (which increments on every retry, like after an exception).
- attr_accessor :executions
+ # Number of times this job has been executed (which increments on every retry, like after an exception).
+ attr_accessor :executions
- # I18n.locale to be used during the job.
- attr_accessor :locale
+ # I18n.locale to be used during the job.
+ attr_accessor :locale
- # Timezone to be used during the job.
- attr_accessor :timezone
- end
+ # Timezone to be used during the job.
+ attr_accessor :timezone
# These methods will be included into any Active Job object, adding
# helpers for de/serialization and creation of job instances.
diff --git a/activejob/lib/active_job/exceptions.rb b/activejob/lib/active_job/exceptions.rb
index d8384c81b6..bc9e168971 100644
--- a/activejob/lib/active_job/exceptions.rb
+++ b/activejob/lib/active_job/exceptions.rb
@@ -46,24 +46,15 @@ module ActiveJob
# end
def retry_on(*exceptions, wait: 3.seconds, attempts: 5, queue: nil, priority: nil)
rescue_from(*exceptions) do |error|
- payload = {
- job: self,
- adapter: self.class.queue_adapter,
- error: error,
- wait: wait
- }
-
if executions < attempts
- ActiveSupport::Notifications.instrument("enqueue_retry.active_job", payload) do
- retry_job wait: determine_delay(wait), queue: queue, priority: priority
- end
+ retry_job wait: determine_delay(wait), queue: queue, priority: priority, error: error
else
if block_given?
- ActiveSupport::Notifications.instrument("retry_stopped.active_job", payload) do
+ instrument :retry_stopped, error: error do
yield self, error
end
else
- ActiveSupport::Notifications.instrument("retry_stopped.active_job", payload)
+ instrument :retry_stopped, error: error
raise error
end
end
@@ -90,16 +81,8 @@ module ActiveJob
# end
def discard_on(*exceptions)
rescue_from(*exceptions) do |error|
- payload = {
- job: self,
- adapter: self.class.queue_adapter,
- error: error
- }
-
- ActiveSupport::Notifications.instrument("discard.active_job", payload) do
- if block_given?
- yield self, error
- end
+ instrument :discard, error: error do
+ yield self, error if block_given?
end
end
end
@@ -127,7 +110,9 @@ module ActiveJob
# end
# end
def retry_job(options = {})
- enqueue options
+ instrument :enqueue_retry, options.slice(:error, :wait) do
+ enqueue options
+ end
end
private
@@ -148,5 +133,11 @@ module ActiveJob
raise "Couldn't determine a delay based on #{seconds_or_duration_or_algorithm.inspect}"
end
end
+
+ def instrument(name, error: nil, wait: nil, &block)
+ payload = { job: self, adapter: self.class.queue_adapter, error: error, wait: wait }
+
+ ActiveSupport::Notifications.instrument("#{name}.active_job", payload, &block)
+ end
end
end
diff --git a/activejob/lib/active_job/logging.rb b/activejob/lib/active_job/logging.rb
index 96a3e6bf48..0abee4ed98 100644
--- a/activejob/lib/active_job/logging.rb
+++ b/activejob/lib/active_job/logging.rb
@@ -94,7 +94,7 @@ module ActiveJob
wait = event.payload[:wait]
error do
- "Retrying #{job.class} in #{wait} seconds, due to a #{ex.class}. The original exception was #{ex.cause.inspect}."
+ "Retrying #{job.class} in #{wait.inspect} seconds, due to a #{ex&.class.inspect}. The original exception was #{ex&.cause.inspect}."
end
end
diff --git a/activejob/lib/active_job/queue_adapter.rb b/activejob/lib/active_job/queue_adapter.rb
index 006a683b85..954bfd1dd1 100644
--- a/activejob/lib/active_job/queue_adapter.rb
+++ b/activejob/lib/active_job/queue_adapter.rb
@@ -22,6 +22,8 @@ module ActiveJob
_queue_adapter
end
+ # Returns string denoting the name of the configured queue adapter.
+ # By default returns +"async"+.
def queue_adapter_name
_queue_adapter_name
end
diff --git a/activejob/lib/active_job/queue_adapters.rb b/activejob/lib/active_job/queue_adapters.rb
index 00c7b407b1..3e3a474fbb 100644
--- a/activejob/lib/active_job/queue_adapters.rb
+++ b/activejob/lib/active_job/queue_adapters.rb
@@ -121,7 +121,7 @@ module ActiveJob
autoload :SuckerPunchAdapter
autoload :TestAdapter
- ADAPTER = "Adapter".freeze
+ ADAPTER = "Adapter"
private_constant :ADAPTER
class << self
diff --git a/activejob/lib/active_job/test_helper.rb b/activejob/lib/active_job/test_helper.rb
index bb9e3e6ca4..261b68d4f8 100644
--- a/activejob/lib/active_job/test_helper.rb
+++ b/activejob/lib/active_job/test_helper.rb
@@ -75,7 +75,7 @@ module ActiveJob
# assert_enqueued_jobs 2
# end
#
- # If a block is passed, that block will cause the specified number of
+ # If a block is passed, asserts that the block will cause the specified number of
# jobs to be enqueued.
#
# def test_jobs_again
@@ -89,7 +89,7 @@ module ActiveJob
# end
# end
#
- # The number of times a specific job was enqueued can be asserted.
+ # Asserts the number of times a specific job was enqueued by passing +:only+ option.
#
# def test_logging_job
# assert_enqueued_jobs 1, only: LoggingJob do
@@ -98,7 +98,7 @@ module ActiveJob
# end
# end
#
- # The number of times a job except specific class was enqueued can be asserted.
+ # Asserts the number of times a job except specific class was enqueued by passing +:except+ option.
#
# def test_logging_job
# assert_enqueued_jobs 1, except: HelloJob do
@@ -107,7 +107,7 @@ module ActiveJob
# end
# end
#
- # The number of times a job is enqueued to a specific queue can also be asserted.
+ # Asserts the number of times a job is enqueued to a specific queue by passing +:queue+ option.
#
# def test_logging_job
# assert_enqueued_jobs 2, queue: 'default' do
@@ -139,7 +139,7 @@ module ActiveJob
# assert_enqueued_jobs 1
# end
#
- # If a block is passed, that block should not cause any job to be enqueued.
+ # If a block is passed, asserts that the block will not cause any job to be enqueued.
#
# def test_jobs_again
# assert_no_enqueued_jobs do
@@ -147,7 +147,7 @@ module ActiveJob
# end
# end
#
- # It can be asserted that no jobs of a specific kind are enqueued:
+ # Asserts that no jobs of a specific kind are enqueued by passing +:only+ option.
#
# def test_no_logging
# assert_no_enqueued_jobs only: LoggingJob do
@@ -155,7 +155,7 @@ module ActiveJob
# end
# end
#
- # It can be asserted that no jobs except specific class are enqueued:
+ # Asserts that no jobs except specific class are enqueued by passing +:except+ option.
#
# def test_no_logging
# assert_no_enqueued_jobs except: HelloJob do
@@ -163,7 +163,7 @@ module ActiveJob
# end
# end
#
- # It can be asserted that no jobs are enqueued to a specific queue:
+ # Asserts that no jobs are enqueued to a specific queue by passing +:queue+ option
#
# def test_no_logging
# assert_no_enqueued_jobs queue: 'default' do
@@ -331,6 +331,22 @@ module ActiveJob
# assert_enqueued_with(job: MyJob, at: Date.tomorrow.noon)
# end
#
+ #
+ # The +args+ argument also accepts a proc which will get passed the actual
+ # job's arguments. Your proc needs to returns a boolean value determining if
+ # the job's arguments matches your expectation. This is useful to check only
+ # for a subset of arguments.
+ #
+ # def test_assert_enqueued_with
+ # expected_args = ->(job_args) do
+ # assert job_args.first.key?(:foo)
+ # end
+ #
+ # MyJob.perform_later(foo: 'bar', other_arg: 'No need to check in the test')
+ # assert_enqueued_with(job: MyJob, args: expected_args, queue: 'low')
+ # end
+ #
+ #
# If a block is passed, that block should cause the job to be
# enqueued with the given arguments.
#
@@ -345,7 +361,7 @@ module ActiveJob
# end
def assert_enqueued_with(job: nil, args: nil, at: nil, queue: nil)
expected = { job: job, args: args, at: at, queue: queue }.compact
- serialized_args = serialize_args_for_assertion(expected)
+ expected_args = prepare_args_for_assertion(expected)
if block_given?
original_enqueued_jobs_count = enqueued_jobs.count
@@ -358,7 +374,15 @@ module ActiveJob
end
matching_job = jobs.find do |enqueued_job|
- serialized_args.all? { |key, value| value == enqueued_job[key] }
+ deserialized_job = deserialize_args_for_assertion(enqueued_job)
+
+ expected_args.all? do |key, value|
+ if value.respond_to?(:call)
+ value.call(deserialized_job[key])
+ else
+ value == deserialized_job[key]
+ end
+ end
end
assert matching_job, "No enqueued job found with #{expected}"
@@ -381,6 +405,22 @@ module ActiveJob
# assert_performed_with(job: MyJob, at: Date.tomorrow.noon)
# end
#
+ # The +args+ argument also accepts a proc which will get passed the actual
+ # job's arguments. Your proc needs to returns a boolean value determining if
+ # the job's arguments matches your expectation. This is useful to check only
+ # for a subset of arguments.
+ #
+ # def test_assert_performed_with
+ # expected_args = ->(job_args) do
+ # assert job_args.first.key?(:foo)
+ # end
+ # MyJob.perform_later(foo: 'bar', other_arg: 'No need to check in the test')
+ #
+ # perform_enqueued_jobs
+ #
+ # assert_performed_with(job: MyJob, args: expected_args, queue: 'high')
+ # end
+ #
# If a block is passed, that block performs all of the jobs that were
# enqueued throughout the duration of the block and asserts that
# the job has been performed with the given arguments in the block.
@@ -396,7 +436,7 @@ module ActiveJob
# end
def assert_performed_with(job: nil, args: nil, at: nil, queue: nil, &block)
expected = { job: job, args: args, at: at, queue: queue }.compact
- serialized_args = serialize_args_for_assertion(expected)
+ expected_args = prepare_args_for_assertion(expected)
if block_given?
original_performed_jobs_count = performed_jobs.count
@@ -408,8 +448,16 @@ module ActiveJob
jobs = performed_jobs
end
- matching_job = jobs.find do |performed_job|
- serialized_args.all? { |key, value| value == performed_job[key] }
+ matching_job = jobs.find do |enqueued_job|
+ deserialized_job = deserialize_args_for_assertion(enqueued_job)
+
+ expected_args.all? do |key, value|
+ if value.respond_to?(:call)
+ value.call(deserialized_job[key])
+ else
+ value == deserialized_job[key]
+ end
+ end
end
assert matching_job, "No performed job found with #{expected}"
@@ -552,10 +600,15 @@ module ActiveJob
end
end
- def serialize_args_for_assertion(args)
- args.dup.tap do |serialized_args|
- serialized_args[:args] = ActiveJob::Arguments.serialize(serialized_args[:args]) if serialized_args[:args]
- serialized_args[:at] = serialized_args[:at].to_f if serialized_args[:at]
+ def prepare_args_for_assertion(args)
+ args.dup.tap do |arguments|
+ arguments[:at] = arguments[:at].to_f if arguments[:at]
+ end
+ end
+
+ def deserialize_args_for_assertion(job)
+ job.dup.tap do |new_job|
+ new_job[:args] = ActiveJob::Arguments.deserialize(new_job[:args]) if new_job[:args]
end
end
diff --git a/activejob/test/cases/logging_test.rb b/activejob/test/cases/logging_test.rb
index 2e8d2ef7c0..b5bf40c83b 100644
--- a/activejob/test/cases/logging_test.rb
+++ b/activejob/test/cases/logging_test.rb
@@ -173,6 +173,11 @@ class LoggingTest < ActiveSupport::TestCase
end
end
+ def test_enqueue_retry_logging_on_retry_job
+ perform_enqueued_jobs { RescueJob.perform_later "david" }
+ assert_match(/Retrying RescueJob in nil seconds, due to a nil\. The original exception was nil\./, @logger.messages)
+ end
+
def test_retry_stopped_logging
perform_enqueued_jobs do
RetryJob.perform_later "CustomCatchError", 6
diff --git a/activejob/test/cases/test_helper_test.rb b/activejob/test/cases/test_helper_test.rb
index 160b876e85..83c71ab1c4 100644
--- a/activejob/test/cases/test_helper_test.rb
+++ b/activejob/test/cases/test_helper_test.rb
@@ -8,6 +8,7 @@ require "jobs/logging_job"
require "jobs/nested_job"
require "jobs/rescue_job"
require "jobs/inherited_job"
+require "jobs/multiple_kwargs_job"
require "models/person"
class EnqueuedJobsTest < ActiveJob::TestCase
@@ -503,7 +504,7 @@ class EnqueuedJobsTest < ActiveJob::TestCase
assert_raise ActiveSupport::TestCase::Assertion do
LoggingJob.perform_later
- assert_enqueued_with(job: LoggingJob) {}
+ assert_enqueued_with(job: LoggingJob) { }
end
error = assert_raise ActiveSupport::TestCase::Assertion do
@@ -537,6 +538,29 @@ class EnqueuedJobsTest < ActiveJob::TestCase
end
end
+ def test_assert_enqueued_with_selective_args
+ args = ->(job_args) do
+ assert_equal 1, job_args.first[:argument1]
+ assert job_args.first[:argument2].key?(:b)
+ end
+
+ assert_enqueued_with(job: MultipleKwargsJob, args: args) do
+ MultipleKwargsJob.perform_later(argument2: { b: 2, a: 1 }, argument1: 1)
+ end
+ end
+
+ def test_assert_enqueued_with_selective_args_fails
+ args = ->(job_args) do
+ false
+ end
+
+ assert_raise ActiveSupport::TestCase::Assertion do
+ assert_enqueued_with(job: MultipleKwargsJob, args: args) do
+ MultipleKwargsJob.perform_later(argument2: { b: 2, a: 1 }, argument1: 1)
+ end
+ end
+ end
+
def test_assert_enqueued_with_with_no_block_args
assert_raise ArgumentError do
NestedJob.set(wait_until: Date.tomorrow.noon).perform_later
@@ -555,6 +579,12 @@ class EnqueuedJobsTest < ActiveJob::TestCase
assert_enqueued_with(job: HelloJob, at: Date.tomorrow.noon)
end
+ def test_assert_enqueued_with_with_hash_arg
+ assert_enqueued_with(job: MultipleKwargsJob, args: [{ argument1: 1, argument2: { a: 1, b: 2 } }]) do
+ MultipleKwargsJob.perform_later(argument2: { b: 2, a: 1 }, argument1: 1)
+ end
+ end
+
def test_assert_enqueued_with_with_global_id_args
ricardo = Person.new(9)
assert_enqueued_with(job: HelloJob, args: [ricardo]) do
@@ -1566,6 +1596,35 @@ class PerformedJobsTest < ActiveJob::TestCase
end
end
+ def test_assert_performed_with_with_hash_arg
+ assert_performed_with(job: MultipleKwargsJob, args: [{ argument1: 1, argument2: { a: 1, b: 2 } }]) do
+ MultipleKwargsJob.perform_later(argument2: { b: 2, a: 1 }, argument1: 1)
+ end
+ end
+
+ def test_assert_performed_with_selective_args
+ args = ->(job_args) do
+ assert_equal 1, job_args.first[:argument1]
+ assert job_args.first[:argument2].key?(:b)
+ end
+
+ assert_performed_with(job: MultipleKwargsJob, args: args) do
+ MultipleKwargsJob.perform_later(argument2: { b: 2, a: 1 }, argument1: 1)
+ end
+ end
+
+ def test_assert_performed_with_selective_args_fails
+ args = ->(job_args) do
+ false
+ end
+
+ assert_raise ActiveSupport::TestCase::Assertion do
+ assert_performed_with(job: MultipleKwargsJob, args: args) do
+ MultipleKwargsJob.perform_later(argument2: { b: 2, a: 1 }, argument1: 1)
+ end
+ end
+ end
+
def test_assert_performed_with_with_global_id_args
ricardo = Person.new(9)
assert_performed_with(job: HelloJob, args: [ricardo]) do
diff --git a/activejob/test/jobs/multiple_kwargs_job.rb b/activejob/test/jobs/multiple_kwargs_job.rb
new file mode 100644
index 0000000000..b355c4ce1a
--- /dev/null
+++ b/activejob/test/jobs/multiple_kwargs_job.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+require_relative "../support/job_buffer"
+
+class MultipleKwargsJob < ActiveJob::Base
+ def perform(argument1:, argument2:)
+ JobBuffer.add("Job with argument1: #{argument1}, argument2: #{argument2}")
+ end
+end
diff --git a/activejob/test/support/integration/adapters/que.rb b/activejob/test/support/integration/adapters/que.rb
index 2a771b08c7..2e7d327b37 100644
--- a/activejob/test/support/integration/adapters/que.rb
+++ b/activejob/test/support/integration/adapters/que.rb
@@ -18,8 +18,8 @@ module QueJobsManager
user = uri.user || ENV["USER"]
pass = uri.password
db = uri.path[1..-1]
- %x{#{"PGPASSWORD=\"#{pass}\"" if pass} psql -c 'drop database if exists "#{db}"' -U #{user} -t template1}
- %x{#{"PGPASSWORD=\"#{pass}\"" if pass} psql -c 'create database "#{db}"' -U #{user} -t template1}
+ %x{#{"PGPASSWORD=\"#{pass}\"" if pass} psql -X -c 'drop database if exists "#{db}"' -U #{user} -t template1}
+ %x{#{"PGPASSWORD=\"#{pass}\"" if pass} psql -X -c 'create database "#{db}"' -U #{user} -t template1}
Que.connection = Sequel.connect(que_url)
Que.migrate!
diff --git a/activejob/test/support/integration/adapters/queue_classic.rb b/activejob/test/support/integration/adapters/queue_classic.rb
index 1b0685a971..dbbdc12b9d 100644
--- a/activejob/test/support/integration/adapters/queue_classic.rb
+++ b/activejob/test/support/integration/adapters/queue_classic.rb
@@ -17,8 +17,8 @@ module QueueClassicJobsManager
user = uri.user || ENV["USER"]
pass = uri.password
db = uri.path[1..-1]
- %x{#{"PGPASSWORD=\"#{pass}\"" if pass} psql -c 'drop database if exists "#{db}"' -U #{user} -t template1}
- %x{#{"PGPASSWORD=\"#{pass}\"" if pass} psql -c 'create database "#{db}"' -U #{user} -t template1}
+ %x{#{"PGPASSWORD=\"#{pass}\"" if pass} psql -X -c 'drop database if exists "#{db}"' -U #{user} -t template1}
+ %x{#{"PGPASSWORD=\"#{pass}\"" if pass} psql -X -c 'create database "#{db}"' -U #{user} -t template1}
QC::Setup.create
QC.default_conn_adapter.disconnect
diff --git a/activejob/test/support/integration/helper.rb b/activejob/test/support/integration/helper.rb
index a02d874e2e..c5fa2b136f 100644
--- a/activejob/test/support/integration/helper.rb
+++ b/activejob/test/support/integration/helper.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-puts "\n\n*** rake aj:integration:#{ENV['AJ_ADAPTER']} ***\n"
+puts "\n\n*** rake test:integration:#{ENV['AJ_ADAPTER']} ***\n"
ENV["RAILS_ENV"] = "test"
ActiveJob::Base.queue_name_prefix = nil
diff --git a/activemodel/lib/active_model/attribute_assignment.rb b/activemodel/lib/active_model/attribute_assignment.rb
index 217bf1ac01..f0e3458f51 100644
--- a/activemodel/lib/active_model/attribute_assignment.rb
+++ b/activemodel/lib/active_model/attribute_assignment.rb
@@ -27,7 +27,7 @@ module ActiveModel
# cat.status # => 'sleeping'
def assign_attributes(new_attributes)
if !new_attributes.respond_to?(:stringify_keys)
- raise ArgumentError, "When assigning attributes, you must pass a hash as an argument."
+ raise ArgumentError, "When assigning attributes, you must pass a hash as an argument, #{new_attributes.class} passed."
end
return if new_attributes.empty?
diff --git a/activemodel/lib/active_model/attribute_methods.rb b/activemodel/lib/active_model/attribute_methods.rb
index 888a431e5f..1ad9071cc2 100644
--- a/activemodel/lib/active_model/attribute_methods.rb
+++ b/activemodel/lib/active_model/attribute_methods.rb
@@ -369,7 +369,7 @@ module ActiveModel
"define_method(:'#{name}') do |*args|"
end
- extra = (extra.map!(&:inspect) << "*args").join(", ".freeze)
+ extra = (extra.map!(&:inspect) << "*args").join(", ")
target = if CALL_COMPILABLE_REGEXP.match?(send)
"#{"self." unless include_private}#{send}(#{extra})"
diff --git a/activemodel/lib/active_model/attributes.rb b/activemodel/lib/active_model/attributes.rb
index 5bf213d593..41fe5168f3 100644
--- a/activemodel/lib/active_model/attributes.rb
+++ b/activemodel/lib/active_model/attributes.rb
@@ -29,7 +29,7 @@ module ActiveModel
private
def define_method_attribute=(name)
- safe_name = name.unpack1("h*".freeze)
+ safe_name = name.unpack1("h*")
ActiveModel::AttributeMethods::AttrNames.set_name_cache safe_name, name
generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1
diff --git a/activemodel/lib/active_model/conversion.rb b/activemodel/lib/active_model/conversion.rb
index cdc1282817..82713ddc81 100644
--- a/activemodel/lib/active_model/conversion.rb
+++ b/activemodel/lib/active_model/conversion.rb
@@ -103,7 +103,7 @@ module ActiveModel
@_to_partial_path ||= begin
element = ActiveSupport::Inflector.underscore(ActiveSupport::Inflector.demodulize(name))
collection = ActiveSupport::Inflector.tableize(name)
- "#{collection}/#{element}".freeze
+ "#{collection}/#{element}"
end
end
end
diff --git a/activemodel/lib/active_model/naming.rb b/activemodel/lib/active_model/naming.rb
index 983401801f..bf23fa3c05 100644
--- a/activemodel/lib/active_model/naming.rb
+++ b/activemodel/lib/active_model/naming.rb
@@ -209,7 +209,7 @@ module ActiveModel
private
def _singularize(string)
- ActiveSupport::Inflector.underscore(string).tr("/".freeze, "_".freeze)
+ ActiveSupport::Inflector.underscore(string).tr("/", "_")
end
end
@@ -252,7 +252,7 @@ module ActiveModel
# Person.model_name.plural # => "people"
def model_name
@_model_name ||= begin
- namespace = parents.detect do |n|
+ namespace = module_parents.detect do |n|
n.respond_to?(:use_relative_model_naming?) && n.use_relative_model_naming?
end
ActiveModel::Name.new(self, namespace)
diff --git a/activemodel/lib/active_model/type/string.rb b/activemodel/lib/active_model/type/string.rb
index 36f13945b1..a9c9bfadb6 100644
--- a/activemodel/lib/active_model/type/string.rb
+++ b/activemodel/lib/active_model/type/string.rb
@@ -16,8 +16,8 @@ module ActiveModel
def cast_value(value)
case value
when ::String then ::String.new(value)
- when true then "t".freeze
- when false then "f".freeze
+ when true then "t"
+ when false then "f"
else value.to_s
end
end
diff --git a/activemodel/lib/active_model/validations/validates.rb b/activemodel/lib/active_model/validations/validates.rb
index 88cca318ef..21c4ce0dfe 100644
--- a/activemodel/lib/active_model/validations/validates.rb
+++ b/activemodel/lib/active_model/validations/validates.rb
@@ -116,7 +116,7 @@ module ActiveModel
key = "#{key.to_s.camelize}Validator"
begin
- validator = key.include?("::".freeze) ? key.constantize : const_get(key)
+ validator = key.include?("::") ? key.constantize : const_get(key)
rescue NameError
raise ArgumentError, "Unknown validator: '#{key}'"
end
diff --git a/activemodel/test/cases/attribute_assignment_test.rb b/activemodel/test/cases/attribute_assignment_test.rb
index b06291f1b4..30e8419685 100644
--- a/activemodel/test/cases/attribute_assignment_test.rb
+++ b/activemodel/test/cases/attribute_assignment_test.rb
@@ -100,9 +100,11 @@ class AttributeAssignmentTest < ActiveModel::TestCase
end
test "an ArgumentError is raised if a non-hash-like object is passed" do
- assert_raises(ArgumentError) do
+ err = assert_raises(ArgumentError) do
Model.new(1)
end
+
+ assert_equal("When assigning attributes, you must pass a hash as an argument, Integer passed.", err.message)
end
test "forbidden attributes cannot be used for mass assignment" do
diff --git a/activemodel/test/cases/attribute_set_test.rb b/activemodel/test/cases/attribute_set_test.rb
index b868dba743..62feb9074e 100644
--- a/activemodel/test/cases/attribute_set_test.rb
+++ b/activemodel/test/cases/attribute_set_test.rb
@@ -217,7 +217,7 @@ module ActiveModel
assert_equal({ foo: "1" }, attributes.to_hash)
end
- test "marshaling dump/load legacy materialized attribute hash" do
+ test "marshalling dump/load legacy materialized attribute hash" do
builder = AttributeSet::Builder.new(foo: Type::String.new)
attributes = builder.build_from_database(foo: "1")
diff --git a/activemodel/test/cases/attribute_test.rb b/activemodel/test/cases/attribute_test.rb
index ea2b0efd11..20c02e689c 100644
--- a/activemodel/test/cases/attribute_test.rb
+++ b/activemodel/test/cases/attribute_test.rb
@@ -78,7 +78,7 @@ module ActiveModel
end
test "duping dups the value" do
- @type.expect(:deserialize, "type cast".dup, ["a value"])
+ @type.expect(:deserialize, +"type cast", ["a value"])
attribute = Attribute.from_database(nil, "a value", @type)
value_from_orig = attribute.value
@@ -246,7 +246,7 @@ module ActiveModel
end
test "with_type preserves mutations" do
- attribute = Attribute.from_database(:foo, "".dup, Type::Value.new)
+ attribute = Attribute.from_database(:foo, +"", Type::Value.new)
attribute.value << "1"
assert_equal 1, attribute.with_type(Type::Integer.new).value
diff --git a/activemodel/test/cases/callbacks_test.rb b/activemodel/test/cases/callbacks_test.rb
index 1ec12d8222..0711dc56ca 100644
--- a/activemodel/test/cases/callbacks_test.rb
+++ b/activemodel/test/cases/callbacks_test.rb
@@ -112,7 +112,7 @@ class CallbacksTest < ActiveModel::TestCase
def callback1; history << "callback1"; end
def callback2; history << "callback2"; end
def create
- run_callbacks(:create) {}
+ run_callbacks(:create) { }
self
end
end
diff --git a/activemodel/test/cases/dirty_test.rb b/activemodel/test/cases/dirty_test.rb
index b38d84fff2..0edbbffa86 100644
--- a/activemodel/test/cases/dirty_test.rb
+++ b/activemodel/test/cases/dirty_test.rb
@@ -94,7 +94,7 @@ class DirtyTest < ActiveModel::TestCase
end
test "attribute mutation" do
- @model.instance_variable_set("@name", "Yam".dup)
+ @model.instance_variable_set("@name", +"Yam")
assert_not_predicate @model, :name_changed?
@model.name.replace("Hadad")
assert_not_predicate @model, :name_changed?
diff --git a/activemodel/test/cases/helper.rb b/activemodel/test/cases/helper.rb
index 91fb9d0a7c..138b1d1bb9 100644
--- a/activemodel/test/cases/helper.rb
+++ b/activemodel/test/cases/helper.rb
@@ -14,12 +14,14 @@ require "active_support/testing/method_call_assertions"
class ActiveModel::TestCase < ActiveSupport::TestCase
include ActiveSupport::Testing::MethodCallAssertions
- # Skips the current run on Rubinius using Minitest::Assertions#skip
- private def rubinius_skip(message = "")
- skip message if RUBY_ENGINE == "rbx"
- end
- # Skips the current run on JRuby using Minitest::Assertions#skip
- private def jruby_skip(message = "")
- skip message if defined?(JRUBY_VERSION)
- end
+ private
+ # Skips the current run on Rubinius using Minitest::Assertions#skip
+ def rubinius_skip(message = "")
+ skip message if RUBY_ENGINE == "rbx"
+ end
+
+ # Skips the current run on JRuby using Minitest::Assertions#skip
+ def jruby_skip(message = "")
+ skip message if defined?(JRUBY_VERSION)
+ end
end
diff --git a/activemodel/test/cases/type/string_test.rb b/activemodel/test/cases/type/string_test.rb
index 825c8bb246..2d85556d20 100644
--- a/activemodel/test/cases/type/string_test.rb
+++ b/activemodel/test/cases/type/string_test.rb
@@ -15,11 +15,11 @@ module ActiveModel
test "cast strings are mutable" do
type = Type::String.new
- s = "foo".dup
+ s = +"foo"
assert_equal false, type.cast(s).frozen?
assert_equal false, s.frozen?
- f = "foo".freeze
+ f = -"foo"
assert_equal false, type.cast(f).frozen?
assert_equal true, f.frozen?
end
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index 477bc1b54a..96fd6a62c6 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,3 +1,40 @@
+* Added `index` option for `change_table` migration helpers.
+ With this change you can create indexes while adding new
+ columns into the existing tables.
+
+ Example:
+
+ change_table(:languages) do |t|
+ t.string :country_code, index: true
+ end
+
+ *Mehmet Emin İNAÇ*
+
+* Fix `transaction` reverting for migrations.
+
+ Before: Commands inside a `transaction` in a reverted migration ran uninverted.
+ Now: This change fixes that by reverting commands inside `transaction` block.
+
+ *fatkodima*, *David Verhasselt*
+
+* Raise an error instead of scanning the filesystem root when `fixture_path` is blank.
+
+ *Gannon McGibbon*, *Max Albrecht*
+
+* Allow `ActiveRecord::Base.configurations=` to be set with a symbolized hash.
+
+ *Gannon McGibbon*
+
+* Don't update counter cache unless the record is actually saved.
+
+ Fixes #31493, #33113, #33117.
+
+ *Ryuta Kamizono*
+
+* Deprecate `ActiveRecord::Result#to_hash` in favor of `ActiveRecord::Result#to_a`.
+
+ *Gannon McGibbon*, *Kevin Cheng*
+
* SQLite3 adapter supports expression indexes.
```
diff --git a/activerecord/examples/performance.rb b/activerecord/examples/performance.rb
index 1a2c78f39b..024e503ec7 100644
--- a/activerecord/examples/performance.rb
+++ b/activerecord/examples/performance.rb
@@ -176,7 +176,7 @@ Benchmark.ips(TIME) do |x|
end
x.report "Model.log" do
- Exhibit.connection.send(:log, "hello", "world") {}
+ Exhibit.connection.send(:log, "hello", "world") { }
end
x.report "AR.execute(query)" do
diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb
index 70b2ca7d7e..ab1e7ad269 100644
--- a/activerecord/lib/active_record/associations.rb
+++ b/activerecord/lib/active_record/associations.rb
@@ -1582,7 +1582,7 @@ module ActiveRecord
# 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.
+ # 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.
@@ -1762,6 +1762,7 @@ module ActiveRecord
# has_and_belongs_to_many :projects, -> { includes(:milestones, :manager) }
# has_and_belongs_to_many :categories, ->(post) {
# where("default_category = ?", post.default_category)
+ # }
#
# === Extensions
#
diff --git a/activerecord/lib/active_record/associations/belongs_to_association.rb b/activerecord/lib/active_record/associations/belongs_to_association.rb
index 3d4ad1dd5b..3346725f2d 100644
--- a/activerecord/lib/active_record/associations/belongs_to_association.rb
+++ b/activerecord/lib/active_record/associations/belongs_to_association.rb
@@ -34,14 +34,28 @@ module ActiveRecord
@updated
end
- def decrement_counters # :nodoc:
+ def decrement_counters
update_counters(-1)
end
- def increment_counters # :nodoc:
+ def increment_counters
update_counters(1)
end
+ def decrement_counters_before_last_save
+ if reflection.polymorphic?
+ model_was = owner.attribute_before_last_save(reflection.foreign_type).try(:constantize)
+ else
+ model_was = klass
+ end
+
+ foreign_key_was = owner.attribute_before_last_save(reflection.foreign_key)
+
+ if foreign_key_was && model_was < ActiveRecord::Base
+ update_counters_via_scope(model_was, foreign_key_was, -1)
+ end
+ end
+
def target_changed?
owner.saved_change_to_attribute?(reflection.foreign_key)
end
@@ -50,11 +64,8 @@ module ActiveRecord
def replace(record)
if record
raise_on_type_mismatch!(record)
- update_counters_on_replace(record)
set_inverse_instance(record)
@updated = true
- else
- decrement_counters
end
replace_keys(record)
@@ -67,11 +78,16 @@ module ActiveRecord
if target && !stale_target?
target.increment!(reflection.counter_cache_column, by, touch: reflection.options[:touch])
else
- counter_cache_target.update_counters(reflection.counter_cache_column => by, touch: reflection.options[:touch])
+ update_counters_via_scope(klass, owner._read_attribute(reflection.foreign_key), by)
end
end
end
+ def update_counters_via_scope(klass, foreign_key, by)
+ scope = klass.unscoped.where!(primary_key(klass) => foreign_key)
+ scope.update_counters(reflection.counter_cache_column => by, touch: reflection.options[:touch])
+ end
+
def find_target?
!loaded? && foreign_key_present? && klass
end
@@ -80,25 +96,12 @@ module ActiveRecord
reflection.counter_cache_column && owner.persisted?
end
- def update_counters_on_replace(record)
- if require_counter_update? && different_target?(record)
- owner.instance_variable_set :@_after_replace_counter_called, true
- record.increment!(reflection.counter_cache_column, touch: reflection.options[:touch])
- decrement_counters
- end
- end
-
- # Checks whether record is different to the current target, without loading it
- def different_target?(record)
- record._read_attribute(primary_key(record)) != owner._read_attribute(reflection.foreign_key)
- end
-
def replace_keys(record)
- owner[reflection.foreign_key] = record ? record._read_attribute(primary_key(record)) : nil
+ owner[reflection.foreign_key] = record ? record._read_attribute(primary_key(record.class)) : nil
end
- def primary_key(record)
- reflection.association_primary_key(record.class)
+ def primary_key(klass)
+ reflection.association_primary_key(klass)
end
def foreign_key_present?
@@ -112,11 +115,6 @@ module ActiveRecord
inverse && inverse.has_one?
end
- def counter_cache_target
- primary_key = reflection.association_primary_key(klass)
- klass.unscoped.where!(primary_key => owner._read_attribute(reflection.foreign_key))
- end
-
def stale_state
result = owner._read_attribute(reflection.foreign_key) { |n| owner.send(:missing_attribute, n, caller) }
result && result.to_s
diff --git a/activerecord/lib/active_record/associations/belongs_to_polymorphic_association.rb b/activerecord/lib/active_record/associations/belongs_to_polymorphic_association.rb
index 3fd2fb5f67..9ae452e7a1 100644
--- a/activerecord/lib/active_record/associations/belongs_to_polymorphic_association.rb
+++ b/activerecord/lib/active_record/associations/belongs_to_polymorphic_association.rb
@@ -19,10 +19,6 @@ module ActiveRecord
owner[reflection.foreign_type] = record ? record.class.polymorphic_name : nil
end
- def different_target?(record)
- super || record.class != klass
- end
-
def inverse_reflection_for(record)
reflection.polymorphic_inverse_of(record.class)
end
diff --git a/activerecord/lib/active_record/associations/builder/belongs_to.rb b/activerecord/lib/active_record/associations/builder/belongs_to.rb
index 0166ed98ca..fc00f1e900 100644
--- a/activerecord/lib/active_record/associations/builder/belongs_to.rb
+++ b/activerecord/lib/active_record/associations/builder/belongs_to.rb
@@ -21,56 +21,16 @@ module ActiveRecord::Associations::Builder # :nodoc:
add_default_callbacks(model, reflection) if reflection.options[:default]
end
- def self.define_accessors(mixin, reflection)
- super
- add_counter_cache_methods mixin
- end
-
- def self.add_counter_cache_methods(mixin)
- return if mixin.method_defined? :belongs_to_counter_cache_after_update
-
- mixin.class_eval do
- def belongs_to_counter_cache_after_update(reflection)
- foreign_key = reflection.foreign_key
- cache_column = reflection.counter_cache_column
-
- if @_after_replace_counter_called ||= false
- @_after_replace_counter_called = false
- elsif association(reflection.name).target_changed?
- if reflection.polymorphic?
- 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_before_last_save foreign_key
- foreign_key = attribute_in_database foreign_key
-
- if foreign_key && model < ActiveRecord::Base
- counter_cache_target(reflection, model, foreign_key).update_counters(cache_column => 1)
- end
-
- if foreign_key_was && model_was < ActiveRecord::Base
- counter_cache_target(reflection, model_was, foreign_key_was).update_counters(cache_column => -1)
- end
- end
- end
-
- private
- def counter_cache_target(reflection, model, foreign_key)
- primary_key = reflection.association_primary_key(model)
- model.unscoped.where!(primary_key => foreign_key)
- end
- end
- end
-
def self.add_counter_cache_callbacks(model, reflection)
cache_column = reflection.counter_cache_column
model.after_update lambda { |record|
- record.belongs_to_counter_cache_after_update(reflection)
+ association = association(reflection.name)
+
+ if association.target_changed?
+ association.increment_counters
+ association.decrement_counters_before_last_save
+ end
}
klass = reflection.class_name.safe_constantize
@@ -121,12 +81,18 @@ module ActiveRecord::Associations::Builder # :nodoc:
BelongsTo.touch_record(record, record.send(changes_method), foreign_key, n, touch, belongs_to_touch_method)
}}
- unless reflection.counter_cache_column
+ if reflection.counter_cache_column
+ touch_callback = callback.(:saved_changes)
+ update_callback = lambda { |record|
+ instance_exec(record, &touch_callback) unless association(reflection.name).target_changed?
+ }
+ model.after_update update_callback, if: :saved_changes?
+ else
model.after_create callback.(:saved_changes), if: :saved_changes?
+ model.after_update callback.(:saved_changes), if: :saved_changes?
model.after_destroy callback.(:changes_to_save)
end
- model.after_update callback.(:saved_changes), if: :saved_changes?
model.after_touch callback.(:changes_to_save)
end
diff --git a/activerecord/lib/active_record/associations/builder/collection_association.rb b/activerecord/lib/active_record/associations/builder/collection_association.rb
index 35a72c3850..ff57c40121 100644
--- a/activerecord/lib/active_record/associations/builder/collection_association.rb
+++ b/activerecord/lib/active_record/associations/builder/collection_association.rb
@@ -24,7 +24,7 @@ module ActiveRecord::Associations::Builder # :nodoc:
if block_given?
extension_module_name = "#{model.name.demodulize}#{name.to_s.camelize}AssociationExtension"
extension = Module.new(&Proc.new)
- model.parent.const_set(extension_module_name, extension)
+ model.module_parent.const_set(extension_module_name, extension)
end
end
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 e3070e0472..0140aa15c8 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
@@ -63,7 +63,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("_").gsub("::", "_").to_sym
middle_options = middle_options join_model
HasMany.create_reflection(lhs_model,
diff --git a/activerecord/lib/active_record/associations/preloader.rb b/activerecord/lib/active_record/associations/preloader.rb
index 5c2ac5b374..8997579527 100644
--- a/activerecord/lib/active_record/associations/preloader.rb
+++ b/activerecord/lib/active_record/associations/preloader.rb
@@ -88,7 +88,6 @@ module ActiveRecord
if records.empty?
[]
else
- records.uniq!
Array.wrap(associations).flat_map { |association|
preloaders_on association, records, preload_scope
}
@@ -102,10 +101,8 @@ module ActiveRecord
case association
when Hash
preloaders_for_hash(association, records, scope, polymorphic_parent)
- when Symbol
+ when Symbol, String
preloaders_for_one(association, records, scope, polymorphic_parent)
- when String
- preloaders_for_one(association.to_sym, records, scope, polymorphic_parent)
else
raise ArgumentError, "#{association.inspect} was not recognized for preload"
end
@@ -113,23 +110,21 @@ module ActiveRecord
def preloaders_for_hash(association, records, scope, polymorphic_parent)
association.flat_map { |parent, child|
- loaders = preloaders_for_one parent, records, scope, polymorphic_parent
-
- recs = loaders.flat_map(&:preloaded_records).uniq
-
- reflection = records.first.class._reflect_on_association(parent)
- polymorphic_parent = reflection && reflection.options[:polymorphic]
-
- loaders.concat Array.wrap(child).flat_map { |assoc|
- preloaders_on assoc, recs, scope, polymorphic_parent
- }
- loaders
+ grouped_records(parent, records, polymorphic_parent).flat_map do |reflection, reflection_records|
+ loaders = preloaders_for_reflection(reflection, reflection_records, scope)
+ recs = loaders.flat_map(&:preloaded_records)
+ child_polymorphic_parent = reflection && reflection.options[:polymorphic]
+ loaders.concat Array.wrap(child).flat_map { |assoc|
+ preloaders_on assoc, recs, scope, child_polymorphic_parent
+ }
+ 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
+ # Functions by instantiating a preloader class such as Preloader::Association 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
@@ -140,12 +135,17 @@ module ActiveRecord
# classes, depending on the polymorphic_type field. So we group by the classes as
# well.
def preloaders_for_one(association, records, scope, polymorphic_parent)
- grouped_records(association, records, polymorphic_parent).flat_map do |reflection, klasses|
- klasses.map do |rhs_klass, rs|
- loader = preloader_for(reflection, rs).new(rhs_klass, rs, reflection, scope)
- loader.run self
- loader
+ grouped_records(association, records, polymorphic_parent)
+ .flat_map do |reflection, reflection_records|
+ preloaders_for_reflection reflection, reflection_records, scope
end
+ end
+
+ def preloaders_for_reflection(reflection, records, scope)
+ records.group_by { |record| record.association(reflection.name).klass }.map do |rhs_klass, rs|
+ loader = preloader_for(reflection, rs).new(rhs_klass, rs, reflection, scope)
+ loader.run self
+ loader
end
end
@@ -153,11 +153,9 @@ module ActiveRecord
h = {}
records.each do |record|
next unless record
- next if polymorphic_parent && !record.class._reflect_on_association(association)
- assoc = record.association(association)
- next unless assoc.klass
- klasses = h[assoc.reflection] ||= {}
- (klasses[assoc.klass] ||= []) << record
+ reflection = record.class._reflect_on_association(association)
+ next if polymorphic_parent && !reflection || !record.association(association).klass
+ (h[reflection] ||= []) << record
end
h
end
diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb
index 701f19a6ae..221ebea8ea 100644
--- a/activerecord/lib/active_record/attribute_methods.rb
+++ b/activerecord/lib/active_record/attribute_methods.rb
@@ -194,9 +194,7 @@ module ActiveRecord
def disallow_raw_sql!(args, permit: COLUMN_NAME) # :nodoc:
unexpected = args.reject do |arg|
- arg.kind_of?(Arel::Node) ||
- arg.is_a?(Arel::Nodes::SqlLiteral) ||
- arg.is_a?(Arel::Attributes::Attribute) ||
+ Arel.arel_node?(arg) ||
arg.to_s.split(/\s*,\s*/).all? { |part| permit.match?(part) }
end
@@ -274,9 +272,9 @@ module ActiveRecord
case name
when :to_partial_path
- name = "to_partial_path".freeze
+ name = "to_partial_path"
when :to_model
- name = "to_model".freeze
+ name = "to_model"
else
name = name.to_s
end
diff --git a/activerecord/lib/active_record/attribute_methods/read.rb b/activerecord/lib/active_record/attribute_methods/read.rb
index 0f7bcba564..903fe86e04 100644
--- a/activerecord/lib/active_record/attribute_methods/read.rb
+++ b/activerecord/lib/active_record/attribute_methods/read.rb
@@ -27,7 +27,7 @@ module ActiveRecord
# 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.unpack1("h*".freeze)
+ safe_name = name.unpack1("h*")
temp_method = "__temp__#{safe_name}"
ActiveRecord::AttributeMethods::AttrNames.set_name_cache safe_name, name
@@ -59,7 +59,7 @@ module ActiveRecord
end
primary_key = self.class.primary_key
- name = primary_key if name == "id".freeze && primary_key
+ name = primary_key if name == "id" && primary_key
sync_with_transaction_state if name == primary_key
_read_attribute(name, &block)
end
diff --git a/activerecord/lib/active_record/attribute_methods/write.rb b/activerecord/lib/active_record/attribute_methods/write.rb
index c7521422bb..62743bc9d8 100644
--- a/activerecord/lib/active_record/attribute_methods/write.rb
+++ b/activerecord/lib/active_record/attribute_methods/write.rb
@@ -13,7 +13,7 @@ module ActiveRecord
private
def define_method_attribute=(name)
- safe_name = name.unpack1("h*".freeze)
+ safe_name = name.unpack1("h*")
ActiveRecord::AttributeMethods::AttrNames.set_name_cache safe_name, name
sync_with_transaction_state = "sync_with_transaction_state" if name == primary_key
@@ -40,7 +40,7 @@ module ActiveRecord
end
primary_key = self.class.primary_key
- name = primary_key if name == "id".freeze && primary_key
+ name = primary_key if name == "id" && primary_key
sync_with_transaction_state if name == primary_key
_write_attribute(name, value)
end
diff --git a/activerecord/lib/active_record/callbacks.rb b/activerecord/lib/active_record/callbacks.rb
index b6852bfc71..1bffe89875 100644
--- a/activerecord/lib/active_record/callbacks.rb
+++ b/activerecord/lib/active_record/callbacks.rb
@@ -318,7 +318,7 @@ module ActiveRecord
_run_touch_callbacks { super }
end
- def increment!(*, touch: nil) # :nodoc:
+ def increment!(attribute, by = 1, touch: nil) # :nodoc:
touch ? _run_touch_callbacks { super } : super
end
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 e977d36cb9..99934a0e31 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
@@ -731,7 +731,7 @@ module ActiveRecord
# 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".dup
+ msg = +"could not obtain ownership of all database connections in #{checkout_timeout} seconds"
thread_report = []
@connections.each do |conn|
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 5b24a467ec..0059f0b773 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
@@ -35,7 +35,7 @@ module ActiveRecord
sql, binds = visitor.compile(arel.ast, collector)
query = klass.query(sql)
else
- collector = PartialQueryCollector.new
+ collector = klass.partial_query_collector
parts, binds = visitor.compile(arel.ast, collector)
query = klass.partial_query(parts)
end
@@ -379,7 +379,7 @@ module ActiveRecord
build_fixture_sql(fixtures, table_name)
end.compact
- table_deletes = tables_to_delete.map { |table| "DELETE FROM #{quote_table_name table}".dup }
+ table_deletes = tables_to_delete.map { |table| +"DELETE FROM #{quote_table_name table}" }
total_sql = Array.wrap(combine_multi_statements(table_deletes + fixture_inserts))
disable_referential_integrity do
@@ -410,16 +410,6 @@ module ActiveRecord
end
end
- # The default strategy for an UPDATE with joins is to use a subquery. This doesn't work
- # on MySQL (even when aliasing the tables), but MySQL allows using JOIN directly in
- # an UPDATE statement, so in the MySQL adapters we redefine this to do that.
- def join_to_update(update, select, key) # :nodoc:
- subselect = subquery_for(key, select)
-
- update.where key.in(subselect)
- end
- alias join_to_delete join_to_update
-
private
def default_insert_value(column)
Arel.sql("DEFAULT")
@@ -460,13 +450,6 @@ module ActiveRecord
total_sql.join(";\n")
end
- # Returns a subquery for the given key using the join information.
- def subquery_for(key, select)
- subselect = select.clone
- subselect.projections = [key]
- subselect
- end
-
# Returns an ActiveRecord::Result instance.
def select(sql, name = nil, binds = [])
exec_query(sql, name, binds, prepare: false)
@@ -507,28 +490,6 @@ module ActiveRecord
value
end
end
-
- class PartialQueryCollector
- def initialize
- @parts = []
- @binds = []
- end
-
- def <<(str)
- @parts << str
- self
- end
-
- def add_bind(obj)
- @binds << obj
- @parts << Arel::Nodes::BindParam.new(1)
- self
- end
-
- def value
- [@parts, @binds]
- end
- 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 98b1348135..07e86afe9a 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
@@ -60,7 +60,7 @@ module ActiveRecord
# Quotes a string, escaping any ' (single quote) and \ (backslash)
# characters.
def quote_string(s)
- s.gsub('\\'.freeze, '\&\&'.freeze).gsub("'".freeze, "''".freeze) # ' (for ruby-mode)
+ s.gsub('\\', '\&\&').gsub("'", "''") # ' (for ruby-mode)
end
# Quotes the column name. Defaults to no quoting.
@@ -95,7 +95,7 @@ module ActiveRecord
end
def quoted_true
- "TRUE".freeze
+ "TRUE"
end
def unquoted_true
@@ -103,7 +103,7 @@ module ActiveRecord
end
def quoted_false
- "FALSE".freeze
+ "FALSE"
end
def unquoted_false
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 529c9d8ca6..9d9e8a4110 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_creation.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_creation.rb
@@ -21,7 +21,7 @@ module ActiveRecord
private
def visit_AlterTable(o)
- sql = "ALTER TABLE #{quote_table_name(o.name)} ".dup
+ 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(" ")
@@ -29,17 +29,17 @@ module ActiveRecord
def visit_ColumnDefinition(o)
o.sql_type = type_to_sql(o.type, o.options)
- column_sql = "#{quote_column_name(o.name)} #{o.sql_type}".dup
+ column_sql = +"#{quote_column_name(o.name)} #{o.sql_type}"
add_column_options!(column_sql, column_options(o)) unless o.type == :primary_key
column_sql
end
def visit_AddColumnDefinition(o)
- "ADD #{accept(o.column)}".dup
+ +"ADD #{accept(o.column)}"
end
def visit_TableDefinition(o)
- create_sql = "CREATE#{' TEMPORARY' if o.temporary} TABLE #{quote_table_name(o.name)} ".dup
+ create_sql = +"CREATE#{' TEMPORARY' if o.temporary} TABLE #{quote_table_name(o.name)} "
statements = o.columns.map { |c| accept c }
statements << accept(o.primary_keys) if o.primary_keys
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 582ac516c7..015204c056 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
@@ -527,7 +527,9 @@ module ActiveRecord
#
# See TableDefinition#column for details of the options you can use.
def column(column_name, type, options = {})
+ index_options = options.delete(:index)
@base.add_column(name, column_name, type, options)
+ index(column_name, index_options.is_a?(Hash) ? index_options : {}) if index_options
end
# Checks to see if a column exists.
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 4a6e915b66..723d8c318d 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
@@ -211,13 +211,13 @@ module ActiveRecord
#
# ====== Add a backend specific option to the generated SQL (MySQL)
#
- # create_table(:suppliers, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8')
+ # create_table(:suppliers, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8mb4')
#
# generates:
#
# CREATE TABLE suppliers (
# id bigint auto_increment PRIMARY KEY
- # ) ENGINE=InnoDB DEFAULT CHARSET=utf8
+ # ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
#
# ====== Rename the primary key column
#
@@ -1380,7 +1380,7 @@ module ActiveRecord
sm_table = quote_table_name(ActiveRecord::SchemaMigration.table_name)
if versions.is_a?(Array)
- sql = "INSERT INTO #{sm_table} (version) VALUES\n".dup
+ sql = +"INSERT INTO #{sm_table} (version) VALUES\n"
sql << versions.map { |v| "(#{quote(v)})" }.join(",\n")
sql << ";\n\n"
sql
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
index 42ed9ce82d..fa10f18cb7 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
@@ -65,7 +65,7 @@ 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"
include ActiveSupport::Callbacks
define_callbacks :checkout, :checkin
@@ -162,7 +162,7 @@ module ActiveRecord
# this method must only be called while holding connection pool's mutex
def lease
if in_use?
- msg = "Cannot lease connection, ".dup
+ msg = +"Cannot lease connection, "
if @owner == Thread.current
msg << "it is already leased by the current thread."
else
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 5d4fc8324f..d40f38fb77 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
@@ -43,9 +43,11 @@ module ActiveRecord
}
class StatementPool < ConnectionAdapters::StatementPool # :nodoc:
- private def dealloc(stmt)
- stmt.close
- end
+ private
+
+ def dealloc(stmt)
+ stmt.close
+ end
end
def initialize(connection, logger, connection_options, config)
@@ -135,7 +137,7 @@ module ActiveRecord
end
def index_algorithms
- { default: "ALGORITHM = DEFAULT".dup, copy: "ALGORITHM = COPY".dup, inplace: "ALGORITHM = INPLACE".dup }
+ { default: +"ALGORITHM = DEFAULT", copy: +"ALGORITHM = COPY", inplace: +"ALGORITHM = INPLACE" }
end
# HELPER METHODS ===========================================
@@ -221,18 +223,6 @@ module ActiveRecord
execute "ROLLBACK"
end
- # In the simple case, MySQL allows us to place JOINs directly into the UPDATE
- # query. However, this does not allow for LIMIT, OFFSET and ORDER. To support
- # these, we must use a subquery.
- def join_to_update(update, select, key) # :nodoc:
- if select.limit || select.offset || select.orders.any?
- super
- else
- update.table select.source
- update.wheres = select.constraints
- end
- end
-
def empty_insert_statement_value(primary_key = nil)
"VALUES ()"
end
@@ -390,7 +380,7 @@ module ActiveRecord
def add_index(table_name, column_name, options = {}) #:nodoc:
index_name, index_type, index_columns, _, index_algorithm, index_using, comment = add_index_options(table_name, column_name, options)
- sql = "CREATE #{index_type} INDEX #{quote_column_name(index_name)} #{index_using} ON #{quote_table_name(table_name)} (#{index_columns}) #{index_algorithm}".dup
+ sql = +"CREATE #{index_type} INDEX #{quote_column_name(index_name)} #{index_using} ON #{quote_table_name(table_name)} (#{index_columns}) #{index_algorithm}"
execute add_sql_comment!(sql, comment)
end
@@ -731,20 +721,6 @@ module ActiveRecord
[remove_column_for_alter(table_name, :updated_at), remove_column_for_alter(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)
- subselect = select.clone
- subselect.projections = [key]
-
- # Materialize subquery by adding distinct
- # to work with MySQL 5.7.6 which sets optimizer_switch='derived_merge=on'
- subselect.distinct unless select.limit || select.offset || select.orders.any?
-
- key_name = quote_column_name(key.name)
- Arel::SelectManager.new(subselect.as("__active_record_temp")).project(Arel.sql(key_name))
- end
-
def supports_rename_index?
mariadb? ? false : version >= "5.7.6"
end
@@ -783,7 +759,7 @@ module ActiveRecord
# https://dev.mysql.com/doc/refman/5.7/en/set-names.html
# (trailing comma because variable_assignments will always have content)
if @config[:encoding]
- encoding = "NAMES #{@config[:encoding]}".dup
+ encoding = +"NAMES #{@config[:encoding]}"
encoding << " COLLATE #{@config[:collation]}" if @config[:collation]
encoding << ", "
end
diff --git a/activerecord/lib/active_record/connection_adapters/mysql/quoting.rb b/activerecord/lib/active_record/connection_adapters/mysql/quoting.rb
index be038403b8..75564a61d6 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql/quoting.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql/quoting.rb
@@ -5,7 +5,7 @@ module ActiveRecord
module MySQL
module Quoting # :nodoc:
def quote_column_name(name)
- @quoted_column_names[name] ||= "`#{super.gsub('`', '``')}`".freeze
+ @quoted_column_names[name] ||= "`#{super.gsub('`', '``')}`"
end
def quote_table_name(name)
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 c9ea653b77..82ed320617 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql/schema_creation.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql/schema_creation.rb
@@ -17,7 +17,7 @@ module ActiveRecord
end
def visit_ChangeColumnDefinition(o)
- change_column_sql = "CHANGE #{quote_column_name(o.name)} #{accept(o.column)}".dup
+ change_column_sql = +"CHANGE #{quote_column_name(o.name)} #{accept(o.column)}"
add_column_position!(change_column_sql, column_options(o.column))
end
@@ -64,7 +64,7 @@ module ActiveRecord
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})".dup, comment)
+ add_sql_comment!((+"#{index_type} INDEX #{quote_column_name(index_name)} #{index_using} (#{index_columns})"), comment)
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/mysql/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/mysql/schema_statements.rb
index 1cf210d85b..e167c01802 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql/schema_statements.rb
@@ -121,7 +121,7 @@ module ActiveRecord
def data_source_sql(name = nil, type: nil)
scope = quoted_scope(name, type: type)
- sql = "SELECT table_name FROM information_schema.tables".dup
+ sql = +"SELECT table_name FROM information_schema.tables"
sql << " WHERE table_schema = #{scope[:schema]}"
sql << " AND table_name = #{scope[:name]}" if scope[:name]
sql << " AND table_type = #{scope[:type]}" if scope[:type]
diff --git a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb
index 10c8c8e8ab..9bdaa00336 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb
@@ -14,7 +14,7 @@ module ActiveRecord
config[:flags] ||= 0
if config[:flags].kind_of? Array
- config[:flags].push "FOUND_ROWS".freeze
+ config[:flags].push "FOUND_ROWS"
else
config[:flags] |= Mysql2::Client::FOUND_ROWS
end
@@ -32,7 +32,7 @@ module ActiveRecord
module ConnectionAdapters
class Mysql2Adapter < AbstractMysqlAdapter
- ADAPTER_NAME = "Mysql2".freeze
+ ADAPTER_NAME = "Mysql2"
include MySQL::DatabaseStatements
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 26abeea7ed..6fbeaa2b9e 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/array.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/array.rb
@@ -33,7 +33,13 @@ module ActiveRecord
def cast(value)
if value.is_a?(::String)
- value = @pg_decoder.decode(value)
+ value = begin
+ @pg_decoder.decode(value)
+ rescue TypeError
+ # malformed array string is treated as [], will raise in PG 2.0 gem
+ # this keeps a consistent implementation
+ []
+ end
end
type_cast_array(value, :cast)
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 79351bc3a4..83c21ba6ea 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
@@ -19,10 +19,10 @@ module ActiveRecord
def run(records)
nodes = records.reject { |row| @store.key? row["oid"].to_i }
mapped = nodes.extract! { |row| @store.key? row["typname"] }
- ranges = nodes.extract! { |row| row["typtype"] == "r".freeze }
- enums = nodes.extract! { |row| row["typtype"] == "e".freeze }
- domains = nodes.extract! { |row| row["typtype"] == "d".freeze }
- arrays = nodes.extract! { |row| row["typinput"] == "array_in".freeze }
+ ranges = nodes.extract! { |row| row["typtype"] == "r" }
+ enums = nodes.extract! { |row| row["typtype"] == "e" }
+ domains = nodes.extract! { |row| row["typtype"] == "d" }
+ arrays = nodes.extract! { |row| row["typinput"] == "array_in" }
composites = nodes.extract! { |row| row["typelem"].to_i != 0 }
mapped.each { |row| register_mapped_type(row) }
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 00da7690a2..fae3ddbad4 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
@@ -686,7 +686,7 @@ module ActiveRecord
def change_column_sql(table_name, column_name, type, options = {})
quoted_column_name = quote_column_name(column_name)
sql_type = type_to_sql(type, options)
- sql = "ALTER COLUMN #{quoted_column_name} TYPE #{sql_type}".dup
+ sql = +"ALTER COLUMN #{quoted_column_name} TYPE #{sql_type}"
if options[:collation]
sql << " COLLATE \"#{options[:collation]}\""
end
@@ -757,7 +757,7 @@ module ActiveRecord
scope = quoted_scope(name, type: type)
scope[:type] ||= "'r','v','m','p','f'" # (r)elation/table, (v)iew, (m)aterialized view, (p)artitioned table, (f)oreign table
- sql = "SELECT c.relname FROM pg_class c LEFT JOIN pg_namespace n ON n.oid = c.relnamespace".dup
+ sql = +"SELECT c.relname FROM pg_class c LEFT JOIN pg_namespace n ON n.oid = c.relnamespace"
sql << " WHERE n.nspname = #{scope[:schema]}"
sql << " AND c.relname = #{scope[:name]}" if scope[:name]
sql << " AND c.relkind IN (#{scope[:type]})"
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 ffd3be26b0..cd69d28139 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/type_metadata.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/type_metadata.rb
@@ -17,7 +17,7 @@ module ActiveRecord
end
def sql_type
- super.gsub(/\[\]$/, "".freeze)
+ super.gsub(/\[\]$/, "")
end
def ==(other)
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
index 3ee344a249..bc6eb11572 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -4,6 +4,14 @@
gem "pg", ">= 0.18", "< 2.0"
require "pg"
+# Use async_exec instead of exec_params on pg versions before 1.1
+class ::PG::Connection
+ unless self.public_method_defined?(:async_exec_params)
+ remove_method :exec_params
+ alias exec_params async_exec
+ end
+end
+
require "active_record/connection_adapters/abstract_adapter"
require "active_record/connection_adapters/statement_pool"
require "active_record/connection_adapters/postgresql/column"
@@ -70,7 +78,7 @@ module ActiveRecord
# In addition, default connection parameters of libpq can be set per environment variables.
# See https://www.postgresql.org/docs/current/static/libpq-envars.html .
class PostgreSQLAdapter < AbstractAdapter
- ADAPTER_NAME = "PostgreSQL".freeze
+ ADAPTER_NAME = "PostgreSQL"
NATIVE_DATABASE_TYPES = {
primary_key: "bigserial primary key",
@@ -440,7 +448,7 @@ module ActiveRecord
end
end
- def get_oid_type(oid, fmod, column_name, sql_type = "".freeze)
+ def get_oid_type(oid, fmod, column_name, sql_type = "")
if !type_map.key?(oid)
load_additional_types([oid])
end
@@ -529,13 +537,13 @@ module ActiveRecord
# Quoted types
when /\A[\(B]?'(.*)'.*::"?([\w. ]+)"?(?:\[\])?\z/m
# The default 'now'::date is CURRENT_DATE
- if $1 == "now".freeze && $2 == "date".freeze
+ if $1 == "now" && $2 == "date"
nil
else
- $1.gsub("''".freeze, "'".freeze)
+ $1.gsub("''", "'")
end
# Boolean types
- when "true".freeze, "false".freeze
+ when "true", "false"
default
# Numeric types
when /\A\(?(-?\d+(\.\d*)?)\)?(::bigint)?\z/
@@ -606,7 +614,7 @@ module ActiveRecord
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)
+ @connection.exec_params(sql, type_casted_binds)
end
end
end
@@ -647,7 +655,7 @@ module ActiveRecord
#
# Check here for more details:
# https://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"
def is_cached_plan_failure?(e)
pgerror = e.cause
code = pgerror.result.result_error_field(PG::PG_DIAG_SQLSTATE)
diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3/quoting.rb b/activerecord/lib/active_record/connection_adapters/sqlite3/quoting.rb
index abedf01f10..b2dcdb5373 100644
--- a/activerecord/lib/active_record/connection_adapters/sqlite3/quoting.rb
+++ b/activerecord/lib/active_record/connection_adapters/sqlite3/quoting.rb
@@ -13,7 +13,7 @@ module ActiveRecord
end
def quote_column_name(name)
- @quoted_column_names[name] ||= %Q("#{super.gsub('"', '""')}").freeze
+ @quoted_column_names[name] ||= %Q("#{super.gsub('"', '""')}")
end
def quoted_time(value)
@@ -26,19 +26,19 @@ module ActiveRecord
end
def quoted_true
- ActiveRecord::ConnectionAdapters::SQLite3Adapter.represent_boolean_as_integer ? "1".freeze : "'t'".freeze
+ ActiveRecord::ConnectionAdapters::SQLite3Adapter.represent_boolean_as_integer ? "1" : "'t'"
end
def unquoted_true
- ActiveRecord::ConnectionAdapters::SQLite3Adapter.represent_boolean_as_integer ? 1 : "t".freeze
+ ActiveRecord::ConnectionAdapters::SQLite3Adapter.represent_boolean_as_integer ? 1 : "t"
end
def quoted_false
- ActiveRecord::ConnectionAdapters::SQLite3Adapter.represent_boolean_as_integer ? "0".freeze : "'f'".freeze
+ ActiveRecord::ConnectionAdapters::SQLite3Adapter.represent_boolean_as_integer ? "0" : "'f'"
end
def unquoted_false
- ActiveRecord::ConnectionAdapters::SQLite3Adapter.represent_boolean_as_integer ? 0 : "f".freeze
+ ActiveRecord::ConnectionAdapters::SQLite3Adapter.represent_boolean_as_integer ? 0 : "f"
end
private
diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/sqlite3/schema_statements.rb
index 46ca7e07a9..48277f0ae2 100644
--- a/activerecord/lib/active_record/connection_adapters/sqlite3/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/sqlite3/schema_statements.rb
@@ -86,7 +86,7 @@ module ActiveRecord
scope = quoted_scope(name, type: type)
scope[:type] ||= "'table','view'"
- sql = "SELECT name FROM sqlite_master WHERE name <> 'sqlite_sequence'".dup
+ sql = +"SELECT name FROM sqlite_master WHERE name <> 'sqlite_sequence'"
sql << " AND name = #{scope[:name]}" if scope[:name]
sql << " AND type IN (#{scope[:type]})"
sql
diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
index d32fd5ea09..81882f6cc1 100644
--- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
@@ -56,7 +56,7 @@ module ActiveRecord
#
# * <tt>:database</tt> - Path to the database file.
class SQLite3Adapter < AbstractAdapter
- ADAPTER_NAME = "SQLite".freeze
+ ADAPTER_NAME = "SQLite"
include SQLite3::Quoting
include SQLite3::SchemaStatements
@@ -576,7 +576,7 @@ module ActiveRecord
column
end
else
- basic_structure.to_hash
+ basic_structure.to_a
end
end
diff --git a/activerecord/lib/active_record/counter_cache.rb b/activerecord/lib/active_record/counter_cache.rb
index aad30b40ea..27c1b7a311 100644
--- a/activerecord/lib/active_record/counter_cache.rb
+++ b/activerecord/lib/active_record/counter_cache.rb
@@ -163,9 +163,7 @@ module ActiveRecord
id = super
each_counter_cached_associations do |association|
- if send(association.reflection.name)
- association.increment_counters
- end
+ association.increment_counters
end
id
@@ -178,9 +176,7 @@ module ActiveRecord
each_counter_cached_associations do |association|
foreign_key = association.reflection.foreign_key.to_sym
unless destroyed_by_association && destroyed_by_association.foreign_key.to_sym == foreign_key
- if send(association.reflection.name)
- association.decrement_counters
- end
+ association.decrement_counters
end
end
end
diff --git a/activerecord/lib/active_record/database_configurations.rb b/activerecord/lib/active_record/database_configurations.rb
index 9ff63c6e10..fa1589511e 100644
--- a/activerecord/lib/active_record/database_configurations.rb
+++ b/activerecord/lib/active_record/database_configurations.rb
@@ -104,7 +104,7 @@ module ActiveRecord
return configs.configurations if configs.is_a?(DatabaseConfigurations)
build_db_config = configs.each_pair.flat_map do |env_name, config|
- walk_configs(env_name, "primary", config)
+ walk_configs(env_name.to_s, "primary", config)
end.compact
if url = ENV["DATABASE_URL"]
@@ -119,7 +119,7 @@ module ActiveRecord
when String
build_db_config_from_string(env_name, spec_name, config)
when Hash
- build_db_config_from_hash(env_name, spec_name, config)
+ build_db_config_from_hash(env_name, spec_name, config.stringify_keys)
end
end
diff --git a/activerecord/lib/active_record/database_configurations/database_config.rb b/activerecord/lib/active_record/database_configurations/database_config.rb
index 6250827b34..adc37cc439 100644
--- a/activerecord/lib/active_record/database_configurations/database_config.rb
+++ b/activerecord/lib/active_record/database_configurations/database_config.rb
@@ -17,6 +17,10 @@ module ActiveRecord
raise NotImplementedError
end
+ def migrations_paths
+ raise NotImplementedError
+ end
+
def url_config?
false
end
diff --git a/activerecord/lib/active_record/database_configurations/hash_config.rb b/activerecord/lib/active_record/database_configurations/hash_config.rb
index 13ffe566cf..c176a62458 100644
--- a/activerecord/lib/active_record/database_configurations/hash_config.rb
+++ b/activerecord/lib/active_record/database_configurations/hash_config.rb
@@ -38,6 +38,13 @@ module ActiveRecord
def replica?
config["replica"]
end
+
+ # The migrations paths for a database configuration. If the
+ # `migrations_paths` key is present in the config, `migrations_paths`
+ # will return its value.
+ def migrations_paths
+ config["migrations_paths"]
+ end
end
end
end
diff --git a/activerecord/lib/active_record/database_configurations/url_config.rb b/activerecord/lib/active_record/database_configurations/url_config.rb
index f526c59d56..81917fc4c1 100644
--- a/activerecord/lib/active_record/database_configurations/url_config.rb
+++ b/activerecord/lib/active_record/database_configurations/url_config.rb
@@ -48,6 +48,13 @@ module ActiveRecord
config["replica"]
end
+ # The migrations paths for a database configuration. If the
+ # `migrations_paths` key is present in the config, `migrations_paths`
+ # will return its value.
+ def migrations_paths
+ config["migrations_paths"]
+ end
+
private
def build_config(original_config, url)
if /^jdbc:/.match?(url)
diff --git a/activerecord/lib/active_record/explain.rb b/activerecord/lib/active_record/explain.rb
index 7ccb938888..919e96cd7a 100644
--- a/activerecord/lib/active_record/explain.rb
+++ b/activerecord/lib/active_record/explain.rb
@@ -18,7 +18,7 @@ module ActiveRecord
# Returns a formatted string ready to be logged.
def exec_explain(queries) # :nodoc:
str = queries.map do |sql, binds|
- msg = "EXPLAIN for: #{sql}".dup
+ msg = +"EXPLAIN for: #{sql}"
unless binds.empty?
msg << " "
msg << binds.map { |attr| render_bind(attr) }.inspect
diff --git a/activerecord/lib/active_record/fixture_set/render_context.rb b/activerecord/lib/active_record/fixture_set/render_context.rb
new file mode 100644
index 0000000000..c90b5343dc
--- /dev/null
+++ b/activerecord/lib/active_record/fixture_set/render_context.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+# NOTE: This class has to be defined in compact style in
+# order for rendering context subclassing to work correctly.
+class ActiveRecord::FixtureSet::RenderContext # :nodoc:
+ def self.create_subclass
+ Class.new(ActiveRecord::FixtureSet.context_class) do
+ def get_binding
+ binding()
+ end
+
+ def binary(path)
+ %(!!binary "#{Base64.strict_encode64(File.read(path))}")
+ end
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb
index 6e0f1a0dfb..e697c30bb6 100644
--- a/activerecord/lib/active_record/fixtures.rb
+++ b/activerecord/lib/active_record/fixtures.rb
@@ -7,6 +7,8 @@ require "set"
require "active_support/dependencies"
require "active_support/core_ext/digest/uuid"
require "active_record/fixture_set/file"
+require "active_record/fixture_set/render_context"
+require "active_record/test_fixtures"
require "active_record/errors"
module ActiveRecord
@@ -851,216 +853,3 @@ module ActiveRecord
end
end
end
-
-module ActiveRecord
- module TestFixtures
- extend ActiveSupport::Concern
-
- def before_setup # :nodoc:
- setup_fixtures
- super
- end
-
- def after_teardown # :nodoc:
- super
- teardown_fixtures
- end
-
- included do
- class_attribute :fixture_path, instance_writer: false
- class_attribute :fixture_table_names, default: []
- class_attribute :fixture_class_names, default: {}
- class_attribute :use_transactional_tests, default: true
- class_attribute :use_instantiated_fixtures, default: false # true, false, or :no_instances
- class_attribute :pre_loaded_fixtures, default: false
- class_attribute :config, default: ActiveRecord::Base
- class_attribute :lock_threads, default: true
- end
-
- module ClassMethods
- # Sets the model class for a fixture when the class name cannot be inferred from the fixture name.
- #
- # Examples:
- #
- # set_fixture_class some_fixture: SomeModel,
- # 'namespaced/fixture' => Another::Model
- #
- # The keys must be the fixture names, that coincide with the short paths to the fixture files.
- def set_fixture_class(class_names = {})
- self.fixture_class_names = fixture_class_names.merge(class_names.stringify_keys)
- end
-
- def fixtures(*fixture_set_names)
- if fixture_set_names.first == :all
- 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)
- end
-
- self.fixture_table_names |= fixture_set_names
- setup_fixture_accessors(fixture_set_names)
- end
-
- def setup_fixture_accessors(fixture_set_names = nil)
- fixture_set_names = Array(fixture_set_names || fixture_table_names)
- methods = Module.new do
- fixture_set_names.each do |fs_name|
- fs_name = fs_name.to_s
- 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
- return_single_record = fixture_names.size == 1
- fixture_names = @loaded_fixtures[fs_name].fixtures.keys if fixture_names.empty?
-
- @fixture_cache[fs_name] ||= {}
-
- instances = fixture_names.map do |f_name|
- f_name = f_name.to_s if f_name.is_a?(Symbol)
- @fixture_cache[fs_name].delete(f_name) if force_reload
-
- if @loaded_fixtures[fs_name][f_name]
- @fixture_cache[fs_name][f_name] ||= @loaded_fixtures[fs_name][f_name].find
- else
- raise StandardError, "No fixture named '#{f_name}' found for fixture set '#{fs_name}'"
- end
- end
-
- return_single_record ? instances.first : instances
- end
- private accessor_name
- end
- end
- include methods
- end
-
- def uses_transaction(*methods)
- @uses_transaction = [] unless defined?(@uses_transaction)
- @uses_transaction.concat methods.map(&:to_s)
- end
-
- def uses_transaction?(method)
- @uses_transaction = [] unless defined?(@uses_transaction)
- @uses_transaction.include?(method.to_s)
- end
- end
-
- def run_in_transaction?
- use_transactional_tests &&
- !self.class.uses_transaction?(method_name)
- end
-
- def setup_fixtures(config = ActiveRecord::Base)
- if pre_loaded_fixtures && !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?
- if @@already_loaded_fixtures[self.class]
- @loaded_fixtures = @@already_loaded_fixtures[self.class]
- else
- @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
- connection.pool.lock_thread = true if lock_threads
- 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
- connection.pool.lock_thread = true if lock_threads
- @fixture_connections << connection
- end
- end
- end
-
- # Load fixtures for every test.
- else
- ActiveRecord::FixtureSet.reset_cache
- @@already_loaded_fixtures[self.class] = nil
- @loaded_fixtures = load_fixtures(config)
- end
-
- # Instantiate fixtures for every test if requested.
- instantiate_fixtures if use_instantiated_fixtures
- end
-
- 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?
- connection.pool.lock_thread = false
- end
- @fixture_connections.clear
- else
- ActiveRecord::FixtureSet.reset_cache
- end
-
- ActiveRecord::Base.clear_active_connections!
- end
-
- def enlist_fixture_connections
- ActiveRecord::Base.connection_handler.connection_pool_list.map(&:connection)
- end
-
- private
- def load_fixtures(config)
- fixtures = ActiveRecord::FixtureSet.create_fixtures(fixture_path, fixture_table_names, fixture_class_names, config)
- Hash[fixtures.map { |f| [f.name, f] }]
- end
-
- def instantiate_fixtures
- if pre_loaded_fixtures
- 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?
- @loaded_fixtures.each_value do |fixture_set|
- ActiveRecord::FixtureSet.instantiate_fixtures(self, fixture_set, load_instances?)
- end
- end
- end
-
- def load_instances?
- use_instantiated_fixtures != :no_instances
- end
- end
-end
-
-class ActiveRecord::FixtureSet::RenderContext # :nodoc:
- def self.create_subclass
- Class.new ActiveRecord::FixtureSet.context_class do
- def get_binding
- binding()
- end
-
- def binary(path)
- %(!!binary "#{Base64.strict_encode64(File.read(path))}")
- end
- end
- end
-end
diff --git a/activerecord/lib/active_record/inheritance.rb b/activerecord/lib/active_record/inheritance.rb
index b25057acda..138fd1cf53 100644
--- a/activerecord/lib/active_record/inheritance.rb
+++ b/activerecord/lib/active_record/inheritance.rb
@@ -180,7 +180,7 @@ module ActiveRecord
# 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 type_name.start_with?("::")
# 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)
diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb
index ea53324829..6e5a610642 100644
--- a/activerecord/lib/active_record/migration.rb
+++ b/activerecord/lib/active_record/migration.rb
@@ -140,8 +140,8 @@ module ActiveRecord
end
class ConcurrentMigrationError < MigrationError #:nodoc:
- DEFAULT_MESSAGE = "Cannot run migrations because another migration process is currently running.".freeze
- RELEASE_LOCK_FAILED_MESSAGE = "Failed to release advisory lock".freeze
+ DEFAULT_MESSAGE = "Cannot run migrations because another migration process is currently running."
+ RELEASE_LOCK_FAILED_MESSAGE = "Failed to release advisory lock"
def initialize(message = DEFAULT_MESSAGE)
super
@@ -161,7 +161,7 @@ module ActiveRecord
class ProtectedEnvironmentError < ActiveRecordError #:nodoc:
def initialize(env = "production")
- msg = "You are attempting to run a destructive action against your '#{env}' database.\n".dup
+ msg = +"You are attempting to run a destructive action against your '#{env}' database.\n"
msg << "If you are sure you want to continue, run the same command with the environment variable:\n"
msg << "DISABLE_DATABASE_ENVIRONMENT_CHECK=1"
super(msg)
@@ -170,7 +170,7 @@ module ActiveRecord
class EnvironmentMismatchError < ActiveRecordError
def initialize(current: nil, stored: nil)
- msg = "You are attempting to modify a database that was last run in `#{ stored }` environment.\n".dup
+ 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 << " rails db:environment:set"
@@ -678,15 +678,13 @@ module ActiveRecord
if connection.respond_to? :revert
connection.revert { yield }
else
- recorder = CommandRecorder.new(connection)
+ recorder = command_recorder
@connection = recorder
suppress_messages do
connection.revert { yield }
end
@connection = recorder.delegate
- recorder.commands.each do |cmd, args, block|
- send(cmd, *args, &block)
- end
+ recorder.replay(self)
end
end
end
@@ -891,7 +889,7 @@ module ActiveRecord
source_migrations.each do |migration|
source = File.binread(migration.filename)
inserted_comment = "# This migration comes from #{scope} (originally #{migration.version})\n"
- magic_comments = "".dup
+ magic_comments = +""
loop do
# If we have a magic comment in the original migration,
# insert our comment after the first newline(end of the magic comment line)
@@ -962,6 +960,10 @@ module ActiveRecord
yield
end
end
+
+ def command_recorder
+ CommandRecorder.new(connection)
+ end
end
# MigrationProxy is used to defer loading of the actual migration classes
@@ -1169,7 +1171,7 @@ module ActiveRecord
def migrations_path=(path)
ActiveSupport::Deprecation.warn \
- "ActiveRecord::Migrator.migrations_paths= is now deprecated and will be removed in Rails 6.0." \
+ "`ActiveRecord::Migrator.migrations_path=` is now deprecated and will be removed in Rails 6.0. " \
"You can set the `migrations_paths` on the `connection` instead through the `database.yml`."
self.migrations_paths = [path]
end
@@ -1299,7 +1301,7 @@ module ActiveRecord
record_version_state_after_migrating(migration.version)
end
rescue => e
- msg = "An error has occurred, ".dup
+ 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
diff --git a/activerecord/lib/active_record/migration/command_recorder.rb b/activerecord/lib/active_record/migration/command_recorder.rb
index dea6d4ec08..82f5121d94 100644
--- a/activerecord/lib/active_record/migration/command_recorder.rb
+++ b/activerecord/lib/active_record/migration/command_recorder.rb
@@ -108,11 +108,17 @@ module ActiveRecord
yield delegate.update_table_definition(table_name, self)
end
+ def replay(migration)
+ commands.each do |cmd, args, block|
+ migration.send(cmd, *args, &block)
+ end
+ end
+
private
module StraightReversions # :nodoc:
private
- { transaction: :transaction,
+ {
execute_block: :execute_block,
create_table: :drop_table,
create_join_table: :drop_join_table,
@@ -133,6 +139,17 @@ module ActiveRecord
include StraightReversions
+ def invert_transaction(args)
+ sub_recorder = CommandRecorder.new(delegate)
+ sub_recorder.revert { yield }
+
+ invertions_proc = proc {
+ sub_recorder.replay(self)
+ }
+
+ [:transaction, args, invertions_proc]
+ end
+
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)."
diff --git a/activerecord/lib/active_record/migration/compatibility.rb b/activerecord/lib/active_record/migration/compatibility.rb
index 0edaaa0cf9..8f6fcfcaea 100644
--- a/activerecord/lib/active_record/migration/compatibility.rb
+++ b/activerecord/lib/active_record/migration/compatibility.rb
@@ -16,6 +16,21 @@ module ActiveRecord
V6_0 = Current
class V5_2 < V6_0
+ module CommandRecorder
+ def invert_transaction(args, &block)
+ [:transaction, args, block]
+ end
+ end
+
+ private
+
+ def command_recorder
+ recorder = super
+ class << recorder
+ prepend CommandRecorder
+ end
+ recorder
+ end
end
class V5_1 < V5_2
diff --git a/activerecord/lib/active_record/model_schema.rb b/activerecord/lib/active_record/model_schema.rb
index 694ff85fa1..5a4a1fb969 100644
--- a/activerecord/lib/active_record/model_schema.rb
+++ b/activerecord/lib/active_record/model_schema.rb
@@ -218,11 +218,11 @@ module ActiveRecord
end
def full_table_name_prefix #:nodoc:
- (parents.detect { |p| p.respond_to?(:table_name_prefix) } || self).table_name_prefix
+ (module_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
+ (module_parents.detect { |p| p.respond_to?(:table_name_suffix) } || self).table_name_suffix
end
# The array of names of environments where destructive actions should be prohibited. By default,
@@ -375,7 +375,7 @@ module ActiveRecord
# default values when instantiating the Active Record object for this table.
def column_defaults
load_schema
- @column_defaults ||= _default_attributes.to_hash
+ @column_defaults ||= _default_attributes.deep_dup.to_hash
end
def _default_attributes # :nodoc:
@@ -503,9 +503,9 @@ module ActiveRecord
def compute_table_name
if base_class?
# 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
+ if module_parent < Base && !module_parent.abstract_class?
+ contained = module_parent.table_name
+ contained = contained.singularize if module_parent.pluralize_table_names
contained += "_"
end
diff --git a/activerecord/lib/active_record/railtie.rb b/activerecord/lib/active_record/railtie.rb
index b213754641..538659d6bd 100644
--- a/activerecord/lib/active_record/railtie.rb
+++ b/activerecord/lib/active_record/railtie.rb
@@ -88,6 +88,31 @@ module ActiveRecord
end
end
+ initializer "Check for cache versioning support" do
+ config.after_initialize do |app|
+ ActiveSupport.on_load(:active_record) do
+ if app.config.active_record.cache_versioning && Rails.cache
+ unless Rails.cache.class.try(:supports_cache_versioning?)
+ raise <<-end_error
+
+You're using a cache store that doesn't support native cache versioning.
+Your best option is to upgrade to a newer version of #{Rails.cache.class}
+that supports cache versioning (#{Rails.cache.class}.supports_cache_versioning? #=> true).
+
+Next best, switch to a different cache store that does support cache versioning:
+https://guides.rubyonrails.org/caching_with_rails.html#cache-stores.
+
+To keep using the current cache store, you can turn off cache versioning entirely:
+
+ config.active_record.cache_versioning = false
+
+end_error
+ end
+ end
+ end
+ end
+ end
+
initializer "active_record.check_schema_cache_dump" do
if config.active_record.delete(:use_schema_cache_dump)
config.after_initialize do |app|
@@ -112,6 +137,14 @@ module ActiveRecord
end
end
+ initializer "active_record.define_attribute_methods" do |app|
+ config.after_initialize do
+ ActiveSupport.on_load(:active_record) do
+ descendants.each(&:define_attribute_methods) if app.config.eager_load
+ end
+ end
+ end
+
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
@@ -135,21 +168,7 @@ module ActiveRecord
initializer "active_record.initialize_database" do
ActiveSupport.on_load(:active_record) do
self.configurations = Rails.application.config.database_configuration
-
- begin
- establish_connection
- rescue ActiveRecord::NoDatabaseError
- warn <<-end_warning
-Oops - You have a database configured, but it doesn't exist yet!
-
-Here's how to get started:
-
- 1. Configure your database in config/database.yml.
- 2. Run `rails db:create` to create the database.
- 3. Run `rails db:setup` to load your database schema.
-end_warning
- raise
- end
+ establish_connection
end
end
diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb
index 86e5d14c81..d5b6082d13 100644
--- a/activerecord/lib/active_record/relation.rb
+++ b/activerecord/lib/active_record/relation.rb
@@ -348,17 +348,17 @@ module ActiveRecord
end
stmt = Arel::UpdateManager.new
-
- stmt.set Arel.sql(@klass.sanitize_sql_for_assignment(updates))
- stmt.table(table)
-
- if has_join_values? || offset_value
- @klass.connection.join_to_update(stmt, arel, arel_attribute(primary_key))
+ stmt.table(arel.join_sources.empty? ? table : arel.source)
+ stmt.key = arel_attribute(primary_key)
+ stmt.take(arel.limit)
+ stmt.offset(arel.offset)
+ stmt.order(*arel.orders)
+ stmt.wheres = arel.constraints
+
+ if updates.is_a?(Hash)
+ stmt.set _substitute_values(updates)
else
- stmt.key = arel_attribute(primary_key)
- stmt.take(arel.limit)
- stmt.order(*arel.orders)
- stmt.wheres = arel.constraints
+ stmt.set Arel.sql(klass.sanitize_sql_for_assignment(updates, table.name))
end
@klass.connection.update stmt, "#{@klass} Update All"
@@ -375,19 +375,22 @@ module ActiveRecord
def update_counters(counters) # :nodoc:
touch = counters.delete(:touch)
- updates = counters.map do |counter_name, value|
- operator = value < 0 ? "-" : "+"
- quoted_column = connection.quote_column_name(counter_name)
- "#{quoted_column} = COALESCE(#{quoted_column}, 0) #{operator} #{value.abs}"
+ updates = {}
+ counters.each do |counter_name, value|
+ attr = arel_attribute(counter_name)
+ bind = predicate_builder.build_bind_attribute(attr.name, value.abs)
+ expr = table.coalesce(Arel::Nodes::UnqualifiedColumn.new(attr), 0)
+ expr = value < 0 ? expr - bind : expr + bind
+ updates[counter_name] = expr.expr
end
if touch
names = touch if touch != true
touch_updates = klass.touch_attributes_with_time(*names)
- updates << klass.sanitize_sql_for_assignment(touch_updates) unless touch_updates.empty?
+ updates.merge!(touch_updates) unless touch_updates.empty?
end
- update_all updates.join(", ")
+ update_all updates
end
# Touches all records in the current relation without instantiating records first with the updated_at/on attributes
@@ -414,14 +417,12 @@ module ActiveRecord
# Person.where(name: 'David').touch_all
# # => "UPDATE \"people\" SET \"updated_at\" = '2018-01-04 22:55:23.132670' WHERE \"people\".\"name\" = 'David'"
def touch_all(*names, time: nil)
- updates = touch_attributes_with_time(*names, time: time)
-
if klass.locking_enabled?
- quoted_locking_column = connection.quote_column_name(klass.locking_column)
- updates = sanitize_sql_for_assignment(updates) + ", #{quoted_locking_column} = COALESCE(#{quoted_locking_column}, 0) + 1"
+ names << { time: time }
+ update_counters(klass.locking_column => 1, touch: names)
+ else
+ update_all klass.touch_attributes_with_time(*names, time: time)
end
-
- update_all(updates)
end
# Destroys the records by instantiating each
@@ -477,13 +478,12 @@ module ActiveRecord
end
stmt = Arel::DeleteManager.new
- stmt.from(table)
-
- if has_join_values? || has_limit_or_offset?
- @klass.connection.join_to_delete(stmt, arel, arel_attribute(primary_key))
- else
- stmt.wheres = arel.constraints
- end
+ stmt.from(arel.join_sources.empty? ? table : arel.source)
+ stmt.key = arel_attribute(primary_key)
+ stmt.take(arel.limit)
+ stmt.offset(arel.offset)
+ stmt.order(*arel.orders)
+ stmt.wheres = arel.constraints
affected = @klass.connection.delete(stmt, "#{@klass} Destroy")
@@ -627,9 +627,15 @@ module ActiveRecord
end
private
-
- def has_join_values?
- joins_values.any? || left_outer_joins_values.any?
+ def _substitute_values(values)
+ values.map do |name, value|
+ attr = arel_attribute(name)
+ unless Arel.arel_node?(value)
+ type = klass.type_for_attribute(attr.name)
+ value = predicate_builder.build_bind_attribute(attr.name, type.cast(value))
+ end
+ [attr, value]
+ end
end
def exec_queries(&block)
diff --git a/activerecord/lib/active_record/relation/delegation.rb b/activerecord/lib/active_record/relation/delegation.rb
index 488f71cdde..8f657840f5 100644
--- a/activerecord/lib/active_record/relation/delegation.rb
+++ b/activerecord/lib/active_record/relation/delegation.rb
@@ -17,7 +17,7 @@ module ActiveRecord
delegate = Class.new(klass) {
include ClassSpecificRelation
}
- mangled_name = klass.name.gsub("::".freeze, "_".freeze)
+ mangled_name = klass.name.gsub("::", "_")
const_set mangled_name, delegate
private_constant mangled_name
diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb
index 0fabfe5518..6f420fe6bb 100644
--- a/activerecord/lib/active_record/relation/finder_methods.rb
+++ b/activerecord/lib/active_record/relation/finder_methods.rb
@@ -338,14 +338,14 @@ module ActiveRecord
name = @klass.name
if ids.nil?
- error = "Couldn't find #{name}".dup
+ error = +"Couldn't find #{name}"
error << " with#{conditions}" if conditions
raise RecordNotFound.new(error, name, key)
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 #{name.pluralize} with '#{key}': ".dup
+ error = +"Couldn't find all #{name.pluralize} with '#{key}': "
error << "(#{ids.join(", ")})#{conditions} (found #{result_size} results, but was looking for #{expected_size})."
error << " Couldn't find #{name.pluralize(not_found_ids.size)} with #{key.to_s.pluralize(not_found_ids.size)} #{not_found_ids.join(', ')}." if not_found_ids
raise RecordNotFound.new(error, name, key, ids)
@@ -416,7 +416,7 @@ module ActiveRecord
raise UnknownPrimaryKey.new(@klass) if primary_key.nil?
expects_array = ids.first.kind_of?(Array)
- return ids.first if expects_array && ids.first.empty?
+ return [] if expects_array && ids.first.empty?
ids = ids.flatten.compact.uniq
diff --git a/activerecord/lib/active_record/relation/merger.rb b/activerecord/lib/active_record/relation/merger.rb
index 07b16a0740..4de7465128 100644
--- a/activerecord/lib/active_record/relation/merger.rb
+++ b/activerecord/lib/active_record/relation/merger.rb
@@ -171,9 +171,7 @@ module ActiveRecord
end
def merge_clauses
- if relation.from_clause.empty? && !other.from_clause.empty?
- relation.from_clause = other.from_clause
- end
+ relation.from_clause = other.from_clause if replace_from_clause?
where_clause = relation.where_clause.merge(other.where_clause)
relation.where_clause = where_clause unless where_clause.empty?
@@ -181,6 +179,11 @@ module ActiveRecord
having_clause = relation.having_clause.merge(other.having_clause)
relation.having_clause = having_clause unless having_clause.empty?
end
+
+ def replace_from_clause?
+ relation.from_clause.empty? && !other.from_clause.empty? &&
+ relation.klass.base_class == other.klass.base_class
+ 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 f734cd0ad8..b59ff912fe 100644
--- a/activerecord/lib/active_record/relation/predicate_builder.rb
+++ b/activerecord/lib/active_record/relation/predicate_builder.rb
@@ -27,7 +27,7 @@ module ActiveRecord
key
else
key = key.to_s
- key.split(".".freeze).first if key.include?(".".freeze)
+ key.split(".").first if key.include?(".")
end
end.compact
end
@@ -115,11 +115,11 @@ module ActiveRecord
def convert_dot_notation_to_hash(attributes)
dot_notation = attributes.select do |k, v|
- k.include?(".".freeze) && !v.is_a?(Hash)
+ k.include?(".") && !v.is_a?(Hash)
end
dot_notation.each_key do |key|
- table_name, column_name = key.split(".".freeze)
+ table_name, column_name = key.split(".")
value = attributes.delete(key)
attributes[table_name] ||= {}
diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb
index 56497e11cb..5170d38fa8 100644
--- a/activerecord/lib/active_record/relation/query_methods.rb
+++ b/activerecord/lib/active_record/relation/query_methods.rb
@@ -939,7 +939,7 @@ module ActiveRecord
arel.having(having_clause.ast) unless having_clause.empty?
if limit_value
limit_attribute = ActiveModel::Attribute.with_cast_value(
- "LIMIT".freeze,
+ "LIMIT",
connection.sanitize_limit(limit_value),
Type.default_value,
)
@@ -947,7 +947,7 @@ module ActiveRecord
end
if offset_value
offset_attribute = ActiveModel::Attribute.with_cast_value(
- "OFFSET".freeze,
+ "OFFSET",
offset_value.to_i,
Type.default_value,
)
diff --git a/activerecord/lib/active_record/result.rb b/activerecord/lib/active_record/result.rb
index 3b2556b1c8..da6d10b6ec 100644
--- a/activerecord/lib/active_record/result.rb
+++ b/activerecord/lib/active_record/result.rb
@@ -21,7 +21,7 @@ module ActiveRecord
# ]
#
# # Get an array of hashes representing the result (column => value):
- # result.to_hash
+ # result.to_a
# # => [{"id" => 1, "title" => "title_1", "body" => "body_1"},
# {"id" => 2, "title" => "title_2", "body" => "body_2"},
# ...
@@ -65,9 +65,12 @@ module ActiveRecord
end
end
- # Returns an array of hashes representing each row record.
def to_hash
- hash_rows
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
+ `ActiveRecord::Result#to_hash` has been renamed to `to_a`.
+ `to_hash` is deprecated and will be removed in Rails 6.1.
+ MSG
+ to_a
end
alias :map! :map
@@ -83,6 +86,8 @@ module ActiveRecord
hash_rows
end
+ alias :to_a :to_ary
+
def [](idx)
hash_rows[idx]
end
diff --git a/activerecord/lib/active_record/statement_cache.rb b/activerecord/lib/active_record/statement_cache.rb
index b41d3504fd..1b1736dcab 100644
--- a/activerecord/lib/active_record/statement_cache.rb
+++ b/activerecord/lib/active_record/statement_cache.rb
@@ -44,7 +44,7 @@ module ActiveRecord
def initialize(values)
@values = values
@indexes = values.each_with_index.find_all { |thing, i|
- Arel::Nodes::BindParam === thing
+ Substitute === thing
}.map(&:last)
end
@@ -56,6 +56,28 @@ module ActiveRecord
end
end
+ class PartialQueryCollector
+ def initialize
+ @parts = []
+ @binds = []
+ end
+
+ def <<(str)
+ @parts << str
+ self
+ end
+
+ def add_bind(obj)
+ @binds << obj
+ @parts << Substitute.new
+ self
+ end
+
+ def value
+ [@parts, @binds]
+ end
+ end
+
def self.query(sql)
Query.new(sql)
end
@@ -64,6 +86,10 @@ module ActiveRecord
PartialQuery.new(values)
end
+ def self.partial_query_collector
+ PartialQueryCollector.new
+ end
+
class Params # :nodoc:
def bind; Substitute.new; end
end
diff --git a/activerecord/lib/active_record/tasks/database_tasks.rb b/activerecord/lib/active_record/tasks/database_tasks.rb
index 16e66982e5..5e29085aff 100644
--- a/activerecord/lib/active_record/tasks/database_tasks.rb
+++ b/activerecord/lib/active_record/tasks/database_tasks.rb
@@ -307,7 +307,7 @@ module ActiveRecord
def check_schema_file(filename)
unless File.exist?(filename)
- message = %{#{filename} doesn't exist yet. Run `rails db:migrate` to create it, then try again.}.dup
+ message = +%{#{filename} doesn't exist yet. Run `rails db:migrate` to create it, then try again.}
message << %{ If you do not intend to use a database, you should instead alter #{Rails.root}/config/application.rb to limit the frameworks that will be loaded.} if defined?(::Rails.root)
Kernel.abort message
end
diff --git a/activerecord/lib/active_record/tasks/mysql_database_tasks.rb b/activerecord/lib/active_record/tasks/mysql_database_tasks.rb
index eddc6fa223..1c1b29b5e1 100644
--- a/activerecord/lib/active_record/tasks/mysql_database_tasks.rb
+++ b/activerecord/lib/active_record/tasks/mysql_database_tasks.rb
@@ -104,7 +104,7 @@ module ActiveRecord
end
def run_cmd_error(cmd, args, action)
- msg = "failed to execute: `#{cmd}`\n".dup
+ 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
diff --git a/activerecord/lib/active_record/tasks/postgresql_database_tasks.rb b/activerecord/lib/active_record/tasks/postgresql_database_tasks.rb
index 533e6953a4..8acb11f75f 100644
--- a/activerecord/lib/active_record/tasks/postgresql_database_tasks.rb
+++ b/activerecord/lib/active_record/tasks/postgresql_database_tasks.rb
@@ -6,8 +6,8 @@ module ActiveRecord
module Tasks # :nodoc:
class PostgreSQLDatabaseTasks # :nodoc:
DEFAULT_ENCODING = ENV["CHARSET"] || "utf8"
- ON_ERROR_STOP_1 = "ON_ERROR_STOP=1".freeze
- SQL_COMMENT_BEGIN = "--".freeze
+ ON_ERROR_STOP_1 = "ON_ERROR_STOP=1"
+ SQL_COMMENT_BEGIN = "--"
delegate :connection, :establish_connection, :clear_active_connections!,
to: ActiveRecord::Base
@@ -82,7 +82,7 @@ module ActiveRecord
def structure_load(filename, extra_flags)
set_psql_env
- args = ["-v", ON_ERROR_STOP_1, "-q", "-f", filename]
+ args = ["-v", ON_ERROR_STOP_1, "-q", "-X", "-f", filename]
args.concat(Array(extra_flags)) if extra_flags
args << configuration["database"]
run_cmd("psql", args, "loading")
@@ -115,7 +115,7 @@ module ActiveRecord
end
def run_cmd_error(cmd, args, action)
- msg = "failed to execute:\n".dup
+ 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
diff --git a/activerecord/lib/active_record/tasks/sqlite_database_tasks.rb b/activerecord/lib/active_record/tasks/sqlite_database_tasks.rb
index d0bad05176..a82cea80ca 100644
--- a/activerecord/lib/active_record/tasks/sqlite_database_tasks.rb
+++ b/activerecord/lib/active_record/tasks/sqlite_database_tasks.rb
@@ -67,7 +67,7 @@ module ActiveRecord
end
def run_cmd_error(cmd, args)
- msg = "failed to execute:\n".dup
+ 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
diff --git a/activerecord/lib/active_record/test_fixtures.rb b/activerecord/lib/active_record/test_fixtures.rb
new file mode 100644
index 0000000000..7b7b3f7112
--- /dev/null
+++ b/activerecord/lib/active_record/test_fixtures.rb
@@ -0,0 +1,201 @@
+# frozen_string_literal: true
+
+module ActiveRecord
+ module TestFixtures
+ extend ActiveSupport::Concern
+
+ def before_setup # :nodoc:
+ setup_fixtures
+ super
+ end
+
+ def after_teardown # :nodoc:
+ super
+ teardown_fixtures
+ end
+
+ included do
+ class_attribute :fixture_path, instance_writer: false
+ class_attribute :fixture_table_names, default: []
+ class_attribute :fixture_class_names, default: {}
+ class_attribute :use_transactional_tests, default: true
+ class_attribute :use_instantiated_fixtures, default: false # true, false, or :no_instances
+ class_attribute :pre_loaded_fixtures, default: false
+ class_attribute :config, default: ActiveRecord::Base
+ class_attribute :lock_threads, default: true
+ end
+
+ module ClassMethods
+ # Sets the model class for a fixture when the class name cannot be inferred from the fixture name.
+ #
+ # Examples:
+ #
+ # set_fixture_class some_fixture: SomeModel,
+ # 'namespaced/fixture' => Another::Model
+ #
+ # The keys must be the fixture names, that coincide with the short paths to the fixture files.
+ def set_fixture_class(class_names = {})
+ self.fixture_class_names = fixture_class_names.merge(class_names.stringify_keys)
+ end
+
+ def fixtures(*fixture_set_names)
+ if fixture_set_names.first == :all
+ raise StandardError, "No fixture path found. Please set `#{self}.fixture_path`." if fixture_path.blank?
+ 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)
+ end
+
+ self.fixture_table_names |= fixture_set_names
+ setup_fixture_accessors(fixture_set_names)
+ end
+
+ def setup_fixture_accessors(fixture_set_names = nil)
+ fixture_set_names = Array(fixture_set_names || fixture_table_names)
+ methods = Module.new do
+ fixture_set_names.each do |fs_name|
+ fs_name = fs_name.to_s
+ 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
+ return_single_record = fixture_names.size == 1
+ fixture_names = @loaded_fixtures[fs_name].fixtures.keys if fixture_names.empty?
+
+ @fixture_cache[fs_name] ||= {}
+
+ instances = fixture_names.map do |f_name|
+ f_name = f_name.to_s if f_name.is_a?(Symbol)
+ @fixture_cache[fs_name].delete(f_name) if force_reload
+
+ if @loaded_fixtures[fs_name][f_name]
+ @fixture_cache[fs_name][f_name] ||= @loaded_fixtures[fs_name][f_name].find
+ else
+ raise StandardError, "No fixture named '#{f_name}' found for fixture set '#{fs_name}'"
+ end
+ end
+
+ return_single_record ? instances.first : instances
+ end
+ private accessor_name
+ end
+ end
+ include methods
+ end
+
+ def uses_transaction(*methods)
+ @uses_transaction = [] unless defined?(@uses_transaction)
+ @uses_transaction.concat methods.map(&:to_s)
+ end
+
+ def uses_transaction?(method)
+ @uses_transaction = [] unless defined?(@uses_transaction)
+ @uses_transaction.include?(method.to_s)
+ end
+ end
+
+ def run_in_transaction?
+ use_transactional_tests &&
+ !self.class.uses_transaction?(method_name)
+ end
+
+ def setup_fixtures(config = ActiveRecord::Base)
+ if pre_loaded_fixtures && !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?
+ if @@already_loaded_fixtures[self.class]
+ @loaded_fixtures = @@already_loaded_fixtures[self.class]
+ else
+ @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
+ connection.pool.lock_thread = true if lock_threads
+ 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
+ connection.pool.lock_thread = true if lock_threads
+ @fixture_connections << connection
+ end
+ end
+ end
+
+ # Load fixtures for every test.
+ else
+ ActiveRecord::FixtureSet.reset_cache
+ @@already_loaded_fixtures[self.class] = nil
+ @loaded_fixtures = load_fixtures(config)
+ end
+
+ # Instantiate fixtures for every test if requested.
+ instantiate_fixtures if use_instantiated_fixtures
+ end
+
+ 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?
+ connection.pool.lock_thread = false
+ end
+ @fixture_connections.clear
+ else
+ ActiveRecord::FixtureSet.reset_cache
+ end
+
+ ActiveRecord::Base.clear_active_connections!
+ end
+
+ def enlist_fixture_connections
+ ActiveRecord::Base.connection_handler.connection_pool_list.map(&:connection)
+ end
+
+ private
+ def load_fixtures(config)
+ fixtures = ActiveRecord::FixtureSet.create_fixtures(fixture_path, fixture_table_names, fixture_class_names, config)
+ Hash[fixtures.map { |f| [f.name, f] }]
+ end
+
+ def instantiate_fixtures
+ if pre_loaded_fixtures
+ 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?
+ @loaded_fixtures.each_value do |fixture_set|
+ ActiveRecord::FixtureSet.instantiate_fixtures(self, fixture_set, load_instances?)
+ end
+ end
+ end
+
+ def load_instances?
+ use_instantiated_fixtures != :no_instances
+ end
+ end
+end
diff --git a/activerecord/lib/arel.rb b/activerecord/lib/arel.rb
index 7d04e1cac6..dab785738e 100644
--- a/activerecord/lib/arel.rb
+++ b/activerecord/lib/arel.rb
@@ -35,6 +35,11 @@ module Arel # :nodoc: all
def self.star
sql "*"
end
+
+ def self.arel_node?(value)
+ value.is_a?(Arel::Node) || value.is_a?(Arel::Attribute) || value.is_a?(Arel::Nodes::SqlLiteral)
+ end
+
## Convenience Alias
Node = Arel::Nodes::Node
end
diff --git a/activerecord/lib/arel/collectors/composite.rb b/activerecord/lib/arel/collectors/composite.rb
index d040d8598d..0533544993 100644
--- a/activerecord/lib/arel/collectors/composite.rb
+++ b/activerecord/lib/arel/collectors/composite.rb
@@ -24,8 +24,7 @@ module Arel # :nodoc: all
[left.value, right.value]
end
- protected
-
+ private
attr_reader :left, :right
end
end
diff --git a/activerecord/lib/arel/collectors/plain_string.rb b/activerecord/lib/arel/collectors/plain_string.rb
index 687d7fbf2f..c0e9fff399 100644
--- a/activerecord/lib/arel/collectors/plain_string.rb
+++ b/activerecord/lib/arel/collectors/plain_string.rb
@@ -4,7 +4,7 @@ module Arel # :nodoc: all
module Collectors
class PlainString
def initialize
- @str = "".dup
+ @str = +""
end
def value
diff --git a/activerecord/lib/arel/collectors/sql_string.rb b/activerecord/lib/arel/collectors/sql_string.rb
index c293a89a74..54e1e562c2 100644
--- a/activerecord/lib/arel/collectors/sql_string.rb
+++ b/activerecord/lib/arel/collectors/sql_string.rb
@@ -15,10 +15,6 @@ module Arel # :nodoc: all
@bind_index += 1
self
end
-
- def compile(bvs)
- value
- end
end
end
end
diff --git a/activerecord/lib/arel/collectors/substitute_binds.rb b/activerecord/lib/arel/collectors/substitute_binds.rb
index 3f40eec8a8..4b894bc4b1 100644
--- a/activerecord/lib/arel/collectors/substitute_binds.rb
+++ b/activerecord/lib/arel/collectors/substitute_binds.rb
@@ -21,8 +21,7 @@ module Arel # :nodoc: all
delegate.value
end
- protected
-
+ private
attr_reader :quoter, :delegate
end
end
diff --git a/activerecord/lib/arel/delete_manager.rb b/activerecord/lib/arel/delete_manager.rb
index 2def581009..fdba937d64 100644
--- a/activerecord/lib/arel/delete_manager.rb
+++ b/activerecord/lib/arel/delete_manager.rb
@@ -2,6 +2,8 @@
module Arel # :nodoc: all
class DeleteManager < Arel::TreeManager
+ include TreeManager::StatementMethods
+
def initialize
super
@ast = Nodes::DeleteStatement.new
@@ -12,14 +14,5 @@ module Arel # :nodoc: all
@ast.relation = relation
self
end
-
- def take(limit)
- @ast.limit = Nodes::Limit.new(Nodes.build_quoted(limit)) if limit
- self
- end
-
- def wheres=(list)
- @ast.wheres = list
- end
end
end
diff --git a/activerecord/lib/arel/factory_methods.rb b/activerecord/lib/arel/factory_methods.rb
index b828bc274e..83ec23e403 100644
--- a/activerecord/lib/arel/factory_methods.rb
+++ b/activerecord/lib/arel/factory_methods.rb
@@ -41,5 +41,9 @@ module Arel # :nodoc: all
def lower(column)
Nodes::NamedFunction.new "LOWER", [Nodes.build_quoted(column)]
end
+
+ def coalesce(*exprs)
+ Nodes::NamedFunction.new "COALESCE", exprs
+ end
end
end
diff --git a/activerecord/lib/arel/nodes/delete_statement.rb b/activerecord/lib/arel/nodes/delete_statement.rb
index eaac05e2f6..a419975335 100644
--- a/activerecord/lib/arel/nodes/delete_statement.rb
+++ b/activerecord/lib/arel/nodes/delete_statement.rb
@@ -3,8 +3,7 @@
module Arel # :nodoc: all
module Nodes
class DeleteStatement < Arel::Nodes::Node
- attr_accessor :left, :right
- attr_accessor :limit
+ attr_accessor :left, :right, :orders, :limit, :offset, :key
alias :relation :left
alias :relation= :left=
@@ -15,6 +14,10 @@ module Arel # :nodoc: all
super()
@left = relation
@right = wheres
+ @orders = []
+ @limit = nil
+ @offset = nil
+ @key = nil
end
def initialize_copy(other)
@@ -24,13 +27,17 @@ module Arel # :nodoc: all
end
def hash
- [self.class, @left, @right].hash
+ [self.class, @left, @right, @orders, @limit, @offset, @key].hash
end
def eql?(other)
self.class == other.class &&
self.left == other.left &&
- self.right == other.right
+ self.right == other.right &&
+ self.orders == other.orders &&
+ self.limit == other.limit &&
+ self.offset == other.offset &&
+ self.key == other.key
end
alias :== :eql?
end
diff --git a/activerecord/lib/arel/nodes/node.rb b/activerecord/lib/arel/nodes/node.rb
index 2b9b1e9828..8086102bde 100644
--- a/activerecord/lib/arel/nodes/node.rb
+++ b/activerecord/lib/arel/nodes/node.rb
@@ -8,16 +8,6 @@ module Arel # :nodoc: all
include Arel::FactoryMethods
include Enumerable
- if $DEBUG
- def _caller
- @caller
- end
-
- def initialize
- @caller = caller.dup
- end
- end
-
###
# Factory method to create a Nodes::Not node that has the recipient of
# the caller as a child.
diff --git a/activerecord/lib/arel/nodes/select_core.rb b/activerecord/lib/arel/nodes/select_core.rb
index 2defe61974..73461ff683 100644
--- a/activerecord/lib/arel/nodes/select_core.rb
+++ b/activerecord/lib/arel/nodes/select_core.rb
@@ -3,13 +3,12 @@
module Arel # :nodoc: all
module Nodes
class SelectCore < Arel::Nodes::Node
- attr_accessor :top, :projections, :wheres, :groups, :windows
+ attr_accessor :projections, :wheres, :groups, :windows
attr_accessor :havings, :source, :set_quantifier
def initialize
super()
@source = JoinSource.new nil
- @top = nil
# https://ronsavage.github.io/SQL/sql-92.bnf.html#set%20quantifier
@set_quantifier = nil
@@ -43,7 +42,7 @@ module Arel # :nodoc: all
def hash
[
- @source, @top, @set_quantifier, @projections,
+ @source, @set_quantifier, @projections,
@wheres, @groups, @havings, @windows
].hash
end
@@ -51,7 +50,6 @@ module Arel # :nodoc: all
def eql?(other)
self.class == other.class &&
self.source == other.source &&
- self.top == other.top &&
self.set_quantifier == other.set_quantifier &&
self.projections == other.projections &&
self.wheres == other.wheres &&
diff --git a/activerecord/lib/arel/nodes/unary.rb b/activerecord/lib/arel/nodes/unary.rb
index a3c0045897..00639304e4 100644
--- a/activerecord/lib/arel/nodes/unary.rb
+++ b/activerecord/lib/arel/nodes/unary.rb
@@ -37,7 +37,6 @@ module Arel # :nodoc: all
On
Ordering
RollUp
- Top
}.each do |name|
const_set(name, Class.new(Unary))
end
diff --git a/activerecord/lib/arel/nodes/update_statement.rb b/activerecord/lib/arel/nodes/update_statement.rb
index 5184b1180f..cfaa19e392 100644
--- a/activerecord/lib/arel/nodes/update_statement.rb
+++ b/activerecord/lib/arel/nodes/update_statement.rb
@@ -3,8 +3,7 @@
module Arel # :nodoc: all
module Nodes
class UpdateStatement < Arel::Nodes::Node
- attr_accessor :relation, :wheres, :values, :orders, :limit
- attr_accessor :key
+ attr_accessor :relation, :wheres, :values, :orders, :limit, :offset, :key
def initialize
@relation = nil
@@ -12,6 +11,7 @@ module Arel # :nodoc: all
@values = []
@orders = []
@limit = nil
+ @offset = nil
@key = nil
end
@@ -22,7 +22,7 @@ module Arel # :nodoc: all
end
def hash
- [@relation, @wheres, @values, @orders, @limit, @key].hash
+ [@relation, @wheres, @values, @orders, @limit, @offset, @key].hash
end
def eql?(other)
@@ -32,6 +32,7 @@ module Arel # :nodoc: all
self.values == other.values &&
self.orders == other.orders &&
self.limit == other.limit &&
+ self.offset == other.offset &&
self.key == other.key
end
alias :== :eql?
diff --git a/activerecord/lib/arel/select_manager.rb b/activerecord/lib/arel/select_manager.rb
index 733176ad9e..a2b2838a3d 100644
--- a/activerecord/lib/arel/select_manager.rb
+++ b/activerecord/lib/arel/select_manager.rb
@@ -222,10 +222,8 @@ module Arel # :nodoc: all
def take(limit)
if limit
@ast.limit = Nodes::Limit.new(limit)
- @ctx.top = Nodes::Top.new(limit)
else
@ast.limit = nil
- @ctx.top = nil
end
self
end
diff --git a/activerecord/lib/arel/table.rb b/activerecord/lib/arel/table.rb
index 686fcdf962..c40c68715a 100644
--- a/activerecord/lib/arel/table.rb
+++ b/activerecord/lib/arel/table.rb
@@ -104,8 +104,7 @@ module Arel # :nodoc: all
!type_caster.nil?
end
- protected
-
+ private
attr_reader :type_caster
end
end
diff --git a/activerecord/lib/arel/tree_manager.rb b/activerecord/lib/arel/tree_manager.rb
index ed47b09a37..0476399618 100644
--- a/activerecord/lib/arel/tree_manager.rb
+++ b/activerecord/lib/arel/tree_manager.rb
@@ -4,6 +4,40 @@ module Arel # :nodoc: all
class TreeManager
include Arel::FactoryMethods
+ module StatementMethods
+ def take(limit)
+ @ast.limit = Nodes::Limit.new(Nodes.build_quoted(limit)) if limit
+ self
+ end
+
+ def offset(offset)
+ @ast.offset = Nodes::Offset.new(Nodes.build_quoted(offset)) if offset
+ self
+ end
+
+ def order(*expr)
+ @ast.orders = expr
+ self
+ end
+
+ def key=(key)
+ @ast.key = Nodes.build_quoted(key)
+ end
+
+ def key
+ @ast.key
+ end
+
+ def wheres=(exprs)
+ @ast.wheres = exprs
+ end
+
+ def where(expr)
+ @ast.wheres << expr
+ self
+ end
+ end
+
attr_reader :ast
def initialize
diff --git a/activerecord/lib/arel/update_manager.rb b/activerecord/lib/arel/update_manager.rb
index fe444343ba..a809dbb307 100644
--- a/activerecord/lib/arel/update_manager.rb
+++ b/activerecord/lib/arel/update_manager.rb
@@ -2,30 +2,14 @@
module Arel # :nodoc: all
class UpdateManager < Arel::TreeManager
+ include TreeManager::StatementMethods
+
def initialize
super
@ast = Nodes::UpdateStatement.new
@ctx = @ast
end
- def take(limit)
- @ast.limit = Nodes::Limit.new(Nodes.build_quoted(limit)) if limit
- self
- end
-
- def key=(key)
- @ast.key = Nodes.build_quoted(key)
- end
-
- def key
- @ast.key
- end
-
- def order(*expr)
- @ast.orders = expr
- self
- end
-
###
# UPDATE +table+
def table(table)
@@ -33,15 +17,6 @@ module Arel # :nodoc: all
self
end
- def wheres=(exprs)
- @ast.wheres = exprs
- end
-
- def where(expr)
- @ast.wheres << expr
- self
- end
-
def set(values)
if String === values
@ast.values = [values]
diff --git a/activerecord/lib/arel/visitors/depth_first.rb b/activerecord/lib/arel/visitors/depth_first.rb
index bcf8f8f980..8f65d303ac 100644
--- a/activerecord/lib/arel/visitors/depth_first.rb
+++ b/activerecord/lib/arel/visitors/depth_first.rb
@@ -34,7 +34,6 @@ module Arel # :nodoc: all
alias :visit_Arel_Nodes_Ordering :unary
alias :visit_Arel_Nodes_Ascending :unary
alias :visit_Arel_Nodes_Descending :unary
- alias :visit_Arel_Nodes_Top :unary
alias :visit_Arel_Nodes_UnqualifiedColumn :unary
def function(o)
@@ -136,12 +135,10 @@ module Arel # :nodoc: all
alias :visit_Arel_Nodes_True :terminal
alias :visit_Arel_Nodes_False :terminal
alias :visit_BigDecimal :terminal
- alias :visit_Bignum :terminal
alias :visit_Class :terminal
alias :visit_Date :terminal
alias :visit_DateTime :terminal
alias :visit_FalseClass :terminal
- alias :visit_Fixnum :terminal
alias :visit_Float :terminal
alias :visit_Integer :terminal
alias :visit_NilClass :terminal
diff --git a/activerecord/lib/arel/visitors/dot.rb b/activerecord/lib/arel/visitors/dot.rb
index d352b81914..9054f0159b 100644
--- a/activerecord/lib/arel/visitors/dot.rb
+++ b/activerecord/lib/arel/visitors/dot.rb
@@ -81,7 +81,6 @@ module Arel # :nodoc: all
alias :visit_Arel_Nodes_Not :unary
alias :visit_Arel_Nodes_Offset :unary
alias :visit_Arel_Nodes_On :unary
- alias :visit_Arel_Nodes_Top :unary
alias :visit_Arel_Nodes_UnqualifiedColumn :unary
alias :visit_Arel_Nodes_Preceding :unary
alias :visit_Arel_Nodes_Following :unary
@@ -212,7 +211,6 @@ module Arel # :nodoc: all
alias :visit_TrueClass :visit_String
alias :visit_FalseClass :visit_String
alias :visit_Integer :visit_String
- alias :visit_Fixnum :visit_String
alias :visit_BigDecimal :visit_String
alias :visit_Float :visit_String
alias :visit_Symbol :visit_String
diff --git a/activerecord/lib/arel/visitors/mssql.rb b/activerecord/lib/arel/visitors/mssql.rb
index 9aedc51d15..d564e19089 100644
--- a/activerecord/lib/arel/visitors/mssql.rb
+++ b/activerecord/lib/arel/visitors/mssql.rb
@@ -12,13 +12,6 @@ module Arel # :nodoc: all
private
- # `top` wouldn't really work here. I.e. User.select("distinct first_name").limit(10) would generate
- # "select top 10 distinct first_name from users", which is invalid query! it should be
- # "select distinct top 10 first_name from users"
- def visit_Arel_Nodes_Top(o)
- ""
- end
-
def visit_Arel_Visitors_MSSQL_RowNumber(o, collector)
collector << "ROW_NUMBER() OVER (ORDER BY "
inject_join(o.children, collector, ", ") << ") as _row_num"
diff --git a/activerecord/lib/arel/visitors/mysql.rb b/activerecord/lib/arel/visitors/mysql.rb
index 37bfb661f0..9d9294fecc 100644
--- a/activerecord/lib/arel/visitors/mysql.rb
+++ b/activerecord/lib/arel/visitors/mysql.rb
@@ -37,6 +37,10 @@ module Arel # :nodoc: all
visit o.expr, collector
end
+ def visit_Arel_Nodes_UnqualifiedColumn(o, collector)
+ visit o.expr, collector
+ end
+
###
# :'(
# http://dev.mysql.com/doc/refman/5.0/en/select.html#id3482214
@@ -52,28 +56,6 @@ module Arel # :nodoc: all
super
end
- def visit_Arel_Nodes_UpdateStatement(o, collector)
- collector << "UPDATE "
- collector = visit o.relation, collector
-
- unless o.values.empty?
- collector << " SET "
- collector = inject_join o.values, collector, ", "
- end
-
- unless o.wheres.empty?
- collector << " WHERE "
- collector = inject_join o.wheres, collector, " AND "
- end
-
- unless o.orders.empty?
- collector << " ORDER BY "
- collector = inject_join o.orders, collector, ", "
- end
-
- maybe_visit o.limit, collector
- end
-
def visit_Arel_Nodes_Concat(o, collector)
collector << " CONCAT("
visit o.left, collector
@@ -82,6 +64,44 @@ module Arel # :nodoc: all
collector << ") "
collector
end
+
+ # In the simple case, MySQL allows us to place JOINs directly into the UPDATE
+ # query. However, this does not allow for LIMIT, OFFSET and ORDER. To support
+ # these, we must use a subquery.
+ def prepare_update_statement(o)
+ if o.offset || has_join_sources?(o) && has_limit_or_offset_or_orders?(o)
+ super
+ else
+ o
+ end
+ end
+
+ def prepare_delete_statement(o)
+ if o.offset || has_join_sources?(o)
+ super
+ else
+ o
+ end
+ 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.
+ def build_subselect(key, o)
+ subselect = super
+
+ # Materialize subquery by adding distinct
+ # to work with MySQL 5.7.6 which sets optimizer_switch='derived_merge=on'
+ unless has_limit_or_offset_or_orders?(subselect)
+ core = subselect.cores.last
+ core.set_quantifier = Arel::Nodes::Distinct.new
+ end
+
+ Nodes::SelectStatement.new.tap do |stmt|
+ core = stmt.cores.last
+ core.froms = Nodes::Grouping.new(subselect).as("__active_record_temp")
+ core.projections = [Arel.sql(quote_column_name(key.name))]
+ end
+ end
end
end
end
diff --git a/activerecord/lib/arel/visitors/to_sql.rb b/activerecord/lib/arel/visitors/to_sql.rb
index 0682c066fb..7c0f6c2e97 100644
--- a/activerecord/lib/arel/visitors/to_sql.rb
+++ b/activerecord/lib/arel/visitors/to_sql.rb
@@ -67,41 +67,23 @@ module Arel # :nodoc: all
@connection = connection
end
- def compile(node, collector = Arel::Collectors::SQLString.new, &block)
- accept(node, collector, &block).value
+ def compile(node, collector = Arel::Collectors::SQLString.new)
+ accept(node, collector).value
end
private
def visit_Arel_Nodes_DeleteStatement(o, collector)
+ o = prepare_delete_statement(o)
+
collector << "DELETE FROM "
collector = visit o.relation, collector
- if o.wheres.any?
- collector << WHERE
- collector = inject_join o.wheres, collector, AND
- end
- maybe_visit o.limit, collector
- end
-
- # FIXME: we should probably have a 2-pass visitor for this
- def build_subselect(key, o)
- stmt = Nodes::SelectStatement.new
- core = stmt.cores.first
- core.froms = o.relation
- core.wheres = o.wheres
- core.projections = [key]
- stmt.limit = o.limit
- stmt.orders = o.orders
- stmt
+ collect_where_for(o, collector)
end
def visit_Arel_Nodes_UpdateStatement(o, collector)
- if o.orders.empty? && o.limit.nil?
- wheres = o.wheres
- else
- wheres = [Nodes::In.new(o.key, [build_subselect(o.key, o)])]
- end
+ o = prepare_update_statement(o)
collector << "UPDATE "
collector = visit o.relation, collector
@@ -110,12 +92,7 @@ module Arel # :nodoc: all
collector = inject_join o.values, collector, ", "
end
- unless wheres.empty?
- collector << " WHERE "
- collector = inject_join wheres, collector, " AND "
- end
-
- collector
+ collect_where_for(o, collector)
end
def visit_Arel_Nodes_InsertStatement(o, collector)
@@ -237,8 +214,6 @@ module Arel # :nodoc: all
def visit_Arel_Nodes_SelectCore(o, collector)
collector << "SELECT"
- collector = maybe_visit o.top, collector
-
collector = maybe_visit o.set_quantifier, collector
collect_nodes_for o.projections, collector, SPACE
@@ -405,11 +380,6 @@ module Arel # :nodoc: all
visit o.expr, collector
end
- # FIXME: this does nothing on most databases, but does on MSSQL
- def visit_Arel_Nodes_Top(o, collector)
- collector
- end
-
def visit_Arel_Nodes_Lock(o, collector)
visit o.expr, collector
end
@@ -644,7 +614,7 @@ module Arel # :nodoc: all
def visit_Arel_Nodes_Assignment(o, collector)
case o.right
- when Arel::Nodes::UnqualifiedColumn, Arel::Attributes::Attribute, Arel::Nodes::BindParam
+ when Arel::Nodes::Node, Arel::Attributes::Attribute
collector = visit o.left, collector
collector << " = "
visit o.right, collector
@@ -739,8 +709,6 @@ module Arel # :nodoc: all
end
alias :visit_Arel_Nodes_SqlLiteral :literal
- alias :visit_Bignum :literal
- alias :visit_Fixnum :literal
alias :visit_Integer :literal
def quoted(o, a)
@@ -823,6 +791,59 @@ module Arel # :nodoc: all
}
end
+ def has_join_sources?(o)
+ o.relation.is_a?(Nodes::JoinSource) && !o.relation.right.empty?
+ end
+
+ def has_limit_or_offset_or_orders?(o)
+ o.limit || o.offset || !o.orders.empty?
+ end
+
+ # The default strategy for an UPDATE with joins is to use a subquery. This doesn't work
+ # on MySQL (even when aliasing the tables), but MySQL allows using JOIN directly in
+ # an UPDATE statement, so in the MySQL visitor we redefine this to do that.
+ def prepare_update_statement(o)
+ if o.key && (has_limit_or_offset_or_orders?(o) || has_join_sources?(o))
+ stmt = o.clone
+ stmt.limit = nil
+ stmt.offset = nil
+ stmt.orders = []
+ stmt.wheres = [Nodes::In.new(o.key, [build_subselect(o.key, o)])]
+ stmt.relation = o.relation.left if has_join_sources?(o)
+ stmt
+ else
+ o
+ end
+ end
+ alias :prepare_delete_statement :prepare_update_statement
+
+ # FIXME: we should probably have a 2-pass visitor for this
+ def build_subselect(key, o)
+ stmt = Nodes::SelectStatement.new
+ core = stmt.cores.first
+ core.froms = o.relation
+ core.wheres = o.wheres
+ core.projections = [key]
+ stmt.limit = o.limit
+ stmt.offset = o.offset
+ stmt.orders = o.orders
+ stmt
+ end
+
+ def collect_where_for(o, collector)
+ unless o.wheres.empty?
+ collector << " WHERE "
+ collector = inject_join o.wheres, collector, " AND "
+ end
+
+ unless o.orders.empty?
+ collector << " ORDER BY "
+ collector = inject_join o.orders, collector, ", "
+ end
+
+ maybe_visit o.limit, collector
+ end
+
def infix_value(o, collector, value)
collector = visit o.left, collector
collector << value
diff --git a/activerecord/lib/rails/generators/active_record/migration.rb b/activerecord/lib/rails/generators/active_record/migration.rb
index 4a17082d66..cbb88d571d 100644
--- a/activerecord/lib/rails/generators/active_record/migration.rb
+++ b/activerecord/lib/rails/generators/active_record/migration.rb
@@ -24,14 +24,25 @@ module ActiveRecord
end
def db_migrate_path
- if migrations_paths = options[:migrations_paths]
- migrations_paths
- elsif defined?(Rails.application) && Rails.application
- Rails.application.config.paths["db/migrate"].to_ary.first
+ if defined?(Rails.application) && Rails.application
+ configured_migrate_path || default_migrate_path
else
"db/migrate"
end
end
+
+ def default_migrate_path
+ Rails.application.config.paths["db/migrate"].to_ary.first
+ end
+
+ def configured_migrate_path
+ return unless database = options[:database]
+ config = ActiveRecord::Base.configurations.configs_for(
+ env_name: Rails.env,
+ spec_name: database,
+ )
+ config&.migrations_paths
+ end
end
end
end
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 281b7afb50..dd79bcf542 100644
--- a/activerecord/lib/rails/generators/active_record/migration/migration_generator.rb
+++ b/activerecord/lib/rails/generators/active_record/migration/migration_generator.rb
@@ -8,7 +8,7 @@ module ActiveRecord
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"
- class_option :migrations_paths, type: :string, desc: "The migration path for your generated migrations. If this is not set it will default to db/migrate"
+ class_option :database, type: :string, aliases: %i(db), desc: "The database for your migration. By default, the current environment's primary database is used."
def create_migration_file
set_local_assigns!
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 25e54f3ac8..eac504f9f1 100644
--- a/activerecord/lib/rails/generators/active_record/model/model_generator.rb
+++ b/activerecord/lib/rails/generators/active_record/model/model_generator.rb
@@ -14,6 +14,7 @@ module ActiveRecord
class_option :parent, type: :string, desc: "The parent class for the generated model"
class_option :indexes, type: :boolean, default: true, desc: "Add indexes for references and belongs_to columns"
class_option :primary_key_type, type: :string, desc: "The type for primary key"
+ class_option :database, type: :string, aliases: %i(db), desc: "The database for your model's migration. By default, the current environment's primary database is used."
# creates the migration file for the model.
def create_migration_file
diff --git a/activerecord/test/cases/adapter_test.rb b/activerecord/test/cases/adapter_test.rb
index a93e5e2b40..64c2b51f83 100644
--- a/activerecord/test/cases/adapter_test.rb
+++ b/activerecord/test/cases/adapter_test.rb
@@ -227,7 +227,7 @@ module ActiveRecord
post = Post.create!(title: "foo", body: "bar")
expected = @connection.select_all("SELECT * FROM posts WHERE id = #{post.id}")
result = @connection.select_all("SELECT * FROM posts WHERE id = #{Arel::Nodes::BindParam.new(nil).to_sql}", nil, [[nil, post.id]])
- assert_equal expected.to_hash, result.to_hash
+ assert_equal expected.to_a, result.to_a
end
def test_insert_update_delete_with_legacy_binds
@@ -290,7 +290,7 @@ module ActiveRecord
def test_log_invalid_encoding
error = assert_raises RuntimeError do
@connection.send :log, "SELECT 'ы' FROM DUAL" do
- raise "ы".dup.force_encoding(Encoding::ASCII_8BIT)
+ raise (+"ы").force_encoding(Encoding::ASCII_8BIT)
end
end
diff --git a/activerecord/test/cases/adapters/mysql2/case_sensitivity_test.rb b/activerecord/test/cases/adapters/mysql2/case_sensitivity_test.rb
index aa870349be..c32475c683 100644
--- a/activerecord/test/cases/adapters/mysql2/case_sensitivity_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/case_sensitivity_test.rb
@@ -9,8 +9,8 @@ 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 "utf8mb4_bin", CollationTest.columns_hash["string_cs_column"].collation
+ assert_equal "utf8mb4_general_ci", CollationTest.columns_hash["string_ci_column"].collation
end
def test_case_sensitive
diff --git a/activerecord/test/cases/adapters/mysql2/charset_collation_test.rb b/activerecord/test/cases/adapters/mysql2/charset_collation_test.rb
index d0c57de65d..0bdbefdfb9 100644
--- a/activerecord/test/cases/adapters/mysql2/charset_collation_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/charset_collation_test.rb
@@ -32,20 +32,20 @@ class Mysql2CharsetCollationTest < ActiveRecord::Mysql2TestCase
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: "utf8mb4", collation: "utf8mb4_bin"
column = @connection.columns(:charset_collations).find { |c| c.name == "title" }
assert_equal :string, column.type
- assert_equal "utf8_bin", column.collation
+ assert_equal "utf8mb4_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: "utf8mb4", collation: "utf8mb4_unicode_ci"
+ @connection.change_column :charset_collations, :description, :text, charset: "utf8mb4", collation: "utf8mb4_general_ci"
column = @connection.columns(:charset_collations).find { |c| c.name == "description" }
assert_equal :text, column.type
- assert_equal "utf8_general_ci", column.collation
+ assert_equal "utf8mb4_general_ci", column.collation
end
test "schema dump includes collation" do
diff --git a/activerecord/test/cases/adapters/postgresql/bytea_test.rb b/activerecord/test/cases/adapters/postgresql/bytea_test.rb
index 64bb6906cd..3988c2adca 100644
--- a/activerecord/test/cases/adapters/postgresql/bytea_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/bytea_test.rb
@@ -49,7 +49,7 @@ class PostgresqlByteaTest < ActiveRecord::PostgreSQLTestCase
end
def test_type_cast_binary_value
- data = "\u001F\x8B".dup.force_encoding("BINARY")
+ data = (+"\u001F\x8B").force_encoding("BINARY")
assert_equal(data, @type.deserialize(data))
end
diff --git a/activerecord/test/cases/adapters/postgresql/money_test.rb b/activerecord/test/cases/adapters/postgresql/money_test.rb
index 61e75e772d..75e5aaed53 100644
--- a/activerecord/test/cases/adapters/postgresql/money_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/money_test.rb
@@ -52,10 +52,10 @@ class PostgresqlMoneyTest < ActiveRecord::PostgreSQLTestCase
def test_money_type_cast
type = PostgresqlMoney.type_for_attribute("wealth")
- assert_equal(12345678.12, type.cast("$12,345,678.12".dup))
- assert_equal(12345678.12, type.cast("$12.345.678,12".dup))
- assert_equal(-1.15, type.cast("-$1.15".dup))
- assert_equal(-2.25, type.cast("($2.25)".dup))
+ 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"))
+ assert_equal(-2.25, type.cast(+"($2.25)"))
end
def test_schema_dumping
@@ -65,7 +65,7 @@ class PostgresqlMoneyTest < ActiveRecord::PostgreSQLTestCase
end
def test_create_and_update_money
- money = PostgresqlMoney.create(wealth: "987.65".dup)
+ money = PostgresqlMoney.create(wealth: +"987.65")
assert_equal 987.65, money.wealth
new_value = BigDecimal("123.45")
diff --git a/activerecord/test/cases/adapters/postgresql/referential_integrity_test.rb b/activerecord/test/cases/adapters/postgresql/referential_integrity_test.rb
index 0bcc214c24..ba477c63f4 100644
--- a/activerecord/test/cases/adapters/postgresql/referential_integrity_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/referential_integrity_test.rb
@@ -101,7 +101,7 @@ class PostgreSQLReferentialIntegrityTest < ActiveRecord::PostgreSQLTestCase
@connection.extend ProgrammerMistake
assert_raises ArgumentError do
- @connection.disable_referential_integrity {}
+ @connection.disable_referential_integrity { }
end
end
diff --git a/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb b/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb
index 7b73621ed9..89052019f8 100644
--- a/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb
+++ b/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb
@@ -87,7 +87,7 @@ module ActiveRecord
def test_connection_no_db
assert_raises(ArgumentError) do
- Base.sqlite3_connection {}
+ Base.sqlite3_connection { }
end
end
@@ -167,7 +167,7 @@ module ActiveRecord
data binary
)
eosql
- str = "\x80".dup.force_encoding("ASCII-8BIT")
+ str = (+"\x80").force_encoding("ASCII-8BIT")
binary = DualEncoding.new name: "いただきます!", data: str
binary.save!
assert_equal str, binary.data
@@ -176,7 +176,7 @@ module ActiveRecord
end
def test_type_cast_should_not_mutate_encoding
- name = "hello".dup.force_encoding(Encoding::ASCII_8BIT)
+ name = (+"hello").force_encoding(Encoding::ASCII_8BIT)
Owner.create(name: name)
assert_equal Encoding::ASCII_8BIT, name.encoding
ensure
diff --git a/activerecord/test/cases/arel/collectors/sql_string_test.rb b/activerecord/test/cases/arel/collectors/sql_string_test.rb
index 380573494d..443c7eb54b 100644
--- a/activerecord/test/cases/arel/collectors/sql_string_test.rb
+++ b/activerecord/test/cases/arel/collectors/sql_string_test.rb
@@ -19,27 +19,21 @@ module Arel
collect(node).value
end
- def ast_with_binds(bv)
+ def ast_with_binds
table = Table.new(:users)
manager = Arel::SelectManager.new table
- manager.where(table[:age].eq(bv))
- manager.where(table[:name].eq(bv))
+ manager.where(table[:age].eq(Nodes::BindParam.new("hello")))
+ manager.where(table[:name].eq(Nodes::BindParam.new("world")))
manager.ast
end
def test_compile
- bv = Nodes::BindParam.new(1)
- collector = collect ast_with_binds bv
-
- sql = collector.compile ["hello", "world"]
+ sql = compile(ast_with_binds)
assert_equal 'SELECT FROM "users" WHERE "users"."age" = ? AND "users"."name" = ?', sql
end
def test_returned_sql_uses_utf8_encoding
- bv = Nodes::BindParam.new(1)
- collector = collect ast_with_binds bv
-
- sql = collector.compile ["hello", "world"]
+ sql = compile(ast_with_binds)
assert_equal sql.encoding, Encoding::UTF_8
end
end
diff --git a/activerecord/test/cases/arel/delete_manager_test.rb b/activerecord/test/cases/arel/delete_manager_test.rb
index 17a5271039..0bad02f4d2 100644
--- a/activerecord/test/cases/arel/delete_manager_test.rb
+++ b/activerecord/test/cases/arel/delete_manager_test.rb
@@ -15,6 +15,7 @@ module Arel
dm = Arel::DeleteManager.new
dm.take 10
dm.from table
+ dm.key = table[:id]
assert_match(/LIMIT 10/, dm.to_sql)
end
diff --git a/activerecord/test/cases/arel/select_manager_test.rb b/activerecord/test/cases/arel/select_manager_test.rb
index c17487ae88..5220950905 100644
--- a/activerecord/test/cases/arel/select_manager_test.rb
+++ b/activerecord/test/cases/arel/select_manager_test.rb
@@ -131,7 +131,7 @@ module Arel
right = table.alias
mgr = table.from
mgr.join(right).on("omg")
- mgr.to_sql.must_be_like %{ SELECT FROM "users" INNER JOIN "users" "users_2" ON omg }
+ mgr.to_sql.must_be_like %{ SELECT FROM "users" INNER JOIN "users" "users_2" ON omg }
end
it "converts to sqlliterals with multiple items" do
@@ -139,7 +139,7 @@ module Arel
right = table.alias
mgr = table.from
mgr.join(right).on("omg", "123")
- mgr.to_sql.must_be_like %{ SELECT FROM "users" INNER JOIN "users" "users_2" ON omg AND 123 }
+ mgr.to_sql.must_be_like %{ SELECT FROM "users" INNER JOIN "users" "users_2" ON omg AND 123 }
end
end
end
diff --git a/activerecord/test/cases/arel/visitors/depth_first_test.rb b/activerecord/test/cases/arel/visitors/depth_first_test.rb
index 3baccc7316..f94ad521d7 100644
--- a/activerecord/test/cases/arel/visitors/depth_first_test.rb
+++ b/activerecord/test/cases/arel/visitors/depth_first_test.rb
@@ -33,7 +33,6 @@ module Arel
Arel::Nodes::Ordering,
Arel::Nodes::StringJoin,
Arel::Nodes::UnqualifiedColumn,
- Arel::Nodes::Top,
Arel::Nodes::Limit,
Arel::Nodes::Else,
].each do |klass|
diff --git a/activerecord/test/cases/arel/visitors/dot_test.rb b/activerecord/test/cases/arel/visitors/dot_test.rb
index 98f3bab620..6b3c132f83 100644
--- a/activerecord/test/cases/arel/visitors/dot_test.rb
+++ b/activerecord/test/cases/arel/visitors/dot_test.rb
@@ -37,7 +37,6 @@ module Arel
Arel::Nodes::Offset,
Arel::Nodes::Ordering,
Arel::Nodes::UnqualifiedColumn,
- Arel::Nodes::Top,
Arel::Nodes::Limit,
].each do |klass|
define_method("test_#{klass.name.gsub('::', '_')}") do
diff --git a/activerecord/test/cases/arel/visitors/to_sql_test.rb b/activerecord/test/cases/arel/visitors/to_sql_test.rb
index e8ac50bfa3..b6426a211e 100644
--- a/activerecord/test/cases/arel/visitors/to_sql_test.rb
+++ b/activerecord/test/cases/arel/visitors/to_sql_test.rb
@@ -221,7 +221,7 @@ module Arel
sql.must_be_like "foo AS bar"
end
- it "should visit_Bignum" do
+ it "should visit_Integer" do
compile 8787878092
end
diff --git a/activerecord/test/cases/associations/belongs_to_associations_test.rb b/activerecord/test/cases/associations/belongs_to_associations_test.rb
index 0cc4ed7127..d520919332 100644
--- a/activerecord/test/cases/associations/belongs_to_associations_test.rb
+++ b/activerecord/test/cases/associations/belongs_to_associations_test.rb
@@ -452,15 +452,39 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
end
def test_belongs_to_counter_with_assigning_nil
- post = Post.find(1)
- comment = Comment.find(1)
+ topic = Topic.create!(title: "debate")
+ reply = Reply.create!(title: "blah!", content: "world around!", topic: topic)
- assert_equal post.id, comment.post_id
- assert_equal 2, Post.find(post.id).comments.size
+ assert_equal topic.id, reply.parent_id
+ assert_equal 1, topic.reload.replies.size
- comment.post = nil
+ reply.topic = nil
+ reply.reload
- assert_equal 1, Post.find(post.id).comments.size
+ assert_equal topic.id, reply.parent_id
+ assert_equal 1, topic.reload.replies.size
+
+ reply.topic = nil
+ reply.save!
+
+ assert_equal 0, topic.reload.replies.size
+ end
+
+ def test_belongs_to_counter_with_assigning_new_object
+ topic = Topic.create!(title: "debate")
+ reply = Reply.create!(title: "blah!", content: "world around!", topic: topic)
+
+ assert_equal topic.id, reply.parent_id
+ assert_equal 1, topic.reload.replies_count
+
+ topic2 = reply.build_topic(title: "debate2")
+ reply.save!
+
+ assert_not_equal topic.id, reply.parent_id
+ assert_equal topic2.id, reply.parent_id
+
+ assert_equal 0, topic.reload.replies_count
+ assert_equal 1, topic2.reload.replies_count
end
def test_belongs_to_with_primary_key_counter
@@ -485,11 +509,13 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
assert_equal 0, debate2.reload.replies_count
reply.topic_with_primary_key = debate2
+ reply.save!
assert_equal 0, debate.reload.replies_count
assert_equal 1, debate2.reload.replies_count
reply.topic_with_primary_key = nil
+ reply.save!
assert_equal 0, debate.reload.replies_count
assert_equal 0, debate2.reload.replies_count
@@ -516,11 +542,13 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
assert_equal 1, Topic.find(topic2.id).replies.size
reply1.topic = nil
+ reply1.save!
assert_equal 0, Topic.find(topic1.id).replies.size
assert_equal 0, Topic.find(topic2.id).replies.size
reply1.topic = topic1
+ reply1.save!
assert_equal 1, Topic.find(topic1.id).replies.size
assert_equal 0, Topic.find(topic2.id).replies.size
@@ -550,7 +578,11 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
def test_belongs_to_counter_after_save
topic = Topic.create!(title: "monday night")
- topic.replies.create!(title: "re: monday night", content: "football")
+
+ assert_queries(2) do
+ topic.replies.create!(title: "re: monday night", content: "football")
+ end
+
assert_equal 1, Topic.find(topic.id)[:replies_count]
topic.save!
@@ -584,8 +616,10 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
debate.touch(time: time)
debate2.touch(time: time)
- reply.parent_title = "debate"
- reply.save!
+ assert_queries(3) do
+ reply.parent_title = "debate"
+ reply.save!
+ end
assert_operator debate.reload.updated_at, :>, time
assert_operator debate2.reload.updated_at, :>, time
@@ -593,7 +627,10 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
debate.touch(time: time)
debate2.touch(time: time)
- reply.topic_with_primary_key = debate2
+ assert_queries(3) do
+ reply.topic_with_primary_key = debate2
+ reply.save!
+ end
assert_operator debate.reload.updated_at, :>, time
assert_operator debate2.reload.updated_at, :>, time
@@ -657,7 +694,7 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
line_item = LineItem.create!
Invoice.create!(line_items: [line_item])
- assert_queries(0) { line_item.save }
+ assert_no_queries { line_item.save }
end
def test_belongs_to_with_touch_option_on_destroy
@@ -752,7 +789,7 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
def test_dont_find_target_when_foreign_key_is_null
tagging = taggings(:thinking_general)
- assert_queries(0) { tagging.super_tag }
+ assert_no_queries { tagging.super_tag }
end
def test_dont_find_target_when_saving_foreign_key_after_stale_association_loaded
@@ -772,6 +809,7 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
reply = Reply.create(title: "re: zoom", content: "speedy quick!")
reply.topic = topic
+ reply.save!
assert_equal 1, topic.reload[:replies_count]
assert_equal 1, topic.replies.size
@@ -827,6 +865,7 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
silly = SillyReply.create(title: "gaga", content: "boo-boo")
silly.reply = reply
+ silly.save!
assert_equal 1, reply.reload[:replies_count]
assert_equal 1, reply.replies.size
diff --git a/activerecord/test/cases/associations/cascaded_eager_loading_test.rb b/activerecord/test/cases/associations/cascaded_eager_loading_test.rb
index e717621928..a9e22c7643 100644
--- a/activerecord/test/cases/associations/cascaded_eager_loading_test.rb
+++ b/activerecord/test/cases/associations/cascaded_eager_loading_test.rb
@@ -117,9 +117,8 @@ 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.parent_id = 1
- assert silly.save
+ reply = Reply.new(title: "gaga", content: "boo-boo", parent_id: 1)
+ assert reply.save
topics = Topic.all.merge!(includes: :replies, order: ["topics.id", "replies_topics.id"]).to_a
assert_no_queries do
@@ -161,6 +160,16 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase
end
end
+ def test_preload_through_missing_records
+ post = Post.where.not(author_id: Author.select(:id)).preload(author: { comments: :post }).first!
+ assert_no_queries { assert_nil post.author }
+ end
+
+ def test_eager_association_loading_with_missing_first_record
+ posts = Post.where(id: 3).preload(author: { comments: :post }).to_a
+ assert_equal posts.size, 1
+ 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
assert_equal vertices(:vertex_4), assert_no_queries { source.sinks.first.sinks.first.sinks.first }
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 c5b2b77bd4..525ad3197a 100644
--- a/activerecord/test/cases/associations/eager_load_nested_include_test.rb
+++ b/activerecord/test/cases/associations/eager_load_nested_include_test.rb
@@ -92,7 +92,7 @@ class EagerLoadPolyAssocsTest < ActiveRecord::TestCase
def test_include_query
res = ShapeExpression.all.merge!(includes: [ :shape, { paint: :non_poly } ]).to_a
assert_equal NUM_SHAPE_EXPRESSIONS, res.size
- assert_queries(0) do
+ assert_no_queries do
res.each do |se|
assert_not_nil se.paint.non_poly, "this is the association that was loading incorrectly before the change"
assert_not_nil se.shape, "just making sure other associations still work"
diff --git a/activerecord/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb
index ca902131f4..39034746c9 100644
--- a/activerecord/test/cases/associations/eager_test.rb
+++ b/activerecord/test/cases/associations/eager_test.rb
@@ -1346,7 +1346,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
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 }
- assert_queries(0) do
+ assert_no_queries do
assert_not_equal 0, post.comments.to_a.count
end
end
@@ -1618,6 +1618,32 @@ class EagerAssociationTest < ActiveRecord::TestCase
end
end
+ # Associations::Preloader#preloaders_on works with hash-like objects
+ test "preloading works with an object that responds to :to_hash" do
+ CustomHash = Class.new(Hash)
+
+ assert_nothing_raised do
+ Post.preload(CustomHash.new(comments: [{ author: :essays }])).first
+ end
+ end
+
+ # Associations::Preloader#preloaders_on works with string-like objects
+ test "preloading works with an object that responds to :to_str" do
+ CustomString = Class.new(String)
+
+ assert_nothing_raised do
+ Post.preload(CustomString.new("comments")).first
+ end
+ end
+
+ # Associations::Preloader#preloaders_on does not work with ranges
+ test "preloading fails when Range is passed" do
+ exception = assert_raises(ArgumentError) do
+ Post.preload(1..10).first
+ end
+ assert_equal("1..10 was not recognized for preload", exception.message)
+ end
+
private
def find_all_ordered(klass, include = nil)
klass.order("#{klass.table_name}.#{klass.primary_key}").includes(include).to_a
diff --git a/activerecord/test/cases/associations/extension_test.rb b/activerecord/test/cases/associations/extension_test.rb
index 5eacb5a3d8..aef8f31112 100644
--- a/activerecord/test/cases/associations/extension_test.rb
+++ b/activerecord/test/cases/associations/extension_test.rb
@@ -89,6 +89,6 @@ class AssociationsExtensionsTest < ActiveRecord::TestCase
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 482302055d..a90dcc0576 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
@@ -310,7 +310,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
def test_build
devel = Developer.find(1)
- proj = assert_no_queries(ignore_none: false) { devel.projects.build("name" => "Projekt") }
+ proj = assert_no_queries { devel.projects.build("name" => "Projekt") }
assert_not_predicate devel.projects, :loaded?
assert_equal devel.projects.last, proj
@@ -325,7 +325,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
def test_new_aliased_to_build
devel = Developer.find(1)
- proj = assert_no_queries(ignore_none: false) { devel.projects.new("name" => "Projekt") }
+ proj = assert_no_queries { devel.projects.new("name" => "Projekt") }
assert_not_predicate devel.projects, :loaded?
assert_equal devel.projects.last, proj
@@ -546,7 +546,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
developer = project.developers.first
- assert_no_queries(ignore_none: false) do
+ assert_no_queries do
assert_predicate project.developers, :loaded?
assert_includes project.developers, developer
end
@@ -741,7 +741,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
def test_get_ids_for_loaded_associations
developer = developers(:david)
developer.projects.reload
- assert_queries(0) do
+ assert_no_queries do
developer.project_ids
developer.project_ids
end
@@ -859,7 +859,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
def test_has_and_belongs_to_many_associations_on_new_records_use_null_relations
projects = Developer.new.projects
- assert_no_queries(ignore_none: false) do
+ assert_no_queries do
assert_equal [], projects
assert_equal [], projects.where(title: "omg")
assert_equal [], projects.pluck(:title)
diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb
index a2f6174dc1..036df1c31e 100644
--- a/activerecord/test/cases/associations/has_many_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_associations_test.rb
@@ -458,7 +458,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_finder_method_with_dirty_target
company = companies(:first_firm)
new_clients = []
- assert_no_queries(ignore_none: false) do
+ assert_no_queries 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")
@@ -478,7 +478,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_finder_bang_method_with_dirty_target
company = companies(:first_firm)
new_clients = []
- assert_no_queries(ignore_none: false) do
+ assert_no_queries 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")
@@ -955,7 +955,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_transactions_when_adding_to_new_record
- assert_no_queries(ignore_none: false) do
+ assert_no_queries do
firm = Firm.new
firm.clients_of_firm.concat(Client.new("name" => "Natural Company"))
end
@@ -970,7 +970,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_new_aliased_to_build
company = companies(:first_firm)
- new_client = assert_no_queries(ignore_none: false) { company.clients_of_firm.new("name" => "Another Client") }
+ new_client = assert_no_queries { company.clients_of_firm.new("name" => "Another Client") }
assert_not_predicate company.clients_of_firm, :loaded?
assert_equal "Another Client", new_client.name
@@ -980,7 +980,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_build
company = companies(:first_firm)
- new_client = assert_no_queries(ignore_none: false) { company.clients_of_firm.build("name" => "Another Client") }
+ new_client = assert_no_queries { company.clients_of_firm.build("name" => "Another Client") }
assert_not_predicate company.clients_of_firm, :loaded?
assert_equal "Another Client", new_client.name
@@ -1037,7 +1037,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 { company.clients_of_firm.build([{ "name" => "Another Client" }, { "name" => "Another Client II" }]) }
assert_equal 2, new_clients.size
end
@@ -1063,7 +1063,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 { company.clients_of_firm.build { |client| client.name = "Another Client" } }
assert_not_predicate company.clients_of_firm, :loaded?
assert_equal "Another Client", new_client.name
@@ -1073,7 +1073,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_build_many_via_block
company = companies(:first_firm)
- new_clients = assert_no_queries(ignore_none: false) do
+ new_clients = assert_no_queries do
company.clients_of_firm.build([{ "name" => "Another Client" }, { "name" => "Another Client II" }]) do |client|
client.name = "changed"
end
@@ -1197,6 +1197,38 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_equal 2, topic.reload.replies.size
end
+ def test_counter_cache_updates_in_memory_after_update_with_inverse_of_disabled
+ topic = Topic.create!(title: "Zoom-zoom-zoom")
+
+ assert_equal 0, topic.replies_count
+
+ reply1 = Reply.create!(title: "re: zoom", content: "speedy quick!")
+ reply2 = Reply.create!(title: "re: zoom 2", content: "OMG lol!")
+
+ assert_queries(4) do
+ topic.replies << [reply1, reply2]
+ end
+
+ assert_equal 2, topic.replies_count
+ assert_equal 2, topic.reload.replies_count
+ end
+
+ def test_counter_cache_updates_in_memory_after_update_with_inverse_of_enabled
+ category = Category.create!(name: "Counter Cache")
+
+ assert_nil category.categorizations_count
+
+ categorization1 = Categorization.create!
+ categorization2 = Categorization.create!
+
+ assert_queries(4) do
+ category.categorizations << [categorization1, categorization2]
+ end
+
+ assert_equal 2, category.categorizations_count
+ assert_equal 2, category.reload.categorizations_count
+ end
+
def test_pushing_association_updates_counter_cache
topic = Topic.order("id ASC").first
reply = Reply.create!
@@ -1234,7 +1266,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_calling_empty_with_counter_cache
post = posts(:welcome)
- assert_queries(0) do
+ assert_no_queries do
assert_not_empty post.comments
end
end
@@ -1332,7 +1364,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_transaction_when_deleting_new_record
- assert_no_queries(ignore_none: false) do
+ assert_no_queries do
firm = Firm.new
client = Client.new("name" => "New Client")
firm.clients_of_firm << client
@@ -1768,7 +1800,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
firm.clients = []
firm.save
- assert_queries(0, ignore_none: true) do
+ assert_no_queries do
firm.clients = []
end
@@ -1790,7 +1822,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_transactions_when_replacing_on_new_record
- assert_no_queries(ignore_none: false) do
+ assert_no_queries do
firm = Firm.new
firm.clients_of_firm = [Client.new("name" => "New Client")]
end
@@ -1803,7 +1835,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_get_ids_for_loaded_associations
company = companies(:first_firm)
company.clients.reload
- assert_queries(0) do
+ assert_no_queries do
company.client_ids
company.client_ids
end
@@ -1830,11 +1862,11 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_get_ids_for_association_on_new_record_does_not_try_to_find_records
- Company.columns # Load schema information so we don't query below
- Contract.columns # if running just this test.
+ # Load schema information so we don't query below if running just this test.
+ companies(:first_client).contract_ids
company = Company.new
- assert_queries(0) do
+ assert_no_queries do
company.contract_ids
end
@@ -1940,7 +1972,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
firm.clients.load_target
assert_predicate firm.clients, :loaded?
- assert_no_queries(ignore_none: false) do
+ assert_no_queries do
firm.clients.first
assert_equal 2, firm.clients.first(2).size
firm.clients.last
@@ -2353,7 +2385,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
test "has many associations on new records use null relations" do
post = Post.new
- assert_no_queries(ignore_none: false) do
+ assert_no_queries do
assert_equal [], post.comments
assert_equal [], post.comments.where(body: "omg")
assert_equal [], post.comments.pluck(:body)
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 d5573b6d02..94ad6e2d4d 100644
--- a/activerecord/test/cases/associations/has_many_through_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb
@@ -71,6 +71,15 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
club1.members.sort_by(&:id)
end
+ def test_preload_multiple_instances_of_the_same_record
+ club = Club.create!(name: "Aaron cool banana club")
+ Membership.create! club: club, member: Member.create!(name: "Aaron")
+ Membership.create! club: club, member: Member.create!(name: "Bob")
+
+ preloaded_clubs = Club.joins(:memberships).preload(:membership).to_a
+ assert_no_queries { preloaded_clubs.each(&:membership) }
+ end
+
def test_ordered_has_many_through
person_prime = Class.new(ActiveRecord::Base) do
def self.name; "Person"; end
@@ -265,7 +274,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
assert_queries(1) { posts(:thinking) }
new_person = nil # so block binding catches it
- assert_queries(0) do
+ assert_no_queries do
new_person = Person.new first_name: "bob"
end
@@ -285,7 +294,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
def test_associate_new_by_building
assert_queries(1) { posts(:thinking) }
- assert_queries(0) do
+ assert_no_queries do
posts(:thinking).people.build(first_name: "Bob")
posts(:thinking).people.new(first_name: "Ted")
end
@@ -562,10 +571,10 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
posts(:welcome).people = [people(:david)]
end
- assert_queries(0) {
+ assert_no_queries do
assert_includes posts(:welcome).people, people(:david)
assert_not_includes posts(:welcome).people, people(:michael)
- }
+ end
assert_includes posts(:welcome).reload.people.reload, people(:david)
assert_not_includes posts(:welcome).reload.people.reload, people(:michael)
@@ -689,7 +698,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
posts(:welcome).people.clear
end
- assert_queries(0) do
+ assert_no_queries do
assert_empty posts(:welcome).people
end
@@ -779,7 +788,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
def test_get_ids_for_loaded_associations
person = people(:michael)
person.posts.reload
- assert_queries(0) do
+ assert_no_queries do
person.post_ids
person.post_ids
end
@@ -1189,7 +1198,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
def test_has_many_through_associations_on_new_records_use_null_relations
person = Person.new
- assert_no_queries(ignore_none: false) do
+ assert_no_queries do
assert_equal [], person.posts
assert_equal [], person.posts.where(body: "omg")
assert_equal [], person.posts.pluck(:body)
diff --git a/activerecord/test/cases/associations/has_one_associations_test.rb b/activerecord/test/cases/associations/has_one_associations_test.rb
index 9eea34d2b9..885d9d7c2c 100644
--- a/activerecord/test/cases/associations/has_one_associations_test.rb
+++ b/activerecord/test/cases/associations/has_one_associations_test.rb
@@ -37,10 +37,10 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
def test_has_one_cache_nils
firm = companies(:another_firm)
assert_queries(1) { assert_nil firm.account }
- assert_queries(0) { assert_nil firm.account }
+ assert_no_queries { assert_nil firm.account }
- firms = Firm.all.merge!(includes: :account).to_a
- assert_queries(0) { firms.each(&:account) }
+ firms = Firm.includes(:account).to_a
+ assert_no_queries { firms.each(&:account) }
end
def test_with_select
@@ -231,7 +231,7 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
end
def test_build_association_dont_create_transaction
- assert_no_queries(ignore_none: false) {
+ assert_no_queries {
Firm.new.build_account
}
end
diff --git a/activerecord/test/cases/associations/nested_through_associations_test.rb b/activerecord/test/cases/associations/nested_through_associations_test.rb
index 03ed1c1d47..5821744530 100644
--- a/activerecord/test/cases/associations/nested_through_associations_test.rb
+++ b/activerecord/test/cases/associations/nested_through_associations_test.rb
@@ -137,7 +137,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
def test_has_many_through_has_one_through_with_has_one_source_reflection_preload
members = assert_queries(4) { Member.includes(:nested_sponsors).to_a }
mustache = sponsors(:moustache_club_sponsor_for_groucho)
- assert_no_queries(ignore_none: false) do
+ assert_no_queries do
assert_equal [mustache], members.first.nested_sponsors
end
end
@@ -196,7 +196,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
# postgresql test if randomly executed then executes "SHOW max_identifier_length". Hence
# the need to ignore certain predefined sqls that deal with system calls.
- assert_no_queries(ignore_none: false) do
+ assert_no_queries do
assert_equal [groucho_details, other_details], members.first.organization_member_details_2.sort_by(&:id)
end
end
diff --git a/activerecord/test/cases/associations_test.rb b/activerecord/test/cases/associations_test.rb
index 739eb02e0c..081da95df7 100644
--- a/activerecord/test/cases/associations_test.rb
+++ b/activerecord/test/cases/associations_test.rb
@@ -80,7 +80,7 @@ class AssociationsTest < ActiveRecord::TestCase
def test_force_reload
firm = Firm.new("name" => "A New Firm, Inc")
firm.save
- firm.clients.each {} # forcing to load all clients
+ firm.clients.each { } # forcing to load all clients
assert firm.clients.empty?, "New firm shouldn't have client objects"
assert_equal 0, firm.clients.size, "New firm should have 0 clients"
diff --git a/activerecord/test/cases/attributes_test.rb b/activerecord/test/cases/attributes_test.rb
index 3bc56694be..2632aec7ab 100644
--- a/activerecord/test/cases/attributes_test.rb
+++ b/activerecord/test/cases/attributes_test.rb
@@ -148,6 +148,20 @@ module ActiveRecord
assert_equal 2, klass.new.counter
end
+ test "procs for default values are evaluated even after column_defaults is called" do
+ klass = Class.new(OverloadedType) do
+ @@counter = 0
+ attribute :counter, :integer, default: -> { @@counter += 1 }
+ end
+
+ assert_equal 1, klass.new.counter
+
+ # column_defaults will increment the counter since the proc is called
+ klass.column_defaults
+
+ assert_equal 3, klass.new.counter
+ end
+
test "procs are memoized before type casting" do
klass = Class.new(OverloadedType) do
@@counter = 0
diff --git a/activerecord/test/cases/autosave_association_test.rb b/activerecord/test/cases/autosave_association_test.rb
index db3a58eba9..42d47527b2 100644
--- a/activerecord/test/cases/autosave_association_test.rb
+++ b/activerecord/test/cases/autosave_association_test.rb
@@ -642,7 +642,7 @@ class TestDefaultAutosaveAssociationOnAHasManyAssociation < ActiveRecord::TestCa
def test_build_before_save
company = companies(:first_firm)
- new_client = assert_no_queries(ignore_none: false) { company.clients_of_firm.build("name" => "Another Client") }
+ new_client = assert_no_queries { company.clients_of_firm.build("name" => "Another Client") }
assert_not_predicate company.clients_of_firm, :loaded?
company.name += "-changed"
@@ -653,7 +653,7 @@ 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 { company.clients_of_firm.build([{ "name" => "Another Client" }, { "name" => "Another Client II" }]) }
company.name += "-changed"
assert_queries(3) { assert company.save }
@@ -662,7 +662,7 @@ class TestDefaultAutosaveAssociationOnAHasManyAssociation < ActiveRecord::TestCa
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 { company.clients_of_firm.build { |client| client.name = "Another Client" } }
assert_not_predicate company.clients_of_firm, :loaded?
company.name += "-changed"
@@ -673,7 +673,7 @@ class TestDefaultAutosaveAssociationOnAHasManyAssociation < ActiveRecord::TestCa
def test_build_many_via_block_before_save
company = companies(:first_firm)
- assert_no_queries(ignore_none: false) do
+ assert_no_queries do
company.clients_of_firm.build([{ "name" => "Another Client" }, { "name" => "Another Client II" }]) do |client|
client.name = "changed"
end
@@ -1100,7 +1100,7 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase
assert @pirate.save
Pirate.transaction do
- assert_queries(0) do
+ assert_no_queries do
assert @pirate.save
end
end
@@ -1181,12 +1181,12 @@ class TestAutosaveAssociationOnAHasOneAssociation < ActiveRecord::TestCase
def test_changed_for_autosave_should_handle_cycles
@ship.pirate = @pirate
- assert_queries(0) { @ship.save! }
+ assert_no_queries { @ship.save! }
@parrot = @pirate.parrots.create(name: "some_name")
@parrot.name = "changed_name"
assert_queries(1) { @ship.save! }
- assert_queries(0) { @ship.save! }
+ assert_no_queries { @ship.save! }
end
def test_should_automatically_save_bang_the_associated_model
diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb
index f6311f9256..09e5517449 100644
--- a/activerecord/test/cases/base_test.rb
+++ b/activerecord/test/cases/base_test.rb
@@ -282,11 +282,13 @@ class BasicsTest < ActiveRecord::TestCase
end
def test_initialize_with_invalid_attribute
- Topic.new("title" => "test",
- "last_read(1i)" => "2005", "last_read(2i)" => "2", "last_read(3i)" => "31")
- rescue ActiveRecord::MultiparameterAssignmentErrors => ex
+ ex = assert_raise(ActiveRecord::MultiparameterAssignmentErrors) do
+ Topic.new("title" => "test",
+ "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00")
+ end
+
assert_equal(1, ex.errors.size)
- assert_equal("last_read", ex.errors[0].attribute)
+ assert_equal("written_on", ex.errors[0].attribute)
end
def test_create_after_initialize_without_block
diff --git a/activerecord/test/cases/batches_test.rb b/activerecord/test/cases/batches_test.rb
index c8163901c6..d21218a997 100644
--- a/activerecord/test/cases/batches_test.rb
+++ b/activerecord/test/cases/batches_test.rb
@@ -24,7 +24,7 @@ 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
@@ -155,7 +155,7 @@ class EachTest < ActiveRecord::TestCase
end
def test_find_in_batches_should_not_use_records_after_yielding_them_in_case_original_array_is_modified
- not_a_post = "not a post".dup
+ 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
assert_nothing_raised do
@@ -183,7 +183,7 @@ 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
@@ -192,7 +192,7 @@ class EachTest < ActiveRecord::TestCase
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
@@ -204,7 +204,7 @@ class EachTest < ActiveRecord::TestCase
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
@@ -213,7 +213,7 @@ class EachTest < ActiveRecord::TestCase
def test_find_in_batches_should_not_error_by_default
assert_nothing_raised do
- PostWithDefaultScope.find_in_batches() {}
+ PostWithDefaultScope.find_in_batches() { }
end
end
@@ -228,7 +228,7 @@ 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
@@ -420,7 +420,7 @@ class EachTest < ActiveRecord::TestCase
end
def test_in_batches_should_not_use_records_after_yielding_them_in_case_original_array_is_modified
- not_a_post = "not a post".dup
+ not_a_post = +"not a post"
def not_a_post.id
raise StandardError.new("not_a_post had #id called on it")
end
@@ -446,7 +446,7 @@ 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
@@ -597,15 +597,15 @@ class EachTest < ActiveRecord::TestCase
table: table_alias,
predicate_builder: predicate_builder
)
- posts.find_each {}
+ posts.find_each { }
end
end
test ".find_each bypasses the query cache for its own queries" do
Post.cache do
assert_queries(2) do
- Post.find_each {}
- Post.find_each {}
+ Post.find_each { }
+ Post.find_each { }
end
end
end
@@ -624,8 +624,8 @@ class EachTest < ActiveRecord::TestCase
test ".find_in_batches bypasses the query cache for its own queries" do
Post.cache do
assert_queries(2) do
- Post.find_in_batches {}
- Post.find_in_batches {}
+ Post.find_in_batches { }
+ Post.find_in_batches { }
end
end
end
@@ -644,8 +644,8 @@ class EachTest < ActiveRecord::TestCase
test ".in_batches bypasses the query cache for its own queries" do
Post.cache do
assert_queries(2) do
- Post.in_batches {}
- Post.in_batches {}
+ Post.in_batches { }
+ Post.in_batches { }
end
end
end
diff --git a/activerecord/test/cases/binary_test.rb b/activerecord/test/cases/binary_test.rb
index d5376ece69..58abdb47f7 100644
--- a/activerecord/test/cases/binary_test.rb
+++ b/activerecord/test/cases/binary_test.rb
@@ -12,7 +12,7 @@ unless current_adapter?(:DB2Adapter)
FIXTURES = %w(flowers.jpg example.log test.txt)
def test_mixed_encoding
- str = "\x80".dup
+ str = +"\x80"
str.force_encoding("ASCII-8BIT")
binary = Binary.new name: "いただきます!", data: str
diff --git a/activerecord/test/cases/callbacks_test.rb b/activerecord/test/cases/callbacks_test.rb
index 253c3099d6..0ea3fb86a6 100644
--- a/activerecord/test/cases/callbacks_test.rb
+++ b/activerecord/test/cases/callbacks_test.rb
@@ -480,7 +480,7 @@ class CallbacksTest < ActiveRecord::TestCase
def test_before_save_doesnt_allow_on_option
exception = assert_raises ArgumentError do
Class.new(ActiveRecord::Base) do
- before_save(on: :create) {}
+ before_save(on: :create) { }
end
end
assert_equal "Unknown key: :on. Valid keys are: :if, :unless, :prepend", exception.message
@@ -489,7 +489,7 @@ class CallbacksTest < ActiveRecord::TestCase
def test_around_save_doesnt_allow_on_option
exception = assert_raises ArgumentError do
Class.new(ActiveRecord::Base) do
- around_save(on: :create) {}
+ around_save(on: :create) { }
end
end
assert_equal "Unknown key: :on. Valid keys are: :if, :unless, :prepend", exception.message
@@ -498,7 +498,7 @@ class CallbacksTest < ActiveRecord::TestCase
def test_after_save_doesnt_allow_on_option
exception = assert_raises ArgumentError do
Class.new(ActiveRecord::Base) do
- after_save(on: :create) {}
+ after_save(on: :create) { }
end
end
assert_equal "Unknown key: :on. Valid keys are: :if, :unless, :prepend", exception.message
diff --git a/activerecord/test/cases/collection_cache_key_test.rb b/activerecord/test/cases/collection_cache_key_test.rb
index a5d908344a..844b2b2162 100644
--- a/activerecord/test/cases/collection_cache_key_test.rb
+++ b/activerecord/test/cases/collection_cache_key_test.rb
@@ -91,12 +91,12 @@ module ActiveRecord
developers = Developer.where(name: "David")
assert_queries(1) { developers.cache_key }
- assert_queries(0) { developers.cache_key }
+ assert_no_queries { developers.cache_key }
end
test "it doesn't trigger any query if the relation is already loaded" do
developers = Developer.where(name: "David").load
- assert_queries(0) { developers.cache_key }
+ assert_no_queries { developers.cache_key }
end
test "relation cache_key changes when the sql query changes" do
diff --git a/activerecord/test/cases/connection_adapters/connection_handler_test.rb b/activerecord/test/cases/connection_adapters/connection_handler_test.rb
index 8c204a2692..51d0cc3d12 100644
--- a/activerecord/test/cases/connection_adapters/connection_handler_test.rb
+++ b/activerecord/test/cases/connection_adapters/connection_handler_test.rb
@@ -151,6 +151,35 @@ module ActiveRecord
ActiveRecord::Base.configurations = @prev_configs
end
+ def test_symbolized_configurations_assignment
+ @prev_configs = ActiveRecord::Base.configurations
+ config = {
+ development: {
+ primary: {
+ adapter: "sqlite3",
+ database: "db/development.sqlite3",
+ },
+ },
+ test: {
+ primary: {
+ adapter: "sqlite3",
+ database: "db/test.sqlite3",
+ },
+ },
+ }
+ ActiveRecord::Base.configurations = config
+ ActiveRecord::Base.configurations.configs_for.each do |db_config|
+ assert_instance_of ActiveRecord::DatabaseConfigurations::HashConfig, db_config
+ assert_instance_of String, db_config.env_name
+ assert_instance_of String, db_config.spec_name
+ db_config.config.keys.each do |key|
+ assert_instance_of String, key
+ end
+ end
+ ensure
+ ActiveRecord::Base.configurations = @prev_configs
+ end
+
def test_retrieve_connection
assert @handler.retrieve_connection(@spec_name)
end
diff --git a/activerecord/test/cases/connection_management_test.rb b/activerecord/test/cases/connection_management_test.rb
index 0941ee3309..b9b5cc0e28 100644
--- a/activerecord/test/cases/connection_management_test.rb
+++ b/activerecord/test/cases/connection_management_test.rb
@@ -106,7 +106,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 06869eeab0..633d56e479 100644
--- a/activerecord/test/cases/connection_pool_test.rb
+++ b/activerecord/test/cases/connection_pool_test.rb
@@ -661,7 +661,7 @@ module ActiveRecord
end
stuck_thread = Thread.new do
- pool.with_connection {}
+ pool.with_connection { }
end
# wait for stuck_thread to get in queue
diff --git a/activerecord/test/cases/core_test.rb b/activerecord/test/cases/core_test.rb
index 6e7ae2efb4..f7fbf3ee8a 100644
--- a/activerecord/test/cases/core_test.rb
+++ b/activerecord/test/cases/core_test.rb
@@ -36,7 +36,7 @@ class CoreTest < ActiveRecord::TestCase
def test_pretty_print_new
topic = Topic.new
- actual = "".dup
+ actual = +""
PP.pp(topic, StringIO.new(actual))
expected = <<~PRETTY
#<Topic:0xXXXXXX
@@ -65,7 +65,7 @@ class CoreTest < ActiveRecord::TestCase
def test_pretty_print_persisted
topic = topics(:first)
- actual = "".dup
+ actual = +""
PP.pp(topic, StringIO.new(actual))
expected = <<~PRETTY
#<Topic:0x\\w+
@@ -93,7 +93,7 @@ class CoreTest < ActiveRecord::TestCase
def test_pretty_print_uninitialized
topic = Topic.allocate
- actual = "".dup
+ actual = +""
PP.pp(topic, StringIO.new(actual))
expected = "#<Topic:XXXXXX not initialized>\n"
assert actual.start_with?(expected.split("XXXXXX").first)
@@ -106,7 +106,7 @@ class CoreTest < ActiveRecord::TestCase
"inspecting topic"
end
end
- actual = "".dup
+ actual = +""
PP.pp(subtopic.new, StringIO.new(actual))
assert_equal "inspecting topic\n", actual
end
diff --git a/activerecord/test/cases/dirty_test.rb b/activerecord/test/cases/dirty_test.rb
index b1ebd20d6b..dfd74bfcb4 100644
--- a/activerecord/test/cases/dirty_test.rb
+++ b/activerecord/test/cases/dirty_test.rb
@@ -336,7 +336,7 @@ class DirtyTest < ActiveRecord::TestCase
end
with_partial_writes Pirate, true do
- assert_queries(0) { 2.times { pirate.save! } }
+ assert_no_queries { 2.times { pirate.save! } }
assert_equal old_updated_on, pirate.reload.updated_on
assert_queries(1) { pirate.catchphrase = "bar"; pirate.save! }
@@ -355,7 +355,7 @@ class DirtyTest < ActiveRecord::TestCase
old_lock_version = person.lock_version
with_partial_writes Person, true do
- assert_queries(0) { 2.times { person.save! } }
+ assert_no_queries { 2.times { person.save! } }
assert_equal old_lock_version, person.reload.lock_version
assert_queries(1) { person.first_name = "bar"; person.save! }
diff --git a/activerecord/test/cases/finder_respond_to_test.rb b/activerecord/test/cases/finder_respond_to_test.rb
index 59af4e6961..e0acd30c22 100644
--- a/activerecord/test/cases/finder_respond_to_test.rb
+++ b/activerecord/test/cases/finder_respond_to_test.rb
@@ -12,7 +12,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)
diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb
index e73c88dd5d..355fb4517f 100644
--- a/activerecord/test/cases/finder_test.rb
+++ b/activerecord/test/cases/finder_test.rb
@@ -371,7 +371,10 @@ class FinderTest < ActiveRecord::TestCase
end
def test_find_an_empty_array
- assert_equal [], Topic.find([])
+ empty_array = []
+ result = Topic.find(empty_array)
+ assert_equal [], result
+ assert_not_same empty_array, result
end
def test_find_doesnt_have_implicit_ordering
diff --git a/activerecord/test/cases/fixtures_test.rb b/activerecord/test/cases/fixtures_test.rb
index c65523d8c1..82ca15b415 100644
--- a/activerecord/test/cases/fixtures_test.rb
+++ b/activerecord/test/cases/fixtures_test.rb
@@ -974,7 +974,7 @@ class TransactionalFixturesOnConnectionNotification < ActiveRecord::TestCase
connection_id: connection.object_id
}
- message_bus.instrument("!connection.active_record", payload) {}
+ message_bus.instrument("!connection.active_record", payload) { }
end
end
end
@@ -1344,3 +1344,19 @@ class SameNameDifferentDatabaseFixturesTest < ActiveRecord::TestCase
assert_kind_of OtherDog, other_dogs(:lassie)
end
end
+
+class NilFixturePathTest < ActiveRecord::TestCase
+ test "raises an error when all fixtures loaded" do
+ error = assert_raises(StandardError) do
+ TestCase = Class.new(ActiveRecord::TestCase)
+ TestCase.class_eval do
+ self.fixture_path = nil
+ fixtures :all
+ end
+ end
+ assert_equal <<~MSG.squish, error.message
+ No fixture path found.
+ Please set `NilFixturePathTest::TestCase.fixture_path`.
+ MSG
+ end
+end
diff --git a/activerecord/test/cases/integration_test.rb b/activerecord/test/cases/integration_test.rb
index 36cd63c4d4..5687afbc71 100644
--- a/activerecord/test/cases/integration_test.rb
+++ b/activerecord/test/cases/integration_test.rb
@@ -157,18 +157,40 @@ class IntegrationTest < ActiveRecord::TestCase
skip("Subsecond precision is not supported") unless subsecond_precision_supported?
dev = Developer.first
key = dev.cache_key
- dev.touch
+ travel_to dev.updated_at + 0.000001 do
+ dev.touch
+ end
assert_not_equal key, dev.cache_key
end
def test_cache_key_format_is_not_too_precise
- skip("Subsecond precision is not supported") unless subsecond_precision_supported?
dev = Developer.first
dev.touch
key = dev.cache_key
assert_equal key, dev.reload.cache_key
end
+ def test_cache_version_format_is_precise_enough
+ skip("Subsecond precision is not supported") unless subsecond_precision_supported?
+ with_cache_versioning do
+ dev = Developer.first
+ version = dev.cache_version.to_param
+ travel_to Developer.first.updated_at + 0.000001 do
+ dev.touch
+ end
+ assert_not_equal version, dev.cache_version.to_param
+ end
+ end
+
+ def test_cache_version_format_is_not_too_precise
+ with_cache_versioning do
+ dev = Developer.first
+ dev.touch
+ key = dev.cache_version.to_param
+ assert_equal key, dev.reload.cache_version.to_param
+ end
+ end
+
def test_named_timestamps_for_cache_key
assert_deprecated do
owner = owners(:blackbeard)
@@ -185,50 +207,52 @@ class IntegrationTest < ActiveRecord::TestCase
end
def test_cache_key_is_stable_with_versioning_on
- Developer.cache_versioning = true
-
- developer = Developer.first
- first_key = developer.cache_key
+ with_cache_versioning do
+ developer = Developer.first
+ first_key = developer.cache_key
- developer.touch
- second_key = developer.cache_key
+ developer.touch
+ second_key = developer.cache_key
- assert_equal first_key, second_key
- ensure
- Developer.cache_versioning = false
+ assert_equal first_key, second_key
+ end
end
def test_cache_version_changes_with_versioning_on
- Developer.cache_versioning = true
-
- developer = Developer.first
- first_version = developer.cache_version
+ with_cache_versioning do
+ developer = Developer.first
+ first_version = developer.cache_version
- travel 10.seconds do
- developer.touch
- end
+ travel 10.seconds do
+ developer.touch
+ end
- second_version = developer.cache_version
+ second_version = developer.cache_version
- assert_not_equal first_version, second_version
- ensure
- Developer.cache_versioning = false
+ assert_not_equal first_version, second_version
+ end
end
def test_cache_key_retains_version_when_custom_timestamp_is_used
- Developer.cache_versioning = true
+ with_cache_versioning do
+ developer = Developer.first
+ first_key = developer.cache_key_with_version
- developer = Developer.first
- first_key = developer.cache_key_with_version
+ travel 10.seconds do
+ developer.touch
+ end
- travel 10.seconds do
- developer.touch
- end
+ second_key = developer.cache_key_with_version
- second_key = developer.cache_key_with_version
+ assert_not_equal first_key, second_key
+ end
+ end
- assert_not_equal first_key, second_key
+ def with_cache_versioning(value = true)
+ @old_cache_versioning = ActiveRecord::Base.cache_versioning
+ ActiveRecord::Base.cache_versioning = value
+ yield
ensure
- Developer.cache_versioning = false
+ ActiveRecord::Base.cache_versioning = @old_cache_versioning
end
end
diff --git a/activerecord/test/cases/invertible_migration_test.rb b/activerecord/test/cases/invertible_migration_test.rb
index 363beb4780..6cf17ac15d 100644
--- a/activerecord/test/cases/invertible_migration_test.rb
+++ b/activerecord/test/cases/invertible_migration_test.rb
@@ -22,6 +22,14 @@ module ActiveRecord
end
end
+ class InvertibleTransactionMigration < InvertibleMigration
+ def change
+ transaction do
+ super
+ end
+ end
+ end
+
class InvertibleRevertMigration < SilentMigration
def change
revert do
@@ -271,6 +279,14 @@ module ActiveRecord
assert_not revert.connection.table_exists?("horses")
end
+ def test_migrate_revert_transaction
+ migration = InvertibleTransactionMigration.new
+ migration.migrate :up
+ assert migration.connection.table_exists?("horses")
+ migration.migrate :down
+ assert_not migration.connection.table_exists?("horses")
+ end
+
def test_migrate_revert_change_column_default
migration1 = ChangeColumnDefault1.new
migration1.migrate(:up)
diff --git a/activerecord/test/cases/migration/change_table_test.rb b/activerecord/test/cases/migration/change_table_test.rb
index 034bf32165..c108d372d1 100644
--- a/activerecord/test/cases/migration/change_table_test.rb
+++ b/activerecord/test/cases/migration/change_table_test.rb
@@ -164,6 +164,14 @@ module ActiveRecord
end
end
+ def test_column_creates_column_with_index
+ with_change_table do |t|
+ @connection.expect :add_column, nil, [:delete_me, :bar, :integer, {}]
+ @connection.expect :add_index, nil, [:delete_me, :bar, {}]
+ t.column :bar, :integer, index: true
+ end
+ end
+
def test_index_creates_index
with_change_table do |t|
@connection.expect :add_index, nil, [:delete_me, :bar, {}]
diff --git a/activerecord/test/cases/migration/command_recorder_test.rb b/activerecord/test/cases/migration/command_recorder_test.rb
index 1a19b8dafd..01f8628fc5 100644
--- a/activerecord/test/cases/migration/command_recorder_test.rb
+++ b/activerecord/test/cases/migration/command_recorder_test.rb
@@ -117,13 +117,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
@@ -145,7 +145,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
@@ -360,6 +360,16 @@ module ActiveRecord
@recorder.inverse_of :remove_foreign_key, [:dogs]
end
end
+
+ def test_invert_transaction_with_irreversible_inside_is_irreversible
+ assert_raises(ActiveRecord::IrreversibleMigration) do
+ @recorder.revert do
+ @recorder.transaction do
+ @recorder.execute "some sql"
+ end
+ end
+ end
+ end
end
end
end
diff --git a/activerecord/test/cases/migration/compatibility_test.rb b/activerecord/test/cases/migration/compatibility_test.rb
index 69a50674af..017ee7951e 100644
--- a/activerecord/test/cases/migration/compatibility_test.rb
+++ b/activerecord/test/cases/migration/compatibility_test.rb
@@ -127,6 +127,20 @@ module ActiveRecord
assert_match(/LegacyMigration < ActiveRecord::Migration\[4\.2\]/, e.message)
end
+ def test_legacy_migrations_not_raise_exception_on_reverting_transaction
+ migration = Class.new(ActiveRecord::Migration[5.2]) {
+ def change
+ transaction do
+ execute "select 1"
+ end
+ end
+ }.new
+
+ assert_nothing_raised do
+ migration.migrate(:down)
+ end
+ end
+
if current_adapter?(:PostgreSQLAdapter)
class Testing < ActiveRecord::Base
end
diff --git a/activerecord/test/cases/migration/pending_migrations_test.rb b/activerecord/test/cases/migration/pending_migrations_test.rb
index dedb5ea502..119bfd372a 100644
--- a/activerecord/test/cases/migration/pending_migrations_test.rb
+++ b/activerecord/test/cases/migration/pending_migrations_test.rb
@@ -25,7 +25,7 @@ module ActiveRecord
ActiveRecord::Base.connection.drop_table "schema_migrations", if_exists: true
assert_raises ActiveRecord::PendingMigrationError do
- CheckPending.new(Proc.new {}).call({})
+ CheckPending.new(Proc.new { }).call({})
end
end
@@ -34,7 +34,7 @@ module ActiveRecord
migrator = Base.connection.migration_context
capture(:stdout) { migrator.migrate }
- assert_nil CheckPending.new(Proc.new {}).call({})
+ assert_nil CheckPending.new(Proc.new { }).call({})
end
end
end
diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb
index 868bb40ab2..5d060c8899 100644
--- a/activerecord/test/cases/migration_test.rb
+++ b/activerecord/test/cases/migration_test.rb
@@ -87,7 +87,6 @@ class MigrationTest < ActiveRecord::TestCase
def test_migrator_versions
migrations_path = MIGRATIONS_ROOT + "/valid"
- old_path = ActiveRecord::Migrator.migrations_paths
migrator = ActiveRecord::MigrationContext.new(migrations_path)
migrator.up
@@ -100,24 +99,18 @@ class MigrationTest < ActiveRecord::TestCase
ActiveRecord::SchemaMigration.create!(version: 3)
assert_equal true, migrator.needs_migration?
- ensure
- ActiveRecord::MigrationContext.new(old_path)
end
def test_migration_detection_without_schema_migration_table
ActiveRecord::Base.connection.drop_table "schema_migrations", if_exists: true
migrations_path = MIGRATIONS_ROOT + "/valid"
- old_path = ActiveRecord::Migrator.migrations_paths
migrator = ActiveRecord::MigrationContext.new(migrations_path)
assert_equal true, migrator.needs_migration?
- ensure
- ActiveRecord::MigrationContext.new(old_path)
end
def test_any_migrations
- old_path = ActiveRecord::Migrator.migrations_paths
migrator = ActiveRecord::MigrationContext.new(MIGRATIONS_ROOT + "/valid")
assert_predicate migrator, :any_migrations?
@@ -125,8 +118,6 @@ class MigrationTest < ActiveRecord::TestCase
migrator_empty = ActiveRecord::MigrationContext.new(MIGRATIONS_ROOT + "/empty")
assert_not_predicate migrator_empty, :any_migrations?
- ensure
- ActiveRecord::MigrationContext.new(old_path)
end
def test_migration_version
@@ -393,7 +384,6 @@ class MigrationTest < ActiveRecord::TestCase
def test_internal_metadata_stores_environment
current_env = ActiveRecord::ConnectionHandling::DEFAULT_ENV.call
migrations_path = MIGRATIONS_ROOT + "/valid"
- old_path = ActiveRecord::Migrator.migrations_paths
migrator = ActiveRecord::MigrationContext.new(migrations_path)
migrator.up
@@ -410,7 +400,6 @@ class MigrationTest < ActiveRecord::TestCase
migrator.up
assert_equal new_env, ActiveRecord::InternalMetadata[:environment]
ensure
- migrator = ActiveRecord::MigrationContext.new(old_path)
ENV["RAILS_ENV"] = original_rails_env
ENV["RACK_ENV"] = original_rack_env
migrator.up
@@ -422,16 +411,12 @@ class MigrationTest < ActiveRecord::TestCase
current_env = ActiveRecord::ConnectionHandling::DEFAULT_ENV.call
migrations_path = MIGRATIONS_ROOT + "/valid"
- old_path = ActiveRecord::Migrator.migrations_paths
current_env = ActiveRecord::ConnectionHandling::DEFAULT_ENV.call
migrator = ActiveRecord::MigrationContext.new(migrations_path)
migrator.up
assert_equal current_env, ActiveRecord::InternalMetadata[:environment]
assert_equal "bar", ActiveRecord::InternalMetadata[:foo]
- ensure
- migrator = ActiveRecord::MigrationContext.new(old_path)
- migrator.up
end
def test_proper_table_name_on_migration
@@ -1159,7 +1144,7 @@ class CopyMigrationsTest < ActiveRecord::TestCase
def test_check_pending_with_stdlib_logger
old, ActiveRecord::Base.logger = ActiveRecord::Base.logger, ::Logger.new($stdout)
quietly do
- assert_nothing_raised { ActiveRecord::Migration::CheckPending.new(Proc.new {}).call({}) }
+ assert_nothing_raised { ActiveRecord::Migration::CheckPending.new(Proc.new { }).call({}) }
end
ensure
ActiveRecord::Base.logger = old
diff --git a/activerecord/test/cases/migrator_test.rb b/activerecord/test/cases/migrator_test.rb
index 873455cf67..30e199f1c5 100644
--- a/activerecord/test/cases/migrator_test.rb
+++ b/activerecord/test/cases/migrator_test.rb
@@ -100,7 +100,6 @@ class MigratorTest < ActiveRecord::TestCase
def test_finds_migrations_in_subdirectories
migrations = ActiveRecord::MigrationContext.new(MIGRATIONS_ROOT + "/valid_with_subdirectories").migrations
-
[[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
diff --git a/activerecord/test/cases/null_relation_test.rb b/activerecord/test/cases/null_relation_test.rb
index 17527568f8..c5d5ded5ab 100644
--- a/activerecord/test/cases/null_relation_test.rb
+++ b/activerecord/test/cases/null_relation_test.rb
@@ -10,26 +10,26 @@ class NullRelationTest < ActiveRecord::TestCase
fixtures :posts, :comments
def test_none
- assert_no_queries(ignore_none: false) do
+ assert_no_queries do
assert_equal [], Developer.none
assert_equal [], Developer.all.none
end
end
def test_none_chainable
- assert_no_queries(ignore_none: false) do
+ assert_no_queries do
assert_equal [], Developer.none.where(name: "David")
end
end
def test_none_chainable_to_existing_scope_extension_method
- assert_no_queries(ignore_none: false) do
+ assert_no_queries do
assert_equal 1, Topic.anonymous_extension.none.one
end
end
def test_none_chained_to_methods_firing_queries_straight_to_db
- assert_no_queries(ignore_none: false) do
+ assert_no_queries do
assert_equal [], Developer.none.pluck(:id, :name)
assert_equal 0, Developer.none.delete_all
assert_equal 0, Developer.none.update_all(name: "David")
@@ -39,7 +39,7 @@ class NullRelationTest < ActiveRecord::TestCase
end
def test_null_relation_content_size_methods
- assert_no_queries(ignore_none: false) do
+ assert_no_queries do
assert_equal 0, Developer.none.size
assert_equal 0, Developer.none.count
assert_equal true, Developer.none.empty?
@@ -61,7 +61,7 @@ class NullRelationTest < ActiveRecord::TestCase
[:count, :sum].each do |method|
define_method "test_null_relation_#{method}" do
- assert_no_queries(ignore_none: false) do
+ assert_no_queries do
assert_equal 0, Comment.none.public_send(method, :id)
assert_equal Hash.new, Comment.none.group(:post_id).public_send(method, :id)
end
@@ -70,7 +70,7 @@ class NullRelationTest < ActiveRecord::TestCase
[:average, :minimum, :maximum].each do |method|
define_method "test_null_relation_#{method}" do
- assert_no_queries(ignore_none: false) do
+ assert_no_queries do
assert_nil Comment.none.public_send(method, :id)
assert_equal Hash.new, Comment.none.group(:post_id).public_send(method, :id)
end
diff --git a/activerecord/test/cases/persistence_test.rb b/activerecord/test/cases/persistence_test.rb
index 7348a22dd3..4830ff2b5f 100644
--- a/activerecord/test/cases/persistence_test.rb
+++ b/activerecord/test/cases/persistence_test.rb
@@ -13,90 +13,15 @@ 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
-
- # Oracle UPDATE does not support ORDER BY
- unless current_adapter?(:OracleAdapter)
- def test_update_all_ignores_order_without_limit_from_association
- author = authors(:david)
- assert_nothing_raised do
- assert_equal author.posts_with_comments_and_categories.length, author.posts_with_comments_and_categories.update_all([ "body = ?", "bulk update!" ])
- end
- end
-
- def test_update_all_doesnt_ignore_order
- 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")
- rescue ActiveRecord::ActiveRecordError
- false
- end
- end
-
- if test_update_with_order_succeeds.call("id DESC")
- assert_not 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")
- end
- end
- end
-
- def test_update_all_with_order_and_limit_updates_subset_only
- author = authors(:david)
- limited_posts = author.posts_sorted_by_id_limited
- assert_equal 1, limited_posts.size
- assert_equal 2, limited_posts.limit(2).size
- assert_equal 1, limited_posts.update_all([ "body = ?", "bulk update!" ])
- assert_equal "bulk update!", posts(:welcome).body
- assert_not_equal "bulk update!", posts(:thinking).body
- end
-
- def test_update_all_with_order_and_limit_and_offset_updates_subset_only
- author = authors(:david)
- limited_posts = author.posts_sorted_by_id_limited.offset(1)
- assert_equal 1, limited_posts.size
- assert_equal 2, limited_posts.limit(2).size
- assert_equal 1, limited_posts.update_all([ "body = ?", "bulk update!" ])
- assert_equal "bulk update!", posts(:thinking).body
- assert_not_equal "bulk update!", posts(:welcome).body
- end
-
- def test_delete_all_with_order_and_limit_deletes_subset_only
- author = authors(:david)
- limited_posts = Post.where(author: author).order(:id).limit(1)
- assert_equal 1, limited_posts.size
- assert_equal 2, limited_posts.limit(2).size
- assert_equal 1, limited_posts.delete_all
- assert_raise(ActiveRecord::RecordNotFound) { posts(:welcome) }
- assert posts(:thinking)
- end
-
- def test_delete_all_with_order_and_limit_and_offset_deletes_subset_only
- author = authors(:david)
- limited_posts = Post.where(author: author).order(:id).limit(1).offset(1)
- assert_equal 1, limited_posts.size
- assert_equal 2, limited_posts.limit(2).size
- assert_equal 1, limited_posts.delete_all
- assert_raise(ActiveRecord::RecordNotFound) { posts(:thinking) }
- assert posts(:welcome)
- end
- end
+ fixtures :topics, :companies, :developers, :accounts, :minimalistics, :authors, :author_addresses, :posts, :minivans
def test_update_many
topic_data = { 1 => { "content" => "1 updated" }, 2 => { "content" => "2 updated" } }
@@ -145,34 +70,6 @@ class PersistenceTest < ActiveRecord::TestCase
assert_equal Topic.count, Topic.delete_all
end
- def test_delete_all_with_joins_and_where_part_is_hash
- pets = Pet.joins(:toys).where(toys: { name: "Bone" })
-
- assert_equal true, pets.exists?
- assert_equal pets.count, pets.delete_all
- end
-
- def test_delete_all_with_joins_and_where_part_is_not_hash
- pets = Pet.joins(:toys).where("toys.name = ?", "Bone")
-
- assert_equal true, pets.exists?
- assert_equal pets.count, pets.delete_all
- end
-
- def test_delete_all_with_left_joins
- pets = Pet.left_joins(:toys).where(toys: { name: "Bone" })
-
- assert_equal true, pets.exists?
- assert_equal pets.count, pets.delete_all
- end
-
- def test_delete_all_with_includes
- pets = Pet.includes(:toys).where(toys: { name: "Bone" })
-
- assert_equal true, pets.exists?
- assert_equal pets.count, pets.delete_all
- end
-
def test_increment_attribute
assert_equal 50, accounts(:signals37).credit_limit
accounts(:signals37).increment! :credit_limit
@@ -230,16 +127,9 @@ class PersistenceTest < ActiveRecord::TestCase
assert_operator previously_written_on, :<, topic.written_on
end
- def test_destroy_all
- conditions = "author_name = 'Mary'"
- topics_by_mary = Topic.all.merge!(where: conditions, order: "id").to_a
- assert_not_empty topics_by_mary
-
- 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"
- end
+ def test_increment_with_no_arg
+ topic = topics(:first)
+ assert_raises(ArgumentError) { topic.increment! }
end
def test_destroy_many
@@ -556,19 +446,17 @@ class PersistenceTest < ActiveRecord::TestCase
end
def test_update_attribute_does_not_run_sql_if_attribute_is_not_changed
- klass = Class.new(Topic) do
- def self.name; "Topic"; end
- end
- topic = klass.create(title: "Another New Topic")
- assert_queries(0) do
+ topic = Topic.create(title: "Another New Topic")
+ assert_no_queries do
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) { assert topic.update(title: "Another New Topic") }
- assert_queries(0) { assert topic.update(title: "Another New Topic") }
+ assert_no_queries do
+ assert topic.update(title: "Another New Topic")
+ end
end
def test_delete
@@ -638,32 +526,6 @@ class PersistenceTest < ActiveRecord::TestCase
assert_nil Topic.find(2).last_read
end
- def test_update_all_with_joins
- pets = Pet.joins(:toys).where(toys: { name: "Bone" })
-
- assert_equal true, pets.exists?
- assert_equal pets.count, pets.update_all(name: "Bob")
- end
-
- def test_update_all_with_left_joins
- pets = Pet.left_joins(:toys).where(toys: { name: "Bone" })
-
- assert_equal true, pets.exists?
- assert_equal pets.count, pets.update_all(name: "Bob")
- end
-
- def test_update_all_with_includes
- pets = Pet.includes(:toys).where(toys: { name: "Bone" })
-
- assert_equal true, pets.exists?
- assert_equal pets.count, pets.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 0, WarehouseThing.find(1).value
- end
-
def test_delete_new_record
client = Client.new(name: "37signals")
client.delete
@@ -1185,21 +1047,19 @@ class PersistenceTest < ActiveRecord::TestCase
ActiveRecord::Base.connection.disable_query_cache!
end
- class SaveTest < ActiveRecord::TestCase
- def test_save_touch_false
- pet = Pet.create!(
- name: "Bob",
- created_at: 1.day.ago,
- updated_at: 1.day.ago)
+ def test_save_touch_false
+ parrot = Parrot.create!(
+ name: "Bob",
+ created_at: 1.day.ago,
+ updated_at: 1.day.ago)
- created_at = pet.created_at
- updated_at = pet.updated_at
+ created_at = parrot.created_at
+ updated_at = parrot.updated_at
- pet.name = "Barb"
- pet.save!(touch: false)
- assert_equal pet.created_at, created_at
- assert_equal pet.updated_at, updated_at
- end
+ parrot.name = "Barb"
+ parrot.save!(touch: false)
+ assert_equal parrot.created_at, created_at
+ assert_equal parrot.updated_at, updated_at
end
def test_reset_column_information_resets_children
diff --git a/activerecord/test/cases/query_cache_test.rb b/activerecord/test/cases/query_cache_test.rb
index 69be091869..565190c476 100644
--- a/activerecord/test/cases/query_cache_test.rb
+++ b/activerecord/test/cases/query_cache_test.rb
@@ -55,16 +55,6 @@ class QueryCacheTest < ActiveRecord::TestCase
assert_cache :off
end
- private def with_temporary_connection_pool
- old_pool = ActiveRecord::Base.connection_handler.retrieve_connection_pool(ActiveRecord::Base.connection_specification_name)
- new_pool = ActiveRecord::ConnectionAdapters::ConnectionPool.new ActiveRecord::Base.connection_pool.spec
- ActiveRecord::Base.connection_handler.send(:owner_to_pool)["primary"] = new_pool
-
- yield
- ensure
- ActiveRecord::Base.connection_handler.send(:owner_to_pool)["primary"] = old_pool
- end
-
def test_query_cache_across_threads
with_temporary_connection_pool do
begin
@@ -200,7 +190,7 @@ class QueryCacheTest < ActiveRecord::TestCase
Task.cache do
assert_queries(2) { Task.find(1); Task.find(2) }
end
- assert_queries(0) { Task.find(1); Task.find(1); Task.find(2) }
+ assert_no_queries { Task.find(1); Task.find(1); Task.find(2) }
end
end
@@ -382,7 +372,7 @@ class QueryCacheTest < ActiveRecord::TestCase
end
# Check that if the same query is run again, no queries are executed
- assert_queries(0) do
+ assert_no_queries do
assert_equal 0, Post.where(title: "test").to_a.count
end
@@ -437,8 +427,9 @@ class QueryCacheTest < ActiveRecord::TestCase
# Clear places where type information is cached
Task.reset_column_information
Task.initialize_find_by_cache
+ Task.define_attribute_methods
- assert_queries(0) do
+ assert_no_queries do
Task.find(1)
end
end
@@ -495,6 +486,17 @@ class QueryCacheTest < ActiveRecord::TestCase
end
private
+
+ def with_temporary_connection_pool
+ old_pool = ActiveRecord::Base.connection_handler.retrieve_connection_pool(ActiveRecord::Base.connection_specification_name)
+ new_pool = ActiveRecord::ConnectionAdapters::ConnectionPool.new ActiveRecord::Base.connection_pool.spec
+ ActiveRecord::Base.connection_handler.send(:owner_to_pool)["primary"] = new_pool
+
+ yield
+ ensure
+ ActiveRecord::Base.connection_handler.send(:owner_to_pool)["primary"] = old_pool
+ end
+
def middleware(&app)
executor = Class.new(ActiveSupport::Executor)
ActiveRecord::QueryCache.install_executor_hooks executor
diff --git a/activerecord/test/cases/relation/delete_all_test.rb b/activerecord/test/cases/relation/delete_all_test.rb
new file mode 100644
index 0000000000..446d7621ea
--- /dev/null
+++ b/activerecord/test/cases/relation/delete_all_test.rb
@@ -0,0 +1,104 @@
+# frozen_string_literal: true
+
+require "cases/helper"
+require "models/author"
+require "models/post"
+require "models/pet"
+require "models/toy"
+
+class DeleteAllTest < ActiveRecord::TestCase
+ fixtures :authors, :author_addresses, :posts, :pets, :toys
+
+ def test_destroy_all
+ davids = Author.where(name: "David")
+
+ # Force load
+ assert_equal [authors(:david)], davids.to_a
+ assert_predicate davids, :loaded?
+
+ assert_difference("Author.count", -1) do
+ destroyed = davids.destroy_all
+ assert_equal [authors(:david)], destroyed
+ assert_predicate destroyed.first, :frozen?
+ end
+
+ assert_equal [], davids.to_a
+ assert_predicate davids, :loaded?
+ end
+
+ def test_delete_all
+ davids = Author.where(name: "David")
+
+ assert_difference("Author.count", -1) { davids.delete_all }
+ assert_not_predicate davids, :loaded?
+ end
+
+ def test_delete_all_loaded
+ davids = Author.where(name: "David")
+
+ # Force load
+ assert_equal [authors(:david)], davids.to_a
+ assert_predicate davids, :loaded?
+
+ assert_difference("Author.count", -1) { davids.delete_all }
+
+ assert_equal [], davids.to_a
+ assert_predicate davids, :loaded?
+ end
+
+ def test_delete_all_with_unpermitted_relation_raises_error
+ 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 }
+ end
+
+ def test_delete_all_with_joins_and_where_part_is_hash
+ pets = Pet.joins(:toys).where(toys: { name: "Bone" })
+
+ assert_equal true, pets.exists?
+ assert_equal pets.count, pets.delete_all
+ end
+
+ def test_delete_all_with_joins_and_where_part_is_not_hash
+ pets = Pet.joins(:toys).where("toys.name = ?", "Bone")
+
+ assert_equal true, pets.exists?
+ assert_equal pets.count, pets.delete_all
+ end
+
+ def test_delete_all_with_left_joins
+ pets = Pet.left_joins(:toys).where(toys: { name: "Bone" })
+
+ assert_equal true, pets.exists?
+ assert_equal pets.count, pets.delete_all
+ end
+
+ def test_delete_all_with_includes
+ pets = Pet.includes(:toys).where(toys: { name: "Bone" })
+
+ assert_equal true, pets.exists?
+ assert_equal pets.count, pets.delete_all
+ end
+
+ unless current_adapter?(:OracleAdapter)
+ def test_delete_all_with_order_and_limit_deletes_subset_only
+ author = authors(:david)
+ limited_posts = Post.where(author: author).order(:id).limit(1)
+ assert_equal 1, limited_posts.size
+ assert_equal 2, limited_posts.limit(2).size
+ assert_equal 1, limited_posts.delete_all
+ assert_raise(ActiveRecord::RecordNotFound) { posts(:welcome) }
+ assert posts(:thinking)
+ end
+
+ def test_delete_all_with_order_and_limit_and_offset_deletes_subset_only
+ author = authors(:david)
+ limited_posts = Post.where(author: author).order(:id).limit(1).offset(1)
+ assert_equal 1, limited_posts.size
+ assert_equal 2, limited_posts.limit(2).size
+ assert_equal 1, limited_posts.delete_all
+ assert_raise(ActiveRecord::RecordNotFound) { posts(:thinking) }
+ assert posts(:welcome)
+ end
+ end
+end
diff --git a/activerecord/test/cases/relation/merging_test.rb b/activerecord/test/cases/relation/merging_test.rb
index 6e7998d15a..224e4f39a8 100644
--- a/activerecord/test/cases/relation/merging_test.rb
+++ b/activerecord/test/cases/relation/merging_test.rb
@@ -122,6 +122,10 @@ class RelationMergingTest < ActiveRecord::TestCase
assert_not_empty relation.from_clause
end
+ def test_merging_with_from_clause_on_different_class
+ assert Comment.joins(:post).merge(Post.from("posts")).first
+ end
+
def test_merging_with_order_with_binds
relation = Post.all.merge(Post.order([Arel.sql("title LIKE ?"), "%suffix"]))
assert_equal ["title LIKE '%suffix'"], relation.order_values
diff --git a/activerecord/test/cases/relation/update_all_test.rb b/activerecord/test/cases/relation/update_all_test.rb
new file mode 100644
index 0000000000..09c365f31b
--- /dev/null
+++ b/activerecord/test/cases/relation/update_all_test.rb
@@ -0,0 +1,239 @@
+# frozen_string_literal: true
+
+require "cases/helper"
+require "models/author"
+require "models/category"
+require "models/comment"
+require "models/computer"
+require "models/developer"
+require "models/post"
+require "models/person"
+require "models/pet"
+require "models/toy"
+require "models/topic"
+require "models/tag"
+require "models/tagging"
+require "models/warehouse_thing"
+
+class UpdateAllTest < ActiveRecord::TestCase
+ fixtures :authors, :author_addresses, :comments, :developers, :posts, :people, :pets, :toys, :tags, :taggings, "warehouse-things"
+
+ class TopicWithCallbacks < ActiveRecord::Base
+ self.table_name = :topics
+ cattr_accessor :topic_count
+ before_update { |topic| topic.author_name = "David" if topic.author_name.blank? }
+ after_update { |topic| topic.class.topic_count = topic.class.count }
+ end
+
+ def test_update_all_with_scope
+ tag = Tag.first
+ Post.tagged_with(tag.id).update_all(title: "rofl")
+ posts = Post.tagged_with(tag.id).all.to_a
+ assert_operator posts.length, :>, 0
+ posts.each { |post| assert_equal "rofl", post.title }
+ end
+
+ def test_update_all_with_non_standard_table_name
+ assert_equal 1, WarehouseThing.where(id: 1).update_all(["value = ?", 0])
+ assert_equal 0, WarehouseThing.find(1).value
+ end
+
+ def test_update_all_with_blank_argument
+ assert_raises(ArgumentError) { Comment.update_all({}) }
+ end
+
+ def test_update_all_with_joins
+ pets = Pet.joins(:toys).where(toys: { name: "Bone" })
+
+ assert_equal true, pets.exists?
+ assert_equal pets.count, pets.update_all(name: "Bob")
+ end
+
+ def test_update_all_with_left_joins
+ pets = Pet.left_joins(:toys).where(toys: { name: "Bone" })
+
+ assert_equal true, pets.exists?
+ assert_equal pets.count, pets.update_all(name: "Bob")
+ end
+
+ def test_update_all_with_includes
+ pets = Pet.includes(:toys).where(toys: { name: "Bone" })
+
+ assert_equal true, pets.exists?
+ assert_equal pets.count, pets.update_all(name: "Bob")
+ 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)
+ assert_equal posts(:thinking), comments(:greetings).post
+ 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)
+ 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)
+ count = all_comments.count
+ comments = all_comments.offset(1)
+
+ 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")
+ count = all_comments.count
+ comments = all_comments.offset(1)
+
+ 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_counters_with_joins
+ assert_nil pets(:parrot).integer
+
+ Pet.joins(:toys).where(toys: { name: "Bone" }).update_counters(integer: 1)
+
+ assert_equal 1, pets(:parrot).reload.integer
+ end
+
+ def test_touch_all_updates_records_timestamps
+ david = developers(:david)
+ david_previously_updated_at = david.updated_at
+ jamis = developers(:jamis)
+ jamis_previously_updated_at = jamis.updated_at
+ Developer.where(name: "David").touch_all
+
+ assert_not_equal david_previously_updated_at, david.reload.updated_at
+ assert_equal jamis_previously_updated_at, jamis.reload.updated_at
+ end
+
+ def test_touch_all_with_custom_timestamp
+ developer = developers(:david)
+ previously_created_at = developer.created_at
+ previously_updated_at = developer.updated_at
+ Developer.where(name: "David").touch_all(:created_at)
+ developer.reload
+
+ assert_not_equal previously_created_at, developer.created_at
+ assert_not_equal previously_updated_at, developer.updated_at
+ end
+
+ def test_touch_all_with_given_time
+ developer = developers(:david)
+ previously_created_at = developer.created_at
+ previously_updated_at = developer.updated_at
+ new_time = Time.utc(2015, 2, 16, 4, 54, 0)
+ Developer.where(name: "David").touch_all(:created_at, time: new_time)
+ developer.reload
+
+ assert_not_equal previously_created_at, developer.created_at
+ assert_not_equal previously_updated_at, developer.updated_at
+ assert_equal new_time, developer.created_at
+ assert_equal new_time, developer.updated_at
+ end
+
+ def test_touch_all_updates_locking_column
+ person = people(:david)
+
+ assert_difference -> { person.reload.lock_version }, +1 do
+ Person.where(first_name: "David").touch_all
+ end
+ end
+
+ def test_update_on_relation
+ 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")
+
+ assert_equal TopicWithCallbacks.count, TopicWithCallbacks.topic_count
+
+ 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
+ end
+
+ def test_update_with_ids_on_relation
+ topic1 = TopicWithCallbacks.create!(title: "arel", author_name: nil)
+ topic2 = TopicWithCallbacks.create!(title: "activerecord", author_name: nil)
+ topics = TopicWithCallbacks.none
+ topics.update(
+ [topic1.id, topic2.id],
+ [{ title: "adequaterecord" }, { title: "adequaterecord" }]
+ )
+
+ assert_equal TopicWithCallbacks.count, TopicWithCallbacks.topic_count
+
+ 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
+ end
+
+ def test_update_on_relation_passing_active_record_object_is_not_permitted
+ topic = Topic.create!(title: "Foo", author_name: nil)
+ assert_raises(ArgumentError) do
+ Topic.where(id: topic.id).update(topic, title: "Bar")
+ end
+ end
+
+ # Oracle UPDATE does not support ORDER BY
+ unless current_adapter?(:OracleAdapter)
+ def test_update_all_ignores_order_without_limit_from_association
+ author = authors(:david)
+ assert_nothing_raised do
+ assert_equal author.posts_with_comments_and_categories.length, author.posts_with_comments_and_categories.update_all([ "body = ?", "bulk update!" ])
+ end
+ end
+
+ def test_update_all_doesnt_ignore_order
+ 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")
+ rescue ActiveRecord::ActiveRecordError
+ false
+ end
+ end
+
+ if test_update_with_order_succeeds.call("id DESC")
+ # test that this wasn't a fluke and using an incorrect order results in an exception
+ assert_not test_update_with_order_succeeds.call("id ASC")
+ 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")
+ end
+ end
+ end
+
+ def test_update_all_with_order_and_limit_updates_subset_only
+ author = authors(:david)
+ limited_posts = author.posts_sorted_by_id_limited
+ assert_equal 1, limited_posts.size
+ assert_equal 2, limited_posts.limit(2).size
+ assert_equal 1, limited_posts.update_all([ "body = ?", "bulk update!" ])
+ assert_equal "bulk update!", posts(:welcome).body
+ assert_not_equal "bulk update!", posts(:thinking).body
+ end
+
+ def test_update_all_with_order_and_limit_and_offset_updates_subset_only
+ author = authors(:david)
+ limited_posts = author.posts_sorted_by_id_limited.offset(1)
+ assert_equal 1, limited_posts.size
+ assert_equal 2, limited_posts.limit(2).size
+ assert_equal 1, limited_posts.update_all([ "body = ?", "bulk update!" ])
+ assert_equal "bulk update!", posts(:thinking).body
+ assert_not_equal "bulk update!", posts(:welcome).body
+ end
+ end
+end
diff --git a/activerecord/test/cases/relation_test.rb b/activerecord/test/cases/relation_test.rb
index fbeb617b29..68161f6a84 100644
--- a/activerecord/test/cases/relation_test.rb
+++ b/activerecord/test/cases/relation_test.rb
@@ -232,7 +232,7 @@ module ActiveRecord
assert_equal 3, nb_inner_join, "Wrong amount of INNER JOIN in query"
# using `\W` as the column separator
- assert queries.any? { |sql| %r[INNER\s+JOIN\s+#{Author.quoted_table_name}\s+\Wauthors_categorizations\W]i.match?(sql) }, "Should be aliasing the child INNER JOINs in query"
+ assert queries.any? { |sql| %r[INNER\s+JOIN\s+#{Regexp.escape(Author.quoted_table_name)}\s+\Wauthors_categorizations\W]i.match?(sql) }, "Should be aliasing the child INNER JOINs in query"
end
def test_relation_with_merged_joins_aliased_works
diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb
index c14dc23cf3..9914a61033 100644
--- a/activerecord/test/cases/relations_test.rb
+++ b/activerecord/test/cases/relations_test.rb
@@ -29,13 +29,6 @@ require "models/subscriber"
class RelationTest < ActiveRecord::TestCase
fixtures :authors, :author_addresses, :topics, :entrants, :developers, :people, :companies, :developers_projects, :accounts, :categories, :categorizations, :categories_posts, :posts, :comments, :tags, :taggings, :cars, :minivans
- class TopicWithCallbacks < ActiveRecord::Base
- self.table_name = :topics
- cattr_accessor :topic_count
- before_update { |topic| topic.author_name = "David" if topic.author_name.blank? }
- after_update { |topic| topic.class.topic_count = topic.class.count }
- end
-
def test_do_not_double_quote_string_id
van = Minivan.last
assert van
@@ -863,45 +856,6 @@ class RelationTest < ActiveRecord::TestCase
assert_equal authors(:bob), authors.last
end
- def test_destroy_all
- davids = Author.where(name: "David")
-
- # Force load
- assert_equal [authors(:david)], davids.to_a
- assert_predicate davids, :loaded?
-
- assert_difference("Author.count", -1) { davids.destroy_all }
-
- assert_equal [], davids.to_a
- assert_predicate davids, :loaded?
- end
-
- def test_delete_all
- davids = Author.where(name: "David")
-
- assert_difference("Author.count", -1) { davids.delete_all }
- assert_not_predicate davids, :loaded?
- end
-
- def test_delete_all_loaded
- davids = Author.where(name: "David")
-
- # Force load
- assert_equal [authors(:david)], davids.to_a
- assert_predicate davids, :loaded?
-
- assert_difference("Author.count", -1) { davids.delete_all }
-
- assert_equal [], davids.to_a
- assert_predicate davids, :loaded?
- end
-
- def test_delete_all_with_unpermitted_relation_raises_error
- 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 }
- end
-
def test_select_with_aggregates
posts = Post.select(:title, :body)
@@ -986,14 +940,6 @@ class RelationTest < ActiveRecord::TestCase
assert_queries(1) { assert_equal 11, posts.load.size }
end
- def test_update_all_with_scope
- tag = Tag.first
- 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 }
- end
-
def test_count_explicit_columns
Post.update_all(comments_count: nil)
posts = Post.all
@@ -1497,132 +1443,6 @@ class RelationTest < ActiveRecord::TestCase
assert_equal authors(:david), Author.order("id DESC , name DESC").last
end
- def test_update_all_with_blank_argument
- assert_raises(ArgumentError) { Comment.update_all({}) }
- end
-
- def test_update_all_with_joins
- 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 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)
- 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)
- 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)
- count = all_comments.count
- comments = all_comments.offset(1)
-
- 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")
- count = all_comments.count
- comments = all_comments.offset(1)
-
- 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_touch_all_updates_records_timestamps
- david = developers(:david)
- david_previously_updated_at = david.updated_at
- jamis = developers(:jamis)
- jamis_previously_updated_at = jamis.updated_at
- Developer.where(name: "David").touch_all
-
- assert_not_equal david_previously_updated_at, david.reload.updated_at
- assert_equal jamis_previously_updated_at, jamis.reload.updated_at
- end
-
- def test_touch_all_with_custom_timestamp
- developer = developers(:david)
- previously_created_at = developer.created_at
- previously_updated_at = developer.updated_at
- Developer.where(name: "David").touch_all(:created_at)
- developer = developer.reload
-
- assert_not_equal previously_created_at, developer.created_at
- assert_not_equal previously_updated_at, developer.updated_at
- end
-
- def test_touch_all_with_given_time
- developer = developers(:david)
- previously_created_at = developer.created_at
- previously_updated_at = developer.updated_at
- new_time = Time.utc(2015, 2, 16, 4, 54, 0)
- Developer.where(name: "David").touch_all(:created_at, time: new_time)
- developer = developer.reload
-
- assert_not_equal previously_created_at, developer.created_at
- assert_not_equal previously_updated_at, developer.updated_at
- assert_equal new_time, developer.created_at
- assert_equal new_time, developer.updated_at
- end
-
- def test_touch_all_updates_locking_column
- person = people(:david)
-
- assert_difference -> { person.reload.lock_version }, +1 do
- Person.where(first_name: "David").touch_all
- end
- end
-
- def test_update_on_relation
- 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")
-
- assert_equal TopicWithCallbacks.count, TopicWithCallbacks.topic_count
-
- 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
- end
-
- def test_update_with_ids_on_relation
- topic1 = TopicWithCallbacks.create!(title: "arel", author_name: nil)
- topic2 = TopicWithCallbacks.create!(title: "activerecord", author_name: nil)
- topics = TopicWithCallbacks.none
- topics.update(
- [topic1.id, topic2.id],
- [{ title: "adequaterecord" }, { title: "adequaterecord" }]
- )
-
- assert_equal TopicWithCallbacks.count, TopicWithCallbacks.topic_count
-
- 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
- end
-
- def test_update_on_relation_passing_active_record_object_is_not_permitted
- topic = Topic.create!(title: "Foo", author_name: nil)
- assert_raises(ArgumentError) do
- Topic.where(id: topic.id).update(topic, title: "Bar")
- end
- end
-
def test_distinct
tag1 = Tag.create(name: "Foo")
tag2 = Tag.create(name: "Foo")
diff --git a/activerecord/test/cases/result_test.rb b/activerecord/test/cases/result_test.rb
index 68fcafb682..825aee2423 100644
--- a/activerecord/test/cases/result_test.rb
+++ b/activerecord/test/cases/result_test.rb
@@ -21,12 +21,22 @@ module ActiveRecord
assert_equal 3, result.length
end
- test "to_hash returns row_hashes" do
+ test "to_a 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" },
- ], result.to_hash
+ ], result.to_a
+ end
+
+ test "to_hash (deprecated) returns row_hashes" do
+ assert_deprecated 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" },
+ ], result.to_hash
+ end
end
test "first returns first row as a hash" do
diff --git a/activerecord/test/cases/scoping/named_scoping_test.rb b/activerecord/test/cases/scoping/named_scoping_test.rb
index 4214f347fb..f707951a16 100644
--- a/activerecord/test/cases/scoping/named_scoping_test.rb
+++ b/activerecord/test/cases/scoping/named_scoping_test.rb
@@ -489,7 +489,7 @@ class NamedScopingTest < ActiveRecord::TestCase
[:public_method, :protected_method, :private_method].each do |reserved_method|
assert Topic.respond_to?(reserved_method, true)
assert_called(ActiveRecord::Base.logger, :warn) do
- silence_warnings { Topic.scope(reserved_method, -> {}) }
+ silence_warnings { Topic.scope(reserved_method, -> { }) }
end
end
end
diff --git a/activerecord/test/cases/suppressor_test.rb b/activerecord/test/cases/suppressor_test.rb
index b68f0033d9..9be5356901 100644
--- a/activerecord/test/cases/suppressor_test.rb
+++ b/activerecord/test/cases/suppressor_test.rb
@@ -66,7 +66,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/postgresql_rake_test.rb b/activerecord/test/cases/tasks/postgresql_rake_test.rb
index 8c6e8d79f3..065ba7734c 100644
--- a/activerecord/test/cases/tasks/postgresql_rake_test.rb
+++ b/activerecord/test/cases/tasks/postgresql_rake_test.rb
@@ -496,7 +496,7 @@ if current_adapter?(:PostgreSQLAdapter)
assert_called_with(
Kernel,
:system,
- ["psql", "-v", "ON_ERROR_STOP=1", "-q", "-f", filename, @configuration["database"]],
+ ["psql", "-v", "ON_ERROR_STOP=1", "-q", "-X", "-f", filename, @configuration["database"]],
returns: true
) do
ActiveRecord::Tasks::DatabaseTasks.structure_load(@configuration, filename)
@@ -505,7 +505,7 @@ if current_adapter?(:PostgreSQLAdapter)
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"]]
+ expected_command = ["psql", "-v", "ON_ERROR_STOP=1", "-q", "-X", "-f", filename, "--noop", @configuration["database"]]
assert_called_with(Kernel, :system, expected_command, returns: true) do
with_structure_load_flags(["--noop"]) do
@@ -519,7 +519,7 @@ if current_adapter?(:PostgreSQLAdapter)
assert_called_with(
Kernel,
:system,
- ["psql", "-v", "ON_ERROR_STOP=1", "-q", "-f", filename, @configuration["database"]],
+ ["psql", "-v", "ON_ERROR_STOP=1", "-q", "-X", "-f", filename, @configuration["database"]],
returns: true
) do
ActiveRecord::Tasks::DatabaseTasks.structure_load(@configuration, filename)
diff --git a/activerecord/test/cases/touch_later_test.rb b/activerecord/test/cases/touch_later_test.rb
index 925a4609a2..cd3d5ed7d1 100644
--- a/activerecord/test/cases/touch_later_test.rb
+++ b/activerecord/test/cases/touch_later_test.rb
@@ -100,7 +100,7 @@ class TouchLaterTest < ActiveRecord::TestCase
def test_touch_later_dont_hit_the_db
invoice = Invoice.create!
- assert_queries(0) do
+ assert_no_queries do
invoice.touch_later
end
end
diff --git a/activerecord/test/cases/transaction_isolation_test.rb b/activerecord/test/cases/transaction_isolation_test.rb
index 9663955f1f..2932969412 100644
--- a/activerecord/test/cases/transaction_isolation_test.rb
+++ b/activerecord/test/cases/transaction_isolation_test.rb
@@ -90,7 +90,7 @@ else
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 @@ else
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 b13cf88c00..50740054f7 100644
--- a/activerecord/test/cases/transactions_test.rb
+++ b/activerecord/test/cases/transactions_test.rb
@@ -930,7 +930,7 @@ class TransactionTest < ActiveRecord::TestCase
klass = Class.new(ActiveRecord::Base) do
self.table_name = "transaction_without_primary_keys"
- after_commit {} # necessary to trigger the has_transactional_callbacks branch
+ after_commit { } # necessary to trigger the has_transactional_callbacks branch
end
assert_no_difference(-> { klass.count }) do
@@ -945,7 +945,7 @@ class TransactionTest < ActiveRecord::TestCase
def test_empty_transaction_is_not_materialized
assert_no_queries do
- Topic.transaction {}
+ Topic.transaction { }
end
end
@@ -968,7 +968,7 @@ class TransactionTest < ActiveRecord::TestCase
def test_savepoint_does_not_materialize_transaction
assert_no_queries do
Topic.transaction do
- Topic.transaction(requires_new: true) {}
+ Topic.transaction(requires_new: true) { }
end
end
end
@@ -991,7 +991,7 @@ class TransactionTest < ActiveRecord::TestCase
Topic.connection.raw_connection
assert_sql(/BEGIN/i, /COMMIT/i) do
- Topic.transaction {}
+ Topic.transaction { }
end
end
@@ -1001,7 +1001,7 @@ class TransactionTest < ActiveRecord::TestCase
Topic.connection_pool.checkin connection
assert_no_queries do
- connection.transaction {}
+ connection.transaction { }
end
end
diff --git a/activerecord/test/cases/type/type_map_test.rb b/activerecord/test/cases/type/type_map_test.rb
index f3699c11a2..1ce515a90c 100644
--- a/activerecord/test/cases/type/type_map_test.rb
+++ b/activerecord/test/cases/type/type_map_test.rb
@@ -32,7 +32,7 @@ module ActiveRecord
end
def test_fuzzy_lookup
- string = String.new
+ string = +""
mapping = TypeMap.new
mapping.register_type(/varchar/i, string)
@@ -41,7 +41,7 @@ module ActiveRecord
end
def test_aliasing_types
- string = String.new
+ string = +""
mapping = TypeMap.new
mapping.register_type(/string/i, string)
@@ -73,7 +73,7 @@ module ActiveRecord
end
def test_register_proc
- string = String.new
+ string = +""
binary = Binary.new
mapping = TypeMap.new
diff --git a/activerecord/test/models/author.rb b/activerecord/test/models/author.rb
index 75932c7eb6..8b5a2fa0c8 100644
--- a/activerecord/test/models/author.rb
+++ b/activerecord/test/models/author.rb
@@ -81,7 +81,7 @@ class Author < ActiveRecord::Base
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 :categorizations, -> { }
has_many :categories, through: :categorizations
has_many :named_categories, through: :categorizations
diff --git a/activerecord/test/models/reply.rb b/activerecord/test/models/reply.rb
index 0ea110f4f8..0807bcf875 100644
--- a/activerecord/test/models/reply.rb
+++ b/activerecord/test/models/reply.rb
@@ -9,6 +9,10 @@ class Reply < Topic
has_many :silly_unique_replies, dependent: :destroy, foreign_key: "parent_id"
end
+class SillyReply < Topic
+ belongs_to :reply, foreign_key: "parent_id", counter_cache: :replies_count
+end
+
class UniqueReply < Reply
belongs_to :topic, foreign_key: "parent_id", counter_cache: true
validates_uniqueness_of :content, scope: "parent_id"
@@ -54,10 +58,6 @@ class WrongReply < Reply
end
end
-class SillyReply < Reply
- 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"
diff --git a/activerecord/test/models/topic.rb b/activerecord/test/models/topic.rb
index 72699046f9..4aad6a4498 100644
--- a/activerecord/test/models/topic.rb
+++ b/activerecord/test/models/topic.rb
@@ -24,7 +24,7 @@ class Topic < ActiveRecord::Base
end
scope "approved_as_string", -> { where(approved: true) }
- scope :anonymous_extension, -> {} do
+ scope :anonymous_extension, -> { } do
def one
1
end
diff --git a/activerecord/test/schema/mysql2_specific_schema.rb b/activerecord/test/schema/mysql2_specific_schema.rb
index c384297658..499280cb0c 100644
--- a/activerecord/test/schema/mysql2_specific_schema.rb
+++ b/activerecord/test/schema/mysql2_specific_schema.rb
@@ -46,8 +46,8 @@ ActiveRecord::Schema.define do
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: "utf8mb4_bin"
+ t.string :string_ci_column, limit: 1, collation: "utf8mb4_general_ci"
t.binary :binary_column, limit: 1
end
diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb
index 27c7348f5c..2aaf393009 100644
--- a/activerecord/test/schema/schema.rb
+++ b/activerecord/test/schema/schema.rb
@@ -947,7 +947,7 @@ ActiveRecord::Schema.define do
end
[:circles, :squares, :triangles, :non_poly_ones, :non_poly_twos].each do |t|
- create_table(t, force: true) {}
+ create_table(t, force: true) { }
end
create_table :men, force: true do |t|
diff --git a/activestorage/app/models/active_storage/blob.rb b/activestorage/app/models/active_storage/blob.rb
index e7f2615b0f..53aa9f0237 100644
--- a/activestorage/app/models/active_storage/blob.rb
+++ b/activestorage/app/models/active_storage/blob.rb
@@ -35,6 +35,10 @@ class ActiveStorage::Blob < ActiveRecord::Base
scope :unattached, -> { left_joins(:attachments).where(ActiveStorage::Attachment.table_name => { blob_id: nil }) }
+ before_destroy(prepend: true) do
+ raise ActiveRecord::InvalidForeignKey if attachments.exists?
+ end
+
class << self
# You can used the signed ID of a blob to refer to it on the client side without fear of tampering.
# This is particularly helpful for direct uploads where the client-side needs to refer to the blob
diff --git a/activestorage/app/models/active_storage/filename.rb b/activestorage/app/models/active_storage/filename.rb
index bebb5e61b3..2a03e0173d 100644
--- a/activestorage/app/models/active_storage/filename.rb
+++ b/activestorage/app/models/active_storage/filename.rb
@@ -3,8 +3,6 @@
# Encapsulates a string representing a filename to provide convenient access to parts of it and sanitization.
# A Filename instance is returned by ActiveStorage::Blob#filename, and is comparable so it can be used for sorting.
class ActiveStorage::Filename
- require_dependency "active_storage/filename/parameters"
-
include Comparable
class << self
@@ -60,10 +58,6 @@ class ActiveStorage::Filename
@filename.encode(Encoding::UTF_8, invalid: :replace, undef: :replace, replace: "�").strip.tr("\u{202E}%$|:;/\t\r\n\\", "-")
end
- def parameters #:nodoc:
- Parameters.new self
- end
-
# Returns the sanitized version of the filename.
def to_s
sanitized.to_s
diff --git a/activestorage/app/models/active_storage/filename/parameters.rb b/activestorage/app/models/active_storage/filename/parameters.rb
deleted file mode 100644
index fb9ea10e49..0000000000
--- a/activestorage/app/models/active_storage/filename/parameters.rb
+++ /dev/null
@@ -1,36 +0,0 @@
-# frozen_string_literal: true
-
-class ActiveStorage::Filename::Parameters #:nodoc:
- attr_reader :filename
-
- def initialize(filename)
- @filename = filename
- end
-
- def combined
- "#{ascii}; #{utf8}"
- end
-
- TRADITIONAL_ESCAPED_CHAR = /[^ A-Za-z0-9!#$+.^_`|~-]/
-
- def ascii
- 'filename="' + percent_escape(I18n.transliterate(filename.sanitized), TRADITIONAL_ESCAPED_CHAR) + '"'
- end
-
- RFC_5987_ESCAPED_CHAR = /[^A-Za-z0-9!#$&+.^_`|~-]/
-
- def utf8
- "filename*=UTF-8''" + percent_escape(filename.sanitized, RFC_5987_ESCAPED_CHAR)
- end
-
- def to_s
- combined
- end
-
- private
- def percent_escape(string, pattern)
- string.gsub(pattern) do |char|
- char.bytes.map { |byte| "%%%02X" % byte }.join
- end
- end
-end
diff --git a/activestorage/lib/active_storage/service.rb b/activestorage/lib/active_storage/service.rb
index f915518f52..54ba08fb87 100644
--- a/activestorage/lib/active_storage/service.rb
+++ b/activestorage/lib/active_storage/service.rb
@@ -1,6 +1,8 @@
# frozen_string_literal: true
require "active_storage/log_subscriber"
+require "action_dispatch"
+require "action_dispatch/http/content_disposition"
module ActiveStorage
# Abstract class serving as an interface for concrete services.
@@ -122,7 +124,8 @@ module ActiveStorage
end
def content_disposition_with(type: "inline", filename:)
- (type.to_s.presence_in(%w( attachment inline )) || "inline") + "; #{filename.parameters}"
+ disposition = (type.to_s.presence_in(%w( attachment inline )) || "inline")
+ ActionDispatch::Http::ContentDisposition.format(disposition: disposition, filename: filename.sanitized)
end
end
end
diff --git a/activestorage/test/dummy/app/assets/config/manifest.js b/activestorage/test/dummy/app/assets/config/manifest.js
index a8adebe722..bb109908b2 100644
--- a/activestorage/test/dummy/app/assets/config/manifest.js
+++ b/activestorage/test/dummy/app/assets/config/manifest.js
@@ -1,5 +1,3 @@
//= link_tree ../images
-//= link_directory ../javascripts .js
//= link_directory ../stylesheets .css
-//= link active_storage_manifest.js
diff --git a/activestorage/test/dummy/config/environments/production.rb b/activestorage/test/dummy/config/environments/production.rb
index 34ac3bc561..be7f5b80d4 100644
--- a/activestorage/test/dummy/config/environments/production.rb
+++ b/activestorage/test/dummy/config/environments/production.rb
@@ -25,8 +25,7 @@ Rails.application.configure do
# Apache or NGINX already handles this.
config.public_file_server.enabled = ENV["RAILS_SERVE_STATIC_FILES"].present?
- # Compress JavaScripts and CSS.
- config.assets.js_compressor = :uglifier
+ # Compress CSS using a preprocessor.
# config.assets.css_compressor = :sass
# Do not fallback to assets pipeline if a precompiled asset is missed.
diff --git a/activestorage/test/dummy/config/webpacker.yml b/activestorage/test/dummy/config/webpacker.yml
new file mode 100644
index 0000000000..c1515a2e95
--- /dev/null
+++ b/activestorage/test/dummy/config/webpacker.yml
@@ -0,0 +1,72 @@
+# Note: You must restart bin/webpack-dev-server for changes to take effect
+
+default: &default
+ source_path: app/javascript
+ source_entry_path: packs
+ public_output_path: packs
+ cache_path: tmp/cache/webpacker
+ check_yarn_integrity: false
+
+ # Additional paths webpack should lookup modules
+ # ['app/assets', 'engine/foo/app/assets']
+ resolved_paths: []
+
+ # Reload manifest.json on all requests so we reload latest compiled packs
+ cache_manifest: false
+
+ extensions:
+ - .js
+ - .sass
+ - .scss
+ - .css
+ - .module.sass
+ - .module.scss
+ - .module.css
+ - .png
+ - .svg
+ - .gif
+ - .jpeg
+ - .jpg
+
+development:
+ <<: *default
+ compile: true
+
+ # Verifies that versions and hashed value of the package contents in the project's package.json
+ check_yarn_integrity: true
+
+ # Reference: https://webpack.js.org/configuration/dev-server/
+ dev_server:
+ https: false
+ host: localhost
+ port: 3035
+ public: localhost:3035
+ hmr: false
+ # Inline should be set to true if using HMR
+ inline: true
+ overlay: true
+ compress: true
+ disable_host_check: true
+ use_local_ip: false
+ quiet: false
+ headers:
+ 'Access-Control-Allow-Origin': '*'
+ watch_options:
+ ignored: /node_modules/
+
+
+test:
+ <<: *default
+ compile: true
+
+ # Compile test packs to a separate directory
+ public_output_path: packs-test
+
+production:
+ <<: *default
+
+ # Production depends on precompilation of packs prior to booting for performance.
+ compile: false
+
+ # Cache manifest.json for performance
+ cache_manifest: true
diff --git a/activestorage/test/models/blob_test.rb b/activestorage/test/models/blob_test.rb
index 88c106a08b..1a6a89de56 100644
--- a/activestorage/test/models/blob_test.rb
+++ b/activestorage/test/models/blob_test.rb
@@ -185,7 +185,7 @@ class ActiveStorage::BlobTest < ActiveSupport::TestCase
private
def expected_url_for(blob, disposition: :inline, filename: nil)
filename ||= blob.filename
- query_string = { content_type: blob.content_type, disposition: "#{disposition}; #{filename.parameters}" }.to_param
+ query_string = { content_type: blob.content_type, disposition: ActionDispatch::Http::ContentDisposition.format(disposition: disposition, filename: filename.sanitized) }.to_param
"https://example.com/rails/active_storage/disk/#{ActiveStorage.verifier.generate(blob.key, expires_in: 5.minutes, purpose: :blob_key)}/#{filename}?#{query_string}"
end
end
diff --git a/activestorage/test/models/filename/parameters_test.rb b/activestorage/test/models/filename/parameters_test.rb
deleted file mode 100644
index 431be00639..0000000000
--- a/activestorage/test/models/filename/parameters_test.rb
+++ /dev/null
@@ -1,32 +0,0 @@
-# frozen_string_literal: true
-
-require "test_helper"
-
-class ActiveStorage::Filename::ParametersTest < ActiveSupport::TestCase
- test "parameterizing a Latin filename" do
- filename = ActiveStorage::Filename.new("racecar.jpg")
-
- assert_equal %(filename="racecar.jpg"), filename.parameters.ascii
- assert_equal "filename*=UTF-8''racecar.jpg", filename.parameters.utf8
- assert_equal "#{filename.parameters.ascii}; #{filename.parameters.utf8}", filename.parameters.combined
- assert_equal filename.parameters.combined, filename.parameters.to_s
- end
-
- test "parameterizing a Latin filename with accented characters" do
- filename = ActiveStorage::Filename.new("råcëçâr.jpg")
-
- assert_equal %(filename="racecar.jpg"), filename.parameters.ascii
- assert_equal "filename*=UTF-8''r%C3%A5c%C3%AB%C3%A7%C3%A2r.jpg", filename.parameters.utf8
- assert_equal "#{filename.parameters.ascii}; #{filename.parameters.utf8}", filename.parameters.combined
- assert_equal filename.parameters.combined, filename.parameters.to_s
- end
-
- test "parameterizing a non-Latin filename" do
- filename = ActiveStorage::Filename.new("автомобиль.jpg")
-
- assert_equal %(filename="%3F%3F%3F%3F%3F%3F%3F%3F%3F%3F.jpg"), filename.parameters.ascii
- assert_equal "filename*=UTF-8''%D0%B0%D0%B2%D1%82%D0%BE%D0%BC%D0%BE%D0%B1%D0%B8%D0%BB%D1%8C.jpg", filename.parameters.utf8
- assert_equal "#{filename.parameters.ascii}; #{filename.parameters.utf8}", filename.parameters.combined
- assert_equal filename.parameters.combined, filename.parameters.to_s
- end
-end
diff --git a/activestorage/test/models/filename_test.rb b/activestorage/test/models/filename_test.rb
index 88405e41c0..715116309f 100644
--- a/activestorage/test/models/filename_test.rb
+++ b/activestorage/test/models/filename_test.rb
@@ -30,8 +30,8 @@ class ActiveStorage::FilenameTest < ActiveSupport::TestCase
end
test "sanitize transcodes to valid UTF-8" do
- { "\xF6".dup.force_encoding(Encoding::ISO8859_1) => "ö",
- "\xC3".dup.force_encoding(Encoding::ISO8859_1) => "Ã",
+ { (+"\xF6").force_encoding(Encoding::ISO8859_1) => "ö",
+ (+"\xC3").force_encoding(Encoding::ISO8859_1) => "Ã",
"\xAD" => "�",
"\xCF" => "�",
"\x00" => "",
diff --git a/activestorage/test/service/shared_service_tests.rb b/activestorage/test/service/shared_service_tests.rb
index 58f189af2b..ca2490f2bc 100644
--- a/activestorage/test/service/shared_service_tests.rb
+++ b/activestorage/test/service/shared_service_tests.rb
@@ -6,7 +6,7 @@ require "active_support/core_ext/securerandom"
module ActiveStorage::Service::SharedServiceTests
extend ActiveSupport::Concern
- FIXTURE_DATA = "\211PNG\r\n\032\n\000\000\000\rIHDR\000\000\000\020\000\000\000\020\001\003\000\000\000%=m\"\000\000\000\006PLTE\000\000\000\377\377\377\245\331\237\335\000\000\0003IDATx\234c\370\377\237\341\377_\206\377\237\031\016\2603\334?\314p\1772\303\315\315\f7\215\031\356\024\203\320\275\317\f\367\201R\314\f\017\300\350\377\177\000Q\206\027(\316]\233P\000\000\000\000IEND\256B`\202".dup.force_encoding(Encoding::BINARY)
+ FIXTURE_DATA = (+"\211PNG\r\n\032\n\000\000\000\rIHDR\000\000\000\020\000\000\000\020\001\003\000\000\000%=m\"\000\000\000\006PLTE\000\000\000\377\377\377\245\331\237\335\000\000\0003IDATx\234c\370\377\237\341\377_\206\377\237\031\016\2603\334?\314p\1772\303\315\315\f7\215\031\356\024\203\320\275\317\f\367\201R\314\f\017\300\350\377\177\000Q\206\027(\316]\233P\000\000\000\000IEND\256B`\202").force_encoding(Encoding::BINARY)
included do
setup do
@@ -77,7 +77,7 @@ module ActiveStorage::Service::SharedServiceTests
test "downloading a nonexistent file in chunks" do
assert_raises(ActiveStorage::FileNotFoundError) do
- @service.download(SecureRandom.base58(24)) {}
+ @service.download(SecureRandom.base58(24)) { }
end
end
diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md
index 37bd4da15e..586ed28693 100644
--- a/activesupport/CHANGELOG.md
+++ b/activesupport/CHANGELOG.md
@@ -1,3 +1,26 @@
+* Rename `Module#parent`, `Module#parents`, and `Module#parent_name` to
+ `module_parent`, `module_parents`, and `module_parent_name`.
+
+ *Gannon McGibbon*
+
+* Deprecate the use of `LoggerSilence` in favor of `ActiveSupport::LoggerSilence`
+
+ *Edouard Chin*
+
+* Deprecate using negative limits in `String#first` and `String#last`.
+
+ *Gannon McGibbon*, *Eric Turner*
+
+* Fix bug where `#without` for `ActiveSupport::HashWithIndifferentAccess` would fail
+ with symbol arguments
+
+ *Abraham Chan*
+
+* Treat `#delete_prefix`, `#delete_suffix` and `#unicode_normalize` results as non-`html_safe`.
+ Ensure safety of arguments for `#insert`, `#[]=` and `#replace` calls on `html_safe` Strings.
+
+ *Janosch Müller*
+
* Changed `ActiveSupport::TaggedLogging.new` to return a new logger instance instead
of mutating the one received as parameter.
diff --git a/activesupport/lib/active_support/backtrace_cleaner.rb b/activesupport/lib/active_support/backtrace_cleaner.rb
index 1796956bd7..62973eca58 100644
--- a/activesupport/lib/active_support/backtrace_cleaner.rb
+++ b/activesupport/lib/active_support/backtrace_cleaner.rb
@@ -93,7 +93,7 @@ module ActiveSupport
return if gems_paths.empty?
gems_regexp = %r{(#{gems_paths.join('|')})/(bundler/)?gems/([^/]+)-([\w.]+)/(.*)}
- gems_result = '\3 (\4) \5'.freeze
+ gems_result = '\3 (\4) \5'
add_filter { |line| line.sub(gems_regexp, gems_result) }
end
diff --git a/activesupport/lib/active_support/cache.rb b/activesupport/lib/active_support/cache.rb
index 8e516de4c9..2be79d86dd 100644
--- a/activesupport/lib/active_support/cache.rb
+++ b/activesupport/lib/active_support/cache.rb
@@ -596,9 +596,13 @@ module ActiveSupport
# Merges the default options with ones specific to a method call.
def merged_options(call_options)
if call_options
- options.merge(call_options)
+ if options.empty?
+ call_options
+ else
+ options.merge(call_options)
+ end
else
- options.dup
+ options
end
end
@@ -643,7 +647,7 @@ module ActiveSupport
if key.size > 1
key = key.collect { |element| expanded_key(element) }
else
- key = key.first
+ key = expanded_key(key.first)
end
when Hash
key = key.sort_by { |k, _| k.to_s }.collect { |k, v| "#{k}=#{v}" }
@@ -694,7 +698,7 @@ module ActiveSupport
end
def get_entry_value(entry, name, options)
- instrument(:fetch_hit, name, options) {}
+ instrument(:fetch_hit, name, options) { }
entry.value
end
diff --git a/activesupport/lib/active_support/cache/file_store.rb b/activesupport/lib/active_support/cache/file_store.rb
index 04c54c30d0..53a2b07536 100644
--- a/activesupport/lib/active_support/cache/file_store.rb
+++ b/activesupport/lib/active_support/cache/file_store.rb
@@ -26,6 +26,11 @@ module ActiveSupport
@cache_path = cache_path.to_s
end
+ # Advertise cache versioning support.
+ def self.supports_cache_versioning?
+ true
+ end
+
# Deletes all items from the cache. In this case it deletes all the entries in the specified
# file store directory except for .keep or .gitkeep. Be careful which directory is specified in your
# config file when using +FileStore+ because everything in that directory will be deleted.
diff --git a/activesupport/lib/active_support/cache/mem_cache_store.rb b/activesupport/lib/active_support/cache/mem_cache_store.rb
index 2840781dde..174c784deb 100644
--- a/activesupport/lib/active_support/cache/mem_cache_store.rb
+++ b/activesupport/lib/active_support/cache/mem_cache_store.rb
@@ -47,6 +47,11 @@ module ActiveSupport
end
end
+ # Advertise cache versioning support.
+ def self.supports_cache_versioning?
+ true
+ end
+
prepend Strategy::LocalCache
prepend LocalCacheWithRaw
diff --git a/activesupport/lib/active_support/cache/memory_store.rb b/activesupport/lib/active_support/cache/memory_store.rb
index 564ac17241..106b616529 100644
--- a/activesupport/lib/active_support/cache/memory_store.rb
+++ b/activesupport/lib/active_support/cache/memory_store.rb
@@ -30,6 +30,11 @@ module ActiveSupport
@pruning = false
end
+ # Advertise cache versioning support.
+ def self.supports_cache_versioning?
+ true
+ end
+
# Delete all data stored in a given cache store.
def clear(options = nil)
synchronize do
diff --git a/activesupport/lib/active_support/cache/null_store.rb b/activesupport/lib/active_support/cache/null_store.rb
index 1a5983db43..8452a28fd8 100644
--- a/activesupport/lib/active_support/cache/null_store.rb
+++ b/activesupport/lib/active_support/cache/null_store.rb
@@ -12,6 +12,11 @@ module ActiveSupport
class NullStore < Store
prepend Strategy::LocalCache
+ # Advertise cache versioning support.
+ def self.supports_cache_versioning?
+ true
+ end
+
def clear(options = nil)
end
diff --git a/activesupport/lib/active_support/cache/redis_cache_store.rb b/activesupport/lib/active_support/cache/redis_cache_store.rb
index 5737450b4a..9a55e49e27 100644
--- a/activesupport/lib/active_support/cache/redis_cache_store.rb
+++ b/activesupport/lib/active_support/cache/redis_cache_store.rb
@@ -66,6 +66,11 @@ module ActiveSupport
SCAN_BATCH_SIZE = 1000
private_constant :SCAN_BATCH_SIZE
+ # Advertise cache versioning support.
+ def self.supports_cache_versioning?
+ true
+ end
+
# Support raw values in the local cache strategy.
module LocalCacheWithRaw # :nodoc:
private
diff --git a/activesupport/lib/active_support/configurable.rb b/activesupport/lib/active_support/configurable.rb
index 2610114d8f..6159e45230 100644
--- a/activesupport/lib/active_support/configurable.rb
+++ b/activesupport/lib/active_support/configurable.rb
@@ -105,7 +105,7 @@ module ActiveSupport
# end
#
# User.hair_colors # => [:brown, :black, :blonde, :red]
- def config_accessor(*names)
+ def config_accessor(*names) #:doc:
options = names.extract_options!
names.each do |name|
diff --git a/activesupport/lib/active_support/core_ext/class/subclasses.rb b/activesupport/lib/active_support/core_ext/class/subclasses.rb
index 75e65337b7..56fb46a88d 100644
--- a/activesupport/lib/active_support/core_ext/class/subclasses.rb
+++ b/activesupport/lib/active_support/core_ext/class/subclasses.rb
@@ -3,7 +3,7 @@
class Class
begin
# Test if this Ruby supports each_object against singleton_class
- ObjectSpace.each_object(Numeric.singleton_class) {}
+ ObjectSpace.each_object(Numeric.singleton_class) { }
# Returns an array with all classes that are < than its receiver.
#
diff --git a/activesupport/lib/active_support/core_ext/load_error.rb b/activesupport/lib/active_support/core_ext/load_error.rb
index 6b0dcab905..b81ed0605e 100644
--- a/activesupport/lib/active_support/core_ext/load_error.rb
+++ b/activesupport/lib/active_support/core_ext/load_error.rb
@@ -4,6 +4,6 @@ class LoadError
# Returns true if the given path name (except perhaps for the ".rb"
# extension) is the missing file which caused the exception to be raised.
def is_missing?(location)
- location.sub(/\.rb$/, "".freeze) == path.to_s.sub(/\.rb$/, "".freeze)
+ location.sub(/\.rb$/, "") == path.to_s.sub(/\.rb$/, "")
end
end
diff --git a/activesupport/lib/active_support/core_ext/module/introspection.rb b/activesupport/lib/active_support/core_ext/module/introspection.rb
index c5bb598bd1..9b6df40596 100644
--- a/activesupport/lib/active_support/core_ext/module/introspection.rb
+++ b/activesupport/lib/active_support/core_ext/module/introspection.rb
@@ -5,8 +5,8 @@ require "active_support/inflector"
class Module
# Returns the name of the module containing this one.
#
- # M::N.parent_name # => "M"
- def parent_name
+ # M::N.module_parent_name # => "M"
+ def module_parent_name
if defined?(@parent_name)
@parent_name
else
@@ -16,6 +16,14 @@ class Module
end
end
+ def parent_name
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
+ `Module#parent_name` has been renamed to `module_parent_name`.
+ `parent_name` is deprecated and will be removed in Rails 6.1.
+ MSG
+ module_parent_name
+ end
+
# Returns the module which contains this one according to its name.
#
# module M
@@ -24,15 +32,23 @@ class Module
# end
# X = M::N
#
- # M::N.parent # => M
- # X.parent # => M
+ # M::N.module_parent # => M
+ # X.module_parent # => M
#
# The parent of top-level and anonymous modules is Object.
#
- # M.parent # => Object
- # Module.new.parent # => Object
+ # M.module_parent # => Object
+ # Module.new.module_parent # => Object
+ def module_parent
+ module_parent_name ? ActiveSupport::Inflector.constantize(module_parent_name) : Object
+ end
+
def parent
- parent_name ? ActiveSupport::Inflector.constantize(parent_name) : Object
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
+ `Module#parent` has been renamed to `module_parent`.
+ `parent` is deprecated and will be removed in Rails 6.1.
+ MSG
+ module_parent
end
# Returns all the parents of this module according to its name, ordered from
@@ -44,13 +60,13 @@ class Module
# end
# X = M::N
#
- # M.parents # => [Object]
- # M::N.parents # => [M, Object]
- # X.parents # => [M, Object]
- def parents
+ # M.module_parents # => [Object]
+ # M::N.module_parents # => [M, Object]
+ # X.module_parents # => [M, Object]
+ def module_parents
parents = []
- if parent_name
- parts = parent_name.split("::")
+ if module_parent_name
+ parts = module_parent_name.split("::")
until parts.empty?
parents << ActiveSupport::Inflector.constantize(parts * "::")
parts.pop
@@ -59,4 +75,12 @@ class Module
parents << Object unless parents.include? Object
parents
end
+
+ def parents
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
+ `Module#parents` has been renamed to `module_parents`.
+ `parents` is deprecated and will be removed in Rails 6.1.
+ MSG
+ module_parents
+ end
end
diff --git a/activesupport/lib/active_support/core_ext/object/try.rb b/activesupport/lib/active_support/core_ext/object/try.rb
index aa6896af32..ef8a1f476d 100644
--- a/activesupport/lib/active_support/core_ext/object/try.rb
+++ b/activesupport/lib/active_support/core_ext/object/try.rb
@@ -143,14 +143,14 @@ class NilClass
#
# With +try+
# @person.try(:children).try(:first).try(:name)
- def try(*args)
+ def try(method_name = nil, *args)
nil
end
# Calling +try!+ on +nil+ always returns +nil+.
#
# nil.try!(:name) # => nil
- def try!(*args)
+ def try!(method_name = nil, *args)
nil
end
end
diff --git a/activesupport/lib/active_support/core_ext/string/access.rb b/activesupport/lib/active_support/core_ext/string/access.rb
index 58591bbaaf..4ca24028b0 100644
--- a/activesupport/lib/active_support/core_ext/string/access.rb
+++ b/activesupport/lib/active_support/core_ext/string/access.rb
@@ -75,6 +75,10 @@ class String
# str.first(0) # => ""
# str.first(6) # => "hello"
def first(limit = 1)
+ ActiveSupport::Deprecation.warn(
+ "Calling String#first with a negative integer limit " \
+ "will raise an ArgumentError in Rails 6.1."
+ ) if limit < 0
if limit == 0
""
elsif limit >= size
@@ -95,6 +99,10 @@ class String
# str.last(0) # => ""
# str.last(6) # => "hello"
def last(limit = 1)
+ ActiveSupport::Deprecation.warn(
+ "Calling String#last with a negative integer limit " \
+ "will raise an ArgumentError in Rails 6.1."
+ ) if limit < 0
if limit == 0
""
elsif limit >= size
diff --git a/activesupport/lib/active_support/core_ext/string/output_safety.rb b/activesupport/lib/active_support/core_ext/string/output_safety.rb
index d837bb10aa..3a80de4617 100644
--- a/activesupport/lib/active_support/core_ext/string/output_safety.rb
+++ b/activesupport/lib/active_support/core_ext/string/output_safety.rb
@@ -134,8 +134,9 @@ end
module ActiveSupport #:nodoc:
class SafeBuffer < String
UNSAFE_STRING_METHODS = %w(
- capitalize chomp chop delete downcase gsub lstrip next reverse rstrip
- slice squeeze strip sub succ swapcase tr tr_s upcase
+ capitalize chomp chop delete delete_prefix delete_suffix
+ downcase gsub lstrip next reverse rstrip slice squeeze strip
+ sub succ swapcase tr tr_s unicode_normalize upcase
)
alias_method :original_concat, :concat
@@ -186,10 +187,22 @@ module ActiveSupport #:nodoc:
end
alias << concat
+ def insert(index, value)
+ super(index, html_escape_interpolated_argument(value))
+ end
+
def prepend(value)
super(html_escape_interpolated_argument(value))
end
+ def replace(value)
+ super(html_escape_interpolated_argument(value))
+ end
+
+ def []=(index, value)
+ super(index, html_escape_interpolated_argument(value))
+ end
+
def +(other)
dup.concat(other)
end
diff --git a/activesupport/lib/active_support/core_ext/string/strip.rb b/activesupport/lib/active_support/core_ext/string/strip.rb
index 6f9834bb16..60e9952ee6 100644
--- a/activesupport/lib/active_support/core_ext/string/strip.rb
+++ b/activesupport/lib/active_support/core_ext/string/strip.rb
@@ -20,7 +20,7 @@ class String
# Technically, it looks for the least indented non-empty line
# in the whole string, and removes that amount of leading whitespace.
def strip_heredoc
- gsub(/^#{scan(/^[ \t]*(?=\S)/).min}/, "".freeze).tap do |stripped|
+ gsub(/^#{scan(/^[ \t]*(?=\S)/).min}/, "").tap do |stripped|
stripped.freeze if frozen?
end
end
diff --git a/activesupport/lib/active_support/dependencies.rb b/activesupport/lib/active_support/dependencies.rb
index 238a9f0ee6..66e0bea00e 100644
--- a/activesupport/lib/active_support/dependencies.rb
+++ b/activesupport/lib/active_support/dependencies.rb
@@ -144,7 +144,7 @@ module ActiveSupport #:nodoc:
# Normalize the list of new constants, and add them to the list we will return
new_constants.each do |suffix|
- constants << ([namespace, suffix] - ["Object"]).join("::".freeze)
+ constants << ([namespace, suffix] - ["Object"]).join("::")
end
end
constants
@@ -410,7 +410,7 @@ module ActiveSupport #:nodoc:
next unless expanded_path.start_with?(expanded_root)
root_size = expanded_root.size
- next if expanded_path[root_size] != ?/.freeze
+ next if expanded_path[root_size] != ?/
nesting = expanded_path[(root_size + 1)..-1]
paths << nesting.camelize unless nesting.blank?
@@ -505,7 +505,7 @@ module ActiveSupport #:nodoc:
if file_path
expanded = File.expand_path(file_path)
- expanded.sub!(/\.rb\z/, "".freeze)
+ expanded.sub!(/\.rb\z/, "")
if loading.include?(expanded)
raise "Circular dependency detected while autoloading constant #{qualified_name}"
@@ -521,8 +521,8 @@ module ActiveSupport #:nodoc:
end
elsif mod = autoload_module!(from_mod, const_name, qualified_name, path_suffix)
return mod
- elsif (parent = from_mod.parent) && parent != from_mod &&
- ! from_mod.parents.any? { |p| p.const_defined?(const_name, false) }
+ elsif (parent = from_mod.module_parent) && parent != from_mod &&
+ ! from_mod.module_parents.any? { |p| p.const_defined?(const_name, false) }
# If our parents do not have a constant named +const_name+ then we are free
# to attempt to load upwards. If they do have such a constant, then this
# const_missing must be due to from_mod::const_name, which should not
diff --git a/activesupport/lib/active_support/deprecation/behaviors.rb b/activesupport/lib/active_support/deprecation/behaviors.rb
index 3abd25aa85..725667d139 100644
--- a/activesupport/lib/active_support/deprecation/behaviors.rb
+++ b/activesupport/lib/active_support/deprecation/behaviors.rb
@@ -43,7 +43,7 @@ module ActiveSupport
deprecation_horizon: deprecation_horizon)
},
- silence: ->(message, callstack, deprecation_horizon, gem_name) {},
+ silence: ->(message, callstack, deprecation_horizon, gem_name) { },
}
# Behavior module allows to determine how to display deprecation messages.
diff --git a/activesupport/lib/active_support/duration/iso8601_parser.rb b/activesupport/lib/active_support/duration/iso8601_parser.rb
index 414f727705..d3233e6111 100644
--- a/activesupport/lib/active_support/duration/iso8601_parser.rb
+++ b/activesupport/lib/active_support/duration/iso8601_parser.rb
@@ -13,8 +13,8 @@ module ActiveSupport
class ParsingError < ::ArgumentError; end
PERIOD_OR_COMMA = /\.|,/
- PERIOD = ".".freeze
- COMMA = ",".freeze
+ PERIOD = "."
+ COMMA = ","
SIGN_MARKER = /\A\-|\+|/
DATE_MARKER = /P/
diff --git a/activesupport/lib/active_support/duration/iso8601_serializer.rb b/activesupport/lib/active_support/duration/iso8601_serializer.rb
index 84ae29c1ec..1125454919 100644
--- a/activesupport/lib/active_support/duration/iso8601_serializer.rb
+++ b/activesupport/lib/active_support/duration/iso8601_serializer.rb
@@ -14,14 +14,14 @@ module ActiveSupport
# Builds and returns output string.
def serialize
parts, sign = normalize
- return "PT0S".freeze if parts.empty?
+ return "PT0S" if parts.empty?
- output = "P".dup
+ output = +"P"
output << "#{parts[:years]}Y" if parts.key?(:years)
output << "#{parts[:months]}M" if parts.key?(:months)
output << "#{parts[:weeks]}W" if parts.key?(:weeks)
output << "#{parts[:days]}D" if parts.key?(:days)
- time = "".dup
+ time = +""
time << "#{parts[:hours]}H" if parts.key?(:hours)
time << "#{parts[:minutes]}M" if parts.key?(:minutes)
if parts.key?(:seconds)
diff --git a/activesupport/lib/active_support/encrypted_configuration.rb b/activesupport/lib/active_support/encrypted_configuration.rb
index 3c6da10548..cc1d026737 100644
--- a/activesupport/lib/active_support/encrypted_configuration.rb
+++ b/activesupport/lib/active_support/encrypted_configuration.rb
@@ -39,7 +39,7 @@ module ActiveSupport
end
def deserialize(config)
- config.present? ? YAML.load(config, content_path) : {}
+ YAML.load(config).presence || {}
end
end
end
diff --git a/activesupport/lib/active_support/hash_with_indifferent_access.rb b/activesupport/lib/active_support/hash_with_indifferent_access.rb
index e4afc8af93..2fac579469 100644
--- a/activesupport/lib/active_support/hash_with_indifferent_access.rb
+++ b/activesupport/lib/active_support/hash_with_indifferent_access.rb
@@ -2,6 +2,7 @@
require "active_support/core_ext/hash/keys"
require "active_support/core_ext/hash/reverse_merge"
+require "active_support/core_ext/hash/except"
module ActiveSupport
# Implements a hash where keys <tt>:foo</tt> and <tt>"foo"</tt> are considered
@@ -279,6 +280,8 @@ module ActiveSupport
super(convert_key(key))
end
+ alias_method :without, :except
+
def stringify_keys!; self end
def deep_stringify_keys!; self end
def stringify_keys; dup end
diff --git a/activesupport/lib/active_support/inflector/methods.rb b/activesupport/lib/active_support/inflector/methods.rb
index 7359de762a..1af9833d46 100644
--- a/activesupport/lib/active_support/inflector/methods.rb
+++ b/activesupport/lib/active_support/inflector/methods.rb
@@ -73,7 +73,7 @@ module ActiveSupport
string = string.sub(inflections.acronyms_camelize_regex) { |match| match.downcase }
end
string.gsub!(/(?:_|(\/))([a-z\d]*)/i) { "#{$1}#{inflections.acronyms[$2] || $2.capitalize}" }
- string.gsub!("/".freeze, "::".freeze)
+ string.gsub!("/", "::")
string
end
@@ -90,11 +90,11 @@ module ActiveSupport
# camelize(underscore('SSLError')) # => "SslError"
def underscore(camel_cased_word)
return camel_cased_word unless /[A-Z-]|::/.match?(camel_cased_word)
- word = camel_cased_word.to_s.gsub("::".freeze, "/".freeze)
- word.gsub!(inflections.acronyms_underscore_regex) { "#{$1 && '_'.freeze }#{$2.downcase}" }
- word.gsub!(/([A-Z\d]+)([A-Z][a-z])/, '\1_\2'.freeze)
- word.gsub!(/([a-z\d])([A-Z])/, '\1_\2'.freeze)
- word.tr!("-".freeze, "_".freeze)
+ word = camel_cased_word.to_s.gsub("::", "/")
+ word.gsub!(inflections.acronyms_underscore_regex) { "#{$1 && '_' }#{$2.downcase}" }
+ word.gsub!(/([A-Z\d]+)([A-Z][a-z])/, '\1_\2')
+ word.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
+ word.tr!("-", "_")
word.downcase!
word
end
@@ -130,11 +130,11 @@ module ActiveSupport
inflections.humans.each { |(rule, replacement)| break if result.sub!(rule, replacement) }
- result.sub!(/\A_+/, "".freeze)
+ result.sub!(/\A_+/, "")
unless keep_id_suffix
- result.sub!(/_id\z/, "".freeze)
+ result.sub!(/_id\z/, "")
end
- result.tr!("_".freeze, " ".freeze)
+ result.tr!("_", " ")
result.gsub!(/([a-z\d]*)/i) do |match|
"#{inflections.acronyms[match.downcase] || match.downcase}"
@@ -199,14 +199,14 @@ module ActiveSupport
# classify('calculus') # => "Calculus"
def classify(table_name)
# strip out any leading schema name
- camelize(singularize(table_name.to_s.sub(/.*\./, "".freeze)))
+ camelize(singularize(table_name.to_s.sub(/.*\./, "")))
end
# Replaces underscores with dashes in the string.
#
# dasherize('puni_puni') # => "puni-puni"
def dasherize(underscored_word)
- underscored_word.tr("_".freeze, "-".freeze)
+ underscored_word.tr("_", "-")
end
# Removes the module part from the expression in the string.
@@ -269,7 +269,7 @@ module ActiveSupport
# NameError is raised when the name is not in CamelCase or the constant is
# unknown.
def constantize(camel_cased_word)
- names = camel_cased_word.split("::".freeze)
+ names = camel_cased_word.split("::")
# Trigger a built-in NameError exception including the ill-formed constant in the message.
Object.const_get(camel_cased_word) if names.empty?
@@ -364,7 +364,7 @@ module ActiveSupport
# const_regexp("Foo::Bar::Baz") # => "Foo(::Bar(::Baz)?)?"
# const_regexp("::") # => "::"
def const_regexp(camel_cased_word)
- parts = camel_cased_word.split("::".freeze)
+ parts = camel_cased_word.split("::")
return Regexp.escape(camel_cased_word) if parts.blank?
diff --git a/activesupport/lib/active_support/inflector/transliterate.rb b/activesupport/lib/active_support/inflector/transliterate.rb
index 6f2ca4999c..dbc8b8a2fa 100644
--- a/activesupport/lib/active_support/inflector/transliterate.rb
+++ b/activesupport/lib/active_support/inflector/transliterate.rb
@@ -58,7 +58,7 @@ module ActiveSupport
# I18n.locale = :de
# transliterate('Jürgen')
# # => "Juergen"
- def transliterate(string, replacement = "?".freeze)
+ def transliterate(string, replacement = "?")
raise ArgumentError, "Can only transliterate strings. Received #{string.class.name}" unless string.is_a?(String)
I18n.transliterate(
@@ -97,7 +97,7 @@ module ActiveSupport
parameterized_string.gsub!(/[^a-z0-9\-_]+/i, separator)
unless separator.nil? || separator.empty?
- if separator == "-".freeze
+ if separator == "-"
re_duplicate_separator = /-{2,}/
re_leading_trailing_separator = /^-|-$/i
else
@@ -108,7 +108,7 @@ module ActiveSupport
# No more than one of the separator in a row.
parameterized_string.gsub!(re_duplicate_separator, separator)
# Remove leading/trailing separator.
- parameterized_string.gsub!(re_leading_trailing_separator, "".freeze)
+ parameterized_string.gsub!(re_leading_trailing_separator, "")
end
parameterized_string.downcase! unless preserve_case
diff --git a/activesupport/lib/active_support/logger.rb b/activesupport/lib/active_support/logger.rb
index 8152a182b4..b8555c887b 100644
--- a/activesupport/lib/active_support/logger.rb
+++ b/activesupport/lib/active_support/logger.rb
@@ -6,7 +6,6 @@ require "logger"
module ActiveSupport
class Logger < ::Logger
- include ActiveSupport::LoggerThreadSafeLevel
include LoggerSilence
# Returns true if the logger destination matches one of the sources
@@ -81,20 +80,6 @@ module ActiveSupport
def initialize(*args)
super
@formatter = SimpleFormatter.new
- after_initialize if respond_to? :after_initialize
- end
-
- def add(severity, message = nil, progname = nil, &block)
- return true if @logdev.nil? || (severity || UNKNOWN) < level
- super
- end
-
- Logger::Severity.constants.each do |severity|
- class_eval(<<-EOT, __FILE__, __LINE__ + 1)
- def #{severity.downcase}? # def debug?
- Logger::#{severity} >= level # DEBUG >= level
- end # end
- EOT
end
# Simple formatter which only displays the message.
diff --git a/activesupport/lib/active_support/logger_silence.rb b/activesupport/lib/active_support/logger_silence.rb
index 89f32b6782..b2444c1e34 100644
--- a/activesupport/lib/active_support/logger_silence.rb
+++ b/activesupport/lib/active_support/logger_silence.rb
@@ -2,28 +2,44 @@
require "active_support/concern"
require "active_support/core_ext/module/attribute_accessors"
-require "concurrent"
+require "active_support/logger_thread_safe_level"
module LoggerSilence
extend ActiveSupport::Concern
included do
- cattr_accessor :silencer, default: true
+ ActiveSupport::Deprecation.warn(
+ "Including LoggerSilence is deprecated and will be removed in Rails 6.1. " \
+ "Please use `ActiveSupport::LoggerSilence` instead"
+ )
+
+ include ActiveSupport::LoggerSilence
end
+end
+
+module ActiveSupport
+ module LoggerSilence
+ extend ActiveSupport::Concern
+
+ included do
+ cattr_accessor :silencer, default: true
+ include ActiveSupport::LoggerThreadSafeLevel
+ end
- # Silences the logger for the duration of the block.
- def silence(temporary_level = Logger::ERROR)
- if silencer
- begin
- old_local_level = local_level
- self.local_level = temporary_level
+ # Silences the logger for the duration of the block.
+ def silence(temporary_level = Logger::ERROR)
+ if silencer
+ begin
+ old_local_level = local_level
+ self.local_level = temporary_level
+ yield self
+ ensure
+ self.local_level = old_local_level
+ end
+ else
yield self
- ensure
- self.local_level = old_local_level
end
- else
- yield self
end
end
end
diff --git a/activesupport/lib/active_support/logger_thread_safe_level.rb b/activesupport/lib/active_support/logger_thread_safe_level.rb
index ba32813d3d..f16c90cfc6 100644
--- a/activesupport/lib/active_support/logger_thread_safe_level.rb
+++ b/activesupport/lib/active_support/logger_thread_safe_level.rb
@@ -1,13 +1,30 @@
# frozen_string_literal: true
require "active_support/concern"
+require "active_support/core_ext/module/attribute_accessors"
+require "concurrent"
module ActiveSupport
module LoggerThreadSafeLevel # :nodoc:
extend ActiveSupport::Concern
+ included do
+ cattr_accessor :local_levels, default: Concurrent::Map.new(initial_capacity: 2), instance_accessor: false
+ end
+
+ Logger::Severity.constants.each do |severity|
+ class_eval(<<-EOT, __FILE__, __LINE__ + 1)
+ def #{severity.downcase}? # def debug?
+ Logger::#{severity} >= level # DEBUG >= level
+ end # end
+ EOT
+ end
+
def after_initialize
- @local_levels = Concurrent::Map.new(initial_capacity: 2)
+ ActiveSupport::Deprecation.warn(
+ "Logger don't need to call #after_initialize directly anymore. It will be deprecated without replacement in " \
+ "Rails 6.1."
+ )
end
def local_log_id
@@ -15,19 +32,24 @@ module ActiveSupport
end
def local_level
- @local_levels[local_log_id]
+ self.class.local_levels[local_log_id]
end
def local_level=(level)
if level
- @local_levels[local_log_id] = level
+ self.class.local_levels[local_log_id] = level
else
- @local_levels.delete(local_log_id)
+ self.class.local_levels.delete(local_log_id)
end
end
def level
local_level || super
end
+
+ def add(severity, message = nil, progname = nil, &block) # :nodoc:
+ return true if @logdev.nil? || (severity || UNKNOWN) < level
+ super
+ end
end
end
diff --git a/activesupport/lib/active_support/message_encryptor.rb b/activesupport/lib/active_support/message_encryptor.rb
index 404404cad1..6f7302e732 100644
--- a/activesupport/lib/active_support/message_encryptor.rb
+++ b/activesupport/lib/active_support/message_encryptor.rb
@@ -182,7 +182,7 @@ module ActiveSupport
def _decrypt(encrypted_message, purpose)
cipher = new_cipher
- encrypted_data, iv, auth_tag = encrypted_message.split("--".freeze).map { |v| ::Base64.strict_decode64(v) }
+ encrypted_data, iv, auth_tag = encrypted_message.split("--").map { |v| ::Base64.strict_decode64(v) }
# Currently the OpenSSL bindings do not raise an error if auth_tag is
# truncated, which would allow an attacker to easily forge it. See
diff --git a/activesupport/lib/active_support/message_verifier.rb b/activesupport/lib/active_support/message_verifier.rb
index 83c39c0a86..64c557bec6 100644
--- a/activesupport/lib/active_support/message_verifier.rb
+++ b/activesupport/lib/active_support/message_verifier.rb
@@ -122,7 +122,7 @@ module ActiveSupport
def valid_message?(signed_message)
return if signed_message.nil? || !signed_message.valid_encoding? || signed_message.blank?
- data, digest = signed_message.split("--".freeze)
+ data, digest = signed_message.split("--")
data.present? && digest.present? && ActiveSupport::SecurityUtils.secure_compare(digest, generate_digest(data))
end
@@ -150,7 +150,7 @@ module ActiveSupport
def verified(signed_message, purpose: nil, **)
if valid_message?(signed_message)
begin
- data = signed_message.split("--".freeze)[0]
+ data = signed_message.split("--")[0]
message = Messages::Metadata.verify(decode(data), purpose)
@serializer.load(message) if message
rescue ArgumentError => argument_error
diff --git a/activesupport/lib/active_support/number_helper/number_to_currency_converter.rb b/activesupport/lib/active_support/number_helper/number_to_currency_converter.rb
index a25e22cbd3..aef5b62aed 100644
--- a/activesupport/lib/active_support/number_helper/number_to_currency_converter.rb
+++ b/activesupport/lib/active_support/number_helper/number_to_currency_converter.rb
@@ -15,7 +15,7 @@ module ActiveSupport
end
rounded_number = NumberToRoundedConverter.convert(number, options)
- format.gsub("%n".freeze, rounded_number).gsub("%u".freeze, options[:unit])
+ format.gsub("%n", rounded_number).gsub("%u", options[:unit])
end
private
diff --git a/activesupport/lib/active_support/number_helper/number_to_delimited_converter.rb b/activesupport/lib/active_support/number_helper/number_to_delimited_converter.rb
index d5b5706705..05427fef53 100644
--- a/activesupport/lib/active_support/number_helper/number_to_delimited_converter.rb
+++ b/activesupport/lib/active_support/number_helper/number_to_delimited_converter.rb
@@ -14,7 +14,7 @@ module ActiveSupport
private
def parts
- left, right = number.to_s.split(".".freeze)
+ left, right = number.to_s.split(".")
left.gsub!(delimiter_pattern) do |digit_to_delimit|
"#{digit_to_delimit}#{options[:delimiter]}"
end
diff --git a/activesupport/lib/active_support/number_helper/number_to_human_converter.rb b/activesupport/lib/active_support/number_helper/number_to_human_converter.rb
index 03eb6671ec..908f788ee3 100644
--- a/activesupport/lib/active_support/number_helper/number_to_human_converter.rb
+++ b/activesupport/lib/active_support/number_helper/number_to_human_converter.rb
@@ -25,7 +25,7 @@ module ActiveSupport
rounded_number = NumberToRoundedConverter.convert(number, options)
unit = determine_unit(units, exponent)
- format.gsub("%n".freeze, rounded_number).gsub("%u".freeze, unit).strip
+ format.gsub("%n", rounded_number).gsub("%u", unit).strip
end
private
diff --git a/activesupport/lib/active_support/number_helper/number_to_human_size_converter.rb b/activesupport/lib/active_support/number_helper/number_to_human_size_converter.rb
index 842f2fc8df..0c72096b72 100644
--- a/activesupport/lib/active_support/number_helper/number_to_human_size_converter.rb
+++ b/activesupport/lib/active_support/number_helper/number_to_human_size_converter.rb
@@ -22,7 +22,7 @@ module ActiveSupport
human_size = number / (base**exponent)
number_to_format = NumberToRoundedConverter.convert(human_size, options)
end
- conversion_format.gsub("%n".freeze, number_to_format).gsub("%u".freeze, unit)
+ conversion_format.gsub("%n", number_to_format).gsub("%u", unit)
end
private
diff --git a/activesupport/lib/active_support/number_helper/number_to_percentage_converter.rb b/activesupport/lib/active_support/number_helper/number_to_percentage_converter.rb
index 4dcdad2e2c..6618ecffd5 100644
--- a/activesupport/lib/active_support/number_helper/number_to_percentage_converter.rb
+++ b/activesupport/lib/active_support/number_helper/number_to_percentage_converter.rb
@@ -7,7 +7,7 @@ module ActiveSupport
def convert
rounded_number = NumberToRoundedConverter.convert(number, options)
- options[:format].gsub("%n".freeze, rounded_number)
+ options[:format].gsub("%n", rounded_number)
end
end
end
diff --git a/activesupport/lib/active_support/number_helper/number_to_rounded_converter.rb b/activesupport/lib/active_support/number_helper/number_to_rounded_converter.rb
index eb528a0583..0ee5ef92dd 100644
--- a/activesupport/lib/active_support/number_helper/number_to_rounded_converter.rb
+++ b/activesupport/lib/active_support/number_helper/number_to_rounded_converter.rb
@@ -20,9 +20,9 @@ module ActiveSupport
formatted_string =
if BigDecimal === rounded_number && rounded_number.finite?
s = rounded_number.to_s("F")
- s << "0".freeze * precision
- a, b = s.split(".".freeze, 2)
- a << ".".freeze
+ s << "0" * precision
+ a, b = s.split(".", 2)
+ a << "."
a << b[0, precision]
else
"%00.#{precision}f" % rounded_number
diff --git a/activesupport/lib/active_support/subscriber.rb b/activesupport/lib/active_support/subscriber.rb
index 5a4c3d74af..9562149f8d 100644
--- a/activesupport/lib/active_support/subscriber.rb
+++ b/activesupport/lib/active_support/subscriber.rb
@@ -92,7 +92,7 @@ module ActiveSupport
event.finish!
event.payload.merge!(payload)
- method = name.split(".".freeze).first
+ method = name.split(".").first
send(method, event)
end
diff --git a/activesupport/lib/active_support/tagged_logging.rb b/activesupport/lib/active_support/tagged_logging.rb
index dd72da500c..d8a86d997e 100644
--- a/activesupport/lib/active_support/tagged_logging.rb
+++ b/activesupport/lib/active_support/tagged_logging.rb
@@ -46,7 +46,7 @@ module ActiveSupport
def current_tags
# We use our object ID here to avoid conflicting with other instances
- thread_key = @thread_key ||= "activesupport_tagged_logging_tags:#{object_id}".freeze
+ thread_key = @thread_key ||= "activesupport_tagged_logging_tags:#{object_id}"
Thread.current[thread_key] ||= []
end
diff --git a/activesupport/lib/active_support/time_with_zone.rb b/activesupport/lib/active_support/time_with_zone.rb
index fb6956f64f..3be5f6f7b5 100644
--- a/activesupport/lib/active_support/time_with_zone.rb
+++ b/activesupport/lib/active_support/time_with_zone.rb
@@ -43,8 +43,8 @@ module ActiveSupport
"Time"
end
- PRECISIONS = Hash.new { |h, n| h[n] = "%FT%T.%#{n}N".freeze }
- PRECISIONS[0] = "%FT%T".freeze
+ PRECISIONS = Hash.new { |h, n| h[n] = "%FT%T.%#{n}N" }
+ PRECISIONS[0] = "%FT%T"
include Comparable, DateAndTime::Compatibility
attr_reader :time_zone
@@ -147,7 +147,7 @@ module ActiveSupport
#
# Time.zone.now.xmlschema # => "2014-12-04T11:02:37-05:00"
def xmlschema(fraction_digits = 0)
- "#{time.strftime(PRECISIONS[fraction_digits.to_i])}#{formatted_offset(true, 'Z'.freeze)}"
+ "#{time.strftime(PRECISIONS[fraction_digits.to_i])}#{formatted_offset(true, 'Z')}"
end
alias_method :iso8601, :xmlschema
alias_method :rfc3339, :xmlschema
diff --git a/activesupport/lib/active_support/xml_mini/jdom.rb b/activesupport/lib/active_support/xml_mini/jdom.rb
index 7f94a64016..32fe6ade28 100644
--- a/activesupport/lib/active_support/xml_mini/jdom.rb
+++ b/activesupport/lib/active_support/xml_mini/jdom.rb
@@ -18,7 +18,7 @@ module ActiveSupport
module XmlMini_JDOM #:nodoc:
extend self
- CONTENT_KEY = "__content__".freeze
+ CONTENT_KEY = "__content__"
NODE_TYPE_NAMES = %w{ATTRIBUTE_NODE CDATA_SECTION_NODE COMMENT_NODE DOCUMENT_FRAGMENT_NODE
DOCUMENT_NODE DOCUMENT_TYPE_NODE ELEMENT_NODE ENTITY_NODE ENTITY_REFERENCE_NODE NOTATION_NODE
@@ -169,7 +169,7 @@ module ActiveSupport
# element::
# XML element to be checked.
def empty_content?(element)
- text = "".dup
+ text = +""
child_nodes = element.child_nodes
(0...child_nodes.length).each do |i|
item = child_nodes.item(i)
diff --git a/activesupport/lib/active_support/xml_mini/libxml.rb b/activesupport/lib/active_support/xml_mini/libxml.rb
index 0b000fea60..c2e999ef6c 100644
--- a/activesupport/lib/active_support/xml_mini/libxml.rb
+++ b/activesupport/lib/active_support/xml_mini/libxml.rb
@@ -34,7 +34,7 @@ module LibXML #:nodoc:
end
module Node #:nodoc:
- CONTENT_ROOT = "__content__".freeze
+ CONTENT_ROOT = "__content__"
# Convert XML document to hash.
#
@@ -55,7 +55,7 @@ module LibXML #:nodoc:
if c.element?
c.to_hash(node_hash)
elsif c.text? || c.cdata?
- node_hash[CONTENT_ROOT] ||= "".dup
+ node_hash[CONTENT_ROOT] ||= +""
node_hash[CONTENT_ROOT] << c.content
end
end
diff --git a/activesupport/lib/active_support/xml_mini/libxmlsax.rb b/activesupport/lib/active_support/xml_mini/libxmlsax.rb
index dcf16e6084..ac8acdfc3c 100644
--- a/activesupport/lib/active_support/xml_mini/libxmlsax.rb
+++ b/activesupport/lib/active_support/xml_mini/libxmlsax.rb
@@ -13,8 +13,8 @@ module ActiveSupport
class HashBuilder
include LibXML::XML::SaxParser::Callbacks
- CONTENT_KEY = "__content__".freeze
- HASH_SIZE_KEY = "__hash_size__".freeze
+ CONTENT_KEY = "__content__"
+ HASH_SIZE_KEY = "__hash_size__"
attr_reader :hash
@@ -23,7 +23,7 @@ module ActiveSupport
end
def on_start_document
- @hash = { CONTENT_KEY => "".dup }
+ @hash = { CONTENT_KEY => +"" }
@hash_stack = [@hash]
end
@@ -33,7 +33,7 @@ module ActiveSupport
end
def on_start_element(name, attrs = {})
- new_hash = { CONTENT_KEY => "".dup }.merge!(attrs)
+ new_hash = { CONTENT_KEY => +"" }.merge!(attrs)
new_hash[HASH_SIZE_KEY] = new_hash.size + 1
case current_hash[name]
diff --git a/activesupport/lib/active_support/xml_mini/nokogiri.rb b/activesupport/lib/active_support/xml_mini/nokogiri.rb
index 5ee6fc8159..f76513f48b 100644
--- a/activesupport/lib/active_support/xml_mini/nokogiri.rb
+++ b/activesupport/lib/active_support/xml_mini/nokogiri.rb
@@ -38,7 +38,7 @@ module ActiveSupport
end
module Node #:nodoc:
- CONTENT_ROOT = "__content__".freeze
+ CONTENT_ROOT = "__content__"
# Convert XML document to hash.
#
@@ -59,7 +59,7 @@ module ActiveSupport
if c.element?
c.to_hash(node_hash)
elsif c.text? || c.cdata?
- node_hash[CONTENT_ROOT] ||= "".dup
+ node_hash[CONTENT_ROOT] ||= +""
node_hash[CONTENT_ROOT] << c.content
end
end
diff --git a/activesupport/lib/active_support/xml_mini/nokogirisax.rb b/activesupport/lib/active_support/xml_mini/nokogirisax.rb
index b01ed00a14..55cd72e093 100644
--- a/activesupport/lib/active_support/xml_mini/nokogirisax.rb
+++ b/activesupport/lib/active_support/xml_mini/nokogirisax.rb
@@ -16,8 +16,8 @@ module ActiveSupport
# Class that will build the hash while the XML document
# is being parsed using SAX events.
class HashBuilder < Nokogiri::XML::SAX::Document
- CONTENT_KEY = "__content__".freeze
- HASH_SIZE_KEY = "__hash_size__".freeze
+ CONTENT_KEY = "__content__"
+ HASH_SIZE_KEY = "__hash_size__"
attr_reader :hash
@@ -39,7 +39,7 @@ module ActiveSupport
end
def start_element(name, attrs = [])
- new_hash = { CONTENT_KEY => "".dup }.merge!(Hash[attrs])
+ new_hash = { CONTENT_KEY => +"" }.merge!(Hash[attrs])
new_hash[HASH_SIZE_KEY] = new_hash.size + 1
case current_hash[name]
diff --git a/activesupport/lib/active_support/xml_mini/rexml.rb b/activesupport/lib/active_support/xml_mini/rexml.rb
index 32458d5b0d..8d6e3af066 100644
--- a/activesupport/lib/active_support/xml_mini/rexml.rb
+++ b/activesupport/lib/active_support/xml_mini/rexml.rb
@@ -8,7 +8,7 @@ module ActiveSupport
module XmlMini_REXML #:nodoc:
extend self
- CONTENT_KEY = "__content__".freeze
+ CONTENT_KEY = "__content__"
# Parse an XML Document string or IO into a simple hash.
#
@@ -76,7 +76,7 @@ module ActiveSupport
hash
else
# must use value to prevent double-escaping
- texts = "".dup
+ texts = +""
element.texts.each { |t| texts << t.value }
merge!(hash, CONTENT_KEY, texts)
end
diff --git a/activesupport/test/abstract_unit.rb b/activesupport/test/abstract_unit.rb
index f214898145..168d3655d3 100644
--- a/activesupport/test/abstract_unit.rb
+++ b/activesupport/test/abstract_unit.rb
@@ -29,17 +29,18 @@ I18n.enforce_available_locales = false
class ActiveSupport::TestCase
include ActiveSupport::Testing::MethodCallAssertions
- # Skips the current run on Rubinius using Minitest::Assertions#skip
- private def rubinius_skip(message = "")
- skip message if RUBY_ENGINE == "rbx"
- end
-
- # Skips the current run on JRuby using Minitest::Assertions#skip
- private def jruby_skip(message = "")
- skip message if defined?(JRUBY_VERSION)
- end
-
- def frozen_error_class
- Object.const_defined?(:FrozenError) ? FrozenError : RuntimeError
- end
+ private
+ # Skips the current run on Rubinius using Minitest::Assertions#skip
+ def rubinius_skip(message = "")
+ skip message if RUBY_ENGINE == "rbx"
+ end
+
+ # Skips the current run on JRuby using Minitest::Assertions#skip
+ def jruby_skip(message = "")
+ skip message if defined?(JRUBY_VERSION)
+ end
+
+ def frozen_error_class
+ Object.const_defined?(:FrozenError) ? FrozenError : RuntimeError
+ end
end
diff --git a/activesupport/test/benchmarkable_test.rb b/activesupport/test/benchmarkable_test.rb
index cb7a69cccf..59a71d99be 100644
--- a/activesupport/test/benchmarkable_test.rb
+++ b/activesupport/test/benchmarkable_test.rb
@@ -59,13 +59,13 @@ class BenchmarkableTest < ActiveSupport::TestCase
def test_within_level
logger.level = ActiveSupport::Logger::DEBUG
- benchmark("included_debug_run", level: :debug) {}
+ benchmark("included_debug_run", level: :debug) { }
assert_last_logged "included_debug_run"
end
def test_outside_level
logger.level = ActiveSupport::Logger::ERROR
- benchmark("skipped_debug_run", level: :debug) {}
+ benchmark("skipped_debug_run", level: :debug) { }
assert_no_match(/skipped_debug_run/, buffer.last)
ensure
logger.level = ActiveSupport::Logger::DEBUG
diff --git a/activesupport/test/broadcast_logger_test.rb b/activesupport/test/broadcast_logger_test.rb
index 181113e70a..7dfa8a62bd 100644
--- a/activesupport/test/broadcast_logger_test.rb
+++ b/activesupport/test/broadcast_logger_test.rb
@@ -114,7 +114,17 @@ module ActiveSupport
assert_equal [[::Logger::FATAL, "seen", nil]], log2.adds
end
+ test "Including top constant LoggerSilence is deprecated" do
+ assert_deprecated("Please use `ActiveSupport::LoggerSilence`") do
+ Class.new(CustomLogger) do
+ include ::LoggerSilence
+ end
+ end
+ end
+
class CustomLogger
+ include ActiveSupport::LoggerSilence
+
attr_reader :adds, :closed, :chevrons
attr_accessor :level, :progname, :formatter, :local_level
@@ -166,7 +176,6 @@ module ActiveSupport
end
class FakeLogger < CustomLogger
- include LoggerSilence
end
end
end
diff --git a/activesupport/test/cache/behaviors/cache_store_behavior.rb b/activesupport/test/cache/behaviors/cache_store_behavior.rb
index e17c09ba39..9f54b1e7de 100644
--- a/activesupport/test/cache/behaviors/cache_store_behavior.rb
+++ b/activesupport/test/cache/behaviors/cache_store_behavior.rb
@@ -290,6 +290,55 @@ module CacheStoreBehavior
assert_equal "bar", @cache.read("fu/foo")
end
+ InstanceTest = Struct.new(:name, :id) do
+ def cache_key
+ "#{name}/#{id}"
+ end
+
+ def to_param
+ "hello"
+ end
+ end
+
+ def test_array_with_single_instance_as_cache_key_uses_cache_key_method
+ test_instance_one = InstanceTest.new("test", 1)
+ test_instance_two = InstanceTest.new("test", 2)
+
+ @cache.write([test_instance_one], "one")
+ @cache.write([test_instance_two], "two")
+
+ assert_equal "one", @cache.read([test_instance_one])
+ assert_equal "two", @cache.read([test_instance_two])
+ end
+
+ def test_array_with_multiple_instances_as_cache_key_uses_cache_key_method
+ test_instance_one = InstanceTest.new("test", 1)
+ test_instance_two = InstanceTest.new("test", 2)
+ test_instance_three = InstanceTest.new("test", 3)
+
+ @cache.write([test_instance_one, test_instance_three], "one")
+ @cache.write([test_instance_two, test_instance_three], "two")
+
+ assert_equal "one", @cache.read([test_instance_one, test_instance_three])
+ assert_equal "two", @cache.read([test_instance_two, test_instance_three])
+ end
+
+ def test_format_of_expanded_key_for_single_instance
+ test_instance_one = InstanceTest.new("test", 1)
+
+ expanded_key = @cache.send(:expanded_key, test_instance_one)
+
+ assert_equal expanded_key, test_instance_one.cache_key
+ end
+
+ def test_format_of_expanded_key_for_single_instance_in_array
+ test_instance_one = InstanceTest.new("test", 1)
+
+ expanded_key = @cache.send(:expanded_key, [test_instance_one])
+
+ assert_equal expanded_key, test_instance_one.cache_key
+ end
+
def test_hash_as_cache_key
@cache.write({ foo: 1, fu: 2 }, "bar")
assert_equal "bar", @cache.read("foo=1/fu=2")
@@ -319,7 +368,7 @@ module CacheStoreBehavior
end
def test_original_store_objects_should_not_be_immutable
- bar = "bar".dup
+ bar = +"bar"
@cache.write("foo", bar)
assert_nothing_raised { bar.gsub!(/.*/, "baz") }
end
@@ -424,7 +473,7 @@ module CacheStoreBehavior
@events << ActiveSupport::Notifications::Event.new(*args)
end
assert @cache.write(key, "1", raw: true)
- assert @cache.fetch(key) {}
+ assert @cache.fetch(key) { }
assert_equal 1, @events.length
assert_equal "cache_read.active_support", @events[0].name
assert_equal :fetch, @events[0].payload[:super_operation]
@@ -438,7 +487,7 @@ module CacheStoreBehavior
ActiveSupport::Notifications.subscribe(/^cache_(.*)\.active_support$/) do |*args|
@events << ActiveSupport::Notifications::Event.new(*args)
end
- assert_not @cache.fetch("bad_key") {}
+ assert_not @cache.fetch("bad_key") { }
assert_equal 3, @events.length
assert_equal "cache_read.active_support", @events[0].name
assert_equal "cache_generate.active_support", @events[1].name
diff --git a/activesupport/test/cache/behaviors/encoded_key_cache_behavior.rb b/activesupport/test/cache/behaviors/encoded_key_cache_behavior.rb
index da16142496..842400f4a3 100644
--- a/activesupport/test/cache/behaviors/encoded_key_cache_behavior.rb
+++ b/activesupport/test/cache/behaviors/encoded_key_cache_behavior.rb
@@ -6,7 +6,7 @@
module EncodedKeyCacheBehavior
Encoding.list.each do |encoding|
define_method "test_#{encoding.name.underscore}_encoded_values" do
- key = "foo".dup.force_encoding(encoding)
+ key = (+"foo").force_encoding(encoding)
assert @cache.write(key, "1", raw: true)
assert_equal "1", @cache.read(key)
assert_equal "1", @cache.fetch(key)
@@ -18,7 +18,7 @@ module EncodedKeyCacheBehavior
end
def test_common_utf8_values
- key = "\xC3\xBCmlaut".dup.force_encoding(Encoding::UTF_8)
+ key = (+"\xC3\xBCmlaut").force_encoding(Encoding::UTF_8)
assert @cache.write(key, "1", raw: true)
assert_equal "1", @cache.read(key)
assert_equal "1", @cache.fetch(key)
@@ -29,7 +29,7 @@ module EncodedKeyCacheBehavior
end
def test_retains_encoding
- key = "\xC3\xBCmlaut".dup.force_encoding(Encoding::UTF_8)
+ key = (+"\xC3\xBCmlaut").force_encoding(Encoding::UTF_8)
assert @cache.write(key, "1", raw: true)
assert_equal Encoding::UTF_8, key.encoding
end
diff --git a/activesupport/test/cache/cache_key_test.rb b/activesupport/test/cache/cache_key_test.rb
index 84e656f504..c2240d03c2 100644
--- a/activesupport/test/cache/cache_key_test.rb
+++ b/activesupport/test/cache/cache_key_test.rb
@@ -47,7 +47,7 @@ class CacheKeyTest < ActiveSupport::TestCase
end
def test_expand_cache_key_respond_to_cache_key
- key = "foo".dup
+ key = +"foo"
def key.cache_key
:foo_key
end
@@ -55,7 +55,7 @@ class CacheKeyTest < ActiveSupport::TestCase
end
def test_expand_cache_key_array_with_something_that_responds_to_cache_key
- key = "foo".dup
+ key = +"foo"
def key.cache_key
:foo_key
end
diff --git a/activesupport/test/cache/local_cache_middleware_test.rb b/activesupport/test/cache/local_cache_middleware_test.rb
index e59fae0b4c..e46fa59784 100644
--- a/activesupport/test/cache/local_cache_middleware_test.rb
+++ b/activesupport/test/cache/local_cache_middleware_test.rb
@@ -17,7 +17,7 @@ module ActiveSupport
})
_, _, body = middleware.call({})
assert LocalCacheRegistry.cache_for(key), "should still have a cache"
- body.each {}
+ body.each { }
assert LocalCacheRegistry.cache_for(key), "should still have a cache"
body.close
assert_nil LocalCacheRegistry.cache_for(key)
diff --git a/activesupport/test/callbacks_test.rb b/activesupport/test/callbacks_test.rb
index 5c9a3b29e7..466b364e9d 100644
--- a/activesupport/test/callbacks_test.rb
+++ b/activesupport/test/callbacks_test.rb
@@ -953,7 +953,7 @@ module CallbacksTest
def test_proc_arity_2
assert_raises(ArgumentError) do
- klass = build_class(->(x, y) {})
+ klass = build_class(->(x, y) { })
klass.new.run
end
end
@@ -1032,7 +1032,7 @@ module CallbacksTest
def test_proc_arity2
assert_raises(ArgumentError) do
- object = build_class(->(a, b) {}).new
+ object = build_class(->(a, b) { }).new
object.run
end
end
diff --git a/activesupport/test/core_ext/array/extract_test.rb b/activesupport/test/core_ext/array/extract_test.rb
index 200727667c..f26e055033 100644
--- a/activesupport/test/core_ext/array/extract_test.rb
+++ b/activesupport/test/core_ext/array/extract_test.rb
@@ -35,7 +35,7 @@ class ExtractTest < ActiveSupport::TestCase
empty_array = []
array_id = empty_array.object_id
- new_empty_array = empty_array.extract! {}
+ new_empty_array = empty_array.extract! { }
assert_equal [], new_empty_array
assert_equal [], empty_array
diff --git a/activesupport/test/core_ext/date_and_time_compatibility_test.rb b/activesupport/test/core_ext/date_and_time_compatibility_test.rb
index 266829a452..58a24b60b6 100644
--- a/activesupport/test/core_ext/date_and_time_compatibility_test.rb
+++ b/activesupport/test/core_ext/date_and_time_compatibility_test.rb
@@ -248,7 +248,7 @@ class DateAndTimeCompatibilityTest < ActiveSupport::TestCase
def test_string_to_time_frozen_preserves_timezone
with_preserve_timezone(true) do
with_env_tz "US/Eastern" do
- source = "2016-04-23T15:11:12+01:00".freeze
+ source = "2016-04-23T15:11:12+01:00"
time = source.to_time
assert_instance_of Time, time
@@ -262,7 +262,7 @@ class DateAndTimeCompatibilityTest < ActiveSupport::TestCase
def test_string_to_time_frozen_does_not_preserve_time_zone
with_preserve_timezone(false) do
with_env_tz "US/Eastern" do
- source = "2016-04-23T15:11:12+01:00".freeze
+ source = "2016-04-23T15:11:12+01:00"
time = source.to_time
assert_instance_of Time, time
diff --git a/activesupport/test/core_ext/module/concerning_test.rb b/activesupport/test/core_ext/module/concerning_test.rb
index 374114c11b..38fd60463d 100644
--- a/activesupport/test/core_ext/module/concerning_test.rb
+++ b/activesupport/test/core_ext/module/concerning_test.rb
@@ -5,7 +5,7 @@ require "active_support/core_ext/module/concerning"
class ModuleConcerningTest < ActiveSupport::TestCase
def test_concerning_declares_a_concern_and_includes_it_immediately
- klass = Class.new { concerning(:Foo) {} }
+ klass = Class.new { concerning(:Foo) { } }
assert_includes klass.ancestors, klass::Foo, klass.ancestors.inspect
end
end
diff --git a/activesupport/test/core_ext/module/introspection_test.rb b/activesupport/test/core_ext/module/introspection_test.rb
index 76d3012239..d8409d5e44 100644
--- a/activesupport/test/core_ext/module/introspection_test.rb
+++ b/activesupport/test/core_ext/module/introspection_test.rb
@@ -15,25 +15,43 @@ module ParentA
end
class IntrospectionTest < ActiveSupport::TestCase
+ def test_module_parent_name
+ assert_equal "ParentA", ParentA::B.module_parent_name
+ assert_equal "ParentA::B", ParentA::B::C.module_parent_name
+ assert_nil ParentA.module_parent_name
+ end
+
+ def test_module_parent_name_when_frozen
+ assert_equal "ParentA", ParentA::FrozenB.module_parent_name
+ assert_equal "ParentA::B", ParentA::B::FrozenC.module_parent_name
+ end
+
def test_parent_name
- assert_equal "ParentA", ParentA::B.parent_name
- assert_equal "ParentA::B", ParentA::B::C.parent_name
- assert_nil ParentA.parent_name
+ assert_deprecated do
+ assert_equal "ParentA", ParentA::B.parent_name
+ end
end
- def test_parent_name_when_frozen
- assert_equal "ParentA", ParentA::FrozenB.parent_name
- assert_equal "ParentA::B", ParentA::B::FrozenC.parent_name
+ def test_module_parent
+ assert_equal ParentA::B, ParentA::B::C.module_parent
+ assert_equal ParentA, ParentA::B.module_parent
+ assert_equal Object, ParentA.module_parent
end
def test_parent
- assert_equal ParentA::B, ParentA::B::C.parent
- assert_equal ParentA, ParentA::B.parent
- assert_equal Object, ParentA.parent
+ assert_deprecated do
+ assert_equal ParentA, ParentA::B.parent
+ end
+ end
+
+ def test_module_parents
+ assert_equal [ParentA::B, ParentA, Object], ParentA::B::C.module_parents
+ assert_equal [ParentA, Object], ParentA::B.module_parents
end
def test_parents
- assert_equal [ParentA::B, ParentA, Object], ParentA::B::C.parents
- assert_equal [ParentA, Object], ParentA::B.parents
+ assert_deprecated do
+ assert_equal [ParentA, Object], ParentA::B.parents
+ end
end
end
diff --git a/activesupport/test/core_ext/object/instance_variables_test.rb b/activesupport/test/core_ext/object/instance_variables_test.rb
index a3d8daab5b..9052d209a3 100644
--- a/activesupport/test/core_ext/object/instance_variables_test.rb
+++ b/activesupport/test/core_ext/object/instance_variables_test.rb
@@ -19,15 +19,15 @@ class ObjectInstanceVariableTest < ActiveSupport::TestCase
end
def test_instance_exec_passes_arguments_to_block
- assert_equal %w(hello goodbye), "hello".dup.instance_exec("goodbye") { |v| [self, v] }
+ assert_equal %w(hello goodbye), (+"hello").instance_exec("goodbye") { |v| [self, v] }
end
def test_instance_exec_with_frozen_obj
- assert_equal %w(olleh goodbye), "hello".freeze.instance_exec("goodbye") { |v| [reverse, v] }
+ assert_equal %w(olleh goodbye), "hello".instance_exec("goodbye") { |v| [reverse, v] }
end
def test_instance_exec_nested
- assert_equal %w(goodbye olleh bar), "hello".dup.instance_exec("goodbye") { |arg|
+ assert_equal %w(goodbye olleh bar), (+"hello").instance_exec("goodbye") { |arg|
[arg] + instance_exec("bar") { |v| [reverse, v] } }
end
end
diff --git a/activesupport/test/core_ext/range_ext_test.rb b/activesupport/test/core_ext/range_ext_test.rb
index 7c7a78f461..4b8efb8a93 100644
--- a/activesupport/test/core_ext/range_ext_test.rb
+++ b/activesupport/test/core_ext/range_ext_test.rb
@@ -108,14 +108,14 @@ class RangeTest < ActiveSupport::TestCase
def test_each_on_time_with_zone
twz = ActiveSupport::TimeWithZone.new(nil, ActiveSupport::TimeZone["Eastern Time (US & Canada)"], Time.utc(2006, 11, 28, 10, 30))
assert_raises TypeError do
- ((twz - 1.hour)..twz).each {}
+ ((twz - 1.hour)..twz).each { }
end
end
def test_step_on_time_with_zone
twz = ActiveSupport::TimeWithZone.new(nil, ActiveSupport::TimeZone["Eastern Time (US & Canada)"], Time.utc(2006, 11, 28, 10, 30))
assert_raises TypeError do
- ((twz - 1.hour)..twz).step(1) {}
+ ((twz - 1.hour)..twz).step(1) { }
end
end
@@ -131,11 +131,11 @@ class RangeTest < ActiveSupport::TestCase
def test_date_time_with_each
datetime = DateTime.now
- assert(((datetime - 1.hour)..datetime).each {})
+ assert(((datetime - 1.hour)..datetime).each { })
end
def test_date_time_with_step
datetime = DateTime.now
- assert(((datetime - 1.hour)..datetime).step(1) {})
+ assert(((datetime - 1.hour)..datetime).step(1) { })
end
end
diff --git a/activesupport/test/core_ext/string_ext_test.rb b/activesupport/test/core_ext/string_ext_test.rb
index b8de16cc5e..2468fe3603 100644
--- a/activesupport/test/core_ext/string_ext_test.rb
+++ b/activesupport/test/core_ext/string_ext_test.rb
@@ -25,7 +25,7 @@ class StringInflectionsTest < ActiveSupport::TestCase
end
def test_strip_heredoc_on_a_frozen_string
- assert "".freeze.strip_heredoc.frozen?
+ assert "".strip_heredoc.frozen?
end
def test_strip_heredoc_on_a_string_with_no_lines
@@ -245,8 +245,8 @@ class StringInflectionsTest < ActiveSupport::TestCase
end
def test_string_squish
- original = %{\u205f\u3000 A string surrounded by various unicode spaces,
- with tabs(\t\t), newlines(\n\n), unicode nextlines(\u0085\u0085) and many spaces( ). \u00a0\u2007}.dup
+ original = +%{\u205f\u3000 A string surrounded by various unicode spaces,
+ with tabs(\t\t), newlines(\n\n), unicode nextlines(\u0085\u0085) and many spaces( ). \u00a0\u2007}
expected = "A string surrounded by various unicode spaces, " \
"with tabs( ), newlines( ), unicode nextlines( ) and many spaces( )."
@@ -378,8 +378,8 @@ class StringInflectionsTest < ActiveSupport::TestCase
end
def test_truncate_multibyte
- assert_equal "\354\225\204\353\246\254\353\236\221 \354\225\204\353\246\254 ...".dup.force_encoding(Encoding::UTF_8),
- "\354\225\204\353\246\254\353\236\221 \354\225\204\353\246\254 \354\225\204\353\235\274\353\246\254\354\230\244".dup.force_encoding(Encoding::UTF_8).truncate(10)
+ assert_equal (+"\354\225\204\353\246\254\353\236\221 \354\225\204\353\246\254 ...").force_encoding(Encoding::UTF_8),
+ (+"\354\225\204\353\246\254\353\236\221 \354\225\204\353\246\254 \354\225\204\353\235\274\353\246\254\354\230\244").force_encoding(Encoding::UTF_8).truncate(10)
end
def test_truncate_should_not_be_html_safe
@@ -400,7 +400,7 @@ class StringInflectionsTest < ActiveSupport::TestCase
end
def test_remove!
- original = "This is a very good day to die".dup
+ original = +"This is a very good day to die"
assert_equal "This is a good day to die", original.remove!(" very")
assert_equal "This is a good day to die", original
assert_equal "This is a good day", original.remove!(" to ", /die/)
@@ -469,6 +469,15 @@ class StringAccessTest < ActiveSupport::TestCase
assert_not_same different_string, string
end
+ test "#first with negative Integer is deprecated" do
+ string = "hello"
+ message = "Calling String#first with a negative integer limit " \
+ "will raise an ArgumentError in Rails 6.1."
+ assert_deprecated(message) do
+ string.first(-1)
+ end
+ end
+
test "#last returns the last character" do
assert_equal "o", "hello".last
assert_equal "x", "x".last
@@ -487,6 +496,15 @@ class StringAccessTest < ActiveSupport::TestCase
assert_not_same different_string, string
end
+ test "#last with negative Integer is deprecated" do
+ string = "hello"
+ message = "Calling String#last with a negative integer limit " \
+ "will raise an ArgumentError in Rails 6.1."
+ assert_deprecated(message) do
+ string.last(-1)
+ end
+ end
+
test "access returns a real string" do
hash = {}
hash["h"] = true
@@ -733,7 +751,7 @@ end
class OutputSafetyTest < ActiveSupport::TestCase
def setup
- @string = "hello".dup
+ @string = +"hello"
@object = Class.new(Object) do
def to_s
"other"
@@ -809,7 +827,7 @@ class OutputSafetyTest < ActiveSupport::TestCase
end
test "Concatting safe onto unsafe yields unsafe" do
- @other_string = "other".dup
+ @other_string = +"other"
string = @string.html_safe
@other_string.concat(string)
@@ -832,7 +850,7 @@ class OutputSafetyTest < ActiveSupport::TestCase
end
test "Concatting safe onto unsafe with << yields unsafe" do
- @other_string = "other".dup
+ @other_string = +"other"
string = @string.html_safe
@other_string << string
@@ -888,7 +906,55 @@ class OutputSafetyTest < ActiveSupport::TestCase
test "Concatting an integer to safe always yields safe" do
string = @string.html_safe
string = string.concat(13)
- assert_equal "hello".dup.concat(13), string
+ assert_equal (+"hello").concat(13), string
+ assert_predicate string, :html_safe?
+ end
+
+ test "Inserting safe into safe yields safe" do
+ string = "foo".html_safe
+ string.insert(0, "<b>".html_safe)
+
+ assert_equal "<b>foo", string
+ assert_predicate string, :html_safe?
+ end
+
+ test "Inserting unsafe into safe yields escaped safe" do
+ string = "foo".html_safe
+ string.insert(0, "<b>")
+
+ assert_equal "&lt;b&gt;foo", string
+ assert_predicate string, :html_safe?
+ end
+
+ test "Replacing safe with safe yields safe" do
+ string = "foo".html_safe
+ string.replace("<b>".html_safe)
+
+ assert_equal "<b>", string
+ assert_predicate string, :html_safe?
+ end
+
+ test "Replacing safe with unsafe yields escaped safe" do
+ string = "foo".html_safe
+ string.replace("<b>")
+
+ assert_equal "&lt;b&gt;", string
+ assert_predicate string, :html_safe?
+ end
+
+ test "Replacing index of safe with safe yields safe" do
+ string = "foo".html_safe
+ string[0] = "<b>".html_safe
+
+ assert_equal "<b>oo", string
+ assert_predicate string, :html_safe?
+ end
+
+ test "Replacing index of safe with unsafe yields escaped safe" do
+ string = "foo".html_safe
+ string[0] = "<b>"
+
+ assert_equal "&lt;b&gt;oo", string
assert_predicate string, :html_safe?
end
diff --git a/activesupport/test/core_ext/time_ext_test.rb b/activesupport/test/core_ext/time_ext_test.rb
index e1cb22fda8..7078f3506d 100644
--- a/activesupport/test/core_ext/time_ext_test.rb
+++ b/activesupport/test/core_ext/time_ext_test.rb
@@ -950,39 +950,39 @@ class TimeExtCalculationsTest < ActiveSupport::TestCase
end
class TimeExtMarshalingTest < ActiveSupport::TestCase
- def test_marshaling_with_utc_instance
+ def test_marshalling_with_utc_instance
t = Time.utc(2000)
- unmarshaled = Marshal.load(Marshal.dump(t))
- assert_equal "UTC", unmarshaled.zone
- assert_equal t, unmarshaled
+ unmarshalled = Marshal.load(Marshal.dump(t))
+ assert_equal "UTC", unmarshalled.zone
+ assert_equal t, unmarshalled
end
- def test_marshaling_with_local_instance
+ def test_marshalling_with_local_instance
t = Time.local(2000)
- unmarshaled = Marshal.load(Marshal.dump(t))
- assert_equal t.zone, unmarshaled.zone
- assert_equal t, unmarshaled
+ unmarshalled = Marshal.load(Marshal.dump(t))
+ assert_equal t.zone, unmarshalled.zone
+ assert_equal t, unmarshalled
end
- def test_marshaling_with_frozen_utc_instance
+ def test_marshalling_with_frozen_utc_instance
t = Time.utc(2000).freeze
- unmarshaled = Marshal.load(Marshal.dump(t))
- assert_equal "UTC", unmarshaled.zone
- assert_equal t, unmarshaled
+ unmarshalled = Marshal.load(Marshal.dump(t))
+ assert_equal "UTC", unmarshalled.zone
+ assert_equal t, unmarshalled
end
- def test_marshaling_with_frozen_local_instance
+ def test_marshalling_with_frozen_local_instance
t = Time.local(2000).freeze
- unmarshaled = Marshal.load(Marshal.dump(t))
- assert_equal t.zone, unmarshaled.zone
- assert_equal t, unmarshaled
+ unmarshalled = Marshal.load(Marshal.dump(t))
+ assert_equal t.zone, unmarshalled.zone
+ assert_equal t, unmarshalled
end
def test_marshalling_preserves_fractional_seconds
t = Time.parse("00:00:00.500")
- unmarshaled = Marshal.load(Marshal.dump(t))
- assert_equal t.to_f, unmarshaled.to_f
- assert_equal t, unmarshaled
+ unmarshalled = Marshal.load(Marshal.dump(t))
+ assert_equal t.to_f, unmarshalled.to_f
+ assert_equal t, unmarshalled
end
def test_last_quarter_on_31st
diff --git a/activesupport/test/core_ext/time_with_zone_test.rb b/activesupport/test/core_ext/time_with_zone_test.rb
index e650209268..f6e836e446 100644
--- a/activesupport/test/core_ext/time_with_zone_test.rb
+++ b/activesupport/test/core_ext/time_with_zone_test.rb
@@ -1105,7 +1105,7 @@ class TimeWithZoneMethodsForTimeAndDateTimeTest < ActiveSupport::TestCase
def test_use_zone_raises_on_invalid_timezone
Time.zone = "Alaska"
assert_raise ArgumentError do
- Time.use_zone("No such timezone exists") {}
+ Time.use_zone("No such timezone exists") { }
end
assert_equal ActiveSupport::TimeZone["Alaska"], Time.zone
end
diff --git a/activesupport/test/dependencies_test.rb b/activesupport/test/dependencies_test.rb
index a486ef5eac..e144971e9f 100644
--- a/activesupport/test/dependencies_test.rb
+++ b/activesupport/test/dependencies_test.rb
@@ -816,7 +816,7 @@ class DependenciesTest < ActiveSupport::TestCase
end
def test_new_contants_in_without_constants
- assert_equal [], (ActiveSupport::Dependencies.new_constants_in(Object) {})
+ assert_equal [], (ActiveSupport::Dependencies.new_constants_in(Object) { })
assert ActiveSupport::Dependencies.constant_watch_stack.all? { |k, v| v.empty? }
end
@@ -892,7 +892,7 @@ class DependenciesTest < ActiveSupport::TestCase
def test_new_constants_in_with_illegal_module_name_raises_correct_error
assert_raise(NameError) do
- ActiveSupport::Dependencies.new_constants_in("Illegal-Name") {}
+ ActiveSupport::Dependencies.new_constants_in("Illegal-Name") { }
end
end
diff --git a/activesupport/test/encrypted_configuration_test.rb b/activesupport/test/encrypted_configuration_test.rb
index 93ccf457de..387d6e1c1f 100644
--- a/activesupport/test/encrypted_configuration_test.rb
+++ b/activesupport/test/encrypted_configuration_test.rb
@@ -42,6 +42,12 @@ class EncryptedConfigurationTest < ActiveSupport::TestCase
assert @credentials.something[:good]
end
+ test "reading comment-only configuration" do
+ @credentials.write("# comment")
+
+ assert_equal @credentials.config, {}
+ end
+
test "change configuration by key file" do
@credentials.write({ something: { good: true } }.to_yaml)
@credentials.change do |config_file|
diff --git a/activesupport/test/evented_file_update_checker_test.rb b/activesupport/test/evented_file_update_checker_test.rb
index d3af0dbef3..a557608986 100644
--- a/activesupport/test/evented_file_update_checker_test.rb
+++ b/activesupport/test/evented_file_update_checker_test.rb
@@ -38,7 +38,7 @@ class EventedFileUpdateCheckerTest < ActiveSupport::TestCase
FileUtils.touch(tmpfiles)
- checker = new_checker(tmpfiles) {}
+ checker = new_checker(tmpfiles) { }
assert_not_predicate checker, :updated?
# Pipes used for flow control across fork.
diff --git a/activesupport/test/executor_test.rb b/activesupport/test/executor_test.rb
index af441064dd..3026f002c3 100644
--- a/activesupport/test/executor_test.rb
+++ b/activesupport/test/executor_test.rb
@@ -23,7 +23,7 @@ class ExecutorTest < ActiveSupport::TestCase
executor.to_run { @foo = true }
executor.to_complete { result = @foo }
- executor.wrap {}
+ executor.wrap { }
assert result
end
@@ -85,7 +85,7 @@ class ExecutorTest < ActiveSupport::TestCase
executor.register_hook(hook)
- executor.wrap {}
+ executor.wrap { }
assert_equal :some_state, supplied_state
end
@@ -105,7 +105,7 @@ class ExecutorTest < ActiveSupport::TestCase
executor.register_hook(hook)
- executor.wrap {}
+ executor.wrap { }
assert_nil supplied_state
end
@@ -129,7 +129,7 @@ class ExecutorTest < ActiveSupport::TestCase
executor.register_hook(hook)
assert_raises(DummyError) do
- executor.wrap {}
+ executor.wrap { }
end
assert_equal :none, supplied_state
@@ -154,7 +154,7 @@ class ExecutorTest < ActiveSupport::TestCase
end
assert_raises(DummyError) do
- executor.wrap {}
+ executor.wrap { }
end
assert_equal :some_state, supplied_state
@@ -187,7 +187,7 @@ class ExecutorTest < ActiveSupport::TestCase
executor.register_hook(hook_class.new(:c), outer: true)
executor.register_hook(hook_class.new(:d))
- executor.wrap {}
+ executor.wrap { }
assert_equal [:run_c, :run_a, :run_b, :run_d, :complete_a, :complete_b, :complete_d, :complete_c], invoked
assert_equal [:state_a, :state_b, :state_d, :state_c], supplied_state
@@ -209,9 +209,9 @@ class ExecutorTest < ActiveSupport::TestCase
executor.register_hook(hook)
before = RubyVM.stat(:class_serial)
- executor.wrap {}
- executor.wrap {}
- executor.wrap {}
+ executor.wrap { }
+ executor.wrap { }
+ executor.wrap { }
after = RubyVM.stat(:class_serial)
assert_equal before, after
diff --git a/activesupport/test/hash_with_indifferent_access_test.rb b/activesupport/test/hash_with_indifferent_access_test.rb
index eebff18ef1..af67ed21c8 100644
--- a/activesupport/test/hash_with_indifferent_access_test.rb
+++ b/activesupport/test/hash_with_indifferent_access_test.rb
@@ -672,6 +672,17 @@ class HashWithIndifferentAccessTest < ActiveSupport::TestCase
assert_equal "bender", slice["login"]
end
+ def test_indifferent_without
+ original = { a: "x", b: "y", c: 10 }.with_indifferent_access
+ expected = { c: 10 }.with_indifferent_access
+
+ [["a", "b"], [:a, :b]].each do |keys|
+ # Should return a new hash without the given keys.
+ assert_equal expected, original.without(*keys), keys.inspect
+ assert_not_equal expected, original
+ end
+ end
+
def test_indifferent_extract
original = { :a => 1, "b" => 2, :c => 3, "d" => 4 }.with_indifferent_access
expected = { a: 1, b: 2 }.with_indifferent_access
diff --git a/activesupport/test/logger_test.rb b/activesupport/test/logger_test.rb
index 773b0daa1d..160e1156b6 100644
--- a/activesupport/test/logger_test.rb
+++ b/activesupport/test/logger_test.rb
@@ -40,7 +40,7 @@ class LoggerTest < ActiveSupport::TestCase
logger = Logger.new f
logger.level = Logger::DEBUG
- str = "\x80".dup
+ str = +"\x80"
str.force_encoding("ASCII-8BIT")
logger.add Logger::DEBUG, str
@@ -58,7 +58,7 @@ class LoggerTest < ActiveSupport::TestCase
logger = Logger.new f
logger.level = Logger::DEBUG
- str = "\x80".dup
+ str = +"\x80"
str.force_encoding("ASCII-8BIT")
logger.add Logger::DEBUG, str
diff --git a/activesupport/test/multibyte_chars_test.rb b/activesupport/test/multibyte_chars_test.rb
index 061446c782..11c4822748 100644
--- a/activesupport/test/multibyte_chars_test.rb
+++ b/activesupport/test/multibyte_chars_test.rb
@@ -53,7 +53,7 @@ class MultibyteCharsTest < ActiveSupport::TestCase
end
def test_forwarded_method_with_non_string_result_should_be_returned_verbatim
- str = "".dup
+ str = +""
str.singleton_class.class_eval { def __method_for_multibyte_testing_with_integer_result; 1; end }
@chars.wrapped_string.singleton_class.class_eval { def __method_for_multibyte_testing_with_integer_result; 1; end }
@@ -61,14 +61,14 @@ class MultibyteCharsTest < ActiveSupport::TestCase
end
def test_should_concatenate
- mb_a = "a".dup.mb_chars
- mb_b = "b".dup.mb_chars
+ mb_a = (+"a").mb_chars
+ mb_b = (+"b").mb_chars
assert_equal "ab", mb_a + "b"
assert_equal "ab", "a" + mb_b
assert_equal "ab", mb_a + mb_b
assert_equal "ab", mb_a << "b"
- assert_equal "ab", "a".dup << mb_b
+ assert_equal "ab", (+"a") << mb_b
assert_equal "abb", mb_a << mb_b
end
@@ -80,7 +80,7 @@ class MultibyteCharsTest < ActiveSupport::TestCase
def test_concatenation_should_return_a_proxy_class_instance
assert_equal ActiveSupport::Multibyte.proxy_class, ("a".mb_chars + "b").class
- assert_equal ActiveSupport::Multibyte.proxy_class, ("a".dup.mb_chars << "b").class
+ assert_equal ActiveSupport::Multibyte.proxy_class, ((+"a").mb_chars << "b").class
end
def test_ascii_strings_are_treated_at_utf8_strings
@@ -90,8 +90,8 @@ class MultibyteCharsTest < ActiveSupport::TestCase
def test_concatenate_should_return_proxy_instance
assert(("a".mb_chars + "b").kind_of?(@proxy_class))
assert(("a".mb_chars + "b".mb_chars).kind_of?(@proxy_class))
- assert(("a".dup.mb_chars << "b").kind_of?(@proxy_class))
- assert(("a".dup.mb_chars << "b".mb_chars).kind_of?(@proxy_class))
+ assert(((+"a").mb_chars << "b").kind_of?(@proxy_class))
+ assert(((+"a").mb_chars << "b".mb_chars).kind_of?(@proxy_class))
end
def test_should_return_string_as_json
@@ -135,7 +135,7 @@ class MultibyteCharsUTF8BehaviourTest < ActiveSupport::TestCase
end
def test_tidy_bytes_bang_should_change_wrapped_string
- original = " Un bUen café \x92".dup
+ original = +" Un bUen café \x92"
proxy = chars(original.dup)
proxy.tidy_bytes!
assert_not_equal original, proxy.to_s
@@ -152,7 +152,7 @@ class MultibyteCharsUTF8BehaviourTest < ActiveSupport::TestCase
end
def test_string_methods_are_chainable
- assert chars("".dup).insert(0, "").kind_of?(ActiveSupport::Multibyte.proxy_class)
+ assert chars(+"").insert(0, "").kind_of?(ActiveSupport::Multibyte.proxy_class)
assert chars("").rjust(1).kind_of?(ActiveSupport::Multibyte.proxy_class)
assert chars("").ljust(1).kind_of?(ActiveSupport::Multibyte.proxy_class)
assert chars("").center(1).kind_of?(ActiveSupport::Multibyte.proxy_class)
@@ -197,7 +197,7 @@ class MultibyteCharsUTF8BehaviourTest < ActiveSupport::TestCase
end
def test_should_use_character_offsets_for_insert_offsets
- assert_equal "", "".dup.mb_chars.insert(0, "")
+ assert_equal "", (+"").mb_chars.insert(0, "")
assert_equal "こわにちわ", @chars.insert(1, "わ")
assert_equal "こわわわにちわ", @chars.insert(2, "わわ")
assert_equal "わこわわわにちわ", @chars.insert(0, "わ")
@@ -420,13 +420,13 @@ class MultibyteCharsUTF8BehaviourTest < ActiveSupport::TestCase
end
def test_slice_bang_removes_the_slice_from_the_receiver
- chars = "úüù".dup.mb_chars
+ chars = (+"úüù").mb_chars
chars.slice!(0, 2)
assert_equal "ù", chars
end
def test_slice_bang_returns_nil_and_does_not_modify_receiver_if_out_of_bounds
- string = "úüù".dup
+ string = +"úüù"
chars = string.mb_chars
assert_nil chars.slice!(4, 5)
assert_equal "úüù", chars
diff --git a/activesupport/test/multibyte_test_helpers.rb b/activesupport/test/multibyte_test_helpers.rb
index 98f36aa939..7565655f25 100644
--- a/activesupport/test/multibyte_test_helpers.rb
+++ b/activesupport/test/multibyte_test_helpers.rb
@@ -27,9 +27,9 @@ module MultibyteTestHelpers
CACHE_DIR = "#{Dir.tmpdir}/cache/unicode_conformance/#{ActiveSupport::Multibyte::Unicode::UNICODE_VERSION}"
FileUtils.mkdir_p(CACHE_DIR)
- UNICODE_STRING = "こにちわ".freeze
- ASCII_STRING = "ohayo".freeze
- BYTE_STRING = "\270\236\010\210\245".dup.force_encoding("ASCII-8BIT").freeze
+ UNICODE_STRING = "こにちわ"
+ ASCII_STRING = "ohayo"
+ BYTE_STRING = (+"\270\236\010\210\245").force_encoding("ASCII-8BIT").freeze
def chars(str)
ActiveSupport::Multibyte::Chars.new(str)
diff --git a/activesupport/test/notifications_test.rb b/activesupport/test/notifications_test.rb
index 62817a839a..54fd4345fb 100644
--- a/activesupport/test/notifications_test.rb
+++ b/activesupport/test/notifications_test.rb
@@ -90,7 +90,7 @@ module Notifications
ActiveSupport::Notifications.subscribe("foo", TestSubscriber.new)
ActiveSupport::Notifications.instrument("foo") do
- ActiveSupport::Notifications.subscribe("foo") {}
+ ActiveSupport::Notifications.subscribe("foo") { }
end
ensure
ActiveSupport::Notifications.notifier = old_notifier
diff --git a/activesupport/test/reloader_test.rb b/activesupport/test/reloader_test.rb
index 976917c1a1..1b7cc253d9 100644
--- a/activesupport/test/reloader_test.rb
+++ b/activesupport/test/reloader_test.rb
@@ -35,13 +35,13 @@ class ReloaderTest < ActiveSupport::TestCase
r = new_reloader { true }
invoked = false
r.to_run { invoked = true }
- r.wrap {}
+ r.wrap { }
assert invoked
r = new_reloader { false }
invoked = false
r.to_run { invoked = true }
- r.wrap {}
+ r.wrap { }
assert_not invoked
end
@@ -53,7 +53,7 @@ class ReloaderTest < ActiveSupport::TestCase
reloader.executor.to_run { called << :executor_run }
reloader.executor.to_complete { called << :executor_complete }
- reloader.wrap {}
+ reloader.wrap { }
assert_equal [:executor_run, :reloader_run, :prepare, :reloader_complete, :executor_complete], called
called = []
@@ -63,7 +63,7 @@ class ReloaderTest < ActiveSupport::TestCase
reloader.check = lambda { false }
called = []
- reloader.wrap {}
+ reloader.wrap { }
assert_equal [:executor_run, :executor_complete], called
called = []
diff --git a/activesupport/test/safe_buffer_test.rb b/activesupport/test/safe_buffer_test.rb
index 70dec6b3d2..49a3951623 100644
--- a/activesupport/test/safe_buffer_test.rb
+++ b/activesupport/test/safe_buffer_test.rb
@@ -75,16 +75,41 @@ class SafeBufferTest < ActiveSupport::TestCase
assert_equal "my_test", str
end
- test "Should not return safe buffer from gsub" do
- altered_buffer = @buffer.gsub("", "asdf")
- assert_equal "asdf", altered_buffer
- assert_not_predicate altered_buffer, :html_safe?
- end
+ {
+ capitalize: nil,
+ chomp: nil,
+ chop: nil,
+ delete: "foo",
+ delete_prefix: "foo",
+ delete_suffix: "foo",
+ downcase: nil,
+ gsub: ["foo", "bar"],
+ lstrip: nil,
+ next: nil,
+ reverse: nil,
+ rstrip: nil,
+ slice: "foo",
+ squeeze: nil,
+ strip: nil,
+ sub: ["foo", "bar"],
+ succ: nil,
+ swapcase: nil,
+ tr: ["foo", "bar"],
+ tr_s: ["foo", "bar"],
+ unicode_normalize: nil,
+ upcase: nil,
+ }.each do |unsafe_method, dummy_args|
+ test "Should not return safe buffer from #{unsafe_method}" do
+ skip unless String.method_defined?(unsafe_method)
+ altered_buffer = @buffer.send(unsafe_method, *dummy_args)
+ assert_not_predicate altered_buffer, :html_safe?
+ end
- test "Should not return safe buffer from gsub!" do
- @buffer.gsub!("", "asdf")
- assert_equal "asdf", @buffer
- assert_not_predicate @buffer, :html_safe?
+ test "Should not return safe buffer from #{unsafe_method}!" do
+ skip unless String.method_defined?("#{unsafe_method}!")
+ @buffer.send("#{unsafe_method}!", *dummy_args)
+ assert_not_predicate @buffer, :html_safe?
+ end
end
test "Should escape dirty buffers on add" do
diff --git a/activesupport/test/share_lock_test.rb b/activesupport/test/share_lock_test.rb
index 42fd5eefc1..34479020e1 100644
--- a/activesupport/test/share_lock_test.rb
+++ b/activesupport/test/share_lock_test.rb
@@ -11,29 +11,29 @@ class ShareLockTest < ActiveSupport::TestCase
def test_reentrancy
thread = Thread.new do
- @lock.sharing { @lock.sharing {} }
- @lock.exclusive { @lock.exclusive {} }
+ @lock.sharing { @lock.sharing { } }
+ @lock.exclusive { @lock.exclusive { } }
end
assert_threads_not_stuck thread
end
def test_sharing_doesnt_block
with_thread_waiting_in_lock_section(:sharing) do |sharing_thread_latch|
- assert_threads_not_stuck(Thread.new { @lock.sharing {} })
+ assert_threads_not_stuck(Thread.new { @lock.sharing { } })
end
end
def test_sharing_blocks_exclusive
with_thread_waiting_in_lock_section(:sharing) do |sharing_thread_release_latch|
@lock.exclusive(no_wait: true) { flunk } # polling should fail
- exclusive_thread = Thread.new { @lock.exclusive {} }
+ exclusive_thread = Thread.new { @lock.exclusive { } }
assert_threads_stuck_but_releasable_by_latch exclusive_thread, sharing_thread_release_latch
end
end
def test_exclusive_blocks_sharing
with_thread_waiting_in_lock_section(:exclusive) do |exclusive_thread_release_latch|
- sharing_thread = Thread.new { @lock.sharing {} }
+ sharing_thread = Thread.new { @lock.sharing { } }
assert_threads_stuck_but_releasable_by_latch sharing_thread, exclusive_thread_release_latch
end
end
@@ -42,7 +42,7 @@ class ShareLockTest < ActiveSupport::TestCase
with_thread_waiting_in_lock_section(:sharing) do |sharing_thread_release_latch|
exclusive_threads = (1..2).map do
Thread.new do
- @lock.exclusive {}
+ @lock.exclusive { }
end
end
@@ -53,7 +53,7 @@ class ShareLockTest < ActiveSupport::TestCase
def test_sharing_is_upgradeable_to_exclusive
upgrading_thread = Thread.new do
@lock.sharing do
- @lock.exclusive {}
+ @lock.exclusive { }
end
end
assert_threads_not_stuck upgrading_thread
@@ -66,7 +66,7 @@ class ShareLockTest < ActiveSupport::TestCase
upgrading_thread = Thread.new do
@lock.sharing do
in_sharing.count_down
- @lock.exclusive {}
+ @lock.exclusive { }
end
end
@@ -81,7 +81,7 @@ class ShareLockTest < ActiveSupport::TestCase
exclusive_threads = (1..2).map do
Thread.new do
@lock.send(use_upgrading ? :sharing : :tap) do
- @lock.exclusive(purpose: :load, compatible: [:load, :unload]) {}
+ @lock.exclusive(purpose: :load, compatible: [:load, :unload]) { }
end
end
end
@@ -95,7 +95,7 @@ class ShareLockTest < ActiveSupport::TestCase
with_thread_waiting_in_lock_section(:sharing) do |sharing_thread_release_latch|
thread = Thread.new do
@lock.sharing do
- @lock.exclusive {}
+ @lock.exclusive { }
end
end
@@ -105,7 +105,7 @@ class ShareLockTest < ActiveSupport::TestCase
sharing_thread_release_latch.count_down
thread = Thread.new do
- @lock.exclusive {}
+ @lock.exclusive { }
end
assert_threads_not_stuck thread
@@ -121,13 +121,13 @@ class ShareLockTest < ActiveSupport::TestCase
Thread.new do
@lock.send(use_upgrading ? :sharing : :tap) do
together.wait
- @lock.exclusive(purpose: :red, compatible: [:green, :purple]) {}
+ @lock.exclusive(purpose: :red, compatible: [:green, :purple]) { }
end
end,
Thread.new do
@lock.send(use_upgrading ? :sharing : :tap) do
together.wait
- @lock.exclusive(purpose: :blue, compatible: [:green]) {}
+ @lock.exclusive(purpose: :blue, compatible: [:green]) { }
end
end
]
@@ -138,7 +138,7 @@ class ShareLockTest < ActiveSupport::TestCase
# a sharing block. While it's blocked, it holds no lock, so it
# doesn't interfere with any other attempts.
no_purpose_thread = Thread.new do
- @lock.exclusive {}
+ @lock.exclusive { }
end
assert_threads_stuck no_purpose_thread
@@ -147,7 +147,7 @@ class ShareLockTest < ActiveSupport::TestCase
# lock, but as soon as that's released, it can run --
# regardless of whether those threads hold share locks.
compatible_thread = Thread.new do
- @lock.exclusive(purpose: :green, compatible: []) {}
+ @lock.exclusive(purpose: :green, compatible: []) { }
end
assert_threads_stuck compatible_thread
@@ -231,7 +231,7 @@ class ShareLockTest < ActiveSupport::TestCase
assert_threads_stuck waiting_exclusive
late_share_attempt = Thread.new do
- @lock.sharing {}
+ @lock.sharing { }
end
assert_threads_stuck late_share_attempt
@@ -252,14 +252,14 @@ class ShareLockTest < ActiveSupport::TestCase
@lock.sharing do
ready.wait
attempt_reentrancy.wait
- @lock.sharing {}
+ @lock.sharing { }
end
end
exclusive = Thread.new do
@lock.sharing do
ready.wait
- @lock.exclusive {}
+ @lock.exclusive { }
end
end
@@ -280,7 +280,7 @@ class ShareLockTest < ActiveSupport::TestCase
Thread.new do
@lock.sharing do
ready.wait
- @lock.exclusive(purpose: :x, compatible: [:x], after_compatible: [:x]) {}
+ @lock.exclusive(purpose: :x, compatible: [:x], after_compatible: [:x]) { }
done.wait
end
end
@@ -297,7 +297,7 @@ class ShareLockTest < ActiveSupport::TestCase
Thread.new do
@lock.sharing do
ready.wait
- @lock.exclusive(purpose: :x) {}
+ @lock.exclusive(purpose: :x) { }
done.wait
end
end,
@@ -323,7 +323,7 @@ class ShareLockTest < ActiveSupport::TestCase
Thread.new do
@lock.sharing do
ready.wait
- @lock.exclusive(purpose: :x) {}
+ @lock.exclusive(purpose: :x) { }
done.wait
end
end,
@@ -352,7 +352,7 @@ class ShareLockTest < ActiveSupport::TestCase
Thread.new do
@lock.sharing do
ready.wait
- @lock.exclusive(purpose: :x) {}
+ @lock.exclusive(purpose: :x) { }
done.wait
end
end,
@@ -386,7 +386,7 @@ class ShareLockTest < ActiveSupport::TestCase
incompatible_thread = Thread.new do
@lock.sharing do
ready.wait
- @lock.exclusive(purpose: :x) {}
+ @lock.exclusive(purpose: :x) { }
end
end
@@ -418,7 +418,7 @@ class ShareLockTest < ActiveSupport::TestCase
incompatible_thread = Thread.new do
ready.wait
- @lock.exclusive(purpose: :z) {}
+ @lock.exclusive(purpose: :z) { }
end
recursive_yield_shares_thread = Thread.new do
@@ -427,7 +427,7 @@ class ShareLockTest < ActiveSupport::TestCase
@lock.yield_shares(compatible: [:y]) do
do_nesting.wait
@lock.sharing do
- @lock.yield_shares(compatible: [:x, :y]) {}
+ @lock.yield_shares(compatible: [:x, :y]) { }
end
after_nesting.wait
end
@@ -439,12 +439,12 @@ class ShareLockTest < ActiveSupport::TestCase
assert_threads_stuck incompatible_thread
compatible_thread = Thread.new do
- @lock.exclusive(purpose: :y) {}
+ @lock.exclusive(purpose: :y) { }
end
assert_threads_not_stuck compatible_thread
post_nesting_incompatible_thread = Thread.new do
- @lock.exclusive(purpose: :x) {}
+ @lock.exclusive(purpose: :x) { }
end
assert_threads_stuck post_nesting_incompatible_thread
diff --git a/activesupport/test/silence_logger_test.rb b/activesupport/test/silence_logger_test.rb
new file mode 100644
index 0000000000..bd0c6b7f86
--- /dev/null
+++ b/activesupport/test/silence_logger_test.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+require "abstract_unit"
+require "active_support/logger_silence"
+require "logger"
+
+class LoggerSilenceTest < ActiveSupport::TestCase
+ class MyLogger < ::Logger
+ include ActiveSupport::LoggerSilence
+ end
+
+ setup do
+ @io = StringIO.new
+ @logger = MyLogger.new(@io)
+ end
+
+ test "#silence silences the log" do
+ @logger.silence(Logger::ERROR) do
+ @logger.info("Foo")
+ end
+ @io.rewind
+
+ assert_empty @io.read
+ end
+
+ test "#debug? is true when setting the temporary level to Logger::DEBUG" do
+ @logger.level = Logger::INFO
+
+ @logger.silence(Logger::DEBUG) do
+ assert_predicate @logger, :debug?
+ end
+
+ assert_predicate @logger, :info?
+ end
+end
diff --git a/activesupport/test/xml_mini/rexml_engine_test.rb b/activesupport/test/xml_mini/rexml_engine_test.rb
index 34bf81fa75..b711619ba7 100644
--- a/activesupport/test/xml_mini/rexml_engine_test.rb
+++ b/activesupport/test/xml_mini/rexml_engine_test.rb
@@ -12,7 +12,7 @@ class REXMLEngineTest < XMLMiniEngineTest
end
def test_parse_from_frozen_string
- xml_string = "<root></root>".freeze
+ xml_string = "<root></root>"
assert_equal({ "root" => {} }, ActiveSupport::XmlMini.parse(xml_string))
end
diff --git a/activesupport/test/xml_mini/xml_mini_engine_test.rb b/activesupport/test/xml_mini/xml_mini_engine_test.rb
index 5c4c28d9b7..c62e7e32c9 100644
--- a/activesupport/test/xml_mini/xml_mini_engine_test.rb
+++ b/activesupport/test/xml_mini/xml_mini_engine_test.rb
@@ -78,7 +78,7 @@ class XMLMiniEngineTest < ActiveSupport::TestCase
end
def test_parse_from_frozen_string
- xml_string = "<root/>".freeze
+ xml_string = "<root/>"
assert_equal({ "root" => {} }, ActiveSupport::XmlMini.parse(xml_string))
end
diff --git a/ci/qunit-selenium-runner.rb b/ci/qunit-selenium-runner.rb
index 05bcab8cdb..132b3d17eb 100644
--- a/ci/qunit-selenium-runner.rb
+++ b/ci/qunit-selenium-runner.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "qunit/selenium/test_runner"
-require "chromedriver/helper"
+require "chromedriver-helper"
driver_options = Selenium::WebDriver::Chrome::Options.new
driver_options.add_argument("--headless")
diff --git a/ci/travis.rb b/ci/travis.rb
index 861063afa5..1de281046f 100755
--- a/ci/travis.rb
+++ b/ci/travis.rb
@@ -9,10 +9,10 @@ commands = [
'mysql -e "grant all privileges on activerecord_unittest.* to rails@localhost;"',
'mysql -e "grant all privileges on activerecord_unittest2.* to rails@localhost;"',
'mysql -e "grant all privileges on inexistent_activerecord_unittest.* to rails@localhost;"',
- 'mysql -e "create database activerecord_unittest;"',
- 'mysql -e "create database activerecord_unittest2;"',
- 'psql -c "create database activerecord_unittest;" -U postgres',
- 'psql -c "create database activerecord_unittest2;" -U postgres'
+ 'mysql -e "create database activerecord_unittest default character set utf8mb4;"',
+ 'mysql -e "create database activerecord_unittest2 default character set utf8mb4;"',
+ 'psql -c "create database -E UTF8 -T template0 activerecord_unittest;" -U postgres',
+ 'psql -c "create database -E UTF8 -T template0 activerecord_unittest2;" -U postgres'
]
commands.each do |command|
diff --git a/guides/rails_guides/markdown.rb b/guides/rails_guides/markdown.rb
index 61b371363e..a98aa8fe66 100644
--- a/guides/rails_guides/markdown.rb
+++ b/guides/rails_guides/markdown.rb
@@ -103,7 +103,7 @@ module RailsGuides
hierarchy = hierarchy[0, 3] + [node]
end
- node[:id] = dom_id(hierarchy)
+ node[:id] = dom_id(hierarchy) unless node[:id]
node.inner_html = "#{node_index(hierarchy)} #{node.inner_html}"
end
end
diff --git a/guides/rails_guides/markdown/renderer.rb b/guides/rails_guides/markdown/renderer.rb
index 8095b8c898..f186ac526f 100644
--- a/guides/rails_guides/markdown/renderer.rb
+++ b/guides/rails_guides/markdown/renderer.rb
@@ -29,7 +29,12 @@ HTML
# Always increase the heading level by 1, so we can use h1, h2 heading in the document
header_level += 1
- %(<h#{header_level}>#{text}</h#{header_level}>)
+ header_with_id = text.scan(/(.*){#(.*)}/)
+ unless header_with_id.empty?
+ %(<h#{header_level} id="#{header_with_id[0][1].strip}">#{header_with_id[0][0].strip}</h#{header_level}>)
+ else
+ %(<h#{header_level}>#{text}</h#{header_level}>)
+ end
end
def paragraph(text)
diff --git a/guides/source/3_0_release_notes.md b/guides/source/3_0_release_notes.md
index e793146c2c..e936644daf 100644
--- a/guides/source/3_0_release_notes.md
+++ b/guides/source/3_0_release_notes.md
@@ -38,7 +38,7 @@ If you're upgrading an existing application, it's a great idea to have good test
Rails 3.0 requires Ruby 1.8.7 or higher. Support for all of the previous Ruby versions has been dropped officially and you should upgrade as early as possible. Rails 3.0 is also compatible with Ruby 1.9.2.
-TIP: Note that Ruby 1.8.7 p248 and p249 have marshaling bugs that crash Rails 3.0. Ruby Enterprise Edition have these fixed since release 1.8.7-2010.02 though. On the 1.9 front, Ruby 1.9.1 is not usable because it outright segfaults on Rails 3.0, so if you want to use Rails 3 with 1.9.x jump on 1.9.2 for smooth sailing.
+TIP: Note that Ruby 1.8.7 p248 and p249 have marshalling bugs that crash Rails 3.0. Ruby Enterprise Edition have these fixed since release 1.8.7-2010.02 though. On the 1.9 front, Ruby 1.9.1 is not usable because it outright segfaults on Rails 3.0, so if you want to use Rails 3 with 1.9.x jump on 1.9.2 for smooth sailing.
### Rails Application object
diff --git a/guides/source/3_1_release_notes.md b/guides/source/3_1_release_notes.md
index 8c3dc3454d..d6981656ee 100644
--- a/guides/source/3_1_release_notes.md
+++ b/guides/source/3_1_release_notes.md
@@ -26,7 +26,7 @@ If you're upgrading an existing application, it's a great idea to have good test
Rails 3.1 requires Ruby 1.8.7 or higher. Support for all of the previous Ruby versions has been dropped officially and you should upgrade as early as possible. Rails 3.1 is also compatible with Ruby 1.9.2.
-TIP: Note that Ruby 1.8.7 p248 and p249 have marshaling bugs that crash Rails. Ruby Enterprise Edition have these fixed since release 1.8.7-2010.02 though. On the 1.9 front, Ruby 1.9.1 is not usable because it outright segfaults, so if you want to use 1.9.x jump on 1.9.2 for smooth sailing.
+TIP: Note that Ruby 1.8.7 p248 and p249 have marshalling bugs that crash Rails. Ruby Enterprise Edition have these fixed since release 1.8.7-2010.02 though. On the 1.9 front, Ruby 1.9.1 is not usable because it outright segfaults, so if you want to use 1.9.x jump on 1.9.2 for smooth sailing.
### What to update in your apps
diff --git a/guides/source/4_2_release_notes.md b/guides/source/4_2_release_notes.md
index f7c40d19e9..51d06bd07d 100644
--- a/guides/source/4_2_release_notes.md
+++ b/guides/source/4_2_release_notes.md
@@ -44,7 +44,7 @@ to their respective adapters. Active Job comes pre-configured with an inline
runner that executes jobs right away.
Jobs often need to take Active Record objects as arguments. Active Job passes
-object references as URIs (uniform resource identifiers) instead of marshaling
+object references as URIs (uniform resource identifiers) instead of marshalling
the object itself. The new [Global ID](https://github.com/rails/globalid)
library builds URIs and looks up the objects they reference. Passing Active
Record objects as job arguments just works by using Global ID internally.
diff --git a/guides/source/active_record_callbacks.md b/guides/source/active_record_callbacks.md
index 5946acb412..ebdee446f9 100644
--- a/guides/source/active_record_callbacks.md
+++ b/guides/source/active_record_callbacks.md
@@ -435,7 +435,9 @@ class PictureFile < ApplicationRecord
end
```
-WARNING. The `after_commit` and `after_rollback` callbacks are called for all models created, updated, or destroyed within a transaction block. However, if an exception is raised within one of these callbacks, the exception will bubble up and any remaining `after_commit` or `after_rollback` methods will _not_ be executed. As such, if your callback code could raise an exception, you'll need to rescue it and handle it within the callback in order to allow other callbacks to run.
+WARNING. When a transaction completes, the `after_commit` or `after_rollback` callbacks are called for all models created, updated, or destroyed within that transaction. However, if an exception is raised within one of these callbacks, the exception will bubble up and any remaining `after_commit` or `after_rollback` methods will _not_ be executed. As such, if your callback code could raise an exception, you'll need to rescue it and handle it within the callback in order to allow other callbacks to run.
+
+WARNING. The code executed within `after_commit` or `after_rollback` callbacks is itself not enclosed within a transaction.
WARNING. Using both `after_create_commit` and `after_update_commit` in the same model will only allow the last callback defined to take effect, and will override all others.
diff --git a/guides/source/active_record_querying.md b/guides/source/active_record_querying.md
index 91cc175095..02055e59f0 100644
--- a/guides/source/active_record_querying.md
+++ b/guides/source/active_record_querying.md
@@ -1261,7 +1261,7 @@ articles, all the articles would still be loaded. By using `joins` (an INNER
JOIN), the join conditions **must** match, otherwise no records will be
returned.
-NOTE: If an association is eager loaded as part of a join, any fields from a custom select clause will not present be on the loaded models.
+NOTE: If an association is eager loaded as part of a join, any fields from a custom select clause will not be present on the loaded models.
This is because it is ambiguous whether they should appear on the parent record, or the child.
Scopes
diff --git a/guides/source/active_record_validations.md b/guides/source/active_record_validations.md
index 3f13ef8d10..c98f24d786 100644
--- a/guides/source/active_record_validations.md
+++ b/guides/source/active_record_validations.md
@@ -844,9 +844,9 @@ class Person < ApplicationRecord
end
```
-You can also use `on:` to define custom context.
-Custom contexts need to be triggered explicitly
-by passing name of the context to `valid?`, `invalid?` or `save`.
+You can also use `on:` to define custom contexts. Custom contexts need to be
+triggered explicitly by passing the name of the context to `valid?`,
+`invalid?`, or `save`.
```ruby
class Person < ApplicationRecord
@@ -854,14 +854,32 @@ class Person < ApplicationRecord
validates :age, numericality: true, on: :account_setup
end
-person = Person.new
+person = Person.new(age: 'thirty-three')
+person.valid? # => true
+person.valid?(:account_setup) # => false
+person.errors.messages
+ # => {:email=>["has already been taken"], :age=>["is not a number"]}
```
-`person.valid?(:account_setup)` executes both the validations
-without saving the model. And `person.save(context: :account_setup)`
-validates `person` in `account_setup` context before saving.
-On explicit triggers, model is validated by
-validations of only that context and validations without context.
+`person.valid?(:account_setup)` executes both the validations without saving
+the model. `person.save(context: :account_setup)` validates `person` in the
+`account_setup` context before saving.
+
+When triggered by an explicit context, validations are run for that context,
+as well as any validations _without_ a context.
+
+```ruby
+class Person < ApplicationRecord
+ validates :email, uniqueness: true, on: :account_setup
+ validates :age, numericality: true, on: :account_setup
+ validates :name, presence: true
+end
+
+person = Person.new
+person.valid?(:account_setup) # => false
+person.errors.messages
+ # => {:email=>["has already been taken"], :age=>["is not a number"], :name=>["can't be blank"]}
+```
Strict Validations
------------------
diff --git a/guides/source/active_storage_overview.md b/guides/source/active_storage_overview.md
index 1c15d075b9..d5387219f5 100644
--- a/guides/source/active_storage_overview.md
+++ b/guides/source/active_storage_overview.md
@@ -58,6 +58,8 @@ amazon:
service: S3
access_key_id: ""
secret_access_key: ""
+ bucket: ""
+ region: "" # e.g. 'us-east-1'
```
Tell Active Storage which service to use by setting
@@ -80,6 +82,14 @@ To use the Amazon S3 service in production, you add the following to
config.active_storage.service = :amazon
```
+To use the test service when testing, you add the following to
+`config/environments/test.rb`:
+
+```ruby
+# Store uploaded files on the local file system in a temporary directory.
+config.active_storage.service = :test
+```
+
Continue reading for more information on the built-in service adapters (e.g.
`Disk` and `S3`) and the configuration they require.
diff --git a/guides/source/active_support_core_extensions.md b/guides/source/active_support_core_extensions.md
index f9fc7044ba..6b0554bb5f 100644
--- a/guides/source/active_support_core_extensions.md
+++ b/guides/source/active_support_core_extensions.md
@@ -590,9 +590,9 @@ NOTE: Defined in `active_support/core_ext/module/attribute_accessors.rb`.
### Parents
-#### `parent`
+#### `module_parent`
-The `parent` method on a nested named module returns the module that contains its corresponding constant:
+The `module_parent` method on a nested named module returns the module that contains its corresponding constant:
```ruby
module X
@@ -603,19 +603,19 @@ module X
end
M = X::Y::Z
-X::Y::Z.parent # => X::Y
-M.parent # => X::Y
+X::Y::Z.module_parent # => X::Y
+M.module_parent # => X::Y
```
-If the module is anonymous or belongs to the top-level, `parent` returns `Object`.
+If the module is anonymous or belongs to the top-level, `module_parent` returns `Object`.
-WARNING: Note that in that case `parent_name` returns `nil`.
+WARNING: Note that in that case `module_parent_name` returns `nil`.
NOTE: Defined in `active_support/core_ext/module/introspection.rb`.
-#### `parent_name`
+#### `module_parent_name`
-The `parent_name` method on a nested named module returns the fully qualified name of the module that contains its corresponding constant:
+The `module_parent_name` method on a nested named module returns the fully qualified name of the module that contains its corresponding constant:
```ruby
module X
@@ -626,19 +626,19 @@ module X
end
M = X::Y::Z
-X::Y::Z.parent_name # => "X::Y"
-M.parent_name # => "X::Y"
+X::Y::Z.module_parent_name # => "X::Y"
+M.module_parent_name # => "X::Y"
```
-For top-level or anonymous modules `parent_name` returns `nil`.
+For top-level or anonymous modules `module_parent_name` returns `nil`.
-WARNING: Note that in that case `parent` returns `Object`.
+WARNING: Note that in that case `module_parent` returns `Object`.
NOTE: Defined in `active_support/core_ext/module/introspection.rb`.
-#### `parents`
+#### `module_parents`
-The method `parents` calls `parent` on the receiver and upwards until `Object` is reached. The chain is returned in an array, from bottom to top:
+The method `module_parents` calls `module_parent` on the receiver and upwards until `Object` is reached. The chain is returned in an array, from bottom to top:
```ruby
module X
@@ -649,8 +649,8 @@ module X
end
M = X::Y::Z
-X::Y::Z.parents # => [X::Y, X, Object]
-M.parents # => [X::Y, X, Object]
+X::Y::Z.module_parents # => [X::Y, X, Object]
+M.module_parents # => [X::Y, X, Object]
```
NOTE: Defined in `active_support/core_ext/module/introspection.rb`.
diff --git a/guides/source/active_support_instrumentation.md b/guides/source/active_support_instrumentation.md
index 69c6a6e414..9963125fa2 100644
--- a/guides/source/active_support_instrumentation.md
+++ b/guides/source/active_support_instrumentation.md
@@ -467,6 +467,7 @@ Active Job
| `:job` | Job object |
| `:adapter` | QueueAdapter object processing the job |
| `:error` | The error that caused the retry |
+| `:wait` | The delay of the retry |
### perform_start.active_job
diff --git a/guides/source/asset_pipeline.md b/guides/source/asset_pipeline.md
index bf046a3341..66cf9da33b 100644
--- a/guides/source/asset_pipeline.md
+++ b/guides/source/asset_pipeline.md
@@ -184,9 +184,8 @@ the file `scaffolds.css` (or `scaffolds.scss` if `sass-rails` is in the
`Gemfile`.)
For example, if you generate a `ProjectsController`, Rails will also add a new
-file at `app/assets/javascripts/projects.coffee` and another at
-`app/assets/stylesheets/projects.scss`. By default these files will be ready
-to use by your application immediately using the `require_tree` directive. See
+file at `app/assets/stylesheets/projects.scss`. By default these files will be
+ready to use by your application immediately using the `require_tree` directive. See
[Manifest Files and Directives](#manifest-files-and-directives) for more details
on require_tree.
diff --git a/guides/source/association_basics.md b/guides/source/association_basics.md
index 008c7345e9..a2231c55d7 100644
--- a/guides/source/association_basics.md
+++ b/guides/source/association_basics.md
@@ -1075,13 +1075,13 @@ end
You can use the `includes` method to specify second-order associations that should be eager-loaded when this association is used. For example, consider these models:
```ruby
-class LineItem < ApplicationRecord
+class Chapter < ApplicationRecord
belongs_to :book
end
class Book < ApplicationRecord
belongs_to :author
- has_many :line_items
+ has_many :chapters
end
class Author < ApplicationRecord
@@ -1089,16 +1089,16 @@ class Author < ApplicationRecord
end
```
-If you frequently retrieve authors directly from line items (`@line_item.book.author`), then you can make your code somewhat more efficient by including authors in the association from line items to books:
+If you frequently retrieve authors directly from chapters (`@chapter.book.author`), then you can make your code somewhat more efficient by including authors in the association from chapters to books:
```ruby
-class LineItem < ApplicationRecord
+class Chapter < ApplicationRecord
belongs_to :book, -> { includes :author }
end
class Book < ApplicationRecord
belongs_to :author
- has_many :line_items
+ has_many :chapters
end
class Author < ApplicationRecord
@@ -1779,8 +1779,8 @@ The `group` method supplies an attribute name to group the result set by, using
```ruby
class Author < ApplicationRecord
- has_many :line_items, -> { group 'books.id' },
- through: :books
+ has_many :chapters, -> { group 'books.id' },
+ through: :books
end
```
@@ -1795,27 +1795,27 @@ end
class Book < ApplicationRecord
belongs_to :author
- has_many :line_items
+ has_many :chapters
end
-class LineItem < ApplicationRecord
+class Chapter < ApplicationRecord
belongs_to :book
end
```
-If you frequently retrieve line items directly from authors (`@author.books.line_items`), then you can make your code somewhat more efficient by including line items in the association from authors to books:
+If you frequently retrieve chapters directly from authors (`@author.books.chapters`), then you can make your code somewhat more efficient by including chapters in the association from authors to books:
```ruby
class Author < ApplicationRecord
- has_many :books, -> { includes :line_items }
+ has_many :books, -> { includes :chapters }
end
class Book < ApplicationRecord
belongs_to :author
- has_many :line_items
+ has_many :chapters
end
-class LineItem < ApplicationRecord
+class Chapter < ApplicationRecord
belongs_to :book
end
```
diff --git a/guides/source/caching_with_rails.md b/guides/source/caching_with_rails.md
index 8aaa71c557..321eee637f 100644
--- a/guides/source/caching_with_rails.md
+++ b/guides/source/caching_with_rails.md
@@ -362,7 +362,7 @@ This class provides the foundation for interacting with the cache in Rails. This
The main methods to call are `read`, `write`, `delete`, `exist?`, and `fetch`. The fetch method takes a block and will either return an existing value from the cache, or evaluate the block and write the result to the cache if no value exists.
-There are some common options used by all cache implementations. These can be passed to the constructor or the various methods to interact with entries.
+There are some common options that can be used by all cache implementations. These can be passed to the constructor or the various methods to interact with entries.
* `:namespace` - This option can be used to create a namespace within the cache store. It is especially useful if your application shares a cache with other applications.
@@ -370,7 +370,7 @@ There are some common options used by all cache implementations. These can be pa
* `:compress_threshold` - Defaults to 1kB. Cache entries larger than this threshold, specified in bytes, are compressed.
-* `:expires_in` - This option sets an expiration time in seconds for the cache entry when it will be automatically removed from the cache.
+* `:expires_in` - This option sets an expiration time in seconds for the cache entry, if the cache store supports it, when it will be automatically removed from the cache.
* `:race_condition_ttl` - This option is used in conjunction with the `:expires_in` option. It will prevent race conditions when cache entries expire by preventing multiple processes from simultaneously regenerating the same entry (also known as the dog pile effect). This option sets the number of seconds that an expired entry can be reused while a new value is being regenerated. It's a good practice to set this value if you use the `:expires_in` option.
diff --git a/guides/source/command_line.md b/guides/source/command_line.md
index 7fa0a49203..5fd3ad17de 100644
--- a/guides/source/command_line.md
+++ b/guides/source/command_line.md
@@ -185,7 +185,7 @@ The controller generator is expecting parameters in the form of `generate contro
```bash
$ rails generate controller Greetings hello
create app/controllers/greetings_controller.rb
- route get "greetings/hello"
+ route get 'greetings/hello'
invoke erb
create app/views/greetings
create app/views/greetings/hello.html.erb
@@ -193,9 +193,8 @@ $ rails generate controller Greetings hello
create test/controllers/greetings_controller_test.rb
invoke helper
create app/helpers/greetings_helper.rb
+ invoke test_unit
invoke assets
- invoke coffee
- create app/assets/javascripts/greetings.coffee
invoke scss
create app/assets/stylesheets/greetings.scss
```
diff --git a/guides/source/configuring.md b/guides/source/configuring.md
index e372b3a816..812565b207 100644
--- a/guides/source/configuring.md
+++ b/guides/source/configuring.md
@@ -119,12 +119,11 @@ defaults to `:debug` for all environments. The available log levels are: `:debug
* `config.logger` is the logger that will be used for `Rails.logger` and any related Rails logging such as `ActiveRecord::Base.logger`. It defaults to an instance of `ActiveSupport::TaggedLogging` that wraps an instance of `ActiveSupport::Logger` which outputs a log to the `log/` directory. You can supply a custom logger, to get full compatibility you must follow these guidelines:
* To support a formatter, you must manually assign a formatter from the `config.log_formatter` value to the logger.
* To support tagged logs, the log instance must be wrapped with `ActiveSupport::TaggedLogging`.
- * To support silencing, the logger must include `LoggerSilence` and `ActiveSupport::LoggerThreadSafeLevel` modules. The `ActiveSupport::Logger` class already includes these modules.
+ * To support silencing, the logger must include `ActiveSupport::LoggerSilence` module. The `ActiveSupport::Logger` class already includes these modules.
```ruby
class MyLogger < ::Logger
- include ActiveSupport::LoggerThreadSafeLevel
- include LoggerSilence
+ include ActiveSupport::LoggerSilence
end
mylogger = MyLogger.new(STDOUT)
@@ -1014,7 +1013,6 @@ If you choose to use MySQL or MariaDB instead of the shipped SQLite3 database, y
```yaml
development:
adapter: mysql2
- encoding: utf8
database: blog_development
pool: 5
username: root
@@ -1024,6 +1022,8 @@ development:
If your development database has a root user with an empty password, this configuration should work for you. Otherwise, change the username and password in the `development` section as appropriate.
+NOTE: If your MySQL version is 5.5 or 5.6 and want to use the `utf8mb4` character set by default, please configure your MySQL server to support the longer key prefix by enabling `innodb_large_prefix` system variable.
+
Advisory Locks are enabled by default on MySQL and are used to make database migrations concurrent safe. You can disable advisory locks by setting `advisory_locks` to `false`:
```yaml
diff --git a/guides/source/contributing_to_ruby_on_rails.md b/guides/source/contributing_to_ruby_on_rails.md
index b5e40aa40f..1beadd78d6 100644
--- a/guides/source/contributing_to_ruby_on_rails.md
+++ b/guides/source/contributing_to_ruby_on_rails.md
@@ -135,7 +135,7 @@ learn about Ruby on Rails, and the API, which serves as a reference.
You can help improve the Rails guides by making them more coherent, consistent, or readable, adding missing information, correcting factual errors, fixing typos, or bringing them up to date with the latest edge Rails.
To do so, make changes to Rails guides source files (located [here](https://github.com/rails/rails/tree/master/guides/source) on GitHub). Then open a pull request to apply your
-changes to master branch.
+changes to the master branch.
When working with documentation, please take into account the [API Documentation Guidelines](api_documentation_guidelines.html) and the [Ruby on Rails Guides Guidelines](ruby_on_rails_guides_guidelines.html).
@@ -336,6 +336,26 @@ $ bundle exec ruby -w -Itest test/mail_layout_test.rb -n test_explicit_class_lay
The `-n` option allows you to run a single method instead of the whole
file.
+#### Running tests with a specific seed
+
+Test execution is randomized with a randomization seed. If you are experiencing random
+test failures you can more accurately reproduce a failing test scenario by specifically
+setting the randomization seed.
+
+Running all tests for a component:
+
+```bash
+$ cd actionmailer
+$ SEED=15002 bundle exec rake test
+```
+
+Running a single test file:
+
+```bash
+$ cd actionmailer
+$ SEED=15002 bundle exec ruby -w -Itest test/mail_layout_test.rb
+```
+
#### Testing Active Record
First, create the databases you'll need. You can find a list of the required
@@ -488,18 +508,10 @@ Navigate to the Rails [GitHub repository](https://github.com/rails/rails) and pr
Add the new remote to your local repository on your local machine:
```bash
-$ git remote add mine https://github.com/<your user name>/rails.git
-```
-
-Push to your remote:
-
-```bash
-$ git push mine my_new_branch
+$ git remote add fork https://github.com/<your user name>/rails.git
```
-You might have cloned your forked repository into your machine and might want to add the original Rails repository as a remote instead, if that's the case here's what you have to do.
-
-In the directory you cloned your fork:
+You may have cloned your local repository from rails/rails or you may have cloned from your forked repository. To avoid ambiguity the following git commands assume that you have made a "rails" remote that points to rails/rails.
```bash
$ git remote add rails https://github.com/rails/rails.git
@@ -516,23 +528,17 @@ Merge the new content:
```bash
$ git checkout master
$ git rebase rails/master
+$ git checkout my_new_branch
+$ git rebase rails/master
```
Update your fork:
```bash
-$ git push origin master
-```
-
-If you want to update another branch:
-
-```bash
-$ git checkout branch_name
-$ git rebase rails/branch_name
-$ git push origin branch_name
+$ git push fork master
+$ git push fork my_new_branch
```
-
### Issue a Pull Request
Navigate to the Rails repository you just pushed to (e.g.
@@ -582,29 +588,15 @@ branches, squashing makes it easier to revert bad commits, and the git history
can be a bit easier to follow. Rails is a large project, and a bunch of
extraneous commits can add a lot of noise.
-In order to do this, you'll need to have a git remote that points at the main
-Rails repository. This is useful anyway, but just in case you don't have it set
-up, make sure that you do this first:
-
-```bash
-$ git remote add upstream https://github.com/rails/rails.git
-```
-
-You can call this remote whatever you'd like, but if you don't use `upstream`,
-then change the name to your own in the instructions below.
-
-Given that your remote branch is called `my_pull_request`, then you can do the
-following:
-
```bash
-$ git fetch upstream
-$ git checkout my_pull_request
-$ git rebase -i upstream/master
+$ git fetch rails
+$ git checkout my_new_branch
+$ git rebase -i rails/master
< Choose 'squash' for all of your commits except the first one. >
< Edit the commit message to make sense, and describe all your changes. >
-$ git push origin my_pull_request -f
+$ git push fork my_new_branch -f
```
You should be able to refresh the pull request on GitHub and see that it has
@@ -620,7 +612,7 @@ you can force push to your branch on GitHub as described earlier in
squashing commits section:
```bash
-$ git push origin my_pull_request -f
+$ git push fork my_new_branch -f
```
This will update the branch and pull request on GitHub with your new code. Do
@@ -632,7 +624,7 @@ note that using force push may result in commits being lost on the remote branch
If you want to add a fix to older versions of Ruby on Rails, you'll need to set up and switch to your own local tracking branch. Here is an example to switch to the 4-0-stable branch:
```bash
-$ git branch --track 4-0-stable origin/4-0-stable
+$ git branch --track 4-0-stable rails/4-0-stable
$ git checkout 4-0-stable
```
diff --git a/guides/source/form_helpers.md b/guides/source/form_helpers.md
index 3660772fb9..b5e2c49487 100644
--- a/guides/source/form_helpers.md
+++ b/guides/source/form_helpers.md
@@ -775,7 +775,7 @@ You might want to render a form with a set of edit fields for each of a person's
<%= form_with model: @person do |person_form| %>
<%= person_form.text_field :name %>
<% @person.addresses.each do |address| %>
- <%= person_form.fields_for address, index: address.id do |address_form|%>
+ <%= person_form.fields_for address, index: address.id do |address_form| %>
<%= address_form.text_field :city %>
<% end %>
<% end %>
@@ -972,7 +972,7 @@ This form allows users to remove addresses:
<ul>
<%= f.fields_for :addresses do |addresses_form| %>
<li>
- <%= addresses_form.check_box :_destroy%>
+ <%= addresses_form.check_box :_destroy %>
<%= addresses_form.label :kind %>
<%= addresses_form.text_field :kind %>
...
diff --git a/guides/source/generators.md b/guides/source/generators.md
index 89424a161b..f028d14998 100644
--- a/guides/source/generators.md
+++ b/guides/source/generators.md
@@ -203,8 +203,6 @@ $ rails generate scaffold User name:string
create test/application_system_test_case.rb
create test/system/users_test.rb
invoke assets
- invoke coffee
- create app/assets/javascripts/users.coffee
invoke scss
create app/assets/stylesheets/users.scss
invoke scss
@@ -426,9 +424,8 @@ $ rails generate scaffold Comment body:text
create test/application_system_test_case.rb
create test/system/comments_test.rb
invoke assets
- invoke coffee
- create app/assets/javascripts/comments.coffee
invoke scss
+ create app/assets/stylesheets/scaffolds.scss
```
Fallbacks allow your generators to have a single responsibility, increasing code reuse and reducing the amount of duplication.
diff --git a/guides/source/getting_started.md b/guides/source/getting_started.md
index b59f4bd0ed..e2f558d74c 100644
--- a/guides/source/getting_started.md
+++ b/guides/source/getting_started.md
@@ -272,8 +272,6 @@ invoke helper
create app/helpers/welcome_helper.rb
invoke test_unit
invoke assets
-invoke coffee
-create app/assets/javascripts/welcome.coffee
invoke scss
create app/assets/stylesheets/welcome.scss
```
@@ -1665,7 +1663,6 @@ This creates five files and one empty directory:
| app/views/comments/ | Views of the controller are stored here |
| test/controllers/comments_controller_test.rb | The test for the controller |
| app/helpers/comments_helper.rb | A view helper file |
-| app/assets/javascripts/comments.coffee | CoffeeScript for the controller |
| app/assets/stylesheets/comments.scss | Cascading style sheet for the controller |
Like with any blog, our readers will create their comments directly after
diff --git a/guides/source/testing.md b/guides/source/testing.md
index de93e1c653..9de2229672 100644
--- a/guides/source/testing.md
+++ b/guides/source/testing.md
@@ -1028,8 +1028,8 @@ You should test for things such as:
* was the web request successful?
* was the user redirected to the right page?
* was the user successfully authenticated?
-* was the correct object stored in the response template?
* was the appropriate message displayed to the user in the view?
+* was the correct information displayed in the response?
The easiest way to see functional tests in action is to generate a controller using the scaffold generator:
diff --git a/guides/source/upgrading_ruby_on_rails.md b/guides/source/upgrading_ruby_on_rails.md
index befd4e08c0..a0553c1ccc 100644
--- a/guides/source/upgrading_ruby_on_rails.md
+++ b/guides/source/upgrading_ruby_on_rails.md
@@ -41,7 +41,7 @@ Rails generally stays close to the latest released Ruby version when it's releas
* Rails 3.2.x is the last branch to support Ruby 1.8.7.
* Rails 3 and above require Ruby 1.8.7 or higher. Support for all of the previous Ruby versions has been dropped officially. You should upgrade as early as possible.
-TIP: Ruby 1.8.7 p248 and p249 have marshaling bugs that crash Rails. Ruby Enterprise Edition has these fixed since the release of 1.8.7-2010.02. On the 1.9 front, Ruby 1.9.1 is not usable because it outright segfaults, so if you want to use 1.9.x, jump straight to 1.9.3 for smooth sailing.
+TIP: Ruby 1.8.7 p248 and p249 have marshalling bugs that crash Rails. Ruby Enterprise Edition has these fixed since the release of 1.8.7-2010.02. On the 1.9 front, Ruby 1.9.1 is not usable because it outright segfaults, so if you want to use 1.9.x, jump straight to 1.9.3 for smooth sailing.
### The Update Task
diff --git a/guides/source/working_with_javascript_in_rails.md b/guides/source/working_with_javascript_in_rails.md
index 36f5039883..c36b3faa6c 100644
--- a/guides/source/working_with_javascript_in_rails.md
+++ b/guides/source/working_with_javascript_in_rails.md
@@ -494,10 +494,6 @@ replace the entire `<body>` of the page with the `<body>` of the response. It
will then use PushState to change the URL to the correct one, preserving
refresh semantics and giving you pretty URLs.
-The only thing you have to do to enable Turbolinks is have it in your `Gemfile`,
-and put `//= require turbolinks` in your JavaScript manifest, which is usually
-`app/assets/javascripts/application.js`.
-
If you want to disable Turbolinks for certain links, add a `data-turbolinks="false"`
attribute to the tag:
diff --git a/package.json b/package.json
new file mode 100644
index 0000000000..88029de141
--- /dev/null
+++ b/package.json
@@ -0,0 +1,14 @@
+{
+ "private": true,
+ "workspaces": [
+ "actioncable",
+ "activestorage",
+ "actionview",
+ "tmp/templates/app_template",
+ "railties/test/fixtures/tmp/bukkits/**/test/dummy",
+ "railties/test/fixtures/tmp/bukkits/**/spec/dummy"
+ ],
+ "dependencies": {
+ "webpack": "^4.17.1"
+ }
+}
diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md
index b916fda1cb..925dac7b3c 100644
--- a/railties/CHANGELOG.md
+++ b/railties/CHANGELOG.md
@@ -1,3 +1,58 @@
+* Use Webpacker by default to manage app-level JavaScript through the new app/javascript directory.
+ Sprockets is now solely in charge, by default, of compiling CSS and other static assets.
+ Action Cable channel generators will create ES6 stubs rather than use CoffeeScript.
+ Active Storage, Action Cable, Turbolinks, and Rails-UJS are loaded by a new application.js pack.
+ Generators no longer generate JavaScript stubs.
+
+ *DHH*, *Lachlan Sylvester*
+
+* Refactors `migrations_paths` command option in generators
+ to `database` (aliased as `db`). Now, the migrations paths
+ will be read from the specified database configuration in the
+ current environment.
+
+ ```
+ bin/rails g model Chair brand:string --database=kingston
+ invoke active_record
+ create db/kingston_migrate/20180830151055_create_chairs.rb
+ ```
+
+ `--database` can be used with the migration, model, and scaffold generators.
+
+ *Gannon McGibbon*
+
+* Adds an option to the model generator to allow setting the
+ migrations paths for that migration. This is useful for
+ applications that use multiple databases and put migrations
+ per database in their own directories.
+
+ ```
+ bin/rails g model Room capacity:integer --migrations-paths=db/kingston_migrate
+ invoke active_record
+ create db/kingston_migrate/20180830151055_create_rooms.rb
+ ```
+
+ Because rails scaffolding uses the model generator, you can
+ also specify migrations paths with the scaffold generator.
+
+ *Gannon McGibbon*
+
+* Raise an error when "recyclable cache keys" are being used by a cache store
+ that does not explicitly support it. Custom cache keys that do support this feature
+ can bypass this error by implementing the `supports_cache_versioning?` method on their
+ class and returning a truthy value.
+
+ *Richard Schneeman*
+
+* Support environment specific credentials file.
+
+ For `production` environment look first for `config/credentials/production.yml.enc` file that can be decrypted by
+ `ENV["RAILS_MASTER_KEY"]` or `config/credentials/production.key` master key.
+ Edit given environment credentials file by command `rails credentials:edit --environment production`.
+ Default paths can be overwritten by setting `config.credentials.content_path` and `config.credentials.key_path`.
+
+ *Wojciech Wnętrzak*
+
* Make `ActiveSupport::Cache::NullStore` the default cache store in the test environment.
*Michael C. Nelson*
diff --git a/railties/Rakefile b/railties/Rakefile
index e1be0ceb40..445f6217b3 100644
--- a/railties/Rakefile
+++ b/railties/Rakefile
@@ -28,6 +28,9 @@ namespace :test do
require "bundler/setup" unless defined?(Bundler)
require "active_support"
+ # Only generate the template app once.
+ require_relative "test/isolation/abstract_unit"
+
failing_files = []
dirs = (ENV["TEST_DIR"] || ENV["TEST_DIRS"] || "**").split(",")
diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb
index 26ed195dcc..656786246d 100644
--- a/railties/lib/rails/application.rb
+++ b/railties/lib/rails/application.rb
@@ -438,8 +438,12 @@ module Rails
# Decrypts the credentials hash as kept in +config/credentials.yml.enc+. This file is encrypted with
# the Rails master key, which is either taken from <tt>ENV["RAILS_MASTER_KEY"]</tt> or from loading
# +config/master.key+.
+ # If specific credentials file exists for current environment, it takes precedence, thus for +production+
+ # environment look first for +config/credentials/production.yml.enc+ with master key taken
+ # from <tt>ENV["RAILS_MASTER_KEY"]</tt> or from loading +config/credentials/production.key+.
+ # Default behavior can be overwritten by setting +config.credentials.content_path+ and +config.credentials.key_path+.
def credentials
- @credentials ||= encrypted("config/credentials.yml.enc")
+ @credentials ||= encrypted(config.credentials.content_path, key_path: config.credentials.key_path)
end
# Shorthand to decrypt any encrypted configurations or files.
diff --git a/railties/lib/rails/application/configuration.rb b/railties/lib/rails/application/configuration.rb
index f4cbd2b9d0..eae902a938 100644
--- a/railties/lib/rails/application/configuration.rb
+++ b/railties/lib/rails/application/configuration.rb
@@ -17,7 +17,7 @@ module Rails
:session_options, :time_zone, :reload_classes_only_on_change,
:beginning_of_week, :filter_redirect, :x, :enable_dependency_loading,
:read_encrypted_secrets, :log_level, :content_security_policy_report_only,
- :content_security_policy_nonce_generator, :require_master_key
+ :content_security_policy_nonce_generator, :require_master_key, :credentials
attr_reader :encoding, :api_only, :loaded_config_version
@@ -60,6 +60,9 @@ module Rails
@content_security_policy_nonce_generator = nil
@require_master_key = false
@loaded_config_version = nil
+ @credentials = ActiveSupport::OrderedOptions.new
+ @credentials.content_path = default_credentials_content_path
+ @credentials.key_path = default_credentials_key_path
end
def load_defaults(target_version)
@@ -273,6 +276,27 @@ module Rails
true
end
end
+
+ private
+ def credentials_available_for_current_env?
+ File.exist?("#{root}/config/credentials/#{Rails.env}.yml.enc")
+ end
+
+ def default_credentials_content_path
+ if credentials_available_for_current_env?
+ File.join(root, "config", "credentials", "#{Rails.env}.yml.enc")
+ else
+ File.join(root, "config", "credentials.yml.enc")
+ end
+ end
+
+ def default_credentials_key_path
+ if credentials_available_for_current_env?
+ File.join(root, "config", "credentials", "#{Rails.env}.key")
+ else
+ File.join(root, "config", "master.key")
+ end
+ end
end
end
end
diff --git a/railties/lib/rails/backtrace_cleaner.rb b/railties/lib/rails/backtrace_cleaner.rb
index b1e3c923b7..7c2eb1dc42 100644
--- a/railties/lib/rails/backtrace_cleaner.rb
+++ b/railties/lib/rails/backtrace_cleaner.rb
@@ -6,13 +6,13 @@ module Rails
class BacktraceCleaner < ActiveSupport::BacktraceCleaner
APP_DIRS_PATTERN = /^\/?(app|config|lib|test|\(\w*\))/
RENDER_TEMPLATE_PATTERN = /:in `.*_\w+_{2,3}\d+_\d+'/
- EMPTY_STRING = "".freeze
- SLASH = "/".freeze
- DOT_SLASH = "./".freeze
+ EMPTY_STRING = ""
+ SLASH = "/"
+ DOT_SLASH = "./"
def initialize
super
- @root = "#{Rails.root}/".freeze
+ @root = "#{Rails.root}/"
add_filter { |line| line.sub(@root, EMPTY_STRING) }
add_filter { |line| line.sub(RENDER_TEMPLATE_PATTERN, EMPTY_STRING) }
add_filter { |line| line.sub(DOT_SLASH, SLASH) } # for tests
diff --git a/railties/lib/rails/commands/credentials/USAGE b/railties/lib/rails/commands/credentials/USAGE
index ea429f58d8..6b33d1ab74 100644
--- a/railties/lib/rails/commands/credentials/USAGE
+++ b/railties/lib/rails/commands/credentials/USAGE
@@ -38,3 +38,12 @@ the encrypted credentials.
When the temporary file is next saved the contents are encrypted and written to
`config/credentials.yml.enc` while the file itself is destroyed to prevent credentials
from leaking.
+
+=== Environment Specific Credentials
+
+It is possible to have credentials for each environment. If the file for current environment exists it will take
+precedence over `config/credentials.yml.enc`, thus for `production` environment first look for
+`config/credentials/production.yml.enc` that can be decrypted using master key taken from `ENV["RAILS_MASTER_KEY"]`
+or stored in `config/credentials/production.key`.
+To edit given file use command `rails credentials:edit --environment production`
+Default paths can be overwritten by setting `config.credentials.content_path` and `config.credentials.key_path`.
diff --git a/railties/lib/rails/commands/credentials/credentials_command.rb b/railties/lib/rails/commands/credentials/credentials_command.rb
index 65c5218fc8..4b30d208e0 100644
--- a/railties/lib/rails/commands/credentials/credentials_command.rb
+++ b/railties/lib/rails/commands/credentials/credentials_command.rb
@@ -8,6 +8,9 @@ module Rails
class CredentialsCommand < Rails::Command::Base # :nodoc:
include Helpers::Editor
+ class_option :environment, aliases: "-e", type: :string,
+ desc: "Uses credentials from config/credentials/:environment.yml.enc encrypted by config/credentials/:environment.key key"
+
no_commands do
def help
say "Usage:\n #{self.class.banner}"
@@ -20,58 +23,74 @@ module Rails
require_application_and_environment!
ensure_editor_available(command: "bin/rails credentials:edit") || (return)
- ensure_master_key_has_been_added if Rails.application.credentials.key.nil?
- ensure_credentials_have_been_added
+
+ encrypted = Rails.application.encrypted(content_path, key_path: key_path)
+
+ ensure_encryption_key_has_been_added(key_path) if encrypted.key.nil?
+ ensure_encrypted_file_has_been_added(content_path, key_path)
catch_editing_exceptions do
- change_credentials_in_system_editor
+ change_encrypted_file_in_system_editor(content_path, key_path)
end
- say "New credentials encrypted and saved."
+ say "File encrypted and saved."
+ rescue ActiveSupport::MessageEncryptor::InvalidMessage
+ say "Couldn't decrypt #{content_path}. Perhaps you passed the wrong key?"
end
def show
require_application_and_environment!
- say Rails.application.credentials.read.presence || missing_credentials_message
+ encrypted = Rails.application.encrypted(content_path, key_path: key_path)
+
+ say encrypted.read.presence || missing_encrypted_message(key: encrypted.key, key_path: key_path, file_path: content_path)
end
private
- def ensure_master_key_has_been_added
- master_key_generator.add_master_key_file
- master_key_generator.ignore_master_key_file
+ def content_path
+ options[:environment] ? "config/credentials/#{options[:environment]}.yml.enc" : "config/credentials.yml.enc"
+ end
+
+ def key_path
+ options[:environment] ? "config/credentials/#{options[:environment]}.key" : "config/master.key"
+ end
+
+
+ def ensure_encryption_key_has_been_added(key_path)
+ encryption_key_file_generator.add_key_file(key_path)
+ encryption_key_file_generator.ignore_key_file(key_path)
end
- def ensure_credentials_have_been_added
- credentials_generator.add_credentials_file_silently
+ def ensure_encrypted_file_has_been_added(file_path, key_path)
+ encrypted_file_generator.add_encrypted_file_silently(file_path, key_path)
end
- def change_credentials_in_system_editor
- Rails.application.credentials.change do |tmp_path|
+ def change_encrypted_file_in_system_editor(file_path, key_path)
+ Rails.application.encrypted(file_path, key_path: key_path).change do |tmp_path|
system("#{ENV["EDITOR"]} #{tmp_path}")
end
end
- def master_key_generator
+ def encryption_key_file_generator
require "rails/generators"
- require "rails/generators/rails/master_key/master_key_generator"
+ require "rails/generators/rails/encryption_key_file/encryption_key_file_generator"
- Rails::Generators::MasterKeyGenerator.new
+ Rails::Generators::EncryptionKeyFileGenerator.new
end
- def credentials_generator
+ def encrypted_file_generator
require "rails/generators"
- require "rails/generators/rails/credentials/credentials_generator"
+ require "rails/generators/rails/encrypted_file/encrypted_file_generator"
- Rails::Generators::CredentialsGenerator.new
+ Rails::Generators::EncryptedFileGenerator.new
end
- def missing_credentials_message
- if Rails.application.credentials.key.nil?
- "Missing master key to decrypt credentials. See `rails credentials:help`"
+ def missing_encrypted_message(key:, key_path:, file_path:)
+ if key.nil?
+ "Missing '#{key_path}' to decrypt credentials. See `rails credentials:help`"
else
- "No credentials have been added yet. Use `rails credentials:edit` to change that."
+ "File '#{file_path}' does not exist. Use `rails credentials:edit` to change that."
end
end
end
diff --git a/railties/lib/rails/commands/dbconsole/dbconsole_command.rb b/railties/lib/rails/commands/dbconsole/dbconsole_command.rb
index 806b7de6d6..0fac7d34a0 100644
--- a/railties/lib/rails/commands/dbconsole/dbconsole_command.rb
+++ b/railties/lib/rails/commands/dbconsole/dbconsole_command.rb
@@ -75,7 +75,7 @@ module Rails
args += ["-P", "#{config['password']}"] if config["password"]
if config["host"]
- host_arg = "#{config['host']}".dup
+ host_arg = +"#{config['host']}"
host_arg << ":#{config['port']}" if config["port"]
args += ["-S", host_arg]
end
diff --git a/railties/lib/rails/commands/server/server_command.rb b/railties/lib/rails/commands/server/server_command.rb
index 9d517f3239..70789e0303 100644
--- a/railties/lib/rails/commands/server/server_command.rb
+++ b/railties/lib/rails/commands/server/server_command.rb
@@ -109,7 +109,7 @@ module Rails
RACK_SERVERS = %w(cgi fastcgi webrick lsws scgi thin puma unicorn)
DEFAULT_PORT = 3000
- DEFAULT_PID_PATH = "tmp/pids/server.pid".freeze
+ DEFAULT_PID_PATH = "tmp/pids/server.pid"
argument :using, optional: true
diff --git a/railties/lib/rails/generators.rb b/railties/lib/rails/generators.rb
index ed672ae48e..cd8f16e247 100644
--- a/railties/lib/rails/generators.rb
+++ b/railties/lib/rails/generators.rb
@@ -33,8 +33,6 @@ module Rails
rails: {
actions: "-a",
orm: "-o",
- javascripts: "-j",
- javascript_engine: "-je",
resource_controller: "-c",
scaffold_controller: "-c",
stylesheets: "-y",
diff --git a/railties/lib/rails/generators/app_base.rb b/railties/lib/rails/generators/app_base.rb
index 8991c547ca..410e53b4c0 100644
--- a/railties/lib/rails/generators/app_base.rb
+++ b/railties/lib/rails/generators/app_base.rb
@@ -68,10 +68,7 @@ module Rails
class_option :skip_listen, type: :boolean, default: false,
desc: "Don't generate configuration that depends on the listen gem"
- class_option :skip_coffee, type: :boolean, default: false,
- desc: "Don't use CoffeeScript"
-
- class_option :skip_javascript, type: :boolean, aliases: "-J", default: false,
+ class_option :skip_javascript, type: :boolean, aliases: "-J", default: name == "plugin",
desc: "Skip JavaScript files"
class_option :skip_turbolinks, type: :boolean, default: false,
@@ -327,24 +324,17 @@ module Rails
def assets_gemfile_entry
return [] if options[:skip_sprockets]
- gems = []
- gems << GemfileEntry.version("sass-rails", "~> 5.0",
- "Use SCSS for stylesheets")
-
- if !options[:skip_javascript]
- gems << GemfileEntry.version("uglifier",
- ">= 1.3.0",
- "Use Uglifier as compressor for JavaScript assets")
- end
-
- gems
+ GemfileEntry.version("sass-rails", "~> 5.0", "Use SCSS for stylesheets")
end
def webpacker_gemfile_entry
- return [] unless options[:webpack]
+ return [] if options[:skip_javascript]
- comment = "Transpile app-like JavaScript. Read more: https://github.com/rails/webpacker"
- GemfileEntry.new "webpacker", nil, comment
+ if options.dev? || options.edge?
+ GemfileEntry.github "webpacker", "rails/webpacker", nil, "Use development version of Webpacker"
+ else
+ GemfileEntry.new "webpacker", nil, "Transpile app-like JavaScript. Read more: https://github.com/rails/webpacker"
+ end
end
def jbuilder_gemfile_entry
@@ -352,34 +342,12 @@ module Rails
GemfileEntry.new "jbuilder", "~> 2.5", comment, {}, options[:api]
end
- def coffee_gemfile_entry
- GemfileEntry.version "coffee-rails", "~> 4.2", "Use CoffeeScript for .coffee assets and views"
- end
-
def javascript_gemfile_entry
- if options[:skip_javascript] || options[:skip_sprockets]
+ if options[:skip_javascript] || options[:skip_turbolinks]
[]
else
- gems = [javascript_runtime_gemfile_entry]
- gems << coffee_gemfile_entry unless options[:skip_coffee]
-
- unless options[:skip_turbolinks]
- gems << GemfileEntry.version("turbolinks", "~> 5",
- "Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks")
- end
-
- gems
- end
- end
-
- def javascript_runtime_gemfile_entry
- comment = "See https://github.com/rails/execjs#readme for more supported runtimes"
- if defined?(JRUBY_VERSION)
- GemfileEntry.version "therubyrhino", nil, comment
- elsif RUBY_PLATFORM.match?(/mingw|mswin/)
- GemfileEntry.version "duktape", nil, comment
- else
- GemfileEntry.new "mini_racer", nil, comment, { platforms: :ruby }, true
+ [ GemfileEntry.version("turbolinks", "~> 5",
+ "Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks") ]
end
end
@@ -452,9 +420,9 @@ module Rails
end
def run_webpack
- if !(webpack = options[:webpack]).nil?
+ unless options[:skip_javascript]
rails_command "webpacker:install"
- rails_command "webpacker:install:#{webpack}" unless webpack == "webpack"
+ rails_command "webpacker:install:#{options[:webpack]}" if options[:webpack] && options[:webpack] != "webpack"
end
end
diff --git a/railties/lib/rails/generators/generated_attribute.rb b/railties/lib/rails/generators/generated_attribute.rb
index f7fd30a5fb..3f20f5a718 100644
--- a/railties/lib/rails/generators/generated_attribute.rb
+++ b/railties/lib/rails/generators/generated_attribute.rb
@@ -153,7 +153,7 @@ module Rails
end
def inject_options
- "".dup.tap { |s| options_for_migration.each { |k, v| s << ", #{k}: #{v.inspect}" } }
+ (+"").tap { |s| options_for_migration.each { |k, v| s << ", #{k}: #{v.inspect}" } }
end
def inject_index_options
diff --git a/railties/lib/rails/generators/js/assets/assets_generator.rb b/railties/lib/rails/generators/js/assets/assets_generator.rb
deleted file mode 100644
index 9d32c666dc..0000000000
--- a/railties/lib/rails/generators/js/assets/assets_generator.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-# frozen_string_literal: true
-
-require "rails/generators/named_base"
-
-module Js # :nodoc:
- module Generators # :nodoc:
- class AssetsGenerator < Rails::Generators::NamedBase # :nodoc:
- source_root File.expand_path("templates", __dir__)
-
- def copy_javascript
- copy_file "javascript.js", File.join("app/assets/javascripts", class_path, "#{file_name}.js")
- end
- end
- end
-end
diff --git a/railties/lib/rails/generators/js/assets/templates/javascript.js b/railties/lib/rails/generators/js/assets/templates/javascript.js
deleted file mode 100644
index dee720facd..0000000000
--- a/railties/lib/rails/generators/js/assets/templates/javascript.js
+++ /dev/null
@@ -1,2 +0,0 @@
-// Place all the behaviors and hooks related to the matching controller here.
-// All this logic will automatically be available in application.js.
diff --git a/railties/lib/rails/generators/rails/app/app_generator.rb b/railties/lib/rails/generators/rails/app/app_generator.rb
index a6d160f1eb..cefaffcb20 100644
--- a/railties/lib/rails/generators/rails/app/app_generator.rb
+++ b/railties/lib/rails/generators/rails/app/app_generator.rb
@@ -80,7 +80,6 @@ module Rails
directory "app"
keep_file "app/assets/images"
- empty_directory_with_keep_file "app/assets/javascripts/channels" unless options[:skip_action_cable]
keep_file "app/controllers/concerns"
keep_file "app/models/concerns"
@@ -260,7 +259,7 @@ module Rails
desc: "Don't run bundle install"
class_option :webpack, type: :string, default: nil,
- desc: "Preconfigure for app-like JavaScript with Webpack (options: #{WEBPACKS.join('/')})"
+ desc: "Preconfigure Webpack with a particular framework (options: #{WEBPACKS.join('/')})"
def initialize(*args)
super
@@ -409,7 +408,7 @@ module Rails
def delete_js_folder_skipping_javascript
if options[:skip_javascript]
- remove_dir "app/assets/javascripts"
+ remove_dir "app/javascript"
end
end
@@ -436,7 +435,8 @@ module Rails
def delete_action_cable_files_skipping_action_cable
if options[:skip_action_cable]
- remove_file "app/assets/javascripts/cable.js"
+ remove_file "app/javascript/channels/consumer.js"
+ remove_dir "app/javascript/channels"
remove_dir "app/channels"
end
end
diff --git a/railties/lib/rails/generators/rails/app/templates/app/assets/config/manifest.js.tt b/railties/lib/rails/generators/rails/app/templates/app/assets/config/manifest.js.tt
index 70b579d10e..591819335f 100644
--- a/railties/lib/rails/generators/rails/app/templates/app/assets/config/manifest.js.tt
+++ b/railties/lib/rails/generators/rails/app/templates/app/assets/config/manifest.js.tt
@@ -1,5 +1,2 @@
//= link_tree ../images
-<% unless options.skip_javascript -%>
-//= link_directory ../javascripts .js
-<% end -%>
//= link_directory ../stylesheets .css
diff --git a/railties/lib/rails/generators/rails/app/templates/app/assets/javascripts/application.js.tt b/railties/lib/rails/generators/rails/app/templates/app/assets/javascripts/application.js.tt
deleted file mode 100644
index 5183bcd256..0000000000
--- a/railties/lib/rails/generators/rails/app/templates/app/assets/javascripts/application.js.tt
+++ /dev/null
@@ -1,22 +0,0 @@
-// This is a manifest file that'll be compiled into application.js, which will include all the files
-// listed below.
-//
-// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, or any plugin's
-// vendor/assets/javascripts directory can be referenced here using a relative path.
-//
-// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
-// compiled file. JavaScript code in this file should be added after the last require_* statement.
-//
-// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
-// about supported directives.
-//
-<% unless options[:skip_javascript] -%>
-//= require rails-ujs
-<% unless skip_active_storage? -%>
-//= require activestorage
-<% end -%>
-<% unless options[:skip_turbolinks] -%>
-//= require turbolinks
-<% end -%>
-<% end -%>
-//= require_tree .
diff --git a/railties/lib/rails/generators/rails/app/templates/app/assets/javascripts/cable.js.tt b/railties/lib/rails/generators/rails/app/templates/app/javascript/channels/consumer.js
index 739aa5f022..76ca3d0f2f 100644
--- a/railties/lib/rails/generators/rails/app/templates/app/assets/javascripts/cable.js.tt
+++ b/railties/lib/rails/generators/rails/app/templates/app/javascript/channels/consumer.js
@@ -1,13 +1,6 @@
// Action Cable provides the framework to deal with WebSockets in Rails.
// You can generate new channels where WebSocket features live using the `rails generate channel` command.
-//
-//= require action_cable
-//= require_self
-//= require_tree ./channels
-(function() {
- this.App || (this.App = {});
+import ActionCable from "actioncable"
- App.cable = ActionCable.createConsumer();
-
-}).call(this);
+export default ActionCable.createConsumer()
diff --git a/railties/lib/rails/generators/rails/app/templates/app/javascript/channels/index.js b/railties/lib/rails/generators/rails/app/templates/app/javascript/channels/index.js
new file mode 100644
index 0000000000..5da1ce2dce
--- /dev/null
+++ b/railties/lib/rails/generators/rails/app/templates/app/javascript/channels/index.js
@@ -0,0 +1,5 @@
+// Load all the channels within this directory and all subdirectories.
+// Channel files must be named *_channel.js.
+
+const channels = require.context('.', true, /\_channel\.js$/)
+channels.keys().forEach(channels)
diff --git a/railties/lib/rails/generators/rails/app/templates/app/javascript/packs/application.js.tt b/railties/lib/rails/generators/rails/app/templates/app/javascript/packs/application.js.tt
new file mode 100644
index 0000000000..4d7a145cd6
--- /dev/null
+++ b/railties/lib/rails/generators/rails/app/templates/app/javascript/packs/application.js.tt
@@ -0,0 +1,21 @@
+// This file is automatically compiled by Webpack, along with any other files
+// present in this directory. You're encouraged to place your actual application logic in
+// a relevant structure within app/javascript and only use these pack files to reference
+// that code so it'll be compiled.
+
+import Rails from "rails-ujs"
+Rails.start()
+<%- unless options[:skip_turbolinks] -%>
+
+import Turbolinks from "turbolinks"
+Turbolinks.start()
+<%- end -%>
+<%- unless skip_active_storage? -%>
+
+import * as ActiveStorage from "activestorage"
+ActiveStorage.start()
+<%- end -%>
+<%- unless options[:skip_action_cable] -%>
+
+import "channels"
+<%- end -%>
diff --git a/railties/lib/rails/generators/rails/app/templates/app/jobs/application_job.rb.tt b/railties/lib/rails/generators/rails/app/templates/app/jobs/application_job.rb.tt
index a009ace51c..d394c3d106 100644
--- a/railties/lib/rails/generators/rails/app/templates/app/jobs/application_job.rb.tt
+++ b/railties/lib/rails/generators/rails/app/templates/app/jobs/application_job.rb.tt
@@ -1,2 +1,7 @@
class ApplicationJob < ActiveJob::Base
+ # Automatically retry jobs that encountered a deadlock
+ # retry_on ActiveRecord::Deadlocked
+
+ # Most jobs are safe to ignore if the underlying records are no longer available
+ # discard_on ActiveJob::DeserializationError
end
diff --git a/railties/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb.tt b/railties/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb.tt
index ef715f1368..9a7267c783 100644
--- a/railties/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb.tt
+++ b/railties/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb.tt
@@ -9,11 +9,11 @@
<%%= stylesheet_link_tag 'application', media: 'all' %>
<%- else -%>
<%- unless options[:skip_turbolinks] -%>
- <%%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
- <%%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
+ <%%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
+ <%%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
<%- else -%>
- <%%= stylesheet_link_tag 'application', media: 'all' %>
- <%%= javascript_include_tag 'application' %>
+ <%%= stylesheet_link_tag 'application', media: 'all' %>
+ <%%= javascript_pack_tag 'application' %>
<%- end -%>
<%- end -%>
</head>
diff --git a/railties/lib/rails/generators/rails/app/templates/bin/setup.tt b/railties/lib/rails/generators/rails/app/templates/bin/setup.tt
index 233b5a1d95..955a424878 100644
--- a/railties/lib/rails/generators/rails/app/templates/bin/setup.tt
+++ b/railties/lib/rails/generators/rails/app/templates/bin/setup.tt
@@ -1,5 +1,4 @@
require 'fileutils'
-include FileUtils
# path to your application root.
APP_ROOT = File.expand_path('..', __dir__)
@@ -8,7 +7,7 @@ def system!(*args)
system(*args) || abort("\n== Command #{args} failed ==")
end
-chdir APP_ROOT do
+FileUtils.chdir APP_ROOT do
# This script is a starting point to setup your application.
# Add necessary setup steps to this file.
diff --git a/railties/lib/rails/generators/rails/app/templates/bin/update.tt b/railties/lib/rails/generators/rails/app/templates/bin/update.tt
index 99c2430bc6..ed17959e1d 100644
--- a/railties/lib/rails/generators/rails/app/templates/bin/update.tt
+++ b/railties/lib/rails/generators/rails/app/templates/bin/update.tt
@@ -1,5 +1,4 @@
require 'fileutils'
-include FileUtils
# path to your application root.
APP_ROOT = File.expand_path('..', __dir__)
@@ -8,7 +7,7 @@ def system!(*args)
system(*args) || abort("\n== Command #{args} failed ==")
end
-chdir APP_ROOT do
+FileUtils.chdir APP_ROOT do
# This script is a way to update your development environment automatically.
# Add necessary update steps to this file.
diff --git a/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt
index 888336af92..08befd9196 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt
+++ b/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt
@@ -23,12 +23,7 @@ Rails.application.configure do
config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present?
<%- unless options.skip_sprockets? -%>
- <%- if options.skip_javascript? -%>
- # Compress CSS.
- <%- else -%>
- # Compress JavaScripts and CSS.
- config.assets.js_compressor = :uglifier
- <%- end -%>
+ # Compress CSS using a preprocessor.
# config.assets.css_compressor = :sass
# Do not fallback to assets pipeline if a precompiled asset is missed.
diff --git a/railties/lib/rails/generators/rails/app/templates/package.json.tt b/railties/lib/rails/generators/rails/app/templates/package.json.tt
index 46db57dcbe..a654cba39c 100644
--- a/railties/lib/rails/generators/rails/app/templates/package.json.tt
+++ b/railties/lib/rails/generators/rails/app/templates/package.json.tt
@@ -1,5 +1,11 @@
{
"name": "<%= app_name %>",
"private": true,
- "dependencies": {}
+ "dependencies": {
+ "rails-ujs": ">=5.2.1"<% unless options[:skip_turbolinks] %>,
+ "turbolinks": "5.1.1"<% end -%><% unless skip_active_storage? %>,
+ "activestorage": ">=5.2.1"<% end -%><% unless options[:skip_action_cable] %>,
+ "actioncable": ">=5.2.1"<% end -%>
+ },
+ "version": "0.1.0"
}
diff --git a/railties/lib/rails/generators/rails/assets/USAGE b/railties/lib/rails/generators/rails/assets/USAGE
index d2e5ed4482..ee73d05808 100644
--- a/railties/lib/rails/generators/rails/assets/USAGE
+++ b/railties/lib/rails/generators/rails/assets/USAGE
@@ -5,16 +5,13 @@ Description:
To create an asset within a folder, specify the asset's name as a
path like 'parent/name'.
- This generates a JavaScript stub in app/assets/javascripts and a stylesheet
- stub in app/assets/stylesheets.
+ This generates a stylesheet stub in app/assets/stylesheets.
- If CoffeeScript is available, JavaScripts will be generated with the .coffee extension.
If Sass 3 is available, stylesheets will be generated with the .scss extension.
Example:
`rails generate assets posts`
Posts assets.
- JavaScript: app/assets/javascripts/posts.js
Stylesheet: app/assets/stylesheets/posts.css
diff --git a/railties/lib/rails/generators/rails/assets/assets_generator.rb b/railties/lib/rails/generators/rails/assets/assets_generator.rb
index ffb695a1f3..9ce8570172 100644
--- a/railties/lib/rails/generators/rails/assets/assets_generator.rb
+++ b/railties/lib/rails/generators/rails/assets/assets_generator.rb
@@ -3,22 +3,14 @@
module Rails
module Generators
class AssetsGenerator < NamedBase # :nodoc:
- class_option :javascripts, type: :boolean, desc: "Generate JavaScripts"
class_option :stylesheets, type: :boolean, desc: "Generate Stylesheets"
-
- class_option :javascript_engine, desc: "Engine for JavaScripts"
class_option :stylesheet_engine, desc: "Engine for Stylesheets"
private
-
def asset_name
file_name
end
- hook_for :javascript_engine do |javascript_engine|
- invoke javascript_engine, [name] if options[:javascripts]
- end
-
hook_for :stylesheet_engine do |stylesheet_engine|
invoke stylesheet_engine, [name] if options[:stylesheets]
end
diff --git a/railties/lib/rails/generators/rails/assets/templates/javascript.js b/railties/lib/rails/generators/rails/assets/templates/javascript.js
deleted file mode 100644
index dee720facd..0000000000
--- a/railties/lib/rails/generators/rails/assets/templates/javascript.js
+++ /dev/null
@@ -1,2 +0,0 @@
-// Place all the behaviors and hooks related to the matching controller here.
-// All this logic will automatically be available in application.js.
diff --git a/railties/lib/rails/generators/rails/plugin/plugin_generator.rb b/railties/lib/rails/generators/rails/plugin/plugin_generator.rb
index 8cc42325bb..9ec0ccbe7a 100644
--- a/railties/lib/rails/generators/rails/plugin/plugin_generator.rb
+++ b/railties/lib/rails/generators/rails/plugin/plugin_generator.rb
@@ -113,7 +113,7 @@ task default: :test
end
def test_dummy_assets
- template "rails/javascripts.js", "#{dummy_path}/app/assets/javascripts/application.js", force: true
+ template "rails/javascripts.js", "#{dummy_path}/app/javascript/packs/application.js", force: true
template "rails/stylesheets.css", "#{dummy_path}/app/assets/stylesheets/application.css", force: true
template "rails/dummy_manifest.js", "#{dummy_path}/app/assets/config/manifest.js", force: true
end
diff --git a/railties/lib/rails/generators/testing/behaviour.rb b/railties/lib/rails/generators/testing/behaviour.rb
index 6ab88bd59f..238ffdd677 100644
--- a/railties/lib/rails/generators/testing/behaviour.rb
+++ b/railties/lib/rails/generators/testing/behaviour.rb
@@ -67,6 +67,9 @@ module Rails
def run_generator(args = default_arguments, config = {})
capture(:stdout) do
args += ["--skip-bundle"] unless args.include? "--dev"
+ args |= ["--skip-bootsnap"] unless args.include? "--no-skip-bootsnap"
+ args |= ["--skip-javascript"] unless args.include? "--no-skip-javascript"
+
generator_class.start(args, config.reverse_merge(destination_root: destination_root))
end
end
diff --git a/railties/lib/rails/info.rb b/railties/lib/rails/info.rb
index d5c9973c6b..3df36efc4c 100644
--- a/railties/lib/rails/info.rb
+++ b/railties/lib/rails/info.rb
@@ -41,7 +41,7 @@ module Rails
alias inspect to_s
def to_html
- "<table>".dup.tap do |table|
+ (+"<table>").tap do |table|
properties.each do |(name, value)|
table << %(<tr><td class="name">#{CGI.escapeHTML(name.to_s)}</td>)
formatted_value = if value.kind_of?(Array)
diff --git a/railties/lib/rails/railtie.rb b/railties/lib/rails/railtie.rb
index 88dd932370..a67b90e285 100644
--- a/railties/lib/rails/railtie.rb
+++ b/railties/lib/rails/railtie.rb
@@ -224,7 +224,7 @@ module Rails
end
def railtie_namespace #:nodoc:
- @railtie_namespace ||= self.class.parents.detect { |n| n.respond_to?(:railtie_namespace) }
+ @railtie_namespace ||= self.class.module_parents.detect { |n| n.respond_to?(:railtie_namespace) }
end
protected
diff --git a/railties/lib/rails/source_annotation_extractor.rb b/railties/lib/rails/source_annotation_extractor.rb
index 2d66a4dc7d..d7170e6282 100644
--- a/railties/lib/rails/source_annotation_extractor.rb
+++ b/railties/lib/rails/source_annotation_extractor.rb
@@ -50,7 +50,7 @@ module Rails
# If +options+ has a flag <tt>:tag</tt> the tag is shown as in the example above.
# Otherwise the string contains just line and text.
def to_s(options = {})
- s = "[#{line.to_s.rjust(options[:indent])}] ".dup
+ s = +"[#{line.to_s.rjust(options[:indent])}] "
s << "[#{tag}] " if options[:tag]
s << text
end
diff --git a/railties/lib/rails/tasks/statistics.rake b/railties/lib/rails/tasks/statistics.rake
index 594db91eec..8cacf4a49f 100644
--- a/railties/lib/rails/tasks/statistics.rake
+++ b/railties/lib/rails/tasks/statistics.rake
@@ -11,6 +11,7 @@ STATS_DIRECTORIES = [
%w(Mailers app/mailers),
%w(Channels app/channels),
%w(JavaScripts app/assets/javascripts),
+ %w(JavaScript app/javascript),
%w(Libraries lib/),
%w(APIs app/apis),
%w(Controller\ tests test/controllers),
diff --git a/railties/lib/rails/test_unit/runner.rb b/railties/lib/rails/test_unit/runner.rb
index 6332a9d422..d38952bb30 100644
--- a/railties/lib/rails/test_unit/runner.rb
+++ b/railties/lib/rails/test_unit/runner.rb
@@ -12,8 +12,8 @@ module Rails
class << self
def attach_before_load_options(opts)
- opts.on("--warnings", "-w", "Run with Ruby warnings enabled") {}
- opts.on("-e", "--environment ENV", "Run tests in the ENV environment") {}
+ opts.on("--warnings", "-w", "Run with Ruby warnings enabled") { }
+ opts.on("-e", "--environment ENV", "Run tests in the ENV environment") { }
end
def parse_options(argv)
diff --git a/railties/test/abstract_unit.rb b/railties/test/abstract_unit.rb
index b42f37d6b9..9c866263f0 100644
--- a/railties/test/abstract_unit.rb
+++ b/railties/test/abstract_unit.rb
@@ -21,12 +21,14 @@ end
class ActiveSupport::TestCase
include ActiveSupport::Testing::Stream
- # Skips the current run on Rubinius using Minitest::Assertions#skip
- private def rubinius_skip(message = "")
- skip message if RUBY_ENGINE == "rbx"
- end
- # Skips the current run on JRuby using Minitest::Assertions#skip
- private def jruby_skip(message = "")
- skip message if defined?(JRUBY_VERSION)
- end
+ private
+ # Skips the current run on Rubinius using Minitest::Assertions#skip
+ def rubinius_skip(message = "")
+ skip message if RUBY_ENGINE == "rbx"
+ end
+
+ # Skips the current run on JRuby using Minitest::Assertions#skip
+ def jruby_skip(message = "")
+ skip message if defined?(JRUBY_VERSION)
+ end
end
diff --git a/railties/test/application/assets_test.rb b/railties/test/application/assets_test.rb
index 4ca6d02b85..46ee0d670e 100644
--- a/railties/test/application/assets_test.rb
+++ b/railties/test/application/assets_test.rb
@@ -68,20 +68,6 @@ module ApplicationTests
assert_equal 'a = "/assets/rails.png";', last_response.body.strip
end
- test "assets do not require compressors until it is used" do
- app_file "app/assets/javascripts/demo.js.erb", "<%= :alert %>();"
- add_to_env_config "production", "config.assets.compile = true"
- add_to_env_config "production", "config.assets.precompile = []"
-
- # Load app env
- app "production"
-
- assert_not defined?(Uglifier)
- get "/assets/demo.js"
- assert_match "alert()", last_response.body
- assert defined?(Uglifier)
- end
-
test "precompile creates the file, gives it the original asset's content and run in production as default" do
app_file "app/assets/config/manifest.js", "//= link_tree ../javascripts"
app_file "app/assets/javascripts/application.js", "alert();"
@@ -443,13 +429,13 @@ module ApplicationTests
end
test "digested assets are not mistakenly removed" do
- app_file "app/assets/application.js", "alert();"
+ app_file "app/assets/application.css", "div { font-weight: bold }"
add_to_config "config.assets.compile = true"
precompile!
- files = Dir["#{app_path}/public/assets/application-*.js"]
- assert_equal 1, files.length, "Expected digested application.js asset to be generated, but none found"
+ files = Dir["#{app_path}/public/assets/application-*.css"]
+ assert_equal 1, files.length, "Expected digested application.css asset to be generated, but none found"
end
test "digested assets are removed from configured path" do
diff --git a/railties/test/application/bin_setup_test.rb b/railties/test/application/bin_setup_test.rb
index 54934dbe24..d02100d94c 100644
--- a/railties/test/application/bin_setup_test.rb
+++ b/railties/test/application/bin_setup_test.rb
@@ -43,18 +43,20 @@ module ApplicationTests
# Ignore line that's only output by Bundler < 1.14
output.sub!(/^Resolving dependencies\.\.\.\n/, "")
+ # Suppress Bundler platform warnings from output
+ output.gsub!(/^The dependency .* will be unused .*\.\n/, "")
- assert_equal(<<-OUTPUT, output)
-== Installing dependencies ==
-The Gemfile's dependencies are satisfied
+ assert_equal(<<~OUTPUT, output)
+ == Installing dependencies ==
+ The Gemfile's dependencies are satisfied
-== Preparing database ==
-Created database 'db/development.sqlite3'
-Created database 'db/test.sqlite3'
+ == Preparing database ==
+ Created database 'db/development.sqlite3'
+ Created database 'db/test.sqlite3'
-== Removing old logs and tempfiles ==
+ == Removing old logs and tempfiles ==
-== Restarting application server ==
+ == Restarting application server ==
OUTPUT
end
end
diff --git a/railties/test/application/configuration_test.rb b/railties/test/application/configuration_test.rb
index 11cd6dd1e6..9b01d42b1e 100644
--- a/railties/test/application/configuration_test.rb
+++ b/railties/test/application/configuration_test.rb
@@ -124,6 +124,18 @@ module ApplicationTests
assert_equal "MyLogger", Rails.application.config.logger.class.name
end
+ test "raises an error if cache does not support recyclable cache keys" do
+ build_app(initializers: true)
+ add_to_env_config "production", "config.cache_store = Class.new {}.new"
+ add_to_env_config "production", "config.active_record.cache_versioning = true"
+
+ error = assert_raise(RuntimeError) do
+ app "production"
+ end
+
+ assert_match(/You're using a cache/, error.message)
+ end
+
test "a renders exception on pending migration" do
add_to_config <<-RUBY
config.active_record.migration_error = :page_load
@@ -298,6 +310,53 @@ module ApplicationTests
assert_equal %w(noop_email).to_set, PostsMailer.instance_variable_get(:@action_methods)
end
+ test "does not eager load attribute methods in development" do
+ app_file "app/models/post.rb", <<-RUBY
+ class Post < ActiveRecord::Base
+ end
+ RUBY
+
+ app_file "config/initializers/active_record.rb", <<-RUBY
+ ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
+ ActiveRecord::Migration.verbose = false
+ ActiveRecord::Schema.define(version: 1) do
+ create_table :posts do |t|
+ t.string :title
+ end
+ end
+ RUBY
+
+ app "development"
+
+ assert_not_includes Post.instance_methods, :title
+ end
+
+ test "eager loads attribute methods in production" do
+ app_file "app/models/post.rb", <<-RUBY
+ class Post < ActiveRecord::Base
+ end
+ RUBY
+
+ app_file "config/initializers/active_record.rb", <<-RUBY
+ ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
+ ActiveRecord::Migration.verbose = false
+ ActiveRecord::Schema.define(version: 1) do
+ create_table :posts do |t|
+ t.string :title
+ end
+ end
+ RUBY
+
+ add_to_config <<-RUBY
+ config.eager_load = true
+ config.cache_classes = true
+ RUBY
+
+ app "production"
+
+ assert_includes Post.instance_methods, :title
+ end
+
test "initialize an eager loaded, cache classes app" do
add_to_config <<-RUBY
config.eager_load = true
diff --git a/railties/test/application/middleware_test.rb b/railties/test/application/middleware_test.rb
index 5efaf841d4..631f5bac7f 100644
--- a/railties/test/application/middleware_test.rb
+++ b/railties/test/application/middleware_test.rb
@@ -25,6 +25,7 @@ module ApplicationTests
boot!
assert_equal [
+ "Webpacker::DevServerProxy",
"Rack::Sendfile",
"ActionDispatch::Static",
"ActionDispatch::Executor",
@@ -56,6 +57,7 @@ module ApplicationTests
boot!
assert_equal [
+ "Webpacker::DevServerProxy",
"Rack::Sendfile",
"ActionDispatch::Static",
"ActionDispatch::Executor",
@@ -138,7 +140,7 @@ module ApplicationTests
add_to_config "config.ssl_options = { redirect: { host: 'example.com' } }"
boot!
- assert_equal [{ redirect: { host: "example.com" } }], Rails.application.middleware.first.args
+ assert_equal [{ redirect: { host: "example.com" } }], Rails.application.middleware[1].args
end
test "removing Active Record omits its middleware" do
@@ -222,30 +224,31 @@ module ApplicationTests
test "insert middleware after" do
add_to_config "config.middleware.insert_after Rack::Sendfile, Rack::Config"
boot!
- assert_equal "Rack::Config", middleware.second
+ assert_equal "Rack::Config", middleware.third
end
test "unshift middleware" do
add_to_config "config.middleware.unshift Rack::Config"
boot!
- assert_equal "Rack::Config", middleware.first
+ assert_equal "Rack::Config", middleware.second
end
test "Rails.cache does not respond to middleware" do
add_to_config "config.cache_store = :memory_store"
boot!
- assert_equal "Rack::Runtime", middleware.fourth
+ assert_equal "Rack::Runtime", middleware.fifth
end
test "Rails.cache does respond to middleware" do
boot!
- assert_equal "Rack::Runtime", middleware.fifth
+ assert_equal "ActiveSupport::Cache::Strategy::LocalCache", middleware.fifth
+ assert_equal "Rack::Runtime", middleware[5]
end
test "insert middleware before" do
add_to_config "config.middleware.insert_before Rack::Sendfile, Rack::Config"
boot!
- assert_equal "Rack::Config", middleware.first
+ assert_equal "Rack::Config", middleware.second
end
test "can't change middleware after it's built" do
diff --git a/railties/test/application/rake/dbs_test.rb b/railties/test/application/rake/dbs_test.rb
index b1038ba5d6..039987ac8c 100644
--- a/railties/test/application/rake/dbs_test.rb
+++ b/railties/test/application/rake/dbs_test.rb
@@ -26,12 +26,13 @@ module ApplicationTests
FileUtils.rm_rf("#{app_path}/config/database.yml")
end
- def db_create_and_drop(expected_database)
+ def db_create_and_drop(expected_database, environment_loaded: true)
Dir.chdir(app_path) do
output = rails("db:create")
assert_match(/Created database/, output)
assert File.exist?(expected_database)
- assert_equal expected_database, ActiveRecord::Base.connection_config[:database]
+ yield if block_given?
+ assert_equal expected_database, ActiveRecord::Base.connection_config[:database] if environment_loaded
output = rails("db:drop")
assert_match(/Dropped database/, output)
assert_not File.exist?(expected_database)
@@ -62,11 +63,16 @@ module ApplicationTests
end
RUBY
- app "development"
-
- assert_equal true, Rails.application.config.read_encrypted_secrets
+ app_file "lib/tasks/check_env.rake", <<-RUBY
+ Rake::Task["db:create"].enhance do
+ File.write("tmp/config_value", Rails.application.config.read_encrypted_secrets)
+ end
+ RUBY
- db_create_and_drop "db/development.sqlite3"
+ db_create_and_drop("db/development.sqlite3", environment_loaded: false) do
+ assert File.exist?("tmp/config_value")
+ assert_equal "true", File.read("tmp/config_value")
+ end
end
def with_database_existing
diff --git a/railties/test/application/rake_test.rb b/railties/test/application/rake_test.rb
index 1522a2bbc5..a0cdae898b 100644
--- a/railties/test/application/rake_test.rb
+++ b/railties/test/application/rake_test.rb
@@ -118,7 +118,7 @@ module ApplicationTests
end
def test_code_statistics_sanity
- assert_match "Code LOC: 25 Test LOC: 0 Code to Test Ratio: 1:0.0",
+ assert_match "Code LOC: 32 Test LOC: 0 Code to Test Ratio: 1:0.0",
rails("stats")
end
@@ -190,6 +190,7 @@ module ApplicationTests
rails "generate", "model", "Cart"
rails "generate", "scaffold", "LineItems", "product:references", "cart:belongs_to"
with_rails_env("test") { rails("db:migrate") }
+ rails("webpacker:compile")
output = rails("test")
assert_match(/7 runs, 9 assertions, 0 failures, 0 errors/, output)
diff --git a/railties/test/commands/credentials_test.rb b/railties/test/commands/credentials_test.rb
index 5b8b9e4eda..7842b0db61 100644
--- a/railties/test/commands/credentials_test.rb
+++ b/railties/test/commands/credentials_test.rb
@@ -55,6 +55,14 @@ class Rails::Command::CredentialsCommandTest < ActiveSupport::TestCase
end
end
+ test "edit command modifies file specified by environment option" do
+ assert_match(/access_key_id: 123/, run_edit_command(environment: "production"))
+ Dir.chdir(app_path) do
+ assert File.exist?("config/credentials/production.key")
+ assert File.exist?("config/credentials/production.yml.enc")
+ end
+ end
+
test "show credentials" do
assert_match(/access_key_id: 123/, run_show_command)
end
@@ -70,17 +78,25 @@ class Rails::Command::CredentialsCommandTest < ActiveSupport::TestCase
remove_file "config/master.key"
add_to_config "config.require_master_key = false"
- assert_match(/Missing master key to decrypt credentials/, run_show_command)
+ assert_match(/Missing 'config\/master\.key' to decrypt credentials/, run_show_command)
+ end
+
+ test "show command displays content specified by environment option" do
+ run_edit_command(environment: "production")
+
+ assert_match(/access_key_id: 123/, run_show_command(environment: "production"))
end
private
- def run_edit_command(editor: "cat")
+ def run_edit_command(editor: "cat", environment: nil, **options)
switch_env("EDITOR", editor) do
- rails "credentials:edit"
+ args = environment ? ["--environment", environment] : []
+ rails "credentials:edit", args, **options
end
end
- def run_show_command(**options)
- rails "credentials:show", **options
+ def run_show_command(environment: nil, **options)
+ args = environment ? ["--environment", environment] : []
+ rails "credentials:show", args, **options
end
end
diff --git a/railties/test/console_helpers.rb b/railties/test/console_helpers.rb
index 8350fce5ee..67f55fdc45 100644
--- a/railties/test/console_helpers.rb
+++ b/railties/test/console_helpers.rb
@@ -9,7 +9,7 @@ module ConsoleHelpers
def assert_output(expected, io, timeout = 10)
timeout = Time.now + timeout
- output = "".dup
+ output = +""
until output.include?(expected) || Time.now > timeout
if IO.select([io], [], [], 0.1)
output << io.read(1)
diff --git a/railties/test/credentials_test.rb b/railties/test/credentials_test.rb
new file mode 100644
index 0000000000..03370e0fc7
--- /dev/null
+++ b/railties/test/credentials_test.rb
@@ -0,0 +1,49 @@
+# frozen_string_literal: true
+
+require "isolation/abstract_unit"
+
+class Rails::CredentialsTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+
+ setup :build_app
+ teardown :teardown_app
+
+ test "reads credentials from environment specific path" do
+ with_credentials do |content, key|
+ Dir.chdir(app_path) do
+ Dir.mkdir("config/credentials")
+ File.write("config/credentials/production.yml.enc", content)
+ File.write("config/credentials/production.key", key)
+ end
+
+ app("production")
+
+ assert_equal "revealed", Rails.application.credentials.mystery
+ end
+ end
+
+ test "reads credentials from customized path and key" do
+ with_credentials do |content, key|
+ Dir.chdir(app_path) do
+ Dir.mkdir("config/credentials")
+ File.write("config/credentials/staging.yml.enc", content)
+ File.write("config/credentials/staging.key", key)
+ end
+
+ add_to_env_config("production", "config.credentials.content_path = config.root.join('config/credentials/staging.yml.enc')")
+ add_to_env_config("production", "config.credentials.key_path = config.root.join('config/credentials/staging.key')")
+ app("production")
+
+ assert_equal "revealed", Rails.application.credentials.mystery
+ end
+ end
+
+ private
+ def with_credentials
+ key = "2117e775dc2024d4f49ddf3aeb585919"
+ # secret_key_base: secret
+ # mystery: revealed
+ content = "vgvKu4MBepIgZ5VHQMMPwnQNsLlWD9LKmJHu3UA/8yj6x+3fNhz3DwL9brX7UA==--qLdxHP6e34xeTAiI--nrcAsleXuo9NqiEuhntAhw=="
+ yield(content, key)
+ end
+end
diff --git a/railties/test/engine/commands_test.rb b/railties/test/engine/commands_test.rb
index 48c93af80c..0e5167578d 100644
--- a/railties/test/engine/commands_test.rb
+++ b/railties/test/engine/commands_test.rb
@@ -24,7 +24,7 @@ class Rails::Engine::CommandsTest < ActiveSupport::TestCase
def test_runner_command_work_inside_engine
output = capture(:stdout) do
- Dir.chdir(plugin_path) { system("bin/rails runner 'puts Rails.env'") }
+ Dir.chdir(plugin_path) { system({ "SKIP_REQUIRE_WEBPACKER" => "true" }, "bin/rails runner 'puts Rails.env'") }
end
assert_equal "test", output.strip
@@ -67,6 +67,7 @@ class Rails::Engine::CommandsTest < ActiveSupport::TestCase
def spawn_command(command, fd)
Process.spawn(
+ { "SKIP_REQUIRE_WEBPACKER" => "true" },
"#{plugin_path}/bin/rails #{command}",
in: fd, out: fd, err: fd
)
diff --git a/railties/test/generators/actions_test.rb b/railties/test/generators/actions_test.rb
index da52b6076a..6d53230eab 100644
--- a/railties/test/generators/actions_test.rb
+++ b/railties/test/generators/actions_test.rb
@@ -125,7 +125,7 @@ class ActionsTest < Rails::Generators::TestCase
def test_gem_works_even_if_frozen_string_is_passed_as_argument
run_generator
- action :gem, "frozen_gem".freeze, "1.0.0".freeze
+ action :gem, -"frozen_gem", -"1.0.0"
assert_file "Gemfile", /^gem 'frozen_gem', '1.0.0'$/
end
diff --git a/railties/test/generators/api_app_generator_test.rb b/railties/test/generators/api_app_generator_test.rb
index c2540f4091..4f2894e71e 100644
--- a/railties/test/generators/api_app_generator_test.rb
+++ b/railties/test/generators/api_app_generator_test.rb
@@ -40,7 +40,6 @@ class ApiAppGeneratorTest < Rails::Generators::TestCase
end
assert_file "Gemfile" do |content|
- assert_no_match(/gem 'coffee-rails'/, content)
assert_no_match(/gem 'sass-rails'/, content)
assert_no_match(/gem 'web-console'/, content)
assert_no_match(/gem 'capybara'/, content)
diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb
index 14cdd8b5c9..cd6bdd11c0 100644
--- a/railties/test/generators/app_generator_test.rb
+++ b/railties/test/generators/app_generator_test.rb
@@ -13,10 +13,9 @@ DEFAULT_APP_FILES = %w(
config.ru
app/assets/config/manifest.js
app/assets/images
- app/assets/javascripts
- app/assets/javascripts/application.js
- app/assets/javascripts/cable.js
- app/assets/javascripts/channels
+ app/javascript
+ app/javascript/channels
+ app/javascript/packs/application.js
app/assets/stylesheets
app/assets/stylesheets/application.css
app/channels/application_cable/channel.rb
@@ -114,12 +113,12 @@ class AppGeneratorTest < Rails::Generators::TestCase
end
def test_assets
- run_generator
+ run_generator [destination_root, "--no-skip-javascript"]
assert_file("app/views/layouts/application.html.erb", /stylesheet_link_tag\s+'application', media: 'all', 'data-turbolinks-track': 'reload'/)
- assert_file("app/views/layouts/application.html.erb", /javascript_include_tag\s+'application', 'data-turbolinks-track': 'reload'/)
+ assert_file("app/views/layouts/application.html.erb", /javascript_pack_tag\s+'application', 'data-turbolinks-track': 'reload'/)
assert_file("app/assets/stylesheets/application.css")
- assert_file("app/assets/javascripts/application.js")
+ assert_file("app/javascript/packs/application.js")
end
def test_application_job_file_present
@@ -216,12 +215,13 @@ class AppGeneratorTest < Rails::Generators::TestCase
def test_new_application_load_defaults
app_root = File.join(destination_root, "myfirstapp")
run_generator [app_root]
+
output = nil
assert_file "#{app_root}/config/application.rb", /\s+config\.load_defaults #{Rails::VERSION::STRING.to_f}/
Dir.chdir(app_root) do
- output = `./bin/rails r "puts Rails.application.config.assets.unknown_asset_fallback"`
+ output = `SKIP_REQUIRE_WEBPACKER=true ./bin/rails r "puts Rails.application.config.assets.unknown_asset_fallback"`
end
assert_equal "false\n", output
@@ -559,7 +559,6 @@ class AppGeneratorTest < Rails::Generators::TestCase
run_generator
assert_gem "sass-rails"
- assert_gem "uglifier"
end
def test_action_cable_redis_gems
@@ -601,49 +600,15 @@ class AppGeneratorTest < Rails::Generators::TestCase
end
end
- def test_inclusion_of_javascript_runtime
- run_generator
- if defined?(JRUBY_VERSION)
- assert_gem "therubyrhino"
- elsif RUBY_PLATFORM =~ /mingw|mswin/
- assert_gem "duktape"
- else
- assert_file "Gemfile", /# gem 'mini_racer', platforms: :ruby/
- end
- end
-
- def test_rails_ujs_is_the_default_ujs_library
- run_generator
- assert_file "app/assets/javascripts/application.js" do |contents|
- assert_match %r{^//= require rails-ujs}, contents
- end
- end
-
def test_javascript_is_skipped_if_required
run_generator [destination_root, "--skip-javascript"]
- assert_no_file "app/assets/javascripts"
+ assert_no_file "app/javascript"
assert_file "app/views/layouts/application.html.erb" do |contents|
assert_match(/stylesheet_link_tag\s+'application', media: 'all' %>/, contents)
assert_no_match(/javascript_include_tag\s+'application' \%>/, contents)
end
-
- assert_no_gem "coffee-rails"
- assert_no_gem "uglifier"
-
- assert_file "config/environments/production.rb" do |content|
- assert_no_match(/config\.assets\.js_compressor = :uglifier/, content)
- end
- end
-
- def test_coffeescript_is_skipped_if_required
- run_generator [destination_root, "--skip-coffee"]
-
- assert_file "Gemfile" do |content|
- assert_no_match(/coffee-rails/, content)
- assert_match(/uglifier/, content)
- end
end
def test_inclusion_of_jbuilder
@@ -819,22 +784,22 @@ class AppGeneratorTest < Rails::Generators::TestCase
assert_no_gem "spring"
end
- def test_webpack_option
+ def test_skip_javascript_option
command_check = -> command, *_ do
@called ||= 0
if command == "webpacker:install"
@called += 1
- assert_equal 1, @called, "webpacker:install expected to be called once, but was called #{@called} times."
+ assert_equal 0, @called, "webpacker:install expected not to be called once, but was called #{@called} times."
end
end
- generator([destination_root], webpack: "webpack").stub(:rails_command, command_check) do
+ generator([destination_root], skip_javascript: true).stub(:rails_command, command_check) do
generator.stub :bundle_command, nil do
quietly { generator.invoke_all }
end
end
- assert_gem "webpacker"
+ assert_no_gem "webpacker"
end
def test_webpack_option_with_js_framework
@@ -856,22 +821,24 @@ class AppGeneratorTest < Rails::Generators::TestCase
quietly { generator.invoke_all }
end
end
+
+ assert_gem "webpacker"
end
def test_generator_if_skip_turbolinks_is_given
- run_generator [destination_root, "--skip-turbolinks"]
+ run_generator [destination_root, "--skip-turbolinks", "--no-skip-javascript"]
assert_no_gem "turbolinks"
assert_file "app/views/layouts/application.html.erb" do |content|
assert_no_match(/data-turbolinks-track/, content)
end
- assert_file "app/assets/javascripts/application.js" do |content|
+ assert_file "app/javascript/packs/application.js" do |content|
assert_no_match(/turbolinks/, content)
end
end
def test_bootsnap
- run_generator
+ run_generator [destination_root, "--no-skip-bootsnap"]
unless defined?(JRUBY_VERSION)
assert_gem "bootsnap"
@@ -964,7 +931,7 @@ class AppGeneratorTest < Rails::Generators::TestCase
def test_after_bundle_callback
path = "http://example.org/rails_template"
- template = %{ after_bundle { run 'echo ran after_bundle' } }.dup
+ template = +%{ after_bundle { run 'echo ran after_bundle' } }
template.instance_eval "def read; self; end" # Make the string respond to read
check_open = -> *args do
@@ -972,7 +939,7 @@ class AppGeneratorTest < Rails::Generators::TestCase
template
end
- sequence = ["git init", "install", "binstubs bundler", "exec spring binstub --all", "echo ran after_bundle"]
+ sequence = ["git init", "install", "binstubs bundler", "exec spring binstub --all", "webpacker:install", "echo ran after_bundle"]
@sequence_step ||= 0
ensure_bundler_first = -> command, options = nil do
assert_equal sequence[@sequence_step], command, "commands should be called in sequence #{sequence}"
@@ -989,7 +956,7 @@ class AppGeneratorTest < Rails::Generators::TestCase
end
end
- assert_equal 5, @sequence_step
+ assert_equal 6, @sequence_step
end
def test_gitignore
diff --git a/railties/test/generators/assets_generator_test.rb b/railties/test/generators/assets_generator_test.rb
index 3cec41dbf8..83d2429acf 100644
--- a/railties/test/generators/assets_generator_test.rb
+++ b/railties/test/generators/assets_generator_test.rb
@@ -9,13 +9,11 @@ class AssetsGeneratorTest < Rails::Generators::TestCase
def test_assets
run_generator
- assert_file "app/assets/javascripts/posts.js"
assert_file "app/assets/stylesheets/posts.css"
end
def test_skipping_assets
- run_generator ["posts", "--no-stylesheets", "--no-javascripts"]
- assert_no_file "app/assets/javascripts/posts.js"
+ run_generator ["posts", "--no-stylesheets"]
assert_no_file "app/assets/stylesheets/posts.css"
end
end
diff --git a/railties/test/generators/channel_generator_test.rb b/railties/test/generators/channel_generator_test.rb
index e543cc11b8..1cb8465539 100644
--- a/railties/test/generators/channel_generator_test.rb
+++ b/railties/test/generators/channel_generator_test.rb
@@ -26,8 +26,8 @@ class ChannelGeneratorTest < Rails::Generators::TestCase
assert_match(/class ChatChannel < ApplicationCable::Channel/, channel)
end
- assert_file "app/assets/javascripts/channels/chat.js" do |channel|
- assert_match(/App\.chat = App\.cable\.subscriptions\.create\("ChatChannel/, channel)
+ assert_file "app/javascript/channels/chat_channel.js" do |channel|
+ assert_match(/import consumer from "\.\/consumer"\s+consumer\.subscriptions\.create\("ChatChannel/, channel)
end
end
@@ -40,8 +40,8 @@ class ChannelGeneratorTest < Rails::Generators::TestCase
assert_match(/def mute/, channel)
end
- assert_file "app/assets/javascripts/channels/chat.js" do |channel|
- assert_match(/App\.chat = App\.cable\.subscriptions\.create\("ChatChannel/, channel)
+ assert_file "app/javascript/channels/chat_channel.js" do |channel|
+ assert_match(/import consumer from "\.\/consumer"\s+consumer\.subscriptions\.create\("ChatChannel/, channel)
assert_match(/,\n\n speak/, channel)
assert_match(/,\n\n mute: function\(\) \{\n return this\.perform\('mute'\);\n \}\n\}\);/, channel)
end
@@ -54,15 +54,17 @@ class ChannelGeneratorTest < Rails::Generators::TestCase
assert_match(/class ChatChannel < ApplicationCable::Channel/, channel)
end
- assert_no_file "app/assets/javascripts/channels/chat.js"
+ assert_no_file "app/javascript/channels/chat_channel.js"
end
- def test_cable_js_is_created_if_not_present_already
+ def test_consumer_js_is_created_if_not_present_already
run_generator ["chat"]
- FileUtils.rm("#{destination_root}/app/assets/javascripts/cable.js")
+ FileUtils.rm("#{destination_root}/app/javascript/channels/index.js")
+ FileUtils.rm("#{destination_root}/app/javascript/channels/consumer.js")
run_generator ["camp"]
- assert_file "app/assets/javascripts/cable.js"
+ assert_file "app/javascript/channels/index.js"
+ assert_file "app/javascript/channels/consumer.js"
end
def test_channel_on_revoke
@@ -70,11 +72,12 @@ class ChannelGeneratorTest < Rails::Generators::TestCase
run_generator ["chat"], behavior: :revoke
assert_no_file "app/channels/chat_channel.rb"
- assert_no_file "app/assets/javascripts/channels/chat.js"
+ assert_no_file "app/javascript/channels/chat_channel.js"
assert_file "app/channels/application_cable/channel.rb"
assert_file "app/channels/application_cable/connection.rb"
- assert_file "app/assets/javascripts/cable.js"
+ assert_file "app/javascript/channels/index.js"
+ assert_file "app/javascript/channels/consumer.js"
end
def test_channel_suffix_is_not_duplicated
@@ -83,7 +86,7 @@ class ChannelGeneratorTest < Rails::Generators::TestCase
assert_no_file "app/channels/chat_channel_channel.rb"
assert_file "app/channels/chat_channel.rb"
- assert_no_file "app/assets/javascripts/channels/chat_channel.js"
- assert_file "app/assets/javascripts/channels/chat.js"
+ assert_no_file "app/javascript/channels/chat_channel_channel.js"
+ assert_file "app/javascript/channels/chat_channel.js"
end
end
diff --git a/railties/test/generators/controller_generator_test.rb b/railties/test/generators/controller_generator_test.rb
index 021004c9b8..8786756c68 100644
--- a/railties/test/generators/controller_generator_test.rb
+++ b/railties/test/generators/controller_generator_test.rb
@@ -39,13 +39,11 @@ class ControllerGeneratorTest < Rails::Generators::TestCase
def test_invokes_assets
run_generator
- assert_file "app/assets/javascripts/account.js"
assert_file "app/assets/stylesheets/account.css"
end
def test_does_not_invoke_assets_if_required
run_generator ["account", "--skip-assets"]
- assert_no_file "app/assets/javascripts/account.js"
assert_no_file "app/assets/stylesheets/account.css"
end
@@ -132,9 +130,6 @@ class ControllerGeneratorTest < Rails::Generators::TestCase
assert_no_file "app/helpers/account_controller_helper.rb"
assert_file "app/helpers/account_helper.rb"
- assert_no_file "app/assets/javascripts/account_controller.js"
- assert_file "app/assets/javascripts/account.js"
-
assert_no_file "app/assets/stylesheets/account_controller.css"
assert_file "app/assets/stylesheets/account.css"
end
diff --git a/railties/test/generators/generators_test_helper.rb b/railties/test/generators/generators_test_helper.rb
index ad2a55f496..25d5dba1d8 100644
--- a/railties/test/generators/generators_test_helper.rb
+++ b/railties/test/generators/generators_test_helper.rb
@@ -42,6 +42,21 @@ module GeneratorsTestHelper
end
end
+ def with_secondary_database_configuration
+ original_configurations = ActiveRecord::Base.configurations
+ ActiveRecord::Base.configurations = {
+ test: {
+ secondary: {
+ database: "db/secondary.sqlite3",
+ migrations_paths: "db/secondary_migrate",
+ },
+ },
+ }
+ yield
+ ensure
+ ActiveRecord::Base.configurations = original_configurations
+ end
+
def copy_routes
routes = File.expand_path("../../lib/rails/generators/rails/app/templates/config/routes.rb.tt", __dir__)
destination = File.join(destination_root, "config")
diff --git a/railties/test/generators/migration_generator_test.rb b/railties/test/generators/migration_generator_test.rb
index 5c57d607fc..5812cbdfc9 100644
--- a/railties/test/generators/migration_generator_test.rb
+++ b/railties/test/generators/migration_generator_test.rb
@@ -254,11 +254,13 @@ class MigrationGeneratorTest < Rails::Generators::TestCase
end
end
- def test_migrations_paths_puts_migrations_in_that_folder
- run_generator ["create_books", "--migrations_paths=db/test_migrate"]
- assert_migration "db/test_migrate/create_books.rb" do |content|
- assert_method :change, content do |change|
- assert_match(/create_table :books/, change)
+ def test_database_puts_migrations_in_configured_folder
+ with_secondary_database_configuration do
+ run_generator ["create_books", "--database=secondary"]
+ assert_migration "db/secondary_migrate/create_books.rb" do |content|
+ assert_method :change, content do |change|
+ assert_match(/create_table :books/, change)
+ end
end
end
end
diff --git a/railties/test/generators/model_generator_test.rb b/railties/test/generators/model_generator_test.rb
index 7febdfae96..b06db6dd8a 100644
--- a/railties/test/generators/model_generator_test.rb
+++ b/railties/test/generators/model_generator_test.rb
@@ -392,6 +392,17 @@ class ModelGeneratorTest < Rails::Generators::TestCase
end
end
+ def test_database_puts_migrations_in_configured_folder
+ with_secondary_database_configuration do
+ run_generator ["account", "--database=secondary"]
+ assert_migration "db/secondary_migrate/create_accounts.rb" do |content|
+ assert_method :change, content do |change|
+ assert_match(/create_table :accounts/, change)
+ end
+ end
+ end
+ end
+
def test_required_belongs_to_adds_required_association
run_generator ["account", "supplier:references{required}"]
diff --git a/railties/test/generators/plugin_generator_test.rb b/railties/test/generators/plugin_generator_test.rb
index 20acd23329..66286fc554 100644
--- a/railties/test/generators/plugin_generator_test.rb
+++ b/railties/test/generators/plugin_generator_test.rb
@@ -140,10 +140,6 @@ class PluginGeneratorTest < Rails::Generators::TestCase
run_generator
assert_file "test/dummy/app/assets/stylesheets/application.css"
-
- assert_file "test/dummy/app/assets/javascripts/application.js" do |contents|
- assert_no_match(/jquery/, contents)
- end
end
def test_ensure_that_plugin_options_are_not_passed_to_app_generator
@@ -210,28 +206,10 @@ class PluginGeneratorTest < Rails::Generators::TestCase
assert_no_file "#{destination_root}/Gemfile.lock"
end
- def test_skipping_javascripts_without_mountable_option
- run_generator
- assert_no_file "app/assets/javascripts/bukkits/application.js"
- end
-
- def test_javascripts_generation
- run_generator [destination_root, "--mountable"]
- assert_file "app/assets/javascripts/bukkits/application.js" do |content|
- assert_match "//= require rails-ujs", content
- assert_match "//= require activestorage", content
- assert_match "//= require_tree .", content
- end
- assert_file "app/views/layouts/bukkits/application.html.erb" do |content|
- assert_match "javascript_include_tag", content
- end
- end
-
- def test_skip_javascripts
+ def test_skip_javascript
run_generator [destination_root, "--skip-javascript", "--mountable"]
- assert_no_file "app/assets/javascripts/bukkits/application.js"
assert_file "app/views/layouts/bukkits/application.html.erb" do |content|
- assert_no_match "javascript_include_tag", content
+ assert_no_match "javascript_pack_tag", content
end
end
@@ -264,7 +242,6 @@ class PluginGeneratorTest < Rails::Generators::TestCase
def test_creating_engine_in_full_mode
run_generator [destination_root, "--full"]
- assert_file "app/assets/javascripts/bukkits"
assert_file "app/assets/stylesheets/bukkits"
assert_file "app/assets/images/bukkits"
assert_file "app/models"
@@ -280,7 +257,7 @@ class PluginGeneratorTest < Rails::Generators::TestCase
def test_creating_engine_with_hyphenated_name_in_full_mode
run_generator [File.join(destination_root, "hyphenated-name"), "--full"]
- assert_file "hyphenated-name/app/assets/javascripts/hyphenated/name"
+ assert_no_file "hyphenated-name/app/assets/javascripts/hyphenated/name"
assert_file "hyphenated-name/app/assets/stylesheets/hyphenated/name"
assert_file "hyphenated-name/app/assets/images/hyphenated/name"
assert_file "hyphenated-name/app/models"
@@ -297,7 +274,7 @@ class PluginGeneratorTest < Rails::Generators::TestCase
def test_creating_engine_with_hyphenated_and_underscored_name_in_full_mode
run_generator [File.join(destination_root, "my_hyphenated-name"), "--full"]
- assert_file "my_hyphenated-name/app/assets/javascripts/my_hyphenated/name"
+ assert_no_file "my_hyphenated-name/app/assets/javascripts/my_hyphenated/name"
assert_file "my_hyphenated-name/app/assets/stylesheets/my_hyphenated/name"
assert_file "my_hyphenated-name/app/assets/images/my_hyphenated/name"
assert_file "my_hyphenated-name/app/models"
@@ -318,7 +295,7 @@ class PluginGeneratorTest < Rails::Generators::TestCase
def test_create_mountable_application_with_mountable_option
run_generator [destination_root, "--mountable"]
- assert_file "app/assets/javascripts/bukkits"
+ assert_no_file "app/assets/javascripts/bukkits"
assert_file "app/assets/stylesheets/bukkits"
assert_file "app/assets/images/bukkits"
assert_file "config/routes.rb", /Bukkits::Engine\.routes\.draw do/
@@ -334,7 +311,7 @@ class PluginGeneratorTest < Rails::Generators::TestCase
assert_match "<%= csrf_meta_tags %>", contents
assert_match "<%= csp_meta_tag %>", contents
assert_match(/stylesheet_link_tag\s+['"]bukkits\/application['"]/, contents)
- assert_match(/javascript_include_tag\s+['"]bukkits\/application['"]/, contents)
+ assert_no_match(/javascript_include_tag\s+['"]bukkits\/application['"]/, contents)
assert_match "<%= yield %>", contents
end
assert_file "test/test_helper.rb" do |content|
@@ -348,7 +325,7 @@ class PluginGeneratorTest < Rails::Generators::TestCase
def test_create_mountable_application_with_mountable_option_and_hypenated_name
run_generator [File.join(destination_root, "hyphenated-name"), "--mountable"]
- assert_file "hyphenated-name/app/assets/javascripts/hyphenated/name"
+ assert_no_file "hyphenated-name/app/assets/javascripts/hyphenated/name"
assert_file "hyphenated-name/app/assets/stylesheets/hyphenated/name"
assert_file "hyphenated-name/app/assets/images/hyphenated/name"
assert_file "hyphenated-name/config/routes.rb", /Hyphenated::Name::Engine\.routes\.draw do/
@@ -364,13 +341,13 @@ class PluginGeneratorTest < Rails::Generators::TestCase
assert_file "hyphenated-name/app/views/layouts/hyphenated/name/application.html.erb" do |contents|
assert_match "<title>Hyphenated name</title>", contents
assert_match(/stylesheet_link_tag\s+['"]hyphenated\/name\/application['"]/, contents)
- assert_match(/javascript_include_tag\s+['"]hyphenated\/name\/application['"]/, contents)
+ assert_no_match(/javascript_include_tag\s+['"]hyphenated\/name\/application['"]/, contents)
end
end
def test_create_mountable_application_with_mountable_option_and_hypenated_and_underscored_name
run_generator [File.join(destination_root, "my_hyphenated-name"), "--mountable"]
- assert_file "my_hyphenated-name/app/assets/javascripts/my_hyphenated/name"
+ assert_no_file "my_hyphenated-name/app/assets/javascripts/my_hyphenated/name"
assert_file "my_hyphenated-name/app/assets/stylesheets/my_hyphenated/name"
assert_file "my_hyphenated-name/app/assets/images/my_hyphenated/name"
assert_file "my_hyphenated-name/config/routes.rb", /MyHyphenated::Name::Engine\.routes\.draw do/
@@ -386,13 +363,13 @@ class PluginGeneratorTest < Rails::Generators::TestCase
assert_file "my_hyphenated-name/app/views/layouts/my_hyphenated/name/application.html.erb" do |contents|
assert_match "<title>My hyphenated name</title>", contents
assert_match(/stylesheet_link_tag\s+['"]my_hyphenated\/name\/application['"]/, contents)
- assert_match(/javascript_include_tag\s+['"]my_hyphenated\/name\/application['"]/, contents)
+ assert_no_match(/javascript_include_tag\s+['"]my_hyphenated\/name\/application['"]/, contents)
end
end
def test_create_mountable_application_with_mountable_option_and_multiple_hypenates_in_name
run_generator [File.join(destination_root, "deep-hyphenated-name"), "--mountable"]
- assert_file "deep-hyphenated-name/app/assets/javascripts/deep/hyphenated/name"
+ assert_no_file "deep-hyphenated-name/app/assets/javascripts/deep/hyphenated/name"
assert_file "deep-hyphenated-name/app/assets/stylesheets/deep/hyphenated/name"
assert_file "deep-hyphenated-name/app/assets/images/deep/hyphenated/name"
assert_file "deep-hyphenated-name/config/routes.rb", /Deep::Hyphenated::Name::Engine\.routes\.draw do/
@@ -408,7 +385,7 @@ class PluginGeneratorTest < Rails::Generators::TestCase
assert_file "deep-hyphenated-name/app/views/layouts/deep/hyphenated/name/application.html.erb" do |contents|
assert_match "<title>Deep hyphenated name</title>", contents
assert_match(/stylesheet_link_tag\s+['"]deep\/hyphenated\/name\/application['"]/, contents)
- assert_match(/javascript_include_tag\s+['"]deep\/hyphenated\/name\/application['"]/, contents)
+ assert_no_match(/javascript_include_tag\s+['"]deep\/hyphenated\/name\/application['"]/, contents)
end
end
@@ -737,7 +714,7 @@ class PluginGeneratorTest < Rails::Generators::TestCase
def test_after_bundle_callback
path = "http://example.org/rails_template"
- template = %{ after_bundle { run "echo ran after_bundle" } }.dup
+ template = +%{ after_bundle { run "echo ran after_bundle" } }
template.instance_eval "def read; self; end" # Make the string respond to read
check_open = -> *args do
diff --git a/railties/test/generators/resource_generator_test.rb b/railties/test/generators/resource_generator_test.rb
index 7a470d0d91..b99b4baf6b 100644
--- a/railties/test/generators/resource_generator_test.rb
+++ b/railties/test/generators/resource_generator_test.rb
@@ -65,7 +65,7 @@ class ResourceGeneratorTest < Rails::Generators::TestCase
end
def test_plural_names_are_singularized
- content = run_generator ["accounts".freeze]
+ content = run_generator ["accounts"]
assert_file "app/models/account.rb", /class Account < ApplicationRecord/
assert_file "test/models/account_test.rb", /class AccountTest/
assert_match(/\[WARNING\] The model name 'accounts' was recognized as a plural, using the singular 'account' instead\. Override with --force-plural or setup custom inflection rules for this noun before running the generator\./, content)
@@ -79,7 +79,7 @@ class ResourceGeneratorTest < Rails::Generators::TestCase
end
def test_mass_nouns_do_not_throw_warnings
- content = run_generator ["sheep".freeze]
+ content = run_generator ["sheep"]
assert_no_match(/\[WARNING\]/, content)
end
diff --git a/railties/test/generators/scaffold_generator_test.rb b/railties/test/generators/scaffold_generator_test.rb
index e90834bc2b..21b5013484 100644
--- a/railties/test/generators/scaffold_generator_test.rb
+++ b/railties/test/generators/scaffold_generator_test.rb
@@ -93,7 +93,6 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase
# Assets
assert_file "app/assets/stylesheets/scaffold.css"
- assert_file "app/assets/javascripts/product_lines.js"
assert_file "app/assets/stylesheets/product_lines.css"
end
@@ -166,7 +165,6 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase
# Assets
assert_no_file "app/assets/stylesheets/scaffold.css"
- assert_no_file "app/assets/javascripts/product_lines.js"
assert_no_file "app/assets/stylesheets/product_lines.css"
end
@@ -222,7 +220,6 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase
# Assets
assert_file "app/assets/stylesheets/scaffold.css", /:visited/
- assert_no_file "app/assets/javascripts/product_lines.js"
assert_no_file "app/assets/stylesheets/product_lines.css"
end
@@ -299,7 +296,6 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase
# Assets
assert_file "app/assets/stylesheets/scaffold.css", /:visited/
- assert_file "app/assets/javascripts/admin/roles.js"
assert_file "app/assets/stylesheets/admin/roles.css"
end
@@ -335,7 +331,6 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase
# Assets
assert_file "app/assets/stylesheets/scaffold.css"
- assert_no_file "app/assets/javascripts/admin/roles.js"
assert_no_file "app/assets/stylesheets/admin/roles.css"
end
@@ -380,28 +375,24 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase
def test_scaffold_generator_no_assets_with_switch_no_assets
run_generator [ "posts", "--no-assets" ]
assert_no_file "app/assets/stylesheets/scaffold.css"
- assert_no_file "app/assets/javascripts/posts.js"
assert_no_file "app/assets/stylesheets/posts.css"
end
def test_scaffold_generator_no_assets_with_switch_assets_false
run_generator [ "posts", "--assets=false" ]
assert_no_file "app/assets/stylesheets/scaffold.css"
- assert_no_file "app/assets/javascripts/posts.js"
assert_no_file "app/assets/stylesheets/posts.css"
end
def test_scaffold_generator_no_scaffold_stylesheet_with_switch_no_scaffold_stylesheet
run_generator [ "posts", "--no-scaffold-stylesheet" ]
assert_no_file "app/assets/stylesheets/scaffold.css"
- assert_file "app/assets/javascripts/posts.js"
assert_file "app/assets/stylesheets/posts.css"
end
def test_scaffold_generator_no_scaffold_stylesheet_with_switch_scaffold_stylesheet_false
run_generator [ "posts", "--scaffold-stylesheet=false" ]
assert_no_file "app/assets/stylesheets/scaffold.css"
- assert_file "app/assets/javascripts/posts.js"
assert_file "app/assets/stylesheets/posts.css"
end
@@ -429,17 +420,9 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase
def test_scaffold_generator_no_stylesheets
run_generator [ "posts", "--no-stylesheets" ]
assert_no_file "app/assets/stylesheets/scaffold.css"
- assert_file "app/assets/javascripts/posts.js"
assert_no_file "app/assets/stylesheets/posts.css"
end
- def test_scaffold_generator_no_javascripts
- run_generator [ "posts", "--no-javascripts" ]
- assert_file "app/assets/stylesheets/scaffold.css"
- assert_no_file "app/assets/javascripts/posts.js"
- assert_file "app/assets/stylesheets/posts.css"
- end
-
def test_scaffold_generator_outputs_error_message_on_missing_attribute_type
run_generator ["post", "title", "body:text", "author"]
@@ -476,6 +459,14 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase
end
end
+ def test_scaffold_generator_database
+ with_secondary_database_configuration do
+ run_generator ["posts", "--database=secondary"]
+
+ assert_migration "db/secondary_migrate/create_posts.rb"
+ end
+ end
+
def test_scaffold_generator_password_digest
run_generator ["user", "name", "password:digest"]
@@ -622,7 +613,6 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase
assert File.exist?("app/helpers/bukkits/users_helper.rb")
- assert File.exist?("app/assets/javascripts/bukkits/users.js")
assert File.exist?("app/assets/stylesheets/bukkits/users.css")
end
end
@@ -652,7 +642,6 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase
assert_not File.exist?("app/helpers/bukkits/users_helper.rb")
- assert_not File.exist?("app/assets/javascripts/bukkits/users.js")
assert_not File.exist?("app/assets/stylesheets/bukkits/users.css")
end
end
diff --git a/railties/test/generators/shared_generator_tests.rb b/railties/test/generators/shared_generator_tests.rb
index aa577e4234..b766fa1a71 100644
--- a/railties/test/generators/shared_generator_tests.rb
+++ b/railties/test/generators/shared_generator_tests.rb
@@ -27,7 +27,7 @@ module SharedGeneratorTests
end
def test_skeleton_is_created
- run_generator
+ run_generator [destination_root, "--no-skip-javascript"]
default_files.each { |path| assert_file path }
end
@@ -83,7 +83,7 @@ module SharedGeneratorTests
def test_template_is_executed_when_supplied_an_https_path
path = "https://gist.github.com/josevalim/103208/raw/"
- template = %{ say "It works!" }.dup
+ template = +%{ say "It works!" }
template.instance_eval "def read; self; end" # Make the string respond to read
check_open = -> *args do
@@ -196,10 +196,12 @@ module SharedGeneratorTests
end
def test_generator_for_active_storage
- run_generator
+ run_generator [destination_root, "--no-skip-javascript"]
- assert_file "#{application_path}/app/assets/javascripts/application.js" do |content|
- assert_match(/^\/\/= require activestorage/, content)
+ unless generator_class.name == "Rails::Generators::PluginGenerator"
+ assert_file "#{application_path}/app/javascript/packs/application.js" do |content|
+ assert_match(/^import \* as ActiveStorage from "activestorage"\nActiveStorage.start\(\)/, content)
+ end
end
assert_file "#{application_path}/config/environments/development.rb" do |content|
@@ -224,11 +226,11 @@ module SharedGeneratorTests
end
def test_generator_if_skip_active_storage_is_given
- run_generator [destination_root, "--skip-active-storage"]
+ run_generator [destination_root, "--skip-active-storage", "--no-skip-javascript"]
assert_file "#{application_path}/config/application.rb", /#\s+require\s+["']active_storage\/engine["']/
- assert_file "#{application_path}/app/assets/javascripts/application.js" do |content|
+ assert_file "#{application_path}/app/javascript/packs/application.js" do |content|
assert_no_match(/^\/\/= require activestorage/, content)
end
@@ -254,12 +256,12 @@ module SharedGeneratorTests
end
def test_generator_does_not_generate_active_storage_contents_if_skip_active_record_is_given
- run_generator [destination_root, "--skip-active-record"]
+ run_generator [destination_root, "--skip-active-record", "--no-skip-javascript"]
assert_file "#{application_path}/config/application.rb", /#\s+require\s+["']active_storage\/engine["']/
- assert_file "#{application_path}/app/assets/javascripts/application.js" do |content|
- assert_no_match(/^\/\/= require activestorage/, content)
+ assert_file "#{application_path}/app/javascript/packs/application.js" do |content|
+ assert_no_match(/^import * as ActiveStorage from "activestorage"\nActiveStorage.start()/, content)
end
assert_file "#{application_path}/config/environments/development.rb" do |content|
@@ -303,8 +305,8 @@ module SharedGeneratorTests
run_generator [destination_root, "--skip-action-cable"]
assert_file "#{application_path}/config/application.rb", /#\s+require\s+["']action_cable\/engine["']/
assert_no_file "#{application_path}/config/cable.yml"
- assert_no_file "#{application_path}/app/assets/javascripts/cable.js"
- assert_no_directory "#{application_path}/app/assets/javascripts/channels"
+ assert_no_file "#{application_path}/app/javascript/consumer.js"
+ assert_no_directory "#{application_path}/app/javascript/channels"
assert_no_directory "#{application_path}/app/channels"
assert_file "Gemfile" do |content|
assert_no_match(/redis/, content)
@@ -320,8 +322,6 @@ module SharedGeneratorTests
assert_file "Gemfile" do |content|
assert_no_match(/sass-rails/, content)
- assert_no_match(/uglifier/, content)
- assert_no_match(/coffee-rails/, content)
end
assert_file "#{application_path}/config/environments/development.rb" do |content|
@@ -330,7 +330,6 @@ module SharedGeneratorTests
assert_file "#{application_path}/config/environments/production.rb" do |content|
assert_no_match(/config\.assets\.digest/, content)
- assert_no_match(/config\.assets\.js_compressor/, content)
assert_no_match(/config\.assets\.css_compressor/, content)
assert_no_match(/config\.assets\.compile/, content)
end
diff --git a/railties/test/isolation/abstract_unit.rb b/railties/test/isolation/abstract_unit.rb
index 96d21b3ae2..e44f21380e 100644
--- a/railties/test/isolation/abstract_unit.rb
+++ b/railties/test/isolation/abstract_unit.rb
@@ -30,11 +30,11 @@ require "rails/secrets"
module TestHelpers
module Paths
def app_template_path
- File.join Dir.tmpdir, "app_template"
+ File.join RAILS_FRAMEWORK_ROOT, "tmp/templates/app_template"
end
def tmp_path(*args)
- @tmp_path ||= File.realpath(Dir.mktmpdir)
+ @tmp_path ||= File.realpath(Dir.mktmpdir(nil, File.join(RAILS_FRAMEWORK_ROOT, "tmp")))
File.join(@tmp_path, *args)
end
@@ -71,7 +71,7 @@ module TestHelpers
end
def extract_body(response)
- "".dup.tap do |body|
+ (+"").tap do |body|
response[2].each { |chunk| body << chunk }
end
end
@@ -469,17 +469,28 @@ Module.new do
# Build a rails app
FileUtils.rm_rf(app_template_path)
- FileUtils.mkdir(app_template_path)
+ FileUtils.mkdir_p(app_template_path)
+
+ Dir.chdir "#{RAILS_FRAMEWORK_ROOT}/actionview" do
+ `yarn build`
+ end
`#{Gem.ruby} #{RAILS_FRAMEWORK_ROOT}/railties/exe/rails new #{app_template_path} --skip-gemfile --skip-listen --no-rc`
File.open("#{app_template_path}/config/boot.rb", "w") do |f|
f.puts "require 'rails/all'"
end
+ Dir.chdir(app_template_path) { `yarn add https://github.com/rails/webpacker.git` } # Use the latest version.
+
+ # Manually install `webpack` as bin symlinks are not created for subdependencies
+ # in workspaces. See https://github.com/yarnpkg/yarn/issues/4964
+ Dir.chdir(app_template_path) { `yarn add webpack@4.17.1 --tilde` }
+ Dir.chdir(app_template_path) { `yarn add webpack-cli` }
+
# Fake 'Bundler.require' -- we run using the repo's Gemfile, not an
# app-specific one: we don't want to require every gem that lists.
contents = File.read("#{app_template_path}/config/application.rb")
- contents.sub!(/^Bundler\.require.*/, "%w(turbolinks).each { |r| require r }")
+ contents.sub!(/^Bundler\.require.*/, "%w(turbolinks webpacker).each { |r| require r }")
File.write("#{app_template_path}/config/application.rb", contents)
require "rails"
diff --git a/railties/test/rack_logger_test.rb b/railties/test/rack_logger_test.rb
index 6e8f333e1d..ac37062e6d 100644
--- a/railties/test/rack_logger_test.rb
+++ b/railties/test/rack_logger_test.rb
@@ -56,7 +56,7 @@ module Rails
end
def test_notification
- logger = TestLogger.new {}
+ logger = TestLogger.new { }
assert_difference("subscriber.starts.length") do
assert_difference("subscriber.finishes.length") do