aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.rubocop.yml22
-rw-r--r--.travis.yml52
-rw-r--r--Gemfile13
-rw-r--r--Gemfile.lock151
-rw-r--r--RELEASING_RAILS.md16
-rw-r--r--actioncable/CHANGELOG.md10
-rw-r--r--actioncable/README.md2
-rw-r--r--actioncable/Rakefile3
-rw-r--r--actioncable/actioncable.gemspec2
-rw-r--r--actioncable/lib/action_cable/channel/base.rb27
-rw-r--r--actioncable/lib/action_cable/channel/naming.rb2
-rw-r--r--actioncable/lib/action_cable/connection/base.rb21
-rw-r--r--actioncable/lib/action_cable/connection/message_buffer.rb2
-rw-r--r--actioncable/lib/action_cable/connection/subscriptions.rb2
-rw-r--r--actioncable/lib/action_cable/connection/tagged_logger_proxy.rb4
-rw-r--r--actioncable/lib/action_cable/connection/web_socket.rb2
-rw-r--r--actioncable/lib/action_cable/engine.rb2
-rw-r--r--actioncable/lib/action_cable/helpers/action_cable_helper.rb2
-rw-r--r--actioncable/lib/action_cable/remote_connections.rb2
-rw-r--r--actioncable/lib/action_cable/subscription_adapter/evented_redis.rb4
-rw-r--r--actioncable/lib/action_cable/subscription_adapter/subscriber_map.rb2
-rw-r--r--actioncable/lib/rails/generators/channel/USAGE4
-rw-r--r--actioncable/lib/rails/generators/channel/channel_generator.rb2
-rw-r--r--actioncable/test/channel/periodic_timers_test.rb2
-rw-r--r--actioncable/test/client_test.rb22
-rw-r--r--actioncable/test/connection/identifier_test.rb2
-rw-r--r--actioncable/test/connection/multiple_identifiers_test.rb2
-rw-r--r--actioncable/test/connection/string_identifier_test.rb2
-rw-r--r--actioncable/test/javascript/src/test_helpers/consumer_test_helper.coffee10
-rw-r--r--actioncable/test/javascript/src/test_helpers/index.coffee3
-rw-r--r--actioncable/test/javascript/src/unit/subscription_test.coffee40
-rw-r--r--actioncable/test/javascript/src/unit/subscriptions_test.coffee25
-rw-r--r--actioncable/test/stubs/room.rb2
-rw-r--r--actioncable/test/subscription_adapter/evented_redis_test.rb26
-rw-r--r--actioncable/test/worker_test.rb2
-rwxr-xr-xactionmailer/bin/test2
-rw-r--r--actionmailer/lib/action_mailer/base.rb24
-rw-r--r--actionmailer/lib/action_mailer/delivery_methods.rb4
-rw-r--r--actionmailer/lib/action_mailer/inline_preview_interceptor.rb2
-rw-r--r--actionmailer/lib/action_mailer/message_delivery.rb6
-rw-r--r--actionmailer/lib/action_mailer/preview.rb10
-rw-r--r--actionmailer/lib/action_mailer/railtie.rb4
-rw-r--r--actionmailer/lib/action_mailer/test_case.rb14
-rw-r--r--actionmailer/lib/rails/generators/mailer/mailer_generator.rb5
-rw-r--r--actionmailer/test/base_test.rb2
-rw-r--r--actionmailer/test/delivery_methods_test.rb2
-rw-r--r--actionmailer/test/i18n_with_controller_test.rb2
-rw-r--r--actionmailer/test/mail_helper_test.rb2
-rw-r--r--actionmailer/test/mailers/base_mailer.rb6
-rw-r--r--actionmailer/test/message_delivery_test.rb2
-rw-r--r--actionmailer/test/url_test.rb8
-rw-r--r--actionpack/CHANGELOG.md48
-rwxr-xr-xactionpack/bin/test2
-rw-r--r--actionpack/lib/abstract_controller/base.rb8
-rw-r--r--actionpack/lib/abstract_controller/caching.rb4
-rw-r--r--actionpack/lib/abstract_controller/collector.rb2
-rw-r--r--actionpack/lib/abstract_controller/rendering.rb4
-rw-r--r--actionpack/lib/action_controller/metal.rb4
-rw-r--r--actionpack/lib/action_controller/metal/conditional_get.rb2
-rw-r--r--actionpack/lib/action_controller/metal/data_streaming.rb9
-rw-r--r--actionpack/lib/action_controller/metal/etag_with_template_digest.rb2
-rw-r--r--actionpack/lib/action_controller/metal/exceptions.rb2
-rw-r--r--actionpack/lib/action_controller/metal/flash.rb2
-rw-r--r--actionpack/lib/action_controller/metal/force_ssl.rb2
-rw-r--r--actionpack/lib/action_controller/metal/http_authentication.rb10
-rw-r--r--actionpack/lib/action_controller/metal/instrumentation.rb6
-rw-r--r--actionpack/lib/action_controller/metal/live.rb2
-rw-r--r--actionpack/lib/action_controller/metal/parameter_encoding.rb39
-rw-r--r--actionpack/lib/action_controller/metal/params_wrapper.rb4
-rw-r--r--actionpack/lib/action_controller/metal/redirecting.rb8
-rw-r--r--actionpack/lib/action_controller/metal/renderers.rb2
-rw-r--r--actionpack/lib/action_controller/metal/rendering.rb8
-rw-r--r--actionpack/lib/action_controller/metal/request_forgery_protection.rb46
-rw-r--r--actionpack/lib/action_controller/metal/streaming.rb6
-rw-r--r--actionpack/lib/action_controller/metal/strong_parameters.rb68
-rw-r--r--actionpack/lib/action_controller/railtie.rb2
-rw-r--r--actionpack/lib/action_controller/renderer.rb6
-rw-r--r--actionpack/lib/action_controller/test_case.rb8
-rw-r--r--actionpack/lib/action_dispatch/http/filter_parameters.rb10
-rw-r--r--actionpack/lib/action_dispatch/http/mime_negotiation.rb12
-rw-r--r--actionpack/lib/action_dispatch/http/mime_type.rb4
-rw-r--r--actionpack/lib/action_dispatch/http/parameter_filter.rb2
-rw-r--r--actionpack/lib/action_dispatch/http/parameters.rb15
-rw-r--r--actionpack/lib/action_dispatch/http/request.rb2
-rw-r--r--actionpack/lib/action_dispatch/http/response.rb10
-rw-r--r--actionpack/lib/action_dispatch/http/upload.rb6
-rw-r--r--actionpack/lib/action_dispatch/http/url.rb4
-rw-r--r--actionpack/lib/action_dispatch/journey/formatter.rb12
-rw-r--r--actionpack/lib/action_dispatch/journey/gtg/builder.rb2
-rw-r--r--actionpack/lib/action_dispatch/journey/gtg/transition_table.rb4
-rw-r--r--actionpack/lib/action_dispatch/journey/nfa/builder.rb2
-rw-r--r--actionpack/lib/action_dispatch/journey/nfa/transition_table.rb2
-rw-r--r--actionpack/lib/action_dispatch/journey/parser.rb373
-rw-r--r--actionpack/lib/action_dispatch/journey/parser.y5
-rw-r--r--actionpack/lib/action_dispatch/journey/parser_extras.rb6
-rw-r--r--actionpack/lib/action_dispatch/journey/route.rb25
-rw-r--r--actionpack/lib/action_dispatch/journey/router.rb4
-rw-r--r--actionpack/lib/action_dispatch/journey/router/utils.rb6
-rw-r--r--actionpack/lib/action_dispatch/journey/scanner.rb30
-rw-r--r--actionpack/lib/action_dispatch/journey/visitors.rb6
-rw-r--r--actionpack/lib/action_dispatch/middleware/cookies.rb8
-rw-r--r--actionpack/lib/action_dispatch/middleware/debug_exceptions.rb10
-rw-r--r--actionpack/lib/action_dispatch/middleware/exception_wrapper.rb2
-rw-r--r--actionpack/lib/action_dispatch/middleware/flash.rb5
-rw-r--r--actionpack/lib/action_dispatch/middleware/remote_ip.rb6
-rw-r--r--actionpack/lib/action_dispatch/middleware/session/abstract_store.rb11
-rw-r--r--actionpack/lib/action_dispatch/middleware/session/cookie_store.rb4
-rw-r--r--actionpack/lib/action_dispatch/middleware/ssl.rb2
-rw-r--r--actionpack/lib/action_dispatch/request/session.rb8
-rw-r--r--actionpack/lib/action_dispatch/request/utils.rb11
-rw-r--r--actionpack/lib/action_dispatch/routing.rb2
-rw-r--r--actionpack/lib/action_dispatch/routing/mapper.rb95
-rw-r--r--actionpack/lib/action_dispatch/routing/polymorphic_routes.rb6
-rw-r--r--actionpack/lib/action_dispatch/routing/redirection.rb8
-rw-r--r--actionpack/lib/action_dispatch/routing/route_set.rb11
-rw-r--r--actionpack/lib/action_dispatch/routing/url_for.rb6
-rw-r--r--actionpack/lib/action_dispatch/testing/assertions/response.rb2
-rw-r--r--actionpack/lib/action_dispatch/testing/assertions/routing.rb6
-rw-r--r--actionpack/lib/action_dispatch/testing/integration.rb213
-rw-r--r--actionpack/lib/action_dispatch/testing/request_encoder.rb31
-rw-r--r--actionpack/lib/action_dispatch/testing/test_process.rb36
-rw-r--r--actionpack/test/abstract/collector_test.rb2
-rw-r--r--actionpack/test/abstract_unit.rb2
-rw-r--r--actionpack/test/controller/caching_test.rb2
-rw-r--r--actionpack/test/controller/filters_test.rb54
-rw-r--r--actionpack/test/controller/flash_hash_test.rb12
-rw-r--r--actionpack/test/controller/http_digest_authentication_test.rb4
-rw-r--r--actionpack/test/controller/http_token_authentication_test.rb3
-rw-r--r--actionpack/test/controller/integration_test.rb65
-rw-r--r--actionpack/test/controller/mime/accept_format_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/base_test.rb2
-rw-r--r--actionpack/test/controller/new_base/render_context_test.rb16
-rw-r--r--actionpack/test/controller/new_base/render_streaming_test.rb6
-rw-r--r--actionpack/test/controller/parameter_encoding_test.rb39
-rw-r--r--actionpack/test/controller/parameters/mutators_test.rb4
-rw-r--r--actionpack/test/controller/parameters/parameters_permit_test.rb73
-rw-r--r--actionpack/test/controller/parameters/serialization_test.rb10
-rw-r--r--actionpack/test/controller/params_wrapper_test.rb12
-rw-r--r--actionpack/test/controller/redirect_test.rb2
-rw-r--r--actionpack/test/controller/render_json_test.rb2
-rw-r--r--actionpack/test/controller/render_test.rb2
-rw-r--r--actionpack/test/controller/renderer_test.rb8
-rw-r--r--actionpack/test/controller/renderers_test.rb2
-rw-r--r--actionpack/test/controller/request/test_request_test.rb12
-rw-r--r--actionpack/test/controller/request_forgery_protection_test.rb2
-rw-r--r--actionpack/test/controller/rescue_test.rb4
-rw-r--r--actionpack/test/controller/resources_test.rb8
-rw-r--r--actionpack/test/controller/routing_test.rb12
-rw-r--r--actionpack/test/controller/send_file_test.rb11
-rw-r--r--actionpack/test/controller/test_case_test.rb64
-rw-r--r--actionpack/test/controller/url_for_integration_test.rb188
-rw-r--r--actionpack/test/controller/url_for_test.rb10
-rw-r--r--actionpack/test/dispatch/cookies_test.rb30
-rw-r--r--actionpack/test/dispatch/debug_exceptions_test.rb19
-rw-r--r--actionpack/test/dispatch/header_test.rb4
-rw-r--r--actionpack/test/dispatch/mime_type_test.rb2
-rw-r--r--actionpack/test/dispatch/reloader_test.rb2
-rw-r--r--actionpack/test/dispatch/request/multipart_params_parsing_test.rb4
-rw-r--r--actionpack/test/dispatch/request/query_string_parsing_test.rb4
-rw-r--r--actionpack/test/dispatch/request/url_encoded_params_parsing_test.rb2
-rw-r--r--actionpack/test/dispatch/request_test.rb52
-rw-r--r--actionpack/test/dispatch/response_test.rb13
-rw-r--r--actionpack/test/dispatch/routing/ipv6_redirect_test.rb2
-rw-r--r--actionpack/test/dispatch/routing_test.rb14
-rw-r--r--actionpack/test/dispatch/session/cache_store_test.rb6
-rw-r--r--actionpack/test/dispatch/session/cookie_store_test.rb6
-rw-r--r--actionpack/test/dispatch/session/mem_cache_store_test.rb2
-rw-r--r--actionpack/test/dispatch/static_test.rb40
-rw-r--r--actionpack/test/dispatch/test_request_test.rb2
-rw-r--r--actionpack/test/dispatch/uploaded_file_test.rb8
-rw-r--r--actionpack/test/fixtures/multipart/mona_lisa.jpgbin159528 -> 0 bytes
-rw-r--r--actionpack/test/fixtures/multipart/ruby_on_rails.jpgbin0 -> 45142 bytes
-rw-r--r--actionpack/test/fixtures/session_autoload_test/session_autoload_test/foo.rb2
-rw-r--r--actionpack/test/journey/gtg/builder_test.rb10
-rw-r--r--actionpack/test/journey/gtg/transition_table_test.rb2
-rw-r--r--actionpack/test/journey/nfa/simulator_test.rb2
-rw-r--r--actionpack/test/journey/nfa/transition_table_test.rb4
-rw-r--r--actionpack/test/journey/route_test.rb4
-rw-r--r--actionpack/test/journey/router_test.rb22
-rw-r--r--actionpack/test/journey/routes_test.rb2
-rw-r--r--actionpack/test/lib/controller/fake_models.rb4
-rw-r--r--actionview/.gitignore2
-rw-r--r--actionview/CHANGELOG.md50
-rw-r--r--actionview/RUNNING_UJS_TESTS.rdoc7
-rw-r--r--actionview/Rakefile17
-rw-r--r--actionview/app/assets/javascripts/config.coffee37
-rw-r--r--actionview/app/assets/javascripts/features/confirm.coffee26
-rw-r--r--actionview/app/assets/javascripts/features/disable.coffee78
-rw-r--r--actionview/app/assets/javascripts/features/method.coffee34
-rw-r--r--actionview/app/assets/javascripts/features/remote.coffee100
-rw-r--r--actionview/app/assets/javascripts/rails-ujs.coffee76
-rw-r--r--actionview/app/assets/javascripts/utils/ajax.coffee95
-rw-r--r--actionview/app/assets/javascripts/utils/csrf.coffee25
-rw-r--r--actionview/app/assets/javascripts/utils/dom.coffee28
-rw-r--r--actionview/app/assets/javascripts/utils/event.coffee40
-rw-r--r--actionview/app/assets/javascripts/utils/form.coffee61
-rwxr-xr-xactionview/bin/test2
-rw-r--r--actionview/blade.yml11
-rw-r--r--actionview/lib/action_view/context.rb2
-rw-r--r--actionview/lib/action_view/flows.rb2
-rw-r--r--actionview/lib/action_view/helpers/asset_tag_helper.rb34
-rw-r--r--actionview/lib/action_view/helpers/asset_url_helper.rb7
-rw-r--r--actionview/lib/action_view/helpers/atom_feed_helper.rb8
-rw-r--r--actionview/lib/action_view/helpers/cache_helper.rb12
-rw-r--r--actionview/lib/action_view/helpers/date_helper.rb8
-rw-r--r--actionview/lib/action_view/helpers/form_helper.rb387
-rw-r--r--actionview/lib/action_view/helpers/form_tag_helper.rb2
-rw-r--r--actionview/lib/action_view/helpers/number_helper.rb6
-rw-r--r--actionview/lib/action_view/helpers/output_safety_helper.rb2
-rw-r--r--actionview/lib/action_view/helpers/tag_helper.rb7
-rw-r--r--actionview/lib/action_view/helpers/tags/base.rb37
-rw-r--r--actionview/lib/action_view/helpers/tags/collection_check_boxes.rb4
-rw-r--r--actionview/lib/action_view/helpers/tags/collection_helpers.rb14
-rw-r--r--actionview/lib/action_view/helpers/tags/collection_radio_buttons.rb2
-rw-r--r--actionview/lib/action_view/helpers/tags/label.rb4
-rw-r--r--actionview/lib/action_view/helpers/tags/translator.rb2
-rw-r--r--actionview/lib/action_view/helpers/text_helper.rb2
-rw-r--r--actionview/lib/action_view/helpers/translation_helper.rb1
-rw-r--r--actionview/lib/action_view/helpers/url_helper.rb25
-rw-r--r--actionview/lib/action_view/layouts.rb31
-rw-r--r--actionview/lib/action_view/log_subscriber.rb12
-rw-r--r--actionview/lib/action_view/lookup_context.rb18
-rw-r--r--actionview/lib/action_view/railtie.rb6
-rw-r--r--actionview/lib/action_view/record_identifier.rb4
-rw-r--r--actionview/lib/action_view/renderer/abstract_renderer.rb8
-rw-r--r--actionview/lib/action_view/renderer/partial_renderer.rb5
-rw-r--r--actionview/lib/action_view/renderer/streaming_template_renderer.rb2
-rw-r--r--actionview/lib/action_view/renderer/template_renderer.rb4
-rw-r--r--actionview/lib/action_view/rendering.rb6
-rw-r--r--actionview/lib/action_view/routing_url_for.rb19
-rw-r--r--actionview/lib/action_view/template.rb26
-rw-r--r--actionview/lib/action_view/template/error.rb1
-rw-r--r--actionview/lib/action_view/template/handlers/builder.rb4
-rw-r--r--actionview/lib/action_view/template/handlers/erb.rb1
-rw-r--r--actionview/lib/action_view/template/resolver.rb14
-rw-r--r--actionview/lib/action_view/template/text.rb7
-rw-r--r--actionview/lib/action_view/test_case.rb6
-rw-r--r--actionview/lib/action_view/testing/resolvers.rb3
-rw-r--r--actionview/lib/action_view/view_paths.rb12
-rw-r--r--actionview/package.json36
-rw-r--r--actionview/test/abstract_unit.rb2
-rw-r--r--actionview/test/actionpack/abstract/helper_test.rb2
-rw-r--r--actionview/test/actionpack/controller/layout_test.rb3
-rw-r--r--actionview/test/actionpack/controller/render_test.rb4
-rw-r--r--actionview/test/activerecord/form_helper_activerecord_test.rb2
-rw-r--r--actionview/test/activerecord/relation_cache_test.rb2
-rw-r--r--actionview/test/fixtures/test/test_template_with_delegation_reserved_keywords.erb1
-rw-r--r--actionview/test/lib/controller/fake_models.rb4
-rw-r--r--actionview/test/template/asset_tag_helper_test.rb2
-rw-r--r--actionview/test/template/capture_helper_test.rb14
-rw-r--r--actionview/test/template/compiled_templates_test.rb10
-rw-r--r--actionview/test/template/date_helper_test.rb92
-rw-r--r--actionview/test/template/digestor_test.rb6
-rw-r--r--actionview/test/template/form_helper/form_with_test.rb2231
-rw-r--r--actionview/test/template/form_helper_test.rb58
-rw-r--r--actionview/test/template/form_options_helper_test.rb70
-rw-r--r--actionview/test/template/form_tag_helper_test.rb14
-rw-r--r--actionview/test/template/javascript_helper_test.rb8
-rw-r--r--actionview/test/template/number_helper_test.rb14
-rw-r--r--actionview/test/template/output_safety_helper_test.rb4
-rw-r--r--actionview/test/template/test_case_test.rb2
-rw-r--r--actionview/test/template/text_helper_test.rb18
-rw-r--r--actionview/test/template/text_test.rb14
-rw-r--r--actionview/test/template/url_helper_test.rb29
-rw-r--r--actionview/test/ujs/.gitignore1
-rw-r--r--actionview/test/ujs/config.ru3
-rw-r--r--actionview/test/ujs/public/test/.eslintrc.yml21
-rw-r--r--actionview/test/ujs/public/test/call-remote-callbacks.js469
-rw-r--r--actionview/test/ujs/public/test/call-remote.js247
-rw-r--r--actionview/test/ujs/public/test/csrf-refresh.js24
-rw-r--r--actionview/test/ujs/public/test/csrf-token.js27
-rw-r--r--actionview/test/ujs/public/test/data-confirm.js288
-rw-r--r--actionview/test/ujs/public/test/data-disable-with.js391
-rw-r--r--actionview/test/ujs/public/test/data-disable.js321
-rw-r--r--actionview/test/ujs/public/test/data-method.js85
-rw-r--r--actionview/test/ujs/public/test/data-remote.js400
-rw-r--r--actionview/test/ujs/public/test/override.js56
-rw-r--r--actionview/test/ujs/public/test/settings.js116
-rw-r--r--actionview/test/ujs/public/vendor/jquery.metadata.js122
-rw-r--r--actionview/test/ujs/public/vendor/qunit.css237
-rw-r--r--actionview/test/ujs/public/vendor/qunit.js2288
-rw-r--r--actionview/test/ujs/server.rb105
-rw-r--r--actionview/test/ujs/views/layouts/application.html.erb40
-rw-r--r--actionview/test/ujs/views/tests/index.html.erb25
-rw-r--r--activejob/Rakefile2
-rw-r--r--activejob/lib/active_job/configured_job.rb2
-rw-r--r--activejob/lib/active_job/core.rb2
-rw-r--r--activejob/lib/active_job/enqueuing.rb6
-rw-r--r--activejob/lib/active_job/exceptions.rb4
-rw-r--r--activejob/lib/active_job/queue_adapter.rb1
-rw-r--r--activejob/lib/active_job/queue_adapters/qu_adapter.rb2
-rw-r--r--activejob/lib/active_job/queue_name.rb2
-rw-r--r--activejob/lib/active_job/queue_priority.rb2
-rw-r--r--activejob/lib/active_job/railtie.rb2
-rw-r--r--activejob/lib/active_job/test_helper.rb58
-rw-r--r--activejob/test/cases/queue_naming_test.rb2
-rw-r--r--activejob/test/cases/queue_priority_test.rb4
-rw-r--r--activejob/test/cases/test_helper_test.rb8
-rw-r--r--activejob/test/helper.rb3
-rw-r--r--activejob/test/models/person.rb2
-rw-r--r--activejob/test/support/backburner/inline.rb2
-rw-r--r--activejob/test/support/delayed_job/delayed/backend/test.rb6
-rw-r--r--activejob/test/support/integration/adapters/que.rb2
-rw-r--r--activejob/test/support/integration/adapters/queue_classic.rb2
-rw-r--r--activejob/test/support/integration/adapters/sneakers.rb4
-rw-r--r--activejob/test/support/integration/dummy_app_template.rb3
-rw-r--r--activejob/test/support/integration/helper.rb1
-rw-r--r--activejob/test/support/integration/test_case_helpers.rb11
-rw-r--r--activemodel/CHANGELOG.md10
-rwxr-xr-xactivemodel/bin/test2
-rw-r--r--activemodel/lib/active_model/attribute_methods.rb17
-rw-r--r--activemodel/lib/active_model/callbacks.rb6
-rw-r--r--activemodel/lib/active_model/errors.rb22
-rw-r--r--activemodel/lib/active_model/forbidden_attributes_protection.rb2
-rw-r--r--activemodel/lib/active_model/model.rb6
-rw-r--r--activemodel/lib/active_model/naming.rb3
-rw-r--r--activemodel/lib/active_model/serializers/json.rb2
-rw-r--r--activemodel/lib/active_model/type.rb6
-rw-r--r--activemodel/lib/active_model/type/decimal_without_scale.rb11
-rw-r--r--activemodel/lib/active_model/type/helpers/accepts_multiparameter_time.rb4
-rw-r--r--activemodel/lib/active_model/type/helpers/mutable.rb4
-rw-r--r--activemodel/lib/active_model/type/helpers/numeric.rb4
-rw-r--r--activemodel/lib/active_model/type/helpers/time_value.rb8
-rw-r--r--activemodel/lib/active_model/type/integer.rb2
-rw-r--r--activemodel/lib/active_model/type/registry.rb4
-rw-r--r--activemodel/lib/active_model/type/text.rb11
-rw-r--r--activemodel/lib/active_model/validations.rb12
-rw-r--r--activemodel/lib/active_model/validations/acceptance.rb4
-rw-r--r--activemodel/lib/active_model/validations/callbacks.rb4
-rw-r--r--activemodel/lib/active_model/validations/format.rb1
-rw-r--r--activemodel/lib/active_model/validations/length.rb2
-rw-r--r--activemodel/lib/active_model/validations/numericality.rb22
-rw-r--r--activemodel/lib/active_model/validations/validates.rb8
-rw-r--r--activemodel/lib/active_model/validations/with.rb4
-rw-r--r--activemodel/lib/active_model/validator.rb2
-rw-r--r--activemodel/test/cases/attribute_assignment_test.rb2
-rw-r--r--activemodel/test/cases/errors_test.rb32
-rw-r--r--activemodel/test/cases/secure_password_test.rb2
-rw-r--r--activemodel/test/cases/serialization_test.rb64
-rw-r--r--activemodel/test/cases/type/big_integer_test.rb24
-rw-r--r--activemodel/test/cases/type/binary_test.rb15
-rw-r--r--activemodel/test/cases/type/boolean_test.rb39
-rw-r--r--activemodel/test/cases/type/date_test.rb19
-rw-r--r--activemodel/test/cases/type/date_time_test.rb38
-rw-r--r--activemodel/test/cases/type/float_test.rb22
-rw-r--r--activemodel/test/cases/type/immutable_string_test.rb21
-rw-r--r--activemodel/test/cases/type/integer_test.rb11
-rw-r--r--activemodel/test/cases/type/registry_test.rb52
-rw-r--r--activemodel/test/cases/type/string_test.rb37
-rw-r--r--activemodel/test/cases/type/time_test.rb21
-rw-r--r--activemodel/test/cases/type/value_test.rb14
-rw-r--r--activemodel/test/cases/types_test.rb125
-rw-r--r--activemodel/test/cases/validations/absence_validation_test.rb2
-rw-r--r--activemodel/test/cases/validations/acceptance_validation_test.rb4
-rw-r--r--activemodel/test/cases/validations/conditional_validation_test.rb2
-rw-r--r--activemodel/test/cases/validations/confirmation_validation_test.rb4
-rw-r--r--activemodel/test/cases/validations/length_validation_test.rb26
-rw-r--r--activemodel/test/cases/validations/numericality_validation_test.rb8
-rw-r--r--activemodel/test/cases/validations/presence_validation_test.rb2
-rw-r--r--activemodel/test/validators/email_validator.rb1
-rw-r--r--activerecord/CHANGELOG.md175
-rw-r--r--activerecord/Rakefile4
-rwxr-xr-xactiverecord/bin/test2
-rw-r--r--activerecord/examples/performance.rb4
-rw-r--r--activerecord/lib/active_record/aggregations.rb6
-rw-r--r--activerecord/lib/active_record/associations.rb11
-rw-r--r--activerecord/lib/active_record/associations/association.rb13
-rw-r--r--activerecord/lib/active_record/associations/association_scope.rb2
-rw-r--r--activerecord/lib/active_record/associations/builder/belongs_to.rb30
-rw-r--r--activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb8
-rw-r--r--activerecord/lib/active_record/associations/builder/singular_association.rb11
-rw-r--r--activerecord/lib/active_record/associations/collection_association.rb60
-rw-r--r--activerecord/lib/active_record/associations/collection_proxy.rb29
-rw-r--r--activerecord/lib/active_record/associations/has_many_association.rb2
-rw-r--r--activerecord/lib/active_record/associations/has_many_through_association.rb9
-rw-r--r--activerecord/lib/active_record/associations/has_one_association.rb5
-rw-r--r--activerecord/lib/active_record/associations/join_dependency.rb18
-rw-r--r--activerecord/lib/active_record/associations/preloader/association.rb2
-rw-r--r--activerecord/lib/active_record/associations/preloader/through_association.rb6
-rw-r--r--activerecord/lib/active_record/associations/singular_association.rb9
-rw-r--r--activerecord/lib/active_record/associations/through_association.rb4
-rw-r--r--activerecord/lib/active_record/attribute.rb19
-rw-r--r--activerecord/lib/active_record/attribute/user_provided_default.rb2
-rw-r--r--activerecord/lib/active_record/attribute_assignment.rb2
-rw-r--r--activerecord/lib/active_record/attribute_decorators.rb22
-rw-r--r--activerecord/lib/active_record/attribute_methods/dirty.rb179
-rw-r--r--activerecord/lib/active_record/attribute_methods/primary_key.rb9
-rw-r--r--activerecord/lib/active_record/attribute_methods/query.rb2
-rw-r--r--activerecord/lib/active_record/attribute_methods/read.rb9
-rw-r--r--activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb4
-rw-r--r--activerecord/lib/active_record/attribute_methods/write.rb10
-rw-r--r--activerecord/lib/active_record/attribute_mutation_tracker.rb52
-rw-r--r--activerecord/lib/active_record/attribute_set.rb2
-rw-r--r--activerecord/lib/active_record/attribute_set/builder.rb2
-rw-r--r--activerecord/lib/active_record/attribute_set/yaml_encoder.rb2
-rw-r--r--activerecord/lib/active_record/attributes.rb8
-rw-r--r--activerecord/lib/active_record/autosave_association.rb16
-rw-r--r--activerecord/lib/active_record/base.rb2
-rw-r--r--activerecord/lib/active_record/callbacks.rb17
-rw-r--r--activerecord/lib/active_record/coders/yaml_column.rb1
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb80
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/database_limits.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb8
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb31
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb12
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb4
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb30
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_adapter.rb43
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb77
-rw-r--r--activerecord/lib/active_record/connection_adapters/column.rb24
-rw-r--r--activerecord/lib/active_record/connection_adapters/connection_specification.rb4
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql/column.rb4
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql/database_statements.rb8
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql/schema_definitions.rb5
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql/schema_dumper.rb10
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb10
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb8
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid/array.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid/bit.rb6
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid/hstore.rb6
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/schema_definitions.rb36
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/schema_dumper.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb48
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/type_metadata.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/utils.rb12
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb51
-rw-r--r--activerecord/lib/active_record/connection_adapters/schema_cache.rb16
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb23
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb13
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb123
-rw-r--r--activerecord/lib/active_record/connection_adapters/statement_pool.rb2
-rw-r--r--activerecord/lib/active_record/core.rb16
-rw-r--r--activerecord/lib/active_record/define_callbacks.rb20
-rw-r--r--activerecord/lib/active_record/dynamic_matchers.rb1
-rw-r--r--activerecord/lib/active_record/enum.rb2
-rw-r--r--activerecord/lib/active_record/errors.rb38
-rw-r--r--activerecord/lib/active_record/explain.rb1
-rw-r--r--activerecord/lib/active_record/fixture_set/file.rb7
-rw-r--r--activerecord/lib/active_record/fixtures.rb24
-rw-r--r--activerecord/lib/active_record/integration.rb6
-rw-r--r--activerecord/lib/active_record/locking/optimistic.rb26
-rw-r--r--activerecord/lib/active_record/log_subscriber.rb31
-rw-r--r--activerecord/lib/active_record/migration.rb21
-rw-r--r--activerecord/lib/active_record/migration/compatibility.rb17
-rw-r--r--activerecord/lib/active_record/model_schema.rb169
-rw-r--r--activerecord/lib/active_record/null_relation.rb9
-rw-r--r--activerecord/lib/active_record/persistence.rb22
-rw-r--r--activerecord/lib/active_record/query_cache.rb18
-rw-r--r--activerecord/lib/active_record/railtie.rb8
-rw-r--r--activerecord/lib/active_record/railties/controller_runtime.rb4
-rw-r--r--activerecord/lib/active_record/railties/databases.rake16
-rw-r--r--activerecord/lib/active_record/reflection.rb29
-rw-r--r--activerecord/lib/active_record/relation.rb16
-rw-r--r--activerecord/lib/active_record/relation/batches/batch_enumerator.rb2
-rw-r--r--activerecord/lib/active_record/relation/calculations.rb12
-rw-r--r--activerecord/lib/active_record/relation/delegation.rb5
-rw-r--r--activerecord/lib/active_record/relation/finder_methods.rb220
-rw-r--r--activerecord/lib/active_record/relation/predicate_builder.rb2
-rw-r--r--activerecord/lib/active_record/relation/predicate_builder/array_handler.rb2
-rw-r--r--activerecord/lib/active_record/relation/predicate_builder/association_query_handler.rb2
-rw-r--r--activerecord/lib/active_record/relation/predicate_builder/base_handler.rb2
-rw-r--r--activerecord/lib/active_record/relation/predicate_builder/class_handler.rb2
-rw-r--r--activerecord/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb2
-rw-r--r--activerecord/lib/active_record/relation/query_methods.rb14
-rw-r--r--activerecord/lib/active_record/relation/spawn_methods.rb2
-rw-r--r--activerecord/lib/active_record/relation/where_clause.rb2
-rw-r--r--activerecord/lib/active_record/relation/where_clause_factory.rb2
-rw-r--r--activerecord/lib/active_record/sanitization.rb27
-rw-r--r--activerecord/lib/active_record/schema.rb4
-rw-r--r--activerecord/lib/active_record/schema_dumper.rb4
-rw-r--r--activerecord/lib/active_record/scoping.rb2
-rw-r--r--activerecord/lib/active_record/scoping/default.rb12
-rw-r--r--activerecord/lib/active_record/scoping/named.rb14
-rw-r--r--activerecord/lib/active_record/statement_cache.rb6
-rw-r--r--activerecord/lib/active_record/store.rb7
-rw-r--r--activerecord/lib/active_record/table_metadata.rb2
-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.rb4
-rw-r--r--activerecord/lib/active_record/timestamp.rb4
-rw-r--r--activerecord/lib/active_record/touch_later.rb2
-rw-r--r--activerecord/lib/active_record/transactions.rb25
-rw-r--r--activerecord/lib/active_record/type.rb10
-rw-r--r--activerecord/lib/active_record/type/adapter_specific_registry.rb4
-rw-r--r--activerecord/lib/active_record/type/decimal_without_scale.rb9
-rw-r--r--activerecord/lib/active_record/type/helpers.rb5
-rw-r--r--activerecord/lib/active_record/type/internal/abstract_json.rb4
-rw-r--r--activerecord/lib/active_record/type/serialized.rb4
-rw-r--r--activerecord/lib/active_record/type/text.rb9
-rw-r--r--activerecord/lib/active_record/type/unsigned_integer.rb (renamed from activemodel/lib/active_model/type/unsigned_integer.rb)4
-rw-r--r--activerecord/lib/active_record/type/value.rb5
-rw-r--r--activerecord/lib/active_record/type_caster/connection.rb2
-rw-r--r--activerecord/lib/active_record/type_caster/map.rb2
-rw-r--r--activerecord/lib/active_record/validations.rb8
-rw-r--r--activerecord/lib/active_record/validations/associated.rb2
-rw-r--r--activerecord/lib/active_record/validations/presence.rb2
-rw-r--r--activerecord/lib/active_record/validations/uniqueness.rb15
-rw-r--r--activerecord/lib/rails/generators/active_record/migration/migration_generator.rb6
-rw-r--r--activerecord/lib/rails/generators/active_record/model/model_generator.rb2
-rw-r--r--activerecord/test/active_record/connection_adapters/fake_adapter.rb2
-rw-r--r--activerecord/test/assets/schema_dump_5_1.yml345
-rw-r--r--activerecord/test/cases/adapter_test.rb48
-rw-r--r--activerecord/test/cases/adapters/mysql2/active_schema_test.rb3
-rw-r--r--activerecord/test/cases/adapters/mysql2/connection_test.rb12
-rw-r--r--activerecord/test/cases/adapters/mysql2/json_test.rb16
-rw-r--r--activerecord/test/cases/adapters/mysql2/legacy_migration_test.rb60
-rw-r--r--activerecord/test/cases/adapters/mysql2/mysql2_adapter_test.rb13
-rw-r--r--activerecord/test/cases/adapters/mysql2/reserved_word_test.rb12
-rw-r--r--activerecord/test/cases/adapters/mysql2/schema_test.rb2
-rw-r--r--activerecord/test/cases/adapters/mysql2/transaction_test.rb24
-rw-r--r--activerecord/test/cases/adapters/mysql2/unsigned_type_test.rb7
-rw-r--r--activerecord/test/cases/adapters/postgresql/active_schema_test.rb4
-rw-r--r--activerecord/test/cases/adapters/postgresql/array_test.rb24
-rw-r--r--activerecord/test/cases/adapters/postgresql/bytea_test.rb8
-rw-r--r--activerecord/test/cases/adapters/postgresql/connection_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/geometric_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/hstore_test.rb36
-rw-r--r--activerecord/test/cases/adapters/postgresql/json_test.rb20
-rw-r--r--activerecord/test/cases/adapters/postgresql/legacy_migration_test.rb54
-rw-r--r--activerecord/test/cases/adapters/postgresql/prepared_statements_disabled_test.rb25
-rw-r--r--activerecord/test/cases/adapters/postgresql/prepared_statements_test.rb21
-rw-r--r--activerecord/test/cases/adapters/postgresql/quoting_test.rb6
-rw-r--r--activerecord/test/cases/adapters/postgresql/schema_authorization_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/schema_test.rb6
-rw-r--r--activerecord/test/cases/adapters/postgresql/timestamp_test.rb4
-rw-r--r--activerecord/test/cases/adapters/postgresql/transaction_test.rb6
-rw-r--r--activerecord/test/cases/adapters/postgresql/utils_test.rb10
-rw-r--r--activerecord/test/cases/adapters/postgresql/uuid_test.rb62
-rw-r--r--activerecord/test/cases/adapters/sqlite3/copy_table_test.rb2
-rw-r--r--activerecord/test/cases/adapters/sqlite3/legacy_migration_test.rb59
-rw-r--r--activerecord/test/cases/adapters/sqlite3/quoting_test.rb2
-rw-r--r--activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb2
-rw-r--r--activerecord/test/cases/ar_schema_test.rb4
-rw-r--r--activerecord/test/cases/associations/belongs_to_associations_test.rb30
-rw-r--r--activerecord/test/cases/associations/callbacks_test.rb4
-rw-r--r--activerecord/test/cases/associations/cascaded_eager_loading_test.rb8
-rw-r--r--activerecord/test/cases/associations/eager_load_includes_full_sti_class_test.rb4
-rw-r--r--activerecord/test/cases/associations/eager_load_nested_include_test.rb4
-rw-r--r--activerecord/test/cases/associations/eager_test.rb46
-rw-r--r--activerecord/test/cases/associations/extension_test.rb2
-rw-r--r--activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb33
-rw-r--r--activerecord/test/cases/associations/has_many_associations_test.rb25
-rw-r--r--activerecord/test/cases/associations/has_many_through_associations_test.rb38
-rw-r--r--activerecord/test/cases/associations/has_one_associations_test.rb14
-rw-r--r--activerecord/test/cases/associations/has_one_through_associations_test.rb8
-rw-r--r--activerecord/test/cases/associations/join_model_test.rb30
-rw-r--r--activerecord/test/cases/associations/left_outer_join_association_test.rb1
-rw-r--r--activerecord/test/cases/attribute_methods/read_test.rb1
-rw-r--r--activerecord/test/cases/attribute_methods_test.rb37
-rw-r--r--activerecord/test/cases/attribute_set_test.rb2
-rw-r--r--activerecord/test/cases/attributes_test.rb8
-rw-r--r--activerecord/test/cases/autosave_association_test.rb35
-rw-r--r--activerecord/test/cases/base_test.rb40
-rw-r--r--activerecord/test/cases/calculations_test.rb12
-rw-r--r--activerecord/test/cases/callbacks_test.rb8
-rw-r--r--activerecord/test/cases/collection_cache_key_test.rb6
-rw-r--r--activerecord/test/cases/comment_test.rb3
-rw-r--r--activerecord/test/cases/connection_adapters/connection_handler_test.rb2
-rw-r--r--activerecord/test/cases/connection_adapters/merge_and_resolve_default_url_config_test.rb68
-rw-r--r--activerecord/test/cases/connection_adapters/quoting_test.rb13
-rw-r--r--activerecord/test/cases/connection_adapters/schema_cache_test.rb43
-rw-r--r--activerecord/test/cases/connection_pool_test.rb126
-rw-r--r--activerecord/test/cases/connection_specification/resolver_test.rb4
-rw-r--r--activerecord/test/cases/defaults_test.rb2
-rw-r--r--activerecord/test/cases/dirty_test.rb85
-rw-r--r--activerecord/test/cases/finder_test.rb79
-rw-r--r--activerecord/test/cases/fixture_set/file_test.rb2
-rw-r--r--activerecord/test/cases/fixtures_test.rb39
-rw-r--r--activerecord/test/cases/forbidden_attributes_protection_test.rb4
-rw-r--r--activerecord/test/cases/integration_test.rb4
-rw-r--r--activerecord/test/cases/json_serialization_test.rb8
-rw-r--r--activerecord/test/cases/locking_test.rb146
-rw-r--r--activerecord/test/cases/migration/change_schema_test.rb18
-rw-r--r--activerecord/test/cases/migration/change_table_test.rb7
-rw-r--r--activerecord/test/cases/migration/foreign_key_test.rb8
-rw-r--r--activerecord/test/cases/migration/references_foreign_key_test.rb8
-rw-r--r--activerecord/test/cases/migration/references_statements_test.rb2
-rw-r--r--activerecord/test/cases/migration_test.rb10
-rw-r--r--activerecord/test/cases/migrator_test.rb8
-rw-r--r--activerecord/test/cases/multiparameter_attributes_test.rb15
-rw-r--r--activerecord/test/cases/nested_attributes_test.rb6
-rw-r--r--activerecord/test/cases/nested_attributes_with_callbacks_test.rb8
-rw-r--r--activerecord/test/cases/persistence_test.rb24
-rw-r--r--activerecord/test/cases/primary_keys_test.rb131
-rw-r--r--activerecord/test/cases/query_cache_test.rb171
-rw-r--r--activerecord/test/cases/quoting_test.rb4
-rw-r--r--activerecord/test/cases/reflection_test.rb4
-rw-r--r--activerecord/test/cases/relation/or_test.rb2
-rw-r--r--activerecord/test/cases/relation/where_test.rb12
-rw-r--r--activerecord/test/cases/relation_test.rb6
-rw-r--r--activerecord/test/cases/relations_test.rb47
-rw-r--r--activerecord/test/cases/sanitize_test.rb4
-rw-r--r--activerecord/test/cases/schema_dumper_test.rb11
-rw-r--r--activerecord/test/cases/schema_loading_test.rb2
-rw-r--r--activerecord/test/cases/scoping/default_scoping_test.rb5
-rw-r--r--activerecord/test/cases/scoping/named_scoping_test.rb4
-rw-r--r--activerecord/test/cases/scoping/relation_scoping_test.rb4
-rw-r--r--activerecord/test/cases/secure_token_test.rb2
-rw-r--r--activerecord/test/cases/serialized_attribute_test.rb12
-rw-r--r--activerecord/test/cases/tasks/database_tasks_test.rb2
-rw-r--r--activerecord/test/cases/tasks/mysql_rake_test.rb6
-rw-r--r--activerecord/test/cases/tasks/postgresql_rake_test.rb6
-rw-r--r--activerecord/test/cases/tasks/sqlite_rake_test.rb6
-rw-r--r--activerecord/test/cases/test_case.rb5
-rw-r--r--activerecord/test/cases/test_fixtures_test.rb2
-rw-r--r--activerecord/test/cases/transaction_callbacks_test.rb57
-rw-r--r--activerecord/test/cases/transactions_test.rb10
-rw-r--r--activerecord/test/cases/type/unsigned_integer_test.rb (renamed from activemodel/test/cases/type/unsigned_integer_test.rb)5
-rw-r--r--activerecord/test/cases/validations/association_validation_test.rb2
-rw-r--r--activerecord/test/cases/validations/uniqueness_validation_test.rb16
-rw-r--r--activerecord/test/cases/view_test.rb24
-rw-r--r--activerecord/test/cases/yaml_serialization_test.rb2
-rw-r--r--activerecord/test/config.example.yml3
-rw-r--r--activerecord/test/fixtures/naked/yml/courses_with_invalid_key.yml3
-rw-r--r--activerecord/test/fixtures/other_dogs.yml2
-rw-r--r--activerecord/test/models/admin/user.rb4
-rw-r--r--activerecord/test/models/company.rb4
-rw-r--r--activerecord/test/models/company_in_module.rb2
-rw-r--r--activerecord/test/models/eye.rb4
-rw-r--r--activerecord/test/models/other_dog.rb5
-rw-r--r--activerecord/test/models/pirate.rb16
-rw-r--r--activerecord/test/models/post.rb6
-rw-r--r--activerecord/test/models/subject.rb2
-rw-r--r--activerecord/test/models/topic.rb2
-rw-r--r--activerecord/test/schema/postgresql_specific_schema.rb1
-rw-r--r--activerecord/test/schema/schema.rb22
-rw-r--r--activerecord/test/support/config.rb7
-rw-r--r--activerecord/test/support/connection.rb1
-rw-r--r--activesupport/CHANGELOG.md128
-rwxr-xr-xactivesupport/bin/generate_tables10
-rwxr-xr-xactivesupport/bin/test2
-rw-r--r--activesupport/lib/active_support/cache.rb25
-rw-r--r--activesupport/lib/active_support/cache/file_store.rb13
-rw-r--r--activesupport/lib/active_support/cache/mem_cache_store.rb28
-rw-r--r--activesupport/lib/active_support/cache/memory_store.rb14
-rw-r--r--activesupport/lib/active_support/cache/null_store.rb8
-rw-r--r--activesupport/lib/active_support/cache/strategy/local_cache.rb21
-rw-r--r--activesupport/lib/active_support/cache/strategy/local_cache_middleware.rb4
-rw-r--r--activesupport/lib/active_support/callbacks.rb22
-rw-r--r--activesupport/lib/active_support/concurrency/latch.rb25
-rw-r--r--activesupport/lib/active_support/core_ext/array/conversions.rb10
-rw-r--r--activesupport/lib/active_support/core_ext/class/subclasses.rb1
-rw-r--r--activesupport/lib/active_support/core_ext/date_and_time/calculations.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/date_time/conversions.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/hash/compact.rb40
-rw-r--r--activesupport/lib/active_support/core_ext/hash/conversions.rb6
-rw-r--r--activesupport/lib/active_support/core_ext/hash/reverse_merge.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/hash/transform_values.rb5
-rw-r--r--activesupport/lib/active_support/core_ext/kernel/debugger.rb3
-rw-r--r--activesupport/lib/active_support/core_ext/kernel/reporting.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/load_error.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/module.rb1
-rw-r--r--activesupport/lib/active_support/core_ext/module/aliasing.rb48
-rw-r--r--activesupport/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb4
-rw-r--r--activesupport/lib/active_support/core_ext/module/delegation.rb9
-rw-r--r--activesupport/lib/active_support/core_ext/module/introspection.rb8
-rw-r--r--activesupport/lib/active_support/core_ext/module/method_transplanting.rb3
-rw-r--r--activesupport/lib/active_support/core_ext/module/qualified_const.rb70
-rw-r--r--activesupport/lib/active_support/core_ext/numeric/conversions.rb5
-rw-r--r--activesupport/lib/active_support/core_ext/object/duplicable.rb111
-rw-r--r--activesupport/lib/active_support/core_ext/regexp.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/securerandom.rb4
-rw-r--r--activesupport/lib/active_support/core_ext/string/access.rb4
-rw-r--r--activesupport/lib/active_support/core_ext/string/indent.rb4
-rw-r--r--activesupport/lib/active_support/core_ext/string/inflections.rb8
-rw-r--r--activesupport/lib/active_support/core_ext/string/output_safety.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/struct.rb3
-rw-r--r--activesupport/lib/active_support/core_ext/time/calculations.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/time/marshal.rb3
-rw-r--r--activesupport/lib/active_support/dependencies.rb5
-rw-r--r--activesupport/lib/active_support/duration.rb33
-rw-r--r--activesupport/lib/active_support/duration/iso8601_serializer.rb4
-rw-r--r--activesupport/lib/active_support/file_update_checker.rb2
-rw-r--r--activesupport/lib/active_support/gzip.rb2
-rw-r--r--activesupport/lib/active_support/hash_with_indifferent_access.rb30
-rw-r--r--activesupport/lib/active_support/i18n_railtie.rb2
-rw-r--r--activesupport/lib/active_support/inflector/methods.rb8
-rw-r--r--activesupport/lib/active_support/inflector/transliterate.rb6
-rw-r--r--activesupport/lib/active_support/json/encoding.rb5
-rw-r--r--activesupport/lib/active_support/key_generator.rb2
-rw-r--r--activesupport/lib/active_support/lazy_load_hooks.rb4
-rw-r--r--activesupport/lib/active_support/log_subscriber.rb4
-rw-r--r--activesupport/lib/active_support/log_subscriber/test_helper.rb2
-rw-r--r--activesupport/lib/active_support/logger.rb4
-rw-r--r--activesupport/lib/active_support/multibyte/chars.rb8
-rw-r--r--activesupport/lib/active_support/multibyte/unicode.rb30
-rw-r--r--activesupport/lib/active_support/notifications.rb4
-rw-r--r--activesupport/lib/active_support/notifications/instrumenter.rb2
-rw-r--r--activesupport/lib/active_support/number_helper.rb4
-rw-r--r--activesupport/lib/active_support/number_helper/number_converter.rb10
-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.rb8
-rw-r--r--activesupport/lib/active_support/number_helper/number_to_phone_converter.rb2
-rw-r--r--activesupport/lib/active_support/number_helper/number_to_rounded_converter.rb2
-rw-r--r--activesupport/lib/active_support/ordered_hash.rb2
-rw-r--r--activesupport/lib/active_support/ordered_options.rb4
-rw-r--r--activesupport/lib/active_support/per_thread_registry.rb4
-rw-r--r--activesupport/lib/active_support/rails.rb6
-rw-r--r--activesupport/lib/active_support/rescuable.rb10
-rw-r--r--activesupport/lib/active_support/subscriber.rb9
-rw-r--r--activesupport/lib/active_support/tagged_logging.rb11
-rw-r--r--activesupport/lib/active_support/test_case.rb2
-rw-r--r--activesupport/lib/active_support/testing/declarative.rb2
-rw-r--r--activesupport/lib/active_support/testing/isolation.rb12
-rw-r--r--activesupport/lib/active_support/values/time_zone.rb38
-rw-r--r--activesupport/lib/active_support/xml_mini.rb20
-rw-r--r--activesupport/lib/active_support/xml_mini/jdom.rb2
-rw-r--r--activesupport/lib/active_support/xml_mini/libxml.rb2
-rw-r--r--activesupport/lib/active_support/xml_mini/nokogiri.rb2
-rw-r--r--activesupport/lib/active_support/xml_mini/rexml.rb2
-rw-r--r--activesupport/test/autoloading_fixtures/prepend.rb8
-rw-r--r--activesupport/test/autoloading_fixtures/prepend/sub_class_conflict.rb2
-rw-r--r--activesupport/test/broadcast_logger_test.rb24
-rw-r--r--activesupport/test/caching_test.rb45
-rw-r--r--activesupport/test/callbacks_test.rb97
-rw-r--r--activesupport/test/class_cache_test.rb2
-rw-r--r--activesupport/test/concern_test.rb2
-rw-r--r--activesupport/test/constantize_test_cases.rb5
-rw-r--r--activesupport/test/core_ext/array/conversions_test.rb7
-rw-r--r--activesupport/test/core_ext/array/grouping_test.rb2
-rw-r--r--activesupport/test/core_ext/class_test.rb10
-rw-r--r--activesupport/test/core_ext/date_and_time_behavior.rb294
-rw-r--r--activesupport/test/core_ext/date_and_time_compatibility_test.rb4
-rw-r--r--activesupport/test/core_ext/date_ext_test.rb142
-rw-r--r--activesupport/test/core_ext/date_time_ext_test.rb202
-rw-r--r--activesupport/test/core_ext/duration_test.rb76
-rw-r--r--activesupport/test/core_ext/enumerable_test.rb22
-rw-r--r--activesupport/test/core_ext/hash_ext_test.rb95
-rw-r--r--activesupport/test/core_ext/load_error_test.rb8
-rw-r--r--activesupport/test/core_ext/module/attribute_accessor_per_thread_test.rb6
-rw-r--r--activesupport/test/core_ext/module/qualified_const_test.rb118
-rw-r--r--activesupport/test/core_ext/module_test.rb247
-rw-r--r--activesupport/test/core_ext/numeric_ext_test.rb86
-rw-r--r--activesupport/test/core_ext/object/blank_test.rb2
-rw-r--r--activesupport/test/core_ext/object/deep_dup_test.rb8
-rw-r--r--activesupport/test/core_ext/object/duplicable_test.rb10
-rw-r--r--activesupport/test/core_ext/object/inclusion_test.rb6
-rw-r--r--activesupport/test/core_ext/range_ext_test.rb6
-rw-r--r--activesupport/test/core_ext/string_ext_test.rb36
-rw-r--r--activesupport/test/core_ext/time_ext_test.rb606
-rw-r--r--activesupport/test/core_ext/time_with_zone_test.rb196
-rw-r--r--activesupport/test/dependencies_test.rb16
-rw-r--r--activesupport/test/deprecation_test.rb6
-rw-r--r--activesupport/test/descendants_tracker_test_cases.rb2
-rw-r--r--activesupport/test/evented_file_update_checker_test.rb2
-rw-r--r--activesupport/test/executor_test.rb4
-rw-r--r--activesupport/test/file_update_checker_shared_tests.rb18
-rw-r--r--activesupport/test/gzip_test.rb2
-rw-r--r--activesupport/test/inflector_test.rb18
-rw-r--r--activesupport/test/inflector_test_cases.rb2
-rw-r--r--activesupport/test/json/decoding_test.rb18
-rw-r--r--activesupport/test/json/encoding_test.rb46
-rw-r--r--activesupport/test/json/encoding_test_cases.rb20
-rw-r--r--activesupport/test/message_encryptor_test.rb2
-rw-r--r--activesupport/test/message_verifier_test.rb2
-rw-r--r--activesupport/test/multibyte_chars_test.rb28
-rw-r--r--activesupport/test/multibyte_conformance_test.rb2
-rw-r--r--activesupport/test/multibyte_grapheme_break_conformance_test.rb2
-rw-r--r--activesupport/test/multibyte_normalization_conformance_test.rb2
-rw-r--r--activesupport/test/multibyte_test_helpers.rb2
-rw-r--r--activesupport/test/notifications/evented_notification_test.rb2
-rw-r--r--activesupport/test/notifications/instrumenter_test.rb4
-rw-r--r--activesupport/test/notifications_test.rb2
-rw-r--r--activesupport/test/number_helper_test.rb75
-rw-r--r--activesupport/test/ordered_hash_test.rb6
-rw-r--r--activesupport/test/rescuable_test.rb2
-rw-r--r--activesupport/test/safe_buffer_test.rb4
-rw-r--r--activesupport/test/test_case_test.rb4
-rw-r--r--activesupport/test/time_zone_test.rb116
-rw-r--r--activesupport/test/xml_mini/jdom_engine_test.rb154
-rw-r--r--activesupport/test/xml_mini/libxml_engine_test.rb202
-rw-r--r--activesupport/test/xml_mini/libxmlsax_engine_test.rb197
-rw-r--r--activesupport/test/xml_mini/nokogiri_engine_test.rb217
-rw-r--r--activesupport/test/xml_mini/nokogirisax_engine_test.rb218
-rw-r--r--activesupport/test/xml_mini/rexml_engine_test.rb41
-rw-r--r--activesupport/test/xml_mini/xml_mini_engine_test.rb257
-rw-r--r--activesupport/test/xml_mini_test.rb56
-rwxr-xr-xci/travis.rb2
-rw-r--r--guides/Rakefile5
-rw-r--r--[-rwxr-xr-x]guides/assets/stylesheets/responsive-tables.css0
-rw-r--r--guides/bug_report_templates/active_record_migrations_gem.rb2
-rw-r--r--guides/bug_report_templates/active_record_migrations_master.rb2
-rw-r--r--guides/bug_report_templates/benchmark.rb49
-rw-r--r--guides/rails_guides/generator.rb2
-rw-r--r--guides/rails_guides/helpers.rb2
-rw-r--r--guides/rails_guides/indexer.rb2
-rw-r--r--guides/rails_guides/kindle.rb16
-rw-r--r--guides/rails_guides/levenshtein.rb4
-rw-r--r--guides/rails_guides/markdown.rb2
-rw-r--r--guides/rails_guides/markdown/renderer.rb4
-rw-r--r--guides/source/5_0_release_notes.md13
-rw-r--r--guides/source/action_cable_overview.md2
-rw-r--r--guides/source/action_controller_overview.md18
-rw-r--r--guides/source/action_mailer_basics.md2
-rw-r--r--guides/source/action_view_overview.md2
-rw-r--r--guides/source/active_record_callbacks.md6
-rw-r--r--guides/source/active_record_migrations.md24
-rw-r--r--guides/source/active_record_postgresql.md2
-rw-r--r--guides/source/active_record_querying.md3
-rw-r--r--guides/source/active_support_core_extensions.md58
-rw-r--r--guides/source/active_support_instrumentation.md18
-rw-r--r--guides/source/asset_pipeline.md32
-rw-r--r--guides/source/association_basics.md4
-rw-r--r--guides/source/configuring.md8
-rw-r--r--guides/source/contributing_to_ruby_on_rails.md45
-rw-r--r--guides/source/development_dependencies_install.md4
-rw-r--r--guides/source/form_helpers.md2
-rw-r--r--guides/source/generators.md30
-rw-r--r--guides/source/getting_started.md15
-rw-r--r--guides/source/i18n.md2
-rw-r--r--guides/source/initialization.md165
-rw-r--r--guides/source/layout.html.erb2
-rw-r--r--guides/source/layouts_and_rendering.md4
-rw-r--r--guides/source/rails_on_rack.md1
-rw-r--r--guides/source/routing.md8
-rw-r--r--guides/source/security.md4
-rw-r--r--guides/source/testing.md12
-rw-r--r--guides/source/upgrading_ruby_on_rails.md6
-rw-r--r--guides/w3c_validator.rb2
-rw-r--r--railties/CHANGELOG.md31
-rw-r--r--railties/Rakefile1
-rw-r--r--railties/lib/rails.rb4
-rw-r--r--railties/lib/rails/application.rb8
-rw-r--r--railties/lib/rails/application_controller.rb2
-rw-r--r--railties/lib/rails/code_statistics.rb2
-rw-r--r--railties/lib/rails/command.rb8
-rw-r--r--railties/lib/rails/command/behavior.rb16
-rw-r--r--railties/lib/rails/commands/application/application_command.rb2
-rw-r--r--railties/lib/rails/commands/console/console_command.rb2
-rw-r--r--railties/lib/rails/commands/dbconsole/dbconsole_command.rb11
-rw-r--r--railties/lib/rails/commands/destroy/destroy_command.rb4
-rw-r--r--railties/lib/rails/commands/generate/generate_command.rb6
-rw-r--r--railties/lib/rails/commands/help/help_command.rb2
-rw-r--r--railties/lib/rails/commands/new/new_command.rb2
-rw-r--r--railties/lib/rails/commands/plugin/plugin_command.rb4
-rw-r--r--railties/lib/rails/commands/rake/rake_command.rb2
-rw-r--r--railties/lib/rails/commands/runner/USAGE2
-rw-r--r--railties/lib/rails/commands/runner/runner_command.rb5
-rw-r--r--railties/lib/rails/commands/server/server_command.rb4
-rw-r--r--railties/lib/rails/commands/test/test_command.rb4
-rw-r--r--railties/lib/rails/commands/version/version_command.rb2
-rw-r--r--railties/lib/rails/configuration.rb4
-rw-r--r--railties/lib/rails/console/app.rb4
-rw-r--r--railties/lib/rails/engine.rb36
-rw-r--r--railties/lib/rails/engine/configuration.rb2
-rw-r--r--railties/lib/rails/generators.rb407
-rw-r--r--railties/lib/rails/generators/actions.rb32
-rw-r--r--railties/lib/rails/generators/actions/create_migration.rb6
-rw-r--r--railties/lib/rails/generators/active_model.rb6
-rw-r--r--railties/lib/rails/generators/app_base.rb66
-rw-r--r--railties/lib/rails/generators/base.rb34
-rw-r--r--railties/lib/rails/generators/erb.rb2
-rw-r--r--railties/lib/rails/generators/erb/mailer/mailer_generator.rb2
-rw-r--r--railties/lib/rails/generators/erb/scaffold/scaffold_generator.rb2
-rw-r--r--railties/lib/rails/generators/generated_attribute.rb4
-rw-r--r--railties/lib/rails/generators/migration.rb2
-rw-r--r--railties/lib/rails/generators/named_base.rb80
-rw-r--r--railties/lib/rails/generators/rails/app/app_generator.rb43
-rw-r--r--railties/lib/rails/generators/rails/app/templates/Gemfile5
-rw-r--r--railties/lib/rails/generators/rails/app/templates/app/assets/javascripts/application.js.tt10
-rw-r--r--railties/lib/rails/generators/rails/app/templates/app/assets/stylesheets/application.css4
-rw-r--r--railties/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb.tt2
-rw-r--r--railties/lib/rails/generators/rails/app/templates/bin/setup.tt (renamed from railties/lib/rails/generators/rails/app/templates/bin/setup)6
-rw-r--r--railties/lib/rails/generators/rails/app/templates/bin/update.tt (renamed from railties/lib/rails/generators/rails/app/templates/bin/update)3
-rw-r--r--railties/lib/rails/generators/rails/app/templates/bin/yarn9
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/initializers/assets.rb.tt6
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults.rb.tt2
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/puma.rb12
-rw-r--r--railties/lib/rails/generators/rails/app/templates/gitignore5
-rw-r--r--railties/lib/rails/generators/rails/app/templates/package.json5
-rw-r--r--railties/lib/rails/generators/rails/assets/assets_generator.rb2
-rw-r--r--railties/lib/rails/generators/rails/controller/controller_generator.rb27
-rw-r--r--railties/lib/rails/generators/rails/generator/generator_generator.rb2
-rw-r--r--railties/lib/rails/generators/rails/plugin/plugin_generator.rb10
-rw-r--r--railties/lib/rails/generators/resource_helpers.rb6
-rw-r--r--railties/lib/rails/generators/test_unit/generator/generator_generator.rb2
-rw-r--r--railties/lib/rails/generators/test_unit/mailer/mailer_generator.rb2
-rw-r--r--railties/lib/rails/generators/testing/behaviour.rb10
-rw-r--r--railties/lib/rails/initializable.rb2
-rw-r--r--railties/lib/rails/mailers_controller.rb10
-rw-r--r--railties/lib/rails/paths.rb1
-rw-r--r--railties/lib/rails/rack/logger.rb66
-rw-r--r--railties/lib/rails/railtie.rb31
-rw-r--r--railties/lib/rails/railtie/configurable.rb2
-rw-r--r--railties/lib/rails/source_annotation_extractor.rb8
-rw-r--r--railties/lib/rails/tasks/statistics.rake2
-rw-r--r--railties/lib/rails/test_unit/railtie.rb4
-rw-r--r--railties/test/app_loader_test.rb2
-rw-r--r--railties/test/application/assets_test.rb6
-rw-r--r--railties/test/application/configuration/custom_test.rb2
-rw-r--r--railties/test/application/configuration_test.rb45
-rw-r--r--railties/test/application/generators_test.rb15
-rw-r--r--railties/test/application/initializers/frameworks_test.rb2
-rw-r--r--railties/test/application/initializers/hooks_test.rb4
-rw-r--r--railties/test/application/loading_test.rb2
-rw-r--r--railties/test/application/middleware/session_test.rb6
-rw-r--r--railties/test/application/middleware_test.rb4
-rw-r--r--railties/test/application/rake_test.rb30
-rw-r--r--railties/test/application/runner_test.rb17
-rw-r--r--railties/test/application/test_runner_test.rb12
-rw-r--r--railties/test/backtrace_cleaner_test.rb2
-rw-r--r--railties/test/code_statistics_calculator_test.rb4
-rw-r--r--railties/test/commands/dbconsole_test.rb18
-rw-r--r--railties/test/commands/server_test.rb2
-rw-r--r--railties/test/generators/actions_test.rb2
-rw-r--r--railties/test/generators/api_app_generator_test.rb7
-rw-r--r--railties/test/generators/app_generator_test.rb58
-rw-r--r--railties/test/generators/controller_generator_test.rb2
-rw-r--r--railties/test/generators/generator_test.rb2
-rw-r--r--railties/test/generators/model_generator_test.rb16
-rw-r--r--railties/test/generators/named_base_test.rb2
-rw-r--r--railties/test/generators/plugin_generator_test.rb11
-rw-r--r--railties/test/generators/plugin_test_runner_test.rb2
-rw-r--r--railties/test/generators/shared_generator_tests.rb2
-rw-r--r--railties/test/isolation/abstract_unit.rb3
-rw-r--r--railties/test/path_generation_test.rb2
-rw-r--r--railties/test/rails_info_test.rb2
-rw-r--r--railties/test/railties/engine_test.rb6
-rw-r--r--railties/test/railties/generators_test.rb2
-rwxr-xr-xtools/profile2
-rw-r--r--tools/test.rb5
924 files changed, 18070 insertions, 7251 deletions
diff --git a/.rubocop.yml b/.rubocop.yml
index 985a036556..629f5d86ea 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -6,6 +6,7 @@ AllCops:
Exclude:
- '**/templates/**/*'
- '**/vendor/**/*'
+ - 'actionpack/lib/action_dispatch/journey/parser.rb'
# Prefer &&/|| over and/or.
Style/AndOr:
@@ -50,6 +51,24 @@ Style/IndentationConsistency:
Style/IndentationWidth:
Enabled: true
+Style/SpaceAfterColon:
+ Enabled: true
+
+Style/SpaceAfterComma:
+ Enabled: true
+
+Style/SpaceAroundEqualsInParameterDefault:
+ Enabled: true
+
+Style/SpaceAroundKeyword:
+ Enabled: true
+
+Style/SpaceAroundOperators:
+ Enabled: true
+
+Style/SpaceBeforeFirstArg:
+ Enabled: true
+
# Defining a method with parameters needs parentheses.
Style/MethodDefParentheses:
Enabled: true
@@ -66,6 +85,9 @@ Style/SpaceInsideBlockBraces:
Style/SpaceInsideHashLiteralBraces:
Enabled: true
+Style/SpaceInsideParens:
+ Enabled: true
+
# Check quotes usage according to lint rule below.
Style/StringLiterals:
Enabled: true
diff --git a/.travis.yml b/.travis.yml
index 88dd6b7b5d..ad65de48aa 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -10,7 +10,6 @@ cache:
services:
- memcached
- redis
- - rabbitmq
addons:
postgresql: "9.4"
@@ -18,12 +17,11 @@ addons:
bundler_args: --without test --jobs 3 --retry 3
before_install:
- "rm ${BUNDLE_GEMFILE}.lock"
+ - "gem update bundler"
- "[ -f /tmp/beanstalkd-1.10/Makefile ] || (curl -L https://github.com/kr/beanstalkd/archive/v1.10.tar.gz | tar xz -C /tmp)"
- "pushd /tmp/beanstalkd-1.10 && make && (./beanstalkd &); popd"
before_script:
- - bundle update
-
# Set Sauce Labs username and access key. Obfuscated, purposefully not encrypted.
# Decodes to e.g. `export VARIABLE=VALUE`
- $(base64 --decode <<< "ZXhwb3J0IFNBVUNFX0FDQ0VTU19LRVk9YTAzNTM0M2YtZTkyMi00MGIzLWFhM2MtMDZiM2VhNjM1YzQ4")
@@ -32,40 +30,62 @@ before_script:
script: 'ci/travis.rb'
env:
+ global:
+ - "JRUBY_OPTS='--dev -J-Xmx1024M'"
matrix:
- "GEM=railties"
- - "GEM=ap"
- - "GEM=ac"
- - "GEM=ac:integration"
+ - "GEM=ap,ac"
- "GEM=am,amo,as,av,aj"
- "GEM=as PRESERVE_TIMEZONES=1"
- "GEM=ar:mysql2"
- "GEM=ar:sqlite3"
- "GEM=ar:postgresql"
- - "GEM=aj:integration"
- "GEM=guides"
+ - "GEM=ac:integration"
rvm:
- - 2.2.5
- - 2.3.1
+ - 2.2.6
+ - 2.3.3
+ - 2.4.0
- ruby-head
matrix:
include:
- # Latest compiled version in http://rubies.travis-ci.org
- - rvm: 2.3.1
+ - rvm: 2.2.6
+ env: "GEM=aj:integration"
+ services:
+ - memcached
+ - redis
+ - rabbitmq
+ - rvm: 2.3.3
+ env: "GEM=aj:integration"
+ services:
+ - memcached
+ - redis
+ - rabbitmq
+ - rvm: ruby-head
+ env: "GEM=aj:integration"
+ services:
+ - memcached
+ - redis
+ - rabbitmq
+ - rvm: 2.3.3
env:
- - "GEM=ar:mysql2"
+ - "GEM=ar:mysql2 MYSQL=mariadb"
addons:
mariadb: 10.0
- - rvm: jruby-9.0.5.0
+ - rvm: jruby-9.1.5.0
+ jdk: oraclejdk8
+ env:
+ - "GEM=ap"
+ - rvm: jruby-9.1.5.0
jdk: oraclejdk8
env:
- - "JRUBY_OPTS='--dev -J-Xmx1024M'"
- - "GEM='ap'"
+ - "GEM=am,aj"
allow_failures:
- rvm: ruby-head
- - rvm: jruby-9.0.5.0
+ - rvm: jruby-9.1.5.0
+ - env: "GEM=ac:integration"
fast_finish: true
notifications:
diff --git a/Gemfile b/Gemfile
index 53044252a0..e1e892c7f3 100644
--- a/Gemfile
+++ b/Gemfile
@@ -29,22 +29,25 @@ gem "bcrypt", "~> 3.1.11", require: false
# sprockets.
gem "uglifier", ">= 1.3.0", require: false
-# Track stable branch of sass because it doesn't have circular require warnings.
-gem "sass", github: "sass/sass", branch: "stable", require: false
-
# FIXME: Remove this fork after https://github.com/nex3/rb-inotify/pull/49 is fixed.
gem "rb-inotify", github: "matthewd/rb-inotify", branch: "close-handling", require: false
+# Explicitly avoid 1.x that doesn't support Ruby 2.4+
+gem "json", ">= 2.0.0"
+
+gem "rubocop", require: false
+
group :doc do
- gem "sdoc", "1.0.0.beta2"
+ gem "sdoc", "1.0.0.rc1"
gem "redcarpet", "~> 3.2.3", platforms: :ruby
gem "w3c_validators"
- gem "kindlerb", "0.1.1"
+ gem "kindlerb", "~> 1.2.0"
end
# Active Support.
gem "dalli", ">= 2.2.1"
gem "listen", ">= 3.0.5", "< 3.2", require: false
+gem "libxml-ruby", platforms: :ruby
# Active Job.
group :job do
diff --git a/Gemfile.lock b/Gemfile.lock
index d4059c607d..2f6b8001ec 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -40,7 +40,7 @@ GIT
GIT
remote: https://github.com/resque/resque.git
- revision: 20d885065ac19e7f7d7a982f4ed1296083db0300
+ revision: 835a4a7e61a2e33832dbf11975ecfab54af293c6
specs:
resque (1.27.0)
mono_logger (~> 1.0)
@@ -49,19 +49,12 @@ GIT
sinatra (>= 0.9.2)
vegas (~> 0.1.2)
-GIT
- remote: https://github.com/sass/sass.git
- revision: 7716e67f3507c6f65878c69aa49ec358ebf675c7
- branch: stable
- specs:
- sass (3.4.22)
-
PATH
remote: .
specs:
actioncable (5.1.0.alpha)
actionpack (= 5.1.0.alpha)
- nio4r (~> 1.2)
+ nio4r (~> 2.0)
websocket-driver (~> 0.6.1)
actionmailer (5.1.0.alpha)
actionpack (= 5.1.0.alpha)
@@ -118,9 +111,11 @@ PATH
GEM
remote: https://rubygems.org/
specs:
- addressable (2.4.0)
+ addressable (2.5.0)
+ public_suffix (~> 2.0, >= 2.0.2)
amq-protocol (2.0.1)
- arel (7.1.2)
+ arel (7.1.4)
+ ast (2.3.0)
backburner (1.3.1)
beaneater (~> 1.0)
concurrent-ruby (~> 1.0.1)
@@ -130,26 +125,25 @@ GEM
bcrypt (3.1.11-x86-mingw32)
beaneater (1.0.0)
benchmark-ips (2.7.2)
- blade (0.5.6)
+ blade (0.7.0)
activesupport (>= 3.0.0)
- blade-qunit_adapter (~> 1.20.0)
+ blade-qunit_adapter (~> 2.0.1)
coffee-script
coffee-script-source
curses (~> 1.0.0)
eventmachine
faye
sprockets (>= 3.0)
- sprockets-export (~> 0.9.1)
thin (>= 1.6.0)
thor (~> 0.19.1)
useragent (~> 0.16.7)
- blade-qunit_adapter (1.20.0)
- blade-sauce_labs_plugin (0.5.3)
+ blade-qunit_adapter (2.0.1)
+ blade-sauce_labs_plugin (0.6.2)
childprocess
faraday
selenium-webdriver
builder (3.2.2)
- bunny (2.2.2)
+ bunny (2.6.2)
amq-protocol (>= 2.0.1)
byebug (9.0.6)
childprocess (0.5.9)
@@ -160,9 +154,9 @@ GEM
coffee-script (2.4.1)
coffee-script-source
execjs
- coffee-script-source (1.10.0)
- concurrent-ruby (1.0.2)
- connection_pool (2.2.0)
+ coffee-script-source (1.12.2)
+ concurrent-ruby (1.0.4)
+ connection_pool (2.2.1)
cookiejar (0.3.3)
curses (1.0.2)
daemons (1.2.4)
@@ -181,13 +175,13 @@ GEM
eventmachine (>= 1.0.0.beta.4)
erubis (2.7.0)
event_emitter (0.2.5)
- eventmachine (1.2.0.1)
- eventmachine (1.2.0.1-x64-mingw32)
- eventmachine (1.2.0.1-x86-mingw32)
+ eventmachine (1.2.1)
+ eventmachine (1.2.1-x64-mingw32)
+ eventmachine (1.2.1-x86-mingw32)
execjs (2.7.0)
- faraday (0.9.2)
+ faraday (0.10.0)
multipart-post (>= 1.2, < 3)
- faye (1.2.2)
+ faye (1.2.3)
cookiejar (>= 0.3.0)
em-http-request (>= 0.3.0)
eventmachine (>= 0.12.0)
@@ -195,12 +189,10 @@ GEM
multi_json (>= 1.0.0)
rack (>= 1.0.0)
websocket-driver (>= 0.5.1)
- faye-websocket (0.10.4)
+ faye-websocket (0.10.5)
eventmachine (>= 0.12.0)
websocket-driver (>= 0.5.1)
ffi (1.9.14)
- ffi (1.9.14-x64-mingw32)
- ffi (1.9.14-x86-mingw32)
globalid (0.3.7)
activesupport (>= 4.1.0)
hiredis (0.6.1)
@@ -211,9 +203,10 @@ GEM
railties (>= 4.2.0)
thor (>= 0.14, < 2.0)
json (2.0.2)
- kindlerb (0.1.1)
+ kindlerb (1.2.0)
mustache
nokogiri
+ libxml-ruby (2.9.0)
listen (3.1.5)
rb-fsevent (~> 0.9, >= 0.9.4)
rb-inotify (~> 0.9, >= 0.9.7)
@@ -235,25 +228,25 @@ GEM
multi_json (1.12.1)
multipart-post (2.0.0)
mustache (1.0.3)
- mysql2 (0.4.4)
- mysql2 (0.4.4-x64-mingw32)
- mysql2 (0.4.4-x86-mingw32)
- nio4r (1.2.1)
- nokogiri (1.6.8)
+ mysql2 (0.4.5)
+ mysql2 (0.4.5-x64-mingw32)
+ mysql2 (0.4.5-x86-mingw32)
+ nio4r (2.0.0)
+ nokogiri (1.7.0)
mini_portile2 (~> 2.1.0)
- pkg-config (~> 1.1.7)
- nokogiri (1.6.8-x64-mingw32)
+ nokogiri (1.7.0-x64-mingw32)
mini_portile2 (~> 2.1.0)
- pkg-config (~> 1.1.7)
- nokogiri (1.6.8-x86-mingw32)
+ nokogiri (1.7.0-x86-mingw32)
mini_portile2 (~> 2.1.0)
- pkg-config (~> 1.1.7)
+ parser (2.3.3.1)
+ ast (~> 2.2)
pg (0.19.0)
pg (0.19.0-x64-mingw32)
pg (0.19.0-x86-mingw32)
- pkg-config (1.1.7)
- psych (2.1.1)
- puma (3.6.0)
+ powerpack (0.1.1)
+ psych (2.2.2)
+ public_suffix (2.0.4)
+ puma (3.6.2)
qu (0.2.0)
multi_json
qu-redis (0.2.0)
@@ -269,16 +262,17 @@ GEM
rack
rack-test (0.6.3)
rack (>= 1.0)
- rails-dom-testing (2.0.1)
+ rails-dom-testing (2.0.2)
activesupport (>= 4.2.0, < 6.0)
- nokogiri (~> 1.6.0)
+ nokogiri (~> 1.6)
rails-html-sanitizer (1.0.3)
loofah (~> 2.0)
- rake (11.3.0)
- rb-fsevent (0.9.7)
- rdoc (5.0.0.beta2)
+ rainbow (2.1.0)
+ rake (12.0.0)
+ rb-fsevent (0.9.8)
+ rdoc (5.0.0)
redcarpet (3.2.3)
- redis (3.3.1)
+ redis (3.3.2)
redis-namespace (1.5.2)
redis (~> 3.0, >= 3.0.4)
resque-scheduler (4.3.0)
@@ -286,49 +280,57 @@ GEM
redis (~> 3.3)
resque (~> 1.26)
rufus-scheduler (~> 3.2)
- ruby_dep (1.4.0)
+ rubocop (0.46.0)
+ parser (>= 2.3.1.1, < 3.0)
+ powerpack (~> 0.1)
+ rainbow (>= 1.99.1, < 3.0)
+ ruby-progressbar (~> 1.7)
+ unicode-display_width (~> 1.0, >= 1.0.1)
+ ruby-progressbar (1.8.1)
+ ruby_dep (1.5.0)
rubyzip (1.2.0)
- rufus-scheduler (3.2.2)
+ rufus-scheduler (3.3.1)
+ tzinfo
+ sass (3.4.23)
sass-rails (5.0.6)
railties (>= 4.0.0, < 6)
sass (~> 3.1)
sprockets (>= 2.8, < 4.0)
sprockets-rails (>= 2.0, < 4.0)
tilt (>= 1.1, < 3)
- sdoc (1.0.0.beta2)
- rdoc (= 5.0.0.beta2)
- selenium-webdriver (2.53.4)
+ sdoc (1.0.0.rc1)
+ rdoc (= 5.0.0)
+ selenium-webdriver (3.0.5)
childprocess (~> 0.5)
rubyzip (~> 1.0)
websocket (~> 1.0)
- sequel (4.39.0)
+ sequel (4.41.0)
serverengine (1.5.11)
sigdump (~> 0.2.2)
- sidekiq (4.2.2)
+ sidekiq (4.2.7)
concurrent-ruby (~> 1.0)
connection_pool (~> 2.2, >= 2.2.0)
- rack-protection (~> 1.5)
+ rack-protection (>= 1.5.0)
redis (~> 3.2, >= 3.2.1)
sigdump (0.2.4)
simple_uuid (0.4.0)
sinatra (1.0)
rack (>= 1.0)
- sneakers (2.3.5)
- bunny (~> 2.2.0)
+ sneakers (2.4.0)
+ bunny (~> 2.6)
serverengine (~> 1.5.11)
thor
thread (~> 0.1.7)
- sprockets (3.7.0)
+ sprockets (3.7.1)
concurrent-ruby (~> 1.0)
rack (> 1, < 3)
- sprockets-export (0.9.1)
sprockets-rails (3.2.0)
actionpack (>= 4.0)
activesupport (>= 4.0)
sprockets (>= 3.0.0)
- sqlite3 (1.3.11)
- sqlite3 (1.3.11-x64-mingw32)
- sqlite3 (1.3.11-x86-mingw32)
+ sqlite3 (1.3.12)
+ sqlite3 (1.3.12-x64-mingw32)
+ sqlite3 (1.3.12-x86-mingw32)
stackprof (0.2.10)
sucker_punch (2.0.2)
concurrent-ruby (~> 1.0.0)
@@ -336,7 +338,7 @@ GEM
daemons (~> 1.0, >= 1.0.9)
eventmachine (~> 1.0, >= 1.0.4)
rack (>= 1, < 3)
- thor (0.19.1)
+ thor (0.19.4)
thread (0.1.7)
thread_safe (0.3.5)
tilt (2.0.5)
@@ -345,16 +347,17 @@ GEM
turbolinks-source (5.0.0)
tzinfo (1.2.2)
thread_safe (~> 0.1)
- tzinfo-data (1.2016.7)
+ tzinfo-data (1.2016.10)
tzinfo (>= 1.0.0)
- uglifier (3.0.2)
+ uglifier (3.0.4)
execjs (>= 0.3.0, < 3)
+ unicode-display_width (1.1.2)
useragent (0.16.8)
vegas (0.1.11)
rack (>= 1.0.0)
- w3c_validators (1.2)
- json
- nokogiri
+ w3c_validators (1.3.1)
+ json (~> 2.0)
+ nokogiri (~> 1.6)
wdm (0.1.1)
websocket (1.2.3)
websocket-driver (0.6.4)
@@ -383,7 +386,9 @@ DEPENDENCIES
em-hiredis
hiredis
jquery-rails
- kindlerb (= 0.1.1)
+ json (>= 2.0.0)
+ kindlerb (~> 1.2.0)
+ libxml-ruby
listen (>= 3.0.5, < 3.2)
minitest (< 5.3.4)
mocha (~> 0.14)
@@ -404,9 +409,9 @@ DEPENDENCIES
redis
resque!
resque-scheduler
- sass!
+ rubocop
sass-rails
- sdoc (= 1.0.0.beta2)
+ sdoc (= 1.0.0.rc1)
sequel
sidekiq
sneakers
@@ -421,4 +426,4 @@ DEPENDENCIES
websocket-client-simple!
BUNDLED WITH
- 1.13.2
+ 1.13.7
diff --git a/RELEASING_RAILS.md b/RELEASING_RAILS.md
index 4e6b811d3e..10a8bca3b3 100644
--- a/RELEASING_RAILS.md
+++ b/RELEASING_RAILS.md
@@ -103,17 +103,19 @@ branch.
Run `rake install` to generate the gems and install them locally. Then try
generating a new app and ensure that nothing explodes.
-Verify that Action Cable's package.json is updated with the RC version.
+Verify that Action Cable and Action View's package.json files are updated with
+the RC version.
This will stop you from looking silly when you push an RC to rubygems.org and
then realize it is broken.
### Release to RubyGems and NPM.
-IMPORTANT: The Action Cable client is released as an NPM package, so you must
-have Node.js installed, have an NPM account (npmjs.com), and be an actioncable
-package owner (`npm owner ls actioncable`) to do a full release. Do not release
-until you're set up with NPM!
+IMPORTANT: The Action Cable client and Action View's UJS adapter are released
+as NPM packages, so you must have Node.js installed, have an NPM account
+(npmjs.com), and be a package owner for `actioncable` and `rails-ujs` (you can
+check this via `npm owner ls actioncable` and `npm owner ls rails-ujs`) in
+order to do a full release. Do not release until you're set up with NPM!
Run `rake release`. This will populate the gemspecs and NPM package.json with
the current RAILS_VERSION, commit the changes, tag it, and push the gems to
@@ -179,7 +181,7 @@ more explanation on a particular step, see the RC steps.
Today, do this stuff in this order:
* Apply security patches to the release branch
-* Update CHANGELOG with security fixes.
+* Update CHANGELOG with security fixes
* Update RAILS_VERSION to remove the rc
* Build and test the gem
* Release the gems
@@ -206,7 +208,7 @@ so we need to give them the security fixes in patch form.
* Blog announcements
* Twitter announcements
-* Merge the release branch to the stable branch.
+* Merge the release branch to the stable branch
* Drink beer (or other cocktail)
## Misc
diff --git a/actioncable/CHANGELOG.md b/actioncable/CHANGELOG.md
index d70d32ce07..4f17274a01 100644
--- a/actioncable/CHANGELOG.md
+++ b/actioncable/CHANGELOG.md
@@ -1,7 +1,7 @@
* Permit same-origin connections by default.
- New option `config.action_cable.allow_same_origin_as_host = false`
- to disable.
+ Added new option `config.action_cable.allow_same_origin_as_host = false`
+ to disable this behaviour.
*Dávid Halász*, *Matthew Draper*
@@ -13,17 +13,17 @@
*Vladimir Dementyev*
-* Buffer writes to websocket connections, to avoid blocking threads
+* Buffer now writes to WebSocket connections, to avoid blocking threads
that could be doing more useful things.
*Matthew Draper*, *Tinco Andringa*
-* Protect against concurrent writes to a websocket connection from
+* Protect against concurrent writes to a WebSocket connection from
multiple threads; the underlying OS write is not always threadsafe.
*Tinco Andringa*
-* Add ActiveSupport::Notifications hook to Broadcaster#broadcast.
+* Add `ActiveSupport::Notifications` hook to `Broadcaster#broadcast`.
*Matthew Wear*
diff --git a/actioncable/README.md b/actioncable/README.md
index 8ad9aeb1f1..cccb55a196 100644
--- a/actioncable/README.md
+++ b/actioncable/README.md
@@ -51,7 +51,7 @@ module ApplicationCable
self.current_user = find_verified_user
end
- protected
+ private
def find_verified_user
if current_user = User.find_by(id: cookies.signed[:user_id])
current_user
diff --git a/actioncable/Rakefile b/actioncable/Rakefile
index 648de57004..87d443919c 100644
--- a/actioncable/Rakefile
+++ b/actioncable/Rakefile
@@ -1,7 +1,6 @@
require "rake/testtask"
require "pathname"
require "action_cable"
-require "blade"
dir = File.dirname(__FILE__)
@@ -25,6 +24,7 @@ namespace :test do
end
task :integration do
+ require "blade"
if ENV["CI"]
Blade.start(interface: :ci)
else
@@ -36,6 +36,7 @@ end
namespace :assets do
desc "Compile Action Cable assets"
task :compile do
+ require "blade"
Blade.build
end
end
diff --git a/actioncable/actioncable.gemspec b/actioncable/actioncable.gemspec
index e7f91d0e34..6d95f022fa 100644
--- a/actioncable/actioncable.gemspec
+++ b/actioncable/actioncable.gemspec
@@ -20,6 +20,6 @@ Gem::Specification.new do |s|
s.add_dependency "actionpack", version
- s.add_dependency "nio4r", "~> 1.2"
+ s.add_dependency "nio4r", "~> 2.0"
s.add_dependency "websocket-driver", "~> 0.6.1"
end
diff --git a/actioncable/lib/action_cable/channel/base.rb b/actioncable/lib/action_cable/channel/base.rb
index a866044f95..6739a62ba0 100644
--- a/actioncable/lib/action_cable/channel/base.rb
+++ b/actioncable/lib/action_cable/channel/base.rb
@@ -122,16 +122,16 @@ module ActionCable
end
end
- protected
+ private
# action_methods are cached and there is sometimes need to refresh
# them. ::clear_action_methods! allows you to do that, so next time
# you run action_methods, they will be recalculated.
- def clear_action_methods!
+ def clear_action_methods! # :doc:
@action_methods = nil
end
# Refresh the cached action_methods when a new action_method is added.
- def method_added(name)
+ def method_added(name) # :doc:
super
clear_action_methods!
end
@@ -189,22 +189,22 @@ module ActionCable
end
end
- protected
+ private
# Called once a consumer has become a subscriber of the channel. Usually the place to setup any streams
# you want this channel to be sending to the subscriber.
- def subscribed
+ def subscribed # :doc:
# Override in subclasses
end
# Called once a consumer has cut its cable connection. Can be used for cleaning up connections or marking
# users as offline or the like.
- def unsubscribed
+ def unsubscribed # :doc:
# Override in subclasses
end
# Transmit a hash of data to the subscriber. The hash will automatically be wrapped in a JSON envelope with
# the proper channel identifier marked as the recipient.
- def transmit(data, via: nil)
+ def transmit(data, via: nil) # :doc:
logger.info "#{self.class.name} transmitting #{data.inspect.truncate(300)}".tap { |m| m << " (via #{via})" if via }
payload = { channel_class: self.class.name, data: data, via: via }
@@ -213,33 +213,32 @@ module ActionCable
end
end
- def ensure_confirmation_sent
+ def ensure_confirmation_sent # :doc:
return if subscription_rejected?
@defer_subscription_confirmation_counter.decrement
transmit_subscription_confirmation unless defer_subscription_confirmation?
end
- def defer_subscription_confirmation!
+ def defer_subscription_confirmation! # :doc:
@defer_subscription_confirmation_counter.increment
end
- def defer_subscription_confirmation?
+ def defer_subscription_confirmation? # :doc:
@defer_subscription_confirmation_counter.value > 0
end
- def subscription_confirmation_sent?
+ def subscription_confirmation_sent? # :doc:
@subscription_confirmation_sent
end
- def reject
+ def reject # :doc:
@reject_subscription = true
end
- def subscription_rejected?
+ def subscription_rejected? # :doc:
@reject_subscription
end
- private
def delegate_connection_identifiers
connection.identifiers.each do |identifier|
define_singleton_method(identifier) do
diff --git a/actioncable/lib/action_cable/channel/naming.rb b/actioncable/lib/action_cable/channel/naming.rb
index b7e88bf73d..b565cb3cac 100644
--- a/actioncable/lib/action_cable/channel/naming.rb
+++ b/actioncable/lib/action_cable/channel/naming.rb
@@ -12,7 +12,7 @@ module ActionCable
# Chats::AppearancesChannel.channel_name # => 'chats:appearances'
# FooChats::BarAppearancesChannel.channel_name # => 'foo_chats:bar_appearances'
def channel_name
- @channel_name ||= name.sub(/Channel$/, "").gsub("::",":").underscore
+ @channel_name ||= name.sub(/Channel$/, "").gsub("::", ":").underscore
end
end
diff --git a/actioncable/lib/action_cable/connection/base.rb b/actioncable/lib/action_cable/connection/base.rb
index dfee123ea2..0a517a532d 100644
--- a/actioncable/lib/action_cable/connection/base.rb
+++ b/actioncable/lib/action_cable/connection/base.rb
@@ -22,13 +22,10 @@ module ActionCable
# # Any cleanup work needed when the cable connection is cut.
# end
#
- # protected
+ # private
# def find_verified_user
- # if current_user = User.find_by_identity cookies.signed[:identity_id]
- # current_user
- # else
+ # User.find_by_identity(cookies.signed[:identity_id]) ||
# reject_unauthorized_connection
- # end
# end
# end
# end
@@ -136,9 +133,15 @@ module ActionCable
send_async :handle_close
end
+ # TODO Change this to private once we've dropped Ruby 2.2 support.
+ # Workaround for Ruby 2.2 "private attribute?" warning.
protected
+ attr_reader :websocket
+ attr_reader :message_buffer
+
+ private
# The request that initiated the WebSocket connection is available here. This gives access to the environment, cookies, etc.
- def request
+ def request # :doc:
@request ||= begin
environment = Rails.application.env_config.merge(env) if defined?(Rails.application) && Rails.application
ActionDispatch::Request.new(environment || env)
@@ -146,14 +149,10 @@ module ActionCable
end
# The cookies of the request that initiated the WebSocket connection. Useful for performing authorization checks.
- def cookies
+ def cookies # :doc:
request.cookie_jar
end
- attr_reader :websocket
- attr_reader :message_buffer
-
- private
def encode(cable_message)
@coder.encode cable_message
end
diff --git a/actioncable/lib/action_cable/connection/message_buffer.rb b/actioncable/lib/action_cable/connection/message_buffer.rb
index 6a80770cae..4ccd322644 100644
--- a/actioncable/lib/action_cable/connection/message_buffer.rb
+++ b/actioncable/lib/action_cable/connection/message_buffer.rb
@@ -28,6 +28,8 @@ module ActionCable
receive_buffered_messages
end
+ # TODO Change this to private once we've dropped Ruby 2.2 support.
+ # Workaround for Ruby 2.2 "private attribute?" warning.
protected
attr_reader :connection
attr_reader :buffered_messages
diff --git a/actioncable/lib/action_cable/connection/subscriptions.rb b/actioncable/lib/action_cable/connection/subscriptions.rb
index 00511aead5..abf42c99d5 100644
--- a/actioncable/lib/action_cable/connection/subscriptions.rb
+++ b/actioncable/lib/action_cable/connection/subscriptions.rb
@@ -61,6 +61,8 @@ module ActionCable
subscriptions.each { |id, channel| remove_subscription(channel) }
end
+ # TODO Change this to private once we've dropped Ruby 2.2 support.
+ # Workaround for Ruby 2.2 "private attribute?" warning.
protected
attr_reader :connection, :subscriptions
diff --git a/actioncable/lib/action_cable/connection/tagged_logger_proxy.rb b/actioncable/lib/action_cable/connection/tagged_logger_proxy.rb
index 41afa9680a..aef549aa86 100644
--- a/actioncable/lib/action_cable/connection/tagged_logger_proxy.rb
+++ b/actioncable/lib/action_cable/connection/tagged_logger_proxy.rb
@@ -31,8 +31,8 @@ module ActionCable
end
end
- protected
- def log(type, message)
+ private
+ def log(type, message) # :doc:
tag(@logger) { @logger.send type, message }
end
end
diff --git a/actioncable/lib/action_cable/connection/web_socket.rb b/actioncable/lib/action_cable/connection/web_socket.rb
index 382141b89f..03eb6e2ea8 100644
--- a/actioncable/lib/action_cable/connection/web_socket.rb
+++ b/actioncable/lib/action_cable/connection/web_socket.rb
@@ -32,6 +32,8 @@ module ActionCable
websocket.rack_response
end
+ # TODO Change this to private once we've dropped Ruby 2.2 support.
+ # Workaround for Ruby 2.2 "private attribute?" warning.
protected
attr_reader :websocket
end
diff --git a/actioncable/lib/action_cable/engine.rb b/actioncable/lib/action_cable/engine.rb
index 4c5c975cd8..e23527b84e 100644
--- a/actioncable/lib/action_cable/engine.rb
+++ b/actioncable/lib/action_cable/engine.rb
@@ -34,7 +34,7 @@ module ActionCable
previous_connection_class = self.connection_class
self.connection_class = -> { "ApplicationCable::Connection".safe_constantize || previous_connection_class.call }
- options.each { |k,v| send("#{k}=", v) }
+ options.each { |k, v| send("#{k}=", v) }
end
end
diff --git a/actioncable/lib/action_cable/helpers/action_cable_helper.rb b/actioncable/lib/action_cable/helpers/action_cable_helper.rb
index 2081a37db6..f53be0bc31 100644
--- a/actioncable/lib/action_cable/helpers/action_cable_helper.rb
+++ b/actioncable/lib/action_cable/helpers/action_cable_helper.rb
@@ -6,7 +6,7 @@ module ActionCable
#
# <head>
# <%= action_cable_meta_tag %>
- # <%= javascript_include_tag 'application', 'data-turbolinks-track' => true %>
+ # <%= javascript_include_tag 'application', 'data-turbolinks-track' => 'reload' %>
# </head>
#
# This is then used by Action Cable to determine the URL of your WebSocket server.
diff --git a/actioncable/lib/action_cable/remote_connections.rb b/actioncable/lib/action_cable/remote_connections.rb
index 720ba52d19..d2856bc6ae 100644
--- a/actioncable/lib/action_cable/remote_connections.rb
+++ b/actioncable/lib/action_cable/remote_connections.rb
@@ -54,7 +54,7 @@ module ActionCable
def set_identifier_instance_vars(ids)
raise InvalidIdentifiersError unless valid_identifiers?(ids)
- ids.each { |k,v| instance_variable_set("@#{k}", v) }
+ ids.each { |k, v| instance_variable_set("@#{k}", v) }
end
def valid_identifiers?(ids)
diff --git a/actioncable/lib/action_cable/subscription_adapter/evented_redis.rb b/actioncable/lib/action_cable/subscription_adapter/evented_redis.rb
index bcd46d2a0e..c3018c5281 100644
--- a/actioncable/lib/action_cable/subscription_adapter/evented_redis.rb
+++ b/actioncable/lib/action_cable/subscription_adapter/evented_redis.rb
@@ -68,10 +68,10 @@ module ActionCable
end
def ensure_reactor_running
- return if EventMachine.reactor_running?
+ return if EventMachine.reactor_running? && EventMachine.reactor_thread
@@mutex.synchronize do
Thread.new { EventMachine.run } unless EventMachine.reactor_running?
- Thread.pass until EventMachine.reactor_running?
+ Thread.pass until EventMachine.reactor_running? && EventMachine.reactor_thread
end
end
end
diff --git a/actioncable/lib/action_cable/subscription_adapter/subscriber_map.rb b/actioncable/lib/action_cable/subscription_adapter/subscriber_map.rb
index 4ec513e3ba..4cce86dcca 100644
--- a/actioncable/lib/action_cable/subscription_adapter/subscriber_map.rb
+++ b/actioncable/lib/action_cable/subscription_adapter/subscriber_map.rb
@@ -2,7 +2,7 @@ module ActionCable
module SubscriptionAdapter
class SubscriberMap
def initialize
- @subscribers = Hash.new { |h,k| h[k] = [] }
+ @subscribers = Hash.new { |h, k| h[k] = [] }
@sync = Mutex.new
end
diff --git a/actioncable/lib/rails/generators/channel/USAGE b/actioncable/lib/rails/generators/channel/USAGE
index 6249553c22..dd109fda80 100644
--- a/actioncable/lib/rails/generators/channel/USAGE
+++ b/actioncable/lib/rails/generators/channel/USAGE
@@ -3,7 +3,7 @@ Description:
Stubs out a new cable channel for the server (in Ruby) and client (in CoffeeScript).
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/javascript/cable.js after generating any channels.
+ Note: Turn on the cable connection in app/assets/javascripts/cable.js after generating any channels.
Example:
========
@@ -11,4 +11,4 @@ Example:
creates a Chat channel class and CoffeeScript asset:
Channel: app/channels/chat_channel.rb
- Assets: app/assets/javascript/channels/chat.coffee
+ Assets: app/assets/javascripts/channels/chat.coffee
diff --git a/actioncable/lib/rails/generators/channel/channel_generator.rb b/actioncable/lib/rails/generators/channel/channel_generator.rb
index 20d807c033..04b787c3a4 100644
--- a/actioncable/lib/rails/generators/channel/channel_generator.rb
+++ b/actioncable/lib/rails/generators/channel/channel_generator.rb
@@ -23,7 +23,7 @@ module Rails
generate_application_cable_files
end
- protected
+ private
def file_name
@_file_name ||= super.gsub(/_channel/i, "")
end
diff --git a/actioncable/test/channel/periodic_timers_test.rb b/actioncable/test/channel/periodic_timers_test.rb
index 2ee711fd29..0cc4992ef6 100644
--- a/actioncable/test/channel/periodic_timers_test.rb
+++ b/actioncable/test/channel/periodic_timers_test.rb
@@ -32,7 +32,7 @@ class ActionCable::Channel::PeriodicTimersTest < ActiveSupport::TestCase
timers.each_with_index do |timer, i|
assert_kind_of Proc, timer[0]
- assert_equal i+1, timer[1][:every]
+ assert_equal i + 1, timer[1][:every]
end
end
diff --git a/actioncable/test/client_test.rb b/actioncable/test/client_test.rb
index db10a7ad16..98a114a5f4 100644
--- a/actioncable/test/client_test.rb
+++ b/actioncable/test/client_test.rb
@@ -186,9 +186,9 @@ class ClientTest < ActionCable::TestCase
c = websocket_client(port)
assert_equal({ "type" => "welcome" }, c.read_message) # pop the first welcome message off the stack
c.send_message command: "subscribe", identifier: JSON.generate(channel: "ClientTest::EchoChannel")
- assert_equal({ "identifier"=>"{\"channel\":\"ClientTest::EchoChannel\"}", "type"=>"confirm_subscription" }, c.read_message)
+ assert_equal({ "identifier" => "{\"channel\":\"ClientTest::EchoChannel\"}", "type" => "confirm_subscription" }, c.read_message)
c.send_message command: "message", identifier: JSON.generate(channel: "ClientTest::EchoChannel"), data: JSON.generate(action: "ding", message: "hello")
- assert_equal({ "identifier"=>"{\"channel\":\"ClientTest::EchoChannel\"}", "message"=>{ "dong"=>"hello" } }, c.read_message)
+ assert_equal({ "identifier" => "{\"channel\":\"ClientTest::EchoChannel\"}", "message" => { "dong" => "hello" } }, c.read_message)
c.close
end
end
@@ -203,9 +203,9 @@ class ClientTest < ActionCable::TestCase
concurrently(clients) do |c|
assert_equal({ "type" => "welcome" }, c.read_message) # pop the first welcome message off the stack
c.send_message command: "subscribe", identifier: JSON.generate(channel: "ClientTest::EchoChannel")
- assert_equal({ "identifier"=>'{"channel":"ClientTest::EchoChannel"}', "type"=>"confirm_subscription" }, c.read_message)
+ assert_equal({ "identifier" => '{"channel":"ClientTest::EchoChannel"}', "type" => "confirm_subscription" }, c.read_message)
c.send_message command: "message", identifier: JSON.generate(channel: "ClientTest::EchoChannel"), data: JSON.generate(action: "ding", message: "hello")
- assert_equal({ "identifier"=>'{"channel":"ClientTest::EchoChannel"}', "message"=>{ "dong"=>"hello" } }, c.read_message)
+ assert_equal({ "identifier" => '{"channel":"ClientTest::EchoChannel"}', "message" => { "dong" => "hello" } }, c.read_message)
barrier_1.wait WAIT_WHEN_EXPECTING_EVENT
c.send_message command: "message", identifier: JSON.generate(channel: "ClientTest::EchoChannel"), data: JSON.generate(action: "bulk", message: "hello")
barrier_2.wait WAIT_WHEN_EXPECTING_EVENT
@@ -223,9 +223,9 @@ class ClientTest < ActionCable::TestCase
concurrently(clients) do |c|
assert_equal({ "type" => "welcome" }, c.read_message) # pop the first welcome message off the stack
c.send_message command: "subscribe", identifier: JSON.generate(channel: "ClientTest::EchoChannel")
- assert_equal({ "identifier"=>'{"channel":"ClientTest::EchoChannel"}', "type"=>"confirm_subscription" }, c.read_message)
+ assert_equal({ "identifier" => '{"channel":"ClientTest::EchoChannel"}', "type" => "confirm_subscription" }, c.read_message)
c.send_message command: "message", identifier: JSON.generate(channel: "ClientTest::EchoChannel"), data: JSON.generate(action: "ding", message: "hello")
- assert_equal({ "identifier"=>'{"channel":"ClientTest::EchoChannel"}', "message"=>{ "dong"=>"hello" } }, c.read_message)
+ assert_equal({ "identifier" => '{"channel":"ClientTest::EchoChannel"}', "message" => { "dong" => "hello" } }, c.read_message)
end
concurrently(clients, &:close)
@@ -237,16 +237,16 @@ class ClientTest < ActionCable::TestCase
c = websocket_client(port)
assert_equal({ "type" => "welcome" }, c.read_message) # pop the first welcome message off the stack
c.send_message command: "subscribe", identifier: JSON.generate(channel: "ClientTest::EchoChannel")
- assert_equal({ "identifier"=>"{\"channel\":\"ClientTest::EchoChannel\"}", "type"=>"confirm_subscription" }, c.read_message)
+ assert_equal({ "identifier" => "{\"channel\":\"ClientTest::EchoChannel\"}", "type" => "confirm_subscription" }, c.read_message)
c.send_message command: "message", identifier: JSON.generate(channel: "ClientTest::EchoChannel"), data: JSON.generate(action: "delay", message: "hello")
c.close # disappear before write
c = websocket_client(port)
assert_equal({ "type" => "welcome" }, c.read_message) # pop the first welcome message off the stack
c.send_message command: "subscribe", identifier: JSON.generate(channel: "ClientTest::EchoChannel")
- assert_equal({ "identifier"=>"{\"channel\":\"ClientTest::EchoChannel\"}", "type"=>"confirm_subscription" }, c.read_message)
+ assert_equal({ "identifier" => "{\"channel\":\"ClientTest::EchoChannel\"}", "type" => "confirm_subscription" }, c.read_message)
c.send_message command: "message", identifier: JSON.generate(channel: "ClientTest::EchoChannel"), data: JSON.generate(action: "ding", message: "hello")
- assert_equal({ "identifier"=>'{"channel":"ClientTest::EchoChannel"}', "message"=>{ "dong"=>"hello" } }, c.read_message)
+ assert_equal({ "identifier" => '{"channel":"ClientTest::EchoChannel"}', "message" => { "dong" => "hello" } }, c.read_message)
c.close # disappear before read
end
end
@@ -259,7 +259,7 @@ class ClientTest < ActionCable::TestCase
c = websocket_client(port)
assert_equal({ "type" => "welcome" }, c.read_message)
c.send_message command: "subscribe", identifier: identifier
- assert_equal({ "identifier"=>"{\"channel\":\"ClientTest::EchoChannel\"}", "type"=>"confirm_subscription" }, c.read_message)
+ assert_equal({ "identifier" => "{\"channel\":\"ClientTest::EchoChannel\"}", "type" => "confirm_subscription" }, c.read_message)
assert_equal(1, app.connections.count)
assert(app.remote_connections.where(identifier: identifier))
@@ -280,7 +280,7 @@ class ClientTest < ActionCable::TestCase
c = websocket_client(port)
assert_equal({ "type" => "welcome" }, c.read_message)
c.send_message command: "subscribe", identifier: JSON.generate(channel: "ClientTest::EchoChannel")
- assert_equal({ "identifier"=>"{\"channel\":\"ClientTest::EchoChannel\"}", "type"=>"confirm_subscription" }, c.read_message)
+ assert_equal({ "identifier" => "{\"channel\":\"ClientTest::EchoChannel\"}", "type" => "confirm_subscription" }, c.read_message)
ActionCable.server.restart
c.wait_for_close
diff --git a/actioncable/test/connection/identifier_test.rb b/actioncable/test/connection/identifier_test.rb
index a4dfcc06f0..f3d3bc0603 100644
--- a/actioncable/test/connection/identifier_test.rb
+++ b/actioncable/test/connection/identifier_test.rb
@@ -53,7 +53,7 @@ class ActionCable::Connection::IdentifierTest < ActionCable::TestCase
end
end
- protected
+ private
def open_connection_with_stubbed_pubsub
server = TestServer.new
server.stubs(:adapter).returns(stub_everything("adapter"))
diff --git a/actioncable/test/connection/multiple_identifiers_test.rb b/actioncable/test/connection/multiple_identifiers_test.rb
index 67e68355c5..ca1a08f4d6 100644
--- a/actioncable/test/connection/multiple_identifiers_test.rb
+++ b/actioncable/test/connection/multiple_identifiers_test.rb
@@ -19,7 +19,7 @@ class ActionCable::Connection::MultipleIdentifiersTest < ActionCable::TestCase
end
end
- protected
+ private
def open_connection_with_stubbed_pubsub
server = TestServer.new
server.stubs(:pubsub).returns(stub_everything("pubsub"))
diff --git a/actioncable/test/connection/string_identifier_test.rb b/actioncable/test/connection/string_identifier_test.rb
index 87484765e5..6d53e249cb 100644
--- a/actioncable/test/connection/string_identifier_test.rb
+++ b/actioncable/test/connection/string_identifier_test.rb
@@ -21,7 +21,7 @@ class ActionCable::Connection::StringIdentifierTest < ActionCable::TestCase
end
end
- protected
+ private
def open_connection_with_stubbed_pubsub
@server = TestServer.new
@server.stubs(:pubsub).returns(stub_everything("pubsub"))
diff --git a/actioncable/test/javascript/src/test_helpers/consumer_test_helper.coffee b/actioncable/test/javascript/src/test_helpers/consumer_test_helper.coffee
index 6b145dede8..a9e95c37f0 100644
--- a/actioncable/test/javascript/src/test_helpers/consumer_test_helper.coffee
+++ b/actioncable/test/javascript/src/test_helpers/consumer_test_helper.coffee
@@ -21,6 +21,16 @@ TestHelpers.consumerTest = (name, options = {}, callback) ->
assert.equal clients.length, 1
assert.equal clients[0].readyState, WebSocket.OPEN
+ server.broadcastTo = (subscription, data = {}, callback) ->
+ data.identifier = subscription.identifier
+
+ if data.message_type
+ data.type = ActionCable.INTERNAL.message_types[data.message_type]
+ delete data.message_type
+
+ server.send(JSON.stringify(data))
+ TestHelpers.defer(callback)
+
done = ->
consumer.disconnect()
server.close()
diff --git a/actioncable/test/javascript/src/test_helpers/index.coffee b/actioncable/test/javascript/src/test_helpers/index.coffee
index d36524d9cc..c84cbbcb2c 100644
--- a/actioncable/test/javascript/src/test_helpers/index.coffee
+++ b/actioncable/test/javascript/src/test_helpers/index.coffee
@@ -4,5 +4,8 @@
ActionCable.TestHelpers =
testURL: "ws://cable.example.com/"
+ defer: (callback) ->
+ setTimeout(callback, 1)
+
originalWebSocket = ActionCable.WebSocket
QUnit.testDone -> ActionCable.WebSocket = originalWebSocket
diff --git a/actioncable/test/javascript/src/unit/subscription_test.coffee b/actioncable/test/javascript/src/unit/subscription_test.coffee
new file mode 100644
index 0000000000..07027ed170
--- /dev/null
+++ b/actioncable/test/javascript/src/unit/subscription_test.coffee
@@ -0,0 +1,40 @@
+{module, test} = QUnit
+{consumerTest} = ActionCable.TestHelpers
+
+module "ActionCable.Subscription", ->
+ consumerTest "#initialized callback", ({server, consumer, assert, done}) ->
+ consumer.subscriptions.create "chat",
+ initialized: ->
+ assert.ok true
+ done()
+
+ consumerTest "#connected callback", ({server, consumer, assert, done}) ->
+ subscription = consumer.subscriptions.create "chat",
+ connected: ->
+ assert.ok true
+ done()
+
+ server.broadcastTo(subscription, message_type: "confirmation")
+
+ consumerTest "#disconnected callback", ({server, consumer, assert, done}) ->
+ subscription = consumer.subscriptions.create "chat",
+ disconnected: ->
+ assert.ok true
+ done()
+
+ server.broadcastTo subscription, message_type: "confirmation", ->
+ server.close()
+
+ consumerTest "#perform", ({consumer, server, assert, done}) ->
+ subscription = consumer.subscriptions.create "chat",
+ connected: ->
+ @perform(publish: "hi")
+
+ server.on "message", (message) ->
+ data = JSON.parse(message)
+ assert.equal data.identifier, subscription.identifier
+ assert.equal data.command, "message"
+ assert.deepEqual data.data, JSON.stringify(action: { publish: "hi" })
+ done()
+
+ server.broadcastTo(subscription, message_type: "confirmation")
diff --git a/actioncable/test/javascript/src/unit/subscriptions_test.coffee b/actioncable/test/javascript/src/unit/subscriptions_test.coffee
new file mode 100644
index 0000000000..170b370e4a
--- /dev/null
+++ b/actioncable/test/javascript/src/unit/subscriptions_test.coffee
@@ -0,0 +1,25 @@
+{module, test} = QUnit
+{consumerTest} = ActionCable.TestHelpers
+
+module "ActionCable.Subscriptions", ->
+ consumerTest "create subscription with channel string", ({consumer, server, assert, done}) ->
+ channel = "chat"
+
+ server.on "message", (message) ->
+ data = JSON.parse(message)
+ assert.equal data.command, "subscribe"
+ assert.equal data.identifier, JSON.stringify({channel})
+ done()
+
+ consumer.subscriptions.create(channel)
+
+ consumerTest "create subscription with channel object", ({consumer, server, assert, done}) ->
+ channel = channel: "chat", room: "action"
+
+ server.on "message", (message) ->
+ data = JSON.parse(message)
+ assert.equal data.command, "subscribe"
+ assert.equal data.identifier, JSON.stringify(channel)
+ done()
+
+ consumer.subscriptions.create(channel)
diff --git a/actioncable/test/stubs/room.rb b/actioncable/test/stubs/room.rb
index 9e521cf3a6..1664b07d12 100644
--- a/actioncable/test/stubs/room.rb
+++ b/actioncable/test/stubs/room.rb
@@ -1,7 +1,7 @@
class Room
attr_reader :id, :name
- def initialize(id, name="Campfire")
+ def initialize(id, name = "Campfire")
@id = id
@name = name
end
diff --git a/actioncable/test/subscription_adapter/evented_redis_test.rb b/actioncable/test/subscription_adapter/evented_redis_test.rb
index f316bc46ef..2401950aa7 100644
--- a/actioncable/test/subscription_adapter/evented_redis_test.rb
+++ b/actioncable/test/subscription_adapter/evented_redis_test.rb
@@ -23,6 +23,32 @@ class EventedRedisAdapterTest < ActionCable::TestCase
$VERBOSE = @previous_verbose
end
+ def test_slow_eventmachine
+ require "eventmachine"
+ require "thread"
+
+ lock = Mutex.new
+
+ EventMachine.singleton_class.class_eval do
+ alias_method :delayed_initialize_event_machine, :initialize_event_machine
+ define_method(:initialize_event_machine) do
+ lock.synchronize do
+ sleep 0.5
+ delayed_initialize_event_machine
+ end
+ end
+ end
+
+ test_basic_broadcast
+ ensure
+ lock.synchronize do
+ EventMachine.singleton_class.class_eval do
+ alias_method :initialize_event_machine, :delayed_initialize_event_machine
+ remove_method :delayed_initialize_event_machine
+ end
+ end
+ end
+
def cable_config
{ adapter: "evented_redis", url: "redis://127.0.0.1:6379/12" }
end
diff --git a/actioncable/test/worker_test.rb b/actioncable/test/worker_test.rb
index 63dc453840..3385593f74 100644
--- a/actioncable/test/worker_test.rb
+++ b/actioncable/test/worker_test.rb
@@ -9,7 +9,7 @@ class WorkerTest < ActiveSupport::TestCase
end
def process(message)
- @last_action = [ :process, message ]
+ @last_action = [ :process, message ]
end
def connection
diff --git a/actionmailer/bin/test b/actionmailer/bin/test
index 84a05bba08..a7beb14b27 100755
--- a/actionmailer/bin/test
+++ b/actionmailer/bin/test
@@ -2,5 +2,3 @@
COMPONENT_ROOT = File.expand_path("..", __dir__)
require File.expand_path("../tools/test", COMPONENT_ROOT)
-
-exit Minitest.run(ARGV)
diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb
index e9966c7ff5..19408f2a48 100644
--- a/actionmailer/lib/action_mailer/base.rb
+++ b/actionmailer/lib/action_mailer/base.rb
@@ -544,9 +544,9 @@ module ActionMailer
end
end
- protected
+ private
- def set_payload_for_mail(payload, mail) #:nodoc:
+ def set_payload_for_mail(payload, mail)
payload[:mailer] = name
payload[:message_id] = mail.message_id
payload[:subject] = mail.subject
@@ -558,7 +558,7 @@ module ActionMailer
payload[:mail] = mail.encoded
end
- def method_missing(method_name, *args) # :nodoc:
+ def method_missing(method_name, *args)
if action_methods.include?(method_name.to_s)
MessageDelivery.new(self, method_name, *args)
else
@@ -566,9 +566,7 @@ module ActionMailer
end
end
- private
-
- def respond_to_missing?(method, include_all = false) #:nodoc:
+ def respond_to_missing?(method, include_all = false)
action_methods.include?(method.to_s)
end
end
@@ -601,7 +599,7 @@ module ActionMailer
def body; "" end
def header; {} end
- def respond_to?(string, include_all=false)
+ def respond_to?(string, include_all = false)
true
end
@@ -830,7 +828,7 @@ module ActionMailer
message
end
- protected
+ private
# Used by #mail to set the content type of the message.
#
@@ -841,7 +839,7 @@ module ActionMailer
# If there is no content type passed in via headers, and there are no
# attachments, or the message is multipart, then the default content type is
# used.
- def set_content_type(m, user_content_type, class_default)
+ def set_content_type(m, user_content_type, class_default) # :doc:
params = m.content_type_parameters || {}
case
when user_content_type.present?
@@ -863,18 +861,16 @@ module ActionMailer
# If it does not find a translation for the +subject+ under the specified scope it will default to a
# humanized version of the <tt>action_name</tt>.
# If the subject has interpolations, you can pass them through the +interpolations+ parameter.
- def default_i18n_subject(interpolations = {})
+ def default_i18n_subject(interpolations = {}) # :doc:
mailer_scope = self.class.mailer_name.tr("/", ".")
I18n.t(:subject, interpolations.merge(scope: [mailer_scope, action_name], default: action_name.humanize))
end
# Emails do not support relative path links.
- def self.supports_path?
+ def self.supports_path? # :doc:
false
end
- private
-
def apply_defaults(headers)
default_values = self.class.default.map do |key, value|
[
@@ -933,7 +929,7 @@ module ActionMailer
def create_parts_from_responses(m, responses)
if responses.size == 1 && !m.has_attachments?
- responses[0].each { |k,v| m[k] = v }
+ responses[0].each { |k, v| m[k] = v }
elsif responses.size > 1 && m.has_attachments?
container = Mail::Part.new
container.content_type = "multipart/alternative"
diff --git a/actionmailer/lib/action_mailer/delivery_methods.rb b/actionmailer/lib/action_mailer/delivery_methods.rb
index 6be2c91da6..be98f4c65e 100644
--- a/actionmailer/lib/action_mailer/delivery_methods.rb
+++ b/actionmailer/lib/action_mailer/delivery_methods.rb
@@ -52,13 +52,13 @@ module ActionMailer
# add_delivery_method :sendmail, Mail::Sendmail,
# location: '/usr/sbin/sendmail',
# arguments: '-i'
- def add_delivery_method(symbol, klass, default_options={})
+ def add_delivery_method(symbol, klass, default_options = {})
class_attribute(:"#{symbol}_settings") unless respond_to?(:"#{symbol}_settings")
send(:"#{symbol}_settings=", default_options)
self.delivery_methods = delivery_methods.merge(symbol.to_sym => klass).freeze
end
- def wrap_delivery_behavior(mail, method=nil, options=nil) # :nodoc:
+ def wrap_delivery_behavior(mail, method = nil, options = nil) # :nodoc:
method ||= self.delivery_method
mail.delivery_handler = self
diff --git a/actionmailer/lib/action_mailer/inline_preview_interceptor.rb b/actionmailer/lib/action_mailer/inline_preview_interceptor.rb
index b7318f0092..9087d335fa 100644
--- a/actionmailer/lib/action_mailer/inline_preview_interceptor.rb
+++ b/actionmailer/lib/action_mailer/inline_preview_interceptor.rb
@@ -11,7 +11,7 @@ module ActionMailer
# ActionMailer::Base.preview_interceptors.delete(ActionMailer::InlinePreviewInterceptor)
#
class InlinePreviewInterceptor
- PATTERN = /src=(?:"cid:[^"]+"|'cid:[^']+')/i
+ PATTERN = /src=(?:"cid:[^"]+"|'cid:[^']+')/i
include Base64
diff --git a/actionmailer/lib/action_mailer/message_delivery.rb b/actionmailer/lib/action_mailer/message_delivery.rb
index 994d297768..cf7c57e6bf 100644
--- a/actionmailer/lib/action_mailer/message_delivery.rb
+++ b/actionmailer/lib/action_mailer/message_delivery.rb
@@ -56,7 +56,7 @@ module ActionMailer
# * <tt>:wait</tt> - Enqueue the email to be delivered with a delay
# * <tt>:wait_until</tt> - Enqueue the email to be delivered at (after) a specific date / time
# * <tt>:queue</tt> - Enqueue the email on the specified queue
- def deliver_later!(options={})
+ def deliver_later!(options = {})
enqueue_delivery :deliver_now!, options
end
@@ -72,7 +72,7 @@ module ActionMailer
# * <tt>:wait</tt> - Enqueue the email to be delivered with a delay.
# * <tt>:wait_until</tt> - Enqueue the email to be delivered at (after) a specific date / time.
# * <tt>:queue</tt> - Enqueue the email on the specified queue.
- def deliver_later(options={})
+ def deliver_later(options = {})
enqueue_delivery :deliver_now, options
end
@@ -106,7 +106,7 @@ module ActionMailer
end
end
- def enqueue_delivery(delivery_method, options={})
+ def enqueue_delivery(delivery_method, options = {})
if processed?
::Kernel.raise "You've accessed the message before asking to " \
"deliver it later, so you may have made local changes that would " \
diff --git a/actionmailer/lib/action_mailer/preview.rb b/actionmailer/lib/action_mailer/preview.rb
index 93a11453bf..c147ca78d0 100644
--- a/actionmailer/lib/action_mailer/preview.rb
+++ b/actionmailer/lib/action_mailer/preview.rb
@@ -94,22 +94,22 @@ module ActionMailer
name.sub(/Preview$/, "").underscore
end
- protected
- def load_previews #:nodoc:
+ private
+ def load_previews
if preview_path
Dir["#{preview_path}/**/*_preview.rb"].each { |file| require_dependency file }
end
end
- def preview_path #:nodoc:
+ def preview_path
Base.preview_path
end
- def show_previews #:nodoc:
+ def show_previews
Base.show_previews
end
- def inform_preview_interceptors(message) #:nodoc:
+ def inform_preview_interceptors(message)
Base.preview_interceptors.each do |interceptor|
interceptor.previewing_email(message)
end
diff --git a/actionmailer/lib/action_mailer/railtie.rb b/actionmailer/lib/action_mailer/railtie.rb
index c47d7781cc..913df8cf93 100644
--- a/actionmailer/lib/action_mailer/railtie.rb
+++ b/actionmailer/lib/action_mailer/railtie.rb
@@ -28,7 +28,7 @@ module ActionMailer
options.cache_store ||= Rails.cache
if options.show_previews
- options.preview_path ||= defined?(Rails.root) ? "#{Rails.root}/test/mailers/previews" : nil
+ options.preview_path ||= defined?(Rails.root) ? "#{Rails.root}/test/mailers/previews" : nil
end
# make sure readers methods get compiled
@@ -44,7 +44,7 @@ module ActionMailer
register_preview_interceptors(options.delete(:preview_interceptors))
register_observers(options.delete(:observers))
- options.each { |k,v| send("#{k}=", v) }
+ options.each { |k, v| send("#{k}=", v) }
end
ActiveSupport.on_load(:action_dispatch_integration_test) { include ActionMailer::TestCase::ClearTestDeliveries }
diff --git a/actionmailer/lib/action_mailer/test_case.rb b/actionmailer/lib/action_mailer/test_case.rb
index 1d09a4ee96..ebc7b37961 100644
--- a/actionmailer/lib/action_mailer/test_case.rb
+++ b/actionmailer/lib/action_mailer/test_case.rb
@@ -73,38 +73,36 @@ module ActionMailer
end
end
- protected
+ private
- def initialize_test_deliveries # :nodoc:
+ def initialize_test_deliveries
set_delivery_method :test
@old_perform_deliveries = ActionMailer::Base.perform_deliveries
ActionMailer::Base.perform_deliveries = true
ActionMailer::Base.deliveries.clear
end
- def restore_test_deliveries # :nodoc:
+ def restore_test_deliveries
restore_delivery_method
ActionMailer::Base.perform_deliveries = @old_perform_deliveries
end
- def set_delivery_method(method) # :nodoc:
+ def set_delivery_method(method)
@old_delivery_method = ActionMailer::Base.delivery_method
ActionMailer::Base.delivery_method = method
end
- def restore_delivery_method # :nodoc:
+ def restore_delivery_method
ActionMailer::Base.deliveries.clear
ActionMailer::Base.delivery_method = @old_delivery_method
end
- def set_expected_mail # :nodoc:
+ def set_expected_mail
@expected = Mail.new
@expected.content_type ["text", "plain", { "charset" => charset }]
@expected.mime_version = "1.0"
end
- private
-
def charset
"UTF-8"
end
diff --git a/actionmailer/lib/rails/generators/mailer/mailer_generator.rb b/actionmailer/lib/rails/generators/mailer/mailer_generator.rb
index 9dd7ee7a27..4a8d0178de 100644
--- a/actionmailer/lib/rails/generators/mailer/mailer_generator.rb
+++ b/actionmailer/lib/rails/generators/mailer/mailer_generator.rb
@@ -19,12 +19,11 @@ module Rails
hook_for :template_engine, :test_framework
- protected
- def file_name
+ private
+ def file_name # :doc:
@_file_name ||= super.gsub(/_mailer/i, "")
end
- private
def application_mailer_file_name
@_application_mailer_file_name ||= if mountable_engine?
"app/mailers/#{namespaced_path}/application_mailer.rb"
diff --git a/actionmailer/test/base_test.rb b/actionmailer/test/base_test.rb
index 3bca69890d..91049e33f9 100644
--- a/actionmailer/test/base_test.rb
+++ b/actionmailer/test/base_test.rb
@@ -829,7 +829,7 @@ class BaseTest < ActiveSupport::TestCase
assert_equal "special indeed!", mail["X-Special-Header"].to_s
end
- protected
+ private
# Execute the block setting the given values and restoring old values after
# the block is executed.
diff --git a/actionmailer/test/delivery_methods_test.rb b/actionmailer/test/delivery_methods_test.rb
index 898d32c1e2..f64a69019f 100644
--- a/actionmailer/test/delivery_methods_test.rb
+++ b/actionmailer/test/delivery_methods_test.rb
@@ -87,7 +87,7 @@ class MailDeliveryTest < ActiveSupport::TestCase
from: "jose@test.plataformatec.com"
}
- def welcome(hash={})
+ def welcome(hash = {})
mail(DEFAULT_HEADERS.merge(hash))
end
end
diff --git a/actionmailer/test/i18n_with_controller_test.rb b/actionmailer/test/i18n_with_controller_test.rb
index 039685ffe5..a87a9c9861 100644
--- a/actionmailer/test/i18n_with_controller_test.rb
+++ b/actionmailer/test/i18n_with_controller_test.rb
@@ -66,7 +66,7 @@ class ActionMailerI18nWithControllerTest < ActionDispatch::IntegrationTest
end
end
- protected
+ private
def with_translation(locale, data)
I18n.backend.store_translations(locale, data)
diff --git a/actionmailer/test/mail_helper_test.rb b/actionmailer/test/mail_helper_test.rb
index 972629e477..a2e04b5f48 100644
--- a/actionmailer/test/mail_helper_test.rb
+++ b/actionmailer/test/mail_helper_test.rb
@@ -65,7 +65,7 @@ The second
end
end
- protected
+ private
def mail_with_defaults(&block)
mail(to: "test@localhost", from: "tester@example.com",
diff --git a/actionmailer/test/mailers/base_mailer.rb b/actionmailer/test/mailers/base_mailer.rb
index 0d45376070..2a8884959c 100644
--- a/actionmailer/test/mailers/base_mailer.rb
+++ b/actionmailer/test/mailers/base_mailer.rb
@@ -100,18 +100,18 @@ class BaseMailer < ActionMailer::Base
end
end
- def implicit_different_template(template_name="")
+ def implicit_different_template(template_name = "")
mail(template_name: template_name)
end
- def explicit_different_template(template_name="")
+ def explicit_different_template(template_name = "")
mail do |format|
format.text { render template: "#{mailer_name}/#{template_name}" }
format.html { render template: "#{mailer_name}/#{template_name}" }
end
end
- def different_layout(layout_name="")
+ def different_layout(layout_name = "")
mail do |format|
format.text { render layout: layout_name }
format.html { render layout: layout_name }
diff --git a/actionmailer/test/message_delivery_test.rb b/actionmailer/test/message_delivery_test.rb
index 0eb81a8496..a79d77e1e5 100644
--- a/actionmailer/test/message_delivery_test.rb
+++ b/actionmailer/test/message_delivery_test.rb
@@ -76,7 +76,7 @@ class MessageDeliveryTest < ActiveSupport::TestCase
test "should enqueue a delivery with a delay" do
travel_to Time.new(2004, 11, 24, 01, 04, 44) do
- assert_performed_with(job: ActionMailer::DeliveryJob, at: Time.current.to_f+600.seconds, args: ["DelayedMailer", "test_message", "deliver_now", 1, 2, 3]) do
+ assert_performed_with(job: ActionMailer::DeliveryJob, at: Time.current.to_f + 600.seconds, args: ["DelayedMailer", "test_message", "deliver_now", 1, 2, 3]) do
@mail.deliver_later wait: 600.seconds
end
end
diff --git a/actionmailer/test/url_test.rb b/actionmailer/test/url_test.rb
index 27f6e8a491..6dbfb3a1ff 100644
--- a/actionmailer/test/url_test.rb
+++ b/actionmailer/test/url_test.rb
@@ -50,11 +50,11 @@ class ActionMailerUrlTest < ActionMailer::TestCase
end
end
- def encode( text, charset="UTF-8" )
- quoted_printable( text, charset )
+ def encode(text, charset = "UTF-8")
+ quoted_printable(text, charset)
end
- def new_mail( charset="UTF-8" )
+ def new_mail(charset = "UTF-8")
mail = Mail.new
mail.mime_version = "1.0"
if charset
@@ -81,7 +81,7 @@ class ActionMailerUrlTest < ActionMailer::TestCase
AppRoutes.draw do
ActiveSupport::Deprecation.silence do
get ":controller(/:action(/:id))"
- get "/welcome" => "foo#bar", as: "welcome"
+ get "/welcome" => "foo#bar", as: "welcome"
get "/dummy_model" => "foo#baz", as: "dummy_model"
end
end
diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md
index eef3db8f24..2f0c677ad1 100644
--- a/actionpack/CHANGELOG.md
+++ b/actionpack/CHANGELOG.md
@@ -1,3 +1,51 @@
+* Use accept header in integration tests with `as: :json`
+
+ Instead of appending the `format` to the request path, Rails will figure
+ out the format from the header instead.
+
+ This allows devs to use `:as` on routes that don't have a format.
+
+ Fixes #27144.
+
+ *Kasper Timm Hansen*
+
+* Reset a new session directly after its creation in `ActionDispatch::IntegrationTest#open_session`.
+
+ Fixes #22742.
+
+ *Tawan Sierek*
+
+* Fixes incorrect output from rails routes when using singular resources.
+
+ Fixes #26606.
+
+ *Erick Reyna*
+
+* Fixes multiple calls to `logger.fatal` instead of a single call,
+ for every line in an exception backtrace, when printing trace
+ from `DebugExceptions` middleware.
+
+ Fixes #26134.
+
+ *Vipul A M*
+
+* Add support for arbitrary hashes in strong parameters:
+
+ ```ruby
+ params.permit(preferences: {})
+ ```
+
+ *Xavier Noria*
+
+* Add `ActionController::Parameters#merge!`, which behaves the same as `Hash#merge!`.
+
+ *Yuji Yaginuma*
+
+* Allow keys not found in `RACK_KEY_TRANSLATION` for setting the environment when rendering
+ arbitrary templates.
+
+ *Sammy Larbi*
+
* Remove deprecated support to non-keyword arguments in `ActionDispatch::IntegrationTest#process`,
`#get`, `#post`, `#patch`, `#put`, `#delete`, and `#head`.
diff --git a/actionpack/bin/test b/actionpack/bin/test
index 84a05bba08..a7beb14b27 100755
--- a/actionpack/bin/test
+++ b/actionpack/bin/test
@@ -2,5 +2,3 @@
COMPONENT_ROOT = File.expand_path("..", __dir__)
require File.expand_path("../tools/test", COMPONENT_ROOT)
-
-exit Minitest.run(ARGV)
diff --git a/actionpack/lib/abstract_controller/base.rb b/actionpack/lib/abstract_controller/base.rb
index 8e588812f8..603c2e9ea7 100644
--- a/actionpack/lib/abstract_controller/base.rb
+++ b/actionpack/lib/abstract_controller/base.rb
@@ -215,7 +215,7 @@ module AbstractController
# ==== Returns
# * <tt>string</tt> - The name of the method that handles the action
# * false - No valid method name could be found.
- # Raise AbstractController::ActionNotFound.
+ # Raise +AbstractController::ActionNotFound+.
def _find_action_name(action_name)
_valid_action_name?(action_name) && method_for_action(action_name)
end
@@ -231,11 +231,11 @@ module AbstractController
# with a template matching the action name is considered to exist.
#
# If you override this method to handle additional cases, you may
- # also provide a method (like _handle_method_missing) to handle
+ # also provide a method (like +_handle_method_missing+) to handle
# the case.
#
- # If none of these conditions are true, and method_for_action
- # returns nil, an AbstractController::ActionNotFound exception will be raised.
+ # If none of these conditions are true, and +method_for_action+
+ # returns +nil+, an +AbstractController::ActionNotFound+ exception will be raised.
#
# ==== Parameters
# * <tt>action_name</tt> - An action name to find a method name for
diff --git a/actionpack/lib/abstract_controller/caching.rb b/actionpack/lib/abstract_controller/caching.rb
index d222880922..26e3f08bc1 100644
--- a/actionpack/lib/abstract_controller/caching.rb
+++ b/actionpack/lib/abstract_controller/caching.rb
@@ -52,9 +52,9 @@ module AbstractController
self.class._view_cache_dependencies.map { |dep| instance_exec(&dep) }.compact
end
- protected
+ private
# Convenience accessor.
- def cache(key, options = {}, &block)
+ def cache(key, options = {}, &block) # :doc:
if cache_configured?
cache_store.fetch(ActiveSupport::Cache.expand_cache_key(key, :controller), options, &block)
else
diff --git a/actionpack/lib/abstract_controller/collector.rb b/actionpack/lib/abstract_controller/collector.rb
index 57714b0588..40ae5aa1ca 100644
--- a/actionpack/lib/abstract_controller/collector.rb
+++ b/actionpack/lib/abstract_controller/collector.rb
@@ -19,7 +19,7 @@ module AbstractController
generate_method_for_mime(mime) unless instance_methods.include?(mime.to_sym)
end
- protected
+ private
def method_missing(symbol, &block)
unless mime_constant = Mime[symbol]
diff --git a/actionpack/lib/abstract_controller/rendering.rb b/actionpack/lib/abstract_controller/rendering.rb
index 2cb22cb53d..d339580435 100644
--- a/actionpack/lib/abstract_controller/rendering.rb
+++ b/actionpack/lib/abstract_controller/rendering.rb
@@ -1,6 +1,4 @@
require "abstract_controller/error"
-require "active_support/concern"
-require "active_support/core_ext/class/attribute"
require "action_view"
require "action_view/view_paths"
require "set"
@@ -80,7 +78,7 @@ module AbstractController
# <tt>render :action => "foo"</tt> and <tt>render "foo/bar"</tt> to
# <tt>render :file => "foo/bar"</tt>.
# :api: plugin
- def _normalize_args(action=nil, options={})
+ def _normalize_args(action = nil, options = {})
if action.respond_to?(:permitted?)
if action.permitted?
action
diff --git a/actionpack/lib/action_controller/metal.rb b/actionpack/lib/action_controller/metal.rb
index ed93a2f09c..9dab7aeef4 100644
--- a/actionpack/lib/action_controller/metal.rb
+++ b/actionpack/lib/action_controller/metal.rb
@@ -139,8 +139,8 @@ module ActionController
end
end
- def self.encoding_for_param(action, param) # :nodoc:
- ::Encoding::UTF_8
+ def self.binary_params_for?(action) # :nodoc:
+ false
end
# Delegates to the class' <tt>controller_name</tt>
diff --git a/actionpack/lib/action_controller/metal/conditional_get.rb b/actionpack/lib/action_controller/metal/conditional_get.rb
index 89bf60a0bb..eb636fa3f6 100644
--- a/actionpack/lib/action_controller/metal/conditional_get.rb
+++ b/actionpack/lib/action_controller/metal/conditional_get.rb
@@ -238,7 +238,7 @@ module ActionController
)
options.delete(:private)
- response.cache_control[:extras] = options.map { |k,v| "#{k}=#{v}" }
+ response.cache_control[:extras] = options.map { |k, v| "#{k}=#{v}" }
response.date = Time.now unless response.date?
end
diff --git a/actionpack/lib/action_controller/metal/data_streaming.rb b/actionpack/lib/action_controller/metal/data_streaming.rb
index f089c8423b..731e03e2fc 100644
--- a/actionpack/lib/action_controller/metal/data_streaming.rb
+++ b/actionpack/lib/action_controller/metal/data_streaming.rb
@@ -11,7 +11,7 @@ module ActionController #:nodoc:
DEFAULT_SEND_FILE_TYPE = "application/octet-stream".freeze #:nodoc:
DEFAULT_SEND_FILE_DISPOSITION = "attachment".freeze #:nodoc:
- protected
+ private
# Sends the file. This uses a server-appropriate method (such as X-Sendfile)
# via the Rack::Sendfile middleware. The header to use is set via
# +config.action_dispatch.x_sendfile_header+.
@@ -70,7 +70,6 @@ module ActionController #:nodoc:
send_file_headers! options
self.status = options[:status] || 200
- self.content_type = options[:type] if options.key?(:type)
self.content_type = options[:content_type] if options.key?(:content_type)
response.send_file path
end
@@ -109,10 +108,12 @@ module ActionController #:nodoc:
render options.slice(:status, :content_type).merge(body: data)
end
- private
def send_file_headers!(options)
type_provided = options.has_key?(:type)
+ self.content_type = DEFAULT_SEND_FILE_TYPE
+ response.sending_file = true
+
content_type = options.fetch(:type, DEFAULT_SEND_FILE_TYPE)
raise ArgumentError, ":type option required" if content_type.nil?
@@ -137,8 +138,6 @@ module ActionController #:nodoc:
headers["Content-Transfer-Encoding"] = "binary"
- response.sending_file = true
-
# Fix a problem with IE 6.0 on opening downloaded files:
# If Cache-Control: no-cache is set (which Rails does by default),
# IE removes the file it just downloaded from its cache immediately
diff --git a/actionpack/lib/action_controller/metal/etag_with_template_digest.rb b/actionpack/lib/action_controller/metal/etag_with_template_digest.rb
index 6c103bb042..798564db96 100644
--- a/actionpack/lib/action_controller/metal/etag_with_template_digest.rb
+++ b/actionpack/lib/action_controller/metal/etag_with_template_digest.rb
@@ -40,7 +40,7 @@ module ActionController
end
# Pick the template digest to include in the ETag. If the +:template+ option
- # is present, use the named template. If +:template+ is nil or absent, use
+ # is present, use the named template. If +:template+ is +nil+ or absent, use
# the default controller/action template. If +:template+ is false, omit the
# template digest from the ETag.
def pick_template_for_etag(options)
diff --git a/actionpack/lib/action_controller/metal/exceptions.rb b/actionpack/lib/action_controller/metal/exceptions.rb
index 3761e6172b..175dd9eb9e 100644
--- a/actionpack/lib/action_controller/metal/exceptions.rb
+++ b/actionpack/lib/action_controller/metal/exceptions.rb
@@ -14,7 +14,7 @@ module ActionController
class RoutingError < ActionControllerError #:nodoc:
attr_reader :failures
- def initialize(message, failures=[])
+ def initialize(message, failures = [])
super(message)
@failures = failures
end
diff --git a/actionpack/lib/action_controller/metal/flash.rb b/actionpack/lib/action_controller/metal/flash.rb
index 65351284b9..347fbf0e74 100644
--- a/actionpack/lib/action_controller/metal/flash.rb
+++ b/actionpack/lib/action_controller/metal/flash.rb
@@ -42,7 +42,7 @@ module ActionController #:nodoc:
end
end
- protected
+ private
def redirect_to(options = {}, response_status_and_flash = {}) #:doc:
self.class._flash_types.each do |flash_type|
if type = response_status_and_flash.delete(flash_type)
diff --git a/actionpack/lib/action_controller/metal/force_ssl.rb b/actionpack/lib/action_controller/metal/force_ssl.rb
index b8976497a4..9d43e752ac 100644
--- a/actionpack/lib/action_controller/metal/force_ssl.rb
+++ b/actionpack/lib/action_controller/metal/force_ssl.rb
@@ -89,7 +89,7 @@ module ActionController
end
secure_url = ActionDispatch::Http::URL.url_for(options.slice(*URL_OPTIONS))
- flash.keep if respond_to?(:flash)
+ flash.keep if respond_to?(:flash) && request.respond_to?(:flash)
redirect_to secure_url, options.slice(*REDIRECT_OPTIONS)
end
end
diff --git a/actionpack/lib/action_controller/metal/http_authentication.rb b/actionpack/lib/action_controller/metal/http_authentication.rb
index a335bf109e..0575360068 100644
--- a/actionpack/lib/action_controller/metal/http_authentication.rb
+++ b/actionpack/lib/action_controller/metal/http_authentication.rb
@@ -28,7 +28,7 @@ module ActionController
# class ApplicationController < ActionController::Base
# before_action :set_account, :authenticate
#
- # protected
+ # private
# def set_account
# @account = Account.find_by(url_name: request.subdomains.first)
# end
@@ -224,7 +224,7 @@ module ActionController
# Returns the expected response for a request of +http_method+ to +uri+ with the decoded +credentials+ and the expected +password+
# Optional parameter +password_is_ha1+ is set to +true+ by default, since best practice is to store ha1 digest instead
# of a plain-text password.
- def expected_response(http_method, uri, credentials, password, password_is_ha1=true)
+ def expected_response(http_method, uri, credentials, password, password_is_ha1 = true)
ha1 = password_is_ha1 ? password : ha1(credentials, password)
ha2 = ::Digest::MD5.hexdigest([http_method.to_s.upcase, uri].join(":"))
::Digest::MD5.hexdigest([ha1, credentials[:nonce], credentials[:nc], credentials[:cnonce], credentials[:qop], ha2].join(":"))
@@ -246,7 +246,7 @@ module ActionController
def decode_credentials(header)
ActiveSupport::HashWithIndifferentAccess[header.to_s.gsub(/^Digest\s+/, "").split(",").map do |pair|
key, value = pair.split("=", 2)
- [key.strip, value.to_s.gsub(/^"|"$/,"").delete('\'')]
+ [key.strip, value.to_s.gsub(/^"|"$/, "").delete('\'')]
end]
end
@@ -314,7 +314,7 @@ module ActionController
# Can be much shorter if the Stale directive is implemented. This would
# allow a user to use new nonce without prompting the user again for their
# username and password.
- def validate_nonce(secret_key, request, value, seconds_to_timeout=5*60)
+ def validate_nonce(secret_key, request, value, seconds_to_timeout = 5 * 60)
return false if value.nil?
t = ::Base64.decode64(value).split(":").first.to_i
nonce(secret_key, t) == value && (t - Time.now.to_i).abs <= seconds_to_timeout
@@ -363,7 +363,7 @@ module ActionController
# class ApplicationController < ActionController::Base
# before_action :set_account, :authenticate
#
- # protected
+ # private
# def set_account
# @account = Account.find_by(url_name: request.subdomains.first)
# end
diff --git a/actionpack/lib/action_controller/metal/instrumentation.rb b/actionpack/lib/action_controller/metal/instrumentation.rb
index 2ede96c667..924686218f 100644
--- a/actionpack/lib/action_controller/metal/instrumentation.rb
+++ b/actionpack/lib/action_controller/metal/instrumentation.rb
@@ -46,7 +46,7 @@ module ActionController
render_output
end
- def send_file(path, options={})
+ def send_file(path, options = {})
ActiveSupport::Notifications.instrument("send_file.action_controller",
options.merge(path: path)) do
super
@@ -83,14 +83,14 @@ module ActionController
# end
#
# :api: plugin
- def cleanup_view_runtime #:nodoc:
+ def cleanup_view_runtime
yield
end
# Every time after an action is processed, this method is invoked
# with the payload, so you can add more information.
# :api: plugin
- def append_info_to_payload(payload) #:nodoc:
+ def append_info_to_payload(payload)
payload[:view_runtime] = view_runtime
end
diff --git a/actionpack/lib/action_controller/metal/live.rb b/actionpack/lib/action_controller/metal/live.rb
index 26a16104db..fed99e6c82 100644
--- a/actionpack/lib/action_controller/metal/live.rb
+++ b/actionpack/lib/action_controller/metal/live.rb
@@ -247,7 +247,7 @@ module ActionController
# Since we're processing the view in a different thread, copy the
# thread locals from the main thread to the child thread. :'(
- locals.each { |k,v| t2[k] = v }
+ locals.each { |k, v| t2[k] = v }
begin
super(name)
diff --git a/actionpack/lib/action_controller/metal/parameter_encoding.rb b/actionpack/lib/action_controller/metal/parameter_encoding.rb
index c457fd0d06..962532ff09 100644
--- a/actionpack/lib/action_controller/metal/parameter_encoding.rb
+++ b/actionpack/lib/action_controller/metal/parameter_encoding.rb
@@ -1,5 +1,5 @@
module ActionController
- # Allows encoding to be specified per parameter per action.
+ # Specify binary encoding for parameters for a given action.
module ParameterEncoding
extend ActiveSupport::Concern
@@ -13,17 +13,36 @@ module ActionController
@_parameter_encodings = {}
end
- def encoding_for_param(action, param) # :nodoc:
- if @_parameter_encodings[action.to_s] && @_parameter_encodings[action.to_s][param.to_s]
- @_parameter_encodings[action.to_s][param.to_s]
- else
- super
- end
+ def binary_params_for?(action) # :nodoc:
+ @_parameter_encodings[action.to_s]
end
- def parameter_encoding(action, param_name, encoding)
- @_parameter_encodings[action.to_s] ||= {}
- @_parameter_encodings[action.to_s][param_name.to_s] = encoding
+ # Specify that a given action's parameters should all be encoded as
+ # ASCII-8BIT (it "skips" the encoding default of UTF-8).
+ #
+ # For example, a controller would use it like this:
+ #
+ # class RepositoryController < ActionController::Base
+ # skip_parameter_encoding :show
+ #
+ # def show
+ # @repo = Repository.find_by_filesystem_path params[:file_path]
+ #
+ # # `repo_name` is guaranteed to be UTF-8, but was ASCII-8BIT, so
+ # # tag it as such
+ # @repo_name = params[:repo_name].force_encoding 'UTF-8'
+ # end
+ #
+ # def index
+ # @repositories = Repository.all
+ # end
+ # end
+ #
+ # The show action in the above controller would have all parameter values
+ # encoded as ASCII-8BIT. This is useful in the case where an application
+ # must handle data but encoding of the data is unknown, like file system data.
+ def skip_parameter_encoding(action)
+ @_parameter_encodings[action.to_s] = true
end
end
end
diff --git a/actionpack/lib/action_controller/metal/params_wrapper.rb b/actionpack/lib/action_controller/metal/params_wrapper.rb
index 9d1b740025..7fc898f034 100644
--- a/actionpack/lib/action_controller/metal/params_wrapper.rb
+++ b/actionpack/lib/action_controller/metal/params_wrapper.rb
@@ -135,7 +135,7 @@ module ActionController
#
# This method also does namespace lookup. Foo::Bar::UsersController will
# try to find Foo::Bar::User, Foo::User and finally User.
- def _default_wrap_model #:nodoc:
+ def _default_wrap_model
return nil if klass.anonymous?
model_name = klass.name.sub(/Controller$/, "").classify
@@ -205,7 +205,7 @@ module ActionController
model = name_or_model_or_options
end
- opts = Options.from_hash _wrapper_options.to_h.slice(:format).merge(options)
+ opts = Options.from_hash _wrapper_options.to_h.slice(:format).merge(options)
opts.model = model
opts.klass = self
diff --git a/actionpack/lib/action_controller/metal/redirecting.rb b/actionpack/lib/action_controller/metal/redirecting.rb
index 2b060e3698..30798c1d99 100644
--- a/actionpack/lib/action_controller/metal/redirecting.rb
+++ b/actionpack/lib/action_controller/metal/redirecting.rb
@@ -1,12 +1,4 @@
module ActionController
- class RedirectBackError < AbstractController::Error #:nodoc:
- DEFAULT_MESSAGE = 'No HTTP_REFERER was set in the request to this action, so redirect_to :back could not be called successfully. If this is a test, make sure to specify request.env["HTTP_REFERER"].'
-
- def initialize(message = nil)
- super(message || DEFAULT_MESSAGE)
- end
- end
-
module Redirecting
extend ActiveSupport::Concern
diff --git a/actionpack/lib/action_controller/metal/renderers.rb b/actionpack/lib/action_controller/metal/renderers.rb
index f8a037189c..733aca195d 100644
--- a/actionpack/lib/action_controller/metal/renderers.rb
+++ b/actionpack/lib/action_controller/metal/renderers.rb
@@ -104,7 +104,7 @@ module ActionController
#
# Since <tt>ActionController::Metal</tt> controllers cannot render, the controller
# must include <tt>AbstractController::Rendering</tt>, <tt>ActionController::Rendering</tt>,
- # and <tt>ActionController::Renderers</tt>, and have at lest one renderer.
+ # and <tt>ActionController::Renderers</tt>, and have at least one renderer.
#
# Rather than including <tt>ActionController::Renderers::All</tt> and including all renderers,
# you may specify which renderers to include by passing the renderer name or names to
diff --git a/actionpack/lib/action_controller/metal/rendering.rb b/actionpack/lib/action_controller/metal/rendering.rb
index 67365a143e..cdd09e832b 100644
--- a/actionpack/lib/action_controller/metal/rendering.rb
+++ b/actionpack/lib/action_controller/metal/rendering.rb
@@ -67,20 +67,20 @@ module ActionController
end
def _set_rendered_content_type(format)
- unless response.content_type
+ if format && !response.content_type
self.content_type = format.to_s
end
end
# Normalize arguments by catching blocks and setting them on :update.
- def _normalize_args(action=nil, options={}, &blk) #:nodoc:
+ def _normalize_args(action = nil, options = {}, &blk)
options = super
options[:update] = blk if block_given?
options
end
# Normalize both text and status options.
- def _normalize_options(options) #:nodoc:
+ def _normalize_options(options)
_normalize_text(options)
if options[:html]
@@ -103,7 +103,7 @@ module ActionController
end
# Process controller specific options, as status, content-type and location.
- def _process_options(options) #:nodoc:
+ def _process_options(options)
status, content_type, location = options.values_at(:status, :content_type, :location)
self.status = status if status
diff --git a/actionpack/lib/action_controller/metal/request_forgery_protection.rb b/actionpack/lib/action_controller/metal/request_forgery_protection.rb
index 3d3c121280..e8965a6561 100644
--- a/actionpack/lib/action_controller/metal/request_forgery_protection.rb
+++ b/actionpack/lib/action_controller/metal/request_forgery_protection.rb
@@ -152,7 +152,7 @@ module ActionController #:nodoc:
request.cookie_jar = NullCookieJar.build(request, {})
end
- protected
+ private
class NullSessionHash < Rack::Session::Abstract::SessionHash #:nodoc:
def initialize(req)
@@ -197,7 +197,7 @@ module ActionController #:nodoc:
end
end
- protected
+ private
# The actual before_action that is used to verify the CSRF token.
# Don't override this directly. Provide your own forgery protection
# strategy instead. If you override, you'll disable same-origin
@@ -208,7 +208,7 @@ module ActionController #:nodoc:
# enabled on an action, this before_action flags its after_action to
# verify that JavaScript responses are for XHR requests, ensuring they
# follow the browser's same-origin policy.
- def verify_authenticity_token
+ def verify_authenticity_token # :doc:
mark_for_same_origin_verification!
if !verified_request?
@@ -219,7 +219,7 @@ module ActionController #:nodoc:
end
end
- def handle_unverified_request
+ def handle_unverified_request # :doc:
forgery_protection_strategy.new(self).handle_unverified_request
end
@@ -233,7 +233,7 @@ module ActionController #:nodoc:
# If `verify_authenticity_token` was run (indicating that we have
# forgery protection enabled for this request) then also verify that
# we aren't serving an unauthorized cross-origin response.
- def verify_same_origin_request
+ def verify_same_origin_request # :doc:
if marked_for_same_origin_verification? && non_xhr_javascript_response?
if logger && log_warning_on_csrf_failure
logger.warn CROSS_ORIGIN_JAVASCRIPT_WARNING
@@ -243,18 +243,18 @@ module ActionController #:nodoc:
end
# GET requests are checked for cross-origin JavaScript after rendering.
- def mark_for_same_origin_verification!
+ def mark_for_same_origin_verification! # :doc:
@marked_for_same_origin_verification = request.get?
end
# If the `verify_authenticity_token` before_action ran, verify that
# JavaScript responses are only served to same-origin GET requests.
- def marked_for_same_origin_verification?
+ def marked_for_same_origin_verification? # :doc:
@marked_for_same_origin_verification ||= false
end
# Check for cross-origin JavaScript responses.
- def non_xhr_javascript_response?
+ def non_xhr_javascript_response? # :doc:
content_type =~ %r(\Atext/javascript) && !request.xhr?
end
@@ -265,20 +265,20 @@ module ActionController #:nodoc:
# * Is it a GET or HEAD request? Gets should be safe and idempotent
# * Does the form_authenticity_token match the given token value from the params?
# * Does the X-CSRF-Token header match the form_authenticity_token
- def verified_request?
+ def verified_request? # :doc:
!protect_against_forgery? || request.get? || request.head? ||
(valid_request_origin? && any_authenticity_token_valid?)
end
# Checks if any of the authenticity tokens from the request are valid.
- def any_authenticity_token_valid?
+ def any_authenticity_token_valid? # :doc:
request_authenticity_tokens.any? do |token|
valid_authenticity_token?(session, token)
end
end
# Possible authenticity tokens sent in the request.
- def request_authenticity_tokens
+ def request_authenticity_tokens # :doc:
[form_authenticity_param, request.x_csrf_token]
end
@@ -290,7 +290,7 @@ module ActionController #:nodoc:
# Creates a masked version of the authenticity token that varies
# on each request. The masking is used to mitigate SSL attacks
# like BREACH.
- def masked_authenticity_token(session, form_options: {})
+ def masked_authenticity_token(session, form_options: {}) # :doc:
action, method = form_options.values_at(:action, :method)
raw_token = if per_form_csrf_tokens && action && method
@@ -309,7 +309,7 @@ module ActionController #:nodoc:
# Checks the client's masked token to see if it matches the
# session token. Essentially the inverse of
# +masked_authenticity_token+.
- def valid_authenticity_token?(session, encoded_masked_token)
+ def valid_authenticity_token?(session, encoded_masked_token) # :doc:
if encoded_masked_token.nil? || encoded_masked_token.empty? || !encoded_masked_token.is_a?(String)
return false
end
@@ -340,7 +340,7 @@ module ActionController #:nodoc:
end
end
- def unmask_token(masked_token)
+ def unmask_token(masked_token) # :doc:
# Split the token into the one-time pad and the encrypted
# value and decrypt it
one_time_pad = masked_token[0...AUTHENTICITY_TOKEN_LENGTH]
@@ -348,11 +348,11 @@ module ActionController #:nodoc:
xor_byte_strings(one_time_pad, encrypted_csrf_token)
end
- def compare_with_real_token(token, session)
+ def compare_with_real_token(token, session) # :doc:
ActiveSupport::SecurityUtils.secure_compare(token, real_csrf_token(session))
end
- def valid_per_form_csrf_token?(token, session)
+ def valid_per_form_csrf_token?(token, session) # :doc:
if per_form_csrf_tokens
correct_token = per_form_csrf_token(
session,
@@ -366,12 +366,12 @@ module ActionController #:nodoc:
end
end
- def real_csrf_token(session)
+ def real_csrf_token(session) # :doc:
session[:_csrf_token] ||= SecureRandom.base64(AUTHENTICITY_TOKEN_LENGTH)
Base64.strict_decode64(session[:_csrf_token])
end
- def per_form_csrf_token(session, action_path, method)
+ def per_form_csrf_token(session, action_path, method) # :doc:
OpenSSL::HMAC.digest(
OpenSSL::Digest::SHA256.new,
real_csrf_token(session),
@@ -379,25 +379,25 @@ module ActionController #:nodoc:
)
end
- def xor_byte_strings(s1, s2)
+ def xor_byte_strings(s1, s2) # :doc:
s2_bytes = s2.bytes
s1.each_byte.with_index { |c1, i| s2_bytes[i] ^= c1 }
s2_bytes.pack("C*")
end
# The form's authenticity parameter. Override to provide your own.
- def form_authenticity_param
+ def form_authenticity_param # :doc:
params[request_forgery_protection_token]
end
# Checks if the controller allows forgery protection.
- def protect_against_forgery?
+ def protect_against_forgery? # :doc:
allow_forgery_protection
end
# Checks if the request originated from the same origin by looking at the
# Origin header.
- def valid_request_origin?
+ def valid_request_origin? # :doc:
if forgery_protection_origin_check
# We accept blank origin headers because some user agents don't send it.
request.origin.nil? || request.origin == request.base_url
@@ -406,7 +406,7 @@ module ActionController #:nodoc:
end
end
- def normalize_action_path(action_path)
+ def normalize_action_path(action_path) # :doc:
uri = URI.parse(action_path)
uri.path.chomp("/")
end
diff --git a/actionpack/lib/action_controller/metal/streaming.rb b/actionpack/lib/action_controller/metal/streaming.rb
index 481f19f1ef..877a08b222 100644
--- a/actionpack/lib/action_controller/metal/streaming.rb
+++ b/actionpack/lib/action_controller/metal/streaming.rb
@@ -193,10 +193,10 @@ module ActionController #:nodoc:
module Streaming
extend ActiveSupport::Concern
- protected
+ private
# Set proper cache control and transfer encoding when streaming
- def _process_options(options) #:nodoc:
+ def _process_options(options)
super
if options[:stream]
if request.version == "HTTP/1.0"
@@ -210,7 +210,7 @@ module ActionController #:nodoc:
end
# Call render_body if we are streaming instead of usual +render+.
- def _render_template(options) #:nodoc:
+ def _render_template(options)
if options.delete(:stream)
Rack::Chunked::Body.new view_renderer.render_body(view_context, options)
else
diff --git a/actionpack/lib/action_controller/metal/strong_parameters.rb b/actionpack/lib/action_controller/metal/strong_parameters.rb
index b64617d084..acfeca1fcb 100644
--- a/actionpack/lib/action_controller/metal/strong_parameters.rb
+++ b/actionpack/lib/action_controller/metal/strong_parameters.rb
@@ -71,8 +71,8 @@ module ActionController
# * +permit_all_parameters+ - If it's +true+, all the parameters will be
# permitted by default. The default is +false+.
# * +action_on_unpermitted_parameters+ - Allow to control the behavior when parameters
- # that are not explicitly permitted are found. The values can be <tt>:log</tt> to
- # write a message on the logger or <tt>:raise</tt> to raise
+ # that are not explicitly permitted are found. The values can be +false+ to just filter them
+ # out, <tt>:log</tt> to additionally write a message on the logger, or <tt>:raise</tt> to raise
# ActionController::UnpermittedParameters exception. The default value is <tt>:log</tt>
# in test and development environments, +false+ otherwise.
#
@@ -334,6 +334,15 @@ module ActionController
# params = ActionController::Parameters.new(tags: ['rails', 'parameters'])
# params.permit(tags: [])
#
+ # Sometimes it is not possible or convenient to declare the valid keys of
+ # a hash parameter or its internal structure. Just map to an empty hash:
+ #
+ # params.permit(preferences: {})
+ #
+ # but be careful because this opens the door to arbitrary input. In this
+ # case, +permit+ ensures values in the returned structure are permitted
+ # scalars and filters out anything else.
+ #
# You can also use +permit+ on nested parameters, like:
#
# params = ActionController::Parameters.new({
@@ -382,14 +391,15 @@ module ActionController
case filter
when Symbol, String
permitted_scalar_filter(params, filter)
- when Hash then
+ when Hash
hash_filter(params, filter)
end
end
unpermitted_parameters!(params) if self.class.action_on_unpermitted_parameters
- params.permit!
+ params.permitted = true
+ params
end
# Returns a parameter for the given +key+. If not found,
@@ -539,7 +549,7 @@ module ActionController
new_instance_with_inherited_permitted_status(@parameters.select(&block))
end
- # Equivalent to Hash#keep_if, but returns nil if no changes were made.
+ # Equivalent to Hash#keep_if, but returns +nil+ if no changes were made.
def select!(&block)
@parameters.select!(&block)
self
@@ -573,6 +583,13 @@ module ActionController
)
end
+ # Returns current <tt>ActionController::Parameters</tt> instance which
+ # +other_hash+ merges into current hash.
+ def merge!(other_hash)
+ @parameters.merge!(other_hash.to_h)
+ self
+ end
+
# This is required by ActiveModel attribute assignment, so that user can
# pass +Parameters+ to a mass assignment methods in a model. It should not
# matter as we are using +HashWithIndifferentAccess+ internally.
@@ -680,7 +697,7 @@ module ActionController
when Parameters
if object.fields_for_style?
hash = object.class.new
- object.each { |k,v| hash[k] = yield v }
+ object.each { |k, v| hash[k] = yield v }
hash
else
yield object
@@ -759,6 +776,7 @@ module ActionController
end
EMPTY_ARRAY = []
+ EMPTY_HASH = {}
def hash_filter(params, filter)
filter = filter.with_indifferent_access
@@ -772,6 +790,11 @@ module ActionController
array_of_permitted_scalars?(self[key]) do |val|
params[key] = val
end
+ elsif filter[key] == EMPTY_HASH
+ # Declaration { preferences: {} }.
+ if value.is_a?(Parameters)
+ params[key] = permit_any_in_parameters(value)
+ end
elsif non_scalar?(value)
# Declaration { user: :name } or { user: [:name, :age, { address: ... }] }.
params[key] = each_element(value) do |element|
@@ -781,6 +804,39 @@ module ActionController
end
end
+ def permit_any_in_parameters(params)
+ self.class.new.tap do |sanitized|
+ params.each do |key, value|
+ case value
+ when ->(v) { permitted_scalar?(v) }
+ sanitized[key] = value
+ when Array
+ sanitized[key] = permit_any_in_array(value)
+ when Parameters
+ sanitized[key] = permit_any_in_parameters(value)
+ else
+ # Filter this one out.
+ end
+ end
+ sanitized.permitted = true
+ end
+ end
+
+ def permit_any_in_array(array)
+ [].tap do |sanitized|
+ array.each do |element|
+ case element
+ when ->(e) { permitted_scalar?(e) }
+ sanitized << element
+ when Parameters
+ sanitized << permit_any_in_parameters(element)
+ else
+ # Filter this one out.
+ end
+ end
+ end
+ end
+
def initialize_copy(source)
super
@parameters = @parameters.dup
diff --git a/actionpack/lib/action_controller/railtie.rb b/actionpack/lib/action_controller/railtie.rb
index 6513a556ee..a7cdfe6a98 100644
--- a/actionpack/lib/action_controller/railtie.rb
+++ b/actionpack/lib/action_controller/railtie.rb
@@ -51,7 +51,7 @@ module ActionController
extend ::AbstractController::Railties::RoutesHelpers.with(app.routes)
extend ::ActionController::Railties::Helpers
- options.each do |k,v|
+ options.each do |k, v|
k = "#{k}="
if respond_to?(k)
send(k, v)
diff --git a/actionpack/lib/action_controller/renderer.rb b/actionpack/lib/action_controller/renderer.rb
index 0739f16965..3ff80e6a39 100644
--- a/actionpack/lib/action_controller/renderer.rb
+++ b/actionpack/lib/action_controller/renderer.rb
@@ -83,7 +83,7 @@ module ActionController
private
def normalize_keys(env)
new_env = {}
- env.each_pair { |k,v| new_env[rack_key_for(k)] = rack_value_for(k, v) }
+ env.each_pair { |k, v| new_env[rack_key_for(k)] = rack_value_for(k, v) }
new_env
end
@@ -102,7 +102,9 @@ module ActionController
method: ->(v) { v.upcase },
}
- def rack_key_for(key); RACK_KEY_TRANSLATION[key]; end
+ def rack_key_for(key)
+ RACK_KEY_TRANSLATION.fetch(key, key.to_s)
+ end
def rack_value_for(key, value)
RACK_VALUE_TRANSLATION.fetch(key, IDENTITY).call value
diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb
index 33c0201951..441667e556 100644
--- a/actionpack/lib/action_controller/test_case.rb
+++ b/actionpack/lib/action_controller/test_case.rb
@@ -3,6 +3,7 @@ require "active_support/core_ext/hash/conversions"
require "active_support/core_ext/object/to_query"
require "active_support/core_ext/module/anonymous"
require "active_support/core_ext/hash/keys"
+require "active_support/testing/constant_lookup"
require "action_controller/template_assertions"
require "rails-dom-testing"
@@ -112,8 +113,9 @@ module ActionController
end
end
- set_header "CONTENT_LENGTH", data.length.to_s
- set_header "rack.input", StringIO.new(data)
+ data_stream = StringIO.new(data)
+ set_header "CONTENT_LENGTH", data_stream.length.to_s
+ set_header "rack.input", data_stream
end
fetch_header("PATH_INFO") do |k|
@@ -513,8 +515,6 @@ module ActionController
@request = @controller.request
@response = @controller.response
- @request.delete_header "HTTP_COOKIE"
-
if @request.have_cookie_jar?
unless @request.cookie_jar.committed?
@request.cookie_jar.write(@response)
diff --git a/actionpack/lib/action_dispatch/http/filter_parameters.rb b/actionpack/lib/action_dispatch/http/filter_parameters.rb
index e5874a39f6..e584b84d92 100644
--- a/actionpack/lib/action_dispatch/http/filter_parameters.rb
+++ b/actionpack/lib/action_dispatch/http/filter_parameters.rb
@@ -51,28 +51,28 @@ module ActionDispatch
@filtered_path ||= query_string.empty? ? path : "#{path}?#{filtered_query_string}"
end
- protected
+ private
- def parameter_filter
+ def parameter_filter # :doc:
parameter_filter_for fetch_header("action_dispatch.parameter_filter") {
return NULL_PARAM_FILTER
}
end
- def env_filter
+ def env_filter # :doc:
user_key = fetch_header("action_dispatch.parameter_filter") {
return NULL_ENV_FILTER
}
parameter_filter_for(Array(user_key) + ENV_MATCH)
end
- def parameter_filter_for(filters)
+ def parameter_filter_for(filters) # :doc:
ParameterFilter.new(filters)
end
KV_RE = "[^&;=]+"
PAIR_RE = %r{(#{KV_RE})=(#{KV_RE})}
- def filtered_query_string
+ def filtered_query_string # :doc:
query_string.gsub(PAIR_RE) do |_|
parameter_filter.filter([[$1, $2]]).first.join("=")
end
diff --git a/actionpack/lib/action_dispatch/http/mime_negotiation.rb b/actionpack/lib/action_dispatch/http/mime_negotiation.rb
index d0c9413efa..c4fe3a5c09 100644
--- a/actionpack/lib/action_dispatch/http/mime_negotiation.rb
+++ b/actionpack/lib/action_dispatch/http/mime_negotiation.rb
@@ -29,8 +29,8 @@ module ActionDispatch
content_mime_type && content_mime_type.to_s
end
- def has_content_type?
- has_header? "CONTENT_TYPE"
+ def has_content_type? # :nodoc:
+ get_header "CONTENT_TYPE"
end
# Returns the accepted MIME type for the request.
@@ -150,20 +150,20 @@ module ActionDispatch
order.include?(Mime::ALL) ? format : nil
end
- protected
+ private
BROWSER_LIKE_ACCEPTS = /,\s*\*\/\*|\*\/\*\s*,/
- def valid_accept_header
+ def valid_accept_header # :doc:
(xhr? && (accept.present? || content_mime_type)) ||
(accept.present? && accept !~ BROWSER_LIKE_ACCEPTS)
end
- def use_accept_header
+ def use_accept_header # :doc:
!self.class.ignore_accept_header
end
- def format_from_path_extension
+ def format_from_path_extension # :doc:
path = get_header("action_dispatch.original_path") || get_header("PATH_INFO")
if match = path && path.match(/\.(\w+)\z/)
Mime[match.captures.first]
diff --git a/actionpack/lib/action_dispatch/http/mime_type.rb b/actionpack/lib/action_dispatch/http/mime_type.rb
index 58eb8d0baf..6b718e3682 100644
--- a/actionpack/lib/action_dispatch/http/mime_type.rb
+++ b/actionpack/lib/action_dispatch/http/mime_type.rb
@@ -278,6 +278,8 @@ module Mime
def all?; false; end
+ # TODO Change this to private once we've dropped Ruby 2.2 support.
+ # Workaround for Ruby 2.2 "private attribute?" warning.
protected
attr_reader :string, :synonyms
@@ -295,7 +297,7 @@ module Mime
end
end
- def respond_to_missing?(method, include_private = false) #:nodoc:
+ def respond_to_missing?(method, include_private = false)
method.to_s.ends_with? "?"
end
end
diff --git a/actionpack/lib/action_dispatch/http/parameter_filter.rb b/actionpack/lib/action_dispatch/http/parameter_filter.rb
index 01fe35f5c6..889f55a52a 100644
--- a/actionpack/lib/action_dispatch/http/parameter_filter.rb
+++ b/actionpack/lib/action_dispatch/http/parameter_filter.rb
@@ -50,7 +50,7 @@ module ActionDispatch
def initialize(regexps, deep_regexps, blocks)
@regexps = regexps
@deep_regexps = deep_regexps.any? ? deep_regexps : nil
- @blocks = blocks
+ @blocks = blocks
end
def call(original_params, parents = [])
diff --git a/actionpack/lib/action_dispatch/http/parameters.rb b/actionpack/lib/action_dispatch/http/parameters.rb
index ddd15b748b..ad4aadacf5 100644
--- a/actionpack/lib/action_dispatch/http/parameters.rb
+++ b/actionpack/lib/action_dispatch/http/parameters.rb
@@ -45,7 +45,7 @@ module ActionDispatch
query_parameters.dup
end
params.merge!(path_parameters)
- params = set_custom_encoding(params)
+ params = set_binary_encoding(params)
set_header("action_dispatch.request.parameters", params)
params
end
@@ -73,21 +73,16 @@ module ActionDispatch
private
- def set_custom_encoding(params)
+ def set_binary_encoding(params)
action = params[:action]
- params.each do |k, v|
- if v.is_a?(String) && v.encoding != encoding_template(action, k)
- params[k] = v.force_encoding(encoding_template(action, k))
+ if controller_class.binary_params_for?(action)
+ ActionDispatch::Request::Utils.each_param_value(params) do |param|
+ param.force_encoding ::Encoding::ASCII_8BIT
end
end
-
params
end
- def encoding_template(action, param)
- controller_class.encoding_for_param(action, param)
- end
-
def parse_formatted_parameters(parsers)
return yield if content_length.zero? || content_mime_type.nil?
diff --git a/actionpack/lib/action_dispatch/http/request.rb b/actionpack/lib/action_dispatch/http/request.rb
index 9986d6e1e9..a886358399 100644
--- a/actionpack/lib/action_dispatch/http/request.rb
+++ b/actionpack/lib/action_dispatch/http/request.rb
@@ -69,7 +69,7 @@ module ActionDispatch
PASS_NOT_FOUND = Class.new { # :nodoc:
def self.action(_); self; end
def self.call(_); [404, { "X-Cascade" => "pass" }, []]; end
- def self.encoding_for_param(action, param); ::Encoding::UTF_8; end
+ def self.binary_params_for?(action); false; end
}
def controller_class
diff --git a/actionpack/lib/action_dispatch/http/response.rb b/actionpack/lib/action_dispatch/http/response.rb
index e8173e2a99..516a2af69a 100644
--- a/actionpack/lib/action_dispatch/http/response.rb
+++ b/actionpack/lib/action_dispatch/http/response.rb
@@ -39,7 +39,7 @@ module ActionDispatch # :nodoc:
super(header)
end
- def []=(k,v)
+ def []=(k, v)
if @response.sending? || @response.sent?
raise ActionDispatch::IllegalStateError, "header already sent"
end
@@ -227,7 +227,9 @@ module ActionDispatch # :nodoc:
return unless content_type
new_header_info = parse_content_type(content_type.to_s)
prev_header_info = parsed_content_type_header
- set_content_type new_header_info.mime_type, new_header_info.charset || prev_header_info.charset || self.class.default_charset
+ charset = new_header_info.charset || prev_header_info.charset
+ charset ||= self.class.default_charset unless prev_header_info.mime_type
+ set_content_type new_header_info.mime_type, charset
end
# Sets the HTTP response's content MIME type. For example, in the controller
@@ -249,7 +251,7 @@ module ActionDispatch # :nodoc:
end
end
- # Sets the HTTP character set. In case of nil parameter
+ # Sets the HTTP character set. In case of +nil+ parameter
# it sets the charset to utf-8.
#
# response.charset = 'utf-16' # => 'utf-16'
@@ -408,7 +410,7 @@ module ActionDispatch # :nodoc:
def parse_content_type(content_type)
if content_type
type, charset = content_type.split(/;\s*charset=/)
- type = nil if type.empty?
+ type = nil if type && type.empty?
ContentTypeHeader.new(type, charset)
else
NullContentTypeHeader
diff --git a/actionpack/lib/action_dispatch/http/upload.rb b/actionpack/lib/action_dispatch/http/upload.rb
index 9aa73c862b..61ba052e45 100644
--- a/actionpack/lib/action_dispatch/http/upload.rb
+++ b/actionpack/lib/action_dispatch/http/upload.rb
@@ -24,7 +24,7 @@ module ActionDispatch
attr_accessor :headers
def initialize(hash) # :nodoc:
- @tempfile = hash[:tempfile]
+ @tempfile = hash[:tempfile]
raise(ArgumentError, ":tempfile is required") unless @tempfile
@original_filename = hash[:filename]
@@ -40,7 +40,7 @@ module ActionDispatch
end
# Shortcut for +tempfile.read+.
- def read(length=nil, buffer=nil)
+ def read(length = nil, buffer = nil)
@tempfile.read(length, buffer)
end
@@ -50,7 +50,7 @@ module ActionDispatch
end
# Shortcut for +tempfile.close+.
- def close(unlink_now=false)
+ def close(unlink_now = false)
@tempfile.close(unlink_now)
end
diff --git a/actionpack/lib/action_dispatch/http/url.rb b/actionpack/lib/action_dispatch/http/url.rb
index 06ffa983d1..a6937d54ff 100644
--- a/actionpack/lib/action_dispatch/http/url.rb
+++ b/actionpack/lib/action_dispatch/http/url.rb
@@ -66,7 +66,7 @@ module ActionDispatch
end
def path_for(options)
- path = options[:script_name].to_s.chomp("/".freeze)
+ path = options[:script_name].to_s.chomp("/".freeze)
path << options[:path] if options.key?(:path)
add_trailing_slash(path) if options[:trailing_slash]
@@ -80,7 +80,7 @@ module ActionDispatch
def add_params(path, params)
params = { params: params } unless params.is_a?(Hash)
- params.reject! { |_,v| v.to_param.nil? }
+ params.reject! { |_, v| v.to_param.nil? }
query = params.to_query
path << "?#{query}" unless query.empty?
end
diff --git a/actionpack/lib/action_dispatch/journey/formatter.rb b/actionpack/lib/action_dispatch/journey/formatter.rb
index dc8b24b089..1d239addf8 100644
--- a/actionpack/lib/action_dispatch/journey/formatter.rb
+++ b/actionpack/lib/action_dispatch/journey/formatter.rb
@@ -1,10 +1,11 @@
require "action_controller/metal/exceptions"
module ActionDispatch
+ # :stopdoc:
module Journey
# The Formatter class is used for formatting URLs. For example, parameters
# passed to +url_for+ in Rails will eventually call Formatter#generate.
- class Formatter # :nodoc:
+ class Formatter
attr_reader :routes
def initialize(routes)
@@ -47,7 +48,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}"
+ 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?
@@ -91,7 +92,11 @@ module ActionDispatch
else
routes = non_recursive(cache, options)
- hash = routes.group_by { |_, r| r.score(options) }
+ supplied_keys = options.each_with_object({}) do |(k, v), h|
+ h[k.to_s] = true if v
+ end
+
+ hash = routes.group_by { |_, r| r.score(supplied_keys) }
hash.keys.sort.reverse_each do |score|
break if score < 0
@@ -178,4 +183,5 @@ module ActionDispatch
end
end
end
+ # :startdoc:
end
diff --git a/actionpack/lib/action_dispatch/journey/gtg/builder.rb b/actionpack/lib/action_dispatch/journey/gtg/builder.rb
index 9990c66627..0f8bed89bf 100644
--- a/actionpack/lib/action_dispatch/journey/gtg/builder.rb
+++ b/actionpack/lib/action_dispatch/journey/gtg/builder.rb
@@ -17,7 +17,7 @@ module ActionDispatch
def transition_table
dtrans = TransitionTable.new
marked = {}
- state_id = Hash.new { |h,k| h[k] = h.length }
+ state_id = Hash.new { |h, k| h[k] = h.length }
start = firstpos(root)
dstates = [start]
diff --git a/actionpack/lib/action_dispatch/journey/gtg/transition_table.rb b/actionpack/lib/action_dispatch/journey/gtg/transition_table.rb
index 0be18dc26f..beb9f1ef3b 100644
--- a/actionpack/lib/action_dispatch/journey/gtg/transition_table.rb
+++ b/actionpack/lib/action_dispatch/journey/gtg/transition_table.rb
@@ -12,7 +12,7 @@ module ActionDispatch
@regexp_states = {}
@string_states = {}
@accepting = {}
- @memos = Hash.new { |h,k| h[k] = [] }
+ @memos = Hash.new { |h, k| h[k] = [] }
end
def add_accepting(state)
@@ -56,7 +56,7 @@ module ActionDispatch
end
def as_json(options = nil)
- simple_regexp = Hash.new { |h,k| h[k] = {} }
+ simple_regexp = Hash.new { |h, k| h[k] = {} }
@regexp_states.each do |from, hash|
hash.each do |re, to|
diff --git a/actionpack/lib/action_dispatch/journey/nfa/builder.rb b/actionpack/lib/action_dispatch/journey/nfa/builder.rb
index 19e5752ae5..532f765094 100644
--- a/actionpack/lib/action_dispatch/journey/nfa/builder.rb
+++ b/actionpack/lib/action_dispatch/journey/nfa/builder.rb
@@ -36,7 +36,7 @@ module ActionDispatch
def visit_OR(node)
from = @i += 1
children = node.children.map { |c| visit(c) }
- to = @i += 1
+ to = @i += 1
children.each do |child|
@tt[from, child.first] = nil
diff --git a/actionpack/lib/action_dispatch/journey/nfa/transition_table.rb b/actionpack/lib/action_dispatch/journey/nfa/transition_table.rb
index 4737adc724..543a670da0 100644
--- a/actionpack/lib/action_dispatch/journey/nfa/transition_table.rb
+++ b/actionpack/lib/action_dispatch/journey/nfa/transition_table.rb
@@ -10,7 +10,7 @@ module ActionDispatch
attr_reader :memos
def initialize
- @table = Hash.new { |h,f| h[f] = {} }
+ @table = Hash.new { |h, f| h[f] = {} }
@memos = {}
@accepting = nil
@inverted = nil
diff --git a/actionpack/lib/action_dispatch/journey/parser.rb b/actionpack/lib/action_dispatch/journey/parser.rb
index 01ff2109cb..db42b64c4b 100644
--- a/actionpack/lib/action_dispatch/journey/parser.rb
+++ b/actionpack/lib/action_dispatch/journey/parser.rb
@@ -1,196 +1,199 @@
#
# DO NOT MODIFY!!!!
-# This file is automatically generated by Racc 1.4.11
+# This file is automatically generated by Racc 1.4.14
# from Racc grammer file "".
#
-require "racc/parser.rb"
+require 'racc/parser.rb'
+
+# :stopdoc:
require "action_dispatch/journey/parser_extras"
module ActionDispatch
module Journey
class Parser < Racc::Parser
- ##### State transition tables begin ###
-
- racc_action_table = [
- 13, 15, 14, 7, 21, 16, 8, 19, 13, 15,
- 14, 7, 17, 16, 8, 13, 15, 14, 7, 24,
- 16, 8, 13, 15, 14, 7, 19, 16, 8 ]
-
- racc_action_check = [
- 2, 2, 2, 2, 17, 2, 2, 2, 0, 0,
- 0, 0, 1, 0, 0, 19, 19, 19, 19, 20,
- 19, 19, 7, 7, 7, 7, 22, 7, 7 ]
-
- racc_action_pointer = [
- 6, 12, -2, nil, nil, nil, nil, 20, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 4, nil, 13,
- 13, nil, 17, nil, nil ]
-
- racc_action_default = [
- -19, -19, -2, -3, -4, -5, -6, -19, -10, -11,
- -12, -13, -14, -15, -16, -17, -18, -19, -1, -19,
- -19, 25, -8, -9, -7 ]
-
- racc_goto_table = [
- 1, 22, 18, 23, nil, nil, nil, 20 ]
-
- racc_goto_check = [
- 1, 2, 1, 3, nil, nil, nil, 1 ]
-
- racc_goto_pointer = [
- nil, 0, -18, -16, nil, nil, nil, nil, nil, nil,
- nil ]
-
- racc_goto_default = [
- nil, nil, 2, 3, 4, 5, 6, 9, 10, 11,
- 12 ]
-
- racc_reduce_table = [
- 0, 0, :racc_error,
- 2, 11, :_reduce_1,
- 1, 11, :_reduce_2,
- 1, 11, :_reduce_none,
- 1, 12, :_reduce_none,
- 1, 12, :_reduce_none,
- 1, 12, :_reduce_none,
- 3, 15, :_reduce_7,
- 3, 13, :_reduce_8,
- 3, 13, :_reduce_9,
- 1, 16, :_reduce_10,
- 1, 14, :_reduce_none,
- 1, 14, :_reduce_none,
- 1, 14, :_reduce_none,
- 1, 14, :_reduce_none,
- 1, 19, :_reduce_15,
- 1, 17, :_reduce_16,
- 1, 18, :_reduce_17,
- 1, 20, :_reduce_18 ]
-
- racc_reduce_n = 19
-
- racc_shift_n = 25
-
- racc_token_table = {
- false => 0,
- :error => 1,
- :SLASH => 2,
- :LITERAL => 3,
- :SYMBOL => 4,
- :LPAREN => 5,
- :RPAREN => 6,
- :DOT => 7,
- :STAR => 8,
- :OR => 9 }
-
- racc_nt_base = 10
-
- racc_use_result_var = false
-
- Racc_arg = [
- racc_action_table,
- racc_action_check,
- racc_action_default,
- racc_action_pointer,
- racc_goto_table,
- racc_goto_check,
- racc_goto_default,
- racc_goto_pointer,
- racc_nt_base,
- racc_reduce_table,
- racc_token_table,
- racc_shift_n,
- racc_reduce_n,
- racc_use_result_var ]
-
- Racc_token_to_s_table = [
- "$end",
- "error",
- "SLASH",
- "LITERAL",
- "SYMBOL",
- "LPAREN",
- "RPAREN",
- "DOT",
- "STAR",
- "OR",
- "$start",
- "expressions",
- "expression",
- "or",
- "terminal",
- "group",
- "star",
- "symbol",
- "literal",
- "slash",
- "dot" ]
-
- Racc_debug_parser = false
-
- ##### State transition tables end #####
-
- # reduce 0 omitted
-
- def _reduce_1(val, _values)
- Cat.new(val.first, val.last)
- end
-
- def _reduce_2(val, _values)
- val.first
- end
-
- # reduce 3 omitted
-
- # reduce 4 omitted
-
- # reduce 5 omitted
-
- # reduce 6 omitted
-
- def _reduce_7(val, _values)
- Group.new(val[1])
- end
-
- def _reduce_8(val, _values)
- Or.new([val.first, val.last])
- end
-
- def _reduce_9(val, _values)
- Or.new([val.first, val.last])
- end
-
- def _reduce_10(val, _values)
- Star.new(Symbol.new(val.last))
- end
-
- # reduce 11 omitted
-
- # reduce 12 omitted
-
- # reduce 13 omitted
-
- # reduce 14 omitted
-
- def _reduce_15(val, _values)
- Slash.new("/")
- end
-
- def _reduce_16(val, _values)
- Symbol.new(val.first)
- end
-
- def _reduce_17(val, _values)
- Literal.new(val.first)
- end
-
- def _reduce_18(val, _values)
- Dot.new(val.first)
- end
-
- def _reduce_none(val, _values)
- val[0]
- end
+##### State transition tables begin ###
+
+racc_action_table = [
+ 13, 15, 14, 7, 19, 16, 8, 19, 13, 15,
+ 14, 7, 17, 16, 8, 13, 15, 14, 7, 21,
+ 16, 8, 13, 15, 14, 7, 24, 16, 8 ]
+
+racc_action_check = [
+ 2, 2, 2, 2, 22, 2, 2, 2, 19, 19,
+ 19, 19, 1, 19, 19, 7, 7, 7, 7, 17,
+ 7, 7, 0, 0, 0, 0, 20, 0, 0 ]
+
+racc_action_pointer = [
+ 20, 12, -2, nil, nil, nil, nil, 13, nil, nil,
+ nil, nil, nil, nil, nil, nil, nil, 19, nil, 6,
+ 20, nil, -5, nil, nil ]
+
+racc_action_default = [
+ -19, -19, -2, -3, -4, -5, -6, -19, -10, -11,
+ -12, -13, -14, -15, -16, -17, -18, -19, -1, -19,
+ -19, 25, -8, -9, -7 ]
+
+racc_goto_table = [
+ 1, 22, 18, 23, nil, nil, nil, 20 ]
+
+racc_goto_check = [
+ 1, 2, 1, 3, nil, nil, nil, 1 ]
+
+racc_goto_pointer = [
+ nil, 0, -18, -16, nil, nil, nil, nil, nil, nil,
+ nil ]
+
+racc_goto_default = [
+ nil, nil, 2, 3, 4, 5, 6, 9, 10, 11,
+ 12 ]
+
+racc_reduce_table = [
+ 0, 0, :racc_error,
+ 2, 11, :_reduce_1,
+ 1, 11, :_reduce_2,
+ 1, 11, :_reduce_none,
+ 1, 12, :_reduce_none,
+ 1, 12, :_reduce_none,
+ 1, 12, :_reduce_none,
+ 3, 15, :_reduce_7,
+ 3, 13, :_reduce_8,
+ 3, 13, :_reduce_9,
+ 1, 16, :_reduce_10,
+ 1, 14, :_reduce_none,
+ 1, 14, :_reduce_none,
+ 1, 14, :_reduce_none,
+ 1, 14, :_reduce_none,
+ 1, 19, :_reduce_15,
+ 1, 17, :_reduce_16,
+ 1, 18, :_reduce_17,
+ 1, 20, :_reduce_18 ]
+
+racc_reduce_n = 19
+
+racc_shift_n = 25
+
+racc_token_table = {
+ false => 0,
+ :error => 1,
+ :SLASH => 2,
+ :LITERAL => 3,
+ :SYMBOL => 4,
+ :LPAREN => 5,
+ :RPAREN => 6,
+ :DOT => 7,
+ :STAR => 8,
+ :OR => 9 }
+
+racc_nt_base = 10
+
+racc_use_result_var = false
+
+Racc_arg = [
+ racc_action_table,
+ racc_action_check,
+ racc_action_default,
+ racc_action_pointer,
+ racc_goto_table,
+ racc_goto_check,
+ racc_goto_default,
+ racc_goto_pointer,
+ racc_nt_base,
+ racc_reduce_table,
+ racc_token_table,
+ racc_shift_n,
+ racc_reduce_n,
+ racc_use_result_var ]
+
+Racc_token_to_s_table = [
+ "$end",
+ "error",
+ "SLASH",
+ "LITERAL",
+ "SYMBOL",
+ "LPAREN",
+ "RPAREN",
+ "DOT",
+ "STAR",
+ "OR",
+ "$start",
+ "expressions",
+ "expression",
+ "or",
+ "terminal",
+ "group",
+ "star",
+ "symbol",
+ "literal",
+ "slash",
+ "dot" ]
+
+Racc_debug_parser = false
+
+##### State transition tables end #####
+
+# reduce 0 omitted
+
+def _reduce_1(val, _values)
+ Cat.new(val.first, val.last)
+end
+
+def _reduce_2(val, _values)
+ val.first
+end
+
+# reduce 3 omitted
+
+# reduce 4 omitted
+
+# reduce 5 omitted
+
+# reduce 6 omitted
+
+def _reduce_7(val, _values)
+ Group.new(val[1])
+end
+
+def _reduce_8(val, _values)
+ Or.new([val.first, val.last])
+end
+
+def _reduce_9(val, _values)
+ Or.new([val.first, val.last])
+end
+
+def _reduce_10(val, _values)
+ Star.new(Symbol.new(val.last))
+end
+
+# reduce 11 omitted
+
+# reduce 12 omitted
+
+# reduce 13 omitted
+
+# reduce 14 omitted
+
+def _reduce_15(val, _values)
+ Slash.new(val.first)
+end
+
+def _reduce_16(val, _values)
+ Symbol.new(val.first)
+end
+
+def _reduce_17(val, _values)
+ Literal.new(val.first)
+end
+
+def _reduce_18(val, _values)
+ Dot.new(val.first)
+end
+
+def _reduce_none(val, _values)
+ val[0]
+end
+
end # class Parser
- end # module Journey
-end # module ActionDispatch
+ end # module Journey
+ end # module ActionDispatch
diff --git a/actionpack/lib/action_dispatch/journey/parser.y b/actionpack/lib/action_dispatch/journey/parser.y
index d3f7c4d765..f9b1a7a958 100644
--- a/actionpack/lib/action_dispatch/journey/parser.y
+++ b/actionpack/lib/action_dispatch/journey/parser.y
@@ -30,7 +30,7 @@ rule
| dot
;
slash
- : SLASH { Slash.new('/') }
+ : SLASH { Slash.new(val.first) }
;
symbol
: SYMBOL { Symbol.new(val.first) }
@@ -45,5 +45,6 @@ rule
end
---- header
+# :stopdoc:
-require 'action_dispatch/journey/parser_extras'
+require "action_dispatch/journey/parser_extras"
diff --git a/actionpack/lib/action_dispatch/journey/parser_extras.rb b/actionpack/lib/action_dispatch/journey/parser_extras.rb
index ec26e634e8..4c7e82d93c 100644
--- a/actionpack/lib/action_dispatch/journey/parser_extras.rb
+++ b/actionpack/lib/action_dispatch/journey/parser_extras.rb
@@ -2,8 +2,9 @@ require "action_dispatch/journey/scanner"
require "action_dispatch/journey/nodes/node"
module ActionDispatch
- module Journey # :nodoc:
- class Parser < Racc::Parser # :nodoc:
+ # :stopdoc:
+ module Journey
+ class Parser < Racc::Parser
include Journey::Nodes
def self.parse(string)
@@ -24,4 +25,5 @@ module ActionDispatch
end
end
end
+ # :startdoc:
end
diff --git a/actionpack/lib/action_dispatch/journey/route.rb b/actionpack/lib/action_dispatch/journey/route.rb
index a9713ff292..f2ac4818d8 100644
--- a/actionpack/lib/action_dispatch/journey/route.rb
+++ b/actionpack/lib/action_dispatch/journey/route.rb
@@ -1,6 +1,7 @@
module ActionDispatch
- module Journey # :nodoc:
- class Route # :nodoc:
+ # :stopdoc:
+ module Journey
+ class Route
attr_reader :app, :path, :defaults, :name, :precedence
attr_reader :constraints, :internal
@@ -80,9 +81,9 @@ module ActionDispatch
end
end
- def requirements # :nodoc:
+ def requirements
# needed for rails `rails routes`
- @defaults.merge(path.requirements).delete_if { |_,v|
+ @defaults.merge(path.requirements).delete_if { |_, v|
/.+?/ == v
}
end
@@ -95,13 +96,18 @@ module ActionDispatch
required_parts + required_defaults.keys
end
- def score(constraints)
+ def score(supplied_keys)
required_keys = path.required_names
- supplied_keys = constraints.map { |k,v| v && k.to_s }.compact
- return -1 unless (required_keys - supplied_keys).empty?
+ required_keys.each do |k|
+ return -1 unless supplied_keys.include?(k)
+ end
+
+ score = 0
+ path.names.each do |k|
+ score += 1 if supplied_keys.include?(k)
+ end
- score = (supplied_keys & path.names).length
score + (required_defaults.length * 2)
end
@@ -123,7 +129,7 @@ module ActionDispatch
end
def required_defaults
- @required_defaults ||= @defaults.dup.delete_if do |k,_|
+ @required_defaults ||= @defaults.dup.delete_if do |k, _|
parts.include?(k) || !required_default?(k)
end
end
@@ -176,4 +182,5 @@ module ActionDispatch
end
end
end
+ # :startdoc:
end
diff --git a/actionpack/lib/action_dispatch/journey/router.rb b/actionpack/lib/action_dispatch/journey/router.rb
index d0ef549335..084ae9325e 100644
--- a/actionpack/lib/action_dispatch/journey/router.rb
+++ b/actionpack/lib/action_dispatch/journey/router.rb
@@ -109,9 +109,9 @@ module ActionDispatch
routes.sort_by!(&:precedence)
routes.map! { |r|
- match_data = r.path.match(req.path_info)
+ match_data = r.path.match(req.path_info)
path_parameters = r.defaults.dup
- match_data.names.zip(match_data.captures) { |name,val|
+ match_data.names.zip(match_data.captures) { |name, val|
path_parameters[name.to_sym] = Utils.unescape_uri(val) if val
}
[match_data, path_parameters, r]
diff --git a/actionpack/lib/action_dispatch/journey/router/utils.rb b/actionpack/lib/action_dispatch/journey/router/utils.rb
index ce5d350763..d641642338 100644
--- a/actionpack/lib/action_dispatch/journey/router/utils.rb
+++ b/actionpack/lib/action_dispatch/journey/router/utils.rb
@@ -58,12 +58,12 @@ module ActionDispatch
uri.gsub(ESCAPED) { |match| [match[1, 2].hex].pack("C") }.force_encoding(encoding)
end
- protected
- def escape(component, pattern)
+ private
+ def escape(component, pattern) # :doc:
component.gsub(pattern) { |unsafe| percent_encode(unsafe) }.force_encoding(US_ASCII)
end
- def percent_encode(unsafe)
+ def percent_encode(unsafe) # :doc:
safe = EMPTY.dup
unsafe.each_byte { |b| safe << DEC2HEX[b] }
safe
diff --git a/actionpack/lib/action_dispatch/journey/scanner.rb b/actionpack/lib/action_dispatch/journey/scanner.rb
index 4b8c8ab063..7dbb39b26d 100644
--- a/actionpack/lib/action_dispatch/journey/scanner.rb
+++ b/actionpack/lib/action_dispatch/journey/scanner.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: true
require "strscan"
module ActionDispatch
@@ -35,22 +36,23 @@ module ActionDispatch
def scan
case
# /
- when text = @ss.scan(/\//)
- [:SLASH, text]
+ when @ss.skip(/\//)
+ [:SLASH, "/"]
+ when @ss.skip(/\(/)
+ [:LPAREN, "("]
+ when @ss.skip(/\)/)
+ [:RPAREN, ")"]
+ when @ss.skip(/\|/)
+ [:OR, "|"]
+ when @ss.skip(/\./)
+ [:DOT, "."]
+ when text = @ss.scan(/:\w+/)
+ [:SYMBOL, text]
when text = @ss.scan(/\*\w+/)
[:STAR, text]
- when text = @ss.scan(/(?<!\\)\(/)
- [:LPAREN, text]
- when text = @ss.scan(/(?<!\\)\)/)
- [:RPAREN, text]
- when text = @ss.scan(/\|/)
- [:OR, text]
- when text = @ss.scan(/\./)
- [:DOT, text]
- when text = @ss.scan(/(?<!\\):\w+/)
- [:SYMBOL, text]
- when text = @ss.scan(/(?:[\w%\-~!$&'*+,;=@]|\\:|\\\(|\\\))+/)
- [:LITERAL, text.tr('\\', "")]
+ when text = @ss.scan(/(?:[\w%\-~!$&'*+,;=@]|\\[:()])+/)
+ text.tr! "\\", ""
+ [:LITERAL, text]
# any char
when text = @ss.scan(/./)
[:LITERAL, text]
diff --git a/actionpack/lib/action_dispatch/journey/visitors.rb b/actionpack/lib/action_dispatch/journey/visitors.rb
index 452dc84cc5..cda859cba4 100644
--- a/actionpack/lib/action_dispatch/journey/visitors.rb
+++ b/actionpack/lib/action_dispatch/journey/visitors.rb
@@ -1,5 +1,6 @@
module ActionDispatch
- module Journey # :nodoc:
+ # :stopdoc:
+ module Journey
class Format
ESCAPE_PATH = ->(value) { Router::Utils.escape_path(value) }
ESCAPE_SEGMENT = ->(value) { Router::Utils.escape_segment(value) }
@@ -21,7 +22,7 @@ module ActionDispatch
@children = []
@parameters = []
- parts.each_with_index do |object,i|
+ parts.each_with_index do |object, i|
case object
when Journey::Format
@children << i
@@ -261,4 +262,5 @@ module ActionDispatch
end
end
end
+ # :startdoc:
end
diff --git a/actionpack/lib/action_dispatch/middleware/cookies.rb b/actionpack/lib/action_dispatch/middleware/cookies.rb
index 6f4fab396a..956c53e813 100644
--- a/actionpack/lib/action_dispatch/middleware/cookies.rb
+++ b/actionpack/lib/action_dispatch/middleware/cookies.rb
@@ -179,7 +179,7 @@ module ActionDispatch
# Returns a jar that'll automatically generate a signed representation of cookie value and verify it when reading from
# the cookie again. This is useful for creating cookies with values that the user is not supposed to change. If a signed
- # cookie was tampered with by the user (or a 3rd party), nil will be returned.
+ # cookie was tampered with by the user (or a 3rd party), +nil+ will be returned.
#
# If +secrets.secret_key_base+ and +secrets.secret_token+ (deprecated) are both set,
# legacy cookies signed with the old key generator will be transparently upgraded.
@@ -202,7 +202,7 @@ module ActionDispatch
end
# Returns a jar that'll automatically encrypt cookie values before sending them to the client and will decrypt them for read.
- # If the cookie was tampered with by the user (or a 3rd party), nil will be returned.
+ # If the cookie was tampered with by the user (or a 3rd party), +nil+ will be returned.
#
# If +secrets.secret_key_base+ and +secrets.secret_token+ (deprecated) are both set,
# legacy cookies signed with the old key generator will be transparently upgraded.
@@ -332,13 +332,13 @@ module ActionDispatch
def update_cookies_from_jar
request_jar = @request.cookie_jar.instance_variable_get(:@cookies)
- set_cookies = request_jar.reject { |k,_| @delete_cookies.key?(k) }
+ set_cookies = request_jar.reject { |k, _| @delete_cookies.key?(k) }
@cookies.update set_cookies if set_cookies
end
def to_header
- @cookies.map { |k,v| "#{escape(k)}=#{escape(v)}" }.join "; "
+ @cookies.map { |k, v| "#{escape(k)}=#{escape(v)}" }.join "; "
end
def handle_options(options) #:nodoc:
diff --git a/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb b/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb
index ee644f41c8..1c720c5a8e 100644
--- a/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb
+++ b/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb
@@ -38,7 +38,9 @@ module ActionDispatch
end
def render(*)
- if logger = ActionView::Base.logger
+ logger = ActionView::Base.logger
+
+ if logger && logger.respond_to?(:silence)
logger.silence { super }
else
super
@@ -173,7 +175,11 @@ module ActionDispatch
end
def log_array(logger, array)
- array.map { |line| logger.fatal line }
+ if logger.formatter && logger.formatter.respond_to?(:tags_text)
+ logger.fatal array.join("\n#{logger.formatter.tags_text}")
+ else
+ logger.fatal array.join("\n")
+ end
end
def logger(request)
diff --git a/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb b/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb
index 99dc37c568..397f0a8b92 100644
--- a/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb
+++ b/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb
@@ -127,7 +127,7 @@ module ActionDispatch
File.open(full_path, "r") do |file|
start = [line - 3, 0].max
lines = file.each_line.drop(start).take(6)
- Hash[*(start+1..(lines.count+start)).zip(lines).flatten]
+ Hash[*(start + 1..(lines.count + start)).zip(lines).flatten]
end
end
end
diff --git a/actionpack/lib/action_dispatch/middleware/flash.rb b/actionpack/lib/action_dispatch/middleware/flash.rb
index 6900934712..cbe2f4be4d 100644
--- a/actionpack/lib/action_dispatch/middleware/flash.rb
+++ b/actionpack/lib/action_dispatch/middleware/flash.rb
@@ -129,7 +129,7 @@ module ActionDispatch
end
# Builds a hash containing the flashes to keep for the next request.
- # If there are none to keep, returns nil.
+ # If there are none to keep, returns +nil+.
def to_session_value #:nodoc:
flashes_to_keep = @flashes.except(*@discard)
return nil if flashes_to_keep.empty?
@@ -281,7 +281,8 @@ module ActionDispatch
@now
end
- def stringify_array(array)
+ private
+ def stringify_array(array) # :doc:
array.map do |item|
item.kind_of?(Symbol) ? item.to_s : item
end
diff --git a/actionpack/lib/action_dispatch/middleware/remote_ip.rb b/actionpack/lib/action_dispatch/middleware/remote_ip.rb
index 523eeb5b05..9f1ae80b97 100644
--- a/actionpack/lib/action_dispatch/middleware/remote_ip.rb
+++ b/actionpack/lib/action_dispatch/middleware/remote_ip.rb
@@ -153,9 +153,9 @@ module ActionDispatch
@ip ||= calculate_ip
end
- protected
+ private
- def ips_from(header)
+ def ips_from(header) # :doc:
return [] unless header
# Split the comma-separated list into an array of strings
ips = header.strip.split(/[,\s]+/)
@@ -171,7 +171,7 @@ module ActionDispatch
end
end
- def filter_proxies(ips)
+ def filter_proxies(ips) # :doc:
ips.reject do |ip|
@proxies.any? { |proxy| proxy === ip }
end
diff --git a/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb b/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb
index 49b82e7128..97c937b0b1 100644
--- a/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb
+++ b/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb
@@ -27,17 +27,16 @@ module ActionDispatch
sid
end
- protected
+ private
- def initialize_sid
+ def initialize_sid # :doc:
@default_options.delete(:sidbits)
@default_options.delete(:secure_random)
end
- private
- def make_request(env)
- ActionDispatch::Request.new env
- end
+ def make_request(env)
+ ActionDispatch::Request.new env
+ end
end
module StaleSessionCheck
diff --git a/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb b/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb
index 8409109ede..57d325a9d8 100644
--- a/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb
+++ b/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb
@@ -63,7 +63,7 @@ module ActionDispatch
# Other useful options include <tt>:key</tt>, <tt>:secure</tt> and
# <tt>:httponly</tt>.
class CookieStore < AbstractStore
- def initialize(app, options={})
+ def initialize(app, options = {})
super(app, options.merge!(cookie_only: true))
end
@@ -102,7 +102,7 @@ module ActionDispatch
end
end
- def persistent_session_id!(data, sid=nil)
+ def persistent_session_id!(data, sid = nil)
data ||= {}
data["session_id"] ||= sid || generate_sid
data
diff --git a/actionpack/lib/action_dispatch/middleware/ssl.rb b/actionpack/lib/action_dispatch/middleware/ssl.rb
index c9bd417aa2..557721c301 100644
--- a/actionpack/lib/action_dispatch/middleware/ssl.rb
+++ b/actionpack/lib/action_dispatch/middleware/ssl.rb
@@ -23,7 +23,7 @@ module ActionDispatch
# `180.days` (recommended).
# * `subdomains`: Set to `true` to tell the browser to apply these settings
# to all subdomains. This protects your cookies from interception by a
- # vulnerable site on a subdomain. Defaults to `false`.
+ # vulnerable site on a subdomain. Defaults to `true`.
# * `preload`: Advertise that this site may be included in browsers'
# preloaded HSTS lists. HSTS protects your site on every visit *except the
# first visit* since it hasn't seen your HSTS header yet. To close this
diff --git a/actionpack/lib/action_dispatch/request/session.rb b/actionpack/lib/action_dispatch/request/session.rb
index b883ca0f61..a2a80f39fc 100644
--- a/actionpack/lib/action_dispatch/request/session.rb
+++ b/actionpack/lib/action_dispatch/request/session.rb
@@ -53,7 +53,7 @@ module ActionDispatch
}
end
- def []=(k,v); @delegate[k] = v; end
+ def []=(k, v); @delegate[k] = v; end
def to_hash; @delegate.dup; end
def values_at(*args); @delegate.values_at(*args); end
end
@@ -85,7 +85,7 @@ module ActionDispatch
end
# Returns value of the key stored in the session or
- # nil if the given key is not found in the session.
+ # +nil+ if the given key is not found in the session.
def [](key)
load_for_read!
@delegate[key.to_s]
@@ -124,7 +124,7 @@ module ActionDispatch
# Returns the session as Hash.
def to_hash
load_for_read!
- @delegate.dup.delete_if { |_,v| v.nil? }
+ @delegate.dup.delete_if { |_, v| v.nil? }
end
# Updates the session with given Hash.
@@ -162,7 +162,7 @@ module ActionDispatch
# :bar
# end
# # => :bar
- def fetch(key, default=Unspecified, &block)
+ def fetch(key, default = Unspecified, &block)
load_for_read!
if default == Unspecified
@delegate.fetch(key.to_s, &block)
diff --git a/actionpack/lib/action_dispatch/request/utils.rb b/actionpack/lib/action_dispatch/request/utils.rb
index 282bdbd2be..01bc871e5f 100644
--- a/actionpack/lib/action_dispatch/request/utils.rb
+++ b/actionpack/lib/action_dispatch/request/utils.rb
@@ -4,6 +4,17 @@ module ActionDispatch
mattr_accessor :perform_deep_munge
self.perform_deep_munge = true
+ def self.each_param_value(params, &block)
+ case params
+ when Array
+ params.each { |element| each_param_value(element, &block) }
+ when Hash
+ params.each_value { |value| each_param_value(value, &block) }
+ when String
+ block.call params
+ end
+ end
+
def self.normalize_encode_params(params)
if perform_deep_munge
NoNilParamEncoder.normalize_encode_params params
diff --git a/actionpack/lib/action_dispatch/routing.rb b/actionpack/lib/action_dispatch/routing.rb
index 61ebd0b8db..c554ce98bc 100644
--- a/actionpack/lib/action_dispatch/routing.rb
+++ b/actionpack/lib/action_dispatch/routing.rb
@@ -1,3 +1,5 @@
+require "active_support/core_ext/string/filters"
+
module ActionDispatch
# The routing module provides URL rewriting in native Ruby. It's a way to
# redirect incoming requests to controllers and actions. This replaces
diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb
index c481c190bf..089aa9f78e 100644
--- a/actionpack/lib/action_dispatch/routing/mapper.rb
+++ b/actionpack/lib/action_dispatch/routing/mapper.rb
@@ -1,7 +1,6 @@
require "active_support/core_ext/hash/slice"
require "active_support/core_ext/enumerable"
require "active_support/core_ext/array/extract_options"
-require "active_support/core_ext/regexp"
require "action_dispatch/routing/redirection"
require "action_dispatch/routing/endpoint"
@@ -568,7 +567,7 @@ module ActionDispatch
# [:format]
# Allows you to specify the default value for optional +format+
# segment or disable it by supplying +false+.
- def match(path, options=nil)
+ def match(path, options = nil)
end
# Mount a Rack-based application to be used within the application.
@@ -997,65 +996,65 @@ module ActionDispatch
end
private
- def merge_path_scope(parent, child) #:nodoc:
+ def merge_path_scope(parent, child)
Mapper.normalize_path("#{parent}/#{child}")
end
- def merge_shallow_path_scope(parent, child) #:nodoc:
+ def merge_shallow_path_scope(parent, child)
Mapper.normalize_path("#{parent}/#{child}")
end
- def merge_as_scope(parent, child) #:nodoc:
+ def merge_as_scope(parent, child)
parent ? "#{parent}_#{child}" : child
end
- def merge_shallow_prefix_scope(parent, child) #:nodoc:
+ def merge_shallow_prefix_scope(parent, child)
parent ? "#{parent}_#{child}" : child
end
- def merge_module_scope(parent, child) #:nodoc:
+ def merge_module_scope(parent, child)
parent ? "#{parent}/#{child}" : child
end
- def merge_controller_scope(parent, child) #:nodoc:
+ def merge_controller_scope(parent, child)
child
end
- def merge_action_scope(parent, child) #:nodoc:
+ def merge_action_scope(parent, child)
child
end
- def merge_via_scope(parent, child) #:nodoc:
+ def merge_via_scope(parent, child)
child
end
- def merge_format_scope(parent, child) #:nodoc:
+ def merge_format_scope(parent, child)
child
end
- def merge_path_names_scope(parent, child) #:nodoc:
+ def merge_path_names_scope(parent, child)
merge_options_scope(parent, child)
end
- def merge_constraints_scope(parent, child) #:nodoc:
+ def merge_constraints_scope(parent, child)
merge_options_scope(parent, child)
end
- def merge_defaults_scope(parent, child) #:nodoc:
+ def merge_defaults_scope(parent, child)
merge_options_scope(parent, child)
end
- def merge_blocks_scope(parent, child) #:nodoc:
+ def merge_blocks_scope(parent, child)
merged = parent ? parent.dup : []
merged << child if child
merged
end
- def merge_options_scope(parent, child) #:nodoc:
+ def merge_options_scope(parent, child)
(parent || {}).merge(child)
end
- def merge_shallow_scope(parent, child) #:nodoc:
+ def merge_shallow_scope(parent, child)
child ? true : false
end
@@ -1245,11 +1244,11 @@ module ActionDispatch
# the plural):
#
# GET /profile/new
- # POST /profile
# GET /profile
# GET /profile/edit
# PATCH/PUT /profile
# DELETE /profile
+ # POST /profile
#
# === Options
# Takes same options as +resources+.
@@ -1267,15 +1266,15 @@ module ActionDispatch
concerns(options[:concerns]) if options[:concerns]
- collection do
- post :create
- end if parent_resource.actions.include?(:create)
-
new do
get :new
end if parent_resource.actions.include?(:new)
set_member_mappings_for_resource
+
+ collection do
+ post :create
+ end if parent_resource.actions.include?(:create)
end
end
@@ -1620,13 +1619,13 @@ module ActionDispatch
end
end
- protected
+ private
- def parent_resource #:nodoc:
+ def parent_resource
@scope[:scope_level_resource]
end
- def apply_common_behavior_for(method, resources, options, &block) #:nodoc:
+ def apply_common_behavior_for(method, resources, options, &block)
if resources.length > 1
resources.each { |r| send(method, r, options, &block) }
return true
@@ -1659,39 +1658,39 @@ module ActionDispatch
false
end
- def apply_action_options(options) # :nodoc:
+ def apply_action_options(options)
return options if action_options? options
options.merge scope_action_options
end
- def action_options?(options) #:nodoc:
+ def action_options?(options)
options[:only] || options[:except]
end
- def scope_action_options #:nodoc:
+ def scope_action_options
@scope[:action_options] || {}
end
- def resource_scope? #:nodoc:
+ def resource_scope?
@scope.resource_scope?
end
- def resource_method_scope? #:nodoc:
+ def resource_method_scope?
@scope.resource_method_scope?
end
- def nested_scope? #:nodoc:
+ def nested_scope?
@scope.nested?
end
- def with_scope_level(kind)
+ def with_scope_level(kind) # :doc:
@scope = @scope.new_level(kind)
yield
ensure
@scope = @scope.parent
end
- def resource_scope(resource) #:nodoc:
+ def resource_scope(resource)
@scope = @scope.new(scope_level_resource: resource)
controller(resource.resource_scope) { yield }
@@ -1699,7 +1698,7 @@ module ActionDispatch
@scope = @scope.parent
end
- def nested_options #:nodoc:
+ def nested_options
options = { as: parent_resource.member_name }
options[:constraints] = {
parent_resource.nested_param => param_constraint
@@ -1708,25 +1707,25 @@ module ActionDispatch
options
end
- def shallow_nesting_depth #:nodoc:
+ def shallow_nesting_depth
@scope.find_all { |node|
node.frame[:scope_level_resource]
}.count { |node| node.frame[:scope_level_resource].shallow? }
end
- def param_constraint? #:nodoc:
+ def param_constraint?
@scope[:constraints] && @scope[:constraints][parent_resource.param].is_a?(Regexp)
end
- def param_constraint #:nodoc:
+ def param_constraint
@scope[:constraints][parent_resource.param]
end
- def canonical_action?(action) #:nodoc:
+ def canonical_action?(action)
resource_method_scope? && CANONICAL_ACTIONS.include?(action.to_s)
end
- def shallow_scope #:nodoc:
+ def shallow_scope
scope = { as: @scope[:shallow_prefix],
path: @scope[:shallow_path] }
@scope = @scope.new scope
@@ -1736,7 +1735,7 @@ module ActionDispatch
@scope = @scope.parent
end
- def path_for_action(action, path) #:nodoc:
+ def path_for_action(action, path)
return "#{@scope[:path]}/#{path}" if path
if canonical_action?(action)
@@ -1746,11 +1745,11 @@ module ActionDispatch
end
end
- def action_path(name) #:nodoc:
+ def action_path(name)
@scope[:path_names][name.to_sym] || name
end
- def prefix_name_for_action(as, action) #:nodoc:
+ def prefix_name_for_action(as, action)
if as
prefix = as
elsif !canonical_action?(action)
@@ -1762,7 +1761,7 @@ module ActionDispatch
end
end
- def name_for_action(as, action) #:nodoc:
+ def name_for_action(as, action)
prefix = prefix_name_for_action(as, action)
name_prefix = @scope[:as]
@@ -1788,7 +1787,7 @@ module ActionDispatch
end
end
- def set_member_mappings_for_resource
+ def set_member_mappings_for_resource # :doc:
member do
get :edit if parent_resource.actions.include?(:edit)
get :show if parent_resource.actions.include?(:show)
@@ -1800,12 +1799,10 @@ module ActionDispatch
end
end
- def api_only?
+ def api_only? # :doc:
@set.api_only?
end
- private
-
def path_scope(path)
@scope = @scope.new(path: merge_path_scope(@scope[:path], path))
yield
@@ -1869,7 +1866,7 @@ module ActionDispatch
path =~ %r{^/?[-\w]+/[-\w/]+$}
end
- def decomposed_match(path, controller, options, _path, to, via, formatted, anchor, options_constraints) # :nodoc:
+ def decomposed_match(path, controller, options, _path, to, via, formatted, anchor, options_constraints)
if on = options.delete(:on)
send(on) { decomposed_match(path, controller, options, _path, to, via, formatted, anchor, options_constraints) }
else
@@ -1884,7 +1881,7 @@ module ActionDispatch
end
end
- def add_route(action, controller, options, _path, to, via, formatted, anchor, options_constraints) # :nodoc:
+ def add_route(action, controller, options, _path, to, via, formatted, anchor, options_constraints)
path = path_for_action(action, _path)
raise ArgumentError, "path is required" if path.blank?
diff --git a/actionpack/lib/action_dispatch/routing/polymorphic_routes.rb b/actionpack/lib/action_dispatch/routing/polymorphic_routes.rb
index 4f1aaeefc8..432b9bf4c1 100644
--- a/actionpack/lib/action_dispatch/routing/polymorphic_routes.rb
+++ b/actionpack/lib/action_dispatch/routing/polymorphic_routes.rb
@@ -160,7 +160,7 @@ module ActionDispatch
CACHE = { "path" => {}, "url" => {} }
def self.get(action, type)
- type = type.to_s
+ type = type.to_s
CACHE[type].fetch(action) { build action, type }
end
@@ -266,7 +266,7 @@ module ActionDispatch
args = []
- route = record_list.map { |parent|
+ route = record_list.map { |parent|
case parent
when Symbol, String
parent.to_s
@@ -304,7 +304,7 @@ module ActionDispatch
private
def get_method_for_class(klass)
- name = @key_strategy.call klass.model_name
+ name = @key_strategy.call klass.model_name
get_method_for_string name
end
diff --git a/actionpack/lib/action_dispatch/routing/redirection.rb b/actionpack/lib/action_dispatch/routing/redirection.rb
index 87bcceccc0..4e2318a45e 100644
--- a/actionpack/lib/action_dispatch/routing/redirection.rb
+++ b/actionpack/lib/action_dispatch/routing/redirection.rb
@@ -61,15 +61,15 @@ module ActionDispatch
end
def escape(params)
- Hash[params.map { |k,v| [k, Rack::Utils.escape(v)] }]
+ Hash[params.map { |k, v| [k, Rack::Utils.escape(v)] }]
end
def escape_fragment(params)
- Hash[params.map { |k,v| [k, Journey::Router::Utils.escape_fragment(v)] }]
+ Hash[params.map { |k, v| [k, Journey::Router::Utils.escape_fragment(v)] }]
end
def escape_path(params)
- Hash[params.map { |k,v| [k, Journey::Router::Utils.escape_path(v)] }]
+ Hash[params.map { |k, v| [k, Journey::Router::Utils.escape_path(v)] }]
end
end
@@ -128,7 +128,7 @@ module ActionDispatch
end
def inspect
- "redirect(#{status}, #{options.map { |k,v| "#{k}: #{v}" }.join(', ')})"
+ "redirect(#{status}, #{options.map { |k, v| "#{k}: #{v}" }.join(', ')})"
end
end
diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb
index a1bc357c8b..5853adb110 100644
--- a/actionpack/lib/action_dispatch/routing/route_set.rb
+++ b/actionpack/lib/action_dispatch/routing/route_set.rb
@@ -1,5 +1,4 @@
require "action_dispatch/journey"
-require "active_support/concern"
require "active_support/core_ext/object/to_query"
require "active_support/core_ext/hash/slice"
require "active_support/core_ext/module/remove_method"
@@ -71,7 +70,7 @@ module ActionDispatch
private :routes
def initialize
- @routes = {}
+ @routes = {}
@path_helpers = Set.new
@url_helpers = Set.new
@url_helpers_module = Module.new
@@ -208,7 +207,7 @@ module ActionDispatch
params = parameterize_args(args) { |missing_key|
missing_keys << missing_key
}
- constraints = Hash[@route.requirements.merge(params).sort_by { |k,v| k.to_s }]
+ constraints = Hash[@route.requirements.merge(params).sort_by { |k, v| k.to_s }]
message = "No route matches #{constraints.inspect}"
message << ", missing required keys: #{missing_keys.sort.inspect}"
@@ -647,11 +646,11 @@ module ActionDispatch
# Generate the path indicated by the arguments, and return an array of
# the keys that were not used to generate it.
- def extra_keys(options, recall={})
+ def extra_keys(options, recall = {})
generate_extras(options, recall).last
end
- def generate_extras(options, recall={})
+ def generate_extras(options, recall = {})
route_key = options.delete :use_route
path, params = generate(route_key, options, recall)
return path, params.keys
@@ -693,7 +692,7 @@ module ActionDispatch
password = options.delete :password
end
- recall = options.delete(:_recall) { {} }
+ recall = options.delete(:_recall) { {} }
original_script_name = options.delete(:original_script_name)
script_name = find_script_name options
diff --git a/actionpack/lib/action_dispatch/routing/url_for.rb b/actionpack/lib/action_dispatch/routing/url_for.rb
index a1ac5a2b6c..3e564f13d8 100644
--- a/actionpack/lib/action_dispatch/routing/url_for.rb
+++ b/actionpack/lib/action_dispatch/routing/url_for.rb
@@ -198,14 +198,16 @@ module ActionDispatch
_routes.optimize_routes_generation? && default_url_options.empty?
end
- def _with_routes(routes)
+ private
+
+ def _with_routes(routes) # :doc:
old_routes, @_routes = @_routes, routes
yield
ensure
@_routes = old_routes
end
- def _routes_context
+ def _routes_context # :doc:
self
end
end
diff --git a/actionpack/lib/action_dispatch/testing/assertions/response.rb b/actionpack/lib/action_dispatch/testing/assertions/response.rb
index a2eaccd9ef..817737341c 100644
--- a/actionpack/lib/action_dispatch/testing/assertions/response.rb
+++ b/actionpack/lib/action_dispatch/testing/assertions/response.rb
@@ -50,7 +50,7 @@ module ActionDispatch
#
# # Asserts that the redirection matches the regular expression
# assert_redirected_to %r(\Ahttp://example.org)
- def assert_redirected_to(options = {}, message=nil)
+ def assert_redirected_to(options = {}, message = nil)
assert_response(:redirect, message)
return true if options === @response.location
diff --git a/actionpack/lib/action_dispatch/testing/assertions/routing.rb b/actionpack/lib/action_dispatch/testing/assertions/routing.rb
index e53bc6af12..454dcb9307 100644
--- a/actionpack/lib/action_dispatch/testing/assertions/routing.rb
+++ b/actionpack/lib/action_dispatch/testing/assertions/routing.rb
@@ -37,7 +37,7 @@ module ActionDispatch
#
# # Test a custom route
# assert_recognizes({controller: 'items', action: 'show', id: '1'}, 'view/item1')
- def assert_recognizes(expected_options, path, extras={}, msg=nil)
+ def assert_recognizes(expected_options, path, extras = {}, msg = nil)
if path.is_a?(Hash) && path[:method].to_s == "all"
[:get, :post, :put, :delete].each do |method|
assert_recognizes(expected_options, path.merge(method: method), extras, msg)
@@ -75,7 +75,7 @@ module ActionDispatch
#
# # Asserts that the generated route gives us our custom route
# assert_generates "changesets/12", { controller: 'scm', action: 'show_diff', revision: "12" }
- def assert_generates(expected_path, options, defaults={}, extras={}, message=nil)
+ def assert_generates(expected_path, options, defaults = {}, extras = {}, message = nil)
if expected_path =~ %r{://}
fail_on(URI::InvalidURIError, message) do
uri = URI.parse(expected_path)
@@ -119,7 +119,7 @@ module ActionDispatch
#
# # Tests a route with an HTTP method
# assert_routing({ method: 'put', path: '/product/321' }, { controller: "product", action: "update", id: "321" })
- def assert_routing(path, options, defaults={}, extras={}, message=nil)
+ def assert_routing(path, options, defaults = {}, extras = {}, message = nil)
assert_recognizes(options, path, extras, message)
controller, default_controller = options[:controller], defaults[:controller]
diff --git a/actionpack/lib/action_dispatch/testing/integration.rb b/actionpack/lib/action_dispatch/testing/integration.rb
index d137473ef4..021ffec862 100644
--- a/actionpack/lib/action_dispatch/testing/integration.rb
+++ b/actionpack/lib/action_dispatch/testing/integration.rb
@@ -11,62 +11,37 @@ require "action_dispatch/testing/request_encoder"
module ActionDispatch
module Integration #:nodoc:
module RequestHelpers
- # Performs a GET request with the given parameters.
- #
- # - +path+: The URI (as a String) on which you want to perform a GET
- # request.
- # - +params+: The HTTP parameters that you want to pass. This may
- # be +nil+,
- # a Hash, or a String that is appropriately encoded
- # (<tt>application/x-www-form-urlencoded</tt> or
- # <tt>multipart/form-data</tt>).
- # - +headers+: Additional headers to pass, as a Hash. The headers will be
- # merged into the Rack env hash.
- # - +env+: Additional env to pass, as a Hash. The headers will be
- # merged into the Rack env hash.
- #
- # This method returns a Response object, which one can use to
- # inspect the details of the response. Furthermore, if this method was
- # called from an ActionDispatch::IntegrationTest object, then that
- # object's <tt>@response</tt> instance variable will point to the same
- # response object.
- #
- # You can also perform POST, PATCH, PUT, DELETE, and HEAD requests with
- # +#post+, +#patch+, +#put+, +#delete+, and +#head+.
- #
- # Example:
- #
- # get '/feed', params: { since: 201501011400 }
- # post '/profile', headers: { "X-Test-Header" => "testvalue" }
+ # Performs a GET request with the given parameters. See +#process+ for more
+ # details.
def get(path, **args)
process(:get, path, **args)
end
- # Performs a POST request with the given parameters. See +#get+ for more
+ # Performs a POST request with the given parameters. See +#process+ for more
# details.
def post(path, **args)
process(:post, path, **args)
end
- # Performs a PATCH request with the given parameters. See +#get+ for more
+ # Performs a PATCH request with the given parameters. See +#process+ for more
# details.
def patch(path, **args)
process(:patch, path, **args)
end
- # Performs a PUT request with the given parameters. See +#get+ for more
+ # Performs a PUT request with the given parameters. See +#process+ for more
# details.
def put(path, **args)
process(:put, path, **args)
end
- # Performs a DELETE request with the given parameters. See +#get+ for
+ # Performs a DELETE request with the given parameters. See +#process+ for
# more details.
def delete(path, **args)
process(:delete, path, **args)
end
- # Performs a HEAD request with the given parameters. See +#get+ for more
+ # Performs a HEAD request with the given parameters. See +#process+ for more
# details.
def head(path, *args)
process(:head, path, *args)
@@ -94,7 +69,7 @@ module ActionDispatch
DEFAULT_HOST = "www.example.com"
include Minitest::Assertions
- include RequestHelpers, Assertions
+ include TestProcess, RequestHelpers, Assertions
%w( status status_message headers body redirect? ).each do |method|
delegate method, to: :response, allow_nil: true
@@ -198,101 +173,126 @@ module ActionDispatch
@https
end
- # Set the host name to use in the next request.
+ # Performs the actual request.
#
- # session.host! "www.example.com"
- alias :host! :host=
-
- private
- def _mock_session
- @_mock_session ||= Rack::MockSession.new(@app, host)
+ # - +method+: The HTTP method (GET, POST, PATCH, PUT, DELETE, HEAD, OPTIONS)
+ # as a symbol.
+ # - +path+: The URI (as a String) on which you want to perform the
+ # request.
+ # - +params+: The HTTP parameters that you want to pass. This may
+ # be +nil+,
+ # a Hash, or a String that is appropriately encoded
+ # (<tt>application/x-www-form-urlencoded</tt> or
+ # <tt>multipart/form-data</tt>).
+ # - +headers+: Additional headers to pass, as a Hash. The headers will be
+ # merged into the Rack env hash.
+ # - +env+: Additional env to pass, as a Hash. The headers will be
+ # merged into the Rack env hash.
+ #
+ # This method is rarely used directly. Use +#get+, +#post+, or other standard
+ # HTTP methods in integration tests. +#process+ is only required when using a
+ # request method that doesn't have a method defined in the integration tests.
+ #
+ # This method returns a Response object, which one can use to
+ # inspect the details of the response. Furthermore, if this method was
+ # called from an ActionDispatch::IntegrationTest object, then that
+ # object's <tt>@response</tt> instance variable will point to the same
+ # response object.
+ #
+ # Example:
+ # process :get, '/author', params: { since: 201501011400 }
+ def process(method, path, params: nil, headers: nil, env: nil, xhr: false, as: nil)
+ request_encoder = RequestEncoder.encoder(as)
+ headers ||= {}
+
+ if method == :get && as == :json && params
+ headers["X-Http-Method-Override"] = "GET"
+ method = :post
end
- # Performs the actual request.
- def process(method, path, params: nil, headers: nil, env: nil, xhr: false, as: nil)
- request_encoder = RequestEncoder.encoder(as)
- headers ||= {}
+ if path =~ %r{://}
+ path = build_expanded_path(path) do |location|
+ https! URI::HTTPS === location if location.scheme
- if method == :get && as == :json && params
- headers["X-Http-Method-Override"] = "GET"
- method = :post
+ if url_host = location.host
+ default = Rack::Request::DEFAULT_PORTS[location.scheme]
+ url_host += ":#{location.port}" if default != location.port
+ host! url_host
+ end
end
+ end
- if path =~ %r{://}
- path = build_expanded_path(path, request_encoder) do |location|
- https! URI::HTTPS === location if location.scheme
+ hostname, port = host.split(":")
- if url_host = location.host
- default = Rack::Request::DEFAULT_PORTS[location.scheme]
- url_host += ":#{location.port}" if default != location.port
- host! url_host
- end
- end
- elsif as
- path = build_expanded_path(path, request_encoder)
- end
+ request_env = {
+ :method => method,
+ :params => request_encoder.encode_params(params),
- hostname, port = host.split(":")
+ "SERVER_NAME" => hostname,
+ "SERVER_PORT" => port || (https? ? "443" : "80"),
+ "HTTPS" => https? ? "on" : "off",
+ "rack.url_scheme" => https? ? "https" : "http",
- request_env = {
- :method => method,
- :params => request_encoder.encode_params(params),
+ "REQUEST_URI" => path,
+ "HTTP_HOST" => host,
+ "REMOTE_ADDR" => remote_addr,
+ "CONTENT_TYPE" => request_encoder.content_type,
+ "HTTP_ACCEPT" => request_encoder.accept_header || accept
+ }
- "SERVER_NAME" => hostname,
- "SERVER_PORT" => port || (https? ? "443" : "80"),
- "HTTPS" => https? ? "on" : "off",
- "rack.url_scheme" => https? ? "https" : "http",
+ wrapped_headers = Http::Headers.from_hash({})
+ wrapped_headers.merge!(headers) if headers
- "REQUEST_URI" => path,
- "HTTP_HOST" => host,
- "REMOTE_ADDR" => remote_addr,
- "CONTENT_TYPE" => request_encoder.content_type,
- "HTTP_ACCEPT" => accept
- }
+ if xhr
+ wrapped_headers["HTTP_X_REQUESTED_WITH"] = "XMLHttpRequest"
+ wrapped_headers["HTTP_ACCEPT"] ||= [Mime[:js], Mime[:html], Mime[:xml], "text/xml", "*/*"].join(", ")
+ end
- wrapped_headers = Http::Headers.from_hash({})
- wrapped_headers.merge!(headers) if headers
+ # this modifies the passed request_env directly
+ if wrapped_headers.present?
+ Http::Headers.from_hash(request_env).merge!(wrapped_headers)
+ end
+ if env.present?
+ Http::Headers.from_hash(request_env).merge!(env)
+ end
- if xhr
- wrapped_headers["HTTP_X_REQUESTED_WITH"] = "XMLHttpRequest"
- wrapped_headers["HTTP_ACCEPT"] ||= [Mime[:js], Mime[:html], Mime[:xml], "text/xml", "*/*"].join(", ")
- end
+ session = Rack::Test::Session.new(_mock_session)
- # this modifies the passed request_env directly
- if wrapped_headers.present?
- Http::Headers.from_hash(request_env).merge!(wrapped_headers)
- end
- if env.present?
- Http::Headers.from_hash(request_env).merge!(env)
- end
+ # NOTE: rack-test v0.5 doesn't build a default uri correctly
+ # Make sure requested path is always a full uri
+ session.request(build_full_uri(path, request_env), request_env)
- session = Rack::Test::Session.new(_mock_session)
+ @request_count += 1
+ @request = ActionDispatch::Request.new(session.last_request.env)
+ response = _mock_session.last_response
+ @response = ActionDispatch::TestResponse.from_response(response)
+ @response.request = @request
+ @html_document = nil
+ @url_options = nil
- # NOTE: rack-test v0.5 doesn't build a default uri correctly
- # Make sure requested path is always a full uri
- session.request(build_full_uri(path, request_env), request_env)
+ @controller = @request.controller_instance
- @request_count += 1
- @request = ActionDispatch::Request.new(session.last_request.env)
- response = _mock_session.last_response
- @response = ActionDispatch::TestResponse.from_response(response)
- @response.request = @request
- @html_document = nil
- @url_options = nil
+ response.status
+ end
- @controller = @request.controller_instance
+ # Set the host name to use in the next request.
+ #
+ # session.host! "www.example.com"
+ alias :host! :host=
- response.status
+ private
+ def _mock_session
+ @_mock_session ||= Rack::MockSession.new(@app, host)
end
def build_full_uri(path, env)
"#{env['rack.url_scheme']}://#{env['SERVER_NAME']}:#{env['SERVER_PORT']}#{path}"
end
- def build_expanded_path(path, request_encoder)
+ def build_expanded_path(path)
location = URI.parse(path)
yield location if block_given?
- path = request_encoder.append_format_to location.path
+ path = location.path
location.query ? "#{path}?#{location.query}" : path
end
end
@@ -366,6 +366,7 @@ module ActionDispatch
# simultaneously.
def open_session
dup.tap do |session|
+ session.reset!
yield session if block_given?
end
end
@@ -576,13 +577,15 @@ module ActionDispatch
# end
# end
#
- # The +as+ option sets the format to JSON, sets the content type to
+ # The +as+ option passes an "application/json" Accept header (thereby setting
+ # the request format to JSON unless overridden), sets the content type to
# "application/json" and encodes the parameters as JSON.
#
# Calling +parsed_body+ on the response parses the response body based on the
# last response MIME type.
#
- # For any custom MIME types you've registered, you can even add your own encoders with:
+ # Out of the box, only <tt>:json</tt> is supported. But for any custom MIME
+ # types you've registered, you can add your own encoders with:
#
# ActionDispatch::IntegrationTest.register_encoder :wibble,
# param_encoder: -> params { params.to_wibble },
@@ -595,9 +598,7 @@ module ActionDispatch
# Consult the Rails Testing Guide for more.
class IntegrationTest < ActiveSupport::TestCase
- include TestProcess
-
- undef :assigns
+ include TestProcess::FixtureFile
module UrlOptions
extend ActiveSupport::Concern
diff --git a/actionpack/lib/action_dispatch/testing/request_encoder.rb b/actionpack/lib/action_dispatch/testing/request_encoder.rb
index b0b994b2d0..8c27e9ecb7 100644
--- a/actionpack/lib/action_dispatch/testing/request_encoder.rb
+++ b/actionpack/lib/action_dispatch/testing/request_encoder.rb
@@ -1,10 +1,17 @@
module ActionDispatch
class RequestEncoder # :nodoc:
- @encoders = {}
+ class IdentityEncoder
+ def content_type; end
+ def accept_header; end
+ def encode_params(params); params; end
+ def response_parser; -> body { body }; end
+ end
+
+ @encoders = { identity: IdentityEncoder.new }
attr_reader :response_parser
- def initialize(mime_name, param_encoder, response_parser, url_encoded_form = false)
+ def initialize(mime_name, param_encoder, response_parser)
@mime = Mime[mime_name]
unless @mime
@@ -12,21 +19,15 @@ module ActionDispatch
"unregistered MIME Type: #{mime_name}. See `Mime::Type.register`."
end
- @url_encoded_form = url_encoded_form
- @path_format = ".#{@mime.symbol}" unless @url_encoded_form
- @response_parser = response_parser || -> body { body }
- @param_encoder = param_encoder || :"to_#{@mime.symbol}".to_proc
+ @response_parser = response_parser || -> body { body }
+ @param_encoder = param_encoder || :"to_#{@mime.symbol}".to_proc
end
- def append_format_to(path)
- if @url_encoded_form
- path
- else
- path + @path_format
- end
+ def content_type
+ @mime.to_s
end
- def content_type
+ def accept_header
@mime.to_s
end
@@ -40,7 +41,7 @@ module ActionDispatch
end
def self.encoder(name)
- @encoders[name] || WWWFormEncoder
+ @encoders[name] || @encoders[:identity]
end
def self.register_encoder(mime_name, param_encoder: nil, response_parser: nil)
@@ -48,7 +49,5 @@ module ActionDispatch
end
register_encoder :json, response_parser: -> body { JSON.parse(body) }
-
- WWWFormEncoder = new(:url_encoded_form, -> params { params }, nil, true)
end
end
diff --git a/actionpack/lib/action_dispatch/testing/test_process.rb b/actionpack/lib/action_dispatch/testing/test_process.rb
index 8b03b776fa..0282eb15c3 100644
--- a/actionpack/lib/action_dispatch/testing/test_process.rb
+++ b/actionpack/lib/action_dispatch/testing/test_process.rb
@@ -3,6 +3,26 @@ require "action_dispatch/middleware/flash"
module ActionDispatch
module TestProcess
+ module FixtureFile
+ # Shortcut for <tt>Rack::Test::UploadedFile.new(File.join(ActionDispatch::IntegrationTest.fixture_path, path), type)</tt>:
+ #
+ # post :change_avatar, avatar: fixture_file_upload('files/spongebob.png', 'image/png')
+ #
+ # To upload binary files on Windows, pass <tt>:binary</tt> as the last parameter.
+ # This will not affect other platforms:
+ #
+ # post :change_avatar, avatar: fixture_file_upload('files/spongebob.png', 'image/png', :binary)
+ def fixture_file_upload(path, mime_type = nil, binary = false)
+ if self.class.respond_to?(:fixture_path) && self.class.fixture_path &&
+ !File.exist?(path)
+ path = File.join(self.class.fixture_path, path)
+ end
+ Rack::Test::UploadedFile.new(path, mime_type, binary)
+ end
+ end
+
+ include FixtureFile
+
def assigns(key = nil)
raise NoMethodError,
"assigns has been extracted to a gem. To continue using it,
@@ -24,21 +44,5 @@ module ActionDispatch
def redirect_to_url
@response.redirect_url
end
-
- # Shortcut for <tt>Rack::Test::UploadedFile.new(File.join(ActionDispatch::IntegrationTest.fixture_path, path), type)</tt>:
- #
- # post :change_avatar, avatar: fixture_file_upload('files/spongebob.png', 'image/png')
- #
- # To upload binary files on Windows, pass <tt>:binary</tt> as the last parameter.
- # This will not affect other platforms:
- #
- # post :change_avatar, avatar: fixture_file_upload('files/spongebob.png', 'image/png', :binary)
- def fixture_file_upload(path, mime_type = nil, binary = false)
- if self.class.respond_to?(:fixture_path) && self.class.fixture_path &&
- !File.exist?(path)
- path = File.join(self.class.fixture_path, path)
- end
- Rack::Test::UploadedFile.new(path, mime_type, binary)
- end
end
end
diff --git a/actionpack/test/abstract/collector_test.rb b/actionpack/test/abstract/collector_test.rb
index 7fe19e6b10..1cd3526483 100644
--- a/actionpack/test/abstract/collector_test.rb
+++ b/actionpack/test/abstract/collector_test.rb
@@ -55,7 +55,7 @@ module AbstractController
collector.js(:bar) { :baz }
assert_equal [Mime[:html], [], nil], collector.responses[0]
assert_equal [Mime[:text], [:foo], nil], collector.responses[1]
- assert_equal [Mime[:js], [:bar]], collector.responses[2][0,2]
+ assert_equal [Mime[:js], [:bar]], collector.responses[2][0, 2]
assert_equal :baz, collector.responses[2][2].call
end
end
diff --git a/actionpack/test/abstract_unit.rb b/actionpack/test/abstract_unit.rb
index 6d28753947..11a9092527 100644
--- a/actionpack/test/abstract_unit.rb
+++ b/actionpack/test/abstract_unit.rb
@@ -351,7 +351,7 @@ class ResourcesController < ActionController::Base
end
class CommentsController < ResourcesController; end
-class AccountsController < ResourcesController; end
+class AccountsController < ResourcesController; end
class ImagesController < ResourcesController; end
# Skips the current run on Rubinius using Minitest::Assertions#skip
diff --git a/actionpack/test/controller/caching_test.rb b/actionpack/test/controller/caching_test.rb
index 18490c7d73..fa8d9dc09a 100644
--- a/actionpack/test/controller/caching_test.rb
+++ b/actionpack/test/controller/caching_test.rb
@@ -58,7 +58,7 @@ class FragmentCachingTest < ActionController::TestCase
def test_fragment_cache_key
assert_equal "views/what a key", @controller.fragment_cache_key("what a key")
assert_equal "views/test.host/fragment_caching_test/some_action",
- @controller.fragment_cache_key(controller: "fragment_caching_test",action: "some_action")
+ @controller.fragment_cache_key(controller: "fragment_caching_test", action: "some_action")
end
def test_read_fragment_with_caching_enabled
diff --git a/actionpack/test/controller/filters_test.rb b/actionpack/test/controller/filters_test.rb
index a34878cee8..f9701585a9 100644
--- a/actionpack/test/controller/filters_test.rb
+++ b/actionpack/test/controller/filters_test.rb
@@ -55,7 +55,7 @@ class FilterTest < ActionController::TestCase
end
end
- protected
+ private
(1..3).each do |i|
define_method "try_#{i}" do
instance_variable_set :@try, i
@@ -296,7 +296,7 @@ class FilterTest < ActionController::TestCase
render inline: "ran action"
end
- protected
+ private
def find_user
@ran_filter ||= []
@ran_filter << "find_user"
@@ -428,7 +428,7 @@ class FilterTest < ActionController::TestCase
render plain: "bar"
end
- protected
+ private
def first
@first = true
end
@@ -506,20 +506,20 @@ class FilterTest < ActionController::TestCase
private
def filter_one
- @filters ||= []
- @filters << "filter_one"
+ @filters ||= []
+ @filters << "filter_one"
end
def action_two
- @filters << "action_two"
+ @filters << "action_two"
end
def non_yielding_action
- @filters << "it didn't yield"
+ @filters << "it didn't yield"
end
def action_three
- @filters << "action_three"
+ @filters << "action_three"
end
end
@@ -911,7 +911,7 @@ class ControllerWithFilterInstance < PostsController
end
class ControllerWithProcFilter < PostsController
- around_action(only: :no_raise) do |c,b|
+ around_action(only: :no_raise) do |c, b|
c.instance_variable_set(:"@before", true)
b.call
c.instance_variable_set(:"@after", true)
@@ -961,34 +961,34 @@ class YieldingAroundFiltersTest < ActionController::TestCase
def test_base
controller = PostsController
- assert_nothing_raised { test_process(controller,"no_raise") }
- assert_nothing_raised { test_process(controller,"raises_before") }
- assert_nothing_raised { test_process(controller,"raises_after") }
- assert_nothing_raised { test_process(controller,"no_action") }
+ assert_nothing_raised { test_process(controller, "no_raise") }
+ assert_nothing_raised { test_process(controller, "raises_before") }
+ assert_nothing_raised { test_process(controller, "raises_after") }
+ assert_nothing_raised { test_process(controller, "no_action") }
end
def test_with_symbol
controller = ControllerWithSymbolAsFilter
- assert_nothing_raised { test_process(controller,"no_raise") }
- assert_raise(Before) { test_process(controller,"raises_before") }
- assert_raise(After) { test_process(controller,"raises_after") }
- assert_nothing_raised { test_process(controller,"no_raise") }
+ assert_nothing_raised { test_process(controller, "no_raise") }
+ assert_raise(Before) { test_process(controller, "raises_before") }
+ assert_raise(After) { test_process(controller, "raises_after") }
+ assert_nothing_raised { test_process(controller, "no_raise") }
end
def test_with_class
controller = ControllerWithFilterClass
- assert_nothing_raised { test_process(controller,"no_raise") }
- assert_raise(After) { test_process(controller,"raises_after") }
+ assert_nothing_raised { test_process(controller, "no_raise") }
+ assert_raise(After) { test_process(controller, "raises_after") }
end
def test_with_instance
controller = ControllerWithFilterInstance
- assert_nothing_raised { test_process(controller,"no_raise") }
- assert_raise(After) { test_process(controller,"raises_after") }
+ assert_nothing_raised { test_process(controller, "no_raise") }
+ assert_raise(After) { test_process(controller, "raises_after") }
end
def test_with_proc
- test_process(ControllerWithProcFilter,"no_raise")
+ test_process(ControllerWithProcFilter, "no_raise")
assert @controller.instance_variable_get(:@before)
assert @controller.instance_variable_get(:@after)
end
@@ -997,25 +997,25 @@ class YieldingAroundFiltersTest < ActionController::TestCase
controller = ControllerWithNestedFilters
assert_nothing_raised do
begin
- test_process(controller,"raises_both")
+ test_process(controller, "raises_both")
rescue Before, After
end
end
assert_raise Before do
begin
- test_process(controller,"raises_both")
+ test_process(controller, "raises_both")
rescue After
end
end
end
def test_action_order_with_all_action_types
- test_process(ControllerWithAllTypesOfFilters,"no_raise")
+ test_process(ControllerWithAllTypesOfFilters, "no_raise")
assert_equal "before around (before yield) around_again (before yield) around_again (after yield) after around (after yield)", @controller.instance_variable_get(:@ran_filter).join(" ")
end
def test_action_order_with_skip_action_method
- test_process(ControllerWithTwoLessFilters,"no_raise")
+ test_process(ControllerWithTwoLessFilters, "no_raise")
assert_equal "before around (before yield) around (after yield)", @controller.instance_variable_get(:@ran_filter).join(" ")
end
@@ -1040,7 +1040,7 @@ class YieldingAroundFiltersTest < ActionController::TestCase
assert_equal 3, controller.instance_variable_get(:@try)
end
- protected
+ private
def test_process(controller, action = "show")
@controller = controller.is_a?(Class) ? controller.new : controller
process(action)
diff --git a/actionpack/test/controller/flash_hash_test.rb b/actionpack/test/controller/flash_hash_test.rb
index 32f0db71f5..45b598a594 100644
--- a/actionpack/test/controller/flash_hash_test.rb
+++ b/actionpack/test/controller/flash_hash_test.rb
@@ -63,10 +63,10 @@ module ActionDispatch
assert_equal({ "flashes" => { "foo" => "bar" }, "discard" => [] }, @hash.to_session_value)
@hash.discard("foo")
- assert_equal(nil, @hash.to_session_value)
+ assert_nil(@hash.to_session_value)
@hash.sweep
- assert_equal(nil, @hash.to_session_value)
+ assert_nil(@hash.to_session_value)
end
def test_from_session_value
@@ -75,7 +75,7 @@ module ActionDispatch
session = Marshal.load(Base64.decode64(rails_3_2_cookie))
hash = Flash::FlashHash.from_session_value(session["flash"])
assert_equal({ "greeting" => "Hello" }, hash.to_hash)
- assert_equal(nil, hash.to_session_value)
+ assert_nil(hash.to_session_value)
end
def test_from_session_value_on_json_serializer
@@ -84,7 +84,7 @@ module ActionDispatch
hash = Flash::FlashHash.from_session_value(session["flash"])
assert_equal({ "greeting" => "Hello" }, hash.to_hash)
- assert_equal(nil, hash.to_session_value)
+ assert_nil(hash.to_session_value)
assert_equal "Hello", hash[:greeting]
assert_equal "Hello", hash["greeting"]
end
@@ -102,8 +102,8 @@ module ActionDispatch
@hash["foo"] = "bar"
things = []
- @hash.each do |k,v|
- things << [k,v]
+ @hash.each do |k, v|
+ things << [k, v]
end
assert_equal([%w{ hello world }, %w{ foo bar }].sort, things.sort)
diff --git a/actionpack/test/controller/http_digest_authentication_test.rb b/actionpack/test/controller/http_digest_authentication_test.rb
index 343b7b643d..0b59e123d7 100644
--- a/actionpack/test/controller/http_digest_authentication_test.rb
+++ b/actionpack/test/controller/http_digest_authentication_test.rb
@@ -7,7 +7,7 @@ class HttpDigestAuthenticationTest < ActionController::TestCase
before_action :authenticate_with_request, only: :display
USERS = { "lifo" => "world", "pretty" => "please",
- "dhh" => ::Digest::MD5::hexdigest(["dhh","SuperSecret","secret"].join(":")) }
+ "dhh" => ::Digest::MD5::hexdigest(["dhh", "SuperSecret", "secret"].join(":")) }
def index
render plain: "Hello Secret"
@@ -180,7 +180,7 @@ class HttpDigestAuthenticationTest < ActionController::TestCase
test "authentication request with password stored as ha1 digest hash" do
@request.env["HTTP_AUTHORIZATION"] = encode_credentials(username: "dhh",
- password: ::Digest::MD5::hexdigest(["dhh","SuperSecret","secret"].join(":")),
+ password: ::Digest::MD5::hexdigest(["dhh", "SuperSecret", "secret"].join(":")),
password_is_ha1: true)
get :display
diff --git a/actionpack/test/controller/http_token_authentication_test.rb b/actionpack/test/controller/http_token_authentication_test.rb
index 3842136682..09d2793c9a 100644
--- a/actionpack/test/controller/http_token_authentication_test.rb
+++ b/actionpack/test/controller/http_token_authentication_test.rb
@@ -166,8 +166,7 @@ class HttpTokenAuthenticationTest < ActionController::TestCase
test "token_and_options returns nil with no value after the equal sign" do
actual = ActionController::HttpAuthentication::Token.token_and_options(malformed_request).first
- expected = nil
- assert_equal(expected, actual)
+ assert_nil actual
end
test "raw_params returns a tuple of two key value pair strings" do
diff --git a/actionpack/test/controller/integration_test.rb b/actionpack/test/controller/integration_test.rb
index 652c0f0dd2..4fae4071b1 100644
--- a/actionpack/test/controller/integration_test.rb
+++ b/actionpack/test/controller/integration_test.rb
@@ -145,7 +145,7 @@ class IntegrationTestTest < ActiveSupport::TestCase
name.to_s == "foo" ? "pass" : super
end
end
- @test.class.superclass.__send__(:include, mixin)
+ @test.class.superclass.include(mixin)
begin
assert_equal "pass", @test.foo
ensure
@@ -153,14 +153,6 @@ class IntegrationTestTest < ActiveSupport::TestCase
mixin.__send__(:remove_method, :method_missing)
end
end
-
- def test_assigns_is_undefined_and_not_point_to_the_gem
- e = assert_raises(NoMethodError) do
- @test.assigns(:foo)
- end
-
- assert_match(/undefined method/, e.message)
- end
end
# Tests that integration tests don't call Controller test methods for processing.
@@ -281,7 +273,7 @@ class IntegrationProcessTest < ActionDispatch::IntegrationTest
self.cookies["cookie_2"] = "oatmeal"
get "/cookie_monster"
assert_equal "cookie_1=; path=/\ncookie_3=chocolate; path=/", headers["Set-Cookie"]
- assert_equal({ "cookie_1"=>"", "cookie_2"=>"oatmeal", "cookie_3"=>"chocolate" }, cookies.to_hash)
+ assert_equal({ "cookie_1" => "", "cookie_2" => "oatmeal", "cookie_3" => "chocolate" }, cookies.to_hash)
end
end
@@ -291,14 +283,14 @@ class IntegrationProcessTest < ActionDispatch::IntegrationTest
assert_response :success
assert_equal "foo=bar; path=/", headers["Set-Cookie"]
- assert_equal({ "foo"=>"bar" }, cookies.to_hash)
+ assert_equal({ "foo" => "bar" }, cookies.to_hash)
get "/get_cookie"
assert_response :success
assert_equal "bar", body
- assert_equal nil, headers["Set-Cookie"]
- assert_equal({ "foo"=>"bar" }, cookies.to_hash)
+ assert_nil headers["Set-Cookie"]
+ assert_equal({ "foo" => "bar" }, cookies.to_hash)
end
end
@@ -310,14 +302,14 @@ class IntegrationProcessTest < ActionDispatch::IntegrationTest
assert_response :success
assert_equal "foo=bar; path=/", headers["Set-Cookie"]
- assert_equal({ "foo"=>"bar" }, cookies.to_hash)
+ assert_equal({ "foo" => "bar" }, cookies.to_hash)
get "/get_cookie"
assert_response :success
assert_equal "bar", body
- assert_equal nil, headers["Set-Cookie"]
- assert_equal({ "foo"=>"bar" }, cookies.to_hash)
+ assert_nil headers["Set-Cookie"]
+ assert_equal({ "foo" => "bar" }, cookies.to_hash)
end
end
@@ -364,6 +356,14 @@ class IntegrationProcessTest < ActionDispatch::IntegrationTest
end
end
+ test "creation of multiple integration sessions" do
+ integration_session # initialize first session
+ a = open_session
+ b = open_session
+
+ refute_same(a.integration_session, b.integration_session)
+ end
+
def test_get_with_query_string
with_test_route_set do
get "/get_with_params?foo=bar"
@@ -924,12 +924,16 @@ class IntegrationRequestsWithSessionSetup < ActionDispatch::IntegrationTest
def test_cookies_set_in_setup_are_persisted_through_the_session
get "/foo"
- assert_equal({ "user_name"=>"david" }, cookies.to_hash)
+ assert_equal({ "user_name" => "david" }, cookies.to_hash)
end
end
class IntegrationRequestEncodersTest < ActionDispatch::IntegrationTest
class FooController < ActionController::Base
+ def foos
+ render plain: "ok"
+ end
+
def foos_json
render json: params.permit(:foo)
end
@@ -958,13 +962,30 @@ class IntegrationRequestEncodersTest < ActionDispatch::IntegrationTest
def test_encoding_as_json
post_to_foos as: :json do
assert_response :success
- assert_match "foos_json.json", request.path
assert_equal "application/json", request.content_type
+ assert_equal "application/json", request.accepts.first.to_s
+ assert_equal :json, request.format.ref
assert_equal({ "foo" => "fighters" }, request.request_parameters)
assert_equal({ "foo" => "fighters" }, response.parsed_body)
end
end
+ def test_doesnt_mangle_request_path
+ with_routing do |routes|
+ routes.draw do
+ ActiveSupport::Deprecation.silence do
+ post ":action" => FooController
+ end
+ end
+
+ post "/foos"
+ assert_equal "/foos", request.path
+
+ post "/foos_json", as: :json
+ assert_equal "/foos_json", request.path
+ end
+ end
+
def test_encoding_as_without_mime_registration
assert_raise ArgumentError do
ActionDispatch::IntegrationTest.register_encoder :wibble
@@ -979,8 +1000,10 @@ class IntegrationRequestEncodersTest < ActionDispatch::IntegrationTest
post_to_foos as: :wibble do
assert_response :success
- assert_match "foos_wibble.wibble", request.path
+ assert_equal "/foos_wibble", request.path
assert_equal "text/wibble", request.content_type
+ assert_equal "text/wibble", request.accepts.first.to_s
+ assert_equal :wibble, request.format.ref
assert_equal Hash.new, request.request_parameters # Unregistered MIME Type can't be parsed.
assert_equal "ok", response.parsed_body
end
@@ -1078,8 +1101,8 @@ class IntegrationFileUploadTest < ActionDispatch::IntegrationTest
def test_fixture_file_upload
post "/test_file_upload",
params: {
- file: fixture_file_upload("/mona_lisa.jpg", "image/jpg")
+ file: fixture_file_upload("/ruby_on_rails.jpg", "image/jpg")
}
- assert_equal "159528", @response.body
+ assert_equal "45142", @response.body
end
end
diff --git a/actionpack/test/controller/mime/accept_format_test.rb b/actionpack/test/controller/mime/accept_format_test.rb
index a2834c9f39..a22fa39051 100644
--- a/actionpack/test/controller/mime/accept_format_test.rb
+++ b/actionpack/test/controller/mime/accept_format_test.rb
@@ -40,7 +40,7 @@ class PostController < AbstractPostController
respond_to(:html, :iphone, :js)
end
-protected
+private
def with_iphone
request.format = "iphone" if request.env["HTTP_ACCEPT"] == "text/iphone"
diff --git a/actionpack/test/controller/mime/respond_to_test.rb b/actionpack/test/controller/mime/respond_to_test.rb
index c5f8165d04..818dc119eb 100644
--- a/actionpack/test/controller/mime/respond_to_test.rb
+++ b/actionpack/test/controller/mime/respond_to_test.rb
@@ -273,7 +273,7 @@ class RespondToController < ActionController::Base
end
end
- protected
+ private
def set_layout
case action_name
when "all_types_with_layout", "iphone_with_html_response_type"
diff --git a/actionpack/test/controller/new_base/bare_metal_test.rb b/actionpack/test/controller/new_base/bare_metal_test.rb
index 9956f0b107..054757fab3 100644
--- a/actionpack/test/controller/new_base/bare_metal_test.rb
+++ b/actionpack/test/controller/new_base/bare_metal_test.rb
@@ -52,7 +52,7 @@ module BareMetalTest
controller.set_request!(ActionDispatch::Request.empty)
controller.set_response!(BareController.make_response!(controller.request))
controller.index
- assert_equal nil, controller.response_body
+ assert_nil controller.response_body
end
end
diff --git a/actionpack/test/controller/new_base/base_test.rb b/actionpack/test/controller/new_base/base_test.rb
index 3765d406fc..b891df4c0f 100644
--- a/actionpack/test/controller/new_base/base_test.rb
+++ b/actionpack/test/controller/new_base/base_test.rb
@@ -25,7 +25,7 @@ module Dispatching
render body: "actions: #{action_methods.to_a.sort.join(', ')}"
end
- protected
+ private
def authenticate
end
end
diff --git a/actionpack/test/controller/new_base/render_context_test.rb b/actionpack/test/controller/new_base/render_context_test.rb
index b64468a94e..25b73ac78c 100644
--- a/actionpack/test/controller/new_base/render_context_test.rb
+++ b/actionpack/test/controller/new_base/render_context_test.rb
@@ -26,16 +26,14 @@ module RenderContext
render action: "hello_world", layout: "basic"
end
- protected
-
- # 3) Set view_context to self
- def view_context
- self
- end
+ protected def __controller_method__
+ "controller context!"
+ end
- def __controller_method__
- "controller context!"
- end
+ # 3) Set view_context to self
+ private def view_context
+ self
+ end
end
class RenderContextTest < Rack::TestCase
diff --git a/actionpack/test/controller/new_base/render_streaming_test.rb b/actionpack/test/controller/new_base/render_streaming_test.rb
index 5cd8f82323..1177b8b03e 100644
--- a/actionpack/test/controller/new_base/render_streaming_test.rb
+++ b/actionpack/test/controller/new_base/render_streaming_test.rb
@@ -101,12 +101,12 @@ module RenderStreaming
assert_body "Hello world, I'm here!"
assert_status 200
assert_equal "22", headers["Content-Length"]
- assert_equal nil, headers["Transfer-Encoding"]
+ assert_nil headers["Transfer-Encoding"]
end
- def assert_streaming!(cache="no-cache")
+ def assert_streaming!(cache = "no-cache")
assert_status 200
- assert_equal nil, headers["Content-Length"]
+ assert_nil headers["Content-Length"]
assert_equal "chunked", headers["Transfer-Encoding"]
assert_equal cache, headers["Cache-Control"]
end
diff --git a/actionpack/test/controller/parameter_encoding_test.rb b/actionpack/test/controller/parameter_encoding_test.rb
index 7840b4f5c4..234d0bddd1 100644
--- a/actionpack/test/controller/parameter_encoding_test.rb
+++ b/actionpack/test/controller/parameter_encoding_test.rb
@@ -1,9 +1,8 @@
require "abstract_unit"
class ParameterEncodingController < ActionController::Base
- parameter_encoding :test_bar, :bar, Encoding::ASCII_8BIT
- parameter_encoding :test_baz, :baz, Encoding::ISO_8859_1
- parameter_encoding :test_baz_to_ascii, :baz, Encoding::ASCII_8BIT
+ skip_parameter_encoding :test_bar
+ skip_parameter_encoding :test_all_values_encoding
def test_foo
render body: params[:foo].encoding
@@ -13,16 +12,8 @@ class ParameterEncodingController < ActionController::Base
render body: params[:bar].encoding
end
- def test_baz
- render body: params[:baz].encoding
- end
-
- def test_no_change_to_baz
- render body: params[:baz].encoding
- end
-
- def test_baz_to_ascii
- render body: params[:baz].encoding
+ def test_all_values_encoding
+ render body: ::JSON.dump(params.values.map(&:encoding).map(&:name))
end
end
@@ -36,32 +27,18 @@ class ParameterEncodingTest < ActionController::TestCase
assert_equal "UTF-8", @response.body
end
- test "properly transcodes ASCII_8BIT parameters into declared encodings" do
+ test "properly encodes ASCII_8BIT parameters into binary" do
post :test_bar, params: { "foo" => "foo", "bar" => "bar", "baz" => "baz" }
assert_response :success
assert_equal "ASCII-8BIT", @response.body
end
- test "properly transcodes ISO_8859_1 parameters into declared encodings" do
- post :test_baz, params: { "foo" => "foo", "bar" => "bar", "baz" => "baz" }
-
- assert_response :success
- assert_equal "ISO-8859-1", @response.body
- end
-
- test "does not transcode parameters when not specified" do
- post :test_no_change_to_baz, params: { "foo" => "foo", "bar" => "bar", "baz" => "baz" }
+ test "properly encodes all ASCII_8BIT parameters into binary" do
+ post :test_all_values_encoding, params: { "foo" => "foo", "bar" => "bar", "baz" => "baz" }
assert_response :success
- assert_equal "UTF-8", @response.body
- end
-
- test "respects different encoding declarations for a param per action" do
- post :test_baz_to_ascii, params: { "foo" => "foo", "bar" => "bar", "baz" => "baz" }
-
- assert_response :success
- assert_equal "ASCII-8BIT", @response.body
+ assert_equal ["ASCII-8BIT"], JSON.parse(@response.body).uniq
end
test "does not raise an error when passed a param declared as ASCII-8BIT that contains invalid bytes" do
diff --git a/actionpack/test/controller/parameters/mutators_test.rb b/actionpack/test/controller/parameters/mutators_test.rb
index e060e5180f..e61bbdbe13 100644
--- a/actionpack/test/controller/parameters/mutators_test.rb
+++ b/actionpack/test/controller/parameters/mutators_test.rb
@@ -45,11 +45,11 @@ class ParametersMutatorsTest < ActiveSupport::TestCase
test "keep_if retains permitted status" do
@params.permit!
- assert @params.keep_if { |k,v| k == "person" }.permitted?
+ assert @params.keep_if { |k, v| k == "person" }.permitted?
end
test "keep_if retains unpermitted status" do
- assert_not @params.keep_if { |k,v| k == "person" }.permitted?
+ assert_not @params.keep_if { |k, v| k == "person" }.permitted?
end
test "reject! retains permitted status" do
diff --git a/actionpack/test/controller/parameters/parameters_permit_test.rb b/actionpack/test/controller/parameters/parameters_permit_test.rb
index 728d8e1279..b62a3d6d7b 100644
--- a/actionpack/test/controller/parameters/parameters_permit_test.rb
+++ b/actionpack/test/controller/parameters/parameters_permit_test.rb
@@ -28,7 +28,7 @@ class ParametersPermitTest < ActiveSupport::TestCase
end
def walk_permitted(params)
- params.each do |k,v|
+ params.each do |k, v|
case v
when ActionController::Parameters
walk_permitted v
@@ -39,13 +39,13 @@ class ParametersPermitTest < ActiveSupport::TestCase
end
test "iteration should not impact permit" do
- hash = { "foo"=>{ "bar"=>{ "0"=>{ "baz"=>"hello", "zot"=>"1" } } } }
+ hash = { "foo" => { "bar" => { "0" => { "baz" => "hello", "zot" => "1" } } } }
params = ActionController::Parameters.new(hash)
walk_permitted params
sanitized = params[:foo].permit(bar: [:baz])
- assert_equal({ "0"=>{ "baz"=>"hello" } }, sanitized[:bar].to_unsafe_h)
+ assert_equal({ "0" => { "baz" => "hello" } }, sanitized[:bar].to_unsafe_h)
end
test "if nothing is permitted, the hash becomes empty" do
@@ -141,7 +141,7 @@ class ParametersPermitTest < ActiveSupport::TestCase
permitted = params.permit(:a, c: [], b: [])
assert_equal 1, permitted[:a]
assert_equal [1, 2, 3], permitted[:b]
- assert_equal nil, permitted[:c]
+ assert_nil permitted[:c]
end
test "key to empty array: arrays of permitted scalars pass" do
@@ -168,6 +168,44 @@ class ParametersPermitTest < ActiveSupport::TestCase
end
end
+ test "key to empty hash: arbitrary hashes are permitted" do
+ params = ActionController::Parameters.new(
+ username: "fxn",
+ preferences: {
+ scheme: "Marazul",
+ font: {
+ name: "Source Code Pro",
+ size: 12
+ },
+ tabstops: [4, 8, 12, 16],
+ suspicious: [true, Object.new, false, /yo!/],
+ dubious: [{ a: :a, b: /wtf!/ }, { c: :c }],
+ injected: Object.new
+ },
+ hacked: 1 # not a hash
+ )
+
+ permitted = params.permit(:username, preferences: {}, hacked: {})
+
+ assert permitted.permitted?
+ assert permitted[:preferences].permitted?
+ assert permitted[:preferences][:font].permitted?
+ assert permitted[:preferences][:dubious].all?(&:permitted?)
+
+ assert_equal "fxn", permitted[:username]
+ assert_equal "Marazul", permitted[:preferences][:scheme]
+ assert_equal "Source Code Pro", permitted[:preferences][:font][:name]
+ assert_equal 12, permitted[:preferences][:font][:size]
+ assert_equal [4, 8, 12, 16], permitted[:preferences][:tabstops]
+ assert_equal [true, false], permitted[:preferences][:suspicious]
+ assert_equal :a, permitted[:preferences][:dubious][0][:a]
+ assert_equal :c, permitted[:preferences][:dubious][1][:c]
+
+ assert_filtered_out permitted[:preferences][:dubious][0], :b
+ assert_filtered_out permitted[:preferences], :injected
+ assert_filtered_out permitted, :hacked
+ end
+
test "fetch raises ParameterMissing exception" do
e = assert_raises(ActionController::ParameterMissing) do
@params.fetch :foo
@@ -178,7 +216,7 @@ class ParametersPermitTest < ActiveSupport::TestCase
test "fetch with a default value of a hash does not mutate the object" do
params = ActionController::Parameters.new({})
params.fetch :foo, {}
- assert_equal nil, params[:foo]
+ assert_nil params[:foo]
end
test "hashes in array values get wrapped" do
@@ -216,8 +254,8 @@ class ParametersPermitTest < ActiveSupport::TestCase
end
test "fetch doesnt raise ParameterMissing exception if there is a default that is nil" do
- assert_equal nil, @params.fetch(:foo, nil)
- assert_equal nil, @params.fetch(:foo) { nil }
+ assert_nil @params.fetch(:foo, nil)
+ assert_nil @params.fetch(:foo) { nil }
end
test "KeyError in fetch block should not be covered up" do
@@ -244,6 +282,23 @@ class ParametersPermitTest < ActiveSupport::TestCase
assert merged_params[:id]
end
+ test "not permitted is sticky beyond merge!" do
+ assert_not @params.merge!(a: "b").permitted?
+ end
+
+ test "permitted is sticky beyond merge!" do
+ @params.permit!
+ assert @params.merge!(a: "b").permitted?
+ end
+
+ test "merge! with parameters" do
+ other_params = ActionController::Parameters.new(id: "1234").permit!
+ @params.merge!(other_params)
+
+ assert_equal "1234", @params[:id]
+ assert_equal "32", @params[:person][:age]
+ end
+
test "modifying the parameters" do
@params[:person][:hometown] = "Chicago"
@params[:person][:family] = { brother: "Jonas" }
@@ -322,8 +377,8 @@ class ParametersPermitTest < ActiveSupport::TestCase
end
test "to_unsafe_h returns unfiltered params even after accessing few keys" do
- params = ActionController::Parameters.new("f"=>{ "language_facet"=>["Tibetan"] })
- expected = { "f"=>{ "language_facet"=>["Tibetan"] } }
+ params = ActionController::Parameters.new("f" => { "language_facet" => ["Tibetan"] })
+ expected = { "f" => { "language_facet" => ["Tibetan"] } }
assert params["f"].is_a? ActionController::Parameters
assert_equal expected, params.to_unsafe_h
diff --git a/actionpack/test/controller/parameters/serialization_test.rb b/actionpack/test/controller/parameters/serialization_test.rb
index 4fb1564c68..6fba2fde91 100644
--- a/actionpack/test/controller/parameters/serialization_test.rb
+++ b/actionpack/test/controller/parameters/serialization_test.rb
@@ -14,12 +14,10 @@ class ParametersSerializationTest < ActiveSupport::TestCase
test "yaml serialization" do
params = ActionController::Parameters.new(key: :value)
- assert_equal <<-end_of_yaml.strip_heredoc, YAML.dump(params)
- --- !ruby/object:ActionController::Parameters
- parameters: !ruby/hash:ActiveSupport::HashWithIndifferentAccess
- key: :value
- permitted: false
- end_of_yaml
+ yaml_dump = YAML.dump(params)
+ assert_match("--- !ruby/object:ActionController::Parameters", yaml_dump)
+ assert_match(/parameters: !ruby\/hash:ActiveSupport::HashWithIndifferentAccess\n\s+key: :value/, yaml_dump)
+ assert_match("permitted: false", yaml_dump)
end
test "yaml deserialization" do
diff --git a/actionpack/test/controller/params_wrapper_test.rb b/actionpack/test/controller/params_wrapper_test.rb
index 1549405fe7..faa57c4559 100644
--- a/actionpack/test/controller/params_wrapper_test.rb
+++ b/actionpack/test/controller/params_wrapper_test.rb
@@ -50,7 +50,7 @@ class ParamsWrapperTest < ActionController::TestCase
with_default_wrapper_options do
@request.env["CONTENT_TYPE"] = "application/json"
post :parse, params: { "username" => "sikachu" }
- assert_equal @request.filtered_parameters, "controller" => "params_wrapper_test/users", "action" => "parse", "username" => "sikachu", "user" => { "username" => "sikachu" }
+ assert_equal({ "controller" => "params_wrapper_test/users", "action" => "parse", "username" => "sikachu", "user" => { "username" => "sikachu" } }, @request.filtered_parameters)
end
end
@@ -212,6 +212,16 @@ class ParamsWrapperTest < ActionController::TestCase
)
end
end
+
+ def test_handles_empty_content_type
+ with_default_wrapper_options do
+ @request.env["CONTENT_TYPE"] = nil
+ _controller_class.dispatch(:parse, @request, @response)
+
+ assert_equal 200, @response.status
+ assert_equal "", @response.body
+ end
+ end
end
class NamespacedParamsWrapperTest < ActionController::TestCase
diff --git a/actionpack/test/controller/redirect_test.rb b/actionpack/test/controller/redirect_test.rb
index 0e61b92bcf..e4e968dfdb 100644
--- a/actionpack/test/controller/redirect_test.rb
+++ b/actionpack/test/controller/redirect_test.rb
@@ -123,7 +123,7 @@ class RedirectController < ActionController::Base
def rescue_errors(e) raise e end
- protected
+ private
def dashbord_url(id, message)
url_for action: "dashboard", params: { "id" => id, "message" => message }
end
diff --git a/actionpack/test/controller/render_json_test.rb b/actionpack/test/controller/render_json_test.rb
index 213829bd9e..79552ec8f1 100644
--- a/actionpack/test/controller/render_json_test.rb
+++ b/actionpack/test/controller/render_json_test.rb
@@ -5,7 +5,7 @@ require "pathname"
class RenderJsonTest < ActionController::TestCase
class JsonRenderable
- def as_json(options={})
+ def as_json(options = {})
hash = { a: :b, c: :d, e: :f }
hash.except!(*options[:except]) if options[:except]
hash
diff --git a/actionpack/test/controller/render_test.rb b/actionpack/test/controller/render_test.rb
index 76139d59e2..0c0f18f200 100644
--- a/actionpack/test/controller/render_test.rb
+++ b/actionpack/test/controller/render_test.rb
@@ -368,7 +368,7 @@ class ExpiresInRenderTest < ActionController::TestCase
end
def test_date_header_when_expires_in
- time = Time.mktime(2011,10,30)
+ time = Time.mktime(2011, 10, 30)
Time.stub :now, time do
get :conditional_hello_with_expires_in
assert_equal Time.now.httpdate, @response.headers["Date"]
diff --git a/actionpack/test/controller/renderer_test.rb b/actionpack/test/controller/renderer_test.rb
index d6f09f2d90..866600b935 100644
--- a/actionpack/test/controller/renderer_test.rb
+++ b/actionpack/test/controller/renderer_test.rb
@@ -60,6 +60,14 @@ class RendererTest < ActiveSupport::TestCase
assert_equal "true", content
end
+ test "rendering with custom env using a key that is not in RACK_KEY_TRANSLATION" do
+ value = "warden is here"
+ renderer = ApplicationController.renderer.new warden: value
+ content = renderer.render inline: "<%= request.env['warden'] %>"
+
+ assert_equal value, content
+ end
+
test "rendering with defaults" do
renderer = ApplicationController.renderer.new https: true
content = renderer.render inline: "<%= request.ssl? %>"
diff --git a/actionpack/test/controller/renderers_test.rb b/actionpack/test/controller/renderers_test.rb
index 122f5be549..ccc700d79c 100644
--- a/actionpack/test/controller/renderers_test.rb
+++ b/actionpack/test/controller/renderers_test.rb
@@ -10,7 +10,7 @@ class RenderersTest < ActionController::TestCase
end
end
class JsonRenderable
- def as_json(options={})
+ def as_json(options = {})
hash = { a: :b, c: :d, e: :f }
hash.except!(*options[:except]) if options[:except]
hash
diff --git a/actionpack/test/controller/request/test_request_test.rb b/actionpack/test/controller/request/test_request_test.rb
index fa049fbc9f..b67ae72c0a 100644
--- a/actionpack/test/controller/request/test_request_test.rb
+++ b/actionpack/test/controller/request/test_request_test.rb
@@ -8,7 +8,17 @@ class ActionController::TestRequestTest < ActionController::TestCase
def test_mutating_session_options_does_not_affect_default_options
@request.session_options[:myparam] = 123
- assert_equal nil, ActionController::TestSession::DEFAULT_OPTIONS[:myparam]
+ assert_nil ActionController::TestSession::DEFAULT_OPTIONS[:myparam]
+ end
+
+ def test_content_length_has_bytes_count_value
+ non_ascii_parameters = { data: { content: "Latin + Кириллица" } }
+ @request.set_header "REQUEST_METHOD", "POST"
+ @request.set_header "CONTENT_TYPE", "application/json"
+ @request.assign_parameters(@routes, "test", "create", non_ascii_parameters,
+ "/test", [:data, :controller, :action])
+ assert_equal(StringIO.new(non_ascii_parameters.to_json).length.to_s,
+ @request.get_header("CONTENT_LENGTH"))
end
ActionDispatch::Session::AbstractStore::DEFAULT_OPTIONS.each_key do |option|
diff --git a/actionpack/test/controller/request_forgery_protection_test.rb b/actionpack/test/controller/request_forgery_protection_test.rb
index 90d5ab3c67..d645ddfdbe 100644
--- a/actionpack/test/controller/request_forgery_protection_test.rb
+++ b/actionpack/test/controller/request_forgery_protection_test.rb
@@ -92,7 +92,7 @@ class PrependProtectForgeryBaseController < ActionController::Base
render inline: "OK"
end
- protected
+ private
def add_called_callback(name)
@called_callbacks ||= []
diff --git a/actionpack/test/controller/rescue_test.rb b/actionpack/test/controller/rescue_test.rb
index a98e6479b7..9ae22c4554 100644
--- a/actionpack/test/controller/rescue_test.rb
+++ b/actionpack/test/controller/rescue_test.rb
@@ -149,7 +149,7 @@ class RescueController < ActionController::Base
raise RangeError
end
- protected
+ private
def deny_access
head :forbidden
end
@@ -327,7 +327,7 @@ class RescueTest < ActionDispatch::IntegrationTest
raise "b00m"
end
- protected
+ private
def show_errors(exception)
render plain: exception.message
end
diff --git a/actionpack/test/controller/resources_test.rb b/actionpack/test/controller/resources_test.rb
index b572e7e8d5..fad34dacce 100644
--- a/actionpack/test/controller/resources_test.rb
+++ b/actionpack/test/controller/resources_test.rb
@@ -27,7 +27,7 @@ class ResourcesTest < ActionController::TestCase
def test_override_paths_for_member_and_collection_methods
collection_methods = { rss: :get, reorder: :post, csv: :post }
- member_methods = { rss: :get, atom: :get, upload: :post, fix: :post }
+ member_methods = { rss: :get, atom: :get, upload: :post, fix: :post }
path_names = { new: "nuevo", rss: "canal", fix: "corrigir" }
with_restful_routing :messages,
@@ -792,7 +792,7 @@ class ResourcesTest < ActionController::TestCase
end
assert_simply_restful_for :product_reviews, controller: "messages", as: "reviews", name_prefix: "product_", path_prefix: "products/1/", options: { product_id: "1" }
- assert_simply_restful_for :tutor_reviews,controller: "comments", as: "reviews", name_prefix: "tutor_", path_prefix: "tutors/1/", options: { tutor_id: "1" }
+ assert_simply_restful_for :tutor_reviews, controller: "comments", as: "reviews", name_prefix: "tutor_", path_prefix: "tutors/1/", options: { tutor_id: "1" }
end
end
@@ -1089,7 +1089,7 @@ class ResourcesTest < ActionController::TestCase
end
end
- protected
+ private
def with_restful_routing(*args)
options = args.extract_options!
collection_methods = options.delete(:collection)
@@ -1306,7 +1306,7 @@ class ResourcesTest < ActionController::TestCase
end
def assert_named_route(expected, route, options)
- actual = @controller.send(route, options) rescue $!.class.name
+ actual = @controller.send(route, options) rescue $!.class.name
assert_equal expected, actual, "Error on route: #{route}(#{options.inspect})"
end
diff --git a/actionpack/test/controller/routing_test.rb b/actionpack/test/controller/routing_test.rb
index 7be2ad2b28..56b39510bb 100644
--- a/actionpack/test/controller/routing_test.rb
+++ b/actionpack/test/controller/routing_test.rb
@@ -91,7 +91,7 @@ class LegacyRouteSetTests < ActiveSupport::TestCase
end
hash = ActiveSupport::JSON.decode get(URI("http://example.org/journey/faithfully-omg"))
- assert_equal({ "artist"=>"journey", "song"=>"faithfully" }, hash)
+ assert_equal({ "artist" => "journey", "song" => "faithfully" }, hash)
end
def test_id_with_dash
@@ -103,7 +103,7 @@ class LegacyRouteSetTests < ActiveSupport::TestCase
end
hash = ActiveSupport::JSON.decode get(URI("http://example.org/journey/faithfully-omg"))
- assert_equal({ "id"=>"faithfully-omg" }, hash)
+ assert_equal({ "id" => "faithfully-omg" }, hash)
end
def test_dash_with_custom_regexp
@@ -115,7 +115,7 @@ class LegacyRouteSetTests < ActiveSupport::TestCase
end
hash = ActiveSupport::JSON.decode get(URI("http://example.org/journey/123-omg"))
- assert_equal({ "artist"=>"journey", "song"=>"123" }, hash)
+ assert_equal({ "artist" => "journey", "song" => "123" }, hash)
assert_equal "Not Found", get(URI("http://example.org/journey/faithfully-omg"))
end
@@ -128,7 +128,7 @@ class LegacyRouteSetTests < ActiveSupport::TestCase
end
hash = ActiveSupport::JSON.decode get(URI("http://example.org/journey/omg-faithfully"))
- assert_equal({ "artist"=>"journey", "song"=>"faithfully" }, hash)
+ assert_equal({ "artist" => "journey", "song" => "faithfully" }, hash)
end
def test_pre_dash_with_custom_regexp
@@ -140,7 +140,7 @@ class LegacyRouteSetTests < ActiveSupport::TestCase
end
hash = ActiveSupport::JSON.decode get(URI("http://example.org/journey/omg-123"))
- assert_equal({ "artist"=>"journey", "song"=>"123" }, hash)
+ assert_equal({ "artist" => "journey", "song" => "123" }, hash)
assert_equal "Not Found", get(URI("http://example.org/journey/omg-faithfully"))
end
@@ -1931,7 +1931,7 @@ class RackMountIntegrationTests < ActiveSupport::TestCase
get "clients" => "projects#index"
get "ignorecase/geocode/:postalcode" => "geocode#show", :postalcode => /hx\d\d-\d[a-z]{2}/i
- get "extended/geocode/:postalcode" => "geocode#show",:constraints => {
+ get "extended/geocode/:postalcode" => "geocode#show", :constraints => {
postalcode: /# Postcode format
\d{5} #Prefix
(-\d{4})? #Suffix
diff --git a/actionpack/test/controller/send_file_test.rb b/actionpack/test/controller/send_file_test.rb
index 8dc565ac8d..9e6b975fe2 100644
--- a/actionpack/test/controller/send_file_test.rb
+++ b/actionpack/test/controller/send_file_test.rb
@@ -180,7 +180,7 @@ class SendFileTest < ActionController::TestCase
"file.zip" => "application/zip",
"file.unk" => "application/octet-stream",
"zip" => "application/octet-stream"
- }.each do |filename,expected_type|
+ }.each do |filename, expected_type|
get __method__, params: { filename: filename }
assert_equal expected_type, response.content_type
end
@@ -241,10 +241,17 @@ class SendFileTest < ActionController::TestCase
assert_equal "text/calendar; charset=utf-8", response.headers["Content-Type"]
end
+ def test_send_file_charset_with_type_options_key_without_charset
+ @controller = SendFileWithActionControllerLive.new
+ @controller.options = { type: "image/png" }
+ response = process("file")
+ assert_equal "image/png", response.headers["Content-Type"]
+ end
+
def test_send_file_charset_with_content_type_options_key
@controller = SendFileWithActionControllerLive.new
@controller.options = { content_type: "text/calendar" }
response = process("file")
- assert_equal "text/calendar; charset=utf-8", response.headers["Content-Type"]
+ assert_equal "text/calendar", response.headers["Content-Type"]
end
end
diff --git a/actionpack/test/controller/test_case_test.rb b/actionpack/test/controller/test_case_test.rb
index 82f42e306f..874f9c3c42 100644
--- a/actionpack/test/controller/test_case_test.rb
+++ b/actionpack/test/controller/test_case_test.rb
@@ -100,11 +100,11 @@ HTML
end
def test_xml_output
- response.content_type = "application/xml"
+ response.content_type = params[:response_as]
render plain: <<XML
<?xml version="1.0" encoding="UTF-8"?>
<root>
- <area>area is an empty tag in HTML, raising an error if not in xml mode</area>
+ <area><p>area is an empty tag in HTML, so it won't contain this content</p></area>
</root>
XML
end
@@ -231,7 +231,7 @@ XML
def test_document_body_and_params_with_post
post :test_params, params: { id: 1 }
- assert_equal({ "id"=>"1", "controller"=>"test_case_test/test", "action"=>"test_params" }, ::JSON.parse(@response.body))
+ assert_equal({ "id" => "1", "controller" => "test_case_test/test", "action" => "test_params" }, ::JSON.parse(@response.body))
end
def test_document_body_with_post
@@ -374,18 +374,18 @@ XML
assert_equal "OK", @response.body
end
- def test_should_not_impose_childless_html_tags_in_xml
- process :test_xml_output
+ def test_should_impose_childless_html_tags_in_html
+ process :test_xml_output, params: { response_as: "text/html" }
- begin
- $stderr = StringIO.new
- assert_select "area" #This will cause a warning if content is processed as HTML
- $stderr.rewind && err = $stderr.read
- ensure
- $stderr = STDERR
- end
+ # <area> auto-closes, so the <p> becomes a sibling
+ assert_select "root > area + p"
+ end
+
+ def test_should_not_impose_childless_html_tags_in_xml
+ process :test_xml_output, params: { response_as: "application/xml" }
- assert err.empty?
+ # <area> is not special, so the <p> is its child
+ assert_select "root > area > p"
end
def test_assert_generates
@@ -651,7 +651,7 @@ XML
def test_xhr_with_params
get :test_params, params: { id: 1 }, xhr: true
- assert_equal({ "id"=>"1", "controller"=>"test_case_test/test", "action"=>"test_params" }, ::JSON.parse(@response.body))
+ assert_equal({ "id" => "1", "controller" => "test_case_test/test", "action" => "test_params" }, ::JSON.parse(@response.body))
end
def test_xhr_with_session
@@ -753,10 +753,10 @@ XML
def test_multiple_mixed_method_process_should_scrub_rack_input
post :test_params, params: { id: 1, foo: "an foo" }
- assert_equal({ "id"=>"1", "foo" => "an foo", "controller"=>"test_case_test/test", "action"=>"test_params" }, ::JSON.parse(@response.body))
+ assert_equal({ "id" => "1", "foo" => "an foo", "controller" => "test_case_test/test", "action" => "test_params" }, ::JSON.parse(@response.body))
get :test_params, params: { bar: "an bar" }
- assert_equal({ "bar"=>"an bar", "controller"=>"test_case_test/test", "action"=>"test_params" }, ::JSON.parse(@response.body))
+ assert_equal({ "bar" => "an bar", "controller" => "test_case_test/test", "action" => "test_params" }, ::JSON.parse(@response.body))
end
%w(controller response request).each do |variable|
@@ -781,7 +781,7 @@ XML
READ_PLAIN = "r:binary"
def test_test_uploaded_file
- filename = "mona_lisa.jpg"
+ filename = "ruby_on_rails.jpg"
path = "#{FILES_DIR}/#{filename}"
content_type = "image/png"
expected = File.read(path)
@@ -801,13 +801,13 @@ XML
def test_fixture_path_is_accessed_from_self_instead_of_active_support_test_case
TestCaseTest.stub :fixture_path, FILES_DIR do
- uploaded_file = fixture_file_upload("/mona_lisa.jpg", "image/png")
- assert_equal File.open("#{FILES_DIR}/mona_lisa.jpg", READ_PLAIN).read, uploaded_file.read
+ uploaded_file = fixture_file_upload("/ruby_on_rails.jpg", "image/png")
+ assert_equal File.open("#{FILES_DIR}/ruby_on_rails.jpg", READ_PLAIN).read, uploaded_file.read
end
end
def test_test_uploaded_file_with_binary
- filename = "mona_lisa.jpg"
+ filename = "ruby_on_rails.jpg"
path = "#{FILES_DIR}/#{filename}"
content_type = "image/png"
@@ -819,7 +819,7 @@ XML
end
def test_fixture_file_upload_with_binary
- filename = "mona_lisa.jpg"
+ filename = "ruby_on_rails.jpg"
path = "#{FILES_DIR}/#{filename}"
content_type = "image/jpg"
@@ -831,44 +831,44 @@ XML
end
def test_fixture_file_upload_should_be_able_access_to_tempfile
- file = fixture_file_upload(FILES_DIR + "/mona_lisa.jpg", "image/jpg")
+ file = fixture_file_upload(FILES_DIR + "/ruby_on_rails.jpg", "image/jpg")
assert file.respond_to?(:tempfile), "expected tempfile should respond on fixture file object, got nothing"
end
def test_fixture_file_upload
post :test_file_upload,
params: {
- file: fixture_file_upload(FILES_DIR + "/mona_lisa.jpg", "image/jpg")
+ file: fixture_file_upload(FILES_DIR + "/ruby_on_rails.jpg", "image/jpg")
}
- assert_equal "159528", @response.body
+ assert_equal "45142", @response.body
end
def test_fixture_file_upload_relative_to_fixture_path
TestCaseTest.stub :fixture_path, FILES_DIR do
- uploaded_file = fixture_file_upload("mona_lisa.jpg", "image/jpg")
- assert_equal File.open("#{FILES_DIR}/mona_lisa.jpg", READ_PLAIN).read, uploaded_file.read
+ uploaded_file = fixture_file_upload("ruby_on_rails.jpg", "image/jpg")
+ assert_equal File.open("#{FILES_DIR}/ruby_on_rails.jpg", READ_PLAIN).read, uploaded_file.read
end
end
def test_fixture_file_upload_ignores_fixture_path_given_full_path
TestCaseTest.stub :fixture_path, File.dirname(__FILE__) do
- uploaded_file = fixture_file_upload("#{FILES_DIR}/mona_lisa.jpg", "image/jpg")
- assert_equal File.open("#{FILES_DIR}/mona_lisa.jpg", READ_PLAIN).read, uploaded_file.read
+ uploaded_file = fixture_file_upload("#{FILES_DIR}/ruby_on_rails.jpg", "image/jpg")
+ assert_equal File.open("#{FILES_DIR}/ruby_on_rails.jpg", READ_PLAIN).read, uploaded_file.read
end
end
def test_fixture_file_upload_ignores_nil_fixture_path
- uploaded_file = fixture_file_upload("#{FILES_DIR}/mona_lisa.jpg", "image/jpg")
- assert_equal File.open("#{FILES_DIR}/mona_lisa.jpg", READ_PLAIN).read, uploaded_file.read
+ uploaded_file = fixture_file_upload("#{FILES_DIR}/ruby_on_rails.jpg", "image/jpg")
+ assert_equal File.open("#{FILES_DIR}/ruby_on_rails.jpg", READ_PLAIN).read, uploaded_file.read
end
def test_action_dispatch_uploaded_file_upload
- filename = "mona_lisa.jpg"
+ filename = "ruby_on_rails.jpg"
path = "#{FILES_DIR}/#{filename}"
post :test_file_upload, params: {
file: Rack::Test::UploadedFile.new(path, "image/jpg", true)
}
- assert_equal "159528", @response.body
+ assert_equal "45142", @response.body
end
def test_test_uploaded_file_exception_when_file_doesnt_exist
diff --git a/actionpack/test/controller/url_for_integration_test.rb b/actionpack/test/controller/url_for_integration_test.rb
index c7ea6c5ef6..f640e77b99 100644
--- a/actionpack/test/controller/url_for_integration_test.rb
+++ b/actionpack/test/controller/url_for_integration_test.rb
@@ -43,7 +43,7 @@ module ActionPack
get "clients" => "projects#index"
get "ignorecase/geocode/:postalcode" => "geocode#show", :postalcode => /hx\d\d-\d[a-z]{2}/i
- get "extended/geocode/:postalcode" => "geocode#show",:constraints => {
+ get "extended/geocode/:postalcode" => "geocode#show", :constraints => {
postalcode: /# Postcode format
\d{5} #Prefix
(-\d{4})? #Suffix
@@ -73,99 +73,99 @@ module ActionPack
end
[
- ["/admin/users",[ { use_route: "admin_users" }]],
- ["/admin/users",[ { controller: "admin/users" }]],
- ["/admin/users",[ { controller: "admin/users", action: "index" }]],
- ["/admin/users",[ { action: "index" }, { controller: "admin/users", action: "index" }, "/admin/users"]],
- ["/admin/users",[ { controller: "users", action: "index" }, { controller: "admin/accounts", action: "show", id: "1" }, "/admin/accounts/show/1"]],
- ["/people",[ { controller: "/people", action: "index" }, { controller: "admin/accounts", action: "foo", id: "bar" }, "/admin/accounts/foo/bar"]],
-
- ["/admin/posts",[ { controller: "admin/posts" }]],
- ["/admin/posts/new",[ { controller: "admin/posts", action: "new" }]],
-
- ["/blog/2009",[ { controller: "posts", action: "show_date", year: 2009 }]],
- ["/blog/2009/1",[ { controller: "posts", action: "show_date", year: 2009, month: 1 }]],
- ["/blog/2009/1/1",[ { controller: "posts", action: "show_date", year: 2009, month: 1, day: 1 }]],
-
- ["/archive/2010",[ { controller: "archive", action: "index", year: "2010" }]],
- ["/archive",[ { controller: "archive", action: "index" }]],
- ["/archive?year=january",[ { controller: "archive", action: "index", year: "january" }]],
-
- ["/people",[ { controller: "people", action: "index" }]],
- ["/people",[ { action: "index" }, { controller: "people", action: "index" }, "/people"]],
- ["/people",[ { action: "index" }, { controller: "people", action: "show", id: "1" }, "/people/show/1"]],
- ["/people",[ { controller: "people", action: "index" }, { controller: "people", action: "show", id: "1" }, "/people/show/1"]],
- ["/people",[ {}, { controller: "people", action: "index" }, "/people"]],
- ["/people/1",[ { controller: "people", action: "show" }, { controller: "people", action: "show", id: "1" }, "/people/show/1"]],
- ["/people/new",[ { use_route: "new_person" }]],
- ["/people/new",[ { controller: "people", action: "new" }]],
- ["/people/1",[ { use_route: "person", id: "1" }]],
- ["/people/1",[ { controller: "people", action: "show", id: "1" }]],
- ["/people/1.xml",[ { controller: "people", action: "show", id: "1", format: "xml" }]],
- ["/people/1",[ { controller: "people", action: "show", id: 1 }]],
- ["/people/1",[ { controller: "people", action: "show", id: Model.new("1") }]],
- ["/people/1",[ { action: "show", id: "1" }, { controller: "people", action: "index" }, "/people"]],
- ["/people/1",[ { action: "show", id: 1 }, { controller: "people", action: "show", id: "1" }, "/people/show/1"]],
- ["/people",[ { controller: "people", action: "index" }, { controller: "people", action: "show", id: "1" }, "/people/show/1"]],
- ["/people/1",[ {}, { controller: "people", action: "show", id: "1" }, "/people/show/1"]],
- ["/people/1",[ { controller: "people", action: "show" }, { controller: "people", action: "index", id: "1" }, "/people/index/1"]],
- ["/people/1/edit",[ { controller: "people", action: "edit", id: "1" }]],
- ["/people/1/edit.xml",[ { controller: "people", action: "edit", id: "1", format: "xml" }]],
- ["/people/1/edit",[ { use_route: "edit_person", id: "1" }]],
- ["/people/1?legacy=true",[ { controller: "people", action: "show", id: "1", legacy: "true" }]],
- ["/people?legacy=true",[ { controller: "people", action: "index", legacy: "true" }]],
-
- ["/id_default/2",[ { controller: "foo", action: "id_default", id: "2" }]],
- ["/id_default",[ { controller: "foo", action: "id_default", id: "1" }]],
- ["/id_default",[ { controller: "foo", action: "id_default", id: 1 }]],
- ["/id_default",[ { controller: "foo", action: "id_default" }]],
- ["/optional/bar",[ { controller: "posts", action: "index", optional: "bar" }]],
- ["/posts",[ { controller: "posts", action: "index" }]],
-
- ["/project",[ { controller: "project", action: "index" }]],
- ["/projects/1",[ { controller: "project", action: "index", project_id: "1" }]],
- ["/projects/1",[ { controller: "project", action: "index" }, { project_id: "1", controller: "project", action: "index" }, "/projects/1"]],
- ["/projects/1",[ { use_route: "project", controller: "project", action: "index", project_id: "1" }]],
- ["/projects/1",[ { use_route: "project", controller: "project", action: "index" }, { controller: "project", action: "index", project_id: "1" }, "/projects/1"]],
-
- ["/clients",[ { controller: "projects", action: "index" }]],
- ["/clients?project_id=1",[ { controller: "projects", action: "index", project_id: "1" }]],
- ["/clients",[ { controller: "projects", action: "index" }, { project_id: "1", controller: "project", action: "index" }, "/projects/1"]],
-
- ["/comment/20",[ { id: 20 }, { controller: "comments", action: "show" }, "/comments/show"]],
- ["/comment/20",[ { controller: "comments", id: 20, action: "show" }]],
- ["/comments/boo",[ { controller: "comments", action: "boo" }]],
-
- ["/ws/posts/show/1",[ { controller: "posts", action: "show", id: "1", ws: true }]],
- ["/ws/posts",[ { controller: "posts", action: "index", ws: true }]],
-
- ["/account",[ { controller: "account", action: "subscription" }]],
- ["/account/billing",[ { controller: "account", action: "billing" }]],
-
- ["/pages/1/notes/show/1",[ { page_id: "1", controller: "notes", action: "show", id: "1" }]],
- ["/pages/1/notes/list",[ { page_id: "1", controller: "notes", action: "list" }]],
- ["/pages/1/notes",[ { page_id: "1", controller: "notes", action: "index" }]],
- ["/pages/1/notes",[ { page_id: "1", controller: "notes" }]],
- ["/notes",[ { page_id: nil, controller: "notes" }]],
- ["/notes",[ { controller: "notes" }]],
- ["/notes/print",[ { controller: "notes", action: "print" }]],
- ["/notes/print",[ {}, { controller: "notes", action: "print" }, "/notes/print"]],
-
- ["/notes/index/1",[ { controller: "notes" }, { controller: "notes", action: "index", id: "1" }, "/notes/index/1"]],
- ["/notes/index/1",[ { controller: "notes" }, { controller: "notes", id: "1", action: "index" }, "/notes/index/1"]],
- ["/notes/index/1",[ { action: "index" }, { controller: "notes", id: "1", action: "index" }, "/notes/index/1"]],
- ["/notes/index/1",[ {}, { controller: "notes", id: "1", action: "index" }, "/notes/index/1"]],
- ["/notes/show/1",[ {}, { controller: "notes", action: "show", id: "1" }, "/notes/show/1"]],
- ["/posts",[ { controller: "posts" }, { controller: "notes", action: "show", id: "1" }, "/notes/show/1"]],
- ["/notes/list",[ { action: "list" }, { controller: "notes", action: "show", id: "1" }, "/notes/show/1"]],
-
- ["/posts/ping",[ { controller: "posts", action: "ping" }]],
- ["/posts/show/1",[ { controller: "posts", action: "show", id: "1" }]],
- ["/posts/show/1",[ { controller: "posts", action: "show", id: "1", format: "" }]],
- ["/posts",[ { controller: "posts" }]],
- ["/posts",[ { controller: "posts", action: "index" }]],
- ["/posts/create",[ { action: "create" }, { day: nil, month: nil, controller: "posts", action: "show_date" }, "/blog"]],
- ["/posts?foo=bar",[ { controller: "posts", foo: "bar" }]],
+ ["/admin/users", [ { use_route: "admin_users" }]],
+ ["/admin/users", [ { controller: "admin/users" }]],
+ ["/admin/users", [ { controller: "admin/users", action: "index" }]],
+ ["/admin/users", [ { action: "index" }, { controller: "admin/users", action: "index" }, "/admin/users"]],
+ ["/admin/users", [ { controller: "users", action: "index" }, { controller: "admin/accounts", action: "show", id: "1" }, "/admin/accounts/show/1"]],
+ ["/people", [ { controller: "/people", action: "index" }, { controller: "admin/accounts", action: "foo", id: "bar" }, "/admin/accounts/foo/bar"]],
+
+ ["/admin/posts", [ { controller: "admin/posts" }]],
+ ["/admin/posts/new", [ { controller: "admin/posts", action: "new" }]],
+
+ ["/blog/2009", [ { controller: "posts", action: "show_date", year: 2009 }]],
+ ["/blog/2009/1", [ { controller: "posts", action: "show_date", year: 2009, month: 1 }]],
+ ["/blog/2009/1/1", [ { controller: "posts", action: "show_date", year: 2009, month: 1, day: 1 }]],
+
+ ["/archive/2010", [ { controller: "archive", action: "index", year: "2010" }]],
+ ["/archive", [ { controller: "archive", action: "index" }]],
+ ["/archive?year=january", [ { controller: "archive", action: "index", year: "january" }]],
+
+ ["/people", [ { controller: "people", action: "index" }]],
+ ["/people", [ { action: "index" }, { controller: "people", action: "index" }, "/people"]],
+ ["/people", [ { action: "index" }, { controller: "people", action: "show", id: "1" }, "/people/show/1"]],
+ ["/people", [ { controller: "people", action: "index" }, { controller: "people", action: "show", id: "1" }, "/people/show/1"]],
+ ["/people", [ {}, { controller: "people", action: "index" }, "/people"]],
+ ["/people/1", [ { controller: "people", action: "show" }, { controller: "people", action: "show", id: "1" }, "/people/show/1"]],
+ ["/people/new", [ { use_route: "new_person" }]],
+ ["/people/new", [ { controller: "people", action: "new" }]],
+ ["/people/1", [ { use_route: "person", id: "1" }]],
+ ["/people/1", [ { controller: "people", action: "show", id: "1" }]],
+ ["/people/1.xml", [ { controller: "people", action: "show", id: "1", format: "xml" }]],
+ ["/people/1", [ { controller: "people", action: "show", id: 1 }]],
+ ["/people/1", [ { controller: "people", action: "show", id: Model.new("1") }]],
+ ["/people/1", [ { action: "show", id: "1" }, { controller: "people", action: "index" }, "/people"]],
+ ["/people/1", [ { action: "show", id: 1 }, { controller: "people", action: "show", id: "1" }, "/people/show/1"]],
+ ["/people", [ { controller: "people", action: "index" }, { controller: "people", action: "show", id: "1" }, "/people/show/1"]],
+ ["/people/1", [ {}, { controller: "people", action: "show", id: "1" }, "/people/show/1"]],
+ ["/people/1", [ { controller: "people", action: "show" }, { controller: "people", action: "index", id: "1" }, "/people/index/1"]],
+ ["/people/1/edit", [ { controller: "people", action: "edit", id: "1" }]],
+ ["/people/1/edit.xml", [ { controller: "people", action: "edit", id: "1", format: "xml" }]],
+ ["/people/1/edit", [ { use_route: "edit_person", id: "1" }]],
+ ["/people/1?legacy=true", [ { controller: "people", action: "show", id: "1", legacy: "true" }]],
+ ["/people?legacy=true", [ { controller: "people", action: "index", legacy: "true" }]],
+
+ ["/id_default/2", [ { controller: "foo", action: "id_default", id: "2" }]],
+ ["/id_default", [ { controller: "foo", action: "id_default", id: "1" }]],
+ ["/id_default", [ { controller: "foo", action: "id_default", id: 1 }]],
+ ["/id_default", [ { controller: "foo", action: "id_default" }]],
+ ["/optional/bar", [ { controller: "posts", action: "index", optional: "bar" }]],
+ ["/posts", [ { controller: "posts", action: "index" }]],
+
+ ["/project", [ { controller: "project", action: "index" }]],
+ ["/projects/1", [ { controller: "project", action: "index", project_id: "1" }]],
+ ["/projects/1", [ { controller: "project", action: "index" }, { project_id: "1", controller: "project", action: "index" }, "/projects/1"]],
+ ["/projects/1", [ { use_route: "project", controller: "project", action: "index", project_id: "1" }]],
+ ["/projects/1", [ { use_route: "project", controller: "project", action: "index" }, { controller: "project", action: "index", project_id: "1" }, "/projects/1"]],
+
+ ["/clients", [ { controller: "projects", action: "index" }]],
+ ["/clients?project_id=1", [ { controller: "projects", action: "index", project_id: "1" }]],
+ ["/clients", [ { controller: "projects", action: "index" }, { project_id: "1", controller: "project", action: "index" }, "/projects/1"]],
+
+ ["/comment/20", [ { id: 20 }, { controller: "comments", action: "show" }, "/comments/show"]],
+ ["/comment/20", [ { controller: "comments", id: 20, action: "show" }]],
+ ["/comments/boo", [ { controller: "comments", action: "boo" }]],
+
+ ["/ws/posts/show/1", [ { controller: "posts", action: "show", id: "1", ws: true }]],
+ ["/ws/posts", [ { controller: "posts", action: "index", ws: true }]],
+
+ ["/account", [ { controller: "account", action: "subscription" }]],
+ ["/account/billing", [ { controller: "account", action: "billing" }]],
+
+ ["/pages/1/notes/show/1", [ { page_id: "1", controller: "notes", action: "show", id: "1" }]],
+ ["/pages/1/notes/list", [ { page_id: "1", controller: "notes", action: "list" }]],
+ ["/pages/1/notes", [ { page_id: "1", controller: "notes", action: "index" }]],
+ ["/pages/1/notes", [ { page_id: "1", controller: "notes" }]],
+ ["/notes", [ { page_id: nil, controller: "notes" }]],
+ ["/notes", [ { controller: "notes" }]],
+ ["/notes/print", [ { controller: "notes", action: "print" }]],
+ ["/notes/print", [ {}, { controller: "notes", action: "print" }, "/notes/print"]],
+
+ ["/notes/index/1", [ { controller: "notes" }, { controller: "notes", action: "index", id: "1" }, "/notes/index/1"]],
+ ["/notes/index/1", [ { controller: "notes" }, { controller: "notes", id: "1", action: "index" }, "/notes/index/1"]],
+ ["/notes/index/1", [ { action: "index" }, { controller: "notes", id: "1", action: "index" }, "/notes/index/1"]],
+ ["/notes/index/1", [ {}, { controller: "notes", id: "1", action: "index" }, "/notes/index/1"]],
+ ["/notes/show/1", [ {}, { controller: "notes", action: "show", id: "1" }, "/notes/show/1"]],
+ ["/posts", [ { controller: "posts" }, { controller: "notes", action: "show", id: "1" }, "/notes/show/1"]],
+ ["/notes/list", [ { action: "list" }, { controller: "notes", action: "show", id: "1" }, "/notes/show/1"]],
+
+ ["/posts/ping", [ { controller: "posts", action: "ping" }]],
+ ["/posts/show/1", [ { controller: "posts", action: "show", id: "1" }]],
+ ["/posts/show/1", [ { controller: "posts", action: "show", id: "1", format: "" }]],
+ ["/posts", [ { controller: "posts" }]],
+ ["/posts", [ { controller: "posts", action: "index" }]],
+ ["/posts/create", [ { action: "create" }, { day: nil, month: nil, controller: "posts", action: "show_date" }, "/blog"]],
+ ["/posts?foo=bar", [ { controller: "posts", foo: "bar" }]],
["/posts?foo%5B%5D=bar&foo%5B%5D=baz", [{ controller: "posts", foo: ["bar", "baz"] }]],
["/posts?page=2", [{ controller: "posts", page: 2 }]],
["/posts?q%5Bfoo%5D%5Ba%5D=b", [{ controller: "posts", q: { foo: { a: "b" } } }]],
diff --git a/actionpack/test/controller/url_for_test.rb b/actionpack/test/controller/url_for_test.rb
index 8d7190365d..4b6f33c545 100644
--- a/actionpack/test/controller/url_for_test.rb
+++ b/actionpack/test/controller/url_for_test.rb
@@ -223,13 +223,13 @@ module AbstractController
def test_trailing_slash
add_host!
options = { controller: "foo", trailing_slash: true, action: "bar", id: "33" }
- assert_equal("http://www.basecamphq.com/foo/bar/33/", W.new.url_for(options) )
+ assert_equal("http://www.basecamphq.com/foo/bar/33/", W.new.url_for(options))
end
def test_trailing_slash_with_protocol
add_host!
- options = { trailing_slash: true,protocol: "https", controller: "foo", action: "bar", id: "33" }
- assert_equal("https://www.basecamphq.com/foo/bar/33/", W.new.url_for(options) )
+ options = { trailing_slash: true, protocol: "https", controller: "foo", action: "bar", id: "33" }
+ assert_equal("https://www.basecamphq.com/foo/bar/33/", W.new.url_for(options))
assert_equal "https://www.basecamphq.com/foo/bar/33/?query=string", W.new.url_for(options.merge(query: "string"))
end
@@ -238,7 +238,7 @@ module AbstractController
assert_equal "/foo/", W.new.url_for(options.merge(only_path: true))
options.update(action: "bar", id: "33")
assert_equal "/foo/bar/33/", W.new.url_for(options.merge(only_path: true))
- assert_equal "/foo/bar/33/?query=string", W.new.url_for(options.merge(query: "string",only_path: true))
+ assert_equal "/foo/bar/33/?query=string", W.new.url_for(options.merge(query: "string", only_path: true))
end
def test_trailing_slash_with_anchor
@@ -423,7 +423,7 @@ module AbstractController
first_class.default_url_options[:host] = first_host
second_class.default_url_options[:host] = second_host
- assert_equal first_host, first_class.default_url_options[:host]
+ assert_equal first_host, first_class.default_url_options[:host]
assert_equal second_host, second_class.default_url_options[:host]
end
diff --git a/actionpack/test/dispatch/cookies_test.rb b/actionpack/test/dispatch/cookies_test.rb
index 6dcd62572a..664faa31bb 100644
--- a/actionpack/test/dispatch/cookies_test.rb
+++ b/actionpack/test/dispatch/cookies_test.rb
@@ -43,7 +43,7 @@ class CookieJarTest < ActiveSupport::TestCase
def test_each
request.cookie_jar["foo"] = :bar
list = []
- request.cookie_jar.each do |k,v|
+ request.cookie_jar.each do |k, v|
list << [k, v]
end
@@ -52,7 +52,7 @@ class CookieJarTest < ActiveSupport::TestCase
def test_enumerable
request.cookie_jar["foo"] = :bar
- actual = request.cookie_jar.map { |k,v| [k.to_s, v.to_s] }
+ actual = request.cookie_jar.map { |k, v| [k.to_s, v.to_s] }
assert_equal [["foo", "bar"]], actual
end
@@ -95,17 +95,17 @@ class CookiesTest < ActionController::TestCase
end
def authenticate_for_fourteen_days
- cookies["user_name"] = { "value" => "david", "expires" => Time.utc(2005, 10, 10,5) }
+ cookies["user_name"] = { "value" => "david", "expires" => Time.utc(2005, 10, 10, 5) }
head :ok
end
def authenticate_for_fourteen_days_with_symbols
- cookies[:user_name] = { value: "david", expires: Time.utc(2005, 10, 10,5) }
+ cookies[:user_name] = { value: "david", expires: Time.utc(2005, 10, 10, 5) }
head :ok
end
def set_multiple_cookies
- cookies["user_name"] = { "value" => "david", "expires" => Time.utc(2005, 10, 10,5) }
+ cookies["user_name"] = { "value" => "david", "expires" => Time.utc(2005, 10, 10, 5) }
cookies["login"] = "XJ-122"
head :ok
end
@@ -205,7 +205,7 @@ class CookiesTest < ActionController::TestCase
def delete_and_set_cookie
cookies.delete :user_name
- cookies[:user_name] = { value: "david", expires: Time.utc(2005, 10, 10,5) }
+ cookies[:user_name] = { value: "david", expires: Time.utc(2005, 10, 10, 5) }
head :ok
end
@@ -272,6 +272,10 @@ class CookiesTest < ActionController::TestCase
def noop
head :ok
end
+
+ def encrypted_cookie
+ cookies.encrypted["foo"]
+ end
end
tests TestController
@@ -940,8 +944,8 @@ class CookiesTest < ActionController::TestCase
@request.headers["Cookie"] = "user_id=45"
get :get_signed_cookie
- assert_equal nil, @controller.send(:cookies).signed[:user_id]
- assert_equal nil, @response.cookies["user_id"]
+ assert_nil @controller.send(:cookies).signed[:user_id]
+ assert_nil @response.cookies["user_id"]
end
def test_legacy_signed_cookie_is_treated_as_nil_by_encrypted_cookie_jar_if_tampered
@@ -951,8 +955,8 @@ class CookiesTest < ActionController::TestCase
@request.headers["Cookie"] = "foo=baz"
get :get_encrypted_cookie
- assert_equal nil, @controller.send(:cookies).encrypted[:foo]
- assert_equal nil, @response.cookies["foo"]
+ assert_nil @controller.send(:cookies).encrypted[:foo]
+ assert_nil @response.cookies["foo"]
end
def test_cookie_with_all_domain_option
@@ -1189,6 +1193,12 @@ class CookiesTest < ActionController::TestCase
assert_equal "david", cookies[:user_name]
end
+ def test_cookies_are_not_cleared
+ cookies.encrypted["foo"] = "bar"
+ get :noop
+ assert_equal "bar", @controller.encrypted_cookie
+ end
+
private
def assert_cookie_header(expected)
header = @response.headers["Set-Cookie"]
diff --git a/actionpack/test/dispatch/debug_exceptions_test.rb b/actionpack/test/dispatch/debug_exceptions_test.rb
index 2c5e09e283..ea477e8908 100644
--- a/actionpack/test/dispatch/debug_exceptions_test.rb
+++ b/actionpack/test/dispatch/debug_exceptions_test.rb
@@ -287,7 +287,7 @@ class DebugExceptionsTest < ActionDispatch::IntegrationTest
test "does not show filtered parameters" do
@app = DevelopmentApp
- get "/", params: { "foo"=>"bar" }, headers: { "action_dispatch.show_exceptions" => true,
+ get "/", params: { "foo" => "bar" }, headers: { "action_dispatch.show_exceptions" => true,
"action_dispatch.parameter_filter" => [:foo] }
assert_response 500
assert_match("&quot;foo&quot;=&gt;&quot;[FILTERED]&quot;", body)
@@ -384,6 +384,23 @@ class DebugExceptionsTest < ActionDispatch::IntegrationTest
end
end
+ test "logs with non active support loggers" do
+ @app = DevelopmentApp
+ io = StringIO.new
+ logger = Logger.new(io)
+
+ _old, ActionView::Base.logger = ActionView::Base.logger, logger
+ begin
+ assert_nothing_raised do
+ get "/", headers: { "action_dispatch.show_exceptions" => true, "action_dispatch.logger" => logger }
+ end
+ ensure
+ ActionView::Base.logger = _old
+ end
+
+ assert_match(/puke/, io.rewind && io.read)
+ end
+
test "uses backtrace cleaner from env" do
@app = DevelopmentApp
backtrace_cleaner = ActiveSupport::BacktraceCleaner.new
diff --git a/actionpack/test/dispatch/header_test.rb b/actionpack/test/dispatch/header_test.rb
index 6febd5cb68..958450072e 100644
--- a/actionpack/test/dispatch/header_test.rb
+++ b/actionpack/test/dispatch/header_test.rb
@@ -155,8 +155,8 @@ class HeaderTest < ActiveSupport::TestCase
headers = make_headers(env)
headers["Referer"] = "http://example.com/"
headers.merge! "CONTENT_TYPE" => "text/plain"
- assert_equal({ "HTTP_REFERER"=>"http://example.com/",
- "CONTENT_TYPE"=>"text/plain" }, env)
+ assert_equal({ "HTTP_REFERER" => "http://example.com/",
+ "CONTENT_TYPE" => "text/plain" }, env)
end
test "fetch exception" do
diff --git a/actionpack/test/dispatch/mime_type_test.rb b/actionpack/test/dispatch/mime_type_test.rb
index e558efb88d..2ca03c535a 100644
--- a/actionpack/test/dispatch/mime_type_test.rb
+++ b/actionpack/test/dispatch/mime_type_test.rb
@@ -89,7 +89,7 @@ class MimeTypeTest < ActiveSupport::TestCase
# (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322; InfoPath.1)
test "parse other broken acceptlines" do
accept = "image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-shockwave-flash, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, , pronto/1.00.00, sslvpn/1.00.00.00, */*"
- expect = ["image/gif", "image/x-xbitmap", "image/jpeg","image/pjpeg", "application/x-shockwave-flash", "application/vnd.ms-excel", "application/vnd.ms-powerpoint", "application/msword", "pronto/1.00.00", "sslvpn/1.00.00.00", "*/*"]
+ expect = ["image/gif", "image/x-xbitmap", "image/jpeg", "image/pjpeg", "application/x-shockwave-flash", "application/vnd.ms-excel", "application/vnd.ms-powerpoint", "application/msword", "pronto/1.00.00", "sslvpn/1.00.00.00", "*/*"]
assert_equal expect.map(&:to_s), Mime::Type.parse(accept).map(&:to_s)
end
diff --git a/actionpack/test/dispatch/reloader_test.rb b/actionpack/test/dispatch/reloader_test.rb
index e74b8e40fd..db68549b84 100644
--- a/actionpack/test/dispatch/reloader_test.rb
+++ b/actionpack/test/dispatch/reloader_test.rb
@@ -178,7 +178,7 @@ class ReloaderTest < ActiveSupport::TestCase
def test_cleanup_callbacks_are_called_on_exceptions
cleaned = false
assert_deprecated do
- Reloader.to_cleanup { cleaned = true }
+ Reloader.to_cleanup { cleaned = true }
end
begin
diff --git a/actionpack/test/dispatch/request/multipart_params_parsing_test.rb b/actionpack/test/dispatch/request/multipart_params_parsing_test.rb
index e572c722a0..01c5ff1429 100644
--- a/actionpack/test/dispatch/request/multipart_params_parsing_test.rb
+++ b/actionpack/test/dispatch/request/multipart_params_parsing_test.rb
@@ -129,7 +129,7 @@ class MultipartParamsParsingTest < ActionDispatch::IntegrationTest
params = parse_multipart("none")
assert_equal %w(submit-name), params.keys.sort
assert_equal "Larry", params["submit-name"]
- assert_equal nil, params["files"]
+ assert_nil params["files"]
end
test "parses empty upload file" do
@@ -142,7 +142,7 @@ class MultipartParamsParsingTest < ActionDispatch::IntegrationTest
test "uploads and reads binary file" do
with_test_routing do
- fixture = FIXTURE_PATH + "/mona_lisa.jpg"
+ fixture = FIXTURE_PATH + "/ruby_on_rails.jpg"
params = { uploaded_data: fixture_file_upload(fixture, "image/jpg") }
post "/read", params: params
end
diff --git a/actionpack/test/dispatch/request/query_string_parsing_test.rb b/actionpack/test/dispatch/request/query_string_parsing_test.rb
index 5c992be216..2499c33cef 100644
--- a/actionpack/test/dispatch/request/query_string_parsing_test.rb
+++ b/actionpack/test/dispatch/request/query_string_parsing_test.rb
@@ -97,7 +97,7 @@ class QueryStringParsingTest < ActionDispatch::IntegrationTest
assert_parses({ "action" => { "foo" => { "bar" => nil } } }, "action[foo][bar]")
assert_parses({ "action" => { "foo" => { "bar" => [] } } }, "action[foo][bar][]")
assert_parses({ "action" => { "foo" => [] } }, "action[foo][]")
- assert_parses({ "action"=>{ "foo"=>[{ "bar"=>nil }] } }, "action[foo][][bar]")
+ assert_parses({ "action" => { "foo" => [{ "bar" => nil }] } }, "action[foo][][bar]")
end
def test_array_parses_without_nil
@@ -114,7 +114,7 @@ class QueryStringParsingTest < ActionDispatch::IntegrationTest
assert_parses({ "action" => { "foo" => { "bar" => [nil] } } }, "action[foo][bar][]")
assert_parses({ "action" => { "foo" => [nil] } }, "action[foo][]")
assert_parses({ "action" => { "foo" => [{ "bar" => nil }] } }, "action[foo][][bar]")
- assert_parses({ "action" => ["1",nil] }, "action[]=1&action[]")
+ assert_parses({ "action" => ["1", nil] }, "action[]=1&action[]")
ensure
ActionDispatch::Request::Utils.perform_deep_munge = old_perform_deep_munge
end
diff --git a/actionpack/test/dispatch/request/url_encoded_params_parsing_test.rb b/actionpack/test/dispatch/request/url_encoded_params_parsing_test.rb
index 5c7558e48d..1169bf0cdb 100644
--- a/actionpack/test/dispatch/request/url_encoded_params_parsing_test.rb
+++ b/actionpack/test/dispatch/request/url_encoded_params_parsing_test.rb
@@ -55,7 +55,7 @@ class UrlEncodedParamsParsingTest < ActionDispatch::IntegrationTest
"products[second]=Pc",
"=Save"
].join("&")
- expected = {
+ expected = {
"customers" => {
"boston" => {
"first" => {
diff --git a/actionpack/test/dispatch/request_test.rb b/actionpack/test/dispatch/request_test.rb
index 13a87b8976..e11b93b4f0 100644
--- a/actionpack/test/dispatch/request_test.rb
+++ b/actionpack/test/dispatch/request_test.rb
@@ -18,7 +18,7 @@ class BaseRequestTest < ActiveSupport::TestCase
ActionDispatch::Http::URL.url_for(options)
end
- protected
+ private
def stub_request(env = {})
ip_spoofing_check = env.key?(:ip_spoofing_check) ? env.delete(:ip_spoofing_check) : true
@trusted_proxies ||= nil
@@ -94,13 +94,13 @@ class RequestIP < BaseRequestTest
assert_equal "3.4.5.6", request.remote_ip
request = stub_request "HTTP_X_FORWARDED_FOR" => "unknown,192.168.0.1"
- assert_equal nil, request.remote_ip
+ assert_nil request.remote_ip
request = stub_request "HTTP_X_FORWARDED_FOR" => "9.9.9.9, 3.4.5.6, 172.31.4.4, 10.0.0.1"
assert_equal "3.4.5.6", request.remote_ip
request = stub_request "HTTP_X_FORWARDED_FOR" => "not_ip_address"
- assert_equal nil, request.remote_ip
+ assert_nil request.remote_ip
end
test "remote ip spoof detection" do
@@ -154,7 +154,7 @@ class RequestIP < BaseRequestTest
assert_equal "fe80:0000:0000:0000:0202:b3ff:fe1e:8329", request.remote_ip
request = stub_request "HTTP_X_FORWARDED_FOR" => "unknown,::1"
- assert_equal nil, request.remote_ip
+ assert_nil request.remote_ip
request = stub_request "HTTP_X_FORWARDED_FOR" => "2001:0db8:85a3:0000:0000:8a2e:0370:7334, fe80:0000:0000:0000:0202:b3ff:fe1e:8329, ::1, fc00::, fc01::, fdff"
assert_equal "fe80:0000:0000:0000:0202:b3ff:fe1e:8329", request.remote_ip
@@ -163,7 +163,7 @@ class RequestIP < BaseRequestTest
assert_equal "FE00::", request.remote_ip
request = stub_request "HTTP_X_FORWARDED_FOR" => "not_ip_address"
- assert_equal nil, request.remote_ip
+ assert_nil request.remote_ip
end
test "remote ip v6 spoof detection" do
@@ -200,7 +200,7 @@ class RequestIP < BaseRequestTest
assert_equal "3.4.5.6", request.remote_ip
request = stub_request "HTTP_X_FORWARDED_FOR" => "67.205.106.73,unknown"
- assert_equal nil, request.remote_ip
+ assert_nil request.remote_ip
request = stub_request "HTTP_X_FORWARDED_FOR" => "9.9.9.9, 3.4.5.6, 10.0.0.1, 67.205.106.73"
assert_equal "3.4.5.6", request.remote_ip
@@ -222,7 +222,7 @@ class RequestIP < BaseRequestTest
assert_equal "::1", request.remote_ip
request = stub_request "HTTP_X_FORWARDED_FOR" => "unknown,fe80:0000:0000:0000:0202:b3ff:fe1e:8329"
- assert_equal nil, request.remote_ip
+ assert_nil request.remote_ip
request = stub_request "HTTP_X_FORWARDED_FOR" => "fe80:0000:0000:0000:0202:b3ff:fe1e:8329,2001:0db8:85a3:0000:0000:8a2e:0370:7334"
assert_equal "2001:0db8:85a3:0000:0000:8a2e:0370:7334", request.remote_ip
@@ -345,7 +345,7 @@ class RequestPort < BaseRequestTest
test "optional port" do
request = stub_request "HTTP_HOST" => "www.example.org:80"
- assert_equal nil, request.optional_port
+ assert_nil request.optional_port
request = stub_request "HTTP_HOST" => "www.example.org:8080"
assert_equal 8080, request.optional_port
@@ -537,7 +537,7 @@ class RequestCGI < BaseRequestTest
assert_equal "Basic", request.auth_type
assert_equal 0, request.content_length
- assert_equal nil, request.content_mime_type
+ assert_nil request.content_mime_type
assert_equal "CGI/1.1", request.gateway_interface
assert_equal "*/*", request.accept
assert_equal "UTF-8", request.accept_charset
@@ -581,7 +581,7 @@ class RequestCookie < BaseRequestTest
# some Nokia phone browsers omit the space after the semicolon separator.
# some developers have grown accustomed to using comma in cookie values.
- request = stub_request("HTTP_COOKIE"=>"_session_id=c84ace847,96670c052c6ceb2451fb0f2;is_admin=yes")
+ request = stub_request("HTTP_COOKIE" => "_session_id=c84ace847,96670c052c6ceb2451fb0f2;is_admin=yes")
assert_equal "c84ace847", request.cookies["_session_id"], request.cookies.inspect
assert_equal "yes", request.cookies["is_admin"], request.cookies.inspect
end
@@ -596,7 +596,7 @@ class RequestParamsParsing < BaseRequestTest
"rack.input" => StringIO.new("flamenco=love")
)
- assert_equal({ "flamenco"=> "love" }, request.request_parameters)
+ assert_equal({ "flamenco" => "love" }, request.request_parameters)
end
test "doesnt interpret request uri as query string when missing" do
@@ -774,8 +774,8 @@ class RequestMethod < BaseRequestTest
ensure
# Reset original acronym set
ActiveSupport::Inflector.inflections do |inflect|
- inflect.send(:instance_variable_set,"@acronyms",existing_acrnoyms)
- inflect.send(:instance_variable_set,"@acronym_regex",existing_acrnoym_regex)
+ inflect.send(:instance_variable_set, "@acronyms", existing_acrnoyms)
+ inflect.send(:instance_variable_set, "@acronym_regex", existing_acrnoym_regex)
end
end
end
@@ -957,7 +957,7 @@ class RequestMimeType < BaseRequestTest
end
test "no content type" do
- assert_equal nil, stub_request.content_mime_type
+ assert_nil stub_request.content_mime_type
end
test "content type is XML" do
@@ -978,7 +978,7 @@ class RequestMimeType < BaseRequestTest
"HTTP_X_REQUESTED_WITH" => "XMLHttpRequest"
)
- assert_equal nil, request.negotiate_mime([Mime[:xml], Mime[:json]])
+ assert_nil request.negotiate_mime([Mime[:xml], Mime[:json]])
assert_equal Mime[:html], request.negotiate_mime([Mime[:xml], Mime[:html]])
assert_equal Mime[:html], request.negotiate_mime([Mime[:xml], Mime::ALL])
end
@@ -1072,14 +1072,14 @@ end
class RequestParameterFilter < BaseRequestTest
test "process parameter filter" do
test_hashes = [
- [{ "foo"=>"bar" },{ "foo"=>"bar" },%w'food'],
- [{ "foo"=>"bar" },{ "foo"=>"[FILTERED]" },%w'foo'],
- [{ "foo"=>"bar", "bar"=>"foo" },{ "foo"=>"[FILTERED]", "bar"=>"foo" },%w'foo baz'],
- [{ "foo"=>"bar", "baz"=>"foo" },{ "foo"=>"[FILTERED]", "baz"=>"[FILTERED]" },%w'foo baz'],
- [{ "bar"=>{ "foo"=>"bar","bar"=>"foo" } },{ "bar"=>{ "foo"=>"[FILTERED]","bar"=>"foo" } },%w'fo'],
- [{ "foo"=>{ "foo"=>"bar","bar"=>"foo" } },{ "foo"=>"[FILTERED]" },%w'f banana'],
- [{ "deep"=>{ "cc"=>{ "code"=>"bar","bar"=>"foo" },"ss"=>{ "code"=>"bar" } } },{ "deep"=>{ "cc"=>{ "code"=>"[FILTERED]","bar"=>"foo" },"ss"=>{ "code"=>"bar" } } },%w'deep.cc.code'],
- [{ "baz"=>[{ "foo"=>"baz" }, "1"] }, { "baz"=>[{ "foo"=>"[FILTERED]" }, "1"] }, [/foo/]]]
+ [{ "foo" => "bar" }, { "foo" => "bar" }, %w'food'],
+ [{ "foo" => "bar" }, { "foo" => "[FILTERED]" }, %w'foo'],
+ [{ "foo" => "bar", "bar" => "foo" }, { "foo" => "[FILTERED]", "bar" => "foo" }, %w'foo baz'],
+ [{ "foo" => "bar", "baz" => "foo" }, { "foo" => "[FILTERED]", "baz" => "[FILTERED]" }, %w'foo baz'],
+ [{ "bar" => { "foo" => "bar", "bar" => "foo" } }, { "bar" => { "foo" => "[FILTERED]", "bar" => "foo" } }, %w'fo'],
+ [{ "foo" => { "foo" => "bar", "bar" => "foo" } }, { "foo" => "[FILTERED]" }, %w'f banana'],
+ [{ "deep" => { "cc" => { "code" => "bar", "bar" => "foo" }, "ss" => { "code" => "bar" } } }, { "deep" => { "cc" => { "code" => "[FILTERED]", "bar" => "foo" }, "ss" => { "code" => "bar" } } }, %w'deep.cc.code'],
+ [{ "baz" => [{ "foo" => "baz" }, "1"] }, { "baz" => [{ "foo" => "[FILTERED]" }, "1"] }, [/foo/]]]
test_hashes.each do |before_filter, after_filter, filter_words|
parameter_filter = ActionDispatch::Http::ParameterFilter.new(filter_words)
@@ -1091,8 +1091,8 @@ class RequestParameterFilter < BaseRequestTest
}
parameter_filter = ActionDispatch::Http::ParameterFilter.new(filter_words)
- before_filter["barg"] = { :bargain=>"gain", "blah"=>"bar", "bar"=>{ "bargain"=>{ "blah"=>"foo" } } }
- after_filter["barg"] = { :bargain=>"niag", "blah"=>"[FILTERED]", "bar"=>{ "bargain"=>{ "blah"=>"[FILTERED]" } } }
+ before_filter["barg"] = { :bargain => "gain", "blah" => "bar", "bar" => { "bargain" => { "blah" => "foo" } } }
+ after_filter["barg"] = { :bargain => "niag", "blah" => "[FILTERED]", "bar" => { "bargain" => { "blah" => "[FILTERED]" } } }
assert_equal after_filter, parameter_filter.filter(before_filter)
end
@@ -1192,7 +1192,7 @@ class RequestEtag < BaseRequestTest
test "doesn't match absent If-None-Match" do
request = stub_request
- assert_equal nil, request.if_none_match
+ assert_nil request.if_none_match
assert_equal [], request.if_none_match_etags
assert_not request.etag_matches?("foo")
diff --git a/actionpack/test/dispatch/response_test.rb b/actionpack/test/dispatch/response_test.rb
index 4e547ab7d5..7433c5ce0c 100644
--- a/actionpack/test/dispatch/response_test.rb
+++ b/actionpack/test/dispatch/response_test.rb
@@ -74,7 +74,7 @@ class ResponseTest < ActiveSupport::TestCase
@response.body = "Hello, World!"
# even though there's no explicitly set content-type,
- assert_equal nil, @response.content_type
+ assert_nil @response.content_type
# after the action reads back @response.body,
assert_equal "Hello, World!", @response.body
@@ -110,6 +110,11 @@ class ResponseTest < ActiveSupport::TestCase
assert_equal "application/aaron", @response.content_type
end
+ def test_empty_content_type_returns_nil
+ @response.headers["Content-Type"] = ""
+ assert_nil @response.content_type
+ end
+
test "simple output" do
@response.body = "Hello, World!"
@@ -131,7 +136,7 @@ class ResponseTest < ActiveSupport::TestCase
def test_only_set_charset_still_defaults_to_text_html
response = ActionDispatch::Response.new
response.charset = "utf-16"
- _,headers,_ = response.to_a
+ _, headers, _ = response.to_a
assert_equal "text/html; charset=utf-16", headers["Content-Type"]
end
@@ -229,7 +234,7 @@ class ResponseTest < ActiveSupport::TestCase
test "multiple cookies" do
@response.set_cookie("user_name", value: "david", path: "/")
- @response.set_cookie("login", value: "foo&bar", path: "/", expires: Time.utc(2005, 10, 10,5))
+ @response.set_cookie("login", value: "foo&bar", path: "/", expires: Time.utc(2005, 10, 10, 5))
_status, headers, _body = @response.to_a
assert_equal "user_name=david; path=/\nlogin=foo%26bar; path=/; expires=Mon, 10 Oct 2005 05:00:00 -0000", headers["Set-Cookie"]
assert_equal({ "login" => "foo&bar", "user_name" => "david" }, @response.cookies)
@@ -237,7 +242,7 @@ class ResponseTest < ActiveSupport::TestCase
test "delete cookies" do
@response.set_cookie("user_name", value: "david", path: "/")
- @response.set_cookie("login", value: "foo&bar", path: "/", expires: Time.utc(2005, 10, 10,5))
+ @response.set_cookie("login", value: "foo&bar", path: "/", expires: Time.utc(2005, 10, 10, 5))
@response.delete_cookie("login")
assert_equal({ "user_name" => "david", "login" => nil }, @response.cookies)
end
diff --git a/actionpack/test/dispatch/routing/ipv6_redirect_test.rb b/actionpack/test/dispatch/routing/ipv6_redirect_test.rb
index 4987ed84e4..179aee9ba7 100644
--- a/actionpack/test/dispatch/routing/ipv6_redirect_test.rb
+++ b/actionpack/test/dispatch/routing/ipv6_redirect_test.rb
@@ -7,7 +7,7 @@ class IPv6IntegrationTest < ActionDispatch::IntegrationTest
class ::BadRouteRequestController < ActionController::Base
include Routes.url_helpers
def index
- render text: foo_path
+ render plain: foo_path
end
def foo
diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb
index c01065932a..474d053af6 100644
--- a/actionpack/test/dispatch/routing_test.rb
+++ b/actionpack/test/dispatch/routing_test.rb
@@ -1933,7 +1933,7 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
post :preview, on: :new
end
- resource :admin, path_names: { new: "novo" }, path: "administrador" do
+ resource :admin, path_names: { new: "novo" }, path: "administrador" do
post :preview, on: :new
end
@@ -3736,7 +3736,7 @@ private
https!(old_https)
end
- def verify_redirect(url, status=301)
+ def verify_redirect(url, status = 301)
assert_equal status, @response.status
assert_equal url, @response.headers["Location"]
assert_equal expected_redirect_body(url), @response.body
@@ -4163,7 +4163,7 @@ class TestRedirectInterpolation < ActionDispatch::IntegrationTest
end
private
- def verify_redirect(url, status=301)
+ def verify_redirect(url, status = 301)
assert_equal status, @response.status
assert_equal url, @response.headers["Location"]
assert_equal expected_redirect_body(url), @response.body
@@ -4189,7 +4189,7 @@ class TestConstraintsAccessingParameters < ActionDispatch::IntegrationTest
test "parameters are reset between constraint checks" do
get "/bar"
- assert_equal nil, @request.params[:foo]
+ assert_nil @request.params[:foo]
assert_equal "bar", @request.params[:bar]
end
end
@@ -4696,15 +4696,15 @@ class TestUrlGenerationErrors < ActionDispatch::IntegrationTest
end
test "url helpers raise message with mixed parameters when generation fails" do
- url, missing = { action: "show", controller: "products", id: nil, "id"=>"url-tested" }, [:id]
+ url, missing = { action: "show", controller: "products", id: nil, "id" => "url-tested" }, [:id]
message = "No route matches #{url.inspect}, possible unmatched constraints: #{missing.inspect}"
# Optimized url helper
- error = assert_raises(ActionController::UrlGenerationError) { product_path(nil, "id"=>"url-tested") }
+ error = assert_raises(ActionController::UrlGenerationError) { product_path(nil, "id" => "url-tested") }
assert_equal message, error.message
# Non-optimized url helper
- error = assert_raises(ActionController::UrlGenerationError, message) { product_path(id: nil, "id"=>"url-tested") }
+ error = assert_raises(ActionController::UrlGenerationError, message) { product_path(id: nil, "id" => "url-tested") }
assert_equal message, error.message
end
end
diff --git a/actionpack/test/dispatch/session/cache_store_test.rb b/actionpack/test/dispatch/session/cache_store_test.rb
index a60629a7ee..859059063f 100644
--- a/actionpack/test/dispatch/session/cache_store_test.rb
+++ b/actionpack/test/dispatch/session/cache_store_test.rb
@@ -142,20 +142,20 @@ class CacheStoreTest < ActionDispatch::IntegrationTest
get "/get_session_value"
assert_response :success
- assert_equal nil, headers["Set-Cookie"], "should not resend the cookie again if session_id cookie is already exists"
+ assert_nil headers["Set-Cookie"], "should not resend the cookie again if session_id cookie is already exists"
end
end
def test_prevents_session_fixation
with_test_route_set do
- assert_equal nil, @cache.read("_session_id:0xhax")
+ assert_nil @cache.read("_session_id:0xhax")
cookies["_session_id"] = "0xhax"
get "/set_session_value"
assert_response :success
assert_not_equal "0xhax", cookies["_session_id"]
- assert_equal nil, @cache.read("_session_id:0xhax")
+ assert_nil @cache.read("_session_id:0xhax")
assert_equal({ "foo" => "bar" }, @cache.read("_session_id:#{cookies['_session_id']}"))
end
end
diff --git a/actionpack/test/dispatch/session/cookie_store_test.rb b/actionpack/test/dispatch/session/cookie_store_test.rb
index 013d289c6d..2a1053be16 100644
--- a/actionpack/test/dispatch/session/cookie_store_test.rb
+++ b/actionpack/test/dispatch/session/cookie_store_test.rb
@@ -108,7 +108,7 @@ class CookieStoreTest < ActionDispatch::IntegrationTest
with_test_route_set(secure: true) do
get "/set_session_value"
assert_response :success
- assert_equal nil, headers["Set-Cookie"]
+ assert_nil headers["Set-Cookie"]
end
end
@@ -169,7 +169,7 @@ class CookieStoreTest < ActionDispatch::IntegrationTest
with_test_route_set do
get "/no_session_access"
assert_response :success
- assert_equal nil, headers["Set-Cookie"]
+ assert_nil headers["Set-Cookie"]
end
end
@@ -179,7 +179,7 @@ class CookieStoreTest < ActionDispatch::IntegrationTest
"fef868465920f415f2c0652d6910d3af288a0367"
get "/no_session_access"
assert_response :success
- assert_equal nil, headers["Set-Cookie"]
+ assert_nil headers["Set-Cookie"]
end
end
diff --git a/actionpack/test/dispatch/session/mem_cache_store_test.rb b/actionpack/test/dispatch/session/mem_cache_store_test.rb
index c2d0719b4e..121e9ebef7 100644
--- a/actionpack/test/dispatch/session/mem_cache_store_test.rb
+++ b/actionpack/test/dispatch/session/mem_cache_store_test.rb
@@ -157,7 +157,7 @@ class MemCacheStoreTest < ActionDispatch::IntegrationTest
get "/get_session_value"
assert_response :success
- assert_equal nil, headers["Set-Cookie"], "should not resend the cookie again if session_id cookie is already exists"
+ assert_nil headers["Set-Cookie"], "should not resend the cookie again if session_id cookie is already exists"
end
rescue Dalli::RingError => ex
skip ex.message, ex.backtrace
diff --git a/actionpack/test/dispatch/static_test.rb b/actionpack/test/dispatch/static_test.rb
index aca70e180c..d8bc96e3e0 100644
--- a/actionpack/test/dispatch/static_test.rb
+++ b/actionpack/test/dispatch/static_test.rb
@@ -143,16 +143,16 @@ module StaticTests
assert_equal "Accept-Encoding", response.headers["Vary"]
assert_equal "gzip", response.headers["Content-Encoding"]
- response = get(file_name, "HTTP_ACCEPT_ENCODING" => "Gzip")
- assert_gzip file_name, response
+ response = get(file_name, "HTTP_ACCEPT_ENCODING" => "Gzip")
+ assert_gzip file_name, response
- response = get(file_name, "HTTP_ACCEPT_ENCODING" => "GZIP")
- assert_gzip file_name, response
+ response = get(file_name, "HTTP_ACCEPT_ENCODING" => "GZIP")
+ assert_gzip file_name, response
- response = get(file_name, "HTTP_ACCEPT_ENCODING" => "compress;q=0.5, gzip;q=1.0")
- assert_gzip file_name, response
+ response = get(file_name, "HTTP_ACCEPT_ENCODING" => "compress;q=0.5, gzip;q=1.0")
+ assert_gzip file_name, response
- response = get(file_name, "HTTP_ACCEPT_ENCODING" => "")
+ response = get(file_name, "HTTP_ACCEPT_ENCODING" => "")
assert_not_equal "gzip", response.headers["Content-Encoding"]
end
@@ -166,7 +166,7 @@ module StaticTests
def test_serves_gzip_with_propper_content_type_fallback
file_name = "/gzip/foo.zoo"
response = get(file_name, "HTTP_ACCEPT_ENCODING" => "gzip")
- assert_gzip file_name, response
+ assert_gzip file_name, response
default_response = get(file_name) # no gzip
assert_equal default_response.headers["Content-Type"], response.headers["Content-Type"]
@@ -177,9 +177,9 @@ module StaticTests
last_modified = File.mtime(File.join(@root, "#{file_name}.gz"))
response = get(file_name, "HTTP_ACCEPT_ENCODING" => "gzip", "HTTP_IF_MODIFIED_SINCE" => last_modified.httpdate)
assert_equal 304, response.status
- assert_equal nil, response.headers["Content-Type"]
- assert_equal nil, response.headers["Content-Encoding"]
- assert_equal nil, response.headers["Vary"]
+ assert_nil response.headers["Content-Type"]
+ assert_nil response.headers["Content-Encoding"]
+ assert_nil response.headers["Vary"]
end
def test_serves_files_with_headers
@@ -204,7 +204,7 @@ module StaticTests
end
# Windows doesn't allow \ / : * ? " < > | in filenames
- unless RbConfig::CONFIG["host_os"] =~ /mswin|mingw/
+ unless Gem.win_platform?
def test_serves_static_file_with_colon
with_static_file "/foo/foo:bar.html" do |file|
assert_html file, get("/foo/foo%3Abar.html")
@@ -269,14 +269,14 @@ class StaticTest < ActiveSupport::TestCase
filename = "shared.html.erb"
assert File.exist?(File.join(@root, "..", filename))
env = {
- "REQUEST_METHOD"=>"GET",
- "REQUEST_PATH"=>"/..%2F#{filename}",
- "PATH_INFO"=>"/..%2F#{filename}",
- "REQUEST_URI"=>"/..%2F#{filename}",
- "HTTP_VERSION"=>"HTTP/1.1",
- "SERVER_NAME"=>"localhost",
- "SERVER_PORT"=>"8080",
- "QUERY_STRING"=>""
+ "REQUEST_METHOD" => "GET",
+ "REQUEST_PATH" => "/..%2F#{filename}",
+ "PATH_INFO" => "/..%2F#{filename}",
+ "REQUEST_URI" => "/..%2F#{filename}",
+ "HTTP_VERSION" => "HTTP/1.1",
+ "SERVER_NAME" => "localhost",
+ "SERVER_PORT" => "8080",
+ "QUERY_STRING" => ""
}
assert_equal(DummyApp.call(nil), @app.call(env))
end
diff --git a/actionpack/test/dispatch/test_request_test.rb b/actionpack/test/dispatch/test_request_test.rb
index b479af781d..85a6df4975 100644
--- a/actionpack/test/dispatch/test_request_test.rb
+++ b/actionpack/test/dispatch/test_request_test.rb
@@ -30,7 +30,7 @@ class TestRequestTest < ActiveSupport::TestCase
req = ActionDispatch::TestRequest.create({})
assert_equal({}, req.cookies)
- assert_equal nil, req.env["HTTP_COOKIE"]
+ assert_nil req.env["HTTP_COOKIE"]
req.cookie_jar["user_name"] = "david"
assert_cookies({ "user_name" => "david" }, req.cookie_jar)
diff --git a/actionpack/test/dispatch/uploaded_file_test.rb b/actionpack/test/dispatch/uploaded_file_test.rb
index 60d0246a68..51680216e4 100644
--- a/actionpack/test/dispatch/uploaded_file_test.rb
+++ b/actionpack/test/dispatch/uploaded_file_test.rb
@@ -58,25 +58,25 @@ module ActionDispatch
end
def test_delegates_close_to_tempfile
- tf = Class.new { def close(unlink_now=false); "thunderhorse" end }
+ tf = Class.new { def close(unlink_now = false); "thunderhorse" end }
uf = Http::UploadedFile.new(tempfile: tf.new)
assert_equal "thunderhorse", uf.close
end
def test_close_accepts_parameter
- tf = Class.new { def close(unlink_now=false); "thunderhorse: #{unlink_now}" end }
+ tf = Class.new { def close(unlink_now = false); "thunderhorse: #{unlink_now}" end }
uf = Http::UploadedFile.new(tempfile: tf.new)
assert_equal "thunderhorse: true", uf.close(true)
end
def test_delegates_read_to_tempfile
- tf = Class.new { def read(length=nil, buffer=nil); "thunderhorse" end }
+ tf = Class.new { def read(length = nil, buffer = nil); "thunderhorse" end }
uf = Http::UploadedFile.new(tempfile: tf.new)
assert_equal "thunderhorse", uf.read
end
def test_delegates_read_to_tempfile_with_params
- tf = Class.new { def read(length=nil, buffer=nil); [length, buffer] end }
+ tf = Class.new { def read(length = nil, buffer = nil); [length, buffer] end }
uf = Http::UploadedFile.new(tempfile: tf.new)
assert_equal %w{ thunder horse }, uf.read(*%w{ thunder horse })
end
diff --git a/actionpack/test/fixtures/multipart/mona_lisa.jpg b/actionpack/test/fixtures/multipart/mona_lisa.jpg
deleted file mode 100644
index 5cf3bef3d0..0000000000
--- a/actionpack/test/fixtures/multipart/mona_lisa.jpg
+++ /dev/null
Binary files differ
diff --git a/actionpack/test/fixtures/multipart/ruby_on_rails.jpg b/actionpack/test/fixtures/multipart/ruby_on_rails.jpg
new file mode 100644
index 0000000000..ed284ea0ba
--- /dev/null
+++ b/actionpack/test/fixtures/multipart/ruby_on_rails.jpg
Binary files differ
diff --git a/actionpack/test/fixtures/session_autoload_test/session_autoload_test/foo.rb b/actionpack/test/fixtures/session_autoload_test/session_autoload_test/foo.rb
index cd0be6d1b5..18fa5cd923 100644
--- a/actionpack/test/fixtures/session_autoload_test/session_autoload_test/foo.rb
+++ b/actionpack/test/fixtures/session_autoload_test/session_autoload_test/foo.rb
@@ -1,6 +1,6 @@
module SessionAutoloadTest
class Foo
- def initialize(bar="baz")
+ def initialize(bar = "baz")
@bar = bar
end
def inspect
diff --git a/actionpack/test/journey/gtg/builder_test.rb b/actionpack/test/journey/gtg/builder_test.rb
index 2b314cdd4e..aa8427b265 100644
--- a/actionpack/test/journey/gtg/builder_test.rb
+++ b/actionpack/test/journey/gtg/builder_test.rb
@@ -5,18 +5,18 @@ module ActionDispatch
module GTG
class TestBuilder < ActiveSupport::TestCase
def test_following_states_multi
- table = tt ["a|a"]
+ table = tt ["a|a"]
assert_equal 1, table.move([0], "a").length
end
def test_following_states_multi_regexp
- table = tt [":a|b"]
+ table = tt [":a|b"]
assert_equal 1, table.move([0], "fooo").length
assert_equal 2, table.move([0], "b").length
end
def test_multi_path
- table = tt ["/:a/d", "/b/c"]
+ table = tt ["/:a/d", "/b/c"]
[
[1, "/"],
@@ -38,7 +38,7 @@ module ActionDispatch
/articles/:id(.:format)
}
- sim = NFA::Simulator.new table
+ sim = NFA::Simulator.new table
match = sim.match "/articles/new"
assert_equal 2, match.memos.length
@@ -52,7 +52,7 @@ module ActionDispatch
/articles/new(.:format)
}
- sim = NFA::Simulator.new table
+ sim = NFA::Simulator.new table
match = sim.match "/articles/new"
assert_equal 2, match.memos.length
diff --git a/actionpack/test/journey/gtg/transition_table_test.rb b/actionpack/test/journey/gtg/transition_table_test.rb
index 4c8b5032eb..c7315c0338 100644
--- a/actionpack/test/journey/gtg/transition_table_test.rb
+++ b/actionpack/test/journey/gtg/transition_table_test.rb
@@ -92,7 +92,7 @@ module ActionDispatch
private
def asts(paths)
- parser = Journey::Parser.new
+ parser = Journey::Parser.new
paths.map { |x|
ast = parser.parse x
ast.each { |n| n.memo = ast }
diff --git a/actionpack/test/journey/nfa/simulator_test.rb b/actionpack/test/journey/nfa/simulator_test.rb
index 183c892a53..38f99398cb 100644
--- a/actionpack/test/journey/nfa/simulator_test.rb
+++ b/actionpack/test/journey/nfa/simulator_test.rb
@@ -80,7 +80,7 @@ module ActionDispatch
ast = Nodes::Or.new routes
- nfa = Journey::NFA::Builder.new ast
+ nfa = Journey::NFA::Builder.new ast
sim = Simulator.new nfa.transition_table
md = sim.match "/articles"
assert_equal [asts.first], md.memos
diff --git a/actionpack/test/journey/nfa/transition_table_test.rb b/actionpack/test/journey/nfa/transition_table_test.rb
index f3cf36a064..0bc6bc1cf8 100644
--- a/actionpack/test/journey/nfa/transition_table_test.rb
+++ b/actionpack/test/journey/nfa/transition_table_test.rb
@@ -53,10 +53,10 @@ module ActionDispatch
end
def test_alphabet
- table = tt "a|:a"
+ table = tt "a|:a"
assert_equal [/[^\.\/\?]+/, "a"], table.alphabet
- table = tt "a|a"
+ table = tt "a|a"
assert_equal ["a"], table.alphabet
end
diff --git a/actionpack/test/journey/route_test.rb b/actionpack/test/journey/route_test.rb
index b6414fd101..8fd73970b8 100644
--- a/actionpack/test/journey/route_test.rb
+++ b/actionpack/test/journey/route_test.rb
@@ -26,7 +26,7 @@ module ActionDispatch
end
def test_path_requirements_override_defaults
- path = Path::Pattern.build(":name", { name: /love/ }, "/", true)
+ path = Path::Pattern.build(":name", { name: /love/ }, "/", true)
defaults = { name: "tender" }
route = Route.build("name", nil, path, {}, [], defaults)
assert_equal(/love/, route.requirements[:name])
@@ -96,7 +96,7 @@ module ActionDispatch
path = Path::Pattern.from_string "/:controller(/:action(/:id))(.:format)"
generic = Route.build "name", nil, path, constraints, [], {}
- knowledge = { id: 20, controller: "pages", action: "show" }
+ knowledge = { "id" => true, "controller" => true, "action" => true }
routes = [specific, generic]
diff --git a/actionpack/test/journey/router_test.rb b/actionpack/test/journey/router_test.rb
index 7b5916eb72..f223a125a3 100644
--- a/actionpack/test/journey/router_test.rb
+++ b/actionpack/test/journey/router_test.rb
@@ -6,8 +6,8 @@ module ActionDispatch
attr_reader :mapper, :routes, :route_set, :router
def setup
- @app = Routing::RouteSet::Dispatcher.new({})
- @route_set = ActionDispatch::Routing::RouteSet.new
+ @app = Routing::RouteSet::Dispatcher.new({})
+ @route_set = ActionDispatch::Routing::RouteSet.new
@routes = @route_set.router.routes
@router = @route_set.router
@formatter = @route_set.formatter
@@ -116,7 +116,7 @@ module ActionDispatch
end
def test_clear_trailing_slash_from_script_name_on_root_unanchored_routes
- app = lambda { |env| [200, {}, ["success!"]] }
+ app = lambda { |env| [200, {}, ["success!"]] }
get "/weblog", to: app
env = rack_env("SCRIPT_NAME" => "", "PATH_INFO" => "/weblog")
@@ -233,7 +233,7 @@ module ActionDispatch
nil,
Hash[params],
{},
- lambda { |k,v| parameterized << [k,v]; v })
+ lambda { |k, v| parameterized << [k, v]; v })
assert_equal params.map(&:to_s).sort, parameterized.map(&:to_s).sort
end
@@ -289,15 +289,15 @@ module ActionDispatch
relative_url_root: nil
}
redirection_parameters = {
- "action"=>"show",
+ "action" => "show",
}
missing_key = "name"
- missing_parameters ={
+ missing_parameters = {
missing_key => "task_1"
}
request_parameters = primarty_parameters.merge(redirection_parameters).merge(missing_parameters)
- message = "No route matches #{Hash[request_parameters.sort_by { |k,v|k.to_s }].inspect}, missing required keys: #{[missing_key.to_sym].inspect}"
+ message = "No route matches #{Hash[request_parameters.sort_by { |k, v|k.to_s }].inspect}, missing required keys: #{[missing_key.to_sym].inspect}"
error = assert_raises(ActionController::UrlGenerationError) do
@formatter.generate(
@@ -338,7 +338,7 @@ module ActionDispatch
route = @routes.first
env = rails_env "PATH_INFO" => request_path
- called = false
+ called = false
router.recognize(env) do |r, params|
assert_equal route, r
@@ -358,7 +358,7 @@ module ActionDispatch
get "/:segment/*splat", to: "foo#bar"
env = rails_env "PATH_INFO" => request_path
- called = false
+ called = false
route = @routes.first
router.recognize(env) do |r, params|
@@ -395,7 +395,7 @@ module ActionDispatch
get "/books(/:action(.:format))", controller: "books"
route = @routes.first
- env = rails_env "PATH_INFO" => "/books/list.rss"
+ env = rails_env "PATH_INFO" => "/books/list.rss"
expected = { controller: "books", action: "list", format: "rss" }
called = false
router.recognize(env) do |r, params|
@@ -427,7 +427,7 @@ module ActionDispatch
get "/books(/:action(.:format))", to: "foo#bar"
env = rails_env "PATH_INFO" => "/books/list.rss",
- "REQUEST_METHOD" => "HEAD"
+ "REQUEST_METHOD" => "HEAD"
called = false
router.recognize(env) do |r, params|
diff --git a/actionpack/test/journey/routes_test.rb b/actionpack/test/journey/routes_test.rb
index ca735ea022..d8db5ffad1 100644
--- a/actionpack/test/journey/routes_test.rb
+++ b/actionpack/test/journey/routes_test.rb
@@ -6,7 +6,7 @@ module ActionDispatch
attr_reader :routes, :mapper
def setup
- @route_set = ActionDispatch::Routing::RouteSet.new
+ @route_set = ActionDispatch::Routing::RouteSet.new
@routes = @route_set.router.routes
@router = @route_set.router
@mapper = ActionDispatch::Routing::Mapper.new @route_set
diff --git a/actionpack/test/lib/controller/fake_models.rb b/actionpack/test/lib/controller/fake_models.rb
index ce9522d12a..046b4167bb 100644
--- a/actionpack/test/lib/controller/fake_models.rb
+++ b/actionpack/test/lib/controller/fake_models.rb
@@ -6,7 +6,7 @@ class Customer < Struct.new(:name, :id)
undef_method :to_json
- def to_xml(options={})
+ def to_xml(options = {})
if options[:builder]
options[:builder].name name
else
@@ -14,7 +14,7 @@ class Customer < Struct.new(:name, :id)
end
end
- def to_js(options={})
+ def to_js(options = {})
"name: #{name.inspect}"
end
alias :to_text :to_js
diff --git a/actionview/.gitignore b/actionview/.gitignore
new file mode 100644
index 0000000000..0a04b29786
--- /dev/null
+++ b/actionview/.gitignore
@@ -0,0 +1,2 @@
+/lib/assets/compiled
+/tmp
diff --git a/actionview/CHANGELOG.md b/actionview/CHANGELOG.md
index 67bd9b5c8f..2a38ca7b63 100644
--- a/actionview/CHANGELOG.md
+++ b/actionview/CHANGELOG.md
@@ -1,13 +1,57 @@
+* Return correct object name in form helper method after `fields_for`.
+
+ Fixes #26931.
+
+ *Yuji Yaginuma*
+
+* Use `ActionView::Resolver.caching?` (`config.action_view.cache_template_loading`)
+ to enable template recompilation.
+
+ Before it was enabled by `consider_all_requests_local`, which caused
+ recompilation in tests.
+
+ *Max Melentiev*
+
+* Add `form_with` to unify `form_tag` and `form_for` usage.
+
+ Used like `form_tag` (where just the open tag is output):
+
+ ```erb
+ <%= form_with scope: :post, url: super_special_posts_path %>
+ ```
+
+ Used like `form_for`:
+
+ ```erb
+ <%= form_with model: @post do |form| %>
+ <%= form.text_field :title %>
+ <% end %>
+ ```
+
+ *Kasper Timm Hansen*, *Marek Kirejczyk*
+
+* Add `fields` form helper method.
+
+ ```erb
+ <%= fields :comment, model: @comment do |fields| %>
+ <%= fields.text_field :title %>
+ <% end %>
+ ```
+
+ Can also be used within form helpers such as `form_with`.
+
+ *Kasper Timm Hansen*
+
* Removed deprecated `#original_exception` in `ActionView::Template::Error`.
*Rafael Mendonça França*
-* Render now accepts any keys for locals, including reserved words
+* Render now accepts any keys for locals, including reserved keywords.
Only locals with valid variable names get set directly. Others
- will still be available in local_assigns.
+ will still be available in `local_assigns`.
- Example of render with reserved words:
+ Example of render with reserved keywords:
```erb
<%= render "example", class: "text-center", message: "Hello world!" %>
diff --git a/actionview/RUNNING_UJS_TESTS.rdoc b/actionview/RUNNING_UJS_TESTS.rdoc
new file mode 100644
index 0000000000..a575624a06
--- /dev/null
+++ b/actionview/RUNNING_UJS_TESTS.rdoc
@@ -0,0 +1,7 @@
+== Running UJS tests
+
+Ensure that you can build the project and run tests.
+Run rake ujs:server first, and then run the web tests by
+visiting http://localhost:4567 in your browser.
+
+rake ujs:server
diff --git a/actionview/Rakefile b/actionview/Rakefile
index 6c3fc59b0a..cba4684076 100644
--- a/actionview/Rakefile
+++ b/actionview/Rakefile
@@ -3,7 +3,7 @@ require "rake/testtask"
desc "Default Task"
task default: :test
-task :package
+task package: "assets:compile"
# Run the unit tests
@@ -46,6 +46,21 @@ namespace :test do
end
end
+namespace :ujs do
+ desc "Starts the test server"
+ task :server do
+ system "bundle exec rackup test/ujs/config.ru -p 4567 -s puma"
+ end
+end
+
+namespace :assets do
+ desc "Compile Action View assets"
+ task :compile do
+ require "blade"
+ Blade.build
+ end
+end
+
task :lines do
load File.expand_path("..", File.dirname(__FILE__)) + "/tools/line_statistics"
files = FileList["lib/**/*.rb"]
diff --git a/actionview/app/assets/javascripts/config.coffee b/actionview/app/assets/javascripts/config.coffee
new file mode 100644
index 0000000000..3d4706b0e1
--- /dev/null
+++ b/actionview/app/assets/javascripts/config.coffee
@@ -0,0 +1,37 @@
+#= export Rails
+
+@Rails =
+ # Link elements bound by jquery-ujs
+ linkClickSelector: 'a[data-confirm], a[data-method], a[data-remote]:not([disabled]), a[data-disable-with], a[data-disable]'
+
+ # Button elements bound by jquery-ujs
+ buttonClickSelector:
+ selector: 'button[data-remote]:not([form]), button[data-confirm]:not([form])'
+ exclude: 'form button'
+
+ # Select elements bound by jquery-ujs
+ inputChangeSelector: 'select[data-remote], input[data-remote], textarea[data-remote]'
+
+ # Form elements bound by jquery-ujs
+ formSubmitSelector: 'form'
+
+ # Form input elements bound by jquery-ujs
+ formInputClickSelector: 'form input[type=submit], form input[type=image], form button[type=submit], form button:not([type]), input[type=submit][form], input[type=image][form], button[type=submit][form], button[form]:not([type])'
+
+ # Form input elements disabled during form submission
+ formDisableSelector: 'input[data-disable-with]:enabled, button[data-disable-with]:enabled, textarea[data-disable-with]:enabled, input[data-disable]:enabled, button[data-disable]:enabled, textarea[data-disable]:enabled'
+
+ # Form input elements re-enabled after form submission
+ formEnableSelector: 'input[data-disable-with]:disabled, button[data-disable-with]:disabled, textarea[data-disable-with]:disabled, input[data-disable]:disabled, button[data-disable]:disabled, textarea[data-disable]:disabled'
+
+ # Form required input elements
+ requiredInputSelector: 'input[name][required]:not([disabled]), textarea[name][required]:not([disabled])'
+
+ # Form file input elements
+ fileInputSelector: 'input[name][type=file]:not([disabled])'
+
+ # Link onClick disable selector with possible reenable after remote submission
+ linkDisableSelector: 'a[data-disable-with], a[data-disable]'
+
+ # Button onClick disable selector with possible reenable after remote submission
+ buttonDisableSelector: 'button[data-remote][data-disable-with], button[data-remote][data-disable]'
diff --git a/actionview/app/assets/javascripts/features/confirm.coffee b/actionview/app/assets/javascripts/features/confirm.coffee
new file mode 100644
index 0000000000..72b5aaa218
--- /dev/null
+++ b/actionview/app/assets/javascripts/features/confirm.coffee
@@ -0,0 +1,26 @@
+#= require_tree ../utils
+
+{ fire, stopEverything } = Rails
+
+Rails.handleConfirm = (e) ->
+ stopEverything(e) unless allowAction(this)
+
+# For 'data-confirm' attribute:
+# - Fires `confirm` event
+# - Shows the confirmation dialog
+# - Fires the `confirm:complete` event
+#
+# Returns `true` if no function stops the chain and user chose yes `false` otherwise.
+# Attaching a handler to the element's `confirm` event that returns a `falsy` value cancels the confirmation dialog.
+# Attaching a handler to the element's `confirm:complete` event that returns a `falsy` value makes this function
+# return false. The `confirm:complete` event is fired whether or not the user answered true or false to the dialog.
+allowAction = (element) ->
+ message = element.getAttribute('data-confirm')
+ return true unless message
+
+ answer = false
+ if fire(element, 'confirm')
+ try answer = confirm(message)
+ callback = fire(element, 'confirm:complete', [answer])
+
+ answer and callback
diff --git a/actionview/app/assets/javascripts/features/disable.coffee b/actionview/app/assets/javascripts/features/disable.coffee
new file mode 100644
index 0000000000..e8cce7da40
--- /dev/null
+++ b/actionview/app/assets/javascripts/features/disable.coffee
@@ -0,0 +1,78 @@
+#= require_tree ../utils
+
+{ matches, getData, setData, stopEverything, formElements } = Rails
+
+# Unified function to enable an element (link, button and form)
+Rails.enableElement = (e) ->
+ element = if e instanceof Event then e.target else e
+ if matches(element, Rails.linkDisableSelector)
+ enableLinkElement(element)
+ else if matches(element, Rails.buttonDisableSelector) or matches(element, Rails.formEnableSelector)
+ enableFormElement(element)
+ else if matches(element, Rails.formSubmitSelector)
+ enableFormElements(element)
+
+# Unified function to disable an element (link, button and form)
+Rails.disableElement = (e) ->
+ element = if e instanceof Event then e.target else e
+ if matches(element, Rails.linkDisableSelector)
+ disableLinkElement(element)
+ else if matches(element, Rails.buttonDisableSelector) or matches(element, Rails.formDisableSelector)
+ disableFormElement(element)
+ else if matches(element, Rails.formSubmitSelector)
+ disableFormElements(element)
+
+# Replace element's html with the 'data-disable-with' after storing original html
+# and prevent clicking on it
+disableLinkElement = (element) ->
+ replacement = element.getAttribute('data-disable-with')
+ if replacement?
+ setData(element, 'ujs:enable-with', element.innerHTML) # store enabled state
+ element.innerHTML = replacement
+ element.addEventListener('click', stopEverything) # prevent further clicking
+ setData(element, 'ujs:disabled', true)
+
+# Restore element to its original state which was disabled by 'disableLinkElement' above
+enableLinkElement = (element) ->
+ originalText = getData(element, 'ujs:enable-with')
+ if originalText?
+ element.innerHTML = originalText # set to old enabled state
+ setData(element, 'ujs:enable-with', null) # clean up cache
+ element.removeEventListener('click', stopEverything) # enable element
+ setData(element, 'ujs:disabled', null)
+
+# Disables form elements:
+# - Caches element value in 'ujs:enable-with' data store
+# - Replaces element text with value of 'data-disable-with' attribute
+# - Sets disabled property to true
+disableFormElements = (form) ->
+ formElements(form, Rails.formDisableSelector).forEach(disableFormElement)
+
+disableFormElement = (element) ->
+ replacement = element.getAttribute('data-disable-with')
+ if replacement?
+ if matches(element, 'button')
+ setData(element, 'ujs:enable-with', element.innerHTML)
+ element.innerHTML = replacement
+ else
+ setData(element, 'ujs:enable-with', element.value)
+ element.value = replacement
+ element.disabled = true
+ setData(element, 'ujs:disabled', true)
+
+# Re-enables disabled form elements:
+# - Replaces element text with cached value from 'ujs:enable-with' data store (created in `disableFormElements`)
+# - Sets disabled property to false
+enableFormElements = (form) ->
+ formElements(form, Rails.formEnableSelector).forEach(enableFormElement)
+
+enableFormElement = (element) ->
+ originalText = getData(element, 'ujs:enable-with')
+ if originalText?
+ if matches(element, 'button')
+ element.innerHTML = originalText
+ else
+ element.value = originalText
+ setData(element, 'ujs:enable-with', null) # clean up cache
+ element.disabled = false
+ setData(element, 'ujs:disabled', null)
diff --git a/actionview/app/assets/javascripts/features/method.coffee b/actionview/app/assets/javascripts/features/method.coffee
new file mode 100644
index 0000000000..d04d9414dd
--- /dev/null
+++ b/actionview/app/assets/javascripts/features/method.coffee
@@ -0,0 +1,34 @@
+#= require_tree ../utils
+
+{ stopEverything } = Rails
+
+# Handles "data-method" on links such as:
+# <a href="/users/5" data-method="delete" rel="nofollow" data-confirm="Are you sure?">Delete</a>
+Rails.handleMethod = (e) ->
+ link = this
+ method = link.getAttribute('data-method')
+ return unless method
+
+ href = Rails.href(link)
+ csrfToken = Rails.csrfToken()
+ csrfParam = Rails.csrfParam()
+ form = document.createElement('form')
+ formContent = "<input name='_method' value='#{method}' type='hidden' />"
+
+ if csrfParam? and csrfToken? and not Rails.isCrossDomain(href)
+ formContent += "<input name='#{csrfParam}' value='#{csrfToken}' type='hidden' />"
+
+ # Must trigger submit by click on a button, else "submit" event handler won't work!
+ # https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/submit
+ formContent += '<input type="submit" />'
+
+ form.method = 'post'
+ form.action = href
+ form.target = link.target
+ form.innerHTML = formContent
+ form.style.display = 'none'
+
+ document.body.appendChild(form)
+ form.querySelector('[type="submit"]').click()
+
+ stopEverything(e)
diff --git a/actionview/app/assets/javascripts/features/remote.coffee b/actionview/app/assets/javascripts/features/remote.coffee
new file mode 100644
index 0000000000..30a5dc21fa
--- /dev/null
+++ b/actionview/app/assets/javascripts/features/remote.coffee
@@ -0,0 +1,100 @@
+#= require_tree ../utils
+
+{
+ matches, getData, setData
+ fire, stopEverything
+ ajax, isCrossDomain
+ blankInputs, serializeElement
+} = Rails
+
+# Checks "data-remote" if true to handle the request through a XHR request.
+isRemote = (element) ->
+ value = element.getAttribute('data-remote')
+ value? and value isnt 'false'
+
+# Submits "remote" forms and links with ajax
+Rails.handleRemote = (e) ->
+ element = this
+
+ return true unless isRemote(element)
+ unless fire(element, 'ajax:before')
+ fire(element, 'ajax:stopped')
+ return false
+
+ withCredentials = element.getAttribute('data-with-credentials')
+ dataType = element.getAttribute('data-type') or 'script'
+
+ if matches(element, Rails.formSubmitSelector)
+ # memoized value from clicked submit button
+ button = getData(element, 'ujs:submit-button')
+ method = getData(element, 'ujs:submit-button-formmethod') or element.method
+ url = getData(element, 'ujs:submit-button-formaction') or element.getAttribute('action') or location.href
+
+ # strip query string if it's a GET request
+ url = url.replace(/\?.*$/, '') if method.toUpperCase() is 'GET'
+
+ if element.enctype is 'multipart/form-data'
+ data = new FormData(element)
+ data.append(button.name, button.value) if button?
+ else
+ data = serializeElement(element, button)
+
+ setData(element, 'ujs:submit-button', null)
+ setData(element, 'ujs:submit-button-formmethod', null)
+ setData(element, 'ujs:submit-button-formaction', null)
+ else if matches(element, Rails.buttonClickSelector) or matches(element, Rails.inputChangeSelector)
+ method = element.getAttribute('data-method')
+ url = element.getAttribute('data-url')
+ data = serializeElement(element, element.getAttribute('data-params'))
+ else
+ method = element.getAttribute('data-method')
+ url = Rails.href(element)
+ data = element.getAttribute('data-params')
+
+ ajax(
+ type: method or 'GET'
+ url: url
+ data: data
+ dataType: dataType
+ # stopping the "ajax:beforeSend" event will cancel the ajax request
+ beforeSend: (xhr, options) ->
+ if fire(element, 'ajax:beforeSend', [xhr, options])
+ fire(element, 'ajax:send', [xhr])
+ else
+ fire(element, 'ajax:stopped')
+ xhr.abort()
+ success: (args...) -> fire(element, 'ajax:success', args)
+ error: (args...) -> fire(element, 'ajax:error', args)
+ complete: (args...) -> fire(element, 'ajax:complete', args)
+ crossDomain: isCrossDomain(url)
+ withCredentials: withCredentials? and withCredentials isnt 'false'
+ )
+ stopEverything(e)
+
+# Check whether any required fields are empty
+# In both ajax mode and normal mode
+Rails.validateForm = (e) ->
+ form = this
+ return if form.noValidate or getData(form, 'ujs:formnovalidate-button')
+ # Skip other logic when required values are missing or file upload is present
+ blankRequiredInputs = blankInputs(form, Rails.requiredInputSelector, false)
+ if blankRequiredInputs.length > 0 and fire(form, 'ajax:aborted:required', [blankRequiredInputs])
+ stopEverything(e)
+
+Rails.formSubmitButtonClick = (e) ->
+ button = this
+ form = button.form
+ return unless form
+ # Register the pressed submit button
+ setData(form, 'ujs:submit-button', name: button.name, value: button.value) if button.name
+ # Save attributes from button
+ setData(form, 'ujs:formnovalidate-button', button.formNoValidate)
+ setData(form, 'ujs:submit-button-formaction', button.getAttribute('formaction'))
+ setData(form, 'ujs:submit-button-formmethod', button.getAttribute('formmethod'))
+
+Rails.handleMetaClick = (e) ->
+ link = this
+ method = (link.getAttribute('data-method') or 'GET').toUpperCase()
+ data = link.getAttribute('data-params')
+ metaClick = e.metaKey or e.ctrlKey
+ e.stopImmediatePropagation() if metaClick and method is 'GET' and not data
diff --git a/actionview/app/assets/javascripts/rails-ujs.coffee b/actionview/app/assets/javascripts/rails-ujs.coffee
new file mode 100644
index 0000000000..f96d2eb6fd
--- /dev/null
+++ b/actionview/app/assets/javascripts/rails-ujs.coffee
@@ -0,0 +1,76 @@
+#
+# Unobtrusive JavaScript
+# https://github.com/rails/rails-ujs
+#
+# Released under the MIT license
+#
+#= require ./config
+#= require_tree ./utils
+#= require_tree ./features
+
+{
+ fire, delegate
+ getData, $
+ refreshCSRFTokens, CSRFProtection
+ enableElement, disableElement
+ handleConfirm
+ handleRemote, validateForm, formSubmitButtonClick, handleMetaClick
+ handleMethod
+} = Rails
+
+# For backward compatibility
+if jQuery? and not jQuery.rails
+ jQuery.rails = Rails
+ jQuery.ajaxPrefilter (options, originalOptions, xhr) ->
+ CSRFProtection(xhr) unless options.crossDomain
+
+Rails.start = ->
+ # Cut down on the number of issues from people inadvertently including jquery_ujs twice
+ # by detecting and raising an error when it happens.
+ throw new Error('jquery-ujs has already been loaded!') if window._rails_loaded
+
+ # This event works the same as the load event, except that it fires every
+ # time the page is loaded.
+ # See https://github.com/rails/jquery-ujs/issues/357
+ # See https://developer.mozilla.org/en-US/docs/Using_Firefox_1.5_caching
+ window.addEventListener 'pageshow', ->
+ $(Rails.formEnableSelector).forEach (el) ->
+ enableElement(el) if getData(el, 'ujs:disabled')
+ $(Rails.linkDisableSelector).forEach (el) ->
+ enableElement(el) if getData(el, 'ujs:disabled')
+
+ delegate document, Rails.linkDisableSelector, 'ajax:complete', enableElement
+ delegate document, Rails.linkDisableSelector, 'ajax:stopped', enableElement
+ delegate document, Rails.buttonDisableSelector, 'ajax:complete', enableElement
+ delegate document, Rails.buttonDisableSelector, 'ajax:stopped', enableElement
+
+ delegate document, Rails.linkClickSelector, 'click', handleConfirm
+ delegate document, Rails.linkClickSelector, 'click', handleMetaClick
+ delegate document, Rails.linkClickSelector, 'click', disableElement
+ delegate document, Rails.linkClickSelector, 'click', handleRemote
+ delegate document, Rails.linkClickSelector, 'click', handleMethod
+
+ delegate document, Rails.buttonClickSelector, 'click', handleConfirm
+ delegate document, Rails.buttonClickSelector, 'click', disableElement
+ delegate document, Rails.buttonClickSelector, 'click', handleRemote
+
+ delegate document, Rails.inputChangeSelector, 'change', handleConfirm
+ delegate document, Rails.inputChangeSelector, 'change', handleRemote
+
+ delegate document, Rails.formSubmitSelector, 'submit', handleConfirm
+ delegate document, Rails.formSubmitSelector, 'submit', validateForm
+ delegate document, Rails.formSubmitSelector, 'submit', handleRemote
+ # Normal mode submit
+ # Slight timeout so that the submit button gets properly serialized
+ delegate document, Rails.formSubmitSelector, 'submit', (e) -> setTimeout((-> disableElement(e)), 13)
+ delegate document, Rails.formSubmitSelector, 'ajax:send', disableElement
+ delegate document, Rails.formSubmitSelector, 'ajax:complete', enableElement
+
+ delegate document, Rails.formInputClickSelector, 'click', handleConfirm
+ delegate document, Rails.formInputClickSelector, 'click', formSubmitButtonClick
+
+ document.addEventListener('DOMContentLoaded', refreshCSRFTokens)
+ window._rails_loaded = true
+
+if window.Rails is Rails and fire(document, 'rails:attachBindings')
+ Rails.start()
diff --git a/actionview/app/assets/javascripts/utils/ajax.coffee b/actionview/app/assets/javascripts/utils/ajax.coffee
new file mode 100644
index 0000000000..9af515beda
--- /dev/null
+++ b/actionview/app/assets/javascripts/utils/ajax.coffee
@@ -0,0 +1,95 @@
+#= require ./csrf
+#= require ./event
+
+{ CSRFProtection, fire } = Rails
+
+AcceptHeaders =
+ '*': '*/*'
+ text: 'text/plain'
+ html: 'text/html'
+ xml: 'application/xml, text/xml'
+ json: 'application/json, text/javascript'
+ script: 'text/javascript, application/javascript, application/ecmascript, application/x-ecmascript'
+
+Rails.ajax = (options) ->
+ options = prepareOptions(options)
+ xhr = createXHR options, ->
+ response = processResponse(xhr.response, xhr.getResponseHeader('Content-Type'))
+ if xhr.status // 100 == 2
+ options.success?(response, xhr.statusText, xhr)
+ else
+ options.error?(response, xhr.statusText, xhr)
+ options.complete?(xhr, xhr.statusText)
+ # Call beforeSend hook
+ options.beforeSend?(xhr, options)
+ # Send the request
+ if xhr.readyState is XMLHttpRequest.OPENED
+ xhr.send(options.data)
+ else
+ fire(document, 'ajaxStop') # to be compatible with jQuery.ajax
+
+prepareOptions = (options) ->
+ options.type = options.type.toUpperCase()
+ # append data to url if it's a GET request
+ if options.type is 'GET' and options.data
+ if options.url.indexOf('?') < 0
+ options.url += '?' + options.data
+ else
+ options.url += '&' + options.data
+ # Use "*" as default dataType
+ options.dataType = '*' unless AcceptHeaders[options.dataType]?
+ options.accept = AcceptHeaders[options.dataType]
+ options.accept += ', */*; q=0.01' if options.dataType isnt '*'
+ options
+
+createXHR = (options, done) ->
+ xhr = new XMLHttpRequest()
+ # Open and setup xhr
+ xhr.open(options.type, options.url, true)
+ xhr.setRequestHeader('Accept', options.accept)
+ # Set Content-Type only when sending a string
+ # Sending FormData will automatically set Content-Type to multipart/form-data
+ if typeof options.data is 'string'
+ xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8')
+ xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest') unless options.crossDomain
+ # Add X-CSRF-Token
+ CSRFProtection(xhr)
+ xhr.withCredentials = !!options.withCredentials
+ xhr.onreadystatechange = ->
+ done(xhr) if xhr.readyState is XMLHttpRequest.DONE
+ xhr
+
+processResponse = (response, type) ->
+ if typeof response is 'string' and typeof type is 'string'
+ if type.match(/\bjson\b/)
+ try response = JSON.parse(response)
+ else if type.match(/\bjavascript\b/)
+ script = document.createElement('script')
+ script.innerHTML = response
+ document.body.appendChild(script)
+ else if type.match(/\b(xml|html|svg)\b/)
+ parser = new DOMParser()
+ type = type.replace(/;.+/, '') # remove something like ';charset=utf-8'
+ try response = parser.parseFromString(response, type)
+ response
+
+# Default way to get an element's href. May be overridden at Rails.href.
+Rails.href = (element) -> element.href
+
+# Determines if the request is a cross domain request.
+Rails.isCrossDomain = (url) ->
+ originAnchor = document.createElement('a')
+ originAnchor.href = location.href
+ urlAnchor = document.createElement('a')
+ try
+ urlAnchor.href = url
+ # If URL protocol is false or is a string containing a single colon
+ # *and* host are false, assume it is not a cross-domain request
+ # (should only be the case for IE7 and IE compatibility mode).
+ # Otherwise, evaluate protocol and host of the URL against the origin
+ # protocol and host.
+ !(((!urlAnchor.protocol || urlAnchor.protocol == ':') && !urlAnchor.host) ||
+ (originAnchor.protocol + '//' + originAnchor.host == urlAnchor.protocol + '//' + urlAnchor.host))
+ catch e
+ # If there is an error parsing the URL, assume it is crossDomain.
+ true
diff --git a/actionview/app/assets/javascripts/utils/csrf.coffee b/actionview/app/assets/javascripts/utils/csrf.coffee
new file mode 100644
index 0000000000..4eb5ebb414
--- /dev/null
+++ b/actionview/app/assets/javascripts/utils/csrf.coffee
@@ -0,0 +1,25 @@
+#= require ./dom
+
+{ $ } = Rails
+
+# Up-to-date Cross-Site Request Forgery token
+csrfToken = Rails.csrfToken = ->
+ meta = document.querySelector('meta[name=csrf-token]')
+ meta and meta.content
+
+# URL param that must contain the CSRF token
+csrfParam = Rails.csrfParam = ->
+ meta = document.querySelector('meta[name=csrf-param]')
+ meta and meta.content
+
+# Make sure that every Ajax request sends the CSRF token
+Rails.CSRFProtection = (xhr) ->
+ token = csrfToken()
+ xhr.setRequestHeader('X-CSRF-Token', token) if token?
+
+# Make sure that all forms have actual up-to-date tokens (cached forms contain old ones)
+Rails.refreshCSRFTokens = ->
+ token = csrfToken()
+ param = csrfParam()
+ if token? and param?
+ $('form input[name="' + param + '"]').forEach (input) -> input.value = token
diff --git a/actionview/app/assets/javascripts/utils/dom.coffee b/actionview/app/assets/javascripts/utils/dom.coffee
new file mode 100644
index 0000000000..6bef618147
--- /dev/null
+++ b/actionview/app/assets/javascripts/utils/dom.coffee
@@ -0,0 +1,28 @@
+m = Element.prototype.matches or
+ Element.prototype.matchesSelector or
+ Element.prototype.mozMatchesSelector or
+ Element.prototype.msMatchesSelector or
+ Element.prototype.oMatchesSelector or
+ Element.prototype.webkitMatchesSelector
+
+Rails.matches = (element, selector) ->
+ if selector.exclude?
+ m.call(element, selector.selector) and not m.call(element, selector.exclude)
+ else
+ m.call(element, selector)
+
+# get and set data on a given element using "expando properties"
+# See: https://developer.mozilla.org/en-US/docs/Glossary/Expando
+expando = '_ujsData'
+
+Rails.getData = (element, key) ->
+ element[expando]?[key]
+
+Rails.setData = (element, key, value) ->
+ element[expando] ?= {}
+ element[expando][key] = value
+
+# a wrapper for document.querySelectorAll
+# returns an Array
+Rails.$ = (selector) ->
+ Array.prototype.slice.call(document.querySelectorAll(selector))
diff --git a/actionview/app/assets/javascripts/utils/event.coffee b/actionview/app/assets/javascripts/utils/event.coffee
new file mode 100644
index 0000000000..049b2a3ecd
--- /dev/null
+++ b/actionview/app/assets/javascripts/utils/event.coffee
@@ -0,0 +1,40 @@
+#= require ./dom
+
+{ matches } = Rails
+
+# Polyfill for CustomEvent in IE9+
+# https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent#Polyfill
+CustomEvent = window.CustomEvent
+
+if typeof CustomEvent is 'function'
+ CustomEvent = (event, params) ->
+ evt = document.createEvent('CustomEvent')
+ evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail)
+ evt
+ CustomEvent.prototype = window.Event.prototype
+
+# Triggers an custom event on an element and returns false if the event result is false
+fire = Rails.fire = (obj, name, data) ->
+ event = new CustomEvent(
+ name,
+ bubbles: true,
+ cancelable: true,
+ detail: data,
+ )
+ obj.dispatchEvent(event)
+ !event.defaultPrevented
+
+# Helper function, needed to provide consistent behavior in IE
+Rails.stopEverything = (e) ->
+ fire(e.target, 'ujs:everythingStopped')
+ e.preventDefault()
+ e.stopPropagation()
+ e.stopImmediatePropagation()
+
+Rails.delegate = (element, selector, eventType, handler) ->
+ element.addEventListener eventType, (e) ->
+ target = e.target
+ target = target.parentNode until not (target instanceof Element) or matches(target, selector)
+ if target instanceof Element and handler.call(target, e) == false
+ e.preventDefault()
+ e.stopPropagation()
diff --git a/actionview/app/assets/javascripts/utils/form.coffee b/actionview/app/assets/javascripts/utils/form.coffee
new file mode 100644
index 0000000000..251113deda
--- /dev/null
+++ b/actionview/app/assets/javascripts/utils/form.coffee
@@ -0,0 +1,61 @@
+#= require ./dom
+
+{ matches } = Rails
+
+toArray = (e) -> Array.prototype.slice.call(e)
+
+Rails.serializeElement = (element, additionalParam) ->
+ inputs = [element]
+ inputs = toArray(element.elements) if matches(element, 'form')
+ params = []
+
+ inputs.forEach (input) ->
+ return unless input.name
+ if matches(input, 'select')
+ toArray(input.options).forEach (option) ->
+ params.push(name: input.name, value: option.value) if option.selected
+ else if input.type isnt 'radio' and input.type isnt 'checkbox' or input.checked
+ params.push(name: input.name, value: input.value)
+
+ params.push(additionalParam) if additionalParam
+
+ params.map (param) ->
+ if param.name?
+ "#{encodeURIComponent(param.name)}=#{encodeURIComponent(param.value)}"
+ else
+ param
+ .join('&')
+
+# Helper function that returns form elements that match the specified CSS selector
+# If form is actually a "form" element this will return associated elements outside the from that have
+# the html form attribute set
+Rails.formElements = (form, selector) ->
+ if matches(form, 'form')
+ toArray(form.elements).filter (el) -> matches(el, selector)
+ else
+ toArray(form.querySelectorAll(selector))
+
+# Helper function which checks for blank inputs in a form that match the specified CSS selector
+Rails.blankInputs = (form, selector, nonBlank) ->
+ foundInputs = []
+ requiredInputs = toArray(form.querySelectorAll(selector or 'input, textarea'))
+ checkedRadioButtonNames = {}
+
+ requiredInputs.forEach (input) ->
+ if input.type is 'radio'
+ # Don't count unchecked required radio as blank if other radio with same name is checked,
+ # regardless of whether same-name radio input has required attribute or not. The spec
+ # states https://www.w3.org/TR/html5/forms.html#the-required-attribute
+ radioName = input.name
+ # Skip if we've already seen the radio with this name.
+ unless checkedRadioButtonNames[radioName]
+ # If none checked
+ if form.querySelectorAll("input[type=radio][name='#{radioName}']:checked").length == 0
+ radios = form.querySelectorAll("input[type=radio][name='#{radioName}']")
+ foundInputs = foundInputs.concat(toArray(radios))
+ # We only need to check each name once.
+ checkedRadioButtonNames[radioName] = radioName
+ else
+ valueToCheck = if input.type is 'checkbox' then input.checked else !!input.value
+ foundInputs.push(input) if valueToCheck is nonBlank
+ foundInputs
diff --git a/actionview/bin/test b/actionview/bin/test
index 84a05bba08..a7beb14b27 100755
--- a/actionview/bin/test
+++ b/actionview/bin/test
@@ -2,5 +2,3 @@
COMPONENT_ROOT = File.expand_path("..", __dir__)
require File.expand_path("../tools/test", COMPONENT_ROOT)
-
-exit Minitest.run(ARGV)
diff --git a/actionview/blade.yml b/actionview/blade.yml
new file mode 100644
index 0000000000..9e5eb953a4
--- /dev/null
+++ b/actionview/blade.yml
@@ -0,0 +1,11 @@
+load_paths:
+ - app/assets/javascripts
+
+logical_paths:
+ - rails-ujs.js
+
+build:
+ logical_paths:
+ - rails-ujs.js
+ path: lib/assets/compiled
+ clean: true
diff --git a/actionview/lib/action_view/context.rb b/actionview/lib/action_view/context.rb
index ee263df484..31aa73a0cf 100644
--- a/actionview/lib/action_view/context.rb
+++ b/actionview/lib/action_view/context.rb
@@ -28,7 +28,7 @@ module ActionView
# returns the correct buffer on +yield+. This is usually
# overwritten by helpers to add more behavior.
# :api: plugin
- def _layout_for(name=nil)
+ def _layout_for(name = nil)
name ||= :layout
view_flow.get(name).html_safe
end
diff --git a/actionview/lib/action_view/flows.rb b/actionview/lib/action_view/flows.rb
index 16874c1194..6d5f57a570 100644
--- a/actionview/lib/action_view/flows.rb
+++ b/actionview/lib/action_view/flows.rb
@@ -5,7 +5,7 @@ module ActionView
attr_reader :content
def initialize
- @content = Hash.new { |h,k| h[k] = ActiveSupport::SafeBuffer.new }
+ @content = Hash.new { |h, k| h[k] = ActiveSupport::SafeBuffer.new }
end
# Called by _layout_for to read stored values.
diff --git a/actionview/lib/action_view/helpers/asset_tag_helper.rb b/actionview/lib/action_view/helpers/asset_tag_helper.rb
index b1563ac490..72a094c629 100644
--- a/actionview/lib/action_view/helpers/asset_tag_helper.rb
+++ b/actionview/lib/action_view/helpers/asset_tag_helper.rb
@@ -1,6 +1,5 @@
require "active_support/core_ext/array/extract_options"
require "active_support/core_ext/hash/keys"
-require "active_support/core_ext/regexp"
require "action_view/helpers/asset_url_helper"
require "action_view/helpers/tag_helper"
@@ -36,18 +35,37 @@ module ActionView
# When the Asset Pipeline is enabled, you can pass the name of your manifest as
# source, and include other JavaScript or CoffeeScript files inside the manifest.
#
+ # ==== Options
+ #
+ # When the last parameter is a hash you can add HTML attributes using that
+ # parameter. The following options are supported:
+ #
+ # * <tt>:extname</tt> - Append a extention to the generated url unless the extension
+ # already exists. This only applies for relative urls.
+ # * <tt>:protocol</tt> - Sets the protocol of the generated url, this option only
+ # applies when a relative url and +host+ options are provided.
+ # * <tt>:host</tt> - When a relative url is provided the host is added to the
+ # that path.
+ # * <tt>:skip_pipeline</tt> - This option is used to bypass the asset pipeline
+ # when it is set to true.
+ #
+ # ==== Examples
+ #
# javascript_include_tag "xmlhr"
- # # => <script src="/assets/xmlhr.js?1284139606"></script>
+ # # => <script src="/assets/xmlhr.debug-1284139606.js"></script>
+ #
+ # javascript_include_tag "xmlhr", host: "localhost", protocol: "https"
+ # # => <script src="https://localhost/assets/xmlhr.debug-1284139606.js"></script>
#
# javascript_include_tag "template.jst", extname: false
- # # => <script src="/assets/template.jst?1284139606"></script>
+ # # => <script src="/assets/template.debug-1284139606.jst"></script>
#
# javascript_include_tag "xmlhr.js"
- # # => <script src="/assets/xmlhr.js?1284139606"></script>
+ # # => <script src="/assets/xmlhr.debug-1284139606.js"></script>
#
# javascript_include_tag "common.javascript", "/elsewhere/cools"
- # # => <script src="/assets/common.javascript?1284139606"></script>
- # # <script src="/elsewhere/cools.js?1423139606"></script>
+ # # => <script src="/assets/common.javascript.debug-1284139606.js"></script>
+ # # <script src="/elsewhere/cools.debug-1284139606.js"></script>
#
# javascript_include_tag "http://www.example.com/xmlhr"
# # => <script src="http://www.example.com/xmlhr"></script>
@@ -169,7 +187,7 @@ module ActionView
#
# favicon_link_tag 'mb-icon.png', rel: 'apple-touch-icon', type: 'image/png'
# # => <link href="/assets/mb-icon.png" rel="apple-touch-icon" type="image/png" />
- def favicon_link_tag(source="favicon.ico", options={})
+ def favicon_link_tag(source = "favicon.ico", options = {})
tag("link", {
rel: "shortcut icon",
type: "image/x-icon",
@@ -207,7 +225,7 @@ module ActionView
# # => <img alt="Icon" class="menu_icon" src="/icons/icon.gif" />
# image_tag("/icons/icon.gif", data: { title: 'Rails Application' })
# # => <img data-title="Rails Application" src="/icons/icon.gif" />
- def image_tag(source, options={})
+ def image_tag(source, options = {})
options = options.symbolize_keys
check_for_image_tag_errors(options)
diff --git a/actionview/lib/action_view/helpers/asset_url_helper.rb b/actionview/lib/action_view/helpers/asset_url_helper.rb
index e0de2ff4d6..c6a5e04aba 100644
--- a/actionview/lib/action_view/helpers/asset_url_helper.rb
+++ b/actionview/lib/action_view/helpers/asset_url_helper.rb
@@ -1,5 +1,4 @@
require "zlib"
-require "active_support/core_ext/regexp"
module ActionView
# = Action View Asset URL Helpers
@@ -97,8 +96,8 @@ module ActionView
# have SSL certificates for each of the asset hosts this technique allows you
# to avoid warnings in the client about mixed media.
# Note that the request parameter might not be supplied, e.g. when the assets
- # are precompiled via a Rake task. Make sure to use a Proc instead of a lambda,
- # since a Proc allows missing parameters and sets them to nil.
+ # are precompiled via a Rake task. Make sure to use a +Proc+ instead of a lambda,
+ # since a +Proc+ allows missing parameters and sets them to +nil+.
#
# config.action_controller.asset_host = Proc.new { |source, request|
# if request && request.ssl?
@@ -233,7 +232,7 @@ module ActionView
stylesheet: ".css"
}
- # Compute extname to append to asset path. Returns nil if
+ # Compute extname to append to asset path. Returns +nil+ if
# nothing should be added.
def compute_asset_extname(source, options = {})
return if options[:extname] == false
diff --git a/actionview/lib/action_view/helpers/atom_feed_helper.rb b/actionview/lib/action_view/helpers/atom_feed_helper.rb
index 09d243c46d..3538515aee 100644
--- a/actionview/lib/action_view/helpers/atom_feed_helper.rb
+++ b/actionview/lib/action_view/helpers/atom_feed_helper.rb
@@ -103,7 +103,7 @@ module ActionView
xml = options.delete(:xml) || eval("xml", block.binding)
xml.instruct!
if options[:instruct]
- options[:instruct].each do |target,attrs|
+ options[:instruct].each do |target, attrs|
if attrs.respond_to?(:keys)
xml.instruct!(target, attrs)
elsif attrs.respond_to?(:each)
@@ -113,7 +113,7 @@ module ActionView
end
feed_opts = { "xml:lang" => options[:language] || "en-US", "xmlns" => "http://www.w3.org/2005/Atom" }
- feed_opts.merge!(options).reject! { |k,v| !k.to_s.match(/^xml/) }
+ feed_opts.merge!(options).reject! { |k, v| !k.to_s.match(/^xml/) }
xml.feed(feed_opts) do
xml.id(options[:id] || "tag:#{request.host},#{options[:schema_date]}:#{request.fullpath.split(".")[0]}")
@@ -163,7 +163,7 @@ module ActionView
@xml, @view, @feed_options = xml, view, feed_options
end
- # Accepts a Date or Time object and inserts it in the proper format. If nil is passed, current time in UTC is used.
+ # Accepts a Date or Time object and inserts it in the proper format. If +nil+ is passed, current time in UTC is used.
def updated(date_or_time = nil)
@xml.updated((date_or_time || Time.now.utc).xmlschema)
end
@@ -174,7 +174,7 @@ module ActionView
#
# * <tt>:published</tt>: Time first published. Defaults to the created_at attribute on the record if one such exists.
# * <tt>:updated</tt>: Time of update. Defaults to the updated_at attribute on the record if one such exists.
- # * <tt>:url</tt>: The URL for this entry or false or nil for not having a link tag. Defaults to the polymorphic_url for the record.
+ # * <tt>:url</tt>: The URL for this entry or +false+ or +nil+ for not having a link tag. Defaults to the +polymorphic_url+ for the record.
# * <tt>:id</tt>: The ID for this entry. Defaults to "tag:#{@view.request.host},#{@feed_options[:schema_date]}:#{record.class}/#{record.id}"
# * <tt>:type</tt>: The TYPE for this entry. Defaults to "text/html".
def entry(record, options = {})
diff --git a/actionview/lib/action_view/helpers/cache_helper.rb b/actionview/lib/action_view/helpers/cache_helper.rb
index 5258a01144..7fdf0fd0e1 100644
--- a/actionview/lib/action_view/helpers/cache_helper.rb
+++ b/actionview/lib/action_view/helpers/cache_helper.rb
@@ -88,7 +88,7 @@ module ActionView
#
# === Explicit dependencies
#
- # Some times you'll have template dependencies that can't be derived at all. This is typically
+ # Sometimes you'll have template dependencies that can't be derived at all. This is typically
# the case when you have template rendering that happens in helpers. Here's an example:
#
# <%= render_sortable_todolists @project.todolists %>
@@ -215,10 +215,10 @@ module ActionView
private
- def fragment_name_with_digest(name, virtual_path) #:nodoc:
+ def fragment_name_with_digest(name, virtual_path)
virtual_path ||= @virtual_path
if virtual_path
- name = controller.url_for(name).split("://").last if name.is_a?(Hash)
+ name = controller.url_for(name).split("://").last if name.is_a?(Hash)
digest = Digestor.digest name: virtual_path, finder: lookup_context, dependencies: view_cache_dependencies
[ name, digest ]
else
@@ -226,7 +226,7 @@ module ActionView
end
end
- def fragment_for(name = {}, options = nil, &block) #:nodoc:
+ def fragment_for(name = {}, options = nil, &block)
if content = read_fragment_for(name, options)
@cache_hit = true
content
@@ -236,11 +236,11 @@ module ActionView
end
end
- def read_fragment_for(name, options) #:nodoc:
+ def read_fragment_for(name, options)
controller.read_fragment(name, options)
end
- def write_fragment_for(name, options) #:nodoc:
+ def write_fragment_for(name, options)
# VIEW TODO: Make #capture usable outside of ERB
# This dance is needed because Builder can't use capture
pos = output_buffer.length
diff --git a/actionview/lib/action_view/helpers/date_helper.rb b/actionview/lib/action_view/helpers/date_helper.rb
index 04c5fd4218..09dc6ef6bd 100644
--- a/actionview/lib/action_view/helpers/date_helper.rb
+++ b/actionview/lib/action_view/helpers/date_helper.rb
@@ -98,7 +98,7 @@ module ActionView
from_time = from_time.to_time if from_time.respond_to?(:to_time)
to_time = to_time.to_time if to_time.respond_to?(:to_time)
from_time, to_time = to_time, from_time if from_time > to_time
- distance_in_minutes = ((to_time - from_time)/60.0).round
+ distance_in_minutes = ((to_time - from_time) / 60.0).round
distance_in_seconds = (to_time - from_time).round
I18n.with_options locale: options[:locale], scope: options[:scope] do |locale|
@@ -220,7 +220,7 @@ module ActionView
# the respective locale (e.g. [:year, :month, :day] in the en locale that ships with Rails).
# * <tt>:include_blank</tt> - Include a blank option in every select field so it's possible to set empty
# dates.
- # * <tt>:default</tt> - Set a default date if the affected date isn't set or is nil.
+ # * <tt>:default</tt> - Set a default date if the affected date isn't set or is +nil+.
# * <tt>:selected</tt> - Set a date that overrides the actual value.
# * <tt>:disabled</tt> - Set to true if you want show the select fields as disabled.
# * <tt>:prompt</tt> - Set to true (for a generic prompt), a prompt string or a hash of prompt strings
@@ -267,7 +267,7 @@ module ActionView
# date_select("article", "written_on", default: 3.days.from_now)
#
# # Generates a date select that when POSTed is stored in the article variable, in the written_on attribute
- # # which is set in the form with todays date, regardless of the value in the Active Record object.
+ # # which is set in the form with today's date, regardless of the value in the Active Record object.
# date_select("article", "written_on", selected: Date.today)
#
# # Generates a date select that when POSTed is stored in the credit_card variable, in the bill_due attribute
@@ -866,7 +866,7 @@ module ActionView
end
# Returns translated month names, but also ensures that a custom month
- # name array has a leading nil element.
+ # name array has a leading +nil+ element.
def month_names
@month_names ||= begin
month_names = @options[:use_month_names] || translated_month_names
diff --git a/actionview/lib/action_view/helpers/form_helper.rb b/actionview/lib/action_view/helpers/form_helper.rb
index 124a14f1d9..26a625e4fe 100644
--- a/actionview/lib/action_view/helpers/form_helper.rb
+++ b/actionview/lib/action_view/helpers/form_helper.rb
@@ -474,6 +474,266 @@ module ActionView
end
private :apply_form_for_options!
+ # Creates a form tag based on mixing URLs, scopes, or models.
+ #
+ # # Using just a URL:
+ # <%= form_with url: posts_path do |form| %>
+ # <%= form.text_field :title %>
+ # <% end %>
+ # # =>
+ # <form action="/posts" method="post" data-remote="true">
+ # <input type="text" name="title">
+ # </form>
+ #
+ # # Adding a scope prefixes the input field names:
+ # <%= form_with scope: :post, url: posts_path do |form| %>
+ # <%= form.text_field :title %>
+ # <% end %>
+ # # =>
+ # <form action="/posts" method="post" data-remote="true">
+ # <input type="text" name="post[title]">
+ # </form>
+ #
+ # # Using a model infers both the URL and scope:
+ # <%= form_with model: Post.new do |form| %>
+ # <%= form.text_field :title %>
+ # <% end %>
+ # # =>
+ # <form action="/posts" method="post" data-remote="true">
+ # <input type="text" name="post[title]">
+ # </form>
+ #
+ # # An existing model makes an update form and fills out field values:
+ # <%= form_with model: Post.first do |form| %>
+ # <%= form.text_field :title %>
+ # <% end %>
+ # # =>
+ # <form action="/posts/1" method="post" data-remote="true">
+ # <input type="hidden" name="_method" value="patch">
+ # <input type="text" name="post[title]" value="<the title of the post>">
+ # </form>
+ #
+ # # Though the fields don't have to correspond to model attributes:
+ # <%= form_with model: Cat.new do |form| %>
+ # <%= form.text_field :cats_dont_have_gills %>
+ # <%= form.text_field :but_in_forms_they_can %>
+ # <% end %>
+ # # =>
+ # <form action="/cats" method="post" data-remote="true">
+ # <input type="text" name="cat[cats_dont_have_gills]">
+ # <input type="text" name="cat[but_in_forms_they_can]">
+ # </form>
+ #
+ # The parameters in the forms are accessible in controllers according to
+ # their name nesting. So inputs named +title+ and <tt>post[title]</tt> are
+ # accessible as <tt>params[:title]</tt> and <tt>params[:post][:title]</tt>
+ # respectively.
+ #
+ # By default +form_with+ attaches the <tt>data-remote</tt> attribute
+ # submitting the form via an XMLHTTPRequest in the background if an
+ # Unobtrusive JavaScript driver, like rails-ujs, is used. See the
+ # <tt>:local</tt> option for more.
+ #
+ # For ease of comparison the examples above left out the submit button,
+ # as well as the auto generated hidden fields that enable UTF-8 support
+ # and adds an authenticity token needed for cross site request forgery
+ # protection.
+ #
+ # ==== +form_with+ options
+ #
+ # * <tt>:url</tt> - The URL the form submits to. Akin to values passed to
+ # +url_for+ or +link_to+. For example, you may use a named route
+ # directly. When a <tt>:scope</tt> is passed without a <tt>:url</tt> the
+ # form just submits to the current URL.
+ # * <tt>:method</tt> - The method to use when submitting the form, usually
+ # either "get" or "post". If "patch", "put", "delete", or another verb
+ # is used, a hidden input named <tt>_method</tt> is added to
+ # simulate the verb over post.
+ # * <tt>:format</tt> - The format of the route the form submits to.
+ # Useful when submitting to another resource type, like <tt>:json</tt>.
+ # Skipped if a <tt>:url</tt> is passed.
+ # * <tt>:scope</tt> - The scope to prefix input field names with and
+ # thereby how the submitted parameters are grouped in controllers.
+ # * <tt>:model</tt> - A model object to infer the <tt>:url</tt> and
+ # <tt>:scope</tt> by, plus fill out input field values.
+ # So if a +title+ attribute is set to "Ahoy!" then a +title+ input
+ # field's value would be "Ahoy!".
+ # If the model is a new record a create form is generated, if an
+ # existing record, however, an update form is generated.
+ # Pass <tt>:scope</tt> or <tt>:url</tt> to override the defaults.
+ # E.g. turn <tt>params[:post]</tt> into <tt>params[:article]</tt>.
+ # * <tt>:authenticity_token</tt> - Authenticity token to use in the form.
+ # Override with a custom authenticity token or pass <tt>false</tt> to
+ # skip the authenticity token field altogether.
+ # Useful when submitting to an external resource like a payment gateway
+ # that might limit the valid fields.
+ # Remote forms may omit the embedded authenticity token by setting
+ # <tt>config.action_view.embed_authenticity_token_in_remote_forms = false</tt>.
+ # This is helpful when fragment-caching the form. Remote forms
+ # get the authenticity token from the <tt>meta</tt> tag, so embedding is
+ # unnecessary unless you support browsers without JavaScript.
+ # * <tt>:local</tt> - By default form submits are remote and unobstrusive XHRs.
+ # Disable remote submits with <tt>local: true</tt>.
+ # * <tt>:skip_enforcing_utf8</tt> - By default a hidden field named +utf8+
+ # is output to enforce UTF-8 submits. Set to true to skip the field.
+ # * <tt>:builder</tt> - Override the object used to build the form.
+ # * <tt>:id</tt> - Optional HTML id attribute.
+ # * <tt>:class</tt> - Optional HTML class attribute.
+ # * <tt>:data</tt> - Optional HTML data attributes.
+ # * <tt>:html</tt> - Other optional HTML attributes for the form tag.
+ #
+ # === Examples
+ #
+ # When not passing a block, +form_with+ just generates an opening form tag.
+ #
+ # <%= form_with(model: @post, url: super_posts_path) %>
+ # <%= form_with(model: @post, scope: :article) %>
+ # <%= form_with(model: @post, format: :json) %>
+ # <%= form_with(model: @post, authenticity_token: false) %> # Disables the token.
+ #
+ # For namespaced routes, like +admin_post_url+:
+ #
+ # <%= form_with(model: [ :admin, @post ]) do |form| %>
+ # ...
+ # <% end %>
+ #
+ # If your resource has associations defined, for example, you want to add comments
+ # to the document given that the routes are set correctly:
+ #
+ # <%= form_with(model: [ @document, Comment.new ]) do |form| %>
+ # ...
+ # <% end %>
+ #
+ # Where <tt>@document = Document.find(params[:id])</tt>.
+ #
+ # When using labels +form_with+ requires setting the id on the field being
+ # labelled:
+ #
+ # <%= form_with(model: @post) do |form| %>
+ # <%= form.label :title %>
+ # <%= form.text_field :title, id: :post_title %>
+ # <% end %>
+ #
+ # See +label+ for more on how the +for+ attribute is derived.
+ #
+ # === Mixing with other form helpers
+ #
+ # While +form_with+ uses a FormBuilder object it's possible to mix and
+ # match the stand-alone FormHelper methods and methods
+ # from FormTagHelper:
+ #
+ # <%= form_with scope: :person do |form| %>
+ # <%= form.text_field :first_name %>
+ # <%= form.text_field :last_name %>
+ #
+ # <%= text_area :person, :biography %>
+ # <%= check_box_tag "person[admin]", "1", @person.company.admin? %>
+ #
+ # <%= form.submit %>
+ # <% end %>
+ #
+ # Same goes for the methods in FormOptionHelper and DateHelper designed
+ # to work with an object as a base, like
+ # FormOptionHelper#collection_select and DateHelper#datetime_select.
+ #
+ # === Setting the method
+ #
+ # You can force the form to use the full array of HTTP verbs by setting
+ #
+ # method: (:get|:post|:patch|:put|:delete)
+ #
+ # in the options hash. If the verb is not GET or POST, which are natively
+ # supported by HTML forms, the form will be set to POST and a hidden input
+ # called _method will carry the intended verb for the server to interpret.
+ #
+ # === Setting HTML options
+ #
+ # You can set data attributes directly in a data hash, but HTML options
+ # besides id and class must be wrapped in an HTML key:
+ #
+ # <%= form_with(model: @post, data: { behavior: "autosave" }, html: { name: "go" }) do |form| %>
+ # ...
+ # <% end %>
+ #
+ # generates
+ #
+ # <form action="/posts/123" method="post" data-behavior="autosave" name="go">
+ # <input name="_method" type="hidden" value="patch" />
+ # ...
+ # </form>
+ #
+ # === Removing hidden model id's
+ #
+ # The +form_with+ method automatically includes the model id as a hidden field in the form.
+ # This is used to maintain the correlation between the form data and its associated model.
+ # Some ORM systems do not use IDs on nested models so in this case you want to be able
+ # to disable the hidden id.
+ #
+ # In the following example the Post model has many Comments stored within it in a NoSQL database,
+ # thus there is no primary key for comments.
+ #
+ # <%= form_with(model: @post) do |form| %>
+ # <%= form.fields(:comments, skip_id: true) do |fields| %>
+ # ...
+ # <% end %>
+ # <% end %>
+ #
+ # === Customized form builders
+ #
+ # You can also build forms using a customized FormBuilder class. Subclass
+ # FormBuilder and override or define some more helpers, then use your
+ # custom builder. For example, let's say you made a helper to
+ # automatically add labels to form inputs.
+ #
+ # <%= form_with model: @person, url: { action: "create" }, builder: LabellingFormBuilder do |form| %>
+ # <%= form.text_field :first_name %>
+ # <%= form.text_field :last_name %>
+ # <%= form.text_area :biography %>
+ # <%= form.check_box :admin %>
+ # <%= form.submit %>
+ # <% end %>
+ #
+ # In this case, if you use:
+ #
+ # <%= render form %>
+ #
+ # The rendered template is <tt>people/_labelling_form</tt> and the local
+ # variable referencing the form builder is called
+ # <tt>labelling_form</tt>.
+ #
+ # The custom FormBuilder class is automatically merged with the options
+ # of a nested +fields+ call, unless it's explicitly set.
+ #
+ # In many cases you will want to wrap the above in another helper, so you
+ # could do something like the following:
+ #
+ # def labelled_form_with(**options, &block)
+ # form_with(**options.merge(builder: LabellingFormBuilder), &block)
+ # end
+ def form_with(model: nil, scope: nil, url: nil, format: nil, **options)
+ options[:allow_method_names_outside_object] = true
+ options[:skip_default_ids] = true
+
+ if model
+ url ||= polymorphic_path(model, format: format)
+
+ model = model.last if model.is_a?(Array)
+ scope ||= model_name_from_record_or_class(model).param_key
+ end
+
+ if block_given?
+ builder = instantiate_builder(scope, model, options)
+ output = capture(builder, &Proc.new)
+ options[:multipart] ||= builder.multipart?
+
+ html_options = html_options_for_form_with(url, model, options)
+ form_tag_with_body(html_options, output)
+ else
+ html_options = html_options_for_form_with(url, model, options)
+ form_tag_html(html_options)
+ end
+ end
+
# Creates a scope around a specific model object like form_for, but
# doesn't create the form tags themselves. This makes fields_for suitable
# for specifying additional model objects in the same form.
@@ -720,6 +980,74 @@ module ActionView
capture(builder, &block)
end
+ # Scopes input fields with either an explicit scope or model.
+ # Like +form_with+ does with <tt>:scope</tt> or <tt>:model</tt>,
+ # except it doesn't output the form tags.
+ #
+ # # Using a scope prefixes the input field names:
+ # <%= fields :comment do |fields| %>
+ # <%= fields.text_field :body %>
+ # <% end %>
+ # # => <input type="text" name="comment[body]>
+ #
+ # # Using a model infers the scope and assigns field values:
+ # <%= fields model: Comment.new(body: "full bodied") do |fields| %<
+ # <%= fields.text_field :body %>
+ # <% end %>
+ # # =>
+ # <input type="text" name="comment[body] value="full bodied">
+ #
+ # # Using +fields+ with +form_with+:
+ # <%= form_with model: @post do |form| %>
+ # <%= form.text_field :title %>
+ #
+ # <%= form.fields :comment do |fields| %>
+ # <%= fields.text_field :body %>
+ # <% end %>
+ # <% end %>
+ #
+ # Much like +form_with+ a FormBuilder instance associated with the scope
+ # or model is yielded, so any generated field names are prefixed with
+ # either the passed scope or the scope inferred from the <tt>:model</tt>.
+ #
+ # When using labels +fields+ requires setting the id on the field being
+ # labelled:
+ #
+ # <%= fields :comment do |fields| %>
+ # <%= fields.label :body %>
+ # <%= fields.text_field :body, id: :comment_body %>
+ # <% end %>
+ #
+ # See +label+ for more on how the +for+ attribute is derived.
+ #
+ # === Mixing with other form helpers
+ #
+ # While +form_with+ uses a FormBuilder object it's possible to mix and
+ # match the stand-alone FormHelper methods and methods
+ # from FormTagHelper:
+ #
+ # <%= fields model: @comment do |fields| %>
+ # <%= fields.text_field :body %>
+ #
+ # <%= text_area :commenter, :biography %>
+ # <%= check_box_tag "comment[all_caps]", "1", @comment.commenter.hulk_mode? %>
+ # <% end %>
+ #
+ # Same goes for the methods in FormOptionHelper and DateHelper designed
+ # to work with an object as a base, like
+ # FormOptionHelper#collection_select and DateHelper#datetime_select.
+ def fields(scope = nil, model: nil, **options, &block)
+ options[:allow_method_names_outside_object] = true
+ options[:skip_default_ids] = true
+
+ if model
+ scope ||= model_name_from_record_or_class(model).param_key
+ end
+
+ builder = instantiate_builder(scope, model, options)
+ capture(builder, &block)
+ end
+
# Returns a label tag tailored for labelling an input field for a specified attribute (identified by +method+) on an object
# assigned to the template (identified by +object+). The text of label will default to the attribute name unless a translation
# is found in the current I18n locale (through helpers.label.<modelname>.<attribute>) or you specify it explicitly.
@@ -1175,6 +1503,32 @@ module ActionView
end
private
+ def html_options_for_form_with(url_for_options = nil, model = nil, html: {}, local: false,
+ skip_enforcing_utf8: false, **options)
+ html_options = options.slice(:id, :class, :multipart, :method, :data).merge(html)
+ html_options[:method] ||= :patch if model.respond_to?(:persisted?) && model.persisted?
+ html_options[:enforce_utf8] = !skip_enforcing_utf8
+
+ html_options[:enctype] = "multipart/form-data" if html_options.delete(:multipart)
+
+ # The following URL is unescaped, this is just a hash of options, and it is the
+ # responsibility of the caller to escape all the values.
+ html_options[:action] = url_for(url_for_options || {})
+ html_options[:"accept-charset"] = "UTF-8"
+ html_options[:"data-remote"] = true unless local
+
+ if !local && !embed_authenticity_token_in_remote_forms &&
+ html_options[:authenticity_token].blank?
+ # The authenticity token is taken from the meta tag in this case
+ html_options[:authenticity_token] = false
+ elsif html_options[:authenticity_token] == true
+ # Include the default authenticity_token, which is only generated when its set to nil,
+ # but we needed the true value to override the default of no authenticity_token on data-remote.
+ html_options[:authenticity_token] = nil
+ end
+
+ html_options.stringify_keys!
+ end
def instantiate_builder(record_name, record_object, options)
case record_name
@@ -1183,7 +1537,7 @@ module ActionView
object_name = record_name
else
object = record_name
- object_name = model_name_from_record_or_class(object).param_key
+ object_name = model_name_from_record_or_class(object).param_key if object
end
builder = options[:builder] || default_form_builder_class
@@ -1249,7 +1603,7 @@ module ActionView
# The methods which wrap a form helper call.
class_attribute :field_helpers
- self.field_helpers = [:fields_for, :label, :text_field, :password_field,
+ self.field_helpers = [:fields_for, :fields, :label, :text_field, :password_field,
:hidden_field, :file_field, :text_area, :check_box,
:radio_button, :color_field, :search_field,
:telephone_field, :phone_field, :date_field,
@@ -1285,7 +1639,10 @@ module ActionView
def initialize(object_name, object, template, options)
@nested_child_index = {}
@object_name, @object, @template, @options = object_name, object, template, options
- @default_options = @options ? @options.slice(:index, :namespace) : {}
+ @default_options = @options ? @options.slice(:index, :namespace, :skip_default_ids, :allow_method_names_outside_object) : {}
+
+ convert_to_legacy_options(@options)
+
if @object_name.to_s.match(/\[\]$/)
if (object ||= @template.instance_variable_get("@#{Regexp.last_match.pre_match}")) && object.respond_to?(:to_param)
@auto_index = object.to_param
@@ -1293,11 +1650,12 @@ module ActionView
raise ArgumentError, "object[] naming but object param and @object var don't exist or don't respond to to_param: #{object.inspect}"
end
end
+
@multipart = nil
@index = options[:index] || options[:child_index]
end
- (field_helpers - [:label, :check_box, :radio_button, :fields_for, :hidden_field, :file_field]).each do |selector|
+ (field_helpers - [:label, :check_box, :radio_button, :fields_for, :fields, :hidden_field, :file_field]).each do |selector|
class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
def #{selector}(method, options = {}) # def text_field(method, options = {})
@template.send( # @template.send(
@@ -1566,10 +1924,11 @@ module ActionView
record_name = model_name_from_record_or_class(record_object).param_key
end
+ object_name = @object_name
index = if options.has_key?(:index)
options[:index]
elsif defined?(@auto_index)
- self.object_name = @object_name.to_s.sub(/\[\]$/,"")
+ object_name = object_name.to_s.sub(/\[\]$/, "")
@auto_index
end
@@ -1586,6 +1945,16 @@ module ActionView
@template.fields_for(record_name, record_object, fields_options, &block)
end
+ # See the docs for the <tt>ActionView::FormHelper.fields</tt> helper method.
+ def fields(scope = nil, model: nil, **options, &block)
+ options[:allow_method_names_outside_object] = true
+ options[:skip_default_ids] = true
+
+ convert_to_legacy_options(options)
+
+ fields_for(scope || model, model, **options, &block)
+ end
+
# Returns a label tag tailored for labelling an input field for a specified attribute (identified by +method+) on an object
# assigned to the template (identified by +object+). The text of label will default to the attribute name unless a translation
# is found in the current I18n locale (through helpers.label.<modelname>.<attribute>) or you specify it explicitly.
@@ -1809,7 +2178,7 @@ module ActionView
# post:
# create: "Add %{model}"
#
- def submit(value=nil, options={})
+ def submit(value = nil, options = {})
value, options = nil, value if value.is_a?(Hash)
value ||= submit_default_value
@template.submit_tag(value, options)
@@ -1934,6 +2303,12 @@ module ActionView
@nested_child_index[name] ||= -1
@nested_child_index[name] += 1
end
+
+ def convert_to_legacy_options(options)
+ if options.key?(:skip_id)
+ options[:include_id] = !options.delete(:skip_id)
+ end
+ end
end
end
diff --git a/actionview/lib/action_view/helpers/form_tag_helper.rb b/actionview/lib/action_view/helpers/form_tag_helper.rb
index 7bd473507b..ffc52569f2 100644
--- a/actionview/lib/action_view/helpers/form_tag_helper.rb
+++ b/actionview/lib/action_view/helpers/form_tag_helper.rb
@@ -461,7 +461,7 @@ module ActionView
end
# Creates a button element that defines a <tt>submit</tt> button,
- # <tt>reset</tt>button or a generic button which can be used in
+ # <tt>reset</tt> button or a generic button which can be used in
# JavaScript, for example. You can use the button tag as a regular
# submit tag but it isn't supported in legacy browsers. However,
# the button tag does allow for richer labels such as images and emphasis,
diff --git a/actionview/lib/action_view/helpers/number_helper.rb b/actionview/lib/action_view/helpers/number_helper.rb
index 75b898c3e9..9e80f0b2ee 100644
--- a/actionview/lib/action_view/helpers/number_helper.rb
+++ b/actionview/lib/action_view/helpers/number_helper.rb
@@ -171,6 +171,9 @@ module ActionView
# to ",").
# * <tt>:separator</tt> - Sets the separator between the
# fractional and integer digits (defaults to ".").
+ # * <tt>:delimiter_pattern</tt> - Sets a custom regular expression used for
+ # deriving the placement of delimiter. Helpful when using currency formats
+ # like INR.
# * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when
# the argument is invalid.
#
@@ -187,6 +190,9 @@ module ActionView
# number_with_delimiter(98765432.98, delimiter: " ", separator: ",")
# # => 98 765 432,98
#
+ # number_with_delimiter("123456.78",
+ # delimiter_pattern: /(\d+?)(?=(\d\d)+(\d)(?!\d))/) # => "1,23,456.78"
+ #
# number_with_delimiter("112a", raise: true) # => raise InvalidNumberError
def number_with_delimiter(number, options = {})
delegate_number_helper_method(:number_to_delimited, number, options)
diff --git a/actionview/lib/action_view/helpers/output_safety_helper.rb b/actionview/lib/action_view/helpers/output_safety_helper.rb
index 8e63e59fac..9f1a890f6a 100644
--- a/actionview/lib/action_view/helpers/output_safety_helper.rb
+++ b/actionview/lib/action_view/helpers/output_safety_helper.rb
@@ -28,7 +28,7 @@ module ActionView #:nodoc:
# safe_join([raw("<p>foo</p>"), raw("<p>bar</p>")], raw("<br />")
# # => "<p>foo</p><br /><p>bar</p>"
#
- def safe_join(array, sep=$,)
+ def safe_join(array, sep = $,)
sep = ERB::Util.unwrapped_html_escape(sep)
array.flatten.map! { |i| ERB::Util.unwrapped_html_escape(i) }.join(sep).html_safe
diff --git a/actionview/lib/action_view/helpers/tag_helper.rb b/actionview/lib/action_view/helpers/tag_helper.rb
index b625c1a194..306b71c85e 100644
--- a/actionview/lib/action_view/helpers/tag_helper.rb
+++ b/actionview/lib/action_view/helpers/tag_helper.rb
@@ -138,8 +138,7 @@ module ActionView
#
# ==== Options
#
- # Any passed options become attributes on the generated tag.
- # You can only use symbols (not strings) for the attribute names.
+ # Use symbol keyed options to add attributes to the generated tag.
#
# tag.section class: %w( kitties puppies )
# # => <section class="kitties puppies"></section>
@@ -201,7 +200,7 @@ module ActionView
# hash to +options+. Set +escape+ to false to disable attribute value
# escaping.
#
- # ==== Options (Legacy syntax)
+ # ==== Options
#
# You can use symbols or strings for the attribute names.
#
@@ -211,7 +210,7 @@ module ActionView
# HTML5 <tt>data-*</tt> attributes can be set with a single +data+ key
# pointing to a hash of sub-attributes.
#
- # ==== Examples (Legacy syntax)
+ # ==== Examples
#
# tag("br")
# # => <br />
diff --git a/actionview/lib/action_view/helpers/tags/base.rb b/actionview/lib/action_view/helpers/tags/base.rb
index e3e3c8b109..74d6324771 100644
--- a/actionview/lib/action_view/helpers/tags/base.rb
+++ b/actionview/lib/action_view/helpers/tags/base.rb
@@ -11,8 +11,10 @@ module ActionView
@object_name, @method_name = object_name.to_s.dup, method_name.to_s.dup
@template_object = template_object
- @object_name.sub!(/\[\]$/,"") || @object_name.sub!(/\[\]\]$/,"]")
+ @object_name.sub!(/\[\]$/, "") || @object_name.sub!(/\[\]\]$/, "]")
@object = retrieve_object(options.delete(:object))
+ @skip_default_ids = options.delete(:skip_default_ids)
+ @allow_method_names_outside_object = options.delete(:allow_method_names_outside_object)
@options = options
@auto_index = Regexp.last_match ? retrieve_autoindex(Regexp.last_match.pre_match) : nil
end
@@ -25,7 +27,11 @@ module ActionView
private
def value(object)
- object.public_send @method_name if object
+ if @allow_method_names_outside_object
+ object.public_send @method_name if object && object.respond_to?(@method_name)
+ else
+ object.public_send @method_name if object
+ end
end
def value_before_type_cast(object)
@@ -81,15 +87,21 @@ module ActionView
def add_default_name_and_id(options)
index = name_and_id_index(options)
options["name"] = options.fetch("name") { tag_name(options["multiple"], index) }
- options["id"] = options.fetch("id") { tag_id(index) }
- if namespace = options.delete("namespace")
- options["id"] = options["id"] ? "#{namespace}_#{options['id']}" : namespace
+
+ unless skip_default_ids?
+ options["id"] = options.fetch("id") { tag_id(index) }
+ if namespace = options.delete("namespace")
+ options["id"] = options["id"] ? "#{namespace}_#{options['id']}" : namespace
+ end
end
end
def tag_name(multiple = false, index = nil)
# a little duplication to construct less strings
- if index
+ case
+ when @object_name.empty?
+ "#{sanitized_method_name}#{"[]" if multiple}"
+ when index
"#{@object_name}[#{index}][#{sanitized_method_name}]#{"[]" if multiple}"
else
"#{@object_name}[#{sanitized_method_name}]#{"[]" if multiple}"
@@ -98,7 +110,10 @@ module ActionView
def tag_id(index = nil)
# a little duplication to construct less strings
- if index
+ case
+ when @object_name.empty?
+ sanitized_method_name.dup
+ when index
"#{sanitized_object_name}_#{index}_#{sanitized_method_name}"
else
"#{sanitized_object_name}_#{sanitized_method_name}"
@@ -110,7 +125,7 @@ module ActionView
end
def sanitized_method_name
- @sanitized_method_name ||= @method_name.sub(/\?$/,"")
+ @sanitized_method_name ||= @method_name.sub(/\?$/, "")
end
def sanitized_value(value)
@@ -152,7 +167,11 @@ module ActionView
end
def name_and_id_index(options)
- options.key?("index") ? options.delete("index") || "" : @auto_index
+ options.key?("index") ? options.delete("index") || "" : @auto_index
+ end
+
+ def skip_default_ids?
+ @skip_default_ids
end
end
end
diff --git a/actionview/lib/action_view/helpers/tags/collection_check_boxes.rb b/actionview/lib/action_view/helpers/tags/collection_check_boxes.rb
index 2a6bf49567..7252d4f2d9 100644
--- a/actionview/lib/action_view/helpers/tags/collection_check_boxes.rb
+++ b/actionview/lib/action_view/helpers/tags/collection_check_boxes.rb
@@ -7,7 +7,7 @@ module ActionView
include CollectionHelpers
class CheckBoxBuilder < Builder # :nodoc:
- def check_box(extra_html_options={})
+ def check_box(extra_html_options = {})
html_options = extra_html_options.merge(@input_html_options)
html_options[:multiple] = true
@template_object.check_box(@object_name, @method_name, html_options, @value, nil)
@@ -24,7 +24,7 @@ module ActionView
builder.check_box + builder.label
end
- def hidden_field_name #:nodoc:
+ def hidden_field_name
"#{super}[]"
end
end
diff --git a/actionview/lib/action_view/helpers/tags/collection_helpers.rb b/actionview/lib/action_view/helpers/tags/collection_helpers.rb
index 36575b2fd0..75d237eb35 100644
--- a/actionview/lib/action_view/helpers/tags/collection_helpers.rb
+++ b/actionview/lib/action_view/helpers/tags/collection_helpers.rb
@@ -17,7 +17,7 @@ module ActionView
@input_html_options = input_html_options
end
- def label(label_html_options={}, &block)
+ def label(label_html_options = {}, &block)
html_options = @input_html_options.slice(:index, :namespace).merge(label_html_options)
html_options[:for] ||= @input_html_options[:id] if @input_html_options[:id]
@@ -43,7 +43,7 @@ module ActionView
# Generate default options for collection helpers, such as :checked and
# :disabled.
- def default_html_options_for_collection(item, value) #:nodoc:
+ def default_html_options_for_collection(item, value)
html_options = @html_options.dup
[:checked, :selected, :disabled, :readonly].each do |option|
@@ -67,11 +67,11 @@ module ActionView
html_options
end
- def sanitize_attribute_name(value) #:nodoc:
+ def sanitize_attribute_name(value)
"#{sanitized_method_name}_#{sanitized_value(value)}"
end
- def render_collection #:nodoc:
+ def render_collection
@collection.map do |item|
value = value_for_collection(item, @value_method)
text = value_for_collection(item, @text_method)
@@ -82,7 +82,7 @@ module ActionView
end.join.html_safe
end
- def render_collection_for(builder_class, &block) #:nodoc:
+ def render_collection_for(builder_class, &block)
options = @options.stringify_keys
rendered_collection = render_collection do |item, value, text, default_html_options|
builder = instantiate_builder(builder_class, item, value, text, default_html_options)
@@ -103,12 +103,12 @@ module ActionView
end
end
- def hidden_field #:nodoc:
+ def hidden_field
hidden_name = @html_options[:name] || hidden_field_name
@template_object.hidden_field_tag(hidden_name, "", id: nil)
end
- def hidden_field_name #:nodoc:
+ def hidden_field_name
"#{tag_name(false, @options[:index])}"
end
end
diff --git a/actionview/lib/action_view/helpers/tags/collection_radio_buttons.rb b/actionview/lib/action_view/helpers/tags/collection_radio_buttons.rb
index eed7941cd6..a5f72af9ff 100644
--- a/actionview/lib/action_view/helpers/tags/collection_radio_buttons.rb
+++ b/actionview/lib/action_view/helpers/tags/collection_radio_buttons.rb
@@ -7,7 +7,7 @@ module ActionView
include CollectionHelpers
class RadioButtonBuilder < Builder # :nodoc:
- def radio_button(extra_html_options={})
+ def radio_button(extra_html_options = {})
html_options = extra_html_options.merge(@input_html_options)
@template_object.radio_button(@object_name, @method_name, @value, html_options)
end
diff --git a/actionview/lib/action_view/helpers/tags/label.rb b/actionview/lib/action_view/helpers/tags/label.rb
index b31d5fda66..cab15ae201 100644
--- a/actionview/lib/action_view/helpers/tags/label.rb
+++ b/actionview/lib/action_view/helpers/tags/label.rb
@@ -73,6 +73,10 @@ module ActionView
def render_component(builder)
builder.translation
end
+
+ def skip_default_ids?
+ false # The id is used as the `for` attribute.
+ end
end
end
end
diff --git a/actionview/lib/action_view/helpers/tags/translator.rb b/actionview/lib/action_view/helpers/tags/translator.rb
index 62b1df81c6..ced835eaa8 100644
--- a/actionview/lib/action_view/helpers/tags/translator.rb
+++ b/actionview/lib/action_view/helpers/tags/translator.rb
@@ -14,6 +14,8 @@ module ActionView
translated_attribute || human_attribute_name
end
+ # TODO Change this to private once we've dropped Ruby 2.2 support.
+ # Workaround for Ruby 2.2 "private attribute?" warning.
protected
attr_reader :object_name, :method_and_value, :scope, :model
diff --git a/actionview/lib/action_view/helpers/text_helper.rb b/actionview/lib/action_view/helpers/text_helper.rb
index bd3371ccc8..07dccf5b41 100644
--- a/actionview/lib/action_view/helpers/text_helper.rb
+++ b/actionview/lib/action_view/helpers/text_helper.rb
@@ -151,7 +151,7 @@ module ActionView
# defined in <tt>:radius</tt> (which defaults to 100). If the excerpt radius overflows the beginning or end of the +text+,
# then the <tt>:omission</tt> option (which defaults to "...") will be prepended/appended accordingly. Use the
# <tt>:separator</tt> option to choose the delimitation. The resulting string will be stripped in any case. If the +phrase+
- # isn't found, nil is returned.
+ # isn't found, +nil+ is returned.
#
# excerpt('This is an example', 'an', radius: 5)
# # => ...s is an exam...
diff --git a/actionview/lib/action_view/helpers/translation_helper.rb b/actionview/lib/action_view/helpers/translation_helper.rb
index cbabaf5757..47ed41a129 100644
--- a/actionview/lib/action_view/helpers/translation_helper.rb
+++ b/actionview/lib/action_view/helpers/translation_helper.rb
@@ -1,6 +1,5 @@
require "action_view/helpers/tag_helper"
require "active_support/core_ext/string/access"
-require "active_support/core_ext/regexp"
require "i18n/exceptions"
module ActionView
diff --git a/actionview/lib/action_view/helpers/url_helper.rb b/actionview/lib/action_view/helpers/url_helper.rb
index dad0e9dac3..58a4a04dcb 100644
--- a/actionview/lib/action_view/helpers/url_helper.rb
+++ b/actionview/lib/action_view/helpers/url_helper.rb
@@ -2,7 +2,6 @@ require "action_view/helpers/javascript_helper"
require "active_support/core_ext/array/access"
require "active_support/core_ext/hash/keys"
require "active_support/core_ext/string/output_safety"
-require "active_support/core_ext/regexp"
module ActionView
# = Action View URL Helpers
@@ -106,10 +105,9 @@ module ActionView
# driver to prompt with the question specified (in this case, the
# resulting text would be <tt>question?</tt>. If the user accepts, the
# link is processed normally, otherwise no action is taken.
- # * <tt>:disable_with</tt> - Value of this parameter will be
- # used as the value for a disabled version of the submit
- # button when the form is submitted. This feature is provided
- # by the unobtrusive JavaScript driver.
+ # * <tt>:disable_with</tt> - Value of this parameter will be used as the
+ # name for a disabled version of the link. This feature is provided by
+ # the unobtrusive JavaScript driver.
#
# ==== Examples
# Because it relies on +url_for+, +link_to+ supports both older-style controller/action/id arguments
@@ -564,7 +562,7 @@ module ActionView
html_options = html_options.stringify_keys
html_options["data-remote"] = "true".freeze if link_to_remote_options?(options) || link_to_remote_options?(html_options)
- method = html_options.delete("method".freeze)
+ method = html_options.delete("method".freeze)
add_method_to_attributes!(html_options, method) if method
@@ -587,7 +585,7 @@ module ActionView
html_options["data-method".freeze] = method
end
- def token_tag(token=nil, form_options: {})
+ def token_tag(token = nil, form_options: {})
if token != false && protect_against_forgery?
token ||= form_authenticity_token(form_options: form_options)
tag(:input, type: "hidden", name: request_forgery_protection_token.to_s, value: token)
@@ -616,7 +614,18 @@ module ActionView
#
# to_form_params({ name: 'Denmark' }, 'country')
# # => [{name: 'country[name]', value: 'Denmark'}]
- def to_form_params(attribute, namespace = nil) # :nodoc:
+ def to_form_params(attribute, namespace = nil)
+ attribute = if attribute.respond_to?(:permitted?)
+ unless attribute.permitted?
+ raise ArgumentError, "Attempting to generate a buttom from non-sanitized request parameters!" \
+ " Whitelist and sanitize passed parameters to be secure."
+ end
+
+ attribute.to_h
+ else
+ attribute
+ end
+
params = []
case attribute
when Hash
diff --git a/actionview/lib/action_view/layouts.rb b/actionview/lib/action_view/layouts.rb
index b083de1396..18e395a67f 100644
--- a/actionview/lib/action_view/layouts.rb
+++ b/actionview/lib/action_view/layouts.rb
@@ -1,6 +1,5 @@
require "action_view/rendering"
require "active_support/core_ext/module/remove_method"
-require "active_support/core_ext/regexp"
module ActionView
# Layouts reverse the common pattern of including shared headers and footers in many templates to isolate changes in
@@ -92,16 +91,16 @@ module ActionView
# layout false
#
# In these examples, we have three implicit lookup scenarios:
- # * The BankController uses the "bank" layout.
- # * The ExchangeController uses the "exchange" layout.
- # * The CurrencyController inherits the layout from BankController.
+ # * The +BankController+ uses the "bank" layout.
+ # * The +ExchangeController+ uses the "exchange" layout.
+ # * The +CurrencyController+ inherits the layout from BankController.
#
# However, when a layout is explicitly set, the explicitly set layout wins:
- # * The InformationController uses the "information" layout, explicitly set.
- # * The TellerController also uses the "information" layout, because the parent explicitly set it.
- # * The EmployeeController uses the "employee" layout, because it set the layout to nil, resetting the parent configuration.
- # * The VaultController chooses a layout dynamically by calling the <tt>access_level_layout</tt> method.
- # * The TillController does not use a layout at all.
+ # * The +InformationController+ uses the "information" layout, explicitly set.
+ # * The +TellerController+ also uses the "information" layout, because the parent explicitly set it.
+ # * The +EmployeeController+ uses the "employee" layout, because it set the layout to +nil+, resetting the parent configuration.
+ # * The +VaultController+ chooses a layout dynamically by calling the <tt>access_level_layout</tt> method.
+ # * The +TillController+ does not use a layout at all.
#
# == Types of layouts
#
@@ -149,8 +148,8 @@ module ActionView
# The template will be looked always in <tt>app/views/layouts/</tt> folder. But you can point
# <tt>layouts</tt> folder direct also. <tt>layout "layouts/demo"</tt> is the same as <tt>layout "demo"</tt>.
#
- # Setting the layout to nil forces it to be looked up in the filesystem and fallbacks to the parent behavior if none exists.
- # Setting it to nil is useful to re-enable template lookup overriding a previous configuration set in the parent:
+ # Setting the layout to +nil+ forces it to be looked up in the filesystem and fallbacks to the parent behavior if none exists.
+ # Setting it to +nil+ is useful to re-enable template lookup overriding a previous configuration set in the parent:
#
# class ApplicationController < ActionController::Base
# layout "application"
@@ -255,7 +254,7 @@ module ActionView
# true:: raise an ArgumentError
# nil:: Force default layout behavior with inheritance
#
- # Return value of Proc & Symbol arguments should be String, false, true or nil
+ # Return value of +Proc+ and +Symbol+ arguments should be +String+, +false+, +true+ or +nil+
# with the same meaning as described above.
# ==== Parameters
# * <tt>layout</tt> - The layout to use.
@@ -339,7 +338,7 @@ module ActionView
#
# ==== Returns
# * <tt>String</tt> - A template name
- def _implied_layout_name # :nodoc:
+ def _implied_layout_name
controller_path
end
end
@@ -405,11 +404,11 @@ module ActionView
#
# ==== Parameters
# * <tt>formats</tt> - The formats accepted to this layout
- # * <tt>require_layout</tt> - If set to true and layout is not found,
- # an +ArgumentError+ exception is raised (defaults to false)
+ # * <tt>require_layout</tt> - If set to +true+ and layout is not found,
+ # an +ArgumentError+ exception is raised (defaults to +false+)
#
# ==== Returns
- # * <tt>template</tt> - The template object for the default layout (or nil)
+ # * <tt>template</tt> - The template object for the default layout (or +nil+)
def _default_layout(formats, require_layout = false)
begin
value = _layout(formats) if action_has_layout?
diff --git a/actionview/lib/action_view/log_subscriber.rb b/actionview/lib/action_view/log_subscriber.rb
index c9f308c2a2..d03e1a51b8 100644
--- a/actionview/lib/action_view/log_subscriber.rb
+++ b/actionview/lib/action_view/log_subscriber.rb
@@ -51,20 +51,20 @@ module ActionView
ActionView::Base.logger
end
- protected
+ private
EMPTY = ""
- def from_rails_root(string)
+ def from_rails_root(string) # :doc:
string = string.sub(rails_root, EMPTY)
string.sub!(VIEWS_PATTERN, EMPTY)
string
end
- def rails_root
+ def rails_root # :doc:
@root ||= "#{Rails.root}/"
end
- def render_count(payload)
+ def render_count(payload) # :doc:
if payload[:cache_hits]
"[#{payload[:cache_hits]} / #{payload[:count]} cache hits]"
else
@@ -72,7 +72,7 @@ module ActionView
end
end
- def cache_message(payload)
+ def cache_message(payload) # :doc:
if payload[:cache_hit]
"[cache hit]"
else
@@ -80,8 +80,6 @@ module ActionView
end
end
- private
-
def log_rendering_start(payload)
info do
message = " Rendering #{from_rails_root(payload[:identifier])}"
diff --git a/actionview/lib/action_view/lookup_context.rb b/actionview/lib/action_view/lookup_context.rb
index 9d6c762cc4..f385a7cd04 100644
--- a/actionview/lib/action_view/lookup_context.rb
+++ b/actionview/lib/action_view/lookup_context.rb
@@ -93,9 +93,9 @@ module ActionView
@cache = old_value
end
- protected
+ private
- def _set_detail(key, value)
+ def _set_detail(key, value) # :doc:
@details = @details.dup if @details_key
@details_key = nil
@details[key] = value
@@ -149,16 +149,16 @@ module ActionView
added_resolvers.times { view_paths.pop }
end
- protected
+ private
- def args_for_lookup(name, prefixes, partial, keys, details_options) #:nodoc:
+ def args_for_lookup(name, prefixes, partial, keys, details_options)
name, prefixes = normalize_name(name, prefixes)
details, details_key = detail_args_for(details_options)
[name, prefixes, partial || false, details, details_key, keys]
end
# Compute details hash and key according to user options (e.g. passed from #render).
- def detail_args_for(options)
+ def detail_args_for(options) # :doc:
return @details, details_key if options.empty? # most common path.
user_details = @details.merge(options)
@@ -171,13 +171,13 @@ module ActionView
[user_details, details_key]
end
- def args_for_any(name, prefixes, partial) # :nodoc:
+ def args_for_any(name, prefixes, partial)
name, prefixes = normalize_name(name, prefixes)
details, details_key = detail_args_for_any
[name, prefixes, partial || false, details, details_key]
end
- def detail_args_for_any # :nodoc:
+ def detail_args_for_any
@detail_args_for_any ||= begin
details = {}
@@ -200,11 +200,11 @@ module ActionView
# Support legacy foo.erb names even though we now ignore .erb
# as well as incorrectly putting part of the path in the template
# name instead of the prefix.
- def normalize_name(name, prefixes) #:nodoc:
+ def normalize_name(name, prefixes)
prefixes = prefixes.presence
parts = name.to_s.split("/".freeze)
parts.shift if parts.first.empty?
- name = parts.pop
+ name = parts.pop
return name, prefixes || [""] if parts.empty?
diff --git a/actionview/lib/action_view/railtie.rb b/actionview/lib/action_view/railtie.rb
index 42795ca2c7..d344d98f4b 100644
--- a/actionview/lib/action_view/railtie.rb
+++ b/actionview/lib/action_view/railtie.rb
@@ -3,7 +3,7 @@ require "rails"
module ActionView
# = Action View Railtie
- class Railtie < Rails::Railtie # :nodoc:
+ class Railtie < Rails::Engine # :nodoc:
config.action_view = ActiveSupport::OrderedOptions.new
config.action_view.embed_authenticity_token_in_remote_forms = false
config.action_view.debug_missing_translation = true
@@ -23,7 +23,7 @@ module ActionView
initializer "action_view.set_configs" do |app|
ActiveSupport.on_load(:action_view) do
- app.config.action_view.each do |k,v|
+ app.config.action_view.each do |k, v|
send "#{k}=", v
end
end
@@ -39,7 +39,7 @@ module ActionView
initializer "action_view.per_request_digest_cache" do |app|
ActiveSupport.on_load(:action_view) do
- if app.config.consider_all_requests_local
+ unless ActionView::Resolver.caching?
app.executor.to_run ActionView::Digestor::PerExecutionDigestCacheExpiry
end
end
diff --git a/actionview/lib/action_view/record_identifier.rb b/actionview/lib/action_view/record_identifier.rb
index b39acfa0b5..48bea315a9 100644
--- a/actionview/lib/action_view/record_identifier.rb
+++ b/actionview/lib/action_view/record_identifier.rb
@@ -92,7 +92,7 @@ module ActionView
end
end
- protected
+ private
# Returns a string representation of the key attribute(s) that is suitable for use in an HTML DOM id.
# This can be overwritten to customize the default generated string representation if desired.
@@ -102,7 +102,7 @@ module ActionView
# overwritten version of the method. By default, this implementation passes the key string through a
# method that replaces all characters that are invalid inside DOM ids, with valid ones. You need to
# make sure yourself that your dom ids are valid, in case you overwrite this method.
- def record_key_for_dom_id(record)
+ def record_key_for_dom_id(record) # :doc:
key = convert_to_model(record).to_key
key ? key.join(JOIN) : key
end
diff --git a/actionview/lib/action_view/renderer/abstract_renderer.rb b/actionview/lib/action_view/renderer/abstract_renderer.rb
index 3c85be49cd..0b315eb569 100644
--- a/actionview/lib/action_view/renderer/abstract_renderer.rb
+++ b/actionview/lib/action_view/renderer/abstract_renderer.rb
@@ -25,9 +25,9 @@ module ActionView
raise NotImplementedError
end
- protected
+ private
- def extract_details(options)
+ def extract_details(options) # :doc:
@lookup_context.registered_details.each_with_object({}) do |key, details|
value = options[key]
@@ -35,7 +35,7 @@ module ActionView
end
end
- def instrument(name, **options)
+ def instrument(name, **options) # :doc:
options[:identifier] ||= (@template && @template.identifier) || @path
ActiveSupport::Notifications.instrument("render_#{name}.action_view", options) do |payload|
@@ -43,7 +43,7 @@ module ActionView
end
end
- def prepend_formats(formats)
+ def prepend_formats(formats) # :doc:
formats = Array(formats)
return if formats.empty? || @lookup_context.html_fallback_for_js
diff --git a/actionview/lib/action_view/renderer/partial_renderer.rb b/actionview/lib/action_view/renderer/partial_renderer.rb
index dfe38c488f..b8a79da97f 100644
--- a/actionview/lib/action_view/renderer/partial_renderer.rb
+++ b/actionview/lib/action_view/renderer/partial_renderer.rb
@@ -1,5 +1,4 @@
require "concurrent/map"
-require "active_support/core_ext/regexp"
require "action_view/renderer/partial_renderer/collection_caching"
module ActionView
@@ -99,7 +98,7 @@ module ActionView
#
# <%= render partial: "ad", collection: @advertisements, spacer_template: "ad_divider" %>
#
- # If the given <tt>:collection</tt> is nil or empty, <tt>render</tt> will return nil. This will allow you
+ # If the given <tt>:collection</tt> is +nil+ or empty, <tt>render</tt> will return nil. This will allow you
# to specify a text which will displayed instead by using this form:
#
# <%= render(partial: "ad", collection: @advertisements) || "There's no ad to be displayed" %>
@@ -358,7 +357,7 @@ module ActionView
# set to that string. Otherwise, the +options[:partial]+ object must
# respond to +to_partial_path+ in order to setup the path.
def setup(context, options, block)
- @view = context
+ @view = context
@options = options
@block = block
diff --git a/actionview/lib/action_view/renderer/streaming_template_renderer.rb b/actionview/lib/action_view/renderer/streaming_template_renderer.rb
index 2434250b2d..7ede034492 100644
--- a/actionview/lib/action_view/renderer/streaming_template_renderer.rb
+++ b/actionview/lib/action_view/renderer/streaming_template_renderer.rb
@@ -29,7 +29,7 @@ module ActionView
# This is the same logging logic as in ShowExceptions middleware.
# TODO Once "exceptron" is in, refactor this piece to simply re-use exceptron.
- def log_error(exception) #:nodoc:
+ def log_error(exception)
logger = ActionView::Base.logger
return unless logger
diff --git a/actionview/lib/action_view/renderer/template_renderer.rb b/actionview/lib/action_view/renderer/template_renderer.rb
index f40bf8f6e2..54317199de 100644
--- a/actionview/lib/action_view/renderer/template_renderer.rb
+++ b/actionview/lib/action_view/renderer/template_renderer.rb
@@ -44,7 +44,7 @@ module ActionView
# Renders the given template. A string representing the layout can be
# supplied as well.
- def render_template(template, layout_name = nil, locals = nil) #:nodoc:
+ def render_template(template, layout_name = nil, locals = nil)
view, locals = @view, locals || {}
render_with_layout(layout_name, locals) do |layout|
@@ -54,7 +54,7 @@ module ActionView
end
end
- def render_with_layout(path, locals) #:nodoc:
+ def render_with_layout(path, locals)
layout = path && find_layout(path, locals.keys, [formats.first])
content = yield(layout)
diff --git a/actionview/lib/action_view/rendering.rb b/actionview/lib/action_view/rendering.rb
index 3ca7f9d220..0e72316eb7 100644
--- a/actionview/lib/action_view/rendering.rb
+++ b/actionview/lib/action_view/rendering.rb
@@ -91,7 +91,7 @@ module ActionView
# Find and render a template based on the options given.
# :api: private
- def _render_template(options) #:nodoc:
+ def _render_template(options)
variant = options.delete(:variant)
assigns = options.delete(:assigns)
context = view_context
@@ -104,7 +104,7 @@ module ActionView
end
# Assign the rendered format to look up context.
- def _process_format(format) #:nodoc:
+ def _process_format(format)
super
lookup_context.formats = [format.to_sym]
lookup_context.rendered_format = lookup_context.formats.first
@@ -113,7 +113,7 @@ module ActionView
# Normalize args by converting render "foo" to render :action => "foo" and
# render "foo/bar" to render :template => "foo/bar".
# :api: private
- def _normalize_args(action=nil, options={})
+ def _normalize_args(action = nil, options = {})
options = super(action, options)
case action
when NilClass
diff --git a/actionview/lib/action_view/routing_url_for.rb b/actionview/lib/action_view/routing_url_for.rb
index 669cffab1a..687ba7c1b4 100644
--- a/actionview/lib/action_view/routing_url_for.rb
+++ b/actionview/lib/action_view/routing_url_for.rb
@@ -122,18 +122,15 @@ module ActionView
controller.url_options
end
- def _routes_context #:nodoc:
- controller
- end
- protected :_routes_context
-
- def optimize_routes_generation? #:nodoc:
- controller.respond_to?(:optimize_routes_generation?, true) ?
- controller.optimize_routes_generation? : super
- end
- protected :optimize_routes_generation?
-
private
+ def _routes_context
+ controller
+ end
+
+ def optimize_routes_generation?
+ controller.respond_to?(:optimize_routes_generation?, true) ?
+ controller.optimize_routes_generation? : super
+ end
def _generate_paths_by_default
true
diff --git a/actionview/lib/action_view/template.rb b/actionview/lib/action_view/template.rb
index c01dd1c028..4b793c3b16 100644
--- a/actionview/lib/action_view/template.rb
+++ b/actionview/lib/action_view/template.rb
@@ -1,6 +1,5 @@
require "active_support/core_ext/object/try"
require "active_support/core_ext/kernel/singleton_class"
-require "active_support/core_ext/module/delegation"
require "thread"
module ActionView
@@ -141,7 +140,7 @@ module ActionView
end
# Returns whether the underlying handler supports streaming. If so,
- # a streaming buffer *may* be passed when it start rendering.
+ # a streaming buffer *may* be passed when it starts rendering.
def supports_streaming?
handler.respond_to?(:supports_streaming?) && handler.supports_streaming?
end
@@ -152,7 +151,7 @@ module ActionView
# This method is instrumented as "!render_template.action_view". Notice that
# we use a bang in this instrumentation because you don't want to
# consume this in production. This is only slow if it's being listened to.
- def render(view, locals, buffer=nil, &block)
+ def render(view, locals, buffer = nil, &block)
instrument_render_template do
compile!(view)
view.send(method_name, locals, buffer, &block)
@@ -232,11 +231,11 @@ module ActionView
end
end
- protected
+ private
# Compile a template. This method ensures a template is compiled
# just once and removes the source after it is compiled.
- def compile!(view) #:nodoc:
+ def compile!(view)
return if @compiled
# Templates can be used concurrently in threaded environments
@@ -277,9 +276,8 @@ module ActionView
# encode the source into <tt>Encoding.default_internal</tt>.
# In general, this means that templates will be UTF-8 inside of Rails,
# regardless of the original source encoding.
- def compile(mod) #:nodoc:
+ def compile(mod)
encode!
- method_name = self.method_name
code = @handler.call(self)
# Make sure that the resulting String to be eval'd is in the
@@ -310,7 +308,7 @@ module ActionView
ObjectSpace.define_finalizer(self, Finalizer[method_name, mod])
end
- def handle_render_error(view, e) #:nodoc:
+ def handle_render_error(view, e)
if e.is_a?(Template::Error)
e.sub_template_of(self)
raise e
@@ -324,17 +322,17 @@ module ActionView
end
end
- def locals_code #:nodoc:
+ def locals_code
# Only locals with valid variable names get set directly. Others will
# still be available in local_assigns.
- locals = @locals.to_set - Module::DELEGATION_RESERVED_METHOD_NAMES
+ locals = @locals - Module::RUBY_RESERVED_KEYWORDS
locals = locals.grep(/\A(?![A-Z0-9])(?:[[:alnum:]_]|[^\0-\177])+\z/)
# Double assign to suppress the dreaded 'assigned but unused variable' warning
locals.each_with_object("") { |key, code| code << "#{key} = #{key} = local_assigns[:#{key}];" }
end
- def method_name #:nodoc:
+ def method_name
@method_name ||= begin
m = "_#{identifier_method_name}__#{@identifier.hash}_#{__id__}"
m.tr!("-".freeze, "_".freeze)
@@ -342,16 +340,14 @@ module ActionView
end
end
- def identifier_method_name #:nodoc:
+ def identifier_method_name
inspect.tr("^a-z_".freeze, "_".freeze)
end
- def instrument(action, &block)
+ def instrument(action, &block) # :doc:
ActiveSupport::Notifications.instrument("#{action}.action_view".freeze, instrument_payload, &block)
end
- private
-
def instrument_render_template(&block)
ActiveSupport::Notifications.instrument("!render_template.action_view".freeze, instrument_payload, &block)
end
diff --git a/actionview/lib/action_view/template/error.rb b/actionview/lib/action_view/template/error.rb
index 4010677477..cc90477190 100644
--- a/actionview/lib/action_view/template/error.rb
+++ b/actionview/lib/action_view/template/error.rb
@@ -1,5 +1,4 @@
require "active_support/core_ext/enumerable"
-require "active_support/core_ext/regexp"
module ActionView
# = Action View Errors
diff --git a/actionview/lib/action_view/template/handlers/builder.rb b/actionview/lib/action_view/template/handlers/builder.rb
index e08a5b5db8..494b543152 100644
--- a/actionview/lib/action_view/template/handlers/builder.rb
+++ b/actionview/lib/action_view/template/handlers/builder.rb
@@ -13,9 +13,9 @@ module ActionView
";xml.target!;"
end
- protected
+ private
- def require_engine
+ def require_engine # :doc:
@required ||= begin
require "builder"
true
diff --git a/actionview/lib/action_view/template/handlers/erb.rb b/actionview/lib/action_view/template/handlers/erb.rb
index 6f07de1813..5d047a6991 100644
--- a/actionview/lib/action_view/template/handlers/erb.rb
+++ b/actionview/lib/action_view/template/handlers/erb.rb
@@ -1,5 +1,4 @@
require "erubis"
-require "active_support/core_ext/regexp"
module ActionView
class Template
diff --git a/actionview/lib/action_view/template/resolver.rb b/actionview/lib/action_view/template/resolver.rb
index 5a2948d5a9..9da13663d7 100644
--- a/actionview/lib/action_view/template/resolver.rb
+++ b/actionview/lib/action_view/template/resolver.rb
@@ -141,13 +141,13 @@ module ActionView
end
# Normalizes the arguments and passes it on to find_templates.
- def find_all(name, prefix=nil, partial=false, details={}, key=nil, locals=[])
+ def find_all(name, prefix = nil, partial = false, details = {}, key = nil, locals = [])
cached(key, [name, prefix, partial], details, locals) do
find_templates(name, prefix, partial, details)
end
end
- def find_all_anywhere(name, prefix, partial=false, details={}, key=nil, locals=[])
+ def find_all_anywhere(name, prefix, partial = false, details = {}, key = nil, locals = [])
cached(key, [name, prefix, partial], details, locals) do
find_templates(name, prefix, partial, details, true)
end
@@ -177,7 +177,7 @@ module ActionView
# always check the cache before hitting the resolver. Otherwise,
# it always hits the resolver but if the key is present, check if the
# resolver is fresher before returning it.
- def cached(key, path_info, details, locals) #:nodoc:
+ def cached(key, path_info, details, locals)
name, prefix, partial = path_info
locals = locals.map(&:to_s).sort!
@@ -191,7 +191,7 @@ module ActionView
end
# Ensures all the resolver information is set in the template.
- def decorate(templates, path_info, details, locals) #:nodoc:
+ def decorate(templates, path_info, details, locals)
cached = nil
templates.each do |t|
t.locals = locals
@@ -207,7 +207,7 @@ module ActionView
EXTENSIONS = { locale: ".", formats: ".", variants: "+", handlers: "." }
DEFAULT_PATTERN = ":prefix/:action{.:locale,}{.:formats,}{+:variants,}{.:handlers,}"
- def initialize(pattern=nil)
+ def initialize(pattern = nil)
@pattern = pattern || DEFAULT_PATTERN
super()
end
@@ -297,7 +297,7 @@ module ActionView
handler = Template.handler_for_extension(extension)
format, variant = pieces.last.split(EXTENSIONS[:variants], 2) if pieces.last
- format &&= Template::Types[format]
+ format &&= Template::Types[format]
[handler, format, variant]
end
@@ -342,7 +342,7 @@ module ActionView
# * <tt>:handlers</tt> - possible handlers (for example erb, haml, builder...)
#
class FileSystemResolver < PathResolver
- def initialize(path, pattern=nil)
+ def initialize(path, pattern = nil)
raise ArgumentError, "path already is a Resolver class" if path.is_a?(Resolver)
super(pattern)
@path = File.expand_path(path)
diff --git a/actionview/lib/action_view/template/text.rb b/actionview/lib/action_view/template/text.rb
index e8d4e18f04..380528d6ef 100644
--- a/actionview/lib/action_view/template/text.rb
+++ b/actionview/lib/action_view/template/text.rb
@@ -4,10 +4,9 @@ module ActionView #:nodoc:
class Text #:nodoc:
attr_accessor :type
- def initialize(string, type = nil)
+ def initialize(string)
@string = string.to_s
- @type = Types[type] || type if type
- @type ||= Types[:text]
+ @type = Types[:text]
end
def identifier
@@ -25,7 +24,7 @@ module ActionView #:nodoc:
end
def formats
- [@type.respond_to?(:ref) ? @type.ref : @type.to_s]
+ [@type.ref]
end
end
end
diff --git a/actionview/lib/action_view/test_case.rb b/actionview/lib/action_view/test_case.rb
index 3eb1ac0826..5fb7bb54b5 100644
--- a/actionview/lib/action_view/test_case.rb
+++ b/actionview/lib/action_view/test_case.rb
@@ -18,7 +18,7 @@ module ActionView
end
def controller_path=(path)
- self.class.controller_path=(path)
+ self.class.controller_path = (path)
end
def initialize
@@ -270,8 +270,8 @@ module ActionView
end
if routes &&
- ( routes.named_routes.route_defined?(selector) ||
- routes.mounted_helpers.method_defined?(selector) )
+ (routes.named_routes.route_defined?(selector) ||
+ routes.mounted_helpers.method_defined?(selector))
@controller.__send__(selector, *args)
else
super
diff --git a/actionview/lib/action_view/testing/resolvers.rb b/actionview/lib/action_view/testing/resolvers.rb
index 5cb9f66529..f4a7a9138c 100644
--- a/actionview/lib/action_view/testing/resolvers.rb
+++ b/actionview/lib/action_view/testing/resolvers.rb
@@ -1,4 +1,3 @@
-require "active_support/core_ext/regexp"
require "action_view/template/resolver"
module ActionView #:nodoc:
@@ -9,7 +8,7 @@ module ActionView #:nodoc:
class FixtureResolver < PathResolver
attr_reader :hash
- def initialize(hash = {}, pattern=nil)
+ def initialize(hash = {}, pattern = nil)
super(pattern)
@hash = hash
end
diff --git a/actionview/lib/action_view/view_paths.rb b/actionview/lib/action_view/view_paths.rb
index b5cde5b43f..a9638d1e6d 100644
--- a/actionview/lib/action_view/view_paths.rb
+++ b/actionview/lib/action_view/view_paths.rb
@@ -46,10 +46,22 @@ module ActionView
{}
end
+ # Append a path to the list of view paths for the current <tt>LookupContext</tt>.
+ #
+ # ==== Parameters
+ # * <tt>path</tt> - If a String is provided, it gets converted into
+ # the default view path. You may also provide a custom view path
+ # (see ActionView::PathSet for more information)
def append_view_path(path)
lookup_context.view_paths.push(*path)
end
+ # Prepend a path to the list of view paths for the current <tt>LookupContext</tt>.
+ #
+ # ==== Parameters
+ # * <tt>path</tt> - If a String is provided, it gets converted into
+ # the default view path. You may also provide a custom view path
+ # (see ActionView::PathSet for more information)
def prepend_view_path(path)
lookup_context.view_paths.unshift(*path)
end
diff --git a/actionview/package.json b/actionview/package.json
new file mode 100644
index 0000000000..ec3306c299
--- /dev/null
+++ b/actionview/package.json
@@ -0,0 +1,36 @@
+{
+ "name": "rails-ujs",
+ "version": "0.0.1",
+ "description": "Ruby on Rails unobtrusive scripting adapter",
+ "main": "lib/assets/compiled/rails-ujs.js",
+ "files": [
+ "lib/assets/compiled/*.js"
+ ],
+ "directories": {
+ "test": "test"
+ },
+ "scripts": {
+ "build": "bundle exec blade build",
+ "test": "echo \"See the README: https://github.com/rails/rails-ujs#how-to-run-tests\" && exit 1",
+ "lint": "coffeelint src && eslint test/public/test",
+ },
+ "repository": {
+ "type": "git",
+ "url": "rails/rails"
+ },
+ "contributors": [
+ "Stephen St. Martin",
+ "Steve Schwartz",
+ "Dangyi Liu",
+ "All contributors"
+ ],
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/rails/rails/issues"
+ },
+ "homepage": "http://rubyonrails.org/",
+ "devDependencies": {
+ "coffeelint": "^1.15.7",
+ "eslint": "^2.13.1"
+ }
+}
diff --git a/actionview/test/abstract_unit.rb b/actionview/test/abstract_unit.rb
index 88c7189d22..5bc4151087 100644
--- a/actionview/test/abstract_unit.rb
+++ b/actionview/test/abstract_unit.rb
@@ -163,7 +163,7 @@ class ActionDispatch::IntegrationTest < ActiveSupport::TestCase
# Stub Rails dispatcher so it does not get controller references and
# simply return the controller#action as Rack::Body.
class StubDispatcher < ::ActionDispatch::Routing::RouteSet::Dispatcher
- protected
+ private
def controller_reference(controller_param)
controller_param
end
diff --git a/actionview/test/actionpack/abstract/helper_test.rb b/actionview/test/actionpack/abstract/helper_test.rb
index 5a2f0839e5..83237518d7 100644
--- a/actionview/test/actionpack/abstract/helper_test.rb
+++ b/actionview/test/actionpack/abstract/helper_test.rb
@@ -109,7 +109,7 @@ module AbstractController
class InvalidHelpersTest < ActiveSupport::TestCase
def test_controller_raise_error_about_real_require_problem
e = assert_raise(LoadError) { AbstractInvalidHelpers.helper(:invalid_require) }
- assert_equal "No such file to load -- very_invalid_file_name", e.message
+ assert_equal "No such file to load -- very_invalid_file_name.rb", e.message
end
def test_controller_raise_error_about_missing_helper
diff --git a/actionview/test/actionpack/controller/layout_test.rb b/actionview/test/actionpack/controller/layout_test.rb
index 00147d31f3..b79835ff34 100644
--- a/actionview/test/actionpack/controller/layout_test.rb
+++ b/actionview/test/actionpack/controller/layout_test.rb
@@ -1,6 +1,5 @@
require "abstract_unit"
require "active_support/core_ext/array/extract_options"
-require "active_support/core_ext/regexp"
# The view_paths array must be set on Base and not LayoutTest so that LayoutTest's inherited
# method has access to the view_paths array when looking for a layout to automatically assign.
@@ -253,7 +252,7 @@ class LayoutStatusIsRenderedTest < ActionController::TestCase
end
end
-unless /mswin|mingw/.match?(RbConfig::CONFIG["host_os"])
+unless Gem.win_platform?
class LayoutSymlinkedTest < LayoutTest
layout "symlinked/symlinked_layout"
end
diff --git a/actionview/test/actionpack/controller/render_test.rb b/actionview/test/actionpack/controller/render_test.rb
index cd89dceb45..b4a757d6b8 100644
--- a/actionview/test/actionpack/controller/render_test.rb
+++ b/actionview/test/actionpack/controller/render_test.rb
@@ -11,7 +11,7 @@ class Customer < Struct.new(:name, :id)
undef_method :to_json
- def to_xml(options={})
+ def to_xml(options = {})
if options[:builder]
options[:builder].name name
else
@@ -19,7 +19,7 @@ class Customer < Struct.new(:name, :id)
end
end
- def to_js(options={})
+ def to_js(options = {})
"name: #{name.inspect}"
end
alias :to_text :to_js
diff --git a/actionview/test/activerecord/form_helper_activerecord_test.rb b/actionview/test/activerecord/form_helper_activerecord_test.rb
index 6152ec4720..0f7960b408 100644
--- a/actionview/test/activerecord/form_helper_activerecord_test.rb
+++ b/actionview/test/activerecord/form_helper_activerecord_test.rb
@@ -52,7 +52,7 @@ class FormHelperActiveRecordTest < ActionView::TestCase
assert_dom_equal expected, output_buffer
end
- protected
+ private
def hidden_fields(method = nil)
txt = %{<input name="utf8" type="hidden" value="&#x2713;" />}
diff --git a/actionview/test/activerecord/relation_cache_test.rb b/actionview/test/activerecord/relation_cache_test.rb
index 880e80a8dc..43f7242ee9 100644
--- a/actionview/test/activerecord/relation_cache_test.rb
+++ b/actionview/test/activerecord/relation_cache_test.rb
@@ -1,6 +1,6 @@
require "active_record_unit"
-class RelationCacheTest < ActionView::TestCase
+class RelationCacheTest < ActionView::TestCase
tests ActionView::Helpers::CacheHelper
def setup
diff --git a/actionview/test/fixtures/test/test_template_with_delegation_reserved_keywords.erb b/actionview/test/fixtures/test/test_template_with_delegation_reserved_keywords.erb
new file mode 100644
index 0000000000..edfe52e422
--- /dev/null
+++ b/actionview/test/fixtures/test/test_template_with_delegation_reserved_keywords.erb
@@ -0,0 +1 @@
+<%= _ %> <%= arg %> <%= args %> <%= block %> \ No newline at end of file
diff --git a/actionview/test/lib/controller/fake_models.rb b/actionview/test/lib/controller/fake_models.rb
index cc5f5c1d59..80649db88b 100644
--- a/actionview/test/lib/controller/fake_models.rb
+++ b/actionview/test/lib/controller/fake_models.rb
@@ -6,7 +6,7 @@ class Customer < Struct.new(:name, :id)
undef_method :to_json
- def to_xml(options={})
+ def to_xml(options = {})
if options[:builder]
options[:builder].name name
else
@@ -14,7 +14,7 @@ class Customer < Struct.new(:name, :id)
end
end
- def to_js(options={})
+ def to_js(options = {})
"name: #{name.inspect}"
end
alias :to_text :to_js
diff --git a/actionview/test/template/asset_tag_helper_test.rb b/actionview/test/template/asset_tag_helper_test.rb
index 3bdab42f7a..07a6452cc1 100644
--- a/actionview/test/template/asset_tag_helper_test.rb
+++ b/actionview/test/template/asset_tag_helper_test.rb
@@ -630,7 +630,7 @@ class AssetTagHelperNonVhostTest < ActionView::TestCase
end
def test_should_return_nothing_if_asset_host_isnt_configured
- assert_equal nil, compute_asset_host("foo")
+ assert_nil compute_asset_host("foo")
end
def test_should_current_request_host_is_always_returned_for_request
diff --git a/actionview/test/template/capture_helper_test.rb b/actionview/test/template/capture_helper_test.rb
index 54bf9b4c33..7f37523eeb 100644
--- a/actionview/test/template/capture_helper_test.rb
+++ b/actionview/test/template/capture_helper_test.rb
@@ -127,18 +127,18 @@ class CaptureHelperTest < ActionView::TestCase
def test_content_for_returns_nil_when_writing
assert ! content_for?(:title)
- assert_equal nil, content_for(:title, "foo")
- assert_equal nil, content_for(:title) { output_buffer << "bar"; nil }
- assert_equal nil, content_for(:title) { output_buffer << " \n "; nil }
+ assert_nil content_for(:title, "foo")
+ assert_nil content_for(:title) { output_buffer << "bar"; nil }
+ assert_nil content_for(:title) { output_buffer << " \n "; nil }
assert_equal "foobar", content_for(:title)
- assert_equal nil, content_for(:title, "foo", flush: true)
- assert_equal nil, content_for(:title, flush: true) { output_buffer << "bar"; nil }
- assert_equal nil, content_for(:title, flush: true) { output_buffer << " \n "; nil }
+ assert_nil content_for(:title, "foo", flush: true)
+ assert_nil content_for(:title, flush: true) { output_buffer << "bar"; nil }
+ assert_nil content_for(:title, flush: true) { output_buffer << " \n "; nil }
assert_equal "bar", content_for(:title)
end
def test_content_for_returns_nil_when_content_missing
- assert_equal nil, content_for(:some_missing_key)
+ assert_nil content_for(:some_missing_key)
end
def test_content_for_question_mark
diff --git a/actionview/test/template/compiled_templates_test.rb b/actionview/test/template/compiled_templates_test.rb
index 3ecac46d34..40ac867b38 100644
--- a/actionview/test/template/compiled_templates_test.rb
+++ b/actionview/test/template/compiled_templates_test.rb
@@ -24,6 +24,16 @@ class CompiledTemplatesTest < ActiveSupport::TestCase
assert_equal locals.inspect, render(file: "test/render_file_inspect_local_assigns", locals: locals)
end
+ def test_template_with_delegation_reserved_keywords
+ locals = {
+ _: "one",
+ arg: "two",
+ args: "three",
+ block: "four",
+ }
+ assert_equal "one two three four", render(file: "test/test_template_with_delegation_reserved_keywords", locals: locals)
+ end
+
def test_template_with_unicode_identifier
assert_equal "🎂", render(file: "test/render_file_unicode_local", locals: { 🎃: "🎂" })
end
diff --git a/actionview/test/template/date_helper_test.rb b/actionview/test/template/date_helper_test.rb
index 44e5a8c346..a84ac18bee 100644
--- a/actionview/test/template/date_helper_test.rb
+++ b/actionview/test/template/date_helper_test.rb
@@ -18,7 +18,7 @@ class DateHelperTest < ActionView::TestCase
end
end
- def assert_distance_of_time_in_words(from, to=nil)
+ def assert_distance_of_time_in_words(from, to = nil)
to ||= from
# 0..1 minute with :include_seconds => true
@@ -167,9 +167,9 @@ class DateHelperTest < ActionView::TestCase
def test_distance_in_words_with_integers
assert_equal "1 minute", distance_of_time_in_words(59)
- assert_equal "about 1 hour", distance_of_time_in_words(60*60)
+ assert_equal "about 1 hour", distance_of_time_in_words(60 * 60)
assert_equal "1 minute", distance_of_time_in_words(0, 59)
- assert_equal "about 1 hour", distance_of_time_in_words(60*60, 0)
+ assert_equal "about 1 hour", distance_of_time_in_words(60 * 60, 0)
assert_equal "about 3 years", distance_of_time_in_words(10**8)
assert_equal "about 3 years", distance_of_time_in_words(0, 10**8)
end
@@ -385,7 +385,7 @@ class DateHelperTest < ActionView::TestCase
month_names = %w(Januar Februar Marts April Maj Juni Juli August September Oktober November December)
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) }
+ 1.upto(12) { |month| expected << %(<option value="#{month}"#{' selected="selected"' if month == 8}>#{month_names[month - 1]}</option>\n) }
expected << "</select>\n"
assert_dom_equal expected, select_month(Time.mktime(2003, 8, 16), use_month_names: month_names)
@@ -848,7 +848,7 @@ class DateHelperTest < ActionView::TestCase
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"
- expected << %(<select id="date_first_year" name="date[first][year]">\n)
+ 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"
@@ -881,8 +881,8 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_date_with_no_start_year
- expected = %(<select id="date_first_year" name="date[first][year]">\n)
- (Date.today.year-5).upto(Date.today.year+1) do |y|
+ 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)
else
@@ -900,12 +900,12 @@ class DateHelperTest < ActionView::TestCase
expected << "</select>\n"
assert_dom_equal expected, select_date(
- Time.mktime(Date.today.year, 8, 16), end_year: Date.today.year+1, prefix: "date[first]"
+ Time.mktime(Date.today.year, 8, 16), end_year: Date.today.year + 1, prefix: "date[first]"
)
end
def test_select_date_with_no_end_year
- expected = %(<select id="date_first_year" name="date[first][year]">\n)
+ 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)
@@ -929,8 +929,8 @@ 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)
- (Date.today.year-5).upto(Date.today.year+5) do |y|
+ 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)
else
@@ -969,8 +969,8 @@ 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)
- (Date.today.year-5).upto(Date.today.year+1) { |y| expected << %(<option value="#{y}">#{y}</option>\n) }
+ 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"
expected << %(<select id="date_first_month" name="date[first][month]">\n)
@@ -981,7 +981,7 @@ class DateHelperTest < ActionView::TestCase
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">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"
- assert_dom_equal expected, select_date(0, end_year: Date.today.year+1, prefix: "date[first]")
+ assert_dom_equal expected, select_date(0, end_year: Date.today.year + 1, prefix: "date[first]")
end
def test_select_date_with_zero_value_and_no_end_year
@@ -1002,8 +1002,8 @@ 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)
- (Date.today.year-5).upto(Date.today.year+5) { |y| expected << %(<option value="#{y}">#{y}</option>\n) }
+ 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"
expected << %(<select id="date_first_month" name="date[first][month]">\n)
@@ -1018,8 +1018,8 @@ 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)
- (Date.today.year-5).upto(Date.today.year+5) { |y| expected << %(<option value="#{y}">#{y}</option>\n) }
+ 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"
expected << %(<select id="date_first_month" name="date[first][month]">\n)
@@ -1286,8 +1286,8 @@ 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)
- (Date.today.year-5).upto(Date.today.year+5) { |y| expected << %(<option value="#{y}">#{y}</option>\n) }
+ 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"
expected << %(<select id="date_first_month" name="date[first][month]">\n)
@@ -1709,7 +1709,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)
+ 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)
@@ -1733,7 +1733,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)
+ 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)
@@ -1851,7 +1851,7 @@ class DateHelperTest < ActionView::TestCase
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 << %{<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}
expected << "</select>\n"
@@ -1882,7 +1882,7 @@ class DateHelperTest < ActionView::TestCase
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 << %{<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}
expected << "</select>\n"
@@ -1901,7 +1901,7 @@ class DateHelperTest < ActionView::TestCase
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 << %{<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}
expected << "</select>\n"
@@ -2003,7 +2003,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}
+ 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"
@@ -2011,7 +2011,7 @@ class DateHelperTest < ActionView::TestCase
1.upto(12) { |i| expected << %(<option value="#{i}"#{' selected="selected"' if i == 6}>#{Date::MONTHNAMES[i]}</option>\n) }
expected << "</select>\n"
- expected << %{<select id="post_written_on_1i" name="post[written_on(1i)]">\n}
+ expected << %{<select id="post_written_on_1i" name="post[written_on(1i)]">\n}
1999.upto(2009) { |i| expected << %(<option value="#{i}"#{' selected="selected"' if i == 2004}>#{i}</option>\n) }
expected << "</select>\n"
@@ -2021,8 +2021,8 @@ class DateHelperTest < ActionView::TestCase
def test_date_select_with_nil
@post = Post.new
- start_year = Time.now.year-5
- end_year = Time.now.year+5
+ 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}
start_year.upto(end_year) { |i| expected << %(<option value="#{i}"#{' selected="selected"' if i == Time.now.year}>#{i}</option>\n) }
expected << "</select>\n"
@@ -2041,8 +2041,8 @@ class DateHelperTest < ActionView::TestCase
def test_date_select_with_nil_and_blank
@post = Post.new
- start_year = Time.now.year-5
- end_year = Time.now.year+5
+ 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}
expected << "<option value=\"\"></option>\n"
start_year.upto(end_year) { |i| expected << %(<option value="#{i}">#{i}</option>\n) }
@@ -2064,11 +2064,11 @@ class DateHelperTest < ActionView::TestCase
def test_date_select_with_nil_and_blank_and_order
@post = Post.new
- start_year = Time.now.year-5
- end_year = Time.now.year+5
+ start_year = Time.now.year - 5
+ end_year = Time.now.year + 5
expected = '<input name="post[written_on(3i)]" type="hidden" id="post_written_on_3i" value="1"/>' + "\n"
- expected << %{<select id="post_written_on_1i" name="post[written_on(1i)]">\n}
+ 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"
@@ -2084,8 +2084,8 @@ class DateHelperTest < ActionView::TestCase
def test_date_select_with_nil_and_blank_and_discard_month
@post = Post.new
- start_year = Time.now.year-5
- end_year = Time.now.year+5
+ 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}
expected << "<option value=\"\"></option>\n"
@@ -2782,7 +2782,7 @@ class DateHelperTest < ActionView::TestCase
def test_datetime_select_with_infinity # Float
@post = Post.new
- @post.updated_at = (-1.0/0)
+ @post.updated_at = (-1.0 / 0)
datetime_select("post", "updated_at")
end
@@ -2903,8 +2903,8 @@ 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)
- (Date.today.year-5).upto(Date.today.year+1) { |y| expected << %(<option value="#{y}">#{y}</option>\n) }
+ 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"
expected << %(<select id="date_first_month" name="date[first][month]">\n)
@@ -2915,7 +2915,7 @@ class DateHelperTest < ActionView::TestCase
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">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"
- assert_dom_equal expected, select_date(0, end_year: Date.today.year+1, prefix: "date[first]")
+ assert_dom_equal expected, select_date(0, end_year: Date.today.year + 1, prefix: "date[first]")
end
def test_date_select_with_zero_value_and_no_end_year
@@ -2936,8 +2936,8 @@ 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)
- (Date.today.year-5).upto(Date.today.year+5) { |y| expected << %(<option value="#{y}">#{y}</option>\n) }
+ 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"
expected << %(<select id="date_first_month" name="date[first][month]">\n)
@@ -2952,8 +2952,8 @@ 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)
- (Date.today.year-5).upto(Date.today.year+5) { |y| expected << %(<option value="#{y}">#{y}</option>\n) }
+ 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"
expected << %(<select id="date_first_month" name="date[first][month]">\n)
@@ -2968,8 +2968,8 @@ 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)
- (Date.today.year-5).upto(Date.today.year+5) { |y| expected << %(<option value="#{y}">#{y}</option>\n) }
+ 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"
expected << %(<select id="date_first_month" name="date[first][month]">\n)
diff --git a/actionview/test/template/digestor_test.rb b/actionview/test/template/digestor_test.rb
index 093ff28c14..a814cab686 100644
--- a/actionview/test/template/digestor_test.rb
+++ b/actionview/test/template/digestor_test.rb
@@ -139,7 +139,7 @@ class TemplateDigestorTest < ActionView::TestCase
end
def test_getting_of_doubly_nested_dependencies
- doubly_nested = [{ "comments/comments"=>["comments/comment"] }, "messages/message"]
+ doubly_nested = [{ "comments/comments" => ["comments/comment"] }, "messages/message"]
assert_equal doubly_nested, nested_dependencies("messages/peek")
end
@@ -150,13 +150,13 @@ class TemplateDigestorTest < ActionView::TestCase
end
def test_nested_template_deps
- nested_deps = ["messages/header", { "comments/comments"=>["comments/comment"] }, "messages/actions/move", "events/event", "messages/something_missing", "messages/something_missing_1", "messages/message", "messages/form"]
+ nested_deps = ["messages/header", { "comments/comments" => ["comments/comment"] }, "messages/actions/move", "events/event", "messages/something_missing", "messages/something_missing_1", "messages/message", "messages/form"]
assert_equal nested_deps, nested_dependencies("messages/show")
end
def test_nested_template_deps_with_non_default_rendered_format
finder.rendered_format = nil
- nested_deps = [{ "comments/comments"=>["comments/comment"] }]
+ nested_deps = [{ "comments/comments" => ["comments/comment"] }]
assert_equal nested_deps, nested_dependencies("messages/thread")
end
diff --git a/actionview/test/template/form_helper/form_with_test.rb b/actionview/test/template/form_helper/form_with_test.rb
new file mode 100644
index 0000000000..3a91c7dce7
--- /dev/null
+++ b/actionview/test/template/form_helper/form_with_test.rb
@@ -0,0 +1,2231 @@
+require "abstract_unit"
+require "controller/fake_models"
+
+class FormWithTest < ActionView::TestCase
+ include RenderERBUtils
+end
+
+class FormWithActsLikeFormTagTest < FormWithTest
+ tests ActionView::Helpers::FormTagHelper
+
+ setup do
+ @controller = BasicController.new
+ end
+
+ def hidden_fields(options = {})
+ method = options[:method]
+ skip_enforcing_utf8 = options.fetch(:skip_enforcing_utf8, false)
+
+ "".tap do |txt|
+ unless skip_enforcing_utf8
+ txt << %{<input name="utf8" type="hidden" value="&#x2713;" />}
+ end
+
+ if method && !%w(get post).include?(method.to_s)
+ txt << %{<input name="_method" type="hidden" value="#{method}" />}
+ end
+ end
+ end
+
+ def form_text(action = "http://www.example.com", local: false, **options)
+ enctype, html_class, id, method = options.values_at(:enctype, :html_class, :id, :method)
+
+ method = method.to_s == "get" ? "get" : "post"
+
+ 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
+ txt << %{ id="#{id}"} if id
+ txt << %{ method="#{method}">}
+ end
+
+ def whole_form(action = "http://www.example.com", options = {})
+ out = form_text(action, options) + hidden_fields(options)
+
+ if block_given?
+ out << yield << "</form>"
+ end
+
+ out
+ end
+
+ def url_for(options)
+ if options.is_a?(Hash)
+ "http://www.example.com"
+ else
+ super
+ end
+ end
+
+ def test_form_with_multipart
+ actual = form_with(multipart: true)
+
+ expected = whole_form("http://www.example.com", enctype: true)
+ assert_dom_equal expected, actual
+ end
+
+ def test_form_with_with_method_patch
+ actual = form_with(method: :patch)
+
+ expected = whole_form("http://www.example.com", method: :patch)
+ assert_dom_equal expected, actual
+ end
+
+ def test_form_with_with_method_put
+ actual = form_with(method: :put)
+
+ expected = whole_form("http://www.example.com", method: :put)
+ assert_dom_equal expected, actual
+ end
+
+ def test_form_with_with_method_delete
+ actual = form_with(method: :delete)
+
+ expected = whole_form("http://www.example.com", method: :delete)
+ assert_dom_equal expected, actual
+ end
+
+ def test_form_with_with_local_true
+ actual = form_with(local: true)
+
+ expected = whole_form("http://www.example.com", local: true)
+ assert_dom_equal expected, actual
+ end
+
+ def test_form_with_skip_enforcing_utf8_true
+ actual = form_with(skip_enforcing_utf8: true)
+ expected = whole_form("http://www.example.com", skip_enforcing_utf8: true)
+ assert_dom_equal expected, actual
+ assert actual.html_safe?
+ end
+
+ def test_form_with_with_block_in_erb
+ output_buffer = render_erb("<%= form_with(url: 'http://www.example.com') do %>Hello world!<% end %>")
+
+ expected = whole_form { "Hello world!" }
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_form_with_with_block_and_method_in_erb
+ output_buffer = render_erb("<%= form_with(url: 'http://www.example.com', method: :put) do %>Hello world!<% end %>")
+
+ expected = whole_form("http://www.example.com", method: "put") do
+ "Hello world!"
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_form_with_with_block_in_erb_and_local_true
+ output_buffer = render_erb("<%= form_with(url: 'http://www.example.com', local: true) do %>Hello world!<% end %>")
+
+ expected = whole_form("http://www.example.com", local: true) do
+ "Hello world!"
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+end
+
+class FormWithActsLikeFormForTest < FormWithTest
+ def form_with(*)
+ @output_buffer = super
+ end
+
+ teardown do
+ I18n.backend.reload!
+ end
+
+ setup do
+ # Create "label" locale for testing I18n label helpers
+ I18n.backend.store_translations "label",
+ activemodel: {
+ attributes: {
+ post: {
+ cost: "Total cost"
+ },
+ "post/language": {
+ spanish: "Espanol"
+ }
+ }
+ },
+ helpers: {
+ label: {
+ post: {
+ body: "Write entire text here",
+ color: {
+ red: "Rojo"
+ },
+ comments: {
+ body: "Write body here"
+ }
+ },
+ tag: {
+ value: "Tag"
+ },
+ post_delegate: {
+ title: "Delegate model_name title"
+ }
+ }
+ }
+
+ # Create "submit" locale for testing I18n submit helpers
+ I18n.backend.store_translations "submit",
+ helpers: {
+ submit: {
+ create: "Create %{model}",
+ update: "Confirm %{model} changes",
+ submit: "Save changes",
+ another_post: {
+ update: "Update your %{model}"
+ }
+ }
+ }
+
+ I18n.backend.store_translations "placeholder",
+ activemodel: {
+ attributes: {
+ post: {
+ cost: "Total cost"
+ },
+ "post/cost": {
+ uk: "Pounds"
+ }
+ }
+ },
+ helpers: {
+ placeholder: {
+ post: {
+ title: "What is this about?",
+ written_on: {
+ spanish: "Escrito en"
+ },
+ comments: {
+ body: "Write body here"
+ }
+ },
+ post_delegate: {
+ title: "Delegate model_name title"
+ },
+ tag: {
+ value: "Tag"
+ }
+ }
+ }
+
+ @post = Post.new
+ @comment = Comment.new
+ def @post.errors()
+ Class.new {
+ def [](field); field == "author_name" ? ["can't be empty"] : [] end
+ def empty?() false end
+ def count() 1 end
+ def full_messages() ["Author name can't be empty"] end
+ }.new
+ end
+ def @post.to_key; [123]; end
+ def @post.id; 0; end
+ def @post.id_before_type_cast; "omg"; end
+ def @post.id_came_from_user?; true; end
+ def @post.to_param; "123"; end
+
+ @post.persisted = true
+ @post.title = "Hello World"
+ @post.author_name = ""
+ @post.body = "Back to the hill and over it again!"
+ @post.secret = 1
+ @post.written_on = Date.new(2004, 6, 15)
+
+ @post.comments = []
+ @post.comments << @comment
+
+ @post.tags = []
+ @post.tags << Tag.new
+
+ @post_delegator = PostDelegator.new
+
+ @post_delegator.title = "Hello World"
+
+ @car = Car.new("#000FFF")
+ end
+
+ Routes = ActionDispatch::Routing::RouteSet.new
+ Routes.draw do
+ resources :posts do
+ resources :comments
+ end
+
+ namespace :admin do
+ resources :posts do
+ resources :comments
+ end
+ end
+
+ get "/foo", to: "controller#action"
+ root to: "main#index"
+ end
+
+ def _routes
+ Routes
+ end
+
+ include Routes.url_helpers
+
+ def url_for(object)
+ @url_for_options = object
+
+ if object.is_a?(Hash) && object[:use_route].blank? && object[:controller].blank?
+ object.merge!(controller: "main", action: "index")
+ end
+
+ super
+ end
+
+ def test_form_with_requires_arguments
+ error = assert_raises(ArgumentError) do
+ form_for(nil, html: { id: "create-post" }) do
+ end
+ end
+ assert_equal "First argument in form cannot contain nil or be empty", error.message
+
+ error = assert_raises(ArgumentError) do
+ form_for([nil, nil], html: { id: "create-post" }) do
+ end
+ end
+ assert_equal "First argument in form cannot contain nil or be empty", error.message
+ end
+
+ def test_form_with
+ form_with(model: @post, id: "create-post") do |f|
+ concat f.label(:title) { "The Title" }
+ concat f.text_field(:title)
+ concat f.text_area(:body)
+ concat f.check_box(:secret)
+ concat f.submit("Create post")
+ concat f.button("Create post")
+ concat f.button {
+ concat content_tag(:span, "Create post")
+ }
+ end
+
+ expected = whole_form("/posts/123", "create-post", method: "patch") do
+ "<label for='post_title'>The Title</label>" +
+ "<input name='post[title]' type='text' value='Hello World' />" +
+ "<textarea name='post[body]'>\nBack to the hill and over it again!</textarea>" +
+ "<input name='post[secret]' type='hidden' value='0' />" +
+ "<input name='post[secret]' checked='checked' type='checkbox' value='1' />" +
+ "<input name='commit' data-disable-with='Create post' type='submit' value='Create post' />" +
+ "<button name='button' type='submit'>Create post</button>" +
+ "<button name='button' type='submit'><span>Create post</span></button>"
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_form_with_only_url_on_create
+ form_with(url: "/posts") do |f|
+ concat f.label :title, "Label me"
+ concat f.text_field :title
+ end
+
+ expected = whole_form("/posts") do
+ '<label for="title">Label me</label>' +
+ '<input type="text" name="title">'
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_form_with_only_url_on_update
+ form_with(url: "/posts/123") do |f|
+ concat f.label :title, "Label me"
+ concat f.text_field :title
+ end
+
+ expected = whole_form("/posts/123") do
+ '<label for="title">Label me</label>' +
+ '<input type="text" name="title">'
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_form_with_general_attributes
+ form_with(url: "/posts/123") do |f|
+ concat f.text_field :no_model_to_back_this_badboy
+ end
+
+ expected = whole_form("/posts/123") do
+ '<input type="text" name="no_model_to_back_this_badboy">'
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_form_with_attribute_not_on_model
+ form_with(model: @post) do |f|
+ concat f.text_field :this_dont_exist_on_post
+ end
+
+ expected = whole_form("/posts/123", method: :patch) do
+ '<input type="text" name="post[this_dont_exist_on_post]">'
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ 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
+
+ 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|
+ assert_dom_equal '<input type="hidden" name="other_name[private_property]">', f.hidden_field(:private_property)
+ assert_dom_equal '<input type="hidden" name="other_name[protected_property]">', f.hidden_field(:protected_property)
+ end
+ end
+
+ def test_form_with_with_collection_radio_buttons
+ post = Post.new
+ def post.active; false; end
+ form_with(model: post) do |f|
+ concat f.collection_radio_buttons(:active, [true, false], :to_s, :to_s)
+ end
+
+ expected = whole_form("/posts") do
+ "<input type='hidden' name='post[active]' value='' />" +
+ "<input name='post[active]' type='radio' value='true' />" +
+ "<label for='post_active_true'>true</label>" +
+ "<input checked='checked' name='post[active]' type='radio' value='false' />" +
+ "<label for='post_active_false'>false</label>"
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_form_with_with_collection_radio_buttons_with_custom_builder_block
+ post = Post.new
+ def post.active; false; end
+
+ form_with(model: post) do |f|
+ rendered_radio_buttons = f.collection_radio_buttons(:active, [true, false], :to_s, :to_s) do |b|
+ b.label { b.radio_button + b.text }
+ end
+ concat rendered_radio_buttons
+ end
+
+ expected = whole_form("/posts") do
+ "<input type='hidden' name='post[active]' value='' />" +
+ "<label for='post_active_true'>" +
+ "<input name='post[active]' type='radio' value='true' />" +
+ "true</label>" +
+ "<label for='post_active_false'>" +
+ "<input checked='checked' name='post[active]' type='radio' value='false' />" +
+ "false</label>"
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_form_with_with_collection_radio_buttons_with_custom_builder_block_does_not_leak_the_template
+ post = Post.new
+ def post.active; false; end
+ def post.id; 1; end
+
+ form_with(model: post) do |f|
+ rendered_radio_buttons = f.collection_radio_buttons(:active, [true, false], :to_s, :to_s) do |b|
+ b.label { b.radio_button + b.text }
+ end
+ concat rendered_radio_buttons
+ concat f.hidden_field :id
+ end
+
+ expected = whole_form("/posts") do
+ "<input type='hidden' name='post[active]' value='' />" +
+ "<label for='post_active_true'>" +
+ "<input name='post[active]' type='radio' value='true' />" +
+ "true</label>" +
+ "<label for='post_active_false'>" +
+ "<input checked='checked' name='post[active]' type='radio' value='false' />" +
+ "false</label>" +
+ "<input name='post[id]' type='hidden' value='1' />"
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_form_with_with_index_and_with_collection_radio_buttons
+ post = Post.new
+ def post.active; false; end
+
+ form_with(model: post, index: "1") do |f|
+ concat f.collection_radio_buttons(:active, [true, false], :to_s, :to_s)
+ end
+
+ expected = whole_form("/posts") do
+ "<input type='hidden' name='post[1][active]' value='' />" +
+ "<input name='post[1][active]' type='radio' value='true' />" +
+ "<label for='post_1_active_true'>true</label>" +
+ "<input checked='checked' name='post[1][active]' type='radio' value='false' />" +
+ "<label for='post_1_active_false'>false</label>"
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_form_with_with_collection_check_boxes
+ post = Post.new
+ def post.tag_ids; [1, 3]; end
+ collection = (1..3).map { |i| [i, "Tag #{i}"] }
+ form_with(model: post) do |f|
+ concat f.collection_check_boxes(:tag_ids, collection, :first, :last)
+ end
+
+ expected = whole_form("/posts") do
+ "<input name='post[tag_ids][]' type='hidden' value='' />" +
+ "<input checked='checked' name='post[tag_ids][]' type='checkbox' value='1' />" +
+ "<label for='post_tag_ids_1'>Tag 1</label>" +
+ "<input name='post[tag_ids][]' type='checkbox' value='2' />" +
+ "<label for='post_tag_ids_2'>Tag 2</label>" +
+ "<input checked='checked' name='post[tag_ids][]' type='checkbox' value='3' />" +
+ "<label for='post_tag_ids_3'>Tag 3</label>"
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_form_with_with_collection_check_boxes_with_custom_builder_block
+ post = Post.new
+ def post.tag_ids; [1, 3]; end
+ collection = (1..3).map { |i| [i, "Tag #{i}"] }
+ form_with(model: post) do |f|
+ rendered_check_boxes = f.collection_check_boxes(:tag_ids, collection, :first, :last) do |b|
+ b.label { b.check_box + b.text }
+ end
+ concat rendered_check_boxes
+ end
+
+ expected = whole_form("/posts") do
+ "<input name='post[tag_ids][]' type='hidden' value='' />" +
+ "<label for='post_tag_ids_1'>" +
+ "<input checked='checked' name='post[tag_ids][]' type='checkbox' value='1' />" +
+ "Tag 1</label>" +
+ "<label for='post_tag_ids_2'>" +
+ "<input name='post[tag_ids][]' type='checkbox' value='2' />" +
+ "Tag 2</label>" +
+ "<label for='post_tag_ids_3'>" +
+ "<input checked='checked' name='post[tag_ids][]' type='checkbox' value='3' />" +
+ "Tag 3</label>"
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_form_with_with_collection_check_boxes_with_custom_builder_block_does_not_leak_the_template
+ post = Post.new
+ def post.tag_ids; [1, 3]; end
+ def post.id; 1; end
+ collection = (1..3).map { |i| [i, "Tag #{i}"] }
+
+ form_with(model: post) do |f|
+ rendered_check_boxes = f.collection_check_boxes(:tag_ids, collection, :first, :last) do |b|
+ b.label { b.check_box + b.text }
+ end
+ concat rendered_check_boxes
+ concat f.hidden_field :id
+ end
+
+ expected = whole_form("/posts") do
+ "<input name='post[tag_ids][]' type='hidden' value='' />" +
+ "<label for='post_tag_ids_1'>" +
+ "<input checked='checked' name='post[tag_ids][]' type='checkbox' value='1' />" +
+ "Tag 1</label>" +
+ "<label for='post_tag_ids_2'>" +
+ "<input name='post[tag_ids][]' type='checkbox' value='2' />" +
+ "Tag 2</label>" +
+ "<label for='post_tag_ids_3'>" +
+ "<input checked='checked' name='post[tag_ids][]' type='checkbox' value='3' />" +
+ "Tag 3</label>" +
+ "<input name='post[id]' type='hidden' value='1' />"
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_form_with_index_and_with_collection_check_boxes
+ post = Post.new
+ def post.tag_ids; [1]; end
+ collection = [[1, "Tag 1"]]
+
+ form_with(model: post, index: "1") do |f|
+ concat f.collection_check_boxes(:tag_ids, collection, :first, :last)
+ end
+
+ expected = whole_form("/posts") do
+ "<input name='post[1][tag_ids][]' type='hidden' value='' />" +
+ "<input checked='checked' name='post[1][tag_ids][]' type='checkbox' value='1' />" +
+ "<label for='post_1_tag_ids_1'>Tag 1</label>"
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_form_with_with_file_field_generate_multipart
+ form_with(model: @post, id: "create-post") do |f|
+ concat f.file_field(:file)
+ end
+
+ expected = whole_form("/posts/123", "create-post", method: "patch", multipart: true) do
+ "<input name='post[file]' type='file' />"
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_fields_with_file_field_generate_multipart
+ form_with(model: @post) do |f|
+ concat f.fields(:comment, model: @post) { |c|
+ concat c.file_field(:file)
+ }
+ end
+
+ expected = whole_form("/posts/123", method: "patch", multipart: true) do
+ "<input name='post[comment][file]' type='file' />"
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_form_with_with_format
+ form_with(model: @post, format: :json, id: "edit_post_123", class: "edit_post") do |f|
+ concat f.label(:title)
+ end
+
+ expected = whole_form("/posts/123.json", "edit_post_123", "edit_post", method: "patch") do
+ "<label for='post_title'>Title</label>"
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_form_with_with_format_and_url
+ form_with(model: @post, format: :json, url: "/") do |f|
+ concat f.label(:title)
+ end
+
+ expected = whole_form("/", method: "patch") do
+ "<label for='post_title'>Title</label>"
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_form_with_with_model_using_relative_model_naming
+ blog_post = Blog::Post.new("And his name will be forty and four.", 44)
+
+ form_with(model: blog_post) do |f|
+ concat f.text_field :title
+ concat f.submit("Edit post")
+ end
+
+ expected = whole_form("/posts/44", method: "patch") do
+ "<input name='post[title]' type='text' value='And his name will be forty and four.' />" +
+ "<input name='commit' data-disable-with='Edit post' type='submit' value='Edit post' />"
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_form_with_with_symbol_scope
+ form_with(model: @post, scope: "other_name", id: "create-post") do |f|
+ concat f.label(:title, class: "post_title")
+ concat f.text_field(:title)
+ concat f.text_area(:body)
+ concat f.check_box(:secret)
+ concat f.submit("Create post")
+ end
+
+ expected = whole_form("/posts/123", "create-post", method: "patch") do
+ "<label for='other_name_title' class='post_title'>Title</label>" +
+ "<input name='other_name[title]' value='Hello World' type='text' />" +
+ "<textarea name='other_name[body]'>\nBack to the hill and over it again!</textarea>" +
+ "<input name='other_name[secret]' value='0' type='hidden' />" +
+ "<input name='other_name[secret]' checked='checked' value='1' type='checkbox' />" +
+ "<input name='commit' value='Create post' data-disable-with='Create post' type='submit' />"
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_form_with_with_method_as_part_of_html_options
+ form_with(model: @post, url: "/", id: "create-post", html: { method: :delete }) do |f|
+ concat f.text_field(:title)
+ concat f.text_area(:body)
+ concat f.check_box(:secret)
+ end
+
+ expected = whole_form("/", "create-post", method: "delete") do
+ "<input name='post[title]' type='text' value='Hello World' />" +
+ "<textarea name='post[body]'>\nBack to the hill and over it again!</textarea>" +
+ "<input name='post[secret]' type='hidden' value='0' />" +
+ "<input name='post[secret]' checked='checked' type='checkbox' value='1' />"
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_form_with_with_method
+ form_with(model: @post, url: "/", method: :delete, id: "create-post") do |f|
+ concat f.text_field(:title)
+ concat f.text_area(:body)
+ concat f.check_box(:secret)
+ end
+
+ expected = whole_form("/", "create-post", method: "delete") do
+ "<input name='post[title]' type='text' value='Hello World' />" +
+ "<textarea name='post[body]'>\nBack to the hill and over it again!</textarea>" +
+ "<input name='post[secret]' type='hidden' value='0' />" +
+ "<input name='post[secret]' checked='checked' type='checkbox' value='1' />"
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_form_with_with_search_field
+ # Test case for bug which would emit an "object" attribute
+ # when used with form_for using a search_field form helper
+ form_with(model: Post.new, url: "/search", id: "search-post", method: :get) do |f|
+ concat f.search_field(:title)
+ end
+
+ expected = whole_form("/search", "search-post", method: "get") do
+ "<input name='post[title]' type='search' />"
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_form_with_enables_remote_by_default
+ form_with(model: @post, url: "/", id: "create-post", method: :patch) do |f|
+ concat f.text_field(:title)
+ concat f.text_area(:body)
+ concat f.check_box(:secret)
+ end
+
+ expected = whole_form("/", "create-post", method: "patch") do
+ "<input name='post[title]' type='text' value='Hello World' />" +
+ "<textarea name='post[body]'>\nBack to the hill and over it again!</textarea>" +
+ "<input name='post[secret]' type='hidden' value='0' />" +
+ "<input name='post[secret]' checked='checked' type='checkbox' value='1' />"
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_form_with_skip_enforcing_utf8_true
+ form_with(scope: :post, skip_enforcing_utf8: true) do |f|
+ concat f.text_field(:title)
+ end
+
+ expected = whole_form("/", skip_enforcing_utf8: true) do
+ "<input name='post[title]' type='text' value='Hello World' />"
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_form_with_skip_enforcing_utf8_false
+ form_with(scope: :post, skip_enforcing_utf8: false) do |f|
+ concat f.text_field(:title)
+ end
+
+ expected = whole_form("/", skip_enforcing_utf8: false) do
+ "<input name='post[title]' type='text' value='Hello World' />"
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_form_with_without_object
+ form_with(scope: :post, id: "create-post") do |f|
+ concat f.text_field(:title)
+ concat f.text_area(:body)
+ concat f.check_box(:secret)
+ end
+
+ expected = whole_form("/", "create-post") do
+ "<input name='post[title]' type='text' value='Hello World' />" +
+ "<textarea name='post[body]'>\nBack to the hill and over it again!</textarea>" +
+ "<input name='post[secret]' type='hidden' value='0' />" +
+ "<input name='post[secret]' checked='checked' type='checkbox' value='1' />"
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_form_with_with_index
+ form_with(model: @post, scope: "post[]") do |f|
+ concat f.label(:title)
+ concat f.text_field(:title)
+ concat f.text_area(:body)
+ concat f.check_box(:secret)
+ end
+
+ expected = whole_form("/posts/123", method: "patch") do
+ "<label for='post_123_title'>Title</label>" +
+ "<input name='post[123][title]' type='text' value='Hello World' />" +
+ "<textarea name='post[123][body]'>\nBack to the hill and over it again!</textarea>" +
+ "<input name='post[123][secret]' type='hidden' value='0' />" +
+ "<input name='post[123][secret]' checked='checked' type='checkbox' value='1' />"
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_form_with_with_nil_index_option_override
+ form_with(model: @post, scope: "post[]", index: nil) do |f|
+ concat f.text_field(:title)
+ concat f.text_area(:body)
+ concat f.check_box(:secret)
+ end
+
+ expected = whole_form("/posts/123", method: "patch") do
+ "<input name='post[][title]' type='text' value='Hello World' />" +
+ "<textarea name='post[][body]'>\nBack to the hill and over it again!</textarea>" +
+ "<input name='post[][secret]' type='hidden' value='0' />" +
+ "<input name='post[][secret]' checked='checked' type='checkbox' value='1' />"
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_form_with_label_error_wrapping
+ form_with(model: @post) do |f|
+ concat f.label(:author_name, class: "label")
+ concat f.text_field(:author_name)
+ concat f.submit("Create post")
+ end
+
+ expected = whole_form("/posts/123", method: "patch") do
+ "<div class='field_with_errors'><label for='post_author_name' class='label'>Author name</label></div>" +
+ "<div class='field_with_errors'><input name='post[author_name]' type='text' value='' /></div>" +
+ "<input name='commit' data-disable-with='Create post' type='submit' value='Create post' />"
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_form_with_label_error_wrapping_without_conventional_instance_variable
+ post = remove_instance_variable :@post
+
+ form_with(model: post) do |f|
+ concat f.label(:author_name, class: "label")
+ concat f.text_field(:author_name)
+ concat f.submit("Create post")
+ end
+
+ expected = whole_form("/posts/123", method: "patch") do
+ "<div class='field_with_errors'><label for='post_author_name' class='label'>Author name</label></div>" +
+ "<div class='field_with_errors'><input name='post[author_name]' type='text' value='' /></div>" +
+ "<input name='commit' data-disable-with='Create post' type='submit' value='Create post' />"
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_form_with_label_error_wrapping_block_and_non_block_versions
+ form_with(model: @post) do |f|
+ concat f.label(:author_name, "Name", class: "label")
+ concat f.label(:author_name, class: "label") { "Name" }
+ end
+
+ expected = whole_form("/posts/123", method: "patch") do
+ "<div class='field_with_errors'><label for='post_author_name' class='label'>Name</label></div>" +
+ "<div class='field_with_errors'><label for='post_author_name' class='label'>Name</label></div>"
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_form_with_with_namespace
+ skip "Do namespaces still make sense?"
+ form_for(@post, namespace: "namespace") do |f|
+ concat f.text_field(:title)
+ concat f.text_area(:body)
+ concat f.check_box(:secret)
+ end
+
+ expected = whole_form("/posts/123", "namespace_edit_post_123", "edit_post", method: "patch") do
+ "<input name='post[title]' type='text' value='Hello World' />" +
+ "<textarea name='post[body]'>\nBack to the hill and over it again!</textarea>" +
+ "<input name='post[secret]' type='hidden' value='0' />" +
+ "<input name='post[secret]' checked='checked' type='checkbox' value='1' />"
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_submit_with_object_as_new_record_and_locale_strings
+ with_locale :submit do
+ @post.persisted = false
+ @post.stub(:to_key, nil) do
+ form_with(model: @post) do |f|
+ concat f.submit
+ end
+
+ expected = whole_form("/posts") do
+ "<input name='commit' data-disable-with='Create Post' type='submit' value='Create Post' />"
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+ end
+ end
+
+ def test_submit_with_object_as_existing_record_and_locale_strings
+ with_locale :submit do
+ form_with(model: @post) do |f|
+ concat f.submit
+ end
+
+ expected = whole_form("/posts/123", method: "patch") do
+ "<input name='commit' data-disable-with='Confirm Post changes' type='submit' value='Confirm Post changes' />"
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+ end
+
+ def test_submit_without_object_and_locale_strings
+ with_locale :submit do
+ form_with(scope: :post) do |f|
+ concat f.submit class: "extra"
+ end
+
+ expected = whole_form do
+ "<input name='commit' class='extra' data-disable-with='Save changes' type='submit' value='Save changes' />"
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+ end
+
+ def test_submit_with_object_and_nested_lookup
+ with_locale :submit do
+ form_with(model: @post, scope: :another_post) do |f|
+ concat f.submit
+ end
+
+ expected = whole_form("/posts/123", method: "patch") do
+ "<input name='commit' data-disable-with='Update your Post' type='submit' value='Update your Post' />"
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+ end
+
+ def test_fields_with_attributes_not_on_model
+ form_with(model: @post) do |f|
+ concat f.fields(:comment) { |c|
+ concat c.text_field :dont_exist_on_model
+ }
+ end
+
+ expected = whole_form("/posts/123", method: :patch) do
+ '<input type="text" name="post[comment][dont_exist_on_model]">'
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_fields_with_attributes_not_on_model_deep_nested
+ @comment.save
+ form_with(scope: :posts) do |f|
+ f.fields("post[]", model: @post) do |f2|
+ f2.text_field(:id)
+ @post.comments.each do |comment|
+ concat f2.fields("comment[]", model: comment) { |c|
+ concat c.text_field(:dont_exist_on_model)
+ }
+ end
+ end
+ end
+
+ expected = whole_form do
+ '<input name="posts[post][0][comment][1][dont_exist_on_model]" type="text">'
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_nested_fields
+ @comment.body = "Hello World"
+ form_with(model: @post) do |f|
+ concat f.fields(model: @comment) { |c|
+ concat c.text_field(:body)
+ }
+ end
+
+ expected = whole_form("/posts/123", method: "patch") do
+ "<input name='post[comment][body]' type='text' value='Hello World' />"
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_deep_nested_fields
+ @comment.save
+ form_with(scope: :posts) do |f|
+ f.fields("post[]", model: @post) do |f2|
+ f2.text_field(:id)
+ @post.comments.each do |comment|
+ concat f2.fields("comment[]", model: comment) { |c|
+ concat c.text_field(:name)
+ }
+ end
+ end
+ end
+
+ expected = whole_form do
+ "<input name='posts[post][0][comment][1][name]' type='text' value='comment #1' />"
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_nested_fields_with_nested_collections
+ form_with(model: @post, scope: "post[]") do |f|
+ concat f.text_field(:title)
+ concat f.fields("comment[]", model: @comment) { |c|
+ concat c.text_field(:name)
+ }
+ end
+
+ expected = whole_form("/posts/123", method: "patch") do
+ "<input name='post[123][title]' type='text' value='Hello World' />" +
+ "<input name='post[123][comment][][name]' type='text' value='new comment' />"
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_nested_fields_with_index_and_parent_fields
+ form_with(model: @post, index: 1) do |c|
+ concat c.text_field(:title)
+ concat c.fields("comment", model: @comment, index: 1) { |r|
+ concat r.text_field(:name)
+ }
+ end
+
+ expected = whole_form("/posts/123", method: "patch") do
+ "<input name='post[1][title]' type='text' value='Hello World' />" +
+ "<input name='post[1][comment][1][name]' type='text' value='new comment' />"
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_form_with_with_index_and_nested_fields
+ output_buffer = form_with(model: @post, index: 1) do |f|
+ concat f.fields(:comment, model: @post) { |c|
+ concat c.text_field(:title)
+ }
+ end
+
+ expected = whole_form("/posts/123", method: "patch") do
+ "<input name='post[1][comment][title]' type='text' value='Hello World' />"
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_nested_fields_with_index_on_both
+ form_with(model: @post, index: 1) do |f|
+ concat f.fields(:comment, model: @post, index: 5) { |c|
+ concat c.text_field(:title)
+ }
+ end
+
+ expected = whole_form("/posts/123", method: "patch") do
+ "<input name='post[1][comment][5][title]' type='text' value='Hello World' />"
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_nested_fields_with_auto_index
+ form_with(model: @post, scope: "post[]") do |f|
+ concat f.fields(:comment, model: @post) { |c|
+ concat c.text_field(:title)
+ }
+ end
+
+ expected = whole_form("/posts/123", method: "patch") do
+ "<input name='post[123][comment][title]' type='text' value='Hello World' />"
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_nested_fields_with_index_radio_button
+ form_with(model: @post) do |f|
+ concat f.fields(:comment, model: @post, index: 5) { |c|
+ concat c.radio_button(:title, "hello")
+ }
+ end
+
+ expected = whole_form("/posts/123", method: "patch") do
+ "<input name='post[comment][5][title]' type='radio' value='hello' />"
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_nested_fields_with_auto_index_on_both
+ form_with(model: @post, scope: "post[]") do |f|
+ concat f.fields("comment[]", model: @post) { |c|
+ concat c.text_field(:title)
+ }
+ end
+
+ expected = whole_form("/posts/123", method: "patch") do
+ "<input name='post[123][comment][123][title]' type='text' value='Hello World' />"
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_nested_fields_with_index_and_auto_index
+ output_buffer = form_with(model: @post, scope: "post[]") do |f|
+ concat f.fields(:comment, model: @post, index: 5) { |c|
+ concat c.text_field(:title)
+ }
+ end
+
+ output_buffer << form_with(model: @post, index: 1) do |f|
+ concat f.fields("comment[]", model: @post) { |c|
+ concat c.text_field(:title)
+ }
+ end
+
+ expected = whole_form("/posts/123", method: "patch") do
+ "<input name='post[123][comment][5][title]' type='text' value='Hello World' />"
+ end + whole_form("/posts/123", method: "patch") do
+ "<input name='post[1][comment][123][title]' type='text' value='Hello World' />"
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_nested_fields_with_a_new_record_on_a_nested_attributes_one_to_one_association
+ @post.author = Author.new
+
+ form_with(model: @post) do |f|
+ concat f.text_field(:title)
+ concat f.fields(:author) { |af|
+ concat af.text_field(:name)
+ }
+ end
+
+ expected = whole_form("/posts/123", method: "patch") do
+ '<input name="post[title]" type="text" value="Hello World" />' +
+ '<input name="post[author_attributes][name]" type="text" value="new author" />'
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_nested_fields_with_explicitly_passed_object_on_a_nested_attributes_one_to_one_association
+ form_with(model: @post) do |f|
+ f.fields(:author, model: Author.new(123)) do |af|
+ assert_not_nil af.object
+ assert_equal 123, af.object.id
+ end
+ end
+ end
+
+ def test_nested_fields_with_an_existing_record_on_a_nested_attributes_one_to_one_association
+ @post.author = Author.new(321)
+
+ form_with(model: @post) do |f|
+ concat f.text_field(:title)
+ concat f.fields(:author) { |af|
+ concat af.text_field(:name)
+ }
+ end
+
+ expected = whole_form("/posts/123", method: "patch") do
+ '<input name="post[title]" type="text" value="Hello World" />' +
+ '<input name="post[author_attributes][name]" type="text" value="author #321" />' +
+ '<input name="post[author_attributes][id]" type="hidden" value="321" />'
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_nested_fields_with_an_existing_record_on_a_nested_attributes_one_to_one_association_using_erb_and_inline_block
+ @post.author = Author.new(321)
+
+ form_with(model: @post) do |f|
+ concat f.text_field(:title)
+ concat f.fields(:author) { |af|
+ af.text_field(:name)
+ }
+ end
+
+ expected = whole_form("/posts/123", method: "patch") do
+ '<input name="post[title]" type="text" value="Hello World" />' +
+ '<input name="post[author_attributes][name]" type="text" value="author #321" />' +
+ '<input name="post[author_attributes][id]" type="hidden" value="321" />'
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_nested_fields_with_an_existing_record_on_a_nested_attributes_one_to_one_association_with_disabled_hidden_id
+ @post.author = Author.new(321)
+
+ form_with(model: @post) do |f|
+ concat f.text_field(:title)
+ concat f.fields(:author, skip_id: true) { |af|
+ af.text_field(:name)
+ }
+ end
+
+ expected = whole_form("/posts/123", method: "patch") do
+ '<input name="post[title]" type="text" value="Hello World" />' +
+ '<input name="post[author_attributes][name]" type="text" value="author #321" />'
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_nested_fields_with_an_existing_record_on_a_nested_attributes_one_to_one_association_with_disabled_hidden_id_inherited
+ @post.author = Author.new(321)
+
+ form_with(model: @post, skip_id: true) do |f|
+ concat f.text_field(:title)
+ concat f.fields(:author) { |af|
+ af.text_field(:name)
+ }
+ end
+
+ expected = whole_form("/posts/123", method: "patch") do
+ '<input name="post[title]" type="text" value="Hello World" />' +
+ '<input name="post[author_attributes][name]" type="text" value="author #321" />'
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_nested_fields_with_an_existing_record_on_a_nested_attributes_one_to_one_association_with_disabled_hidden_id_override
+ @post.author = Author.new(321)
+
+ form_with(model: @post, skip_id: true) do |f|
+ concat f.text_field(:title)
+ concat f.fields(:author, skip_id: false) { |af|
+ af.text_field(:name)
+ }
+ end
+
+ expected = whole_form("/posts/123", method: "patch") do
+ '<input name="post[title]" type="text" value="Hello World" />' +
+ '<input name="post[author_attributes][name]" type="text" value="author #321" />' +
+ '<input name="post[author_attributes][id]" type="hidden" value="321" />'
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_nested_fields_with_existing_records_on_a_nested_attributes_one_to_one_association_with_explicit_hidden_field_placement
+ @post.author = Author.new(321)
+
+ form_with(model: @post) do |f|
+ concat f.text_field(:title)
+ concat f.fields(:author) { |af|
+ concat af.hidden_field(:id)
+ concat af.text_field(:name)
+ }
+ end
+
+ expected = whole_form("/posts/123", method: "patch") do
+ '<input name="post[title]" type="text" value="Hello World" />' +
+ '<input name="post[author_attributes][id]" type="hidden" value="321" />' +
+ '<input name="post[author_attributes][name]" type="text" value="author #321" />'
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_nested_fields_with_existing_records_on_a_nested_attributes_collection_association
+ @post.comments = Array.new(2) { |id| Comment.new(id + 1) }
+
+ form_with(model: @post) do |f|
+ concat f.text_field(:title)
+ @post.comments.each do |comment|
+ concat f.fields(:comments, model: comment) { |cf|
+ concat cf.text_field(:name)
+ }
+ end
+ end
+
+ expected = whole_form("/posts/123", method: "patch") do
+ '<input name="post[title]" type="text" value="Hello World" />' +
+ '<input name="post[comments_attributes][0][name]" type="text" value="comment #1" />' +
+ '<input name="post[comments_attributes][0][id]" type="hidden" value="1" />' +
+ '<input name="post[comments_attributes][1][name]" type="text" value="comment #2" />' +
+ '<input name="post[comments_attributes][1][id]" type="hidden" value="2" />'
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_nested_fields_with_existing_records_on_a_nested_attributes_collection_association_with_disabled_hidden_id
+ @post.comments = Array.new(2) { |id| Comment.new(id + 1) }
+ @post.author = Author.new(321)
+
+ form_with(model: @post) do |f|
+ concat f.text_field(:title)
+ concat f.fields(:author) { |af|
+ concat af.text_field(:name)
+ }
+ @post.comments.each do |comment|
+ concat f.fields(:comments, model: comment, skip_id: true) { |cf|
+ concat cf.text_field(:name)
+ }
+ end
+ end
+
+ expected = whole_form("/posts/123", method: "patch") do
+ '<input name="post[title]" type="text" value="Hello World" />' +
+ '<input name="post[author_attributes][name]" type="text" value="author #321" />' +
+ '<input name="post[author_attributes][id]" type="hidden" value="321" />' +
+ '<input name="post[comments_attributes][0][name]" type="text" value="comment #1" />' +
+ '<input name="post[comments_attributes][1][name]" type="text" value="comment #2" />'
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_nested_fields_with_existing_records_on_a_nested_attributes_collection_association_with_disabled_hidden_id_inherited
+ @post.comments = Array.new(2) { |id| Comment.new(id + 1) }
+ @post.author = Author.new(321)
+
+ form_with(model: @post, skip_id: true) do |f|
+ concat f.text_field(:title)
+ concat f.fields(:author) { |af|
+ concat af.text_field(:name)
+ }
+ @post.comments.each do |comment|
+ concat f.fields(:comments, model: comment) { |cf|
+ concat cf.text_field(:name)
+ }
+ end
+ end
+
+ expected = whole_form("/posts/123", method: "patch") do
+ '<input name="post[title]" type="text" value="Hello World" />' +
+ '<input name="post[author_attributes][name]" type="text" value="author #321" />' +
+ '<input name="post[comments_attributes][0][name]" type="text" value="comment #1" />' +
+ '<input name="post[comments_attributes][1][name]" type="text" value="comment #2" />'
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_nested_fields_with_existing_records_on_a_nested_attributes_collection_association_with_disabled_hidden_id_override
+ @post.comments = Array.new(2) { |id| Comment.new(id + 1) }
+ @post.author = Author.new(321)
+
+ form_with(model: @post, skip_id: true) do |f|
+ concat f.text_field(:title)
+ concat f.fields(:author, skip_id: false) { |af|
+ concat af.text_field(:name)
+ }
+ @post.comments.each do |comment|
+ concat f.fields(:comments, model: comment) { |cf|
+ concat cf.text_field(:name)
+ }
+ end
+ end
+
+ expected = whole_form("/posts/123", method: "patch") do
+ '<input name="post[title]" type="text" value="Hello World" />' +
+ '<input name="post[author_attributes][name]" type="text" value="author #321" />' +
+ '<input name="post[author_attributes][id]" type="hidden" value="321" />' +
+ '<input name="post[comments_attributes][0][name]" type="text" value="comment #1" />' +
+ '<input name="post[comments_attributes][1][name]" type="text" value="comment #2" />'
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_nested_fields_with_existing_records_on_a_nested_attributes_collection_association_using_erb_and_inline_block
+ @post.comments = Array.new(2) { |id| Comment.new(id + 1) }
+
+ form_with(model: @post) do |f|
+ concat f.text_field(:title)
+ @post.comments.each do |comment|
+ concat f.fields(:comments, model: comment) { |cf|
+ cf.text_field(:name)
+ }
+ end
+ end
+
+ expected = whole_form("/posts/123", method: "patch") do
+ '<input name="post[title]" type="text" value="Hello World" />' +
+ '<input name="post[comments_attributes][0][name]" type="text" value="comment #1" />' +
+ '<input name="post[comments_attributes][0][id]" type="hidden" value="1" />' +
+ '<input name="post[comments_attributes][1][name]" type="text" value="comment #2" />' +
+ '<input name="post[comments_attributes][1][id]" type="hidden" value="2" />'
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_nested_fields_with_existing_records_on_a_nested_attributes_collection_association_with_explicit_hidden_field_placement
+ @post.comments = Array.new(2) { |id| Comment.new(id + 1) }
+
+ form_with(model: @post) do |f|
+ concat f.text_field(:title)
+ @post.comments.each do |comment|
+ concat f.fields(:comments, model: comment) { |cf|
+ concat cf.hidden_field(:id)
+ concat cf.text_field(:name)
+ }
+ end
+ end
+
+ expected = whole_form("/posts/123", method: "patch") do
+ '<input name="post[title]" type="text" value="Hello World" />' +
+ '<input name="post[comments_attributes][0][id]" type="hidden" value="1" />' +
+ '<input name="post[comments_attributes][0][name]" type="text" value="comment #1" />' +
+ '<input name="post[comments_attributes][1][id]" type="hidden" value="2" />' +
+ '<input name="post[comments_attributes][1][name]" type="text" value="comment #2" />'
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_nested_fields_with_new_records_on_a_nested_attributes_collection_association
+ @post.comments = [Comment.new, Comment.new]
+
+ form_with(model: @post) do |f|
+ concat f.text_field(:title)
+ @post.comments.each do |comment|
+ concat f.fields(:comments, model: comment) { |cf|
+ concat cf.text_field(:name)
+ }
+ end
+ end
+
+ expected = whole_form("/posts/123", method: "patch") do
+ '<input name="post[title]" type="text" value="Hello World" />' +
+ '<input name="post[comments_attributes][0][name]" type="text" value="new comment" />' +
+ '<input name="post[comments_attributes][1][name]" type="text" value="new comment" />'
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_nested_fields_with_existing_and_new_records_on_a_nested_attributes_collection_association
+ @post.comments = [Comment.new(321), Comment.new]
+
+ form_with(model: @post) do |f|
+ concat f.text_field(:title)
+ @post.comments.each do |comment|
+ concat f.fields(:comments, model: comment) { |cf|
+ concat cf.text_field(:name)
+ }
+ end
+ end
+
+ expected = whole_form("/posts/123", method: "patch") do
+ '<input name="post[title]" type="text" value="Hello World" />' +
+ '<input name="post[comments_attributes][0][name]" type="text" value="comment #321" />' +
+ '<input name="post[comments_attributes][0][id]" type="hidden" value="321" />' +
+ '<input name="post[comments_attributes][1][name]" type="text" value="new comment" />'
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_nested_fields_with_an_empty_supplied_attributes_collection
+ form_with(model: @post) do |f|
+ concat f.text_field(:title)
+ f.fields(:comments, model: []) do |cf|
+ concat cf.text_field(:name)
+ end
+ end
+
+ expected = whole_form("/posts/123", method: "patch") do
+ '<input name="post[title]" type="text" value="Hello World" />'
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_nested_fields_with_existing_records_on_a_supplied_nested_attributes_collection
+ @post.comments = Array.new(2) { |id| Comment.new(id + 1) }
+
+ form_with(model: @post) do |f|
+ concat f.text_field(:title)
+ concat f.fields(:comments, model: @post.comments) { |cf|
+ concat cf.text_field(:name)
+ }
+ end
+
+ expected = whole_form("/posts/123", method: "patch") do
+ '<input name="post[title]" type="text" value="Hello World" />' +
+ '<input name="post[comments_attributes][0][name]" type="text" value="comment #1" />' +
+ '<input name="post[comments_attributes][0][id]" type="hidden" value="1" />' +
+ '<input name="post[comments_attributes][1][name]" type="text" value="comment #2" />' +
+ '<input name="post[comments_attributes][1][id]" type="hidden" value="2" />'
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_nested_fields_arel_like
+ @post.comments = ArelLike.new
+
+ form_with(model: @post) do |f|
+ concat f.text_field(:title)
+ concat f.fields(:comments, model: @post.comments) { |cf|
+ concat cf.text_field(:name)
+ }
+ end
+
+ expected = whole_form("/posts/123", method: "patch") do
+ '<input name="post[title]" type="text" value="Hello World" />' +
+ '<input name="post[comments_attributes][0][name]" type="text" value="comment #1" />' +
+ '<input name="post[comments_attributes][0][id]" type="hidden" value="1" />' +
+ '<input name="post[comments_attributes][1][name]" type="text" value="comment #2" />' +
+ '<input name="post[comments_attributes][1][id]" type="hidden" value="2" />'
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_nested_fields_label_translation_with_more_than_10_records
+ @post.comments = Array.new(11) { |id| Comment.new(id + 1) }
+
+ params = 11.times.map { ["post.comments.body", default: [:"comment.body", ""], scope: "helpers.label"] }
+ assert_called_with(I18n, :t, params, returns: "Write body here") do
+ form_with(model: @post) do |f|
+ f.fields(:comments) do |cf|
+ concat cf.label(:body)
+ end
+ end
+ end
+ end
+
+ def test_nested_fields_with_existing_records_on_a_supplied_nested_attributes_collection_different_from_record_one
+ comments = Array.new(2) { |id| Comment.new(id + 1) }
+ @post.comments = []
+
+ form_with(model: @post) do |f|
+ concat f.text_field(:title)
+ concat f.fields(:comments, model: comments) { |cf|
+ concat cf.text_field(:name)
+ }
+ end
+
+ expected = whole_form("/posts/123", method: "patch") do
+ '<input name="post[title]" type="text" value="Hello World" />' +
+ '<input name="post[comments_attributes][0][name]" type="text" value="comment #1" />' +
+ '<input name="post[comments_attributes][0][id]" type="hidden" value="1" />' +
+ '<input name="post[comments_attributes][1][name]" type="text" value="comment #2" />' +
+ '<input name="post[comments_attributes][1][id]" type="hidden" value="2" />'
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_nested_fields_on_a_nested_attributes_collection_association_yields_only_builder
+ @post.comments = [Comment.new(321), Comment.new]
+ yielded_comments = []
+
+ form_with(model: @post) do |f|
+ concat f.text_field(:title)
+ concat f.fields(:comments) { |cf|
+ concat cf.text_field(:name)
+ yielded_comments << cf.object
+ }
+ end
+
+ expected = whole_form("/posts/123", method: "patch") do
+ '<input name="post[title]" type="text" value="Hello World" />' +
+ '<input name="post[comments_attributes][0][name]" type="text" value="comment #321" />' +
+ '<input name="post[comments_attributes][0][id]" type="hidden" value="321" />' +
+ '<input name="post[comments_attributes][1][name]" type="text" value="new comment" />'
+ end
+
+ assert_dom_equal expected, output_buffer
+ assert_equal yielded_comments, @post.comments
+ end
+
+ def test_nested_fields_with_child_index_option_override_on_a_nested_attributes_collection_association
+ @post.comments = []
+
+ form_with(model: @post) do |f|
+ concat f.fields(:comments, model: Comment.new(321), child_index: "abc") { |cf|
+ concat cf.text_field(:name)
+ }
+ end
+
+ expected = whole_form("/posts/123", method: "patch") do
+ '<input name="post[comments_attributes][abc][name]" type="text" value="comment #321" />' +
+ '<input name="post[comments_attributes][abc][id]" type="hidden" value="321" />'
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_nested_fields_with_child_index_as_lambda_option_override_on_a_nested_attributes_collection_association
+ @post.comments = []
+
+ form_with(model: @post) do |f|
+ concat f.fields(:comments, model: Comment.new(321), child_index: -> { "abc" }) { |cf|
+ concat cf.text_field(:name)
+ }
+ end
+
+ expected = whole_form("/posts/123", method: "patch") do
+ '<input name="post[comments_attributes][abc][name]" type="text" value="comment #321" />' +
+ '<input name="post[comments_attributes][abc][id]" type="hidden" value="321" />'
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ class FakeAssociationProxy
+ def to_ary
+ [1, 2, 3]
+ end
+ end
+
+ def test_nested_fields_with_child_index_option_override_on_a_nested_attributes_collection_association_with_proxy
+ @post.comments = FakeAssociationProxy.new
+
+ form_with(model: @post) do |f|
+ concat f.fields(:comments, model: Comment.new(321), child_index: "abc") { |cf|
+ concat cf.text_field(:name)
+ }
+ end
+
+ expected = whole_form("/posts/123", method: "patch") do
+ '<input name="post[comments_attributes][abc][name]" type="text" value="comment #321" />' +
+ '<input name="post[comments_attributes][abc][id]" type="hidden" value="321" />'
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_nested_fields_index_method_with_existing_records_on_a_nested_attributes_collection_association
+ @post.comments = Array.new(2) { |id| Comment.new(id + 1) }
+
+ form_with(model: @post) do |f|
+ expected = 0
+ @post.comments.each do |comment|
+ f.fields(:comments, model: comment) { |cf|
+ assert_equal cf.index, expected
+ expected += 1
+ }
+ end
+ end
+ end
+
+ def test_nested_fields_index_method_with_existing_and_new_records_on_a_nested_attributes_collection_association
+ @post.comments = [Comment.new(321), Comment.new]
+
+ form_with(model: @post) do |f|
+ expected = 0
+ @post.comments.each do |comment|
+ f.fields(:comments, model: comment) { |cf|
+ assert_equal cf.index, expected
+ expected += 1
+ }
+ end
+ end
+ end
+
+ def test_nested_fields_index_method_with_existing_records_on_a_supplied_nested_attributes_collection
+ @post.comments = Array.new(2) { |id| Comment.new(id + 1) }
+
+ form_with(model: @post) do |f|
+ expected = 0
+ f.fields(:comments, model: @post.comments) { |cf|
+ assert_equal cf.index, expected
+ expected += 1
+ }
+ end
+ end
+
+ def test_nested_fields_index_method_with_child_index_option_override_on_a_nested_attributes_collection_association
+ @post.comments = []
+
+ form_with(model: @post) do |f|
+ f.fields(:comments, model: Comment.new(321), child_index: "abc") { |cf|
+ assert_equal cf.index, "abc"
+ }
+ end
+ end
+
+ def test_nested_fields_uses_unique_indices_for_different_collection_associations
+ @post.comments = [Comment.new(321)]
+ @post.tags = [Tag.new(123), Tag.new(456)]
+ @post.comments[0].relevances = []
+ @post.tags[0].relevances = []
+ @post.tags[1].relevances = []
+
+ form_with(model: @post) do |f|
+ concat f.fields(:comments, model: @post.comments[0]) { |cf|
+ concat cf.text_field(:name)
+ concat cf.fields(:relevances, model: CommentRelevance.new(314)) { |crf|
+ concat crf.text_field(:value)
+ }
+ }
+ concat f.fields(:tags, model: @post.tags[0]) { |tf|
+ concat tf.text_field(:value)
+ concat tf.fields(:relevances, model: TagRelevance.new(3141)) { |trf|
+ concat trf.text_field(:value)
+ }
+ }
+ concat f.fields("tags", model: @post.tags[1]) { |tf|
+ concat tf.text_field(:value)
+ concat tf.fields(:relevances, model: TagRelevance.new(31415)) { |trf|
+ concat trf.text_field(:value)
+ }
+ }
+ end
+
+ expected = whole_form("/posts/123", method: "patch") do
+ '<input name="post[comments_attributes][0][name]" type="text" value="comment #321" />' +
+ '<input name="post[comments_attributes][0][relevances_attributes][0][value]" type="text" value="commentrelevance #314" />' +
+ '<input name="post[comments_attributes][0][relevances_attributes][0][id]" type="hidden" value="314" />' +
+ '<input name="post[comments_attributes][0][id]" type="hidden" value="321" />' +
+ '<input name="post[tags_attributes][0][value]" type="text" value="tag #123" />' +
+ '<input name="post[tags_attributes][0][relevances_attributes][0][value]" type="text" value="tagrelevance #3141" />' +
+ '<input name="post[tags_attributes][0][relevances_attributes][0][id]" type="hidden" value="3141" />' +
+ '<input name="post[tags_attributes][0][id]" type="hidden" value="123" />' +
+ '<input name="post[tags_attributes][1][value]" type="text" value="tag #456" />' +
+ '<input name="post[tags_attributes][1][relevances_attributes][0][value]" type="text" value="tagrelevance #31415" />' +
+ '<input name="post[tags_attributes][1][relevances_attributes][0][id]" type="hidden" value="31415" />' +
+ '<input name="post[tags_attributes][1][id]" type="hidden" value="456" />'
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_nested_fields_with_hash_like_model
+ @author = HashBackedAuthor.new
+
+ form_with(model: @post) do |f|
+ concat f.fields(:author, model: @author) { |af|
+ concat af.text_field(:name)
+ }
+ end
+
+ expected = whole_form("/posts/123", method: "patch") do
+ '<input name="post[author_attributes][name]" type="text" value="hash backed author" />'
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_fields
+ output_buffer = fields(:post, model: @post) do |f|
+ concat f.text_field(:title)
+ concat f.text_area(:body)
+ concat f.check_box(:secret)
+ end
+
+ expected =
+ "<input name='post[title]' type='text' value='Hello World' />" +
+ "<textarea name='post[body]'>\nBack to the hill and over it again!</textarea>" +
+ "<input name='post[secret]' type='hidden' value='0' />" +
+ "<input name='post[secret]' checked='checked' type='checkbox' value='1' />"
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_fields_with_index
+ output_buffer = fields("post[]", model: @post) do |f|
+ concat f.text_field(:title)
+ concat f.text_area(:body)
+ concat f.check_box(:secret)
+ end
+
+ expected =
+ "<input name='post[123][title]' type='text' value='Hello World' />" +
+ "<textarea name='post[123][body]'>\nBack to the hill and over it again!</textarea>" +
+ "<input name='post[123][secret]' type='hidden' value='0' />" +
+ "<input name='post[123][secret]' checked='checked' type='checkbox' value='1' />"
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_fields_with_nil_index_option_override
+ output_buffer = fields("post[]", model: @post, index: nil) do |f|
+ concat f.text_field(:title)
+ concat f.text_area(:body)
+ concat f.check_box(:secret)
+ end
+
+ expected =
+ "<input name='post[][title]' type='text' value='Hello World' />" +
+ "<textarea name='post[][body]'>\nBack to the hill and over it again!</textarea>" +
+ "<input name='post[][secret]' type='hidden' value='0' />" +
+ "<input name='post[][secret]' checked='checked' type='checkbox' value='1' />"
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_fields_with_index_option_override
+ output_buffer = fields("post[]", model: @post, index: "abc") do |f|
+ concat f.text_field(:title)
+ concat f.text_area(:body)
+ concat f.check_box(:secret)
+ end
+
+ expected =
+ "<input name='post[abc][title]' type='text' value='Hello World' />" +
+ "<textarea name='post[abc][body]'>\nBack to the hill and over it again!</textarea>" +
+ "<input name='post[abc][secret]' type='hidden' value='0' />" +
+ "<input name='post[abc][secret]' checked='checked' type='checkbox' value='1' />"
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_fields_without_object
+ output_buffer = fields(:post) do |f|
+ concat f.text_field(:title)
+ concat f.text_area(:body)
+ concat f.check_box(:secret)
+ end
+
+ expected =
+ "<input name='post[title]' type='text' value='Hello World' />" +
+ "<textarea name='post[body]'>\nBack to the hill and over it again!</textarea>" +
+ "<input name='post[secret]' type='hidden' value='0' />" +
+ "<input name='post[secret]' checked='checked' type='checkbox' value='1' />"
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_fields_with_only_object
+ output_buffer = fields(model: @post) do |f|
+ concat f.text_field(:title)
+ concat f.text_area(:body)
+ concat f.check_box(:secret)
+ end
+
+ expected =
+ "<input name='post[title]' type='text' value='Hello World' />" +
+ "<textarea name='post[body]'>\nBack to the hill and over it again!</textarea>" +
+ "<input name='post[secret]' type='hidden' value='0' />" +
+ "<input name='post[secret]' checked='checked' type='checkbox' value='1' />"
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_fields_object_with_bracketed_name
+ output_buffer = fields("author[post]", model: @post) do |f|
+ concat f.label(:title)
+ concat f.text_field(:title)
+ end
+
+ assert_dom_equal "<label for=\"author_post_title\">Title</label>" +
+ "<input name='author[post][title]' type='text' value='Hello World' />",
+ output_buffer
+ end
+
+ def test_fields_object_with_bracketed_name_and_index
+ output_buffer = fields("author[post]", model: @post, index: 1) do |f|
+ concat f.label(:title)
+ concat f.text_field(:title)
+ end
+
+ assert_dom_equal "<label for=\"author_post_1_title\">Title</label>" +
+ "<input name='author[post][1][title]' type='text' value='Hello World' />",
+ output_buffer
+ end
+
+ def test_form_builder_does_not_have_form_with_method
+ assert_not_includes ActionView::Helpers::FormBuilder.instance_methods, :form_with
+ end
+
+ def test_form_with_and_fields
+ form_with(model: @post, scope: :post, id: "create-post") do |post_form|
+ concat post_form.text_field(:title)
+ concat post_form.text_area(:body)
+
+ concat fields(:parent_post, model: @post) { |parent_fields|
+ concat parent_fields.check_box(:secret)
+ }
+ end
+
+ expected = whole_form("/posts/123", "create-post", method: "patch") do
+ "<input name='post[title]' type='text' value='Hello World' />" +
+ "<textarea name='post[body]'>\nBack to the hill and over it again!</textarea>" +
+ "<input name='parent_post[secret]' type='hidden' value='0' />" +
+ "<input name='parent_post[secret]' checked='checked' type='checkbox' value='1' />"
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_form_with_and_fields_with_object
+ form_with(model: @post, scope: :post, id: "create-post") do |post_form|
+ concat post_form.text_field(:title)
+ concat post_form.text_area(:body)
+
+ concat post_form.fields(model: @comment) { |comment_fields|
+ concat comment_fields.text_field(:name)
+ }
+ end
+
+ expected = whole_form("/posts/123", "create-post", method: "patch") do
+ "<input name='post[title]' type='text' value='Hello World' />" +
+ "<textarea name='post[body]'>\nBack to the hill and over it again!</textarea>" +
+ "<input name='post[comment][name]' type='text' value='new comment' />"
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_form_with_and_fields_with_non_nested_association_and_without_object
+ form_with(model: @post) do |f|
+ concat f.fields(:category) { |c|
+ concat c.text_field(:name)
+ }
+ end
+
+ expected = whole_form("/posts/123", method: "patch") do
+ "<input name='post[category][name]' type='text' />"
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ class LabelledFormBuilder < ActionView::Helpers::FormBuilder
+ (field_helpers - %w(hidden_field)).each do |selector|
+ class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
+ def #{selector}(field, *args, &proc)
+ ("<label for='\#{field}'>\#{field.to_s.humanize}:</label> " + super + "<br/>").html_safe
+ end
+ RUBY_EVAL
+ end
+ end
+
+ def test_form_with_with_labelled_builder
+ form_with(model: @post, builder: LabelledFormBuilder) do |f|
+ concat f.text_field(:title)
+ concat f.text_area(:body)
+ concat f.check_box(:secret)
+ end
+
+ expected = whole_form("/posts/123", method: "patch") do
+ "<label for='title'>Title:</label> <input name='post[title]' type='text' value='Hello World' /><br/>" +
+ "<label for='body'>Body:</label> <textarea name='post[body]'>\nBack to the hill and over it again!</textarea><br/>" +
+ "<label for='secret'>Secret:</label> <input name='post[secret]' type='hidden' value='0' /><input name='post[secret]' checked='checked' type='checkbox' value='1' /><br/>"
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_default_form_builder
+ old_default_form_builder, ActionView::Base.default_form_builder =
+ ActionView::Base.default_form_builder, LabelledFormBuilder
+
+ form_with(model: @post) do |f|
+ concat f.text_field(:title)
+ concat f.text_area(:body)
+ concat f.check_box(:secret)
+ end
+
+ expected = whole_form("/posts/123", method: "patch") do
+ "<label for='title'>Title:</label> <input name='post[title]' type='text' value='Hello World' /><br/>" +
+ "<label for='body'>Body:</label> <textarea name='post[body]'>\nBack to the hill and over it again!</textarea><br/>" +
+ "<label for='secret'>Secret:</label> <input name='post[secret]' type='hidden' value='0' /><input name='post[secret]' checked='checked' type='checkbox' value='1' /><br/>"
+ end
+
+ assert_dom_equal expected, output_buffer
+ ensure
+ ActionView::Base.default_form_builder = old_default_form_builder
+ end
+
+ def test_lazy_loading_default_form_builder
+ old_default_form_builder, ActionView::Base.default_form_builder =
+ ActionView::Base.default_form_builder, "FormWithActsLikeFormForTest::LabelledFormBuilder"
+
+ form_with(model: @post) do |f|
+ concat f.text_field(:title)
+ end
+
+ expected = whole_form("/posts/123", method: "patch") do
+ "<label for='title'>Title:</label> <input name='post[title]' type='text' value='Hello World' /><br/>"
+ end
+
+ assert_dom_equal expected, output_buffer
+ ensure
+ ActionView::Base.default_form_builder = old_default_form_builder
+ end
+
+ def test_form_builder_override
+ self.default_form_builder = LabelledFormBuilder
+
+ output_buffer = fields(:post, model: @post) do |f|
+ concat f.text_field(:title)
+ end
+
+ expected = "<label for='title'>Title:</label> <input name='post[title]' type='text' value='Hello World' /><br/>"
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_lazy_loading_form_builder_override
+ self.default_form_builder = "FormWithActsLikeFormForTest::LabelledFormBuilder"
+
+ output_buffer = fields(:post, model: @post) do |f|
+ concat f.text_field(:title)
+ end
+
+ expected = "<label for='title'>Title:</label> <input name='post[title]' type='text' value='Hello World' /><br/>"
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_fields_with_labelled_builder
+ output_buffer = fields(:post, model: @post, builder: LabelledFormBuilder) do |f|
+ concat f.text_field(:title)
+ concat f.text_area(:body)
+ concat f.check_box(:secret)
+ end
+
+ expected =
+ "<label for='title'>Title:</label> <input name='post[title]' type='text' value='Hello World' /><br/>" +
+ "<label for='body'>Body:</label> <textarea name='post[body]'>\nBack to the hill and over it again!</textarea><br/>" +
+ "<label for='secret'>Secret:</label> <input name='post[secret]' type='hidden' value='0' /><input name='post[secret]' checked='checked' type='checkbox' value='1' /><br/>"
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_form_with_with_labelled_builder_with_nested_fields_without_options_hash
+ klass = nil
+
+ form_with(model: @post, builder: LabelledFormBuilder) do |f|
+ f.fields(:comments, model: Comment.new) do |nested_fields|
+ klass = nested_fields.class
+ ""
+ end
+ end
+
+ assert_equal LabelledFormBuilder, klass
+ end
+
+ def test_form_with_with_labelled_builder_with_nested_fields_with_options_hash
+ klass = nil
+
+ form_with(model: @post, builder: LabelledFormBuilder) do |f|
+ f.fields(:comments, model: Comment.new, index: "foo") do |nested_fields|
+ klass = nested_fields.class
+ ""
+ end
+ end
+
+ assert_equal LabelledFormBuilder, klass
+ end
+
+ def test_form_with_with_labelled_builder_path
+ path = nil
+
+ form_with(model: @post, builder: LabelledFormBuilder) do |f|
+ path = f.to_partial_path
+ ""
+ end
+
+ assert_equal "labelled_form", path
+ end
+
+ class LabelledFormBuilderSubclass < LabelledFormBuilder; end
+
+ def test_form_with_with_labelled_builder_with_nested_fields_with_custom_builder
+ klass = nil
+
+ form_with(model: @post, builder: LabelledFormBuilder) do |f|
+ f.fields(:comments, model: Comment.new, builder: LabelledFormBuilderSubclass) do |nested_fields|
+ klass = nested_fields.class
+ ""
+ end
+ end
+
+ assert_equal LabelledFormBuilderSubclass, klass
+ end
+
+ def test_form_with_with_html_options_adds_options_to_form_tag
+ form_with(model: @post, html: { id: "some_form", class: "some_class", multipart: true }) do |f| end
+ expected = whole_form("/posts/123", "some_form", "some_class", method: "patch", multipart: "multipart/form-data")
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_form_with_with_string_url_option
+ form_with(model: @post, url: "http://www.otherdomain.com") do |f| end
+
+ assert_dom_equal whole_form("http://www.otherdomain.com", method: "patch"), output_buffer
+ end
+
+ def test_form_with_with_hash_url_option
+ form_with(model: @post, url: { controller: "controller", action: "action" }) do |f| end
+
+ assert_equal "controller", @url_for_options[:controller]
+ assert_equal "action", @url_for_options[:action]
+ end
+
+ def test_form_with_with_record_url_option
+ form_with(model: @post, url: @post) do |f| end
+
+ expected = whole_form("/posts/123", method: "patch")
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_form_with_with_existing_object
+ form_with(model: @post) do |f| end
+
+ expected = whole_form("/posts/123", method: "patch")
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_form_with_with_new_object
+ post = Post.new
+ post.persisted = false
+ def post.to_key; nil; end
+
+ form_with(model: post) {}
+
+ expected = whole_form("/posts")
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_form_with_with_existing_object_in_list
+ @comment.save
+ 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]) {}
+
+ expected = whole_form(post_comments_path(@post))
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_form_with_with_existing_object_and_namespace_in_list
+ @comment.save
+ 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]) {}
+
+ expected = whole_form(admin_post_comments_path(@post))
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_form_with_with_existing_object_and_custom_url
+ form_with(model: @post, url: "/super_posts") do |f| end
+
+ expected = whole_form("/super_posts", method: "patch")
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_form_with_with_default_method_as_patch
+ 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" }) {}
+ assert_match %r|data-behavior="stuff"|, output_buffer
+ assert_match %r|data-remote="true"|, output_buffer
+ end
+
+ def test_fields_returns_block_result
+ output = fields(model: Post.new) { |f| "fields" }
+ assert_equal "fields", output
+ end
+
+ def test_form_with_only_instantiates_builder_once
+ initialization_count = 0
+ builder_class = Class.new(ActionView::Helpers::FormBuilder) do
+ define_method :initialize do |*args|
+ super(*args)
+ initialization_count += 1
+ end
+ end
+
+ form_with(model: @post, builder: builder_class) {}
+ assert_equal 1, initialization_count, "form builder instantiated more than once"
+ end
+
+ private
+ def hidden_fields(options = {})
+ method = options[:method]
+
+ if options.fetch(:skip_enforcing_utf8, false)
+ txt = ""
+ else
+ txt = %{<input name="utf8" type="hidden" value="&#x2713;" />}
+ end
+
+ if method && !%w(get post).include?(method.to_s)
+ txt << %{<input name="_method" type="hidden" value="#{method}" />}
+ end
+
+ txt
+ end
+
+ def form_text(action = "/", id = nil, html_class = nil, local = nil, multipart = nil, method = nil)
+ 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
+ txt << %{ id="#{id}"} if id
+ method = method.to_s == "get" ? "get" : "post"
+ txt << %{ method="#{method}">}
+ end
+
+ def whole_form(action = "/", id = nil, html_class = nil, local: false, **options)
+ contents = block_given? ? yield : ""
+
+ method, multipart = options.values_at(:method, :multipart)
+
+ form_text(action, id, html_class, local, multipart, method) + hidden_fields(options.slice :method, :skip_enforcing_utf8) + contents + "</form>"
+ end
+
+ def protect_against_forgery?
+ false
+ end
+
+ def with_locale(testing_locale = :label)
+ old_locale, I18n.locale = I18n.locale, testing_locale
+ yield
+ ensure
+ I18n.locale = old_locale
+ end
+end
diff --git a/actionview/test/template/form_helper_test.rb b/actionview/test/template/form_helper_test.rb
index 395c4e4f41..2bc0434771 100644
--- a/actionview/test/template/form_helper_test.rb
+++ b/actionview/test/template/form_helper_test.rb
@@ -257,7 +257,7 @@ class FormHelperTest < ActionView::TestCase
end
def test_label_with_non_active_record_object
- form_for(OpenStruct.new(name:"ok"), as: "person", url: "an_url", html: { id: "create-person" }) do |f|
+ form_for(OpenStruct.new(name: "ok"), as: "person", url: "an_url", html: { id: "create-person" }) do |f|
f.label(:name)
end
@@ -600,7 +600,7 @@ class FormHelperTest < ActionView::TestCase
def test_check_box_checked_if_option_checked_is_present
assert_dom_equal(
'<input name="post[secret]" type="hidden" value="0" /><input checked="checked" id="post_secret" name="post[secret]" type="checkbox" value="1" />',
- check_box("post", "secret", "checked"=>"checked")
+ check_box("post", "secret", "checked" => "checked")
)
end
@@ -746,7 +746,7 @@ class FormHelperTest < ActionView::TestCase
end
def test_check_box_with_multiple_behavior
- @post.comment_ids = [2,3]
+ @post.comment_ids = [2, 3]
assert_dom_equal(
'<input name="post[comment_ids][]" type="hidden" value="0" /><input id="post_comment_ids_1" name="post[comment_ids][]" type="checkbox" value="1" />',
check_box("post", "comment_ids", { multiple: true }, 1)
@@ -758,7 +758,7 @@ class FormHelperTest < ActionView::TestCase
end
def test_check_box_with_multiple_behavior_and_index
- @post.comment_ids = [2,3]
+ @post.comment_ids = [2, 3]
assert_dom_equal(
'<input name="post[foo][comment_ids][]" type="hidden" value="0" /><input id="post_foo_comment_ids_1" name="post[foo][comment_ids][]" type="checkbox" value="1" />',
check_box("post", "comment_ids", { multiple: true, index: "foo" }, 1)
@@ -1033,7 +1033,7 @@ class FormHelperTest < ActionView::TestCase
def test_date_field_with_value_attr
expected = %{<input id="post_written_on" name="post[written_on]" type="date" value="2013-06-29" />}
- value = Date.new(2013,6,29)
+ value = Date.new(2013, 6, 29)
assert_dom_equal(expected, date_field("post", "written_on", value: value))
end
@@ -1141,7 +1141,7 @@ class FormHelperTest < ActionView::TestCase
def test_datetime_field_with_value_attr
expected = %{<input id="post_written_on" name="post[written_on]" type="datetime-local" value="2013-06-29T13:37:00+00:00" />}
- value = DateTime.new(2013,6,29,13,37)
+ value = DateTime.new(2013, 6, 29, 13, 37)
assert_dom_equal(expected, datetime_field("post", "written_on", value: value))
end
@@ -1436,7 +1436,7 @@ class FormHelperTest < ActionView::TestCase
)
assert_dom_equal(
%{<input id="post_#{pid}_title" name="post[#{pid}][title]" type="text" value="Hello World" />},
- text_field("post[]","title")
+ text_field("post[]", "title")
)
assert_dom_equal(
%{<textarea id="post_#{pid}_body" name="post[#{pid}][body]">\nBack to the hill and over it again!</textarea>},
@@ -1559,10 +1559,10 @@ class FormHelperTest < ActionView::TestCase
expected = whole_form("/posts", "new_post", "new_post") do
"<input type='hidden' name='post[active]' value='' />" +
- "<label for='post_active_true'>"+
+ "<label for='post_active_true'>" +
"<input id='post_active_true' name='post[active]' type='radio' value='true' />" +
"true</label>" +
- "<label for='post_active_false'>"+
+ "<label for='post_active_false'>" +
"<input checked='checked' id='post_active_false' name='post[active]' type='radio' value='false' />" +
"false</label>"
end
@@ -1585,12 +1585,12 @@ class FormHelperTest < ActionView::TestCase
expected = whole_form("/posts", "new_post_1", "new_post") do
"<input type='hidden' name='post[active]' value='' />" +
- "<label for='post_active_true'>"+
+ "<label for='post_active_true'>" +
"<input id='post_active_true' name='post[active]' type='radio' value='true' />" +
"true</label>" +
- "<label for='post_active_false'>"+
+ "<label for='post_active_false'>" +
"<input checked='checked' id='post_active_false' name='post[active]' type='radio' value='false' />" +
- "false</label>"+
+ "false</label>" +
"<input id='post_id' name='post[id]' type='hidden' value='1' />"
end
@@ -1698,7 +1698,7 @@ class FormHelperTest < ActionView::TestCase
end
expected = whole_form("/posts", "new_post_1", "new_post") do
- "<input name='post[tag_ids][]' type='hidden' value='' />"+
+ "<input name='post[tag_ids][]' type='hidden' value='' />" +
"<label for='post_tag_ids_1'>" +
"<input checked='checked' id='post_tag_ids_1' name='post[tag_ids][]' type='checkbox' value='1' />" +
"Tag 1</label>" +
@@ -1751,8 +1751,6 @@ class FormHelperTest < ActionView::TestCase
end
def test_form_for_with_file_field_generate_multipart
- Post.send :attr_accessor, :file
-
form_for(@post, html: { id: "create-post" }) do |f|
concat f.file_field(:file)
end
@@ -1765,8 +1763,6 @@ class FormHelperTest < ActionView::TestCase
end
def test_fields_for_with_file_field_generate_multipart
- Comment.send :attr_accessor, :file
-
form_for(@post) do |f|
concat f.fields_for(:comment, @post) { |c|
concat c.file_field(:file)
@@ -1817,7 +1813,7 @@ class FormHelperTest < ActionView::TestCase
concat f.submit("Create post")
end
- expected = whole_form("/posts/123", "create-post", "edit_other_name", method: "patch") do
+ expected = whole_form("/posts/123", "create-post", "edit_other_name", method: "patch") do
"<label for='other_name_title' class='post_title'>Title</label>" +
"<input name='other_name[title]' id='other_name_title' value='Hello World' type='text' />" +
"<textarea name='other_name[body]' id='other_name_body'>\nBack to the hill and over it again!</textarea>" +
@@ -1833,9 +1829,9 @@ class FormHelperTest < ActionView::TestCase
obj = Class.new do
private
- def private_property
- raise "This method should not be called."
- end
+ def private_property
+ raise "This method should not be called."
+ end
end.new
form_for(obj, as: "other_name", url: "/", html: { id: "edit-other-name" }) do |f|
@@ -1850,7 +1846,7 @@ class FormHelperTest < ActionView::TestCase
concat f.check_box(:secret)
end
- expected = whole_form("/", "create-post", "edit_post", method: "delete") do
+ expected = whole_form("/", "create-post", "edit_post", method: "delete") do
"<input name='post[title]' type='text' id='post_title' value='Hello World' />" +
"<textarea name='post[body]' id='post_body'>\nBack to the hill and over it again!</textarea>" +
"<input name='post[secret]' type='hidden' value='0' />" +
@@ -1867,7 +1863,7 @@ class FormHelperTest < ActionView::TestCase
concat f.check_box(:secret)
end
- expected = whole_form("/", "create-post", "edit_post", method: "delete") do
+ expected = whole_form("/", "create-post", "edit_post", method: "delete") do
"<input name='post[title]' type='text' id='post_title' value='Hello World' />" +
"<textarea name='post[body]' id='post_body'>\nBack to the hill and over it again!</textarea>" +
"<input name='post[secret]' type='hidden' value='0' />" +
@@ -1884,7 +1880,7 @@ class FormHelperTest < ActionView::TestCase
concat f.search_field(:title)
end
- expected = whole_form("/search", "search-post", "new_post", method: "get") do
+ expected = whole_form("/search", "search-post", "new_post", method: "get") do
"<input name='post[title]' type='search' id='post_title' />"
end
@@ -1898,7 +1894,7 @@ class FormHelperTest < ActionView::TestCase
concat f.check_box(:secret)
end
- expected = whole_form("/", "create-post", "edit_post", method: "patch", remote: true) do
+ expected = whole_form("/", "create-post", "edit_post", method: "patch", remote: true) do
"<input name='post[title]' type='text' id='post_title' value='Hello World' />" +
"<textarea name='post[body]' id='post_body'>\nBack to the hill and over it again!</textarea>" +
"<input name='post[secret]' type='hidden' value='0' />" +
@@ -1939,7 +1935,7 @@ class FormHelperTest < ActionView::TestCase
concat f.check_box(:secret)
end
- expected = whole_form("/", "create-post", "edit_post", method: "patch", remote: true) do
+ expected = whole_form("/", "create-post", "edit_post", method: "patch", remote: true) do
"<input name='post[title]' type='text' id='post_title' value='Hello World' />" +
"<textarea name='post[body]' id='post_body'>\nBack to the hill and over it again!</textarea>" +
"<input name='post[secret]' type='hidden' value='0' />" +
@@ -1958,7 +1954,7 @@ class FormHelperTest < ActionView::TestCase
concat f.check_box(:secret)
end
- expected = whole_form("/posts", "new_post", "new_post", remote: true) do
+ expected = whole_form("/posts", "new_post", "new_post", remote: true) do
"<input name='post[title]' type='text' id='post_title' value='Hello World' />" +
"<textarea name='post[body]' id='post_body'>\nBack to the hill and over it again!</textarea>" +
"<input name='post[secret]' type='hidden' value='0' />" +
@@ -2266,11 +2262,13 @@ class FormHelperTest < ActionView::TestCase
concat f.fields_for("comment[]", @comment) { |c|
concat c.text_field(:name)
}
+ concat f.text_field(:body)
end
expected = whole_form("/posts/123", "edit_post[]", "edit_post[]", method: "patch") do
"<input name='post[123][title]' type='text' id='post_123_title' value='Hello World' />" +
- "<input name='post[123][comment][][name]' type='text' id='post_123_comment__name' value='new comment' />"
+ "<input name='post[123][comment][][name]' type='text' id='post_123_comment__name' value='new comment' />" +
+ "<input name='post[123][body]' type='text' id='post_123_body' value='Back to the hill and over it again!' />"
end
assert_dom_equal expected, output_buffer
@@ -2853,7 +2851,7 @@ class FormHelperTest < ActionView::TestCase
@post.comments = []
form_for(@post) do |f|
- concat f.fields_for(:comments, Comment.new(321), child_index: -> { "abc" } ) { |cf|
+ concat f.fields_for(:comments, Comment.new(321), child_index: -> { "abc" }) { |cf|
concat cf.text_field(:name)
}
end
@@ -3443,7 +3441,7 @@ class FormHelperTest < ActionView::TestCase
assert_equal 1, initialization_count, "form builder instantiated more than once"
end
- protected
+ private
def hidden_fields(options = {})
method = options[:method]
diff --git a/actionview/test/template/form_options_helper_test.rb b/actionview/test/template/form_options_helper_test.rb
index 477d4f9eca..51f5d25958 100644
--- a/actionview/test/template/form_options_helper_test.rb
+++ b/actionview/test/template/form_options_helper_test.rb
@@ -258,7 +258,7 @@ class FormOptionsHelperTest < ActionView::TestCase
end
def test_collection_options_with_preselected_value_as_string_and_option_value_is_integer
- albums = [ Album.new(1, "first","rap"), Album.new(2, "second","pop")]
+ albums = [ Album.new(1, "first", "rap"), Album.new(2, "second", "pop")]
assert_dom_equal(
%(<option selected="selected" value="1">rap</option>\n<option value="2">pop</option>),
options_from_collection_for_select(albums, "id", "genre", selected: "1")
@@ -266,7 +266,7 @@ class FormOptionsHelperTest < ActionView::TestCase
end
def test_collection_options_with_preselected_value_as_integer_and_option_value_is_string
- albums = [ Album.new("1", "first","rap"), Album.new("2", "second","pop")]
+ albums = [ Album.new("1", "first", "rap"), Album.new("2", "second", "pop")]
assert_dom_equal(
%(<option selected="selected" value="1">rap</option>\n<option value="2">pop</option>),
@@ -275,7 +275,7 @@ class FormOptionsHelperTest < ActionView::TestCase
end
def test_collection_options_with_preselected_value_as_string_and_option_value_is_float
- albums = [ Album.new(1.0, "first","rap"), Album.new(2.0, "second","pop")]
+ albums = [ Album.new(1.0, "first", "rap"), Album.new(2.0, "second", "pop")]
assert_dom_equal(
%(<option value="1.0">rap</option>\n<option value="2.0" selected="selected">pop</option>),
@@ -284,7 +284,7 @@ class FormOptionsHelperTest < ActionView::TestCase
end
def test_collection_options_with_preselected_value_as_nil
- albums = [ Album.new(1.0, "first","rap"), Album.new(2.0, "second","pop")]
+ albums = [ Album.new(1.0, "first", "rap"), Album.new(2.0, "second", "pop")]
assert_dom_equal(
%(<option value="1.0">rap</option>\n<option value="2.0">pop</option>),
@@ -293,7 +293,7 @@ class FormOptionsHelperTest < ActionView::TestCase
end
def test_collection_options_with_disabled_value_as_nil
- albums = [ Album.new(1.0, "first","rap"), Album.new(2.0, "second","pop")]
+ albums = [ Album.new(1.0, "first", "rap"), Album.new(2.0, "second", "pop")]
assert_dom_equal(
%(<option value="1.0">rap</option>\n<option value="2.0">pop</option>),
@@ -302,7 +302,7 @@ class FormOptionsHelperTest < ActionView::TestCase
end
def test_collection_options_with_disabled_value_as_array
- albums = [ Album.new(1.0, "first","rap"), Album.new(2.0, "second","pop")]
+ albums = [ Album.new(1.0, "first", "rap"), Album.new(2.0, "second", "pop")]
assert_dom_equal(
%(<option disabled="disabled" value="1.0">rap</option>\n<option disabled="disabled" value="2.0">pop</option>),
@@ -311,11 +311,11 @@ class FormOptionsHelperTest < ActionView::TestCase
end
def test_collection_options_with_preselected_values_as_string_array_and_option_value_is_float
- albums = [ Album.new(1.0, "first","rap"), Album.new(2.0, "second","pop"), Album.new(3.0, "third","country") ]
+ albums = [ Album.new(1.0, "first", "rap"), Album.new(2.0, "second", "pop"), Album.new(3.0, "third", "country") ]
assert_dom_equal(
%(<option value="1.0" selected="selected">rap</option>\n<option value="2.0">pop</option>\n<option value="3.0" selected="selected">country</option>),
- options_from_collection_for_select(albums, "id", "genre", ["1.0","3.0"])
+ options_from_collection_for_select(albums, "id", "genre", ["1.0", "3.0"])
)
end
@@ -335,9 +335,9 @@ class FormOptionsHelperTest < ActionView::TestCase
"<optgroup label=\"North America\"><option value=\"US\">United States</option>\n<option value=\"Canada\">Canada</option></optgroup><optgroup label=\"Europe\"><option value=\"GB\">Great Britain</option>\n<option value=\"Germany\">Germany</option></optgroup>",
grouped_options_for_select([
["North America",
- [["United States","US"],"Canada"]],
+ [["United States", "US"], "Canada"]],
["Europe",
- [["Great Britain","GB"], "Germany"]]
+ [["Great Britain", "GB"], "Germany"]]
])
)
end
@@ -346,8 +346,8 @@ class FormOptionsHelperTest < ActionView::TestCase
assert_dom_equal(
"<optgroup label=\"North America\" data-foo=\"bar\"><option value=\"US\">United States</option>\n<option value=\"Canada\">Canada</option></optgroup><optgroup label=\"Europe\" disabled=\"disabled\"><option value=\"GB\">Great Britain</option>\n<option value=\"Germany\">Germany</option></optgroup>",
grouped_options_for_select([
- ["North America", [["United States","US"],"Canada"], data: { foo: "bar" }],
- ["Europe", [["Great Britain","GB"], "Germany"], disabled: "disabled"]
+ ["North America", [["United States", "US"], "Canada"], data: { foo: "bar" }],
+ ["Europe", [["Great Britain", "GB"], "Germany"], disabled: "disabled"]
])
)
end
@@ -356,38 +356,38 @@ class FormOptionsHelperTest < ActionView::TestCase
assert_dom_equal(
"<optgroup label=\"----------\"><option value=\"US\">US</option>\n<option value=\"Canada\">Canada</option></optgroup><optgroup label=\"----------\"><option value=\"GB\">GB</option>\n<option value=\"Germany\">Germany</option></optgroup>",
- grouped_options_for_select([["US","Canada"] , ["GB", "Germany"]], nil, divider: "----------")
+ grouped_options_for_select([["US", "Canada"] , ["GB", "Germany"]], nil, divider: "----------")
)
end
def test_grouped_options_for_select_with_selected_and_prompt
assert_dom_equal(
"<option value=\"\">Choose a product...</option><optgroup label=\"Hats\"><option value=\"Baseball Cap\">Baseball Cap</option>\n<option selected=\"selected\" value=\"Cowboy Hat\">Cowboy Hat</option></optgroup>",
- grouped_options_for_select([["Hats", ["Baseball Cap","Cowboy Hat"]]], "Cowboy Hat", prompt: "Choose a product...")
+ grouped_options_for_select([["Hats", ["Baseball Cap", "Cowboy Hat"]]], "Cowboy Hat", prompt: "Choose a product...")
)
end
def test_grouped_options_for_select_with_selected_and_prompt_true
assert_dom_equal(
"<option value=\"\">Please select</option><optgroup label=\"Hats\"><option value=\"Baseball Cap\">Baseball Cap</option>\n<option selected=\"selected\" value=\"Cowboy Hat\">Cowboy Hat</option></optgroup>",
- grouped_options_for_select([["Hats", ["Baseball Cap","Cowboy Hat"]]], "Cowboy Hat", prompt: true)
+ grouped_options_for_select([["Hats", ["Baseball Cap", "Cowboy Hat"]]], "Cowboy Hat", prompt: true)
)
end
def test_grouped_options_for_select_returns_html_safe_string
- assert grouped_options_for_select([["Hats", ["Baseball Cap","Cowboy Hat"]]]).html_safe?
+ assert grouped_options_for_select([["Hats", ["Baseball Cap", "Cowboy Hat"]]]).html_safe?
end
def test_grouped_options_for_select_with_prompt_returns_html_escaped_string
assert_dom_equal(
"<option value=\"\">&lt;Choose One&gt;</option><optgroup label=\"Hats\"><option value=\"Baseball Cap\">Baseball Cap</option>\n<option value=\"Cowboy Hat\">Cowboy Hat</option></optgroup>",
- grouped_options_for_select([["Hats", ["Baseball Cap","Cowboy Hat"]]], nil, prompt: "<Choose One>"))
+ grouped_options_for_select([["Hats", ["Baseball Cap", "Cowboy Hat"]]], nil, prompt: "<Choose One>"))
end
def test_optgroups_with_with_options_with_hash
assert_dom_equal(
"<optgroup label=\"North America\"><option value=\"United States\">United States</option>\n<option value=\"Canada\">Canada</option></optgroup><optgroup label=\"Europe\"><option value=\"Denmark\">Denmark</option>\n<option value=\"Germany\">Germany</option></optgroup>",
- grouped_options_for_select("North America" => ["United States","Canada"], "Europe" => ["Denmark","Germany"])
+ grouped_options_for_select("North America" => ["United States", "Canada"], "Europe" => ["Denmark", "Germany"])
)
end
@@ -402,7 +402,7 @@ class FormOptionsHelperTest < ActionView::TestCase
end
def test_time_zone_options_with_selected
- opts = time_zone_options_for_select( "D" )
+ opts = time_zone_options_for_select("D")
assert_dom_equal "<option value=\"A\">A</option>\n" +
"<option value=\"B\">B</option>\n" +
"<option value=\"C\">C</option>\n" +
@@ -412,7 +412,7 @@ class FormOptionsHelperTest < ActionView::TestCase
end
def test_time_zone_options_with_unknown_selected
- opts = time_zone_options_for_select( "K" )
+ opts = time_zone_options_for_select("K")
assert_dom_equal "<option value=\"A\">A</option>\n" +
"<option value=\"B\">B</option>\n" +
"<option value=\"C\">C</option>\n" +
@@ -422,8 +422,8 @@ class FormOptionsHelperTest < ActionView::TestCase
end
def test_time_zone_options_with_priority_zones
- zones = [ ActiveSupport::TimeZone.new( "B" ), ActiveSupport::TimeZone.new( "E" ) ]
- opts = time_zone_options_for_select( nil, zones )
+ zones = [ ActiveSupport::TimeZone.new("B"), ActiveSupport::TimeZone.new("E") ]
+ opts = time_zone_options_for_select(nil, zones)
assert_dom_equal "<option value=\"B\">B</option>\n" +
"<option value=\"E\">E</option>" +
"<option value=\"\" disabled=\"disabled\">-------------</option>\n" +
@@ -434,8 +434,8 @@ class FormOptionsHelperTest < ActionView::TestCase
end
def test_time_zone_options_with_selected_priority_zones
- zones = [ ActiveSupport::TimeZone.new( "B" ), ActiveSupport::TimeZone.new( "E" ) ]
- opts = time_zone_options_for_select( "E", zones )
+ zones = [ ActiveSupport::TimeZone.new("B"), ActiveSupport::TimeZone.new("E") ]
+ opts = time_zone_options_for_select("E", zones)
assert_dom_equal "<option value=\"B\">B</option>\n" +
"<option value=\"E\" selected=\"selected\">E</option>" +
"<option value=\"\" disabled=\"disabled\">-------------</option>\n" +
@@ -446,8 +446,8 @@ class FormOptionsHelperTest < ActionView::TestCase
end
def test_time_zone_options_with_unselected_priority_zones
- zones = [ ActiveSupport::TimeZone.new( "B" ), ActiveSupport::TimeZone.new( "E" ) ]
- opts = time_zone_options_for_select( "C", zones )
+ zones = [ ActiveSupport::TimeZone.new("B"), ActiveSupport::TimeZone.new("E") ]
+ opts = time_zone_options_for_select("C", zones)
assert_dom_equal "<option value=\"B\">B</option>\n" +
"<option value=\"E\">E</option>" +
"<option value=\"\" disabled=\"disabled\">-------------</option>\n" +
@@ -459,7 +459,7 @@ class FormOptionsHelperTest < ActionView::TestCase
def test_time_zone_options_with_priority_zones_does_not_mutate_time_zones
original_zones = ActiveSupport::TimeZone.all.dup
- zones = [ ActiveSupport::TimeZone.new( "B" ), ActiveSupport::TimeZone.new( "E" ) ]
+ zones = [ ActiveSupport::TimeZone.new("B"), ActiveSupport::TimeZone.new("E") ]
time_zone_options_for_select(nil, zones)
assert_equal original_zones, ActiveSupport::TimeZone.all
end
@@ -628,7 +628,7 @@ class FormOptionsHelperTest < ActionView::TestCase
end
def test_select_with_multiple_to_add_hidden_input
- output_buffer = select(:post, :category, "", {}, multiple: true)
+ output_buffer = select(:post, :category, "", {}, multiple: true)
assert_dom_equal(
"<input type=\"hidden\" name=\"post[category][]\" value=\"\"/><select multiple=\"multiple\" id=\"post_category\" name=\"post[category][]\"></select>",
output_buffer
@@ -636,7 +636,7 @@ class FormOptionsHelperTest < ActionView::TestCase
end
def test_select_with_multiple_and_without_hidden_input
- output_buffer = select(:post, :category, "", { include_hidden: false }, multiple: true)
+ output_buffer = select(:post, :category, "", { include_hidden: false }, multiple: true)
assert_dom_equal(
"<select multiple=\"multiple\" id=\"post_category\" name=\"post[category][]\"></select>",
output_buffer
@@ -644,7 +644,7 @@ class FormOptionsHelperTest < ActionView::TestCase
end
def test_select_with_multiple_and_with_explicit_name_ending_with_brackets
- output_buffer = select(:post, :category, [], { include_hidden: false }, multiple: true, name: "post[category][]")
+ output_buffer = select(:post, :category, [], { include_hidden: false }, multiple: true, name: "post[category][]")
assert_dom_equal(
"<select multiple=\"multiple\" id=\"post_category\" name=\"post[category][]\"></select>",
output_buffer
@@ -652,7 +652,7 @@ class FormOptionsHelperTest < ActionView::TestCase
end
def test_select_with_multiple_and_disabled_to_add_disabled_hidden_input
- output_buffer = select(:post, :category, "", {}, multiple: true, disabled: true)
+ output_buffer = select(:post, :category, "", {}, multiple: true, disabled: true)
assert_dom_equal(
"<input disabled=\"disabled\"type=\"hidden\" name=\"post[category][]\" value=\"\"/><select multiple=\"multiple\" disabled=\"disabled\" id=\"post_category\" name=\"post[category][]\"></select>",
output_buffer
@@ -1030,7 +1030,7 @@ class FormOptionsHelperTest < ActionView::TestCase
def test_time_zone_select
@firm = Firm.new("D")
- html = time_zone_select( "firm", "time_zone" )
+ html = time_zone_select("firm", "time_zone")
assert_dom_equal "<select id=\"firm_time_zone\" name=\"firm[time_zone]\">" +
"<option value=\"A\">A</option>\n" +
"<option value=\"B\">B</option>\n" +
@@ -1180,7 +1180,7 @@ class FormOptionsHelperTest < ActionView::TestCase
def test_time_zone_select_with_priority_zones
@firm = Firm.new("D")
zones = [ ActiveSupport::TimeZone.new("A"), ActiveSupport::TimeZone.new("D") ]
- html = time_zone_select("firm", "time_zone", zones )
+ html = time_zone_select("firm", "time_zone", zones)
assert_dom_equal "<select id=\"firm_time_zone\" name=\"firm[time_zone]\">" +
"<option value=\"A\">A</option>\n" +
"<option value=\"D\" selected=\"selected\">D</option>" +
@@ -1236,7 +1236,7 @@ class FormOptionsHelperTest < ActionView::TestCase
@firm = Firm.new()
@firm.time_zone = nil
- html = time_zone_select( "firm", "time_zone", nil, default: "B" )
+ html = time_zone_select("firm", "time_zone", nil, default: "B")
assert_dom_equal "<select id=\"firm_time_zone\" name=\"firm[time_zone]\">" +
"<option value=\"A\">A</option>\n" +
"<option value=\"B\" selected=\"selected\">B</option>\n" +
@@ -1250,7 +1250,7 @@ class FormOptionsHelperTest < ActionView::TestCase
def test_time_zone_select_with_default_time_zone_and_value
@firm = Firm.new("D")
- html = time_zone_select( "firm", "time_zone", nil, default: "B" )
+ html = time_zone_select("firm", "time_zone", nil, default: "B")
assert_dom_equal "<select id=\"firm_time_zone\" name=\"firm[time_zone]\">" +
"<option value=\"A\">A</option>\n" +
"<option value=\"B\">B</option>\n" +
diff --git a/actionview/test/template/form_tag_helper_test.rb b/actionview/test/template/form_tag_helper_test.rb
index da929cac8f..1248a0bb09 100644
--- a/actionview/test/template/form_tag_helper_test.rb
+++ b/actionview/test/template/form_tag_helper_test.rb
@@ -65,13 +65,13 @@ class FormTagHelperTest < ActionView::TestCase
end
def test_check_box_tag_disabled
- actual = check_box_tag "admin","1", false, disabled: true
+ actual = check_box_tag "admin", "1", false, disabled: true
expected = %(<input id="admin" disabled="disabled" name="admin" type="checkbox" value="1" />)
assert_dom_equal expected, actual
end
def test_check_box_tag_default_checked
- actual = check_box_tag "admin","1", true
+ actual = check_box_tag "admin", "1", true
expected = %(<input id="admin" checked="checked" name="admin" type="checkbox" value="1" />)
assert_dom_equal expected, actual
end
@@ -694,31 +694,31 @@ class FormTagHelperTest < ActionView::TestCase
def test_text_area_tag_options_symbolize_keys_side_effects
options = { option: "random_option" }
text_area_tag "body", "hello world", options
- assert_equal options, option: "random_option"
+ assert_equal({ option: "random_option" }, options)
end
def test_submit_tag_options_symbolize_keys_side_effects
options = { option: "random_option" }
submit_tag "submit value", options
- assert_equal options, option: "random_option"
+ assert_equal({ option: "random_option" }, options)
end
def test_button_tag_options_symbolize_keys_side_effects
options = { option: "random_option" }
button_tag "button value", options
- assert_equal options, option: "random_option"
+ assert_equal({ option: "random_option" }, options)
end
def test_image_submit_tag_options_symbolize_keys_side_effects
options = { option: "random_option" }
image_submit_tag "submit source", options
- assert_equal options, option: "random_option"
+ assert_equal({ option: "random_option" }, options)
end
def test_image_label_tag_options_symbolize_keys_side_effects
options = { option: "random_option" }
label_tag "submit source", "title", options
- assert_equal options, option: "random_option"
+ assert_equal({ option: "random_option" }, options)
end
def protect_against_forgery?
diff --git a/actionview/test/template/javascript_helper_test.rb b/actionview/test/template/javascript_helper_test.rb
index 0468c845d2..c7670b056b 100644
--- a/actionview/test/template/javascript_helper_test.rb
+++ b/actionview/test/template/javascript_helper_test.rb
@@ -7,7 +7,7 @@ class JavaScriptHelperTest < ActionView::TestCase
setup do
@old_escape_html_entities_in_json = ActiveSupport.escape_html_entities_in_json
- ActiveSupport.escape_html_entities_in_json = true
+ ActiveSupport.escape_html_entities_in_json = true
@template = self
end
@@ -18,7 +18,7 @@ class JavaScriptHelperTest < ActionView::TestCase
def test_escape_javascript
assert_equal "", escape_javascript(nil)
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 %(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).force_encoding(Encoding::UTF_8).encode!)
assert_equal %(unicode &#x2029; newline), escape_javascript(%(unicode \342\200\251 newline).force_encoding(Encoding::UTF_8).encode!)
@@ -47,8 +47,8 @@ class JavaScriptHelperTest < ActionView::TestCase
# Setting the :extname option will control what extension (if any) is appended to the url for assets
def test_javascript_include_tag
assert_dom_equal "<script src='/foo.js'></script>", javascript_include_tag("/foo")
- assert_dom_equal "<script src='/foo'></script>", javascript_include_tag("/foo", extname: false )
- assert_dom_equal "<script src='/foo.bar'></script>", javascript_include_tag("/foo", extname: ".bar" )
+ assert_dom_equal "<script src='/foo'></script>", javascript_include_tag("/foo", extname: false)
+ assert_dom_equal "<script src='/foo.bar'></script>", javascript_include_tag("/foo", extname: ".bar")
end
def test_javascript_tag_with_options
diff --git a/actionview/test/template/number_helper_test.rb b/actionview/test/template/number_helper_test.rb
index 2a2ada2b36..678120a9c9 100644
--- a/actionview/test/template/number_helper_test.rb
+++ b/actionview/test/template/number_helper_test.rb
@@ -4,7 +4,7 @@ class NumberHelperTest < ActionView::TestCase
tests ActionView::Helpers::NumberHelper
def test_number_to_phone
- assert_equal nil, number_to_phone(nil)
+ assert_nil number_to_phone(nil)
assert_equal "555-1234", number_to_phone(5551234)
assert_equal "(800) 555-1212 x 123", number_to_phone(8005551212, area_code: true, extension: 123)
assert_equal "+18005551212", number_to_phone(8005551212, country_code: 1, delimiter: "")
@@ -13,7 +13,7 @@ class NumberHelperTest < ActionView::TestCase
end
def test_number_to_currency
- assert_equal nil, number_to_currency(nil)
+ assert_nil number_to_currency(nil)
assert_equal "$1,234,567,890.50", number_to_currency(1234567890.50)
assert_equal "$1,234,567,892", number_to_currency(1234567891.50, precision: 0)
assert_equal "1,234,567,890.50 - K&#269;", number_to_currency("-1234567890.50", unit: raw("K&#269;"), format: "%n %u", negative_format: "%n - %u")
@@ -25,7 +25,7 @@ class NumberHelperTest < ActionView::TestCase
end
def test_number_to_percentage
- assert_equal nil, number_to_percentage(nil)
+ assert_nil number_to_percentage(nil)
assert_equal "100.000%", number_to_percentage(100)
assert_equal "100.000 %", number_to_percentage(100, format: "%n %")
assert_equal "&lt;b&gt;100.000&lt;/b&gt; %", number_to_percentage(100, format: "<b>%n</b> %")
@@ -43,13 +43,13 @@ class NumberHelperTest < ActionView::TestCase
end
def test_number_with_delimiter
- assert_equal nil, number_with_delimiter(nil)
+ assert_nil number_with_delimiter(nil)
assert_equal "12,345,678", number_with_delimiter(12345678)
assert_equal "0", number_with_delimiter(0)
end
def test_number_with_precision
- assert_equal nil, number_with_precision(nil)
+ assert_nil number_with_precision(nil)
assert_equal "-111.235", number_with_precision(-111.2346)
assert_equal "111.00", number_with_precision(111, precision: 2)
assert_equal "0.00100", number_with_precision(0.001, precision: 5)
@@ -57,13 +57,13 @@ class NumberHelperTest < ActionView::TestCase
end
def test_number_to_human_size
- assert_equal nil, number_to_human_size(nil)
+ assert_nil number_to_human_size(nil)
assert_equal "3 Bytes", number_to_human_size(3.14159265)
assert_equal "1.2 MB", number_to_human_size(1234567, precision: 2)
end
def test_number_to_human
- assert_equal nil, number_to_human(nil)
+ assert_nil number_to_human(nil)
assert_equal "0", number_to_human(0)
assert_equal "1.23 Thousand", number_to_human(1234)
assert_equal "489.0 Thousand", number_to_human(489000, precision: 4, strip_insignificant_zeros: false)
diff --git a/actionview/test/template/output_safety_helper_test.rb b/actionview/test/template/output_safety_helper_test.rb
index 98b1938276..8691bb11ee 100644
--- a/actionview/test/template/output_safety_helper_test.rb
+++ b/actionview/test/template/output_safety_helper_test.rb
@@ -26,10 +26,10 @@ class OutputSafetyHelperTest < ActionView::TestCase
end
test "safe_join should work recursively similarly to Array.join" do
- joined = safe_join(["a",["b","c"]], ":")
+ joined = safe_join(["a", ["b", "c"]], ":")
assert_equal "a:b:c", joined
- joined = safe_join(['"a"',["<b>","<c>"]], " <br/> ")
+ joined = safe_join(['"a"', ["<b>", "<c>"]], " <br/> ")
assert_equal "&quot;a&quot; &lt;br/&gt; &lt;b&gt; &lt;br/&gt; &lt;c&gt;", joined
end
diff --git a/actionview/test/template/test_case_test.rb b/actionview/test/template/test_case_test.rb
index 41225000f0..3deddd5706 100644
--- a/actionview/test/template/test_case_test.rb
+++ b/actionview/test/template/test_case_test.rb
@@ -50,7 +50,7 @@ module ActionView
end
test "retrieve non existing config values" do
- assert_equal nil, ActionView::Base.new.config.something_odd
+ assert_nil ActionView::Base.new.config.something_odd
end
test "works without testing a helper module" do
diff --git a/actionview/test/template/text_helper_test.rb b/actionview/test/template/text_helper_test.rb
index fb557c24ac..251c98230f 100644
--- a/actionview/test/template/text_helper_test.rb
+++ b/actionview/test/template/text_helper_test.rb
@@ -316,7 +316,7 @@ class TextHelperTest < ActionView::TestCase
end
def test_excerpt_with_omission
- assert_equal("[...]is a beautiful morn[...]", excerpt("This is a beautiful morning", "beautiful", omission: "[...]",radius: 5))
+ assert_equal("[...]is a beautiful morn[...]", excerpt("This is a beautiful morning", "beautiful", omission: "[...]", radius: 5))
assert_equal(
"This is the ultimate supercalifragilisticexpialidoceous very looooooooooooooooooong looooooooooooong beautiful morning with amazing sunshine and awesome tempera[...]",
excerpt("This is the ultimate supercalifragilisticexpialidoceous very looooooooooooooooooong looooooooooooong beautiful morning with amazing sunshine and awesome temperatures. So what are you gonna do about it?", "very",
@@ -329,7 +329,7 @@ class TextHelperTest < ActionView::TestCase
end
def test_excerpt_does_not_modify_the_options_hash
- options = { omission: "[...]",radius: 5 }
+ options = { omission: "[...]", radius: 5 }
passed_options = options.dup
excerpt("This is a beautiful morning", "beautiful", passed_options)
assert_equal options, passed_options
@@ -463,11 +463,11 @@ class TextHelperTest < ActionView::TestCase
end
def test_current_cycle_with_default_name
- cycle("even","odd")
+ cycle("even", "odd")
assert_equal "even", current_cycle
- cycle("even","odd")
+ cycle("even", "odd")
assert_equal "odd", current_cycle
- cycle("even","odd")
+ cycle("even", "odd")
assert_equal "even", current_cycle
end
@@ -486,13 +486,13 @@ class TextHelperTest < ActionView::TestCase
end
def test_current_cycle_with_more_than_two_names
- cycle(1,2,3)
+ cycle(1, 2, 3)
assert_equal "1", current_cycle
- cycle(1,2,3)
+ cycle(1, 2, 3)
assert_equal "2", current_cycle
- cycle(1,2,3)
+ cycle(1, 2, 3)
assert_equal "3", current_cycle
- cycle(1,2,3)
+ cycle(1, 2, 3)
assert_equal "1", current_cycle
end
diff --git a/actionview/test/template/text_test.rb b/actionview/test/template/text_test.rb
index 6510688f97..ee526dc367 100644
--- a/actionview/test/template/text_test.rb
+++ b/actionview/test/template/text_test.rb
@@ -1,17 +1,7 @@
require "abstract_unit"
class TextTest < ActiveSupport::TestCase
- test "formats returns symbol for recognized MIME type" do
- assert_equal [:text], ActionView::Template::Text.new("", :text).formats
- end
-
- test "formats returns string for recognized MIME type when MIME does not have symbol" do
- foo = Mime::Type.lookup("foo")
- assert_nil foo.to_sym
- assert_equal ["foo"], ActionView::Template::Text.new("", foo).formats
- end
-
- test "formats returns string for unknown MIME type" do
- assert_equal ["foo"], ActionView::Template::Text.new("", "foo").formats
+ test "formats always return :text" do
+ assert_equal [:text], ActionView::Template::Text.new("").formats
end
end
diff --git a/actionview/test/template/url_helper_test.rb b/actionview/test/template/url_helper_test.rb
index 2ef2be65d2..1e64385b52 100644
--- a/actionview/test/template/url_helper_test.rb
+++ b/actionview/test/template/url_helper_test.rb
@@ -221,6 +221,33 @@ class UrlHelperTest < ActiveSupport::TestCase
)
end
+ class FakeParams
+ def initialize(permitted = true)
+ @permitted = permitted
+ end
+
+ def permitted?
+ @permitted
+ end
+
+ def to_h
+ { foo: :bar, baz: "quux" }
+ end
+ end
+
+ def test_button_to_with_permited_strong_params
+ assert_dom_equal(
+ %{<form action="http://www.example.com" class="button_to" method="post"><input type="submit" value="Hello" /><input type="hidden" name="baz" value="quux" /><input type="hidden" name="foo" value="bar" /></form>},
+ button_to("Hello", "http://www.example.com", params: FakeParams.new)
+ )
+ end
+
+ def test_button_to_with_unpermited_strong_params
+ assert_raises(ArgumentError) do
+ button_to("Hello", "http://www.example.com", params: FakeParams.new(false))
+ end
+ end
+
def test_button_to_with_nested_hash_params
assert_dom_equal(
%{<form action="http://www.example.com" class="button_to" method="post"><input type="submit" value="Hello" /><input type="hidden" name="foo[bar]" value="baz" /></form>},
@@ -783,7 +810,7 @@ class TasksController < ActionController::Base
render_default
end
- protected
+ private
def render_default
render inline: "<%= link_to_unless_current('tasks', tasks_path) %>\n" +
"<%= link_to_unless_current('tasks', tasks_url) %>"
diff --git a/actionview/test/ujs/.gitignore b/actionview/test/ujs/.gitignore
new file mode 100644
index 0000000000..31dbbff57c
--- /dev/null
+++ b/actionview/test/ujs/.gitignore
@@ -0,0 +1 @@
+/log
diff --git a/actionview/test/ujs/config.ru b/actionview/test/ujs/config.ru
new file mode 100644
index 0000000000..414c2063c3
--- /dev/null
+++ b/actionview/test/ujs/config.ru
@@ -0,0 +1,3 @@
+$LOAD_PATH.unshift File.expand_path("..", __FILE__)
+require "server"
+run UJS::Server
diff --git a/actionview/test/ujs/public/test/.eslintrc.yml b/actionview/test/ujs/public/test/.eslintrc.yml
new file mode 100644
index 0000000000..06d7dd36ea
--- /dev/null
+++ b/actionview/test/ujs/public/test/.eslintrc.yml
@@ -0,0 +1,21 @@
+env:
+ browser: true
+extends: eslint:recommended
+rules:
+ no-undef: off
+ no-unused-vars: off
+ indent: off
+ linebreak-style: ['error', 'unix']
+ quotes: ['error', 'single']
+ semi: ['error', 'never']
+ no-shadow: ['error'] # Prevent potential errors
+ no-console: 'off'
+ # styles
+ space-before-function-paren: ['error', 'never']
+ space-before-blocks: 'error'
+ brace-style: ['error', '1tbs', { allowSingleLine: true }]
+ key-spacing: 'error'
+ array-bracket-spacing: 'error'
+ comma-spacing: 'error'
+ comma-dangle: 'off'
+ eol-last: 'error'
diff --git a/actionview/test/ujs/public/test/call-remote-callbacks.js b/actionview/test/ujs/public/test/call-remote-callbacks.js
new file mode 100644
index 0000000000..082d10bfbd
--- /dev/null
+++ b/actionview/test/ujs/public/test/call-remote-callbacks.js
@@ -0,0 +1,469 @@
+(function() {
+
+module('call-remote-callbacks', {
+ setup: function() {
+ $('#qunit-fixture').append($('<form />', {
+ action: '/echo', method: 'get', 'data-remote': 'true'
+ }))
+ },
+ teardown: function() {
+ $(document).undelegate('form[data-remote]', 'ajax:beforeSend')
+ $(document).undelegate('form[data-remote]', 'ajax:before')
+ $(document).undelegate('form[data-remote]', 'ajax:send')
+ $(document).undelegate('form[data-remote]', 'ajax:complete')
+ $(document).undelegate('form[data-remote]', 'ajax:success')
+ $(document).unbind('ajaxStop')
+ $(document).unbind('iframe:loading')
+ }
+})
+
+function start_after_submit(form) {
+ form.bindNative('ajax:complete', function() {
+ ok(true, 'ajax:complete')
+ start()
+ })
+}
+
+function submit(fn) {
+ var form = $('form')
+ start_after_submit(form)
+
+ if (fn) fn(form)
+ form.triggerNative('submit')
+}
+
+function submit_with_button(submit_button) {
+ var form = $('form')
+ start_after_submit(form)
+
+ submit_button.triggerNative('click')
+}
+
+asyncTest('modifying form fields with "ajax:before" sends modified data in request', 4, function() {
+ $('form[data-remote]')
+ .append($('<input type="text" name="user_name" value="john">'))
+ .append($('<input type="text" name="removed_user_name" value="john">'))
+ .bindNative('ajax:before', function() {
+ var form = $(this)
+ form
+ .append($('<input />', {name: 'other_user_name', value: 'jonathan'}))
+ .find('input[name="removed_user_name"]').remove()
+ form
+ .find('input[name="user_name"]').val('steve')
+ })
+
+ submit(function(form) {
+ form.bindNative('ajax:success', function(e, data, status, xhr) {
+ equal(data.params.user_name, 'steve', 'modified field value should have been submitted')
+ equal(data.params.other_user_name, 'jonathan', 'added field value should have been submitted')
+ equal(data.params.removed_user_name, undefined, 'removed field value should be undefined')
+ })
+ })
+})
+
+asyncTest('modifying data("type") with "ajax:before" requests new dataType in request', 2, function() {
+ $('form[data-remote]').data('type', 'html')
+ .bindNative('ajax:before', function() {
+ this.setAttribute('data-type', 'xml')
+ })
+
+ submit(function(form) {
+ form.bindNative('ajax:beforeSend', function(e, xhr, settings) {
+ equal(settings.dataType, 'xml', 'modified dataType should have been requested')
+ })
+ })
+})
+
+asyncTest('setting data("with-credentials",true) with "ajax:before" uses new setting in request', 2, function() {
+ $('form[data-remote]').data('with-credentials', false)
+ .bindNative('ajax:before', function() {
+ this.setAttribute('data-with-credentials', true)
+ })
+
+ submit(function(form) {
+ form.bindNative('ajax:beforeSend', function(e, xhr, settings) {
+ equal(settings.withCredentials, true, 'setting modified in ajax:before should have forced withCredentials request')
+ })
+ })
+})
+
+asyncTest('stopping the "ajax:beforeSend" event aborts the request', 1, function() {
+ submit(function(form) {
+ form.bindNative('ajax:beforeSend', function() {
+ ok(true, 'aborting request in ajax:beforeSend')
+ return false
+ })
+ form.unbind('ajax:send').bindNative('ajax:send', function() {
+ ok(false, 'ajax:send should not run')
+ })
+ form.unbind('ajax:complete').bindNative('ajax:complete', function() {
+ ok(false, 'ajax:complete should not run')
+ })
+ form.bindNative('ajax:error', function(e, xhr, status, error) {
+ ok(false, 'ajax:error should not run')
+ })
+ $(document).bindNative('ajaxStop', function() {
+ start()
+ })
+ })
+})
+
+asyncTest('blank required form input field should abort request and trigger "ajax:aborted:required" event', 5, function() {
+ $(document).bind('iframe:loading', function() {
+ ok(false, 'form should not get submitted')
+ })
+
+ var form = $('form[data-remote]')
+ .append($('<input type="text" name="user_name" required="required">'))
+ .append($('<textarea name="user_bio" required="required"></textarea>'))
+ .bindNative('ajax:beforeSend', function() {
+ ok(false, 'ajax:beforeSend should not run')
+ })
+ .bindNative('ajax:aborted:required', function(e, data) {
+ data = $(data)
+ ok(data.length == 2, 'ajax:aborted:required event is passed all blank required inputs (jQuery objects)')
+ ok(data.first().is('input[name="user_name"]'), 'ajax:aborted:required adds blank required input to data')
+ ok(data.last().is('textarea[name="user_bio"]'), 'ajax:aborted:required adds blank required textarea to data')
+ ok(true, 'ajax:aborted:required should run')
+ })
+ .triggerNative('submit')
+
+ setTimeout(function() {
+ form.find('input[required],textarea[required]').val('Tyler')
+ form.unbind('ajax:beforeSend')
+ submit()
+ }, 13)
+})
+
+asyncTest('blank required form input for non-remote form should abort normal submission', 1, function() {
+ var form = $('form[data-remote]')
+ .append($('<input type="text" name="user_name" required="required">'))
+ .removeAttr('data-remote')
+ .bindNative('ujs:everythingStopped', function() {
+ ok(true, 'ujs:everythingStopped should run')
+ })
+ .triggerNative('submit')
+
+ setTimeout(function() {
+ start()
+ }, 13)
+})
+
+asyncTest('form should be submitted with blank required fields if handler is bound to "ajax:aborted:required" event that returns false', 1, function() {
+ var form = $('form[data-remote]')
+ .append($('<input type="text" name="user_name" required="required">'))
+ .bindNative('ajax:beforeSend', function() {
+ ok(true, 'ajax:beforeSend should run')
+ })
+ .bindNative('ajax:aborted:required', function() {
+ return false
+ })
+ .triggerNative('submit')
+
+ setTimeout(function() {
+ start()
+ }, 13)
+})
+
+asyncTest('disabled fields should not be included in blank required check', 2, function() {
+ var form = $('form[data-remote]')
+ .append($('<input type="text" name="user_name" required="required" disabled="disabled">'))
+ .append($('<textarea name="user_bio" required="required" disabled="disabled"></textarea>'))
+ .bindNative('ajax:beforeSend', function() {
+ ok(true, 'ajax:beforeSend should run')
+ })
+ .bindNative('ajax:aborted:required', function() {
+ ok(false, 'ajax:aborted:required should not run')
+ })
+
+ submit()
+})
+
+asyncTest('form should be submitted with blank required fields if it has the "novalidate" attribute', 2, function() {
+ var form = $('form[data-remote]')
+ .append($('<input type="text" name="user_name" required="required">'))
+ .attr('novalidate', 'novalidate')
+ .bindNative('ajax:beforeSend', function() {
+ ok(true, 'ajax:beforeSend should run')
+ })
+ .bindNative('ajax:aborted:required', function() {
+ ok(false, 'ajax:aborted:required should not run')
+ })
+
+ submit()
+})
+
+asyncTest('form should be submitted with blank required fields if the button has the "formnovalidate" attribute', 2, function() {
+ var submit_button = $('<input type="submit" formnovalidate>')
+ var form = $('form[data-remote]')
+ .append($('<input type="text" name="user_name" required="required">'))
+ .append(submit_button)
+ .bindNative('ajax:beforeSend', function() {
+ ok(true, 'ajax:beforeSend should run')
+ })
+ .bindNative('ajax:aborted:required', function() {
+ ok(false, 'ajax:aborted:required should not run')
+ })
+
+ submit_with_button(submit_button)
+})
+
+asyncTest('blank required form input for non-remote form with "novalidate" attribute should not abort normal submission', 1, function() {
+ $(document).bind('iframe:loading', function() {
+ ok(true, 'form should get submitted')
+ })
+
+ var form = $('form[data-remote]')
+ .append($('<input type="text" name="user_name" required="required">'))
+ .removeAttr('data-remote')
+ .attr('novalidate', 'novalidate')
+ .triggerNative('submit')
+
+ setTimeout(function() {
+ start()
+ }, 13)
+})
+
+asyncTest('unchecked required checkbox should abort form submission', 1, function() {
+ var form = $('form[data-remote]')
+ .append($('<input type="checkbox" name="agree" required="required">'))
+ .removeAttr('data-remote')
+ .bindNative('ujs:everythingStopped', function() {
+ ok(true, 'ujs:everythingStopped should run')
+ })
+ .triggerNative('submit')
+
+ setTimeout(function() {
+ start()
+ }, 13)
+})
+
+asyncTest('unchecked required radio should abort form submission', 3, function() {
+ var form = $('form[data-remote]')
+ .append($('<input type="radio" name="yes_no_none" required="required" value=1>'))
+ .append($('<input type="radio" name="yes_no_none" required="required" value=2>'))
+ .removeAttr('data-remote')
+ .bindNative('ujs:everythingStopped', function() {
+ ok(true, 'ujs:everythingStopped should run')
+ })
+ .bindNative('ajax:aborted:required', function(e, data) {
+ data = $(data)
+ equal(data.length, 2, 'blankRequiredInputs should include both radios')
+ ok(data.first().is('input[type=radio][value=1]'), 'blankRequiredInputs[0] should be the first radio')
+ })
+ .triggerNative('submit')
+
+ setTimeout(function() {
+ start()
+ }, 13)
+})
+
+asyncTest('required radio should only require one to be checked', 1, function() {
+ $(document).bind('iframe:loading', function() {
+ ok(true, 'form should get submitted')
+ })
+
+ var form = $('form[data-remote]')
+ .append($('<input type="radio" name="yes_no" required="required" value=1 id="checkme">'))
+ .append($('<input type="radio" name="yes_no" required="required" value=2>'))
+ .removeAttr('data-remote')
+ .bindNative('ujs:everythingStopped', function() {
+ ok(false, 'ujs:everythingStopped should not run')
+ })
+ .find('#checkme').prop('checked', true)
+ .end()
+ .triggerNative('submit')
+
+ setTimeout(function() {
+ start()
+ }, 13)
+})
+
+asyncTest('required radio should only require one to be checked if not all radios are required', 1, function() {
+ $(document).bind('iframe:loading', function() {
+ ok(true, 'form should get submitted')
+ })
+
+ var form = $('form[data-remote]')
+ // Check the radio that is not required
+ .append($('<input type="radio" name="yes_no_maybe" value=1 >'))
+ // Check the radio that is not required
+ .append($('<input type="radio" name="yes_no_maybe" value=2 id="checkme">'))
+ // Only one needs to be required
+ .append($('<input type="radio" name="yes_no_maybe" required="required" value=3>'))
+ .removeAttr('data-remote')
+ .bindNative('ujs:everythingStopped', function() {
+ ok(false, 'ujs:everythingStopped should not run')
+ })
+ .find('#checkme').prop('checked', true)
+ .end()
+ .triggerNative('submit')
+
+ setTimeout(function() {
+ start()
+ }, 13)
+})
+
+function skipIt() {
+ // This test cannot work due to the security feature in browsers which makes the value
+ // attribute of file input fields readonly, so it cannot be set with default value.
+ // This is what the test would look like though if browsers let us automate this test.
+ asyncTest('non-blank file form input field should abort remote request, but submit normally', 5, function() {
+ var form = $('form[data-remote]')
+ .append($('<input type="file" name="attachment" value="default.png">'))
+ .bindNative('ajax:beforeSend', function() {
+ ok(false, 'ajax:beforeSend should not run')
+ })
+ .bind('iframe:loading', function() {
+ ok(true, 'form should get submitted')
+ })
+ .bindNative('ajax:aborted:file', function(e, data) {
+ ok(data.length == 1, 'ajax:aborted:file event is passed all non-blank file inputs (jQuery objects)')
+ ok(data.first().is('input[name="attachment"]'), 'ajax:aborted:file adds non-blank file input to data')
+ ok(true, 'ajax:aborted:file event should run')
+ })
+ .triggerNative('submit')
+
+ setTimeout(function() {
+ form.find('input[type="file"]').val('')
+ form.unbind('ajax:beforeSend')
+ submit()
+ }, 13)
+ })
+
+ asyncTest('file form input field should not abort remote request if file form input does not have a name attribute', 5, function() {
+ var form = $('form[data-remote]')
+ .append($('<input type="file" value="default.png">'))
+ .bindNative('ajax:beforeSend', function() {
+ ok(true, 'ajax:beforeSend should run')
+ })
+ .bind('iframe:loading', function() {
+ ok(true, 'form should get submitted')
+ })
+ .bindNative('ajax:aborted:file', function(e, data) {
+ ok(false, 'ajax:aborted:file should not run')
+ })
+ .triggerNative('submit')
+
+ setTimeout(function() {
+ form.find('input[type="file"]').val('')
+ form.unbind('ajax:beforeSend')
+ submit()
+ }, 13)
+ })
+
+ asyncTest('blank file input field should abort request entirely if handler bound to "ajax:aborted:file" event that returns false', 1, function() {
+ var form = $('form[data-remote]')
+ .append($('<input type="file" name="attachment" value="default.png">'))
+ .bindNative('ajax:beforeSend', function() {
+ ok(false, 'ajax:beforeSend should not run')
+ })
+ .bind('iframe:loading', function() {
+ ok(false, 'form should not get submitted')
+ })
+ .bindNative('ajax:aborted:file', function() {
+ return false
+ })
+ .triggerNative('submit')
+
+ setTimeout(function() {
+ form.find('input[type="file"]').val('')
+ form.unbind('ajax:beforeSend')
+ submit()
+ }, 13)
+ })
+}
+
+asyncTest('"ajax:beforeSend" can be observed and stopped with event delegation', 1, function() {
+ $(document).delegate('form[data-remote]', 'ajax:beforeSend', function() {
+ ok(true, 'ajax:beforeSend observed with event delegation')
+ return false
+ })
+
+ submit(function(form) {
+ form.unbind('ajax:send').bindNative('ajax:send', function() {
+ ok(false, 'ajax:send should not run')
+ })
+ form.unbind('ajax:complete').bindNative('ajax:complete', function() {
+ ok(false, 'ajax:complete should not run')
+ })
+ $(document).bindNative('ajaxStop', function() {
+ start()
+ })
+ })
+})
+
+asyncTest('"ajax:beforeSend", "ajax:send", "ajax:success" and "ajax:complete" are triggered', 9, function() {
+ submit(function(form) {
+ form.bindNative('ajax:beforeSend', function(e, xhr, settings) {
+ ok(xhr.setRequestHeader, 'first argument to "ajax:beforeSend" should be an XHR object')
+ equal(settings.url, '/echo', 'second argument to "ajax:beforeSend" should be a settings object')
+ })
+ form.bindNative('ajax:send', function(e, xhr) {
+ ok(xhr.abort, 'first argument to "ajax:send" should be an XHR object')
+ })
+ form.bindNative('ajax:success', function(e, data, status, xhr) {
+ ok(data.REQUEST_METHOD, 'first argument to ajax:success should be a data object')
+ equal(status, 'OK', 'second argument to ajax:success should be a status string')
+ ok(xhr.getResponseHeader, 'third argument to "ajax:success" should be an XHR object')
+ })
+ form.bindNative('ajax:complete', function(e, xhr, status) {
+ ok(xhr.getResponseHeader, 'first argument to "ajax:complete" should be an XHR object')
+ equal(status, 'OK', 'second argument to ajax:complete should be a status string')
+ })
+ })
+})
+
+if(window.phantom !== undefined) {
+ asyncTest('"ajax:beforeSend", "ajax:send", "ajax:error" and "ajax:complete" are triggered on error', 7, function() {
+ submit(function(form) {
+ form.attr('action', '/error')
+ form.bindNative('ajax:beforeSend', function(arg) { ok(true, 'ajax:beforeSend') })
+ form.bindNative('ajax:send', function(arg) { ok(true, 'ajax:send') })
+ form.bindNative('ajax:error', function(e, xhr, status, error) {
+ ok(xhr.getResponseHeader, 'first argument to "ajax:error" should be an XHR object')
+ equal(status, 'error', 'second argument to ajax:error should be a status string')
+ // Firefox 8 returns "Forbidden " with trailing space
+ equal($.trim(error), 'Forbidden', 'third argument to ajax:error should be an HTTP status response')
+ // Opera returns "0" for HTTP code
+ equal(xhr.status, window.opera ? 0 : 403, 'status code should be 403')
+ })
+ })
+ })
+}
+
+// IF THIS TEST IS FAILING, TRY INCREASING THE TIMEOUT AT THE BOTTOM TO > 100
+asyncTest('binding to ajax callbacks via .delegate() triggers handlers properly', 4, function() {
+ $(document)
+ .delegate('form[data-remote]', 'ajax:beforeSend', function() {
+ ok(true, 'ajax:beforeSend handler is triggered')
+ })
+ .delegate('form[data-remote]', 'ajax:send', function() {
+ ok(true, 'ajax:send handler is triggered')
+ })
+ .delegate('form[data-remote]', 'ajax:complete', function() {
+ ok(true, 'ajax:complete handler is triggered')
+ })
+ .delegate('form[data-remote]', 'ajax:success', function() {
+ ok(true, 'ajax:success handler is triggered')
+ })
+ $('form[data-remote]').triggerNative('submit')
+
+ setTimeout(function() {
+ start()
+ }, 63)
+})
+
+asyncTest('binding to ajax:send event to call jquery methods on ajax object', 2, function() {
+ $('form[data-remote]')
+ .bindNative('ajax:send', function(e, xhr) {
+ ok(true, 'event should fire')
+ equal(typeof(xhr.abort), 'function', 'event should pass jqXHR object')
+ xhr.abort()
+ })
+ .triggerNative('submit')
+
+ setTimeout(function() { start() }, 35)
+})
+
+})()
diff --git a/actionview/test/ujs/public/test/call-remote.js b/actionview/test/ujs/public/test/call-remote.js
new file mode 100644
index 0000000000..dbeb8ad832
--- /dev/null
+++ b/actionview/test/ujs/public/test/call-remote.js
@@ -0,0 +1,247 @@
+(function() {
+
+function buildForm(attrs) {
+ attrs = $.extend({ action: '/echo', 'data-remote': 'true' }, attrs)
+
+ $('#qunit-fixture').append($('<form />', attrs))
+ .find('form').append($('<input type="text" name="user_name" value="john">'))
+}
+
+module('call-remote')
+
+function submit(fn) {
+ $('form')
+ .bindNative('ajax:success', fn)
+ .bindNative('ajax:complete', function() { start() })
+ .triggerNative('submit')
+}
+
+asyncTest('form method is read from "method" and not from "data-method"', 1, function() {
+ buildForm({ method: 'post', 'data-method': 'get' })
+
+ submit(function(e, data, status, xhr) {
+ App.assertPostRequest(data)
+ })
+})
+
+asyncTest('form method is not read from "data-method" attribute in case of missing "method"', 1, function() {
+ buildForm({ 'data-method': 'put' })
+
+ submit(function(e, data, status, xhr) {
+ App.assertGetRequest(data)
+ })
+})
+
+asyncTest('form method is read from submit button "formmethod" if submit is triggered by that button', 1, function() {
+ var submitButton = $('<input type="submit" formmethod="get">')
+ buildForm({ method: 'post' })
+
+ $('#qunit-fixture').find('form').append(submitButton)
+ .bindNative('ajax:success', function(e, data, status, xhr) {
+ App.assertGetRequest(data)
+ })
+ .bindNative('ajax:complete', function() { start() })
+
+ submitButton.triggerNative('click')
+})
+
+asyncTest('form default method is GET', 1, function() {
+ buildForm()
+
+ submit(function(e, data, status, xhr) {
+ App.assertGetRequest(data)
+ })
+})
+
+asyncTest('form url is picked up from "action"', 1, function() {
+ buildForm({ method: 'post' })
+
+ submit(function(e, data, status, xhr) {
+ App.assertRequestPath(data, '/echo')
+ })
+})
+
+asyncTest('form url is read from "action" not "href"', 1, function() {
+ buildForm({ method: 'post', href: '/echo2' })
+
+ submit(function(e, data, status, xhr) {
+ App.assertRequestPath(data, '/echo')
+ })
+})
+
+asyncTest('form url is read from submit button "formaction" if submit is triggered by that button', 1, function() {
+ var submitButton = $('<input type="submit" formaction="/echo">')
+ buildForm({ method: 'post', href: '/echo2' })
+
+ $('#qunit-fixture').find('form').append(submitButton)
+ .bindNative('ajax:success', function(e, data, status, xhr) {
+ App.assertRequestPath(data, '/echo')
+ })
+ .bindNative('ajax:complete', function() { start() })
+
+ submitButton.triggerNative('click')
+})
+
+asyncTest('prefer JS, but accept any format', 1, function() {
+ buildForm({ method: 'post' })
+
+ submit(function(e, data, status, xhr) {
+ var accept = data.HTTP_ACCEPT
+ ok(accept.match(/text\/javascript.+\*\/\*/), 'Accept: ' + accept)
+ })
+})
+
+asyncTest('JS code should be executed', 1, function() {
+ buildForm({ method: 'post', 'data-type': 'script' })
+
+ $('form').append('<input type="text" name="content_type" value="text/javascript">')
+ $('form').append('<input type="text" name="content" value="ok(true, \'remote code should be run\')">')
+
+ submit()
+})
+
+asyncTest('XML document should be parsed', 1, function() {
+ buildForm({ method: 'post', 'data-type': 'html' })
+
+ $('form').append('<input type="text" name="content_type" value="application/xml">')
+ $('form').append('<input type="text" name="content" value="<p>hello</p>">')
+
+ submit(function(e, data, status, xhr) {
+ ok(data instanceof Document, 'returned data should be an XML document')
+ })
+})
+
+asyncTest('accept application/json if "data-type" is json', 1, function() {
+ buildForm({ method: 'post', 'data-type': 'json' })
+
+ submit(function(e, data, status, xhr) {
+ equal(data.HTTP_ACCEPT, 'application/json, text/javascript, */*; q=0.01')
+ })
+})
+
+asyncTest('allow empty "data-remote" attribute', 1, function() {
+ var form = $('#qunit-fixture').append($('<form action="/echo" data-remote />')).find('form')
+
+ submit(function() {
+ ok(true, 'form with empty "data-remote" attribute is also allowed')
+ })
+})
+
+asyncTest('query string in form action should be stripped in a GET request in normal submit', 1, function() {
+ buildForm({ action: '/echo?param1=abc', 'data-remote': 'false' })
+
+ $(document).one('iframe:loaded', function(e, data) {
+ equal(data.params.param1, undefined, '"param1" should not be passed to server')
+ start()
+ })
+
+ $('#qunit-fixture form').triggerNative('submit')
+})
+
+asyncTest('query string in form action should be stripped in a GET request in ajax submit', 1, function() {
+ buildForm({ action: '/echo?param1=abc' })
+
+ submit(function(e, data, status, xhr) {
+ equal(data.params.param1, undefined, '"param1" should not be passed to server')
+ })
+})
+
+asyncTest('query string in form action should not be stripped in a POST request in normal submit', 1, function() {
+ buildForm({ action: '/echo?param1=abc', method: 'post', 'data-remote': 'false' })
+
+ $(document).one('iframe:loaded', function(e, data) {
+ equal(data.params.param1, 'abc', '"param1" should be passed to server')
+ start()
+ })
+
+ $('#qunit-fixture form').triggerNative('submit')
+})
+
+asyncTest('query string in form action should not be stripped in a POST request in ajax submit', 1, function() {
+ buildForm({ action: '/echo?param1=abc', method: 'post' })
+
+ submit(function(e, data, status, xhr) {
+ equal(data.params.param1, 'abc', '"param1" should be passed to server')
+ })
+})
+
+asyncTest('allow empty form "action"', 1, function() {
+ var currentLocation, ajaxLocation
+
+ buildForm({ action: '' })
+
+ $('#qunit-fixture').find('form')
+ .bindNative('ajax:beforeSend', function(e, xhr, settings) {
+ // Get current location (the same way jQuery does)
+ try {
+ currentLocation = location.href
+ } catch(err) {
+ currentLocation = document.createElement( 'a' )
+ currentLocation.href = ''
+ currentLocation = currentLocation.href
+ }
+ currentLocation = currentLocation.replace(/\?.*$/, '')
+
+ // Actual location (strip out settings.data that jQuery serializes and appends)
+ // HACK: can no longer use settings.data below to see what was appended to URL, as of
+ // jQuery 1.6.3 (see http://bugs.jquery.com/ticket/10202 and https://github.com/jquery/jquery/pull/544)
+ ajaxLocation = settings.url.replace('user_name=john', '').replace(/&$/, '').replace(/\?$/, '')
+ equal(ajaxLocation.match(/^(.*)/)[1], currentLocation, 'URL should be current page by default')
+
+ // Prevent the request from actually getting sent to the current page and
+ // causing an error.
+ return false
+ })
+ .triggerNative('submit')
+
+ setTimeout(function() { start() }, 13)
+})
+
+asyncTest('sends CSRF token in custom header', 1, function() {
+ buildForm({ method: 'post' })
+ $('#qunit-fixture').append('<meta name="csrf-token" content="cf50faa3fe97702ca1ae" />')
+
+ submit(function(e, data, status, xhr) {
+ equal(data.HTTP_X_CSRF_TOKEN, 'cf50faa3fe97702ca1ae', 'X-CSRF-Token header should be sent')
+ })
+})
+
+asyncTest('intelligently guesses crossDomain behavior when target URL has a different protocol and/or hostname', 1, function() {
+
+ // Don't set data-cross-domain here, just set action to be a different domain than localhost
+ buildForm({ action: 'http://www.alfajango.com' })
+ $('#qunit-fixture').append('<meta name="csrf-token" content="cf50faa3fe97702ca1ae" />')
+
+ $('#qunit-fixture').find('form')
+ .bindNative('ajax:beforeSend', function(evt, req, settings) {
+
+ equal(settings.crossDomain, true, 'crossDomain should be set to true')
+
+ // prevent request from actually getting sent off-domain
+ return false
+ })
+ .triggerNative('submit')
+
+ setTimeout(function() { start() }, 13)
+})
+
+asyncTest('intelligently guesses crossDomain behavior when target URL consists of only a path', 1, function() {
+
+ // Don't set data-cross-domain here, just set action to be a different domain than localhost
+ buildForm({ action: '/just/a/path' })
+ $('#qunit-fixture').append('<meta name="csrf-token" content="cf50faa3fe97702ca1ae" />')
+
+ $('#qunit-fixture').find('form')
+ .bindNative('ajax:beforeSend', function(evt, req, settings) {
+
+ equal(settings.crossDomain, false, 'crossDomain should be set to false')
+
+ // prevent request from actually getting sent off-domain
+ return false
+ })
+ .triggerNative('submit')
+
+ setTimeout(function() { start() }, 13)
+})
+
+})()
diff --git a/actionview/test/ujs/public/test/csrf-refresh.js b/actionview/test/ujs/public/test/csrf-refresh.js
new file mode 100644
index 0000000000..e302042542
--- /dev/null
+++ b/actionview/test/ujs/public/test/csrf-refresh.js
@@ -0,0 +1,24 @@
+(function() {
+
+module('csrf-refresh', {})
+
+asyncTest('refresh all csrf tokens', 1, function() {
+ var correctToken = 'cf50faa3fe97702ca1ae'
+
+ var form = $('<form />')
+ var input = $('<input>').attr({ type: 'hidden', name: 'authenticity_token', id: 'authenticity_token', value: 'foo' })
+ input.appendTo(form)
+
+ $('#qunit-fixture')
+ .append('<meta name="csrf-param" content="authenticity_token"/>')
+ .append('<meta name="csrf-token" content="' + correctToken + '"/>')
+ .append(form)
+
+ $.rails.refreshCSRFTokens()
+ currentToken = $('#qunit-fixture #authenticity_token').val()
+
+ start()
+ equal(currentToken, correctToken)
+})
+
+})()
diff --git a/actionview/test/ujs/public/test/csrf-token.js b/actionview/test/ujs/public/test/csrf-token.js
new file mode 100644
index 0000000000..388b40e057
--- /dev/null
+++ b/actionview/test/ujs/public/test/csrf-token.js
@@ -0,0 +1,27 @@
+(function() {
+
+module('csrf-token', {})
+
+asyncTest('find csrf token', 1, function() {
+ var correctToken = 'cf50faa3fe97702ca1ae'
+
+ $('#qunit-fixture').append('<meta name="csrf-token" content="' + correctToken + '"/>')
+
+ currentToken = $.rails.csrfToken()
+
+ start()
+ equal(currentToken, correctToken)
+})
+
+asyncTest('find csrf param', 1, function() {
+ var correctParam = 'authenticity_token'
+
+ $('#qunit-fixture').append('<meta name="csrf-param" content="' + correctParam + '"/>')
+
+ currentParam = $.rails.csrfParam()
+
+ start()
+ equal(currentParam, correctParam)
+})
+
+})()
diff --git a/actionview/test/ujs/public/test/data-confirm.js b/actionview/test/ujs/public/test/data-confirm.js
new file mode 100644
index 0000000000..28190c2250
--- /dev/null
+++ b/actionview/test/ujs/public/test/data-confirm.js
@@ -0,0 +1,288 @@
+module('data-confirm', {
+ setup: function() {
+ $('#qunit-fixture').append($('<a />', {
+ href: '/echo',
+ 'data-remote': 'true',
+ 'data-confirm': 'Are you absolutely sure?',
+ text: 'my social security number'
+ }))
+
+ $('#qunit-fixture').append($('<button />', {
+ 'data-url': '/echo',
+ 'data-remote': 'true',
+ 'data-confirm': 'Are you absolutely sure?',
+ text: 'Click me'
+ }))
+
+ $('#qunit-fixture').append($('<form />', {
+ id: 'confirm',
+ action: '/echo',
+ 'data-remote': 'true'
+ }))
+
+ $('#qunit-fixture').append($('<input />', {
+ type: 'submit',
+ form: 'confirm',
+ 'data-confirm': 'Are you absolutely sure?'
+ }))
+
+ this.windowConfirm = window.confirm
+ },
+ teardown: function() {
+ window.confirm = this.windowConfirm
+ }
+})
+
+asyncTest('clicking on a link with data-confirm attribute. Confirm yes.', 6, function() {
+ var message
+ // auto-confirm:
+ window.confirm = function(msg) { message = msg; return true }
+
+ $('a[data-confirm]')
+ .bindNative('confirm:complete', function(e, data) {
+ App.assertCallbackInvoked('confirm:complete')
+ ok(data == true, 'confirm:complete passes in confirm answer (true)')
+ })
+ .bindNative('ajax:success', function(e, data, status, xhr) {
+ App.assertCallbackInvoked('ajax:success')
+ App.assertRequestPath(data, '/echo')
+ App.assertGetRequest(data)
+
+ equal(message, 'Are you absolutely sure?')
+ start()
+ })
+ .triggerNative('click')
+})
+
+asyncTest('clicking on a button with data-confirm attribute. Confirm yes.', 6, function() {
+ var message
+ // auto-confirm:
+ window.confirm = function(msg) { message = msg; return true }
+
+ $('button[data-confirm]')
+ .bindNative('confirm:complete', function(e, data) {
+ App.assertCallbackInvoked('confirm:complete')
+ ok(data == true, 'confirm:complete passes in confirm answer (true)')
+ })
+ .bindNative('ajax:success', function(e, data, status, xhr) {
+ App.assertCallbackInvoked('ajax:success')
+ App.assertRequestPath(data, '/echo')
+ App.assertGetRequest(data)
+
+ equal(message, 'Are you absolutely sure?')
+ start()
+ })
+ .triggerNative('click')
+})
+
+asyncTest('clicking on a link with data-confirm attribute. Confirm No.', 3, function() {
+ var message
+ // auto-decline:
+ window.confirm = function(msg) { message = msg; return false }
+
+ $('a[data-confirm]')
+ .bindNative('confirm:complete', function(e, data) {
+ App.assertCallbackInvoked('confirm:complete')
+ ok(data == false, 'confirm:complete passes in confirm answer (false)')
+ })
+ .bindNative('ajax:beforeSend', function(e, data, status, xhr) {
+ App.assertCallbackNotInvoked('ajax:beforeSend')
+ })
+ .triggerNative('click')
+
+ setTimeout(function() {
+ equal(message, 'Are you absolutely sure?')
+ start()
+ }, 50)
+})
+
+asyncTest('clicking on a button with data-confirm attribute. Confirm No.', 3, function() {
+ var message
+ // auto-decline:
+ window.confirm = function(msg) { message = msg; return false }
+
+ $('button[data-confirm]')
+ .bindNative('confirm:complete', function(e, data) {
+ App.assertCallbackInvoked('confirm:complete')
+ ok(data == false, 'confirm:complete passes in confirm answer (false)')
+ })
+ .bindNative('ajax:beforeSend', function(e, data, status, xhr) {
+ App.assertCallbackNotInvoked('ajax:beforeSend')
+ })
+ .triggerNative('click')
+
+ setTimeout(function() {
+ equal(message, 'Are you absolutely sure?')
+ start()
+ }, 50)
+})
+
+asyncTest('clicking on a button with data-confirm attribute. Confirm error.', 3, function() {
+ var message
+ // auto-decline:
+ window.confirm = function(msg) { message = msg; throw 'some random error' }
+
+ $('button[data-confirm]')
+ .bindNative('confirm:complete', function(e, data) {
+ App.assertCallbackInvoked('confirm:complete')
+ ok(data == false, 'confirm:complete passes in confirm answer (false)')
+ })
+ .bindNative('ajax:beforeSend', function(e, data, status, xhr) {
+ App.assertCallbackNotInvoked('ajax:beforeSend')
+ })
+ .triggerNative('click')
+
+ setTimeout(function() {
+ equal(message, 'Are you absolutely sure?')
+ start()
+ }, 50)
+})
+
+asyncTest('clicking on a submit button with form and data-confirm attributes. Confirm No.', 3, function() {
+ var message
+ // auto-decline:
+ window.confirm = function(msg) { message = msg; return false }
+
+ $('input[type=submit][form]')
+ .bindNative('confirm:complete', function(e, data) {
+ App.assertCallbackInvoked('confirm:complete')
+ ok(data == false, 'confirm:complete passes in confirm answer (false)')
+ })
+ .bindNative('ajax:beforeSend', function(e, data, status, xhr) {
+ App.assertCallbackNotInvoked('ajax:beforeSend')
+ })
+ .triggerNative('click')
+
+ setTimeout(function() {
+ equal(message, 'Are you absolutely sure?')
+ start()
+ }, 50)
+})
+
+asyncTest('binding to confirm event of a link and returning false', 1, function() {
+ // redefine confirm function so we can make sure it's not called
+ window.confirm = function(msg) {
+ ok(false, 'confirm dialog should not be called')
+ }
+
+ $('a[data-confirm]')
+ .bindNative('confirm', function() {
+ App.assertCallbackInvoked('confirm')
+ return false
+ })
+ .bindNative('confirm:complete', function() {
+ App.assertCallbackNotInvoked('confirm:complete')
+ })
+ .triggerNative('click')
+
+ setTimeout(function() {
+ start()
+ }, 50)
+})
+
+asyncTest('binding to confirm event of a button and returning false', 1, function() {
+ // redefine confirm function so we can make sure it's not called
+ window.confirm = function(msg) {
+ ok(false, 'confirm dialog should not be called')
+ }
+
+ $('button[data-confirm]')
+ .bindNative('confirm', function() {
+ App.assertCallbackInvoked('confirm')
+ return false
+ })
+ .bindNative('confirm:complete', function() {
+ App.assertCallbackNotInvoked('confirm:complete')
+ })
+ .triggerNative('click')
+
+ setTimeout(function() {
+ start()
+ }, 50)
+})
+
+asyncTest('binding to confirm:complete event of a link and returning false', 2, function() {
+ // auto-confirm:
+ window.confirm = function(msg) {
+ ok(true, 'confirm dialog should be called')
+ return true
+ }
+
+ $('a[data-confirm]')
+ .bindNative('confirm:complete', function() {
+ App.assertCallbackInvoked('confirm:complete')
+ return false
+ })
+ .bindNative('ajax:beforeSend', function() {
+ App.assertCallbackNotInvoked('ajax:beforeSend')
+ })
+ .triggerNative('click')
+
+ setTimeout(function() {
+ start()
+ }, 50)
+})
+
+asyncTest('binding to confirm:complete event of a button and returning false', 2, function() {
+ // auto-confirm:
+ window.confirm = function(msg) {
+ ok(true, 'confirm dialog should be called')
+ return true
+ }
+
+ $('button[data-confirm]')
+ .bindNative('confirm:complete', function() {
+ App.assertCallbackInvoked('confirm:complete')
+ return false
+ })
+ .bindNative('ajax:beforeSend', function() {
+ App.assertCallbackNotInvoked('ajax:beforeSend')
+ })
+ .triggerNative('click')
+
+ setTimeout(function() {
+ start()
+ }, 50)
+})
+
+asyncTest('a button inside a form only confirms once', 1, function() {
+ var confirmations = 0
+ window.confirm = function(msg) {
+ confirmations++
+ return true
+ }
+
+ $('#qunit-fixture').append($('<form />').append($('<button />', {
+ 'data-remote': 'true',
+ 'data-confirm': 'Are you absolutely sure?',
+ text: 'Click me'
+ })))
+
+ $('form > button[data-confirm]').triggerNative('click')
+
+ ok(confirmations === 1, 'confirmation counter should be 1, but it was ' + confirmations)
+ start()
+})
+
+asyncTest('clicking on the children of a link should also trigger a confirm', 6, function() {
+ var message
+ // auto-confirm:
+ window.confirm = function(msg) { message = msg; return true }
+
+ $('a[data-confirm]')
+ .html('<strong>Click me</strong>')
+ .bindNative('confirm:complete', function(e, data) {
+ App.assertCallbackInvoked('confirm:complete')
+ ok(data == true, 'confirm:complete passes in confirm answer (true)')
+ })
+ .bindNative('ajax:success', function(e, data, status, xhr) {
+ App.assertCallbackInvoked('ajax:success')
+ App.assertRequestPath(data, '/echo')
+ App.assertGetRequest(data)
+
+ equal(message, 'Are you absolutely sure?')
+ start()
+ })
+ .find('strong')
+ .triggerNative('click')
+})
diff --git a/actionview/test/ujs/public/test/data-disable-with.js b/actionview/test/ujs/public/test/data-disable-with.js
new file mode 100644
index 0000000000..b29cbbc867
--- /dev/null
+++ b/actionview/test/ujs/public/test/data-disable-with.js
@@ -0,0 +1,391 @@
+module('data-disable-with', {
+ setup: function() {
+ $('#qunit-fixture').append($('<form />', {
+ action: '/echo',
+ 'data-remote': 'true',
+ method: 'post'
+ }))
+ .find('form')
+ .append($('<input type="text" data-disable-with="processing ..." name="user_name" value="john" />'))
+
+ $('#qunit-fixture').append($('<form />', {
+ action: '/echo',
+ method: 'post',
+ id: 'not_remote'
+ }))
+ .find('form:last')
+ // WEEIRDD: the form won't submit to an iframe if the button is name="submit" (??!)
+ .append($('<input type="submit" data-disable-with="submitting ..." name="submit2" value="Submit" />'))
+
+ $('#qunit-fixture').append($('<a />', {
+ text: 'Click me',
+ href: '/echo',
+ 'data-disable-with': 'clicking...'
+ }))
+
+ $('#qunit-fixture').append($('<input />', {
+ type: 'submit',
+ form: 'not_remote',
+ 'data-disable-with': 'form attr submitting',
+ name: 'submit3',
+ value: 'Form Attr Submit'
+ }))
+
+ $('#qunit-fixture').append($('<button />', {
+ text: 'Click me',
+ 'data-remote': true,
+ 'data-url': '/echo',
+ 'data-disable-with': 'clicking...'
+ }))
+ },
+ teardown: function() {
+ $(document).unbind('iframe:loaded')
+ }
+})
+
+asyncTest('form input field with "data-disable-with" attribute', 7, function() {
+ var form = $('form[data-remote]'), input = form.find('input[type=text]')
+
+ App.checkEnabledState(input, 'john')
+
+ form.bindNative('ajax:success', function(e, data) {
+ setTimeout(function() {
+ App.checkEnabledState(input, 'john')
+ equal(data.params.user_name, 'john')
+ start()
+ }, 13)
+ })
+ form.triggerNative('submit')
+
+ App.checkDisabledState(input, 'processing ...')
+})
+
+asyncTest('blank form input field with "data-disable-with" attribute', 7, function() {
+ var form = $('form[data-remote]'), input = form.find('input[type=text]')
+
+ input.val('')
+ App.checkEnabledState(input, '')
+
+ form.bindNative('ajax:success', function(e, data) {
+ setTimeout(function() {
+ App.checkEnabledState(input, '')
+ equal(data.params.user_name, '')
+ start()
+ }, 13)
+ })
+ form.triggerNative('submit')
+
+ App.checkDisabledState(input, 'processing ...')
+})
+
+asyncTest('form button with "data-disable-with" attribute', 6, function() {
+ var form = $('form[data-remote]'), button = $('<button data-disable-with="submitting ..." name="submit2">Submit</button>')
+ form.append(button)
+
+ App.checkEnabledState(button, 'Submit')
+
+ form.bindNative('ajax:success', function(e, data) {
+ setTimeout(function() {
+ App.checkEnabledState(button, 'Submit')
+ start()
+ }, 13)
+ })
+ form.triggerNative('submit')
+
+ App.checkDisabledState(button, 'submitting ...')
+})
+
+asyncTest('form input[type=submit][data-disable-with] disables', 6, function() {
+ var form = $('form:not([data-remote])'), input = form.find('input[type=submit]')
+
+ App.checkEnabledState(input, 'Submit')
+
+ $(document).bind('iframe:loaded', function(e, data) {
+ setTimeout(function() {
+ App.checkDisabledState(input, 'submitting ...')
+ start()
+ }, 30)
+ })
+ form.triggerNative('submit')
+
+ setTimeout(function() {
+ App.checkDisabledState(input, 'submitting ...')
+ }, 30)
+})
+
+test('form input[type=submit][data-disable-with] re-enables when `pageshow` event is triggered', function() {
+ var form = $('form:not([data-remote])'), input = form.find('input[type=submit]')
+
+ App.checkEnabledState(input, 'Submit')
+
+ // Emulate the disabled state without submitting the form at all, what is the
+ // state after going back on firefox after submitting a form.
+ //
+ // See https://github.com/rails/jquery-ujs/issues/357
+ $.rails.disableElement(form[0])
+
+ App.checkDisabledState(input, 'submitting ...')
+
+ $(window).triggerNative('pageshow')
+
+ App.checkEnabledState(input, 'Submit')
+})
+
+asyncTest('form[data-remote] input[type=submit][data-disable-with] is replaced in ajax callback', 2, function() {
+ var form = $('form:not([data-remote])').attr('data-remote', 'true'), origFormContents = form.html()
+
+ form.bindNative('ajax:success', function() {
+ form.html(origFormContents)
+
+ setTimeout(function() {
+ var input = form.find('input[type=submit]')
+ App.checkEnabledState(input, 'Submit')
+ start()
+ }, 30)
+ }).triggerNative('submit')
+})
+
+asyncTest('form[data-remote] input[data-disable-with] is replaced with disabled field in ajax callback', 2, function() {
+ var form = $('form:not([data-remote])').attr('data-remote', 'true'), input = form.find('input[type=submit]'),
+ newDisabledInput = input.clone().attr('disabled', 'disabled')
+
+ form.bindNative('ajax:success', function() {
+ input.replaceWith(newDisabledInput)
+
+ setTimeout(function() {
+ App.checkEnabledState(newDisabledInput, 'Submit')
+ start()
+ }, 30)
+ }).triggerNative('submit')
+})
+
+asyncTest('form input[type=submit][data-disable-with] using "form" attribute disables', 6, function() {
+ var form = $('#not_remote'), input = $('input[form=not_remote]')
+ App.checkEnabledState(input, 'Form Attr Submit')
+
+ $(document).bind('iframe:loaded', function(e, data) {
+ setTimeout(function() {
+ App.checkDisabledState(input, 'form attr submitting')
+ start()
+ }, 30)
+ })
+ form.triggerNative('submit')
+
+ setTimeout(function() {
+ App.checkDisabledState(input, 'form attr submitting')
+ }, 30)
+
+})
+
+asyncTest('form[data-remote] textarea[data-disable-with] attribute', 3, function() {
+ var form = $('form[data-remote]'),
+ textarea = $('<textarea data-disable-with="processing ..." name="user_bio">born, lived, died.</textarea>').appendTo(form)
+
+ form.bindNative('ajax:success', function(e, data) {
+ setTimeout(function() {
+ equal(data.params.user_bio, 'born, lived, died.')
+ start()
+ }, 13)
+ })
+ form.triggerNative('submit')
+
+ App.checkDisabledState(textarea, 'processing ...')
+})
+
+asyncTest('a[data-disable-with] disables', 4, function() {
+ var link = $('a[data-disable-with]')
+
+ App.checkEnabledState(link, 'Click me')
+
+ link.triggerNative('click')
+ App.checkDisabledState(link, 'clicking...')
+ start()
+})
+
+test('a[data-disable-with] re-enables when `pageshow` event is triggered', function() {
+ var link = $('a[data-disable-with]')
+
+ App.checkEnabledState(link, 'Click me')
+
+ link.triggerNative('click')
+ App.checkDisabledState(link, 'clicking...')
+
+ $(window).triggerNative('pageshow')
+ App.checkEnabledState(link, 'Click me')
+})
+
+asyncTest('a[data-remote][data-disable-with] disables and re-enables', 6, function() {
+ var link = $('a[data-disable-with]').attr('data-remote', true)
+
+ App.checkEnabledState(link, 'Click me')
+
+ link
+ .bindNative('ajax:beforeSend', function() {
+ App.checkDisabledState(link, 'clicking...')
+ })
+ .bindNative('ajax:complete', function() {
+ setTimeout( function() {
+ App.checkEnabledState(link, 'Click me')
+ start()
+ }, 15)
+ })
+ .triggerNative('click')
+})
+
+asyncTest('a[data-remote][data-disable-with] re-enables when `ajax:before` event is cancelled', 6, function() {
+ var link = $('a[data-disable-with]').attr('data-remote', true)
+
+ App.checkEnabledState(link, 'Click me')
+
+ link
+ .bindNative('ajax:before', function() {
+ App.checkDisabledState(link, 'clicking...')
+ return false
+ })
+ .triggerNative('click')
+
+ setTimeout(function() {
+ App.checkEnabledState(link, 'Click me')
+ start()
+ }, 30)
+})
+
+asyncTest('a[data-remote][data-disable-with] re-enables when `ajax:beforeSend` event is cancelled', 6, function() {
+ var link = $('a[data-disable-with]').attr('data-remote', true)
+
+ App.checkEnabledState(link, 'Click me')
+
+ link
+ .bindNative('ajax:beforeSend', function() {
+ App.checkDisabledState(link, 'clicking...')
+ return false
+ })
+ .triggerNative('click')
+
+ setTimeout(function() {
+ App.checkEnabledState(link, 'Click me')
+ start()
+ }, 30)
+})
+
+asyncTest('a[data-remote][data-disable-with] re-enables when `ajax:error` event is triggered', 6, function() {
+ var link = $('a[data-disable-with]').attr('data-remote', true).attr('href', '/error')
+
+ App.checkEnabledState(link, 'Click me')
+
+ link
+ .bindNative('ajax:beforeSend', function() {
+ App.checkDisabledState(link, 'clicking...')
+ })
+ .triggerNative('click')
+
+ setTimeout(function() {
+ App.checkEnabledState(link, 'Click me')
+ start()
+ }, 30)
+})
+
+asyncTest('form[data-remote] input|button|textarea[data-disable-with] does not disable when `ajax:beforeSend` event is cancelled', 8, function() {
+ var form = $('form[data-remote]'),
+ input = form.find('input:text'),
+ button = $('<button data-disable-with="submitting ..." name="submit2">Submit</button>').appendTo(form),
+ textarea = $('<textarea data-disable-with="processing ..." name="user_bio">born, lived, died.</textarea>').appendTo(form),
+ submit = $('<input type="submit" data-disable-with="submitting ..." name="submit2" value="Submit" />').appendTo(form)
+
+ form
+ .bindNative('ajax:beforeSend', function() {
+ return false
+ })
+ .triggerNative('submit')
+
+ App.checkEnabledState(input, 'john')
+ App.checkEnabledState(button, 'Submit')
+ App.checkEnabledState(textarea, 'born, lived, died.')
+ App.checkEnabledState(submit, 'Submit')
+
+ start()
+})
+
+asyncTest('ctrl-clicking on a link does not disables the link', 6, function() {
+ var link = $('a[data-disable-with]')
+
+ App.checkEnabledState(link, 'Click me')
+
+ link.triggerNative('click', { metaKey: true })
+ App.checkEnabledState(link, 'Click me')
+
+ link.triggerNative('click', { metaKey: true })
+ App.checkEnabledState(link, 'Click me')
+ start()
+})
+
+asyncTest('button[data-remote][data-disable-with] disables and re-enables', 6, function() {
+ var button = $('button[data-remote][data-disable-with]')
+
+ App.checkEnabledState(button, 'Click me')
+
+ button
+ .bindNative('ajax:send', function() {
+ App.checkDisabledState(button, 'clicking...')
+ })
+ .bindNative('ajax:complete', function() {
+ setTimeout( function() {
+ App.checkEnabledState(button, 'Click me')
+ start()
+ }, 15)
+ })
+ .triggerNative('click')
+})
+
+asyncTest('button[data-remote][data-disable-with] re-enables when `ajax:before` event is cancelled', 6, function() {
+ var button = $('button[data-remote][data-disable-with]')
+
+ App.checkEnabledState(button, 'Click me')
+
+ button
+ .bindNative('ajax:before', function() {
+ App.checkDisabledState(button, 'clicking...')
+ return false
+ })
+ .triggerNative('click')
+
+ setTimeout(function() {
+ App.checkEnabledState(button, 'Click me')
+ start()
+ }, 30)
+})
+
+asyncTest('button[data-remote][data-disable-with] re-enables when `ajax:beforeSend` event is cancelled', 6, function() {
+ var button = $('button[data-remote][data-disable-with]')
+
+ App.checkEnabledState(button, 'Click me')
+
+ button
+ .bindNative('ajax:beforeSend', function() {
+ App.checkDisabledState(button, 'clicking...')
+ return false
+ })
+ .triggerNative('click')
+
+ setTimeout(function() {
+ App.checkEnabledState(button, 'Click me')
+ start()
+ }, 30)
+})
+
+asyncTest('button[data-remote][data-disable-with] re-enables when `ajax:error` event is triggered', 6, function() {
+ var button = $('a[data-disable-with]').attr('data-remote', true).attr('href', '/error')
+
+ App.checkEnabledState(button, 'Click me')
+
+ button
+ .bindNative('ajax:send', function() {
+ App.checkDisabledState(button, 'clicking...')
+ })
+ .triggerNative('click')
+
+ setTimeout(function() {
+ App.checkEnabledState(button, 'Click me')
+ start()
+ }, 30)
+})
diff --git a/actionview/test/ujs/public/test/data-disable.js b/actionview/test/ujs/public/test/data-disable.js
new file mode 100644
index 0000000000..ccc38cf9ae
--- /dev/null
+++ b/actionview/test/ujs/public/test/data-disable.js
@@ -0,0 +1,321 @@
+module('data-disable', {
+ setup: function() {
+ $('#qunit-fixture').append($('<form />', {
+ action: '/echo',
+ 'data-remote': 'true',
+ method: 'post'
+ }))
+ .find('form')
+ .append($('<input type="text" data-disable name="user_name" value="john" />'))
+
+ $('#qunit-fixture').append($('<form />', {
+ action: '/echo',
+ method: 'post'
+ }))
+ .find('form:last')
+ // WEEIRDD: the form won't submit to an iframe if the button is name="submit" (??!)
+ .append($('<input type="submit" data-disable name="submit2" value="Submit" />'))
+
+ $('#qunit-fixture').append($('<a />', {
+ text: 'Click me',
+ href: '/echo',
+ 'data-disable': 'true'
+ }))
+
+ $('#qunit-fixture').append($('<button />', {
+ text: 'Click me',
+ 'data-remote': true,
+ 'data-url': '/echo',
+ 'data-disable': 'true'
+ }))
+ },
+ teardown: function() {
+ $(document).unbind('iframe:loaded')
+ }
+})
+
+asyncTest('form input field with "data-disable" attribute', 7, function() {
+ var form = $('form[data-remote]'), input = form.find('input[type=text]')
+
+ App.checkEnabledState(input, 'john')
+
+ form.bindNative('ajax:success', function(e, data) {
+ setTimeout(function() {
+ App.checkEnabledState(input, 'john')
+ equal(data.params.user_name, 'john')
+ start()
+ }, 13)
+ })
+ form.triggerNative('submit')
+
+ App.checkDisabledState(input, 'john')
+})
+
+asyncTest('form button with "data-disable" attribute', 7, function() {
+ var form = $('form[data-remote]'), button = $('<button data-disable name="submit2">Submit</button>')
+ form.append(button)
+
+ App.checkEnabledState(button, 'Submit')
+
+ form.bindNative('ajax:success', function(e, data) {
+ setTimeout(function() {
+ App.checkEnabledState(button, 'Submit')
+ start()
+ }, 13)
+ })
+ form.triggerNative('submit')
+
+ App.checkDisabledState(button, 'Submit')
+ equal(button.data('ujs:enable-with'), undefined)
+})
+
+asyncTest('form input[type=submit][data-disable] disables', 6, function() {
+ var form = $('form:not([data-remote])'), input = form.find('input[type=submit]')
+
+ App.checkEnabledState(input, 'Submit')
+
+ // WEEIRDD: attaching this handler makes the test work in IE7
+ $(document).bind('iframe:loading', function(e, f) {})
+
+ $(document).bind('iframe:loaded', function(e, data) {
+ setTimeout(function() {
+ App.checkDisabledState(input, 'Submit')
+ start()
+ }, 30)
+ })
+ form.triggerNative('submit')
+
+ setTimeout(function() {
+ App.checkDisabledState(input, 'Submit')
+ }, 30)
+})
+
+asyncTest('form[data-remote] input[type=submit][data-disable] is replaced in ajax callback', 2, function() {
+ var form = $('form:not([data-remote])').attr('data-remote', 'true'), origFormContents = form.html()
+
+ form.bindNative('ajax:success', function() {
+ form.html(origFormContents)
+
+ setTimeout(function() {
+ var input = form.find('input[type=submit]')
+ App.checkEnabledState(input, 'Submit')
+ start()
+ }, 30)
+ }).triggerNative('submit')
+})
+
+asyncTest('form[data-remote] input[data-disable] is replaced with disabled field in ajax callback', 2, function() {
+ var form = $('form:not([data-remote])').attr('data-remote', 'true'), input = form.find('input[type=submit]'),
+ newDisabledInput = input.clone().attr('disabled', 'disabled')
+
+ form.bindNative('ajax:success', function() {
+ input.replaceWith(newDisabledInput)
+
+ setTimeout(function() {
+ App.checkEnabledState(newDisabledInput, 'Submit')
+ start()
+ }, 30)
+ }).triggerNative('submit')
+})
+
+asyncTest('form[data-remote] textarea[data-disable] attribute', 3, function() {
+ var form = $('form[data-remote]'),
+ textarea = $('<textarea data-disable name="user_bio">born, lived, died.</textarea>').appendTo(form)
+
+ form.bindNative('ajax:success', function(e, data) {
+ setTimeout(function() {
+ equal(data.params.user_bio, 'born, lived, died.')
+ start()
+ }, 13)
+ })
+ form.triggerNative('submit')
+
+ App.checkDisabledState(textarea, 'born, lived, died.')
+})
+
+asyncTest('a[data-disable] disables', 5, function() {
+ var link = $('a[data-disable]')
+
+ App.checkEnabledState(link, 'Click me')
+
+ link.triggerNative('click')
+ App.checkDisabledState(link, 'Click me')
+ equal(link.data('ujs:enable-with'), undefined)
+ start()
+})
+
+asyncTest('a[data-remote][data-disable] disables and re-enables', 6, function() {
+ var link = $('a[data-disable]').attr('data-remote', true)
+
+ App.checkEnabledState(link, 'Click me')
+
+ link
+ .bindNative('ajax:send', function() {
+ App.checkDisabledState(link, 'Click me')
+ })
+ .bindNative('ajax:complete', function() {
+ setTimeout( function() {
+ App.checkEnabledState(link, 'Click me')
+ start()
+ }, 15)
+ })
+ .triggerNative('click')
+})
+
+asyncTest('a[data-remote][data-disable] re-enables when `ajax:before` event is cancelled', 6, function() {
+ var link = $('a[data-disable]').attr('data-remote', true)
+
+ App.checkEnabledState(link, 'Click me')
+
+ link
+ .bindNative('ajax:before', function() {
+ App.checkDisabledState(link, 'Click me')
+ return false
+ })
+ .triggerNative('click')
+
+ setTimeout(function() {
+ App.checkEnabledState(link, 'Click me')
+ start()
+ }, 30)
+})
+
+asyncTest('a[data-remote][data-disable] re-enables when `ajax:beforeSend` event is cancelled', 6, function() {
+ var link = $('a[data-disable]').attr('data-remote', true)
+
+ App.checkEnabledState(link, 'Click me')
+
+ link
+ .bindNative('ajax:beforeSend', function() {
+ App.checkDisabledState(link, 'Click me')
+ return false
+ })
+ .triggerNative('click')
+
+ setTimeout(function() {
+ App.checkEnabledState(link, 'Click me')
+ start()
+ }, 30)
+})
+
+asyncTest('a[data-remote][data-disable] re-enables when `ajax:error` event is triggered', 6, function() {
+ var link = $('a[data-disable]').attr('data-remote', true).attr('href', '/error')
+
+ App.checkEnabledState(link, 'Click me')
+
+ link
+ .bindNative('ajax:send', function() {
+ App.checkDisabledState(link, 'Click me')
+ })
+ .triggerNative('click')
+
+ setTimeout(function() {
+ App.checkEnabledState(link, 'Click me')
+ start()
+ }, 30)
+})
+
+asyncTest('form[data-remote] input|button|textarea[data-disable] does not disable when `ajax:beforeSend` event is cancelled', 8, function() {
+ var form = $('form[data-remote]'),
+ input = form.find('input:text'),
+ button = $('<button data-disable="submitting ..." name="submit2">Submit</button>').appendTo(form),
+ textarea = $('<textarea data-disable name="user_bio">born, lived, died.</textarea>').appendTo(form),
+ submit = $('<input type="submit" data-disable="submitting ..." name="submit2" value="Submit" />').appendTo(form)
+
+ form
+ .bindNative('ajax:beforeSend', function() {
+ return false
+ })
+ .triggerNative('submit')
+
+ App.checkEnabledState(input, 'john')
+ App.checkEnabledState(button, 'Submit')
+ App.checkEnabledState(textarea, 'born, lived, died.')
+ App.checkEnabledState(submit, 'Submit')
+
+ start()
+})
+
+asyncTest('ctrl-clicking on a link does not disables the link', 6, function() {
+ var link = $('a[data-disable]')
+
+ App.checkEnabledState(link, 'Click me')
+
+ link.triggerNative('click', { metaKey: true })
+ App.checkEnabledState(link, 'Click me')
+
+ link.triggerNative('click', { ctrlKey: true })
+ App.checkEnabledState(link, 'Click me')
+ start()
+})
+
+asyncTest('button[data-remote][data-disable] disables and re-enables', 6, function() {
+ var button = $('button[data-remote][data-disable]')
+
+ App.checkEnabledState(button, 'Click me')
+
+ button
+ .bindNative('ajax:send', function() {
+ App.checkDisabledState(button, 'Click me')
+ })
+ .bindNative('ajax:complete', function() {
+ setTimeout( function() {
+ App.checkEnabledState(button, 'Click me')
+ start()
+ }, 15)
+ })
+ .triggerNative('click')
+})
+
+asyncTest('button[data-remote][data-disable] re-enables when `ajax:before` event is cancelled', 6, function() {
+ var button = $('button[data-remote][data-disable]')
+
+ App.checkEnabledState(button, 'Click me')
+
+ button
+ .bindNative('ajax:before', function() {
+ App.checkDisabledState(button, 'Click me')
+ return false
+ })
+ .triggerNative('click')
+
+ setTimeout(function() {
+ App.checkEnabledState(button, 'Click me')
+ start()
+ }, 30)
+})
+
+asyncTest('button[data-remote][data-disable] re-enables when `ajax:beforeSend` event is cancelled', 6, function() {
+ var button = $('button[data-remote][data-disable]')
+
+ App.checkEnabledState(button, 'Click me')
+
+ button
+ .bindNative('ajax:beforeSend', function() {
+ App.checkDisabledState(button, 'Click me')
+ return false
+ })
+ .triggerNative('click')
+
+ setTimeout(function() {
+ App.checkEnabledState(button, 'Click me')
+ start()
+ }, 30)
+})
+
+asyncTest('button[data-remote][data-disable] re-enables when `ajax:error` event is triggered', 6, function() {
+ var button = $('a[data-disable]').attr('data-remote', true).attr('href', '/error')
+
+ App.checkEnabledState(button, 'Click me')
+
+ button
+ .bindNative('ajax:send', function() {
+ App.checkDisabledState(button, 'Click me')
+ })
+ .triggerNative('click')
+
+ setTimeout(function() {
+ App.checkEnabledState(button, 'Click me')
+ start()
+ }, 30)
+})
diff --git a/actionview/test/ujs/public/test/data-method.js b/actionview/test/ujs/public/test/data-method.js
new file mode 100644
index 0000000000..47d940c577
--- /dev/null
+++ b/actionview/test/ujs/public/test/data-method.js
@@ -0,0 +1,85 @@
+(function() {
+
+module('data-method', {
+ setup: function() {
+ $('#qunit-fixture').append($('<a />', {
+ href: '/echo', 'data-method': 'delete', text: 'destroy!'
+ }))
+ },
+ teardown: function() {
+ $(document).unbind('iframe:loaded')
+ }
+})
+
+function submit(fn, options) {
+ $(document).bind('iframe:loaded', function(e, data) {
+ fn(data)
+ start()
+ })
+
+ $('#qunit-fixture').find('a')
+ .triggerNative('click')
+}
+
+asyncTest('link with "data-method" set to "delete"', 3, function() {
+ submit(function(data) {
+ equal(data.REQUEST_METHOD, 'DELETE')
+ strictEqual(data.params.authenticity_token, undefined)
+ strictEqual(data.HTTP_X_CSRF_TOKEN, undefined)
+ })
+})
+
+asyncTest('click on the child of link with "data-method"', 3, function() {
+ $(document).bind('iframe:loaded', function(e, data) {
+ equal(data.REQUEST_METHOD, 'DELETE')
+ strictEqual(data.params.authenticity_token, undefined)
+ strictEqual(data.HTTP_X_CSRF_TOKEN, undefined)
+ start()
+ })
+ $('#qunit-fixture a').html('<strong>destroy!</strong>').find('strong').triggerNative('click')
+})
+
+asyncTest('link with "data-method" and CSRF', 1, function() {
+ $('#qunit-fixture')
+ .append('<meta name="csrf-param" content="authenticity_token"/>')
+ .append('<meta name="csrf-token" content="cf50faa3fe97702ca1ae"/>')
+
+ submit(function(data) {
+ equal(data.params.authenticity_token, 'cf50faa3fe97702ca1ae')
+ })
+})
+
+asyncTest('link "target" should be carried over to generated form', 1, function() {
+ $('a[data-method]').attr('target', 'super-special-frame')
+ submit(function(data) {
+ equal(data.params._target, 'super-special-frame')
+ })
+})
+
+asyncTest('link with "data-method" and cross origin', 1, function() {
+ var data = {}
+
+ $('#qunit-fixture')
+ .append('<meta name="csrf-param" content="authenticity_token"/>')
+ .append('<meta name="csrf-token" content="cf50faa3fe97702ca1ae"/>')
+
+ $(document).on('submit', 'form', function(e) {
+ $(e.currentTarget).serializeArray().map(function(item) {
+ data[item.name] = item.value
+ })
+
+ return false
+ })
+
+ var link = $('#qunit-fixture').find('a')
+
+ link.attr('href', 'http://www.alfajango.com')
+
+ link.triggerNative('click')
+
+ start()
+
+ notEqual(data.authenticity_token, 'cf50faa3fe97702ca1ae')
+})
+
+})()
diff --git a/actionview/test/ujs/public/test/data-remote.js b/actionview/test/ujs/public/test/data-remote.js
new file mode 100644
index 0000000000..a51aa10417
--- /dev/null
+++ b/actionview/test/ujs/public/test/data-remote.js
@@ -0,0 +1,400 @@
+module('data-remote', {
+ setup: function() {
+ $('#qunit-fixture')
+ .append($('<a />', {
+ href: '/echo',
+ 'data-remote': 'true',
+ 'data-params': 'data1=value1&data2=value2',
+ text: 'my address'
+ }))
+ .append($('<button />', {
+ 'data-url': '/echo',
+ 'data-remote': 'true',
+ 'data-params': 'data1=value1&data2=value2',
+ text: 'my button'
+ }))
+ .append($('<form />', {
+ action: '/echo',
+ 'data-remote': 'true',
+ method: 'post',
+ id: 'my-remote-form'
+ }))
+ .append($('<a />', {
+ href: '/echo',
+ 'data-remote': 'true',
+ disabled: 'disabled',
+ text: 'Disabed link'
+ }))
+ .find('form').append($('<input type="text" name="user_name" value="john">'))
+
+ }
+})
+
+asyncTest('ctrl-clicking on a link does not fire ajaxyness', 0, function() {
+ var link = $('a[data-remote]')
+
+ // Ideally, we'd setup an iframe to intercept normal link clicks
+ // and add a test to make sure the iframe:loaded event is triggered.
+ // However, jquery doesn't actually cause a native `click` event and
+ // follow links using `trigger('click')`, it only fires bindings.
+ link
+ .removeAttr('data-params')
+ .bindNative('ajax:beforeSend', function() {
+ ok(false, 'ajax should not be triggered')
+ })
+
+ link.triggerNative('click', { metaKey: true })
+ link.triggerNative('click', { ctrlKey: true })
+
+ setTimeout(function() { start() }, 13)
+})
+
+asyncTest('ctrl-clicking on a link still fires ajax for non-GET links and for links with "data-params"', 2, function() {
+ var link = $('a[data-remote]')
+
+ link
+ .removeAttr('data-params')
+ .attr('data-method', 'POST')
+ .bindNative('ajax:beforeSend', function() {
+ ok(true, 'ajax should be triggered')
+ })
+ .triggerNative('click', { metaKey: true })
+
+ link
+ .removeAttr('data-method')
+ .attr('data-params', 'name=steve')
+ .triggerNative('click', { metaKey: true })
+
+ setTimeout(function() { start() }, 13)
+})
+
+asyncTest('clicking on a link with data-remote attribute', 5, function() {
+ $('a[data-remote]')
+ .bindNative('ajax:success', function(e, data, status, xhr) {
+ App.assertCallbackInvoked('ajax:success')
+ App.assertRequestPath(data, '/echo')
+ console.log(data.params)
+ equal(data.params.data1, 'value1', 'ajax arguments should have key data1 with right value')
+ equal(data.params.data2, 'value2', 'ajax arguments should have key data2 with right value')
+ App.assertGetRequest(data)
+ })
+ .bindNative('ajax:complete', function() { start() })
+ .triggerNative('click')
+})
+
+asyncTest('clicking on a link with both query string in href and data-params', 4, function() {
+ $('a[data-remote]')
+ .attr('href', '/echo?data3=value3')
+ .bindNative('ajax:success', function(e, data, status, xhr) {
+ App.assertGetRequest(data)
+ equal(data.params.data1, 'value1', 'ajax arguments should have key data1 with right value')
+ equal(data.params.data2, 'value2', 'ajax arguments should have key data2 with right value')
+ equal(data.params.data3, 'value3', 'query string in url should be passed to server with right value')
+ })
+ .bindNative('ajax:complete', function() { start() })
+ .triggerNative('click')
+})
+
+asyncTest('clicking on a link with both query string in href and data-params with POST method', 4, function() {
+ $('a[data-remote]')
+ .attr('href', '/echo?data3=value3')
+ .attr('data-method', 'post')
+ .bindNative('ajax:success', function(e, data, status, xhr) {
+ App.assertPostRequest(data)
+ equal(data.params.data1, 'value1', 'ajax arguments should have key data1 with right value')
+ equal(data.params.data2, 'value2', 'ajax arguments should have key data2 with right value')
+ equal(data.params.data3, 'value3', 'query string in url should be passed to server with right value')
+ })
+ .bindNative('ajax:complete', function() { start() })
+ .triggerNative('click')
+})
+
+asyncTest('clicking on a link with disabled attribute', 0, function() {
+ $('a[disabled]')
+ .bindNative('ajax:before', function(e, data, status, xhr) {
+ App.assertCallbackNotInvoked('ajax:success')
+ })
+ .bindNative('ajax:complete', function() { start() })
+ .triggerNative('click')
+
+ setTimeout(function() {
+ start()
+ }, 13)
+})
+
+asyncTest('clicking on a button with data-remote attribute', 5, function() {
+ $('button[data-remote]')
+ .bindNative('ajax:success', function(e, data, status, xhr) {
+ App.assertCallbackInvoked('ajax:success')
+ App.assertRequestPath(data, '/echo')
+ equal(data.params.data1, 'value1', 'ajax arguments should have key data1 with right value')
+ equal(data.params.data2, 'value2', 'ajax arguments should have key data2 with right value')
+ App.assertGetRequest(data)
+ })
+ .bindNative('ajax:complete', function() { start() })
+ .triggerNative('click')
+})
+
+asyncTest('changing a select option with data-remote attribute', 5, function() {
+ $('form')
+ .append(
+ $('<select />', {
+ 'name': 'user_data',
+ 'data-remote': 'true',
+ 'data-params': 'data1=value1',
+ 'data-url': '/echo'
+ })
+ .append($('<option />', {value: 'optionValue1', text: 'option1'}))
+ .append($('<option />', {value: 'optionValue2', text: 'option2'}))
+ )
+
+ $('select[data-remote]')
+ .bindNative('ajax:success', function(e, data, status, xhr) {
+ App.assertCallbackInvoked('ajax:success')
+ App.assertRequestPath(data, '/echo')
+ equal(data.params.user_data, 'optionValue2', 'ajax arguments should have key term with right value')
+ equal(data.params.data1, 'value1', 'ajax arguments should have key data1 with right value')
+ App.assertGetRequest(data)
+ })
+ .bindNative('ajax:complete', function() { start() })
+ .val('optionValue2')
+ .triggerNative('change')
+})
+
+asyncTest('submitting form with data-remote attribute', 4, function() {
+ $('form[data-remote]')
+ .bindNative('ajax:success', function(e, data, status, xhr) {
+ App.assertCallbackInvoked('ajax:success')
+ App.assertRequestPath(data, '/echo')
+ equal(data.params.user_name, 'john', 'ajax arguments should have key user_name with right value')
+ App.assertPostRequest(data)
+ })
+ .bindNative('ajax:complete', function() { start() })
+ .triggerNative('submit')
+})
+
+asyncTest('submitting form with data-remote attribute should include inputs in a fieldset only once', 3, function() {
+ $('form[data-remote]')
+ .append('<fieldset><input name="items[]" value="Item" /></fieldset>')
+ .bindNative('ajax:success', function(e, data, status, xhr) {
+ App.assertCallbackInvoked('ajax:success')
+ equal(data.params.items.length, 1, 'ajax arguments should only have the item once')
+ App.assertPostRequest(data)
+ })
+ .bindNative('ajax:complete', function() {
+ $('form[data-remote], fieldset').remove()
+ start()
+ })
+ .triggerNative('submit')
+})
+
+asyncTest('submitting form with data-remote attribute submits input with matching [form] attribute', 5, function() {
+ $('#qunit-fixture')
+ .append($('<input type="text" name="user_data" value="value1" form="my-remote-form">'))
+
+ $('form[data-remote]')
+ .bindNative('ajax:success', function(e, data, status, xhr) {
+ App.assertCallbackInvoked('ajax:success')
+ App.assertRequestPath(data, '/echo')
+ equal(data.params.user_name, 'john', 'ajax arguments should have key user_name with right value')
+ equal(data.params.user_data, 'value1', 'ajax arguments should have key user_data with right value')
+ App.assertPostRequest(data)
+ })
+ .bindNative('ajax:complete', function() { start() })
+ .triggerNative('submit')
+})
+
+asyncTest('submitting form with data-remote attribute by clicking button with matching [form] attribute', 5, function() {
+ $('form[data-remote]')
+ .bindNative('ajax:success', function(e, data, status, xhr) {
+ App.assertCallbackInvoked('ajax:success')
+ App.assertRequestPath(data, '/echo')
+ equal(data.params.user_name, 'john', 'ajax arguments should have key user_name with right value')
+ equal(data.params.user_data, 'value2', 'ajax arguments should have key user_data with right value')
+ App.assertPostRequest(data)
+ })
+ .bindNative('ajax:complete', function() { start() })
+
+ $('<button />', {
+ type: 'submit',
+ name: 'user_data',
+ value: 'value1',
+ form: 'my-remote-form'
+ })
+ .appendTo($('#qunit-fixture'))
+
+ $('<button />', {
+ type: 'submit',
+ name: 'user_data',
+ value: 'value2',
+ form: 'my-remote-form'
+ })
+ .appendTo($('#qunit-fixture'))
+ .triggerNative('click')
+})
+
+asyncTest('form\'s submit bindings in browsers that don\'t support submit bubbling', 5, function() {
+ var form = $('form[data-remote]'), directBindingCalled = false
+
+ ok(!directBindingCalled, 'nothing is called')
+
+ form
+ .append($('<input type="submit" />'))
+ .bindNative('submit', function(event) {
+ ok(event.type == 'submit', 'submit event handlers are called with submit event')
+ ok(true, 'binding handler is called')
+ directBindingCalled = true
+ })
+ .bindNative('ajax:beforeSend', function() {
+ ok(true, 'form being submitted via ajax')
+ ok(directBindingCalled, 'binding handler already called')
+ })
+ .bindNative('ajax:complete', function() {
+ start()
+ })
+
+ if(!$.support.submitBubbles) {
+ // Must indrectly submit form via click to trigger jQuery's manual submit bubbling in IE
+ form.find('input[type=submit]')
+ .triggerNative('click')
+ } else {
+ form.triggerNative('submit')
+ }
+})
+
+asyncTest('returning false in form\'s submit bindings in non-submit-bubbling browsers', 1, function() {
+ var form = $('form[data-remote]')
+
+ form
+ .append($('<input type="submit" />'))
+ .bindNative('submit', function() {
+ ok(true, 'binding handler is called')
+ return false
+ })
+ .bindNative('ajax:beforeSend', function() {
+ ok(false, 'form should not be submitted')
+ })
+
+ if (!$.support.submitBubbles) {
+ // Must indrectly submit form via click to trigger jQuery's manual submit bubbling in IE
+ form.find('input[type=submit]').triggerNative('click')
+ } else {
+ form.triggerNative('submit')
+ }
+
+ setTimeout(function() { start() }, 13)
+})
+
+asyncTest('clicking on a link with falsy "data-remote" attribute does not fire ajaxyness', 0, function() {
+ $('a[data-remote]')
+ .attr('data-remote', 'false')
+ .bindNative('ajax:beforeSend', function() {
+ ok(false, 'ajax should not be triggered')
+ })
+ .bindNative('click', function() {
+ return false
+ })
+ .triggerNative('click')
+
+ setTimeout(function() { start() }, 20)
+})
+
+asyncTest('ctrl-clicking on a link with falsy "data-remote" attribute does not fire ajaxyness even if "data-params" present', 0, function() {
+ var link = $('a[data-remote]')
+
+ link
+ .removeAttr('data-params')
+ .attr('data-remote', 'false')
+ .attr('data-method', 'POST')
+ .bindNative('ajax:beforeSend', function() {
+ ok(false, 'ajax should not be triggered')
+ })
+ .bindNative('click', function() {
+ return false
+ })
+ .triggerNative('click', { metaKey: true })
+
+ link
+ .removeAttr('data-method')
+ .attr('data-params', 'name=steve')
+ .triggerNative('click', { metaKey: true })
+
+ setTimeout(function() { start() }, 20)
+})
+
+asyncTest('clicking on a button with falsy "data-remote" attribute', 0, function() {
+ $('button[data-remote]:first')
+ .attr('data-remote', 'false')
+ .bindNative('ajax:beforeSend', function() {
+ ok(false, 'ajax should not be triggered')
+ })
+ .bindNative('click', function() {
+ return false
+ })
+ .triggerNative('click')
+
+ setTimeout(function() { start() }, 20)
+})
+
+asyncTest('submitting a form with falsy "data-remote" attribute', 0, function() {
+ $('form[data-remote]:first')
+ .attr('data-remote', 'false')
+ .bindNative('ajax:beforeSend', function() {
+ ok(false, 'ajax should not be triggered')
+ })
+ .bindNative('submit', function() {
+ return false
+ })
+ .triggerNative('submit')
+
+ setTimeout(function() { start() }, 20)
+})
+
+asyncTest('changing a select option with falsy "data-remote" attribute', 0, function() {
+ $('form')
+ .append(
+ $('<select />', {
+ 'name': 'user_data',
+ 'data-remote': 'false',
+ 'data-params': 'data1=value1',
+ 'data-url': '/echo'
+ })
+ .append($('<option />', {value: 'optionValue1', text: 'option1'}))
+ .append($('<option />', {value: 'optionValue2', text: 'option2'}))
+ )
+
+ $('select[data-remote=false]:first')
+ .bindNative('ajax:beforeSend', function() {
+ ok(false, 'ajax should not be triggered')
+ })
+ .val('optionValue2')
+ .triggerNative('change')
+
+ setTimeout(function() { start() }, 20)
+})
+
+asyncTest('form should be serialized correctly', 6, function() {
+ $('form')
+ .append('<textarea name="textarea">textarea</textarea>')
+ .append('<input type="checkbox" name="checkbox[]" value="0" />')
+ .append('<input type="checkbox" checked="checked" name="checkbox[]" value="1" />')
+ .append('<input type="radio" checked="checked" name="radio" value="0" />')
+ .append('<input type="radio" name="radio" value="1" />')
+ .append('<select multiple="multiple" name="select[]">\
+ <option value="1" selected>1</option>\
+ <option value="2" selected>2</option>\
+ <option value="3">3</option>\
+ <option selected>4</option>\
+ </select>')
+ .bindNative('ajax:success', function(e, data, status, xhr) {
+ equal(data.params.checkbox.length, 1)
+ equal(data.params.checkbox[0], '1')
+ equal(data.params.radio, '0')
+ equal(data.params.select.length, 3)
+ equal(data.params.select[2], '4')
+ equal(data.params.textarea, 'textarea')
+
+ start()
+ })
+ .triggerNative('submit')
+})
diff --git a/actionview/test/ujs/public/test/override.js b/actionview/test/ujs/public/test/override.js
new file mode 100644
index 0000000000..be6ec7749b
--- /dev/null
+++ b/actionview/test/ujs/public/test/override.js
@@ -0,0 +1,56 @@
+(function() {
+
+var realHref
+
+module('override', {
+ setup: function() {
+ realHref = $.rails.href
+ $('#qunit-fixture')
+ .append($('<a />', {
+ href: '/real/href', 'data-remote': 'true', 'data-method': 'delete', 'data-href': '/data/href'
+ }))
+ },
+ teardown: function() {
+ $.rails.href = realHref
+ }
+})
+
+asyncTest('the getter for an element\'s href is publicly accessible', 1, function() {
+ ok($.rails.href)
+ start()
+})
+
+asyncTest('the getter for an element\'s href is overridable', 1, function() {
+ $.rails.href = function(element) { return $(element).data('href') }
+ $('#qunit-fixture a')
+ .bindNative('ajax:beforeSend', function(e, xhr, options) {
+ equal('/data/href', options.url)
+ return false
+ })
+ .triggerNative('click')
+ start()
+})
+
+asyncTest('the getter for an element\'s href works normally if not overridden', 1, function() {
+ $('#qunit-fixture a')
+ .bindNative('ajax:beforeSend', function(e, xhr, options) {
+ equal(location.protocol + '//' + location.host + '/real/href', options.url)
+ return false
+ })
+ .triggerNative('click')
+ start()
+})
+
+asyncTest('the event selector strings are overridable', 1, function() {
+ ok($.rails.linkClickSelector.indexOf(', a[data-custom-remote-link]') != -1, 'linkClickSelector contains custom selector')
+ start()
+})
+
+asyncTest('including jquery-ujs multiple times throws error', 1, function() {
+ throws(function() {
+ Rails.start()
+ }, 'appending rails.js again throws error')
+ setTimeout(function() { start() }, 50)
+})
+
+})()
diff --git a/actionview/test/ujs/public/test/settings.js b/actionview/test/ujs/public/test/settings.js
new file mode 100644
index 0000000000..3375456f11
--- /dev/null
+++ b/actionview/test/ujs/public/test/settings.js
@@ -0,0 +1,116 @@
+var App = App || {}
+
+App.assertCallbackInvoked = function(callbackName) {
+ ok(true, callbackName + ' callback should have been invoked')
+}
+
+App.assertCallbackNotInvoked = function(callbackName) {
+ ok(false, callbackName + ' callback should not have been invoked')
+}
+
+App.assertGetRequest = function(requestEnv) {
+ equal(requestEnv['REQUEST_METHOD'], 'GET', 'request type should be GET')
+}
+
+App.assertPostRequest = function(requestEnv) {
+ equal(requestEnv['REQUEST_METHOD'], 'POST', 'request type should be POST')
+}
+
+App.assertRequestPath = function(requestEnv, path) {
+ equal(requestEnv['PATH_INFO'], path, 'request should be sent to right url')
+}
+
+App.getVal = function(el) {
+ return el.is('input,textarea,select') ? el.val() : el.text()
+}
+
+App.disabled = function(el) {
+ return el.is('input,textarea,select,button') ?
+ (el.is(':disabled') && $.rails.getData(el[0], 'ujs:disabled')) :
+ $.rails.getData(el[0], 'ujs:disabled')
+}
+
+App.checkEnabledState = function(el, text) {
+ ok(!App.disabled(el), el.get(0).tagName + ' should not be disabled')
+ equal(App.getVal(el), text, el.get(0).tagName + ' text should be original value')
+}
+
+App.checkDisabledState = function(el, text) {
+ ok(App.disabled(el), el.get(0).tagName + ' should be disabled')
+ equal(App.getVal(el), text, el.get(0).tagName + ' text should be disabled value')
+}
+
+// hijacks normal form submit; lets it submit to an iframe to prevent
+// navigating away from the test suite
+$(document).bind('submit', function(e) {
+ if (!e.isDefaultPrevented()) {
+ var form = $(e.target), action = form.attr('action'),
+ name = 'form-frame' + jQuery.guid++,
+ iframe = $('<iframe name="' + name + '" />'),
+ iframeInput = '<input name="iframe" value="true" type="hidden" />'
+ targetInput = '<input name="_target" value="' + (form.attr('target') || '') + '" type="hidden" />'
+
+ if (action && action.indexOf('iframe') < 0) {
+ if (action.indexOf('?') < 0) {
+ form.attr('action', action + '?iframe=true')
+ } else {
+ form.attr('action', action + '&iframe=true')
+ }
+ }
+ form.attr('target', name).append(iframeInput, targetInput)
+ $('#qunit-fixture').append(iframe)
+ $.event.trigger('iframe:loading', form)
+ }
+})
+
+var MouseEvent = window.MouseEvent
+
+try {
+ new MouseEvent()
+} catch (e) {
+ MouseEvent = function(type, options) {
+ var evt = document.createEvent('MouseEvents')
+ evt.initMouseEvent(type, options.bubbles, options.cancelable, window, options.detail, 0, 0, 80, 20, options.ctrlKey, options.altKey, options.shiftKey, options.metaKey, 0, null)
+ return evt
+ }
+}
+
+$.fn.extend({
+ // trigger an native click event
+ triggerNative: function(type, options) {
+ var el = this[0],
+ event,
+ Evt = {
+ 'click': MouseEvent,
+ 'change': Event,
+ 'pageshow': PageTransitionEvent,
+ 'submit': Event
+ }[type]
+
+ options = options || {}
+ options.bubbles = true
+ options.cancelable = true
+
+ event = new Evt(type, options)
+
+ el.dispatchEvent(event)
+
+ if (type === 'submit' && !event.defaultPrevented) {
+ el.submit()
+ }
+ return this
+ },
+ bindNative: function(event, handler) {
+ if (!handler) return this
+
+ this.bind(event, function(e) {
+ var args = []
+ if (e.originalEvent.detail) {
+ args = e.originalEvent.detail.slice()
+ }
+ args.unshift(e)
+ return handler.apply(this, args)
+ })
+ return this
+ }
+})
diff --git a/actionview/test/ujs/public/vendor/jquery.metadata.js b/actionview/test/ujs/public/vendor/jquery.metadata.js
new file mode 100644
index 0000000000..ad8bfba404
--- /dev/null
+++ b/actionview/test/ujs/public/vendor/jquery.metadata.js
@@ -0,0 +1,122 @@
+/*
+ * Metadata - jQuery plugin for parsing metadata from elements
+ *
+ * Copyright (c) 2006 John Resig, Yehuda Katz, J�örn Zaefferer, Paul McLanahan
+ *
+ * Dual licensed under the MIT and GPL licenses:
+ * http://www.opensource.org/licenses/mit-license.php
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * Revision: $Id: jquery.metadata.js 4187 2007-12-16 17:15:27Z joern.zaefferer $
+ *
+ */
+
+/**
+ * Sets the type of metadata to use. Metadata is encoded in JSON, and each property
+ * in the JSON will become a property of the element itself.
+ *
+ * There are three supported types of metadata storage:
+ *
+ * attr: Inside an attribute. The name parameter indicates *which* attribute.
+ *
+ * class: Inside the class attribute, wrapped in curly braces: { }
+ *
+ * elem: Inside a child element (e.g. a script tag). The
+ * name parameter indicates *which* element.
+ *
+ * The metadata for an element is loaded the first time the element is accessed via jQuery.
+ *
+ * As a result, you can define the metadata type, use $(expr) to load the metadata into the elements
+ * matched by expr, then redefine the metadata type and run another $(expr) for other elements.
+ *
+ * @name $.metadata.setType
+ *
+ * @example <p id="one" class="some_class {item_id: 1, item_label: 'Label'}">This is a p</p>
+ * @before $.metadata.setType("class")
+ * @after $("#one").metadata().item_id == 1; $("#one").metadata().item_label == "Label"
+ * @desc Reads metadata from the class attribute
+ *
+ * @example <p id="one" class="some_class" data="{item_id: 1, item_label: 'Label'}">This is a p</p>
+ * @before $.metadata.setType("attr", "data")
+ * @after $("#one").metadata().item_id == 1; $("#one").metadata().item_label == "Label"
+ * @desc Reads metadata from a "data" attribute
+ *
+ * @example <p id="one" class="some_class"><script>{item_id: 1, item_label: 'Label'}</script>This is a p</p>
+ * @before $.metadata.setType("elem", "script")
+ * @after $("#one").metadata().item_id == 1; $("#one").metadata().item_label == "Label"
+ * @desc Reads metadata from a nested script element
+ *
+ * @param String type The encoding type
+ * @param String name The name of the attribute to be used to get metadata (optional)
+ * @cat Plugins/Metadata
+ * @descr Sets the type of encoding to be used when loading metadata for the first time
+ * @type undefined
+ * @see metadata()
+ */
+
+(function($) {
+
+$.extend({
+ metadata : {
+ defaults : {
+ type: 'class',
+ name: 'metadata',
+ cre: /({.*})/,
+ single: 'metadata'
+ },
+ setType: function( type, name ){
+ this.defaults.type = type;
+ this.defaults.name = name;
+ },
+ get: function( elem, opts ){
+ var settings = $.extend({},this.defaults,opts);
+ // check for empty string in single property
+ if ( !settings.single.length ) settings.single = 'metadata';
+
+ var data = $.data(elem, settings.single);
+ // returned cached data if it already exists
+ if ( data ) return data;
+
+ data = "{}";
+
+ if ( settings.type == "class" ) {
+ var m = settings.cre.exec( elem.className );
+ if ( m )
+ data = m[1];
+ } else if ( settings.type == "elem" ) {
+ if( !elem.getElementsByTagName )
+ return undefined;
+ var e = elem.getElementsByTagName(settings.name);
+ if ( e.length )
+ data = $.trim(e[0].innerHTML);
+ } else if ( elem.getAttribute != undefined ) {
+ var attr = elem.getAttribute( settings.name );
+ if ( attr )
+ data = attr;
+ }
+
+ if ( data.indexOf( '{' ) <0 )
+ data = "{" + data + "}";
+
+ data = eval("(" + data + ")");
+
+ $.data( elem, settings.single, data );
+ return data;
+ }
+ }
+});
+
+/**
+ * Returns the metadata object for the first member of the jQuery object.
+ *
+ * @name metadata
+ * @descr Returns element's metadata object
+ * @param Object opts An object contianing settings to override the defaults
+ * @type jQuery
+ * @cat Plugins/Metadata
+ */
+$.fn.metadata = function( opts ){
+ return $.metadata.get( this[0], opts );
+};
+
+})(jQuery); \ No newline at end of file
diff --git a/actionview/test/ujs/public/vendor/qunit.css b/actionview/test/ujs/public/vendor/qunit.css
new file mode 100644
index 0000000000..93026e3ba3
--- /dev/null
+++ b/actionview/test/ujs/public/vendor/qunit.css
@@ -0,0 +1,237 @@
+/*!
+ * QUnit 1.14.0
+ * http://qunitjs.com/
+ *
+ * Copyright 2013 jQuery Foundation and other contributors
+ * Released under the MIT license
+ * http://jquery.org/license
+ *
+ * Date: 2014-01-31T16:40Z
+ */
+
+/** Font Family and Sizes */
+
+#qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult {
+ font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial, sans-serif;
+}
+
+#qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; }
+#qunit-tests { font-size: smaller; }
+
+
+/** Resets */
+
+#qunit-tests, #qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult, #qunit-modulefilter {
+ margin: 0;
+ padding: 0;
+}
+
+
+/** Header */
+
+#qunit-header {
+ padding: 0.5em 0 0.5em 1em;
+
+ color: #8699A4;
+ background-color: #0D3349;
+
+ font-size: 1.5em;
+ line-height: 1em;
+ font-weight: 400;
+
+ border-radius: 5px 5px 0 0;
+}
+
+#qunit-header a {
+ text-decoration: none;
+ color: #C2CCD1;
+}
+
+#qunit-header a:hover,
+#qunit-header a:focus {
+ color: #FFF;
+}
+
+#qunit-testrunner-toolbar label {
+ display: inline-block;
+ padding: 0 0.5em 0 0.1em;
+}
+
+#qunit-banner {
+ height: 5px;
+}
+
+#qunit-testrunner-toolbar {
+ padding: 0.5em 0 0.5em 2em;
+ color: #5E740B;
+ background-color: #EEE;
+ overflow: hidden;
+}
+
+#qunit-userAgent {
+ padding: 0.5em 0 0.5em 2.5em;
+ background-color: #2B81AF;
+ color: #FFF;
+ text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px;
+}
+
+#qunit-modulefilter-container {
+ float: right;
+}
+
+/** Tests: Pass/Fail */
+
+#qunit-tests {
+ list-style-position: inside;
+}
+
+#qunit-tests li {
+ padding: 0.4em 0.5em 0.4em 2.5em;
+ border-bottom: 1px solid #FFF;
+ list-style-position: inside;
+}
+
+#qunit-tests.hidepass li.pass, #qunit-tests.hidepass li.running {
+ display: none;
+}
+
+#qunit-tests li strong {
+ cursor: pointer;
+}
+
+#qunit-tests li a {
+ padding: 0.5em;
+ color: #C2CCD1;
+ text-decoration: none;
+}
+#qunit-tests li a:hover,
+#qunit-tests li a:focus {
+ color: #000;
+}
+
+#qunit-tests li .runtime {
+ float: right;
+ font-size: smaller;
+}
+
+.qunit-assert-list {
+ margin-top: 0.5em;
+ padding: 0.5em;
+
+ background-color: #FFF;
+
+ border-radius: 5px;
+}
+
+.qunit-collapsed {
+ display: none;
+}
+
+#qunit-tests table {
+ border-collapse: collapse;
+ margin-top: 0.2em;
+}
+
+#qunit-tests th {
+ text-align: right;
+ vertical-align: top;
+ padding: 0 0.5em 0 0;
+}
+
+#qunit-tests td {
+ vertical-align: top;
+}
+
+#qunit-tests pre {
+ margin: 0;
+ white-space: pre-wrap;
+ word-wrap: break-word;
+}
+
+#qunit-tests del {
+ background-color: #E0F2BE;
+ color: #374E0C;
+ text-decoration: none;
+}
+
+#qunit-tests ins {
+ background-color: #FFCACA;
+ color: #500;
+ text-decoration: none;
+}
+
+/*** Test Counts */
+
+#qunit-tests b.counts { color: #000; }
+#qunit-tests b.passed { color: #5E740B; }
+#qunit-tests b.failed { color: #710909; }
+
+#qunit-tests li li {
+ padding: 5px;
+ background-color: #FFF;
+ border-bottom: none;
+ list-style-position: inside;
+}
+
+/*** Passing Styles */
+
+#qunit-tests li li.pass {
+ color: #3C510C;
+ background-color: #FFF;
+ border-left: 10px solid #C6E746;
+}
+
+#qunit-tests .pass { color: #528CE0; background-color: #D2E0E6; }
+#qunit-tests .pass .test-name { color: #366097; }
+
+#qunit-tests .pass .test-actual,
+#qunit-tests .pass .test-expected { color: #999; }
+
+#qunit-banner.qunit-pass { background-color: #C6E746; }
+
+/*** Failing Styles */
+
+#qunit-tests li li.fail {
+ color: #710909;
+ background-color: #FFF;
+ border-left: 10px solid #EE5757;
+ white-space: pre;
+}
+
+#qunit-tests > li:last-child {
+ border-radius: 0 0 5px 5px;
+}
+
+#qunit-tests .fail { color: #000; background-color: #EE5757; }
+#qunit-tests .fail .test-name,
+#qunit-tests .fail .module-name { color: #000; }
+
+#qunit-tests .fail .test-actual { color: #EE5757; }
+#qunit-tests .fail .test-expected { color: #008000; }
+
+#qunit-banner.qunit-fail { background-color: #EE5757; }
+
+
+/** Result */
+
+#qunit-testresult {
+ padding: 0.5em 0.5em 0.5em 2.5em;
+
+ color: #2B81AF;
+ background-color: #D2E0E6;
+
+ border-bottom: 1px solid #FFF;
+}
+#qunit-testresult .module-name {
+ font-weight: 700;
+}
+
+/** Fixture */
+
+#qunit-fixture {
+ position: absolute;
+ top: -10000px;
+ left: -10000px;
+ width: 1000px;
+ height: 1000px;
+}
diff --git a/actionview/test/ujs/public/vendor/qunit.js b/actionview/test/ujs/public/vendor/qunit.js
new file mode 100644
index 0000000000..0e279fde17
--- /dev/null
+++ b/actionview/test/ujs/public/vendor/qunit.js
@@ -0,0 +1,2288 @@
+/*!
+ * QUnit 1.14.0
+ * http://qunitjs.com/
+ *
+ * Copyright 2013 jQuery Foundation and other contributors
+ * Released under the MIT license
+ * http://jquery.org/license
+ *
+ * Date: 2014-01-31T16:40Z
+ */
+
+(function( window ) {
+
+var QUnit,
+ assert,
+ config,
+ onErrorFnPrev,
+ testId = 0,
+ fileName = (sourceFromStacktrace( 0 ) || "" ).replace(/(:\d+)+\)?/, "").replace(/.+\//, ""),
+ toString = Object.prototype.toString,
+ hasOwn = Object.prototype.hasOwnProperty,
+ // Keep a local reference to Date (GH-283)
+ Date = window.Date,
+ setTimeout = window.setTimeout,
+ clearTimeout = window.clearTimeout,
+ defined = {
+ document: typeof window.document !== "undefined",
+ setTimeout: typeof window.setTimeout !== "undefined",
+ sessionStorage: (function() {
+ var x = "qunit-test-string";
+ try {
+ sessionStorage.setItem( x, x );
+ sessionStorage.removeItem( x );
+ return true;
+ } catch( e ) {
+ return false;
+ }
+ }())
+ },
+ /**
+ * Provides a normalized error string, correcting an issue
+ * with IE 7 (and prior) where Error.prototype.toString is
+ * not properly implemented
+ *
+ * Based on http://es5.github.com/#x15.11.4.4
+ *
+ * @param {String|Error} error
+ * @return {String} error message
+ */
+ errorString = function( error ) {
+ var name, message,
+ errorString = error.toString();
+ if ( errorString.substring( 0, 7 ) === "[object" ) {
+ name = error.name ? error.name.toString() : "Error";
+ message = error.message ? error.message.toString() : "";
+ if ( name && message ) {
+ return name + ": " + message;
+ } else if ( name ) {
+ return name;
+ } else if ( message ) {
+ return message;
+ } else {
+ return "Error";
+ }
+ } else {
+ return errorString;
+ }
+ },
+ /**
+ * Makes a clone of an object using only Array or Object as base,
+ * and copies over the own enumerable properties.
+ *
+ * @param {Object} obj
+ * @return {Object} New object with only the own properties (recursively).
+ */
+ objectValues = function( obj ) {
+ // Grunt 0.3.x uses an older version of jshint that still has jshint/jshint#392.
+ /*jshint newcap: false */
+ var key, val,
+ vals = QUnit.is( "array", obj ) ? [] : {};
+ for ( key in obj ) {
+ if ( hasOwn.call( obj, key ) ) {
+ val = obj[key];
+ vals[key] = val === Object(val) ? objectValues(val) : val;
+ }
+ }
+ return vals;
+ };
+
+
+// Root QUnit object.
+// `QUnit` initialized at top of scope
+QUnit = {
+
+ // call on start of module test to prepend name to all tests
+ module: function( name, testEnvironment ) {
+ config.currentModule = name;
+ config.currentModuleTestEnvironment = testEnvironment;
+ config.modules[name] = true;
+ },
+
+ asyncTest: function( testName, expected, callback ) {
+ if ( arguments.length === 2 ) {
+ callback = expected;
+ expected = null;
+ }
+
+ QUnit.test( testName, expected, callback, true );
+ },
+
+ test: function( testName, expected, callback, async ) {
+ var test,
+ nameHtml = "<span class='test-name'>" + escapeText( testName ) + "</span>";
+
+ if ( arguments.length === 2 ) {
+ callback = expected;
+ expected = null;
+ }
+
+ if ( config.currentModule ) {
+ nameHtml = "<span class='module-name'>" + escapeText( config.currentModule ) + "</span>: " + nameHtml;
+ }
+
+ test = new Test({
+ nameHtml: nameHtml,
+ testName: testName,
+ expected: expected,
+ async: async,
+ callback: callback,
+ module: config.currentModule,
+ moduleTestEnvironment: config.currentModuleTestEnvironment,
+ stack: sourceFromStacktrace( 2 )
+ });
+
+ if ( !validTest( test ) ) {
+ return;
+ }
+
+ test.queue();
+ },
+
+ // Specify the number of expected assertions to guarantee that failed test (no assertions are run at all) don't slip through.
+ expect: function( asserts ) {
+ if (arguments.length === 1) {
+ config.current.expected = asserts;
+ } else {
+ return config.current.expected;
+ }
+ },
+
+ start: function( count ) {
+ // QUnit hasn't been initialized yet.
+ // Note: RequireJS (et al) may delay onLoad
+ if ( config.semaphore === undefined ) {
+ QUnit.begin(function() {
+ // This is triggered at the top of QUnit.load, push start() to the event loop, to allow QUnit.load to finish first
+ setTimeout(function() {
+ QUnit.start( count );
+ });
+ });
+ return;
+ }
+
+ config.semaphore -= count || 1;
+ // don't start until equal number of stop-calls
+ if ( config.semaphore > 0 ) {
+ return;
+ }
+ // ignore if start is called more often then stop
+ if ( config.semaphore < 0 ) {
+ config.semaphore = 0;
+ QUnit.pushFailure( "Called start() while already started (QUnit.config.semaphore was 0 already)", null, sourceFromStacktrace(2) );
+ return;
+ }
+ // A slight delay, to avoid any current callbacks
+ if ( defined.setTimeout ) {
+ setTimeout(function() {
+ if ( config.semaphore > 0 ) {
+ return;
+ }
+ if ( config.timeout ) {
+ clearTimeout( config.timeout );
+ }
+
+ config.blocking = false;
+ process( true );
+ }, 13);
+ } else {
+ config.blocking = false;
+ process( true );
+ }
+ },
+
+ stop: function( count ) {
+ config.semaphore += count || 1;
+ config.blocking = true;
+
+ if ( config.testTimeout && defined.setTimeout ) {
+ clearTimeout( config.timeout );
+ config.timeout = setTimeout(function() {
+ QUnit.ok( false, "Test timed out" );
+ config.semaphore = 1;
+ QUnit.start();
+ }, config.testTimeout );
+ }
+ }
+};
+
+// We use the prototype to distinguish between properties that should
+// be exposed as globals (and in exports) and those that shouldn't
+(function() {
+ function F() {}
+ F.prototype = QUnit;
+ QUnit = new F();
+ // Make F QUnit's constructor so that we can add to the prototype later
+ QUnit.constructor = F;
+}());
+
+/**
+ * Config object: Maintain internal state
+ * Later exposed as QUnit.config
+ * `config` initialized at top of scope
+ */
+config = {
+ // The queue of tests to run
+ queue: [],
+
+ // block until document ready
+ blocking: true,
+
+ // when enabled, show only failing tests
+ // gets persisted through sessionStorage and can be changed in UI via checkbox
+ hidepassed: false,
+
+ // by default, run previously failed tests first
+ // very useful in combination with "Hide passed tests" checked
+ reorder: true,
+
+ // by default, modify document.title when suite is done
+ altertitle: true,
+
+ // by default, scroll to top of the page when suite is done
+ scrolltop: true,
+
+ // when enabled, all tests must call expect()
+ requireExpects: false,
+
+ // add checkboxes that are persisted in the query-string
+ // when enabled, the id is set to `true` as a `QUnit.config` property
+ urlConfig: [
+ {
+ id: "noglobals",
+ label: "Check for Globals",
+ tooltip: "Enabling this will test if any test introduces new properties on the `window` object. Stored as query-strings."
+ },
+ {
+ id: "notrycatch",
+ label: "No try-catch",
+ tooltip: "Enabling this will run tests outside of a try-catch block. Makes debugging exceptions in IE reasonable. Stored as query-strings."
+ }
+ ],
+
+ // Set of all modules.
+ modules: {},
+
+ // logging callback queues
+ begin: [],
+ done: [],
+ log: [],
+ testStart: [],
+ testDone: [],
+ moduleStart: [],
+ moduleDone: []
+};
+
+// Initialize more QUnit.config and QUnit.urlParams
+(function() {
+ var i, current,
+ location = window.location || { search: "", protocol: "file:" },
+ params = location.search.slice( 1 ).split( "&" ),
+ length = params.length,
+ urlParams = {};
+
+ if ( params[ 0 ] ) {
+ for ( i = 0; i < length; i++ ) {
+ current = params[ i ].split( "=" );
+ current[ 0 ] = decodeURIComponent( current[ 0 ] );
+
+ // allow just a key to turn on a flag, e.g., test.html?noglobals
+ current[ 1 ] = current[ 1 ] ? decodeURIComponent( current[ 1 ] ) : true;
+ if ( urlParams[ current[ 0 ] ] ) {
+ urlParams[ current[ 0 ] ] = [].concat( urlParams[ current[ 0 ] ], current[ 1 ] );
+ } else {
+ urlParams[ current[ 0 ] ] = current[ 1 ];
+ }
+ }
+ }
+
+ QUnit.urlParams = urlParams;
+
+ // String search anywhere in moduleName+testName
+ config.filter = urlParams.filter;
+
+ // Exact match of the module name
+ config.module = urlParams.module;
+
+ config.testNumber = [];
+ if ( urlParams.testNumber ) {
+
+ // Ensure that urlParams.testNumber is an array
+ urlParams.testNumber = [].concat( urlParams.testNumber );
+ for ( i = 0; i < urlParams.testNumber.length; i++ ) {
+ current = urlParams.testNumber[ i ];
+ config.testNumber.push( parseInt( current, 10 ) );
+ }
+ }
+
+ // Figure out if we're running the tests from a server or not
+ QUnit.isLocal = location.protocol === "file:";
+}());
+
+extend( QUnit, {
+
+ config: config,
+
+ // Initialize the configuration options
+ init: function() {
+ extend( config, {
+ stats: { all: 0, bad: 0 },
+ moduleStats: { all: 0, bad: 0 },
+ started: +new Date(),
+ updateRate: 1000,
+ blocking: false,
+ autostart: true,
+ autorun: false,
+ filter: "",
+ queue: [],
+ semaphore: 1
+ });
+
+ var tests, banner, result,
+ qunit = id( "qunit" );
+
+ if ( qunit ) {
+ qunit.innerHTML =
+ "<h1 id='qunit-header'>" + escapeText( document.title ) + "</h1>" +
+ "<h2 id='qunit-banner'></h2>" +
+ "<div id='qunit-testrunner-toolbar'></div>" +
+ "<h2 id='qunit-userAgent'></h2>" +
+ "<ol id='qunit-tests'></ol>";
+ }
+
+ tests = id( "qunit-tests" );
+ banner = id( "qunit-banner" );
+ result = id( "qunit-testresult" );
+
+ if ( tests ) {
+ tests.innerHTML = "";
+ }
+
+ if ( banner ) {
+ banner.className = "";
+ }
+
+ if ( result ) {
+ result.parentNode.removeChild( result );
+ }
+
+ if ( tests ) {
+ result = document.createElement( "p" );
+ result.id = "qunit-testresult";
+ result.className = "result";
+ tests.parentNode.insertBefore( result, tests );
+ result.innerHTML = "Running...<br/>&nbsp;";
+ }
+ },
+
+ // Resets the test setup. Useful for tests that modify the DOM.
+ /*
+ DEPRECATED: Use multiple tests instead of resetting inside a test.
+ Use testStart or testDone for custom cleanup.
+ This method will throw an error in 2.0, and will be removed in 2.1
+ */
+ reset: function() {
+ var fixture = id( "qunit-fixture" );
+ if ( fixture ) {
+ fixture.innerHTML = config.fixture;
+ }
+ },
+
+ // Safe object type checking
+ is: function( type, obj ) {
+ return QUnit.objectType( obj ) === type;
+ },
+
+ objectType: function( obj ) {
+ if ( typeof obj === "undefined" ) {
+ return "undefined";
+ }
+
+ // Consider: typeof null === object
+ if ( obj === null ) {
+ return "null";
+ }
+
+ var match = toString.call( obj ).match(/^\[object\s(.*)\]$/),
+ type = match && match[1] || "";
+
+ switch ( type ) {
+ case "Number":
+ if ( isNaN(obj) ) {
+ return "nan";
+ }
+ return "number";
+ case "String":
+ case "Boolean":
+ case "Array":
+ case "Date":
+ case "RegExp":
+ case "Function":
+ return type.toLowerCase();
+ }
+ if ( typeof obj === "object" ) {
+ return "object";
+ }
+ return undefined;
+ },
+
+ push: function( result, actual, expected, message ) {
+ if ( !config.current ) {
+ throw new Error( "assertion outside test context, was " + sourceFromStacktrace() );
+ }
+
+ var output, source,
+ details = {
+ module: config.current.module,
+ name: config.current.testName,
+ result: result,
+ message: message,
+ actual: actual,
+ expected: expected
+ };
+
+ message = escapeText( message ) || ( result ? "okay" : "failed" );
+ message = "<span class='test-message'>" + message + "</span>";
+ output = message;
+
+ if ( !result ) {
+ expected = escapeText( QUnit.jsDump.parse(expected) );
+ actual = escapeText( QUnit.jsDump.parse(actual) );
+ output += "<table><tr class='test-expected'><th>Expected: </th><td><pre>" + expected + "</pre></td></tr>";
+
+ if ( actual !== expected ) {
+ output += "<tr class='test-actual'><th>Result: </th><td><pre>" + actual + "</pre></td></tr>";
+ output += "<tr class='test-diff'><th>Diff: </th><td><pre>" + QUnit.diff( expected, actual ) + "</pre></td></tr>";
+ }
+
+ source = sourceFromStacktrace();
+
+ if ( source ) {
+ details.source = source;
+ output += "<tr class='test-source'><th>Source: </th><td><pre>" + escapeText( source ) + "</pre></td></tr>";
+ }
+
+ output += "</table>";
+ }
+
+ runLoggingCallbacks( "log", QUnit, details );
+
+ config.current.assertions.push({
+ result: !!result,
+ message: output
+ });
+ },
+
+ pushFailure: function( message, source, actual ) {
+ if ( !config.current ) {
+ throw new Error( "pushFailure() assertion outside test context, was " + sourceFromStacktrace(2) );
+ }
+
+ var output,
+ details = {
+ module: config.current.module,
+ name: config.current.testName,
+ result: false,
+ message: message
+ };
+
+ message = escapeText( message ) || "error";
+ message = "<span class='test-message'>" + message + "</span>";
+ output = message;
+
+ output += "<table>";
+
+ if ( actual ) {
+ output += "<tr class='test-actual'><th>Result: </th><td><pre>" + escapeText( actual ) + "</pre></td></tr>";
+ }
+
+ if ( source ) {
+ details.source = source;
+ output += "<tr class='test-source'><th>Source: </th><td><pre>" + escapeText( source ) + "</pre></td></tr>";
+ }
+
+ output += "</table>";
+
+ runLoggingCallbacks( "log", QUnit, details );
+
+ config.current.assertions.push({
+ result: false,
+ message: output
+ });
+ },
+
+ url: function( params ) {
+ params = extend( extend( {}, QUnit.urlParams ), params );
+ var key,
+ querystring = "?";
+
+ for ( key in params ) {
+ if ( hasOwn.call( params, key ) ) {
+ querystring += encodeURIComponent( key ) + "=" +
+ encodeURIComponent( params[ key ] ) + "&";
+ }
+ }
+ return window.location.protocol + "//" + window.location.host +
+ window.location.pathname + querystring.slice( 0, -1 );
+ },
+
+ extend: extend,
+ id: id,
+ addEvent: addEvent,
+ addClass: addClass,
+ hasClass: hasClass,
+ removeClass: removeClass
+ // load, equiv, jsDump, diff: Attached later
+});
+
+/**
+ * @deprecated: Created for backwards compatibility with test runner that set the hook function
+ * into QUnit.{hook}, instead of invoking it and passing the hook function.
+ * QUnit.constructor is set to the empty F() above so that we can add to it's prototype here.
+ * Doing this allows us to tell if the following methods have been overwritten on the actual
+ * QUnit object.
+ */
+extend( QUnit.constructor.prototype, {
+
+ // Logging callbacks; all receive a single argument with the listed properties
+ // run test/logs.html for any related changes
+ begin: registerLoggingCallback( "begin" ),
+
+ // done: { failed, passed, total, runtime }
+ done: registerLoggingCallback( "done" ),
+
+ // log: { result, actual, expected, message }
+ log: registerLoggingCallback( "log" ),
+
+ // testStart: { name }
+ testStart: registerLoggingCallback( "testStart" ),
+
+ // testDone: { name, failed, passed, total, runtime }
+ testDone: registerLoggingCallback( "testDone" ),
+
+ // moduleStart: { name }
+ moduleStart: registerLoggingCallback( "moduleStart" ),
+
+ // moduleDone: { name, failed, passed, total }
+ moduleDone: registerLoggingCallback( "moduleDone" )
+});
+
+if ( !defined.document || document.readyState === "complete" ) {
+ config.autorun = true;
+}
+
+QUnit.load = function() {
+ runLoggingCallbacks( "begin", QUnit, {} );
+
+ // Initialize the config, saving the execution queue
+ var banner, filter, i, j, label, len, main, ol, toolbar, val, selection,
+ urlConfigContainer, moduleFilter, userAgent,
+ numModules = 0,
+ moduleNames = [],
+ moduleFilterHtml = "",
+ urlConfigHtml = "",
+ oldconfig = extend( {}, config );
+
+ QUnit.init();
+ extend(config, oldconfig);
+
+ config.blocking = false;
+
+ len = config.urlConfig.length;
+
+ for ( i = 0; i < len; i++ ) {
+ val = config.urlConfig[i];
+ if ( typeof val === "string" ) {
+ val = {
+ id: val,
+ label: val
+ };
+ }
+ config[ val.id ] = QUnit.urlParams[ val.id ];
+ if ( !val.value || typeof val.value === "string" ) {
+ urlConfigHtml += "<input id='qunit-urlconfig-" + escapeText( val.id ) +
+ "' name='" + escapeText( val.id ) +
+ "' type='checkbox'" +
+ ( val.value ? " value='" + escapeText( val.value ) + "'" : "" ) +
+ ( config[ val.id ] ? " checked='checked'" : "" ) +
+ " title='" + escapeText( val.tooltip ) +
+ "'><label for='qunit-urlconfig-" + escapeText( val.id ) +
+ "' title='" + escapeText( val.tooltip ) + "'>" + val.label + "</label>";
+ } else {
+ urlConfigHtml += "<label for='qunit-urlconfig-" + escapeText( val.id ) +
+ "' title='" + escapeText( val.tooltip ) +
+ "'>" + val.label +
+ ": </label><select id='qunit-urlconfig-" + escapeText( val.id ) +
+ "' name='" + escapeText( val.id ) +
+ "' title='" + escapeText( val.tooltip ) +
+ "'><option></option>";
+ selection = false;
+ if ( QUnit.is( "array", val.value ) ) {
+ for ( j = 0; j < val.value.length; j++ ) {
+ urlConfigHtml += "<option value='" + escapeText( val.value[j] ) + "'" +
+ ( config[ val.id ] === val.value[j] ?
+ (selection = true) && " selected='selected'" :
+ "" ) +
+ ">" + escapeText( val.value[j] ) + "</option>";
+ }
+ } else {
+ for ( j in val.value ) {
+ if ( hasOwn.call( val.value, j ) ) {
+ urlConfigHtml += "<option value='" + escapeText( j ) + "'" +
+ ( config[ val.id ] === j ?
+ (selection = true) && " selected='selected'" :
+ "" ) +
+ ">" + escapeText( val.value[j] ) + "</option>";
+ }
+ }
+ }
+ if ( config[ val.id ] && !selection ) {
+ urlConfigHtml += "<option value='" + escapeText( config[ val.id ] ) +
+ "' selected='selected' disabled='disabled'>" +
+ escapeText( config[ val.id ] ) +
+ "</option>";
+ }
+ urlConfigHtml += "</select>";
+ }
+ }
+ for ( i in config.modules ) {
+ if ( config.modules.hasOwnProperty( i ) ) {
+ moduleNames.push(i);
+ }
+ }
+ numModules = moduleNames.length;
+ moduleNames.sort( function( a, b ) {
+ return a.localeCompare( b );
+ });
+ moduleFilterHtml += "<label for='qunit-modulefilter'>Module: </label><select id='qunit-modulefilter' name='modulefilter'><option value='' " +
+ ( config.module === undefined ? "selected='selected'" : "" ) +
+ ">< All Modules ></option>";
+
+
+ for ( i = 0; i < numModules; i++) {
+ moduleFilterHtml += "<option value='" + escapeText( encodeURIComponent(moduleNames[i]) ) + "' " +
+ ( config.module === moduleNames[i] ? "selected='selected'" : "" ) +
+ ">" + escapeText(moduleNames[i]) + "</option>";
+ }
+ moduleFilterHtml += "</select>";
+
+ // `userAgent` initialized at top of scope
+ userAgent = id( "qunit-userAgent" );
+ if ( userAgent ) {
+ userAgent.innerHTML = navigator.userAgent;
+ }
+
+ // `banner` initialized at top of scope
+ banner = id( "qunit-header" );
+ if ( banner ) {
+ banner.innerHTML = "<a href='" + QUnit.url({ filter: undefined, module: undefined, testNumber: undefined }) + "'>" + banner.innerHTML + "</a> ";
+ }
+
+ // `toolbar` initialized at top of scope
+ toolbar = id( "qunit-testrunner-toolbar" );
+ if ( toolbar ) {
+ // `filter` initialized at top of scope
+ filter = document.createElement( "input" );
+ filter.type = "checkbox";
+ filter.id = "qunit-filter-pass";
+
+ addEvent( filter, "click", function() {
+ var tmp,
+ ol = id( "qunit-tests" );
+
+ if ( filter.checked ) {
+ ol.className = ol.className + " hidepass";
+ } else {
+ tmp = " " + ol.className.replace( /[\n\t\r]/g, " " ) + " ";
+ ol.className = tmp.replace( / hidepass /, " " );
+ }
+ if ( defined.sessionStorage ) {
+ if (filter.checked) {
+ sessionStorage.setItem( "qunit-filter-passed-tests", "true" );
+ } else {
+ sessionStorage.removeItem( "qunit-filter-passed-tests" );
+ }
+ }
+ });
+
+ if ( config.hidepassed || defined.sessionStorage && sessionStorage.getItem( "qunit-filter-passed-tests" ) ) {
+ filter.checked = true;
+ // `ol` initialized at top of scope
+ ol = id( "qunit-tests" );
+ ol.className = ol.className + " hidepass";
+ }
+ toolbar.appendChild( filter );
+
+ // `label` initialized at top of scope
+ label = document.createElement( "label" );
+ label.setAttribute( "for", "qunit-filter-pass" );
+ label.setAttribute( "title", "Only show tests and assertions that fail. Stored in sessionStorage." );
+ label.innerHTML = "Hide passed tests";
+ toolbar.appendChild( label );
+
+ urlConfigContainer = document.createElement("span");
+ urlConfigContainer.innerHTML = urlConfigHtml;
+ // For oldIE support:
+ // * Add handlers to the individual elements instead of the container
+ // * Use "click" instead of "change" for checkboxes
+ // * Fallback from event.target to event.srcElement
+ addEvents( urlConfigContainer.getElementsByTagName("input"), "click", function( event ) {
+ var params = {},
+ target = event.target || event.srcElement;
+ params[ target.name ] = target.checked ?
+ target.defaultValue || true :
+ undefined;
+ window.location = QUnit.url( params );
+ });
+ addEvents( urlConfigContainer.getElementsByTagName("select"), "change", function( event ) {
+ var params = {},
+ target = event.target || event.srcElement;
+ params[ target.name ] = target.options[ target.selectedIndex ].value || undefined;
+ window.location = QUnit.url( params );
+ });
+ toolbar.appendChild( urlConfigContainer );
+
+ if (numModules > 1) {
+ moduleFilter = document.createElement( "span" );
+ moduleFilter.setAttribute( "id", "qunit-modulefilter-container" );
+ moduleFilter.innerHTML = moduleFilterHtml;
+ addEvent( moduleFilter.lastChild, "change", function() {
+ var selectBox = moduleFilter.getElementsByTagName("select")[0],
+ selectedModule = decodeURIComponent(selectBox.options[selectBox.selectedIndex].value);
+
+ window.location = QUnit.url({
+ module: ( selectedModule === "" ) ? undefined : selectedModule,
+ // Remove any existing filters
+ filter: undefined,
+ testNumber: undefined
+ });
+ });
+ toolbar.appendChild(moduleFilter);
+ }
+ }
+
+ // `main` initialized at top of scope
+ main = id( "qunit-fixture" );
+ if ( main ) {
+ config.fixture = main.innerHTML;
+ }
+
+ if ( config.autostart ) {
+ QUnit.start();
+ }
+};
+
+if ( defined.document ) {
+ addEvent( window, "load", QUnit.load );
+}
+
+// `onErrorFnPrev` initialized at top of scope
+// Preserve other handlers
+onErrorFnPrev = window.onerror;
+
+// Cover uncaught exceptions
+// Returning true will suppress the default browser handler,
+// returning false will let it run.
+window.onerror = function ( error, filePath, linerNr ) {
+ var ret = false;
+ if ( onErrorFnPrev ) {
+ ret = onErrorFnPrev( error, filePath, linerNr );
+ }
+
+ // Treat return value as window.onerror itself does,
+ // Only do our handling if not suppressed.
+ if ( ret !== true ) {
+ if ( QUnit.config.current ) {
+ if ( QUnit.config.current.ignoreGlobalErrors ) {
+ return true;
+ }
+ QUnit.pushFailure( error, filePath + ":" + linerNr );
+ } else {
+ QUnit.test( "global failure", extend( function() {
+ QUnit.pushFailure( error, filePath + ":" + linerNr );
+ }, { validTest: validTest } ) );
+ }
+ return false;
+ }
+
+ return ret;
+};
+
+function done() {
+ config.autorun = true;
+
+ // Log the last module results
+ if ( config.previousModule ) {
+ runLoggingCallbacks( "moduleDone", QUnit, {
+ name: config.previousModule,
+ failed: config.moduleStats.bad,
+ passed: config.moduleStats.all - config.moduleStats.bad,
+ total: config.moduleStats.all
+ });
+ }
+ delete config.previousModule;
+
+ var i, key,
+ banner = id( "qunit-banner" ),
+ tests = id( "qunit-tests" ),
+ runtime = +new Date() - config.started,
+ passed = config.stats.all - config.stats.bad,
+ html = [
+ "Tests completed in ",
+ runtime,
+ " milliseconds.<br/>",
+ "<span class='passed'>",
+ passed,
+ "</span> assertions of <span class='total'>",
+ config.stats.all,
+ "</span> passed, <span class='failed'>",
+ config.stats.bad,
+ "</span> failed."
+ ].join( "" );
+
+ if ( banner ) {
+ banner.className = ( config.stats.bad ? "qunit-fail" : "qunit-pass" );
+ }
+
+ if ( tests ) {
+ id( "qunit-testresult" ).innerHTML = html;
+ }
+
+ if ( config.altertitle && defined.document && document.title ) {
+ // show ✖ for good, ✔ for bad suite result in title
+ // use escape sequences in case file gets loaded with non-utf-8-charset
+ document.title = [
+ ( config.stats.bad ? "\u2716" : "\u2714" ),
+ document.title.replace( /^[\u2714\u2716] /i, "" )
+ ].join( " " );
+ }
+
+ // clear own sessionStorage items if all tests passed
+ if ( config.reorder && defined.sessionStorage && config.stats.bad === 0 ) {
+ // `key` & `i` initialized at top of scope
+ for ( i = 0; i < sessionStorage.length; i++ ) {
+ key = sessionStorage.key( i++ );
+ if ( key.indexOf( "qunit-test-" ) === 0 ) {
+ sessionStorage.removeItem( key );
+ }
+ }
+ }
+
+ // scroll back to top to show results
+ if ( config.scrolltop && window.scrollTo ) {
+ window.scrollTo(0, 0);
+ }
+
+ runLoggingCallbacks( "done", QUnit, {
+ failed: config.stats.bad,
+ passed: passed,
+ total: config.stats.all,
+ runtime: runtime
+ });
+}
+
+/** @return Boolean: true if this test should be ran */
+function validTest( test ) {
+ var include,
+ filter = config.filter && config.filter.toLowerCase(),
+ module = config.module && config.module.toLowerCase(),
+ fullName = ( test.module + ": " + test.testName ).toLowerCase();
+
+ // Internally-generated tests are always valid
+ if ( test.callback && test.callback.validTest === validTest ) {
+ delete test.callback.validTest;
+ return true;
+ }
+
+ if ( config.testNumber.length > 0 ) {
+ if ( inArray( test.testNumber, config.testNumber ) < 0 ) {
+ return false;
+ }
+ }
+
+ if ( module && ( !test.module || test.module.toLowerCase() !== module ) ) {
+ return false;
+ }
+
+ if ( !filter ) {
+ return true;
+ }
+
+ include = filter.charAt( 0 ) !== "!";
+ if ( !include ) {
+ filter = filter.slice( 1 );
+ }
+
+ // If the filter matches, we need to honour include
+ if ( fullName.indexOf( filter ) !== -1 ) {
+ return include;
+ }
+
+ // Otherwise, do the opposite
+ return !include;
+}
+
+// so far supports only Firefox, Chrome and Opera (buggy), Safari (for real exceptions)
+// Later Safari and IE10 are supposed to support error.stack as well
+// See also https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error/Stack
+function extractStacktrace( e, offset ) {
+ offset = offset === undefined ? 3 : offset;
+
+ var stack, include, i;
+
+ if ( e.stacktrace ) {
+ // Opera
+ return e.stacktrace.split( "\n" )[ offset + 3 ];
+ } else if ( e.stack ) {
+ // Firefox, Chrome
+ stack = e.stack.split( "\n" );
+ if (/^error$/i.test( stack[0] ) ) {
+ stack.shift();
+ }
+ if ( fileName ) {
+ include = [];
+ for ( i = offset; i < stack.length; i++ ) {
+ if ( stack[ i ].indexOf( fileName ) !== -1 ) {
+ break;
+ }
+ include.push( stack[ i ] );
+ }
+ if ( include.length ) {
+ return include.join( "\n" );
+ }
+ }
+ return stack[ offset ];
+ } else if ( e.sourceURL ) {
+ // Safari, PhantomJS
+ // hopefully one day Safari provides actual stacktraces
+ // exclude useless self-reference for generated Error objects
+ if ( /qunit.js$/.test( e.sourceURL ) ) {
+ return;
+ }
+ // for actual exceptions, this is useful
+ return e.sourceURL + ":" + e.line;
+ }
+}
+function sourceFromStacktrace( offset ) {
+ try {
+ throw new Error();
+ } catch ( e ) {
+ return extractStacktrace( e, offset );
+ }
+}
+
+/**
+ * Escape text for attribute or text content.
+ */
+function escapeText( s ) {
+ if ( !s ) {
+ return "";
+ }
+ s = s + "";
+ // Both single quotes and double quotes (for attributes)
+ return s.replace( /['"<>&]/g, function( s ) {
+ switch( s ) {
+ case "'":
+ return "&#039;";
+ case "\"":
+ return "&quot;";
+ case "<":
+ return "&lt;";
+ case ">":
+ return "&gt;";
+ case "&":
+ return "&amp;";
+ }
+ });
+}
+
+function synchronize( callback, last ) {
+ config.queue.push( callback );
+
+ if ( config.autorun && !config.blocking ) {
+ process( last );
+ }
+}
+
+function process( last ) {
+ function next() {
+ process( last );
+ }
+ var start = new Date().getTime();
+ config.depth = config.depth ? config.depth + 1 : 1;
+
+ while ( config.queue.length && !config.blocking ) {
+ if ( !defined.setTimeout || config.updateRate <= 0 || ( ( new Date().getTime() - start ) < config.updateRate ) ) {
+ config.queue.shift()();
+ } else {
+ setTimeout( next, 13 );
+ break;
+ }
+ }
+ config.depth--;
+ if ( last && !config.blocking && !config.queue.length && config.depth === 0 ) {
+ done();
+ }
+}
+
+function saveGlobal() {
+ config.pollution = [];
+
+ if ( config.noglobals ) {
+ for ( var key in window ) {
+ if ( hasOwn.call( window, key ) ) {
+ // in Opera sometimes DOM element ids show up here, ignore them
+ if ( /^qunit-test-output/.test( key ) ) {
+ continue;
+ }
+ config.pollution.push( key );
+ }
+ }
+ }
+}
+
+function checkPollution() {
+ var newGlobals,
+ deletedGlobals,
+ old = config.pollution;
+
+ saveGlobal();
+
+ newGlobals = diff( config.pollution, old );
+ if ( newGlobals.length > 0 ) {
+ QUnit.pushFailure( "Introduced global variable(s): " + newGlobals.join(", ") );
+ }
+
+ deletedGlobals = diff( old, config.pollution );
+ if ( deletedGlobals.length > 0 ) {
+ QUnit.pushFailure( "Deleted global variable(s): " + deletedGlobals.join(", ") );
+ }
+}
+
+// returns a new Array with the elements that are in a but not in b
+function diff( a, b ) {
+ var i, j,
+ result = a.slice();
+
+ for ( i = 0; i < result.length; i++ ) {
+ for ( j = 0; j < b.length; j++ ) {
+ if ( result[i] === b[j] ) {
+ result.splice( i, 1 );
+ i--;
+ break;
+ }
+ }
+ }
+ return result;
+}
+
+function extend( a, b ) {
+ for ( var prop in b ) {
+ if ( hasOwn.call( b, prop ) ) {
+ // Avoid "Member not found" error in IE8 caused by messing with window.constructor
+ if ( !( prop === "constructor" && a === window ) ) {
+ if ( b[ prop ] === undefined ) {
+ delete a[ prop ];
+ } else {
+ a[ prop ] = b[ prop ];
+ }
+ }
+ }
+ }
+
+ return a;
+}
+
+/**
+ * @param {HTMLElement} elem
+ * @param {string} type
+ * @param {Function} fn
+ */
+function addEvent( elem, type, fn ) {
+ if ( elem.addEventListener ) {
+
+ // Standards-based browsers
+ elem.addEventListener( type, fn, false );
+ } else if ( elem.attachEvent ) {
+
+ // support: IE <9
+ elem.attachEvent( "on" + type, fn );
+ } else {
+
+ // Caller must ensure support for event listeners is present
+ throw new Error( "addEvent() was called in a context without event listener support" );
+ }
+}
+
+/**
+ * @param {Array|NodeList} elems
+ * @param {string} type
+ * @param {Function} fn
+ */
+function addEvents( elems, type, fn ) {
+ var i = elems.length;
+ while ( i-- ) {
+ addEvent( elems[i], type, fn );
+ }
+}
+
+function hasClass( elem, name ) {
+ return (" " + elem.className + " ").indexOf(" " + name + " ") > -1;
+}
+
+function addClass( elem, name ) {
+ if ( !hasClass( elem, name ) ) {
+ elem.className += (elem.className ? " " : "") + name;
+ }
+}
+
+function removeClass( elem, name ) {
+ var set = " " + elem.className + " ";
+ // Class name may appear multiple times
+ while ( set.indexOf(" " + name + " ") > -1 ) {
+ set = set.replace(" " + name + " " , " ");
+ }
+ // If possible, trim it for prettiness, but not necessarily
+ elem.className = typeof set.trim === "function" ? set.trim() : set.replace(/^\s+|\s+$/g, "");
+}
+
+function id( name ) {
+ return defined.document && document.getElementById && document.getElementById( name );
+}
+
+function registerLoggingCallback( key ) {
+ return function( callback ) {
+ config[key].push( callback );
+ };
+}
+
+// Supports deprecated method of completely overwriting logging callbacks
+function runLoggingCallbacks( key, scope, args ) {
+ var i, callbacks;
+ if ( QUnit.hasOwnProperty( key ) ) {
+ QUnit[ key ].call(scope, args );
+ } else {
+ callbacks = config[ key ];
+ for ( i = 0; i < callbacks.length; i++ ) {
+ callbacks[ i ].call( scope, args );
+ }
+ }
+}
+
+// from jquery.js
+function inArray( elem, array ) {
+ if ( array.indexOf ) {
+ return array.indexOf( elem );
+ }
+
+ for ( var i = 0, length = array.length; i < length; i++ ) {
+ if ( array[ i ] === elem ) {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+function Test( settings ) {
+ extend( this, settings );
+ this.assertions = [];
+ this.testNumber = ++Test.count;
+}
+
+Test.count = 0;
+
+Test.prototype = {
+ init: function() {
+ var a, b, li,
+ tests = id( "qunit-tests" );
+
+ if ( tests ) {
+ b = document.createElement( "strong" );
+ b.innerHTML = this.nameHtml;
+
+ // `a` initialized at top of scope
+ a = document.createElement( "a" );
+ a.innerHTML = "Rerun";
+ a.href = QUnit.url({ testNumber: this.testNumber });
+
+ li = document.createElement( "li" );
+ li.appendChild( b );
+ li.appendChild( a );
+ li.className = "running";
+ li.id = this.id = "qunit-test-output" + testId++;
+
+ tests.appendChild( li );
+ }
+ },
+ setup: function() {
+ if (
+ // Emit moduleStart when we're switching from one module to another
+ this.module !== config.previousModule ||
+ // They could be equal (both undefined) but if the previousModule property doesn't
+ // yet exist it means this is the first test in a suite that isn't wrapped in a
+ // module, in which case we'll just emit a moduleStart event for 'undefined'.
+ // Without this, reporters can get testStart before moduleStart which is a problem.
+ !hasOwn.call( config, "previousModule" )
+ ) {
+ if ( hasOwn.call( config, "previousModule" ) ) {
+ runLoggingCallbacks( "moduleDone", QUnit, {
+ name: config.previousModule,
+ failed: config.moduleStats.bad,
+ passed: config.moduleStats.all - config.moduleStats.bad,
+ total: config.moduleStats.all
+ });
+ }
+ config.previousModule = this.module;
+ config.moduleStats = { all: 0, bad: 0 };
+ runLoggingCallbacks( "moduleStart", QUnit, {
+ name: this.module
+ });
+ }
+
+ config.current = this;
+
+ this.testEnvironment = extend({
+ setup: function() {},
+ teardown: function() {}
+ }, this.moduleTestEnvironment );
+
+ this.started = +new Date();
+ runLoggingCallbacks( "testStart", QUnit, {
+ name: this.testName,
+ module: this.module
+ });
+
+ /*jshint camelcase:false */
+
+
+ /**
+ * Expose the current test environment.
+ *
+ * @deprecated since 1.12.0: Use QUnit.config.current.testEnvironment instead.
+ */
+ QUnit.current_testEnvironment = this.testEnvironment;
+
+ /*jshint camelcase:true */
+
+ if ( !config.pollution ) {
+ saveGlobal();
+ }
+ if ( config.notrycatch ) {
+ this.testEnvironment.setup.call( this.testEnvironment, QUnit.assert );
+ return;
+ }
+ try {
+ this.testEnvironment.setup.call( this.testEnvironment, QUnit.assert );
+ } catch( e ) {
+ QUnit.pushFailure( "Setup failed on " + this.testName + ": " + ( e.message || e ), extractStacktrace( e, 1 ) );
+ }
+ },
+ run: function() {
+ config.current = this;
+
+ var running = id( "qunit-testresult" );
+
+ if ( running ) {
+ running.innerHTML = "Running: <br/>" + this.nameHtml;
+ }
+
+ if ( this.async ) {
+ QUnit.stop();
+ }
+
+ this.callbackStarted = +new Date();
+
+ if ( config.notrycatch ) {
+ this.callback.call( this.testEnvironment, QUnit.assert );
+ this.callbackRuntime = +new Date() - this.callbackStarted;
+ return;
+ }
+
+ try {
+ this.callback.call( this.testEnvironment, QUnit.assert );
+ this.callbackRuntime = +new Date() - this.callbackStarted;
+ } catch( e ) {
+ this.callbackRuntime = +new Date() - this.callbackStarted;
+
+ QUnit.pushFailure( "Died on test #" + (this.assertions.length + 1) + " " + this.stack + ": " + ( e.message || e ), extractStacktrace( e, 0 ) );
+ // else next test will carry the responsibility
+ saveGlobal();
+
+ // Restart the tests if they're blocking
+ if ( config.blocking ) {
+ QUnit.start();
+ }
+ }
+ },
+ teardown: function() {
+ config.current = this;
+ if ( config.notrycatch ) {
+ if ( typeof this.callbackRuntime === "undefined" ) {
+ this.callbackRuntime = +new Date() - this.callbackStarted;
+ }
+ this.testEnvironment.teardown.call( this.testEnvironment, QUnit.assert );
+ return;
+ } else {
+ try {
+ this.testEnvironment.teardown.call( this.testEnvironment, QUnit.assert );
+ } catch( e ) {
+ QUnit.pushFailure( "Teardown failed on " + this.testName + ": " + ( e.message || e ), extractStacktrace( e, 1 ) );
+ }
+ }
+ checkPollution();
+ },
+ finish: function() {
+ config.current = this;
+ if ( config.requireExpects && this.expected === null ) {
+ QUnit.pushFailure( "Expected number of assertions to be defined, but expect() was not called.", this.stack );
+ } else if ( this.expected !== null && this.expected !== this.assertions.length ) {
+ QUnit.pushFailure( "Expected " + this.expected + " assertions, but " + this.assertions.length + " were run", this.stack );
+ } else if ( this.expected === null && !this.assertions.length ) {
+ QUnit.pushFailure( "Expected at least one assertion, but none were run - call expect(0) to accept zero assertions.", this.stack );
+ }
+
+ var i, assertion, a, b, time, li, ol,
+ test = this,
+ good = 0,
+ bad = 0,
+ tests = id( "qunit-tests" );
+
+ this.runtime = +new Date() - this.started;
+ config.stats.all += this.assertions.length;
+ config.moduleStats.all += this.assertions.length;
+
+ if ( tests ) {
+ ol = document.createElement( "ol" );
+ ol.className = "qunit-assert-list";
+
+ for ( i = 0; i < this.assertions.length; i++ ) {
+ assertion = this.assertions[i];
+
+ li = document.createElement( "li" );
+ li.className = assertion.result ? "pass" : "fail";
+ li.innerHTML = assertion.message || ( assertion.result ? "okay" : "failed" );
+ ol.appendChild( li );
+
+ if ( assertion.result ) {
+ good++;
+ } else {
+ bad++;
+ config.stats.bad++;
+ config.moduleStats.bad++;
+ }
+ }
+
+ // store result when possible
+ if ( QUnit.config.reorder && defined.sessionStorage ) {
+ if ( bad ) {
+ sessionStorage.setItem( "qunit-test-" + this.module + "-" + this.testName, bad );
+ } else {
+ sessionStorage.removeItem( "qunit-test-" + this.module + "-" + this.testName );
+ }
+ }
+
+ if ( bad === 0 ) {
+ addClass( ol, "qunit-collapsed" );
+ }
+
+ // `b` initialized at top of scope
+ b = document.createElement( "strong" );
+ b.innerHTML = this.nameHtml + " <b class='counts'>(<b class='failed'>" + bad + "</b>, <b class='passed'>" + good + "</b>, " + this.assertions.length + ")</b>";
+
+ addEvent(b, "click", function() {
+ var next = b.parentNode.lastChild,
+ collapsed = hasClass( next, "qunit-collapsed" );
+ ( collapsed ? removeClass : addClass )( next, "qunit-collapsed" );
+ });
+
+ addEvent(b, "dblclick", function( e ) {
+ var target = e && e.target ? e.target : window.event.srcElement;
+ if ( target.nodeName.toLowerCase() === "span" || target.nodeName.toLowerCase() === "b" ) {
+ target = target.parentNode;
+ }
+ if ( window.location && target.nodeName.toLowerCase() === "strong" ) {
+ window.location = QUnit.url({ testNumber: test.testNumber });
+ }
+ });
+
+ // `time` initialized at top of scope
+ time = document.createElement( "span" );
+ time.className = "runtime";
+ time.innerHTML = this.runtime + " ms";
+
+ // `li` initialized at top of scope
+ li = id( this.id );
+ li.className = bad ? "fail" : "pass";
+ li.removeChild( li.firstChild );
+ a = li.firstChild;
+ li.appendChild( b );
+ li.appendChild( a );
+ li.appendChild( time );
+ li.appendChild( ol );
+
+ } else {
+ for ( i = 0; i < this.assertions.length; i++ ) {
+ if ( !this.assertions[i].result ) {
+ bad++;
+ config.stats.bad++;
+ config.moduleStats.bad++;
+ }
+ }
+ }
+
+ runLoggingCallbacks( "testDone", QUnit, {
+ name: this.testName,
+ module: this.module,
+ failed: bad,
+ passed: this.assertions.length - bad,
+ total: this.assertions.length,
+ runtime: this.runtime,
+ // DEPRECATED: this property will be removed in 2.0.0, use runtime instead
+ duration: this.runtime
+ });
+
+ QUnit.reset();
+
+ config.current = undefined;
+ },
+
+ queue: function() {
+ var bad,
+ test = this;
+
+ synchronize(function() {
+ test.init();
+ });
+ function run() {
+ // each of these can by async
+ synchronize(function() {
+ test.setup();
+ });
+ synchronize(function() {
+ test.run();
+ });
+ synchronize(function() {
+ test.teardown();
+ });
+ synchronize(function() {
+ test.finish();
+ });
+ }
+
+ // `bad` initialized at top of scope
+ // defer when previous test run passed, if storage is available
+ bad = QUnit.config.reorder && defined.sessionStorage &&
+ +sessionStorage.getItem( "qunit-test-" + this.module + "-" + this.testName );
+
+ if ( bad ) {
+ run();
+ } else {
+ synchronize( run, true );
+ }
+ }
+};
+
+// `assert` initialized at top of scope
+// Assert helpers
+// All of these must either call QUnit.push() or manually do:
+// - runLoggingCallbacks( "log", .. );
+// - config.current.assertions.push({ .. });
+assert = QUnit.assert = {
+ /**
+ * Asserts rough true-ish result.
+ * @name ok
+ * @function
+ * @example ok( "asdfasdf".length > 5, "There must be at least 5 chars" );
+ */
+ ok: function( result, msg ) {
+ if ( !config.current ) {
+ throw new Error( "ok() assertion outside test context, was " + sourceFromStacktrace(2) );
+ }
+ result = !!result;
+ msg = msg || ( result ? "okay" : "failed" );
+
+ var source,
+ details = {
+ module: config.current.module,
+ name: config.current.testName,
+ result: result,
+ message: msg
+ };
+
+ msg = "<span class='test-message'>" + escapeText( msg ) + "</span>";
+
+ if ( !result ) {
+ source = sourceFromStacktrace( 2 );
+ if ( source ) {
+ details.source = source;
+ msg += "<table><tr class='test-source'><th>Source: </th><td><pre>" +
+ escapeText( source ) +
+ "</pre></td></tr></table>";
+ }
+ }
+ runLoggingCallbacks( "log", QUnit, details );
+ config.current.assertions.push({
+ result: result,
+ message: msg
+ });
+ },
+
+ /**
+ * Assert that the first two arguments are equal, with an optional message.
+ * Prints out both actual and expected values.
+ * @name equal
+ * @function
+ * @example equal( format( "Received {0} bytes.", 2), "Received 2 bytes.", "format() replaces {0} with next argument" );
+ */
+ equal: function( actual, expected, message ) {
+ /*jshint eqeqeq:false */
+ QUnit.push( expected == actual, actual, expected, message );
+ },
+
+ /**
+ * @name notEqual
+ * @function
+ */
+ notEqual: function( actual, expected, message ) {
+ /*jshint eqeqeq:false */
+ QUnit.push( expected != actual, actual, expected, message );
+ },
+
+ /**
+ * @name propEqual
+ * @function
+ */
+ propEqual: function( actual, expected, message ) {
+ actual = objectValues(actual);
+ expected = objectValues(expected);
+ QUnit.push( QUnit.equiv(actual, expected), actual, expected, message );
+ },
+
+ /**
+ * @name notPropEqual
+ * @function
+ */
+ notPropEqual: function( actual, expected, message ) {
+ actual = objectValues(actual);
+ expected = objectValues(expected);
+ QUnit.push( !QUnit.equiv(actual, expected), actual, expected, message );
+ },
+
+ /**
+ * @name deepEqual
+ * @function
+ */
+ deepEqual: function( actual, expected, message ) {
+ QUnit.push( QUnit.equiv(actual, expected), actual, expected, message );
+ },
+
+ /**
+ * @name notDeepEqual
+ * @function
+ */
+ notDeepEqual: function( actual, expected, message ) {
+ QUnit.push( !QUnit.equiv(actual, expected), actual, expected, message );
+ },
+
+ /**
+ * @name strictEqual
+ * @function
+ */
+ strictEqual: function( actual, expected, message ) {
+ QUnit.push( expected === actual, actual, expected, message );
+ },
+
+ /**
+ * @name notStrictEqual
+ * @function
+ */
+ notStrictEqual: function( actual, expected, message ) {
+ QUnit.push( expected !== actual, actual, expected, message );
+ },
+
+ "throws": function( block, expected, message ) {
+ var actual,
+ expectedOutput = expected,
+ ok = false;
+
+ // 'expected' is optional
+ if ( !message && typeof expected === "string" ) {
+ message = expected;
+ expected = null;
+ }
+
+ config.current.ignoreGlobalErrors = true;
+ try {
+ block.call( config.current.testEnvironment );
+ } catch (e) {
+ actual = e;
+ }
+ config.current.ignoreGlobalErrors = false;
+
+ if ( actual ) {
+
+ // we don't want to validate thrown error
+ if ( !expected ) {
+ ok = true;
+ expectedOutput = null;
+
+ // expected is an Error object
+ } else if ( expected instanceof Error ) {
+ ok = actual instanceof Error &&
+ actual.name === expected.name &&
+ actual.message === expected.message;
+
+ // expected is a regexp
+ } else if ( QUnit.objectType( expected ) === "regexp" ) {
+ ok = expected.test( errorString( actual ) );
+
+ // expected is a string
+ } else if ( QUnit.objectType( expected ) === "string" ) {
+ ok = expected === errorString( actual );
+
+ // expected is a constructor
+ } else if ( actual instanceof expected ) {
+ ok = true;
+
+ // expected is a validation function which returns true is validation passed
+ } else if ( expected.call( {}, actual ) === true ) {
+ expectedOutput = null;
+ ok = true;
+ }
+
+ QUnit.push( ok, actual, expectedOutput, message );
+ } else {
+ QUnit.pushFailure( message, null, "No exception was thrown." );
+ }
+ }
+};
+
+/**
+ * @deprecated since 1.8.0
+ * Kept assertion helpers in root for backwards compatibility.
+ */
+extend( QUnit.constructor.prototype, assert );
+
+/**
+ * @deprecated since 1.9.0
+ * Kept to avoid TypeErrors for undefined methods.
+ */
+QUnit.constructor.prototype.raises = function() {
+ QUnit.push( false, false, false, "QUnit.raises has been deprecated since 2012 (fad3c1ea), use QUnit.throws instead" );
+};
+
+/**
+ * @deprecated since 1.0.0, replaced with error pushes since 1.3.0
+ * Kept to avoid TypeErrors for undefined methods.
+ */
+QUnit.constructor.prototype.equals = function() {
+ QUnit.push( false, false, false, "QUnit.equals has been deprecated since 2009 (e88049a0), use QUnit.equal instead" );
+};
+QUnit.constructor.prototype.same = function() {
+ QUnit.push( false, false, false, "QUnit.same has been deprecated since 2009 (e88049a0), use QUnit.deepEqual instead" );
+};
+
+// Test for equality any JavaScript type.
+// Author: Philippe Rathé <prathe@gmail.com>
+QUnit.equiv = (function() {
+
+ // Call the o related callback with the given arguments.
+ function bindCallbacks( o, callbacks, args ) {
+ var prop = QUnit.objectType( o );
+ if ( prop ) {
+ if ( QUnit.objectType( callbacks[ prop ] ) === "function" ) {
+ return callbacks[ prop ].apply( callbacks, args );
+ } else {
+ return callbacks[ prop ]; // or undefined
+ }
+ }
+ }
+
+ // the real equiv function
+ var innerEquiv,
+ // stack to decide between skip/abort functions
+ callers = [],
+ // stack to avoiding loops from circular referencing
+ parents = [],
+ parentsB = [],
+
+ getProto = Object.getPrototypeOf || function ( obj ) {
+ /*jshint camelcase:false */
+ return obj.__proto__;
+ },
+ callbacks = (function () {
+
+ // for string, boolean, number and null
+ function useStrictEquality( b, a ) {
+ /*jshint eqeqeq:false */
+ if ( b instanceof a.constructor || a instanceof b.constructor ) {
+ // to catch short annotation VS 'new' annotation of a
+ // declaration
+ // e.g. var i = 1;
+ // var j = new Number(1);
+ return a == b;
+ } else {
+ return a === b;
+ }
+ }
+
+ return {
+ "string": useStrictEquality,
+ "boolean": useStrictEquality,
+ "number": useStrictEquality,
+ "null": useStrictEquality,
+ "undefined": useStrictEquality,
+
+ "nan": function( b ) {
+ return isNaN( b );
+ },
+
+ "date": function( b, a ) {
+ return QUnit.objectType( b ) === "date" && a.valueOf() === b.valueOf();
+ },
+
+ "regexp": function( b, a ) {
+ return QUnit.objectType( b ) === "regexp" &&
+ // the regex itself
+ a.source === b.source &&
+ // and its modifiers
+ a.global === b.global &&
+ // (gmi) ...
+ a.ignoreCase === b.ignoreCase &&
+ a.multiline === b.multiline &&
+ a.sticky === b.sticky;
+ },
+
+ // - skip when the property is a method of an instance (OOP)
+ // - abort otherwise,
+ // initial === would have catch identical references anyway
+ "function": function() {
+ var caller = callers[callers.length - 1];
+ return caller !== Object && typeof caller !== "undefined";
+ },
+
+ "array": function( b, a ) {
+ var i, j, len, loop, aCircular, bCircular;
+
+ // b could be an object literal here
+ if ( QUnit.objectType( b ) !== "array" ) {
+ return false;
+ }
+
+ len = a.length;
+ if ( len !== b.length ) {
+ // safe and faster
+ return false;
+ }
+
+ // track reference to avoid circular references
+ parents.push( a );
+ parentsB.push( b );
+ for ( i = 0; i < len; i++ ) {
+ loop = false;
+ for ( j = 0; j < parents.length; j++ ) {
+ aCircular = parents[j] === a[i];
+ bCircular = parentsB[j] === b[i];
+ if ( aCircular || bCircular ) {
+ if ( a[i] === b[i] || aCircular && bCircular ) {
+ loop = true;
+ } else {
+ parents.pop();
+ parentsB.pop();
+ return false;
+ }
+ }
+ }
+ if ( !loop && !innerEquiv(a[i], b[i]) ) {
+ parents.pop();
+ parentsB.pop();
+ return false;
+ }
+ }
+ parents.pop();
+ parentsB.pop();
+ return true;
+ },
+
+ "object": function( b, a ) {
+ /*jshint forin:false */
+ var i, j, loop, aCircular, bCircular,
+ // Default to true
+ eq = true,
+ aProperties = [],
+ bProperties = [];
+
+ // comparing constructors is more strict than using
+ // instanceof
+ if ( a.constructor !== b.constructor ) {
+ // Allow objects with no prototype to be equivalent to
+ // objects with Object as their constructor.
+ if ( !(( getProto(a) === null && getProto(b) === Object.prototype ) ||
+ ( getProto(b) === null && getProto(a) === Object.prototype ) ) ) {
+ return false;
+ }
+ }
+
+ // stack constructor before traversing properties
+ callers.push( a.constructor );
+
+ // track reference to avoid circular references
+ parents.push( a );
+ parentsB.push( b );
+
+ // be strict: don't ensure hasOwnProperty and go deep
+ for ( i in a ) {
+ loop = false;
+ for ( j = 0; j < parents.length; j++ ) {
+ aCircular = parents[j] === a[i];
+ bCircular = parentsB[j] === b[i];
+ if ( aCircular || bCircular ) {
+ if ( a[i] === b[i] || aCircular && bCircular ) {
+ loop = true;
+ } else {
+ eq = false;
+ break;
+ }
+ }
+ }
+ aProperties.push(i);
+ if ( !loop && !innerEquiv(a[i], b[i]) ) {
+ eq = false;
+ break;
+ }
+ }
+
+ parents.pop();
+ parentsB.pop();
+ callers.pop(); // unstack, we are done
+
+ for ( i in b ) {
+ bProperties.push( i ); // collect b's properties
+ }
+
+ // Ensures identical properties name
+ return eq && innerEquiv( aProperties.sort(), bProperties.sort() );
+ }
+ };
+ }());
+
+ innerEquiv = function() { // can take multiple arguments
+ var args = [].slice.apply( arguments );
+ if ( args.length < 2 ) {
+ return true; // end transition
+ }
+
+ return (function( a, b ) {
+ if ( a === b ) {
+ return true; // catch the most you can
+ } else if ( a === null || b === null || typeof a === "undefined" ||
+ typeof b === "undefined" ||
+ QUnit.objectType(a) !== QUnit.objectType(b) ) {
+ return false; // don't lose time with error prone cases
+ } else {
+ return bindCallbacks(a, callbacks, [ b, a ]);
+ }
+
+ // apply transition with (1..n) arguments
+ }( args[0], args[1] ) && innerEquiv.apply( this, args.splice(1, args.length - 1 )) );
+ };
+
+ return innerEquiv;
+}());
+
+/**
+ * jsDump Copyright (c) 2008 Ariel Flesler - aflesler(at)gmail(dot)com |
+ * http://flesler.blogspot.com Licensed under BSD
+ * (http://www.opensource.org/licenses/bsd-license.php) Date: 5/15/2008
+ *
+ * @projectDescription Advanced and extensible data dumping for Javascript.
+ * @version 1.0.0
+ * @author Ariel Flesler
+ * @link {http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html}
+ */
+QUnit.jsDump = (function() {
+ function quote( str ) {
+ return "\"" + str.toString().replace( /"/g, "\\\"" ) + "\"";
+ }
+ function literal( o ) {
+ return o + "";
+ }
+ function join( pre, arr, post ) {
+ var s = jsDump.separator(),
+ base = jsDump.indent(),
+ inner = jsDump.indent(1);
+ if ( arr.join ) {
+ arr = arr.join( "," + s + inner );
+ }
+ if ( !arr ) {
+ return pre + post;
+ }
+ return [ pre, inner + arr, base + post ].join(s);
+ }
+ function array( arr, stack ) {
+ var i = arr.length, ret = new Array(i);
+ this.up();
+ while ( i-- ) {
+ ret[i] = this.parse( arr[i] , undefined , stack);
+ }
+ this.down();
+ return join( "[", ret, "]" );
+ }
+
+ var reName = /^function (\w+)/,
+ jsDump = {
+ // type is used mostly internally, you can fix a (custom)type in advance
+ parse: function( obj, type, stack ) {
+ stack = stack || [ ];
+ var inStack, res,
+ parser = this.parsers[ type || this.typeOf(obj) ];
+
+ type = typeof parser;
+ inStack = inArray( obj, stack );
+
+ if ( inStack !== -1 ) {
+ return "recursion(" + (inStack - stack.length) + ")";
+ }
+ if ( type === "function" ) {
+ stack.push( obj );
+ res = parser.call( this, obj, stack );
+ stack.pop();
+ return res;
+ }
+ return ( type === "string" ) ? parser : this.parsers.error;
+ },
+ typeOf: function( obj ) {
+ var type;
+ if ( obj === null ) {
+ type = "null";
+ } else if ( typeof obj === "undefined" ) {
+ type = "undefined";
+ } else if ( QUnit.is( "regexp", obj) ) {
+ type = "regexp";
+ } else if ( QUnit.is( "date", obj) ) {
+ type = "date";
+ } else if ( QUnit.is( "function", obj) ) {
+ type = "function";
+ } else if ( typeof obj.setInterval !== undefined && typeof obj.document !== "undefined" && typeof obj.nodeType === "undefined" ) {
+ type = "window";
+ } else if ( obj.nodeType === 9 ) {
+ type = "document";
+ } else if ( obj.nodeType ) {
+ type = "node";
+ } else if (
+ // native arrays
+ toString.call( obj ) === "[object Array]" ||
+ // NodeList objects
+ ( typeof obj.length === "number" && typeof obj.item !== "undefined" && ( obj.length ? obj.item(0) === obj[0] : ( obj.item( 0 ) === null && typeof obj[0] === "undefined" ) ) )
+ ) {
+ type = "array";
+ } else if ( obj.constructor === Error.prototype.constructor ) {
+ type = "error";
+ } else {
+ type = typeof obj;
+ }
+ return type;
+ },
+ separator: function() {
+ return this.multiline ? this.HTML ? "<br />" : "\n" : this.HTML ? "&nbsp;" : " ";
+ },
+ // extra can be a number, shortcut for increasing-calling-decreasing
+ indent: function( extra ) {
+ if ( !this.multiline ) {
+ return "";
+ }
+ var chr = this.indentChar;
+ if ( this.HTML ) {
+ chr = chr.replace( /\t/g, " " ).replace( / /g, "&nbsp;" );
+ }
+ return new Array( this.depth + ( extra || 0 ) ).join(chr);
+ },
+ up: function( a ) {
+ this.depth += a || 1;
+ },
+ down: function( a ) {
+ this.depth -= a || 1;
+ },
+ setParser: function( name, parser ) {
+ this.parsers[name] = parser;
+ },
+ // The next 3 are exposed so you can use them
+ quote: quote,
+ literal: literal,
+ join: join,
+ //
+ depth: 1,
+ // This is the list of parsers, to modify them, use jsDump.setParser
+ parsers: {
+ window: "[Window]",
+ document: "[Document]",
+ error: function(error) {
+ return "Error(\"" + error.message + "\")";
+ },
+ unknown: "[Unknown]",
+ "null": "null",
+ "undefined": "undefined",
+ "function": function( fn ) {
+ var ret = "function",
+ // functions never have name in IE
+ name = "name" in fn ? fn.name : (reName.exec(fn) || [])[1];
+
+ if ( name ) {
+ ret += " " + name;
+ }
+ ret += "( ";
+
+ ret = [ ret, QUnit.jsDump.parse( fn, "functionArgs" ), "){" ].join( "" );
+ return join( ret, QUnit.jsDump.parse(fn,"functionCode" ), "}" );
+ },
+ array: array,
+ nodelist: array,
+ "arguments": array,
+ object: function( map, stack ) {
+ /*jshint forin:false */
+ var ret = [ ], keys, key, val, i;
+ QUnit.jsDump.up();
+ keys = [];
+ for ( key in map ) {
+ keys.push( key );
+ }
+ keys.sort();
+ for ( i = 0; i < keys.length; i++ ) {
+ key = keys[ i ];
+ val = map[ key ];
+ ret.push( QUnit.jsDump.parse( key, "key" ) + ": " + QUnit.jsDump.parse( val, undefined, stack ) );
+ }
+ QUnit.jsDump.down();
+ return join( "{", ret, "}" );
+ },
+ node: function( node ) {
+ var len, i, val,
+ open = QUnit.jsDump.HTML ? "&lt;" : "<",
+ close = QUnit.jsDump.HTML ? "&gt;" : ">",
+ tag = node.nodeName.toLowerCase(),
+ ret = open + tag,
+ attrs = node.attributes;
+
+ if ( attrs ) {
+ for ( i = 0, len = attrs.length; i < len; i++ ) {
+ val = attrs[i].nodeValue;
+ // IE6 includes all attributes in .attributes, even ones not explicitly set.
+ // Those have values like undefined, null, 0, false, "" or "inherit".
+ if ( val && val !== "inherit" ) {
+ ret += " " + attrs[i].nodeName + "=" + QUnit.jsDump.parse( val, "attribute" );
+ }
+ }
+ }
+ ret += close;
+
+ // Show content of TextNode or CDATASection
+ if ( node.nodeType === 3 || node.nodeType === 4 ) {
+ ret += node.nodeValue;
+ }
+
+ return ret + open + "/" + tag + close;
+ },
+ // function calls it internally, it's the arguments part of the function
+ functionArgs: function( fn ) {
+ var args,
+ l = fn.length;
+
+ if ( !l ) {
+ return "";
+ }
+
+ args = new Array(l);
+ while ( l-- ) {
+ // 97 is 'a'
+ args[l] = String.fromCharCode(97+l);
+ }
+ return " " + args.join( ", " ) + " ";
+ },
+ // object calls it internally, the key part of an item in a map
+ key: quote,
+ // function calls it internally, it's the content of the function
+ functionCode: "[code]",
+ // node calls it internally, it's an html attribute value
+ attribute: quote,
+ string: quote,
+ date: quote,
+ regexp: literal,
+ number: literal,
+ "boolean": literal
+ },
+ // if true, entities are escaped ( <, >, \t, space and \n )
+ HTML: false,
+ // indentation unit
+ indentChar: " ",
+ // if true, items in a collection, are separated by a \n, else just a space.
+ multiline: true
+ };
+
+ return jsDump;
+}());
+
+/*
+ * Javascript Diff Algorithm
+ * By John Resig (http://ejohn.org/)
+ * Modified by Chu Alan "sprite"
+ *
+ * Released under the MIT license.
+ *
+ * More Info:
+ * http://ejohn.org/projects/javascript-diff-algorithm/
+ *
+ * Usage: QUnit.diff(expected, actual)
+ *
+ * QUnit.diff( "the quick brown fox jumped over", "the quick fox jumps over" ) == "the quick <del>brown </del> fox <del>jumped </del><ins>jumps </ins> over"
+ */
+QUnit.diff = (function() {
+ /*jshint eqeqeq:false, eqnull:true */
+ function diff( o, n ) {
+ var i,
+ ns = {},
+ os = {};
+
+ for ( i = 0; i < n.length; i++ ) {
+ if ( !hasOwn.call( ns, n[i] ) ) {
+ ns[ n[i] ] = {
+ rows: [],
+ o: null
+ };
+ }
+ ns[ n[i] ].rows.push( i );
+ }
+
+ for ( i = 0; i < o.length; i++ ) {
+ if ( !hasOwn.call( os, o[i] ) ) {
+ os[ o[i] ] = {
+ rows: [],
+ n: null
+ };
+ }
+ os[ o[i] ].rows.push( i );
+ }
+
+ for ( i in ns ) {
+ if ( hasOwn.call( ns, i ) ) {
+ if ( ns[i].rows.length === 1 && hasOwn.call( os, i ) && os[i].rows.length === 1 ) {
+ n[ ns[i].rows[0] ] = {
+ text: n[ ns[i].rows[0] ],
+ row: os[i].rows[0]
+ };
+ o[ os[i].rows[0] ] = {
+ text: o[ os[i].rows[0] ],
+ row: ns[i].rows[0]
+ };
+ }
+ }
+ }
+
+ for ( i = 0; i < n.length - 1; i++ ) {
+ if ( n[i].text != null && n[ i + 1 ].text == null && n[i].row + 1 < o.length && o[ n[i].row + 1 ].text == null &&
+ n[ i + 1 ] == o[ n[i].row + 1 ] ) {
+
+ n[ i + 1 ] = {
+ text: n[ i + 1 ],
+ row: n[i].row + 1
+ };
+ o[ n[i].row + 1 ] = {
+ text: o[ n[i].row + 1 ],
+ row: i + 1
+ };
+ }
+ }
+
+ for ( i = n.length - 1; i > 0; i-- ) {
+ if ( n[i].text != null && n[ i - 1 ].text == null && n[i].row > 0 && o[ n[i].row - 1 ].text == null &&
+ n[ i - 1 ] == o[ n[i].row - 1 ]) {
+
+ n[ i - 1 ] = {
+ text: n[ i - 1 ],
+ row: n[i].row - 1
+ };
+ o[ n[i].row - 1 ] = {
+ text: o[ n[i].row - 1 ],
+ row: i - 1
+ };
+ }
+ }
+
+ return {
+ o: o,
+ n: n
+ };
+ }
+
+ return function( o, n ) {
+ o = o.replace( /\s+$/, "" );
+ n = n.replace( /\s+$/, "" );
+
+ var i, pre,
+ str = "",
+ out = diff( o === "" ? [] : o.split(/\s+/), n === "" ? [] : n.split(/\s+/) ),
+ oSpace = o.match(/\s+/g),
+ nSpace = n.match(/\s+/g);
+
+ if ( oSpace == null ) {
+ oSpace = [ " " ];
+ }
+ else {
+ oSpace.push( " " );
+ }
+
+ if ( nSpace == null ) {
+ nSpace = [ " " ];
+ }
+ else {
+ nSpace.push( " " );
+ }
+
+ if ( out.n.length === 0 ) {
+ for ( i = 0; i < out.o.length; i++ ) {
+ str += "<del>" + out.o[i] + oSpace[i] + "</del>";
+ }
+ }
+ else {
+ if ( out.n[0].text == null ) {
+ for ( n = 0; n < out.o.length && out.o[n].text == null; n++ ) {
+ str += "<del>" + out.o[n] + oSpace[n] + "</del>";
+ }
+ }
+
+ for ( i = 0; i < out.n.length; i++ ) {
+ if (out.n[i].text == null) {
+ str += "<ins>" + out.n[i] + nSpace[i] + "</ins>";
+ }
+ else {
+ // `pre` initialized at top of scope
+ pre = "";
+
+ for ( n = out.n[i].row + 1; n < out.o.length && out.o[n].text == null; n++ ) {
+ pre += "<del>" + out.o[n] + oSpace[n] + "</del>";
+ }
+ str += " " + out.n[i].text + nSpace[i] + pre;
+ }
+ }
+ }
+
+ return str;
+ };
+}());
+
+// For browser, export only select globals
+if ( typeof window !== "undefined" ) {
+ extend( window, QUnit.constructor.prototype );
+ window.QUnit = QUnit;
+}
+
+// For CommonJS environments, export everything
+if ( typeof module !== "undefined" && module.exports ) {
+ module.exports = QUnit;
+}
+
+
+// Get a reference to the global object, like window in browsers
+}( (function() {
+ return this;
+})() ));
diff --git a/actionview/test/ujs/server.rb b/actionview/test/ujs/server.rb
new file mode 100644
index 0000000000..25f70baf5f
--- /dev/null
+++ b/actionview/test/ujs/server.rb
@@ -0,0 +1,105 @@
+require "rails"
+require "action_controller/railtie"
+require "action_view/railtie"
+require "blade"
+require "json"
+
+JQUERY_VERSIONS = %w[ 1.8.0 1.8.1 1.8.2 1.8.3 1.9.0 1.9.1 1.10.0 1.10.1 1.10.2 1.11.0 2.0.0 2.1.0].freeze
+
+module UJS
+ class Server < Rails::Application
+ routes.append do
+ get "/rails-ujs.js" => Blade::Assets.environment
+ get "/" => "tests#index"
+ match "/echo" => "tests#echo", via: :all
+ get "/error" => proc { |env| [403, {}, []] }
+ end
+
+ config.cache_classes = false
+ config.eager_load = false
+ config.secret_key_base = "59d7a4dbd349fa3838d79e330e39690fc22b931e7dc17d9162f03d633d526fbb92dfdb2dc9804c8be3e199631b9c1fbe43fc3e4fc75730b515851849c728d5c7"
+ config.paths["app/views"].unshift("#{Rails.root / "views"}")
+ config.public_file_server.enabled = true
+ config.logger = Logger.new(STDOUT)
+ config.log_level = :error
+ end
+end
+
+module TestsHelper
+ def jquery_link(version)
+ if params[:version] == version
+ "[#{version}]"
+ else
+ "<a href='/?version=#{version}&cdn=#{params[:cdn]}'>#{version}</a>".html_safe
+ end
+ end
+
+ def cdn_link(cdn)
+ if params[:cdn] == cdn
+ "[#{cdn}]"
+ else
+ "<a href='/?version=#{params[:version]}&cdn=#{cdn}'>#{cdn}</a>".html_safe
+ end
+ end
+
+ def jquery_src
+ if params[:version] == "edge"
+ "/vendor/jquery.js"
+ elsif params[:cdn] && params[:cdn] == "googleapis"
+ "https://ajax.googleapis.com/ajax/libs/jquery/#{params[:version]}/jquery.min.js"
+ else
+ "http://code.jquery.com/jquery-#{params[:version]}.js"
+ end
+ end
+
+ def test_to(*names)
+ names = ["/vendor/qunit.js", "settings"] + names
+ names.map { |name| script_tag name }.join("\n").html_safe
+ end
+
+ def script_tag(src)
+ src = "/test/#{src}.js" unless src.index("/")
+ %(<script src="#{src}" type="text/javascript"></script>).html_safe
+ end
+
+ def jquery_versions
+ JQUERY_VERSIONS
+ end
+end
+
+class TestsController < ActionController::Base
+ helper TestsHelper
+ layout "application"
+
+ def index
+ params[:version] ||= ENV["JQUERY_VERSION"] || "1.11.0"
+ params[:cdn] ||= "jquery"
+ render :index
+ end
+
+ def echo
+ data = { params: params.to_unsafe_h }.update(request.env)
+
+ if params[:content_type] && params[:content]
+ render inline: params[:content], content_type: params[:content_type]
+ elsif request.xhr?
+ render json: JSON.generate(data)
+ elsif params[:iframe]
+ payload = JSON.generate(data).gsub("<", "&lt;").gsub(">", "&gt;")
+ html = <<-HTML
+ <script>
+ if (window.top && window.top !== window)
+ window.top.jQuery.event.trigger('iframe:loaded', #{payload})
+ </script>
+ <p>You shouldn't be seeing this. <a href="#{request.env['HTTP_REFERER']}">Go back</a></p>
+ HTML
+
+ render html: html.html_safe
+ else
+ render plain: "ERROR: #{request.path} requested without ajax", status: 404
+ end
+ end
+end
+
+Blade.initialize!
+UJS::Server.initialize!
diff --git a/actionview/test/ujs/views/layouts/application.html.erb b/actionview/test/ujs/views/layouts/application.html.erb
new file mode 100644
index 0000000000..74fa3bd06d
--- /dev/null
+++ b/actionview/test/ujs/views/layouts/application.html.erb
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<html id="html">
+ <head>
+ <title><%= @title %></title>
+ <link href="/vendor/qunit.css" media="screen" rel="stylesheet" type="text/css" media="screen, projection" />
+ <style>
+ #jquery-cdn, #jquery-version {
+ padding: 0 2em .8em 0;
+ text-align: right;
+ font-family: sans-serif;
+ line-height: 1;
+ color: #8699A4;
+ background-color: #0d3349;
+ }
+ #jquery-cdn a, #jquery-version a {
+ color: white;
+ text-decoration: underline;
+ }
+ </style>
+
+ <%= script_tag jquery_src %>
+ <script>
+ // This is for test in override.js.
+ // Must go after jQuery is loaded, but before jquery-ujs.
+ $(document).bind('rails:attachBindings', function() {
+ $.rails.linkClickSelector += ', a[data-custom-remote-link]';
+ // Hijacks link click before ujs binds any handlers
+ // This is only used for ctrl-clicking test on remote links
+ $.rails.delegate(document, '#qunit-fixture a', 'click', function(e) {
+ e.preventDefault();
+ });
+ });
+ </script>
+ <%= script_tag "/rails-ujs.js" %>
+ </head>
+
+ <body id="body">
+ <%= yield %>
+ </body>
+</html>
diff --git a/actionview/test/ujs/views/tests/index.html.erb b/actionview/test/ujs/views/tests/index.html.erb
new file mode 100644
index 0000000000..393a5ee235
--- /dev/null
+++ b/actionview/test/ujs/views/tests/index.html.erb
@@ -0,0 +1,25 @@
+<% @title = "jquery-ujs test" %>
+
+<%= test_to 'data-confirm', 'data-remote', 'data-disable', 'data-disable-with', 'call-remote', 'call-remote-callbacks', 'data-method', 'override', 'csrf-refresh', 'csrf-token' %>
+
+<h1 id="qunit-header"><%= @title %></h1>
+<div id="jquery-cdn">
+ CDN:
+ <%= cdn_link 'jquery' %> •
+ <%= cdn_link 'googleapis' %>
+</div>
+<div id="jquery-version">
+ jQuery version:
+
+ <% jquery_versions.each do |v| %>
+ <%= ' • ' if v != jquery_versions.first %>
+ <%= jquery_link v %>
+ <% end %>
+ <%= (' • ' + jquery_link('edge')) if File.exist?(Rails.root + '/public/vendor/jquery.js') %>
+</div>
+<h2 id="qunit-banner"></h2>
+<div id="qunit-testrunner-toolbar"></div>
+<h2 id="qunit-userAgent"></h2>
+<ol id="qunit-tests"></ol>
+
+<div id="qunit-fixture"></div>
diff --git a/activejob/Rakefile b/activejob/Rakefile
index 3953116061..c074487fbe 100644
--- a/activejob/Rakefile
+++ b/activejob/Rakefile
@@ -2,7 +2,7 @@ require "rake/testtask"
#TODO: add qu back to the list after it support Rails 5.1
ACTIVEJOB_ADAPTERS = %w(async inline delayed_job que queue_classic resque sidekiq sneakers sucker_punch backburner test)
-ACTIVEJOB_ADAPTERS -= %w(queue_classic) if defined?(JRUBY_VERSION)
+ACTIVEJOB_ADAPTERS.delete("queue_classic") if defined?(JRUBY_VERSION)
task default: :test
task test: "test:default"
diff --git a/activejob/lib/active_job/configured_job.rb b/activejob/lib/active_job/configured_job.rb
index 979280b910..2ff31f2dae 100644
--- a/activejob/lib/active_job/configured_job.rb
+++ b/activejob/lib/active_job/configured_job.rb
@@ -1,6 +1,6 @@
module ActiveJob
class ConfiguredJob #:nodoc:
- def initialize(job_class, options={})
+ def initialize(job_class, options = {})
@options = options
@job_class = job_class
end
diff --git a/activejob/lib/active_job/core.rb b/activejob/lib/active_job/core.rb
index a338061766..548ec89ee2 100644
--- a/activejob/lib/active_job/core.rb
+++ b/activejob/lib/active_job/core.rb
@@ -59,7 +59,7 @@ module ActiveJob
# VideoJob.set(queue: :some_queue, wait: 5.minutes).perform_later(Video.last)
# VideoJob.set(queue: :some_queue, wait_until: Time.now.tomorrow).perform_later(Video.last)
# VideoJob.set(queue: :some_queue, wait: 5.minutes, priority: 10).perform_later(Video.last)
- def set(options={})
+ def set(options = {})
ConfiguredJob.new(self, options)
end
end
diff --git a/activejob/lib/active_job/enqueuing.rb b/activejob/lib/active_job/enqueuing.rb
index 18051a7d65..c73117e7f3 100644
--- a/activejob/lib/active_job/enqueuing.rb
+++ b/activejob/lib/active_job/enqueuing.rb
@@ -18,8 +18,8 @@ module ActiveJob
job_or_instantiate(*args).enqueue
end
- protected
- def job_or_instantiate(*args)
+ private
+ def job_or_instantiate(*args) # :doc:
args.first.is_a?(self) ? args.first : new(*args)
end
end
@@ -39,7 +39,7 @@ module ActiveJob
# my_job_instance.enqueue queue: :important
# my_job_instance.enqueue wait_until: Date.tomorrow.midnight
# my_job_instance.enqueue priority: 10
- def enqueue(options={})
+ def enqueue(options = {})
self.scheduled_at = options[:wait].seconds.from_now.to_f if options[:wait]
self.scheduled_at = options[:wait_until].to_f if options[:wait_until]
self.queue_name = self.class.queue_name_from_part(options[:queue]) if options[:queue]
diff --git a/activejob/lib/active_job/exceptions.rb b/activejob/lib/active_job/exceptions.rb
index d236f03d53..c1b5d35313 100644
--- a/activejob/lib/active_job/exceptions.rb
+++ b/activejob/lib/active_job/exceptions.rb
@@ -17,7 +17,7 @@ module ActiveJob
# ==== Options
# * <tt>:wait</tt> - Re-enqueues the job with a delay specified either in seconds (default: 3 seconds),
# as a computing proc that the number of executions so far as an argument, or as a symbol reference of
- # <tt>:exponentially_longer<>, which applies the wait algorithm of <tt>(executions ** 4) + 2</tt>
+ # <tt>:exponentially_longer</tt>, which applies the wait algorithm of <tt>(executions ** 4) + 2</tt>
# (first wait 3s, then 18s, then 83s, etc)
# * <tt>:attempts</tt> - Re-enqueues the job the specified number of times (default: 5 attempts)
# * <tt>:queue</tt> - Re-enqueues the job on a different queue
@@ -104,7 +104,7 @@ module ActiveJob
def determine_delay(seconds_or_duration_or_algorithm)
case seconds_or_duration_or_algorithm
when :exponentially_longer
- (executions ** 4) + 2
+ (executions**4) + 2
when ActiveSupport::Duration
duration = seconds_or_duration_or_algorithm
duration.to_i
diff --git a/activejob/lib/active_job/queue_adapter.rb b/activejob/lib/active_job/queue_adapter.rb
index 2e9f1e61be..bcc555d33e 100644
--- a/activejob/lib/active_job/queue_adapter.rb
+++ b/activejob/lib/active_job/queue_adapter.rb
@@ -1,5 +1,4 @@
require "active_job/queue_adapters/inline_adapter"
-require "active_support/core_ext/class/attribute"
require "active_support/core_ext/string/inflections"
module ActiveJob
diff --git a/activejob/lib/active_job/queue_adapters/qu_adapter.rb b/activejob/lib/active_job/queue_adapters/qu_adapter.rb
index 20cc97ebc7..e8994533e4 100644
--- a/activejob/lib/active_job/queue_adapters/qu_adapter.rb
+++ b/activejob/lib/active_job/queue_adapters/qu_adapter.rb
@@ -32,7 +32,7 @@ module ActiveJob
class JobWrapper < Qu::Job #:nodoc:
def initialize(job_data)
- @job_data = job_data
+ @job_data = job_data
end
def perform
diff --git a/activejob/lib/active_job/queue_name.rb b/activejob/lib/active_job/queue_name.rb
index 143fac9888..352cf62424 100644
--- a/activejob/lib/active_job/queue_name.rb
+++ b/activejob/lib/active_job/queue_name.rb
@@ -16,7 +16,7 @@ module ActiveJob
# post.to_feed!
# end
# end
- def queue_as(part_name=nil, &block)
+ def queue_as(part_name = nil, &block)
if block_given?
self.queue_name = block
else
diff --git a/activejob/lib/active_job/queue_priority.rb b/activejob/lib/active_job/queue_priority.rb
index a48e53b0ef..b02202fcc8 100644
--- a/activejob/lib/active_job/queue_priority.rb
+++ b/activejob/lib/active_job/queue_priority.rb
@@ -17,7 +17,7 @@ module ActiveJob
# end
#
# Specify either an argument or a block.
- def queue_with_priority(priority=nil, &block)
+ def queue_with_priority(priority = nil, &block)
if block_given?
self.priority = block
else
diff --git a/activejob/lib/active_job/railtie.rb b/activejob/lib/active_job/railtie.rb
index e4198a40a5..4a8bf04d70 100644
--- a/activejob/lib/active_job/railtie.rb
+++ b/activejob/lib/active_job/railtie.rb
@@ -15,7 +15,7 @@ module ActiveJob
options.queue_adapter ||= :async
ActiveSupport.on_load(:active_job) do
- options.each { |k,v| send("#{k}=", v) }
+ options.each { |k, v| send("#{k}=", v) }
end
end
diff --git a/activejob/lib/active_job/test_helper.rb b/activejob/lib/active_job/test_helper.rb
index bbd2a0c06c..d01795f0c5 100644
--- a/activejob/lib/active_job/test_helper.rb
+++ b/activejob/lib/active_job/test_helper.rb
@@ -232,16 +232,16 @@ module ActiveJob
# MyJob.set(wait_until: Date.tomorrow.noon).perform_later
# end
# end
- def assert_enqueued_with(args = {})
+ def assert_enqueued_with(job: nil, args: nil, at: nil, queue: nil)
original_enqueued_jobs_count = enqueued_jobs.count
- args.assert_valid_keys(:job, :args, :at, :queue)
- serialized_args = serialize_args_for_assertion(args)
+ expected = { job: job, args: args, at: at, queue: queue }.compact
+ serialized_args = serialize_args_for_assertion(expected)
yield
in_block_jobs = enqueued_jobs.drop(original_enqueued_jobs_count)
- matching_job = in_block_jobs.find do |job|
- serialized_args.all? { |key, value| value == job[key] }
+ matching_job = in_block_jobs.find do |in_block_job|
+ serialized_args.all? { |key, value| value == in_block_job[key] }
end
- assert matching_job, "No enqueued job found with #{args}"
+ assert matching_job, "No enqueued job found with #{expected}"
instantiate_job(matching_job)
end
@@ -256,19 +256,38 @@ module ActiveJob
# MyJob.set(wait_until: Date.tomorrow.noon).perform_later
# end
# end
- def assert_performed_with(args = {})
+ def assert_performed_with(job: nil, args: nil, at: nil, queue: nil)
original_performed_jobs_count = performed_jobs.count
- args.assert_valid_keys(:job, :args, :at, :queue)
- serialized_args = serialize_args_for_assertion(args)
+ expected = { job: job, args: args, at: at, queue: queue }.compact
+ serialized_args = serialize_args_for_assertion(expected)
perform_enqueued_jobs { yield }
in_block_jobs = performed_jobs.drop(original_performed_jobs_count)
- matching_job = in_block_jobs.find do |job|
- serialized_args.all? { |key, value| value == job[key] }
+ matching_job = in_block_jobs.find do |in_block_job|
+ serialized_args.all? { |key, value| value == in_block_job[key] }
end
- assert matching_job, "No performed job found with #{args}"
+ assert matching_job, "No performed job found with #{expected}"
instantiate_job(matching_job)
end
+ # Performs all enqueued jobs in the duration of the block.
+ #
+ # def test_perform_enqueued_jobs
+ # perform_enqueued_jobs do
+ # MyJob.perform_later(1, 2, 3)
+ # end
+ # assert_performed_jobs 1
+ # end
+ #
+ # This method also supports filtering. If the +:only+ option is specified,
+ # then only the listed job(s) will be performed.
+ #
+ # def test_perform_enqueued_jobs_with_only
+ # perform_enqueued_jobs(only: MyJob) do
+ # MyJob.perform_later(1, 2, 3) # will be performed
+ # HelloJob.perform_later(1, 2, 3) # will not be perfomed
+ # end
+ # assert_performed_jobs 1
+ # end
def perform_enqueued_jobs(only: nil)
old_perform_enqueued_jobs = queue_adapter.perform_enqueued_jobs
old_perform_enqueued_at_jobs = queue_adapter.perform_enqueued_at_jobs
@@ -286,20 +305,25 @@ module ActiveJob
end
end
+ # Accesses the queue_adapter set by ActiveJob::Base.
+ #
+ # def test_assert_job_has_custom_queue_adapter_set
+ # assert_instance_of CustomQueueAdapter, HelloJob.queue_adapter
+ # end
def queue_adapter
ActiveJob::Base.queue_adapter
end
private
- def clear_enqueued_jobs # :nodoc:
+ def clear_enqueued_jobs
enqueued_jobs.clear
end
- def clear_performed_jobs # :nodoc:
+ def clear_performed_jobs
performed_jobs.clear
end
- def enqueued_jobs_size(only: nil) # :nodoc:
+ def enqueued_jobs_size(only: nil)
if only
enqueued_jobs.count { |job| Array(only).include?(job.fetch(:job)) }
else
@@ -307,14 +331,14 @@ module ActiveJob
end
end
- def serialize_args_for_assertion(args) # :nodoc:
+ 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]
end
end
- def instantiate_job(payload) # :nodoc:
+ def instantiate_job(payload)
job = payload[:job].new(*payload[:args])
job.scheduled_at = Time.at(payload[:at]) if payload.key?(:at)
job.queue_name = payload[:queue]
diff --git a/activejob/test/cases/queue_naming_test.rb b/activejob/test/cases/queue_naming_test.rb
index 7777e557c9..0e1e0decd1 100644
--- a/activejob/test/cases/queue_naming_test.rb
+++ b/activejob/test/cases/queue_naming_test.rb
@@ -56,7 +56,7 @@ class QueueNamingTest < ActiveSupport::TestCase
original_queue_name = HelloJob.queue_name
begin
- HelloJob.queue_as { self.arguments.first=="1" ? :one : :two }
+ HelloJob.queue_as { self.arguments.first == "1" ? :one : :two }
assert_equal "one", HelloJob.new("1").queue_name
assert_equal "two", HelloJob.new("3").queue_name
ensure
diff --git a/activejob/test/cases/queue_priority_test.rb b/activejob/test/cases/queue_priority_test.rb
index ab4a1bdf7b..b0d0e903fc 100644
--- a/activejob/test/cases/queue_priority_test.rb
+++ b/activejob/test/cases/queue_priority_test.rb
@@ -3,7 +3,7 @@ require "jobs/hello_job"
class QueuePriorityTest < ActiveSupport::TestCase
test "priority unset by default" do
- assert_equal nil, HelloJob.priority
+ assert_nil HelloJob.priority
end
test "uses given priority" do
@@ -32,7 +32,7 @@ class QueuePriorityTest < ActiveSupport::TestCase
original_priority = HelloJob.priority
begin
- HelloJob.queue_with_priority { self.arguments.first=="1" ? 99 : 11 }
+ HelloJob.queue_with_priority { self.arguments.first == "1" ? 99 : 11 }
assert_equal 99, HelloJob.new("1").priority
assert_equal 11, HelloJob.new("3").priority
ensure
diff --git a/activejob/test/cases/test_helper_test.rb b/activejob/test/cases/test_helper_test.rb
index 253c557cc5..51fc6cc0c4 100644
--- a/activejob/test/cases/test_helper_test.rb
+++ b/activejob/test/cases/test_helper_test.rb
@@ -250,17 +250,17 @@ class EnqueuedJobsTest < ActiveJob::TestCase
HelloJob.perform_later
end
- assert_equal 2, ActiveJob::Base.queue_adapter.enqueued_jobs.count
+ assert_equal 2, queue_adapter.enqueued_jobs.count
end
end
class PerformedJobsTest < ActiveJob::TestCase
def test_performed_enqueue_jobs_with_only_option_doesnt_leak_outside_the_block
- assert_equal nil, queue_adapter.filter
+ assert_nil queue_adapter.filter
perform_enqueued_jobs only: HelloJob do
assert_equal HelloJob, queue_adapter.filter
end
- assert_equal nil, queue_adapter.filter
+ assert_nil queue_adapter.filter
end
def test_assert_performed_jobs
@@ -507,7 +507,7 @@ class PerformedJobsTest < ActiveJob::TestCase
HelloJob.perform_later
end
- assert_equal 2, ActiveJob::Base.queue_adapter.performed_jobs.count
+ assert_equal 2, queue_adapter.performed_jobs.count
end
end
diff --git a/activejob/test/helper.rb b/activejob/test/helper.rb
index dbc7dad109..776f7788de 100644
--- a/activejob/test/helper.rb
+++ b/activejob/test/helper.rb
@@ -4,7 +4,8 @@ require "support/job_buffer"
ActiveSupport.halt_callback_chains_on_return_false = false
GlobalID.app = "aj"
-@adapter = ENV["AJ_ADAPTER"] || "inline"
+@adapter = ENV["AJ_ADAPTER"] || "inline"
+puts "Using #{@adapter}"
if ENV["AJ_INTEGRATION_TESTS"]
require "support/integration/helper"
diff --git a/activejob/test/models/person.rb b/activejob/test/models/person.rb
index 76a8f40616..b5d68ad9c1 100644
--- a/activejob/test/models/person.rb
+++ b/activejob/test/models/person.rb
@@ -6,7 +6,7 @@ class Person
attr_reader :id
def self.find(id)
- raise RecordNotFound.new("Cannot find person with ID=404") if id.to_i==404
+ raise RecordNotFound.new("Cannot find person with ID=404") if id.to_i == 404
new(id)
end
diff --git a/activejob/test/support/backburner/inline.rb b/activejob/test/support/backburner/inline.rb
index 647dbf317f..9758332b6f 100644
--- a/activejob/test/support/backburner/inline.rb
+++ b/activejob/test/support/backburner/inline.rb
@@ -2,7 +2,7 @@ require "backburner"
Backburner::Worker.class_eval do
class << self; alias_method :original_enqueue, :enqueue; end
- def self.enqueue(job_class, args=[], opts={})
+ def self.enqueue(job_class, args = [], opts = {})
job_class.perform(*args)
end
end
diff --git a/activejob/test/support/delayed_job/delayed/backend/test.rb b/activejob/test/support/delayed_job/delayed/backend/test.rb
index a900b18e2a..98d731ff1e 100644
--- a/activejob/test/support/delayed_job/delayed/backend/test.rb
+++ b/activejob/test/support/delayed_job/delayed/backend/test.rb
@@ -26,7 +26,7 @@ module Delayed
self.attempts = 0
self.priority = 0
self.id = (self.class.id += 1)
- hash.each { |k,v| send(:"#{k}=", v) }
+ hash.each { |k, v| send(:"#{k}=", v) }
end
@jobs = []
@@ -63,7 +63,7 @@ module Delayed
jobs = jobs.select { |j| Worker.queues.include?(j.queue) } if Worker.queues.any?
jobs = jobs.select { |j| j.priority >= Worker.min_priority } if Worker.min_priority
jobs = jobs.select { |j| j.priority <= Worker.max_priority } if Worker.max_priority
- jobs.sort_by { |j| [j.priority, j.run_at] }[0..limit-1]
+ jobs.sort_by { |j| [j.priority, j.run_at] }[0..limit - 1]
end
# Lock this job for this worker.
@@ -84,7 +84,7 @@ module Delayed
end
def update_attributes(attrs = {})
- attrs.each { |k,v| send(:"#{k}=", v) }
+ attrs.each { |k, v| send(:"#{k}=", v) }
save
end
diff --git a/activejob/test/support/integration/adapters/que.rb b/activejob/test/support/integration/adapters/que.rb
index 3d35a0439f..20faee3427 100644
--- a/activejob/test/support/integration/adapters/que.rb
+++ b/activejob/test/support/integration/adapters/que.rb
@@ -13,7 +13,7 @@ module QueJobsManager
def start_workers
que_url = ENV["QUE_DATABASE_URL"] || "postgres:///active_jobs_que_int_test"
uri = URI.parse(que_url)
- user = uri.user||ENV["USER"]
+ 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}
diff --git a/activejob/test/support/integration/adapters/queue_classic.rb b/activejob/test/support/integration/adapters/queue_classic.rb
index b5d831428e..369693e947 100644
--- a/activejob/test/support/integration/adapters/queue_classic.rb
+++ b/activejob/test/support/integration/adapters/queue_classic.rb
@@ -12,7 +12,7 @@ module QueueClassicJobsManager
def start_workers
uri = URI.parse(ENV["QC_DATABASE_URL"])
- user = uri.user||ENV["USER"]
+ 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}
diff --git a/activejob/test/support/integration/adapters/sneakers.rb b/activejob/test/support/integration/adapters/sneakers.rb
index 08743c1f05..1c8dfaca59 100644
--- a/activejob/test/support/integration/adapters/sneakers.rb
+++ b/activejob/test/support/integration/adapters/sneakers.rb
@@ -39,7 +39,7 @@ module SneakersJobsManager
@pid = fork do
queues = %w(integration_tests)
workers = queues.map do |q|
- worker_klass = "ActiveJobWorker"+Digest::MD5.hexdigest(q)
+ worker_klass = "ActiveJobWorker" + Digest::MD5.hexdigest(q)
Sneakers.const_set(worker_klass, Class.new(ActiveJob::QueueAdapters::SneakersAdapter::JobWrapper) do
from_queue q
end)
@@ -73,7 +73,7 @@ module SneakersJobsManager
true
end
- protected
+ private
def bunny_publisher
@bunny_publisher ||= begin
p = ActiveJob::QueueAdapters::SneakersAdapter::JobWrapper.send(:publisher)
diff --git a/activejob/test/support/integration/dummy_app_template.rb b/activejob/test/support/integration/dummy_app_template.rb
index 62f6fa13f6..29a5691f30 100644
--- a/activejob/test/support/integration/dummy_app_template.rb
+++ b/activejob/test/support/integration/dummy_app_template.rb
@@ -18,12 +18,13 @@ class TestJob < ActiveJob::Base
queue_as :integration_tests
def perform(x)
- File.open(Rails.root.join("tmp/\#{x}"), "wb+") do |f|
+ File.open(Rails.root.join("tmp/\#{x}.new"), "wb+") do |f|
f.write Marshal.dump({
"locale" => I18n.locale.to_s || "en",
"executed_at" => Time.now.to_r
})
end
+ File.rename(Rails.root.join("tmp/\#{x}.new"), Rails.root.join("tmp/\#{x}"))
end
end
CODE
diff --git a/activejob/test/support/integration/helper.rb b/activejob/test/support/integration/helper.rb
index 1aaee2c809..626b932cce 100644
--- a/activejob/test/support/integration/helper.rb
+++ b/activejob/test/support/integration/helper.rb
@@ -5,6 +5,7 @@ ActiveJob::Base.queue_name_prefix = nil
require "rails/generators/rails/app/app_generator"
+require "tmpdir"
dummy_app_path = Dir.mktmpdir + "/dummy"
dummy_app_template = File.expand_path("../dummy_app_template.rb", __FILE__)
args = Rails::Generators::ARGVScrubber.new(["new", dummy_app_path, "--skip-gemfile", "--skip-bundle",
diff --git a/activejob/test/support/integration/test_case_helpers.rb b/activejob/test/support/integration/test_case_helpers.rb
index 4c4c56c9da..41bf9c89d1 100644
--- a/activejob/test/support/integration/test_case_helpers.rb
+++ b/activejob/test/support/integration/test_case_helpers.rb
@@ -1,4 +1,3 @@
-require "active_support/concern"
require "active_support/core_ext/string/inflections"
require "support/integration/jobs_manager"
@@ -18,7 +17,7 @@ module TestCaseHelpers
end
end
- protected
+ private
def jobs_manager
JobsManager.current_manager
@@ -33,7 +32,7 @@ module TestCaseHelpers
adapter_class_symbols.map(&:to_s).include? adapter
end
- def wait_for_jobs_to_finish_for(seconds=60)
+ def wait_for_jobs_to_finish_for(seconds = 60)
begin
Timeout.timeout(seconds) do
while !job_executed do
@@ -48,7 +47,7 @@ module TestCaseHelpers
Dummy::Application.root.join("tmp/#{id}")
end
- def job_executed(id=@id)
+ def job_executed(id = @id)
job_file(id).exist?
end
@@ -56,11 +55,11 @@ module TestCaseHelpers
Marshal.load(File.binread(job_file(id)))
end
- def job_executed_at(id=@id)
+ def job_executed_at(id = @id)
job_data(id)["executed_at"]
end
- def job_executed_in_locale(id=@id)
+ def job_executed_in_locale(id = @id)
job_data(id)["locale"]
end
end
diff --git a/activemodel/CHANGELOG.md b/activemodel/CHANGELOG.md
index 10f1de6706..853a1e7d9d 100644
--- a/activemodel/CHANGELOG.md
+++ b/activemodel/CHANGELOG.md
@@ -1,3 +1,13 @@
+* Moved DecimalWithoutScale, Text, and UnsignedInteger from Active Model to Active Record
+
+ *Iain Beeston*
+
+* Allow indifferent access in `ActiveModel::Errors`.
+
+ `#include?`, `#has_key?`, `#key?`, `#delete` and `#full_messages_for`.
+
+ *Kenichi Kamiya*
+
* Removed deprecated `:tokenizer` in the length validator.
*Rafael Mendonça França*
diff --git a/activemodel/bin/test b/activemodel/bin/test
index 84a05bba08..a7beb14b27 100755
--- a/activemodel/bin/test
+++ b/activemodel/bin/test
@@ -2,5 +2,3 @@
COMPONENT_ROOT = File.expand_path("..", __dir__)
require File.expand_path("../tools/test", COMPONENT_ROOT)
-
-exit Minitest.run(ARGV)
diff --git a/activemodel/lib/active_model/attribute_methods.rb b/activemodel/lib/active_model/attribute_methods.rb
index 96709486f3..09825cf861 100644
--- a/activemodel/lib/active_model/attribute_methods.rb
+++ b/activemodel/lib/active_model/attribute_methods.rb
@@ -1,6 +1,5 @@
require "concurrent/map"
require "mutex_m"
-require "active_support/core_ext/regexp"
module ActiveModel
# Raised when an attribute is not defined.
@@ -335,12 +334,11 @@ module ActiveModel
}.tap { |mod| include mod }
end
- protected
- def instance_method_already_implemented?(method_name) #:nodoc:
+ private
+ def instance_method_already_implemented?(method_name)
generated_attribute_methods.method_defined?(method_name)
end
- private
# The methods +method_missing+ and +respond_to?+ of this module are
# invoked often in a typical rails, both of which invoke the method
# +matched_attribute_method+. The latter method iterates through an
@@ -350,11 +348,11 @@ module ActiveModel
# used to alleviate the GC, which ultimately also speeds up the app
# significantly (in our case our test suite finishes 10% faster with
# this cache).
- def attribute_method_matchers_cache #:nodoc:
+ def attribute_method_matchers_cache
@attribute_method_matchers_cache ||= Concurrent::Map.new(initial_capacity: 4)
end
- def attribute_method_matchers_matching(method_name) #:nodoc:
+ def attribute_method_matchers_matching(method_name)
attribute_method_matchers_cache.compute_if_absent(method_name) do
# Must try to match prefixes/suffixes first, or else the matcher with no prefix/suffix
# will match every time.
@@ -366,7 +364,7 @@ module ActiveModel
# Define a method `name` in `mod` that dispatches to `send`
# using the given `extra` args. This falls back on `define_method`
# and `send` if the given names cannot be compiled.
- def define_proxy_call(include_private, mod, name, send, *extra) #:nodoc:
+ def define_proxy_call(include_private, mod, name, send, *extra)
defn = if NAME_COMPILABLE_REGEXP.match?(name)
"def #{name}(*args)"
else
@@ -459,12 +457,11 @@ module ActiveModel
end
end
- protected
- def attribute_method?(attr_name) #:nodoc:
+ private
+ def attribute_method?(attr_name)
respond_to_without_attributes?(:attributes) && attributes.include?(attr_name)
end
- private
# Returns a struct representing the matching attribute method.
# The struct's attributes are prefix, base and suffix.
def matched_attribute_method(method_name)
diff --git a/activemodel/lib/active_model/callbacks.rb b/activemodel/lib/active_model/callbacks.rb
index 283090e380..e99bfab6fd 100644
--- a/activemodel/lib/active_model/callbacks.rb
+++ b/activemodel/lib/active_model/callbacks.rb
@@ -122,19 +122,19 @@ module ActiveModel
private
- def _define_before_model_callback(klass, callback) #:nodoc:
+ def _define_before_model_callback(klass, callback)
klass.define_singleton_method("before_#{callback}") do |*args, &block|
set_callback(:"#{callback}", :before, *args, &block)
end
end
- def _define_around_model_callback(klass, callback) #:nodoc:
+ def _define_around_model_callback(klass, callback)
klass.define_singleton_method("around_#{callback}") do |*args, &block|
set_callback(:"#{callback}", :around, *args, &block)
end
end
- def _define_after_model_callback(klass, callback) #:nodoc:
+ def _define_after_model_callback(klass, callback)
klass.define_singleton_method("after_#{callback}") do |*args, &block|
options = args.extract_options!
options[:prepend] = true
diff --git a/activemodel/lib/active_model/errors.rb b/activemodel/lib/active_model/errors.rb
index 17ed5de420..9df4ca51fe 100644
--- a/activemodel/lib/active_model/errors.rb
+++ b/activemodel/lib/active_model/errors.rb
@@ -110,6 +110,7 @@ module ActiveModel
# person.errors.include?(:name) # => true
# person.errors.include?(:age) # => false
def include?(attribute)
+ attribute = attribute.to_sym
messages.key?(attribute) && messages[attribute].present?
end
alias :has_key? :include?
@@ -121,8 +122,9 @@ module ActiveModel
# person.errors.delete(:name) # => ["cannot be nil"]
# person.errors[:name] # => []
def delete(key)
- details.delete(key)
- messages.delete(key)
+ attribute = key.to_sym
+ details.delete(attribute)
+ messages.delete(attribute)
end
# When passed a symbol or a name of a method, returns an array of errors
@@ -211,7 +213,7 @@ module ActiveModel
# # <error>name can't be blank</error>
# # <error>name must be specified</error>
# # </errors>
- def to_xml(options={})
+ def to_xml(options = {})
to_a.to_xml({ root: "errors", skip_types: true }.merge!(options))
end
@@ -221,7 +223,7 @@ module ActiveModel
#
# person.errors.as_json # => {:name=>["cannot be nil"]}
# person.errors.as_json(full_messages: true) # => {:name=>["name cannot be nil"]}
- def as_json(options=nil)
+ def as_json(options = nil)
to_hash(options && options[:full_messages])
end
@@ -342,6 +344,7 @@ module ActiveModel
# person.errors.full_messages_for(:name)
# # => ["Name is too short (minimum is 5 characters)", "Name can't be blank"]
def full_messages_for(attribute)
+ attribute = attribute.to_sym
messages[attribute].map { |message| full_message(attribute, message) }
end
@@ -416,16 +419,23 @@ module ActiveModel
I18n.translate(key, options)
end
- def marshal_dump
+ def marshal_dump # :nodoc:
[@base, without_default_proc(@messages), without_default_proc(@details)]
end
- def marshal_load(array)
+ def marshal_load(array) # :nodoc:
@base, @messages, @details = array
apply_default_array(@messages)
apply_default_array(@details)
end
+ def init_with(coder) # :nodoc:
+ coder.map.each { |k, v| instance_variable_set(:"@#{k}", v) }
+ @details ||= {}
+ apply_default_array(@messages)
+ apply_default_array(@details)
+ end
+
private
def normalize_message(attribute, message, options)
case message
diff --git a/activemodel/lib/active_model/forbidden_attributes_protection.rb b/activemodel/lib/active_model/forbidden_attributes_protection.rb
index d2c6a89cc2..45ab8a2ca1 100644
--- a/activemodel/lib/active_model/forbidden_attributes_protection.rb
+++ b/activemodel/lib/active_model/forbidden_attributes_protection.rb
@@ -15,7 +15,7 @@ module ActiveModel
end
module ForbiddenAttributesProtection # :nodoc:
- protected
+ private
def sanitize_for_mass_assignment(attributes)
if attributes.respond_to?(:permitted?)
raise ActiveModel::ForbiddenAttributesError if !attributes.permitted?
diff --git a/activemodel/lib/active_model/model.rb b/activemodel/lib/active_model/model.rb
index f5765c0c54..945a5402a3 100644
--- a/activemodel/lib/active_model/model.rb
+++ b/activemodel/lib/active_model/model.rb
@@ -2,10 +2,10 @@ module ActiveModel
# == Active \Model \Basic \Model
#
# Includes the required interface for an object to interact with
- # <tt>ActionPack</tt>, using different <tt>ActiveModel</tt> modules.
+ # Action Pack and Action View, using different Active Model modules.
# It includes model name introspections, conversions, translations and
# validations. Besides that, it allows you to initialize the object with a
- # hash of attributes, pretty much like <tt>ActiveRecord</tt> does.
+ # hash of attributes, pretty much like Active Record does.
#
# A minimal implementation could be:
#
@@ -75,7 +75,7 @@ module ActiveModel
# person = Person.new(name: 'bob', age: '18')
# person.name # => "bob"
# person.age # => "18"
- def initialize(attributes={})
+ def initialize(attributes = {})
assign_attributes(attributes) if attributes
super()
diff --git a/activemodel/lib/active_model/naming.rb b/activemodel/lib/active_model/naming.rb
index 3830d1486d..9532c63b65 100644
--- a/activemodel/lib/active_model/naming.rb
+++ b/activemodel/lib/active_model/naming.rb
@@ -1,7 +1,6 @@
require "active_support/core_ext/hash/except"
require "active_support/core_ext/module/introspection"
require "active_support/core_ext/module/remove_method"
-require "active_support/core_ext/module/delegation"
module ActiveModel
class Name
@@ -174,7 +173,7 @@ module ActiveModel
# BlogPost.model_name.human # => "Blog post"
#
# Specify +options+ with additional translating options.
- def human(options={})
+ def human(options = {})
return @human unless @klass.respond_to?(:lookup_ancestors) &&
@klass.respond_to?(:i18n_scope)
diff --git a/activemodel/lib/active_model/serializers/json.rb b/activemodel/lib/active_model/serializers/json.rb
index b5e030a59b..a9d92eb92a 100644
--- a/activemodel/lib/active_model/serializers/json.rb
+++ b/activemodel/lib/active_model/serializers/json.rb
@@ -134,7 +134,7 @@ module ActiveModel
# person.name # => "bob"
# person.age # => 22
# person.awesome # => true
- def from_json(json, include_root=include_root_in_json)
+ def from_json(json, include_root = include_root_in_json)
hash = ActiveSupport::JSON.decode(json)
hash = hash.values.first if include_root
self.attributes = hash
diff --git a/activemodel/lib/active_model/type.rb b/activemodel/lib/active_model/type.rb
index 313f17830f..b8e6d2376b 100644
--- a/activemodel/lib/active_model/type.rb
+++ b/activemodel/lib/active_model/type.rb
@@ -7,14 +7,11 @@ require "active_model/type/boolean"
require "active_model/type/date"
require "active_model/type/date_time"
require "active_model/type/decimal"
-require "active_model/type/decimal_without_scale"
require "active_model/type/float"
require "active_model/type/immutable_string"
require "active_model/type/integer"
require "active_model/type/string"
-require "active_model/type/text"
require "active_model/type/time"
-require "active_model/type/unsigned_integer"
require "active_model/type/registry"
@@ -27,7 +24,7 @@ module ActiveModel
delegate :add_modifier, to: :registry
# Add a new type to the registry, allowing it to be referenced as a
- # symbol by ActiveModel::Attributes::ClassMethods#attribute. If your
+ # symbol by ActiveRecord::Attributes::ClassMethods#attribute. If your
# type is only meant to be used with a specific database adapter, you can
# do so by passing +adapter: :postgresql+. If your type has the same
# name as a native type for the current adapter, an exception will be
@@ -53,7 +50,6 @@ module ActiveModel
register(:immutable_string, Type::ImmutableString)
register(:integer, Type::Integer)
register(:string, Type::String)
- register(:text, Type::Text)
register(:time, Type::Time)
end
end
diff --git a/activemodel/lib/active_model/type/decimal_without_scale.rb b/activemodel/lib/active_model/type/decimal_without_scale.rb
deleted file mode 100644
index 985e1038ed..0000000000
--- a/activemodel/lib/active_model/type/decimal_without_scale.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-require "active_model/type/big_integer"
-
-module ActiveModel
- module Type
- class DecimalWithoutScale < BigInteger # :nodoc:
- def type
- :decimal
- end
- end
- end
-end
diff --git a/activemodel/lib/active_model/type/helpers/accepts_multiparameter_time.rb b/activemodel/lib/active_model/type/helpers/accepts_multiparameter_time.rb
index facea12704..f783d286c5 100644
--- a/activemodel/lib/active_model/type/helpers/accepts_multiparameter_time.rb
+++ b/activemodel/lib/active_model/type/helpers/accepts_multiparameter_time.rb
@@ -1,7 +1,7 @@
module ActiveModel
module Type
- module Helpers
- class AcceptsMultiparameterTime < Module # :nodoc:
+ module Helpers # :nodoc: all
+ class AcceptsMultiparameterTime < Module
def initialize(defaults: {})
define_method(:cast) do |value|
if value.is_a?(Hash)
diff --git a/activemodel/lib/active_model/type/helpers/mutable.rb b/activemodel/lib/active_model/type/helpers/mutable.rb
index 4dddbe4e5e..f3a17a1698 100644
--- a/activemodel/lib/active_model/type/helpers/mutable.rb
+++ b/activemodel/lib/active_model/type/helpers/mutable.rb
@@ -1,7 +1,7 @@
module ActiveModel
module Type
- module Helpers
- module Mutable # :nodoc:
+ module Helpers # :nodoc: all
+ module Mutable
def cast(value)
deserialize(serialize(value))
end
diff --git a/activemodel/lib/active_model/type/helpers/numeric.rb b/activemodel/lib/active_model/type/helpers/numeric.rb
index 98533f8771..275822b738 100644
--- a/activemodel/lib/active_model/type/helpers/numeric.rb
+++ b/activemodel/lib/active_model/type/helpers/numeric.rb
@@ -1,7 +1,7 @@
module ActiveModel
module Type
- module Helpers
- module Numeric # :nodoc:
+ module Helpers # :nodoc: all
+ module Numeric
def cast(value)
value = \
case value
diff --git a/activemodel/lib/active_model/type/helpers/time_value.rb b/activemodel/lib/active_model/type/helpers/time_value.rb
index d8c5d929a3..e57a52104b 100644
--- a/activemodel/lib/active_model/type/helpers/time_value.rb
+++ b/activemodel/lib/active_model/type/helpers/time_value.rb
@@ -2,8 +2,8 @@ require "active_support/core_ext/time/zones"
module ActiveModel
module Type
- module Helpers
- module TimeValue # :nodoc:
+ module Helpers # :nodoc: all
+ module TimeValue
def serialize(value)
value = apply_seconds_precision(value)
@@ -33,8 +33,8 @@ module ActiveModel
def apply_seconds_precision(value)
return value unless precision && value.respond_to?(:usec)
number_of_insignificant_digits = 6 - precision
- round_power = 10 ** number_of_insignificant_digits
- value.change(usec: value.usec / round_power * round_power)
+ round_power = 10**number_of_insignificant_digits
+ value.change(usec: value.usec - value.usec % round_power)
end
def type_cast_for_schema(value)
diff --git a/activemodel/lib/active_model/type/integer.rb b/activemodel/lib/active_model/type/integer.rb
index 41dd655a5c..230e45ba60 100644
--- a/activemodel/lib/active_model/type/integer.rb
+++ b/activemodel/lib/active_model/type/integer.rb
@@ -29,6 +29,8 @@ module ActiveModel
result
end
+ # TODO Change this to private once we've dropped Ruby 2.2 support.
+ # Workaround for Ruby 2.2 "private attribute?" warning.
protected
attr_reader :range
diff --git a/activemodel/lib/active_model/type/registry.rb b/activemodel/lib/active_model/type/registry.rb
index d25f1a129e..2d5dd366eb 100644
--- a/activemodel/lib/active_model/type/registry.rb
+++ b/activemodel/lib/active_model/type/registry.rb
@@ -21,6 +21,8 @@ module ActiveModel
end
end
+ # TODO Change this to private once we've dropped Ruby 2.2 support.
+ # Workaround for Ruby 2.2 "private attribute?" warning.
protected
attr_reader :registrations
@@ -55,6 +57,8 @@ module ActiveModel
type_name == name
end
+ # TODO Change this to private once we've dropped Ruby 2.2 support.
+ # Workaround for Ruby 2.2 "private attribute?" warning.
protected
attr_reader :name, :block
diff --git a/activemodel/lib/active_model/type/text.rb b/activemodel/lib/active_model/type/text.rb
deleted file mode 100644
index 7c0d647706..0000000000
--- a/activemodel/lib/active_model/type/text.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-require "active_model/type/string"
-
-module ActiveModel
- module Type
- class Text < String # :nodoc:
- def type
- :text
- end
- end
- end
-end
diff --git a/activemodel/lib/active_model/validations.rb b/activemodel/lib/active_model/validations.rb
index df6d3f2bcb..d460068830 100644
--- a/activemodel/lib/active_model/validations.rb
+++ b/activemodel/lib/active_model/validations.rb
@@ -50,7 +50,7 @@ module ActiveModel
define_callbacks :validate, scope: :name
class_attribute :_validators, instance_writer: false
- self._validators = Hash.new { |h,k| h[k] = [] }
+ self._validators = Hash.new { |h, k| h[k] = [] }
end
module ClassMethods
@@ -68,7 +68,7 @@ module ActiveModel
#
# Options:
# * <tt>:on</tt> - Specifies the contexts where this validation is active.
- # Runs in all validation contexts by default (nil). You can pass a symbol
+ # Runs in all validation contexts by default +nil+. You can pass a symbol
# or an array of symbols. (e.g. <tt>on: :create</tt> or
# <tt>on: :custom_validation_context</tt> or
# <tt>on: [:create, :custom_validation_context]</tt>)
@@ -134,7 +134,7 @@ module ActiveModel
#
# Options:
# * <tt>:on</tt> - Specifies the contexts where this validation is active.
- # Runs in all validation contexts by default (nil). You can pass a symbol
+ # Runs in all validation contexts by default +nil+. You can pass a symbol
# or an array of symbols. (e.g. <tt>on: :create</tt> or
# <tt>on: :custom_validation_context</tt> or
# <tt>on: [:create, :custom_validation_context]</tt>)
@@ -399,14 +399,14 @@ module ActiveModel
# end
alias :read_attribute_for_validation :send
- protected
+ private
- def run_validations! #:nodoc:
+ def run_validations!
_run_validate_callbacks
errors.empty?
end
- def raise_validation_error
+ def raise_validation_error # :doc:
raise(ValidationError.new(self))
end
end
diff --git a/activemodel/lib/active_model/validations/acceptance.rb b/activemodel/lib/active_model/validations/acceptance.rb
index 4fadc795cd..e11005b9ba 100644
--- a/activemodel/lib/active_model/validations/acceptance.rb
+++ b/activemodel/lib/active_model/validations/acceptance.rb
@@ -24,7 +24,7 @@ module ActiveModel
class LazilyDefineAttributes < Module
def initialize(attribute_definition)
- define_method(:respond_to_missing?) do |method_name, include_private=false|
+ define_method(:respond_to_missing?) do |method_name, include_private = false|
super(method_name, include_private) || attribute_definition.matches?(method_name)
end
@@ -56,6 +56,8 @@ module ActiveModel
klass.send(:attr_writer, *attr_writers)
end
+ # TODO Change this to private once we've dropped Ruby 2.2 support.
+ # Workaround for Ruby 2.2 "private attribute?" warning.
protected
attr_reader :attributes
diff --git a/activemodel/lib/active_model/validations/callbacks.rb b/activemodel/lib/active_model/validations/callbacks.rb
index a201f72ed0..70bc1a0624 100644
--- a/activemodel/lib/active_model/validations/callbacks.rb
+++ b/activemodel/lib/active_model/validations/callbacks.rb
@@ -104,10 +104,10 @@ module ActiveModel
end
end
- protected
+ private
# Overwrite run validations to include callbacks.
- def run_validations! #:nodoc:
+ def run_validations!
_run_validation_callbacks { super }
end
end
diff --git a/activemodel/lib/active_model/validations/format.rb b/activemodel/lib/active_model/validations/format.rb
index e4ea42f8f4..fa183885ab 100644
--- a/activemodel/lib/active_model/validations/format.rb
+++ b/activemodel/lib/active_model/validations/format.rb
@@ -1,4 +1,3 @@
-require "active_support/core_ext/regexp"
module ActiveModel
module Validations
diff --git a/activemodel/lib/active_model/validations/length.rb b/activemodel/lib/active_model/validations/length.rb
index f73f16830f..de1524829e 100644
--- a/activemodel/lib/active_model/validations/length.rb
+++ b/activemodel/lib/active_model/validations/length.rb
@@ -6,7 +6,7 @@ module ActiveModel
MESSAGES = { is: :wrong_length, minimum: :too_short, maximum: :too_long }.freeze
CHECKS = { is: :==, minimum: :>=, maximum: :<= }.freeze
- RESERVED_OPTIONS = [:minimum, :maximum, :within, :is, :too_short, :too_long]
+ RESERVED_OPTIONS = [:minimum, :maximum, :within, :is, :too_short, :too_long]
def initialize(options)
if range = (options.delete(:in) || options.delete(:within))
diff --git a/activemodel/lib/active_model/validations/numericality.rb b/activemodel/lib/active_model/validations/numericality.rb
index 48e0e5c9f6..2297faaee5 100644
--- a/activemodel/lib/active_model/validations/numericality.rb
+++ b/activemodel/lib/active_model/validations/numericality.rb
@@ -61,29 +61,29 @@ module ActiveModel
end
end
- protected
+ private
- def is_number?(raw_value)
+ def is_number?(raw_value) # :doc:
!parse_raw_value_as_a_number(raw_value).nil?
rescue ArgumentError, TypeError
false
end
- def parse_raw_value_as_a_number(raw_value)
+ def parse_raw_value_as_a_number(raw_value) # :doc:
Kernel.Float(raw_value) if raw_value !~ /\A0[xX]/
end
- def is_integer?(raw_value)
+ def is_integer?(raw_value) # :doc:
/\A[+-]?\d+\z/ === raw_value.to_s
end
- def filtered_options(value)
+ def filtered_options(value) # :doc:
filtered = options.except(*RESERVED_OPTIONS)
filtered[:value] = value
filtered
end
- def allow_only_integer?(record)
+ def allow_only_integer?(record) # :doc:
case options[:only_integer]
when Symbol
record.send(options[:only_integer])
@@ -94,12 +94,10 @@ module ActiveModel
end
end
- private
-
- def record_attribute_changed_in_place?(record, attr_name)
- record.respond_to?(:attribute_changed_in_place?) &&
- record.attribute_changed_in_place?(attr_name.to_s)
- end
+ def record_attribute_changed_in_place?(record, attr_name)
+ record.respond_to?(:attribute_changed_in_place?) &&
+ record.attribute_changed_in_place?(attr_name.to_s)
+ end
end
module HelperMethods
diff --git a/activemodel/lib/active_model/validations/validates.rb b/activemodel/lib/active_model/validations/validates.rb
index c3cbb6e291..0ce5935f3a 100644
--- a/activemodel/lib/active_model/validations/validates.rb
+++ b/activemodel/lib/active_model/validations/validates.rb
@@ -72,7 +72,7 @@ module ActiveModel
# There is also a list of options that could be used along with validators:
#
# * <tt>:on</tt> - Specifies the contexts where this validation is active.
- # Runs in all validation contexts by default (nil). You can pass a symbol
+ # Runs in all validation contexts by default +nil+. You can pass a symbol
# or an array of symbols. (e.g. <tt>on: :create</tt> or
# <tt>on: :custom_validation_context</tt> or
# <tt>on: [:create, :custom_validation_context]</tt>)
@@ -148,15 +148,15 @@ module ActiveModel
validates(*(attributes << options))
end
- protected
+ private
# When creating custom validators, it might be useful to be able to specify
# additional default keys. This can be done by overwriting this method.
- def _validates_default_keys # :nodoc:
+ def _validates_default_keys
[:if, :unless, :on, :allow_blank, :allow_nil , :strict]
end
- def _parse_validates_options(options) # :nodoc:
+ def _parse_validates_options(options)
case options
when TrueClass
{}
diff --git a/activemodel/lib/active_model/validations/with.rb b/activemodel/lib/active_model/validations/with.rb
index 6de01b3392..e3f7a9bcb2 100644
--- a/activemodel/lib/active_model/validations/with.rb
+++ b/activemodel/lib/active_model/validations/with.rb
@@ -1,3 +1,5 @@
+require "active_support/core_ext/array/extract_options"
+
module ActiveModel
module Validations
class WithValidator < EachValidator # :nodoc:
@@ -43,7 +45,7 @@ module ActiveModel
#
# Configuration options:
# * <tt>:on</tt> - Specifies the contexts where this validation is active.
- # Runs in all validation contexts by default (nil). You can pass a symbol
+ # Runs in all validation contexts by default +nil+. You can pass a symbol
# or an array of symbols. (e.g. <tt>on: :create</tt> or
# <tt>on: :custom_validation_context</tt> or
# <tt>on: [:create, :custom_validation_context]</tt>)
diff --git a/activemodel/lib/active_model/validator.rb b/activemodel/lib/active_model/validator.rb
index 9c6065f61f..8212744170 100644
--- a/activemodel/lib/active_model/validator.rb
+++ b/activemodel/lib/active_model/validator.rb
@@ -104,7 +104,7 @@ module ActiveModel
# Accepts options that will be made available through the +options+ reader.
def initialize(options = {})
- @options = options.except(:class).freeze
+ @options = options.except(:class).freeze
end
# Returns the kind for this validator.
diff --git a/activemodel/test/cases/attribute_assignment_test.rb b/activemodel/test/cases/attribute_assignment_test.rb
index 8eb389d331..fa41d1b95f 100644
--- a/activemodel/test/cases/attribute_assignment_test.rb
+++ b/activemodel/test/cases/attribute_assignment_test.rb
@@ -16,6 +16,8 @@ class AttributeAssignmentTest < ActiveModel::TestCase
raise ErrorFromAttributeWriter
end
+ # TODO Change this to private once we've dropped Ruby 2.2 support.
+ # Workaround for Ruby 2.2 "private attribute?" warning.
protected
attr_writer :metadata
diff --git a/activemodel/test/cases/errors_test.rb b/activemodel/test/cases/errors_test.rb
index 605ad64e4d..e6ba06301d 100644
--- a/activemodel/test/cases/errors_test.rb
+++ b/activemodel/test/cases/errors_test.rb
@@ -1,4 +1,5 @@
require "cases/helper"
+require "yaml"
class ErrorsTest < ActiveModel::TestCase
class Person
@@ -30,7 +31,7 @@ class ErrorsTest < ActiveModel::TestCase
def test_delete
errors = ActiveModel::Errors.new(self)
errors[:foo] << "omg"
- errors.delete(:foo)
+ errors.delete("foo")
assert_empty errors[:foo]
end
@@ -38,6 +39,7 @@ class ErrorsTest < ActiveModel::TestCase
errors = ActiveModel::Errors.new(self)
errors[:foo] << "omg"
assert_includes errors, :foo, "errors should include :foo"
+ assert_includes errors, "foo", "errors should include 'foo' as :foo"
end
def test_dup
@@ -52,6 +54,7 @@ class ErrorsTest < ActiveModel::TestCase
errors = ActiveModel::Errors.new(self)
errors[:foo] << "omg"
assert_equal true, errors.has_key?(:foo), "errors should have key :foo"
+ assert_equal true, errors.has_key?("foo"), "errors should have key 'foo' as :foo"
end
def test_has_no_key
@@ -63,6 +66,7 @@ class ErrorsTest < ActiveModel::TestCase
errors = ActiveModel::Errors.new(self)
errors[:foo] << "omg"
assert_equal true, errors.key?(:foo), "errors should have key :foo"
+ assert_equal true, errors.key?("foo"), "errors should have key 'foo' as :foo"
end
def test_no_key
@@ -150,10 +154,11 @@ class ErrorsTest < ActiveModel::TestCase
assert_equal ["cannot be blank"], person.errors[:name]
end
- test "added? detects if a specific error was added to the object" do
+ test "added? detects indifferent if a specific error was added to the object" do
person = Person.new
person.errors.add(:name, "cannot be blank")
assert person.errors.added?(:name, "cannot be blank")
+ assert person.errors.added?("name", "cannot be blank")
end
test "added? handles symbol message" do
@@ -241,7 +246,7 @@ class ErrorsTest < ActiveModel::TestCase
assert_equal ["name cannot be blank", "name cannot be nil"], person.errors.full_messages
end
- test "full_messages_for contains all the error messages for the given attribute" do
+ test "full_messages_for contains all the error messages for the given attribute indifferent" do
person = Person.new
person.errors.add(:name, "cannot be blank")
person.errors.add(:name, "cannot be nil")
@@ -253,6 +258,7 @@ class ErrorsTest < ActiveModel::TestCase
person.errors.add(:name, "cannot be blank")
person.errors.add(:email, "cannot be blank")
assert_equal ["name cannot be blank"], person.errors.full_messages_for(:name)
+ assert_equal ["name cannot be blank"], person.errors.full_messages_for("name")
end
test "full_messages_for returns an empty list in case there are no errors for the given attribute" do
@@ -360,4 +366,24 @@ class ErrorsTest < ActiveModel::TestCase
assert_equal errors.messages, serialized.messages
assert_equal errors.details, serialized.details
end
+
+ test "errors are backward compatible with the Rails 4.2 format" do
+ yaml = <<-CODE.strip_heredoc
+ --- !ruby/object:ActiveModel::Errors
+ base: &1 !ruby/object:ErrorsTest::Person
+ errors: !ruby/object:ActiveModel::Errors
+ base: *1
+ messages: {}
+ messages: {}
+ CODE
+
+ errors = YAML.load(yaml)
+ errors.add(:name, :invalid)
+ assert_equal({ name: ["is invalid"] }, errors.messages)
+ assert_equal({ name: [{ error: :invalid }] }, errors.details)
+
+ errors.clear
+ assert_equal({}, errors.messages)
+ assert_equal({}, errors.details)
+ end
end
diff --git a/activemodel/test/cases/secure_password_test.rb b/activemodel/test/cases/secure_password_test.rb
index 9423df2c09..77ce86f392 100644
--- a/activemodel/test/cases/secure_password_test.rb
+++ b/activemodel/test/cases/secure_password_test.rb
@@ -179,7 +179,7 @@ class SecurePasswordTest < ActiveModel::TestCase
test "setting a nil password should clear an existing password" do
@existing_user.password = nil
- assert_equal nil, @existing_user.password_digest
+ assert_nil @existing_user.password_digest
end
test "authenticate" do
diff --git a/activemodel/test/cases/serialization_test.rb b/activemodel/test/cases/serialization_test.rb
index 5ee53285a3..f78efd2f0c 100644
--- a/activemodel/test/cases/serialization_test.rb
+++ b/activemodel/test/cases/serialization_test.rb
@@ -51,32 +51,32 @@ class SerializationTest < ActiveModel::TestCase
end
def test_method_serializable_hash_should_work
- expected = { "name"=>"David", "gender"=>"male", "email"=>"david@example.com" }
+ expected = { "name" => "David", "gender" => "male", "email" => "david@example.com" }
assert_equal expected, @user.serializable_hash
end
def test_method_serializable_hash_should_work_with_only_option
- expected = { "name"=>"David" }
+ expected = { "name" => "David" }
assert_equal expected, @user.serializable_hash(only: [:name])
end
def test_method_serializable_hash_should_work_with_except_option
- expected = { "gender"=>"male", "email"=>"david@example.com" }
+ expected = { "gender" => "male", "email" => "david@example.com" }
assert_equal expected, @user.serializable_hash(except: [:name])
end
def test_method_serializable_hash_should_work_with_methods_option
- expected = { "name"=>"David", "gender"=>"male", "foo"=>"i_am_foo", "bar"=>"i_am_bar", "email"=>"david@example.com" }
+ expected = { "name" => "David", "gender" => "male", "foo" => "i_am_foo", "bar" => "i_am_bar", "email" => "david@example.com" }
assert_equal expected, @user.serializable_hash(methods: [:foo, :bar])
end
def test_method_serializable_hash_should_work_with_only_and_methods
- expected = { "foo"=>"i_am_foo", "bar"=>"i_am_bar" }
+ expected = { "foo" => "i_am_foo", "bar" => "i_am_bar" }
assert_equal expected, @user.serializable_hash(only: [], methods: [:foo, :bar])
end
def test_method_serializable_hash_should_work_with_except_and_methods
- expected = { "gender"=>"male", "foo"=>"i_am_foo", "bar"=>"i_am_bar" }
+ expected = { "gender" => "male", "foo" => "i_am_foo", "bar" => "i_am_bar" }
assert_equal expected, @user.serializable_hash(except: [:name, :email], methods: [:foo, :bar])
end
@@ -94,21 +94,21 @@ class SerializationTest < ActiveModel::TestCase
end
def test_include_option_with_singular_association
- expected = { "name"=>"David", "gender"=>"male", "email"=>"david@example.com",
- "address"=>{ "street"=>"123 Lane", "city"=>"Springfield", "state"=>"CA", "zip"=>11111 } }
+ expected = { "name" => "David", "gender" => "male", "email" => "david@example.com",
+ "address" => { "street" => "123 Lane", "city" => "Springfield", "state" => "CA", "zip" => 11111 } }
assert_equal expected, @user.serializable_hash(include: :address)
end
def test_include_option_with_plural_association
- expected = { "email"=>"david@example.com", "gender"=>"male", "name"=>"David",
- "friends"=>[{ "name"=>"Joe", "email"=>"joe@example.com", "gender"=>"male" },
- { "name"=>"Sue", "email"=>"sue@example.com", "gender"=>"female" }] }
+ expected = { "email" => "david@example.com", "gender" => "male", "name" => "David",
+ "friends" => [{ "name" => "Joe", "email" => "joe@example.com", "gender" => "male" },
+ { "name" => "Sue", "email" => "sue@example.com", "gender" => "female" }] }
assert_equal expected, @user.serializable_hash(include: :friends)
end
def test_include_option_with_empty_association
@user.friends = []
- expected = { "email"=>"david@example.com", "gender"=>"male", "name"=>"David", "friends"=>[] }
+ expected = { "email" => "david@example.com", "gender" => "male", "name" => "David", "friends" => [] }
assert_equal expected, @user.serializable_hash(include: :friends)
end
@@ -124,52 +124,52 @@ class SerializationTest < ActiveModel::TestCase
def test_include_option_with_ary
@user.friends = FriendList.new(@user.friends)
- expected = { "email"=>"david@example.com", "gender"=>"male", "name"=>"David",
- "friends"=>[{ "name"=>"Joe", "email"=>"joe@example.com", "gender"=>"male" },
- { "name"=>"Sue", "email"=>"sue@example.com", "gender"=>"female" }] }
+ expected = { "email" => "david@example.com", "gender" => "male", "name" => "David",
+ "friends" => [{ "name" => "Joe", "email" => "joe@example.com", "gender" => "male" },
+ { "name" => "Sue", "email" => "sue@example.com", "gender" => "female" }] }
assert_equal expected, @user.serializable_hash(include: :friends)
end
def test_multiple_includes
- expected = { "email"=>"david@example.com", "gender"=>"male", "name"=>"David",
- "address"=>{ "street"=>"123 Lane", "city"=>"Springfield", "state"=>"CA", "zip"=>11111 },
- "friends"=>[{ "name"=>"Joe", "email"=>"joe@example.com", "gender"=>"male" },
- { "name"=>"Sue", "email"=>"sue@example.com", "gender"=>"female" }] }
+ expected = { "email" => "david@example.com", "gender" => "male", "name" => "David",
+ "address" => { "street" => "123 Lane", "city" => "Springfield", "state" => "CA", "zip" => 11111 },
+ "friends" => [{ "name" => "Joe", "email" => "joe@example.com", "gender" => "male" },
+ { "name" => "Sue", "email" => "sue@example.com", "gender" => "female" }] }
assert_equal expected, @user.serializable_hash(include: [:address, :friends])
end
def test_include_with_options
- expected = { "email"=>"david@example.com", "gender"=>"male", "name"=>"David",
- "address"=>{ "street"=>"123 Lane" } }
+ expected = { "email" => "david@example.com", "gender" => "male", "name" => "David",
+ "address" => { "street" => "123 Lane" } }
assert_equal expected, @user.serializable_hash(include: { address: { only: "street" } })
end
def test_nested_include
@user.friends.first.friends = [@user]
- expected = { "email"=>"david@example.com", "gender"=>"male", "name"=>"David",
- "friends"=>[{ "name"=>"Joe", "email"=>"joe@example.com", "gender"=>"male",
- "friends"=> [{ "email"=>"david@example.com", "gender"=>"male", "name"=>"David" }] },
- { "name"=>"Sue", "email"=>"sue@example.com", "gender"=>"female", "friends"=> [] }] }
+ expected = { "email" => "david@example.com", "gender" => "male", "name" => "David",
+ "friends" => [{ "name" => "Joe", "email" => "joe@example.com", "gender" => "male",
+ "friends" => [{ "email" => "david@example.com", "gender" => "male", "name" => "David" }] },
+ { "name" => "Sue", "email" => "sue@example.com", "gender" => "female", "friends" => [] }] }
assert_equal expected, @user.serializable_hash(include: { friends: { include: :friends } })
end
def test_only_include
- expected = { "name"=>"David", "friends" => [{ "name" => "Joe" }, { "name" => "Sue" }] }
+ expected = { "name" => "David", "friends" => [{ "name" => "Joe" }, { "name" => "Sue" }] }
assert_equal expected, @user.serializable_hash(only: :name, include: { friends: { only: :name } })
end
def test_except_include
- expected = { "name"=>"David", "email"=>"david@example.com",
- "friends"=> [{ "name" => "Joe", "email" => "joe@example.com" },
+ expected = { "name" => "David", "email" => "david@example.com",
+ "friends" => [{ "name" => "Joe", "email" => "joe@example.com" },
{ "name" => "Sue", "email" => "sue@example.com" }] }
assert_equal expected, @user.serializable_hash(except: :gender, include: { friends: { except: :gender } })
end
def test_multiple_includes_with_options
- expected = { "email"=>"david@example.com", "gender"=>"male", "name"=>"David",
- "address"=>{ "street"=>"123 Lane" },
- "friends"=>[{ "name"=>"Joe", "email"=>"joe@example.com", "gender"=>"male" },
- { "name"=>"Sue", "email"=>"sue@example.com", "gender"=>"female" }] }
+ expected = { "email" => "david@example.com", "gender" => "male", "name" => "David",
+ "address" => { "street" => "123 Lane" },
+ "friends" => [{ "name" => "Joe", "email" => "joe@example.com", "gender" => "male" },
+ { "name" => "Sue", "email" => "sue@example.com", "gender" => "female" }] }
assert_equal expected, @user.serializable_hash(include: [{ address: { only: "street" } }, :friends])
end
end
diff --git a/activemodel/test/cases/type/big_integer_test.rb b/activemodel/test/cases/type/big_integer_test.rb
new file mode 100644
index 0000000000..56002b7cc6
--- /dev/null
+++ b/activemodel/test/cases/type/big_integer_test.rb
@@ -0,0 +1,24 @@
+require "cases/helper"
+require "active_model/type"
+
+module ActiveModel
+ module Type
+ class BigIntegerTest < ActiveModel::TestCase
+ def test_type_cast_big_integer
+ type = Type::BigInteger.new
+ assert_equal 1, type.cast(1)
+ assert_equal 1, type.cast("1")
+ end
+
+ def test_small_values
+ type = Type::BigInteger.new
+ assert_equal(-9999999999999999999999999999999, type.serialize(-9999999999999999999999999999999))
+ end
+
+ def test_large_values
+ type = Type::BigInteger.new
+ assert_equal 9999999999999999999999999999999, type.serialize(9999999999999999999999999999999)
+ end
+ end
+ end
+end
diff --git a/activemodel/test/cases/type/binary_test.rb b/activemodel/test/cases/type/binary_test.rb
new file mode 100644
index 0000000000..e9c2ccfca4
--- /dev/null
+++ b/activemodel/test/cases/type/binary_test.rb
@@ -0,0 +1,15 @@
+require "cases/helper"
+require "active_model/type"
+
+module ActiveModel
+ module Type
+ class BinaryTest < ActiveModel::TestCase
+ def test_type_cast_binary
+ type = Type::Binary.new
+ assert_nil type.cast(nil)
+ assert_equal "1", type.cast("1")
+ assert_equal 1, type.cast(1)
+ end
+ end
+ end
+end
diff --git a/activemodel/test/cases/type/boolean_test.rb b/activemodel/test/cases/type/boolean_test.rb
new file mode 100644
index 0000000000..92e5aebfb7
--- /dev/null
+++ b/activemodel/test/cases/type/boolean_test.rb
@@ -0,0 +1,39 @@
+require "cases/helper"
+require "active_model/type"
+
+module ActiveModel
+ module Type
+ class BooleanTest < ActiveModel::TestCase
+ def test_type_cast_boolean
+ type = Type::Boolean.new
+ assert type.cast("").nil?
+ assert type.cast(nil).nil?
+
+ assert type.cast(true)
+ assert type.cast(1)
+ assert type.cast("1")
+ assert type.cast("t")
+ assert type.cast("T")
+ assert type.cast("true")
+ assert type.cast("TRUE")
+ assert type.cast("on")
+ assert type.cast("ON")
+ assert type.cast(" ")
+ assert type.cast("\u3000\r\n")
+ assert type.cast("\u0000")
+ assert type.cast("SOMETHING RANDOM")
+
+ # explicitly check for false vs nil
+ assert_equal false, type.cast(false)
+ assert_equal false, type.cast(0)
+ assert_equal false, type.cast("0")
+ assert_equal false, type.cast("f")
+ assert_equal false, type.cast("F")
+ assert_equal false, type.cast("false")
+ assert_equal false, type.cast("FALSE")
+ assert_equal false, type.cast("off")
+ assert_equal false, type.cast("OFF")
+ end
+ end
+ end
+end
diff --git a/activemodel/test/cases/type/date_test.rb b/activemodel/test/cases/type/date_test.rb
new file mode 100644
index 0000000000..0cc90e99d3
--- /dev/null
+++ b/activemodel/test/cases/type/date_test.rb
@@ -0,0 +1,19 @@
+require "cases/helper"
+require "active_model/type"
+
+module ActiveModel
+ module Type
+ class DateTest < ActiveModel::TestCase
+ def test_type_cast_date
+ type = Type::Date.new
+ assert_nil type.cast(nil)
+ assert_nil type.cast("")
+ assert_nil type.cast(" ")
+ assert_nil type.cast("ABC")
+
+ date_string = ::Time.now.utc.strftime("%F")
+ assert_equal date_string, type.cast(date_string).strftime("%F")
+ end
+ end
+ end
+end
diff --git a/activemodel/test/cases/type/date_time_test.rb b/activemodel/test/cases/type/date_time_test.rb
new file mode 100644
index 0000000000..75a7fc686e
--- /dev/null
+++ b/activemodel/test/cases/type/date_time_test.rb
@@ -0,0 +1,38 @@
+require "cases/helper"
+require "active_model/type"
+
+module ActiveModel
+ module Type
+ class DateTimeTest < ActiveModel::TestCase
+ def test_type_cast_datetime_and_timestamp
+ type = Type::DateTime.new
+ assert_nil type.cast(nil)
+ assert_nil type.cast("")
+ assert_nil type.cast(" ")
+ assert_nil type.cast("ABC")
+
+ datetime_string = ::Time.now.utc.strftime("%FT%T")
+ assert_equal datetime_string, type.cast(datetime_string).strftime("%FT%T")
+ end
+
+ def test_string_to_time_with_timezone
+ ["UTC", "US/Eastern"].each do |zone|
+ with_timezone_config default: zone do
+ type = Type::DateTime.new
+ assert_equal ::Time.utc(2013, 9, 4, 0, 0, 0), type.cast("Wed, 04 Sep 2013 03:00:00 EAT")
+ end
+ end
+ end
+
+ private
+
+ def with_timezone_config(default:)
+ old_zone_default = ::Time.zone_default
+ ::Time.zone_default = ::Time.find_zone(default)
+ yield
+ ensure
+ ::Time.zone_default = old_zone_default
+ end
+ end
+ end
+end
diff --git a/activemodel/test/cases/type/float_test.rb b/activemodel/test/cases/type/float_test.rb
new file mode 100644
index 0000000000..2e34f57f7e
--- /dev/null
+++ b/activemodel/test/cases/type/float_test.rb
@@ -0,0 +1,22 @@
+require "cases/helper"
+require "active_model/type"
+
+module ActiveModel
+ module Type
+ class FloatTest < ActiveModel::TestCase
+ def test_type_cast_float
+ type = Type::Float.new
+ assert_equal 1.0, type.cast("1")
+ end
+
+ def test_changing_float
+ type = Type::Float.new
+
+ assert type.changed?(5.0, 5.0, "5wibble")
+ assert_not type.changed?(5.0, 5.0, "5")
+ assert_not type.changed?(5.0, 5.0, "5.0")
+ assert_not type.changed?(nil, nil, nil)
+ end
+ end
+ end
+end
diff --git a/activemodel/test/cases/type/immutable_string_test.rb b/activemodel/test/cases/type/immutable_string_test.rb
new file mode 100644
index 0000000000..23e58974fb
--- /dev/null
+++ b/activemodel/test/cases/type/immutable_string_test.rb
@@ -0,0 +1,21 @@
+require "cases/helper"
+require "active_model/type"
+
+module ActiveModel
+ module Type
+ class ImmutableStringTest < ActiveModel::TestCase
+ test "cast strings are frozen" do
+ s = "foo"
+ type = Type::ImmutableString.new
+ assert_equal true, type.cast(s).frozen?
+ end
+
+ test "immutable strings are not duped coming out" do
+ s = "foo"
+ type = Type::ImmutableString.new
+ assert_same s, type.cast(s)
+ assert_same s, type.deserialize(s)
+ end
+ end
+ end
+end
diff --git a/activemodel/test/cases/type/integer_test.rb b/activemodel/test/cases/type/integer_test.rb
index e00246b602..2b9b03f3cf 100644
--- a/activemodel/test/cases/type/integer_test.rb
+++ b/activemodel/test/cases/type/integer_test.rb
@@ -1,5 +1,6 @@
require "cases/helper"
require "active_model/type"
+require "active_support/core_ext/numeric/time"
module ActiveModel
module Type
@@ -19,7 +20,7 @@ module ActiveModel
test "random objects cast to nil" do
type = Type::Integer.new
- assert_nil type.cast([1,2])
+ assert_nil type.cast([1, 2])
assert_nil type.cast(1 => 2)
assert_nil type.cast(1..2)
end
@@ -32,7 +33,7 @@ module ActiveModel
test "casting nan and infinity" do
type = Type::Integer.new
assert_nil type.cast(::Float::NAN)
- assert_nil type.cast(1.0/0.0)
+ assert_nil type.cast(1.0 / 0.0)
end
test "casting booleans for database" do
@@ -41,6 +42,12 @@ module ActiveModel
assert_equal 0, type.serialize(false)
end
+ test "casting duration" do
+ type = Type::Integer.new
+ assert_equal 1800, type.cast(30.minutes)
+ assert_equal 7200, type.cast(2.hours)
+ end
+
test "changed?" do
type = Type::Integer.new
diff --git a/activemodel/test/cases/type/registry_test.rb b/activemodel/test/cases/type/registry_test.rb
index 2a48998a62..927b6d0307 100644
--- a/activemodel/test/cases/type/registry_test.rb
+++ b/activemodel/test/cases/type/registry_test.rb
@@ -2,38 +2,40 @@ require "cases/helper"
require "active_model/type"
module ActiveModel
- class RegistryTest < ActiveModel::TestCase
- test "a class can be registered for a symbol" do
- registry = Type::Registry.new
- registry.register(:foo, ::String)
- registry.register(:bar, ::Array)
+ module Type
+ class RegistryTest < ActiveModel::TestCase
+ test "a class can be registered for a symbol" do
+ registry = Type::Registry.new
+ registry.register(:foo, ::String)
+ registry.register(:bar, ::Array)
- assert_equal "", registry.lookup(:foo)
- assert_equal [], registry.lookup(:bar)
- end
-
- test "a block can be registered" do
- registry = Type::Registry.new
- registry.register(:foo) do |*args|
- [*args, "block for foo"]
+ assert_equal "", registry.lookup(:foo)
+ assert_equal [], registry.lookup(:bar)
end
- registry.register(:bar) do |*args|
- [*args, "block for bar"]
+
+ test "a block can be registered" do
+ registry = Type::Registry.new
+ registry.register(:foo) do |*args|
+ [*args, "block for foo"]
+ end
+ registry.register(:bar) do |*args|
+ [*args, "block for bar"]
+ end
+
+ assert_equal [:foo, 1, "block for foo"], registry.lookup(:foo, 1)
+ assert_equal [:foo, 2, "block for foo"], registry.lookup(:foo, 2)
+ assert_equal [:bar, 1, 2, 3, "block for bar"], registry.lookup(:bar, 1, 2, 3)
end
- assert_equal [:foo, 1, "block for foo"], registry.lookup(:foo, 1)
- assert_equal [:foo, 2, "block for foo"], registry.lookup(:foo, 2)
- assert_equal [:bar, 1, 2, 3, "block for bar"], registry.lookup(:bar, 1, 2, 3)
- end
+ test "a reasonable error is given when no type is found" do
+ registry = Type::Registry.new
- test "a reasonable error is given when no type is found" do
- registry = Type::Registry.new
+ e = assert_raises(ArgumentError) do
+ registry.lookup(:foo)
+ end
- e = assert_raises(ArgumentError) do
- registry.lookup(:foo)
+ assert_equal "Unknown type :foo", e.message
end
-
- assert_equal "Unknown type :foo", e.message
end
end
end
diff --git a/activemodel/test/cases/type/string_test.rb b/activemodel/test/cases/type/string_test.rb
index 7b25a1ef74..222083817e 100644
--- a/activemodel/test/cases/type/string_test.rb
+++ b/activemodel/test/cases/type/string_test.rb
@@ -2,26 +2,27 @@ require "cases/helper"
require "active_model/type"
module ActiveModel
- class StringTypeTest < ActiveModel::TestCase
- test "type casting" do
- type = Type::String.new
- assert_equal "t", type.cast(true)
- assert_equal "f", type.cast(false)
- assert_equal "123", type.cast(123)
- end
+ module Type
+ class StringTest < ActiveModel::TestCase
+ test "type casting" do
+ type = Type::String.new
+ assert_equal "t", type.cast(true)
+ assert_equal "f", type.cast(false)
+ assert_equal "123", type.cast(123)
+ end
- test "immutable strings are not duped coming out" do
- s = "foo"
- type = Type::ImmutableString.new
- assert_same s, type.cast(s)
- assert_same s, type.deserialize(s)
- end
+ test "cast strings are mutable" do
+ s = "foo"
+ type = Type::String.new
+ assert_equal false, type.cast(s).frozen?
+ end
- test "values are duped coming out" do
- s = "foo"
- type = Type::String.new
- assert_not_same s, type.cast(s)
- assert_not_same s, type.deserialize(s)
+ test "values are duped coming out" do
+ s = "foo"
+ type = Type::String.new
+ assert_not_same s, type.cast(s)
+ assert_not_same s, type.deserialize(s)
+ end
end
end
end
diff --git a/activemodel/test/cases/type/time_test.rb b/activemodel/test/cases/type/time_test.rb
new file mode 100644
index 0000000000..0cc4d33caa
--- /dev/null
+++ b/activemodel/test/cases/type/time_test.rb
@@ -0,0 +1,21 @@
+require "cases/helper"
+require "active_model/type"
+
+module ActiveModel
+ module Type
+ class TimeTest < ActiveModel::TestCase
+ def test_type_cast_time
+ type = Type::Time.new
+ assert_nil type.cast(nil)
+ assert_nil type.cast("")
+ assert_nil type.cast("ABC")
+
+ time_string = ::Time.now.utc.strftime("%T")
+ assert_equal time_string, type.cast(time_string).strftime("%T")
+
+ assert_equal ::Time.utc(2000, 1, 1, 16, 45, 54), type.cast("2015-06-13T19:45:54+03:00")
+ assert_equal ::Time.utc(1999, 12, 31, 21, 7, 8), type.cast("06:07:08+09:00")
+ end
+ end
+ end
+end
diff --git a/activemodel/test/cases/type/value_test.rb b/activemodel/test/cases/type/value_test.rb
new file mode 100644
index 0000000000..d8b3e7f164
--- /dev/null
+++ b/activemodel/test/cases/type/value_test.rb
@@ -0,0 +1,14 @@
+require "cases/helper"
+require "active_model/type"
+
+module ActiveModel
+ module Type
+ class ValueTest < ActiveModel::TestCase
+ def test_type_equality
+ assert_equal Type::Value.new, Type::Value.new
+ assert_not_equal Type::Value.new, Type::Integer.new
+ assert_not_equal Type::Value.new(precision: 1), Type::Value.new(precision: 2)
+ end
+ end
+ end
+end
diff --git a/activemodel/test/cases/types_test.rb b/activemodel/test/cases/types_test.rb
deleted file mode 100644
index c46775cb41..0000000000
--- a/activemodel/test/cases/types_test.rb
+++ /dev/null
@@ -1,125 +0,0 @@
-require "cases/helper"
-require "active_model/type"
-require "active_support/core_ext/numeric/time"
-
-module ActiveModel
- class TypesTest < ActiveModel::TestCase
- def test_type_cast_boolean
- type = Type::Boolean.new
- assert type.cast("").nil?
- assert type.cast(nil).nil?
-
- assert type.cast(true)
- assert type.cast(1)
- assert type.cast("1")
- assert type.cast("t")
- assert type.cast("T")
- assert type.cast("true")
- assert type.cast("TRUE")
- assert type.cast("on")
- assert type.cast("ON")
- assert type.cast(" ")
- assert type.cast("\u3000\r\n")
- assert type.cast("\u0000")
- assert type.cast("SOMETHING RANDOM")
-
- # explicitly check for false vs nil
- assert_equal false, type.cast(false)
- assert_equal false, type.cast(0)
- assert_equal false, type.cast("0")
- assert_equal false, type.cast("f")
- assert_equal false, type.cast("F")
- assert_equal false, type.cast("false")
- assert_equal false, type.cast("FALSE")
- assert_equal false, type.cast("off")
- assert_equal false, type.cast("OFF")
- end
-
- def test_type_cast_float
- type = Type::Float.new
- assert_equal 1.0, type.cast("1")
- end
-
- def test_changing_float
- type = Type::Float.new
-
- assert type.changed?(5.0, 5.0, "5wibble")
- assert_not type.changed?(5.0, 5.0, "5")
- assert_not type.changed?(5.0, 5.0, "5.0")
- assert_not type.changed?(nil, nil, nil)
- end
-
- def test_type_cast_binary
- type = Type::Binary.new
- assert_equal nil, type.cast(nil)
- assert_equal "1", type.cast("1")
- assert_equal 1, type.cast(1)
- end
-
- def test_type_cast_time
- type = Type::Time.new
- assert_equal nil, type.cast(nil)
- assert_equal nil, type.cast("")
- assert_equal nil, type.cast("ABC")
-
- time_string = Time.now.utc.strftime("%T")
- assert_equal time_string, type.cast(time_string).strftime("%T")
-
- assert_equal ::Time.utc(2000, 1, 1, 16, 45, 54), type.cast("2015-06-13T19:45:54+03:00")
- assert_equal ::Time.utc(1999, 12, 31, 21, 7, 8), type.cast("06:07:08+09:00")
- end
-
- def test_type_cast_datetime_and_timestamp
- type = Type::DateTime.new
- assert_equal nil, type.cast(nil)
- assert_equal nil, type.cast("")
- assert_equal nil, type.cast(" ")
- assert_equal nil, type.cast("ABC")
-
- datetime_string = Time.now.utc.strftime("%FT%T")
- assert_equal datetime_string, type.cast(datetime_string).strftime("%FT%T")
- end
-
- def test_type_cast_date
- type = Type::Date.new
- assert_equal nil, type.cast(nil)
- assert_equal nil, type.cast("")
- assert_equal nil, type.cast(" ")
- assert_equal nil, type.cast("ABC")
-
- date_string = Time.now.utc.strftime("%F")
- assert_equal date_string, type.cast(date_string).strftime("%F")
- end
-
- def test_type_cast_duration_to_integer
- type = Type::Integer.new
- assert_equal 1800, type.cast(30.minutes)
- assert_equal 7200, type.cast(2.hours)
- end
-
- def test_string_to_time_with_timezone
- ["UTC", "US/Eastern"].each do |zone|
- with_timezone_config default: zone do
- type = Type::DateTime.new
- assert_equal Time.utc(2013, 9, 4, 0, 0, 0), type.cast("Wed, 04 Sep 2013 03:00:00 EAT")
- end
- end
- end
-
- def test_type_equality
- assert_equal Type::Value.new, Type::Value.new
- assert_not_equal Type::Value.new, Type::Integer.new
- assert_not_equal Type::Value.new(precision: 1), Type::Value.new(precision: 2)
- end
-
- private
-
- def with_timezone_config(default:)
- old_zone_default = ::Time.zone_default
- ::Time.zone_default = ::Time.find_zone(default)
- yield
- ensure
- ::Time.zone_default = old_zone_default
- end
- end
-end
diff --git a/activemodel/test/cases/validations/absence_validation_test.rb b/activemodel/test/cases/validations/absence_validation_test.rb
index 3e48e591e9..833f694c5a 100644
--- a/activemodel/test/cases/validations/absence_validation_test.rb
+++ b/activemodel/test/cases/validations/absence_validation_test.rb
@@ -19,7 +19,7 @@ class AbsenceValidationTest < ActiveModel::TestCase
assert_equal ["must be blank"], t.errors[:title]
assert_equal ["must be blank"], t.errors[:content]
t.title = ""
- t.content = "something"
+ t.content = "something"
assert t.invalid?
assert_equal ["must be blank"], t.errors[:content]
assert_equal [], t.errors[:title]
diff --git a/activemodel/test/cases/validations/acceptance_validation_test.rb b/activemodel/test/cases/validations/acceptance_validation_test.rb
index 55ab213498..fbd994e914 100644
--- a/activemodel/test/cases/validations/acceptance_validation_test.rb
+++ b/activemodel/test/cases/validations/acceptance_validation_test.rb
@@ -19,7 +19,7 @@ class AcceptanceValidationTest < ActiveModel::TestCase
def test_terms_of_service_agreement
Topic.validates_acceptance_of(:terms_of_service)
- t = Topic.new("title" => "We should be confirmed","terms_of_service" => "")
+ t = Topic.new("title" => "We should be confirmed", "terms_of_service" => "")
assert t.invalid?
assert_equal ["must be accepted"], t.errors[:terms_of_service]
@@ -30,7 +30,7 @@ class AcceptanceValidationTest < ActiveModel::TestCase
def test_eula
Topic.validates_acceptance_of(:eula, message: "must be abided")
- t = Topic.new("title" => "We should be confirmed","eula" => "")
+ t = Topic.new("title" => "We should be confirmed", "eula" => "")
assert t.invalid?
assert_equal ["must be abided"], t.errors[:eula]
diff --git a/activemodel/test/cases/validations/conditional_validation_test.rb b/activemodel/test/cases/validations/conditional_validation_test.rb
index 5e81083b63..4881008017 100644
--- a/activemodel/test/cases/validations/conditional_validation_test.rb
+++ b/activemodel/test/cases/validations/conditional_validation_test.rb
@@ -106,7 +106,7 @@ class ConditionalValidationTest < ActiveModel::TestCase
def test_unless_validation_using_block_false
# When the block returns false
Topic.validates_length_of(:title, maximum: 5, too_long: "hoo %{count}",
- unless: Proc.new { |r| r.title != "uhohuhoh" } )
+ unless: Proc.new { |r| r.title != "uhohuhoh" })
t = Topic.new("title" => "uhohuhoh", "content" => "whatever")
assert t.invalid?
assert t.errors[:title].any?
diff --git a/activemodel/test/cases/validations/confirmation_validation_test.rb b/activemodel/test/cases/validations/confirmation_validation_test.rb
index b88e1c4ca4..7ddf3ad273 100644
--- a/activemodel/test/cases/validations/confirmation_validation_test.rb
+++ b/activemodel/test/cases/validations/confirmation_validation_test.rb
@@ -28,7 +28,7 @@ class ConfirmationValidationTest < ActiveModel::TestCase
def test_title_confirmation
Topic.validates_confirmation_of(:title)
- t = Topic.new("title" => "We should be confirmed","title_confirmation" => "")
+ t = Topic.new("title" => "We should be confirmed", "title_confirmation" => "")
assert t.invalid?
t.title_confirmation = "We should be confirmed"
@@ -61,7 +61,7 @@ class ConfirmationValidationTest < ActiveModel::TestCase
Topic.validates_confirmation_of(:title)
- t = Topic.new("title" => "We should be confirmed","title_confirmation" => "")
+ t = Topic.new("title" => "We should be confirmed", "title_confirmation" => "")
assert t.invalid?
assert_equal ["doesn't match Test Title"], t.errors[:title_confirmation]
ensure
diff --git a/activemodel/test/cases/validations/length_validation_test.rb b/activemodel/test/cases/validations/length_validation_test.rb
index 62d9eaa346..95ee87b401 100644
--- a/activemodel/test/cases/validations/length_validation_test.rb
+++ b/activemodel/test/cases/validations/length_validation_test.rb
@@ -9,7 +9,7 @@ class LengthValidationTest < ActiveModel::TestCase
end
def test_validates_length_of_with_allow_nil
- Topic.validates_length_of( :title, is: 5, allow_nil: true )
+ Topic.validates_length_of(:title, is: 5, allow_nil: true)
assert Topic.new("title" => "ab").invalid?
assert Topic.new("title" => "").invalid?
@@ -18,7 +18,7 @@ class LengthValidationTest < ActiveModel::TestCase
end
def test_validates_length_of_with_allow_blank
- Topic.validates_length_of( :title, is: 5, allow_blank: true )
+ Topic.validates_length_of(:title, is: 5, allow_blank: true)
assert Topic.new("title" => "ab").invalid?
assert Topic.new("title" => "").valid?
@@ -104,7 +104,7 @@ class LengthValidationTest < ActiveModel::TestCase
assert_equal ["is too short (minimum is 3 characters)"], t.errors[:content]
t.title = "abe"
- t.content = "mad"
+ t.content = "mad"
assert t.valid?
end
@@ -161,8 +161,8 @@ class LengthValidationTest < ActiveModel::TestCase
end
def test_validates_length_of_using_bignum
- bigmin = 2 ** 30
- bigmax = 2 ** 32
+ bigmin = 2**30
+ bigmax = 2**32
bigrange = bigmin...bigmax
assert_nothing_raised do
Topic.validates_length_of :title, is: bigmin + 5
@@ -183,7 +183,7 @@ class LengthValidationTest < ActiveModel::TestCase
end
def test_validates_length_of_custom_errors_for_minimum_with_message
- Topic.validates_length_of( :title, minimum: 5, message: "boo %{count}" )
+ Topic.validates_length_of(:title, minimum: 5, message: "boo %{count}")
t = Topic.new("title" => "uhoh", "content" => "whatever")
assert t.invalid?
assert t.errors[:title].any?
@@ -191,7 +191,7 @@ class LengthValidationTest < ActiveModel::TestCase
end
def test_validates_length_of_custom_errors_for_minimum_with_too_short
- Topic.validates_length_of( :title, minimum: 5, too_short: "hoo %{count}" )
+ Topic.validates_length_of(:title, minimum: 5, too_short: "hoo %{count}")
t = Topic.new("title" => "uhoh", "content" => "whatever")
assert t.invalid?
assert t.errors[:title].any?
@@ -199,7 +199,7 @@ class LengthValidationTest < ActiveModel::TestCase
end
def test_validates_length_of_custom_errors_for_maximum_with_message
- Topic.validates_length_of( :title, maximum: 5, message: "boo %{count}" )
+ Topic.validates_length_of(:title, maximum: 5, message: "boo %{count}")
t = Topic.new("title" => "uhohuhoh", "content" => "whatever")
assert t.invalid?
assert t.errors[:title].any?
@@ -220,7 +220,7 @@ class LengthValidationTest < ActiveModel::TestCase
end
def test_validates_length_of_custom_errors_for_maximum_with_too_long
- Topic.validates_length_of( :title, maximum: 5, too_long: "hoo %{count}" )
+ Topic.validates_length_of(:title, maximum: 5, too_long: "hoo %{count}")
t = Topic.new("title" => "uhohuhoh", "content" => "whatever")
assert t.invalid?
assert t.errors[:title].any?
@@ -242,7 +242,7 @@ class LengthValidationTest < ActiveModel::TestCase
end
def test_validates_length_of_custom_errors_for_is_with_message
- Topic.validates_length_of( :title, is: 5, message: "boo %{count}" )
+ Topic.validates_length_of(:title, is: 5, message: "boo %{count}")
t = Topic.new("title" => "uhohuhoh", "content" => "whatever")
assert t.invalid?
assert t.errors[:title].any?
@@ -250,7 +250,7 @@ class LengthValidationTest < ActiveModel::TestCase
end
def test_validates_length_of_custom_errors_for_is_with_wrong_length
- Topic.validates_length_of( :title, is: 5, wrong_length: "hoo %{count}" )
+ Topic.validates_length_of(:title, is: 5, wrong_length: "hoo %{count}")
t = Topic.new("title" => "uhohuhoh", "content" => "whatever")
assert t.invalid?
assert t.errors[:title].any?
@@ -289,7 +289,7 @@ class LengthValidationTest < ActiveModel::TestCase
assert_equal ["is too short (minimum is 3 characters)"], t.errors[:title]
assert_equal ["is too long (maximum is 5 characters)"], t.errors[:content]
t.title = "一二三"
- t.content = "12三"
+ t.content = "12三"
assert t.valid?
end
@@ -403,7 +403,7 @@ class LengthValidationTest < ActiveModel::TestCase
def test_validates_with_diff_in_option
Topic.validates_length_of(:title, is: 5)
- Topic.validates_length_of(:title, is: 5, if: Proc.new { false } )
+ Topic.validates_length_of(:title, is: 5, if: Proc.new { false })
assert Topic.new("title" => "david").valid?
assert Topic.new("title" => "david2").invalid?
diff --git a/activemodel/test/cases/validations/numericality_validation_test.rb b/activemodel/test/cases/validations/numericality_validation_test.rb
index fefab29f15..a1be2de578 100644
--- a/activemodel/test/cases/validations/numericality_validation_test.rb
+++ b/activemodel/test/cases/validations/numericality_validation_test.rb
@@ -20,7 +20,7 @@ class NumericalityValidationTest < ActiveModel::TestCase
INTEGERS = [0, 10, -10] + INTEGER_STRINGS
BIGDECIMAL = BIGDECIMAL_STRINGS.collect! { |bd| BigDecimal.new(bd) }
JUNK = ["not a number", "42 not a number", "0xdeadbeef", "0xinvalidhex", "0Xdeadbeef", "00-1", "--3", "+-3", "+3-1", "-+019.0", "12.12.13.12", "123\nnot a number"]
- INFINITY = [1.0/0.0]
+ INFINITY = [1.0 / 0.0]
def test_default_validates_numericality_of
Topic.validates_numericality_of :approved
@@ -82,7 +82,7 @@ class NumericalityValidationTest < ActiveModel::TestCase
Topic.validates_numericality_of :approved, greater_than: BigDecimal.new("97.18")
invalid!([-97.18, BigDecimal.new("97.18"), BigDecimal("-97.18")], "must be greater than 97.18")
- valid!([97.18, 98, BigDecimal.new("98")]) # Notice the 97.18 as a float is greater than 97.18 as a BigDecimal due to floating point precision
+ valid!([97.19, 98, BigDecimal.new("98"), BigDecimal.new("97.19")])
end
def test_validates_numericality_with_greater_than_using_string_value
@@ -123,7 +123,7 @@ class NumericalityValidationTest < ActiveModel::TestCase
def test_validates_numericality_with_equal_to_using_differing_numeric_types
Topic.validates_numericality_of :approved, equal_to: BigDecimal.new("97.18")
- invalid!([-97.18, 97.18], "must be equal to 97.18")
+ invalid!([-97.18], "must be equal to 97.18")
valid!([BigDecimal.new("97.18")])
end
@@ -165,7 +165,7 @@ class NumericalityValidationTest < ActiveModel::TestCase
def test_validates_numericality_with_less_than_or_equal_to_using_differing_numeric_types
Topic.validates_numericality_of :approved, less_than_or_equal_to: BigDecimal.new("97.18")
- invalid!([97.18, 98], "must be less than or equal to 97.18")
+ invalid!([97.19, 98], "must be less than or equal to 97.18")
valid!([-97.18, BigDecimal.new("-97.18"), BigDecimal.new("97.18")])
end
diff --git a/activemodel/test/cases/validations/presence_validation_test.rb b/activemodel/test/cases/validations/presence_validation_test.rb
index feda817698..642dd0f144 100644
--- a/activemodel/test/cases/validations/presence_validation_test.rb
+++ b/activemodel/test/cases/validations/presence_validation_test.rb
@@ -20,7 +20,7 @@ class PresenceValidationTest < ActiveModel::TestCase
assert_equal ["can't be blank"], t.errors[:content]
t.title = "something"
- t.content = " "
+ t.content = " "
assert t.invalid?
assert_equal ["can't be blank"], t.errors[:content]
diff --git a/activemodel/test/validators/email_validator.rb b/activemodel/test/validators/email_validator.rb
index 43011b277b..9b74d83b37 100644
--- a/activemodel/test/validators/email_validator.rb
+++ b/activemodel/test/validators/email_validator.rb
@@ -1,4 +1,3 @@
-require "active_support/core_ext/regexp"
class EmailValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index 15b49e0a0b..034e45b769 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,13 +1,174 @@
-* Fixed support for case insensitive comparisons of `text` columns in
- PostgreSQL.
+* Notifications see frozen SQL string.
- *Edho Arief*
+ Fixes #23774.
+
+ *Richard Monette*
+
+* RuntimeErrors are no longer translated to `ActiveRecord::StatementInvalid`.
+
+ *Richard Monette*
+
+* Change the schema cache format to use YAML instead of Marshal.
+
+ *Kir Shatrov*
+
+* Support index length and order options using both string and symbol
+ column names.
+
+ Fixes #27243.
+
+ *Ryuta Kamizono*
+
+* Raise `ActiveRecord::RangeError` when values that executed are out of range.
+
+ *Ryuta Kamizono*
+
+* Raise `ActiveRecord::NotNullViolation` when a record cannot be inserted
+ or updated because it would violate a not null constraint.
+
+ *Ryuta Kamizono*
+
+* Emulate db trigger behaviour for after_commit :destroy, :update.
+
+ Race conditions can occur when an ActiveRecord is destroyed
+ twice or destroyed and updated. The callbacks should only be
+ triggered once, similar to a SQL database trigger.
+
+ *Stefan Budeanu*
-* Made ActiveRecord consistently use `ActiveRecord::Type` (not `ActiveModel::Type`)
+* Moved `DecimalWithoutScale`, `Text`, and `UnsignedInteger` from Active Model to Active Record.
*Iain Beeston*
-* Serialize JSON attribute value `nil` as SQL `NULL`, not JSON `null`
+* Fix `write_attribute` method to check whether an attribute is aliased or not, and
+ use the aliased attribute name if needed.
+
+ *Prathamesh Sonpatki*
+
+* Fix `read_attribute` method to check whether an attribute is aliased or not, and
+ use the aliased attribute name if needed.
+
+ Fixes #26417.
+
+ *Prathamesh Sonpatki*
+
+* PostgreSQL & MySQL: Use big integer as primary key type for new tables.
+
+ *Jon McCartie*, *Pavel Pravosud*
+
+* Change the type argument of `ActiveRecord::Base#attribute` to be optional.
+ The default is now `ActiveRecord::Type::Value.new`, which provides no type
+ casting behavior.
+
+ *Sean Griffin*
+
+* Fix that unsigned with zerofill is treated as signed.
+
+ Fixes #27125.
+
+ *Ryuta Kamizono*
+
+* Fix the uniqueness validation scope with a polymorphic association.
+
+ *Sergey Alekseev*
+
+* Raise `ActiveRecord::RecordNotFound` from collection `*_ids` setters
+ for unknown IDs with a better error message.
+
+ Changes the collection `*_ids` setters to cast provided IDs the data
+ type of the primary key set in the association, not the model
+ primary key.
+
+ *Dominic Cleal*
+
+* For PostgreSQL >= 9.4 use `pgcrypto`'s `gen_random_uuid()` instead of
+ `uuid-ossp`'s UUID generation function.
+
+ *Yuji Yaginuma*, *Yaw Boakye*
+
+* Introduce `Model#reload_<association>` to bring back the behavior
+ of `Article.category(true)` where `category` is a singular
+ association.
+
+ The force reloading of the association reader was deprecated
+ in #20888. Unfortunately the suggested alternative of
+ `article.reload.category` does not expose the same behavior.
+
+ This patch adds a reader method with the prefix `reload_` for
+ singular associations. This method has the same semantics as
+ passing true to the association reader used to have.
+
+ *Yves Senn*
+
+* Make sure eager loading `ActiveRecord::Associations` also loads
+ constants defined in `ActiveRecord::Associations::Preloader`.
+
+ *Yves Senn*
+
+* Allow `ActionController::Parameters`-like objects to be passed as
+ values for Postgres HStore columns.
+
+ Fixes #26904.
+
+ *Jon Moss*
+
+* Added `stat` method to `ActiveRecord::ConnectionAdapters::ConnectionPool`.
+
+ Example:
+
+ ActiveRecord::Base.connection_pool.stat # =>
+ { size: 15, connections: 1, busy: 1, dead: 0, idle: 0, waiting: 0, checkout_timeout: 5 }
+
+ *Pavel Evstigneev*
+
+* Avoid `unscope(:order)` when `limit_value` is presented for `count`
+ and `exists?`.
+
+ If `limit_value` is presented, records fetching order is very important
+ for performance. We should not unscope the order in the case.
+
+ *Ryuta Kamizono*
+
+* Fix an Active Record `DateTime` field `NoMethodError` caused by incomplete
+ datetime.
+
+ Fixes #24195.
+
+ *Sen Zhang*
+
+* Allow `slice` to take an array of methods(without the need for splatting).
+
+ *Cohen Carlisle*
+
+* Improved partial writes with HABTM and has many through associations
+ to fire database query only if relation has been changed.
+
+ Fixes #19663.
+
+ *Mehmet Emin İNAÇ*
+
+* Deprecate passing arguments and block at the same time to
+ `ActiveRecord::QueryMethods#select`.
+
+ *Prathamesh Sonpatki*
+
+* Optimistic locking: Added ability to update `locking_column` value.
+ Ignore optimistic locking if trying to update with new `locking_column` value.
+
+ *bogdanvlviv*
+
+* Fixed: Optimistic locking does not work well with `null` in the database.
+
+ Fixes #26024.
+
+ *bogdanvlviv*
+
+* Fixed support for case insensitive comparisons of `text` columns in
+ PostgreSQL.
+
+ *Edho Arief*
+
+* Serialize JSON attribute value `nil` as SQL `NULL`, not JSON `null`.
*Trung Duc Tran*
@@ -111,7 +272,7 @@
successfully rolled back when the column was given and invalid column
type.
- Fixes #26087
+ Fixes #26087.
*Travis O'Neill*
@@ -141,7 +302,7 @@
*Takeshi Akima*
* Virtual attributes will no longer raise when read on models loaded from the
- database
+ database.
*Sean Griffin*
diff --git a/activerecord/Rakefile b/activerecord/Rakefile
index e077d345d6..ec28df8fea 100644
--- a/activerecord/Rakefile
+++ b/activerecord/Rakefile
@@ -50,7 +50,7 @@ end
Rake::TestTask.new(adapter => "#{adapter}:env") { |t|
adapter_short = adapter == "db2" ? adapter : adapter[/^[a-z0-9]+/]
t.libs << "test"
- t.test_files = (Dir.glob( "test/cases/**/*_test.rb" ).reject {
+ t.test_files = (Dir.glob("test/cases/**/*_test.rb").reject {
|x| x.include?("/adapters/")
} + Dir.glob("test/cases/adapters/#{adapter_short}/**/*_test.rb"))
@@ -66,7 +66,7 @@ end
(Dir["test/cases/**/*_test.rb"].reject {
|x| x.include?("/adapters/")
} + Dir["test/cases/adapters/#{adapter_short}/**/*_test.rb"]).all? do |file|
- sh(Gem.ruby, "-w" ,"-Itest", file)
+ sh(Gem.ruby, "-w" , "-Itest", file)
end || raise("Failures")
end
end
diff --git a/activerecord/bin/test b/activerecord/bin/test
index 23add35d45..3a9547e5c1 100755
--- a/activerecord/bin/test
+++ b/activerecord/bin/test
@@ -17,5 +17,3 @@ module Minitest
end
Minitest.extensions.unshift "active_record"
-
-exit Minitest.run(ARGV)
diff --git a/activerecord/examples/performance.rb b/activerecord/examples/performance.rb
index f2fe8875b9..3257dd4ad7 100644
--- a/activerecord/examples/performance.rb
+++ b/activerecord/examples/performance.rb
@@ -2,7 +2,7 @@ require "active_record"
require "benchmark/ips"
TIME = (ENV["BENCHMARK_TIME"] || 20).to_i
-RECORDS = (ENV["BENCHMARK_RECORDS"] || TIME*1000).to_i
+RECORDS = (ENV["BENCHMARK_RECORDS"] || TIME * 1000).to_i
conn = { adapter: "sqlite3", database: ":memory:" }
@@ -42,7 +42,7 @@ class Exhibit < ActiveRecord::Base
def self.feel(exhibits) exhibits.each(&:feel) end
end
-def progress_bar(int); print "." if (int%100).zero? ; end
+def progress_bar(int); print "." if (int % 100).zero? ; end
puts "Generating data..."
diff --git a/activerecord/lib/active_record/aggregations.rb b/activerecord/lib/active_record/aggregations.rb
index 5ca8fe576e..10cbd5429c 100644
--- a/activerecord/lib/active_record/aggregations.rb
+++ b/activerecord/lib/active_record/aggregations.rb
@@ -15,11 +15,11 @@ module ActiveRecord
private
- def clear_aggregation_cache # :nodoc:
+ def clear_aggregation_cache
@aggregation_cache.clear if persisted?
end
- def init_internals # :nodoc:
+ def init_internals
@aggregation_cache = {}
super
end
@@ -206,7 +206,7 @@ module ActiveRecord
# or a Proc that is called when a new value is assigned to the value object. The converter is
# passed the single value that is used in the assignment and is only called if the new value is
# not an instance of <tt>:class_name</tt>. If <tt>:allow_nil</tt> is set to true, the converter
- # can return nil to skip the assignment.
+ # can return +nil+ to skip the assignment.
#
# Option examples:
# composed_of :temperature, mapping: %w(reading celsius)
diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb
index b5f1f1980a..7eb008fc27 100644
--- a/activerecord/lib/active_record/associations.rb
+++ b/activerecord/lib/active_record/associations.rb
@@ -224,6 +224,11 @@ module ActiveRecord
autoload :AliasTracker
end
+ def self.eager_load!
+ super
+ Preloader.eager_load!
+ end
+
# Returns the association instance for the given name, instantiating it if it doesn't already exist
def association(name) #:nodoc:
association = association_instance_get(name)
@@ -255,16 +260,16 @@ module ActiveRecord
private
# Clears out the association cache.
- def clear_association_cache # :nodoc:
+ def clear_association_cache
@association_cache.clear if persisted?
end
- def init_internals # :nodoc:
+ def init_internals
@association_cache = {}
super
end
- # Returns the specified association instance if it exists, nil otherwise.
+ # Returns the specified association instance if it exists, +nil+ otherwise.
def association_instance_get(name)
@association_cache[name]
end
diff --git a/activerecord/lib/active_record/associations/association.rb b/activerecord/lib/active_record/associations/association.rb
index f506614591..84d0493a60 100644
--- a/activerecord/lib/active_record/associations/association.rb
+++ b/activerecord/lib/active_record/associations/association.rb
@@ -112,6 +112,15 @@ module ActiveRecord
record
end
+ # Remove the inverse association, if possible
+ def remove_inverse_instance(record)
+ if invertible_for?(record)
+ inverse = record.association(inverse_reflection_for(record).name)
+ inverse.target = nil
+ inverse.inversed = false
+ end
+ end
+
# Returns the class of the target. belongs_to polymorphic overrides this to look at the
# polymorphic_type field on the owner.
def klass
@@ -166,7 +175,7 @@ module ActiveRecord
def initialize_attributes(record, except_from_scope_attributes = nil) #:nodoc:
except_from_scope_attributes ||= {}
skip_assign = [reflection.foreign_key, reflection.type].compact
- assigned_keys = record.changed
+ assigned_keys = record.changed_attribute_names_to_save
assigned_keys += except_from_scope_attributes.keys.map(&:to_s)
attributes = create_scope.except(*(assigned_keys - skip_assign))
record.assign_attributes(attributes)
@@ -254,7 +263,7 @@ module ActiveRecord
# so that when stale_state is different from the value stored on the last find_target,
# the target is stale.
#
- # This is only relevant to certain associations, which is why it returns nil by default.
+ # This is only relevant to certain associations, which is why it returns +nil+ by default.
def stale_state
end
diff --git a/activerecord/lib/active_record/associations/association_scope.rb b/activerecord/lib/active_record/associations/association_scope.rb
index 12f8c1ccd4..c6d204d3c2 100644
--- a/activerecord/lib/active_record/associations/association_scope.rb
+++ b/activerecord/lib/active_record/associations/association_scope.rb
@@ -49,6 +49,8 @@ module ActiveRecord
binds
end
+ # TODO Change this to private once we've dropped Ruby 2.2 support.
+ # Workaround for Ruby 2.2 "private attribute?" warning.
protected
attr_reader :value_transformation
diff --git a/activerecord/lib/active_record/associations/builder/belongs_to.rb b/activerecord/lib/active_record/associations/builder/belongs_to.rb
index 3121e70a04..a1609ab0fb 100644
--- a/activerecord/lib/active_record/associations/builder/belongs_to.rb
+++ b/activerecord/lib/active_record/associations/builder/belongs_to.rb
@@ -35,17 +35,17 @@ module ActiveRecord::Associations::Builder # :nodoc:
@_after_create_counter_called = false
elsif (@_after_replace_counter_called ||= false)
@_after_replace_counter_called = false
- elsif attribute_changed?(foreign_key) && !new_record?
+ elsif saved_change_to_attribute?(foreign_key) && !new_record?
if reflection.polymorphic?
- model = attribute(reflection.foreign_type).try(:constantize)
- model_was = attribute_was(reflection.foreign_type).try(:constantize)
+ model = attribute_in_database(reflection.foreign_type).try(:constantize)
+ model_was = attribute_before_last_save(reflection.foreign_type).try(:constantize)
else
model = reflection.klass
model_was = reflection.klass
end
- foreign_key_was = attribute_was foreign_key
- foreign_key = attribute foreign_key
+ foreign_key_was = attribute_before_last_save foreign_key
+ foreign_key = attribute_in_database foreign_key
if foreign_key && model.respond_to?(:increment_counter)
model.increment_counter(cache_column, foreign_key)
@@ -70,14 +70,16 @@ module ActiveRecord::Associations::Builder # :nodoc:
klass.attr_readonly cache_column if klass && klass.respond_to?(:attr_readonly)
end
- def self.touch_record(o, foreign_key, name, touch, touch_method) # :nodoc:
- old_foreign_id = o.changed_attributes[foreign_key]
+ def self.touch_record(o, changes, foreign_key, name, touch, touch_method) # :nodoc:
+ old_foreign_id = changes[foreign_key] && changes[foreign_key].first
if old_foreign_id
association = o.association(name)
reflection = association.reflection
if reflection.polymorphic?
- klass = o.public_send("#{reflection.foreign_type}_was").constantize
+ foreign_type = reflection.foreign_type
+ klass = changes[foreign_type] && changes[foreign_type].first || o.public_send(foreign_type)
+ klass = klass.constantize
else
klass = association.klass
end
@@ -107,13 +109,13 @@ module ActiveRecord::Associations::Builder # :nodoc:
n = reflection.name
touch = reflection.options[:touch]
- callback = lambda { |record|
- BelongsTo.touch_record(record, foreign_key, n, touch, belongs_to_touch_method)
- }
+ callback = lambda { |changes_method| lambda { |record|
+ BelongsTo.touch_record(record, record.send(changes_method), foreign_key, n, touch, belongs_to_touch_method)
+ }}
- model.after_save callback, if: :changed?
- model.after_touch callback
- model.after_destroy callback
+ model.after_save callback.(:saved_changes), if: :saved_changes?
+ model.after_touch callback.(:changes_to_save)
+ model.after_destroy callback.(:changes_to_save)
end
def self.add_destroy_callbacks(model, reflection)
diff --git a/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb b/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb
index 047292b2bd..6b71826431 100644
--- a/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb
+++ b/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb
@@ -28,7 +28,7 @@ module ActiveRecord::Associations::Builder # :nodoc:
class_name = options.fetch(:class_name) {
name.to_s.camelize.singularize
}
- KnownClass.new lhs_class, class_name
+ KnownClass.new lhs_class, class_name.to_s
end
end
end
@@ -78,9 +78,9 @@ module ActiveRecord::Associations::Builder # :nodoc:
private
- def self.suppress_composite_primary_key(pk)
- pk unless pk.is_a?(Array)
- end
+ def self.suppress_composite_primary_key(pk)
+ pk unless pk.is_a?(Array)
+ end
}
join_model.name = "HABTM_#{association_name.to_s.camelize}"
diff --git a/activerecord/lib/active_record/associations/builder/singular_association.rb b/activerecord/lib/active_record/associations/builder/singular_association.rb
index bb96202a22..7732b63af6 100644
--- a/activerecord/lib/active_record/associations/builder/singular_association.rb
+++ b/activerecord/lib/active_record/associations/builder/singular_association.rb
@@ -8,7 +8,16 @@ module ActiveRecord::Associations::Builder # :nodoc:
def self.define_accessors(model, reflection)
super
- define_constructors(model.generated_association_methods, reflection.name) if reflection.constructable?
+ mixin = model.generated_association_methods
+ name = reflection.name
+
+ define_constructors(mixin, name) if reflection.constructable?
+
+ mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
+ def reload_#{name}
+ association(:#{name}).force_reload_reader
+ end
+ CODE
end
# Defines the (build|create)_association methods for belongs_to or has_one association
diff --git a/activerecord/lib/active_record/associations/collection_association.rb b/activerecord/lib/active_record/associations/collection_association.rb
index 278c95e27b..13f77c7d4d 100644
--- a/activerecord/lib/active_record/associations/collection_association.rb
+++ b/activerecord/lib/active_record/associations/collection_association.rb
@@ -68,13 +68,17 @@ module ActiveRecord
# Implements the ids writer method, e.g. foo.item_ids= for Foo.has_many :items
def ids_writer(ids)
- pk_type = reflection.primary_key_type
+ pk_type = reflection.association_primary_key_type
ids = Array(ids).reject(&:blank?)
ids.map! { |i| pk_type.cast(i) }
records = klass.where(reflection.association_primary_key => ids).index_by do |r|
r.send(reflection.association_primary_key)
- end.values_at(*ids)
- replace(records)
+ end.values_at(*ids).compact
+ if records.size != ids.size
+ klass.all.raise_record_not_found_exception!(ids, records.size, ids.size, reflection.association_primary_key)
+ else
+ replace(records)
+ end
end
def reset
@@ -192,11 +196,8 @@ module ActiveRecord
# +delete_records+. They are in any case removed from the collection.
def delete(*records)
return if records.empty?
- _options = records.extract_options!
- dependent = _options[:dependent] || options[:dependent]
-
records = find(records) if records.any? { |record| record.kind_of?(Integer) || record.kind_of?(String) }
- delete_or_destroy(records, dependent)
+ delete_or_destroy(records, options[:dependent])
end
# Deletes the +records+ and removes them from this association calling
@@ -222,11 +223,7 @@ module ActiveRecord
# +count_records+, which is a method descendants have to provide.
def size
if !find_target? || loaded?
- if association_scope.distinct_value
- target.uniq.size
- else
- target.size
- end
+ target.size
elsif !association_scope.group_values.empty?
load_target.size
elsif !association_scope.distinct_value && target.is_a?(Array)
@@ -253,13 +250,6 @@ module ActiveRecord
end
end
- def distinct
- seen = {}
- load_target.find_all do |record|
- seen[record.id] = true unless seen.key?(record.id)
- end
- end
-
# Replace this collection with +other_array+. This will perform a diff
# and delete/add only records that have changed.
def replace(other_array)
@@ -309,12 +299,23 @@ module ActiveRecord
def replace_on_target(record, index, skip_callbacks)
callback(:before_add, record) unless skip_callbacks
- yield(record) if block_given?
+ begin
+ if index
+ record_was = target[index]
+ target[index] = record
+ else
+ target << record
+ end
- if index
- @target[index] = record
- else
- append_record(record)
+ yield(record) if block_given?
+ rescue
+ if index
+ target[index] = record_was
+ else
+ target.delete(record)
+ end
+
+ raise
end
callback(:after_add, record) unless skip_callbacks
@@ -375,7 +376,7 @@ module ActiveRecord
persisted.map! do |record|
if mem_record = memory.delete(record)
- ((record.attribute_names & mem_record.attribute_names) - mem_record.changes.keys).each do |name|
+ ((record.attribute_names & mem_record.attribute_names) - mem_record.changed_attribute_names_to_save).each do |name|
mem_record[name] = record[name]
end
@@ -435,8 +436,9 @@ module ActiveRecord
records.each { |record| callback(:after_remove, record) }
end
- # Delete the given records from the association, using one of the methods :destroy,
- # :delete_all or :nullify (or nil, in which case a default is used).
+ # Delete the given records from the association,
+ # using one of the methods +:destroy+, +:delete_all+
+ # or +:nullify+ (or +nil+, in which case a default is used).
def delete_records(records, method)
raise NotImplementedError
end
@@ -511,10 +513,6 @@ module ActiveRecord
load_target.select { |r| ids.include?(r.id.to_s) }
end
end
-
- def append_record(record)
- @target << record unless @target.include?(record)
- end
end
end
end
diff --git a/activerecord/lib/active_record/associations/collection_proxy.rb b/activerecord/lib/active_record/associations/collection_proxy.rb
index dda240585e..0d84805b4d 100644
--- a/activerecord/lib/active_record/associations/collection_proxy.rb
+++ b/activerecord/lib/active_record/associations/collection_proxy.rb
@@ -106,12 +106,6 @@ module ActiveRecord
# # #<Pet id: 2, name: "Spook", person_id: 1>,
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
# # ]
- #
- # person.pets.select(:name) { |pet| pet.name =~ /oo/ }
- # # => [
- # # #<Pet id: 2, name: "Spook">,
- # # #<Pet id: 3, name: "Choo-Choo">
- # # ]
# Finds an object in the collection responding to the +id+. Uses the same
# rules as ActiveRecord::Base.find. Returns ActiveRecord::RecordNotFound
@@ -724,6 +718,12 @@ module ActiveRecord
@association.destroy(*records)
end
+ ##
+ # :method: distinct
+ #
+ # :call-seq:
+ # distinct(value = true)
+ #
# Specifies whether the records should be unique or not.
#
# class Person < ActiveRecord::Base
@@ -738,10 +738,17 @@ module ActiveRecord
#
# person.pets.select(:name).distinct
# # => [#<Pet name: "Fancy-Fancy">]
- def distinct
- @association.distinct
+ #
+ # person.pets.select(:name).distinct.distinct(false)
+ # # => [
+ # # #<Pet name: "Fancy-Fancy">,
+ # # #<Pet name: "Fancy-Fancy">
+ # # ]
+
+ #--
+ def uniq
+ load_target.uniq
end
- alias uniq distinct
def calculate(operation, column_name)
null_scope? ? scope.calculate(operation, column_name) : super
@@ -1119,7 +1126,7 @@ module ActiveRecord
self
end
- protected
+ private
def find_nth_with_limit(index, limit)
load_target if find_from_target?
@@ -1131,8 +1138,6 @@ module ActiveRecord
super
end
- private
-
def null_scope?
@association.null_scope?
end
diff --git a/activerecord/lib/active_record/associations/has_many_association.rb b/activerecord/lib/active_record/associations/has_many_association.rb
index d1d0cc4c49..742cd25509 100644
--- a/activerecord/lib/active_record/associations/has_many_association.rb
+++ b/activerecord/lib/active_record/associations/has_many_association.rb
@@ -72,7 +72,7 @@ module ActiveRecord
# the loaded flag is set to true as well.
def count_records
count = if reflection.has_cached_counter?
- owner._read_attribute reflection.counter_cache_column
+ owner._read_attribute(reflection.counter_cache_column).to_i
else
scope.count
end
diff --git a/activerecord/lib/active_record/associations/has_many_through_association.rb b/activerecord/lib/active_record/associations/has_many_through_association.rb
index 1f264d325a..0c0aefe3b9 100644
--- a/activerecord/lib/active_record/associations/has_many_through_association.rb
+++ b/activerecord/lib/active_record/associations/has_many_through_association.rb
@@ -86,7 +86,10 @@ module ActiveRecord
end
def save_through_record(record)
- build_through_record(record).save!
+ association = build_through_record(record)
+ if association.changed?
+ association.save!
+ end
ensure
@through_records.delete(record.object_id)
end
@@ -203,10 +206,6 @@ module ActiveRecord
def invertible_for?(record)
false
end
-
- def append_record(record)
- @target << record
- end
end
end
end
diff --git a/activerecord/lib/active_record/associations/has_one_association.rb b/activerecord/lib/active_record/associations/has_one_association.rb
index 5ea9577301..21bd668dff 100644
--- a/activerecord/lib/active_record/associations/has_one_association.rb
+++ b/activerecord/lib/active_record/associations/has_one_association.rb
@@ -35,7 +35,7 @@ module ActiveRecord
return target unless target || record
assigning_another_record = target != record
- if assigning_another_record || record.changed?
+ if assigning_another_record || record.has_changes_to_save?
save &&= owner.persisted?
transaction_if(save) do
@@ -86,8 +86,9 @@ module ActiveRecord
target.delete
when :destroy
target.destroy
- else
+ else
nullify_owner_attributes(target)
+ remove_inverse_instance(target)
if target.persisted? && owner.persisted? && !target.save
set_owner_attributes(target)
diff --git a/activerecord/lib/active_record/associations/join_dependency.rb b/activerecord/lib/active_record/associations/join_dependency.rb
index c26c469c1e..4cd1e64c3d 100644
--- a/activerecord/lib/active_record/associations/join_dependency.rb
+++ b/activerecord/lib/active_record/associations/join_dependency.rb
@@ -7,12 +7,12 @@ module ActiveRecord
class Aliases # :nodoc:
def initialize(tables)
@tables = tables
- @alias_cache = tables.each_with_object({}) { |table,h|
- h[table.node] = table.columns.each_with_object({}) { |column,i|
+ @alias_cache = tables.each_with_object({}) { |table, h|
+ h[table.node] = table.columns.each_with_object({}) { |column, i|
i[column.name] = column.alias
}
}
- @name_and_alias_cache = tables.each_with_object({}) { |table,h|
+ @name_and_alias_cache = tables.each_with_object({}) { |table, h|
h[table.node] = table.columns.map { |column|
[column.name, column.alias]
}
@@ -62,7 +62,7 @@ module ActiveRecord
walk_tree assoc, hash
end
when Hash
- associations.each do |k,v|
+ associations.each do |k, v|
cache = hash[k] ||= {}
walk_tree v, cache
end
@@ -126,8 +126,8 @@ module ActiveRecord
end
def aliases
- Aliases.new join_root.each_with_index.map { |join_part,i|
- columns = join_part.column_names.each_with_index.map { |column_name,j|
+ Aliases.new join_root.each_with_index.map { |join_part, i|
+ columns = join_part.column_names.each_with_index.map { |column_name, j|
Aliases::Column.new column_name, "t#{i}_r#{j}"
}
Aliases::Table.new(join_part, columns)
@@ -143,7 +143,7 @@ module ActiveRecord
}
}
- model_cache = Hash.new { |h,klass| h[klass] = {} }
+ model_cache = Hash.new { |h, klass| h[klass] = {} }
parents = model_cache[join_root]
column_aliases = aliases.column_aliases join_root
@@ -223,8 +223,8 @@ module ActiveRecord
[left.children.find { |node2| node1.match? node2 }, node1]
}.partition(&:first)
- ojs = missing.flat_map { |_,n| make_outer_joins left, n }
- intersection.flat_map { |l,r| walk l, r }.concat ojs
+ ojs = missing.flat_map { |_, n| make_outer_joins left, n }
+ intersection.flat_map { |l, r| walk l, r }.concat ojs
end
def find_reflection(klass, name)
diff --git a/activerecord/lib/active_record/associations/preloader/association.rb b/activerecord/lib/active_record/associations/preloader/association.rb
index c79efca920..4072d19380 100644
--- a/activerecord/lib/active_record/associations/preloader/association.rb
+++ b/activerecord/lib/active_record/associations/preloader/association.rb
@@ -113,7 +113,7 @@ module ActiveRecord
return {} if owner_keys.empty?
# Some databases impose a limit on the number of ids in a list (in Oracle it's 1000)
# Make several smaller queries if necessary or make one query if the adapter supports it
- slices = owner_keys.each_slice(klass.connection.in_clause_length || owner_keys.size)
+ slices = owner_keys.each_slice(klass.connection.in_clause_length || owner_keys.size)
@preloaded_records = slices.flat_map do |slice|
records_for(slice).load(&block)
end
diff --git a/activerecord/lib/active_record/associations/preloader/through_association.rb b/activerecord/lib/active_record/associations/preloader/through_association.rb
index be9dfe7686..9d44a02021 100644
--- a/activerecord/lib/active_record/associations/preloader/through_association.rb
+++ b/activerecord/lib/active_record/associations/preloader/through_association.rb
@@ -24,7 +24,7 @@ module ActiveRecord
reset_association owners, through_reflection.name
- middle_records = through_records.flat_map { |(_,rec)| rec }
+ middle_records = through_records.flat_map { |(_, rec)| rec }
preloaders = preloader.preload(middle_records,
source_reflection.name,
@@ -32,13 +32,13 @@ module ActiveRecord
@preloaded_records = preloaders.flat_map(&:preloaded_records)
- middle_to_pl = preloaders.each_with_object({}) do |pl,h|
+ middle_to_pl = preloaders.each_with_object({}) do |pl, h|
pl.owners.each { |middle|
h[middle] = pl
}
end
- through_records.each_with_object({}) do |(lhs,center), records_by_owner|
+ through_records.each_with_object({}) do |(lhs, center), records_by_owner|
pl_to_middle = center.group_by { |record| middle_to_pl[record] }
records_by_owner[lhs] = pl_to_middle.flat_map do |pl, middles|
diff --git a/activerecord/lib/active_record/associations/singular_association.rb b/activerecord/lib/active_record/associations/singular_association.rb
index e386cc0e4c..ee7b7c8bea 100644
--- a/activerecord/lib/active_record/associations/singular_association.rb
+++ b/activerecord/lib/active_record/associations/singular_association.rb
@@ -30,6 +30,13 @@ module ActiveRecord
record
end
+ # Implements the reload reader method, e.g. foo.reload_bar for
+ # Foo.has_one :bar
+ def force_reload_reader
+ klass.uncached { reload }
+ target
+ end
+
private
def create_scope
@@ -51,6 +58,8 @@ module ActiveRecord
sc.execute(binds, klass, conn) do |record|
set_inverse_instance record
end.first
+ rescue ::RangeError
+ nil
end
def replace(record)
diff --git a/activerecord/lib/active_record/associations/through_association.rb b/activerecord/lib/active_record/associations/through_association.rb
index f4129edc5a..6b87993ba3 100644
--- a/activerecord/lib/active_record/associations/through_association.rb
+++ b/activerecord/lib/active_record/associations/through_association.rb
@@ -4,7 +4,7 @@ module ActiveRecord
module ThroughAssociation #:nodoc:
delegate :source_reflection, :through_reflection, to: :reflection
- protected
+ private
# We merge in these scopes for two reasons:
#
@@ -21,8 +21,6 @@ module ActiveRecord
scope
end
- private
-
# Construct attributes for :through pointing to owner and associate. This is used by the
# methods which create and delete records on the association.
#
diff --git a/activerecord/lib/active_record/attribute.rb b/activerecord/lib/active_record/attribute.rb
index 0b08c2a39b..38281158d8 100644
--- a/activerecord/lib/active_record/attribute.rb
+++ b/activerecord/lib/active_record/attribute.rb
@@ -128,11 +128,22 @@ module ActiveRecord
coder["value"] = value if defined?(@value)
end
+ # TODO Change this to private once we've dropped Ruby 2.2 support.
+ # Workaround for Ruby 2.2 "private attribute?" warning.
protected
attr_reader :original_attribute
alias_method :assigned?, :original_attribute
+ def original_value_for_database
+ if assigned?
+ original_attribute.original_value_for_database
+ else
+ _original_value_for_database
+ end
+ end
+
+ private
def initialize_dup(other)
if defined?(@value) && @value.duplicable?
@value = @value.dup
@@ -143,14 +154,6 @@ module ActiveRecord
assigned? && type.changed?(original_value, value, value_before_type_cast)
end
- def original_value_for_database
- if assigned?
- original_attribute.original_value_for_database
- else
- _original_value_for_database
- end
- end
-
def _original_value_for_database
type.serialize(original_value)
end
diff --git a/activerecord/lib/active_record/attribute/user_provided_default.rb b/activerecord/lib/active_record/attribute/user_provided_default.rb
index a4e2c2ec85..57f8bbed76 100644
--- a/activerecord/lib/active_record/attribute/user_provided_default.rb
+++ b/activerecord/lib/active_record/attribute/user_provided_default.rb
@@ -20,6 +20,8 @@ module ActiveRecord
self.class.new(name, user_provided_value, type, original_attribute)
end
+ # TODO Change this to private once we've dropped Ruby 2.2 support.
+ # Workaround for Ruby 2.2 "private attribute?" warning.
protected
attr_reader :user_provided_value
diff --git a/activerecord/lib/active_record/attribute_assignment.rb b/activerecord/lib/active_record/attribute_assignment.rb
index 9843e0ca66..d0dfca0cac 100644
--- a/activerecord/lib/active_record/attribute_assignment.rb
+++ b/activerecord/lib/active_record/attribute_assignment.rb
@@ -12,7 +12,7 @@ module ActiveRecord
private
- def _assign_attributes(attributes) # :nodoc:
+ def _assign_attributes(attributes)
multi_parameter_attributes = {}
nested_parameter_attributes = {}
diff --git a/activerecord/lib/active_record/attribute_decorators.rb b/activerecord/lib/active_record/attribute_decorators.rb
index 340dfe11cf..c39e9ce4c5 100644
--- a/activerecord/lib/active_record/attribute_decorators.rb
+++ b/activerecord/lib/active_record/attribute_decorators.rb
@@ -8,12 +8,34 @@ module ActiveRecord
end
module ClassMethods # :nodoc:
+ # This method is an internal API used to create class macros such as
+ # +serialize+, and features like time zone aware attributes.
+ #
+ # Used to wrap the type of an attribute in a new type.
+ # When the schema for a model is loaded, attributes with the same name as
+ # +column_name+ will have their type yielded to the given block. The
+ # return value of that block will be used instead.
+ #
+ # Subsequent calls where +column_name+ and +decorator_name+ are the same
+ # will override the previous decorator, not decorate twice. This can be
+ # used to create idempotent class macros like +serialize+
def decorate_attribute_type(column_name, decorator_name, &block)
matcher = ->(name, _) { name == column_name.to_s }
key = "_#{column_name}_#{decorator_name}"
decorate_matching_attribute_types(matcher, key, &block)
end
+ # This method is an internal API used to create higher level features like
+ # time zone aware attributes.
+ #
+ # When the schema for a model is loaded, +matcher+ will be called for each
+ # attribute with its name and type. If the matcher returns a truthy value,
+ # the type will then be yielded to the given block, and the return value
+ # of that block will replace the type.
+ #
+ # Subsequent calls to this method with the same value for +decorator_name+
+ # will replace the previous decorator, not decorate twice. This can be
+ # used to ensure that class macros are idempotent.
def decorate_matching_attribute_types(matcher, decorator_name, &block)
reload_schema_from_cache
decorator_name = decorator_name.to_s
diff --git a/activerecord/lib/active_record/attribute_methods/dirty.rb b/activerecord/lib/active_record/attribute_methods/dirty.rb
index c9638bf70b..e20b65e43c 100644
--- a/activerecord/lib/active_record/attribute_methods/dirty.rb
+++ b/activerecord/lib/active_record/attribute_methods/dirty.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: true
require "active_support/core_ext/module/attribute_accessors"
require "active_record/attribute_mutation_tracker"
@@ -15,6 +16,18 @@ module ActiveRecord
class_attribute :partial_writes, instance_writer: false
self.partial_writes = true
+
+ after_create { changes_internally_applied }
+ after_update { changes_internally_applied }
+
+ # Attribute methods for "changed in last call to save?"
+ attribute_method_affix(prefix: "saved_change_to_", suffix: "?")
+ attribute_method_prefix("saved_change_to_")
+ attribute_method_suffix("_before_last_save")
+
+ # Attribute methods for "will change if I call save?"
+ attribute_method_affix(prefix: "will_save_change_to_", suffix: "?")
+ attribute_method_suffix("_change_to_be_saved", "_in_database")
end
# Attempts to +save+ the record and clears changed attributes if successful.
@@ -35,8 +48,8 @@ module ActiveRecord
# <tt>reload</tt> the record and clears changed attributes.
def reload(*)
super.tap do
- @mutation_tracker = nil
@previous_mutation_tracker = nil
+ clear_mutation_trackers
@changed_attributes = HashWithIndifferentAccess.new
end
end
@@ -46,19 +59,26 @@ module ActiveRecord
@attributes = self.class._default_attributes.map do |attr|
attr.with_value_from_user(@attributes.fetch_value(attr.name))
end
- @mutation_tracker = nil
+ clear_mutation_trackers
+ end
+
+ def changes_internally_applied # :nodoc:
+ @mutations_before_last_save = mutation_tracker
+ forget_attribute_assignments
+ @mutations_from_database = AttributeMutationTracker.new(@attributes)
end
def changes_applied
@previous_mutation_tracker = mutation_tracker
@changed_attributes = HashWithIndifferentAccess.new
- store_original_attributes
+ clear_mutation_trackers
end
def clear_changes_information
@previous_mutation_tracker = nil
@changed_attributes = HashWithIndifferentAccess.new
- store_original_attributes
+ forget_attribute_assignments
+ clear_mutation_trackers
end
def raw_write_attribute(attr_name, *)
@@ -80,17 +100,27 @@ module ActiveRecord
if defined?(@cached_changed_attributes)
@cached_changed_attributes
else
+ emit_warning_if_needed("changed_attributes", "saved_changes.transform_values(&:first)")
super.reverse_merge(mutation_tracker.changed_values).freeze
end
end
def changes
cache_changed_attributes do
+ emit_warning_if_needed("changes", "saved_changes")
super
end
end
def previous_changes
+ unless previous_mutation_tracker.equal?(mutations_before_last_save)
+ ActiveSupport::Deprecation.warn(<<-EOW.strip_heredoc)
+ The behavior of `previous_changes` inside of after callbacks is
+ deprecated without replacement. In the next release of Rails,
+ this method inside of `after_save` will return the changes that
+ were just saved.
+ EOW
+ end
previous_mutation_tracker.changes
end
@@ -98,6 +128,109 @@ module ActiveRecord
mutation_tracker.changed_in_place?(attr_name)
end
+ # Did this attribute change when we last saved? This method can be invoked
+ # as `saved_change_to_name?` instead of `saved_change_to_attribute?("name")`.
+ # Behaves similarly to +attribute_changed?+. This method is useful in
+ # after callbacks to determine if the call to save changed a certain
+ # attribute.
+ #
+ # ==== Options
+ #
+ # +from+ When passed, this method will return false unless the original
+ # value is equal to the given option
+ #
+ # +to+ When passed, this method will return false unless the value was
+ # changed to the given value
+ def saved_change_to_attribute?(attr_name, **options)
+ mutations_before_last_save.changed?(attr_name, **options)
+ end
+
+ # Returns the change to an attribute during the last save. If the
+ # attribute was changed, the result will be an array containing the
+ # original value and the saved value.
+ #
+ # Behaves similarly to +attribute_change+. This method is useful in after
+ # callbacks, to see the change in an attribute that just occurred
+ #
+ # This method can be invoked as `saved_change_to_name` in instead of
+ # `saved_change_to_attribute("name")`
+ def saved_change_to_attribute(attr_name)
+ mutations_before_last_save.change_to_attribute(attr_name)
+ end
+
+ # Returns the original value of an attribute before the last save.
+ # Behaves similarly to +attribute_was+. This method is useful in after
+ # callbacks to get the original value of an attribute before the save that
+ # just occurred
+ def attribute_before_last_save(attr_name)
+ mutations_before_last_save.original_value(attr_name)
+ end
+
+ # Did the last call to `save` have any changes to change?
+ def saved_changes?
+ mutations_before_last_save.any_changes?
+ end
+
+ # Returns a hash containing all the changes that were just saved.
+ def saved_changes
+ mutations_before_last_save.changes
+ end
+
+ # Alias for `attribute_changed?`
+ def will_save_change_to_attribute?(attr_name, **options)
+ mutations_from_database.changed?(attr_name, **options)
+ end
+
+ # Alias for `attribute_change`
+ def attribute_change_to_be_saved(attr_name)
+ mutations_from_database.change_to_attribute(attr_name)
+ end
+
+ # Alias for `attribute_was`
+ def attribute_in_database(attr_name)
+ mutations_from_database.original_value(attr_name)
+ end
+
+ # Alias for `changed?`
+ def has_changes_to_save?
+ mutations_from_database.any_changes?
+ end
+
+ # Alias for `changes`
+ def changes_to_save
+ mutations_from_database.changes
+ end
+
+ # Alias for `changed`
+ def changed_attribute_names_to_save
+ changes_to_save.keys
+ end
+
+ # Alias for `changed_attributes`
+ def attributes_in_database
+ changes_to_save.transform_values(&:first)
+ end
+
+ def attribute_was(*)
+ emit_warning_if_needed("attribute_was", "attribute_before_last_save")
+ super
+ end
+
+ def attribute_change(*)
+ emit_warning_if_needed("attribute_change", "saved_change_to_attribute")
+ super
+ end
+
+ def attribute_changed?(*)
+ emit_warning_if_needed("attribute_changed?", "saved_change_to_attribute?")
+ super
+ end
+
+ def changed(*)
+ emit_warning_if_needed("changed", "saved_changes.keys")
+ super
+ end
+
private
def mutation_tracker
@@ -107,12 +240,37 @@ module ActiveRecord
@mutation_tracker ||= AttributeMutationTracker.new(@attributes)
end
+ def emit_warning_if_needed(method_name, new_method_name)
+ unless mutation_tracker.equal?(mutations_from_database)
+ ActiveSupport::Deprecation.warn(<<-EOW.squish)
+ The behavior of `#{method_name}` inside of after callbacks will
+ be changing in the next version of Rails. The new return value will reflect the
+ behavior of calling the method after `save` returned (e.g. the opposite of what
+ it returns now). To maintain the current behavior, use `#{new_method_name}`
+ instead.
+ EOW
+ end
+ end
+
+ def mutations_from_database
+ unless defined?(@mutations_from_database)
+ @mutations_from_database = nil
+ end
+ @mutations_from_database ||= mutation_tracker
+ end
+
def changes_include?(attr_name)
super || mutation_tracker.changed?(attr_name)
end
def clear_attribute_change(attr_name)
mutation_tracker.forget_change(attr_name)
+ mutations_from_database.forget_change(attr_name)
+ end
+
+ def attribute_will_change!(attr_name)
+ super
+ mutations_from_database.force_change(attr_name)
end
def _update_record(*)
@@ -124,18 +282,27 @@ module ActiveRecord
end
def keys_for_partial_write
- changed & self.class.column_names
+ changed_attribute_names_to_save & self.class.column_names
end
- def store_original_attributes
+ def forget_attribute_assignments
@attributes = @attributes.map(&:forgetting_assignment)
+ end
+
+ def clear_mutation_trackers
@mutation_tracker = nil
+ @mutations_from_database = nil
+ @mutations_before_last_save = nil
end
def previous_mutation_tracker
@previous_mutation_tracker ||= NullMutationTracker.instance
end
+ def mutations_before_last_save
+ @mutations_before_last_save ||= previous_mutation_tracker
+ end
+
def cache_changed_attributes
@cached_changed_attributes = changed_attributes
yield
diff --git a/activerecord/lib/active_record/attribute_methods/primary_key.rb b/activerecord/lib/active_record/attribute_methods/primary_key.rb
index 6243398a52..8fcac82a0d 100644
--- a/activerecord/lib/active_record/attribute_methods/primary_key.rb
+++ b/activerecord/lib/active_record/attribute_methods/primary_key.rb
@@ -45,7 +45,12 @@ module ActiveRecord
attribute_was(self.class.primary_key)
end
- protected
+ def id_in_database
+ sync_with_transaction_state
+ attribute_in_database(self.class.primary_key)
+ end
+
+ private
def attribute_method?(attr_name)
attr_name == "id" || super
@@ -60,7 +65,7 @@ module ActiveRecord
end
end
- ID_ATTRIBUTE_METHODS = %w(id id= id? id_before_type_cast id_was).to_set
+ ID_ATTRIBUTE_METHODS = %w(id id= id? id_before_type_cast id_was id_in_database).to_set
def dangerous_attribute_method?(method_name)
super && !ID_ATTRIBUTE_METHODS.include?(method_name)
diff --git a/activerecord/lib/active_record/attribute_methods/query.rb b/activerecord/lib/active_record/attribute_methods/query.rb
index 05f0e974b6..10498f4322 100644
--- a/activerecord/lib/active_record/attribute_methods/query.rb
+++ b/activerecord/lib/active_record/attribute_methods/query.rb
@@ -19,7 +19,7 @@ module ActiveRecord
if Numeric === value || value !~ /[^0-9]/
!value.to_i.zero?
else
- return false if ActiveRecord::Type::Boolean::FALSE_VALUES.include?(value)
+ return false if ActiveModel::Type::Boolean::FALSE_VALUES.include?(value)
!value.blank?
end
elsif value.respond_to?(:zero?)
diff --git a/activerecord/lib/active_record/attribute_methods/read.rb b/activerecord/lib/active_record/attribute_methods/read.rb
index 30f7750884..369a6e35aa 100644
--- a/activerecord/lib/active_record/attribute_methods/read.rb
+++ b/activerecord/lib/active_record/attribute_methods/read.rb
@@ -4,7 +4,7 @@ module ActiveRecord
extend ActiveSupport::Concern
module ClassMethods
- protected
+ private
# We want to generate the methods via module_eval rather than
# define_method, because define_method is slower on dispatch.
@@ -48,7 +48,12 @@ module ActiveRecord
# it has been typecast (for example, "2004-12-12" in a date column is cast
# to a date object, like Date.new(2004, 12, 12)).
def read_attribute(attr_name, &block)
- name = attr_name.to_s
+ name = if self.class.attribute_alias?(attr_name)
+ self.class.attribute_alias(attr_name).to_s
+ else
+ attr_name.to_s
+ end
+
name = self.class.primary_key if name == "id".freeze
_read_attribute(name, &block)
end
diff --git a/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb b/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
index bea1514cdf..500d903857 100644
--- a/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
+++ b/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
@@ -39,7 +39,7 @@ module ActiveRecord
end
def set_time_zone_without_conversion(value)
- ::Time.zone.local_to_utc(value).in_time_zone if value
+ ::Time.zone.local_to_utc(value).try(:in_time_zone) if value
end
def map_avoiding_infinite_recursion(value)
@@ -70,6 +70,7 @@ module ActiveRecord
private
def inherited(subclass)
+ super
# We need to apply this decorator here, rather than on module inclusion. The closure
# created by the matcher would otherwise evaluate for `ActiveRecord::Base`, not the
# sub class being decorated. As such, changes to `time_zone_aware_attributes`, or
@@ -80,7 +81,6 @@ module ActiveRecord
TimeZoneConverter.new(type)
end
end
- super
end
def create_time_zone_conversion_attribute?(name, cast_type)
diff --git a/activerecord/lib/active_record/attribute_methods/write.rb b/activerecord/lib/active_record/attribute_methods/write.rb
index f65c297e01..fe0e01db28 100644
--- a/activerecord/lib/active_record/attribute_methods/write.rb
+++ b/activerecord/lib/active_record/attribute_methods/write.rb
@@ -8,7 +8,7 @@ module ActiveRecord
end
module ClassMethods
- protected
+ private
def define_method_attribute=(name)
safe_name = name.unpack("h*".freeze).first
@@ -29,7 +29,13 @@ module ActiveRecord
# specified +value+. Empty strings for Integer and Float columns are
# turned into +nil+.
def write_attribute(attr_name, value)
- write_attribute_with_type_cast(attr_name, value, true)
+ name = if self.class.attribute_alias?(attr_name)
+ self.class.attribute_alias(attr_name).to_s
+ else
+ attr_name.to_s
+ end
+
+ write_attribute_with_type_cast(name, value, true)
end
def raw_write_attribute(attr_name, value) # :nodoc:
diff --git a/activerecord/lib/active_record/attribute_mutation_tracker.rb b/activerecord/lib/active_record/attribute_mutation_tracker.rb
index c257aef52f..3417090830 100644
--- a/activerecord/lib/active_record/attribute_mutation_tracker.rb
+++ b/activerecord/lib/active_record/attribute_mutation_tracker.rb
@@ -1,7 +1,10 @@
module ActiveRecord
class AttributeMutationTracker # :nodoc:
+ OPTION_NOT_GIVEN = Object.new
+
def initialize(attributes)
@attributes = attributes
+ @forced_changes = Set.new
end
def changed_values
@@ -14,15 +17,29 @@ module ActiveRecord
def changes
attr_names.each_with_object({}.with_indifferent_access) do |attr_name, result|
- if changed?(attr_name)
- result[attr_name] = [attributes[attr_name].original_value, attributes.fetch_value(attr_name)]
+ change = change_to_attribute(attr_name)
+ if change
+ result[attr_name] = change
end
end
end
- def changed?(attr_name)
+ def change_to_attribute(attr_name)
+ if changed?(attr_name)
+ [attributes[attr_name].original_value, attributes.fetch_value(attr_name)]
+ end
+ end
+
+ def any_changes?
+ attr_names.any? { |attr| changed?(attr) }
+ end
+
+ def changed?(attr_name, from: OPTION_NOT_GIVEN, to: OPTION_NOT_GIVEN)
attr_name = attr_name.to_s
- attributes[attr_name].changed?
+ forced_changes.include?(attr_name) ||
+ attributes[attr_name].changed? &&
+ (OPTION_NOT_GIVEN == from || attributes[attr_name].original_value == from) &&
+ (OPTION_NOT_GIVEN == to || attributes[attr_name].value == to)
end
def changed_in_place?(attr_name)
@@ -32,11 +49,22 @@ module ActiveRecord
def forget_change(attr_name)
attr_name = attr_name.to_s
attributes[attr_name] = attributes[attr_name].forgetting_assignment
+ forced_changes.delete(attr_name)
+ end
+
+ def original_value(attr_name)
+ attributes[attr_name].original_value
+ end
+
+ def force_change(attr_name)
+ forced_changes << attr_name.to_s
end
+ # TODO Change this to private once we've dropped Ruby 2.2 support.
+ # Workaround for Ruby 2.2 "private attribute?" warning.
protected
- attr_reader :attributes
+ attr_reader :attributes, :forced_changes
private
@@ -48,14 +76,21 @@ module ActiveRecord
class NullMutationTracker # :nodoc:
include Singleton
- def changed_values
+ def changed_values(*)
{}
end
- def changes
+ def changes(*)
{}
end
+ def change_to_attribute(attr_name)
+ end
+
+ def any_changes?(*)
+ false
+ end
+
def changed?(*)
false
end
@@ -66,5 +101,8 @@ module ActiveRecord
def forget_change(*)
end
+
+ def original_value(*)
+ end
end
end
diff --git a/activerecord/lib/active_record/attribute_set.rb b/activerecord/lib/active_record/attribute_set.rb
index 5bde1f107c..66b278219a 100644
--- a/activerecord/lib/active_record/attribute_set.rb
+++ b/activerecord/lib/active_record/attribute_set.rb
@@ -98,6 +98,8 @@ module ActiveRecord
attributes == other.attributes
end
+ # TODO Change this to private once we've dropped Ruby 2.2 support.
+ # Workaround for Ruby 2.2 "private attribute?" warning.
protected
attr_reader :attributes
diff --git a/activerecord/lib/active_record/attribute_set/builder.rb b/activerecord/lib/active_record/attribute_set/builder.rb
index 661f996e1a..2f624d32af 100644
--- a/activerecord/lib/active_record/attribute_set/builder.rb
+++ b/activerecord/lib/active_record/attribute_set/builder.rb
@@ -90,6 +90,8 @@ module ActiveRecord
@materialized = true
end
+ # TODO Change this to private once we've dropped Ruby 2.2 support.
+ # Workaround for Ruby 2.2 "private attribute?" warning.
protected
attr_reader :types, :values, :additional_types, :delegate_hash, :default
diff --git a/activerecord/lib/active_record/attribute_set/yaml_encoder.rb b/activerecord/lib/active_record/attribute_set/yaml_encoder.rb
index c86cfc4263..899de14792 100644
--- a/activerecord/lib/active_record/attribute_set/yaml_encoder.rb
+++ b/activerecord/lib/active_record/attribute_set/yaml_encoder.rb
@@ -31,6 +31,8 @@ module ActiveRecord
end
end
+ # TODO Change this to private once we've dropped Ruby 2.2 support.
+ # Workaround for Ruby 2.2 "private attribute?" warning.
protected
attr_reader :default_types
diff --git a/activerecord/lib/active_record/attributes.rb b/activerecord/lib/active_record/attributes.rb
index 4b92e5835f..75f5ba3a96 100644
--- a/activerecord/lib/active_record/attributes.rb
+++ b/activerecord/lib/active_record/attributes.rb
@@ -116,7 +116,7 @@ module ActiveRecord
# Users may also define their own custom types, as long as they respond
# to the methods defined on the value type. The method +deserialize+ or
# +cast+ will be called on your type object, with raw input from the
- # database or from your controllers. See ActiveRecord::Type::Value for the
+ # database or from your controllers. See ActiveModel::Type::Value for the
# expected API. It is recommended that your type objects inherit from an
# existing type, or from ActiveRecord::Type::Value
#
@@ -143,7 +143,7 @@ module ActiveRecord
# store_listing.price_in_cents # => 1000
#
# For more details on creating custom types, see the documentation for
- # ActiveRecord::Type::Value. For more details on registering your types
+ # ActiveModel::Type::Value. For more details on registering your types
# to be referenced by a symbol, see ActiveRecord::Type.register. You can
# also pass a type object directly, in place of a symbol.
#
@@ -190,8 +190,8 @@ module ActiveRecord
# The type of an attribute is given the opportunity to change how dirty
# tracking is performed. The methods +changed?+ and +changed_in_place?+
# will be called from ActiveModel::Dirty. See the documentation for those
- # methods in ActiveRecord::Type::Value for more details.
- def attribute(name, cast_type, **options)
+ # methods in ActiveModel::Type::Value for more details.
+ def attribute(name, cast_type = Type::Value.new, **options)
name = name.to_s
reload_schema_from_cache
diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb
index d3e0dee731..9d0b501862 100644
--- a/activerecord/lib/active_record/autosave_association.rb
+++ b/activerecord/lib/active_record/autosave_association.rb
@@ -154,10 +154,10 @@ module ActiveRecord
# Loop prevention for validation of associations
unless @_already_called[name]
begin
- @_already_called[name]=true
+ @_already_called[name] = true
result = instance_eval(&block)
ensure
- @_already_called[name]=false
+ @_already_called[name] = false
end
end
@@ -267,7 +267,7 @@ module ActiveRecord
# Returns whether or not this record has been changed in any way (including whether
# any of its nested autosave associations are likewise changed)
def changed_for_autosave?
- new_record? || changed? || marked_for_destruction? || nested_records_changed_for_autosave?
+ new_record? || has_changes_to_save? || marked_for_destruction? || nested_records_changed_for_autosave?
end
private
@@ -325,7 +325,7 @@ module ActiveRecord
# Returns whether or not the association is valid and applies any errors to
# the parent, <tt>self</tt>, if it wasn't. Skips any <tt>:autosave</tt>
# enabled records if they're marked_for_destruction? or destroyed.
- def association_valid?(reflection, record, index=nil)
+ def association_valid?(reflection, record, index = nil)
return true if record.destroyed? || (reflection.options[:autosave] && record.marked_for_destruction?)
validation_context = self.validation_context unless [:create, :update].include?(self.validation_context)
@@ -383,6 +383,9 @@ module ActiveRecord
if association = association_instance_get(reflection.name)
autosave = reflection.options[:autosave]
+ # reconstruct the scope now that we know the owner's id
+ association.reset_scope if association.respond_to?(:reset_scope)
+
if records = associated_records_to_validate_or_save(association, @new_record_before_save, autosave)
if autosave
records_to_destroy = records.select(&:marked_for_destruction?)
@@ -408,9 +411,6 @@ module ActiveRecord
raise ActiveRecord::Rollback unless saved
end
end
-
- # reconstruct the scope now that we know the owner's id
- association.reset_scope if association.respond_to?(:reset_scope)
end
end
@@ -451,7 +451,7 @@ module ActiveRecord
def record_changed?(reflection, record, key)
record.new_record? ||
(record.has_attribute?(reflection.foreign_key) && record[reflection.foreign_key] != key) ||
- record.attribute_changed?(reflection.foreign_key)
+ record.will_save_change_to_attribute?(reflection.foreign_key)
end
# Saves the associated record if it's new or <tt>:autosave</tt> is enabled.
diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb
index 1e7e939097..ac1aa2df45 100644
--- a/activerecord/lib/active_record/base.rb
+++ b/activerecord/lib/active_record/base.rb
@@ -14,6 +14,7 @@ require "active_support/core_ext/module/introspection"
require "active_support/core_ext/object/duplicable"
require "active_support/core_ext/class/subclasses"
require "active_record/attribute_decorators"
+require "active_record/define_callbacks"
require "active_record/errors"
require "active_record/log_subscriber"
require "active_record/explain_subscriber"
@@ -303,6 +304,7 @@ module ActiveRecord #:nodoc:
include AttributeDecorators
include Locking::Optimistic
include Locking::Pessimistic
+ include DefineCallbacks
include AttributeMethods
include Callbacks
include Timestamp
diff --git a/activerecord/lib/active_record/callbacks.rb b/activerecord/lib/active_record/callbacks.rb
index c616733aa4..be6720ddf3 100644
--- a/activerecord/lib/active_record/callbacks.rb
+++ b/activerecord/lib/active_record/callbacks.rb
@@ -265,17 +265,6 @@ module ActiveRecord
:before_destroy, :around_destroy, :after_destroy, :after_commit, :after_rollback
]
- module ClassMethods # :nodoc:
- include ActiveModel::Callbacks
- end
-
- included do
- include ActiveModel::Validations::Callbacks
-
- define_model_callbacks :initialize, :find, :touch, only: :after
- define_model_callbacks :save, :create, :update, :destroy
- end
-
def destroy #:nodoc:
@_destroy_callback_already_called ||= false
return if @_destroy_callback_already_called
@@ -294,15 +283,15 @@ module ActiveRecord
private
- def create_or_update(*) #:nodoc:
+ def create_or_update(*)
_run_save_callbacks { super }
end
- def _create_record #:nodoc:
+ def _create_record
_run_create_callbacks { super }
end
- def _update_record(*) #:nodoc:
+ def _update_record(*)
_run_update_callbacks { super }
end
end
diff --git a/activerecord/lib/active_record/coders/yaml_column.rb b/activerecord/lib/active_record/coders/yaml_column.rb
index 1c8c9fa272..3a04a10fc9 100644
--- a/activerecord/lib/active_record/coders/yaml_column.rb
+++ b/activerecord/lib/active_record/coders/yaml_column.rb
@@ -1,5 +1,4 @@
require "yaml"
-require "active_support/core_ext/regexp"
module ActiveRecord
module Coders # :nodoc:
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 2d62fd8d50..5ec2fc073e 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
@@ -69,7 +69,7 @@ module ActiveRecord
# threads, which can occur if a programmer forgets to close a
# connection at the end of a thread or a thread dies unexpectedly.
# Regardless of this setting, the Reaper will be invoked before every
- # blocking wait. (Default nil, which means don't schedule the Reaper).
+ # blocking wait. (Default +nil+, which means don't schedule the Reaper).
#
#--
# Synchronization policy:
@@ -116,7 +116,7 @@ module ActiveRecord
end
end
- # If +element+ is in the queue, remove and return it, or nil.
+ # If +element+ is in the queue, remove and return it, or +nil+.
def delete(element)
synchronize do
@queue.delete(element)
@@ -135,7 +135,7 @@ module ActiveRecord
# If +timeout+ is not given, remove and return the head the
# queue if the number of available elements is strictly
# greater than the number of threads currently waiting (that
- # is, don't jump ahead in line). Otherwise, return nil.
+ # is, don't jump ahead in line). Otherwise, return +nil+.
#
# If +timeout+ is given, block if there is no element
# available, waiting up to +timeout+ seconds for an element to
@@ -171,14 +171,14 @@ module ActiveRecord
@queue.size > @num_waiting
end
- # Removes and returns the head of the queue if possible, or nil.
+ # Removes and returns the head of the queue if possible, or +nil+.
def remove
@queue.shift
end
# Remove and return the head the queue if the number of
# available elements is strictly greater than the number of
- # threads currently waiting. Otherwise, return nil.
+ # threads currently waiting. Otherwise, return +nil+.
def no_wait_poll
remove if can_remove_no_wait?
end
@@ -282,7 +282,7 @@ module ActiveRecord
end
# Every +frequency+ seconds, the reaper will call +reap+ on +pool+.
- # A reaper instantiated with a nil frequency will never reap the
+ # A reaper instantiated with a +nil+ frequency will never reap the
# connection pool.
#
# Configure the frequency by setting "reaping_frequency" in your
@@ -307,6 +307,7 @@ module ActiveRecord
end
include MonitorMixin
+ include QueryCache::ConnectionPoolConfiguration
attr_accessor :automatic_reconnect, :checkout_timeout, :schema_cache
attr_reader :spec, :connections, :size, :reaper
@@ -349,8 +350,7 @@ module ActiveRecord
# currently in the process of independently establishing connections to the DB.
@now_connecting = 0
- # A boolean toggle that allows/disallows new connections.
- @new_cons_enabled = true
+ @threads_blocking_new_connections = 0
@available = ConnectionLeasingQueue.new self
end
@@ -445,8 +445,6 @@ module ActiveRecord
# connections in the pool within a timeout interval (default duration is
# <tt>spec.config[:checkout_timeout] * 2</tt> seconds).
def clear_reloadable_connections(raise_on_acquisition_timeout = true)
- num_new_conns_required = 0
-
with_exclusively_acquired_all_connections(raise_on_acquisition_timeout) do
synchronize do
@connections.each do |conn|
@@ -457,24 +455,9 @@ module ActiveRecord
conn.disconnect! if conn.requires_reloading?
end
@connections.delete_if(&:requires_reloading?)
-
@available.clear
-
- if @connections.size < @size
- # because of the pruning done by this method, we might be running
- # low on connections, while threads stuck in queue are helpless
- # (not being able to establish new connections for themselves),
- # see also more detailed explanation in +remove+
- num_new_conns_required = num_waiting_in_queue - @connections.size
- end
-
- @connections.each do |conn|
- @available.add conn
- end
end
end
-
- bulk_make_new_connections(num_new_conns_required) if num_new_conns_required > 0
end
# Clears the cache which maps classes and re-connects connections that
@@ -581,6 +564,24 @@ module ActiveRecord
@available.num_waiting
end
+ # Return connection pool's usage statistic
+ # Example:
+ #
+ # ActiveRecord::Base.connection_pool.stat # => { size: 15, connections: 1, busy: 1, dead: 0, idle: 0, waiting: 0, checkout_timeout: 5 }
+ def stat
+ synchronize do
+ {
+ size: size,
+ connections: @connections.size,
+ busy: @connections.count { |c| c.in_use? && c.owner.alive? },
+ dead: @connections.count { |c| c.in_use? && !c.owner.alive? },
+ idle: @connections.count { |c| !c.in_use? },
+ waiting: num_waiting_in_queue,
+ checkout_timeout: checkout_timeout
+ }
+ end
+ end
+
private
#--
# this is unfortunately not concurrent
@@ -681,13 +682,32 @@ module ActiveRecord
end
def with_new_connections_blocked
- previous_value = nil
synchronize do
- previous_value, @new_cons_enabled = @new_cons_enabled, false
+ @threads_blocking_new_connections += 1
end
+
yield
ensure
- synchronize { @new_cons_enabled = previous_value }
+ num_new_conns_required = 0
+
+ synchronize do
+ @threads_blocking_new_connections -= 1
+
+ if @threads_blocking_new_connections.zero?
+ @available.clear
+
+ num_new_conns_required = num_waiting_in_queue
+
+ @connections.each do |conn|
+ next if conn.in_use?
+
+ @available.add conn
+ num_new_conns_required -= 1
+ end
+ end
+ end
+
+ bulk_make_new_connections(num_new_conns_required) if num_new_conns_required > 0
end
# Acquire a connection by one of 1) immediately removing one
@@ -739,7 +759,7 @@ module ActiveRecord
# and increment @now_connecting, to prevent overstepping this pool's @size
# constraint
do_checkout = synchronize do
- if @new_cons_enabled && (@connections.size + @now_connecting) < @size
+ if @threads_blocking_new_connections.zero? && (@connections.size + @now_connecting) < @size
@now_connecting += 1
end
end
@@ -833,7 +853,7 @@ module ActiveRecord
class ConnectionHandler
def initialize
# These caches are keyed by spec.name (ConnectionSpecification#name).
- @owner_to_pool = Concurrent::Map.new(initial_capacity: 2) do |h,k|
+ @owner_to_pool = Concurrent::Map.new(initial_capacity: 2) do |h, k|
h[k] = Concurrent::Map.new(initial_capacity: 2)
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/database_limits.rb b/activerecord/lib/active_record/connection_adapters/abstract/database_limits.rb
index 95c72f1e20..407e019326 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/database_limits.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/database_limits.rb
@@ -46,7 +46,7 @@ module ActiveRecord
end
# Returns the maximum number of elements in an IN (x,y,z) clause.
- # nil means no limit.
+ # +nil+ means no limit.
def in_clause_length
nil
end
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 aa2dfdd573..e444cec72b 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
@@ -10,9 +10,9 @@ module ActiveRecord
def to_sql(arel, binds = [])
if arel.respond_to?(:ast)
collected = visitor.accept(arel.ast, collector)
- collected.compile(binds, self)
+ collected.compile(binds, self).freeze
else
- arel
+ arel.dup.freeze
end
end
@@ -115,7 +115,7 @@ module ActiveRecord
# Executes an INSERT query and returns the new record's ID
#
- # +id_value+ will be returned unless the value is nil, in
+ # +id_value+ will be returned unless the value is +nil+, in
# which case the database will attempt to calculate the last inserted
# id and return that value.
#
@@ -360,7 +360,7 @@ module ActiveRecord
end
alias join_to_delete join_to_update
- protected
+ private
# Returns a subquery for the given key using the join information.
def subquery_for(key, select)
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb b/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb
index 2f8a89e88e..7eab7de5d3 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb
@@ -4,6 +4,9 @@ module ActiveRecord
class << self
def included(base) #:nodoc:
dirties_query_cache base, :insert, :update, :delete, :rollback_to_savepoint, :rollback_db_transaction
+
+ base.set_callback :checkout, :after, :configure_query_cache!
+ base.set_callback :checkin, :after, :disable_query_cache!
end
def dirties_query_cache(base, *method_names)
@@ -18,11 +21,32 @@ module ActiveRecord
end
end
+ module ConnectionPoolConfiguration
+ def initialize(*)
+ super
+ @query_cache_enabled = Concurrent::Map.new { false }
+ end
+
+ def enable_query_cache!
+ @query_cache_enabled[connection_cache_key(Thread.current)] = true
+ connection.enable_query_cache! if active_connection?
+ end
+
+ def disable_query_cache!
+ @query_cache_enabled.delete connection_cache_key(Thread.current)
+ connection.disable_query_cache! if active_connection?
+ end
+
+ def query_cache_enabled
+ @query_cache_enabled[connection_cache_key(Thread.current)]
+ end
+ end
+
attr_reader :query_cache, :query_cache_enabled
def initialize(*)
super
- @query_cache = Hash.new { |h,sql| h[sql] = {} }
+ @query_cache = Hash.new { |h, sql| h[sql] = {} }
@query_cache_enabled = false
end
@@ -41,6 +65,7 @@ module ActiveRecord
def disable_query_cache!
@query_cache_enabled = false
+ clear_query_cache
end
# Disable the query cache within the block.
@@ -96,6 +121,10 @@ module ActiveRecord
def locked?(arel)
arel.respond_to?(:locked) && arel.locked
end
+
+ def configure_query_cache!
+ enable_query_cache! if pool.query_cache_enabled
+ end
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
index ffde4f2c93..9b324c090b 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
@@ -71,7 +71,7 @@ module ActiveRecord
polymorphic: false,
index: true,
foreign_key: false,
- type: :integer,
+ type: :bigint,
**options
)
@name = name
@@ -100,6 +100,8 @@ module ActiveRecord
end
end
+ # TODO Change this to private once we've dropped Ruby 2.2 support.
+ # Workaround for Ruby 2.2 "private attribute?" warning.
protected
attr_reader :name, :polymorphic, :index, :foreign_key, :type, :options
@@ -475,7 +477,7 @@ module ActiveRecord
# Checks to see if a column exists.
#
- # t.string(:name) unless t.column_exists?(:name, :string)
+ # t.string(:name) unless t.column_exists?(:name, :string)
#
# See {connection.column_exists?}[rdoc-ref:SchemaStatements#column_exists?]
def column_exists?(column_name, type = nil, options = {})
@@ -496,9 +498,9 @@ module ActiveRecord
# Checks to see if an index exists.
#
- # unless t.index_exists?(:branch_id)
- # t.index(:branch_id)
- # end
+ # unless t.index_exists?(:branch_id)
+ # t.index(:branch_id)
+ # end
#
# See {connection.index_exists?}[rdoc-ref:SchemaStatements#index_exists?]
def index_exists?(column_name, options = {})
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb
index 06c89ca072..b912d24626 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb
@@ -35,7 +35,7 @@ module ActiveRecord
end
default = schema_default(column) if column.has_default?
- spec[:default] = default unless default.nil?
+ spec[:default] = default unless default.nil?
spec[:null] = "false" unless column.null
@@ -56,7 +56,7 @@ module ActiveRecord
private
def default_primary_key?(column)
- schema_type(column) == :integer
+ schema_type(column) == :bigint
end
def schema_type(column)
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 1df20a0c56..9c820ce585 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
@@ -996,15 +996,13 @@ module ActiveRecord
def insert_versions_sql(versions) # :nodoc:
sm_table = ActiveRecord::Migrator.schema_migrations_table_name
- if supports_multi_insert?
+ if versions.is_a?(Array)
sql = "INSERT INTO #{sm_table} (version) VALUES\n"
sql << versions.map { |v| "('#{v}')" }.join(",\n")
sql << ";\n\n"
sql
else
- versions.map { |version|
- "INSERT INTO #{sm_table} (version) VALUES ('#{version}');"
- }.join "\n\n"
+ "INSERT INTO #{sm_table} (version) VALUES ('#{versions}');"
end
end
@@ -1042,7 +1040,13 @@ module ActiveRecord
if (duplicate = inserting.detect { |v| inserting.count(v) > 1 })
raise "Duplicate migration #{duplicate}. Please renumber your migrations to resolve the conflict."
end
- execute insert_versions_sql(inserting)
+ if supports_multi_insert?
+ execute insert_versions_sql(inserting)
+ else
+ inserting.each do |v|
+ execute insert_versions_sql(v)
+ end
+ end
end
end
@@ -1116,7 +1120,7 @@ module ActiveRecord
end
def add_index_options(table_name, column_name, comment: nil, **options) # :nodoc:
- if column_name.is_a?(String) && /\W/ === column_name
+ if column_name.is_a?(String) && /\W/.match?(column_name)
column_names = column_name
else
column_names = Array(column_name)
@@ -1165,12 +1169,13 @@ module ActiveRecord
raise NotImplementedError, "#{self.class} does not support changing column comments"
end
- protected
+ private
def add_index_sort_order(quoted_columns, **options)
if order = options[:order]
case order
when Hash
+ order = order.symbolize_keys
quoted_columns.each { |name, column| column << " #{order[name].upcase}" if order[name].present? }
when String
quoted_columns.each { |name, column| column << " #{order.upcase}" if order.present? }
@@ -1199,10 +1204,6 @@ module ActiveRecord
def index_name_for_remove(table_name, options = {})
return options[:name] if can_remove_index_by_name?(options)
- # if the adapter doesn't support the indexes call the best we can do
- # is return the default index name for the options provided
- return index_name(table_name, options) unless respond_to?(:indexes)
-
checks = []
if options.is_a?(Hash)
@@ -1252,7 +1253,6 @@ module ActiveRecord
end
end
- private
def create_table_definition(*args)
TableDefinition.new(*args)
end
@@ -1261,7 +1261,7 @@ module ActiveRecord
AlterTable.new create_table_definition(name)
end
- def index_name_options(column_names) # :nodoc:
+ def index_name_options(column_names)
if column_names.is_a?(String)
column_names = column_names.scan(/\w+/).join("_")
end
@@ -1269,7 +1269,7 @@ module ActiveRecord
{ column: column_names }
end
- def foreign_key_name(table_name, options) # :nodoc:
+ def foreign_key_name(table_name, options)
identifier = "#{table_name}_#{options.fetch(:column)}_fk"
hashed_identifier = Digest::SHA256.hexdigest(identifier).first(10)
options.fetch(:name) do
@@ -1277,7 +1277,7 @@ module ActiveRecord
end
end
- def validate_index_length!(table_name, new_name, internal = false) # :nodoc:
+ def validate_index_length!(table_name, new_name, internal = false)
max_index_length = internal ? index_name_length : allowed_index_name_length
if new_name.length > max_index_length
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
index 0c7197a002..4046b3829d 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
@@ -62,17 +62,17 @@ module ActiveRecord
# notably, the instance methods provided by SchemaStatements are very useful.
class AbstractAdapter
ADAPTER_NAME = "Abstract".freeze
+ include ActiveSupport::Callbacks
+ define_callbacks :checkout, :checkin
+
include Quoting, DatabaseStatements, SchemaStatements
include DatabaseLimits
include QueryCache
- include ActiveSupport::Callbacks
include ColumnDumper
include Savepoints
SIMPLE_INT = /\A\d+\z/
- define_callbacks :checkout, :checkin
-
attr_accessor :visitor, :pool
attr_reader :schema_cache, :owner, :logger
alias :in_use? :owner
@@ -106,7 +106,7 @@ module ActiveRecord
@pool = nil
@schema_cache = SchemaCache.new self
@quoted_column_names, @quoted_table_names = {}, {}
- @visitor = arel_visitor
+ @visitor = arel_visitor
if self.class.type_cast_config_to_boolean(config.fetch(:prepared_statements) { true })
@prepared_statements = true
@@ -161,6 +161,14 @@ module ActiveRecord
SchemaCreation.new self
end
+ # Returns an array of +Column+ objects for the table specified by +table_name+.
+ def columns(table_name) # :nodoc:
+ table_name = table_name.to_s
+ column_definitions(table_name).map do |field|
+ new_column_from_field(table_name, field)
+ end
+ end
+
# this method must only be called while holding connection pool's mutex
def lease
if in_use?
@@ -491,9 +499,9 @@ module ActiveRecord
result
end
- protected
+ private
- def initialize_type_map(m) # :nodoc:
+ def initialize_type_map(m)
register_class_with_limit m, %r(boolean)i, Type::Boolean
register_class_with_limit m, %r(char)i, Type::String
register_class_with_limit m, %r(binary)i, Type::Binary
@@ -524,37 +532,37 @@ module ActiveRecord
end
end
- def reload_type_map # :nodoc:
+ def reload_type_map
type_map.clear
initialize_type_map(type_map)
end
- def register_class_with_limit(mapping, key, klass) # :nodoc:
+ def register_class_with_limit(mapping, key, klass)
mapping.register_type(key) do |*args|
limit = extract_limit(args.last)
klass.new(limit: limit)
end
end
- def register_class_with_precision(mapping, key, klass) # :nodoc:
+ def register_class_with_precision(mapping, key, klass)
mapping.register_type(key) do |*args|
precision = extract_precision(args.last)
klass.new(precision: precision)
end
end
- def extract_scale(sql_type) # :nodoc:
+ def extract_scale(sql_type)
case sql_type
when /\((\d+)\)/ then 0
when /\((\d+)(,(\d+))\)/ then $3.to_i
end
end
- def extract_precision(sql_type) # :nodoc:
+ def extract_precision(sql_type)
$1.to_i if sql_type =~ /\((\d+)(,\d+)?\)/
end
- def extract_limit(sql_type) # :nodoc:
+ def extract_limit(sql_type)
case sql_type
when /^bigint/i
8
@@ -575,7 +583,7 @@ module ActiveRecord
exception
end
- def log(sql, name = "SQL", binds = [], type_casted_binds = [], statement_name = nil)
+ def log(sql, name = "SQL", binds = [], type_casted_binds = [], statement_name = nil) # :doc:
@instrumenter.instrument(
"sql.active_record",
sql: sql,
@@ -590,14 +598,19 @@ module ActiveRecord
def translate_exception(exception, message)
# override in derived class
- ActiveRecord::StatementInvalid.new(message)
+ case exception
+ when RuntimeError
+ exception
+ else
+ ActiveRecord::StatementInvalid.new(message)
+ end
end
def without_prepared_statement?(binds)
!prepared_statements || binds.empty?
end
- def column_for(table_name, column_name) # :nodoc:
+ def column_for(table_name, column_name)
column_name = column_name.to_s
columns(table_name).detect { |c| c.name == column_name } ||
raise(ActiveRecordError, "No such column: #{table_name}.#{column_name}")
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 003ba6eff5..20fcac8a22 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
@@ -9,7 +9,6 @@ require "active_record/connection_adapters/mysql/schema_dumper"
require "active_record/connection_adapters/mysql/type_metadata"
require "active_support/core_ext/string/strip"
-require "active_support/core_ext/regexp"
module ActiveRecord
module ConnectionAdapters
@@ -40,7 +39,7 @@ module ActiveRecord
self.emulate_booleans = true
NATIVE_DATABASE_TYPES = {
- primary_key: "int auto_increment PRIMARY KEY",
+ primary_key: "bigint auto_increment PRIMARY KEY",
string: { name: "varchar", limit: 255 },
text: { name: "text", limit: 65535 },
integer: { name: "int", limit: 4 },
@@ -216,7 +215,11 @@ module ActiveRecord
# Executes the SQL statement in the context of this connection.
def execute(sql, name = nil)
- log(sql, name) { @connection.query(sql) }
+ log(sql, name) do
+ ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
+ @connection.query(sql)
+ end
+ end
end
# Mysql2Adapter doesn't have to free a result after using it, but we use this method
@@ -388,25 +391,21 @@ module ActiveRecord
end
indexes.last.columns << row[:Column_name]
- indexes.last.lengths.merge!(row[:Column_name] => row[:Sub_part]) if row[:Sub_part]
+ indexes.last.lengths.merge!(row[:Column_name] => row[:Sub_part].to_i) if row[:Sub_part]
end
end
indexes
end
- # Returns an array of +Column+ objects for the table specified by +table_name+.
- def columns(table_name) # :nodoc:
- table_name = table_name.to_s
- column_definitions(table_name).map do |field|
- type_metadata = fetch_type_metadata(field[:Type], field[:Extra])
- if type_metadata.type == :datetime && field[:Default] == "CURRENT_TIMESTAMP"
- default, default_function = nil, field[:Default]
- else
- default, default_function = field[:Default], nil
- end
- new_column(field[:Field], default, type_metadata, field[:Null] == "YES", table_name, default_function, field[:Collation], comment: field[:Comment].presence)
+ def new_column_from_field(table_name, field) # :nodoc:
+ type_metadata = fetch_type_metadata(field[:Type], field[:Extra])
+ if type_metadata.type == :datetime && field[:Default] == "CURRENT_TIMESTAMP"
+ default, default_function = nil, field[:Default]
+ else
+ default, default_function = field[:Default], nil
end
+ new_column(field[:Field], default, type_metadata, field[:Null] == "YES", table_name, default_function, field[:Collation], comment: field[:Comment].presence)
end
def table_comment(table_name) # :nodoc:
@@ -650,9 +649,9 @@ module ActiveRecord
!native_database_types[type].nil?
end
- protected
+ private
- def initialize_type_map(m) # :nodoc:
+ def initialize_type_map(m)
super
register_class_with_limit m, %r(char)i, MysqlString
@@ -692,9 +691,9 @@ module ActiveRecord
end
end
- def register_integer_type(mapping, key, options) # :nodoc:
+ def register_integer_type(mapping, key, options)
mapping.register_type(key) do |sql_type|
- if /\bunsigned\z/ === sql_type
+ if /\bunsigned\b/.match?(sql_type)
Type::UnsignedInteger.new(options)
else
Type::Integer.new(options)
@@ -703,7 +702,7 @@ module ActiveRecord
end
def extract_precision(sql_type)
- if /time/ === sql_type
+ if /time/.match?(sql_type)
super || 0
else
super
@@ -718,6 +717,7 @@ module ActiveRecord
if length = options[:length]
case length
when Hash
+ length = length.symbolize_keys
quoted_columns.each { |name, column| column << "(#{length[name]})" if length[name].present? }
when Integer
quoted_columns.each { |name, column| column << "(#{length})" }
@@ -734,9 +734,14 @@ module ActiveRecord
# See https://dev.mysql.com/doc/refman/5.7/en/error-messages-server.html
ER_DUP_ENTRY = 1062
+ ER_NOT_NULL_VIOLATION = 1048
+ ER_DO_NOT_HAVE_DEFAULT = 1364
ER_NO_REFERENCED_ROW_2 = 1452
ER_DATA_TOO_LONG = 1406
+ ER_OUT_OF_RANGE = 1264
ER_LOCK_DEADLOCK = 1213
+ ER_CANNOT_ADD_FOREIGN = 1215
+ ER_CANNOT_CREATE_TABLE = 1005
def translate_exception(exception, message)
case error_number(exception)
@@ -744,8 +749,20 @@ module ActiveRecord
RecordNotUnique.new(message)
when ER_NO_REFERENCED_ROW_2
InvalidForeignKey.new(message)
+ when ER_CANNOT_ADD_FOREIGN
+ mismatched_foreign_key(message)
+ when ER_CANNOT_CREATE_TABLE
+ if message.include?("errno: 150")
+ mismatched_foreign_key(message)
+ else
+ super
+ end
when ER_DATA_TOO_LONG
ValueTooLong.new(message)
+ when ER_OUT_OF_RANGE
+ RangeError.new(message)
+ when ER_NOT_NULL_VIOLATION, ER_DO_NOT_HAVE_DEFAULT
+ NotNullViolation.new(message)
when ER_LOCK_DEADLOCK
Deadlocked.new(message)
else
@@ -770,6 +787,10 @@ module ActiveRecord
options[:null] = column.null
end
+ unless options.key?(:comment)
+ options[:comment] = column.comment
+ end
+
td = create_table_definition(table_name)
cd = td.new_column_definition(column.name, type, options)
schema_creation.accept(ChangeColumnDefinition.new(cd, column.name))
@@ -816,8 +837,6 @@ module ActiveRecord
[remove_column_sql(table_name, :updated_at), remove_column_sql(table_name, :created_at)]
end
- private
-
# 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)
@@ -887,7 +906,7 @@ module ActiveRecord
end.compact.join(", ")
# ...and send them all in one query
- @connection.query "SET #{encoding} #{sql_mode_assignment} #{variable_assignments}"
+ execute "SET #{encoding} #{sql_mode_assignment} #{variable_assignments}"
end
def column_definitions(table_name) # :nodoc:
@@ -911,6 +930,18 @@ module ActiveRecord
MySQL::TableDefinition.new(*args)
end
+ def mismatched_foreign_key(message)
+ parts = message.scan(/`(\w+)`[ $)]/).flatten
+ MismatchedForeignKey.new(
+ self,
+ message: message,
+ table: parts[0],
+ foreign_key: parts[1],
+ target_table: parts[2],
+ primary_key: parts[3],
+ )
+ end
+
def extract_schema_qualified_name(string) # :nodoc:
schema, name = string.to_s.scan(/[^`.\s]+|`[^`]*`/)
schema, name = @config[:database], schema unless name
diff --git a/activerecord/lib/active_record/connection_adapters/column.rb b/activerecord/lib/active_record/connection_adapters/column.rb
index 1808173592..61cd7ae4cc 100644
--- a/activerecord/lib/active_record/connection_adapters/column.rb
+++ b/activerecord/lib/active_record/connection_adapters/column.rb
@@ -29,7 +29,7 @@ module ActiveRecord
end
def bigint?
- /\Abigint\b/ === sql_type
+ /\Abigint\b/.match?(sql_type)
end
# Returns the human name of the column name.
@@ -40,6 +40,28 @@ module ActiveRecord
Base.human_attribute_name(@name)
end
+ def init_with(coder)
+ @name = coder["name"]
+ @table_name = coder["table_name"]
+ @sql_type_metadata = coder["sql_type_metadata"]
+ @null = coder["null"]
+ @default = coder["default"]
+ @default_function = coder["default_function"]
+ @collation = coder["collation"]
+ @comment = coder["comment"]
+ end
+
+ def encode_with(coder)
+ coder["name"] = @name
+ coder["table_name"] = @table_name
+ coder["sql_type_metadata"] = @sql_type_metadata
+ coder["null"] = @null
+ coder["default"] = @default
+ coder["default_function"] = @default_function
+ coder["collation"] = @collation
+ coder["comment"] = @comment
+ end
+
def ==(other)
other.is_a?(Column) &&
attributes_for_hash == other.attributes_for_hash
diff --git a/activerecord/lib/active_record/connection_adapters/connection_specification.rb b/activerecord/lib/active_record/connection_adapters/connection_specification.rb
index 849130ba43..dcf56997db 100644
--- a/activerecord/lib/active_record/connection_adapters/connection_specification.rb
+++ b/activerecord/lib/active_record/connection_adapters/connection_specification.rb
@@ -48,8 +48,8 @@ module ActiveRecord
# Converts the given URL to a full connection hash.
def to_hash
- config = raw_config.reject { |_,value| value.blank? }
- config.map { |key,value| config[key] = uri_parser.unescape(value) if value.is_a? String }
+ config = raw_config.reject { |_, value| value.blank? }
+ config.map { |key, value| config[key] = uri_parser.unescape(value) if value.is_a? String }
config
end
diff --git a/activerecord/lib/active_record/connection_adapters/mysql/column.rb b/activerecord/lib/active_record/connection_adapters/mysql/column.rb
index 296d9a15f8..1499c1681f 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql/column.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql/column.rb
@@ -5,11 +5,11 @@ module ActiveRecord
delegate :extra, to: :sql_type_metadata, allow_nil: true
def unsigned?
- /\bunsigned\z/ === sql_type
+ /\bunsigned(?: zerofill)?\z/.match?(sql_type)
end
def case_sensitive?
- collation && collation !~ /_ci\z/
+ collation && !/_ci\z/.match?(collation)
end
def auto_increment?
diff --git a/activerecord/lib/active_record/connection_adapters/mysql/database_statements.rb b/activerecord/lib/active_record/connection_adapters/mysql/database_statements.rb
index 56800f7590..78e7181266 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql/database_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql/database_statements.rb
@@ -52,14 +52,12 @@ module ActiveRecord
end
alias :exec_update :exec_delete
- protected
+ private
def last_inserted_id(result)
@connection.last_id
end
- private
-
def select_result(sql, name = nil, binds = [])
if without_prepared_statement?(binds)
execute_and_free(sql, name) { |result| yield result }
@@ -86,7 +84,9 @@ module ActiveRecord
end
begin
- result = stmt.execute(*type_casted_binds)
+ result = ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
+ stmt.execute(*type_casted_binds)
+ end
rescue Mysql2::Error => e
if cache_stmt
@statements.delete(sql)
diff --git a/activerecord/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb b/activerecord/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb
index 925555703d..9691060cd3 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb
@@ -47,7 +47,7 @@ module ActiveRecord
def build_separator(widths)
padding = 1
- "+" + widths.map { |w| "-" * (w + (padding*2)) }.join("+") + "+"
+ "+" + widths.map { |w| "-" * (w + (padding * 2)) }.join("+") + "+"
end
def build_cells(items, widths)
diff --git a/activerecord/lib/active_record/connection_adapters/mysql/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/mysql/schema_definitions.rb
index ce773ed75b..0cf40de70f 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql/schema_definitions.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql/schema_definitions.rb
@@ -3,7 +3,10 @@ module ActiveRecord
module MySQL
module ColumnMethods
def primary_key(name, type = :primary_key, **options)
- options[:auto_increment] = true if type == :bigint && !options.key?(:default)
+ if type == :primary_key && !options.key?(:default)
+ options[:auto_increment] = true
+ options[:limit] = 8
+ end
super
end
diff --git a/activerecord/lib/active_record/connection_adapters/mysql/schema_dumper.rb b/activerecord/lib/active_record/connection_adapters/mysql/schema_dumper.rb
index 39221eeb0c..2065816501 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql/schema_dumper.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql/schema_dumper.rb
@@ -3,11 +3,9 @@ module ActiveRecord
module MySQL
module ColumnDumper
def column_spec_for_primary_key(column)
- if column.bigint?
- spec = { id: :bigint.inspect }
- spec[:default] = schema_default(column) || "nil" unless column.auto_increment?
- else
- spec = super
+ spec = super
+ if column.type == :integer && !column.auto_increment?
+ spec[:default] = schema_default(column) || "nil"
end
spec[:unsigned] = "true" if column.unsigned?
spec
@@ -38,7 +36,7 @@ module ActiveRecord
end
def schema_precision(column)
- super unless /time/ === column.sql_type && column.precision == 0
+ super unless /time/.match?(column.sql_type) && column.precision == 0
end
def schema_collation(column)
diff --git a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb
index a3e2c913c5..45e400b75b 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb
@@ -14,12 +14,10 @@ module ActiveRecord
config[:username] = "root" if config[:username].nil?
config[:flags] ||= 0
- if Mysql2::Client.const_defined? :FOUND_ROWS
- if config[:flags].kind_of? Array
- config[:flags].push "FOUND_ROWS".freeze
- else
- config[:flags] |= Mysql2::Client::FOUND_ROWS
- end
+ if config[:flags].kind_of? Array
+ config[:flags].push "FOUND_ROWS".freeze
+ else
+ config[:flags] |= Mysql2::Client::FOUND_ROWS
end
client = Mysql2::Client.new(config)
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb b/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb
index 092543259f..520a50506f 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb
@@ -85,7 +85,9 @@ module ActiveRecord
# Queries the database and returns the results in an Array-like object
def query(sql, name = nil) #:nodoc:
log(sql, name) do
- result_as_array @connection.async_exec(sql)
+ ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
+ result_as_array @connection.async_exec(sql)
+ end
end
end
@@ -95,7 +97,9 @@ module ActiveRecord
# need it specifically, you may want consider the <tt>exec_query</tt> wrapper.
def execute(sql, name = nil)
log(sql, name) do
- @connection.async_exec(sql)
+ ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
+ @connection.async_exec(sql)
+ end
end
end
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 b969503178..d9daaaa23e 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/array.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/array.rb
@@ -35,7 +35,7 @@ module ActiveRecord
if value.is_a?(::Array)
result = @pg_encoder.encode(type_cast_array(value, :serialize))
if encoding = determine_encoding_of_strings(value)
- result.encode!(encoding)
+ result.force_encoding(encoding)
end
result
else
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/bit.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/bit.rb
index 74bff229ea..0a505f46a7 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/bit.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/bit.rb
@@ -34,13 +34,15 @@ module ActiveRecord
end
def binary?
- /\A[01]*\Z/ === value
+ /\A[01]*\Z/.match?(value)
end
def hex?
- /\A[0-9A-F]*\Z/i === value
+ /\A[0-9A-F]*\Z/i.match?(value)
end
+ # TODO Change this to private once we've dropped Ruby 2.2 support.
+ # Workaround for Ruby 2.2 "private attribute?" warning.
protected
attr_reader :value
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/hstore.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/hstore.rb
index 2d3e6a925d..d629ebca91 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/hstore.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/hstore.rb
@@ -12,8 +12,8 @@ module ActiveRecord
def deserialize(value)
if value.is_a?(::String)
::Hash[value.scan(HstorePair).map { |k, v|
- v = v.upcase == "NULL" ? nil : v.gsub(/\A"(.*)"\Z/m,'\1').gsub(/\\(.)/, '\1')
- k = k.gsub(/\A"(.*)"\Z/m,'\1').gsub(/\\(.)/, '\1')
+ v = v.upcase == "NULL" ? nil : v.gsub(/\A"(.*)"\Z/m, '\1').gsub(/\\(.)/, '\1')
+ k = k.gsub(/\A"(.*)"\Z/m, '\1').gsub(/\\(.)/, '\1')
[k, v]
}]
else
@@ -24,6 +24,8 @@ module ActiveRecord
def serialize(value)
if value.is_a?(::Hash)
value.map { |k, v| "#{escape_hstore(k)}=>#{escape_hstore(v)}" }.join(", ")
+ elsif value.respond_to?(:to_unsafe_h)
+ serialize(value.to_unsafe_h)
else
value
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/postgresql/schema_definitions.rb
index a11dbe7dce..4afb4733eb 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_definitions.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_definitions.rb
@@ -11,11 +11,22 @@ module ActiveRecord
# t.timestamps
# end
#
- # By default, this will use the +uuid_generate_v4()+ function from the
- # +uuid-ossp+ extension, which MUST be enabled on your database. To enable
- # the +uuid-ossp+ extension, you can use the +enable_extension+ method in your
- # migrations. To use a UUID primary key without +uuid-ossp+ enabled, you can
- # set the +:default+ option to +nil+:
+ # By default, this will use the +gen_random_uuid()+ function from the
+ # +pgcrypto+ extension. As that extension is only available in
+ # PostgreSQL 9.4+, for earlier versions an explicit default can be set
+ # to use +uuid_generate_v4()+ from the +uuid-ossp+ extension instead:
+ #
+ # create_table :stuffs, id: false do |t|
+ # t.primary_key :id, :uuid, default: "uuid_generate_v4()"
+ # t.uuid :foo_id
+ # t.timestamps
+ # end
+ #
+ # To enable the appropriate extension, which is a requirement, use
+ # the +enable_extension+ method in your migrations.
+ #
+ # To use a UUID primary key without any of the extensions, set the
+ # +:default+ option to +nil+:
#
# create_table :stuffs, id: false do |t|
# t.primary_key :id, :uuid, default: nil
@@ -23,15 +34,24 @@ module ActiveRecord
# t.timestamps
# end
#
- # You may also pass a different UUID generation function from +uuid-ossp+
- # or another library.
+ # You may also pass a custom stored procedure that returns a UUID or use a
+ # different UUID generation function from another library.
#
# Note that setting the UUID primary key default value to +nil+ will
# require you to assure that you always provide a UUID value before saving
# a record (as primary keys cannot be +nil+). This might be done via the
# +SecureRandom.uuid+ method and a +before_save+ callback, for instance.
def primary_key(name, type = :primary_key, **options)
- options[:default] = options.fetch(:default, "uuid_generate_v4()") if type == :uuid
+ if type == :uuid
+ options[:default] = options.fetch(:default, "gen_random_uuid()")
+ elsif options.delete(:auto_increment) == true && %i(integer bigint).include?(type)
+ type = if type == :bigint || options[:limit] == 8
+ :bigserial
+ else
+ :serial
+ end
+ end
+
super
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/schema_dumper.rb b/activerecord/lib/active_record/connection_adapters/postgresql/schema_dumper.rb
index c20baf655c..7808d37deb 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_dumper.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_dumper.rb
@@ -25,7 +25,7 @@ module ActiveRecord
private
def default_primary_key?(column)
- schema_type(column) == :serial
+ schema_type(column) == :bigserial
end
def schema_type(column)
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 83310233f0..9e7487b27f 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
@@ -86,7 +86,7 @@ module ActiveRecord
SELECT c.relname
FROM pg_class c
LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
- WHERE c.relkind IN ('r', 'v','m') -- (r)elation/table, (v)iew, (m)aterialized view
+ WHERE c.relkind IN ('r','v','m') -- (r)elation/table, (v)iew, (m)aterialized view
AND n.nspname = ANY (current_schemas(false))
SQL
end
@@ -108,13 +108,13 @@ module ActiveRecord
name = Utils.extract_schema_qualified_name(name.to_s)
return false unless name.identifier
- select_value(<<-SQL, "SCHEMA").to_i > 0
- SELECT COUNT(*)
+ select_values(<<-SQL, "SCHEMA").any?
+ SELECT c.relname
FROM pg_class c
LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE c.relkind IN ('r','v','m') -- (r)elation/table, (v)iew, (m)aterialized view
- AND c.relname = '#{name.identifier}'
- AND n.nspname = #{name.schema ? "'#{name.schema}'" : 'ANY (current_schemas(false))'}
+ AND c.relname = #{quote(name.identifier)}
+ AND n.nspname = #{name.schema ? quote(name.schema) : "ANY (current_schemas(false))"}
SQL
end
@@ -137,8 +137,8 @@ module ActiveRecord
FROM pg_class c
LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE c.relkind IN ('v','m') -- (v)iew, (m)aterialized view
- AND c.relname = '#{name.identifier}'
- AND n.nspname = #{name.schema ? "'#{name.schema}'" : 'ANY (current_schemas(false))'}
+ AND c.relname = #{quote(name.identifier)}
+ AND n.nspname = #{name.schema ? quote(name.schema) : "ANY (current_schemas(false))"}
SQL
end
@@ -221,21 +221,23 @@ module ActiveRecord
end.compact
end
- # Returns the list of all column definitions for a table.
- def columns(table_name) # :nodoc:
- table_name = table_name.to_s
- column_definitions(table_name).map do |column_name, type, default, notnull, oid, fmod, collation, comment|
- oid = oid.to_i
- fmod = fmod.to_i
- type_metadata = fetch_type_metadata(column_name, type, oid, fmod)
- default_value = extract_value_from_default(default)
- default_function = extract_default_function(default_value, default)
- new_column(column_name, default_value, type_metadata, !notnull, table_name, default_function, collation, comment: comment.presence)
- end
- end
-
- def new_column(*args) # :nodoc:
- PostgreSQLColumn.new(*args)
+ def new_column_from_field(table_name, field) # :nondoc:
+ column_name, type, default, notnull, oid, fmod, collation, comment = field
+ oid = oid.to_i
+ fmod = fmod.to_i
+ type_metadata = fetch_type_metadata(column_name, type, oid, fmod)
+ default_value = extract_value_from_default(default)
+ default_function = extract_default_function(default_value, default)
+ PostgreSQLColumn.new(
+ column_name,
+ default_value,
+ type_metadata,
+ !notnull,
+ table_name,
+ default_function,
+ collation,
+ comment: comment.presence
+ )
end
def table_options(table_name) # :nodoc:
@@ -441,7 +443,7 @@ module ActiveRecord
WITH pk_constraint AS (
SELECT conrelid, unnest(conkey) AS connum FROM pg_constraint
WHERE contype = 'p'
- AND conrelid = '#{quote_table_name(table_name)}'::regclass
+ AND conrelid = #{quote(quote_table_name(table_name))}::regclass
), cons AS (
SELECT conrelid, connum, row_number() OVER() AS rownum FROM pk_constraint
)
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 bcef8ac715..311988625f 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/type_metadata.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/type_metadata.rb
@@ -8,7 +8,7 @@ module ActiveRecord
@type_metadata = type_metadata
@oid = oid
@fmod = fmod
- @array = /\[\]$/ === type_metadata.sql_type
+ @array = /\[\]$/.match?(type_metadata.sql_type)
end
def sql_type
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/utils.rb b/activerecord/lib/active_record/connection_adapters/postgresql/utils.rb
index 9a0b80d7d3..a3f9ce6d64 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/utils.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/utils.rb
@@ -35,6 +35,12 @@ module ActiveRecord
end
protected
+
+ def parts
+ @parts ||= [@schema, @identifier].compact
+ end
+
+ private
def unquote(part)
if part && part.start_with?('"')
part[1..-2]
@@ -42,10 +48,6 @@ module ActiveRecord
part
end
end
-
- def parts
- @parts ||= [@schema, @identifier].compact
- end
end
module Utils # :nodoc:
@@ -53,7 +55,7 @@ module ActiveRecord
# Returns an instance of <tt>ActiveRecord::ConnectionAdapters::PostgreSQL::Name</tt>
# extracted from +string+.
- # +schema+ is nil if not specified in +string+.
+ # +schema+ is +nil+ if not specified in +string+.
# +schema+ and +identifier+ exclude surrounding quotes (regardless of whether provided in +string+)
# +string+ supports the range of schema/table references understood by PostgreSQL, for example:
#
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
index a33e64883e..0ebd907cc0 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -70,7 +70,7 @@ module ActiveRecord
ADAPTER_NAME = "PostgreSQL".freeze
NATIVE_DATABASE_TYPES = {
- primary_key: "serial primary key",
+ primary_key: "bigserial primary key",
string: { name: "character varying" },
text: { name: "text" },
integer: { name: "integer" },
@@ -315,6 +315,10 @@ module ActiveRecord
postgresql_version >= 90300
end
+ def supports_pgcrypto_uuid?
+ postgresql_version >= 90400
+ end
+
def get_advisory_lock(lock_id) # :nodoc:
unless lock_id.is_a?(Integer) && lock_id.bit_length <= 63
raise(ArgumentError, "Postgres requires advisory lock ids to be a signed 64 bit integer")
@@ -400,10 +404,12 @@ module ActiveRecord
@connection.server_version
end
- protected
+ private
# See http://www.postgresql.org/docs/current/static/errcodes-appendix.html
VALUE_LIMIT_VIOLATION = "22001"
+ NUMERIC_VALUE_OUT_OF_RANGE = "22003"
+ NOT_NULL_VIOLATION = "23502"
FOREIGN_KEY_VIOLATION = "23503"
UNIQUE_VIOLATION = "23505"
SERIALIZATION_FAILURE = "40001"
@@ -419,6 +425,10 @@ module ActiveRecord
InvalidForeignKey.new(message)
when VALUE_LIMIT_VIOLATION
ValueTooLong.new(message)
+ when NUMERIC_VALUE_OUT_OF_RANGE
+ RangeError.new(message)
+ when NOT_NULL_VIOLATION
+ NotNullViolation.new(message)
when SERIALIZATION_FAILURE
SerializationFailure.new(message)
when DEADLOCK_DETECTED
@@ -428,9 +438,7 @@ module ActiveRecord
end
end
- private
-
- def get_oid_type(oid, fmod, column_name, sql_type = "") # :nodoc:
+ def get_oid_type(oid, fmod, column_name, sql_type = "")
if !type_map.key?(oid)
load_additional_types(type_map, [oid])
end
@@ -443,7 +451,7 @@ module ActiveRecord
}
end
- def initialize_type_map(m) # :nodoc:
+ def initialize_type_map(m)
register_class_with_limit m, "int2", Type::Integer
register_class_with_limit m, "int4", Type::Integer
register_class_with_limit m, "int8", Type::Integer
@@ -511,7 +519,7 @@ module ActiveRecord
load_additional_types(m)
end
- def extract_limit(sql_type) # :nodoc:
+ def extract_limit(sql_type)
case sql_type
when /^bigint/i, /^int8/i
8
@@ -523,7 +531,7 @@ module ActiveRecord
end
# Extracts the value from a PostgreSQL column default definition.
- def extract_value_from_default(default) # :nodoc:
+ def extract_value_from_default(default)
case default
# Quoted types
when /\A[\(B]?'(.*)'.*::"?([\w. ]+)"?(?:\[\])?\z/m
@@ -549,15 +557,15 @@ module ActiveRecord
end
end
- def extract_default_function(default_value, default) # :nodoc:
+ def extract_default_function(default_value, default)
default if has_default_function?(default_value, default)
end
- def has_default_function?(default_value, default) # :nodoc:
+ def has_default_function?(default_value, default)
!default_value && (%r{\w+\(.*\)|\(.*\)::\w+} === default)
end
- def load_additional_types(type_map, oids = nil) # :nodoc:
+ def load_additional_types(type_map, oids = nil)
initializer = OID::TypeMapInitializer.new(type_map)
if supports_ranges?
@@ -601,7 +609,11 @@ module ActiveRecord
def exec_no_cache(sql, name, binds)
type_casted_binds = type_casted_binds(binds)
- log(sql, name, binds, type_casted_binds) { @connection.async_exec(sql, type_casted_binds) }
+ log(sql, name, binds, type_casted_binds) do
+ ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
+ @connection.async_exec(sql, type_casted_binds)
+ end
+ end
end
def exec_cache(sql, name, binds)
@@ -609,7 +621,9 @@ module ActiveRecord
type_casted_binds = type_casted_binds(binds)
log(sql, name, binds, type_casted_binds, stmt_key) do
- @connection.exec_prepared(stmt_key, type_casted_binds)
+ ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
+ @connection.exec_prepared(stmt_key, type_casted_binds)
+ end
end
rescue ActiveRecord::StatementInvalid => e
raise unless is_cached_plan_failure?(e)
@@ -719,7 +733,7 @@ module ActiveRecord
end
# Returns the current ID of a table's sequence.
- def last_insert_id_result(sequence_name) # :nodoc:
+ def last_insert_id_result(sequence_name)
exec_query("SELECT currval('#{sequence_name}')", "SQL")
end
@@ -741,7 +755,7 @@ module ActiveRecord
# Query implementation notes:
# - format_type includes the column size constraint, e.g. varchar(50)
# - ::regclass is a function that gives the id for a table name
- def column_definitions(table_name) # :nodoc:
+ def column_definitions(table_name)
query(<<-end_sql, "SCHEMA")
SELECT a.attname, format_type(a.atttypid, a.atttypmod),
pg_get_expr(d.adbin, d.adrelid), a.attnotnull, a.atttypid, a.atttypmod,
@@ -750,18 +764,18 @@ module ActiveRecord
col_description(a.attrelid, a.attnum) AS comment
FROM pg_attribute a LEFT JOIN pg_attrdef d
ON a.attrelid = d.adrelid AND a.attnum = d.adnum
- WHERE a.attrelid = '#{quote_table_name(table_name)}'::regclass
+ WHERE a.attrelid = #{quote(quote_table_name(table_name))}::regclass
AND a.attnum > 0 AND NOT a.attisdropped
ORDER BY a.attnum
end_sql
end
- def extract_table_ref_from_insert_sql(sql) # :nodoc:
+ def extract_table_ref_from_insert_sql(sql)
sql[/into\s("[A-Za-z0-9_."\[\]\s]+"|[A-Za-z0-9_."\[\]]+)\s*/im]
$1.strip if $1
end
- def create_table_definition(*args) # :nodoc:
+ def create_table_definition(*args)
PostgreSQL::TableDefinition.new(*args)
end
@@ -792,7 +806,6 @@ module ActiveRecord
map[Integer] = PG::TextEncoder::Integer.new
map[TrueClass] = PG::TextEncoder::Boolean.new
map[FalseClass] = PG::TextEncoder::Boolean.new
- map[Float] = PG::TextEncoder::Float.new
@connection.type_map_for_queries = map
end
diff --git a/activerecord/lib/active_record/connection_adapters/schema_cache.rb b/activerecord/lib/active_record/connection_adapters/schema_cache.rb
index 8219f132c3..3a319c4029 100644
--- a/activerecord/lib/active_record/connection_adapters/schema_cache.rb
+++ b/activerecord/lib/active_record/connection_adapters/schema_cache.rb
@@ -21,6 +21,22 @@ module ActiveRecord
@data_sources = @data_sources.dup
end
+ def encode_with(coder)
+ coder["columns"] = @columns
+ coder["columns_hash"] = @columns_hash
+ coder["primary_keys"] = @primary_keys
+ coder["data_sources"] = @data_sources
+ coder["version"] = ActiveRecord::Migrator.current_version
+ end
+
+ def init_with(coder)
+ @columns = coder["columns"]
+ @columns_hash = coder["columns_hash"]
+ @primary_keys = coder["primary_keys"]
+ @data_sources = coder["data_sources"]
+ @version = coder["version"]
+ end
+
def primary_keys(table_name)
@primary_keys[table_name] ||= data_source_exists?(table_name) ? connection.primary_key(table_name) : nil
end
diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb
new file mode 100644
index 0000000000..d0b38dff4c
--- /dev/null
+++ b/activerecord/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb
@@ -0,0 +1,23 @@
+module ActiveRecord
+ module ConnectionAdapters
+ module SQLite3
+ module ColumnMethods
+ def primary_key(name, type = :primary_key, **options)
+ if options.delete(:auto_increment) == true && %i(integer bigint).include?(type)
+ type = :primary_key
+ end
+
+ super
+ end
+ end
+
+ class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition
+ include ColumnMethods
+ end
+
+ class Table < ActiveRecord::ConnectionAdapters::Table
+ include ColumnMethods
+ end
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb b/activerecord/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb
new file mode 100644
index 0000000000..c027fef83c
--- /dev/null
+++ b/activerecord/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb
@@ -0,0 +1,13 @@
+module ActiveRecord
+ module ConnectionAdapters
+ module SQLite3
+ module ColumnDumper
+ private
+
+ def default_primary_key?(column)
+ schema_type(column) == :integer
+ end
+ end
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
index e2b534b511..e761b9531a 100644
--- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
@@ -3,6 +3,8 @@ require "active_record/connection_adapters/statement_pool"
require "active_record/connection_adapters/sqlite3/explain_pretty_printer"
require "active_record/connection_adapters/sqlite3/quoting"
require "active_record/connection_adapters/sqlite3/schema_creation"
+require "active_record/connection_adapters/sqlite3/schema_definitions"
+require "active_record/connection_adapters/sqlite3/schema_dumper"
gem "sqlite3", "~> 1.3.6"
require "sqlite3"
@@ -52,6 +54,7 @@ module ActiveRecord
ADAPTER_NAME = "SQLite".freeze
include SQLite3::Quoting
+ include SQLite3::ColumnDumper
NATIVE_DATABASE_TYPES = {
primary_key: "INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL",
@@ -75,6 +78,10 @@ module ActiveRecord
end
end
+ def update_table_definition(table_name, base) # :nodoc:
+ SQLite3::Table.new(table_name, base)
+ end
+
def schema_creation # :nodoc:
SQLite3::SchemaCreation.new self
end
@@ -191,30 +198,32 @@ module ActiveRecord
type_casted_binds = type_casted_binds(binds)
log(sql, name, binds, type_casted_binds) do
- # Don't cache statements if they are not prepared
- unless prepare
- stmt = @connection.prepare(sql)
- begin
- cols = stmt.columns
- unless without_prepared_statement?(binds)
- stmt.bind_params(type_casted_binds)
+ ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
+ # Don't cache statements if they are not prepared
+ unless prepare
+ stmt = @connection.prepare(sql)
+ begin
+ cols = stmt.columns
+ unless without_prepared_statement?(binds)
+ stmt.bind_params(type_casted_binds)
+ end
+ records = stmt.to_a
+ ensure
+ stmt.close
end
+ else
+ cache = @statements[sql] ||= {
+ stmt: @connection.prepare(sql)
+ }
+ stmt = cache[:stmt]
+ cols = cache[:cols] ||= stmt.columns
+ stmt.reset!
+ stmt.bind_params(type_casted_binds)
records = stmt.to_a
- ensure
- stmt.close
end
- else
- cache = @statements[sql] ||= {
- stmt: @connection.prepare(sql)
- }
- stmt = cache[:stmt]
- cols = cache[:cols] ||= stmt.columns
- stmt.reset!
- stmt.bind_params(type_casted_binds)
- records = stmt.to_a
- end
- ActiveRecord::Result.new(cols, records)
+ ActiveRecord::Result.new(cols, records)
+ end
end
end
@@ -229,19 +238,23 @@ module ActiveRecord
end
def execute(sql, name = nil) #:nodoc:
- log(sql, name) { @connection.execute(sql) }
+ log(sql, name) do
+ ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
+ @connection.execute(sql)
+ end
+ end
end
def begin_db_transaction #:nodoc:
- log("begin transaction",nil) { @connection.transaction }
+ log("begin transaction", nil) { @connection.transaction }
end
def commit_db_transaction #:nodoc:
- log("commit transaction",nil) { @connection.commit }
+ log("commit transaction", nil) { @connection.commit }
end
def exec_rollback_db_transaction #:nodoc:
- log("rollback transaction",nil) { @connection.rollback }
+ log("rollback transaction", nil) { @connection.rollback }
end
# SCHEMA STATEMENTS ========================================
@@ -298,24 +311,20 @@ module ActiveRecord
select_values(sql, "SCHEMA").any?
end
- # Returns an array of +Column+ objects for the table specified by +table_name+.
- def columns(table_name) # :nodoc:
- table_name = table_name.to_s
- table_structure(table_name).map do |field|
- case field["dflt_value"]
- when /^null$/i
- field["dflt_value"] = nil
- when /^'(.*)'$/m
- field["dflt_value"] = $1.gsub("''", "'")
- when /^"(.*)"$/m
- field["dflt_value"] = $1.gsub('""', '"')
- end
-
- collation = field["collation"]
- sql_type = field["type"]
- type_metadata = fetch_type_metadata(sql_type)
- new_column(field["name"], field["dflt_value"], type_metadata, field["notnull"].to_i == 0, table_name, nil, collation)
+ def new_column_from_field(table_name, field) # :nondoc:
+ case field["dflt_value"]
+ when /^null$/i
+ field["dflt_value"] = nil
+ when /^'(.*)'$/m
+ field["dflt_value"] = $1.gsub("''", "'")
+ when /^"(.*)"$/m
+ field["dflt_value"] = $1.gsub('""', '"')
end
+
+ collation = field["collation"]
+ sql_type = field["type"]
+ type_metadata = fetch_type_metadata(sql_type)
+ new_column(field["name"], field["dflt_value"], type_metadata, field["notnull"].to_i == 0, table_name, nil, collation)
end
# Returns an array of indexes for the given table.
@@ -410,7 +419,7 @@ module ActiveRecord
self.default = options[:default] if include_default
self.null = options[:null] if options.include?(:null)
self.precision = options[:precision] if options.include?(:precision)
- self.scale = options[:scale] if options.include?(:scale)
+ self.scale = options[:scale] if options.include?(:scale)
self.collation = options[:collation] if options.include?(:collation)
end
end
@@ -422,15 +431,16 @@ module ActiveRecord
rename_column_indexes(table_name, column.name, new_column_name)
end
- protected
+ private
def table_structure(table_name)
structure = exec_query("PRAGMA table_info(#{quote_table_name(table_name)})", "SCHEMA")
raise(ActiveRecord::StatementInvalid, "Could not find table '#{table_name}'") if structure.empty?
table_structure_with_collation(table_name, structure)
end
+ alias column_definitions table_structure
- def alter_table(table_name, options = {}) #:nodoc:
+ def alter_table(table_name, options = {})
altered_table_name = "a#{table_name}"
caller = lambda { |definition| yield definition if block_given? }
@@ -441,12 +451,12 @@ module ActiveRecord
end
end
- def move_table(from, to, options = {}, &block) #:nodoc:
+ def move_table(from, to, options = {}, &block)
copy_table(from, to, options, &block)
drop_table(from)
end
- def copy_table(from, to, options = {}) #:nodoc:
+ def copy_table(from, to, options = {})
from_primary_key = primary_key(from)
options[:id] = false
create_table(to, options) do |definition|
@@ -472,7 +482,7 @@ module ActiveRecord
options[:rename] || {})
end
- def copy_table_indexes(from, to, rename = {}) #:nodoc:
+ def copy_table_indexes(from, to, rename = {})
indexes(from).each do |index|
name = index.name
if to == "a#{from}"
@@ -495,7 +505,7 @@ module ActiveRecord
end
end
- def copy_table_contents(from, to, columns, rename = {}) #:nodoc:
+ def copy_table_contents(from, to, columns, rename = {})
column_mappings = Hash[columns.map { |name| [name, name] }]
rename.each { |a| column_mappings[a.last] = a.first }
from_columns = columns(from).collect(&:name)
@@ -520,20 +530,23 @@ module ActiveRecord
# column *column_name* is not unique
when /column(s)? .* (is|are) not unique/, /UNIQUE constraint failed: .*/
RecordNotUnique.new(message)
+ when /.* may not be NULL/, /NOT NULL constraint failed: .*/
+ NotNullViolation.new(message)
else
super
end
end
- private
COLLATE_REGEX = /.*\"(\w+)\".*collate\s+\"(\w+)\".*/i.freeze
def table_structure_with_collation(table_name, basic_structure)
collation_hash = {}
- sql = "SELECT sql FROM
- (SELECT * FROM sqlite_master UNION ALL
- SELECT * FROM sqlite_temp_master)
- WHERE type='table' and name='#{ table_name }' \;"
+ sql = <<-SQL
+ SELECT sql FROM
+ (SELECT * FROM sqlite_master UNION ALL
+ SELECT * FROM sqlite_temp_master)
+ WHERE type = 'table' AND name = #{quote(table_name)}
+ SQL
# Result will have following sample string
# CREATE TABLE "users" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
@@ -564,6 +577,10 @@ module ActiveRecord
basic_structure.to_hash
end
end
+
+ def create_table_definition(*args)
+ SQLite3::TableDefinition.new(*args)
+ end
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/statement_pool.rb b/activerecord/lib/active_record/connection_adapters/statement_pool.rb
index 273b1b0b5c..790db56185 100644
--- a/activerecord/lib/active_record/connection_adapters/statement_pool.rb
+++ b/activerecord/lib/active_record/connection_adapters/statement_pool.rb
@@ -6,7 +6,7 @@ module ActiveRecord
DEFAULT_STATEMENT_LIMIT = 1000
def initialize(statement_limit = nil)
- @cache = Hash.new { |h,pid| h[pid] = {} }
+ @cache = Hash.new { |h, pid| h[pid] = {} }
@statement_limit = statement_limit || DEFAULT_STATEMENT_LIMIT
end
diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb
index 622df0cfc1..d4836faa4b 100644
--- a/activerecord/lib/active_record/core.rb
+++ b/activerecord/lib/active_record/core.rb
@@ -174,7 +174,7 @@ module ActiveRecord
columns_hash.include?(inheritance_column) ||
ids.first.kind_of?(Array)
- id = ids.first
+ id = ids.first
if ActiveRecord::Base === id
id = id.id
ActiveSupport::Deprecation.warn(<<-MSG.squish)
@@ -194,7 +194,7 @@ module ActiveRecord
name, primary_key, id)
end
record
- rescue RangeError
+ rescue ::RangeError
raise RecordNotFound.new("Couldn't find #{name} with an out of range value for '#{primary_key}'",
name, primary_key)
end
@@ -223,7 +223,7 @@ module ActiveRecord
statement.execute(hash.values, self, connection).first
rescue TypeError
raise ActiveRecord::StatementInvalid
- rescue RangeError
+ rescue ::RangeError
nil
end
end
@@ -299,14 +299,14 @@ module ActiveRecord
private
- def cached_find_by_statement(key, &block) # :nodoc:
+ def cached_find_by_statement(key, &block)
cache = @find_by_statement_cache[connection.prepared_statements]
cache[key] || cache.synchronize {
cache[key] ||= StatementCache.create(connection, &block)
}
end
- def relation # :nodoc:
+ def relation
relation = Relation.create(self, arel_table, predicate_builder)
if finder_needs_type_condition? && !ignore_default_scope?
@@ -316,7 +316,7 @@ module ActiveRecord
end
end
- def table_metadata # :nodoc:
+ def table_metadata
TableMetadata.new(self, arel_table)
end
end
@@ -330,8 +330,8 @@ module ActiveRecord
# # Instantiates a single new object
# User.new(first_name: 'Jamie')
def initialize(attributes = nil)
- @attributes = self.class._default_attributes.deep_dup
self.class.define_attribute_methods
+ @attributes = self.class._default_attributes.deep_dup
init_internals
initialize_internals_callback
@@ -538,7 +538,7 @@ module ActiveRecord
# Returns a hash of the given methods with their names as keys and returned values as values.
def slice(*methods)
- Hash[methods.map! { |method| [method, public_send(method)] }].with_indifferent_access
+ Hash[methods.flatten.map! { |method| [method, public_send(method)] }].with_indifferent_access
end
private
diff --git a/activerecord/lib/active_record/define_callbacks.rb b/activerecord/lib/active_record/define_callbacks.rb
new file mode 100644
index 0000000000..7d955a24be
--- /dev/null
+++ b/activerecord/lib/active_record/define_callbacks.rb
@@ -0,0 +1,20 @@
+module ActiveRecord
+ # This module exists because `ActiveRecord::AttributeMethods::Dirty` needs to
+ # define callbacks, but continue to have its version of `save` be the super
+ # method of `ActiveRecord::Callbacks`. This will be removed when the removal
+ # of deprecated code removes this need.
+ module DefineCallbacks
+ extend ActiveSupport::Concern
+
+ module ClassMethods # :nodoc:
+ include ActiveModel::Callbacks
+ end
+
+ included do
+ include ActiveModel::Validations::Callbacks
+
+ define_model_callbacks :initialize, :find, :touch, only: :after
+ define_model_callbacks :save, :create, :update, :destroy
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/dynamic_matchers.rb b/activerecord/lib/active_record/dynamic_matchers.rb
index 9a7a8d25bb..08d42f3dd4 100644
--- a/activerecord/lib/active_record/dynamic_matchers.rb
+++ b/activerecord/lib/active_record/dynamic_matchers.rb
@@ -1,4 +1,3 @@
-require "active_support/core_ext/regexp"
module ActiveRecord
module DynamicMatchers #:nodoc:
diff --git a/activerecord/lib/active_record/enum.rb b/activerecord/lib/active_record/enum.rb
index 0a94ab58dd..0ab03b2ab3 100644
--- a/activerecord/lib/active_record/enum.rb
+++ b/activerecord/lib/active_record/enum.rb
@@ -140,6 +140,8 @@ module ActiveRecord
end
end
+ # TODO Change this to private once we've dropped Ruby 2.2 support.
+ # Workaround for Ruby 2.2 "private attribute?" warning.
protected
attr_reader :name, :mapping, :subtype
diff --git a/activerecord/lib/active_record/errors.rb b/activerecord/lib/active_record/errors.rb
index 8fbe43e3ec..c812a05101 100644
--- a/activerecord/lib/active_record/errors.rb
+++ b/activerecord/lib/active_record/errors.rb
@@ -43,7 +43,7 @@ module ActiveRecord
# Raised when connection to the database could not been established (for example when
# {ActiveRecord::Base.connection=}[rdoc-ref:ConnectionHandling#connection]
- # is given a nil object).
+ # is given a +nil+ object).
class ConnectionNotEstablished < ActiveRecordError
end
@@ -123,10 +123,46 @@ module ActiveRecord
class InvalidForeignKey < WrappedDatabaseException
end
+ # Raised when a foreign key constraint cannot be added because the column type does not match the referenced column type.
+ class MismatchedForeignKey < StatementInvalid
+ def initialize(adapter = nil, message: nil, table: nil, foreign_key: nil, target_table: nil, primary_key: nil)
+ @adapter = adapter
+ if table
+ msg = <<-EOM.strip_heredoc
+ Column `#{foreign_key}` on table `#{table}` has a type of `#{column_type(table, foreign_key)}`.
+ This does not match column `#{primary_key}` on `#{target_table}`, which has type `#{column_type(target_table, primary_key)}`.
+ To resolve this issue, change the type of the `#{foreign_key}` column on `#{table}` to be :integer. (For example `t.integer #{foreign_key}`).
+ EOM
+ else
+ msg = <<-EOM
+ There is a mismatch between the foreign key and primary key column types.
+ Verify that the foreign key column type and the primary key of the associated table match types.
+ EOM
+ end
+ if message
+ msg << "\nOriginal message: #{message}"
+ end
+ super(msg)
+ end
+
+ private
+ def column_type(table, column)
+ @adapter.columns(table).detect { |c| c.name == column }.sql_type
+ end
+ end
+
+ # Raised when a record cannot be inserted or updated because it would violate a not null constraint.
+ class NotNullViolation < StatementInvalid
+ end
+
# Raised when a record cannot be inserted or updated because a value too long for a column type.
class ValueTooLong < StatementInvalid
end
+ # Raised when values that executed are out of range.
+ class RangeError < StatementInvalid
+ end
+
# Raised when number of bind variables in statement given to +:condition+ key
# (for example, when using {ActiveRecord::Base.find}[rdoc-ref:FinderMethods#find] method)
# does not match number of expected values supplied.
diff --git a/activerecord/lib/active_record/explain.rb b/activerecord/lib/active_record/explain.rb
index 980b8e1baa..8f7ae2c33c 100644
--- a/activerecord/lib/active_record/explain.rb
+++ b/activerecord/lib/active_record/explain.rb
@@ -1,4 +1,3 @@
-require "active_support/lazy_load_hooks"
require "active_record/explain_registry"
module ActiveRecord
diff --git a/activerecord/lib/active_record/fixture_set/file.rb b/activerecord/lib/active_record/fixture_set/file.rb
index 5ba354d758..6cf2e01179 100644
--- a/activerecord/lib/active_record/fixture_set/file.rb
+++ b/activerecord/lib/active_record/fixture_set/file.rb
@@ -66,10 +66,13 @@ module ActiveRecord
# Validate our unmarshalled data.
def validate(data)
unless Hash === data || YAML::Omap === data
- raise Fixture::FormatError, "fixture is not a hash"
+ raise Fixture::FormatError, "fixture is not a hash: #{@file}"
end
- raise Fixture::FormatError unless data.all? { |name, row| Hash === row }
+ invalid = data.reject { |_, row| Hash === row }
+ if invalid.any?
+ raise Fixture::FormatError, "fixture key is not a hash: #{@file}, keys: #{invalid.keys.inspect}"
+ end
data
end
end
diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb
index 8b47fbdbe4..3b4532a3f2 100644
--- a/activerecord/lib/active_record/fixtures.rb
+++ b/activerecord/lib/active_record/fixtures.rb
@@ -88,7 +88,7 @@ module ActiveRecord
# assert_equal "Ruby on Rails", @rubyonrails.name
# end
#
- # In order to use these methods to access fixtured data within your testcases, you must specify one of the
+ # In order to use these methods to access fixtured data within your test cases, you must specify one of the
# following in your ActiveSupport::TestCase-derived class:
#
# - to fully enable instantiated fixtures (enable alternate methods #1 and #2 above)
@@ -103,7 +103,7 @@ module ActiveRecord
#
# = Dynamic fixtures with ERB
#
- # Some times you don't care about the content of the fixtures as much as you care about the volume.
+ # Sometimes you don't care about the content of the fixtures as much as you care about the volume.
# In these cases, you can mix ERB in with your YAML fixtures to create a bunch of fixtures for load
# testing, like:
#
@@ -415,9 +415,9 @@ module ActiveRecord
# possibly in a folder with the same name.
#++
- MAX_ID = 2 ** 30 - 1
+ MAX_ID = 2**30 - 1
- @@all_cached_fixtures = Hash.new { |h,k| h[k] = {} }
+ @@all_cached_fixtures = Hash.new { |h, k| h[k] = {} }
def self.default_fixture_model_name(fixture_set_name, config = ActiveRecord::Base) # :nodoc:
config.pluralize_table_names ?
@@ -536,16 +536,16 @@ module ActiveRecord
update_all_loaded_fixtures fixtures_map
connection.transaction(requires_new: true) do
- deleted_tables = Set.new
+ deleted_tables = Hash.new { |h, k| h[k] = Set.new }
fixture_sets.each do |fs|
conn = fs.model_class.respond_to?(:connection) ? fs.model_class.connection : connection
table_rows = fs.table_rows
table_rows.each_key do |table|
- unless deleted_tables.include? table
+ unless deleted_tables[conn].include? table
conn.delete "DELETE FROM #{conn.quote_table_name(table)}", "Fixture Delete"
end
- deleted_tables << table
+ deleted_tables[conn] << table
end
table_rows.each do |fixture_set_name, rows|
@@ -597,18 +597,18 @@ module ActiveRecord
@fixtures = read_fixture_files(path)
- @connection = connection
+ @connection = connection
- @table_name = ( model_class.respond_to?(:table_name) ?
+ @table_name = (model_class.respond_to?(:table_name) ?
model_class.table_name :
- self.class.default_fixture_table_name(name, config) )
+ self.class.default_fixture_table_name(name, config))
end
def [](x)
fixtures[x]
end
- def []=(k,v)
+ def []=(k, v)
fixtures[k] = v
end
@@ -629,7 +629,7 @@ module ActiveRecord
fixtures.delete("DEFAULTS")
# track any join tables we need to insert later
- rows = Hash.new { |h,table| h[table] = [] }
+ rows = Hash.new { |h, table| h[table] = [] }
rows[table_name] = fixtures.map do |label, fixture|
row = fixture.to_hash
diff --git a/activerecord/lib/active_record/integration.rb b/activerecord/lib/active_record/integration.rb
index 3c54c6048d..8e71b60b29 100644
--- a/activerecord/lib/active_record/integration.rb
+++ b/activerecord/lib/active_record/integration.rb
@@ -15,9 +15,9 @@ module ActiveRecord
self.cache_timestamp_format = :usec
end
- # Returns a String, which Action Pack uses for constructing a URL to this
- # object. The default implementation returns this record's id as a String,
- # or nil if this record's unsaved.
+ # Returns a +String+, which Action Pack uses for constructing a URL to this
+ # object. The default implementation returns this record's id as a +String+,
+ # or +nil+ if this record's unsaved.
#
# For example, suppose that you have a User model, and that you have a
# <tt>resources :users</tt> route. Normally, +user_path+ will
diff --git a/activerecord/lib/active_record/locking/optimistic.rb b/activerecord/lib/active_record/locking/optimistic.rb
index 8e8a97990a..2659c60f1f 100644
--- a/activerecord/lib/active_record/locking/optimistic.rb
+++ b/activerecord/lib/active_record/locking/optimistic.rb
@@ -47,6 +47,8 @@ module ActiveRecord
# self.locking_column = :lock_person
# end
#
+ # Please note that the optimistic locking will be ignored if you update the
+ # locking column's value.
module Optimistic
extend ActiveSupport::Concern
@@ -60,13 +62,14 @@ module ActiveRecord
end
private
+
def increment_lock
lock_col = self.class.locking_column
previous_lock_value = send(lock_col).to_i
send(lock_col + "=", previous_lock_value + 1)
end
- def _create_record(attribute_names = self.attribute_names, *) # :nodoc:
+ def _create_record(attribute_names = self.attribute_names, *)
if locking_enabled?
# We always want to persist the locking version, even if we don't detect
# a change from the default, since the database might have no default
@@ -75,23 +78,26 @@ module ActiveRecord
super
end
- def _update_record(attribute_names = self.attribute_names) #:nodoc:
+ def _update_record(attribute_names = self.attribute_names)
return super unless locking_enabled?
- return 0 if attribute_names.empty?
lock_col = self.class.locking_column
- previous_lock_value = send(lock_col).to_i
- increment_lock
- attribute_names += [lock_col]
- attribute_names.uniq!
+ return super if attribute_names.include?(lock_col)
+ return 0 if attribute_names.empty?
begin
+ previous_lock_value = read_attribute_before_type_cast(lock_col)
+
+ increment_lock
+
+ attribute_names.push(lock_col)
+
relation = self.class.unscoped
affected_rows = relation.where(
self.class.primary_key => id,
- lock_col => previous_lock_value,
+ lock_col => previous_lock_value
).update_all(
attributes_for_update(attribute_names).map do |name|
[name, _read_attribute(name)]
@@ -104,9 +110,9 @@ module ActiveRecord
affected_rows
- # If something went wrong, revert the version.
+ # If something went wrong, revert the locking_column value.
rescue Exception
- send(lock_col + "=", previous_lock_value)
+ send(lock_col + "=", previous_lock_value.to_i)
raise
end
end
diff --git a/activerecord/lib/active_record/log_subscriber.rb b/activerecord/lib/active_record/log_subscriber.rb
index ad71c6cde8..4b8d8d9105 100644
--- a/activerecord/lib/active_record/log_subscriber.rb
+++ b/activerecord/lib/active_record/log_subscriber.rb
@@ -15,16 +15,6 @@ module ActiveRecord
rt
end
- def render_bind(attr, type_casted_value)
- value = if attr.type.binary? && attr.value
- "<#{attr.value_for_database.to_s.bytesize} bytes of binary data>"
- else
- type_casted_value
- end
-
- [attr.name, value]
- end
-
def sql(event)
self.class.runtime += event.duration
return unless logger.debug?
@@ -39,7 +29,8 @@ module ActiveRecord
binds = nil
unless (payload[:binds] || []).empty?
- binds = " " + payload[:binds].zip(payload[:type_casted_binds]).map { |attr, value|
+ casted_params = type_casted_binds(payload[:binds], payload[:type_casted_binds])
+ binds = " " + payload[:binds].zip(casted_params).map { |attr, value|
render_bind(attr, value)
}.inspect
end
@@ -52,6 +43,20 @@ module ActiveRecord
private
+ def type_casted_binds(binds, casted_binds)
+ casted_binds || binds.map { |attr| type_cast attr.value_for_database }
+ end
+
+ def render_bind(attr, type_casted_value)
+ value = if attr.type.binary? && attr.value
+ "<#{attr.value_for_database.to_s.bytesize} bytes of binary data>"
+ else
+ type_casted_value
+ end
+
+ [attr.name, value]
+ end
+
def colorize_payload_name(name, payload_name)
if payload_name.blank? || payload_name == "SQL" # SQL vs Model Load/Exists
color(name, MAGENTA, true)
@@ -84,6 +89,10 @@ module ActiveRecord
def logger
ActiveRecord::Base.logger
end
+
+ def type_cast(value)
+ ActiveRecord::Base.connection.type_cast(value)
+ end
end
end
diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb
index 627e93b5b6..cc6bc17b9d 100644
--- a/activerecord/lib/active_record/migration.rb
+++ b/activerecord/lib/active_record/migration.rb
@@ -1,7 +1,6 @@
require "set"
require "zlib"
require "active_support/core_ext/module/attribute_accessors"
-require "active_support/core_ext/regexp"
module ActiveRecord
class MigrationError < ActiveRecordError#:nodoc:
@@ -278,8 +277,10 @@ module ActiveRecord
#
# * <tt>change_column(table_name, column_name, type, options)</tt>: Changes
# the column to a different type using the same parameters as add_column.
- # * <tt>change_column_default(table_name, column_name, default)</tt>: Sets a
- # default value for +column_name+ defined by +default+ on +table_name+.
+ # * <tt>change_column_default(table_name, column_name, default_or_changes)</tt>:
+ # Sets a default value for +column_name+ defined by +default_or_changes+ on
+ # +table_name+. Passing a hash containing <tt>:from</tt> and <tt>:to</tt>
+ # as +default_or_changes+ will make this change reversible in the migration.
# * <tt>change_column_null(table_name, column_name, null, default = nil)</tt>:
# Sets or removes a +NOT NULL+ constraint on +column_name+. The +null+ flag
# indicates whether the value can be +NULL+. See
@@ -768,7 +769,7 @@ module ActiveRecord
when :down then announce "reverting"
end
- time = nil
+ time = nil
ActiveRecord::Base.connection_pool.with_connection do |conn|
time = Benchmark.measure do
exec_migration(conn, direction)
@@ -796,7 +797,7 @@ module ActiveRecord
@connection = nil
end
- def write(text="")
+ def write(text = "")
puts(text) if verbose
end
@@ -806,7 +807,7 @@ module ActiveRecord
write "== %s %s" % [text, "=" * length]
end
- def say(message, subitem=false)
+ def say(message, subitem = false)
write "#{subitem ? " ->" : "--"} #{message}"
end
@@ -990,11 +991,11 @@ module ActiveRecord
end
end
- def rollback(migrations_paths, steps=1)
+ def rollback(migrations_paths, steps = 1)
move(:down, migrations_paths, steps)
end
- def forward(migrations_paths, steps=1)
+ def forward(migrations_paths, steps = 1)
move(:up, migrations_paths, steps)
end
@@ -1231,10 +1232,10 @@ module ActiveRecord
end
def validate(migrations)
- name ,= migrations.group_by(&:name).find { |_,v| v.length > 1 }
+ name , = migrations.group_by(&:name).find { |_, v| v.length > 1 }
raise DuplicateMigrationNameError.new(name) if name
- version ,= migrations.group_by(&:version).find { |_,v| v.length > 1 }
+ version , = migrations.group_by(&:version).find { |_, v| v.length > 1 }
raise DuplicateMigrationVersionError.new(version) if version
end
diff --git a/activerecord/lib/active_record/migration/compatibility.rb b/activerecord/lib/active_record/migration/compatibility.rb
index 04e538baa5..9c357e1604 100644
--- a/activerecord/lib/active_record/migration/compatibility.rb
+++ b/activerecord/lib/active_record/migration/compatibility.rb
@@ -103,6 +103,23 @@ module ActiveRecord
end
class V5_0 < V5_1
+ def create_table(table_name, options = {})
+ if adapter_name == "PostgreSQL"
+ if options[:id] == :uuid && !options[:default]
+ options[:default] = "uuid_generate_v4()"
+ end
+ end
+
+ # Since 5.1 Postgres adapter uses bigserial type for primary
+ # keys by default and MySQL uses bigint. This compat layer makes old migrations utilize
+ # serial/int type instead -- the way it used to work before 5.1.
+ if options[:id].blank?
+ options[:id] = :integer
+ options[:auto_increment] = true
+ end
+
+ super
+ end
end
class V4_2 < V5_0
diff --git a/activerecord/lib/active_record/model_schema.rb b/activerecord/lib/active_record/model_schema.rb
index 76b3169411..2a28c6bf6d 100644
--- a/activerecord/lib/active_record/model_schema.rb
+++ b/activerecord/lib/active_record/model_schema.rb
@@ -2,71 +2,150 @@ module ActiveRecord
module ModelSchema
extend ActiveSupport::Concern
+ ##
+ # :singleton-method: primary_key_prefix_type
+ # :call-seq: primary_key_prefix_type
+ #
+ # The prefix type that will be prepended to every primary key column name.
+ # The options are +:table_name+ and +:table_name_with_underscore+. If the first is specified,
+ # the Product class will look for "productid" instead of "id" as the primary column. If the
+ # latter is specified, the Product class will look for "product_id" instead of "id". Remember
+ # that this is a global setting for all Active Records.
+
+ ##
+ # :singleton-method: primary_key_prefix_type=
+ # :call-seq: primary_key_prefix_type=(prefix_type)
+ #
+ # Sets the prefix type that will be prepended to every primary key column name.
+ # The options are +:table_name+ and +:table_name_with_underscore+. If the first is specified,
+ # the Product class will look for "productid" instead of "id" as the primary column. If the
+ # latter is specified, the Product class will look for "product_id" instead of "id". Remember
+ # that this is a global setting for all Active Records.
+
+ ##
+ # :singleton-method: table_name_prefix
+ # :call-seq: table_name_prefix
+ #
+ # The prefix string to prepend to every table name.
+
+ ##
+ # :singleton-method: table_name_prefix=
+ # :call-seq: table_name_prefix=(prefix)
+ #
+ # Sets the prefix string to prepend to every table name. So if set to "basecamp_", all table
+ # names will be named like "basecamp_projects", "basecamp_people", etc. This is a convenient
+ # way of creating a namespace for tables in a shared database. By default, the prefix is the
+ # empty string.
+ #
+ # If you are organising your models within modules you can add a prefix to the models within
+ # a namespace by defining a singleton method in the parent module called table_name_prefix which
+ # returns your chosen prefix.
+
+ ##
+ # :singleton-method: table_name_suffix
+ # :call-seq: table_name_suffix
+ #
+ # The suffix string to append to every table name.
+
+ ##
+ # :singleton-method: table_name_suffix=
+ # :call-seq: table_name_suffix=(suffix)
+ #
+ # Works like +table_name_prefix=+, but appends instead of prepends (set to "_basecamp" gives "projects_basecamp",
+ # "people_basecamp"). By default, the suffix is the empty string.
+ #
+ # If you are organising your models within modules, you can add a suffix to the models within
+ # a namespace by defining a singleton method in the parent module called table_name_suffix which
+ # returns your chosen suffix.
+
+ ##
+ # :singleton-method: schema_migrations_table_name
+ # :call-seq: schema_migrations_table_name
+ #
+ # The name of the schema migrations table. By default, the value is <tt>"schema_migrations"</tt>.
+
+ ##
+ # :singleton-method: schema_migrations_table_name=
+ # :call-seq: schema_migrations_table_name=(table_name)
+ #
+ # Sets the name of the schema migrations table.
+
+ ##
+ # :singleton-method: internal_metadata_table_name
+ # :call-seq: internal_metadata_table_name
+ #
+ # The name of the internal metadata table. By default, the value is <tt>"ar_internal_metadata"</tt>.
+
+ ##
+ # :singleton-method: internal_metadata_table_name=
+ # :call-seq: internal_metadata_table_name=(table_name)
+ #
+ # Sets the name of the internal metadata table.
+
+ ##
+ # :singleton-method: protected_environments
+ # :call-seq: protected_environments
+ #
+ # The array of names of environments where destructive actions should be prohibited. By default,
+ # the value is <tt>["production"]</tt>.
+
+ ##
+ # :singleton-method: protected_environments=
+ # :call-seq: protected_environments=(environments)
+ #
+ # Sets an array of names of environments where destructive actions should be prohibited.
+
+ ##
+ # :singleton-method: pluralize_table_names
+ # :call-seq: pluralize_table_names
+ #
+ # Indicates whether table names should be the pluralized versions of the corresponding class names.
+ # If true, the default table name for a Product class will be "products". If false, it would just be "product".
+ # See table_name for the full rules on table/class naming. This is true, by default.
+
+ ##
+ # :singleton-method: pluralize_table_names=
+ # :call-seq: pluralize_table_names=(value)
+ #
+ # Set whether table names should be the pluralized versions of the corresponding class names.
+ # If true, the default table name for a Product class will be "products". If false, it would just be "product".
+ # See table_name for the full rules on table/class naming. This is true, by default.
+
+ ##
+ # :singleton-method: ignored_columns
+ # :call-seq: ignored_columns
+ #
+ # The list of columns names the model should ignore. Ignored columns won't have attribute
+ # accessors defined, and won't be referenced in SQL queries.
+
+ ##
+ # :singleton-method: ignored_columns=
+ # :call-seq: ignored_columns=(columns)
+ #
+ # Sets the columns names the model should ignore. Ignored columns won't have attribute
+ # accessors defined, and won't be referenced in SQL queries.
+
included do
- ##
- # :singleton-method:
- # Accessor for the prefix type that will be prepended to every primary key column name.
- # The options are :table_name and :table_name_with_underscore. If the first is specified,
- # the Product class will look for "productid" instead of "id" as the primary column. If the
- # latter is specified, the Product class will look for "product_id" instead of "id". Remember
- # that this is a global setting for all Active Records.
mattr_accessor :primary_key_prefix_type, instance_writer: false
- ##
- # :singleton-method:
- # Accessor for the name of the prefix string to prepend to every table name. So if set
- # to "basecamp_", all table names will be named like "basecamp_projects", "basecamp_people",
- # etc. This is a convenient way of creating a namespace for tables in a shared database.
- # By default, the prefix is the empty string.
- #
- # If you are organising your models within modules you can add a prefix to the models within
- # a namespace by defining a singleton method in the parent module called table_name_prefix which
- # returns your chosen prefix.
class_attribute :table_name_prefix, instance_writer: false
self.table_name_prefix = ""
- ##
- # :singleton-method:
- # Works like +table_name_prefix+, but appends instead of prepends (set to "_basecamp" gives "projects_basecamp",
- # "people_basecamp"). By default, the suffix is the empty string.
- #
- # If you are organising your models within modules, you can add a suffix to the models within
- # a namespace by defining a singleton method in the parent module called table_name_suffix which
- # returns your chosen suffix.
class_attribute :table_name_suffix, instance_writer: false
self.table_name_suffix = ""
- ##
- # :singleton-method:
- # Accessor for the name of the schema migrations table. By default, the value is "schema_migrations"
class_attribute :schema_migrations_table_name, instance_accessor: false
self.schema_migrations_table_name = "schema_migrations"
- ##
- # :singleton-method:
- # Accessor for the name of the internal metadata table. By default, the value is "ar_internal_metadata"
class_attribute :internal_metadata_table_name, instance_accessor: false
self.internal_metadata_table_name = "ar_internal_metadata"
- ##
- # :singleton-method:
- # Accessor for an array of names of environments where destructive actions should be prohibited. By default,
- # the value is ["production"]
class_attribute :protected_environments, instance_accessor: false
self.protected_environments = ["production"]
- ##
- # :singleton-method:
- # Indicates whether table names should be the pluralized versions of the corresponding class names.
- # If true, the default table name for a Product class will be +products+. If false, it would just be +product+.
- # See table_name for the full rules on table/class naming. This is true, by default.
class_attribute :pluralize_table_names, instance_writer: false
self.pluralize_table_names = true
- ##
- # :singleton-method:
- # Accessor for the list of columns names the model should ignore. Ignored columns won't have attribute
- # accessors defined, and won't be referenced in SQL queries.
class_attribute :ignored_columns, instance_accessor: false
self.ignored_columns = [].freeze
@@ -213,7 +292,7 @@ module ActiveRecord
end
# Sets the name of the sequence to use when generating ids to the given
- # value, or (if the value is nil or false) to the value returned by the
+ # value, or (if the value is +nil+ or +false+) to the value returned by the
# given block. This is required for Oracle and is useful for any
# database which relies on sequences for primary key generation.
#
diff --git a/activerecord/lib/active_record/null_relation.rb b/activerecord/lib/active_record/null_relation.rb
index 254550c378..2bb7ed6d5e 100644
--- a/activerecord/lib/active_record/null_relation.rb
+++ b/activerecord/lib/active_record/null_relation.rb
@@ -41,12 +41,11 @@ module ActiveRecord
end
def calculate(operation, _column_name)
- if [:count, :sum].include? operation
+ case operation
+ when :count, :sum
group_values.any? ? Hash.new : 0
- elsif [:average, :minimum, :maximum].include?(operation) && group_values.any?
- Hash.new
- else
- nil
+ when :average, :minimum, :maximum
+ group_values.any? ? Hash.new : nil
end
end
diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb
index 6933f3f9b8..60d8e95b21 100644
--- a/activerecord/lib/active_record/persistence.rb
+++ b/activerecord/lib/active_record/persistence.rb
@@ -181,7 +181,11 @@ module ActiveRecord
_raise_readonly_record_error if readonly?
destroy_associations
self.class.connection.add_transaction_record(self)
- destroy_row if persisted?
+ @_trigger_destroy_callback = if persisted?
+ destroy_row > 0
+ else
+ true
+ end
@destroyed = true
freeze
end
@@ -253,7 +257,11 @@ module ActiveRecord
verify_readonly_attribute(name)
public_send("#{name}=", value)
- changed? ? save(validate: false) : true
+ if has_changes_to_save?
+ save(validate: false)
+ else
+ true
+ end
end
# Updates the attributes of the model from the passed-in hash and saves the
@@ -336,7 +344,7 @@ module ActiveRecord
# record could be saved.
def increment!(attribute, by = 1)
increment(attribute, by)
- change = public_send(attribute) - (attribute_was(attribute.to_s) || 0)
+ change = public_send(attribute) - (attribute_in_database(attribute.to_s) || 0)
self.class.update_counters(id, attribute => change)
clear_attribute_change(attribute) # eww
self
@@ -515,6 +523,7 @@ module ActiveRecord
raise ActiveRecord::StaleObjectError.new(self, "touch")
end
+ @_trigger_update_callback = result
result
else
true
@@ -546,10 +555,13 @@ module ActiveRecord
def _update_record(attribute_names = self.attribute_names)
attributes_values = arel_attributes_with_values_for_update(attribute_names)
if attributes_values.empty?
- 0
+ rows_affected = 0
+ @_trigger_update_callback = true
else
- self.class.unscoped._update_record attributes_values, id, id_was
+ rows_affected = self.class.unscoped._update_record attributes_values, id, id_in_database
+ @_trigger_update_callback = rows_affected > 0
end
+ rows_affected
end
# Creates a record with values matching those of the instance attributes
diff --git a/activerecord/lib/active_record/query_cache.rb b/activerecord/lib/active_record/query_cache.rb
index c45c8c1697..ec246e97bc 100644
--- a/activerecord/lib/active_record/query_cache.rb
+++ b/activerecord/lib/active_record/query_cache.rb
@@ -24,19 +24,19 @@ module ActiveRecord
end
def self.run
- connection = ActiveRecord::Base.connection
- enabled = connection.query_cache_enabled
- connection.enable_query_cache!
+ caching_pool = ActiveRecord::Base.connection_pool
+ caching_was_enabled = caching_pool.query_cache_enabled
- enabled
+ caching_pool.enable_query_cache!
+
+ [caching_pool, caching_was_enabled]
end
- def self.complete(enabled)
- ActiveRecord::Base.connection.clear_query_cache
- ActiveRecord::Base.connection.disable_query_cache! unless enabled
+ def self.complete((caching_pool, caching_was_enabled))
+ caching_pool.disable_query_cache! unless caching_was_enabled
- unless ActiveRecord::Base.connected? && ActiveRecord::Base.connection.transaction_open?
- ActiveRecord::Base.clear_active_connections!
+ ActiveRecord::Base.connection_handler.connection_pool_list.each do |pool|
+ pool.release_connection if pool.active_connection? && !pool.connection.transaction_open?
end
end
diff --git a/activerecord/lib/active_record/railtie.rb b/activerecord/lib/active_record/railtie.rb
index 989d23bc37..2701c5bca9 100644
--- a/activerecord/lib/active_record/railtie.rb
+++ b/activerecord/lib/active_record/railtie.rb
@@ -82,15 +82,15 @@ module ActiveRecord
if config.active_record.delete(:use_schema_cache_dump)
config.after_initialize do |app|
ActiveSupport.on_load(:active_record) do
- filename = File.join(app.config.paths["db"].first, "schema_cache.dump")
+ filename = File.join(app.config.paths["db"].first, "schema_cache.yml")
if File.file?(filename)
- cache = Marshal.load File.binread filename
+ cache = YAML.load(File.read(filename))
if cache.version == ActiveRecord::Migrator.current_version
self.connection.schema_cache = cache
self.connection_pool.schema_cache = cache.dup
else
- warn "Ignoring db/schema_cache.dump because it has expired. The current schema version is #{ActiveRecord::Migrator.current_version}, but the one in the cache is #{cache.version}."
+ warn "Ignoring db/schema_cache.yml because it has expired. The current schema version is #{ActiveRecord::Migrator.current_version}, but the one in the cache is #{cache.version}."
end
end
end
@@ -108,7 +108,7 @@ module ActiveRecord
initializer "active_record.set_configs" do |app|
ActiveSupport.on_load(:active_record) do
- app.config.active_record.each do |k,v|
+ app.config.active_record.each do |k, v|
send "#{k}=", v
end
end
diff --git a/activerecord/lib/active_record/railties/controller_runtime.rb b/activerecord/lib/active_record/railties/controller_runtime.rb
index adb3c6c4e6..8658188623 100644
--- a/activerecord/lib/active_record/railties/controller_runtime.rb
+++ b/activerecord/lib/active_record/railties/controller_runtime.rb
@@ -6,10 +6,14 @@ module ActiveRecord
module ControllerRuntime #:nodoc:
extend ActiveSupport::Concern
+ # TODO Change this to private once we've dropped Ruby 2.2 support.
+ # Workaround for Ruby 2.2 "private attribute?" warning.
protected
attr_internal :db_runtime
+ private
+
def process_action(action, *args)
# We also need to reset the runtime before each action
# because of queries in middleware or in cases we are streaming
diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake
index 46235ab922..25d79a6c7d 100644
--- a/activerecord/lib/active_record/railties/databases.rake
+++ b/activerecord/lib/active_record/railties/databases.rake
@@ -265,19 +265,19 @@ db_namespace = namespace :db do
end
namespace :cache do
- desc "Creates a db/schema_cache.dump file."
+ desc "Creates a db/schema_cache.yml file."
task dump: [:environment, :load_config] do
- con = ActiveRecord::Base.connection
- filename = File.join(ActiveRecord::Tasks::DatabaseTasks.db_dir, "schema_cache.dump")
+ conn = ActiveRecord::Base.connection
+ filename = File.join(ActiveRecord::Tasks::DatabaseTasks.db_dir, "schema_cache.yml")
- con.schema_cache.clear!
- con.data_sources.each { |table| con.schema_cache.add(table) }
- open(filename, "wb") { |f| f.write(Marshal.dump(con.schema_cache)) }
+ conn.schema_cache.clear!
+ conn.data_sources.each { |table| conn.schema_cache.add(table) }
+ open(filename, "wb") { |f| f.write(YAML.dump(conn.schema_cache)) }
end
- desc "Clears a db/schema_cache.dump file."
+ desc "Clears a db/schema_cache.yml file."
task clear: [:environment, :load_config] do
- filename = File.join(ActiveRecord::Tasks::DatabaseTasks.db_dir, "schema_cache.dump")
+ filename = File.join(ActiveRecord::Tasks::DatabaseTasks.db_dir, "schema_cache.yml")
rm_f filename, verbose: false
end
end
diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb
index 57020e00c9..e1a3c59f08 100644
--- a/activerecord/lib/active_record/reflection.rb
+++ b/activerecord/lib/active_record/reflection.rb
@@ -136,8 +136,8 @@ module ActiveRecord
# BelongsToReflection
# HasAndBelongsToManyReflection
# ThroughReflection
- # PolymorphicReflection
- # RuntimeReflection
+ # PolymorphicReflection
+ # RuntimeReflection
class AbstractReflection # :nodoc:
def through_reflection?
false
@@ -282,11 +282,6 @@ module ActiveRecord
end
def autosave=(autosave)
- # autosave and inverse_of do not get along together nowadays. They may
- # for example cause double saves. Thus, we disable this flag. If in the
- # future those two flags are known to work well together, this could be
- # removed.
- @automatic_inverse_of = false
@options[:autosave] = autosave
parent_reflection = self.parent_reflection
if parent_reflection
@@ -402,6 +397,10 @@ module ActiveRecord
options[:primary_key] || primary_key(klass || self.klass)
end
+ def association_primary_key_type
+ klass.type_for_attribute(association_primary_key)
+ end
+
def active_record_primary_key
@active_record_primary_key ||= options[:primary_key] || primary_key(active_record)
end
@@ -541,14 +540,10 @@ module ActiveRecord
# Attempts to find the inverse association name automatically.
# If it cannot find a suitable inverse association name, it returns
- # nil.
+ # +nil+.
def inverse_name
options.fetch(:inverse_of) do
- if @automatic_inverse_of == false
- nil
- else
- @automatic_inverse_of ||= automatic_inverse_of
- end
+ @automatic_inverse_of ||= automatic_inverse_of
end
end
@@ -712,7 +707,7 @@ module ActiveRecord
def initialize(delegate_reflection)
@delegate_reflection = delegate_reflection
- @klass = delegate_reflection.options[:anonymous_class]
+ @klass = delegate_reflection.options[:anonymous_class]
@source_reflection_name = delegate_reflection.options[:source]
end
@@ -855,6 +850,10 @@ module ActiveRecord
actual_source_reflection.options[:primary_key] || primary_key(klass || self.klass)
end
+ def association_primary_key_type
+ klass.type_for_attribute(association_primary_key)
+ end
+
# Gets an array of possible <tt>:through</tt> source reflection names in both singular and plural form.
#
# class Post < ActiveRecord::Base
@@ -988,7 +987,7 @@ module ActiveRecord
delegate(*delegate_methods, to: :delegate_reflection)
end
- class PolymorphicReflection < ThroughReflection # :nodoc:
+ class PolymorphicReflection < AbstractReflection # :nodoc:
def initialize(reflection, previous_reflection)
@reflection = reflection
@previous_reflection = previous_reflection
diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb
index ef629dcb3b..4e941cf2df 100644
--- a/activerecord/lib/active_record/relation.rb
+++ b/activerecord/lib/active_record/relation.rb
@@ -362,6 +362,9 @@ module ActiveRecord
#
# # Update all books that match conditions, but limit it to 5 ordered by date
# Book.where('title LIKE ?', '%Rails%').order(:created_at).limit(5).update_all(author: 'David')
+ #
+ # # Update all invoices and set the number column to its id value.
+ # Invoice.update_all('number = id')
def update_all(updates)
raise ArgumentError, "Empty list of attributes to change" if updates.blank?
@@ -370,7 +373,7 @@ module ActiveRecord
stmt.set Arel.sql(@klass.send(:sanitize_sql_for_assignment, updates))
stmt.table(table)
- if joins_values.any?
+ if has_join_values?
@klass.connection.join_to_update(stmt, arel, arel_attribute(primary_key))
else
stmt.key = arel_attribute(primary_key)
@@ -519,7 +522,7 @@ module ActiveRecord
stmt = Arel::DeleteManager.new
stmt.from(table)
- if joins_values.any?
+ if has_join_values?
@klass.connection.join_to_delete(stmt, arel, arel_attribute(primary_key))
else
stmt.wheres = arel.constraints
@@ -677,13 +680,18 @@ module ActiveRecord
private
+ def has_join_values?
+ joins_values.any? || left_outer_joins_values.any?
+ end
+
def exec_queries(&block)
@records = eager_loading? ? find_with_associations.freeze : @klass.find_by_sql(arel, bound_attributes, &block).freeze
preload = preload_values
- preload += includes_values unless eager_loading?
- preloader = build_preloader
+ preload += includes_values unless eager_loading?
+ preloader = nil
preload.each do |associations|
+ preloader ||= build_preloader
preloader.preload @records, associations
end
diff --git a/activerecord/lib/active_record/relation/batches/batch_enumerator.rb b/activerecord/lib/active_record/relation/batches/batch_enumerator.rb
index 333b3a63cf..3555779ec2 100644
--- a/activerecord/lib/active_record/relation/batches/batch_enumerator.rb
+++ b/activerecord/lib/active_record/relation/batches/batch_enumerator.rb
@@ -7,7 +7,7 @@ module ActiveRecord
@of = of
@relation = relation
@start = start
- @finish = finish
+ @finish = finish
end
# Looping through a collection of records from the database (using the
diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb
index e4676f79a5..827688a663 100644
--- a/activerecord/lib/active_record/relation/calculations.rb
+++ b/activerecord/lib/active_record/relation/calculations.rb
@@ -223,17 +223,17 @@ module ActiveRecord
end
def execute_simple_calculation(operation, column_name, distinct) #:nodoc:
- # PostgreSQL doesn't like ORDER BY when there are no GROUP BY
- relation = unscope(:order)
-
column_alias = column_name
- if operation == "count" && (relation.limit_value || relation.offset_value)
+ if operation == "count" && (limit_value || offset_value)
# Shortcut when limit is zero.
- return 0 if relation.limit_value == 0
+ return 0 if limit_value == 0
- query_builder = build_count_subquery(relation, column_name, distinct)
+ query_builder = build_count_subquery(spawn, column_name, distinct)
else
+ # PostgreSQL doesn't like ORDER BY when there are no GROUP BY
+ relation = unscope(:order)
+
column = aggregate_column(column_name)
select_value = operation_over_aggregate_column(column, operation, distinct)
diff --git a/activerecord/lib/active_record/relation/delegation.rb b/activerecord/lib/active_record/relation/delegation.rb
index d16de4b06c..43dac0ed3d 100644
--- a/activerecord/lib/active_record/relation/delegation.rb
+++ b/activerecord/lib/active_record/relation/delegation.rb
@@ -1,6 +1,3 @@
-require "active_support/concern"
-require "active_support/core_ext/regexp"
-
module ActiveRecord
module Delegation # :nodoc:
module DelegateCache # :nodoc:
@@ -81,7 +78,7 @@ module ActiveRecord
end
end
- protected
+ private
def method_missing(method, *args, &block)
if @klass.respond_to?(method)
diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb
index 5e580ac865..270511bede 100644
--- a/activerecord/lib/active_record/relation/finder_methods.rb
+++ b/activerecord/lib/active_record/relation/finder_methods.rb
@@ -76,7 +76,7 @@ module ActiveRecord
# Post.find_by "published_at < ?", 2.weeks.ago
def find_by(arg, *args)
where(arg, *args).take
- rescue RangeError
+ rescue ::RangeError
nil
end
@@ -84,7 +84,7 @@ module ActiveRecord
# an ActiveRecord::RecordNotFound error.
def find_by!(arg, *args)
where(arg, *args).take!
- rescue RangeError
+ rescue ::RangeError
raise RecordNotFound.new("Couldn't find #{@klass.name} with an out of range value",
@klass.name)
end
@@ -321,7 +321,7 @@ module ActiveRecord
relation = apply_join_dependency(self, construct_join_dependency(eager_loading: false))
return false if ActiveRecord::NullRelation === relation
- relation = relation.except(:select, :order).select(ONE_AS_ONE).limit(1)
+ relation = relation.except(:select, :distinct).select(ONE_AS_ONE).limit(1)
case conditions
when Array, Hash
@@ -333,7 +333,7 @@ module ActiveRecord
end
connection.select_value(relation, "#{name} Exists", relation.bound_attributes) ? true : false
- rescue RangeError
+ rescue ::RangeError
false
end
@@ -345,7 +345,7 @@ module ActiveRecord
# of results obtained should be provided in the +result_size+ argument and
# the expected number of results should be provided in the +expected_size+
# argument.
- def raise_record_not_found_exception!(ids = nil, result_size = nil, expected_size = nil) # :nodoc:
+ def raise_record_not_found_exception!(ids = nil, result_size = nil, expected_size = nil, key = primary_key) # :nodoc:
conditions = arel.where_sql(@klass.arel_engine)
conditions = " [#{conditions}]" if conditions
name = @klass.name
@@ -353,15 +353,15 @@ module ActiveRecord
if ids.nil?
error = "Couldn't find #{name}"
error << " with#{conditions}" if conditions
- raise RecordNotFound, error
+ raise RecordNotFound.new(error, name)
elsif Array(ids).size == 1
- error = "Couldn't find #{name} with '#{primary_key}'=#{ids}#{conditions}"
- raise RecordNotFound.new(error, name, primary_key, ids)
+ 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 '#{primary_key}': "
+ error = "Couldn't find all #{name.pluralize} with '#{key}': "
error << "(#{ids.join(", ")})#{conditions} (found #{result_size} results, but was looking for #{expected_size})"
- raise RecordNotFound, error
+ raise RecordNotFound.new(error, name, primary_key, ids)
end
end
@@ -439,143 +439,141 @@ module ActiveRecord
reflections.none?(&:collection?)
end
- protected
+ private
- def find_with_ids(*ids)
- raise UnknownPrimaryKey.new(@klass) if primary_key.nil?
+ def find_with_ids(*ids)
+ raise UnknownPrimaryKey.new(@klass) if primary_key.nil?
- expects_array = ids.first.kind_of?(Array)
- return ids.first if expects_array && ids.first.empty?
+ expects_array = ids.first.kind_of?(Array)
+ return ids.first if expects_array && ids.first.empty?
- ids = ids.flatten.compact.uniq
+ ids = ids.flatten.compact.uniq
- case ids.size
- when 0
- raise RecordNotFound, "Couldn't find #{@klass.name} without an ID"
- when 1
- result = find_one(ids.first)
- expects_array ? [ result ] : result
- else
- find_some(ids)
+ case ids.size
+ when 0
+ raise RecordNotFound, "Couldn't find #{@klass.name} without an ID"
+ when 1
+ result = find_one(ids.first)
+ expects_array ? [ result ] : result
+ else
+ find_some(ids)
+ end
+ rescue ::RangeError
+ raise RecordNotFound, "Couldn't find #{@klass.name} with an out of range ID"
end
- rescue RangeError
- raise RecordNotFound, "Couldn't find #{@klass.name} with an out of range ID"
- end
- def find_one(id)
- if ActiveRecord::Base === id
- id = id.id
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
+ def find_one(id)
+ if ActiveRecord::Base === id
+ id = id.id
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
You are passing an instance of ActiveRecord::Base to `find`.
Please pass the id of the object by calling `.id`.
MSG
- end
+ end
- relation = where(primary_key => id)
- record = relation.take
+ relation = where(primary_key => id)
+ record = relation.take
- raise_record_not_found_exception!(id, 0, 1) unless record
+ raise_record_not_found_exception!(id, 0, 1) unless record
- record
- end
+ record
+ end
- def find_some(ids)
- return find_some_ordered(ids) unless order_values.present?
+ def find_some(ids)
+ return find_some_ordered(ids) unless order_values.present?
- result = where(primary_key => ids).to_a
+ result = where(primary_key => ids).to_a
- expected_size =
- if limit_value && ids.size > limit_value
- limit_value
- else
- ids.size
- end
+ expected_size =
+ if limit_value && ids.size > limit_value
+ limit_value
+ else
+ ids.size
+ end
- # 11 ids with limit 3, offset 9 should give 2 results.
- if offset_value && (ids.size - offset_value < expected_size)
- expected_size = ids.size - offset_value
- end
+ # 11 ids with limit 3, offset 9 should give 2 results.
+ if offset_value && (ids.size - offset_value < expected_size)
+ expected_size = ids.size - offset_value
+ end
- if result.size == expected_size
- result
- else
- raise_record_not_found_exception!(ids, result.size, expected_size)
+ if result.size == expected_size
+ result
+ else
+ raise_record_not_found_exception!(ids, result.size, expected_size)
+ end
end
- end
- def find_some_ordered(ids)
- ids = ids.slice(offset_value || 0, limit_value || ids.size) || []
+ def find_some_ordered(ids)
+ ids = ids.slice(offset_value || 0, limit_value || ids.size) || []
- result = except(:limit, :offset).where(primary_key => ids).records
+ result = except(:limit, :offset).where(primary_key => ids).records
- if result.size == ids.size
- pk_type = @klass.type_for_attribute(primary_key)
+ if result.size == ids.size
+ pk_type = @klass.type_for_attribute(primary_key)
- records_by_id = result.index_by(&:id)
- ids.map { |id| records_by_id.fetch(pk_type.cast(id)) }
- else
- raise_record_not_found_exception!(ids, result.size, ids.size)
+ records_by_id = result.index_by(&:id)
+ ids.map { |id| records_by_id.fetch(pk_type.cast(id)) }
+ else
+ raise_record_not_found_exception!(ids, result.size, ids.size)
+ end
end
- end
- def find_take
- if loaded?
- records.first
- else
- @take ||= limit(1).records.first
+ def find_take
+ if loaded?
+ records.first
+ else
+ @take ||= limit(1).records.first
+ end
end
- end
- def find_take_with_limit(limit)
- if loaded?
- records.take(limit)
- else
- limit(limit).to_a
+ def find_take_with_limit(limit)
+ if loaded?
+ records.take(limit)
+ else
+ limit(limit).to_a
+ end
end
- end
- def find_nth(index)
- @offsets[offset_index + index] ||= find_nth_with_limit(index, 1).first
- end
+ def find_nth(index)
+ @offsets[offset_index + index] ||= find_nth_with_limit(index, 1).first
+ end
- def find_nth_with_limit(index, limit)
- if loaded?
- records[index, limit] || []
- else
- relation = if order_values.empty? && primary_key
- order(arel_attribute(primary_key).asc)
+ def find_nth_with_limit(index, limit)
+ if loaded?
+ records[index, limit] || []
else
- self
+ relation = if order_values.empty? && primary_key
+ order(arel_attribute(primary_key).asc)
+ else
+ self
+ end
+
+ relation = relation.offset(offset_index + index) unless index.zero?
+ relation.limit(limit).to_a
end
-
- relation = relation.offset(offset_index + index) unless index.zero?
- relation.limit(limit).to_a
end
- end
- def find_nth_from_last(index)
- if loaded?
- records[-index]
- else
- relation = if order_values.empty? && primary_key
- order(arel_attribute(primary_key).asc)
+ def find_nth_from_last(index)
+ if loaded?
+ records[-index]
else
- self
+ relation = if order_values.empty? && primary_key
+ order(arel_attribute(primary_key).asc)
+ else
+ self
+ end
+
+ relation.to_a[-index]
+ # TODO: can be made more performant on large result sets by
+ # for instance, last(index)[-index] (which would require
+ # refactoring the last(n) finder method to make test suite pass),
+ # or by using a combination of reverse_order, limit, and offset,
+ # e.g., reverse_order.offset(index-1).first
end
-
- relation.to_a[-index]
- # TODO: can be made more performant on large result sets by
- # for instance, last(index)[-index] (which would require
- # refactoring the last(n) finder method to make test suite pass),
- # or by using a combination of reverse_order, limit, and offset,
- # e.g., reverse_order.offset(index-1).first
end
- end
-
- private
- def find_last(limit)
- limit ? records.last(limit) : records.last
- end
+ def find_last(limit)
+ limit ? records.last(limit) : records.last
+ end
end
end
diff --git a/activerecord/lib/active_record/relation/predicate_builder.rb b/activerecord/lib/active_record/relation/predicate_builder.rb
index 780a1ee422..f9f6ff403e 100644
--- a/activerecord/lib/active_record/relation/predicate_builder.rb
+++ b/activerecord/lib/active_record/relation/predicate_builder.rb
@@ -66,6 +66,8 @@ module ActiveRecord
handler_for(value).call(attribute, value)
end
+ # TODO Change this to private once we've dropped Ruby 2.2 support.
+ # Workaround for Ruby 2.2 "private attribute?" warning.
protected
attr_reader :table
diff --git a/activerecord/lib/active_record/relation/predicate_builder/array_handler.rb b/activerecord/lib/active_record/relation/predicate_builder/array_handler.rb
index 6400caba06..88b6c37d43 100644
--- a/activerecord/lib/active_record/relation/predicate_builder/array_handler.rb
+++ b/activerecord/lib/active_record/relation/predicate_builder/array_handler.rb
@@ -29,6 +29,8 @@ module ActiveRecord
array_predicates.inject { |composite, predicate| composite.or(predicate) }
end
+ # TODO Change this to private once we've dropped Ruby 2.2 support.
+ # Workaround for Ruby 2.2 "private attribute?" warning.
protected
attr_reader :predicate_builder
diff --git a/activerecord/lib/active_record/relation/predicate_builder/association_query_handler.rb b/activerecord/lib/active_record/relation/predicate_builder/association_query_handler.rb
index 7e20cb2c63..dfffbbd14b 100644
--- a/activerecord/lib/active_record/relation/predicate_builder/association_query_handler.rb
+++ b/activerecord/lib/active_record/relation/predicate_builder/association_query_handler.rb
@@ -28,6 +28,8 @@ module ActiveRecord
predicate_builder.build_from_hash(queries)
end
+ # TODO Change this to private once we've dropped Ruby 2.2 support.
+ # Workaround for Ruby 2.2 "private attribute?" warning.
protected
attr_reader :predicate_builder
diff --git a/activerecord/lib/active_record/relation/predicate_builder/base_handler.rb b/activerecord/lib/active_record/relation/predicate_builder/base_handler.rb
index 65c5159704..3bb1037885 100644
--- a/activerecord/lib/active_record/relation/predicate_builder/base_handler.rb
+++ b/activerecord/lib/active_record/relation/predicate_builder/base_handler.rb
@@ -9,6 +9,8 @@ module ActiveRecord
predicate_builder.build(attribute, value.id)
end
+ # TODO Change this to private once we've dropped Ruby 2.2 support.
+ # Workaround for Ruby 2.2 "private attribute?" warning.
protected
attr_reader :predicate_builder
diff --git a/activerecord/lib/active_record/relation/predicate_builder/class_handler.rb b/activerecord/lib/active_record/relation/predicate_builder/class_handler.rb
index 0a6574fcf1..810937ead6 100644
--- a/activerecord/lib/active_record/relation/predicate_builder/class_handler.rb
+++ b/activerecord/lib/active_record/relation/predicate_builder/class_handler.rb
@@ -10,6 +10,8 @@ module ActiveRecord
predicate_builder.build(attribute, value.name)
end
+ # TODO Change this to private once we've dropped Ruby 2.2 support.
+ # Workaround for Ruby 2.2 "private attribute?" warning.
protected
attr_reader :predicate_builder
diff --git a/activerecord/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb b/activerecord/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb
index 0c7f92b3d0..335124c952 100644
--- a/activerecord/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb
+++ b/activerecord/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb
@@ -21,6 +21,8 @@ module ActiveRecord
end
end
+ # TODO Change this to private once we've dropped Ruby 2.2 support.
+ # Workaround for Ruby 2.2 "private attribute?" warning.
protected
attr_reader :predicate_builder
diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb
index 9fbbe32e7f..5f5d8ceea3 100644
--- a/activerecord/lib/active_record/relation/query_methods.rb
+++ b/activerecord/lib/active_record/relation/query_methods.rb
@@ -4,7 +4,6 @@ require "active_record/relation/where_clause"
require "active_record/relation/where_clause_factory"
require "active_model/forbidden_attributes_protection"
require "active_support/core_ext/string/filters"
-require "active_support/core_ext/regexp"
module ActiveRecord
module QueryMethods
@@ -242,7 +241,16 @@ module ActiveRecord
# Model.select(:field).first.other_field
# # => ActiveModel::MissingAttributeError: missing attribute: other_field
def select(*fields)
- return super if block_given?
+ if block_given?
+ if fields.any?
+ ActiveSupport::Deprecation.warn(<<-WARNING.squish)
+ When select is called with a block, it ignores other arguments. This behavior is now deprecated and will result in an ArgumentError in Rails 5.1. You can safely remove the arguments to resolve the deprecation warning because they do not have any effect on the output of the call to the select method with a block.
+ WARNING
+ end
+
+ return super()
+ end
+
raise ArgumentError, "Call this with at least one field" if fields.empty?
spawn._select!(*fields)
end
@@ -755,7 +763,7 @@ module ActiveRecord
# end
#
def none
- where("1=0").extending!(NullRelation)
+ spawn.none!
end
def none! # :nodoc:
diff --git a/activerecord/lib/active_record/relation/spawn_methods.rb b/activerecord/lib/active_record/relation/spawn_methods.rb
index 190e339ea8..ada89b5ec3 100644
--- a/activerecord/lib/active_record/relation/spawn_methods.rb
+++ b/activerecord/lib/active_record/relation/spawn_methods.rb
@@ -66,7 +66,7 @@ module ActiveRecord
private
- def relation_with(values) # :nodoc:
+ def relation_with(values)
result = Relation.create(klass, table, predicate_builder, values)
result.extend(*extending_values) if extending_values.any?
result
diff --git a/activerecord/lib/active_record/relation/where_clause.rb b/activerecord/lib/active_record/relation/where_clause.rb
index 402f8acfd1..ef0d059d1c 100644
--- a/activerecord/lib/active_record/relation/where_clause.rb
+++ b/activerecord/lib/active_record/relation/where_clause.rb
@@ -84,6 +84,8 @@ module ActiveRecord
@empty ||= new([], [])
end
+ # TODO Change this to private once we've dropped Ruby 2.2 support.
+ # Workaround for Ruby 2.2 "private attribute?" warning.
protected
attr_reader :predicates
diff --git a/activerecord/lib/active_record/relation/where_clause_factory.rb b/activerecord/lib/active_record/relation/where_clause_factory.rb
index 1e7deeffad..737bc278bd 100644
--- a/activerecord/lib/active_record/relation/where_clause_factory.rb
+++ b/activerecord/lib/active_record/relation/where_clause_factory.rb
@@ -27,6 +27,8 @@ module ActiveRecord
WhereClause.new(parts, binds || [])
end
+ # TODO Change this to private once we've dropped Ruby 2.2 support.
+ # Workaround for Ruby 2.2 "private attribute?" warning.
protected
attr_reader :klass, :predicate_builder
diff --git a/activerecord/lib/active_record/sanitization.rb b/activerecord/lib/active_record/sanitization.rb
index e7c0936984..647834b12e 100644
--- a/activerecord/lib/active_record/sanitization.rb
+++ b/activerecord/lib/active_record/sanitization.rb
@@ -1,11 +1,10 @@
-require "active_support/core_ext/regexp"
module ActiveRecord
module Sanitization
extend ActiveSupport::Concern
module ClassMethods
- protected
+ private
# Accepts an array or string of SQL conditions and sanitizes
# them into a valid SQL fragment for a WHERE clause.
@@ -21,7 +20,7 @@ module ActiveRecord
#
# sanitize_sql_for_conditions("name='foo''bar' and group_id='4'")
# # => "name='foo''bar' and group_id='4'"
- def sanitize_sql_for_conditions(condition)
+ def sanitize_sql_for_conditions(condition) # :doc:
return nil if condition.blank?
case condition
@@ -47,7 +46,7 @@ module ActiveRecord
#
# sanitize_sql_for_assignment("name=NULL and group_id='4'")
# # => "name=NULL and group_id='4'"
- def sanitize_sql_for_assignment(assignments, default_table_name = self.table_name)
+ def sanitize_sql_for_assignment(assignments, default_table_name = self.table_name) # :doc:
case assignments
when Array; sanitize_sql_array(assignments)
when Hash; sanitize_sql_hash_for_assignment(assignments, default_table_name)
@@ -63,7 +62,7 @@ module ActiveRecord
#
# sanitize_sql_for_order("id ASC")
# # => "id ASC"
- def sanitize_sql_for_order(condition)
+ def sanitize_sql_for_order(condition) # :doc:
if condition.is_a?(Array) && condition.first.to_s.include?("?")
sanitize_sql_array(condition)
else
@@ -86,7 +85,7 @@ module ActiveRecord
#
# { address: Address.new("813 abc st.", "chicago") }
# # => { address_street: "813 abc st.", address_city: "chicago" }
- def expand_hash_conditions_for_aggregates(attrs)
+ def expand_hash_conditions_for_aggregates(attrs) # :doc:
expanded_attrs = {}
attrs.each do |attr, value|
if aggregation = reflect_on_aggregation(attr.to_sym)
@@ -109,7 +108,7 @@ module ActiveRecord
#
# sanitize_sql_hash_for_assignment({ status: nil, group_id: 1 }, "posts")
# # => "`posts`.`status` = NULL, `posts`.`group_id` = 1"
- def sanitize_sql_hash_for_assignment(attrs, table)
+ def sanitize_sql_hash_for_assignment(attrs, table) # :doc:
c = connection
attrs.map do |attr, value|
value = type_for_attribute(attr.to_s).serialize(value)
@@ -131,7 +130,7 @@ module ActiveRecord
#
# sanitize_sql_like("snake_cased_string", "!")
# # => "snake!_cased!_string"
- def sanitize_sql_like(string, escape_character = "\\")
+ def sanitize_sql_like(string, escape_character = "\\") # :doc:
pattern = Regexp.union(escape_character, "%", "_")
string.gsub(pattern) { |x| [escape_character, x].join }
end
@@ -147,7 +146,7 @@ module ActiveRecord
#
# sanitize_sql_array(["name='%s' and group_id='%s'", "foo'bar", 4])
# # => "name='foo''bar' and group_id='4'"
- def sanitize_sql_array(ary)
+ def sanitize_sql_array(ary) # :doc:
statement, *values = ary
if values.first.is_a?(Hash) && /:\w+/.match?(statement)
replace_named_bind_variables(statement, values.first)
@@ -160,7 +159,7 @@ module ActiveRecord
end
end
- def replace_bind_variables(statement, values) # :nodoc:
+ def replace_bind_variables(statement, values)
raise_if_bind_arity_mismatch(statement, statement.count("?"), values.size)
bound = values.dup
c = connection
@@ -169,7 +168,7 @@ module ActiveRecord
end
end
- def replace_bind_variable(value, c = connection) # :nodoc:
+ def replace_bind_variable(value, c = connection)
if ActiveRecord::Relation === value
value.to_sql
else
@@ -177,7 +176,7 @@ module ActiveRecord
end
end
- def replace_named_bind_variables(statement, bind_vars) # :nodoc:
+ def replace_named_bind_variables(statement, bind_vars)
statement.gsub(/(:?):([a-zA-Z]\w*)/) do |match|
if $1 == ":" # skip postgresql casts
match # return the whole match
@@ -189,7 +188,7 @@ module ActiveRecord
end
end
- def quote_bound_value(value, c = connection) # :nodoc:
+ def quote_bound_value(value, c = connection)
if value.respond_to?(:map) && !value.acts_like?(:string)
if value.respond_to?(:empty?) && value.empty?
c.quote(nil)
@@ -201,7 +200,7 @@ module ActiveRecord
end
end
- def raise_if_bind_arity_mismatch(statement, expected, provided) # :nodoc:
+ def raise_if_bind_arity_mismatch(statement, expected, provided)
unless expected == provided
raise PreparedStatementInvalid, "wrong number of bind variables (#{provided} for #{expected}) in: #{statement}"
end
diff --git a/activerecord/lib/active_record/schema.rb b/activerecord/lib/active_record/schema.rb
index 784a02d2c3..7a2bc9c8af 100644
--- a/activerecord/lib/active_record/schema.rb
+++ b/activerecord/lib/active_record/schema.rb
@@ -40,7 +40,7 @@ module ActiveRecord
# ActiveRecord::Schema.define(version: 20380119000001) do
# ...
# end
- def self.define(info={}, &block)
+ def self.define(info = {}, &block)
new.define(info, &block)
end
@@ -61,7 +61,7 @@ module ActiveRecord
#
# ActiveRecord::Schema.new.migrations_paths
# # => ["db/migrate"] # Rails migration path by default.
- def migrations_paths # :nodoc:
+ def migrations_paths
ActiveRecord::Migrator.migrations_paths
end
end
diff --git a/activerecord/lib/active_record/schema_dumper.rb b/activerecord/lib/active_record/schema_dumper.rb
index c1c6519cfa..12289511b7 100644
--- a/activerecord/lib/active_record/schema_dumper.rb
+++ b/activerecord/lib/active_record/schema_dumper.rb
@@ -17,7 +17,7 @@ module ActiveRecord
@@ignore_tables = []
class << self
- def dump(connection=ActiveRecord::Base.connection, stream=STDOUT, config = ActiveRecord::Base)
+ def dump(connection = ActiveRecord::Base.connection, stream = STDOUT, config = ActiveRecord::Base)
new(connection, generate_options(config)).dump(stream)
stream
end
@@ -162,7 +162,7 @@ HEADER
if (indexes = @connection.indexes(table)).any?
add_index_statements = indexes.map do |index|
table_name = remove_prefix_and_suffix(index.table).inspect
- " add_index #{([table_name]+index_parts(index)).join(', ')}"
+ " add_index #{([table_name] + index_parts(index)).join(', ')}"
end
stream.puts add_index_statements.sort.join("\n")
diff --git a/activerecord/lib/active_record/scoping.rb b/activerecord/lib/active_record/scoping.rb
index d1bd1cd89a..7c00e7e4ed 100644
--- a/activerecord/lib/active_record/scoping.rb
+++ b/activerecord/lib/active_record/scoping.rb
@@ -33,7 +33,7 @@ module ActiveRecord
def populate_with_current_scope_attributes # :nodoc:
return unless self.class.scope_attributes?
- self.class.scope_attributes.each do |att,value|
+ self.class.scope_attributes.each do |att, value|
send("#{att}=", value) if respond_to?("#{att}=")
end
end
diff --git a/activerecord/lib/active_record/scoping/default.rb b/activerecord/lib/active_record/scoping/default.rb
index 9d8253faa3..2daa48859a 100644
--- a/activerecord/lib/active_record/scoping/default.rb
+++ b/activerecord/lib/active_record/scoping/default.rb
@@ -44,7 +44,7 @@ module ActiveRecord
self.current_scope = nil
end
- protected
+ private
# Use this macro in your model to set a default scope for all operations on
# the model.
@@ -87,7 +87,7 @@ module ActiveRecord
# # Should return a scope, you can call 'super' here etc.
# end
# end
- def default_scope(scope = nil)
+ def default_scope(scope = nil) # :doc:
scope = Proc.new if block_given?
if scope.is_a?(Relation) || !scope.respond_to?(:call)
@@ -101,7 +101,7 @@ module ActiveRecord
self.default_scopes += [scope]
end
- def build_default_scope(base_rel = nil) # :nodoc:
+ def build_default_scope(base_rel = nil)
return if abstract_class?
if default_scope_override.nil?
@@ -122,18 +122,18 @@ module ActiveRecord
end
end
- def ignore_default_scope? # :nodoc:
+ def ignore_default_scope?
ScopeRegistry.value_for(:ignore_default_scope, base_class)
end
- def ignore_default_scope=(ignore) # :nodoc:
+ def ignore_default_scope=(ignore)
ScopeRegistry.set_value_for(:ignore_default_scope, base_class, ignore)
end
# The ignore_default_scope flag is used to prevent an infinite recursion
# situation where a default scope references a scope which has a default
# scope which references a scope...
- def evaluate_default_scope # :nodoc:
+ def evaluate_default_scope
return if ignore_default_scope?
begin
diff --git a/activerecord/lib/active_record/scoping/named.rb b/activerecord/lib/active_record/scoping/named.rb
index 094c0e9c6f..27cdf8cb7e 100644
--- a/activerecord/lib/active_record/scoping/named.rb
+++ b/activerecord/lib/active_record/scoping/named.rb
@@ -42,7 +42,7 @@ module ActiveRecord
# Adds a class method for retrieving and querying objects.
# The method is intended to return an ActiveRecord::Relation
# object, which is composable with other scopes.
- # If it returns nil or false, an
+ # If it returns +nil+ or +false+, an
# {all}[rdoc-ref:Scoping::Named::ClassMethods#all] scope is returned instead.
#
# A \scope represents a narrowing of a database query, such as
@@ -171,14 +171,14 @@ module ActiveRecord
end
end
- protected
+ private
- def valid_scope_name?(name)
- if respond_to?(name, true) && logger
- logger.warn "Creating scope :#{name}. " \
- "Overwriting existing method #{self.name}.#{name}."
+ def valid_scope_name?(name)
+ if respond_to?(name, true) && logger
+ logger.warn "Creating scope :#{name}. " \
+ "Overwriting existing method #{self.name}.#{name}."
+ end
end
- end
end
end
end
diff --git a/activerecord/lib/active_record/statement_cache.rb b/activerecord/lib/active_record/statement_cache.rb
index 691940ab70..1877489e55 100644
--- a/activerecord/lib/active_record/statement_cache.rb
+++ b/activerecord/lib/active_record/statement_cache.rb
@@ -41,7 +41,7 @@ module ActiveRecord
class PartialQuery < Query # :nodoc:
def initialize(values)
@values = values
- @indexes = values.each_with_index.find_all { |thing,i|
+ @indexes = values.each_with_index.find_all { |thing, i|
Arel::Nodes::BindParam === thing
}.map(&:last)
end
@@ -68,7 +68,7 @@ module ActiveRecord
class BindMap # :nodoc:
def initialize(bound_attributes)
- @indexes = []
+ @indexes = []
@bound_attributes = bound_attributes
bound_attributes.each_with_index do |attr, i|
@@ -80,7 +80,7 @@ module ActiveRecord
def bind(values)
bas = @bound_attributes.dup
- @indexes.each_with_index { |offset,i| bas[offset] = bas[offset].with_cast_value(values[i]) }
+ @indexes.each_with_index { |offset, i| bas[offset] = bas[offset].with_cast_value(values[i]) }
bas
end
end
diff --git a/activerecord/lib/active_record/store.rb b/activerecord/lib/active_record/store.rb
index 066573192e..d4be20d999 100644
--- a/activerecord/lib/active_record/store.rb
+++ b/activerecord/lib/active_record/store.rb
@@ -121,18 +121,17 @@ module ActiveRecord
end
end
- protected
- def read_store_attribute(store_attribute, key)
+ private
+ def read_store_attribute(store_attribute, key) # :doc:
accessor = store_accessor_for(store_attribute)
accessor.read(self, store_attribute, key)
end
- def write_store_attribute(store_attribute, key, value)
+ def write_store_attribute(store_attribute, key, value) # :doc:
accessor = store_accessor_for(store_attribute)
accessor.write(self, store_attribute, key, value)
end
- private
def store_accessor_for(store_attribute)
type_for_attribute(store_attribute.to_s).accessor
end
diff --git a/activerecord/lib/active_record/table_metadata.rb b/activerecord/lib/active_record/table_metadata.rb
index 58184f3872..b618e5cfcd 100644
--- a/activerecord/lib/active_record/table_metadata.rb
+++ b/activerecord/lib/active_record/table_metadata.rb
@@ -64,6 +64,8 @@ module ActiveRecord
association && association.polymorphic?
end
+ # TODO Change this to private once we've dropped Ruby 2.2 support.
+ # Workaround for Ruby 2.2 "private attribute?" warning.
protected
attr_reader :klass, :arel_table, :association
diff --git a/activerecord/lib/active_record/tasks/database_tasks.rb b/activerecord/lib/active_record/tasks/database_tasks.rb
index a19913f2a8..c6204ac36f 100644
--- a/activerecord/lib/active_record/tasks/database_tasks.rb
+++ b/activerecord/lib/active_record/tasks/database_tasks.rb
@@ -40,7 +40,7 @@ module ActiveRecord
attr_writer :current_config, :db_dir, :migrations_paths, :fixtures_path, :root, :env, :seed_loader
attr_accessor :database_configuration
- LOCAL_HOSTS = ["127.0.0.1", "localhost"]
+ LOCAL_HOSTS = ["127.0.0.1", "localhost"]
def check_protected_environments!
unless ENV["DISABLE_DATABASE_ENVIRONMENT_CHECK"]
diff --git a/activerecord/lib/active_record/tasks/mysql_database_tasks.rb b/activerecord/lib/active_record/tasks/mysql_database_tasks.rb
index 3a5e0b8dfe..5cdb3d53f6 100644
--- a/activerecord/lib/active_record/tasks/mysql_database_tasks.rb
+++ b/activerecord/lib/active_record/tasks/mysql_database_tasks.rb
@@ -14,7 +14,7 @@ module ActiveRecord
connection.create_database configuration["database"], creation_options
establish_connection configuration
rescue ActiveRecord::StatementInvalid => error
- if /database exists/ === error.message
+ if error.message.include?("database exists")
raise DatabaseAlreadyExists
else
raise
diff --git a/activerecord/lib/active_record/tasks/postgresql_database_tasks.rb b/activerecord/lib/active_record/tasks/postgresql_database_tasks.rb
index a3a9430c03..4e9897f7b0 100644
--- a/activerecord/lib/active_record/tasks/postgresql_database_tasks.rb
+++ b/activerecord/lib/active_record/tasks/postgresql_database_tasks.rb
@@ -17,7 +17,7 @@ module ActiveRecord
configuration.merge("encoding" => encoding)
establish_connection configuration
rescue ActiveRecord::StatementInvalid => error
- if /database .* already exists/ === error.message
+ if /database .* already exists/.match?(error.message)
raise DatabaseAlreadyExists
else
raise
@@ -70,7 +70,7 @@ module ActiveRecord
def structure_load(filename)
set_psql_env
args = [ "-v", ON_ERROR_STOP_1, "-q", "-f", filename, configuration["database"] ]
- run_cmd("psql", args, "loading" )
+ run_cmd("psql", args, "loading")
end
private
diff --git a/activerecord/lib/active_record/timestamp.rb b/activerecord/lib/active_record/timestamp.rb
index 6641ab5df1..63100e38a1 100644
--- a/activerecord/lib/active_record/timestamp.rb
+++ b/activerecord/lib/active_record/timestamp.rb
@@ -74,7 +74,7 @@ module ActiveRecord
timestamp_attributes_for_update_in_model.each do |column|
column = column.to_s
- next if attribute_changed?(column)
+ next if will_save_change_to_attribute?(column)
write_attribute(column, current_time)
end
end
@@ -82,7 +82,7 @@ module ActiveRecord
end
def should_record_timestamps?
- record_timestamps && (!partial_writes? || changed?)
+ record_timestamps && (!partial_writes? || has_changes_to_save?)
end
def timestamp_attributes_for_create_in_model
diff --git a/activerecord/lib/active_record/touch_later.rb b/activerecord/lib/active_record/touch_later.rb
index c337a7532f..cacde9c881 100644
--- a/activerecord/lib/active_record/touch_later.rb
+++ b/activerecord/lib/active_record/touch_later.rb
@@ -25,7 +25,7 @@ module ActiveRecord
# touch the parents as we are not calling the after_save callbacks
self.class.reflect_on_all_associations(:belongs_to).each do |r|
if touch = r.options[:touch]
- ActiveRecord::Associations::Builder::BelongsTo.touch_record(self, r.foreign_key, r.name, touch, :touch_later)
+ ActiveRecord::Associations::Builder::BelongsTo.touch_record(self, changes_to_save, r.foreign_key, r.name, touch, :touch_later)
end
end
end
diff --git a/activerecord/lib/active_record/transactions.rb b/activerecord/lib/active_record/transactions.rb
index af3fc88282..f22acd0f77 100644
--- a/activerecord/lib/active_record/transactions.rb
+++ b/activerecord/lib/active_record/transactions.rb
@@ -407,10 +407,10 @@ module ActiveRecord
end
end
- protected
+ private
# Save the new record state and id of a record so it can be restored later if a transaction fails.
- def remember_transaction_record_state #:nodoc:
+ def remember_transaction_record_state
@_start_transaction_state[:id] = id
@_start_transaction_state.reverse_merge!(
new_record: @new_record,
@@ -421,18 +421,18 @@ module ActiveRecord
end
# Clear the new record state and id of a record.
- def clear_transaction_record_state #:nodoc:
+ def clear_transaction_record_state
@_start_transaction_state[:level] = (@_start_transaction_state[:level] || 0) - 1
force_clear_transaction_record_state if @_start_transaction_state[:level] < 1
end
# Force to clear the transaction record state.
- def force_clear_transaction_record_state #:nodoc:
+ def force_clear_transaction_record_state
@_start_transaction_state.clear
end
# Restore the new record state and id of a record that was previously saved by a call to save_record_state.
- def restore_transaction_record_state(force = false) #:nodoc:
+ def restore_transaction_record_state(force = false)
unless @_start_transaction_state.empty?
transaction_level = (@_start_transaction_state[:level] || 0) - 1
if transaction_level < 1 || force
@@ -450,31 +450,30 @@ module ActiveRecord
end
# Determine if a record was created or destroyed in a transaction. State should be one of :new_record or :destroyed.
- def transaction_record_state(state) #:nodoc:
+ def transaction_record_state(state)
@_start_transaction_state[state]
end
# Determine if a transaction included an action for :create, :update, or :destroy. Used in filtering callbacks.
- def transaction_include_any_action?(actions) #:nodoc:
+ def transaction_include_any_action?(actions)
actions.any? do |action|
case action
when :create
transaction_record_state(:new_record)
when :destroy
- destroyed?
+ defined?(@_trigger_destroy_callback) && @_trigger_destroy_callback
when :update
- !(transaction_record_state(:new_record) || destroyed?)
+ !(transaction_record_state(:new_record) || destroyed?) &&
+ (defined?(@_trigger_update_callback) && @_trigger_update_callback)
end
end
end
- private
-
- def set_transaction_state(state) # :nodoc:
+ def set_transaction_state(state)
@transaction_state = state
end
- def has_transactional_callbacks? # :nodoc:
+ def has_transactional_callbacks?
!_rollback_callbacks.empty? || !_commit_callbacks.empty? || !_before_commit_callbacks.empty?
end
diff --git a/activerecord/lib/active_record/type.rb b/activerecord/lib/active_record/type.rb
index 84373dddf2..4f632660a8 100644
--- a/activerecord/lib/active_record/type.rb
+++ b/activerecord/lib/active_record/type.rb
@@ -1,13 +1,14 @@
require "active_model/type"
-require "active_record/type/helpers"
-require "active_record/type/value"
require "active_record/type/internal/abstract_json"
require "active_record/type/internal/timezone"
require "active_record/type/date"
require "active_record/type/date_time"
+require "active_record/type/decimal_without_scale"
require "active_record/type/time"
+require "active_record/type/text"
+require "active_record/type/unsigned_integer"
require "active_record/type/serialized"
require "active_record/type/adapter_specific_registry"
@@ -50,16 +51,15 @@ module ActiveRecord
end
end
+ Helpers = ActiveModel::Type::Helpers
BigInteger = ActiveModel::Type::BigInteger
Binary = ActiveModel::Type::Binary
Boolean = ActiveModel::Type::Boolean
Decimal = ActiveModel::Type::Decimal
- DecimalWithoutScale = ActiveModel::Type::DecimalWithoutScale
Float = ActiveModel::Type::Float
Integer = ActiveModel::Type::Integer
String = ActiveModel::Type::String
- Text = ActiveModel::Type::Text
- UnsignedInteger = ActiveModel::Type::UnsignedInteger
+ Value = ActiveModel::Type::Value
register(:big_integer, Type::BigInteger, override: false)
register(:binary, Type::Binary, override: false)
diff --git a/activerecord/lib/active_record/type/adapter_specific_registry.rb b/activerecord/lib/active_record/type/adapter_specific_registry.rb
index d0f9581576..7cc866f7a7 100644
--- a/activerecord/lib/active_record/type/adapter_specific_registry.rb
+++ b/activerecord/lib/active_record/type/adapter_specific_registry.rb
@@ -50,6 +50,8 @@ module ActiveRecord
priority <=> other.priority
end
+ # TODO Change this to private once we've dropped Ruby 2.2 support.
+ # Workaround for Ruby 2.2 "private attribute?" warning.
protected
attr_reader :name, :block, :adapter, :override
@@ -110,6 +112,8 @@ module ActiveRecord
super | 4
end
+ # TODO Change this to private once we've dropped Ruby 2.2 support.
+ # Workaround for Ruby 2.2 "private attribute?" warning.
protected
attr_reader :options, :klass
diff --git a/activerecord/lib/active_record/type/decimal_without_scale.rb b/activerecord/lib/active_record/type/decimal_without_scale.rb
new file mode 100644
index 0000000000..7ce33e9cd3
--- /dev/null
+++ b/activerecord/lib/active_record/type/decimal_without_scale.rb
@@ -0,0 +1,9 @@
+module ActiveRecord
+ module Type
+ class DecimalWithoutScale < ActiveModel::Type::BigInteger # :nodoc:
+ def type
+ :decimal
+ end
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/type/helpers.rb b/activerecord/lib/active_record/type/helpers.rb
deleted file mode 100644
index a32ccd4bc3..0000000000
--- a/activerecord/lib/active_record/type/helpers.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-module ActiveRecord
- module Type
- Helpers = ActiveModel::Type::Helpers
- end
-end
diff --git a/activerecord/lib/active_record/type/internal/abstract_json.rb b/activerecord/lib/active_record/type/internal/abstract_json.rb
index 67028546e4..e19c5a14da 100644
--- a/activerecord/lib/active_record/type/internal/abstract_json.rb
+++ b/activerecord/lib/active_record/type/internal/abstract_json.rb
@@ -1,8 +1,8 @@
module ActiveRecord
module Type
module Internal # :nodoc:
- class AbstractJson < Type::Value # :nodoc:
- include Type::Helpers::Mutable
+ class AbstractJson < ActiveModel::Type::Value # :nodoc:
+ include ActiveModel::Type::Helpers::Mutable
def type
:json
diff --git a/activerecord/lib/active_record/type/serialized.rb b/activerecord/lib/active_record/type/serialized.rb
index ca12c83b1a..ac9134bfcb 100644
--- a/activerecord/lib/active_record/type/serialized.rb
+++ b/activerecord/lib/active_record/type/serialized.rb
@@ -1,7 +1,7 @@
module ActiveRecord
module Type
- class Serialized < DelegateClass(Type::Value) # :nodoc:
- include Type::Helpers::Mutable
+ class Serialized < DelegateClass(ActiveModel::Type::Value) # :nodoc:
+ include ActiveModel::Type::Helpers::Mutable
attr_reader :subtype, :coder
diff --git a/activerecord/lib/active_record/type/text.rb b/activerecord/lib/active_record/type/text.rb
new file mode 100644
index 0000000000..cb1949700a
--- /dev/null
+++ b/activerecord/lib/active_record/type/text.rb
@@ -0,0 +1,9 @@
+module ActiveRecord
+ module Type
+ class Text < ActiveModel::Type::String # :nodoc:
+ def type
+ :text
+ end
+ end
+ end
+end
diff --git a/activemodel/lib/active_model/type/unsigned_integer.rb b/activerecord/lib/active_record/type/unsigned_integer.rb
index 288fa23efe..9ae0109f9f 100644
--- a/activemodel/lib/active_model/type/unsigned_integer.rb
+++ b/activerecord/lib/active_record/type/unsigned_integer.rb
@@ -1,6 +1,6 @@
-module ActiveModel
+module ActiveRecord
module Type
- class UnsignedInteger < Integer # :nodoc:
+ class UnsignedInteger < ActiveModel::Type::Integer # :nodoc:
private
def max_value
diff --git a/activerecord/lib/active_record/type/value.rb b/activerecord/lib/active_record/type/value.rb
deleted file mode 100644
index 89ef29106b..0000000000
--- a/activerecord/lib/active_record/type/value.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-module ActiveRecord
- module Type
- class Value < ActiveModel::Type::Value; end
- end
-end
diff --git a/activerecord/lib/active_record/type_caster/connection.rb b/activerecord/lib/active_record/type_caster/connection.rb
index 6c54792e26..9f7bbe8843 100644
--- a/activerecord/lib/active_record/type_caster/connection.rb
+++ b/activerecord/lib/active_record/type_caster/connection.rb
@@ -12,6 +12,8 @@ module ActiveRecord
connection.type_cast_from_column(column, value)
end
+ # TODO Change this to private once we've dropped Ruby 2.2 support.
+ # Workaround for Ruby 2.2 "private attribute?" warning.
protected
attr_reader :table_name
diff --git a/activerecord/lib/active_record/type_caster/map.rb b/activerecord/lib/active_record/type_caster/map.rb
index 52529a6b42..9f79723125 100644
--- a/activerecord/lib/active_record/type_caster/map.rb
+++ b/activerecord/lib/active_record/type_caster/map.rb
@@ -11,6 +11,8 @@ module ActiveRecord
type.serialize(value)
end
+ # TODO Change this to private once we've dropped Ruby 2.2 support.
+ # Workaround for Ruby 2.2 "private attribute?" warning.
protected
attr_reader :types
diff --git a/activerecord/lib/active_record/validations.rb b/activerecord/lib/active_record/validations.rb
index ecaf04e39e..9633f226f0 100644
--- a/activerecord/lib/active_record/validations.rb
+++ b/activerecord/lib/active_record/validations.rb
@@ -40,13 +40,13 @@ module ActiveRecord
# The validation process on save can be skipped by passing <tt>validate: false</tt>.
# The regular {ActiveRecord::Base#save}[rdoc-ref:Persistence#save] method is replaced
# with this when the validations module is mixed in, which it is by default.
- def save(options={})
+ def save(options = {})
perform_validations(options) ? super : false
end
# Attempts to save the record just like {ActiveRecord::Base#save}[rdoc-ref:Base#save] but
# will raise an ActiveRecord::RecordInvalid exception instead of returning +false+ if the record is not valid.
- def save!(options={})
+ def save!(options = {})
perform_validations(options) ? super : raise_validation_error
end
@@ -68,7 +68,7 @@ module ActiveRecord
alias_method :validate, :valid?
- protected
+ private
def default_validation_context
new_record? ? :create : :update
@@ -78,7 +78,7 @@ module ActiveRecord
raise(RecordInvalid.new(self))
end
- def perform_validations(options={}) # :nodoc:
+ def perform_validations(options = {})
options[:validate] == false || valid?(options[:context])
end
end
diff --git a/activerecord/lib/active_record/validations/associated.rb b/activerecord/lib/active_record/validations/associated.rb
index b14db85167..c695965d7b 100644
--- a/activerecord/lib/active_record/validations/associated.rb
+++ b/activerecord/lib/active_record/validations/associated.rb
@@ -37,7 +37,7 @@ module ActiveRecord
#
# * <tt>:message</tt> - A custom error message (default is: "is invalid").
# * <tt>:on</tt> - Specifies the contexts where this validation is active.
- # Runs in all validation contexts by default (nil). You can pass a symbol
+ # Runs in all validation contexts by default +nil+. You can pass a symbol
# or an array of symbols. (e.g. <tt>on: :create</tt> or
# <tt>on: :custom_validation_context</tt> or
# <tt>on: [:create, :custom_validation_context]</tt>)
diff --git a/activerecord/lib/active_record/validations/presence.rb b/activerecord/lib/active_record/validations/presence.rb
index ad82ea66c4..ca5eda2f84 100644
--- a/activerecord/lib/active_record/validations/presence.rb
+++ b/activerecord/lib/active_record/validations/presence.rb
@@ -44,7 +44,7 @@ module ActiveRecord
# Configuration options:
# * <tt>:message</tt> - A custom error message (default is: "can't be blank").
# * <tt>:on</tt> - Specifies the contexts where this validation is active.
- # Runs in all validation contexts by default (nil). You can pass a symbol
+ # Runs in all validation contexts by default +nil+. You can pass a symbol
# or an array of symbols. (e.g. <tt>on: :create</tt> or
# <tt>on: :custom_validation_context</tt> or
# <tt>on: [:create, :custom_validation_context]</tt>)
diff --git a/activerecord/lib/active_record/validations/uniqueness.rb b/activerecord/lib/active_record/validations/uniqueness.rb
index 8c4930a81d..9e8edfbfaf 100644
--- a/activerecord/lib/active_record/validations/uniqueness.rb
+++ b/activerecord/lib/active_record/validations/uniqueness.rb
@@ -17,7 +17,7 @@ module ActiveRecord
relation = build_relation(finder_class, attribute, value)
if record.persisted?
if finder_class.primary_key
- relation = relation.where.not(finder_class.primary_key => record.id_was || record.id)
+ relation = relation.where.not(finder_class.primary_key => record.id_in_database || record.id)
else
raise UnknownPrimaryKey.new(finder_class, "Can not validate uniqueness for persisted record without primary key.")
end
@@ -33,13 +33,13 @@ module ActiveRecord
end
end
- protected
+ private
# The check for an existing value should be run from a class that
# isn't abstract. This means working down from the current class
# (self), to the first non-abstract class. Since classes don't know
# their subclasses, we have to build the hierarchy between self and
# the record's class.
- def find_finder_class_for(record) #:nodoc:
+ def find_finder_class_for(record)
class_hierarchy = [record.class]
while class_hierarchy.first != @klass
@@ -49,7 +49,7 @@ module ActiveRecord
class_hierarchy.detect { |klass| !klass.abstract_class? }
end
- def build_relation(klass, attribute, value) # :nodoc:
+ def build_relation(klass, attribute, value)
if reflection = klass._reflect_on_association(attribute)
attribute = reflection.foreign_key
value = value.attributes[reflection.klass.primary_key] unless value.nil?
@@ -85,11 +85,10 @@ module ActiveRecord
def scope_relation(record, relation)
Array(options[:scope]).each do |scope_item|
- if reflection = record.class._reflect_on_association(scope_item)
- scope_value = record.send(reflection.foreign_key)
- scope_item = reflection.foreign_key
+ scope_value = if record.class._reflect_on_association(scope_item)
+ record.association(scope_item).reader
else
- scope_value = record._read_attribute(scope_item)
+ record._read_attribute(scope_item)
end
relation = relation.where(scope_item => scope_value)
end
diff --git a/activerecord/lib/rails/generators/active_record/migration/migration_generator.rb b/activerecord/lib/rails/generators/active_record/migration/migration_generator.rb
index 76ed25ea75..8511531af7 100644
--- a/activerecord/lib/rails/generators/active_record/migration/migration_generator.rb
+++ b/activerecord/lib/rails/generators/active_record/migration/migration_generator.rb
@@ -1,5 +1,4 @@
require "rails/generators/active_record"
-require "active_support/core_ext/regexp"
module ActiveRecord
module Generators # :nodoc:
@@ -14,9 +13,13 @@ module ActiveRecord
migration_template @migration_template, "db/migrate/#{file_name}.rb"
end
+ # TODO Change this to private once we've dropped Ruby 2.2 support.
+ # Workaround for Ruby 2.2 "private attribute?" warning.
protected
attr_reader :migration_action, :join_tables
+ private
+
# Sets the default migration template that is being used for the generation of the migration.
# Depending on command line arguments, the migration template and the table name instance
# variables are set up.
@@ -53,7 +56,6 @@ module ActiveRecord
end.to_sym
end
- private
def attributes_with_index
attributes.select { |a| !a.reference? && a.has_index? }
end
diff --git a/activerecord/lib/rails/generators/active_record/model/model_generator.rb b/activerecord/lib/rails/generators/active_record/model/model_generator.rb
index f1ddc61688..61a8d3c100 100644
--- a/activerecord/lib/rails/generators/active_record/model/model_generator.rb
+++ b/activerecord/lib/rails/generators/active_record/model/model_generator.rb
@@ -33,7 +33,7 @@ module ActiveRecord
hook_for :test_framework
- protected
+ private
def attributes_with_index
attributes.select { |a| !a.reference? && a.has_index? }
diff --git a/activerecord/test/active_record/connection_adapters/fake_adapter.rb b/activerecord/test/active_record/connection_adapters/fake_adapter.rb
index 43c817e057..b0d8050721 100644
--- a/activerecord/test/active_record/connection_adapters/fake_adapter.rb
+++ b/activerecord/test/active_record/connection_adapters/fake_adapter.rb
@@ -9,7 +9,7 @@ module ActiveRecord
class FakeAdapter < AbstractAdapter
attr_accessor :data_sources, :primary_keys
- @columns = Hash.new { |h,k| h[k] = [] }
+ @columns = Hash.new { |h, k| h[k] = [] }
class << self
attr_reader :columns
end
diff --git a/activerecord/test/assets/schema_dump_5_1.yml b/activerecord/test/assets/schema_dump_5_1.yml
new file mode 100644
index 0000000000..f37977daf2
--- /dev/null
+++ b/activerecord/test/assets/schema_dump_5_1.yml
@@ -0,0 +1,345 @@
+--- !ruby/object:ActiveRecord::ConnectionAdapters::SchemaCache
+columns:
+ posts:
+ - &1 !ruby/object:ActiveRecord::ConnectionAdapters::Column
+ name: id
+ table_name: posts
+ sql_type_metadata: !ruby/object:ActiveRecord::ConnectionAdapters::SqlTypeMetadata
+ sql_type: INTEGER
+ type: :integer
+ limit:
+ precision:
+ scale:
+ 'null': false
+ default:
+ default_function:
+ collation:
+ comment:
+ - &2 !ruby/object:ActiveRecord::ConnectionAdapters::Column
+ name: author_id
+ table_name: posts
+ sql_type_metadata: !ruby/object:ActiveRecord::ConnectionAdapters::SqlTypeMetadata
+ sql_type: integer
+ type: :integer
+ limit:
+ precision:
+ scale:
+ 'null': true
+ default:
+ default_function:
+ collation:
+ comment:
+ - &3 !ruby/object:ActiveRecord::ConnectionAdapters::Column
+ name: title
+ table_name: posts
+ sql_type_metadata: !ruby/object:ActiveRecord::ConnectionAdapters::SqlTypeMetadata
+ sql_type: varchar
+ type: :string
+ limit:
+ precision:
+ scale:
+ 'null': false
+ default:
+ default_function:
+ collation:
+ comment:
+ - &4 !ruby/object:ActiveRecord::ConnectionAdapters::Column
+ name: body
+ table_name: posts
+ sql_type_metadata: !ruby/object:ActiveRecord::ConnectionAdapters::SqlTypeMetadata
+ sql_type: text
+ type: :text
+ limit:
+ precision:
+ scale:
+ 'null': false
+ default:
+ default_function:
+ collation:
+ comment:
+ - &5 !ruby/object:ActiveRecord::ConnectionAdapters::Column
+ name: type
+ table_name: posts
+ sql_type_metadata: !ruby/object:ActiveRecord::ConnectionAdapters::SqlTypeMetadata
+ sql_type: varchar
+ type: :string
+ limit:
+ precision:
+ scale:
+ 'null': true
+ default:
+ default_function:
+ collation:
+ comment:
+ - &6 !ruby/object:ActiveRecord::ConnectionAdapters::Column
+ name: comments_count
+ table_name: posts
+ sql_type_metadata: !ruby/object:ActiveRecord::ConnectionAdapters::SqlTypeMetadata
+ sql_type: integer
+ type: :integer
+ limit:
+ precision:
+ scale:
+ 'null': true
+ default: '0'
+ default_function:
+ collation:
+ comment:
+ - &7 !ruby/object:ActiveRecord::ConnectionAdapters::Column
+ name: taggings_with_delete_all_count
+ table_name: posts
+ sql_type_metadata: !ruby/object:ActiveRecord::ConnectionAdapters::SqlTypeMetadata
+ sql_type: integer
+ type: :integer
+ limit:
+ precision:
+ scale:
+ 'null': true
+ default: '0'
+ default_function:
+ collation:
+ comment:
+ - &8 !ruby/object:ActiveRecord::ConnectionAdapters::Column
+ name: taggings_with_destroy_count
+ table_name: posts
+ sql_type_metadata: !ruby/object:ActiveRecord::ConnectionAdapters::SqlTypeMetadata
+ sql_type: integer
+ type: :integer
+ limit:
+ precision:
+ scale:
+ 'null': true
+ default: '0'
+ default_function:
+ collation:
+ comment:
+ - &9 !ruby/object:ActiveRecord::ConnectionAdapters::Column
+ name: tags_count
+ table_name: posts
+ sql_type_metadata: !ruby/object:ActiveRecord::ConnectionAdapters::SqlTypeMetadata
+ sql_type: integer
+ type: :integer
+ limit:
+ precision:
+ scale:
+ 'null': true
+ default: '0'
+ default_function:
+ collation:
+ comment:
+ - &10 !ruby/object:ActiveRecord::ConnectionAdapters::Column
+ name: tags_with_destroy_count
+ table_name: posts
+ sql_type_metadata: !ruby/object:ActiveRecord::ConnectionAdapters::SqlTypeMetadata
+ sql_type: integer
+ type: :integer
+ limit:
+ precision:
+ scale:
+ 'null': true
+ default: '0'
+ default_function:
+ collation:
+ comment:
+ - &11 !ruby/object:ActiveRecord::ConnectionAdapters::Column
+ name: tags_with_nullify_count
+ table_name: posts
+ sql_type_metadata: !ruby/object:ActiveRecord::ConnectionAdapters::SqlTypeMetadata
+ sql_type: integer
+ type: :integer
+ limit:
+ precision:
+ scale:
+ 'null': true
+ default: '0'
+ default_function:
+ collation:
+ comment:
+columns_hash:
+ posts:
+ id: *1
+ author_id: *2
+ title: *3
+ body: *4
+ type: *5
+ comments_count: *6
+ taggings_with_delete_all_count: *7
+ taggings_with_destroy_count: *8
+ tags_count: *9
+ tags_with_destroy_count: *10
+ tags_with_nullify_count: *11
+primary_keys:
+ posts: id
+data_sources:
+ ar_internal_metadata: true
+ table_with_autoincrement: true
+ accounts: true
+ admin_accounts: true
+ admin_users: true
+ aircraft: true
+ articles: true
+ articles_magazines: true
+ articles_tags: true
+ audit_logs: true
+ authors: true
+ author_addresses: true
+ author_favorites: true
+ auto_id_tests: true
+ binaries: true
+ birds: true
+ books: true
+ booleans: true
+ bulbs: true
+ CamelCase: true
+ cars: true
+ carriers: true
+ categories: true
+ categories_posts: true
+ categorizations: true
+ citations: true
+ clubs: true
+ collections: true
+ colnametests: true
+ columns: true
+ comments: true
+ companies: true
+ content: true
+ content_positions: true
+ vegetables: true
+ computers: true
+ computers_developers: true
+ contracts: true
+ customers: true
+ customer_carriers: true
+ dashboards: true
+ developers: true
+ developers_projects: true
+ dog_lovers: true
+ dogs: true
+ doubloons: true
+ edges: true
+ engines: true
+ entrants: true
+ essays: true
+ events: true
+ eyes: true
+ funny_jokes: true
+ cold_jokes: true
+ friendships: true
+ goofy_string_id: true
+ having: true
+ guids: true
+ guitars: true
+ inept_wizards: true
+ integer_limits: true
+ invoices: true
+ iris: true
+ items: true
+ jobs: true
+ jobs_pool: true
+ keyboards: true
+ legacy_things: true
+ lessons: true
+ lessons_students: true
+ students: true
+ lint_models: true
+ line_items: true
+ lions: true
+ lock_without_defaults: true
+ lock_without_defaults_cust: true
+ magazines: true
+ mateys: true
+ members: true
+ member_details: true
+ member_friends: true
+ memberships: true
+ member_types: true
+ mentors: true
+ minivans: true
+ minimalistics: true
+ mixed_case_monkeys: true
+ mixins: true
+ movies: true
+ notifications: true
+ numeric_data: true
+ orders: true
+ organizations: true
+ owners: true
+ paint_colors: true
+ paint_textures: true
+ parrots: true
+ parrots_pirates: true
+ parrots_treasures: true
+ people: true
+ peoples_treasures: true
+ personal_legacy_things: true
+ pets: true
+ pets_treasures: true
+ pirates: true
+ posts: true
+ serialized_posts: true
+ images: true
+ price_estimates: true
+ products: true
+ product_types: true
+ projects: true
+ randomly_named_table1: true
+ randomly_named_table2: true
+ randomly_named_table3: true
+ ratings: true
+ readers: true
+ references: true
+ shape_expressions: true
+ ships: true
+ ship_parts: true
+ prisoners: true
+ shop_accounts: true
+ speedometers: true
+ sponsors: true
+ string_key_objects: true
+ subscribers: true
+ subscriptions: true
+ tags: true
+ taggings: true
+ tasks: true
+ topics: true
+ toys: true
+ traffic_lights: true
+ treasures: true
+ tuning_pegs: true
+ tyres: true
+ variants: true
+ vertices: true
+ warehouse-things: true
+ circles: true
+ squares: true
+ triangles: true
+ non_poly_ones: true
+ non_poly_twos: true
+ men: true
+ faces: true
+ interests: true
+ zines: true
+ wheels: true
+ countries: true
+ treaties: true
+ countries_treaties: true
+ liquid: true
+ molecules: true
+ electrons: true
+ weirds: true
+ nodes: true
+ trees: true
+ hotels: true
+ departments: true
+ cake_designers: true
+ drink_designers: true
+ chefs: true
+ recipes: true
+ records: true
+ overloaded_types: true
+ users: true
+ test_with_keyword_column_name: true
+ fk_test_has_pk: true
+ fk_test_has_fk: true
+version: 0
diff --git a/activerecord/test/cases/adapter_test.rb b/activerecord/test/cases/adapter_test.rb
index 8fa0645b0f..3fce0a1df1 100644
--- a/activerecord/test/cases/adapter_test.rb
+++ b/activerecord/test/cases/adapter_test.rb
@@ -42,8 +42,10 @@ module ActiveRecord
def test_table_exists?
ActiveSupport::Deprecation.silence do
assert @connection.table_exists?("accounts")
- assert !@connection.table_exists?("nonexistingtable")
- assert !@connection.table_exists?(nil)
+ assert @connection.table_exists?(:accounts)
+ assert_not @connection.table_exists?("nonexistingtable")
+ assert_not @connection.table_exists?("'")
+ assert_not @connection.table_exists?(nil)
end
end
@@ -63,26 +65,22 @@ module ActiveRecord
assert @connection.data_source_exists?("accounts")
assert @connection.data_source_exists?(:accounts)
assert_not @connection.data_source_exists?("nonexistingtable")
+ assert_not @connection.data_source_exists?("'")
assert_not @connection.data_source_exists?(nil)
end
def test_indexes
idx_name = "accounts_idx"
- if @connection.respond_to?(:indexes)
- indexes = @connection.indexes("accounts")
- assert indexes.empty?
-
- @connection.add_index :accounts, :firm_id, name: idx_name
- indexes = @connection.indexes("accounts")
- assert_equal "accounts", indexes.first.table
- assert_equal idx_name, indexes.first.name
- assert !indexes.first.unique
- assert_equal ["firm_id"], indexes.first.columns
- else
- warn "#{@connection.class} does not respond to #indexes"
- end
+ indexes = @connection.indexes("accounts")
+ assert indexes.empty?
+ @connection.add_index :accounts, :firm_id, name: idx_name
+ indexes = @connection.indexes("accounts")
+ assert_equal "accounts", indexes.first.table
+ assert_equal idx_name, indexes.first.name
+ assert !indexes.first.unique
+ assert_equal ["firm_id"], indexes.first.columns
ensure
@connection.remove_index(:accounts, name: idx_name) rescue nil
end
@@ -184,6 +182,14 @@ module ActiveRecord
assert_not_nil error.cause
end
+ def test_not_null_violations_are_translated_to_specific_exception
+ error = assert_raises(ActiveRecord::NotNullViolation) do
+ Post.create
+ end
+
+ assert_not_nil error.cause
+ end
+
unless current_adapter?(:SQLite3Adapter)
def test_foreign_key_violations_are_translated_to_specific_exception
error = assert_raises(ActiveRecord::InvalidForeignKey) do
@@ -220,6 +226,14 @@ module ActiveRecord
assert_not_nil error.cause
end
+
+ def test_numeric_value_out_of_ranges_are_translated_to_specific_exception
+ error = assert_raises(ActiveRecord::RangeError) do
+ Book.connection.create("INSERT INTO books(author_id) VALUES (2147483648)")
+ end
+
+ assert_not_nil error.cause
+ end
end
def test_disable_referential_integrity
@@ -271,13 +285,13 @@ module ActiveRecord
unless current_adapter?(:PostgreSQLAdapter)
def test_log_invalid_encoding
- error = assert_raise ActiveRecord::StatementInvalid do
+ error = assert_raises RuntimeError do
@connection.send :log, "SELECT 'ы' FROM DUAL" do
raise "ы".force_encoding(Encoding::ASCII_8BIT)
end
end
- assert_not_nil error.cause
+ assert_not_nil error.message
end
end
diff --git a/activerecord/test/cases/adapters/mysql2/active_schema_test.rb b/activerecord/test/cases/adapters/mysql2/active_schema_test.rb
index a70eb5a094..2a528b2cb1 100644
--- a/activerecord/test/cases/adapters/mysql2/active_schema_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/active_schema_test.rb
@@ -28,12 +28,15 @@ class Mysql2ActiveSchemaTest < ActiveRecord::Mysql2TestCase
expected = "CREATE INDEX `index_people_on_last_name_and_first_name` ON `people` (`last_name`(15), `first_name`(15)) "
assert_equal expected, add_index(:people, [:last_name, :first_name], length: 15)
+ assert_equal expected, add_index(:people, ["last_name", "first_name"], length: 15)
expected = "CREATE INDEX `index_people_on_last_name_and_first_name` ON `people` (`last_name`(15), `first_name`) "
assert_equal expected, add_index(:people, [:last_name, :first_name], length: { last_name: 15 })
+ assert_equal expected, add_index(:people, ["last_name", "first_name"], length: { last_name: 15 })
expected = "CREATE INDEX `index_people_on_last_name_and_first_name` ON `people` (`last_name`(15), `first_name`(10)) "
assert_equal expected, add_index(:people, [:last_name, :first_name], length: { last_name: 15, first_name: 10 })
+ assert_equal expected, add_index(:people, ["last_name", :first_name], length: { last_name: 15, "first_name" => 10 })
%w(SPATIAL FULLTEXT UNIQUE).each do |type|
expected = "CREATE #{type} INDEX `index_people_on_last_name` ON `people` (`last_name`) "
diff --git a/activerecord/test/cases/adapters/mysql2/connection_test.rb b/activerecord/test/cases/adapters/mysql2/connection_test.rb
index 8d8955e5c9..c1de2218e2 100644
--- a/activerecord/test/cases/adapters/mysql2/connection_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/connection_test.rb
@@ -65,18 +65,18 @@ class Mysql2ConnectionTest < ActiveRecord::Mysql2TestCase
def test_execute_after_disconnect
@connection.disconnect!
- error = assert_raise(ActiveRecord::StatementInvalid) do
+
+ assert_raise(ActiveRecord::StatementInvalid) do
@connection.execute("SELECT 1")
end
- assert_match(/closed MySQL connection/, error.message)
end
def test_quote_after_disconnect
@connection.disconnect!
- error = assert_raise(Mysql2::Error) do
+
+ assert_raise(Mysql2::Error) do
@connection.quote("string")
end
- assert_match(/closed MySQL connection/, error.message)
end
def test_active_after_disconnect
@@ -122,7 +122,7 @@ class Mysql2ConnectionTest < ActiveRecord::Mysql2TestCase
def test_passing_arbitary_flags_to_adapter
run_without_connection do |orig_connection|
ActiveRecord::Base.establish_connection(orig_connection.merge(flags: Mysql2::Client::COMPRESS))
- assert_equal (Mysql2::Client::COMPRESS | Mysql2::Client::FOUND_ROWS), ActiveRecord::Base.connection.raw_connection.query_options[:flags]
+ assert_equal (Mysql2::Client::COMPRESS | Mysql2::Client::FOUND_ROWS), ActiveRecord::Base.connection.raw_connection.query_options[:flags]
end
end
@@ -186,7 +186,7 @@ class Mysql2ConnectionTest < ActiveRecord::Mysql2TestCase
"expected release_advisory_lock to return false when there was no lock to release"
end
- protected
+ private
def test_lock_free(lock_name)
@connection.select_value("SELECT IS_FREE_LOCK(#{@connection.quote(lock_name)})") == 1
diff --git a/activerecord/test/cases/adapters/mysql2/json_test.rb b/activerecord/test/cases/adapters/mysql2/json_test.rb
index 630cdb36a4..6954006003 100644
--- a/activerecord/test/cases/adapters/mysql2/json_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/json_test.rb
@@ -67,8 +67,8 @@ if ActiveRecord::Base.connection.supports_json?
assert_equal({ "a_key" => "a_value" }, type.deserialize(data))
assert_equal({}, type.deserialize("{}"))
- assert_equal({ "key"=>nil }, type.deserialize('{"key": null}'))
- assert_equal({ "c"=>"}",'"a"'=>'b "a b' }, type.deserialize(%q({"c":"}", "\"a\"":"b \"a b"})))
+ assert_equal({ "key" => nil }, type.deserialize('{"key": null}'))
+ assert_equal({ "c" => "}", '"a"' => 'b "a b' }, type.deserialize(%q({"c":"}", "\"a\"":"b \"a b"})))
end
def test_rewrite
@@ -87,13 +87,13 @@ if ActiveRecord::Base.connection.supports_json?
def test_select_multikey
@connection.execute %q|insert into json_data_type (payload) VALUES ('{"k1":"v1", "k2":"v2", "k3":[1,2,3]}')|
x = JsonDataType.first
- assert_equal({ "k1" => "v1", "k2" => "v2", "k3" => [1,2,3] }, x.payload)
+ assert_equal({ "k1" => "v1", "k2" => "v2", "k3" => [1, 2, 3] }, x.payload)
end
def test_null_json
@connection.execute "insert into json_data_type (payload) VALUES(null)"
x = JsonDataType.first
- assert_equal(nil, x.payload)
+ assert_nil(x.payload)
end
def test_select_array_json_value
@@ -104,17 +104,17 @@ if ActiveRecord::Base.connection.supports_json?
def test_select_nil_json_after_create
json = JsonDataType.create(payload: nil)
- x = JsonDataType.where(payload:nil).first
+ x = JsonDataType.where(payload: nil).first
assert_equal(json, x)
end
def test_select_nil_json_after_update
json = JsonDataType.create(payload: "foo")
- x = JsonDataType.where(payload:nil).first
- assert_equal(nil, x)
+ x = JsonDataType.where(payload: nil).first
+ assert_nil(x)
json.update_attributes payload: nil
- x = JsonDataType.where(payload:nil).first
+ x = JsonDataType.where(payload: nil).first
assert_equal(json.reload, x)
end
diff --git a/activerecord/test/cases/adapters/mysql2/legacy_migration_test.rb b/activerecord/test/cases/adapters/mysql2/legacy_migration_test.rb
new file mode 100644
index 0000000000..5d3125c2be
--- /dev/null
+++ b/activerecord/test/cases/adapters/mysql2/legacy_migration_test.rb
@@ -0,0 +1,60 @@
+require "cases/helper"
+
+class MysqlLegacyMigrationTest < ActiveRecord::Mysql2TestCase
+ self.use_transactional_tests = false
+
+ class GenerateTableWithoutBigint < ActiveRecord::Migration[5.0]
+ def change
+ create_table :legacy_integer_pk do |table|
+ table.string :foo
+ end
+
+ create_table :override_pk, id: :bigint do |table|
+ table.string :bar
+ end
+ end
+ end
+
+ def setup
+ super
+ @connection = ActiveRecord::Base.connection
+
+ @migration_verbose_old = ActiveRecord::Migration.verbose
+ ActiveRecord::Migration.verbose = false
+
+ migrations = [GenerateTableWithoutBigint.new(nil, 1)]
+
+ ActiveRecord::Migrator.new(:up, migrations).migrate
+ end
+
+ def teardown
+ ActiveRecord::Migration.verbose = @migration_verbose_old
+ @connection.drop_table("legacy_integer_pk")
+ @connection.drop_table("override_pk")
+ ActiveRecord::SchemaMigration.delete_all rescue nil
+ super
+ end
+
+ def test_create_table_uses_integer_as_pkey_by_default
+ col = column(:legacy_integer_pk, :id)
+ assert_equal "int(11)", sql_type_for(col)
+ assert col.auto_increment?
+ end
+
+ def test_create_tables_respects_pk_column_type_override
+ col = column(:override_pk, :id)
+ assert_equal "bigint(20)", sql_type_for(col)
+ end
+
+ private
+
+ def column(table_name, column_name)
+ ActiveRecord::Base.connection
+ .columns(table_name.to_s)
+ .detect { |c| c.name == column_name.to_s }
+ end
+
+ def sql_type_for(col)
+ col && col.sql_type
+ end
+end
diff --git a/activerecord/test/cases/adapters/mysql2/mysql2_adapter_test.rb b/activerecord/test/cases/adapters/mysql2/mysql2_adapter_test.rb
index 69336eb906..aab3dcb724 100644
--- a/activerecord/test/cases/adapters/mysql2/mysql2_adapter_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/mysql2_adapter_test.rb
@@ -65,6 +65,19 @@ class Mysql2AdapterTest < ActiveRecord::Mysql2TestCase
@conn.columns_for_distinct("posts.id", [order])
end
+ def test_errors_for_bigint_fks_on_integer_pk_table
+ # table old_cars has primary key of integer
+
+ error = assert_raises(ActiveRecord::MismatchedForeignKey) do
+ @conn.add_reference :engines, :old_car
+ @conn.add_foreign_key :engines, :old_cars
+ end
+
+ assert_match "Column `old_car_id` on table `engines` has a type of `bigint(20)`", error.message
+ assert_not_nil error.cause
+ @conn.exec_query("ALTER TABLE engines DROP COLUMN old_car_id")
+ end
+
private
def with_example_table(definition = "id int auto_increment primary key, number int, data varchar(255)", &block)
diff --git a/activerecord/test/cases/adapters/mysql2/reserved_word_test.rb b/activerecord/test/cases/adapters/mysql2/reserved_word_test.rb
index 776549eb7a..2c778b1150 100644
--- a/activerecord/test/cases/adapters/mysql2/reserved_word_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/reserved_word_test.rb
@@ -30,11 +30,11 @@ class Mysql2ReservedWordTest < ActiveRecord::Mysql2TestCase
# we call execute directly here (and do similar below) because ActiveRecord::Base#create_table()
# will fail with these table names if these test cases fail
- create_tables_directly "group"=>"id int auto_increment primary key, `order` varchar(255), select_id int",
- "select"=>"id int auto_increment primary key",
- "values"=>"id int auto_increment primary key, group_id int",
- "distinct"=>"id int auto_increment primary key",
- "distinct_select"=>"distinct_id int, select_id int"
+ create_tables_directly "group" => "id int auto_increment primary key, `order` varchar(255), select_id int",
+ "select" => "id int auto_increment primary key",
+ "values" => "id int auto_increment primary key, group_id int",
+ "distinct" => "id int auto_increment primary key",
+ "distinct_select" => "distinct_id int, select_id int"
end
teardown do
@@ -143,7 +143,7 @@ class Mysql2ReservedWordTest < ActiveRecord::Mysql2TestCase
end
# custom create table, uses execute on connection to create a table, note: escapes table_name, does NOT escape columns
- def create_tables_directly (tables, connection = @connection)
+ def create_tables_directly(tables, connection = @connection)
tables.each do |table_name, column_properties|
connection.execute("CREATE TABLE `#{table_name}` ( #{column_properties} )")
end
diff --git a/activerecord/test/cases/adapters/mysql2/schema_test.rb b/activerecord/test/cases/adapters/mysql2/schema_test.rb
index aea930cfe6..1fad5585de 100644
--- a/activerecord/test/cases/adapters/mysql2/schema_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/schema_test.rb
@@ -76,7 +76,7 @@ module ActiveRecord
table = "key_tests"
indexes = @connection.indexes(table).sort_by(&:name)
- assert_equal 3,indexes.size
+ assert_equal 3, indexes.size
index_a = indexes.select { |i| i.name == index_a_name }[0]
index_b = indexes.select { |i| i.name == index_b_name }[0]
diff --git a/activerecord/test/cases/adapters/mysql2/transaction_test.rb b/activerecord/test/cases/adapters/mysql2/transaction_test.rb
index edd5353ee3..16101e38cb 100644
--- a/activerecord/test/cases/adapters/mysql2/transaction_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/transaction_test.rb
@@ -10,6 +10,8 @@ module ActiveRecord
end
setup do
+ @abort, Thread.abort_on_exception = Thread.abort_on_exception, false
+
@connection = ActiveRecord::Base.connection
@connection.clear_cache!
@@ -25,30 +27,34 @@ module ActiveRecord
teardown do
@connection.drop_table "samples", if_exists: true
+
+ Thread.abort_on_exception = @abort
end
test "raises Deadlocked when a deadlock is encountered" do
assert_raises(ActiveRecord::Deadlocked) do
+ barrier = Concurrent::CyclicBarrier.new(2)
+
s1 = Sample.create value: 1
s2 = Sample.create value: 2
thread = Thread.new do
Sample.transaction do
s1.lock!
- sleep 1
+ barrier.wait
s2.update_attributes value: 1
end
end
- sleep 0.5
-
- Sample.transaction do
- s2.lock!
- sleep 1
- s1.update_attributes value: 2
+ begin
+ Sample.transaction do
+ s2.lock!
+ barrier.wait
+ s1.update_attributes value: 2
+ end
+ ensure
+ thread.join
end
-
- thread.join
end
end
end
diff --git a/activerecord/test/cases/adapters/mysql2/unsigned_type_test.rb b/activerecord/test/cases/adapters/mysql2/unsigned_type_test.rb
index 3df11ce11b..a0823be143 100644
--- a/activerecord/test/cases/adapters/mysql2/unsigned_type_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/unsigned_type_test.rb
@@ -15,6 +15,7 @@ class Mysql2UnsignedTypeTest < ActiveRecord::Mysql2TestCase
t.bigint :unsigned_bigint, unsigned: true
t.float :unsigned_float, unsigned: true
t.decimal :unsigned_decimal, unsigned: true, precision: 10, scale: 2
+ t.column :unsigned_zerofill, "int unsigned zerofill"
end
end
@@ -34,10 +35,10 @@ class Mysql2UnsignedTypeTest < ActiveRecord::Mysql2TestCase
assert_raise(ActiveModel::RangeError) do
UnsignedType.create(unsigned_bigint: -10)
end
- assert_raise(ActiveRecord::StatementInvalid) do
+ assert_raise(ActiveRecord::RangeError) do
UnsignedType.create(unsigned_float: -10.0)
end
- assert_raise(ActiveRecord::StatementInvalid) do
+ assert_raise(ActiveRecord::RangeError) do
UnsignedType.create(unsigned_decimal: -10.0)
end
end
@@ -50,7 +51,7 @@ class Mysql2UnsignedTypeTest < ActiveRecord::Mysql2TestCase
t.unsigned_decimal :unsigned_decimal_t, precision: 10, scale: 2
end
- @connection.columns("unsigned_types").select { |c| /^unsigned_/ === c.name }.each do |column|
+ @connection.columns("unsigned_types").select { |c| /^unsigned_/.match?(c.name) }.each do |column|
assert column.unsigned?
end
end
diff --git a/activerecord/test/cases/adapters/postgresql/active_schema_test.rb b/activerecord/test/cases/adapters/postgresql/active_schema_test.rb
index d3c65f3d94..b787de8453 100644
--- a/activerecord/test/cases/adapters/postgresql/active_schema_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/active_schema_test.rb
@@ -39,6 +39,10 @@ class PostgresqlActiveSchemaTest < ActiveRecord::PostgreSQLTestCase
expected = %(CREATE INDEX CONCURRENTLY "index_people_on_last_name" ON "people" ("last_name"))
assert_equal expected, add_index(:people, :last_name, algorithm: :concurrently)
+ expected = %(CREATE INDEX "index_people_on_last_name_and_first_name" ON "people" ("last_name" DESC, "first_name" ASC))
+ assert_equal expected, add_index(:people, [:last_name, :first_name], order: { last_name: :desc, first_name: :asc })
+ assert_equal expected, add_index(:people, ["last_name", :first_name], order: { last_name: :desc, "first_name" => :asc })
+
%w(gin gist hash btree).each do |type|
expected = %(CREATE INDEX "index_people_on_last_name" ON "people" USING #{type} ("last_name"))
assert_equal expected, add_index(:people, :last_name, using: type)
diff --git a/activerecord/test/cases/adapters/postgresql/array_test.rb b/activerecord/test/cases/adapters/postgresql/array_test.rb
index 97960b6c51..680dad9706 100644
--- a/activerecord/test/cases/adapters/postgresql/array_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/array_test.rb
@@ -117,15 +117,15 @@ class PostgresqlArrayTest < ActiveRecord::PostgreSQLTestCase
def test_select_with_strings
@connection.execute "insert into pg_arrays (tags) VALUES ('{1,2,3}')"
x = PgArray.first
- assert_equal(["1","2","3"], x.tags)
+ assert_equal(["1", "2", "3"], x.tags)
end
def test_rewrite_with_strings
@connection.execute "insert into pg_arrays (tags) VALUES ('{1,2,3}')"
x = PgArray.first
- x.tags = ["1","2","3","4"]
+ x.tags = ["1", "2", "3", "4"]
x.save!
- assert_equal ["1","2","3","4"], x.reload.tags
+ assert_equal ["1", "2", "3", "4"], x.reload.tags
end
def test_select_with_integers
@@ -163,28 +163,28 @@ class PostgresqlArrayTest < ActiveRecord::PostgreSQLTestCase
end
def test_strings_with_quotes
- assert_cycle(:tags, ["this has",'some "s that need to be escaped"'])
+ assert_cycle(:tags, ["this has", 'some "s that need to be escaped"'])
end
def test_strings_with_commas
- assert_cycle(:tags, ["this,has","many,values"])
+ assert_cycle(:tags, ["this,has", "many,values"])
end
def test_strings_with_array_delimiters
- assert_cycle(:tags, ["{","}"])
+ assert_cycle(:tags, ["{", "}"])
end
def test_strings_with_null_strings
- assert_cycle(:tags, ["NULL","NULL"])
+ assert_cycle(:tags, ["NULL", "NULL"])
end
def test_contains_nils
- assert_cycle(:tags, ["1",nil,nil])
+ assert_cycle(:tags, ["1", nil, nil])
end
def test_insert_fixture
tag_values = ["val1", "val2", "val3_with_'_multiple_quote_'_chars"]
- @connection.insert_fixture({ "tags" => tag_values }, "pg_arrays" )
+ @connection.insert_fixture({ "tags" => tag_values }, "pg_arrays")
assert_equal(PgArray.last.tags, tag_values)
end
@@ -312,9 +312,9 @@ class PostgresqlArrayTest < ActiveRecord::PostgreSQLTestCase
end
def test_encoding_arrays_of_utf8_strings
- string_with_utf8 = "nový"
- assert_equal [string_with_utf8], @type.deserialize(@type.serialize([string_with_utf8]))
- assert_equal [[string_with_utf8]], @type.deserialize(@type.serialize([[string_with_utf8]]))
+ arrays_of_utf8_strings = %w(nový ファイル)
+ assert_equal arrays_of_utf8_strings, @type.deserialize(@type.serialize(arrays_of_utf8_strings))
+ assert_equal [arrays_of_utf8_strings], @type.deserialize(@type.serialize([arrays_of_utf8_strings]))
end
private
diff --git a/activerecord/test/cases/adapters/postgresql/bytea_test.rb b/activerecord/test/cases/adapters/postgresql/bytea_test.rb
index dc0df8715a..5c207116c4 100644
--- a/activerecord/test/cases/adapters/postgresql/bytea_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/bytea_test.rb
@@ -52,7 +52,7 @@ class PostgresqlByteaTest < ActiveRecord::PostgreSQLTestCase
end
def test_type_case_nil
- assert_equal(nil, @type.deserialize(nil))
+ assert_nil(@type.deserialize(nil))
end
def test_read_value
@@ -66,7 +66,7 @@ class PostgresqlByteaTest < ActiveRecord::PostgreSQLTestCase
def test_read_nil_value
@connection.execute "insert into bytea_data_type (payload) VALUES (null)"
record = ByteaDataType.first
- assert_equal(nil, record.payload)
+ assert_nil(record.payload)
record.delete
end
@@ -106,8 +106,8 @@ class PostgresqlByteaTest < ActiveRecord::PostgreSQLTestCase
def test_write_nil
record = ByteaDataType.create(payload: nil)
assert_not record.new_record?
- assert_equal(nil, record.payload)
- assert_equal(nil, ByteaDataType.where(id: record.id).first.payload)
+ assert_nil(record.payload)
+ assert_nil(ByteaDataType.where(id: record.id).first.payload)
end
class Serializer
diff --git a/activerecord/test/cases/adapters/postgresql/connection_test.rb b/activerecord/test/cases/adapters/postgresql/connection_test.rb
index 48c82cb7b9..45aa748ffc 100644
--- a/activerecord/test/cases/adapters/postgresql/connection_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/connection_test.rb
@@ -245,7 +245,7 @@ module ActiveRecord
end
end
- protected
+ private
def with_warning_suppression
log_level = @connection.client_min_messages
diff --git a/activerecord/test/cases/adapters/postgresql/geometric_test.rb b/activerecord/test/cases/adapters/postgresql/geometric_test.rb
index a65d4d1ad9..c1f3a4ae2c 100644
--- a/activerecord/test/cases/adapters/postgresql/geometric_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/geometric_test.rb
@@ -96,7 +96,7 @@ class PostgresqlPointTest < ActiveRecord::PostgreSQLTestCase
assert_nothing_raised { PostgresqlPoint.new(x: "") }
p = PostgresqlPoint.new(x: "")
- assert_equal nil, p.x
+ assert_nil p.x
end
def test_array_of_points_round_trip
diff --git a/activerecord/test/cases/adapters/postgresql/hstore_test.rb b/activerecord/test/cases/adapters/postgresql/hstore_test.rb
index 9236a67b11..1f35300739 100644
--- a/activerecord/test/cases/adapters/postgresql/hstore_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/hstore_test.rb
@@ -10,6 +10,12 @@ if ActiveRecord::Base.connection.supports_extensions?
store_accessor :settings, :language, :timezone
end
+ class FakeParameters
+ def to_unsafe_h
+ { "hi" => "hi" }
+ end
+ end
+
def setup
@connection = ActiveRecord::Base.connection
@@ -64,8 +70,8 @@ if ActiveRecord::Base.connection.supports_extensions?
@connection.add_column "hstores", "permissions", :hstore, default: '"users"=>"read", "articles"=>"write"'
Hstore.reset_column_information
- assert_equal({ "users"=>"read", "articles"=>"write" }, Hstore.column_defaults["permissions"])
- assert_equal({ "users"=>"read", "articles"=>"write" }, Hstore.new.permissions)
+ assert_equal({ "users" => "read", "articles" => "write" }, Hstore.column_defaults["permissions"])
+ assert_equal({ "users" => "read", "articles" => "write" }, Hstore.new.permissions)
ensure
Hstore.reset_column_information
end
@@ -113,8 +119,8 @@ if ActiveRecord::Base.connection.supports_extensions?
def test_type_cast_hstore
assert_equal({ "1" => "2" }, @type.deserialize("\"1\"=>\"2\""))
assert_equal({}, @type.deserialize(""))
- assert_equal({ "key"=>nil }, @type.deserialize("key => NULL"))
- assert_equal({ "c"=>"}",'"a"'=>'b "a b' }, @type.deserialize(%q(c=>"}", "\"a\""=>"b \"a b")))
+ assert_equal({ "key" => nil }, @type.deserialize("key => NULL"))
+ assert_equal({ "c" => "}", '"a"' => 'b "a b' }, @type.deserialize(%q(c=>"}", "\"a\""=>"b \"a b")))
end
def test_with_store_accessors
@@ -166,23 +172,23 @@ if ActiveRecord::Base.connection.supports_extensions?
end
def test_gen1
- assert_equal('" "=>""', @type.serialize(" "=>""))
+ assert_equal('" "=>""', @type.serialize(" " => ""))
end
def test_gen2
- assert_equal('","=>""', @type.serialize(","=>""))
+ assert_equal('","=>""', @type.serialize("," => ""))
end
def test_gen3
- assert_equal('"="=>""', @type.serialize("="=>""))
+ assert_equal('"="=>""', @type.serialize("=" => ""))
end
def test_gen4
- assert_equal('">"=>""', @type.serialize(">"=>""))
+ assert_equal('">"=>""', @type.serialize(">" => ""))
end
def test_parse1
- assert_equal({ "a"=>nil,"b"=>nil,"c"=>"NuLl","null"=>"c" }, @type.deserialize('a=>null,b=>NuLl,c=>"NuLl",null=>c'))
+ assert_equal({ "a" => nil, "b" => nil, "c" => "NuLl", "null" => "c" }, @type.deserialize('a=>null,b=>NuLl,c=>"NuLl",null=>c'))
end
def test_parse2
@@ -194,19 +200,19 @@ if ActiveRecord::Base.connection.supports_extensions?
end
def test_parse4
- assert_equal({ "=a"=>"q=w" }, @type.deserialize('\=a=>q=w'))
+ assert_equal({ "=a" => "q=w" }, @type.deserialize('\=a=>q=w'))
end
def test_parse5
- assert_equal({ "=a"=>"q=w" }, @type.deserialize('"=a"=>q\=w'))
+ assert_equal({ "=a" => "q=w" }, @type.deserialize('"=a"=>q\=w'))
end
def test_parse6
- assert_equal({ "\"a"=>"q>w" }, @type.deserialize('"\"a"=>q>w'))
+ assert_equal({ "\"a" => "q>w" }, @type.deserialize('"\"a"=>q>w'))
end
def test_parse7
- assert_equal({ "\"a"=>"q\"w" }, @type.deserialize('\"a=>q"w'))
+ assert_equal({ "\"a" => "q\"w" }, @type.deserialize('\"a=>q"w'))
end
def test_rewrite
@@ -321,6 +327,10 @@ if ActiveRecord::Base.connection.supports_extensions?
assert_match %r[t\.hstore "tags",\s+default: {}], output
end
+ def test_supports_to_unsafe_h_values
+ assert_equal("\"hi\"=>\"hi\"", @type.serialize(FakeParameters.new))
+ end
+
private
def assert_array_cycle(array)
# test creation
diff --git a/activerecord/test/cases/adapters/postgresql/json_test.rb b/activerecord/test/cases/adapters/postgresql/json_test.rb
index 273b2c1c7b..93558ac4d2 100644
--- a/activerecord/test/cases/adapters/postgresql/json_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/json_test.rb
@@ -41,8 +41,8 @@ module PostgresqlJSONSharedTestCases
@connection.add_column "json_data_type", "permissions", column_type, default: { "users": "read", "posts": ["read", "write"] }
JsonDataType.reset_column_information
- assert_equal({ "users"=>"read", "posts"=>["read", "write"] }, JsonDataType.column_defaults["permissions"])
- assert_equal({ "users"=>"read", "posts"=>["read", "write"] }, JsonDataType.new.permissions)
+ assert_equal({ "users" => "read", "posts" => ["read", "write"] }, JsonDataType.column_defaults["permissions"])
+ assert_equal({ "users" => "read", "posts" => ["read", "write"] }, JsonDataType.new.permissions)
ensure
JsonDataType.reset_column_information
end
@@ -84,8 +84,8 @@ module PostgresqlJSONSharedTestCases
assert_equal({ "a_key" => "a_value" }, type.deserialize(data))
assert_equal({}, type.deserialize("{}"))
- assert_equal({ "key"=>nil }, type.deserialize('{"key": null}'))
- assert_equal({ "c"=>"}",'"a"'=>'b "a b' }, type.deserialize(%q({"c":"}", "\"a\"":"b \"a b"})))
+ assert_equal({ "key" => nil }, type.deserialize('{"key": null}'))
+ assert_equal({ "c" => "}", '"a"' => 'b "a b' }, type.deserialize(%q({"c":"}", "\"a\"":"b \"a b"})))
end
def test_rewrite
@@ -104,28 +104,28 @@ module PostgresqlJSONSharedTestCases
def test_select_multikey
@connection.execute %q|insert into json_data_type (payload) VALUES ('{"k1":"v1", "k2":"v2", "k3":[1,2,3]}')|
x = JsonDataType.first
- assert_equal({ "k1" => "v1", "k2" => "v2", "k3" => [1,2,3] }, x.payload)
+ assert_equal({ "k1" => "v1", "k2" => "v2", "k3" => [1, 2, 3] }, x.payload)
end
def test_null_json
@connection.execute "insert into json_data_type (payload) VALUES(null)"
x = JsonDataType.first
- assert_equal(nil, x.payload)
+ assert_nil(x.payload)
end
def test_select_nil_json_after_create
json = JsonDataType.create(payload: nil)
- x = JsonDataType.where(payload:nil).first
+ x = JsonDataType.where(payload: nil).first
assert_equal(json, x)
end
def test_select_nil_json_after_update
json = JsonDataType.create(payload: "foo")
- x = JsonDataType.where(payload:nil).first
- assert_equal(nil, x)
+ x = JsonDataType.where(payload: nil).first
+ assert_nil(x)
json.update_attributes payload: nil
- x = JsonDataType.where(payload:nil).first
+ x = JsonDataType.where(payload: nil).first
assert_equal(json.reload, x)
end
diff --git a/activerecord/test/cases/adapters/postgresql/legacy_migration_test.rb b/activerecord/test/cases/adapters/postgresql/legacy_migration_test.rb
new file mode 100644
index 0000000000..082fe95053
--- /dev/null
+++ b/activerecord/test/cases/adapters/postgresql/legacy_migration_test.rb
@@ -0,0 +1,54 @@
+require "cases/helper"
+
+class PostgresqlLegacyMigrationTest < ActiveRecord::PostgreSQLTestCase
+ class GenerateTableWithoutBigserial < ActiveRecord::Migration[5.0]
+ def change
+ create_table :legacy_integer_pk do |table|
+ table.string :foo
+ end
+
+ create_table :override_pk, id: :bigint do |table|
+ table.string :bar
+ end
+ end
+ end
+
+ def setup
+ super
+
+ @migration_verbose_old = ActiveRecord::Migration.verbose
+ ActiveRecord::Migration.verbose = false
+
+ migrations = [GenerateTableWithoutBigserial.new(nil, 1)]
+ ActiveRecord::Migrator.new(:up, migrations).migrate
+ end
+
+ def teardown
+ ActiveRecord::Migration.verbose = @migration_verbose_old
+
+ super
+ end
+
+ def test_create_table_uses_serial_as_pkey_by_default
+ col = column(:legacy_integer_pk, :id)
+ assert_equal "integer", sql_type_for(col)
+ assert col.serial?
+ end
+
+ def test_create_tables_respects_pk_column_type_override
+ col = column(:override_pk, :id)
+ assert_equal "bigint", sql_type_for(col)
+ end
+
+ private
+
+ def column(table_name, column_name)
+ ActiveRecord::Base.connection.
+ columns(table_name.to_s).
+ detect { |c| c.name == column_name.to_s }
+ end
+
+ def sql_type_for(col)
+ col && col.sql_type
+ end
+end
diff --git a/activerecord/test/cases/adapters/postgresql/prepared_statements_disabled_test.rb b/activerecord/test/cases/adapters/postgresql/prepared_statements_disabled_test.rb
new file mode 100644
index 0000000000..8c62690866
--- /dev/null
+++ b/activerecord/test/cases/adapters/postgresql/prepared_statements_disabled_test.rb
@@ -0,0 +1,25 @@
+require "cases/helper"
+require "models/computer"
+require "models/developer"
+
+class PreparedStatementsDisabledTest < ActiveRecord::PostgreSQLTestCase
+ fixtures :developers
+
+ def setup
+ @conn = ActiveRecord::Base.establish_connection :arunit_without_prepared_statements
+ end
+
+ def teardown
+ @conn.release_connection
+ ActiveRecord::Base.establish_connection :arunit
+ end
+
+ def test_select_query_works_even_when_prepared_statements_are_disabled
+ assert_not Developer.connection.prepared_statements
+
+ david = developers(:david)
+
+ assert_equal david, Developer.where(name: "David").last # With Binds
+ assert_operator Developer.count, :>, 0 # Without Binds
+ end
+end
diff --git a/activerecord/test/cases/adapters/postgresql/prepared_statements_test.rb b/activerecord/test/cases/adapters/postgresql/prepared_statements_test.rb
deleted file mode 100644
index b898929f8a..0000000000
--- a/activerecord/test/cases/adapters/postgresql/prepared_statements_test.rb
+++ /dev/null
@@ -1,21 +0,0 @@
-require "cases/helper"
-require "models/developer"
-
-class PreparedStatementsTest < ActiveRecord::PostgreSQLTestCase
- fixtures :developers
-
- def setup
- @default_prepared_statements = Developer.connection_config[:prepared_statements]
- Developer.connection_config[:prepared_statements] = false
- end
-
- def teardown
- Developer.connection_config[:prepared_statements] = @default_prepared_statements
- end
-
- def nothing_raised_with_falsy_prepared_statements
- assert_nothing_raised do
- Developer.where(id: 1)
- end
- end
-end
diff --git a/activerecord/test/cases/adapters/postgresql/quoting_test.rb b/activerecord/test/cases/adapters/postgresql/quoting_test.rb
index 865a3a5098..141baffa5b 100644
--- a/activerecord/test/cases/adapters/postgresql/quoting_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/quoting_test.rb
@@ -18,12 +18,12 @@ module ActiveRecord
end
def test_quote_float_nan
- nan = 0.0/0
+ nan = 0.0 / 0
assert_equal "'NaN'", @conn.quote(nan)
end
def test_quote_float_infinity
- infinity = 1.0/0
+ infinity = 1.0 / 0
assert_equal "'Infinity'", @conn.quote(infinity)
end
@@ -36,7 +36,7 @@ module ActiveRecord
def test_quote_bit_string
value = "'); SELECT * FROM users; /*\n01\n*/--"
type = OID::Bit.new
- assert_equal nil, @conn.quote(type.serialize(value))
+ assert_nil @conn.quote(type.serialize(value))
end
end
end
diff --git a/activerecord/test/cases/adapters/postgresql/schema_authorization_test.rb b/activerecord/test/cases/adapters/postgresql/schema_authorization_test.rb
index 7193f23880..f6a07da85f 100644
--- a/activerecord/test/cases/adapters/postgresql/schema_authorization_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/schema_authorization_test.rb
@@ -110,7 +110,7 @@ class SchemaAuthorizationTest < ActiveRecord::PostgreSQLTestCase
private
def set_session_auth(auth = nil)
- @connection.session_auth = auth || "default"
+ @connection.session_auth = auth || "default"
end
def bind_param(value)
diff --git a/activerecord/test/cases/adapters/postgresql/schema_test.rb b/activerecord/test/cases/adapters/postgresql/schema_test.rb
index 51a2306c59..237e9ff6a5 100644
--- a/activerecord/test/cases/adapters/postgresql/schema_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/schema_test.rb
@@ -383,7 +383,7 @@ class SchemaTest < ActiveRecord::PostgreSQLTestCase
pk, seq = @connection.pk_and_sequence_for(given)
assert_equal "id", pk, "primary key should be found when table referenced as #{given}"
assert_equal pg_name.new(SCHEMA_NAME, "#{PK_TABLE_NAME}_id_seq"), seq, "sequence name should be found when table referenced as #{given}" if given == %("#{SCHEMA_NAME}"."#{PK_TABLE_NAME}")
- assert_equal pg_name.new(SCHEMA_NAME, UNMATCHED_SEQUENCE_NAME), seq, "sequence name should be found when table referenced as #{given}" if given == %("#{SCHEMA_NAME}"."#{UNMATCHED_PK_TABLE_NAME}")
+ assert_equal pg_name.new(SCHEMA_NAME, UNMATCHED_SEQUENCE_NAME), seq, "sequence name should be found when table referenced as #{given}" if given == %("#{SCHEMA_NAME}"."#{UNMATCHED_PK_TABLE_NAME}")
end
end
@@ -393,7 +393,7 @@ class SchemaTest < ActiveRecord::PostgreSQLTestCase
SCHEMA_NAME => SCHEMA_NAME,
%(#{SCHEMA2_NAME},#{SCHEMA_NAME},public) => SCHEMA2_NAME,
%(public,#{SCHEMA2_NAME},#{SCHEMA_NAME}) => "public"
- }.each do |given,expect|
+ }.each do |given, expect|
with_schema_search_path(given) { assert_equal expect, @connection.current_schema }
end
end
@@ -418,7 +418,7 @@ class SchemaTest < ActiveRecord::PostgreSQLTestCase
SCHEMA_NAME => true,
SCHEMA2_NAME => true,
"darkside" => false
- }.each do |given,expect|
+ }.each do |given, expect|
assert_equal expect, @connection.schema_exists?(given)
end
end
diff --git a/activerecord/test/cases/adapters/postgresql/timestamp_test.rb b/activerecord/test/cases/adapters/postgresql/timestamp_test.rb
index e7c1d97d16..962450aada 100644
--- a/activerecord/test/cases/adapters/postgresql/timestamp_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/timestamp_test.rb
@@ -21,7 +21,7 @@ class PostgresqlTimestampTest < ActiveRecord::PostgreSQLTestCase
@connection.reconnect!
timestamp = PostgresqlTimestampWithZone.find(1)
- assert_equal Time.utc(2010,1,1, 11,0,0), timestamp.time
+ assert_equal Time.utc(2010, 1, 1, 11, 0, 0), timestamp.time
assert_instance_of Time, timestamp.time
end
ensure
@@ -35,7 +35,7 @@ class PostgresqlTimestampTest < ActiveRecord::PostgreSQLTestCase
@connection.execute("SET time zone 'America/Jamaica'", "SCHEMA")
timestamp = PostgresqlTimestampWithZone.find(1)
- assert_equal Time.utc(2010,1,1, 11,0,0), timestamp.time
+ assert_equal Time.utc(2010, 1, 1, 11, 0, 0), timestamp.time
assert_instance_of Time, timestamp.time
end
ensure
diff --git a/activerecord/test/cases/adapters/postgresql/transaction_test.rb b/activerecord/test/cases/adapters/postgresql/transaction_test.rb
index c450524de8..9b42d0383d 100644
--- a/activerecord/test/cases/adapters/postgresql/transaction_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/transaction_test.rb
@@ -11,6 +11,8 @@ module ActiveRecord
end
setup do
+ @abort, Thread.abort_on_exception = Thread.abort_on_exception, false
+
@connection = ActiveRecord::Base.connection
@connection.transaction do
@@ -25,6 +27,8 @@ module ActiveRecord
teardown do
@connection.drop_table "samples", if_exists: true
+
+ Thread.abort_on_exception = @abort
end
test "raises SerializationFailure when a serialization failure occurs" do
@@ -85,7 +89,7 @@ module ActiveRecord
end
end
- protected
+ private
def with_warning_suppression
log_level = ActiveRecord::Base.connection.client_min_messages
diff --git a/activerecord/test/cases/adapters/postgresql/utils_test.rb b/activerecord/test/cases/adapters/postgresql/utils_test.rb
index 01c597beae..9f9e3bda2f 100644
--- a/activerecord/test/cases/adapters/postgresql/utils_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/utils_test.rb
@@ -7,13 +7,13 @@ class PostgreSQLUtilsTest < ActiveRecord::PostgreSQLTestCase
def test_extract_schema_qualified_name
{
- %(table_name) => [nil,"table_name"],
- %("table.name") => [nil,"table.name"],
+ %(table_name) => [nil, "table_name"],
+ %("table.name") => [nil, "table.name"],
%(schema.table_name) => %w{schema table_name},
%("schema".table_name) => %w{schema table_name},
%(schema."table_name") => %w{schema table_name},
%("schema"."table_name") => %w{schema table_name},
- %("even spaces".table) => ["even spaces","table"],
+ %("even spaces".table) => ["even spaces", "table"],
%(schema."table.name") => ["schema", "table.name"]
}.each do |given, expect|
assert_equal Name.new(*expect), extract_schema_qualified_name(given)
@@ -56,7 +56,7 @@ class PostgreSQLNameTest < ActiveRecord::PostgreSQLTestCase
test "can be used as hash key" do
hash = { Name.new("schema", "article_seq") => "success" }
assert_equal "success", hash[Name.new("schema", "article_seq")]
- assert_equal nil, hash[Name.new("schema", "articles")]
- assert_equal nil, hash[Name.new("public", "article_seq")]
+ assert_nil hash[Name.new("schema", "articles")]
+ assert_nil hash[Name.new("public", "article_seq")]
end
end
diff --git a/activerecord/test/cases/adapters/postgresql/uuid_test.rb b/activerecord/test/cases/adapters/postgresql/uuid_test.rb
index 9a59691737..f34d50e25c 100644
--- a/activerecord/test/cases/adapters/postgresql/uuid_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/uuid_test.rb
@@ -9,6 +9,10 @@ module PostgresqlUUIDHelper
def drop_table(name)
connection.drop_table name, if_exists: true
end
+
+ def uuid_function
+ connection.supports_pgcrypto_uuid? ? "gen_random_uuid()" : "uuid_generate_v4()"
+ end
end
class PostgresqlUUIDTest < ActiveRecord::PostgreSQLTestCase
@@ -21,6 +25,7 @@ class PostgresqlUUIDTest < ActiveRecord::PostgreSQLTestCase
setup do
enable_extension!("uuid-ossp", connection)
+ enable_extension!("pgcrypto", connection) if connection.supports_pgcrypto_uuid?
connection.create_table "uuid_data_type" do |t|
t.uuid "guid"
@@ -31,14 +36,22 @@ class PostgresqlUUIDTest < ActiveRecord::PostgreSQLTestCase
drop_table "uuid_data_type"
end
+ if ActiveRecord::Base.connection.supports_pgcrypto_uuid?
+ def test_uuid_column_default
+ connection.add_column :uuid_data_type, :thingy, :uuid, null: false, default: "gen_random_uuid()"
+ UUIDType.reset_column_information
+ column = UUIDType.columns_hash["thingy"]
+ assert_equal "gen_random_uuid()", column.default_function
+ end
+ end
+
def test_change_column_default
- @connection.add_column :uuid_data_type, :thingy, :uuid, null: false, default: "uuid_generate_v1()"
+ connection.add_column :uuid_data_type, :thingy, :uuid, null: false, default: "uuid_generate_v1()"
UUIDType.reset_column_information
column = UUIDType.columns_hash["thingy"]
assert_equal "uuid_generate_v1()", column.default_function
- @connection.change_column :uuid_data_type, :thingy, :uuid, null: false, default: "uuid_generate_v4()"
-
+ connection.change_column :uuid_data_type, :thingy, :uuid, null: false, default: "uuid_generate_v4()"
UUIDType.reset_column_information
column = UUIDType.columns_hash["thingy"]
assert_equal "uuid_generate_v4()", column.default_function
@@ -58,12 +71,12 @@ class PostgresqlUUIDTest < ActiveRecord::PostgreSQLTestCase
def test_treat_blank_uuid_as_nil
UUIDType.create! guid: ""
- assert_equal(nil, UUIDType.last.guid)
+ assert_nil(UUIDType.last.guid)
end
def test_treat_invalid_uuid_as_nil
uuid = UUIDType.create! guid: "foobar"
- assert_equal(nil, uuid.guid)
+ assert_nil(uuid.guid)
end
def test_invalid_uuid_dont_modify_before_type_cast
@@ -155,7 +168,7 @@ class PostgresqlUUIDGenerationTest < ActiveRecord::PostgreSQLTestCase
# to test dumping tables which columns have defaults with custom functions
connection.execute <<-SQL
CREATE OR REPLACE FUNCTION my_uuid_generator() RETURNS uuid
- AS $$ SELECT * FROM uuid_generate_v4() $$
+ AS $$ SELECT * FROM #{uuid_function} $$
LANGUAGE SQL VOLATILE;
SQL
@@ -164,11 +177,16 @@ class PostgresqlUUIDGenerationTest < ActiveRecord::PostgreSQLTestCase
t.string "name"
t.uuid "other_uuid_2", default: "my_uuid_generator()"
end
+
+ connection.create_table("pg_uuids_3", id: :uuid) do |t|
+ t.string "name"
+ end
end
teardown do
drop_table "pg_uuids"
drop_table "pg_uuids_2"
+ drop_table "pg_uuids_3"
connection.execute "DROP FUNCTION IF EXISTS my_uuid_generator();"
end
@@ -192,7 +210,7 @@ class PostgresqlUUIDGenerationTest < ActiveRecord::PostgreSQLTestCase
def test_pk_and_sequence_for_uuid_primary_key
pk, seq = connection.pk_and_sequence_for("pg_uuids")
assert_equal "id", pk
- assert_equal nil, seq
+ assert_nil seq
end
def test_schema_dumper_for_uuid_primary_key
@@ -206,6 +224,36 @@ class PostgresqlUUIDGenerationTest < ActiveRecord::PostgreSQLTestCase
assert_match(/\bcreate_table "pg_uuids_2", id: :uuid, default: -> { "my_uuid_generator\(\)" }/, schema)
assert_match(/t\.uuid "other_uuid_2", default: -> { "my_uuid_generator\(\)" }/, schema)
end
+
+ def test_schema_dumper_for_uuid_primary_key_default
+ schema = dump_table_schema "pg_uuids_3"
+ if connection.supports_pgcrypto_uuid?
+ assert_match(/\bcreate_table "pg_uuids_3", id: :uuid, default: -> { "gen_random_uuid\(\)" }/, schema)
+ else
+ assert_match(/\bcreate_table "pg_uuids_3", id: :uuid, default: -> { "uuid_generate_v4\(\)" }/, schema)
+ end
+ end
+
+ if ActiveRecord::Base.connection.supports_pgcrypto_uuid?
+ def test_schema_dumper_for_uuid_primary_key_default_in_legacy_migration
+ @verbose_was = ActiveRecord::Migration.verbose
+ ActiveRecord::Migration.verbose = false
+
+ migration = Class.new(ActiveRecord::Migration[4.2]) do
+ def version; 101 end
+ def migrate(x)
+ create_table("pg_uuids_4", id: :uuid)
+ end
+ end.new
+ ActiveRecord::Migrator.new(:up, [migration]).migrate
+
+ schema = dump_table_schema "pg_uuids_4"
+ assert_match(/\bcreate_table "pg_uuids_4", id: :uuid, default: -> { "uuid_generate_v4\(\)" }/, schema)
+ ensure
+ drop_table "pg_uuids_4"
+ ActiveRecord::Migration.verbose = @verbose_was
+ end
+ end
end
end
diff --git a/activerecord/test/cases/adapters/sqlite3/copy_table_test.rb b/activerecord/test/cases/adapters/sqlite3/copy_table_test.rb
index 8342b05870..91967c1e33 100644
--- a/activerecord/test/cases/adapters/sqlite3/copy_table_test.rb
+++ b/activerecord/test/cases/adapters/sqlite3/copy_table_test.rb
@@ -75,7 +75,7 @@ class CopyTableTest < ActiveRecord::SQLite3TestCase
test_copy_table "binaries", "binaries2"
end
-protected
+private
def copy_table(from, to, options = {})
@connection.copy_table(from, to, { temporary: true }.merge(options))
end
diff --git a/activerecord/test/cases/adapters/sqlite3/legacy_migration_test.rb b/activerecord/test/cases/adapters/sqlite3/legacy_migration_test.rb
new file mode 100644
index 0000000000..fcca8d66b5
--- /dev/null
+++ b/activerecord/test/cases/adapters/sqlite3/legacy_migration_test.rb
@@ -0,0 +1,59 @@
+require "cases/helper"
+
+class SqliteLegacyMigrationTest < ActiveRecord::SQLite3TestCase
+ self.use_transactional_tests = false
+
+ class GenerateTableWithoutBigint < ActiveRecord::Migration[5.0]
+ def change
+ create_table :legacy_integer_pk do |table|
+ table.string :foo
+ end
+
+ create_table :override_pk, id: :bigint do |table|
+ table.string :bar
+ end
+ end
+ end
+
+ def setup
+ super
+ @connection = ActiveRecord::Base.connection
+
+ @migration_verbose_old = ActiveRecord::Migration.verbose
+ ActiveRecord::Migration.verbose = false
+
+ migrations = [GenerateTableWithoutBigint.new(nil, 1)]
+
+ ActiveRecord::Migrator.new(:up, migrations).migrate
+ end
+
+ def teardown
+ ActiveRecord::Migration.verbose = @migration_verbose_old
+ @connection.drop_table("legacy_integer_pk")
+ @connection.drop_table("override_pk")
+ ActiveRecord::SchemaMigration.delete_all rescue nil
+ super
+ end
+
+ def test_create_table_uses_integer_as_pkey_by_default
+ col = column(:legacy_integer_pk, :id)
+ assert_equal "INTEGER", sql_type_for(col)
+ assert primary_key?(:legacy_integer_pk, "id"), "id is not primary key"
+ end
+
+ private
+
+ def column(table_name, column_name)
+ ActiveRecord::Base.connection
+ .columns(table_name.to_s)
+ .detect { |c| c.name == column_name.to_s }
+ end
+
+ def sql_type_for(col)
+ col && col.sql_type
+ end
+
+ def primary_key?(table_name, column)
+ ActiveRecord::Base.connection.execute("PRAGMA table_info(#{table_name})").find { |col| col["name"] == column }["pk"] == 1
+ end
+end
diff --git a/activerecord/test/cases/adapters/sqlite3/quoting_test.rb b/activerecord/test/cases/adapters/sqlite3/quoting_test.rb
index 80a37e83ff..9750840051 100644
--- a/activerecord/test/cases/adapters/sqlite3/quoting_test.rb
+++ b/activerecord/test/cases/adapters/sqlite3/quoting_test.rb
@@ -37,7 +37,7 @@ class SQLite3QuotingTest < ActiveRecord::SQLite3TestCase
end
def test_type_cast_nil
- assert_equal nil, @conn.type_cast(nil)
+ assert_nil @conn.type_cast(nil)
end
def test_type_cast_true
diff --git a/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb b/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb
index 66f9349111..0526191952 100644
--- a/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb
+++ b/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb
@@ -190,7 +190,7 @@ module ActiveRecord
end
def test_type_cast_should_not_mutate_encoding
- name = "hello".force_encoding(Encoding::ASCII_8BIT)
+ name = "hello".force_encoding(Encoding::ASCII_8BIT)
Owner.create(name: name)
assert_equal Encoding::ASCII_8BIT, name.encoding
ensure
diff --git a/activerecord/test/cases/ar_schema_test.rb b/activerecord/test/cases/ar_schema_test.rb
index e3eccad71f..397ac599b9 100644
--- a/activerecord/test/cases/ar_schema_test.rb
+++ b/activerecord/test/cases/ar_schema_test.rb
@@ -54,7 +54,7 @@ if ActiveRecord::Base.connection.supports_migrations?
def test_schema_define_w_table_name_prefix
table_name = ActiveRecord::SchemaMigration.table_name
old_table_name_prefix = ActiveRecord::Base.table_name_prefix
- ActiveRecord::Base.table_name_prefix = "nep_"
+ ActiveRecord::Base.table_name_prefix = "nep_"
ActiveRecord::SchemaMigration.table_name = "nep_#{table_name}"
ActiveRecord::Schema.define(version: 7) do
create_table :fruits do |t|
@@ -66,7 +66,7 @@ if ActiveRecord::Base.connection.supports_migrations?
end
assert_equal 7, ActiveRecord::Migrator::current_version
ensure
- ActiveRecord::Base.table_name_prefix = old_table_name_prefix
+ ActiveRecord::Base.table_name_prefix = old_table_name_prefix
ActiveRecord::SchemaMigration.table_name = table_name
end
diff --git a/activerecord/test/cases/associations/belongs_to_associations_test.rb b/activerecord/test/cases/associations/belongs_to_associations_test.rb
index 2418346d1b..b75e2a47a6 100644
--- a/activerecord/test/cases/associations/belongs_to_associations_test.rb
+++ b/activerecord/test/cases/associations/belongs_to_associations_test.rb
@@ -285,12 +285,22 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
end
def test_failing_create!
- client = Client.create!(name: "Jimmy")
+ client = Client.create!(name: "Jimmy")
assert_raise(ActiveRecord::RecordInvalid) { client.create_account! }
assert_not_nil client.account
assert client.account.new_record?
end
+ def test_reloading_the_belonging_object
+ odegy_account = accounts(:odegy_account)
+
+ assert_equal "Odegy", odegy_account.firm.name
+ Company.where(id: odegy_account.firm_id).update_all(name: "ODEGY")
+ assert_equal "Odegy", odegy_account.firm.name
+
+ assert_equal "ODEGY", odegy_account.reload_firm.name
+ end
+
def test_natural_assignment_to_nil
client = Client.find(3)
client.firm = nil
@@ -346,7 +356,7 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
def test_with_select
assert_equal 1, Company.find(2).firm_with_select.attributes.size
- assert_equal 1, Company.all.merge!(includes: :firm_with_select ).find(2).firm_with_select.attributes.size
+ assert_equal 1, Company.all.merge!(includes: :firm_with_select).find(2).firm_with_select.attributes.size
end
def test_belongs_to_without_counter_cache_option
@@ -1047,7 +1057,7 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
comment.parent = nil
comment.save!
- assert_equal nil, comment.reload.parent
+ assert_nil comment.reload.parent
assert_equal 0, comments(:greetings).reload.children_count
end
@@ -1062,6 +1072,20 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
assert_equal 1, parent.reload.children_count
end
+ def test_belongs_to_with_out_of_range_value_assigning
+ model = Class.new(Comment) do
+ def self.name; "Temp"; end
+ validates :post, presence: true
+ end
+
+ comment = model.new
+ comment.post_id = 9223372036854775808 # out of range in the bigint
+
+ assert_nil comment.post
+ assert_not comment.valid?
+ assert_equal [{ error: :blank }], comment.errors.details[:post]
+ end
+
def test_polymorphic_with_custom_primary_key
toy = Toy.create!
sponsor = Sponsor.create!(sponsorable: toy)
diff --git a/activerecord/test/cases/associations/callbacks_test.rb b/activerecord/test/cases/associations/callbacks_test.rb
index 2f62d0367e..5fd2411f6f 100644
--- a/activerecord/test/cases/associations/callbacks_test.rb
+++ b/activerecord/test/cases/associations/callbacks_test.rb
@@ -109,7 +109,7 @@ class AssociationCallbacksTest < ActiveRecord::TestCase
def self.name; Project.name; end
has_and_belongs_to_many :developers_with_callbacks,
class_name: "Developer",
- before_add: lambda { |o,r|
+ before_add: lambda { |o, r|
dev = r
new_dev = r.new_record?
}
@@ -159,7 +159,7 @@ class AssociationCallbacksTest < ActiveRecord::TestCase
activerecord.reload
assert activerecord.developers_with_callbacks.size == 2
end
- activerecord.developers_with_callbacks.flat_map { |d| ["before_removing#{d.id}","after_removing#{d.id}"] }.sort
+ activerecord.developers_with_callbacks.flat_map { |d| ["before_removing#{d.id}", "after_removing#{d.id}"] }.sort
assert activerecord.developers_with_callbacks.clear
assert_predicate activerecord.developers_log, :empty?
end
diff --git a/activerecord/test/cases/associations/cascaded_eager_loading_test.rb b/activerecord/test/cases/associations/cascaded_eager_loading_test.rb
index e87431bf32..ddb5c7a4aa 100644
--- a/activerecord/test/cases/associations/cascaded_eager_loading_test.rb
+++ b/activerecord/test/cases/associations/cascaded_eager_loading_test.rb
@@ -20,7 +20,7 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase
assert_equal 3, authors.size
assert_equal 5, authors[0].posts.size
assert_equal 3, authors[1].posts.size
- assert_equal 10, authors[0].posts.collect { |post| post.comments.size }.inject(0) { |sum,i| sum+i }
+ assert_equal 10, authors[0].posts.collect { |post| post.comments.size }.inject(0) { |sum, i| sum + i }
end
def test_eager_association_loading_with_cascaded_two_levels_and_one_level
@@ -28,7 +28,7 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase
assert_equal 3, authors.size
assert_equal 5, authors[0].posts.size
assert_equal 3, authors[1].posts.size
- assert_equal 10, authors[0].posts.collect { |post| post.comments.size }.inject(0) { |sum,i| sum+i }
+ assert_equal 10, authors[0].posts.collect { |post| post.comments.size }.inject(0) { |sum, i| sum + i }
assert_equal 1, authors[0].categorizations.size
assert_equal 2, authors[1].categorizations.size
end
@@ -86,7 +86,7 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase
assert_equal 3, authors.size
assert_equal 5, authors[0].posts.size
assert_equal 3, authors[1].posts.size
- assert_equal 10, authors[0].posts.collect { |post| post.comments.size }.inject(0) { |sum,i| sum+i }
+ assert_equal 10, authors[0].posts.collect { |post| post.comments.size }.inject(0) { |sum, i| sum + i }
end
def test_eager_association_loading_with_cascaded_two_levels_and_self_table_reference
@@ -183,6 +183,6 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase
assert_equal 1, authors[1].comments.size
assert_equal 5, authors[0].posts.size
assert_equal 3, authors[1].posts.size
- assert_equal 3, authors[0].posts.collect { |post| post.categorizations.size }.inject(0) { |sum, i| sum+i }
+ assert_equal 3, authors[0].posts.collect { |post| post.categorizations.size }.inject(0) { |sum, i| sum + i }
end
end
diff --git a/activerecord/test/cases/associations/eager_load_includes_full_sti_class_test.rb b/activerecord/test/cases/associations/eager_load_includes_full_sti_class_test.rb
index aa82b9dd2a..4f0fe3236e 100644
--- a/activerecord/test/cases/associations/eager_load_includes_full_sti_class_test.rb
+++ b/activerecord/test/cases/associations/eager_load_includes_full_sti_class_test.rb
@@ -15,8 +15,8 @@ class EagerLoadIncludeFullStiClassNamesTest < ActiveRecord::TestCase
end
def generate_test_objects
- post = Namespaced::Post.create( title: "Great stuff", body: "This is not", author_id: 1 )
- Tagging.create( taggable: post )
+ post = Namespaced::Post.create(title: "Great stuff", body: "This is not", author_id: 1)
+ Tagging.create(taggable: post)
end
def test_class_names
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 a7a8c6a783..e9f551b6b2 100644
--- a/activerecord/test/cases/associations/eager_load_nested_include_test.rb
+++ b/activerecord/test/cases/associations/eager_load_nested_include_test.rb
@@ -12,7 +12,7 @@ module Remembered
included do
after_create :remember
- protected
+ private
def remember; self.class.remembered << self; end
end
@@ -39,7 +39,7 @@ class Triangle < ActiveRecord::Base
has_many :shape_expressions, as: :shape
include Remembered
end
-class PaintColor < ActiveRecord::Base
+class PaintColor < ActiveRecord::Base
has_many :shape_expressions, as: :paint
belongs_to :non_poly, foreign_key: "non_poly_one_id", class_name: "NonPolyOne"
include Remembered
diff --git a/activerecord/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb
index d1c4c1cef8..ff1bf8acd4 100644
--- a/activerecord/test/cases/associations/eager_test.rb
+++ b/activerecord/test/cases/associations/eager_test.rb
@@ -241,7 +241,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
post = assert_queries(1) { Post.all.merge!(includes: { author_with_address: :author_address }).find(post.id) }
# find the post, then find the author which is null so no query for the author or address
assert_no_queries do
- assert_equal nil, post.author_with_address
+ assert_nil post.author_with_address
end
end
@@ -250,7 +250,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
sponsor.update!(sponsorable: nil)
sponsor = assert_queries(1) { Sponsor.all.merge!(includes: :sponsorable).find(sponsor.id) }
assert_no_queries do
- assert_equal nil, sponsor.sponsorable
+ assert_nil sponsor.sponsorable
end
end
@@ -261,7 +261,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
assert_nothing_raised { Sponsor.all.merge!(includes: :sponsorable).find(sponsor.id) }
end
assert_no_queries do
- assert_equal nil, sponsor.sponsorable
+ assert_nil sponsor.sponsorable
end
end
@@ -356,31 +356,31 @@ class EagerAssociationTest < ActiveRecord::TestCase
def test_eager_association_loading_with_belongs_to_and_limit
comments = Comment.all.merge!(includes: :post, limit: 5, order: "comments.id").to_a
assert_equal 5, comments.length
- assert_equal [1,2,3,5,6], comments.collect(&:id)
+ assert_equal [1, 2, 3, 5, 6], comments.collect(&:id)
end
def test_eager_association_loading_with_belongs_to_and_limit_and_conditions
comments = Comment.all.merge!(includes: :post, where: "post_id = 4", limit: 3, order: "comments.id").to_a
assert_equal 3, comments.length
- assert_equal [5,6,7], comments.collect(&:id)
+ assert_equal [5, 6, 7], comments.collect(&:id)
end
def test_eager_association_loading_with_belongs_to_and_limit_and_offset
comments = Comment.all.merge!(includes: :post, limit: 3, offset: 2, order: "comments.id").to_a
assert_equal 3, comments.length
- assert_equal [3,5,6], comments.collect(&:id)
+ assert_equal [3, 5, 6], comments.collect(&:id)
end
def test_eager_association_loading_with_belongs_to_and_limit_and_offset_and_conditions
comments = Comment.all.merge!(includes: :post, where: "post_id = 4", limit: 3, offset: 1, order: "comments.id").to_a
assert_equal 3, comments.length
- assert_equal [6,7,8], comments.collect(&:id)
+ assert_equal [6, 7, 8], comments.collect(&:id)
end
def test_eager_association_loading_with_belongs_to_and_limit_and_offset_and_conditions_array
- comments = Comment.all.merge!(includes: :post, where: ["post_id = ?",4], limit: 3, offset: 1, order: "comments.id").to_a
+ comments = Comment.all.merge!(includes: :post, where: ["post_id = ?", 4], limit: 3, offset: 1, order: "comments.id").to_a
assert_equal 3, comments.length
- assert_equal [6,7,8], comments.collect(&:id)
+ assert_equal [6, 7, 8], comments.collect(&:id)
end
def test_eager_association_loading_with_belongs_to_and_conditions_string_with_unquoted_table_name
@@ -395,14 +395,14 @@ class EagerAssociationTest < ActiveRecord::TestCase
comments = Comment.all.merge!(includes: :post, where: { posts: { id: 4 } }, limit: 3, order: "comments.id").to_a
end
assert_equal 3, comments.length
- assert_equal [5,6,7], comments.collect(&:id)
+ assert_equal [5, 6, 7], comments.collect(&:id)
assert_no_queries do
comments.first.post
end
end
def test_eager_association_loading_with_belongs_to_and_conditions_string_with_quoted_table_name
- quoted_posts_id= Comment.connection.quote_table_name("posts") + "." + Comment.connection.quote_column_name("id")
+ quoted_posts_id = Comment.connection.quote_table_name("posts") + "." + Comment.connection.quote_column_name("id")
assert_nothing_raised do
Comment.includes(:post).references(:posts).where("#{quoted_posts_id} = ?", 4)
end
@@ -415,7 +415,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
end
def test_eager_association_loading_with_belongs_to_and_order_string_with_quoted_table_name
- quoted_posts_id= Comment.connection.quote_table_name("posts") + "." + Comment.connection.quote_column_name("id")
+ quoted_posts_id = Comment.connection.quote_table_name("posts") + "." + Comment.connection.quote_column_name("id")
assert_nothing_raised do
Comment.includes(:post).references(:posts).order(quoted_posts_id)
end
@@ -452,8 +452,8 @@ class EagerAssociationTest < ActiveRecord::TestCase
def test_eager_load_has_many_quotes_table_and_column_names
michael = Person.all.merge!(includes: :references).find(people(:michael).id)
- references(:michael_magician,:michael_unicyclist)
- assert_no_queries { assert_equal references(:michael_magician,:michael_unicyclist), michael.references.sort_by(&:id) }
+ references(:michael_magician, :michael_unicyclist)
+ assert_no_queries { assert_equal references(:michael_magician, :michael_unicyclist), michael.references.sort_by(&:id) }
end
def test_eager_load_has_many_through_quotes_table_and_column_names
@@ -464,7 +464,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
def test_eager_load_has_many_with_string_keys
subscriptions = subscriptions(:webster_awdr, :webster_rfr)
- subscriber =Subscriber.all.merge!(includes: :subscriptions).find(subscribers(:second).id)
+ subscriber = Subscriber.all.merge!(includes: :subscriptions).find(subscribers(:second).id)
assert_equal subscriptions, subscriber.subscriptions.sort_by(&:id)
end
@@ -563,13 +563,13 @@ class EagerAssociationTest < ActiveRecord::TestCase
def test_eager_with_has_many_and_limit_and_conditions
posts = Post.all.merge!(includes: [ :author, :comments ], limit: 2, where: "posts.body = 'hello'", order: "posts.id").to_a
assert_equal 2, posts.size
- assert_equal [4,5], posts.collect(&:id)
+ assert_equal [4, 5], posts.collect(&:id)
end
def test_eager_with_has_many_and_limit_and_conditions_array
posts = Post.all.merge!(includes: [ :author, :comments ], limit: 2, where: [ "posts.body = ?", "hello" ], order: "posts.id").to_a
assert_equal 2, posts.size
- assert_equal [4,5], posts.collect(&:id)
+ assert_equal [4, 5], posts.collect(&:id)
end
def test_eager_with_has_many_and_limit_and_conditions_array_on_the_eagers
@@ -740,7 +740,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
def test_eager_with_invalid_association_reference
assert_raise(ActiveRecord::AssociationNotFoundError, "Association was not found; perhaps you misspelled it? You specified :include => :monkeys") {
- Post.all.merge!(includes: :monkeys ).find(6)
+ Post.all.merge!(includes: :monkeys).find(6)
}
assert_raise(ActiveRecord::AssociationNotFoundError, "Association was not found; perhaps you misspelled it? You specified :include => :monkeys") {
Post.all.merge!(includes: [ :monkeys ]).find(6)
@@ -844,7 +844,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
end
end
- def find_all_ordered(className, include=nil)
+ def find_all_ordered(className, include = nil)
className.all.merge!(order: "#{className.table_name}.#{className.primary_key}", includes: include).to_a
end
@@ -903,8 +903,8 @@ class EagerAssociationTest < ActiveRecord::TestCase
def test_eager_with_multiple_associations_with_same_table_has_many_and_habtm
# Eager includes of has many and habtm associations aren't necessarily sorted in the same way
def assert_equal_after_sort(item1, item2, item3 = nil)
- assert_equal(item1.sort { |a,b| a.id <=> b.id }, item2.sort { |a,b| a.id <=> b.id })
- assert_equal(item3.sort { |a,b| a.id <=> b.id }, item2.sort { |a,b| a.id <=> b.id }) if item3
+ assert_equal(item1.sort { |a, b| a.id <=> b.id }, item2.sort { |a, b| a.id <=> b.id })
+ assert_equal(item3.sort { |a, b| a.id <=> b.id }, item2.sort { |a, b| a.id <=> b.id }) if item3
end
# Test regular association, association with conditions, association with
# STI, and association with conditions assured not to be true
@@ -1163,7 +1163,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
expected = Firm.find(1).clients_using_primary_key.sort_by(&:name)
# Oracle adapter truncates alias to 30 characters
if current_adapter?(:OracleAdapter)
- firm = Firm.all.merge!(includes: :clients_using_primary_key, order: "clients_using_primary_keys_companies"[0,30]+".name").find(1)
+ firm = Firm.all.merge!(includes: :clients_using_primary_key, order: "clients_using_primary_keys_companies"[0, 30] + ".name").find(1)
else
firm = Firm.all.merge!(includes: :clients_using_primary_key, order: "clients_using_primary_keys_companies.name").find(1)
end
@@ -1360,7 +1360,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
test "including associations with where.not adds implicit references" do
author = assert_queries(2) {
- Author.includes(:posts).where.not(posts: { title: "Welcome to the weblog" } ).last
+ Author.includes(:posts).where.not(posts: { title: "Welcome to the weblog" }).last
}
assert_no_queries {
diff --git a/activerecord/test/cases/associations/extension_test.rb b/activerecord/test/cases/associations/extension_test.rb
index cc86e1a16d..974a3080d4 100644
--- a/activerecord/test/cases/associations/extension_test.rb
+++ b/activerecord/test/cases/associations/extension_test.rb
@@ -45,7 +45,7 @@ class AssociationsExtensionsTest < ActiveRecord::TestCase
# Marshaling an association shouldn't make it unusable by wiping its reflection.
assert_not_nil david.association(:projects).reflection
- david_too = Marshal.load(marshalled)
+ david_too = Marshal.load(marshalled)
assert_equal projects(:action_controller), david_too.projects.find_most_recent
end
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 06fc7a4388..4b7ac594cf 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
@@ -86,6 +86,10 @@ class DeveloperWithSymbolClassName < Developer
has_and_belongs_to_many :projects, class_name: :ProjectWithSymbolsForKeys
end
+class DeveloperWithConstantClassName < Developer
+ has_and_belongs_to_many :projects, class_name: ProjectWithSymbolsForKeys
+end
+
class DeveloperWithExtendOption < Developer
module NamedExtension
def category
@@ -249,8 +253,8 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
assert !p.persisted?
assert aredridel.save
assert aredridel.persisted?
- assert_equal no_of_devels+1, Developer.count
- assert_equal no_of_projects+1, Project.count
+ assert_equal no_of_devels + 1, Developer.count
+ assert_equal no_of_projects + 1, Project.count
assert_equal 2, aredridel.projects.size
assert_equal 2, aredridel.projects.reload.size
end
@@ -379,7 +383,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
dev.projects << projects(:active_record)
assert_equal 3, dev.projects.size
- assert_equal 1, dev.projects.distinct.size
+ assert_equal 1, dev.projects.uniq.size
end
def test_distinct_before_the_fact
@@ -939,7 +943,15 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
def test_with_symbol_class_name
assert_nothing_raised do
- DeveloperWithSymbolClassName.new
+ developer = DeveloperWithSymbolClassName.new
+ developer.projects
+ end
+ end
+
+ def test_with_constant_class_name
+ assert_nothing_raised do
+ developer = DeveloperWithConstantClassName.new
+ developer.projects
end
end
@@ -1000,4 +1012,17 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
user = User.create!
assert_nothing_raised { user.jobs_pool.clear }
end
+
+ def test_has_and_belongs_to_many_while_partial_writes_false
+ begin
+ original_partial_writes = ActiveRecord::Base.partial_writes
+ ActiveRecord::Base.partial_writes = false
+ developer = Developer.new(name: "Mehmet Emin İNAÇ")
+ developer.projects << Project.new(name: "Bounty")
+
+ assert developer.save
+ ensure
+ ActiveRecord::Base.partial_writes = original_partial_writes
+ end
+ end
end
diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb
index 0ce67f971b..482b086e5c 100644
--- a/activerecord/test/cases/associations/has_many_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_associations_test.rb
@@ -84,7 +84,7 @@ class HasManyAssociationsTestPrimaryKeys < ActiveRecord::TestCase
david = people(:david)
assert_equal ["A Modest Proposal"], david.essays.map(&:name)
- david.essays = [Essay.create!(name: "Remote Work" )]
+ david.essays = [Essay.create!(name: "Remote Work")]
assert_equal ["Remote Work"], david.essays.map(&:name)
end
@@ -187,7 +187,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
ship.parts.clear
part.reload
- assert_equal nil, part.ship
+ assert_nil part.ship
assert !part.updated_at_changed?
end
@@ -528,7 +528,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_find_should_append_to_association_order
- ordered_clients = companies(:first_firm).clients_sorted_desc.order("companies.id")
+ ordered_clients = companies(:first_firm).clients_sorted_desc.order("companies.id")
assert_equal ["id DESC", "companies.id"], ordered_clients.order_values
end
@@ -788,6 +788,13 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_equal [1], posts(:welcome).comments.select { |c| c.id == 1 }.map(&:id)
end
+ def test_select_with_block_and_specific_attributes
+ assert_deprecated do
+ comments = posts(:welcome).comments.select(:id, :body) { |c| c.id == 1 }
+ assert_equal [1], comments.map(&:id)
+ end
+ end
+
def test_select_without_foreign_key
assert_equal companies(:first_firm).accounts.first.credit_limit, companies(:first_firm).accounts.select(:credit_limit).first.credit_limit
end
@@ -912,6 +919,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
company.clients_of_firm.build("name" => "Another Client")
company.clients_of_firm.build("name" => "Yet Another Client")
assert_equal 4, company.clients_of_firm.size
+ assert_equal 4, company.clients_of_firm.uniq.size
end
def test_collection_not_empty_after_building
@@ -983,7 +991,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_create_without_loading_association
- first_firm = companies(:first_firm)
+ first_firm = companies(:first_firm)
Firm.column_names
Client.column_names
@@ -1735,6 +1743,11 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert !company.clients.loaded?
end
+ def test_counter_cache_on_unloaded_association
+ car = Car.create(name: "My AppliCar")
+ assert_equal car.engines.size, 0
+ end
+
def test_get_ids_ignores_include_option
assert_equal [readers(:michael_welcome).id], posts(:welcome).readers_with_person_ids
end
@@ -2291,7 +2304,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
test "does not duplicate associations when used with natural primary keys" do
speedometer = Speedometer.create!(id: "4")
- speedometer.minivans.create!(minivan_id: "a-van-red" ,name: "a van", color: "red")
+ speedometer.minivans.create!(minivan_id: "a-van-red" , name: "a van", color: "red")
assert_equal 1, speedometer.minivans.to_a.size, "Only one association should be present:\n#{speedometer.minivans.to_a}"
assert_equal 1, speedometer.reload.minivans.to_a.size
@@ -2462,7 +2475,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
test "double insertion of new object to association when same association used in the after create callback of a new object" do
reset_callbacks(:save, Bulb) do
- Bulb.after_save { |record| record.car.bulbs.to_a }
+ Bulb.after_save { |record| record.car.bulbs.load }
car = Car.create!
car.bulbs << Bulb.new
assert_equal 1, car.bulbs.size
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 9f716d7820..fd79d4a5f9 100644
--- a/activerecord/test/cases/associations/has_many_through_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb
@@ -75,7 +75,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
posts = person_prime.includes(:posts).first.posts
assert_operator posts.length, :>, 1
- posts.each_cons(2) do |left,right|
+ posts.each_cons(2) do |left, right|
assert_operator left.id, :>, right.id
end
end
@@ -402,7 +402,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
end
end
- assert_equal nil, reference.reload.job_id
+ assert_nil reference.reload.job_id
ensure
Reference.make_comments = false
end
@@ -423,7 +423,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
end
# Check that the destroy callback on Reference did not run
- assert_equal nil, person.reload.comments
+ assert_nil person.reload.comments
ensure
Reference.make_comments = false
end
@@ -485,7 +485,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
end
references.each do |reference|
- assert_equal nil, reference.reload.job_id
+ assert_nil reference.reload.job_id
end
end
@@ -702,7 +702,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
[:added, :after, "Bob"],
[:added, :before, "Lary"],
[:added, :after, "Lary"]
- ],log.last(6)
+ ], log.last(6)
post.people_with_callbacks.build(first_name: "Ted")
assert_equal [
@@ -716,7 +716,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
[:added, :after, "Sam"]
], log.last(2)
- post.people_with_callbacks = [people(:michael),people(:david), Person.new(first_name: "Julian"), Person.create!(first_name: "Roger")]
+ post.people_with_callbacks = [people(:michael), people(:david), Person.new(first_name: "Julian"), Person.create!(first_name: "Roger")]
assert_equal((%w(Ted Bob Sam Lary) * 2).sort, log[-12..-5].collect(&:last).sort)
assert_equal [
[:added, :before, "Julian"],
@@ -883,10 +883,32 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
end
+ def test_collection_singular_ids_setter_with_changed_primary_key
+ company = companies(:first_firm)
+ client = companies(:first_client)
+ company.clients_using_primary_key_ids = [client.name]
+ assert_equal [client], company.clients_using_primary_key
+ end
+
def test_collection_singular_ids_setter_raises_exception_when_invalid_ids_set
company = companies(:rails_core)
- ids = [Developer.first.id, -9999]
- assert_raises(ActiveRecord::AssociationTypeMismatch) { company.developer_ids= ids }
+ ids = [Developer.first.id, -9999]
+ e = assert_raises(ActiveRecord::RecordNotFound) { company.developer_ids = ids }
+ assert_match(/Couldn't find all Developers with 'id'/, e.message)
+ end
+
+ def test_collection_singular_ids_setter_raises_exception_when_invalid_ids_set_with_changed_primary_key
+ company = companies(:first_firm)
+ ids = [Client.first.name, "unknown client"]
+ e = assert_raises(ActiveRecord::RecordNotFound) { company.clients_using_primary_key_ids = ids }
+ assert_match(/Couldn't find all Clients with 'name'/, e.message)
+ end
+
+ def test_collection_singular_ids_through_setter_raises_exception_when_invalid_ids_set
+ author = authors(:david)
+ ids = [categories(:general).name, "Unknown"]
+ e = assert_raises(ActiveRecord::RecordNotFound) { author.essay_category_ids = ids }
+ assert_equal "Couldn't find all Categories with 'name': (General, Unknown) (found 1 results, but was looking for 2)", e.message
end
def test_build_a_model_from_hm_through_association_with_where_clause
diff --git a/activerecord/test/cases/associations/has_one_associations_test.rb b/activerecord/test/cases/associations/has_one_associations_test.rb
index 1a0e6d2f8e..48fbc5990c 100644
--- a/activerecord/test/cases/associations/has_one_associations_test.rb
+++ b/activerecord/test/cases/associations/has_one_associations_test.rb
@@ -326,6 +326,16 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
end
end
+ def test_reload_association
+ odegy = companies(:odegy)
+
+ assert_equal 53, odegy.account.credit_limit
+ Account.where(id: odegy.account.id).update_all(credit_limit: 80)
+ assert_equal 53, odegy.account.credit_limit
+
+ assert_equal 80, odegy.reload_account.credit_limit
+ end
+
def test_build
firm = Firm.new("name" => "GlobalMegaCorp")
firm.save
@@ -601,7 +611,7 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
new_ship = Ship.create(name: "new name")
assert_queries(2) do
- # One query for updating name and second query for updating pirate_id
+ # One query to nullify the old ship, one query to update the new ship
pirate.ship = new_ship
end
@@ -675,6 +685,6 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
book = SpecialBook.create!(status: "published")
author.book = book
- refute_equal 0, SpecialAuthor.joins(:book).where(books: { status: "published" } ).count
+ refute_equal 0, SpecialAuthor.joins(:book).where(books: { status: "published" }).count
end
end
diff --git a/activerecord/test/cases/associations/has_one_through_associations_test.rb b/activerecord/test/cases/associations/has_one_through_associations_test.rb
index b2f47d2daf..432c3526a5 100644
--- a/activerecord/test/cases/associations/has_one_through_associations_test.rb
+++ b/activerecord/test/cases/associations/has_one_through_associations_test.rb
@@ -82,7 +82,7 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase
def test_set_record_to_nil_should_delete_association
@member.club = nil
@member.reload
- assert_equal nil, @member.current_membership
+ assert_nil @member.current_membership
assert_nil @member.club
end
@@ -110,12 +110,12 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase
# conditions on the through table
assert_equal clubs(:moustache_club), Member.all.merge!(includes: :favourite_club).find(@member.id).favourite_club
memberships(:membership_of_favourite_club).update_columns(favourite: false)
- assert_equal nil, Member.all.merge!(includes: :favourite_club).find(@member.id).reload.favourite_club
+ assert_nil Member.all.merge!(includes: :favourite_club).find(@member.id).reload.favourite_club
# conditions on the source table
assert_equal clubs(:moustache_club), Member.all.merge!(includes: :hairy_club).find(@member.id).hairy_club
clubs(:moustache_club).update_columns(name: "Association of Clean-Shaven Persons")
- assert_equal nil, Member.all.merge!(includes: :hairy_club).find(@member.id).reload.hairy_club
+ assert_nil Member.all.merge!(includes: :hairy_club).find(@member.id).reload.hairy_club
end
def test_has_one_through_polymorphic_with_source_type
@@ -123,7 +123,7 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase
end
def test_eager_has_one_through_polymorphic_with_source_type
- clubs = Club.all.merge!(includes: :sponsored_member, where: ["name = ?","Moustache and Eyebrow Fancier Club"]).to_a
+ clubs = Club.all.merge!(includes: :sponsored_member, where: ["name = ?", "Moustache and Eyebrow Fancier Club"]).to_a
# Only the eyebrow fanciers club has a sponsored_member
assert_not_nil assert_no_queries { clubs[0].sponsored_member }
end
diff --git a/activerecord/test/cases/associations/join_model_test.rb b/activerecord/test/cases/associations/join_model_test.rb
index 15a7ae941a..a4345f3857 100644
--- a/activerecord/test/cases/associations/join_model_test.rb
+++ b/activerecord/test/cases/associations/join_model_test.rb
@@ -155,21 +155,21 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
old_count = posts(:welcome).taggings.count
tagging = posts(:welcome).taggings.create(tag: tags(:misc))
assert_equal "Post", tagging.taggable_type
- assert_equal old_count+1, posts(:welcome).taggings.count
+ assert_equal old_count + 1, posts(:welcome).taggings.count
end
def test_create_bang_polymorphic_with_has_many_scope
old_count = posts(:welcome).taggings.count
tagging = posts(:welcome).taggings.create!(tag: tags(:misc))
assert_equal "Post", tagging.taggable_type
- assert_equal old_count+1, posts(:welcome).taggings.count
+ assert_equal old_count + 1, posts(:welcome).taggings.count
end
def test_create_polymorphic_has_one_with_scope
old_count = Tagging.count
tagging = posts(:welcome).create_tagging(tag: tags(:misc))
assert_equal "Post", tagging.taggable_type
- assert_equal old_count+1, Tagging.count
+ assert_equal old_count + 1, Tagging.count
end
def test_delete_polymorphic_has_many_with_delete_all
@@ -179,7 +179,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
old_count = Tagging.count
post.destroy
- assert_equal old_count-1, Tagging.count
+ assert_equal old_count - 1, Tagging.count
assert_equal 0, posts(:welcome).taggings.count
end
@@ -190,7 +190,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
old_count = Tagging.count
post.destroy
- assert_equal old_count-1, Tagging.count
+ assert_equal old_count - 1, Tagging.count
assert_equal 0, posts(:welcome).taggings.count
end
@@ -212,7 +212,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
old_count = Tagging.count
post.destroy
- assert_equal old_count-1, Tagging.count
+ assert_equal old_count - 1, Tagging.count
posts(:welcome).association(:tagging).reload
assert_nil posts(:welcome).tagging
end
@@ -402,7 +402,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
end
def test_has_many_through_polymorphic_has_one
- assert_equal Tagging.find(1,2).sort_by(&:id), authors(:david).taggings_2
+ assert_equal Tagging.find(1, 2).sort_by(&:id), authors(:david).taggings_2
end
def test_has_many_through_polymorphic_has_many
@@ -413,7 +413,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
author = Author.includes(:taggings).find authors(:david).id
expected_taggings = taggings(:welcome_general, :thinking_general)
assert_no_queries do
- assert_equal expected_taggings, author.taggings.distinct.sort_by(&:id)
+ assert_equal expected_taggings, author.taggings.uniq.sort_by(&:id)
end
end
@@ -421,7 +421,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
author = Author.all.merge!(where: ["name = ?", "David"], includes: :comments, order: "comments.id").first
SpecialComment.new; VerySpecialComment.new
assert_no_queries do
- assert_equal [1,2,3,5,6,7,8,9,10,12], author.comments.collect(&:id)
+ assert_equal [1, 2, 3, 5, 6, 7, 8, 9, 10, 12], author.comments.collect(&:id)
end
end
@@ -493,25 +493,25 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
push = Tag.create!(name: "pushme")
post_thinking = posts(:thinking)
assert_nothing_raised { post_thinking.tags << push }
- assert_nil( wrong = post_thinking.tags.detect { |t| t.class != Tag },
+ assert_nil(wrong = post_thinking.tags.detect { |t| t.class != Tag },
message = "Expected a Tag in tags collection, got #{wrong.class}.")
- assert_nil( wrong = post_thinking.taggings.detect { |t| t.class != Tagging },
+ assert_nil(wrong = post_thinking.taggings.detect { |t| t.class != Tagging },
message = "Expected a Tagging in taggings collection, got #{wrong.class}.")
assert_equal(count + 1, post_thinking.reload.tags.size)
assert_equal(count + 1, post_thinking.tags.reload.size)
assert_kind_of Tag, post_thinking.tags.create!(name: "foo")
- assert_nil( wrong = post_thinking.tags.detect { |t| t.class != Tag },
+ assert_nil(wrong = post_thinking.tags.detect { |t| t.class != Tag },
message = "Expected a Tag in tags collection, got #{wrong.class}.")
- assert_nil( wrong = post_thinking.taggings.detect { |t| t.class != Tagging },
+ assert_nil(wrong = post_thinking.taggings.detect { |t| t.class != Tagging },
message = "Expected a Tagging in taggings collection, got #{wrong.class}.")
assert_equal(count + 2, post_thinking.reload.tags.size)
assert_equal(count + 2, post_thinking.tags.reload.size)
assert_nothing_raised { post_thinking.tags.concat(Tag.create!(name: "abc"), Tag.create!(name: "def")) }
- assert_nil( wrong = post_thinking.tags.detect { |t| t.class != Tag },
+ assert_nil(wrong = post_thinking.tags.detect { |t| t.class != Tag },
message = "Expected a Tag in tags collection, got #{wrong.class}.")
- assert_nil( wrong = post_thinking.taggings.detect { |t| t.class != Tagging },
+ assert_nil(wrong = post_thinking.taggings.detect { |t| t.class != Tagging },
message = "Expected a Tagging in taggings collection, got #{wrong.class}.")
assert_equal(count + 4, post_thinking.reload.tags.size)
assert_equal(count + 4, post_thinking.tags.reload.size)
diff --git a/activerecord/test/cases/associations/left_outer_join_association_test.rb b/activerecord/test/cases/associations/left_outer_join_association_test.rb
index 2cc6468827..42dbbad1c8 100644
--- a/activerecord/test/cases/associations/left_outer_join_association_test.rb
+++ b/activerecord/test/cases/associations/left_outer_join_association_test.rb
@@ -5,7 +5,6 @@ require "models/author"
require "models/essay"
require "models/categorization"
require "models/person"
-require "active_support/core_ext/regexp"
class LeftOuterJoinAssociationTest < ActiveRecord::TestCase
fixtures :authors, :essays, :posts, :comments, :categorizations, :people
diff --git a/activerecord/test/cases/attribute_methods/read_test.rb b/activerecord/test/cases/attribute_methods/read_test.rb
index a8592bd179..978dd93fa4 100644
--- a/activerecord/test/cases/attribute_methods/read_test.rb
+++ b/activerecord/test/cases/attribute_methods/read_test.rb
@@ -14,6 +14,7 @@ module ActiveRecord
def self.decorate_matching_attribute_types(*); end
def self.initialize_generated_modules; end
+ include ActiveRecord::DefineCallbacks
include ActiveRecord::AttributeMethods
def self.attribute_names
diff --git a/activerecord/test/cases/attribute_methods_test.rb b/activerecord/test/cases/attribute_methods_test.rb
index 4c77ecab7c..3dc0c0ce53 100644
--- a/activerecord/test/cases/attribute_methods_test.rb
+++ b/activerecord/test/cases/attribute_methods_test.rb
@@ -92,7 +92,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
test "attribute keys on a new instance" do
t = Topic.new
- assert_equal nil, t.title, "The topics table has a title column, so it should be nil"
+ assert_nil t.title, "The topics table has a title column, so it should be nil"
assert_raise(NoMethodError) { t.title2 }
end
@@ -156,7 +156,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
keyboard = Keyboard.create
keyboard.key_number = "10"
assert_equal "10", keyboard.id_before_type_cast
- assert_equal nil, keyboard.read_attribute_before_type_cast("id")
+ assert_nil keyboard.read_attribute_before_type_cast("id")
assert_equal "10", keyboard.read_attribute_before_type_cast("key_number")
assert_equal "10", keyboard.read_attribute_before_type_cast(:key_number)
end
@@ -213,7 +213,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
record.written_on = "345643456"
assert_equal "345643456", record.written_on_before_type_cast
- assert_equal nil, record.written_on
+ assert_nil record.written_on
record.written_on = "2009-10-11 12:13:14"
assert_equal "2009-10-11 12:13:14", record.written_on_before_type_cast
@@ -294,7 +294,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
topic = Topic.new(new_topic)
assert_equal new_topic[:title], topic.title
- topic.attributes= new_topic_values
+ topic.attributes = new_topic_values
assert_equal new_topic_values[:title], topic.title
end
@@ -319,6 +319,13 @@ class AttributeMethodsTest < ActiveRecord::TestCase
assert_equal "Still another topic: part 4", topic.title
end
+ test "write_attribute can write aliased attributes as well" do
+ topic = Topic.new(title: "Don't change the topic")
+ topic.write_attribute :heading, "New topic"
+
+ assert_equal "New topic", topic.title
+ end
+
test "read_attribute" do
topic = Topic.new
topic.title = "Don't change the topic"
@@ -329,6 +336,16 @@ class AttributeMethodsTest < ActiveRecord::TestCase
assert_equal "Don't change the topic", topic[:title]
end
+ test "read_attribute can read aliased attributes as well" do
+ topic = Topic.new(title: "Don't change the topic")
+
+ assert_equal "Don't change the topic", topic.read_attribute("heading")
+ assert_equal "Don't change the topic", topic["heading"]
+
+ assert_equal "Don't change the topic", topic.read_attribute(:heading)
+ assert_equal "Don't change the topic", topic[:heading]
+ end
+
test "read_attribute raises ActiveModel::MissingAttributeError when the attribute does not exist" do
computer = Computer.select("id").first
assert_raises(ActiveModel::MissingAttributeError) { computer[:developer] }
@@ -609,7 +626,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
utc_time = Time.utc(2008, 1, 1)
cst_time = utc_time.in_time_zone("Central Time (US & Canada)")
in_time_zone "Pacific Time (US & Canada)" do
- record = @target.new
+ record = @target.new
record.written_on = cst_time
assert_equal utc_time, record.written_on
assert_equal ActiveSupport::TimeZone["Pacific Time (US & Canada)"], record.written_on.time_zone
@@ -633,7 +650,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
(-11..13).each do |timezone_offset|
time_string = utc_time.in_time_zone(timezone_offset).to_s
in_time_zone "Pacific Time (US & Canada)" do
- record = @target.new
+ record = @target.new
record.written_on = time_string
assert_equal Time.zone.parse(time_string), record.written_on
assert_equal ActiveSupport::TimeZone["Pacific Time (US & Canada)"], record.written_on.time_zone
@@ -654,7 +671,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
test "setting a time zone-aware attribute to a blank string returns nil" do
in_time_zone "Pacific Time (US & Canada)" do
- record = @target.new
+ record = @target.new
record.written_on = " "
assert_nil record.written_on
assert_nil record[:written_on]
@@ -665,7 +682,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
time_string = "Tue Jan 01 00:00:00 2008"
(-11..13).each do |timezone_offset|
in_time_zone timezone_offset do
- record = @target.new
+ record = @target.new
record.written_on = time_string
assert_equal Time.zone.parse(time_string), record.written_on
assert_equal ActiveSupport::TimeZone[timezone_offset], record.written_on.time_zone
@@ -677,7 +694,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
test "setting a time zone-aware datetime in the current time zone" do
utc_time = Time.utc(2008, 1, 1)
in_time_zone "Pacific Time (US & Canada)" do
- record = @target.new
+ record = @target.new
record.written_on = utc_time.in_time_zone
assert_equal utc_time, record.written_on
assert_equal ActiveSupport::TimeZone["Pacific Time (US & Canada)"], record.written_on.time_zone
@@ -737,7 +754,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
test "time zone-aware attributes do not recurse infinitely on invalid values" do
in_time_zone "Pacific Time (US & Canada)" do
record = @target.new(bonus_time: [])
- assert_equal nil, record.bonus_time
+ assert_nil record.bonus_time
end
end
diff --git a/activerecord/test/cases/attribute_set_test.rb b/activerecord/test/cases/attribute_set_test.rb
index 059b5b2401..bd4b200735 100644
--- a/activerecord/test/cases/attribute_set_test.rb
+++ b/activerecord/test/cases/attribute_set_test.rb
@@ -25,7 +25,7 @@ module ActiveRecord
attributes = builder.build_from_database(foo: "3.3")
assert_equal "3.3", attributes[:foo].value_before_type_cast
- assert_equal nil, attributes[:bar].value_before_type_cast
+ assert_nil attributes[:bar].value_before_type_cast
assert_equal :bar, attributes[:bar].name
end
diff --git a/activerecord/test/cases/attributes_test.rb b/activerecord/test/cases/attributes_test.rb
index f4620ae2da..3705a6be89 100644
--- a/activerecord/test/cases/attributes_test.rb
+++ b/activerecord/test/cases/attributes_test.rb
@@ -255,5 +255,13 @@ module ActiveRecord
assert_includes inspection, "non_existent_decimal"
end
+
+ test "attributes do not require a type" do
+ klass = Class.new(OverloadedType) do
+ attribute :no_type
+ end
+ assert_equal 1, klass.new(no_type: 1).no_type
+ assert_equal "foo", klass.new(no_type: "foo").no_type
+ end
end
end
diff --git a/activerecord/test/cases/autosave_association_test.rb b/activerecord/test/cases/autosave_association_test.rb
index c24d7b8835..eb80ae4f7c 100644
--- a/activerecord/test/cases/autosave_association_test.rb
+++ b/activerecord/test/cases/autosave_association_test.rb
@@ -36,11 +36,11 @@ class TestAutosaveAssociationsInGeneral < ActiveRecord::TestCase
private
- def should_be_cool
- unless self.first_name == "cool"
- errors.add :first_name, "not cool"
+ def should_be_cool
+ unless self.first_name == "cool"
+ errors.add :first_name, "not cool"
+ end
end
- end
}
reference = Class.new(ActiveRecord::Base) {
self.table_name = "references"
@@ -792,6 +792,7 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase
end
@ship.pirate.catchphrase = "Changed Catchphrase"
+ @ship.name_will_change!
assert_raise(RuntimeError) { assert !@pirate.save }
assert_not_nil @pirate.reload.ship
@@ -1130,7 +1131,7 @@ class TestAutosaveAssociationOnAHasOneAssociation < ActiveRecord::TestCase
assert_queries(0) { @ship.save! }
@parrot = @pirate.parrots.create(name: "some_name")
- @parrot.name="changed_name"
+ @parrot.name = "changed_name"
assert_queries(1) { @ship.save! }
assert_queries(0) { @ship.save! }
end
@@ -1698,3 +1699,27 @@ class TestAutosaveAssociationWithTouch < ActiveRecord::TestCase
assert_nothing_raised { invoice.line_items.create(amount: 10) }
end
end
+
+class TestAutosaveAssociationOnAHasManyAssociationWithInverse < ActiveRecord::TestCase
+ class Post < ActiveRecord::Base
+ has_many :comments, inverse_of: :post
+ end
+
+ class Comment < ActiveRecord::Base
+ belongs_to :post, inverse_of: :comments
+
+ attr_accessor :post_comments_count
+ after_save do
+ self.post_comments_count = post.comments.count
+ end
+ end
+
+ def test_after_save_callback_with_autosave
+ post = Post.new(title: "Test", body: "...")
+ comment = post.comments.build(body: "...")
+ post.save!
+
+ assert_equal 1, post.comments.count
+ assert_equal 1, comment.post_comments_count
+ end
+end
diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb
index fafa144c6f..ad9bc60944 100644
--- a/activerecord/test/cases/base_test.rb
+++ b/activerecord/test/cases/base_test.rb
@@ -494,12 +494,12 @@ class BasicsTest < ActiveRecord::TestCase
def test_utc_as_time_zone_and_new
with_timezone_config default: :utc do
- attributes = { "bonus_time(1i)"=>"2000",
- "bonus_time(2i)"=>"1",
- "bonus_time(3i)"=>"1",
- "bonus_time(4i)"=>"10",
- "bonus_time(5i)"=>"35",
- "bonus_time(6i)"=>"50" }
+ attributes = { "bonus_time(1i)" => "2000",
+ "bonus_time(2i)" => "1",
+ "bonus_time(3i)" => "1",
+ "bonus_time(4i)" => "10",
+ "bonus_time(5i)" => "35",
+ "bonus_time(6i)" => "50" }
topic = Topic.new(attributes)
assert_equal Time.utc(2000, 1, 1, 10, 35, 50), topic.bonus_time
end
@@ -622,7 +622,7 @@ class BasicsTest < ActiveRecord::TestCase
Topic.find(topic.id).destroy
end
- assert_equal nil, Topic.find_by_id(topic.id)
+ assert_nil Topic.find_by_id(topic.id)
end
def test_comparison_with_different_objects
@@ -898,7 +898,7 @@ class BasicsTest < ActiveRecord::TestCase
# fixed dates / times
assert_equal Date.new(2004, 1, 1), default.fixed_date
- assert_equal Time.local(2004, 1,1,0,0,0,0), default.fixed_time
+ assert_equal Time.local(2004, 1, 1, 0, 0, 0, 0), default.fixed_time
# char types
assert_equal "Y", default.char1
@@ -1109,7 +1109,7 @@ class BasicsTest < ActiveRecord::TestCase
end
def test_set_table_name_with_inheritance
- k = Class.new( ActiveRecord::Base )
+ k = Class.new(ActiveRecord::Base)
def k.name; "Foo"; end
def k.table_name; super + "ks"; end
assert_equal "foosks", k.table_name
@@ -1160,7 +1160,7 @@ class BasicsTest < ActiveRecord::TestCase
end
def test_find_last
- last = Developer.last
+ last = Developer.last
assert_equal last, Developer.all.merge!(order: "id desc").first
end
@@ -1179,17 +1179,17 @@ class BasicsTest < ActiveRecord::TestCase
end
def test_find_ordered_last
- last = Developer.all.merge!(order: "developers.salary ASC").last
+ last = Developer.all.merge!(order: "developers.salary ASC").last
assert_equal last, Developer.all.merge!(order: "developers.salary ASC").to_a.last
end
def test_find_reverse_ordered_last
- last = Developer.all.merge!(order: "developers.salary DESC").last
+ last = Developer.all.merge!(order: "developers.salary DESC").last
assert_equal last, Developer.all.merge!(order: "developers.salary DESC").to_a.last
end
def test_find_multiple_ordered_last
- last = Developer.all.merge!(order: "developers.name, developers.salary DESC").last
+ last = Developer.all.merge!(order: "developers.name, developers.salary DESC").last
assert_equal last, Developer.all.merge!(order: "developers.name, developers.salary DESC").to_a.last
end
@@ -1204,7 +1204,7 @@ class BasicsTest < ActiveRecord::TestCase
end
def test_find_symbol_ordered_last
- last = Developer.all.merge!(order: :salary).last
+ last = Developer.all.merge!(order: :salary).last
assert_equal last, Developer.all.merge!(order: :salary).to_a.last
end
@@ -1284,7 +1284,7 @@ class BasicsTest < ActiveRecord::TestCase
def test_marshal_round_trip
expected = posts(:welcome)
marshalled = Marshal.dump(expected)
- actual = Marshal.load(marshalled)
+ actual = Marshal.load(marshalled)
assert_equal expected.attributes, actual.attributes
end
@@ -1428,6 +1428,16 @@ class BasicsTest < ActiveRecord::TestCase
assert_nil hash["firm_name"]
end
+ def test_slice_accepts_array_argument
+ attrs = {
+ title: "slice",
+ author_name: "@Cohen-Carlisle",
+ content: "accept arrays so I don't have to splat"
+ }.with_indifferent_access
+ topic = Topic.new(attrs)
+ assert_equal attrs, topic.slice(attrs.keys)
+ end
+
def test_default_values_are_deeply_dupped
company = Company.new
company.description << "foo"
diff --git a/activerecord/test/cases/calculations_test.rb b/activerecord/test/cases/calculations_test.rb
index db2871d383..87e99fb25c 100644
--- a/activerecord/test/cases/calculations_test.rb
+++ b/activerecord/test/cases/calculations_test.rb
@@ -92,14 +92,14 @@ class CalculationsTest < ActiveRecord::TestCase
def test_should_group_by_field
c = Account.group(:firm_id).sum(:credit_limit)
- [1,6,2].each do |firm_id|
+ [1, 6, 2].each do |firm_id|
assert_includes c.keys, firm_id, "Group #{c.inspect} does not contain firm_id #{firm_id}"
end
end
def test_should_group_by_arel_attribute
c = Account.group(Account.arel_table[:firm_id]).sum(:credit_limit)
- [1,6,2].each do |firm_id|
+ [1, 6, 2].each do |firm_id|
assert_includes c.keys, firm_id, "Group #{c.inspect} does not contain firm_id #{firm_id}"
end
end
@@ -453,7 +453,7 @@ class CalculationsTest < ActiveRecord::TestCase
def test_should_count_field_in_joined_table_with_group_by
c = Account.group("accounts.firm_id").joins(:firm).count("companies.id")
- [1,6,2,9].each { |firm_id| assert_includes c.keys, firm_id }
+ [1, 6, 2, 9].each { |firm_id| assert_includes c.keys, firm_id }
end
def test_should_count_field_of_root_table_with_conflicting_group_by_column
@@ -572,7 +572,7 @@ class CalculationsTest < ActiveRecord::TestCase
end
def test_pluck
- assert_equal [1,2,3,4,5], Topic.order(:id).pluck(:id)
+ assert_equal [1, 2, 3, 4, 5], Topic.order(:id).pluck(:id)
end
def test_pluck_without_column_names
@@ -608,7 +608,7 @@ class CalculationsTest < ActiveRecord::TestCase
end
def test_pluck_with_qualified_column_name
- assert_equal [1,2,3,4,5], Topic.order(:id).pluck("topics.id")
+ assert_equal [1, 2, 3, 4, 5], Topic.order(:id).pluck("topics.id")
end
def test_pluck_auto_table_name_prefix
@@ -688,7 +688,7 @@ class CalculationsTest < ActiveRecord::TestCase
def test_pluck_replaces_select_clause
taks_relation = Topic.select(:approved, :id).order(:id)
- assert_equal [1,2,3,4,5], taks_relation.pluck(:id)
+ assert_equal [1, 2, 3, 4, 5], taks_relation.pluck(:id)
assert_equal [false, true, true, true, true], taks_relation.pluck(:approved)
end
diff --git a/activerecord/test/cases/callbacks_test.rb b/activerecord/test/cases/callbacks_test.rb
index 4b517e9d70..11ec6fb2c5 100644
--- a/activerecord/test/cases/callbacks_test.rb
+++ b/activerecord/test/cases/callbacks_test.rb
@@ -449,7 +449,7 @@ class CallbacksTest < ActiveRecord::TestCase
assert david.valid?
assert !david.save
exc = assert_raise(ActiveRecord::RecordNotSaved) { david.save! }
- assert_equal exc.record, david
+ assert_equal david, exc.record
assert_equal "Failed to save the record", exc.message
end
@@ -493,7 +493,7 @@ class CallbacksTest < ActiveRecord::TestCase
assert_deprecated do
assert !david.destroy
exc = assert_raise(ActiveRecord::RecordNotDestroyed) { david.destroy! }
- assert_equal exc.record, david
+ assert_equal david, exc.record
assert_equal "Failed to destroy the record", exc.message
end
assert_not_nil ImmutableDeveloper.find_by_id(1)
@@ -527,7 +527,7 @@ class CallbacksTest < ActiveRecord::TestCase
assert david.valid?
assert !david.save
exc = assert_raise(ActiveRecord::RecordNotSaved) { david.save! }
- assert_equal exc.record, david
+ assert_equal david, exc.record
david = DeveloperWithCanceledCallbacks.find(1)
david.salary = 10_000_000
@@ -554,7 +554,7 @@ class CallbacksTest < ActiveRecord::TestCase
david = DeveloperWithCanceledCallbacks.find(1)
assert !david.destroy
exc = assert_raise(ActiveRecord::RecordNotDestroyed) { david.destroy! }
- assert_equal exc.record, david
+ assert_equal david, exc.record
assert_not_nil ImmutableDeveloper.find_by_id(1)
someone = CallbackHaltedDeveloper.find(1)
diff --git a/activerecord/test/cases/collection_cache_key_test.rb b/activerecord/test/cases/collection_cache_key_test.rb
index a2874438c1..381a78a8e2 100644
--- a/activerecord/test/cases/collection_cache_key_test.rb
+++ b/activerecord/test/cases/collection_cache_key_test.rb
@@ -28,20 +28,20 @@ module ActiveRecord
end
test "it triggers at most one query" do
- developers = Developer.where(name: "David")
+ developers = Developer.where(name: "David")
assert_queries(1) { developers.cache_key }
assert_queries(0) { developers.cache_key }
end
test "it doesn't trigger any query if the relation is already loaded" do
- developers = Developer.where(name: "David").load
+ developers = Developer.where(name: "David").load
assert_queries(0) { developers.cache_key }
end
test "relation cache_key changes when the sql query changes" do
developers = Developer.where(name: "David")
- other_relation = Developer.where(name: "David").where("1 = 1")
+ other_relation = Developer.where(name: "David").where("1 = 1")
assert_not_equal developers.cache_key, other_relation.cache_key
end
diff --git a/activerecord/test/cases/comment_test.rb b/activerecord/test/cases/comment_test.rb
index 262ad319be..a625299e8d 100644
--- a/activerecord/test/cases/comment_test.rb
+++ b/activerecord/test/cases/comment_test.rb
@@ -2,7 +2,6 @@ require "cases/helper"
require "support/schema_dumping_helper"
if ActiveRecord::Base.connection.supports_comments?
-
class CommentTest < ActiveRecord::TestCase
include SchemaDumpingHelper
@@ -102,6 +101,7 @@ if ActiveRecord::Base.connection.supports_comments?
# Do all the stuff from other tests
@connection.add_column :commenteds, :rating, :integer, comment: "I am running out of imagination"
@connection.change_column :commenteds, :content, :string, comment: "Whoa, content describes itself!"
+ @connection.change_column :commenteds, :content, :string
@connection.change_column :commenteds, :obvious, :string, comment: nil
@connection.add_index :commenteds, :obvious, name: "idx_obvious", comment: "We need to see obvious comments"
@@ -135,5 +135,4 @@ if ActiveRecord::Base.connection.supports_comments?
assert_no_match %r[t\.string\s+"absent_comment", comment:\n], output
end
end
-
end
diff --git a/activerecord/test/cases/connection_adapters/connection_handler_test.rb b/activerecord/test/cases/connection_adapters/connection_handler_test.rb
index d5d16e7568..2c33bf22ab 100644
--- a/activerecord/test/cases/connection_adapters/connection_handler_test.rb
+++ b/activerecord/test/cases/connection_adapters/connection_handler_test.rb
@@ -6,7 +6,7 @@ module ActiveRecord
def setup
@handler = ConnectionHandler.new
@spec_name = "primary"
- @pool = @handler.establish_connection(ActiveRecord::Base.configurations["arunit"])
+ @pool = @handler.establish_connection(ActiveRecord::Base.configurations["arunit"])
end
def test_establish_connection_uses_spec_name
diff --git a/activerecord/test/cases/connection_adapters/merge_and_resolve_default_url_config_test.rb b/activerecord/test/cases/connection_adapters/merge_and_resolve_default_url_config_test.rb
index 4bb5c4f2e2..8faa67255d 100644
--- a/activerecord/test/cases/connection_adapters/merge_and_resolve_default_url_config_test.rb
+++ b/activerecord/test/cases/connection_adapters/merge_and_resolve_default_url_config_test.rb
@@ -27,7 +27,7 @@ module ActiveRecord
ENV["DATABASE_URL"] = "postgres://localhost/foo"
config = { "not_production" => { "adapter" => "not_postgres", "database" => "not_foo" } }
actual = resolve_spec(:default_env, config)
- expected = { "adapter"=>"postgresql", "database"=>"foo", "host"=>"localhost", "name"=>"default_env" }
+ expected = { "adapter" => "postgresql", "database" => "foo", "host" => "localhost", "name" => "default_env" }
assert_equal expected, actual
end
@@ -37,7 +37,7 @@ module ActiveRecord
config = { "not_production" => { "adapter" => "not_postgres", "database" => "not_foo" } }
actual = resolve_spec(:foo, config)
- expected = { "adapter" => "postgresql", "database" => "foo", "host" => "localhost","name"=>"foo" }
+ expected = { "adapter" => "postgresql", "database" => "foo", "host" => "localhost", "name" => "foo" }
assert_equal expected, actual
end
@@ -47,7 +47,7 @@ module ActiveRecord
config = { "not_production" => { "adapter" => "not_postgres", "database" => "not_foo" } }
actual = resolve_spec(:foo, config)
- expected = { "adapter" => "postgresql", "database" => "foo", "host" => "localhost","name"=>"foo" }
+ expected = { "adapter" => "postgresql", "database" => "foo", "host" => "localhost", "name" => "foo" }
assert_equal expected, actual
end
@@ -55,13 +55,13 @@ module ActiveRecord
ENV["DATABASE_URL"] = "postgres://localhost/foo"
config = { "production" => { "adapter" => "not_postgres", "database" => "not_foo", "host" => "localhost" } }
actual = resolve_spec(:production, config)
- expected = { "adapter"=>"not_postgres", "database"=>"not_foo", "host"=>"localhost", "name"=>"production" }
+ expected = { "adapter" => "not_postgres", "database" => "not_foo", "host" => "localhost", "name" => "production" }
assert_equal expected, actual
end
def test_resolver_with_database_uri_and_unknown_symbol_key
ENV["DATABASE_URL"] = "postgres://localhost/foo"
- config = { "not_production" => { "adapter" => "not_postgres", "database" => "not_foo" } }
+ config = { "not_production" => { "adapter" => "not_postgres", "database" => "not_foo" } }
assert_raises AdapterNotSpecified do
resolve_spec(:production, config)
end
@@ -71,7 +71,7 @@ module ActiveRecord
ENV["DATABASE_URL"] = "not-postgres://not-localhost/not_foo"
config = { "production" => { "adapter" => "also_not_postgres", "database" => "also_not_foo" } }
actual = resolve_spec("postgres://localhost/foo", config)
- expected = { "adapter"=>"postgresql", "database"=>"foo", "host"=>"localhost" }
+ expected = { "adapter" => "postgresql", "database" => "foo", "host" => "localhost" }
assert_equal expected, actual
end
@@ -85,7 +85,7 @@ module ActiveRecord
ENV["DATABASE_URL"] = "postgres://localhost/foo"
config = { "not_default_env" => { "adapter" => "not_postgres", "database" => "not_foo" } }
actual = resolve_config(config)
- expect_prod = { "adapter"=>"postgresql", "database"=>"foo", "host"=>"localhost" }
+ expect_prod = { "adapter" => "postgresql", "database" => "foo", "host" => "localhost" }
assert_equal expect_prod, actual["default_env"]
end
@@ -93,7 +93,7 @@ module ActiveRecord
ENV["DATABASE_URL"] = "ibm-db://localhost/foo"
config = { "default_env" => { "adapter" => "not_postgres", "database" => "not_foo", "host" => "localhost" } }
actual = resolve_spec(:default_env, config)
- expected = { "adapter"=>"ibm_db", "database"=>"foo", "host"=>"localhost", "name"=>"default_env" }
+ expected = { "adapter" => "ibm_db", "database" => "foo", "host" => "localhost", "name" => "default_env" }
assert_equal expected, actual
end
@@ -142,13 +142,13 @@ module ActiveRecord
"database" => "foo",
"host" => "localhost" }
assert_equal expected, actual["default_env"]
- assert_equal nil, actual["production"]
- assert_equal nil, actual["development"]
- assert_equal nil, actual["test"]
- assert_equal nil, actual[:default_env]
- assert_equal nil, actual[:production]
- assert_equal nil, actual[:development]
- assert_equal nil, actual[:test]
+ assert_nil actual["production"]
+ assert_nil actual["development"]
+ assert_nil actual["test"]
+ assert_nil actual[:default_env]
+ assert_nil actual[:production]
+ assert_nil actual[:development]
+ assert_nil actual[:test]
end
def test_blank_with_database_url_with_rails_env
@@ -162,15 +162,15 @@ module ActiveRecord
"host" => "localhost" }
assert_equal expected, actual["not_production"]
- assert_equal nil, actual["production"]
- assert_equal nil, actual["default_env"]
- assert_equal nil, actual["development"]
- assert_equal nil, actual["test"]
- assert_equal nil, actual[:default_env]
- assert_equal nil, actual[:not_production]
- assert_equal nil, actual[:production]
- assert_equal nil, actual[:development]
- assert_equal nil, actual[:test]
+ assert_nil actual["production"]
+ assert_nil actual["default_env"]
+ assert_nil actual["development"]
+ assert_nil actual["test"]
+ assert_nil actual[:default_env]
+ assert_nil actual[:not_production]
+ assert_nil actual[:production]
+ assert_nil actual[:development]
+ assert_nil actual[:test]
end
def test_blank_with_database_url_with_rack_env
@@ -184,15 +184,15 @@ module ActiveRecord
"host" => "localhost" }
assert_equal expected, actual["not_production"]
- assert_equal nil, actual["production"]
- assert_equal nil, actual["default_env"]
- assert_equal nil, actual["development"]
- assert_equal nil, actual["test"]
- assert_equal nil, actual[:default_env]
- assert_equal nil, actual[:not_production]
- assert_equal nil, actual[:production]
- assert_equal nil, actual[:development]
- assert_equal nil, actual[:test]
+ assert_nil actual["production"]
+ assert_nil actual["default_env"]
+ assert_nil actual["development"]
+ assert_nil actual["test"]
+ assert_nil actual[:default_env]
+ assert_nil actual[:not_production]
+ assert_nil actual[:production]
+ assert_nil actual[:development]
+ assert_nil actual[:test]
end
def test_database_url_with_ipv6_host_and_port
@@ -213,7 +213,7 @@ module ActiveRecord
config = { "default_env" => { "url" => "postgres://localhost/foo" } }
actual = resolve_config(config)
expected = { "default_env" =>
- { "adapter" => "postgresql",
+ { "adapter" => "postgresql",
"database" => "foo",
"host" => "localhost"
}
diff --git a/activerecord/test/cases/connection_adapters/quoting_test.rb b/activerecord/test/cases/connection_adapters/quoting_test.rb
deleted file mode 100644
index 59dcb96ebc..0000000000
--- a/activerecord/test/cases/connection_adapters/quoting_test.rb
+++ /dev/null
@@ -1,13 +0,0 @@
-require "cases/helper"
-
-module ActiveRecord
- module ConnectionAdapters
- module Quoting
- class QuotingTest < ActiveRecord::TestCase
- def test_quoting_classes
- assert_equal "'Object'", AbstractAdapter.new(nil).quote(Object)
- end
- end
- end
- end
-end
diff --git a/activerecord/test/cases/connection_adapters/schema_cache_test.rb b/activerecord/test/cases/connection_adapters/schema_cache_test.rb
index d4459603af..1b4f80fc67 100644
--- a/activerecord/test/cases/connection_adapters/schema_cache_test.rb
+++ b/activerecord/test/cases/connection_adapters/schema_cache_test.rb
@@ -12,6 +12,33 @@ module ActiveRecord
assert_equal "id", @cache.primary_keys("posts")
end
+ def test_yaml_dump_and_load
+ @cache.columns("posts")
+ @cache.columns_hash("posts")
+ @cache.data_sources("posts")
+ @cache.primary_keys("posts")
+
+ new_cache = YAML.load(YAML.dump(@cache))
+ assert_no_queries do
+ assert_equal 11, new_cache.columns("posts").size
+ assert_equal 11, new_cache.columns_hash("posts").size
+ assert new_cache.data_sources("posts")
+ assert_equal "id", new_cache.primary_keys("posts")
+ end
+ end
+
+ def test_yaml_loads_5_1_dump
+ body = File.open(schema_dump_path).read
+ cache = YAML.load(body)
+
+ assert_no_queries do
+ assert_equal 11, cache.columns("posts").size
+ assert_equal 11, cache.columns_hash("posts").size
+ assert cache.data_sources("posts")
+ assert_equal "id", cache.primary_keys("posts")
+ end
+ end
+
def test_primary_key_for_non_existent_table
assert_nil @cache.primary_keys("omgponies")
end
@@ -45,10 +72,12 @@ module ActiveRecord
@cache = Marshal.load(Marshal.dump(@cache))
- assert_equal 11, @cache.columns("posts").size
- assert_equal 11, @cache.columns_hash("posts").size
- assert @cache.data_sources("posts")
- assert_equal "id", @cache.primary_keys("posts")
+ assert_no_queries do
+ assert_equal 11, @cache.columns("posts").size
+ assert_equal 11, @cache.columns_hash("posts").size
+ assert @cache.data_sources("posts")
+ assert_equal "id", @cache.primary_keys("posts")
+ end
end
def test_table_methods_deprecation
@@ -56,6 +85,12 @@ module ActiveRecord
assert_deprecated { assert @cache.tables("posts") }
assert_deprecated { @cache.clear_table_cache!("posts") }
end
+
+ private
+
+ def schema_dump_path
+ "test/assets/schema_dump_5_1.yml"
+ end
end
end
end
diff --git a/activerecord/test/cases/connection_pool_test.rb b/activerecord/test/cases/connection_pool_test.rb
index d7ff9d6880..42600e53fd 100644
--- a/activerecord/test/cases/connection_pool_test.rb
+++ b/activerecord/test/cases/connection_pool_test.rb
@@ -184,14 +184,14 @@ module ActiveRecord
def test_checkout_behaviour
pool = ConnectionPool.new ActiveRecord::Base.connection_pool.spec
- connection = pool.connection
- assert_not_nil connection
+ main_connection = pool.connection
+ assert_not_nil main_connection
threads = []
4.times do |i|
threads << Thread.new(i) do
- connection = pool.connection
- assert_not_nil connection
- connection.close
+ thread_connection = pool.connection
+ assert_not_nil thread_connection
+ thread_connection.close
end
end
@@ -455,49 +455,63 @@ module ActiveRecord
with_single_connection_pool do |pool|
[:disconnect, :disconnect!, :clear_reloadable_connections, :clear_reloadable_connections!].each do |group_action_method|
conn = pool.connection # drain the only available connection
- second_thread_done = Concurrent::CountDownLatch.new
-
- # create a first_thread and let it get into the FIFO queue first
- first_thread = Thread.new do
- pool.with_connection { second_thread_done.wait }
- end
-
- # wait for first_thread to get in queue
- Thread.pass until pool.num_waiting_in_queue == 1
-
- # create a different, later thread, that will attempt to do a "group action",
- # but because of the group action semantics it should be able to preempt the
- # first_thread when a connection is made available
- second_thread = Thread.new do
- pool.send(group_action_method)
- second_thread_done.count_down
- end
+ second_thread_done = Concurrent::Event.new
- # wait for second_thread to get in queue
- Thread.pass until pool.num_waiting_in_queue == 2
-
- # return the only available connection
- pool.checkin(conn)
-
- # if the second_thread is not able to preempt the first_thread,
- # they will temporarily (until either of them timeouts with ConnectionTimeoutError)
- # deadlock and a join(2) timeout will be reached
- failed = true unless second_thread.join(2)
-
- #--- post test clean up start
- second_thread_done.count_down if failed
-
- # after `pool.disconnect()` the first thread will be left stuck in queue, no need to wait for
- # it to timeout with ConnectionTimeoutError
- if (group_action_method == :disconnect || group_action_method == :disconnect!) && pool.num_waiting_in_queue > 0
- pool.with_connection {} # create a new connection in case there are threads still stuck in a queue
+ begin
+ # create a first_thread and let it get into the FIFO queue first
+ first_thread = Thread.new do
+ pool.with_connection { second_thread_done.wait }
+ end
+
+ # wait for first_thread to get in queue
+ Thread.pass until pool.num_waiting_in_queue == 1
+
+ # create a different, later thread, that will attempt to do a "group action",
+ # but because of the group action semantics it should be able to preempt the
+ # first_thread when a connection is made available
+ second_thread = Thread.new do
+ pool.send(group_action_method)
+ second_thread_done.set
+ end
+
+ # wait for second_thread to get in queue
+ Thread.pass until pool.num_waiting_in_queue == 2
+
+ # return the only available connection
+ pool.checkin(conn)
+
+ # if the second_thread is not able to preempt the first_thread,
+ # they will temporarily (until either of them timeouts with ConnectionTimeoutError)
+ # deadlock and a join(2) timeout will be reached
+ assert second_thread.join(2), "#{group_action_method} is not able to preempt other waiting threads"
+
+ ensure
+ # post test clean up
+ failed = !second_thread_done.set?
+
+ if failed
+ second_thread_done.set
+
+ puts
+ puts ">>> test_disconnect_and_clear_reloadable_connections_are_able_to_preempt_other_waiting_threads / #{group_action_method}"
+ p [first_thread, second_thread]
+ p pool.stat
+ p pool.connections.map(&:owner)
+
+ first_thread.join(2)
+ second_thread.join(2)
+
+ puts "---"
+ p [first_thread, second_thread]
+ p pool.stat
+ p pool.connections.map(&:owner)
+ puts "<<<"
+ puts
+ end
+
+ first_thread.join(10) || raise("first_thread got stuck")
+ second_thread.join(10) || raise("second_thread got stuck")
end
-
- first_thread.join
- second_thread.join
- #--- post test clean up end
-
- flunk "#{group_action_method} is not able to preempt other waiting threads" if failed
end
end
end
@@ -526,6 +540,26 @@ module ActiveRecord
end
end
+ def test_connection_pool_stat
+ with_single_connection_pool do |pool|
+ pool.with_connection do |connection|
+ stats = pool.stat
+ assert_equal({ size: 1, connections: 1, busy: 1, dead: 0, idle: 0, waiting: 0, checkout_timeout: 5 }, stats)
+ end
+
+ stats = pool.stat
+ assert_equal({ size: 1, connections: 1, busy: 0, dead: 0, idle: 1, waiting: 0, checkout_timeout: 5 }, stats)
+
+ Thread.new do
+ pool.checkout
+ Thread.current.kill
+ end.join
+
+ stats = pool.stat
+ assert_equal({ size: 1, connections: 1, busy: 0, dead: 1, idle: 0, waiting: 0, checkout_timeout: 5 }, stats)
+ end
+ end
+
private
def with_single_connection_pool
one_conn_spec = ActiveRecord::Base.connection_pool.spec.dup
diff --git a/activerecord/test/cases/connection_specification/resolver_test.rb b/activerecord/test/cases/connection_specification/resolver_test.rb
index 0f62c73f8f..13b5bae13c 100644
--- a/activerecord/test/cases/connection_specification/resolver_test.rb
+++ b/activerecord/test/cases/connection_specification/resolver_test.rb
@@ -4,11 +4,11 @@ module ActiveRecord
module ConnectionAdapters
class ConnectionSpecification
class ResolverTest < ActiveRecord::TestCase
- def resolve(spec, config={})
+ def resolve(spec, config = {})
Resolver.new(config).resolve(spec)
end
- def spec(spec, config={})
+ def spec(spec, config = {})
Resolver.new(config).spec(spec)
end
diff --git a/activerecord/test/cases/defaults_test.rb b/activerecord/test/cases/defaults_test.rb
index fcaff38f82..6532efcf22 100644
--- a/activerecord/test/cases/defaults_test.rb
+++ b/activerecord/test/cases/defaults_test.rb
@@ -169,7 +169,7 @@ if current_adapter?(:Mysql2Adapter)
assert_nil record.non_null_text
assert_nil record.non_null_blob
- assert_raises(ActiveRecord::StatementInvalid) { klass.create }
+ assert_raises(ActiveRecord::NotNullViolation) { klass.create }
end
end
end
diff --git a/activerecord/test/cases/dirty_test.rb b/activerecord/test/cases/dirty_test.rb
index 09bd00291d..0e58e65a07 100644
--- a/activerecord/test/cases/dirty_test.rb
+++ b/activerecord/test/cases/dirty_test.rb
@@ -341,7 +341,7 @@ class DirtyTest < ActiveRecord::TestCase
def test_partial_update_with_optimistic_locking
person = Person.new(first_name: "foo")
- old_lock_version = 1
+ old_lock_version = person.lock_version
with_partial_writes Person, false do
assert_queries(2) { 2.times { person.save! } }
@@ -726,6 +726,89 @@ class DirtyTest < ActiveRecord::TestCase
assert person.changed?
end
+ test "saved_change_to_attribute? returns whether a change occurred in the last save" do
+ person = Person.create!(first_name: "Sean")
+
+ assert person.saved_change_to_first_name?
+ refute person.saved_change_to_gender?
+ assert person.saved_change_to_first_name?(from: nil, to: "Sean")
+ assert person.saved_change_to_first_name?(from: nil)
+ assert person.saved_change_to_first_name?(to: "Sean")
+ refute person.saved_change_to_first_name?(from: "Jim", to: "Sean")
+ refute person.saved_change_to_first_name?(from: "Jim")
+ refute person.saved_change_to_first_name?(to: "Jim")
+ end
+
+ test "saved_change_to_attribute returns the change that occurred in the last save" do
+ person = Person.create!(first_name: "Sean", gender: "M")
+
+ assert_equal [nil, "Sean"], person.saved_change_to_first_name
+ assert_equal [nil, "M"], person.saved_change_to_gender
+
+ person.update(first_name: "Jim")
+
+ assert_equal ["Sean", "Jim"], person.saved_change_to_first_name
+ assert_nil person.saved_change_to_gender
+ end
+
+ test "attribute_before_last_save returns the original value before saving" do
+ person = Person.create!(first_name: "Sean", gender: "M")
+
+ assert_nil person.first_name_before_last_save
+ assert_nil person.gender_before_last_save
+
+ person.first_name = "Jim"
+
+ assert_nil person.first_name_before_last_save
+ assert_nil person.gender_before_last_save
+
+ person.save
+
+ assert_equal "Sean", person.first_name_before_last_save
+ assert_equal "M", person.gender_before_last_save
+ end
+
+ test "saved_changes? returns whether the last call to save changed anything" do
+ person = Person.create!(first_name: "Sean")
+
+ assert person.saved_changes?
+
+ person.save
+
+ refute person.saved_changes?
+ end
+
+ test "saved_changes returns a hash of all the changes that occurred" do
+ person = Person.create!(first_name: "Sean", gender: "M")
+
+ assert_equal [nil, "Sean"], person.saved_changes[:first_name]
+ assert_equal [nil, "M"], person.saved_changes[:gender]
+ assert_equal %w(id first_name gender created_at updated_at).sort, person.saved_changes.keys.sort
+
+ travel(1.second) do
+ person.update(first_name: "Jim")
+ end
+
+ assert_equal ["Sean", "Jim"], person.saved_changes[:first_name]
+ assert_equal %w(first_name lock_version updated_at).sort, person.saved_changes.keys.sort
+ end
+
+ test "changed? in after callbacks returns true but is deprecated" do
+ klass = Class.new(ActiveRecord::Base) do
+ self.table_name = "people"
+
+ after_save do
+ ActiveSupport::Deprecation.silence do
+ raise "changed? should be true" unless changed?
+ end
+ raise "has_changes_to_save? should be false" if has_changes_to_save?
+ end
+ end
+
+ person = klass.create!(first_name: "Sean")
+ refute person.changed?
+ end
+
private
def with_partial_writes(klass, on = true)
old = klass.partial_writes?
diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb
index 51563b347c..d6d24004fe 100644
--- a/activerecord/test/cases/finder_test.rb
+++ b/activerecord/test/cases/finder_test.rb
@@ -49,22 +49,22 @@ class FinderTest < ActiveRecord::TestCase
end
def test_find_with_ids_returning_ordered
- records = Topic.find([4,2,5])
+ records = Topic.find([4, 2, 5])
assert_equal "The Fourth Topic of the day", records[0].title
assert_equal "The Second Topic of the day", records[1].title
assert_equal "The Fifth Topic of the day", records[2].title
- records = Topic.find(4,2,5)
+ records = Topic.find(4, 2, 5)
assert_equal "The Fourth Topic of the day", records[0].title
assert_equal "The Second Topic of the day", records[1].title
assert_equal "The Fifth Topic of the day", records[2].title
- records = Topic.find(["4","2","5"])
+ records = Topic.find(["4", "2", "5"])
assert_equal "The Fourth Topic of the day", records[0].title
assert_equal "The Second Topic of the day", records[1].title
assert_equal "The Fifth Topic of the day", records[2].title
- records = Topic.find("4","2","5")
+ records = Topic.find("4", "2", "5")
assert_equal "The Fourth Topic of the day", records[0].title
assert_equal "The Second Topic of the day", records[1].title
assert_equal "The Fifth Topic of the day", records[2].title
@@ -72,12 +72,12 @@ class FinderTest < ActiveRecord::TestCase
def test_find_with_ids_and_order_clause
# The order clause takes precedence over the informed ids
- records = Topic.order(:author_name).find([5,3,1])
+ records = Topic.order(:author_name).find([5, 3, 1])
assert_equal "The Third Topic of the day", records[0].title
assert_equal "The First Topic", records[1].title
assert_equal "The Fifth Topic of the day", records[2].title
- records = Topic.order(:id).find([5,3,1])
+ records = Topic.order(:id).find([5, 3, 1])
assert_equal "The First Topic", records[0].title
assert_equal "The Third Topic of the day", records[1].title
assert_equal "The Fifth Topic of the day", records[2].title
@@ -85,14 +85,14 @@ class FinderTest < ActiveRecord::TestCase
def test_find_with_ids_with_limit_and_order_clause
# The order clause takes precedence over the informed ids
- records = Topic.limit(2).order(:id).find([5,3,1])
+ records = Topic.limit(2).order(:id).find([5, 3, 1])
assert_equal 2, records.size
assert_equal "The First Topic", records[0].title
assert_equal "The Third Topic of the day", records[1].title
end
def test_find_with_ids_and_limit
- records = Topic.limit(3).find([3,2,5,1,4])
+ records = Topic.limit(3).find([3, 2, 5, 1, 4])
assert_equal 3, records.size
assert_equal "The Third Topic of the day", records[0].title
assert_equal "The Second Topic of the day", records[1].title
@@ -102,7 +102,7 @@ class FinderTest < ActiveRecord::TestCase
def test_find_with_ids_where_and_limit
# Please note that Topic 1 is the only not approved so
# if it were among the first 3 it would raise an ActiveRecord::RecordNotFound
- records = Topic.where(approved: true).limit(3).find([3,2,5,1,4])
+ records = Topic.where(approved: true).limit(3).find([3, 2, 5, 1, 4])
assert_equal 3, records.size
assert_equal "The Third Topic of the day", records[0].title
assert_equal "The Second Topic of the day", records[1].title
@@ -110,7 +110,7 @@ class FinderTest < ActiveRecord::TestCase
end
def test_find_with_ids_and_offset
- records = Topic.offset(2).find([3,2,5,1,4])
+ records = Topic.offset(2).find([3, 2, 5, 1, 4])
assert_equal 3, records.size
assert_equal "The Fifth Topic of the day", records[0].title
assert_equal "The First Topic", records[1].title
@@ -136,7 +136,7 @@ class FinderTest < ActiveRecord::TestCase
# find should handle strings that come from URLs
# (example: Category.find(params[:id]))
def test_find_with_string
- assert_equal(Topic.find(1).title,Topic.find("1").title)
+ assert_equal(Topic.find(1).title, Topic.find("1").title)
end
def test_exists
@@ -151,7 +151,7 @@ class FinderTest < ActiveRecord::TestCase
assert_equal false, Topic.exists?(45)
assert_equal false, Topic.exists?(Topic.new.id)
- assert_raise(NoMethodError) { Topic.exists?([1,2]) }
+ assert_raise(NoMethodError) { Topic.exists?([1, 2]) }
end
def test_exists_with_polymorphic_relation
@@ -175,7 +175,7 @@ class FinderTest < ActiveRecord::TestCase
def test_exists_returns_false_when_parameter_has_invalid_type
assert_equal false, Topic.exists?("foo")
- assert_equal false, Topic.exists?(("9"*53).to_i) # number that's bigger than int
+ assert_equal false, Topic.exists?(("9" * 53).to_i) # number that's bigger than int
end
def test_exists_does_not_select_columns_without_alias
@@ -258,8 +258,8 @@ class FinderTest < ActiveRecord::TestCase
end
def test_find_by_ids_with_limit_and_offset
- assert_equal 2, Entrant.limit(2).find([1,3,2]).size
- entrants = Entrant.limit(3).offset(2).find([1,3,2])
+ assert_equal 2, Entrant.limit(2).find([1, 3, 2]).size
+ entrants = Entrant.limit(3).offset(2).find([1, 3, 2])
assert_equal 1, entrants.size
assert_equal "Ruby Guru", entrants.first.name
@@ -488,12 +488,12 @@ class FinderTest < ActiveRecord::TestCase
assert_equal topics(:fourth), Topic.offset(1).second_to_last
assert_equal topics(:fourth), Topic.offset(2).second_to_last
assert_equal topics(:fourth), Topic.offset(3).second_to_last
- assert_equal nil, Topic.offset(4).second_to_last
- assert_equal nil, Topic.offset(5).second_to_last
+ assert_nil Topic.offset(4).second_to_last
+ assert_nil Topic.offset(5).second_to_last
#test with limit
- # assert_equal nil, Topic.limit(1).second # TODO: currently failing
- assert_equal nil, Topic.limit(1).second_to_last
+ # assert_nil Topic.limit(1).second # TODO: currently failing
+ assert_nil Topic.limit(1).second_to_last
end
def test_second_to_last_have_primary_key_order_by_default
@@ -516,15 +516,15 @@ class FinderTest < ActiveRecord::TestCase
# test with offset
assert_equal topics(:third), Topic.offset(1).third_to_last
assert_equal topics(:third), Topic.offset(2).third_to_last
- assert_equal nil, Topic.offset(3).third_to_last
- assert_equal nil, Topic.offset(4).third_to_last
- assert_equal nil, Topic.offset(5).third_to_last
+ assert_nil Topic.offset(3).third_to_last
+ assert_nil Topic.offset(4).third_to_last
+ assert_nil Topic.offset(5).third_to_last
# test with limit
- # assert_equal nil, Topic.limit(1).third # TODO: currently failing
- assert_equal nil, Topic.limit(1).third_to_last
- # assert_equal nil, Topic.limit(2).third # TODO: currently failing
- assert_equal nil, Topic.limit(2).third_to_last
+ # assert_nil Topic.limit(1).third # TODO: currently failing
+ assert_nil Topic.limit(1).third_to_last
+ # assert_nil Topic.limit(2).third # TODO: currently failing
+ assert_nil Topic.limit(2).third_to_last
end
def test_third_to_last_have_primary_key_order_by_default
@@ -584,7 +584,7 @@ class FinderTest < ActiveRecord::TestCase
end
def test_last_on_loaded_relation_should_not_use_sql
- relation = Topic.limit(10).load
+ relation = Topic.limit(10).load
assert_no_queries do
relation.last
relation.last(2)
@@ -693,27 +693,27 @@ class FinderTest < ActiveRecord::TestCase
end
def test_find_on_hash_conditions_with_range
- assert_equal [1,2], Topic.where(id: 1..2).to_a.map(&:id).sort
+ assert_equal [1, 2], Topic.where(id: 1..2).to_a.map(&:id).sort
assert_raise(ActiveRecord::RecordNotFound) { Topic.where(id: 2..3).find(1) }
end
def test_find_on_hash_conditions_with_end_exclusive_range
- assert_equal [1,2,3], Topic.where(id: 1..3).to_a.map(&:id).sort
- assert_equal [1,2], Topic.where(id: 1...3).to_a.map(&:id).sort
+ assert_equal [1, 2, 3], Topic.where(id: 1..3).to_a.map(&:id).sort
+ assert_equal [1, 2], Topic.where(id: 1...3).to_a.map(&:id).sort
assert_raise(ActiveRecord::RecordNotFound) { Topic.where(id: 2...3).find(3) }
end
def test_find_on_hash_conditions_with_multiple_ranges
- assert_equal [1,2,3], Comment.where(id: 1..3, post_id: 1..2).to_a.map(&:id).sort
+ assert_equal [1, 2, 3], Comment.where(id: 1..3, post_id: 1..2).to_a.map(&:id).sort
assert_equal [1], Comment.where(id: 1..1, post_id: 1..10).to_a.map(&:id).sort
end
def test_find_on_hash_conditions_with_array_of_integers_and_ranges
- assert_equal [1,2,3,5,6,7,8,9], Comment.where(id: [1..2, 3, 5, 6..8, 9]).to_a.map(&:id).sort
+ assert_equal [1, 2, 3, 5, 6, 7, 8, 9], Comment.where(id: [1..2, 3, 5, 6..8, 9]).to_a.map(&:id).sort
end
def test_find_on_hash_conditions_with_array_of_ranges
- assert_equal [1,2,6,7,8], Comment.where(id: [1..2, 6..8]).to_a.map(&:id).sort
+ assert_equal [1, 2, 6, 7, 8], Comment.where(id: [1..2, 6..8]).to_a.map(&:id).sort
end
def test_find_on_multiple_hash_conditions
@@ -983,7 +983,6 @@ class FinderTest < ActiveRecord::TestCase
assert_equal devs[2], Developer.offset(2).first
assert_equal devs[-3], Developer.offset(2).last
- assert_equal devs[-3], Developer.offset(2).last
assert_equal devs[-3], Developer.offset(2).order("id DESC").first
end
@@ -1029,7 +1028,7 @@ class FinderTest < ActiveRecord::TestCase
def test_find_by_id_with_conditions_with_or
assert_nothing_raised do
- Post.where("posts.id <= 3 OR posts.#{QUOTED_TYPE} = 'Post'").find([1,2,3])
+ Post.where("posts.id <= 3 OR posts.#{QUOTED_TYPE} = 'Post'").find([1, 2, 3])
end
end
@@ -1061,8 +1060,8 @@ class FinderTest < ActiveRecord::TestCase
end
def test_select_values
- assert_equal ["1","2","3","4","5","6","7","8","9", "10", "11"], Company.connection.select_values("SELECT id FROM companies ORDER BY id").map!(&:to_s)
- assert_equal ["37signals","Summit","Microsoft", "Flamboyant Software", "Ex Nihilo", "RailsCore", "Leetsoft", "Jadedpixel", "Odegy", "Ex Nihilo Part Deux", "Apex"], Company.connection.select_values("SELECT name FROM companies ORDER BY id")
+ assert_equal ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11"], Company.connection.select_values("SELECT id FROM companies ORDER BY id").map!(&:to_s)
+ assert_equal ["37signals", "Summit", "Microsoft", "Flamboyant Software", "Ex Nihilo", "RailsCore", "Leetsoft", "Jadedpixel", "Odegy", "Ex Nihilo Part Deux", "Apex"], Company.connection.select_values("SELECT name FROM companies ORDER BY id")
end
def test_select_rows
@@ -1167,12 +1166,12 @@ class FinderTest < ActiveRecord::TestCase
end
test "find_by returns nil if the record is missing" do
- assert_equal nil, Post.find_by("1 = 0")
+ assert_nil Post.find_by("1 = 0")
end
test "find_by with associations" do
assert_equal authors(:david), Post.find_by(author: authors(:david)).author
- assert_equal authors(:mary) , Post.find_by(author: authors(:mary) ).author
+ assert_equal authors(:mary) , Post.find_by(author: authors(:mary)).author
end
test "find_by doesn't have implicit ordering" do
@@ -1221,7 +1220,7 @@ class FinderTest < ActiveRecord::TestCase
assert_equal tyre2, zyke.tyres.custom_find_by(id: tyre2.id)
end
- protected
+ private
def table_with_custom_primary_key
yield(Class.new(Toy) do
def self.name
diff --git a/activerecord/test/cases/fixture_set/file_test.rb b/activerecord/test/cases/fixture_set/file_test.rb
index cf2a73595a..533edcc2e0 100644
--- a/activerecord/test/cases/fixture_set/file_test.rb
+++ b/activerecord/test/cases/fixture_set/file_test.rb
@@ -31,7 +31,7 @@ module ActiveRecord
def test_values
File.open(::File.join(FIXTURES_ROOT, "accounts.yml")) do |fh|
- assert_equal [1,2,3,4,5,6].sort, fh.to_a.map(&:last).map { |x|
+ assert_equal [1, 2, 3, 4, 5, 6].sort, fh.to_a.map(&:last).map { |x|
x["id"]
}.sort
end
diff --git a/activerecord/test/cases/fixtures_test.rb b/activerecord/test/cases/fixtures_test.rb
index 3f111447ff..dd48053823 100644
--- a/activerecord/test/cases/fixtures_test.rb
+++ b/activerecord/test/cases/fixtures_test.rb
@@ -12,9 +12,11 @@ require "models/company"
require "models/computer"
require "models/course"
require "models/developer"
+require "models/dog"
require "models/doubloon"
require "models/joke"
require "models/matey"
+require "models/other_dog"
require "models/parrot"
require "models/pirate"
require "models/post"
@@ -192,28 +194,38 @@ class FixturesTest < ActiveRecord::TestCase
end
def test_empty_yaml_fixture
- assert_not_nil ActiveRecord::FixtureSet.new( Account.connection, "accounts", Account, FIXTURES_ROOT + "/naked/yml/accounts")
+ assert_not_nil ActiveRecord::FixtureSet.new(Account.connection, "accounts", Account, FIXTURES_ROOT + "/naked/yml/accounts")
end
def test_empty_yaml_fixture_with_a_comment_in_it
- assert_not_nil ActiveRecord::FixtureSet.new( Account.connection, "companies", Company, FIXTURES_ROOT + "/naked/yml/companies")
+ assert_not_nil ActiveRecord::FixtureSet.new(Account.connection, "companies", Company, FIXTURES_ROOT + "/naked/yml/companies")
end
def test_nonexistent_fixture_file
nonexistent_fixture_path = FIXTURES_ROOT + "/imnothere"
#sanity check to make sure that this file never exists
- assert Dir[nonexistent_fixture_path+"*"].empty?
+ assert Dir[nonexistent_fixture_path + "*"].empty?
assert_raise(Errno::ENOENT) do
- ActiveRecord::FixtureSet.new( Account.connection, "companies", Company, nonexistent_fixture_path)
+ ActiveRecord::FixtureSet.new(Account.connection, "companies", Company, nonexistent_fixture_path)
end
end
def test_dirty_dirty_yaml_file
- assert_raise(ActiveRecord::Fixture::FormatError) do
- ActiveRecord::FixtureSet.new( Account.connection, "courses", Course, FIXTURES_ROOT + "/naked/yml/courses")
+ fixture_path = FIXTURES_ROOT + "/naked/yml/courses"
+ error = assert_raise(ActiveRecord::Fixture::FormatError) do
+ ActiveRecord::FixtureSet.new(Account.connection, "courses", Course, fixture_path)
end
+ assert_equal "fixture is not a hash: #{fixture_path}.yml", error.to_s
+ end
+
+ def test_yaml_file_with_one_invalid_fixture
+ fixture_path = FIXTURES_ROOT + "/naked/yml/courses_with_invalid_key"
+ error = assert_raise(ActiveRecord::Fixture::FormatError) do
+ ActiveRecord::FixtureSet.new(Account.connection, "courses", Course, fixture_path)
+ end
+ assert_equal "fixture key is not a hash: #{fixture_path}.yml, keys: [\"two\"]", error.to_s
end
def test_yaml_file_with_invalid_column
@@ -948,7 +960,7 @@ end
class CustomNameForFixtureOrModelTest < ActiveRecord::TestCase
ActiveRecord::FixtureSet.reset_cache
- set_fixture_class :randomly_named_a9 =>
+ set_fixture_class :randomly_named_a9 =>
ClassNameThatDoesNotFollowCONVENTIONS,
:'admin/randomly_named_a9' =>
Admin::ClassNameThatDoesNotFollowCONVENTIONS1,
@@ -1011,3 +1023,16 @@ class FixtureClassNamesTest < ActiveRecord::TestCase
assert_nil fixture_class_names["unregistered_identifier"]
end
end
+
+class SameNameDifferentDatabaseFixturesTest < ActiveRecord::TestCase
+ fixtures :dogs, :other_dogs
+
+ test "fixtures are properly loaded" do
+ # Force loading the fixtures again to reproduce issue
+ ActiveRecord::FixtureSet.reset_cache
+ create_fixtures("dogs", "other_dogs")
+
+ assert_kind_of Dog, dogs(:sophie)
+ assert_kind_of OtherDog, other_dogs(:lassie)
+ end
+end
diff --git a/activerecord/test/cases/forbidden_attributes_protection_test.rb b/activerecord/test/cases/forbidden_attributes_protection_test.rb
index 75c3493527..ffa3f63e0d 100644
--- a/activerecord/test/cases/forbidden_attributes_protection_test.rb
+++ b/activerecord/test/cases/forbidden_attributes_protection_test.rb
@@ -144,7 +144,7 @@ class ForbiddenAttributesProtectionTest < ActiveRecord::TestCase
end
def test_strong_params_style_objects_work_with_singular_associations
- params = ProtectedParams.new( name: "Stern", ship_attributes: ProtectedParams.new(name: "The Black Rock").permit!).permit!
+ params = ProtectedParams.new(name: "Stern", ship_attributes: ProtectedParams.new(name: "The Black Rock").permit!).permit!
part = ShipPart.new(params)
assert_equal "Stern", part.name
@@ -155,7 +155,7 @@ class ForbiddenAttributesProtectionTest < ActiveRecord::TestCase
params = ProtectedParams.new(
trinkets_attributes: ProtectedParams.new(
"0" => ProtectedParams.new(name: "Necklace").permit!,
- "1" => ProtectedParams.new(name: "Spoon").permit! ) ).permit!
+ "1" => ProtectedParams.new(name: "Spoon").permit!)).permit!
part = ShipPart.new(params)
assert_equal "Necklace", part.trinkets[0].name
diff --git a/activerecord/test/cases/integration_test.rb b/activerecord/test/cases/integration_test.rb
index 00457965d7..d7aa091623 100644
--- a/activerecord/test/cases/integration_test.rb
+++ b/activerecord/test/cases/integration_test.rb
@@ -15,7 +15,7 @@ class IntegrationTest < ActiveRecord::TestCase
def test_to_param_returns_nil_if_not_persisted
client = Client.new
- assert_equal nil, client.to_param
+ assert_nil client.to_param
end
def test_to_param_returns_id_if_not_persisted_but_id_is_set
@@ -89,7 +89,7 @@ class IntegrationTest < ActiveRecord::TestCase
def test_to_param_class_method_uses_default_if_not_persisted
firm = Firm.new(name: "Fancy Shirts")
- assert_equal nil, firm.to_param
+ assert_nil firm.to_param
end
def test_to_param_with_no_arguments
diff --git a/activerecord/test/cases/json_serialization_test.rb b/activerecord/test/cases/json_serialization_test.rb
index b06fed4f0d..155e858822 100644
--- a/activerecord/test/cases/json_serialization_test.rb
+++ b/activerecord/test/cases/json_serialization_test.rb
@@ -102,7 +102,7 @@ class JsonSerializationTest < ActiveRecord::TestCase
end
def test_uses_serializable_hash_with_only_option
- def @contact.serializable_hash(options=nil)
+ def @contact.serializable_hash(options = nil)
super(only: %w(name))
end
@@ -113,7 +113,7 @@ class JsonSerializationTest < ActiveRecord::TestCase
end
def test_uses_serializable_hash_with_except_option
- def @contact.serializable_hash(options=nil)
+ def @contact.serializable_hash(options = nil)
super(except: %w(age))
end
@@ -137,7 +137,7 @@ class JsonSerializationTest < ActiveRecord::TestCase
@contact = ContactSti.new(@contact.attributes)
assert_equal "ContactSti", @contact.type
- def @contact.serializable_hash(options={})
+ def @contact.serializable_hash(options = {})
super({ except: %w(age) }.merge!(options))
end
@@ -243,7 +243,7 @@ class DatabaseConnectedJsonEncodingTest < ActiveRecord::TestCase
assert !@david.posts.first.respond_to?(:favorite_quote)
assert_match %r{"favorite_quote":"Constraints are liberating"}, json
- assert_equal %r{"favorite_quote":}.match(json).size, 1
+ assert_equal 1, %r{"favorite_quote":}.match(json).size
end
def test_should_allow_only_option_for_list_of_authors
diff --git a/activerecord/test/cases/locking_test.rb b/activerecord/test/cases/locking_test.rb
index 13b6f6daaf..95fb670dac 100644
--- a/activerecord/test/cases/locking_test.rb
+++ b/activerecord/test/cases/locking_test.rb
@@ -161,14 +161,6 @@ class OptimisticLockingTest < ActiveRecord::TestCase
assert_equal(error.record.object_id, p2.object_id)
end
- def test_lock_new_with_nil
- p1 = Person.new(first_name: "anika")
- p1.save!
- p1.lock_version = nil # simulate bad fixture or column with no default
- p1.save!
- assert_equal 1, p1.lock_version
- end
-
def test_lock_new_when_explicitly_passing_nil
p1 = Person.new(first_name: "anika", lock_version: nil)
p1.save!
@@ -222,22 +214,149 @@ class OptimisticLockingTest < ActiveRecord::TestCase
def test_lock_without_default_sets_version_to_zero
t1 = LockWithoutDefault.new
+
+ assert_equal 0, t1.lock_version
+ assert_nil t1.lock_version_before_type_cast
+
+ t1.save!
+ t1.reload
+
assert_equal 0, t1.lock_version
+ assert_equal 0, t1.lock_version_before_type_cast
+ end
+
+ def test_lock_without_default_should_work_with_null_in_the_database
+ ActiveRecord::Base.connection.execute("INSERT INTO lock_without_defaults(title) VALUES('title1')")
+ t1 = LockWithoutDefault.last
+ t2 = LockWithoutDefault.last
+
+ assert_equal 0, t1.lock_version
+ assert_nil t1.lock_version_before_type_cast
+ assert_equal 0, t2.lock_version
+ assert_nil t2.lock_version_before_type_cast
+
+ t1.title = "new title1"
+ t2.title = "new title2"
+
+ assert_nothing_raised { t1.save! }
+ assert_equal 1, t1.lock_version
+ assert_equal "new title1", t1.title
+
+ assert_raise(ActiveRecord::StaleObjectError) { t2.save! }
+ assert_equal 0, t2.lock_version
+ assert_equal "new title2", t2.title
+ end
+
+ def test_lock_without_default_should_update_with_lock_col
+ t1 = LockWithoutDefault.create(title: "title1", lock_version: 6)
+
+ assert_equal 6, t1.lock_version
+
+ t1.update(lock_version: 0)
+ t1.reload
- t1.save
- t1 = LockWithoutDefault.find(t1.id)
assert_equal 0, t1.lock_version
end
+ def test_lock_without_default_queries_count
+ t1 = LockWithoutDefault.create(title: "title1")
+
+ assert_equal "title1", t1.title
+ assert_equal 0, t1.lock_version
+
+ assert_queries(1) { t1.update(title: "title2") }
+
+ t1.reload
+ assert_equal "title2", t1.title
+ assert_equal 1, t1.lock_version
+
+ assert_queries(1) { t1.update(title: "title3", lock_version: 6) }
+
+ t1.reload
+ assert_equal "title3", t1.title
+ assert_equal 6, t1.lock_version
+
+ t2 = LockWithoutDefault.new(title: "title1")
+
+ assert_queries(1) { t2.save! }
+
+ t2.reload
+ assert_equal "title1", t2.title
+ assert_equal 0, t2.lock_version
+ end
+
def test_lock_with_custom_column_without_default_sets_version_to_zero
t1 = LockWithCustomColumnWithoutDefault.new
+
assert_equal 0, t1.custom_lock_version
assert_nil t1.custom_lock_version_before_type_cast
t1.save!
t1.reload
+
+ assert_equal 0, t1.custom_lock_version
+ assert_equal 0, t1.custom_lock_version_before_type_cast
+ end
+
+ def test_lock_with_custom_column_without_default_should_work_with_null_in_the_database
+ ActiveRecord::Base.connection.execute("INSERT INTO lock_without_defaults_cust(title) VALUES('title1')")
+
+ t1 = LockWithCustomColumnWithoutDefault.last
+ t2 = LockWithCustomColumnWithoutDefault.last
+
assert_equal 0, t1.custom_lock_version
- assert [0, "0"].include?(t1.custom_lock_version_before_type_cast)
+ assert_nil t1.custom_lock_version_before_type_cast
+ assert_equal 0, t2.custom_lock_version
+ assert_nil t2.custom_lock_version_before_type_cast
+
+ t1.title = "new title1"
+ t2.title = "new title2"
+
+ assert_nothing_raised { t1.save! }
+ assert_equal 1, t1.custom_lock_version
+ assert_equal "new title1", t1.title
+
+ assert_raise(ActiveRecord::StaleObjectError) { t2.save! }
+ assert_equal 0, t2.custom_lock_version
+ assert_equal "new title2", t2.title
+ end
+
+ def test_lock_with_custom_column_without_default_should_update_with_lock_col
+ t1 = LockWithCustomColumnWithoutDefault.create(title: "title1", custom_lock_version: 6)
+
+ assert_equal 6, t1.custom_lock_version
+
+ t1.update(custom_lock_version: 0)
+ t1.reload
+
+ assert_equal 0, t1.custom_lock_version
+ end
+
+ def test_lock_with_custom_column_without_default_queries_count
+ t1 = LockWithCustomColumnWithoutDefault.create(title: "title1")
+
+ assert_equal "title1", t1.title
+ assert_equal 0, t1.custom_lock_version
+
+ assert_queries(1) { t1.update(title: "title2") }
+
+ t1.reload
+ assert_equal "title2", t1.title
+ assert_equal 1, t1.custom_lock_version
+
+ assert_queries(1) { t1.update(title: "title3", custom_lock_version: 6) }
+
+ t1.reload
+ assert_equal "title3", t1.title
+ assert_equal 6, t1.custom_lock_version
+
+ t2 = LockWithCustomColumnWithoutDefault.new(title: "title1")
+
+ assert_queries(1) { t2.save! }
+
+ t2.reload
+ assert_equal "title1", t2.title
+ assert_equal 0, t2.custom_lock_version
end
def test_readonly_attributes
@@ -351,7 +470,7 @@ class OptimisticLockingWithSchemaChangeTest < ActiveRecord::TestCase
private
- def add_counter_column_to(model, col="test_count")
+ def add_counter_column_to(model, col = "test_count")
model.connection.add_column model.table_name, col, :integer, null: false, default: 0
model.reset_column_information
end
@@ -460,7 +579,8 @@ unless in_memory_db?
assert first.end > second.end
end
- protected
+ private
+
def duel(zzz = 5)
t0, t1, t2, t3 = nil, nil, nil, nil
diff --git a/activerecord/test/cases/migration/change_schema_test.rb b/activerecord/test/cases/migration/change_schema_test.rb
index bdb90eaa74..03f9c4a9ed 100644
--- a/activerecord/test/cases/migration/change_schema_test.rb
+++ b/activerecord/test/cases/migration/change_schema_test.rb
@@ -43,7 +43,7 @@ module ActiveRecord
t.column :foo, :string, null: false
end
- assert_raises(ActiveRecord::StatementInvalid) do
+ assert_raises(ActiveRecord::NotNullViolation) do
connection.execute "insert into testings (foo) values (NULL)"
end
end
@@ -233,7 +233,7 @@ module ActiveRecord
end
connection.add_column :testings, :bar, :string, null: false
- assert_raise(ActiveRecord::StatementInvalid) do
+ assert_raise(ActiveRecord::NotNullViolation) do
connection.execute "insert into testings (foo, bar) values ('hello', NULL)"
end
end
@@ -244,12 +244,16 @@ module ActiveRecord
t.column :foo, :string
end
- con = connection
- connection.execute "insert into testings (#{con.quote_column_name('id')}, #{con.quote_column_name('foo')}) values (1, 'hello')"
- assert_nothing_raised { connection.add_column :testings, :bar, :string, null: false, default: "default" }
+ quoted_id = connection.quote_column_name("id")
+ quoted_foo = connection.quote_column_name("foo")
+ quoted_bar = connection.quote_column_name("bar")
+ connection.execute("insert into testings (#{quoted_id}, #{quoted_foo}) values (1, 'hello')")
+ assert_nothing_raised do
+ connection.add_column :testings, :bar, :string, null: false, default: "default"
+ end
- assert_raises(ActiveRecord::StatementInvalid) do
- connection.execute "insert into testings (#{con.quote_column_name('id')}, #{con.quote_column_name('foo')}, #{con.quote_column_name('bar')}) values (2, 'hello', NULL)"
+ assert_raises(ActiveRecord::NotNullViolation) do
+ connection.execute("insert into testings (#{quoted_id}, #{quoted_foo}, #{quoted_bar}) values (2, 'hello', NULL)")
end
end
diff --git a/activerecord/test/cases/migration/change_table_test.rb b/activerecord/test/cases/migration/change_table_test.rb
index ec817a579b..8a4242cf1d 100644
--- a/activerecord/test/cases/migration/change_table_test.rb
+++ b/activerecord/test/cases/migration/change_table_test.rb
@@ -101,7 +101,12 @@ module ActiveRecord
def test_primary_key_creates_primary_key_column
with_change_table do |t|
- @connection.expect :add_column, nil, [:delete_me, :id, :primary_key, primary_key: true, first: true]
+ if current_adapter?(:Mysql2Adapter)
+ @connection.expect :add_column, nil, [:delete_me, :id, :primary_key, { first: true, auto_increment: true, limit: 8, primary_key: true }]
+ else
+ @connection.expect :add_column, nil, [:delete_me, :id, :primary_key, primary_key: true, first: true]
+ end
+
t.primary_key :id, first: true
end
end
diff --git a/activerecord/test/cases/migration/foreign_key_test.rb b/activerecord/test/cases/migration/foreign_key_test.rb
index cab2069754..9be6667aa1 100644
--- a/activerecord/test/cases/migration/foreign_key_test.rb
+++ b/activerecord/test/cases/migration/foreign_key_test.rb
@@ -76,7 +76,7 @@ if ActiveRecord::Base.connection.supports_foreign_keys?
end
def test_add_foreign_key_with_non_standard_primary_key
- with_example_table @connection, "space_shuttles", "pk integer PRIMARY KEY" do
+ with_example_table @connection, "space_shuttles", "pk BIGINT PRIMARY KEY" do
@connection.add_foreign_key(:astronauts, :space_shuttles,
column: "rocket_id", primary_key: "pk", name: "custom_pk")
@@ -101,7 +101,7 @@ if ActiveRecord::Base.connection.supports_foreign_keys?
fk = foreign_keys.first
if current_adapter?(:Mysql2Adapter)
# ON DELETE RESTRICT is the default on MySQL
- assert_equal nil, fk.on_delete
+ assert_nil fk.on_delete
else
assert_equal :restrict, fk.on_delete
end
@@ -229,7 +229,7 @@ if ActiveRecord::Base.connection.supports_foreign_keys?
create_table("cities") { |t| }
create_table("houses") do |t|
- t.column :city_id, :integer
+ t.column :city_id, :bigint
end
add_foreign_key :houses, :cities, column: "city_id"
@@ -261,7 +261,7 @@ if ActiveRecord::Base.connection.supports_foreign_keys?
create_table(:schools)
create_table(:classes) do |t|
- t.column :school_id, :integer
+ t.column :school_id, :bigint
end
add_foreign_key :classes, :schools
end
diff --git a/activerecord/test/cases/migration/references_foreign_key_test.rb b/activerecord/test/cases/migration/references_foreign_key_test.rb
index 528811db49..4957ab8b3d 100644
--- a/activerecord/test/cases/migration/references_foreign_key_test.rb
+++ b/activerecord/test/cases/migration/references_foreign_key_test.rb
@@ -42,7 +42,7 @@ if ActiveRecord::Base.connection.supports_foreign_keys?
test "options hash can be passed" do
@connection.change_table :testing_parents do |t|
- t.integer :other_id
+ t.bigint :other_id
t.index :other_id, unique: true
end
@connection.create_table :testings do |t|
@@ -92,7 +92,7 @@ if ActiveRecord::Base.connection.supports_foreign_keys?
test "foreign keys accept options when changing the table" do
@connection.change_table :testing_parents do |t|
- t.integer :other_id
+ t.bigint :other_id
t.index :other_id, unique: true
end
@connection.create_table :testings
@@ -177,8 +177,8 @@ if ActiveRecord::Base.connection.supports_foreign_keys?
test "multiple foreign keys can be added to the same table" do
@connection.create_table :testings do |t|
- t.integer :col_1
- t.integer :col_2
+ t.bigint :col_1
+ t.bigint :col_2
t.foreign_key :testing_parents, column: :col_1
t.foreign_key :testing_parents, column: :col_2
diff --git a/activerecord/test/cases/migration/references_statements_test.rb b/activerecord/test/cases/migration/references_statements_test.rb
index 8fbe60f24e..df15d7cb45 100644
--- a/activerecord/test/cases/migration/references_statements_test.rb
+++ b/activerecord/test/cases/migration/references_statements_test.rb
@@ -57,7 +57,7 @@ module ActiveRecord
def test_creates_named_unique_index
add_reference table_name, :tag, index: { name: "index_taggings_on_tag_id", unique: true }
- assert index_exists?(table_name, :tag_id, name: "index_taggings_on_tag_id", unique: true )
+ assert index_exists?(table_name, :tag_id, name: "index_taggings_on_tag_id", unique: true)
end
def test_creates_reference_id_with_specified_type
diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb
index 151f3c8efd..082cfd3242 100644
--- a/activerecord/test/cases/migration_test.rb
+++ b/activerecord/test/cases/migration_test.rb
@@ -388,7 +388,7 @@ class MigrationTest < ActiveRecord::TestCase
original_rails_env = ENV["RAILS_ENV"]
original_rack_env = ENV["RACK_ENV"]
ENV["RAILS_ENV"] = ENV["RACK_ENV"] = "foofoo"
- new_env = ActiveRecord::ConnectionHandling::DEFAULT_ENV.call
+ new_env = ActiveRecord::ConnectionHandling::DEFAULT_ENV.call
refute_equal current_env, new_env
@@ -413,7 +413,7 @@ class MigrationTest < ActiveRecord::TestCase
original_rails_env = ENV["RAILS_ENV"]
original_rack_env = ENV["RACK_ENV"]
ENV["RAILS_ENV"] = ENV["RACK_ENV"] = "foofoo"
- new_env = ActiveRecord::ConnectionHandling::DEFAULT_ENV.call
+ new_env = ActiveRecord::ConnectionHandling::DEFAULT_ENV.call
refute_equal current_env, new_env
@@ -429,14 +429,14 @@ class MigrationTest < ActiveRecord::TestCase
def test_internal_metadata_stores_environment_when_other_data_exists
ActiveRecord::InternalMetadata.delete_all
- ActiveRecord::InternalMetadata[:foo] = "bar"
+ ActiveRecord::InternalMetadata[:foo] = "bar"
current_env = ActiveRecord::ConnectionHandling::DEFAULT_ENV.call
migrations_path = MIGRATIONS_ROOT + "/valid"
old_path = ActiveRecord::Migrator.migrations_paths
ActiveRecord::Migrator.migrations_paths = migrations_path
- current_env = ActiveRecord::ConnectionHandling::DEFAULT_ENV.call
+ current_env = ActiveRecord::ConnectionHandling::DEFAULT_ENV.call
ActiveRecord::Migrator.up(migrations_path)
assert_equal current_env, ActiveRecord::InternalMetadata[:environment]
assert_equal "bar", ActiveRecord::InternalMetadata[:foo]
@@ -705,7 +705,7 @@ class MigrationTest < ActiveRecord::TestCase
end
end
- protected
+ private
# This is needed to isolate class_attribute assignments like `table_name_prefix`
# for each test case.
def new_isolated_reminder_class
diff --git a/activerecord/test/cases/migrator_test.rb b/activerecord/test/cases/migrator_test.rb
index 1ba18bc9c2..b775bf0492 100644
--- a/activerecord/test/cases/migrator_test.rb
+++ b/activerecord/test/cases/migrator_test.rb
@@ -11,7 +11,7 @@ class MigratorTest < ActiveRecord::TestCase
def initialize(name = self.class.name, version = nil)
super
- @went_up = false
+ @went_up = false
@went_down = false
end
@@ -344,10 +344,10 @@ class MigratorTest < ActiveRecord::TestCase
_, migrator = migrator_class(3)
migrator.migrate("valid")
- assert_equal([1,2,3], ActiveRecord::Migrator.get_all_versions)
+ assert_equal([1, 2, 3], ActiveRecord::Migrator.get_all_versions)
migrator.rollback("valid")
- assert_equal([1,2], ActiveRecord::Migrator.get_all_versions)
+ assert_equal([1, 2], ActiveRecord::Migrator.get_all_versions)
migrator.rollback("valid")
assert_equal([1], ActiveRecord::Migrator.get_all_versions)
@@ -368,7 +368,7 @@ class MigratorTest < ActiveRecord::TestCase
def sensors(count)
calls = []
migrations = count.times.map { |i|
- m(nil, i + 1) { |c,migration|
+ m(nil, i + 1) { |c, migration|
calls << [c, migration.version]
}
}
diff --git a/activerecord/test/cases/multiparameter_attributes_test.rb b/activerecord/test/cases/multiparameter_attributes_test.rb
index b2f76398df..ceb5724377 100644
--- a/activerecord/test/cases/multiparameter_attributes_test.rb
+++ b/activerecord/test/cases/multiparameter_attributes_test.rb
@@ -260,6 +260,13 @@ class MultiParameterAttributeTest < ActiveRecord::TestCase
topic.attributes = attributes
assert_equal Time.zone.local(2000, 1, 1, 16, 24, 0), topic.bonus_time
assert_not topic.bonus_time.utc?
+
+ attributes = {
+ "written_on(1i)" => "2000", "written_on(2i)" => "", "written_on(3i)" => "",
+ "written_on(4i)" => "", "written_on(5i)" => ""
+ }
+ topic.attributes = attributes
+ assert_nil topic.written_on
end
ensure
Topic.reset_column_information
@@ -280,14 +287,14 @@ class MultiParameterAttributeTest < ActiveRecord::TestCase
unless current_adapter? :OracleAdapter
def test_multiparameter_attributes_setting_time_attribute
- topic = Topic.new( "bonus_time(4i)"=> "01", "bonus_time(5i)" => "05" )
+ topic = Topic.new("bonus_time(4i)" => "01", "bonus_time(5i)" => "05")
assert_equal 1, topic.bonus_time.hour
assert_equal 5, topic.bonus_time.min
end
end
def test_multiparameter_attributes_setting_date_attribute
- topic = Topic.new( "written_on(1i)" => "1952", "written_on(2i)" => "3", "written_on(3i)" => "11" )
+ topic = Topic.new("written_on(1i)" => "1952", "written_on(2i)" => "3", "written_on(3i)" => "11")
assert_equal 1952, topic.written_on.year
assert_equal 3, topic.written_on.month
assert_equal 11, topic.written_on.day
@@ -308,8 +315,8 @@ class MultiParameterAttributeTest < ActiveRecord::TestCase
end
def test_multiparameter_attributes_setting_time_but_not_date_on_date_field
- assert_raise( ActiveRecord::MultiparameterAssignmentErrors ) do
- Topic.new( "written_on(4i)" => "13", "written_on(5i)" => "55" )
+ assert_raise(ActiveRecord::MultiparameterAssignmentErrors) do
+ Topic.new("written_on(4i)" => "13", "written_on(5i)" => "55")
end
end
diff --git a/activerecord/test/cases/nested_attributes_test.rb b/activerecord/test/cases/nested_attributes_test.rb
index a9c3733c20..b87419d203 100644
--- a/activerecord/test/cases/nested_attributes_test.rb
+++ b/activerecord/test/cases/nested_attributes_test.rb
@@ -971,7 +971,7 @@ class TestNestedAttributesWithNonStandardPrimaryKeys < ActiveRecord::TestCase
def test_attr_accessor_of_child_should_be_value_provided_during_update
@owner = owners(:ashley)
@pet1 = pets(:chew)
- attributes = { pets_attributes: { "1"=> { id: @pet1.id,
+ attributes = { pets_attributes: { "1" => { id: @pet1.id,
name: "Foo2",
current_user: "John",
_destroy: true } } }
@@ -1030,13 +1030,13 @@ class TestHasManyAutosaveAssociationWhichItselfHasAutosaveAssociations < ActiveR
end
test "if association is not loaded and association record is saved and then in memory record attributes should be saved" do
- @ship.parts_attributes=[{ id: @part.id,name: "Deck" }]
+ @ship.parts_attributes = [{ id: @part.id, name: "Deck" }]
assert_equal 1, @ship.association(:parts).target.size
assert_equal "Deck", @ship.parts[0].name
end
test "if association is not loaded and child doesn't change and I am saving a grandchild then in memory record should be used" do
- @ship.parts_attributes=[{ id: @part.id,trinkets_attributes: [{ id: @trinket.id, name: "Ruby" }] }]
+ @ship.parts_attributes = [{ id: @part.id, trinkets_attributes: [{ id: @trinket.id, name: "Ruby" }] }]
assert_equal 1, @ship.association(:parts).target.size
assert_equal "Mast", @ship.parts[0].name
assert_no_difference("@ship.parts[0].association(:trinkets).target.size") do
diff --git a/activerecord/test/cases/nested_attributes_with_callbacks_test.rb b/activerecord/test/cases/nested_attributes_with_callbacks_test.rb
index 8954e8c7e3..350a966d40 100644
--- a/activerecord/test/cases/nested_attributes_with_callbacks_test.rb
+++ b/activerecord/test/cases/nested_attributes_with_callbacks_test.rb
@@ -5,13 +5,13 @@ require "models/bird"
class NestedAttributesWithCallbacksTest < ActiveRecord::TestCase
Pirate.has_many(:birds_with_add_load,
class_name: "Bird",
- before_add: proc { |p,b|
+ before_add: proc { |p, b|
@@add_callback_called << b
p.birds_with_add_load.to_a
})
Pirate.has_many(:birds_with_add,
class_name: "Bird",
- before_add: proc { |p,b| @@add_callback_called << b })
+ before_add: proc { |p, b| @@add_callback_called << b })
Pirate.accepts_nested_attributes_for(:birds_with_add_load,
:birds_with_add,
@@ -21,7 +21,7 @@ class NestedAttributesWithCallbacksTest < ActiveRecord::TestCase
@@add_callback_called = []
@pirate = Pirate.new.tap do |pirate|
pirate.catchphrase = "Don't call me!"
- pirate.birds_attributes = [{ name: "Bird1" },{ name: "Bird2" }]
+ pirate.birds_attributes = [{ name: "Bird1" }, { name: "Bird2" }]
pirate.save!
end
@birds = @pirate.birds.to_a
@@ -37,7 +37,7 @@ class NestedAttributesWithCallbacksTest < ActiveRecord::TestCase
def existing_birds_attributes
@birds.map do |bird|
- bird.attributes.slice("id","name")
+ bird.attributes.slice("id", "name")
end
end
diff --git a/activerecord/test/cases/persistence_test.rb b/activerecord/test/cases/persistence_test.rb
index 688c3ed2b1..3f1da82cb4 100644
--- a/activerecord/test/cases/persistence_test.rb
+++ b/activerecord/test/cases/persistence_test.rb
@@ -90,6 +90,14 @@ class PersistenceTest < ActiveRecord::TestCase
assert_equal count, Pet.joins(:toys).where(where_args).delete_all
end
+ def test_delete_all_with_left_joins
+ where_args = { toys: { name: "Bone" } }
+ count = Pet.left_joins(:toys).where(where_args).count
+
+ assert_equal count, 1
+ assert_equal count, Pet.left_joins(:toys).where(where_args).delete_all
+ end
+
def test_delete_all_with_joins_and_where_part_is_not_hash
where_args = ["toys.name = ?", "Bone"]
count = Pet.joins(:toys).where(where_args).count
@@ -453,6 +461,20 @@ class PersistenceTest < ActiveRecord::TestCase
assert_nil Topic.find(2).last_read
end
+ def test_update_all_with_joins
+ where_args = { toys: { name: "Bone" } }
+ count = Pet.left_joins(:toys).where(where_args).count
+
+ assert_equal count, Pet.joins(:toys).where(where_args).update_all(name: "Bob")
+ end
+
+ def test_update_all_with_left_joins
+ where_args = { toys: { name: "Bone" } }
+ count = Pet.left_joins(:toys).where(where_args).count
+
+ assert_equal count, Pet.left_joins(:toys).where(where_args).update_all(name: "Bob")
+ end
+
def test_update_all_with_non_standard_table_name
assert_equal 1, WarehouseThing.where(id: 1).update_all(["value = ?", 0])
assert_equal 0, WarehouseThing.find(1).value
@@ -967,7 +989,7 @@ class PersistenceTest < ActiveRecord::TestCase
self.table_name = :widgets
end
- instance = widget.create!(
+ instance = widget.create!(
name: "Bob",
created_at: 1.day.ago,
updated_at: 1.day.ago)
diff --git a/activerecord/test/cases/primary_keys_test.rb b/activerecord/test/cases/primary_keys_test.rb
index 31d612abd1..1d72899102 100644
--- a/activerecord/test/cases/primary_keys_test.rb
+++ b/activerecord/test/cases/primary_keys_test.rb
@@ -120,7 +120,7 @@ class PrimaryKeysTest < ActiveRecord::TestCase
assert_nothing_raised { MixedCaseMonkey.find(1) }
end
def test_find_with_multiple_ids_should_quote_pkey
- assert_nothing_raised { MixedCaseMonkey.find([1,2]) }
+ assert_nothing_raised { MixedCaseMonkey.find([1, 2]) }
end
def test_instance_update_should_quote_pkey
assert_nothing_raised { MixedCaseMonkey.find(1).save }
@@ -129,12 +129,6 @@ class PrimaryKeysTest < ActiveRecord::TestCase
assert_nothing_raised { MixedCaseMonkey.find(1).destroy }
end
- def test_supports_primary_key
- assert_nothing_raised do
- ActiveRecord::Base.connection.supports_primary_key?
- end
- end
-
if ActiveRecord::Base.connection.supports_primary_key?
def test_primary_key_returns_value_if_it_exists
klass = Class.new(ActiveRecord::Base) do
@@ -154,7 +148,7 @@ class PrimaryKeysTest < ActiveRecord::TestCase
end
def test_quoted_primary_key_after_set_primary_key
- k = Class.new( ActiveRecord::Base )
+ k = Class.new(ActiveRecord::Base)
assert_equal k.connection.quote_column_name("id"), k.quoted_primary_key
k.primary_key = "foo"
assert_equal k.connection.quote_column_name("foo"), k.quoted_primary_key
@@ -216,6 +210,43 @@ class PrimaryKeyWithNoConnectionTest < ActiveRecord::TestCase
end
end
+class PrimaryKeyWithAutoIncrementTest < ActiveRecord::TestCase
+ self.use_transactional_tests = false
+
+ class AutoIncrement < ActiveRecord::Base
+ end
+
+ def setup
+ @connection = ActiveRecord::Base.connection
+ end
+
+ def teardown
+ @connection.drop_table(:auto_increments, if_exists: true)
+ end
+
+ def test_primary_key_with_auto_increment
+ @connection.create_table(:auto_increments, id: :integer, auto_increment: true, force: true)
+ assert_auto_incremented
+ end
+
+ def test_primary_key_with_auto_increment_and_bigint
+ @connection.create_table(:auto_increments, id: :bigint, auto_increment: true, force: true)
+ assert_auto_incremented
+ end
+
+ private
+ def assert_auto_incremented
+ record1 = AutoIncrement.create!
+ assert_not_nil record1.id
+
+ record1.destroy
+
+ record2 = AutoIncrement.create!
+ assert_not_nil record2.id
+ assert_operator record2.id, :>, record1.id
+ end
+end
+
class PrimaryKeyAnyTypeTest < ActiveRecord::TestCase
include SchemaDumpingHelper
@@ -289,85 +320,69 @@ class CompositePrimaryKeyTest < ActiveRecord::TestCase
end
if current_adapter?(:Mysql2Adapter)
- class PrimaryKeyBigintNilDefaultTest < ActiveRecord::TestCase
+ class PrimaryKeyIntegerNilDefaultTest < ActiveRecord::TestCase
include SchemaDumpingHelper
self.use_transactional_tests = false
def setup
@connection = ActiveRecord::Base.connection
- @connection.create_table(:bigint_defaults, id: :bigint, default: nil, force: true)
+ @connection.create_table(:int_defaults, id: :integer, default: nil, force: true)
end
def teardown
- @connection.drop_table :bigint_defaults, if_exists: true
+ @connection.drop_table :int_defaults, if_exists: true
end
- test "primary key with bigint allows default override via nil" do
- column = @connection.columns(:bigint_defaults).find { |c| c.name == "id" }
- assert column.bigint?
+ test "primary key with integer allows default override via nil" do
+ column = @connection.columns(:int_defaults).find { |c| c.name == "id" }
+ assert_equal :integer, column.type
assert_not column.auto_increment?
end
- test "schema dump primary key with bigint default nil" do
- schema = dump_table_schema "bigint_defaults"
- assert_match %r{create_table "bigint_defaults", id: :bigint, default: nil}, schema
+ test "schema dump primary key with int default nil" do
+ schema = dump_table_schema "int_defaults"
+ assert_match %r{create_table "int_defaults", id: :integer, default: nil}, schema
end
end
end
-if current_adapter?(:PostgreSQLAdapter, :Mysql2Adapter)
- class PrimaryKeyBigSerialTest < ActiveRecord::TestCase
- include SchemaDumpingHelper
+class PrimaryKeyIntegerTest < ActiveRecord::TestCase
+ include SchemaDumpingHelper
- self.use_transactional_tests = false
+ self.use_transactional_tests = false
- class Widget < ActiveRecord::Base
- end
+ class Widget < ActiveRecord::Base
+ end
- setup do
- @connection = ActiveRecord::Base.connection
- if current_adapter?(:PostgreSQLAdapter)
- @connection.create_table(:widgets, id: :bigserial, force: true)
- else
- @connection.create_table(:widgets, id: :bigint, force: true)
- end
- end
+ setup do
+ @connection = ActiveRecord::Base.connection
+ @connection.create_table(:widgets, force: true)
+ end
- teardown do
- @connection.drop_table :widgets, if_exists: true
- Widget.reset_column_information
- end
+ teardown do
+ @connection.drop_table :widgets, if_exists: true
+ Widget.reset_column_information
+ end
- test "primary key column type with bigserial" do
- column_type = Widget.type_for_attribute(Widget.primary_key)
- assert_equal :integer, column_type.type
- assert_equal 8, column_type.limit
+ if current_adapter?(:PostgreSQLAdapter, :Mysql2Adapter)
+ test "schema dump primary key with bigserial" do
+ schema = dump_table_schema "widgets"
+ assert_match %r{create_table "widgets", force: :cascade}, schema
end
+ end
- test "primary key with bigserial are automatically numbered" do
- widget = Widget.create!
- assert_not_nil widget.id
- end
+ test "primary key column type" do
+ column_type = Widget.type_for_attribute(Widget.primary_key)
+ assert_equal :integer, column_type.type
- test "schema dump primary key with bigserial" do
- schema = dump_table_schema "widgets"
- if current_adapter?(:PostgreSQLAdapter)
- assert_match %r{create_table "widgets", id: :bigserial, force: :cascade}, schema
- else
- assert_match %r{create_table "widgets", id: :bigint, force: :cascade}, schema
- end
+ if current_adapter?(:PostgreSQLAdapter, :Mysql2Adapter)
+ assert_equal 8, column_type.limit
end
if current_adapter?(:Mysql2Adapter)
- test "primary key column type with options" do
- @connection.create_table(:widgets, id: :primary_key, limit: 8, unsigned: true, force: true)
- column = @connection.columns(:widgets).find { |c| c.name == "id" }
- assert column.auto_increment?
- assert_equal :integer, column.type
- assert_equal 8, column.limit
- assert column.unsigned?
- end
+ column = @connection.columns(:widgets).find { |c| c.name == "id" }
+ assert column.auto_increment?
end
end
end
diff --git a/activerecord/test/cases/query_cache_test.rb b/activerecord/test/cases/query_cache_test.rb
index 16cf2bd2d0..4a49bfe9b1 100644
--- a/activerecord/test/cases/query_cache_test.rb
+++ b/activerecord/test/cases/query_cache_test.rb
@@ -10,13 +10,34 @@ class QueryCacheTest < ActiveRecord::TestCase
fixtures :tasks, :topics, :categories, :posts, :categories_posts
- teardown do
+ class ShouldNotHaveExceptionsLogger < ActiveRecord::LogSubscriber
+ attr_reader :logger
+
+ def initialize
+ super
+ @logger = ::Logger.new File::NULL
+ @exception = false
+ end
+
+ def exception?
+ @exception
+ end
+
+ def sql(event)
+ super
+ rescue
+ @exception = true
+ end
+ end
+
+ def teardown
Task.connection.clear_query_cache
ActiveRecord::Base.connection.disable_query_cache!
+ super
end
def test_exceptional_middleware_clears_and_disables_cache_on_error
- assert !ActiveRecord::Base.connection.query_cache_enabled, "cache off"
+ assert_cache :off
mw = middleware { |env|
Task.find 1
@@ -26,19 +47,66 @@ class QueryCacheTest < ActiveRecord::TestCase
}
assert_raises(RuntimeError) { mw.call({}) }
- assert_equal 0, ActiveRecord::Base.connection.query_cache.length
- assert !ActiveRecord::Base.connection.query_cache_enabled, "cache off"
+ assert_cache :off
end
- def test_exceptional_middleware_leaves_enabled_cache_alone
- ActiveRecord::Base.connection.enable_query_cache!
+ def test_query_cache_across_threads
+ ActiveRecord::Base.connection_pool.connections.each do |conn|
+ assert_cache :off, conn
+ end
- mw = middleware { |env|
- raise "lol borked"
- }
- assert_raises(RuntimeError) { mw.call({}) }
+ assert !ActiveRecord::Base.connection.nil?
+ assert_cache :off
- assert ActiveRecord::Base.connection.query_cache_enabled, "cache on"
+ middleware {
+ assert_cache :clean
+
+ Task.find 1
+ assert_cache :dirty
+
+ thread_1_connection = ActiveRecord::Base.connection
+ ActiveRecord::Base.clear_active_connections!
+ assert_cache :off, thread_1_connection
+
+ started = Concurrent::Event.new
+ checked = Concurrent::Event.new
+
+ thread_2_connection = nil
+ thread = Thread.new {
+ thread_2_connection = ActiveRecord::Base.connection
+
+ assert_equal thread_2_connection, thread_1_connection
+ assert_cache :off
+
+ middleware {
+ assert_cache :clean
+
+ Task.find 1
+ assert_cache :dirty
+
+ started.set
+ checked.wait
+
+ ActiveRecord::Base.clear_active_connections!
+ }.call({})
+ }
+
+ started.wait
+
+ thread_1_connection = ActiveRecord::Base.connection
+ assert_not_equal thread_1_connection, thread_2_connection
+ assert_cache :dirty, thread_2_connection
+ checked.set
+ thread.join
+
+ assert_cache :off, thread_2_connection
+ }.call({})
+
+ ActiveRecord::Base.connection_pool.connections.each do |conn|
+ assert_cache :off, conn
+ end
+ ensure
+ ActiveRecord::Base.clear_all_connections!
end
def test_middleware_delegates
@@ -62,10 +130,10 @@ class QueryCacheTest < ActiveRecord::TestCase
end
def test_cache_enabled_during_call
- assert !ActiveRecord::Base.connection.query_cache_enabled, "cache off"
+ assert_cache :off
mw = middleware { |env|
- assert ActiveRecord::Base.connection.query_cache_enabled, "cache on"
+ assert_cache :clean
[200, {}, nil]
}
mw.call({})
@@ -121,6 +189,33 @@ class QueryCacheTest < ActiveRecord::TestCase
end
end
+ def test_cache_does_not_raise_exceptions
+ logger = ShouldNotHaveExceptionsLogger.new
+ subscriber = ActiveSupport::Notifications.subscribe "sql.active_record", logger
+
+ ActiveRecord::Base.cache do
+ assert_queries(1) { Task.find(1); Task.find(1) }
+ end
+
+ assert_not_predicate logger, :exception?
+ ensure
+ ActiveSupport::Notifications.unsubscribe subscriber
+ end
+
+ def test_query_cache_does_not_allow_sql_key_mutation
+ subscriber = ActiveSupport::Notifications.subscribe("sql.active_record") do |_, _, _, _, payload|
+ payload[:sql].downcase!
+ end
+
+ assert_raises RuntimeError do
+ ActiveRecord::Base.cache do
+ assert_queries(1) { Task.find(1); Task.find(1) }
+ end
+ end
+ ensure
+ ActiveSupport::Notifications.unsubscribe subscriber
+ end
+
def test_cache_is_flat
Task.cache do
assert_queries(1) { Topic.find(1); Topic.find(1); }
@@ -232,12 +327,62 @@ class QueryCacheTest < ActiveRecord::TestCase
end
end
+ def test_query_cache_does_not_establish_connection_if_unconnected
+ ActiveRecord::Base.clear_active_connections!
+ refute ActiveRecord::Base.connection_handler.active_connections? # sanity check
+
+ middleware {
+ refute ActiveRecord::Base.connection_handler.active_connections?, "QueryCache forced ActiveRecord::Base to establish a connection in setup"
+ }.call({})
+
+ refute ActiveRecord::Base.connection_handler.active_connections?, "QueryCache forced ActiveRecord::Base to establish a connection in cleanup"
+ end
+
+ def test_query_cache_is_enabled_on_connections_established_after_middleware_runs
+ ActiveRecord::Base.clear_active_connections!
+ refute ActiveRecord::Base.connection_handler.active_connections? # sanity check
+
+ middleware {
+ assert ActiveRecord::Base.connection.query_cache_enabled, "QueryCache did not get lazily enabled"
+ }.call({})
+ end
+
+ def test_query_caching_is_local_to_the_current_thread
+ ActiveRecord::Base.clear_active_connections!
+
+ middleware {
+ assert ActiveRecord::Base.connection_pool.query_cache_enabled
+ assert ActiveRecord::Base.connection.query_cache_enabled
+
+ Thread.new {
+ refute ActiveRecord::Base.connection_pool.query_cache_enabled
+ refute ActiveRecord::Base.connection.query_cache_enabled
+ }.join
+ }.call({})
+ end
+
private
def middleware(&app)
executor = Class.new(ActiveSupport::Executor)
ActiveRecord::QueryCache.install_executor_hooks executor
lambda { |env| executor.wrap { app.call(env) } }
end
+
+ def assert_cache(state, connection = ActiveRecord::Base.connection)
+ case state
+ when :off
+ assert !connection.query_cache_enabled, "cache should be off"
+ assert connection.query_cache.empty?, "cache should be empty"
+ when :clean
+ assert connection.query_cache_enabled, "cache should be on"
+ assert connection.query_cache.empty?, "cache should be empty"
+ when :dirty
+ assert connection.query_cache_enabled, "cache should be on"
+ assert !connection.query_cache.empty?, "cache should be dirty"
+ else
+ raise "unknown state"
+ end
+ end
end
class QueryCacheExpiryTest < ActiveRecord::TestCase
diff --git a/activerecord/test/cases/quoting_test.rb b/activerecord/test/cases/quoting_test.rb
index 296dafacc2..05b71638c1 100644
--- a/activerecord/test/cases/quoting_test.rb
+++ b/activerecord/test/cases/quoting_test.rb
@@ -124,6 +124,10 @@ module ActiveRecord
assert_equal "'lol'", @quoter.quote(DateTime.now, nil)
end
+ def test_quoting_classes
+ assert_equal "'Object'", @quoter.quote(Object)
+ end
+
def test_crazy_object
crazy = Object.new
e = assert_raises(TypeError) do
diff --git a/activerecord/test/cases/reflection_test.rb b/activerecord/test/cases/reflection_test.rb
index 5dac3d064b..a90058e8bb 100644
--- a/activerecord/test/cases/reflection_test.rb
+++ b/activerecord/test/cases/reflection_test.rb
@@ -86,8 +86,8 @@ class ReflectionTest < ActiveRecord::TestCase
column = @first.column_for_attribute("attribute_that_doesnt_exist")
assert_instance_of ActiveRecord::ConnectionAdapters::NullColumn, column
assert_equal "attribute_that_doesnt_exist", column.name
- assert_equal nil, column.sql_type
- assert_equal nil, column.type
+ assert_nil column.sql_type
+ assert_nil column.type
end
def test_non_existent_types_are_identity_types
diff --git a/activerecord/test/cases/relation/or_test.rb b/activerecord/test/cases/relation/or_test.rb
index 2796595523..abb7ca72dd 100644
--- a/activerecord/test/cases/relation/or_test.rb
+++ b/activerecord/test/cases/relation/or_test.rb
@@ -79,7 +79,7 @@ module ActiveRecord
expected = Post.where("id = 1 or id = 2").to_a
p = Post.where("id = 1")
p.load
- assert_equal p.loaded?, true
+ assert_equal true, p.loaded?
assert_equal expected, p.or(Post.where("id = 2")).to_a
end
diff --git a/activerecord/test/cases/relation/where_test.rb b/activerecord/test/cases/relation/where_test.rb
index 925af49ffe..ed8ffddcf5 100644
--- a/activerecord/test/cases/relation/where_test.rb
+++ b/activerecord/test/cases/relation/where_test.rb
@@ -64,12 +64,12 @@ module ActiveRecord
end
def test_belongs_to_array_value_where
- assert_equal Post.where(author_id: [1,2]).to_sql, Post.where(author: [1,2]).to_sql
+ assert_equal Post.where(author_id: [1, 2]).to_sql, Post.where(author: [1, 2]).to_sql
end
def test_belongs_to_nested_relation_where
- expected = Post.where(author_id: Author.where(id: [1,2])).to_sql
- actual = Post.where(author: Author.where(id: [1,2])).to_sql
+ expected = Post.where(author_id: Author.where(id: [1, 2])).to_sql
+ actual = Post.where(author: Author.where(id: [1, 2])).to_sql
assert_equal expected, actual
end
@@ -87,7 +87,7 @@ module ActiveRecord
def test_belongs_to_nested_where_with_relation
author = authors(:david)
- expected = Author.where(id: author ).joins(:posts)
+ expected = Author.where(id: author).joins(:posts)
actual = Author.where(posts: { author_id: Author.where(id: author.id) }).joins(:posts)
assert_equal expected.to_a, actual.to_a
@@ -127,8 +127,8 @@ module ActiveRecord
end
def test_polymorphic_nested_relation_where
- expected = PriceEstimate.where(estimate_of_type: "Treasure", estimate_of_id: Treasure.where(id: [1,2]))
- actual = PriceEstimate.where(estimate_of: Treasure.where(id: [1,2]))
+ expected = PriceEstimate.where(estimate_of_type: "Treasure", estimate_of_id: Treasure.where(id: [1, 2]))
+ actual = PriceEstimate.where(estimate_of: Treasure.where(id: [1, 2]))
assert_equal expected.to_sql, actual.to_sql
end
diff --git a/activerecord/test/cases/relation_test.rb b/activerecord/test/cases/relation_test.rb
index 23d27ab90a..d5af0cc9a5 100644
--- a/activerecord/test/cases/relation_test.rb
+++ b/activerecord/test/cases/relation_test.rb
@@ -159,7 +159,7 @@ module ActiveRecord
relation = Relation.new(Post, Post.arel_table, Post.predicate_builder)
relation = relation.merge where: { name: :lol }, readonly: true
- assert_equal({ "name"=>:lol }, relation.where_clause.to_h)
+ assert_equal({ "name" => :lol }, relation.where_clause.to_h)
assert_equal true, relation.readonly_value
end
@@ -224,7 +224,7 @@ module ActiveRecord
def test_relation_merging_with_merged_joins_as_symbols
special_comments_with_ratings = SpecialComment.joins(:ratings)
posts_with_special_comments_with_ratings = Post.group("posts.id").joins(:special_comments).merge(special_comments_with_ratings)
- assert_equal({ 2=>1, 4=>3, 5=>1 }, authors(:david).posts.merge(posts_with_special_comments_with_ratings).count)
+ assert_equal({ 2 => 1, 4 => 3, 5 => 1 }, authors(:david).posts.merge(posts_with_special_comments_with_ratings).count)
end
def test_relation_merging_with_joins_as_join_dependency_pick_proper_parent
@@ -274,7 +274,7 @@ module ActiveRecord
join_string = "LEFT OUTER JOIN #{Rating.quoted_table_name} ON #{SpecialComment.quoted_table_name}.id = #{Rating.quoted_table_name}.comment_id"
special_comments_with_ratings = SpecialComment.joins join_string
posts_with_special_comments_with_ratings = Post.group("posts.id").joins(:special_comments).merge(special_comments_with_ratings)
- assert_equal({ 2=>1, 4=>3, 5=>1 }, authors(:david).posts.merge(posts_with_special_comments_with_ratings).count)
+ assert_equal({ 2 => 1, 4 => 3, 5 => 1 }, authors(:david).posts.merge(posts_with_special_comments_with_ratings).count)
end
class EnsureRoundTripTypeCasting < ActiveRecord::Type::Value
diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb
index 2e18c43b1b..981c2d758d 100644
--- a/activerecord/test/cases/relations_test.rb
+++ b/activerecord/test/cases/relations_test.rb
@@ -184,14 +184,14 @@ class RelationTest < ActiveRecord::TestCase
def test_select_with_subquery_in_from_does_not_use_original_table_name
relation = Comment.group(:type).select("COUNT(post_id) AS post_count, type")
- subquery = Comment.from(relation).select("type","post_count")
- assert_equal(relation.map(&:post_count).sort,subquery.map(&:post_count).sort)
+ subquery = Comment.from(relation).select("type", "post_count")
+ assert_equal(relation.map(&:post_count).sort, subquery.map(&:post_count).sort)
end
def test_group_with_subquery_in_from_does_not_use_original_table_name
relation = Comment.group(:type).select("COUNT(post_id) AS post_count,type")
subquery = Comment.from(relation).group("type").average("post_count")
- assert_equal(relation.map(&:post_count).sort,subquery.values.sort)
+ assert_equal(relation.map(&:post_count).sort, subquery.values.sort)
end
def test_finding_with_conditions
@@ -291,7 +291,7 @@ class RelationTest < ActiveRecord::TestCase
assert_includes Topic.order(id: "DESC").to_sql, "DESC"
assert_includes Topic.order(id: "desc").to_sql, "DESC"
assert_includes Topic.order(id: :DESC).to_sql, "DESC"
- assert_includes Topic.order(id: :desc).to_sql,"DESC"
+ assert_includes Topic.order(id: :desc).to_sql, "DESC"
end
def test_raising_exception_on_invalid_hash_params
@@ -365,7 +365,7 @@ class RelationTest < ActiveRecord::TestCase
end
def test_finding_with_sanitized_order
- query = Tag.order(["field(id, ?)", [1,3,2]]).to_sql
+ query = Tag.order(["field(id, ?)", [1, 3, 2]]).to_sql
assert_match(/field\(id, 1,3,2\)/, query)
query = Tag.order(["field(id, ?)", []]).to_sql
@@ -442,7 +442,7 @@ class RelationTest < ActiveRecord::TestCase
assert_no_queries(ignore_none: false) do
assert_equal 0, Developer.none.count
assert_equal 0, Developer.none.calculate(:count, nil)
- assert_equal nil, Developer.none.calculate(:average, "salary")
+ assert_nil Developer.none.calculate(:average, "salary")
end
end
@@ -458,55 +458,55 @@ class RelationTest < ActiveRecord::TestCase
def test_null_relation_sum
ac = Aircraft.new
assert_equal Hash.new, ac.engines.group(:id).sum(:id)
- assert_equal 0, ac.engines.count
+ assert_equal 0, ac.engines.count
ac.save
assert_equal Hash.new, ac.engines.group(:id).sum(:id)
- assert_equal 0, ac.engines.count
+ assert_equal 0, ac.engines.count
end
def test_null_relation_count
ac = Aircraft.new
assert_equal Hash.new, ac.engines.group(:id).count
- assert_equal 0, ac.engines.count
+ assert_equal 0, ac.engines.count
ac.save
assert_equal Hash.new, ac.engines.group(:id).count
- assert_equal 0, ac.engines.count
+ assert_equal 0, ac.engines.count
end
def test_null_relation_size
ac = Aircraft.new
assert_equal Hash.new, ac.engines.group(:id).size
- assert_equal 0, ac.engines.size
+ assert_equal 0, ac.engines.size
ac.save
assert_equal Hash.new, ac.engines.group(:id).size
- assert_equal 0, ac.engines.size
+ assert_equal 0, ac.engines.size
end
def test_null_relation_average
ac = Aircraft.new
assert_equal Hash.new, ac.engines.group(:car_id).average(:id)
- assert_equal nil, ac.engines.average(:id)
+ assert_nil ac.engines.average(:id)
ac.save
assert_equal Hash.new, ac.engines.group(:car_id).average(:id)
- assert_equal nil, ac.engines.average(:id)
+ assert_nil ac.engines.average(:id)
end
def test_null_relation_minimum
ac = Aircraft.new
assert_equal Hash.new, ac.engines.group(:car_id).minimum(:id)
- assert_equal nil, ac.engines.minimum(:id)
+ assert_nil ac.engines.minimum(:id)
ac.save
assert_equal Hash.new, ac.engines.group(:car_id).minimum(:id)
- assert_equal nil, ac.engines.minimum(:id)
+ assert_nil ac.engines.minimum(:id)
end
def test_null_relation_maximum
ac = Aircraft.new
assert_equal Hash.new, ac.engines.group(:car_id).maximum(:id)
- assert_equal nil, ac.engines.maximum(:id)
+ assert_nil ac.engines.maximum(:id)
ac.save
assert_equal Hash.new, ac.engines.group(:car_id).maximum(:id)
- assert_equal nil, ac.engines.maximum(:id)
+ assert_nil ac.engines.maximum(:id)
end
def test_null_relation_in_where_condition
@@ -785,7 +785,6 @@ class RelationTest < ActiveRecord::TestCase
expected_taggings = taggings(:welcome_general, :thinking_general)
assert_no_queries do
- assert_equal expected_taggings, author.taggings.distinct.sort_by(&:id)
assert_equal expected_taggings, author.taggings.uniq.sort_by(&:id)
end
@@ -979,7 +978,7 @@ class RelationTest < ActiveRecord::TestCase
assert ! davids.exists?(42)
assert ! davids.exists?(davids.new.id)
- fake = Author.where(name: "fake author")
+ fake = Author.where(name: "fake author")
assert ! fake.exists?
assert ! fake.exists?(authors(:david).id)
end
@@ -1815,7 +1814,7 @@ class RelationTest < ActiveRecord::TestCase
end
test "find_by returns nil if the record is missing" do
- assert_equal nil, Post.all.find_by("1 = 0")
+ assert_nil Post.all.find_by("1 = 0")
end
test "find_by doesn't have implicit ordering" do
@@ -1964,7 +1963,7 @@ class RelationTest < ActiveRecord::TestCase
end
def test_unscope_removes_binds
- left = Post.where(id: Arel::Nodes::BindParam.new)
+ left = Post.where(id: Arel::Nodes::BindParam.new)
column = Post.columns_hash["id"]
left.bind_values += [[column, 20]]
@@ -1973,8 +1972,8 @@ class RelationTest < ActiveRecord::TestCase
end
def test_merging_removes_rhs_bind_parameters
- left = Post.where(id: 20)
- right = Post.where(id: [1,2,3,4])
+ left = Post.where(id: 20)
+ right = Post.where(id: [1, 2, 3, 4])
merged = left.merge(right)
assert_equal [], merged.bind_values
diff --git a/activerecord/test/cases/sanitize_test.rb b/activerecord/test/cases/sanitize_test.rb
index 464bb12ccb..23bcb0af1e 100644
--- a/activerecord/test/cases/sanitize_test.rb
+++ b/activerecord/test/cases/sanitize_test.rb
@@ -12,8 +12,8 @@ class SanitizeTest < ActiveRecord::TestCase
assert_equal "name='#{quoted_bambi}'", Binary.send(:sanitize_sql_array, ["name='%s'", "Bambi"])
assert_equal "name='#{quoted_bambi}'", Binary.send(:sanitize_sql_array, ["name='%s'", "Bambi".mb_chars])
quoted_bambi_and_thumper = ActiveRecord::Base.connection.quote_string("Bambi\nand\nThumper")
- assert_equal "name='#{quoted_bambi_and_thumper}'",Binary.send(:sanitize_sql_array, ["name='%s'", "Bambi\nand\nThumper"])
- assert_equal "name='#{quoted_bambi_and_thumper}'",Binary.send(:sanitize_sql_array, ["name='%s'", "Bambi\nand\nThumper".mb_chars])
+ assert_equal "name='#{quoted_bambi_and_thumper}'", Binary.send(:sanitize_sql_array, ["name='%s'", "Bambi\nand\nThumper"])
+ assert_equal "name='#{quoted_bambi_and_thumper}'", Binary.send(:sanitize_sql_array, ["name='%s'", "Bambi\nand\nThumper".mb_chars])
end
def test_sanitize_sql_array_handles_bind_variables
diff --git a/activerecord/test/cases/schema_dumper_test.rb b/activerecord/test/cases/schema_dumper_test.rb
index ae3a5651a1..bea78d2a95 100644
--- a/activerecord/test/cases/schema_dumper_test.rb
+++ b/activerecord/test/cases/schema_dumper_test.rb
@@ -148,12 +148,7 @@ class SchemaDumperTest < ActiveRecord::TestCase
assert_match %r{c_int_4.*limit: 4}, output
end
- if current_adapter?(:SQLite3Adapter)
- assert_match %r{c_int_5.*limit: 5}, output
- assert_match %r{c_int_6.*limit: 6}, output
- assert_match %r{c_int_7.*limit: 7}, output
- assert_match %r{c_int_8.*limit: 8}, output
- elsif current_adapter?(:OracleAdapter)
+ if current_adapter?(:SQLite3Adapter, :OracleAdapter)
assert_match %r{c_int_5.*limit: 5}, output
assert_match %r{c_int_6.*limit: 6}, output
assert_match %r{c_int_7.*limit: 7}, output
@@ -346,7 +341,7 @@ class SchemaDumperTest < ActiveRecord::TestCase
create_table("dogs") do |t|
t.column :name, :string
- t.column :owner_id, :integer
+ t.column :owner_id, :bigint
t.index [:name]
t.foreign_key :dog_owners, column: "owner_id" if supports_foreign_keys?
end
@@ -451,7 +446,7 @@ class SchemaDumperDefaultsTest < ActiveRecord::TestCase
def test_schema_dump_with_float_column_infinity_default
skip unless current_adapter?(:PostgreSQLAdapter)
- output = dump_table_schema('infinity_defaults')
+ output = dump_table_schema("infinity_defaults")
assert_match %r{t\.float\s+"float_with_inf_default",\s+default: ::Float::INFINITY}, output
assert_match %r{t\.float\s+"float_with_nan_default",\s+default: ::Float::NAN}, output
end
diff --git a/activerecord/test/cases/schema_loading_test.rb b/activerecord/test/cases/schema_loading_test.rb
index 3d92a5e104..362370ac61 100644
--- a/activerecord/test/cases/schema_loading_test.rb
+++ b/activerecord/test/cases/schema_loading_test.rb
@@ -8,7 +8,7 @@ module SchemaLoadCounter
def load_schema!
self.load_schema_calls ||= 0
- self.load_schema_calls +=1
+ self.load_schema_calls += 1
super
end
end
diff --git a/activerecord/test/cases/scoping/default_scoping_test.rb b/activerecord/test/cases/scoping/default_scoping_test.rb
index 61062da3e1..b408a6000b 100644
--- a/activerecord/test/cases/scoping/default_scoping_test.rb
+++ b/activerecord/test/cases/scoping/default_scoping_test.rb
@@ -5,7 +5,6 @@ require "models/developer"
require "models/computer"
require "models/vehicle"
require "models/cat"
-require "active_support/core_ext/regexp"
class DefaultScopingTest < ActiveRecord::TestCase
fixtures :developers, :posts, :comments
@@ -51,7 +50,7 @@ class DefaultScopingTest < ActiveRecord::TestCase
def test_default_scope_with_conditions_string
assert_equal Developer.where(name: "David").map(&:id).sort, DeveloperCalledDavid.all.map(&:id).sort
- assert_equal nil, DeveloperCalledDavid.create!.name
+ assert_nil DeveloperCalledDavid.create!.name
end
def test_default_scope_with_conditions_hash
@@ -315,7 +314,7 @@ class DefaultScopingTest < ActiveRecord::TestCase
end
def test_create_attribute_overwrites_default_values
- assert_equal nil, PoorDeveloperCalledJamis.create!(salary: nil).salary
+ assert_nil PoorDeveloperCalledJamis.create!(salary: nil).salary
assert_equal 50000, PoorDeveloperCalledJamis.create!(name: "David").salary
end
diff --git a/activerecord/test/cases/scoping/named_scoping_test.rb b/activerecord/test/cases/scoping/named_scoping_test.rb
index 58e1310ab0..995ff4dfc5 100644
--- a/activerecord/test/cases/scoping/named_scoping_test.rb
+++ b/activerecord/test/cases/scoping/named_scoping_test.rb
@@ -119,8 +119,8 @@ class NamedScopingTest < ActiveRecord::TestCase
end
def test_scope_with_STI
- assert_equal 3,Post.containing_the_letter_a.count
- assert_equal 1,SpecialPost.containing_the_letter_a.count
+ assert_equal 3, Post.containing_the_letter_a.count
+ assert_equal 1, SpecialPost.containing_the_letter_a.count
end
def test_has_many_through_associations_have_access_to_scopes
diff --git a/activerecord/test/cases/scoping/relation_scoping_test.rb b/activerecord/test/cases/scoping/relation_scoping_test.rb
index 27b4583457..a1ae57fdbb 100644
--- a/activerecord/test/cases/scoping/relation_scoping_test.rb
+++ b/activerecord/test/cases/scoping/relation_scoping_test.rb
@@ -28,7 +28,7 @@ class RelationScopingTest < ActiveRecord::TestCase
def test_scope_breaks_caching_on_collections
author = authors :david
ids = author.reload.special_posts_with_default_scope.map(&:id)
- assert_equal [1,5,6], ids.sort
+ assert_equal [1, 5, 6], ids.sort
scoped_posts = SpecialPostWithDefaultScope.unscoped do
author = authors :david
author.reload.special_posts_with_default_scope.to_a
@@ -277,7 +277,7 @@ class NestedRelationScopingTest < ActiveRecord::TestCase
assert_equal "David", Developer.first.name
Developer.unscoped.where("name = 'Maiha'") do
- assert_equal nil, Developer.first
+ assert_nil Developer.first
end
# ensure that scoping is restored
diff --git a/activerecord/test/cases/secure_token_test.rb b/activerecord/test/cases/secure_token_test.rb
index eda0229c26..7b9cbee40a 100644
--- a/activerecord/test/cases/secure_token_test.rb
+++ b/activerecord/test/cases/secure_token_test.rb
@@ -27,6 +27,6 @@ class SecureTokenTest < ActiveRecord::TestCase
@user.token = "custom-secure-token"
@user.save
- assert_equal @user.token, "custom-secure-token"
+ assert_equal "custom-secure-token", @user.token
end
end
diff --git a/activerecord/test/cases/serialized_attribute_test.rb b/activerecord/test/cases/serialized_attribute_test.rb
index 8e9514de7c..a469da0a5b 100644
--- a/activerecord/test/cases/serialized_attribute_test.rb
+++ b/activerecord/test/cases/serialized_attribute_test.rb
@@ -107,7 +107,7 @@ class SerializedAttributeTest < ActiveRecord::TestCase
end
def test_serialized_time_attribute
- myobj = Time.local(2008,1,1,1,0)
+ myobj = Time.local(2008, 1, 1, 1, 0)
topic = Topic.create("content" => myobj).reload
assert_equal(myobj, topic.content)
end
@@ -185,14 +185,14 @@ class SerializedAttributeTest < ActiveRecord::TestCase
topic = Topic.new(content: true)
assert topic.save
topic = topic.reload
- assert_equal topic.content, true
+ assert_equal true, topic.content
end
def test_serialized_boolean_value_false
topic = Topic.new(content: false)
assert topic.save
topic = topic.reload
- assert_equal topic.content, false
+ assert_equal false, topic.content
end
def test_serialize_with_coder
@@ -211,7 +211,7 @@ class SerializedAttributeTest < ActiveRecord::TestCase
topic.save!
topic.reload
assert_kind_of some_class, topic.content
- assert_equal topic.content, some_class.new("my value")
+ assert_equal some_class.new("my value"), topic.content
end
def test_serialize_attribute_via_select_method_when_time_zone_available
@@ -313,8 +313,8 @@ class SerializedAttributeTest < ActiveRecord::TestCase
return if value.nil?
value.gsub(" encoded", "")
end
- type = Class.new(ActiveRecord::Type::Value) do
- include ActiveRecord::Type::Helpers::Mutable
+ type = Class.new(ActiveModel::Type::Value) do
+ include ActiveModel::Type::Helpers::Mutable
def serialize(value)
return if value.nil?
diff --git a/activerecord/test/cases/tasks/database_tasks_test.rb b/activerecord/test/cases/tasks/database_tasks_test.rb
index d847a02679..d03231e711 100644
--- a/activerecord/test/cases/tasks/database_tasks_test.rb
+++ b/activerecord/test/cases/tasks/database_tasks_test.rb
@@ -24,7 +24,7 @@ module ActiveRecord
sqlite3: :sqlite_tasks
}
- class DatabaseTasksUtilsTask< ActiveRecord::TestCase
+ class DatabaseTasksUtilsTask < ActiveRecord::TestCase
def test_raises_an_error_when_called_with_protected_environment
ActiveRecord::Migrator.stubs(:current_version).returns(1)
diff --git a/activerecord/test/cases/tasks/mysql_rake_test.rb b/activerecord/test/cases/tasks/mysql_rake_test.rb
index dbe935808e..285e718596 100644
--- a/activerecord/test/cases/tasks/mysql_rake_test.rb
+++ b/activerecord/test/cases/tasks/mysql_rake_test.rb
@@ -59,7 +59,7 @@ if current_adapter?(:Mysql2Adapter)
def test_when_database_created_successfully_outputs_info_to_stdout
ActiveRecord::Tasks::DatabaseTasks.create @configuration
- assert_equal $stdout.string, "Created database 'my-app-db'\n"
+ assert_equal "Created database 'my-app-db'\n", $stdout.string
end
def test_create_when_database_exists_outputs_info_to_stderr
@@ -69,7 +69,7 @@ if current_adapter?(:Mysql2Adapter)
ActiveRecord::Tasks::DatabaseTasks.create @configuration
- assert_equal $stderr.string, "Database 'my-app-db' already exists\n"
+ assert_equal "Database 'my-app-db' already exists\n", $stderr.string
end
end
@@ -205,7 +205,7 @@ if current_adapter?(:Mysql2Adapter)
def test_when_database_dropped_successfully_outputs_info_to_stdout
ActiveRecord::Tasks::DatabaseTasks.drop @configuration
- assert_equal $stdout.string, "Dropped database 'my-app-db'\n"
+ assert_equal "Dropped database 'my-app-db'\n", $stdout.string
end
end
diff --git a/activerecord/test/cases/tasks/postgresql_rake_test.rb b/activerecord/test/cases/tasks/postgresql_rake_test.rb
index b8c8ec88f0..1a73d410d0 100644
--- a/activerecord/test/cases/tasks/postgresql_rake_test.rb
+++ b/activerecord/test/cases/tasks/postgresql_rake_test.rb
@@ -74,7 +74,7 @@ if current_adapter?(:PostgreSQLAdapter)
def test_when_database_created_successfully_outputs_info_to_stdout
ActiveRecord::Tasks::DatabaseTasks.create @configuration
- assert_equal $stdout.string, "Created database 'my-app-db'\n"
+ assert_equal "Created database 'my-app-db'\n", $stdout.string
end
def test_create_when_database_exists_outputs_info_to_stderr
@@ -84,7 +84,7 @@ if current_adapter?(:PostgreSQLAdapter)
ActiveRecord::Tasks::DatabaseTasks.create @configuration
- assert_equal $stderr.string, "Database 'my-app-db' already exists\n"
+ assert_equal "Database 'my-app-db' already exists\n", $stderr.string
end
end
@@ -126,7 +126,7 @@ if current_adapter?(:PostgreSQLAdapter)
def test_when_database_dropped_successfully_outputs_info_to_stdout
ActiveRecord::Tasks::DatabaseTasks.drop @configuration
- assert_equal $stdout.string, "Dropped database 'my-app-db'\n"
+ assert_equal "Dropped database 'my-app-db'\n", $stdout.string
end
end
diff --git a/activerecord/test/cases/tasks/sqlite_rake_test.rb b/activerecord/test/cases/tasks/sqlite_rake_test.rb
index 141048bfe7..0d917f3f6c 100644
--- a/activerecord/test/cases/tasks/sqlite_rake_test.rb
+++ b/activerecord/test/cases/tasks/sqlite_rake_test.rb
@@ -34,7 +34,7 @@ if current_adapter?(:SQLite3Adapter)
def test_when_db_created_successfully_outputs_info_to_stdout
ActiveRecord::Tasks::DatabaseTasks.create @configuration, "/rails/root"
- assert_equal $stdout.string, "Created database '#{@database}'\n"
+ assert_equal "Created database '#{@database}'\n", $stdout.string
end
def test_db_create_when_file_exists
@@ -42,7 +42,7 @@ if current_adapter?(:SQLite3Adapter)
ActiveRecord::Tasks::DatabaseTasks.create @configuration, "/rails/root"
- assert_equal $stderr.string, "Database '#{@database}' already exists\n"
+ assert_equal "Database '#{@database}' already exists\n", $stderr.string
end
def test_db_create_with_file_does_nothing
@@ -128,7 +128,7 @@ if current_adapter?(:SQLite3Adapter)
def test_when_db_dropped_successfully_outputs_info_to_stdout
ActiveRecord::Tasks::DatabaseTasks.drop @configuration, "/rails/root"
- assert_equal $stdout.string, "Dropped database '#{@database}'\n"
+ assert_equal "Dropped database '#{@database}'\n", $stdout.string
end
end
diff --git a/activerecord/test/cases/test_case.rb b/activerecord/test/cases/test_case.rb
index 8eddc5a9ed..31b11c19f7 100644
--- a/activerecord/test/cases/test_case.rb
+++ b/activerecord/test/cases/test_case.rb
@@ -2,7 +2,6 @@ require "active_support/test_case"
require "active_support/testing/autorun"
require "active_support/testing/method_call_assertions"
require "active_support/testing/stream"
-require "active_support/core_ext/regexp"
require "active_record/fixtures"
require "cases/validations_repair_helper"
@@ -64,11 +63,11 @@ module ActiveRecord
assert_queries(0, options, &block)
end
- def assert_column(model, column_name, msg=nil)
+ def assert_column(model, column_name, msg = nil)
assert has_column?(model, column_name), msg
end
- def assert_no_column(model, column_name, msg=nil)
+ def assert_no_column(model, column_name, msg = nil)
assert_not has_column?(model, column_name), msg
end
diff --git a/activerecord/test/cases/test_fixtures_test.rb b/activerecord/test/cases/test_fixtures_test.rb
index 14a5faa85e..7090202a89 100644
--- a/activerecord/test/cases/test_fixtures_test.rb
+++ b/activerecord/test/cases/test_fixtures_test.rb
@@ -3,7 +3,7 @@ require "cases/helper"
class TestFixturesTest < ActiveRecord::TestCase
setup do
@klass = Class.new
- @klass.send(:include, ActiveRecord::TestFixtures)
+ @klass.include(ActiveRecord::TestFixtures)
end
def test_deprecated_use_transactional_fixtures=
diff --git a/activerecord/test/cases/transaction_callbacks_test.rb b/activerecord/test/cases/transaction_callbacks_test.rb
index bd50fe55e9..391bbe8877 100644
--- a/activerecord/test/cases/transaction_callbacks_test.rb
+++ b/activerecord/test/cases/transaction_callbacks_test.rb
@@ -243,14 +243,14 @@ class TransactionCallbacksTest < ActiveRecord::TestCase
end
def test_only_call_after_rollback_on_records_rolled_back_to_a_savepoint
- def @first.rollbacks(i=0); @rollbacks ||= 0; @rollbacks += i if i; end
- def @first.commits(i=0); @commits ||= 0; @commits += i if i; end
+ def @first.rollbacks(i = 0); @rollbacks ||= 0; @rollbacks += i if i; end
+ def @first.commits(i = 0); @commits ||= 0; @commits += i if i; end
@first.after_rollback_block { |r| r.rollbacks(1) }
@first.after_commit_block { |r| r.commits(1) }
second = TopicWithCallbacks.find(3)
- def second.rollbacks(i=0); @rollbacks ||= 0; @rollbacks += i if i; end
- def second.commits(i=0); @commits ||= 0; @commits += i if i; end
+ def second.rollbacks(i = 0); @rollbacks ||= 0; @rollbacks += i if i; end
+ def second.commits(i = 0); @commits ||= 0; @commits += i if i; end
second.after_rollback_block { |r| r.rollbacks(1) }
second.after_commit_block { |r| r.commits(1) }
@@ -269,8 +269,8 @@ class TransactionCallbacksTest < ActiveRecord::TestCase
end
def test_only_call_after_rollback_on_records_rolled_back_to_a_savepoint_when_release_savepoint_fails
- def @first.rollbacks(i=0); @rollbacks ||= 0; @rollbacks += i if i; end
- def @first.commits(i=0); @commits ||= 0; @commits += i if i; end
+ def @first.rollbacks(i = 0); @rollbacks ||= 0; @rollbacks += i if i; end
+ def @first.commits(i = 0); @commits ||= 0; @commits += i if i; end
@first.after_rollback_block { |r| r.rollbacks(1) }
@first.after_commit_block { |r| r.commits(1) }
@@ -449,6 +449,51 @@ class CallbacksOnMultipleActionsTest < ActiveRecord::TestCase
end
end
+class CallbacksOnDestroyUpdateActionRaceTest < ActiveRecord::TestCase
+ class TopicWithHistory < ActiveRecord::Base
+ self.table_name = :topics
+
+ def self.clear_history
+ @@history = []
+ end
+
+ def self.history
+ @@history ||= []
+ end
+ end
+
+ class TopicWithCallbacksOnDestroy < TopicWithHistory
+ after_commit(on: :destroy) { |record| record.class.history << :destroy }
+ end
+
+ class TopicWithCallbacksOnUpdate < TopicWithHistory
+ after_commit(on: :update) { |record| record.class.history << :update }
+ end
+
+ def test_trigger_once_on_multiple_deletions
+ TopicWithCallbacksOnDestroy.clear_history
+ topic = TopicWithCallbacksOnDestroy.new
+ topic.save
+ topic_clone = TopicWithCallbacksOnDestroy.find(topic.id)
+ topic.destroy
+ topic_clone.destroy
+
+ assert_equal [:destroy], TopicWithCallbacksOnDestroy.history
+ end
+
+ def test_trigger_on_update_where_row_was_deleted
+ TopicWithCallbacksOnUpdate.clear_history
+ topic = TopicWithCallbacksOnUpdate.new
+ topic.save
+ topic_clone = TopicWithCallbacksOnUpdate.find(topic.id)
+ topic.destroy
+ topic_clone.author_name = "Test Author"
+ topic_clone.save
+
+ assert_equal [], TopicWithCallbacksOnUpdate.history
+ end
+end
+
class TransactionEnrollmentCallbacksTest < ActiveRecord::TestCase
class TopicWithoutTransactionalEnrollmentCallbacks < ActiveRecord::Base
self.table_name = :topics
diff --git a/activerecord/test/cases/transactions_test.rb b/activerecord/test/cases/transactions_test.rb
index 834365660f..9b1cca8583 100644
--- a/activerecord/test/cases/transactions_test.rb
+++ b/activerecord/test/cases/transactions_test.rb
@@ -98,7 +98,7 @@ class TransactionTest < ActiveRecord::TestCase
end
Topic.transaction do
- @first.approved = true
+ @first.approved = true
@first.save!
end
@@ -160,7 +160,7 @@ class TransactionTest < ActiveRecord::TestCase
assert !@first.approved
Topic.transaction do
- @first.approved = true
+ @first.approved = true
@first.save!
end
assert !Topic.find(@first.id).approved?, "Should not commit the approved flag"
@@ -443,16 +443,16 @@ class TransactionTest < ActiveRecord::TestCase
def test_using_named_savepoints
Topic.transaction do
- @first.approved = true
+ @first.approved = true
@first.save!
Topic.connection.create_savepoint("first")
- @first.approved = false
+ @first.approved = false
@first.save!
Topic.connection.rollback_to_savepoint("first")
assert @first.reload.approved?
- @first.approved = false
+ @first.approved = false
@first.save!
Topic.connection.release_savepoint("first")
assert_not @first.reload.approved?
diff --git a/activemodel/test/cases/type/unsigned_integer_test.rb b/activerecord/test/cases/type/unsigned_integer_test.rb
index 026cb08a06..1cd4dbc2c5 100644
--- a/activemodel/test/cases/type/unsigned_integer_test.rb
+++ b/activerecord/test/cases/type/unsigned_integer_test.rb
@@ -1,9 +1,8 @@
require "cases/helper"
-require "active_model/type"
-module ActiveModel
+module ActiveRecord
module Type
- class UnsignedIntegerTest < ActiveModel::TestCase
+ class UnsignedIntegerTest < ActiveRecord::TestCase
test "unsigned int max value is in range" do
assert_equal(4294967295, UnsignedInteger.new.serialize(4294967295))
end
diff --git a/activerecord/test/cases/validations/association_validation_test.rb b/activerecord/test/cases/validations/association_validation_test.rb
index 66d6ecb928..f5ceb27d97 100644
--- a/activerecord/test/cases/validations/association_validation_test.rb
+++ b/activerecord/test/cases/validations/association_validation_test.rb
@@ -26,7 +26,7 @@ class AssociationValidationTest < ActiveRecord::TestCase
def test_validates_associated_one
Reply.validates :topic, associated: true
- Topic.validates_presence_of( :content )
+ Topic.validates_presence_of(:content)
r = Reply.new("title" => "A reply", "content" => "with content!")
r.topic = Topic.create("title" => "uhohuhoh")
assert !r.valid?
diff --git a/activerecord/test/cases/validations/uniqueness_validation_test.rb b/activerecord/test/cases/validations/uniqueness_validation_test.rb
index 44b4e28777..6d22638592 100644
--- a/activerecord/test/cases/validations/uniqueness_validation_test.rb
+++ b/activerecord/test/cases/validations/uniqueness_validation_test.rb
@@ -6,6 +6,9 @@ require "models/guid"
require "models/event"
require "models/dashboard"
require "models/uuid_item"
+require "models/author"
+require "models/person"
+require "models/essay"
class Wizard < ActiveRecord::Base
self.abstract_class = true
@@ -163,6 +166,19 @@ class UniquenessValidationTest < ActiveRecord::TestCase
assert !r2.valid?, "Saving r2 first time"
end
+ def test_validate_uniqueness_with_polymorphic_object_scope
+ Essay.validates_uniqueness_of(:name, scope: :writer)
+
+ a = Author.create(name: "Sergey")
+ p = Person.create(first_name: "Sergey")
+
+ e1 = a.essays.create(name: "Essay")
+ assert e1.valid?, "Saving e1"
+
+ e2 = p.essays.create(name: "Essay")
+ assert e2.valid?, "Saving e2"
+ end
+
def test_validate_uniqueness_with_composed_attribute_scope
r1 = ReplyWithTitleObject.create "title" => "r1", "content" => "hello world"
assert r1.valid?, "Saving r1"
diff --git a/activerecord/test/cases/view_test.rb b/activerecord/test/cases/view_test.rb
index 0e38cee334..1f326d4b39 100644
--- a/activerecord/test/cases/view_test.rb
+++ b/activerecord/test/cases/view_test.rb
@@ -11,20 +11,21 @@ module ViewBehavior
end
class Ebook < ActiveRecord::Base
+ self.table_name = "ebooks'"
self.primary_key = "id"
end
def setup
super
@connection = ActiveRecord::Base.connection
- create_view "ebooks", <<-SQL
+ create_view "ebooks'", <<-SQL
SELECT id, name, status FROM books WHERE format = 'ebook'
SQL
end
def teardown
super
- drop_view "ebooks"
+ drop_view "ebooks'"
end
def test_reading
@@ -66,15 +67,20 @@ module ViewBehavior
def test_does_not_assume_id_column_as_primary_key
model = Class.new(ActiveRecord::Base) do
- self.table_name = "ebooks"
+ self.table_name = "ebooks'"
end
assert_nil model.primary_key
end
def test_does_not_dump_view_as_table
- schema = dump_table_schema "ebooks"
- assert_no_match %r{create_table "ebooks"}, schema
+ schema = dump_table_schema "ebooks'"
+ assert_no_match %r{create_table "ebooks'"}, schema
end
+
+ private
+ def quote_table_name(name)
+ @connection.quote_table_name(name)
+ end
end
if ActiveRecord::Base.connection.supports_views?
@@ -83,11 +89,11 @@ if ActiveRecord::Base.connection.supports_views?
private
def create_view(name, query)
- @connection.execute "CREATE VIEW #{name} AS #{query}"
+ @connection.execute "CREATE VIEW #{quote_table_name(name)} AS #{query}"
end
def drop_view(name)
- @connection.execute "DROP VIEW #{name}" if @connection.view_exists? name
+ @connection.execute "DROP VIEW #{quote_table_name(name)}" if @connection.view_exists? name
end
end
@@ -206,11 +212,11 @@ if ActiveRecord::Base.connection.respond_to?(:supports_materialized_views?) &&
private
def create_view(name, query)
- @connection.execute "CREATE MATERIALIZED VIEW #{name} AS #{query}"
+ @connection.execute "CREATE MATERIALIZED VIEW #{quote_table_name(name)} AS #{query}"
end
def drop_view(name)
- @connection.execute "DROP MATERIALIZED VIEW #{name}" if @connection.view_exists? name
+ @connection.execute "DROP MATERIALIZED VIEW #{quote_table_name(name)}" if @connection.view_exists? name
end
end
end
diff --git a/activerecord/test/cases/yaml_serialization_test.rb b/activerecord/test/cases/yaml_serialization_test.rb
index 5192e5050a..1571b31329 100644
--- a/activerecord/test/cases/yaml_serialization_test.rb
+++ b/activerecord/test/cases/yaml_serialization_test.rb
@@ -95,7 +95,7 @@ class YamlSerializationTest < ActiveRecord::TestCase
topic = YAML.load(yaml_fixture("rails_4_1"))
assert topic.new_record?
- assert_equal nil, topic.id
+ assert_nil topic.id
assert_equal "The First Topic", topic.title
assert_equal({ omg: :lol }, topic.content)
end
diff --git a/activerecord/test/config.example.yml b/activerecord/test/config.example.yml
index 58e2d45748..4bcb2aeea6 100644
--- a/activerecord/test/config.example.yml
+++ b/activerecord/test/config.example.yml
@@ -77,6 +77,9 @@ connections:
postgresql:
arunit:
min_messages: warning
+ arunit_without_prepared_statements:
+ min_messages: warning
+ prepared_statements: false
arunit2:
min_messages: warning
diff --git a/activerecord/test/fixtures/naked/yml/courses_with_invalid_key.yml b/activerecord/test/fixtures/naked/yml/courses_with_invalid_key.yml
new file mode 100644
index 0000000000..6f9da79b45
--- /dev/null
+++ b/activerecord/test/fixtures/naked/yml/courses_with_invalid_key.yml
@@ -0,0 +1,3 @@
+one:
+ id: 1
+two: ['not a hash']
diff --git a/activerecord/test/fixtures/other_dogs.yml b/activerecord/test/fixtures/other_dogs.yml
new file mode 100644
index 0000000000..b576861929
--- /dev/null
+++ b/activerecord/test/fixtures/other_dogs.yml
@@ -0,0 +1,2 @@
+lassie:
+ id: 1
diff --git a/activerecord/test/models/admin/user.rb b/activerecord/test/models/admin/user.rb
index 2e703f6219..a76e4b6795 100644
--- a/activerecord/test/models/admin/user.rb
+++ b/activerecord/test/models/admin/user.rb
@@ -22,11 +22,11 @@ class Admin::User < ActiveRecord::Base
store :json_data_empty, accessors: [ :is_a_good_guy ], coder: Coder.new
def phone_number
- read_store_attribute(:settings, :phone_number).gsub(/(\d{3})(\d{3})(\d{4})/,'(\1) \2-\3')
+ read_store_attribute(:settings, :phone_number).gsub(/(\d{3})(\d{3})(\d{4})/, '(\1) \2-\3')
end
def phone_number=(value)
- write_store_attribute(:settings, :phone_number, value && value.gsub(/[^\d]/,""))
+ write_store_attribute(:settings, :phone_number, value && value.gsub(/[^\d]/, ""))
end
def color
diff --git a/activerecord/test/models/company.rb b/activerecord/test/models/company.rb
index 025630087c..4561b3132b 100644
--- a/activerecord/test/models/company.rb
+++ b/activerecord/test/models/company.rb
@@ -151,7 +151,7 @@ class Client < Company
# is calling client.destroy, deleting from the database, or setting
# foreign keys to NULL.
def self.destroyed_client_ids
- @destroyed_client_ids ||= Hash.new { |h,k| h[k] = [] }
+ @destroyed_client_ids ||= Hash.new { |h, k| h[k] = [] }
end
before_destroy do |client|
@@ -199,7 +199,7 @@ class Account < ActiveRecord::Base
alias_attribute :available_credit, :credit_limit
def self.destroyed_account_ids
- @destroyed_account_ids ||= Hash.new { |h,k| h[k] = [] }
+ @destroyed_account_ids ||= Hash.new { |h, k| h[k] = [] }
end
# Test private kernel method through collection proxy using has_many.
diff --git a/activerecord/test/models/company_in_module.rb b/activerecord/test/models/company_in_module.rb
index 682f99e365..0782c1eff4 100644
--- a/activerecord/test/models/company_in_module.rb
+++ b/activerecord/test/models/company_in_module.rb
@@ -88,7 +88,7 @@ module MyApplication
validate :check_empty_credit_limit
- protected
+ private
def check_empty_credit_limit
errors.add("credit_card", :blank) if credit_card.blank?
diff --git a/activerecord/test/models/eye.rb b/activerecord/test/models/eye.rb
index ab3b3eacf3..f53c34e4b1 100644
--- a/activerecord/test/models/eye.rb
+++ b/activerecord/test/models/eye.rb
@@ -22,12 +22,12 @@ class Eye < ActiveRecord::Base
alias trace_after_create2 trace_after_create
def trace_after_update
- (@after_update_callbacks_stack ||= []) << iris.changed?
+ (@after_update_callbacks_stack ||= []) << iris.has_changes_to_save?
end
alias trace_after_update2 trace_after_update
def trace_after_save
- (@after_save_callbacks_stack ||= []) << iris.changed?
+ (@after_save_callbacks_stack ||= []) << iris.has_changes_to_save?
end
alias trace_after_save2 trace_after_save
end
diff --git a/activerecord/test/models/other_dog.rb b/activerecord/test/models/other_dog.rb
new file mode 100644
index 0000000000..418caf34be
--- /dev/null
+++ b/activerecord/test/models/other_dog.rb
@@ -0,0 +1,5 @@
+require_dependency "models/arunit2_model"
+
+class OtherDog < ARUnit2Model
+ self.table_name = "dogs"
+end
diff --git a/activerecord/test/models/pirate.rb b/activerecord/test/models/pirate.rb
index 2dc8f9bd84..c532ab426e 100644
--- a/activerecord/test/models/pirate.rb
+++ b/activerecord/test/models/pirate.rb
@@ -9,10 +9,10 @@ class Pirate < ActiveRecord::Base
before_remove: :log_before_remove,
after_remove: :log_after_remove
has_and_belongs_to_many :parrots_with_proc_callbacks, class_name: "Parrot",
- before_add: proc { |p,pa| p.ship_log << "before_adding_proc_parrot_#{pa.id || '<new>'}" },
- after_add: proc { |p,pa| p.ship_log << "after_adding_proc_parrot_#{pa.id || '<new>'}" },
- before_remove: proc { |p,pa| p.ship_log << "before_removing_proc_parrot_#{pa.id}" },
- after_remove: proc { |p,pa| p.ship_log << "after_removing_proc_parrot_#{pa.id}" }
+ before_add: proc { |p, pa| p.ship_log << "before_adding_proc_parrot_#{pa.id || '<new>'}" },
+ after_add: proc { |p, pa| p.ship_log << "after_adding_proc_parrot_#{pa.id || '<new>'}" },
+ before_remove: proc { |p, pa| p.ship_log << "before_removing_proc_parrot_#{pa.id}" },
+ after_remove: proc { |p, pa| p.ship_log << "after_removing_proc_parrot_#{pa.id}" }
has_and_belongs_to_many :autosaved_parrots, class_name: "Parrot", autosave: true
has_many :treasures, as: :looter
@@ -28,10 +28,10 @@ class Pirate < ActiveRecord::Base
before_remove: :log_before_remove,
after_remove: :log_after_remove
has_many :birds_with_proc_callbacks, class_name: "Bird",
- before_add: proc { |p,b| p.ship_log << "before_adding_proc_bird_#{b.id || '<new>'}" },
- after_add: proc { |p,b| p.ship_log << "after_adding_proc_bird_#{b.id || '<new>'}" },
- before_remove: proc { |p,b| p.ship_log << "before_removing_proc_bird_#{b.id}" },
- after_remove: proc { |p,b| p.ship_log << "after_removing_proc_bird_#{b.id}" }
+ before_add: proc { |p, b| p.ship_log << "before_adding_proc_bird_#{b.id || '<new>'}" },
+ after_add: proc { |p, b| p.ship_log << "after_adding_proc_bird_#{b.id || '<new>'}" },
+ before_remove: proc { |p, b| p.ship_log << "before_removing_proc_bird_#{b.id}" },
+ after_remove: proc { |p, b| p.ship_log << "after_removing_proc_bird_#{b.id}" }
has_many :birds_with_reject_all_blank, class_name: "Bird"
has_one :foo_bulb, -> { where name: "foo" }, foreign_key: :car_id, class_name: "Bulb"
diff --git a/activerecord/test/models/post.rb b/activerecord/test/models/post.rb
index 66a99cbcda..e74aedb814 100644
--- a/activerecord/test/models/post.rb
+++ b/activerecord/test/models/post.rb
@@ -47,7 +47,7 @@ class Post < ActiveRecord::Base
scope :typographically_interesting, -> { containing_the_letter_a.or(titled_with_an_apostrophe) }
- has_many :comments do
+ has_many :comments do
def find_most_recent
order("id DESC").first
end
@@ -168,7 +168,7 @@ class Post < ActiveRecord::Base
@log = []
end
- def self.log(message=nil, side=nil, new_record=nil)
+ def self.log(message = nil, side = nil, new_record = nil)
return @log if message.nil?
@log << [message, side, new_record]
end
@@ -231,7 +231,7 @@ end
class SpecialPostWithDefaultScope < ActiveRecord::Base
self.inheritance_column = :disabled
self.table_name = "posts"
- default_scope { where(id: [1, 5,6]) }
+ default_scope { where(id: [1, 5, 6]) }
end
class PostThatLoadsCommentsInAnAfterSaveHook < ActiveRecord::Base
diff --git a/activerecord/test/models/subject.rb b/activerecord/test/models/subject.rb
index 29e290825e..504f68a296 100644
--- a/activerecord/test/models/subject.rb
+++ b/activerecord/test/models/subject.rb
@@ -5,7 +5,7 @@ class Subject < ActiveRecord::Base
# as otherwise synonym test was failing
after_initialize :set_email_address
- protected
+ private
def set_email_address
unless persisted?
self.author_email_address = "test@test.com"
diff --git a/activerecord/test/models/topic.rb b/activerecord/test/models/topic.rb
index db04735d01..0420e2d15c 100644
--- a/activerecord/test/models/topic.rb
+++ b/activerecord/test/models/topic.rb
@@ -73,7 +73,7 @@ class Topic < ActiveRecord::Base
write_attribute(:approved, val)
end
- protected
+ private
def default_written_on
self.written_on = Time.now unless attribute_present?("written_on")
diff --git a/activerecord/test/schema/postgresql_specific_schema.rb b/activerecord/test/schema/postgresql_specific_schema.rb
index f00b858ea6..15ba2d67ab 100644
--- a/activerecord/test/schema/postgresql_specific_schema.rb
+++ b/activerecord/test/schema/postgresql_specific_schema.rb
@@ -1,6 +1,7 @@
ActiveRecord::Schema.define do
enable_extension!("uuid-ossp", ActiveRecord::Base.connection)
+ enable_extension!("pgcrypto", ActiveRecord::Base.connection) if ActiveRecord::Base.connection.supports_pgcrypto_uuid?
create_table :uuid_parents, id: :uuid, force: true do |t|
t.string :name
diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb
index d2fb090118..9d76d0537e 100644
--- a/activerecord/test/schema/schema.rb
+++ b/activerecord/test/schema/schema.rb
@@ -54,7 +54,7 @@ ActiveRecord::Schema.define do
create_table :authors, force: true do |t|
t.string :name, null: false
- t.integer :author_address_id
+ t.bigint :author_address_id
t.integer :author_address_extra_id
t.string :organization_id
t.string :owned_essay_id
@@ -126,6 +126,9 @@ ActiveRecord::Schema.define do
t.timestamps null: false
end
+ create_table :old_cars, id: :integer, force: true do |t|
+ end
+
create_table :carriers, force: true
create_table :categories, force: true do |t|
@@ -303,7 +306,7 @@ ActiveRecord::Schema.define do
end
create_table :engines, force: true do |t|
- t.integer :car_id
+ t.bigint :car_id
end
create_table :entrants, force: true do |t|
@@ -436,10 +439,12 @@ ActiveRecord::Schema.define do
end
create_table :lock_without_defaults, force: true do |t|
+ t.column :title, :string
t.column :lock_version, :integer
end
create_table :lock_without_defaults_cust, force: true do |t|
+ t.column :title, :string
t.column :custom_lock_version, :integer
end
@@ -903,7 +908,6 @@ ActiveRecord::Schema.define do
create_table(t, force: true) {}
end
- # NOTE - the following 4 tables are used by models that have :inverse_of options on the associations
create_table :men, force: true do |t|
t.string :name
end
@@ -927,14 +931,14 @@ ActiveRecord::Schema.define do
t.integer :zine_id
end
- create_table :wheels, force: true do |t|
- t.references :wheelable, polymorphic: true
- end
-
create_table :zines, force: true do |t|
t.string :title
end
+ create_table :wheels, force: true do |t|
+ t.references :wheelable, polymorphic: true
+ end
+
create_table :countries, force: true, id: false, primary_key: "country_id" do |t|
t.string :country_id
t.string :name
@@ -1003,7 +1007,7 @@ ActiveRecord::Schema.define do
if supports_foreign_keys?
# fk_test_has_fk should be before fk_test_has_pk
create_table :fk_test_has_fk, force: true do |t|
- t.integer :fk_id, null: false
+ t.bigint :fk_id, null: false
end
create_table :fk_test_has_pk, force: true, primary_key: "pk_id" do |t|
@@ -1046,3 +1050,5 @@ Professor.connection.create_table :courses_professors, id: false, force: true do
t.references :course
t.references :professor
end
+
+OtherDog.connection.create_table :dogs, force: true
diff --git a/activerecord/test/support/config.rb b/activerecord/test/support/config.rb
index d0717f7b34..aaff408b41 100644
--- a/activerecord/test/support/config.rb
+++ b/activerecord/test/support/config.rb
@@ -1,5 +1,5 @@
require "yaml"
-require "erubis"
+require "erb"
require "fileutils"
require "pathname"
@@ -20,13 +20,14 @@ module ARTest
FileUtils.cp TEST_ROOT + "/config.example.yml", config_file
end
- erb = Erubis::Eruby.new(config_file.read)
+ erb = ERB.new(config_file.read)
expand_config(YAML.parse(erb.result(binding)).transform)
end
def expand_config(config)
config["connections"].each do |adapter, connection|
- dbs = [["arunit", "activerecord_unittest"], ["arunit2", "activerecord_unittest2"]]
+ dbs = [["arunit", "activerecord_unittest"], ["arunit2", "activerecord_unittest2"],
+ ["arunit_without_prepared_statements", "activerecord_unittest"]]
dbs.each do |name, dbname|
unless connection[name].is_a?(Hash)
connection[name] = { "database" => connection[name] }
diff --git a/activerecord/test/support/connection.rb b/activerecord/test/support/connection.rb
index c9260398e2..bc5af36a28 100644
--- a/activerecord/test/support/connection.rb
+++ b/activerecord/test/support/connection.rb
@@ -2,6 +2,7 @@ require "active_support/logger"
require "models/college"
require "models/course"
require "models/professor"
+require "models/other_dog"
module ARTest
def self.connection_name
diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md
index f840783059..873a39dbf6 100644
--- a/activesupport/CHANGELOG.md
+++ b/activesupport/CHANGELOG.md
@@ -1,3 +1,121 @@
+* Change return value of `Rational#duplicable?`, `ComplexClass#duplicable?`
+ to false.
+
+ *utilum*
+
+* Change return value of `NilClass#duplicable?`, `FalseClass#duplicable?`,
+ `TrueClass#duplicable?`, `Symbol#duplicable?` and `Numeric#duplicable?`
+ to true with Ruby 2.4+. These classes can dup with Ruby 2.4+.
+
+ *Yuji Yaginuma*
+
+* Remove deprecated class `ActiveSupport::Concurrency::Latch`
+
+ *Andrew White*
+
+* Remove deprecated separator argument from `parameterize`
+
+ *Andrew White*
+
+* Remove deprecated method `Numeric#to_formatted_s`
+
+ *Andrew White*
+
+* Remove deprecated method `alias_method_chain`
+
+ *Andrew White*
+
+* Remove deprecated constant `MissingSourceFile`
+
+ *Andrew White*
+
+* Remove deprecated methods `Module.qualified_const_defined?`,
+ `Module.qualified_const_get` and `Module.qualified_const_set`
+
+ *Andrew White*
+
+* Remove deprecated `:prefix` option from `number_to_human_size`
+
+ *Andrew White*
+
+* Remove deprecated method `ActiveSupport::HashWithIndifferentAccess.new_from_hash_copying_default`
+
+ *Andrew White*
+
+* Remove deprecated file `active_support/core_ext/time/marshal.rb`
+
+ *Andrew White*
+
+* Remove deprecated file `active_support/core_ext/struct.rb`
+
+ *Andrew White*
+
+* Remove deprecated file `active_support/core_ext/module/method_transplanting.rb`
+
+ *Andrew White*
+
+* Remove deprecated method `Module.local_constants`
+
+ *Andrew White*
+
+* Remove deprecated file `active_support/core_ext/kernel/debugger.rb`
+
+ *Andrew White*
+
+* Remove deprecated method `ActiveSupport::Cache::Store#namespaced_key`
+
+ *Andrew White*
+
+* Remove deprecated method `ActiveSupport::Cache::Strategy::LocalCache::LocalStore#set_cache_value`
+
+ *Andrew White*
+
+* Remove deprecated method `ActiveSupport::Cache::MemCacheStore#escape_key`
+
+ *Andrew White*
+
+* Remove deprecated method `ActiveSupport::Cache::FileStore#key_file_path`
+
+ *Andrew White*
+
+* Ensure duration parsing is consistent across DST changes
+
+ Previously `ActiveSupport::Duration.parse` used `Time.current` and
+ `Time#advance` to calculate the number of seconds in the duration
+ from an arbitrary collection of parts. However as `advance` tries to
+ be consistent across DST boundaries this meant that either the
+ duration was shorter or longer depending on the time of year.
+
+ This was fixed by using an absolute reference point in UTC which
+ isn't subject to DST transitions. An arbitrary date of Jan 1st, 2000
+ was chosen for no other reason that it seemed appropriate.
+
+ Additionally, duration parsing should now be marginally faster as we
+ are no longer creating instances of `ActiveSupport::TimeWithZone`
+ every time we parse a duration string.
+
+ Fixes #26941.
+
+ *Andrew White*
+
+* Use `Hash#compact` and `Hash#compact!` from Ruby 2.4. Old Ruby versions
+ will continue to get these methods from Active Support as before.
+
+ *Prathamesh Sonpatki*
+
+* Fix `ActiveSupport::TimeZone#strptime`.
+ Support for timestamps in format of seconds (%s) and milliseconds (%Q).
+
+ Fixes #26840.
+
+ *Lev Denisov*
+
+* Fix `DateAndTime::Calculations#copy_time_to`. Copy `nsec` instead of `usec`.
+
+ Jumping forward or backward between weeks now preserves nanosecond digits.
+
+ *Josua Schmid*
+
* Fix `ActiveSupport::TimeWithZone#in` across DST boundaries.
Previously calls to `in` were being sent to the non-DST aware
@@ -8,10 +126,10 @@
Time.zone = "US/Eastern"
t = Time.zone.local(2016,11,6,1)
- # => Sun, 06 Nov 2016 01:00:00 EDT -05:00
+ # => Sun, 06 Nov 2016 01:00:00 EDT -05:00
t.in(1.hour)
- # => Sun, 06 Nov 2016 01:00:00 EST -05:00
+ # => Sun, 06 Nov 2016 01:00:00 EST -05:00
Fixes #26580.
@@ -107,12 +225,6 @@
*Genadi Samokovarov*
-* Add `:fallback_string` option to `Array#to_sentence`. If an empty array
- calls the function and a fallback string option is set then it returns the
- fallback string other than an empty string.
-
- *Mohamed Osama*
-
* Fix `ActiveSupport::TimeZone#strptime`. Now raises `ArgumentError` when the
given time doesn't match the format. The error is the same as the one given
by Ruby's `Date.strptime`. Previously it raised
diff --git a/activesupport/bin/generate_tables b/activesupport/bin/generate_tables
index ef220e0329..5d912f375c 100755
--- a/activesupport/bin/generate_tables
+++ b/activesupport/bin/generate_tables
@@ -51,9 +51,9 @@ module ActiveSupport
codepoint.code = $1.hex
codepoint.combining_class = Integer($4)
codepoint.decomp_type = $7
- codepoint.decomp_mapping = ($8=="") ? nil : $8.split.collect(&:hex)
- codepoint.uppercase_mapping = ($16=="") ? 0 : $16.hex
- codepoint.lowercase_mapping = ($17=="") ? 0 : $17.hex
+ codepoint.decomp_mapping = ($8 == "") ? nil : $8.split.collect(&:hex)
+ codepoint.uppercase_mapping = ($16 == "") ? 0 : $16.hex
+ codepoint.lowercase_mapping = ($17 == "") ? 0 : $17.hex
@ucd.codepoints[codepoint.code] = codepoint
end
@@ -92,7 +92,7 @@ module ActiveSupport
end
def normalize_boundary_map
- @ucd.boundary.each do |k,v|
+ @ucd.boundary.each do |k, v|
if [:lf, :cr].include? k
@ucd.boundary[k] = v[0]
end
@@ -101,7 +101,7 @@ module ActiveSupport
def parse
SOURCES.each do |type, url|
- filename = File.join(Dir.tmpdir, "#{url.split('/').last}")
+ filename = File.join(Dir.tmpdir, "#{url.split('/').last}")
unless File.exist?(filename)
$stderr.puts "Downloading #{url.split('/').last}"
File.open(filename, "wb") do |target|
diff --git a/activesupport/bin/test b/activesupport/bin/test
index 84a05bba08..a7beb14b27 100755
--- a/activesupport/bin/test
+++ b/activesupport/bin/test
@@ -2,5 +2,3 @@
COMPONENT_ROOT = File.expand_path("..", __dir__)
require File.expand_path("../tools/test", COMPONENT_ROOT)
-
-exit Minitest.run(ARGV)
diff --git a/activesupport/lib/active_support/cache.rb b/activesupport/lib/active_support/cache.rb
index ad02546755..d1bbd2f405 100644
--- a/activesupport/lib/active_support/cache.rb
+++ b/activesupport/lib/active_support/cache.rb
@@ -471,12 +471,12 @@ module ActiveSupport
raise NotImplementedError.new("#{self.class.name} does not support clear")
end
- protected
+ private
# Adds the namespace defined in the options to a pattern designed to
# match keys. Implementations that support delete_matched should call
# this method to translate a pattern that matches names into one that
# matches namespaced keys.
- def key_matcher(pattern, options)
+ def key_matcher(pattern, options) # :doc:
prefix = options[:namespace].is_a?(Proc) ? options[:namespace].call : options[:namespace]
if prefix
source = pattern.source
@@ -493,25 +493,24 @@ module ActiveSupport
# Reads an entry from the cache implementation. Subclasses must implement
# this method.
- def read_entry(key, options) # :nodoc:
+ def read_entry(key, options)
raise NotImplementedError.new
end
# Writes an entry to the cache implementation. Subclasses must implement
# this method.
- def write_entry(key, entry, options) # :nodoc:
+ def write_entry(key, entry, options)
raise NotImplementedError.new
end
# Deletes an entry from the cache implementation. Subclasses must
# implement this method.
- def delete_entry(key, options) # :nodoc:
+ def delete_entry(key, options)
raise NotImplementedError.new
end
- private
# Merges the default options with ones specific to a method call.
- def merged_options(call_options) # :nodoc:
+ def merged_options(call_options)
if call_options
options.merge(call_options)
else
@@ -522,7 +521,7 @@ module ActiveSupport
# Expands key to be a consistent string value. Invokes +cache_key+ if
# object responds to +cache_key+. Otherwise, +to_param+ method will be
# called. If the key is a Hash, then keys will be sorted alphabetically.
- def expanded_key(key) # :nodoc:
+ def expanded_key(key)
return key.cache_key.to_s if key.respond_to?(:cache_key)
case key
@@ -533,7 +532,7 @@ module ActiveSupport
key = key.first
end
when Hash
- key = key.sort_by { |k,_| k.to_s }.collect { |k,v| "#{k}=#{v}" }
+ key = key.sort_by { |k, _| k.to_s }.collect { |k, v| "#{k}=#{v}" }
end
key.to_param
@@ -549,14 +548,6 @@ module ActiveSupport
key
end
- def namespaced_key(*args)
- ActiveSupport::Deprecation.warn(<<-MESSAGE.strip_heredoc)
- `namespaced_key` is deprecated and will be removed from Rails 5.1.
- Please use `normalize_key` which will return a fully resolved key.
- MESSAGE
- normalize_key(*args)
- end
-
def instrument(operation, key, options = nil)
log { "Cache #{operation}: #{normalize_key(key, options)}#{options.blank? ? "" : " (#{options.inspect})"}" }
diff --git a/activesupport/lib/active_support/cache/file_store.rb b/activesupport/lib/active_support/cache/file_store.rb
index 1971ff182e..d5c8585816 100644
--- a/activesupport/lib/active_support/cache/file_store.rb
+++ b/activesupport/lib/active_support/cache/file_store.rb
@@ -66,7 +66,7 @@ module ActiveSupport
end
end
- protected
+ private
def read_entry(key, options)
if File.exist?(key)
@@ -98,9 +98,8 @@ module ActiveSupport
end
end
- private
# Lock a file for a block so only one process can modify it at a time.
- def lock_file(file_name, &block) # :nodoc:
+ def lock_file(file_name, &block)
if File.exist?(file_name)
File.open(file_name, "r+") do |f|
begin
@@ -138,14 +137,6 @@ module ActiveSupport
File.join(cache_path, DIR_FORMATTER % dir_1, DIR_FORMATTER % dir_2, *fname_paths)
end
- def key_file_path(key)
- ActiveSupport::Deprecation.warn(<<-MESSAGE.strip_heredoc)
- `key_file_path` is deprecated and will be removed from Rails 5.1.
- Please use `normalize_key` which will return a fully resolved key or nothing.
- MESSAGE
- key
- end
-
# Translate a file path into a key.
def file_path_key(path)
fname = path[cache_path.to_s.size..-1].split(File::SEPARATOR, 4).last
diff --git a/activesupport/lib/active_support/cache/mem_cache_store.rb b/activesupport/lib/active_support/cache/mem_cache_store.rb
index cfd5e39bc4..e09cee3335 100644
--- a/activesupport/lib/active_support/cache/mem_cache_store.rb
+++ b/activesupport/lib/active_support/cache/mem_cache_store.rb
@@ -26,7 +26,7 @@ module ActiveSupport
class MemCacheStore < Store
# Provide support for raw values in the local cache strategy.
module LocalCacheWithRaw # :nodoc:
- protected
+ private
def read_entry(key, options)
entry = super
if options[:raw] && local_cache && entry
@@ -35,7 +35,7 @@ module ActiveSupport
entry
end
- def write_entry(key, entry, options) # :nodoc:
+ def write_entry(key, entry, options)
if options[:raw] && local_cache
raw_entry = Entry.new(entry.value.to_s)
raw_entry.expires_at = entry.expires_at
@@ -97,7 +97,7 @@ module ActiveSupport
options = merged_options(options)
keys_to_names = Hash[names.map { |name| [normalize_key(name, options), name] }]
- raw_values = @data.get_multi(keys_to_names.keys, raw: true)
+ raw_values = @data.get_multi(keys_to_names.keys)
values = {}
raw_values.each do |key, value|
entry = deserialize_entry(value)
@@ -110,7 +110,7 @@ module ActiveSupport
# operator and can only be used on values written with the :raw option.
# Calling it on a value not stored with :raw will initialize that value
# to zero.
- def increment(name, amount = 1, options = nil) # :nodoc:
+ def increment(name, amount = 1, options = nil)
options = merged_options(options)
instrument(:increment, name, amount: amount) do
rescue_error_with nil do
@@ -123,7 +123,7 @@ module ActiveSupport
# operator and can only be used on values written with the :raw option.
# Calling it on a value not stored with :raw will initialize that value
# to zero.
- def decrement(name, amount = 1, options = nil) # :nodoc:
+ def decrement(name, amount = 1, options = nil)
options = merged_options(options)
instrument(:decrement, name, amount: amount) do
rescue_error_with nil do
@@ -143,14 +143,14 @@ module ActiveSupport
@data.stats
end
- protected
+ private
# Read an entry from the cache.
- def read_entry(key, options) # :nodoc:
+ def read_entry(key, options)
rescue_error_with(nil) { deserialize_entry(@data.get(key, options)) }
end
# Write an entry to the cache.
- def write_entry(key, entry, options) # :nodoc:
+ def write_entry(key, entry, options)
method = options && options[:unless_exist] ? :add : :set
value = options[:raw] ? entry.value.to_s : entry
expires_in = options[:expires_in].to_i
@@ -164,12 +164,10 @@ module ActiveSupport
end
# Delete an entry from the cache.
- def delete_entry(key, options) # :nodoc:
+ def delete_entry(key, options)
rescue_error_with(false) { @data.delete(key) }
end
- private
-
# Memcache keys are binaries. So we need to force their encoding to binary
# before applying the regular expression to ensure we are escaping all
# characters properly.
@@ -181,14 +179,6 @@ module ActiveSupport
key
end
- def escape_key(key)
- ActiveSupport::Deprecation.warn(<<-MESSAGE.strip_heredoc)
- `escape_key` is deprecated and will be removed from Rails 5.1.
- Please use `normalize_key` which will return a fully resolved key or nothing.
- MESSAGE
- key
- end
-
def deserialize_entry(raw_value)
if raw_value
entry = Marshal.load(raw_value) rescue raw_value
diff --git a/activesupport/lib/active_support/cache/memory_store.rb b/activesupport/lib/active_support/cache/memory_store.rb
index 1a8477f9fe..fea072d91c 100644
--- a/activesupport/lib/active_support/cache/memory_store.rb
+++ b/activesupport/lib/active_support/cache/memory_store.rb
@@ -57,7 +57,7 @@ module ActiveSupport
start_time = Time.now
cleanup
instrument(:prune, target_size, from: @cache_size) do
- keys = synchronize { @key_access.keys.sort { |a,b| @key_access[a].to_f <=> @key_access[b].to_f } }
+ keys = synchronize { @key_access.keys.sort { |a, b| @key_access[a].to_f <=> @key_access[b].to_f } }
keys.each do |key|
delete_entry(key, options)
return if @cache_size <= target_size || (max_time && Time.now - start_time > max_time)
@@ -104,15 +104,15 @@ module ActiveSupport
@monitor.synchronize(&block)
end
- protected
+ private
PER_ENTRY_OVERHEAD = 240
- def cached_size(key, entry) # :nodoc:
+ def cached_size(key, entry)
key.to_s.bytesize + entry.size + PER_ENTRY_OVERHEAD
end
- def read_entry(key, options) # :nodoc:
+ def read_entry(key, options)
entry = @data[key]
synchronize do
if entry
@@ -124,7 +124,7 @@ module ActiveSupport
entry
end
- def write_entry(key, entry, options) # :nodoc:
+ def write_entry(key, entry, options)
entry.dup_value!
synchronize do
old_entry = @data[key]
@@ -141,7 +141,7 @@ module ActiveSupport
end
end
- def delete_entry(key, options) # :nodoc:
+ def delete_entry(key, options)
synchronize do
@key_access.delete(key)
entry = @data.delete(key)
@@ -150,8 +150,6 @@ module ActiveSupport
end
end
- private
-
def modify_value(name, amount, options)
synchronize do
options = merged_options(options)
diff --git a/activesupport/lib/active_support/cache/null_store.rb b/activesupport/lib/active_support/cache/null_store.rb
index 0564ce5312..550659fc56 100644
--- a/activesupport/lib/active_support/cache/null_store.rb
+++ b/activesupport/lib/active_support/cache/null_store.rb
@@ -25,15 +25,15 @@ module ActiveSupport
def delete_matched(matcher, options = nil)
end
- protected
- def read_entry(key, options) # :nodoc:
+ private
+ def read_entry(key, options)
end
- def write_entry(key, entry, options) # :nodoc:
+ def write_entry(key, entry, options)
true
end
- def delete_entry(key, options) # :nodoc:
+ def delete_entry(key, options)
false
end
end
diff --git a/activesupport/lib/active_support/cache/strategy/local_cache.rb b/activesupport/lib/active_support/cache/strategy/local_cache.rb
index ec2e96a106..672eb2bb80 100644
--- a/activesupport/lib/active_support/cache/strategy/local_cache.rb
+++ b/activesupport/lib/active_support/cache/strategy/local_cache.rb
@@ -70,6 +70,7 @@ module ActiveSupport
def with_local_cache
use_temporary_local_cache(LocalStore.new) { yield }
end
+
# Middleware class can be inserted as a Rack handler to be local cache for the
# duration of request.
def middleware
@@ -104,8 +105,8 @@ module ActiveSupport
value
end
- protected
- def read_entry(key, options) # :nodoc:
+ private
+ def read_entry(key, options)
if cache = local_cache
cache.fetch_entry(key) { super }
else
@@ -113,25 +114,17 @@ module ActiveSupport
end
end
- def write_entry(key, entry, options) # :nodoc:
+ def write_entry(key, entry, options)
local_cache.write_entry(key, entry, options) if local_cache
super
end
- def delete_entry(key, options) # :nodoc:
+ def delete_entry(key, options)
local_cache.delete_entry(key, options) if local_cache
super
end
- def set_cache_value(value, name, amount, options) # :nodoc:
- ActiveSupport::Deprecation.warn(<<-MESSAGE.strip_heredoc)
- `set_cache_value` is deprecated and will be removed from Rails 5.1.
- Please use `write_cache_value` instead.
- MESSAGE
- write_cache_value name, value, options
- end
-
- def write_cache_value(name, value, options) # :nodoc:
+ def write_cache_value(name, value, options)
name = normalize_key(name, options)
cache = local_cache
cache.mute do
@@ -143,8 +136,6 @@ module ActiveSupport
end
end
- private
-
def local_cache_key
@local_cache_key ||= "#{self.class.name.underscore}_local_cache_#{object_id}".gsub(/[\/-]/, "_").to_sym
end
diff --git a/activesupport/lib/active_support/cache/strategy/local_cache_middleware.rb b/activesupport/lib/active_support/cache/strategy/local_cache_middleware.rb
index af51f66dda..174cb72b1e 100644
--- a/activesupport/lib/active_support/cache/strategy/local_cache_middleware.rb
+++ b/activesupport/lib/active_support/cache/strategy/local_cache_middleware.rb
@@ -12,9 +12,9 @@ module ActiveSupport
attr_reader :name, :local_cache_key
def initialize(name, local_cache_key)
- @name = name
+ @name = name
@local_cache_key = local_cache_key
- @app = nil
+ @app = nil
end
def new(app)
diff --git a/activesupport/lib/active_support/callbacks.rb b/activesupport/lib/active_support/callbacks.rb
index 890b1cd73b..e6c79f2a38 100644
--- a/activesupport/lib/active_support/callbacks.rb
+++ b/activesupport/lib/active_support/callbacks.rb
@@ -109,16 +109,22 @@ module ActiveSupport
invoke_sequence = Proc.new do
skipped = nil
while true
- current, next_sequence = next_sequence, next_sequence.nested
+ current = next_sequence
current.invoke_before(env)
if current.final?
env.value = !env.halted && (!block_given? || yield)
elsif current.skip?(env)
(skipped ||= []) << current
+ next_sequence = next_sequence.nested
next
else
- expanded = current.expand_call_template(env, invoke_sequence)
- expanded.shift.send(*expanded, &expanded.shift)
+ next_sequence = next_sequence.nested
+ begin
+ target, block, method, *arguments = current.expand_call_template(env, invoke_sequence)
+ target.send(method, *arguments, &block)
+ ensure
+ next_sequence = current
+ end
end
current.invoke_after(env)
skipped.pop.invoke_after(env) while skipped && skipped.first
@@ -293,7 +299,7 @@ module ActiveSupport
attr_reader :chain_config
def initialize(name, filter, kind, options, chain_config)
- @chain_config = chain_config
+ @chain_config = chain_config
@name = name
@kind = kind
@filter = filter
@@ -410,8 +416,8 @@ module ActiveSupport
# values.
def make_lambda
lambda do |target, value, &block|
- c = expand(target, value, block)
- c.shift.send(*c, &c.shift)
+ target, block, method, *arguments = expand(target, value, block)
+ target.send(method, *arguments, &block)
end
end
@@ -419,8 +425,8 @@ module ActiveSupport
# values, but then return the boolean inverse of that result.
def inverted_lambda
lambda do |target, value, &block|
- c = expand(target, value, block)
- ! c.shift.send(*c, &c.shift)
+ target, block, method, *arguments = expand(target, value, block)
+ ! target.send(method, *arguments, &block)
end
end
diff --git a/activesupport/lib/active_support/concurrency/latch.rb b/activesupport/lib/active_support/concurrency/latch.rb
deleted file mode 100644
index 53e09b685c..0000000000
--- a/activesupport/lib/active_support/concurrency/latch.rb
+++ /dev/null
@@ -1,25 +0,0 @@
-require "concurrent/atomic/count_down_latch"
-
-module ActiveSupport
- module Concurrency
- class Latch
- def initialize(count = 1)
- if count == 1
- ActiveSupport::Deprecation.warn("ActiveSupport::Concurrency::Latch is deprecated. Please use Concurrent::Event instead.")
- else
- ActiveSupport::Deprecation.warn("ActiveSupport::Concurrency::Latch is deprecated. Please use Concurrent::CountDownLatch instead.")
- end
-
- @inner = Concurrent::CountDownLatch.new(count)
- end
-
- def release
- @inner.count_down
- end
-
- def await
- @inner.wait(nil)
- end
- end
- end
-end
diff --git a/activesupport/lib/active_support/core_ext/array/conversions.rb b/activesupport/lib/active_support/core_ext/array/conversions.rb
index 1d4c83ae52..cac15e1100 100644
--- a/activesupport/lib/active_support/core_ext/array/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/array/conversions.rb
@@ -40,12 +40,6 @@ class Array
# ['one', 'two', 'three'].to_sentence(words_connector: ' or ', last_word_connector: ' or at least ')
# # => "one or two or at least three"
#
- # [].to_sentence(fallback_string: 'none')
- # # => "none"
- #
- # ['one', 'two'].to_sentence(fallback_string: 'none')
- # # => "one and two"
- #
# Using <tt>:locale</tt> option:
#
# # Given this locale dictionary:
@@ -63,7 +57,7 @@ class Array
# ['uno', 'dos', 'tres'].to_sentence(locale: :es)
# # => "uno o dos o al menos tres"
def to_sentence(options = {})
- options.assert_valid_keys(:words_connector, :two_words_connector, :last_word_connector, :locale, :fallback_string)
+ options.assert_valid_keys(:words_connector, :two_words_connector, :last_word_connector, :locale)
default_connectors = {
words_connector: ", ",
@@ -78,7 +72,7 @@ class Array
case length
when 0
- "#{options[:fallback_string] || ''}"
+ ""
when 1
"#{self[0]}"
when 2
diff --git a/activesupport/lib/active_support/core_ext/class/subclasses.rb b/activesupport/lib/active_support/core_ext/class/subclasses.rb
index 10a7c787f6..62397d9508 100644
--- a/activesupport/lib/active_support/core_ext/class/subclasses.rb
+++ b/activesupport/lib/active_support/core_ext/class/subclasses.rb
@@ -22,6 +22,7 @@ class Class
def descendants
descendants = []
ObjectSpace.each_object(singleton_class) do |k|
+ next if k.singleton_class?
descendants.unshift k unless k == self
end
descendants
diff --git a/activesupport/lib/active_support/core_ext/date_and_time/calculations.rb b/activesupport/lib/active_support/core_ext/date_and_time/calculations.rb
index c614f14289..f2ba7fdda5 100644
--- a/activesupport/lib/active_support/core_ext/date_and_time/calculations.rb
+++ b/activesupport/lib/active_support/core_ext/date_and_time/calculations.rb
@@ -334,7 +334,7 @@ module DateAndTime
end
def copy_time_to(other)
- other.change(hour: hour, min: min, sec: sec, usec: try(:usec))
+ other.change(hour: hour, min: min, sec: sec, nsec: try(:nsec))
end
end
end
diff --git a/activesupport/lib/active_support/core_ext/date_time/conversions.rb b/activesupport/lib/active_support/core_ext/date_time/conversions.rb
index 44ae96dbe8..d9b3743858 100644
--- a/activesupport/lib/active_support/core_ext/date_time/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/date_time/conversions.rb
@@ -64,7 +64,7 @@ class DateTime
# # => Sun, 01 Jan 2012 00:00:00 +0300
# DateTime.civil_from_format :local, 2012, 12, 17
# # => Mon, 17 Dec 2012 00:00:00 +0000
- def self.civil_from_format(utc_or_local, year, month=1, day=1, hour=0, min=0, sec=0)
+ def self.civil_from_format(utc_or_local, year, month = 1, day = 1, hour = 0, min = 0, sec = 0)
if utc_or_local.to_sym == :local
offset = ::Time.local(year, month, day).utc_offset.to_r / 86400
else
diff --git a/activesupport/lib/active_support/core_ext/hash/compact.rb b/activesupport/lib/active_support/core_ext/hash/compact.rb
index 78b3387c3b..e357284be0 100644
--- a/activesupport/lib/active_support/core_ext/hash/compact.rb
+++ b/activesupport/lib/active_support/core_ext/hash/compact.rb
@@ -1,23 +1,27 @@
class Hash
- # Returns a hash with non +nil+ values.
- #
- # hash = { a: true, b: false, c: nil }
- # hash.compact # => { a: true, b: false }
- # hash # => { a: true, b: false, c: nil }
- # { c: nil }.compact # => {}
- # { c: true }.compact # => { c: true }
- def compact
- select { |_, value| !value.nil? }
+ unless Hash.instance_methods(false).include?(:compact)
+ # Returns a hash with non +nil+ values.
+ #
+ # hash = { a: true, b: false, c: nil }
+ # hash.compact # => { a: true, b: false }
+ # hash # => { a: true, b: false, c: nil }
+ # { c: nil }.compact # => {}
+ # { c: true }.compact # => { c: true }
+ def compact
+ select { |_, value| !value.nil? }
+ end
end
- # Replaces current hash with non +nil+ values.
- # Returns nil if no changes were made, otherwise returns the hash.
- #
- # hash = { a: true, b: false, c: nil }
- # hash.compact! # => { a: true, b: false }
- # hash # => { a: true, b: false }
- # { c: true }.compact! # => nil
- def compact!
- reject! { |_, value| value.nil? }
+ unless Hash.instance_methods(false).include?(:compact!)
+ # Replaces current hash with non +nil+ values.
+ # Returns +nil+ if no changes were made, otherwise returns the hash.
+ #
+ # hash = { a: true, b: false, c: nil }
+ # hash.compact! # => { a: true, b: false }
+ # hash # => { a: true, b: false }
+ # { c: true }.compact! # => nil
+ def compact!
+ reject! { |_, value| value.nil? }
+ end
end
end
diff --git a/activesupport/lib/active_support/core_ext/hash/conversions.rb b/activesupport/lib/active_support/core_ext/hash/conversions.rb
index 3cd96ccb9a..2a58a7f1ca 100644
--- a/activesupport/lib/active_support/core_ext/hash/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/hash/conversions.rb
@@ -160,7 +160,7 @@ module ActiveSupport
def normalize_keys(params)
case params
when Hash
- Hash[params.map { |k,v| [k.to_s.tr("-", "_"), normalize_keys(v)] } ]
+ Hash[params.map { |k, v| [k.to_s.tr("-", "_"), normalize_keys(v)] } ]
when Array
params.map { |v| normalize_keys(v) }
else
@@ -187,7 +187,7 @@ module ActiveSupport
end
if become_array?(value)
- _, entries = Array.wrap(value.detect { |k,v| not v.is_a?(String) })
+ _, entries = Array.wrap(value.detect { |k, v| not v.is_a?(String) })
if entries.nil? || value["__content__"].try(:empty?)
[]
else
@@ -206,7 +206,7 @@ module ActiveSupport
elsif become_empty_string?(value)
""
elsif become_hash?(value)
- xml_value = Hash[value.map { |k,v| [k, deep_to_h(v)] }]
+ xml_value = Hash[value.map { |k, v| [k, deep_to_h(v)] }]
# Turn { files: { file: #<StringIO> } } into { files: #<StringIO> } so it is compatible with
# how multipart uploaded files from HTML appear
diff --git a/activesupport/lib/active_support/core_ext/hash/reverse_merge.rb b/activesupport/lib/active_support/core_ext/hash/reverse_merge.rb
index db3e7508e7..efb9d1b8a0 100644
--- a/activesupport/lib/active_support/core_ext/hash/reverse_merge.rb
+++ b/activesupport/lib/active_support/core_ext/hash/reverse_merge.rb
@@ -16,7 +16,7 @@ class Hash
# Destructive +reverse_merge+.
def reverse_merge!(other_hash)
# right wins if there is no left
- merge!( other_hash ) { |key,left,right| left }
+ merge!(other_hash) { |key, left, right| left }
end
alias_method :reverse_update, :reverse_merge!
end
diff --git a/activesupport/lib/active_support/core_ext/hash/transform_values.rb b/activesupport/lib/active_support/core_ext/hash/transform_values.rb
index 7d507ac998..2f693bff0c 100644
--- a/activesupport/lib/active_support/core_ext/hash/transform_values.rb
+++ b/activesupport/lib/active_support/core_ext/hash/transform_values.rb
@@ -16,7 +16,7 @@ class Hash
result[key] = yield(value)
end
result
- end
+ end unless method_defined? :transform_values
# Destructively converts all values using the +block+ operations.
# Same as +transform_values+ but modifies +self+.
@@ -25,5 +25,6 @@ class Hash
each do |key, value|
self[key] = yield(value)
end
- end
+ end unless method_defined? :transform_values!
+ # TODO: Remove this file when supporting only Ruby 2.4+.
end
diff --git a/activesupport/lib/active_support/core_ext/kernel/debugger.rb b/activesupport/lib/active_support/core_ext/kernel/debugger.rb
deleted file mode 100644
index dccfa3e9bb..0000000000
--- a/activesupport/lib/active_support/core_ext/kernel/debugger.rb
+++ /dev/null
@@ -1,3 +0,0 @@
-require "active_support/deprecation"
-
-ActiveSupport::Deprecation.warn("This file is deprecated and will be removed in Rails 5.1 with no replacement.")
diff --git a/activesupport/lib/active_support/core_ext/kernel/reporting.rb b/activesupport/lib/active_support/core_ext/kernel/reporting.rb
index d0197af95f..c02618d5f3 100644
--- a/activesupport/lib/active_support/core_ext/kernel/reporting.rb
+++ b/activesupport/lib/active_support/core_ext/kernel/reporting.rb
@@ -1,7 +1,7 @@
module Kernel
module_function
- # Sets $VERBOSE to nil for the duration of the block and back to its original
+ # Sets $VERBOSE to +nil+ for the duration of the block and back to its original
# value afterwards.
#
# silence_warnings do
diff --git a/activesupport/lib/active_support/core_ext/load_error.rb b/activesupport/lib/active_support/core_ext/load_error.rb
index cd00d1b662..3cf7ea0a97 100644
--- a/activesupport/lib/active_support/core_ext/load_error.rb
+++ b/activesupport/lib/active_support/core_ext/load_error.rb
@@ -27,5 +27,3 @@ class LoadError
location.sub(/\.rb$/, "".freeze) == path.sub(/\.rb$/, "".freeze)
end
end
-
-MissingSourceFile = ActiveSupport::Deprecation::DeprecatedConstantProxy.new("MissingSourceFile", "LoadError")
diff --git a/activesupport/lib/active_support/core_ext/module.rb b/activesupport/lib/active_support/core_ext/module.rb
index 57feea69a5..2930255557 100644
--- a/activesupport/lib/active_support/core_ext/module.rb
+++ b/activesupport/lib/active_support/core_ext/module.rb
@@ -9,4 +9,3 @@ require "active_support/core_ext/module/concerning"
require "active_support/core_ext/module/delegation"
require "active_support/core_ext/module/deprecation"
require "active_support/core_ext/module/remove_method"
-require "active_support/core_ext/module/qualified_const"
diff --git a/activesupport/lib/active_support/core_ext/module/aliasing.rb b/activesupport/lib/active_support/core_ext/module/aliasing.rb
index 4a04bdd446..c48bd3354a 100644
--- a/activesupport/lib/active_support/core_ext/module/aliasing.rb
+++ b/activesupport/lib/active_support/core_ext/module/aliasing.rb
@@ -1,52 +1,4 @@
class Module
- # NOTE: This method is deprecated. Please use <tt>Module#prepend</tt> that
- # comes with Ruby 2.0 or newer instead.
- #
- # Encapsulates the common pattern of:
- #
- # alias_method :foo_without_feature, :foo
- # alias_method :foo, :foo_with_feature
- #
- # With this, you simply do:
- #
- # alias_method_chain :foo, :feature
- #
- # And both aliases are set up for you.
- #
- # Query and bang methods (foo?, foo!) keep the same punctuation:
- #
- # alias_method_chain :foo?, :feature
- #
- # is equivalent to
- #
- # alias_method :foo_without_feature?, :foo?
- # alias_method :foo?, :foo_with_feature?
- #
- # so you can safely chain foo, foo?, foo! and/or foo= with the same feature.
- def alias_method_chain(target, feature)
- ActiveSupport::Deprecation.warn("alias_method_chain is deprecated. Please, use Module#prepend instead. From module, you can access the original method using super.")
-
- # Strip out punctuation on predicates, bang or writer methods since
- # e.g. target?_without_feature is not a valid method name.
- aliased_target, punctuation = target.to_s.sub(/([?!=])$/, ""), $1
- yield(aliased_target, punctuation) if block_given?
-
- with_method = "#{aliased_target}_with_#{feature}#{punctuation}"
- without_method = "#{aliased_target}_without_#{feature}#{punctuation}"
-
- alias_method without_method, target
- alias_method target, with_method
-
- case
- when public_method_defined?(without_method)
- public target
- when protected_method_defined?(without_method)
- protected target
- when private_method_defined?(without_method)
- private target
- end
- end
-
# Allows you to make aliases for attributes, which includes
# getter, setter, and a predicate.
#
diff --git a/activesupport/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb b/activesupport/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb
index b1e6fe71e0..1e82b4acc2 100644
--- a/activesupport/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb
+++ b/activesupport/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb
@@ -34,7 +34,7 @@ class Module
# end
#
# Current.new.user # => NoMethodError
- def thread_mattr_reader(*syms)
+ def thread_mattr_reader(*syms) # :nodoc:
options = syms.extract_options!
syms.each do |sym|
@@ -77,7 +77,7 @@ class Module
# end
#
# Current.new.user = "DHH" # => NoMethodError
- def thread_mattr_writer(*syms)
+ def thread_mattr_writer(*syms) # :nodoc:
options = syms.extract_options!
syms.each do |sym|
raise NameError.new("invalid attribute name: #{sym}") unless /^[_A-Za-z]\w*$/.match?(sym)
diff --git a/activesupport/lib/active_support/core_ext/module/delegation.rb b/activesupport/lib/active_support/core_ext/module/delegation.rb
index f5f4ba61b7..19f692e943 100644
--- a/activesupport/lib/active_support/core_ext/module/delegation.rb
+++ b/activesupport/lib/active_support/core_ext/module/delegation.rb
@@ -6,11 +6,12 @@ class Module
# option is not used.
class DelegationError < NoMethodError; end
+ RUBY_RESERVED_KEYWORDS = %w(alias and BEGIN begin break case class def defined? do
+ else elsif END end ensure false for if in module next nil not or redo rescue retry
+ return self super then true undef unless until when while yield)
+ DELEGATION_RESERVED_KEYWORDS = %w(_ arg args block)
DELEGATION_RESERVED_METHOD_NAMES = Set.new(
- %w(_ arg args alias and BEGIN begin block break case class def defined? do
- else elsif END end ensure false for if in module next nil not or redo
- rescue retry return self super then true undef unless until when while
- yield)
+ RUBY_RESERVED_KEYWORDS + DELEGATION_RESERVED_KEYWORDS
).freeze
# Provides a +delegate+ class method to easily expose contained objects'
diff --git a/activesupport/lib/active_support/core_ext/module/introspection.rb b/activesupport/lib/active_support/core_ext/module/introspection.rb
index 4f854a718b..0665aa88bc 100644
--- a/activesupport/lib/active_support/core_ext/module/introspection.rb
+++ b/activesupport/lib/active_support/core_ext/module/introspection.rb
@@ -55,12 +55,4 @@ class Module
parents << Object unless parents.include? Object
parents
end
-
- def local_constants #:nodoc:
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
- Module#local_constants is deprecated and will be removed in Rails 5.1.
- Use Module#constants(false) instead.
- MSG
- constants(false)
- end
end
diff --git a/activesupport/lib/active_support/core_ext/module/method_transplanting.rb b/activesupport/lib/active_support/core_ext/module/method_transplanting.rb
deleted file mode 100644
index dccfa3e9bb..0000000000
--- a/activesupport/lib/active_support/core_ext/module/method_transplanting.rb
+++ /dev/null
@@ -1,3 +0,0 @@
-require "active_support/deprecation"
-
-ActiveSupport::Deprecation.warn("This file is deprecated and will be removed in Rails 5.1 with no replacement.")
diff --git a/activesupport/lib/active_support/core_ext/module/qualified_const.rb b/activesupport/lib/active_support/core_ext/module/qualified_const.rb
deleted file mode 100644
index 62f0687ae9..0000000000
--- a/activesupport/lib/active_support/core_ext/module/qualified_const.rb
+++ /dev/null
@@ -1,70 +0,0 @@
-require "active_support/core_ext/string/inflections"
-
-#--
-# Allows code reuse in the methods below without polluting Module.
-#++
-
-module ActiveSupport
- module QualifiedConstUtils
- def self.raise_if_absolute(path)
- raise NameError.new("wrong constant name #$&") if path =~ /\A::[^:]+/
- end
-
- def self.names(path)
- path.split("::")
- end
- end
-end
-
-##
-# Extends the API for constants to be able to deal with qualified names. Arguments
-# are assumed to be relative to the receiver.
-#
-#--
-# Qualified names are required to be relative because we are extending existing
-# methods that expect constant names, ie, relative paths of length 1. For example,
-# Object.const_get('::String') raises NameError and so does qualified_const_get.
-#++
-class Module
- def qualified_const_defined?(path, search_parents=true)
- ActiveSupport::Deprecation.warn(<<-MESSAGE.squish)
- Module#qualified_const_defined? is deprecated in favour of the builtin
- Module#const_defined? and will be removed in Rails 5.1.
- MESSAGE
-
- ActiveSupport::QualifiedConstUtils.raise_if_absolute(path)
-
- ActiveSupport::QualifiedConstUtils.names(path).inject(self) do |mod, name|
- return unless mod.const_defined?(name, search_parents)
- mod.const_get(name)
- end
- return true
- end
-
- def qualified_const_get(path)
- ActiveSupport::Deprecation.warn(<<-MESSAGE.squish)
- Module#qualified_const_get is deprecated in favour of the builtin
- Module#const_get and will be removed in Rails 5.1.
- MESSAGE
-
- ActiveSupport::QualifiedConstUtils.raise_if_absolute(path)
-
- ActiveSupport::QualifiedConstUtils.names(path).inject(self) do |mod, name|
- mod.const_get(name)
- end
- end
-
- def qualified_const_set(path, value)
- ActiveSupport::Deprecation.warn(<<-MESSAGE.squish)
- Module#qualified_const_set is deprecated in favour of the builtin
- Module#const_set and will be removed in Rails 5.1.
- MESSAGE
-
- ActiveSupport::QualifiedConstUtils.raise_if_absolute(path)
-
- const_name = path.demodulize
- mod_name = path.deconstantize
- mod = mod_name.empty? ? self : const_get(mod_name)
- mod.const_set(const_name, value)
- end
-end
diff --git a/activesupport/lib/active_support/core_ext/numeric/conversions.rb b/activesupport/lib/active_support/core_ext/numeric/conversions.rb
index cebfda8d31..946f8ddeab 100644
--- a/activesupport/lib/active_support/core_ext/numeric/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/numeric/conversions.rb
@@ -126,11 +126,6 @@ module ActiveSupport::NumericWithFormat
end
end
end
-
- def to_formatted_s(*args)
- to_s(*args)
- end
- deprecate to_formatted_s: :to_s
end
# Ruby 2.4+ unifies Fixnum & Bignum into Integer.
diff --git a/activesupport/lib/active_support/core_ext/object/duplicable.rb b/activesupport/lib/active_support/core_ext/object/duplicable.rb
index aa2282cb7e..c671d34673 100644
--- a/activesupport/lib/active_support/core_ext/object/duplicable.rb
+++ b/activesupport/lib/active_support/core_ext/object/duplicable.rb
@@ -1,7 +1,7 @@
#--
-# Most objects are cloneable, but not all. For example you can't dup +nil+:
+# Most objects are cloneable, but not all. For example you can't dup methods:
#
-# nil.dup # => TypeError: can't dup NilClass
+# method(:puts).dup # => TypeError: allocator undefined for Method
#
# Classes may signal their instances are not duplicable removing +dup+/+clone+
# or raising exceptions from them. So, to dup an arbitrary object you normally
@@ -19,7 +19,7 @@
class Object
# Can you safely dup this object?
#
- # False for +nil+, +false+, +true+, symbol, number, method objects;
+ # False for method objects;
# true otherwise.
def duplicable?
true
@@ -27,52 +27,77 @@ class Object
end
class NilClass
- # +nil+ is not duplicable:
- #
- # nil.duplicable? # => false
- # nil.dup # => TypeError: can't dup NilClass
- def duplicable?
- false
+ begin
+ nil.dup
+ rescue TypeError
+
+ # +nil+ is not duplicable:
+ #
+ # nil.duplicable? # => false
+ # nil.dup # => TypeError: can't dup NilClass
+ def duplicable?
+ false
+ end
end
end
class FalseClass
- # +false+ is not duplicable:
- #
- # false.duplicable? # => false
- # false.dup # => TypeError: can't dup FalseClass
- def duplicable?
- false
+ begin
+ false.dup
+ rescue TypeError
+
+ # +false+ is not duplicable:
+ #
+ # false.duplicable? # => false
+ # false.dup # => TypeError: can't dup FalseClass
+ def duplicable?
+ false
+ end
end
end
class TrueClass
- # +true+ is not duplicable:
- #
- # true.duplicable? # => false
- # true.dup # => TypeError: can't dup TrueClass
- def duplicable?
- false
+ begin
+ true.dup
+ rescue TypeError
+
+ # +true+ is not duplicable:
+ #
+ # true.duplicable? # => false
+ # true.dup # => TypeError: can't dup TrueClass
+ def duplicable?
+ false
+ end
end
end
class Symbol
- # Symbols are not duplicable:
- #
- # :my_symbol.duplicable? # => false
- # :my_symbol.dup # => TypeError: can't dup Symbol
- def duplicable?
- false
+ begin
+ :symbol.dup
+ rescue TypeError
+
+ # Symbols are not duplicable:
+ #
+ # :my_symbol.duplicable? # => false
+ # :my_symbol.dup # => TypeError: can't dup Symbol
+ def duplicable?
+ false
+ end
end
end
class Numeric
- # Numbers are not duplicable:
- #
- # 3.duplicable? # => false
- # 3.dup # => TypeError: can't dup Integer
- def duplicable?
- false
+ begin
+ 1.dup
+ rescue TypeError
+
+ # Numbers are not duplicable:
+ #
+ # 3.duplicable? # => false
+ # 3.dup # => TypeError: can't dup Integer
+ def duplicable?
+ false
+ end
end
end
@@ -96,3 +121,23 @@ class Method
false
end
end
+
+class Complex
+ # Complexes are not duplicable:
+ #
+ # Complex(1).duplicable? # => false
+ # Complex(1).dup # => TypeError: can't copy Complex
+ def duplicable?
+ false
+ end
+end
+
+class Rational
+ # Rationals are not duplicable:
+ #
+ # Rational(1).duplicable? # => false
+ # Rational(1).dup # => TypeError: can't copy Rational
+ def duplicable?
+ false
+ end
+end
diff --git a/activesupport/lib/active_support/core_ext/regexp.rb b/activesupport/lib/active_support/core_ext/regexp.rb
index 062d568228..d77d01bf42 100644
--- a/activesupport/lib/active_support/core_ext/regexp.rb
+++ b/activesupport/lib/active_support/core_ext/regexp.rb
@@ -3,7 +3,7 @@ class Regexp #:nodoc:
options & MULTILINE == MULTILINE
end
- def match?(string, pos=0)
+ def match?(string, pos = 0)
!!match(string, pos)
end unless //.respond_to?(:match?)
end
diff --git a/activesupport/lib/active_support/core_ext/securerandom.rb b/activesupport/lib/active_support/core_ext/securerandom.rb
index 92392d1a92..a57685bea1 100644
--- a/activesupport/lib/active_support/core_ext/securerandom.rb
+++ b/activesupport/lib/active_support/core_ext/securerandom.rb
@@ -1,12 +1,12 @@
require "securerandom"
module SecureRandom
- BASE58_ALPHABET = ("0".."9").to_a + ("A".."Z").to_a + ("a".."z").to_a - ["0", "O", "I", "l"]
+ BASE58_ALPHABET = ("0".."9").to_a + ("A".."Z").to_a + ("a".."z").to_a - ["0", "O", "I", "l"]
# SecureRandom.base58 generates a random base58 string.
#
# The argument _n_ specifies the length, of the random string to be generated.
#
- # If _n_ is not specified or is nil, 16 is assumed. It may be larger in the future.
+ # If _n_ is not specified or is +nil+, 16 is assumed. It may be larger in the future.
#
# The result may contain alphanumeric characters except 0, O, I and l
#
diff --git a/activesupport/lib/active_support/core_ext/string/access.rb b/activesupport/lib/active_support/core_ext/string/access.rb
index caa48e34c5..6133826f37 100644
--- a/activesupport/lib/active_support/core_ext/string/access.rb
+++ b/activesupport/lib/active_support/core_ext/string/access.rb
@@ -3,7 +3,7 @@ class String
# position. The first character of the string is at position 0, the next at
# position 1, and so on. If a range is supplied, a substring containing
# characters at offsets given by the range is returned. In both cases, if an
- # offset is negative, it is counted from the end of the string. Returns nil
+ # offset is negative, it is counted from the end of the string. Returns +nil+
# if the initial offset falls outside the string. Returns an empty string if
# the beginning of the range is greater than the end of the string.
#
@@ -17,7 +17,7 @@ class String
#
# If a Regexp is given, the matching portion of the string is returned.
# If a String is given, that given string is returned if it occurs in
- # the string. In both cases, nil is returned if there is no match.
+ # the string. In both cases, +nil+ is returned if there is no match.
#
# str = "hello"
# str.at(/lo/) # => "lo"
diff --git a/activesupport/lib/active_support/core_ext/string/indent.rb b/activesupport/lib/active_support/core_ext/string/indent.rb
index e87f72fdbd..d7b58301d3 100644
--- a/activesupport/lib/active_support/core_ext/string/indent.rb
+++ b/activesupport/lib/active_support/core_ext/string/indent.rb
@@ -2,7 +2,7 @@ class String
# Same as +indent+, except it indents the receiver in-place.
#
# Returns the indented string, or +nil+ if there was nothing to indent.
- def indent!(amount, indent_string=nil, indent_empty_lines=false)
+ def indent!(amount, indent_string = nil, indent_empty_lines = false)
indent_string = indent_string || self[/^[ \t]/] || " "
re = indent_empty_lines ? /^/ : /^(?!$)/
gsub!(re, indent_string * amount)
@@ -37,7 +37,7 @@ class String
# "foo\n\nbar".indent(2) # => " foo\n\n bar"
# "foo\n\nbar".indent(2, nil, true) # => " foo\n \n bar"
#
- def indent(amount, indent_string=nil, indent_empty_lines=false)
+ def indent(amount, indent_string = nil, indent_empty_lines = false)
dup.tap { |_| _.indent!(amount, indent_string, indent_empty_lines) }
end
end
diff --git a/activesupport/lib/active_support/core_ext/string/inflections.rb b/activesupport/lib/active_support/core_ext/string/inflections.rb
index 7e12700c8c..4eabce79e2 100644
--- a/activesupport/lib/active_support/core_ext/string/inflections.rb
+++ b/activesupport/lib/active_support/core_ext/string/inflections.rb
@@ -67,7 +67,7 @@ class String
end
# +safe_constantize+ tries to find a declared constant with the name specified
- # in the string. It returns nil when the name is not in CamelCase
+ # in the string. It returns +nil+ when the name is not in CamelCase
# or is not initialized. See ActiveSupport::Inflector.safe_constantize
#
# 'Module'.safe_constantize # => Module
@@ -178,11 +178,7 @@ class String
#
# <%= link_to(@person.name, person_path) %>
# # => <a href="/person/1-Donald-E-Knuth">Donald E. Knuth</a>
- def parameterize(sep = :unused, separator: "-", preserve_case: false)
- unless sep == :unused
- ActiveSupport::Deprecation.warn("Passing the separator argument as a positional parameter is deprecated and will soon be removed. Use `separator: '#{sep}'` instead.")
- separator = sep
- end
+ def parameterize(separator: "-", preserve_case: false)
ActiveSupport::Inflector.parameterize(self, separator: separator, preserve_case: preserve_case)
end
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 2b7e556ced..227c34b032 100644
--- a/activesupport/lib/active_support/core_ext/string/output_safety.rb
+++ b/activesupport/lib/active_support/core_ext/string/output_safety.rb
@@ -202,7 +202,7 @@ module ActiveSupport #:nodoc:
def %(args)
case args
when Hash
- escaped_args = Hash[args.map { |k,arg| [k, html_escape_interpolated_argument(arg)] }]
+ escaped_args = Hash[args.map { |k, arg| [k, html_escape_interpolated_argument(arg)] }]
else
escaped_args = Array(args).map { |arg| html_escape_interpolated_argument(arg) }
end
diff --git a/activesupport/lib/active_support/core_ext/struct.rb b/activesupport/lib/active_support/core_ext/struct.rb
deleted file mode 100644
index dccfa3e9bb..0000000000
--- a/activesupport/lib/active_support/core_ext/struct.rb
+++ /dev/null
@@ -1,3 +0,0 @@
-require "active_support/deprecation"
-
-ActiveSupport::Deprecation.warn("This file is deprecated and will be removed in Rails 5.1 with no replacement.")
diff --git a/activesupport/lib/active_support/core_ext/time/calculations.rb b/activesupport/lib/active_support/core_ext/time/calculations.rb
index 2cbfa14772..cbdcb86d6d 100644
--- a/activesupport/lib/active_support/core_ext/time/calculations.rb
+++ b/activesupport/lib/active_support/core_ext/time/calculations.rb
@@ -104,7 +104,7 @@ class Time
raise ArgumentError, "Can't change both :nsec and :usec at the same time: #{options.inspect}" if options[:usec]
new_usec = Rational(new_nsec, 1000)
else
- new_usec = options.fetch(:usec, (options[:hour] || options[:min] || options[:sec]) ? 0 : Rational(nsec, 1000))
+ new_usec = options.fetch(:usec, (options[:hour] || options[:min] || options[:sec]) ? 0 : Rational(nsec, 1000))
end
if utc?
diff --git a/activesupport/lib/active_support/core_ext/time/marshal.rb b/activesupport/lib/active_support/core_ext/time/marshal.rb
deleted file mode 100644
index d095d76ebb..0000000000
--- a/activesupport/lib/active_support/core_ext/time/marshal.rb
+++ /dev/null
@@ -1,3 +0,0 @@
-require "active_support/deprecation"
-
-ActiveSupport::Deprecation.warn("This is deprecated and will be removed in Rails 5.1 with no replacement.")
diff --git a/activesupport/lib/active_support/dependencies.rb b/activesupport/lib/active_support/dependencies.rb
index 216d52164e..e125b657f2 100644
--- a/activesupport/lib/active_support/dependencies.rb
+++ b/activesupport/lib/active_support/dependencies.rb
@@ -6,7 +6,6 @@ require "active_support/core_ext/module/aliasing"
require "active_support/core_ext/module/attribute_accessors"
require "active_support/core_ext/module/introspection"
require "active_support/core_ext/module/anonymous"
-require "active_support/core_ext/module/qualified_const"
require "active_support/core_ext/object/blank"
require "active_support/core_ext/kernel/reporting"
require "active_support/core_ext/load_error"
@@ -108,7 +107,7 @@ module ActiveSupport #:nodoc:
def initialize
@watching = []
- @stack = Hash.new { |h,k| h[k] = [] }
+ @stack = Hash.new { |h, k| h[k] = [] }
end
def each(&block)
@@ -243,7 +242,7 @@ module ActiveSupport #:nodoc:
# resolution deterministic for constants with the same relative name in
# different namespaces whose evaluation would depend on load order
# otherwise.
- def require_dependency(file_name, message = "No such file to load -- %s")
+ def require_dependency(file_name, message = "No such file to load -- %s.rb")
file_name = file_name.to_path if file_name.respond_to?(:to_path)
unless file_name.is_a?(String)
raise ArgumentError, "the file name must either be a String or implement #to_path -- you passed #{file_name.inspect}"
diff --git a/activesupport/lib/active_support/duration.rb b/activesupport/lib/active_support/duration.rb
index 6ec0c3a70a..c9e8c8fdc4 100644
--- a/activesupport/lib/active_support/duration.rb
+++ b/activesupport/lib/active_support/duration.rb
@@ -7,22 +7,30 @@ module ActiveSupport
#
# 1.month.ago # equivalent to Time.now.advance(months: -1)
class Duration
+ EPOCH = ::Time.utc(2000)
+
attr_accessor :value, :parts
autoload :ISO8601Parser, "active_support/duration/iso8601_parser"
autoload :ISO8601Serializer, "active_support/duration/iso8601_serializer"
def initialize(value, parts) #:nodoc:
- @value, @parts = value, parts
+ @value, @parts = value, parts.to_h
+ @parts.default = 0
end
# Adds another Duration or a Numeric to this Duration. Numeric values
# are treated as seconds.
def +(other)
if Duration === other
- Duration.new(value + other.value, @parts + other.parts)
+ parts = @parts.dup
+ other.parts.each do |(key, value)|
+ parts[key] += value
+ end
+ Duration.new(value + other.value, parts)
else
- Duration.new(value + other, @parts + [[:seconds, other]])
+ seconds = @parts[:seconds] + other
+ Duration.new(value + other, @parts.merge(seconds: seconds))
end
end
@@ -33,7 +41,7 @@ module ActiveSupport
end
def -@ #:nodoc:
- Duration.new(-value, parts.map { |type,number| [type, -number] })
+ Duration.new(-value, parts.map { |type, number| [type, -number] })
end
def is_a?(klass) #:nodoc:
@@ -119,7 +127,7 @@ module ActiveSupport
def inspect #:nodoc:
parts.
- reduce(::Hash.new(0)) { |h,(l,r)| h[l] += r; h }.
+ reduce(::Hash.new(0)) { |h, (l, r)| h[l] += r; h }.
sort_by { |unit, _ | [:years, :months, :weeks, :days, :hours, :minutes, :seconds].index(unit) }.
map { |unit, val| "#{val} #{val == 1 ? unit.to_s.chop : unit.to_s}" }.
to_sentence(locale: ::I18n.default_locale)
@@ -129,7 +137,7 @@ module ActiveSupport
to_i
end
- def respond_to_missing?(method, include_private=false) #:nodoc:
+ def respond_to_missing?(method, include_private = false) #:nodoc:
@value.respond_to?(method, include_private)
end
@@ -140,8 +148,7 @@ module ActiveSupport
# If invalid string is provided, it will raise +ActiveSupport::Duration::ISO8601Parser::ParsingError+.
def self.parse(iso8601duration)
parts = ISO8601Parser.new(iso8601duration).parse!
- time = ::Time.current
- new(time.advance(parts) - time, parts)
+ new(EPOCH.advance(parts) - EPOCH, parts)
end
# Build ISO 8601 Duration string for this duration.
@@ -152,10 +159,10 @@ module ActiveSupport
delegate :<=>, to: :value
- protected
+ private
- def sum(sign, time = ::Time.current) #:nodoc:
- parts.inject(time) do |t,(type,number)|
+ def sum(sign, time = ::Time.current)
+ parts.inject(time) do |t, (type, number)|
if t.acts_like?(:time) || t.acts_like?(:date)
if type == :seconds
t.since(sign * number)
@@ -172,9 +179,7 @@ module ActiveSupport
end
end
- private
-
- def method_missing(method, *args, &block) #:nodoc:
+ def method_missing(method, *args, &block)
value.send(method, *args, &block)
end
end
diff --git a/activesupport/lib/active_support/duration/iso8601_serializer.rb b/activesupport/lib/active_support/duration/iso8601_serializer.rb
index 542630b950..51d53e2f8d 100644
--- a/activesupport/lib/active_support/duration/iso8601_serializer.rb
+++ b/activesupport/lib/active_support/duration/iso8601_serializer.rb
@@ -26,7 +26,7 @@ module ActiveSupport
if parts.key?(:seconds)
time << "#{sprintf(@precision ? "%0.0#{@precision}f" : '%g', parts[:seconds])}S"
end
- output << "T#{time}" if time.present?
+ output << "T#{time}" unless time.empty?
"#{sign}#{output}"
end
@@ -37,7 +37,7 @@ module ActiveSupport
# Zero parts are removed as not significant.
# If all parts are negative it will negate all of them and return minus as a sign.
def normalize
- parts = @duration.parts.each_with_object(Hash.new(0)) do |(k,v),p|
+ parts = @duration.parts.each_with_object(Hash.new(0)) do |(k, v), p|
p[k] += v unless v.zero?
end
# If all parts are negative - let's make a negative duration
diff --git a/activesupport/lib/active_support/file_update_checker.rb b/activesupport/lib/active_support/file_update_checker.rb
index 98aceabe21..2dbbfadac1 100644
--- a/activesupport/lib/active_support/file_update_checker.rb
+++ b/activesupport/lib/active_support/file_update_checker.rb
@@ -145,7 +145,7 @@ module ActiveSupport
end
def escape(key)
- key.gsub(",",'\,')
+ key.gsub(",", '\,')
end
def compile_ext(array)
diff --git a/activesupport/lib/active_support/gzip.rb b/activesupport/lib/active_support/gzip.rb
index 4aee8dddba..84eef6a623 100644
--- a/activesupport/lib/active_support/gzip.rb
+++ b/activesupport/lib/active_support/gzip.rb
@@ -25,7 +25,7 @@ module ActiveSupport
end
# Compresses a string using gzip.
- def self.compress(source, level=Zlib::DEFAULT_COMPRESSION, strategy=Zlib::DEFAULT_STRATEGY)
+ def self.compress(source, level = Zlib::DEFAULT_COMPRESSION, strategy = Zlib::DEFAULT_STRATEGY)
output = Stream.new
gz = Zlib::GzipWriter.new(output, level, strategy)
gz.write(source)
diff --git a/activesupport/lib/active_support/hash_with_indifferent_access.rb b/activesupport/lib/active_support/hash_with_indifferent_access.rb
index 74a603c05d..d3f7b46e77 100644
--- a/activesupport/lib/active_support/hash_with_indifferent_access.rb
+++ b/activesupport/lib/active_support/hash_with_indifferent_access.rb
@@ -40,6 +40,12 @@ module ActiveSupport
# rgb = { black: '#000000', white: '#FFFFFF' }.with_indifferent_access
#
# which may be handy.
+ #
+ # To access this class outside of Rails, require the core extension with:
+ #
+ # require "active_support/core_ext/hash/indifferent_access"
+ #
+ # which will, in turn, require this file.
class HashWithIndifferentAccess < Hash
# Returns +true+ so that <tt>Array#extract_options!</tt> finds members of
# this class.
@@ -78,15 +84,6 @@ module ActiveSupport
end
end
- def self.new_from_hash_copying_default(hash)
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
- `ActiveSupport::HashWithIndifferentAccess.new_from_hash_copying_default`
- has been deprecated, and will be removed in Rails 5.1. The behavior of
- this method is now identical to the behavior of `.new`.
- MSG
- new(hash)
- end
-
def self.[](*args)
new.merge!(Hash[*args])
end
@@ -231,7 +228,7 @@ module ActiveSupport
# Same semantics as +reverse_merge+ but modifies the receiver in-place.
def reverse_merge!(other_hash)
- replace(reverse_merge( other_hash ))
+ replace(reverse_merge(other_hash))
end
# Replaces the contents of this hash with other_hash.
@@ -267,6 +264,11 @@ module ActiveSupport
dup.tap { |hash| hash.reject!(*args, &block) }
end
+ def transform_values(*args, &block)
+ return to_enum(:transform_values) unless block_given?
+ dup.tap { |hash| hash.transform_values!(*args, &block) }
+ end
+
# Convert to a regular hash with string keys.
def to_hash
_new_hash = Hash.new
@@ -278,12 +280,12 @@ module ActiveSupport
_new_hash
end
- protected
- def convert_key(key)
+ private
+ def convert_key(key) # :doc:
key.kind_of?(Symbol) ? key.to_s : key
end
- def convert_value(value, options = {})
+ def convert_value(value, options = {}) # :doc:
if value.is_a? Hash
if options[:for] == :to_hash
value.to_hash
@@ -300,7 +302,7 @@ module ActiveSupport
end
end
- def set_defaults(target)
+ def set_defaults(target) # :doc:
if default_proc
target.default_proc = default_proc.dup
else
diff --git a/activesupport/lib/active_support/i18n_railtie.rb b/activesupport/lib/active_support/i18n_railtie.rb
index 08c9ef8f02..b94368df14 100644
--- a/activesupport/lib/active_support/i18n_railtie.rb
+++ b/activesupport/lib/active_support/i18n_railtie.rb
@@ -21,8 +21,6 @@ module I18n
I18n::Railtie.initialize_i18n(app)
end
- protected
-
@i18n_inited = false
# Setup i18n configuration.
diff --git a/activesupport/lib/active_support/inflector/methods.rb b/activesupport/lib/active_support/inflector/methods.rb
index c80243c40a..8fea96a82a 100644
--- a/activesupport/lib/active_support/inflector/methods.rb
+++ b/activesupport/lib/active_support/inflector/methods.rb
@@ -207,7 +207,7 @@ module ActiveSupport
def demodulize(path)
path = path.to_s
if i = path.rindex("::")
- path[(i+2)..-1]
+ path[(i + 2)..-1]
else
path
end
@@ -274,7 +274,7 @@ module ActiveSupport
# Go down the ancestors to check if it is owned directly. The check
# stops when we reach Object or the end of ancestors tree.
- constant = constant.ancestors.inject do |const, ancestor|
+ constant = constant.ancestors.inject(constant) do |const, ancestor|
break const if ancestor == Object
break ancestor if ancestor.const_defined?(name, false)
const
@@ -361,12 +361,12 @@ module ActiveSupport
#
# const_regexp("Foo::Bar::Baz") # => "Foo(::Bar(::Baz)?)?"
# const_regexp("::") # => "::"
- def const_regexp(camel_cased_word) #:nodoc:
+ def const_regexp(camel_cased_word)
parts = camel_cased_word.split("::".freeze)
return Regexp.escape(camel_cased_word) if parts.blank?
- last = parts.pop
+ last = parts.pop
parts.reverse.inject(last) do |acc, part|
part.empty? ? acc : "#{part}(::#{acc})?"
diff --git a/activesupport/lib/active_support/inflector/transliterate.rb b/activesupport/lib/active_support/inflector/transliterate.rb
index 85fa83c803..3e78986e8e 100644
--- a/activesupport/lib/active_support/inflector/transliterate.rb
+++ b/activesupport/lib/active_support/inflector/transliterate.rb
@@ -78,11 +78,7 @@ module ActiveSupport
# parameterize("Donald E. Knuth", preserve_case: true) # => "Donald-E-Knuth"
# parameterize("^trés|Jolie-- ", preserve_case: true) # => "tres-Jolie"
#
- def parameterize(string, sep = :unused, separator: "-", preserve_case: false)
- unless sep == :unused
- ActiveSupport::Deprecation.warn("Passing the separator argument as a positional parameter is deprecated and will soon be removed. Use `separator: '#{sep}'` instead.")
- separator = sep
- end
+ def parameterize(string, separator: "-", preserve_case: false)
# Replace accented chars with their ASCII equivalents.
parameterized_string = transliterate(string)
diff --git a/activesupport/lib/active_support/json/encoding.rb b/activesupport/lib/active_support/json/encoding.rb
index cee731417f..defaf3f395 100644
--- a/activesupport/lib/active_support/json/encoding.rb
+++ b/activesupport/lib/active_support/json/encoding.rb
@@ -68,7 +68,8 @@ module ActiveSupport
:ESCAPE_REGEX_WITHOUT_HTML_ENTITIES, :EscapedString
# Convert an object into a "JSON-ready" representation composed of
- # primitives like Hash, Array, String, Numeric, and true/false/nil.
+ # primitives like Hash, Array, String, Numeric,
+ # and +true+/+false+/+nil+.
# Recursively calls #as_json to the object to recursively build a
# fully JSON-ready object.
#
@@ -84,7 +85,7 @@ module ActiveSupport
when String
EscapedString.new(value)
when Numeric, NilClass, TrueClass, FalseClass
- value
+ value.as_json
when Hash
Hash[value.map { |k, v| [jsonify(k), jsonify(v)] }]
when Array
diff --git a/activesupport/lib/active_support/key_generator.rb b/activesupport/lib/active_support/key_generator.rb
index 1064398d8c..23ab804eb1 100644
--- a/activesupport/lib/active_support/key_generator.rb
+++ b/activesupport/lib/active_support/key_generator.rb
@@ -17,7 +17,7 @@ module ActiveSupport
# Returns a derived key suitable for use. The default key_size is chosen
# to be compatible with the default settings of ActiveSupport::MessageVerifier.
# i.e. OpenSSL::Digest::SHA1#block_length
- def generate_key(salt, key_size=64)
+ def generate_key(salt, key_size = 64)
OpenSSL::PKCS5.pbkdf2_hmac_sha1(@secret, salt, @iterations, key_size)
end
end
diff --git a/activesupport/lib/active_support/lazy_load_hooks.rb b/activesupport/lib/active_support/lazy_load_hooks.rb
index ae1897b886..720ed47331 100644
--- a/activesupport/lib/active_support/lazy_load_hooks.rb
+++ b/activesupport/lib/active_support/lazy_load_hooks.rb
@@ -23,8 +23,8 @@ module ActiveSupport
module LazyLoadHooks
def self.extended(base) # :nodoc:
base.class_eval do
- @load_hooks = Hash.new { |h,k| h[k] = [] }
- @loaded = Hash.new { |h,k| h[k] = [] }
+ @load_hooks = Hash.new { |h, k| h[k] = [] }
+ @loaded = Hash.new { |h, k| h[k] = [] }
end
end
diff --git a/activesupport/lib/active_support/log_subscriber.rb b/activesupport/lib/active_support/log_subscriber.rb
index e5812d75d0..e2c4f33565 100644
--- a/activesupport/lib/active_support/log_subscriber.rb
+++ b/activesupport/lib/active_support/log_subscriber.rb
@@ -85,7 +85,7 @@ module ActiveSupport
logger.error "Could not log #{name.inspect} event. #{e.class}: #{e.message} #{e.backtrace}"
end
- protected
+ private
%w(info debug warn error fatal unknown).each do |level|
class_eval <<-METHOD, __FILE__, __LINE__ + 1
@@ -99,7 +99,7 @@ module ActiveSupport
# option is set to +true+, it also adds bold to the string. This is based
# on the Highline implementation and will automatically append CLEAR to the
# end of the returned String.
- def color(text, color, bold=false)
+ def color(text, color, bold = false) # :doc:
return text unless colorize_logging
color = self.class.const_get(color.upcase) if color.is_a?(Symbol)
bold = bold ? BOLD : ""
diff --git a/activesupport/lib/active_support/log_subscriber/test_helper.rb b/activesupport/lib/active_support/log_subscriber/test_helper.rb
index a7e30632f6..953ee77c2a 100644
--- a/activesupport/lib/active_support/log_subscriber/test_helper.rb
+++ b/activesupport/lib/active_support/log_subscriber/test_helper.rb
@@ -58,7 +58,7 @@ module ActiveSupport
def initialize(level = DEBUG)
@flush_count = 0
@level = level
- @logged = Hash.new { |h,k| h[k] = [] }
+ @logged = Hash.new { |h, k| h[k] = [] }
end
def method_missing(level, message = nil)
diff --git a/activesupport/lib/active_support/logger.rb b/activesupport/lib/active_support/logger.rb
index 3ba6461b57..ea09d7d2df 100644
--- a/activesupport/lib/active_support/logger.rb
+++ b/activesupport/lib/active_support/logger.rb
@@ -59,14 +59,14 @@ module ActiveSupport
define_method(:silence) do |level = Logger::ERROR, &block|
if logger.respond_to?(:silence)
logger.silence(level) do
- if respond_to?(:silence)
+ if defined?(super)
super(level, &block)
else
block.call(self)
end
end
else
- if respond_to?(:silence)
+ if defined?(super)
super(level, &block)
else
block.call(self)
diff --git a/activesupport/lib/active_support/multibyte/chars.rb b/activesupport/lib/active_support/multibyte/chars.rb
index 938e4ebb72..65d6259a06 100644
--- a/activesupport/lib/active_support/multibyte/chars.rb
+++ b/activesupport/lib/active_support/multibyte/chars.rb
@@ -87,7 +87,7 @@ module ActiveSupport #:nodoc:
end
# Works like <tt>String#slice!</tt>, but returns an instance of
- # Chars, or nil if the string was not modified. The string will not be
+ # Chars, or +nil+ if the string was not modified. The string will not be
# modified if the range given is out of bounds
#
# string = 'Welcome'
@@ -210,9 +210,9 @@ module ActiveSupport #:nodoc:
end
end
- protected
+ private
- def translate_offset(byte_offset) #:nodoc:
+ def translate_offset(byte_offset)
return nil if byte_offset.nil?
return 0 if @wrapped_string == ""
@@ -224,7 +224,7 @@ module ActiveSupport #:nodoc:
end
end
- def chars(string) #:nodoc:
+ def chars(string)
self.class.new(string)
end
end
diff --git a/activesupport/lib/active_support/multibyte/unicode.rb b/activesupport/lib/active_support/multibyte/unicode.rb
index 217919ccb8..05cfb249c3 100644
--- a/activesupport/lib/active_support/multibyte/unicode.rb
+++ b/activesupport/lib/active_support/multibyte/unicode.rb
@@ -52,9 +52,9 @@ module ActiveSupport
pos = 0
marker = 0
eoc = codepoints.length
- while(pos < eoc)
+ while (pos < eoc)
pos += 1
- previous = codepoints[pos-1]
+ previous = codepoints[pos - 1]
current = codepoints[pos]
should_break =
@@ -62,19 +62,19 @@ module ActiveSupport
if previous == database.boundary[:cr] && current == database.boundary[:lf]
false
# GB4. (Control|CR|LF) ÷
- elsif previous && in_char_class?(previous, [:control,:cr,:lf])
+ elsif previous && in_char_class?(previous, [:control, :cr, :lf])
true
# GB5. ÷ (Control|CR|LF)
- elsif in_char_class?(current, [:control,:cr,:lf])
+ elsif in_char_class?(current, [:control, :cr, :lf])
true
# GB6. L X (L|V|LV|LVT)
- elsif database.boundary[:l] === previous && in_char_class?(current, [:l,:v,:lv,:lvt])
+ elsif database.boundary[:l] === previous && in_char_class?(current, [:l, :v, :lv, :lvt])
false
# GB7. (LV|V) X (V|T)
- elsif in_char_class?(previous, [:lv,:v]) && in_char_class?(current, [:v,:t])
+ elsif in_char_class?(previous, [:lv, :v]) && in_char_class?(current, [:v, :t])
false
# GB8. (LVT|T) X (T)
- elsif in_char_class?(previous, [:lvt,:t]) && database.boundary[:t] === current
+ elsif in_char_class?(previous, [:lvt, :t]) && database.boundary[:t] === current
false
# GB8a. Regional_Indicator X Regional_Indicator
elsif database.boundary[:regional_indicator] === previous && database.boundary[:regional_indicator] === current
@@ -94,7 +94,7 @@ module ActiveSupport
end
if should_break
- unpacked << codepoints[marker..pos-1]
+ unpacked << codepoints[marker..pos - 1]
marker = pos
end
end
@@ -110,12 +110,12 @@ module ActiveSupport
# Re-order codepoints so the string becomes canonical.
def reorder_characters(codepoints)
- length = codepoints.length- 1
+ length = codepoints.length - 1
pos = 0
while pos < length do
- cp1, cp2 = database.codepoints[codepoints[pos]], database.codepoints[codepoints[pos+1]]
+ cp1, cp2 = database.codepoints[codepoints[pos]], database.codepoints[codepoints[pos + 1]]
if (cp1.combining_class > cp2.combining_class) && (cp2.combining_class > 0)
- codepoints[pos..pos+1] = cp2.code, cp1.code
+ codepoints[pos..pos + 1] = cp2.code, cp1.code
pos += (pos > 0 ? -1 : 1)
else
pos += 1
@@ -157,9 +157,9 @@ module ActiveSupport
lindex = starter_char - HANGUL_LBASE
# -- Hangul
if 0 <= lindex && lindex < HANGUL_LCOUNT
- vindex = codepoints[starter_pos+1] - HANGUL_VBASE rescue vindex = -1
+ vindex = codepoints[starter_pos + 1] - HANGUL_VBASE rescue vindex = -1
if 0 <= vindex && vindex < HANGUL_VCOUNT
- tindex = codepoints[starter_pos+2] - HANGUL_TBASE rescue tindex = -1
+ tindex = codepoints[starter_pos + 2] - HANGUL_TBASE rescue tindex = -1
if 0 <= tindex && tindex < HANGUL_TCOUNT
j = starter_pos + 2
eoa -= 2
@@ -251,7 +251,7 @@ module ActiveSupport
# * <tt>form</tt> - The form you want to normalize in. Should be one of
# the following: <tt>:c</tt>, <tt>:kc</tt>, <tt>:d</tt>, or <tt>:kd</tt>.
# Default is ActiveSupport::Multibyte::Unicode.default_normalization_form.
- def normalize(string, form=nil)
+ def normalize(string, form = nil)
form ||= @default_normalization_form
# See http://www.unicode.org/reports/tr15, Table 1
codepoints = string.codepoints.to_a
@@ -358,7 +358,7 @@ module ActiveSupport
private
- def apply_mapping(string, mapping) #:nodoc:
+ def apply_mapping(string, mapping)
database.codepoints
string.each_codepoint.map do |codepoint|
cp = database.codepoints[codepoint]
diff --git a/activesupport/lib/active_support/notifications.rb b/activesupport/lib/active_support/notifications.rb
index bae5f067ae..2df819e554 100644
--- a/activesupport/lib/active_support/notifications.rb
+++ b/activesupport/lib/active_support/notifications.rb
@@ -13,7 +13,7 @@ module ActiveSupport
# To instrument an event you just need to do:
#
# ActiveSupport::Notifications.instrument('render', extra: :information) do
- # render text: 'Foo'
+ # render plain: 'Foo'
# end
#
# That first executes the block and then notifies all subscribers once done.
@@ -48,7 +48,7 @@ module ActiveSupport
# The block is saved and will be called whenever someone instruments "render":
#
# ActiveSupport::Notifications.instrument('render', extra: :information) do
- # render text: 'Foo'
+ # render plain: 'Foo'
# end
#
# event = events.first
diff --git a/activesupport/lib/active_support/notifications/instrumenter.rb b/activesupport/lib/active_support/notifications/instrumenter.rb
index 23262d5398..e11e2e0689 100644
--- a/activesupport/lib/active_support/notifications/instrumenter.rb
+++ b/activesupport/lib/active_support/notifications/instrumenter.rb
@@ -14,7 +14,7 @@ module ActiveSupport
# Instrument the given block by measuring the time taken to execute it
# and publish it. Notice that events get sent even if an error occurs
# in the passed-in block.
- def instrument(name, payload={})
+ def instrument(name, payload = {})
# some of the listeners might have state
listeners_state = start name, payload
begin
diff --git a/activesupport/lib/active_support/number_helper.rb b/activesupport/lib/active_support/number_helper.rb
index 7a49bbb960..6000ea44be 100644
--- a/activesupport/lib/active_support/number_helper.rb
+++ b/activesupport/lib/active_support/number_helper.rb
@@ -109,7 +109,7 @@ module ActiveSupport
# * <tt>:locale</tt> - Sets the locale to be used for formatting
# (defaults to current locale).
# * <tt>:precision</tt> - Sets the precision of the number
- # (defaults to 3). Keeps the number's precision if nil.
+ # (defaults to 3). Keeps the number's precision if +nil+.
# * <tt>:significant</tt> - If +true+, precision will be the number
# of significant_digits. If +false+, the number of fractional
# digits (defaults to +false+).
@@ -183,7 +183,7 @@ module ActiveSupport
# * <tt>:locale</tt> - Sets the locale to be used for formatting
# (defaults to current locale).
# * <tt>:precision</tt> - Sets the precision of the number
- # (defaults to 3). Keeps the number's precision if nil.
+ # (defaults to 3). Keeps the number's precision if +nil+.
# * <tt>:significant</tt> - If +true+, precision will be the number
# of significant_digits. If +false+, the number of fractional
# digits (defaults to +false+).
diff --git a/activesupport/lib/active_support/number_helper/number_converter.rb b/activesupport/lib/active_support/number_helper/number_converter.rb
index c485a6af63..ce363287cf 100644
--- a/activesupport/lib/active_support/number_helper/number_converter.rb
+++ b/activesupport/lib/active_support/number_helper/number_converter.rb
@@ -139,17 +139,17 @@ module ActiveSupport
@options ||= format_options.merge(opts)
end
- def format_options #:nodoc:
+ def format_options
default_format_options.merge!(i18n_format_options)
end
- def default_format_options #:nodoc:
+ def default_format_options
options = DEFAULTS[:format].dup
options.merge!(DEFAULTS[namespace][:format]) if namespace
options
end
- def i18n_format_options #:nodoc:
+ def i18n_format_options
locale = opts[:locale]
options = I18n.translate(:'number.format', locale: locale, default: {}).dup
@@ -160,7 +160,7 @@ module ActiveSupport
options
end
- def translate_number_value_with_default(key, i18n_options = {}) #:nodoc:
+ def translate_number_value_with_default(key, i18n_options = {})
I18n.translate(key, { default: default_value(key), scope: :number }.merge!(i18n_options))
end
@@ -172,7 +172,7 @@ module ActiveSupport
key.split(".").reduce(DEFAULTS) { |defaults, k| defaults[k.to_sym] }
end
- def valid_float? #:nodoc:
+ def valid_float?
Float(number)
rescue ArgumentError, TypeError
false
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 695ee1dad3..56185ddf4b 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
@@ -18,7 +18,7 @@ module ActiveSupport
units = opts[:units]
exponent = calculate_exponent(units)
- @number = number / (10 ** exponent)
+ @number = number / (10**exponent)
until (rounded_number = NumberToRoundedConverter.convert(number, options)) != NumberToRoundedConverter.convert(1000, options)
@number = number / 1000.0
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 78cb33aa62..f263dbe9f8 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
@@ -7,10 +7,6 @@ module ActiveSupport
self.validate_float = true
def convert
- if opts.key?(:prefix)
- ActiveSupport::Deprecation.warn("The :prefix option of `number_to_human_size` is deprecated and will be removed in Rails 5.1 with no replacement.")
- end
-
@number = Float(number)
# for backwards compatibility with those that didn't add strip_insignificant_zeros to their locale files
@@ -21,7 +17,7 @@ module ActiveSupport
if smaller_than_base?
number_to_format = number.to_i.to_s
else
- human_size = number / (base ** exponent)
+ 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)
@@ -54,7 +50,7 @@ module ActiveSupport
end
def base
- opts[:prefix] == :si ? 1000 : 1024
+ 1024
end
end
end
diff --git a/activesupport/lib/active_support/number_helper/number_to_phone_converter.rb b/activesupport/lib/active_support/number_helper/number_to_phone_converter.rb
index c2612f9a9b..1de9f50f34 100644
--- a/activesupport/lib/active_support/number_helper/number_to_phone_converter.rb
+++ b/activesupport/lib/active_support/number_helper/number_to_phone_converter.rb
@@ -2,7 +2,7 @@ module ActiveSupport
module NumberHelper
class NumberToPhoneConverter < NumberConverter #:nodoc:
def convert
- str = country_code(opts[:country_code])
+ str = country_code(opts[:country_code])
str << convert_to_phone_number(number.to_s.strip)
str << phone_ext(opts[:extension])
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 cfcb0045fb..1f013990ea 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
@@ -52,7 +52,7 @@ module ActiveSupport
[1, 0]
else
digits = digit_count(number)
- multiplier = 10 ** (digits - precision)
+ multiplier = 10**(digits - precision)
rounded_number = calculate_rounded_number(multiplier)
digits = digit_count(rounded_number) # After rounding, the number of digits may have changed
[digits, rounded_number]
diff --git a/activesupport/lib/active_support/ordered_hash.rb b/activesupport/lib/active_support/ordered_hash.rb
index 793bc9e22d..3aa0a14f04 100644
--- a/activesupport/lib/active_support/ordered_hash.rb
+++ b/activesupport/lib/active_support/ordered_hash.rb
@@ -25,7 +25,7 @@ module ActiveSupport
end
def encode_with(coder)
- coder.represent_seq "!omap", map { |k,v| { k => v } }
+ coder.represent_seq "!omap", map { |k, v| { k => v } }
end
def select(*args, &block)
diff --git a/activesupport/lib/active_support/ordered_options.rb b/activesupport/lib/active_support/ordered_options.rb
index 14ed9049be..04d6dfaf9c 100644
--- a/activesupport/lib/active_support/ordered_options.rb
+++ b/activesupport/lib/active_support/ordered_options.rb
@@ -68,9 +68,9 @@ module ActiveSupport
def initialize(parent = nil)
if parent.kind_of?(OrderedOptions)
# use the faster _get when dealing with OrderedOptions
- super() { |h,k| parent._get(k) }
+ super() { |h, k| parent._get(k) }
elsif parent
- super() { |h,k| parent[k] }
+ super() { |h, k| parent[k] }
else
super()
end
diff --git a/activesupport/lib/active_support/per_thread_registry.rb b/activesupport/lib/active_support/per_thread_registry.rb
index 9e6d8d4fd8..02431704d3 100644
--- a/activesupport/lib/active_support/per_thread_registry.rb
+++ b/activesupport/lib/active_support/per_thread_registry.rb
@@ -45,8 +45,8 @@ module ActiveSupport
Thread.current[@per_thread_registry_key] ||= new
end
- protected
- def method_missing(name, *args, &block) # :nodoc:
+ private
+ def method_missing(name, *args, &block)
# Caches the method definition as a singleton method of the receiver.
#
# By letting #delegate handle it, we avoid an enclosure that'll capture args.
diff --git a/activesupport/lib/active_support/rails.rb b/activesupport/lib/active_support/rails.rb
index 57380061f7..f6b018f0d3 100644
--- a/activesupport/lib/active_support/rails.rb
+++ b/activesupport/lib/active_support/rails.rb
@@ -25,3 +25,9 @@ require "active_support/core_ext/module/delegation"
# Defines ActiveSupport::Deprecation.
require "active_support/deprecation"
+
+# Defines Regexp#match?.
+#
+# This should be removed when Rails needs Ruby 2.4 or later, and the require
+# added where other Regexp extensions are being used (easy to grep).
+require "active_support/core_ext/regexp"
diff --git a/activesupport/lib/active_support/rescuable.rb b/activesupport/lib/active_support/rescuable.rb
index dc3f27a16d..ee6592fb5a 100644
--- a/activesupport/lib/active_support/rescuable.rb
+++ b/activesupport/lib/active_support/rescuable.rb
@@ -36,7 +36,7 @@ module ActiveSupport
# render xml: exception, status: 500
# end
#
- # protected
+ # private
# def deny_access
# ...
# end
@@ -74,7 +74,7 @@ module ActiveSupport
#
# If no handler matches the exception, check for a handler matching the
# (optional) exception.cause. If no handler matches the exception or its
- # cause, this returns nil so you can deal with unhandled exceptions.
+ # cause, this returns +nil+, so you can deal with unhandled exceptions.
# Be sure to re-raise unhandled exceptions if this is what you expect.
#
# begin
@@ -83,11 +83,13 @@ module ActiveSupport
# rescue_with_handler(exception) || raise
# end
#
- # Returns the exception if it was handled and nil if it was not.
+ # Returns the exception if it was handled and +nil+ if it was not.
def rescue_with_handler(exception, object: self)
if handler = handler_for_rescue(exception, object: object)
handler.call exception
exception
+ elsif exception
+ rescue_with_handler(exception.cause, object: object)
end
end
@@ -121,7 +123,7 @@ module ActiveSupport
end
end
- handler || find_rescue_handler(exception.cause)
+ handler
end
end
diff --git a/activesupport/lib/active_support/subscriber.rb b/activesupport/lib/active_support/subscriber.rb
index 0f09f1eb63..2924139755 100644
--- a/activesupport/lib/active_support/subscriber.rb
+++ b/activesupport/lib/active_support/subscriber.rb
@@ -1,4 +1,5 @@
require "active_support/per_thread_registry"
+require "active_support/notifications"
module ActiveSupport
# ActiveSupport::Subscriber is an object set to consume
@@ -24,7 +25,7 @@ module ActiveSupport
class Subscriber
class << self
# Attach the subscriber to a namespace.
- def attach_to(namespace, subscriber=new, notifier=ActiveSupport::Notifications)
+ def attach_to(namespace, subscriber = new, notifier = ActiveSupport::Notifications)
@namespace = namespace
@subscriber = subscriber
@notifier = notifier
@@ -51,11 +52,15 @@ module ActiveSupport
@@subscribers ||= []
end
+ # TODO Change this to private once we've dropped Ruby 2.2 support.
+ # Workaround for Ruby 2.2 "private attribute?" warning.
protected
attr_reader :subscriber, :notifier, :namespace
- def add_event_subscriber(event)
+ private
+
+ def add_event_subscriber(event) # :doc:
return if %w{ start finish }.include?(event.to_s)
pattern = "#{event}.#{namespace}"
diff --git a/activesupport/lib/active_support/tagged_logging.rb b/activesupport/lib/active_support/tagged_logging.rb
index 6836378943..ad134c49b6 100644
--- a/activesupport/lib/active_support/tagged_logging.rb
+++ b/activesupport/lib/active_support/tagged_logging.rb
@@ -48,13 +48,12 @@ module ActiveSupport
Thread.current[thread_key] ||= []
end
- private
- def tags_text
- tags = current_tags
- if tags.any?
- tags.collect { |tag| "[#{tag}] " }.join
- end
+ def tags_text
+ tags = current_tags
+ if tags.any?
+ tags.collect { |tag| "[#{tag}] " }.join
end
+ end
end
def self.new(logger)
diff --git a/activesupport/lib/active_support/test_case.rb b/activesupport/lib/active_support/test_case.rb
index 1c599b8851..3de4ccc1da 100644
--- a/activesupport/lib/active_support/test_case.rb
+++ b/activesupport/lib/active_support/test_case.rb
@@ -65,5 +65,7 @@ module ActiveSupport
alias :assert_not_predicate :refute_predicate
alias :assert_not_respond_to :refute_respond_to
alias :assert_not_same :refute_same
+
+ ActiveSupport.run_load_hooks(:active_support_test_case, self)
end
end
diff --git a/activesupport/lib/active_support/testing/declarative.rb b/activesupport/lib/active_support/testing/declarative.rb
index 0bf3643a56..53ab3ebf78 100644
--- a/activesupport/lib/active_support/testing/declarative.rb
+++ b/activesupport/lib/active_support/testing/declarative.rb
@@ -9,7 +9,7 @@ module ActiveSupport
# ...
# end
def test(name, &block)
- test_name = "test_#{name.gsub(/\s+/,'_')}".to_sym
+ test_name = "test_#{name.gsub(/\s+/, '_')}".to_sym
defined = method_defined? test_name
raise "#{test_name} is already defined in #{self}" if defined
if block_given?
diff --git a/activesupport/lib/active_support/testing/isolation.rb b/activesupport/lib/active_support/testing/isolation.rb
index d30b34ecd6..14edd13a8f 100644
--- a/activesupport/lib/active_support/testing/isolation.rb
+++ b/activesupport/lib/active_support/testing/isolation.rb
@@ -78,13 +78,15 @@ module ActiveSupport
"ISOLATION_OUTPUT" => tmpfile.path
}
- load_paths = $-I.map { |p| "-I\"#{File.expand_path(p)}\"" }.join(" ")
- orig_args = ORIG_ARGV.join(" ")
test_opts = "-n#{self.class.name}##{self.name}"
- command = "#{Gem.ruby} #{load_paths} #{$0} '#{orig_args}' #{test_opts}"
- # IO.popen lets us pass env in a cross-platform way
- child = IO.popen(env, command)
+ load_path_args = []
+ $-I.each do |p|
+ load_path_args << "-I"
+ load_path_args << File.expand_path(p)
+ end
+
+ child = IO.popen([env, Gem.ruby, *load_path_args, $0, *ORIG_ARGV, test_opts])
begin
Process.wait(child.pid)
diff --git a/activesupport/lib/active_support/values/time_zone.rb b/activesupport/lib/active_support/values/time_zone.rb
index cb97a0e135..09cb9cbbe1 100644
--- a/activesupport/lib/active_support/values/time_zone.rb
+++ b/activesupport/lib/active_support/values/time_zone.rb
@@ -295,7 +295,7 @@ module ActiveSupport
# zone = ActiveSupport::TimeZone['Central Time (US & Canada)']
# zone.formatted_offset # => "-06:00"
# zone.formatted_offset(false) # => "-0600"
- def formatted_offset(colon=true, alternate_utc_string = nil)
+ def formatted_offset(colon = true, alternate_utc_string = nil)
utc_offset == 0 && alternate_utc_string || self.class.seconds_to_utc_offset(utc_offset, colon)
end
@@ -355,7 +355,7 @@ module ActiveSupport
# components are supplied, then the day of the month defaults to 1:
#
# Time.zone.parse('Mar 2000') # => Wed, 01 Mar 2000 00:00:00 HST -10:00
- def parse(str, now=now())
+ def parse(str, now = now())
parts_to_time(Date._parse(str, false), now)
end
@@ -379,7 +379,7 @@ module ActiveSupport
# components are supplied, then the day of the month defaults to 1:
#
# Time.zone.strptime('Mar 2000', '%b %Y') # => Wed, 01 Mar 2000 00:00:00 HST -10:00
- def strptime(str, format, now=now())
+ def strptime(str, format, now = now())
parts_to_time(DateTime._strptime(str, format), now)
end
@@ -416,7 +416,7 @@ module ActiveSupport
# Adjust the given time to the simultaneous time in UTC. Returns a
# Time.utc() instance.
- def local_to_utc(time, dst=true)
+ def local_to_utc(time, dst = true)
tzinfo.local_to_utc(time, dst)
end
@@ -428,7 +428,7 @@ module ActiveSupport
# Available so that TimeZone instances respond like TZInfo::Timezone
# instances.
- def period_for_local(time, dst=true)
+ def period_for_local(time, dst = true)
tzinfo.period_for_local(time, dst)
end
@@ -441,7 +441,7 @@ module ActiveSupport
end
def encode_with(coder) #:nodoc:
- coder.tag ="!ruby/object:#{self.class}"
+ coder.tag = "!ruby/object:#{self.class}"
coder.map = { "name" => tzinfo.name }
end
@@ -450,17 +450,21 @@ module ActiveSupport
raise ArgumentError, "invalid date" if parts.nil?
return if parts.empty?
- time = Time.new(
- parts.fetch(:year, now.year),
- parts.fetch(:mon, now.month),
- parts.fetch(:mday, parts[:year] || parts[:mon] ? 1 : now.day),
- parts.fetch(:hour, 0),
- parts.fetch(:min, 0),
- parts.fetch(:sec, 0) + parts.fetch(:sec_fraction, 0),
- parts.fetch(:offset, 0)
- )
-
- if parts[:offset]
+ if parts[:seconds]
+ time = Time.at(parts[:seconds])
+ else
+ time = Time.new(
+ parts.fetch(:year, now.year),
+ parts.fetch(:mon, now.month),
+ parts.fetch(:mday, parts[:year] || parts[:mon] ? 1 : now.day),
+ parts.fetch(:hour, 0),
+ parts.fetch(:min, 0),
+ parts.fetch(:sec, 0) + parts.fetch(:sec_fraction, 0),
+ parts.fetch(:offset, 0)
+ )
+ end
+
+ if parts[:offset] || parts[:seconds]
TimeWithZone.new(time.utc, self)
else
TimeWithZone.new(nil, self, time)
diff --git a/activesupport/lib/active_support/xml_mini.rb b/activesupport/lib/active_support/xml_mini.rb
index 921a3447d0..782fb41288 100644
--- a/activesupport/lib/active_support/xml_mini.rb
+++ b/activesupport/lib/active_support/xml_mini.rb
@@ -68,7 +68,17 @@ module ActiveSupport
"datetime" => Proc.new { |time| Time.xmlschema(time).utc rescue ::DateTime.parse(time).utc },
"integer" => Proc.new { |integer| integer.to_i },
"float" => Proc.new { |float| float.to_f },
- "decimal" => Proc.new { |number| BigDecimal(number) },
+ "decimal" => Proc.new do |number|
+ if String === number
+ begin
+ BigDecimal(number)
+ rescue ArgumentError
+ BigDecimal("0")
+ end
+ else
+ BigDecimal(number)
+ end
+ end,
"boolean" => Proc.new { |boolean| %w(1 true).include?(boolean.to_s.strip) },
"string" => Proc.new { |string| string.to_s },
"yaml" => Proc.new { |yaml| YAML::load(yaml) rescue yaml },
@@ -149,16 +159,16 @@ module ActiveSupport
key
end
- protected
+ private
def _dasherize(key)
# $2 must be a non-greedy regex for this to work
- left, middle, right = /\A(_*)(.*?)(_*)\Z/.match(key.strip)[1,3]
+ left, middle, right = /\A(_*)(.*?)(_*)\Z/.match(key.strip)[1, 3]
"#{left}#{middle.tr('_ ', '--')}#{right}"
end
# TODO: Add support for other encodings
- def _parse_binary(bin, entity) #:nodoc:
+ def _parse_binary(bin, entity)
case entity["encoding"]
when "base64"
::Base64.decode64(bin)
@@ -175,8 +185,6 @@ module ActiveSupport
f
end
- private
-
def current_thread_backend
Thread.current[:xml_mini_backend]
end
diff --git a/activesupport/lib/active_support/xml_mini/jdom.rb b/activesupport/lib/active_support/xml_mini/jdom.rb
index 10498c9be0..a7939b3185 100644
--- a/activesupport/lib/active_support/xml_mini/jdom.rb
+++ b/activesupport/lib/active_support/xml_mini/jdom.rb
@@ -141,7 +141,7 @@ module ActiveSupport
attributes = element.attributes
(0...attributes.length).each do |i|
attribute_hash[CONTENT_KEY] ||= ""
- attribute_hash[attributes.item(i).name] = attributes.item(i).value
+ attribute_hash[attributes.item(i).name] = attributes.item(i).value
end
attribute_hash
end
diff --git a/activesupport/lib/active_support/xml_mini/libxml.rb b/activesupport/lib/active_support/xml_mini/libxml.rb
index 8a4aa03963..44b0bdb7dc 100644
--- a/activesupport/lib/active_support/xml_mini/libxml.rb
+++ b/activesupport/lib/active_support/xml_mini/libxml.rb
@@ -40,7 +40,7 @@ module LibXML #:nodoc:
#
# hash::
# Hash to merge the converted element into.
- def to_hash(hash={})
+ def to_hash(hash = {})
node_hash = {}
# Insert node hash into parent hash correctly.
diff --git a/activesupport/lib/active_support/xml_mini/nokogiri.rb b/activesupport/lib/active_support/xml_mini/nokogiri.rb
index b92fa7ea7c..4c2be3f3ec 100644
--- a/activesupport/lib/active_support/xml_mini/nokogiri.rb
+++ b/activesupport/lib/active_support/xml_mini/nokogiri.rb
@@ -44,7 +44,7 @@ module ActiveSupport
#
# hash::
# Hash to merge the converted element into.
- def to_hash(hash={})
+ def to_hash(hash = {})
node_hash = {}
# Insert node hash into parent hash correctly.
diff --git a/activesupport/lib/active_support/xml_mini/rexml.rb b/activesupport/lib/active_support/xml_mini/rexml.rb
index 091a15294c..03fa910fa5 100644
--- a/activesupport/lib/active_support/xml_mini/rexml.rb
+++ b/activesupport/lib/active_support/xml_mini/rexml.rb
@@ -113,7 +113,7 @@ module ActiveSupport
# XML element to extract attributes from.
def get_attributes(element)
attributes = {}
- element.attributes.each { |n,v| attributes[n] = v }
+ element.attributes.each { |n, v| attributes[n] = v }
attributes
end
diff --git a/activesupport/test/autoloading_fixtures/prepend.rb b/activesupport/test/autoloading_fixtures/prepend.rb
new file mode 100644
index 0000000000..3134d1df2b
--- /dev/null
+++ b/activesupport/test/autoloading_fixtures/prepend.rb
@@ -0,0 +1,8 @@
+class SubClassConflict
+end
+
+class Prepend
+ module PrependedModule
+ end
+ prepend PrependedModule
+end
diff --git a/activesupport/test/autoloading_fixtures/prepend/sub_class_conflict.rb b/activesupport/test/autoloading_fixtures/prepend/sub_class_conflict.rb
new file mode 100644
index 0000000000..090dda3043
--- /dev/null
+++ b/activesupport/test/autoloading_fixtures/prepend/sub_class_conflict.rb
@@ -0,0 +1,2 @@
+class Prepend::SubClassConflict
+end
diff --git a/activesupport/test/broadcast_logger_test.rb b/activesupport/test/broadcast_logger_test.rb
index 4b74f1313e..184d0ebddd 100644
--- a/activesupport/test/broadcast_logger_test.rb
+++ b/activesupport/test/broadcast_logger_test.rb
@@ -69,6 +69,20 @@ module ActiveSupport
assert_equal ::Logger::FATAL, log2.local_level
end
+ test "#silence does not break custom loggers" do
+ new_logger = FakeLogger.new
+ custom_logger = CustomLogger.new
+ custom_logger.extend(Logger.broadcast(new_logger))
+
+ custom_logger.silence do
+ custom_logger.error "from error"
+ custom_logger.unknown "from unknown"
+ end
+
+ assert_equal [[::Logger::ERROR, "from error", nil], [::Logger::UNKNOWN, "from unknown", nil]], custom_logger.adds
+ assert_equal [[::Logger::ERROR, "from error", nil], [::Logger::UNKNOWN, "from unknown", nil]], new_logger.adds
+ end
+
test "#silence silences all loggers below the default level of ERROR" do
logger.silence do
logger.debug "test"
@@ -98,9 +112,7 @@ module ActiveSupport
assert_equal [[::Logger::FATAL, "seen", nil]], log2.adds
end
- class FakeLogger
- include LoggerSilence
-
+ class CustomLogger
attr_reader :adds, :closed, :chevrons
attr_accessor :level, :progname, :formatter, :local_level
@@ -142,7 +154,7 @@ module ActiveSupport
@chevrons << x
end
- def add(message_level, message=nil, progname=nil, &block)
+ def add(message_level, message = nil, progname = nil, &block)
@adds << [message_level, message, progname] if message_level >= local_level
end
@@ -150,5 +162,9 @@ module ActiveSupport
@closed = true
end
end
+
+ class FakeLogger < CustomLogger
+ include LoggerSilence
+ end
end
end
diff --git a/activesupport/test/caching_test.rb b/activesupport/test/caching_test.rb
index 0df4173a1a..c543122d91 100644
--- a/activesupport/test/caching_test.rb
+++ b/activesupport/test/caching_test.rb
@@ -316,7 +316,7 @@ module CacheStoreBehavior
def test_should_read_and_write_nil
assert @cache.write("foo", nil)
- assert_equal nil, @cache.read("foo")
+ assert_nil @cache.read("foo")
end
def test_should_read_and_write_false
@@ -464,7 +464,7 @@ module CacheStoreBehavior
Time.stub(:now, Time.at(time)) do
result = @cache.fetch("foo") do
- assert_equal nil, @cache.read("foo")
+ assert_nil @cache.read("foo")
"baz"
end
assert_equal "baz", result
@@ -476,7 +476,7 @@ module CacheStoreBehavior
@cache.write("foo", "bar", expires_in: 60)
Time.stub(:now, time + 71) do
result = @cache.fetch("foo", race_condition_ttl: 10) do
- assert_equal nil, @cache.read("foo")
+ assert_nil @cache.read("foo")
"baz"
end
assert_equal "baz", result
@@ -566,12 +566,6 @@ module CacheStoreBehavior
ensure
ActiveSupport::Notifications.unsubscribe "cache_read.active_support"
end
-
- def test_can_call_deprecated_namesaced_key
- assert_deprecated "`namespaced_key` is deprecated" do
- @cache.send(:namespaced_key, 111, {})
- end
- end
end
# https://rails.lighthouseapp.com/projects/8994/tickets/6225-memcachestore-cant-deal-with-umlauts-and-special-characters
@@ -681,9 +675,9 @@ module LocalCacheBehavior
def test_local_cache_of_read_nil
@cache.with_local_cache do
- assert_equal nil, @cache.read("foo")
+ assert_nil @cache.read("foo")
@cache.send(:bypass_local_cache) { @cache.write "foo", "bar" }
- assert_equal nil, @cache.read("foo")
+ assert_nil @cache.read("foo")
end
end
@@ -747,15 +741,6 @@ module LocalCacheBehavior
app = @cache.middleware.new(app)
app.call({})
end
-
- def test_can_call_deprecated_set_cache_value
- @cache.with_local_cache do
- assert_deprecated "`set_cache_value` is deprecated" do
- @cache.send(:set_cache_value, 1, "foo", :ignored, {})
- end
- assert_equal 1, @cache.read("foo")
- end
- end
end
module AutoloadingCacheBehavior
@@ -838,8 +823,8 @@ class FileStoreTest < ActiveSupport::TestCase
end
def test_long_uri_encoded_keys
- @cache.write("%"*870, 1)
- assert_equal 1, @cache.read("%"*870)
+ @cache.write("%" * 870, 1)
+ assert_equal 1, @cache.read("%" * 870)
end
def test_key_transformation
@@ -859,7 +844,7 @@ class FileStoreTest < ActiveSupport::TestCase
key = "#{'A' * ActiveSupport::Cache::FileStore::FILENAME_MAX_SIZE}"
path = @cache.send(:normalize_key, key, {})
Dir::Tmpname.create(path) do |tmpname, n, opts|
- assert File.basename(tmpname+".lock").length <= 255, "Temp filename too long: #{File.basename(tmpname+'.lock').length}"
+ assert File.basename(tmpname + ".lock").length <= 255, "Temp filename too long: #{File.basename(tmpname + '.lock').length}"
end
end
@@ -918,12 +903,6 @@ class FileStoreTest < ActiveSupport::TestCase
@cache.write(1, nil)
assert_equal false, @cache.write(1, "aaaaaaaaaa", unless_exist: true)
end
-
- def test_can_call_deprecated_key_file_path
- assert_deprecated "`key_file_path` is deprecated" do
- assert_equal 111, @cache.send(:key_file_path, 111)
- end
- end
end
class MemoryStoreTest < ActiveSupport::TestCase
@@ -1004,7 +983,7 @@ class MemoryStoreTest < ActiveSupport::TestCase
end
def test_pruning_is_capped_at_a_max_time
- def @cache.delete_entry (*args)
+ def @cache.delete_entry(*args)
sleep(0.01)
super
end
@@ -1098,12 +1077,6 @@ class MemCacheStoreTest < ActiveSupport::TestCase
value << "bingo"
assert_not_equal value, @cache.read("foo")
end
-
- def test_can_call_deprecated_escape_key
- assert_deprecated "`escape_key` is deprecated" do
- assert_equal 111, @cache.send(:escape_key, 111)
- end
- end
end
class NullStoreTest < ActiveSupport::TestCase
diff --git a/activesupport/test/callbacks_test.rb b/activesupport/test/callbacks_test.rb
index 783952c8c7..28caa30bf1 100644
--- a/activesupport/test/callbacks_test.rb
+++ b/activesupport/test/callbacks_test.rb
@@ -224,10 +224,51 @@ module CallbacksTest
define_callbacks :save
end
- class AroundPerson < MySuper
+ class MySlate < MySuper
attr_reader :history
attr_accessor :save_fails
+ def initialize
+ @history = []
+ end
+
+ def save
+ run_callbacks :save do
+ raise "inside save" if save_fails
+ @history << "running"
+ end
+ end
+
+ def no; false; end
+ def yes; true; end
+
+ def method_missing(sym, *)
+ case sym
+ when /^log_(.*)/
+ @history << $1
+ nil
+ when /^wrap_(.*)/
+ @history << "wrap_#$1"
+ yield
+ @history << "unwrap_#$1"
+ nil
+ when /^double_(.*)/
+ @history << "first_#$1"
+ yield
+ @history << "second_#$1"
+ yield
+ @history << "third_#$1"
+ else
+ super
+ end
+ end
+
+ def respond_to_missing?(sym)
+ sym =~ /^(log|wrap)_/ || super
+ end
+ end
+
+ class AroundPerson < MySlate
set_callback :save, :before, :nope, if: :no
set_callback :save, :before, :nope, unless: :yes
set_callback :save, :after, :tweedle
@@ -242,9 +283,6 @@ module CallbacksTest
set_callback :save, :around, :w0tno, if: :no
set_callback :save, :around, :tweedle_deedle
- def no; false; end
- def yes; true; end
-
def nope
@history << "boom"
end
@@ -283,17 +321,6 @@ module CallbacksTest
yield
@history << "tweedle deedle post"
end
-
- def initialize
- @history = []
- end
-
- def save
- run_callbacks :save do
- raise "inside save" if save_fails
- @history << "running"
- end
- end
end
class AroundPersonResult < MySuper
@@ -408,6 +435,32 @@ module CallbacksTest
end
end
+ class DoubleYieldTest < ActiveSupport::TestCase
+ class DoubleYieldModel < MySlate
+ set_callback :save, :around, :wrap_outer
+ set_callback :save, :around, :double_trouble
+ set_callback :save, :around, :wrap_inner
+ end
+
+ def test_double_save
+ double = DoubleYieldModel.new
+ double.save
+ assert_equal [
+ "wrap_outer",
+ "first_trouble",
+ "wrap_inner",
+ "running",
+ "unwrap_inner",
+ "second_trouble",
+ "wrap_inner",
+ "running",
+ "unwrap_inner",
+ "third_trouble",
+ "unwrap_outer",
+ ], double.history
+ end
+ end
+
class CallStackTest < ActiveSupport::TestCase
def test_tidy_call_stack
around = AroundPerson.new
@@ -834,7 +887,7 @@ module CallbacksTest
def test_returning_false_does_not_halt_callback_if_config_variable_is_not_set
obj = CallbackFalseTerminator.new
obj.save
- assert_equal nil, obj.halted
+ assert_nil obj.halted
assert obj.saved
end
end
@@ -847,7 +900,7 @@ module CallbacksTest
def test_returning_false_does_not_halt_callback_if_config_variable_is_true
obj = CallbackFalseTerminator.new
obj.save
- assert_equal nil, obj.halted
+ assert_nil obj.halted
assert obj.saved
end
end
@@ -860,7 +913,7 @@ module CallbacksTest
def test_returning_false_does_not_halt_callback_if_config_variable_is_false
obj = CallbackFalseTerminator.new
obj.save
- assert_equal nil, obj.halted
+ assert_nil obj.halted
assert obj.saved
end
end
@@ -950,7 +1003,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
@@ -987,7 +1040,7 @@ module CallbacksTest
set_callback :foo, :before, :foo, if: callback
def run; run_callbacks :foo; end
private
- def foo; end
+ def foo; end
}
object = klass.new
object.run
@@ -1029,7 +1082,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
@@ -1151,7 +1204,7 @@ module CallbacksTest
def test_skip_string # raises error
calls = []
- klass = ActiveSupport::Deprecation.silence { build_class("bar") }
+ klass = ActiveSupport::Deprecation.silence { build_class("bar") }
klass.class_eval { define_method(:bar) { calls << klass } }
assert_raises(ArgumentError) { klass.skip "bar" }
klass.new.run
diff --git a/activesupport/test/class_cache_test.rb b/activesupport/test/class_cache_test.rb
index c618fea81a..004b4dc9ce 100644
--- a/activesupport/test/class_cache_test.rb
+++ b/activesupport/test/class_cache_test.rb
@@ -61,7 +61,7 @@ module ActiveSupport
def test_safe_get_constantizes_doesnt_fail_on_invalid_names
assert @cache.empty?
- assert_equal nil, @cache.safe_get("OmgTotallyInvalidConstantName")
+ assert_nil @cache.safe_get("OmgTotallyInvalidConstantName")
end
def test_new_rejects_strings
diff --git a/activesupport/test/concern_test.rb b/activesupport/test/concern_test.rb
index 95507c815d..7a5a5414a7 100644
--- a/activesupport/test/concern_test.rb
+++ b/activesupport/test/concern_test.rb
@@ -76,7 +76,7 @@ class ConcernTest < ActiveSupport::TestCase
end
def test_class_methods_are_extended_only_on_expected_objects
- ::Object.__send__(:include, Qux)
+ ::Object.include(Qux)
Object.extend(Qux::ClassMethods)
# module needs to be created after Qux is included in Object or bug won't
# be triggered
diff --git a/activesupport/test/constantize_test_cases.rb b/activesupport/test/constantize_test_cases.rb
index af2db8c991..32b720bcbb 100644
--- a/activesupport/test/constantize_test_cases.rb
+++ b/activesupport/test/constantize_test_cases.rb
@@ -73,6 +73,11 @@ module ConstantizeTestCases
yield("RaisesNoMethodError")
end
end
+
+ with_autoloading_fixtures do
+ yield("Prepend::SubClassConflict")
+ assert_equal "constant", defined?(Prepend::SubClassConflict)
+ end
end
def run_safe_constantize_tests_on
diff --git a/activesupport/test/core_ext/array/conversions_test.rb b/activesupport/test/core_ext/array/conversions_test.rb
index 469efcd934..29e661a99b 100644
--- a/activesupport/test/core_ext/array/conversions_test.rb
+++ b/activesupport/test/core_ext/array/conversions_test.rb
@@ -25,11 +25,6 @@ class ToSentenceTest < ActiveSupport::TestCase
assert_equal "one, two and three", ["one", "two", "three"].to_sentence(last_word_connector: " and ")
end
- def test_to_sentence_with_fallback_string
- assert_equal "none", [].to_sentence(fallback_string: "none")
- assert_equal "one, two, and three", ["one", "two", "three"].to_sentence(fallback_string: "none")
- end
-
def test_two_elements
assert_equal "one and two", ["one", "two"].to_sentence
assert_equal "one two", ["one", "two"].to_sentence(two_words_connector: " ")
@@ -63,7 +58,7 @@ class ToSentenceTest < ActiveSupport::TestCase
["one", "two"].to_sentence(passing: "invalid option")
end
- assert_equal exception.message, "Unknown key: :passing. Valid keys are: :words_connector, :two_words_connector, :last_word_connector, :locale, :fallback_string"
+ assert_equal exception.message, "Unknown key: :passing. Valid keys are: :words_connector, :two_words_connector, :last_word_connector, :locale"
end
def test_always_returns_string
diff --git a/activesupport/test/core_ext/array/grouping_test.rb b/activesupport/test/core_ext/array/grouping_test.rb
index b06f87c008..4c6aadba8c 100644
--- a/activesupport/test/core_ext/array/grouping_test.rb
+++ b/activesupport/test/core_ext/array/grouping_test.rb
@@ -114,7 +114,7 @@ class SplitTest < ActiveSupport::TestCase
def test_split_with_block
a = (1..10).to_a
assert_equal [[1, 2], [4, 5], [7, 8], [10]], a.split { |i| i % 3 == 0 }
- assert_equal [1, 2, 3, 4, 5, 6, 7, 8, 9 ,10], a
+ assert_equal [1, 2, 3, 4, 5, 6, 7, 8, 9 , 10], a
end
def test_split_with_edge_values
diff --git a/activesupport/test/core_ext/class_test.rb b/activesupport/test/core_ext/class_test.rb
index a9c44907cc..a7905196ae 100644
--- a/activesupport/test/core_ext/class_test.rb
+++ b/activesupport/test/core_ext/class_test.rb
@@ -25,4 +25,14 @@ class ClassTest < ActiveSupport::TestCase
assert_equal [Baz], Bar.subclasses
assert_equal [], Baz.subclasses
end
+
+ def test_descendants_excludes_singleton_classes
+ klass = Parent.new.singleton_class
+ refute Parent.descendants.include?(klass), "descendants should not include singleton classes"
+ end
+
+ def test_subclasses_excludes_singleton_classes
+ klass = Parent.new.singleton_class
+ refute Parent.subclasses.include?(klass), "subclasses should not include singleton classes"
+ end
end
diff --git a/activesupport/test/core_ext/date_and_time_behavior.rb b/activesupport/test/core_ext/date_and_time_behavior.rb
index bf83ac602f..6c77e8f313 100644
--- a/activesupport/test/core_ext/date_and_time_behavior.rb
+++ b/activesupport/test/core_ext/date_and_time_behavior.rb
@@ -2,106 +2,106 @@ require "abstract_unit"
module DateAndTimeBehavior
def test_yesterday
- assert_equal date_time_init(2005,2,21,10,10,10), date_time_init(2005,2,22,10,10,10).yesterday
- assert_equal date_time_init(2005,2,28,10,10,10), date_time_init(2005,3,2,10,10,10).yesterday.yesterday
+ assert_equal date_time_init(2005, 2, 21, 10, 10, 10), date_time_init(2005, 2, 22, 10, 10, 10).yesterday
+ assert_equal date_time_init(2005, 2, 28, 10, 10, 10), date_time_init(2005, 3, 2, 10, 10, 10).yesterday.yesterday
end
def test_prev_day
- assert_equal date_time_init(2005,2,21,10,10,10), date_time_init(2005,2,22,10,10,10).prev_day
- assert_equal date_time_init(2005,2,28,10,10,10), date_time_init(2005,3,2,10,10,10).prev_day.prev_day
+ assert_equal date_time_init(2005, 2, 21, 10, 10, 10), date_time_init(2005, 2, 22, 10, 10, 10).prev_day
+ assert_equal date_time_init(2005, 2, 28, 10, 10, 10), date_time_init(2005, 3, 2, 10, 10, 10).prev_day.prev_day
end
def test_tomorrow
- assert_equal date_time_init(2005,2,23,10,10,10), date_time_init(2005,2,22,10,10,10).tomorrow
- assert_equal date_time_init(2005,3,2,10,10,10), date_time_init(2005,2,28,10,10,10).tomorrow.tomorrow
+ assert_equal date_time_init(2005, 2, 23, 10, 10, 10), date_time_init(2005, 2, 22, 10, 10, 10).tomorrow
+ assert_equal date_time_init(2005, 3, 2, 10, 10, 10), date_time_init(2005, 2, 28, 10, 10, 10).tomorrow.tomorrow
end
def test_next_day
- assert_equal date_time_init(2005,2,23,10,10,10), date_time_init(2005,2,22,10,10,10).next_day
- assert_equal date_time_init(2005,3,2,10,10,10), date_time_init(2005,2,28,10,10,10).next_day.next_day
+ assert_equal date_time_init(2005, 2, 23, 10, 10, 10), date_time_init(2005, 2, 22, 10, 10, 10).next_day
+ assert_equal date_time_init(2005, 3, 2, 10, 10, 10), date_time_init(2005, 2, 28, 10, 10, 10).next_day.next_day
end
def test_days_ago
- assert_equal date_time_init(2005,6,4,10,10,10), date_time_init(2005,6,5,10,10,10).days_ago(1)
- assert_equal date_time_init(2005,5,31,10,10,10), date_time_init(2005,6,5,10,10,10).days_ago(5)
+ assert_equal date_time_init(2005, 6, 4, 10, 10, 10), date_time_init(2005, 6, 5, 10, 10, 10).days_ago(1)
+ assert_equal date_time_init(2005, 5, 31, 10, 10, 10), date_time_init(2005, 6, 5, 10, 10, 10).days_ago(5)
end
def test_days_since
- assert_equal date_time_init(2005,6,6,10,10,10), date_time_init(2005,6,5,10,10,10).days_since(1)
- assert_equal date_time_init(2005,1,1,10,10,10), date_time_init(2004,12,31,10,10,10).days_since(1)
+ assert_equal date_time_init(2005, 6, 6, 10, 10, 10), date_time_init(2005, 6, 5, 10, 10, 10).days_since(1)
+ assert_equal date_time_init(2005, 1, 1, 10, 10, 10), date_time_init(2004, 12, 31, 10, 10, 10).days_since(1)
end
def test_weeks_ago
- assert_equal date_time_init(2005,5,29,10,10,10), date_time_init(2005,6,5,10,10,10).weeks_ago(1)
- assert_equal date_time_init(2005,5,1,10,10,10), date_time_init(2005,6,5,10,10,10).weeks_ago(5)
- assert_equal date_time_init(2005,4,24,10,10,10), date_time_init(2005,6,5,10,10,10).weeks_ago(6)
- assert_equal date_time_init(2005,2,27,10,10,10), date_time_init(2005,6,5,10,10,10).weeks_ago(14)
- assert_equal date_time_init(2004,12,25,10,10,10), date_time_init(2005,1,1,10,10,10).weeks_ago(1)
+ assert_equal date_time_init(2005, 5, 29, 10, 10, 10), date_time_init(2005, 6, 5, 10, 10, 10).weeks_ago(1)
+ assert_equal date_time_init(2005, 5, 1, 10, 10, 10), date_time_init(2005, 6, 5, 10, 10, 10).weeks_ago(5)
+ assert_equal date_time_init(2005, 4, 24, 10, 10, 10), date_time_init(2005, 6, 5, 10, 10, 10).weeks_ago(6)
+ assert_equal date_time_init(2005, 2, 27, 10, 10, 10), date_time_init(2005, 6, 5, 10, 10, 10).weeks_ago(14)
+ assert_equal date_time_init(2004, 12, 25, 10, 10, 10), date_time_init(2005, 1, 1, 10, 10, 10).weeks_ago(1)
end
def test_weeks_since
- assert_equal date_time_init(2005,7,14,10,10,10), date_time_init(2005,7,7,10,10,10).weeks_since(1)
- assert_equal date_time_init(2005,7,14,10,10,10), date_time_init(2005,7,7,10,10,10).weeks_since(1)
- assert_equal date_time_init(2005,7,4,10,10,10), date_time_init(2005,6,27,10,10,10).weeks_since(1)
- assert_equal date_time_init(2005,1,4,10,10,10), date_time_init(2004,12,28,10,10,10).weeks_since(1)
+ assert_equal date_time_init(2005, 7, 14, 10, 10, 10), date_time_init(2005, 7, 7, 10, 10, 10).weeks_since(1)
+ assert_equal date_time_init(2005, 7, 14, 10, 10, 10), date_time_init(2005, 7, 7, 10, 10, 10).weeks_since(1)
+ assert_equal date_time_init(2005, 7, 4, 10, 10, 10), date_time_init(2005, 6, 27, 10, 10, 10).weeks_since(1)
+ assert_equal date_time_init(2005, 1, 4, 10, 10, 10), date_time_init(2004, 12, 28, 10, 10, 10).weeks_since(1)
end
def test_months_ago
- assert_equal date_time_init(2005,5,5,10,10,10), date_time_init(2005,6,5,10,10,10).months_ago(1)
- assert_equal date_time_init(2004,11,5,10,10,10), date_time_init(2005,6,5,10,10,10).months_ago(7)
- assert_equal date_time_init(2004,12,5,10,10,10), date_time_init(2005,6,5,10,10,10).months_ago(6)
- assert_equal date_time_init(2004,6,5,10,10,10), date_time_init(2005,6,5,10,10,10).months_ago(12)
- assert_equal date_time_init(2003,6,5,10,10,10), date_time_init(2005,6,5,10,10,10).months_ago(24)
+ assert_equal date_time_init(2005, 5, 5, 10, 10, 10), date_time_init(2005, 6, 5, 10, 10, 10).months_ago(1)
+ assert_equal date_time_init(2004, 11, 5, 10, 10, 10), date_time_init(2005, 6, 5, 10, 10, 10).months_ago(7)
+ assert_equal date_time_init(2004, 12, 5, 10, 10, 10), date_time_init(2005, 6, 5, 10, 10, 10).months_ago(6)
+ assert_equal date_time_init(2004, 6, 5, 10, 10, 10), date_time_init(2005, 6, 5, 10, 10, 10).months_ago(12)
+ assert_equal date_time_init(2003, 6, 5, 10, 10, 10), date_time_init(2005, 6, 5, 10, 10, 10).months_ago(24)
end
def test_months_since
- assert_equal date_time_init(2005,7,5,10,10,10), date_time_init(2005,6,5,10,10,10).months_since(1)
- assert_equal date_time_init(2006,1,5,10,10,10), date_time_init(2005,12,5,10,10,10).months_since(1)
- assert_equal date_time_init(2005,12,5,10,10,10), date_time_init(2005,6,5,10,10,10).months_since(6)
- assert_equal date_time_init(2006,6,5,10,10,10), date_time_init(2005,12,5,10,10,10).months_since(6)
- assert_equal date_time_init(2006,1,5,10,10,10), date_time_init(2005,6,5,10,10,10).months_since(7)
- assert_equal date_time_init(2006,6,5,10,10,10), date_time_init(2005,6,5,10,10,10).months_since(12)
- assert_equal date_time_init(2007,6,5,10,10,10), date_time_init(2005,6,5,10,10,10).months_since(24)
- assert_equal date_time_init(2005,4,30,10,10,10), date_time_init(2005,3,31,10,10,10).months_since(1)
- assert_equal date_time_init(2005,2,28,10,10,10), date_time_init(2005,1,29,10,10,10).months_since(1)
- assert_equal date_time_init(2005,2,28,10,10,10), date_time_init(2005,1,30,10,10,10).months_since(1)
- assert_equal date_time_init(2005,2,28,10,10,10), date_time_init(2005,1,31,10,10,10).months_since(1)
+ assert_equal date_time_init(2005, 7, 5, 10, 10, 10), date_time_init(2005, 6, 5, 10, 10, 10).months_since(1)
+ assert_equal date_time_init(2006, 1, 5, 10, 10, 10), date_time_init(2005, 12, 5, 10, 10, 10).months_since(1)
+ assert_equal date_time_init(2005, 12, 5, 10, 10, 10), date_time_init(2005, 6, 5, 10, 10, 10).months_since(6)
+ assert_equal date_time_init(2006, 6, 5, 10, 10, 10), date_time_init(2005, 12, 5, 10, 10, 10).months_since(6)
+ assert_equal date_time_init(2006, 1, 5, 10, 10, 10), date_time_init(2005, 6, 5, 10, 10, 10).months_since(7)
+ assert_equal date_time_init(2006, 6, 5, 10, 10, 10), date_time_init(2005, 6, 5, 10, 10, 10).months_since(12)
+ assert_equal date_time_init(2007, 6, 5, 10, 10, 10), date_time_init(2005, 6, 5, 10, 10, 10).months_since(24)
+ assert_equal date_time_init(2005, 4, 30, 10, 10, 10), date_time_init(2005, 3, 31, 10, 10, 10).months_since(1)
+ assert_equal date_time_init(2005, 2, 28, 10, 10, 10), date_time_init(2005, 1, 29, 10, 10, 10).months_since(1)
+ assert_equal date_time_init(2005, 2, 28, 10, 10, 10), date_time_init(2005, 1, 30, 10, 10, 10).months_since(1)
+ assert_equal date_time_init(2005, 2, 28, 10, 10, 10), date_time_init(2005, 1, 31, 10, 10, 10).months_since(1)
end
def test_years_ago
- assert_equal date_time_init(2004,6,5,10,10,10), date_time_init(2005,6,5,10,10,10).years_ago(1)
- assert_equal date_time_init(1998,6,5,10,10,10), date_time_init(2005,6,5,10,10,10).years_ago(7)
- assert_equal date_time_init(2003,2,28,10,10,10), date_time_init(2004,2,29,10,10,10).years_ago(1) # 1 year ago from leap day
+ assert_equal date_time_init(2004, 6, 5, 10, 10, 10), date_time_init(2005, 6, 5, 10, 10, 10).years_ago(1)
+ assert_equal date_time_init(1998, 6, 5, 10, 10, 10), date_time_init(2005, 6, 5, 10, 10, 10).years_ago(7)
+ assert_equal date_time_init(2003, 2, 28, 10, 10, 10), date_time_init(2004, 2, 29, 10, 10, 10).years_ago(1) # 1 year ago from leap day
end
def test_years_since
- assert_equal date_time_init(2006,6,5,10,10,10), date_time_init(2005,6,5,10,10,10).years_since(1)
- assert_equal date_time_init(2012,6,5,10,10,10), date_time_init(2005,6,5,10,10,10).years_since(7)
- assert_equal date_time_init(2005,2,28,10,10,10), date_time_init(2004,2,29,10,10,10).years_since(1) # 1 year since leap day
- assert_equal date_time_init(2182,6,5,10,10,10), date_time_init(2005,6,5,10,10,10).years_since(177)
+ assert_equal date_time_init(2006, 6, 5, 10, 10, 10), date_time_init(2005, 6, 5, 10, 10, 10).years_since(1)
+ assert_equal date_time_init(2012, 6, 5, 10, 10, 10), date_time_init(2005, 6, 5, 10, 10, 10).years_since(7)
+ assert_equal date_time_init(2005, 2, 28, 10, 10, 10), date_time_init(2004, 2, 29, 10, 10, 10).years_since(1) # 1 year since leap day
+ assert_equal date_time_init(2182, 6, 5, 10, 10, 10), date_time_init(2005, 6, 5, 10, 10, 10).years_since(177)
end
def test_beginning_of_month
- assert_equal date_time_init(2005,2,1,0,0,0), date_time_init(2005,2,22,10,10,10).beginning_of_month
+ assert_equal date_time_init(2005, 2, 1, 0, 0, 0), date_time_init(2005, 2, 22, 10, 10, 10).beginning_of_month
end
def test_beginning_of_quarter
- assert_equal date_time_init(2005,1,1,0,0,0), date_time_init(2005,2,15,10,10,10).beginning_of_quarter
- assert_equal date_time_init(2005,1,1,0,0,0), date_time_init(2005,1,1,0,0,0).beginning_of_quarter
- assert_equal date_time_init(2005,10,1,0,0,0), date_time_init(2005,12,31,10,10,10).beginning_of_quarter
- assert_equal date_time_init(2005,4,1,0,0,0), date_time_init(2005,6,30,23,59,59).beginning_of_quarter
+ assert_equal date_time_init(2005, 1, 1, 0, 0, 0), date_time_init(2005, 2, 15, 10, 10, 10).beginning_of_quarter
+ assert_equal date_time_init(2005, 1, 1, 0, 0, 0), date_time_init(2005, 1, 1, 0, 0, 0).beginning_of_quarter
+ assert_equal date_time_init(2005, 10, 1, 0, 0, 0), date_time_init(2005, 12, 31, 10, 10, 10).beginning_of_quarter
+ assert_equal date_time_init(2005, 4, 1, 0, 0, 0), date_time_init(2005, 6, 30, 23, 59, 59).beginning_of_quarter
end
def test_end_of_quarter
- assert_equal date_time_init(2007,3,31,23,59,59,Rational(999999999, 1000)), date_time_init(2007,2,15,10,10,10).end_of_quarter
- assert_equal date_time_init(2007,3,31,23,59,59,Rational(999999999, 1000)), date_time_init(2007,3,31,0,0,0).end_of_quarter
- assert_equal date_time_init(2007,12,31,23,59,59,Rational(999999999, 1000)), date_time_init(2007,12,21,10,10,10).end_of_quarter
- assert_equal date_time_init(2007,6,30,23,59,59,Rational(999999999, 1000)), date_time_init(2007,4,1,0,0,0).end_of_quarter
- assert_equal date_time_init(2008,6,30,23,59,59,Rational(999999999, 1000)), date_time_init(2008,5,31,0,0,0).end_of_quarter
+ assert_equal date_time_init(2007, 3, 31, 23, 59, 59, Rational(999999999, 1000)), date_time_init(2007, 2, 15, 10, 10, 10).end_of_quarter
+ assert_equal date_time_init(2007, 3, 31, 23, 59, 59, Rational(999999999, 1000)), date_time_init(2007, 3, 31, 0, 0, 0).end_of_quarter
+ assert_equal date_time_init(2007, 12, 31, 23, 59, 59, Rational(999999999, 1000)), date_time_init(2007, 12, 21, 10, 10, 10).end_of_quarter
+ assert_equal date_time_init(2007, 6, 30, 23, 59, 59, Rational(999999999, 1000)), date_time_init(2007, 4, 1, 0, 0, 0).end_of_quarter
+ assert_equal date_time_init(2008, 6, 30, 23, 59, 59, Rational(999999999, 1000)), date_time_init(2008, 5, 31, 0, 0, 0).end_of_quarter
end
def test_beginning_of_year
- assert_equal date_time_init(2005,1,1,0,0,0), date_time_init(2005,2,22,10,10,10).beginning_of_year
+ assert_equal date_time_init(2005, 1, 1, 0, 0, 0), date_time_init(2005, 2, 22, 10, 10, 10).beginning_of_year
end
def test_next_week
@@ -110,10 +110,10 @@ module DateAndTimeBehavior
# | 22/2 | | | | | # | | | | 4/3 | | # friday in next week `next_week(:friday)`
# 23/10 | | | | | | # 30/10 | | | | | | # monday in next week `next_week`
# 23/10 | | | | | | # | | 1/11 | | | | # wednesday in next week `next_week(:wednesday)`
- assert_equal date_time_init(2005,2,28,0,0,0), date_time_init(2005,2,22,15,15,10).next_week
- assert_equal date_time_init(2005,3,4,0,0,0), date_time_init(2005,2,22,15,15,10).next_week(:friday)
- assert_equal date_time_init(2006,10,30,0,0,0), date_time_init(2006,10,23,0,0,0).next_week
- assert_equal date_time_init(2006,11,1,0,0,0), date_time_init(2006,10,23,0,0,0).next_week(:wednesday)
+ assert_equal date_time_init(2005, 2, 28, 0, 0, 0), date_time_init(2005, 2, 22, 15, 15, 10).next_week
+ assert_equal date_time_init(2005, 3, 4, 0, 0, 0), date_time_init(2005, 2, 22, 15, 15, 10).next_week(:friday)
+ assert_equal date_time_init(2006, 10, 30, 0, 0, 0), date_time_init(2006, 10, 23, 0, 0, 0).next_week
+ assert_equal date_time_init(2006, 11, 1, 0, 0, 0), date_time_init(2006, 10, 23, 0, 0, 0).next_week(:wednesday)
end
def test_next_week_with_default_beginning_of_week_set
@@ -126,45 +126,47 @@ module DateAndTimeBehavior
end
def test_next_week_at_same_time
- assert_equal date_time_init(2005,2,28,15,15,10), date_time_init(2005,2,22,15,15,10).next_week(:monday, same_time: true)
- assert_equal date_time_init(2005,3,4,15,15,10), date_time_init(2005,2,22,15,15,10).next_week(:friday, same_time: true)
- assert_equal date_time_init(2006,10,30,0,0,0), date_time_init(2006,10,23,0,0,0).next_week(:monday, same_time: true)
- assert_equal date_time_init(2006,11,1,0,0,0), date_time_init(2006,10,23,0,0,0).next_week(:wednesday, same_time: true)
+ assert_equal date_time_init(2005, 2, 28, 15, 15, 10), date_time_init(2005, 2, 22, 15, 15, 10).next_week(:monday, same_time: true)
+ assert_equal date_time_init(2005, 2, 28, 15, 15, 10, 999999), date_time_init(2005, 2, 22, 15, 15, 10, 999999).next_week(:monday, same_time: true)
+ assert_equal date_time_init(2005, 2, 28, 15, 15, 10, Rational(999999999, 1000)), date_time_init(2005, 2, 22, 15, 15, 10, Rational(999999999, 1000)).next_week(:monday, same_time: true)
+ assert_equal date_time_init(2005, 3, 4, 15, 15, 10), date_time_init(2005, 2, 22, 15, 15, 10).next_week(:friday, same_time: true)
+ assert_equal date_time_init(2006, 10, 30, 0, 0, 0), date_time_init(2006, 10, 23, 0, 0, 0).next_week(:monday, same_time: true)
+ assert_equal date_time_init(2006, 11, 1, 0, 0, 0), date_time_init(2006, 10, 23, 0, 0, 0).next_week(:wednesday, same_time: true)
end
def test_next_weekday_on_wednesday
- assert_equal date_time_init(2015,1,8,0,0,0), date_time_init(2015,1,7,0,0,0).next_weekday
- assert_equal date_time_init(2015,1,8,15,15,10), date_time_init(2015,1,7,15,15,10).next_weekday
+ assert_equal date_time_init(2015, 1, 8, 0, 0, 0), date_time_init(2015, 1, 7, 0, 0, 0).next_weekday
+ assert_equal date_time_init(2015, 1, 8, 15, 15, 10), date_time_init(2015, 1, 7, 15, 15, 10).next_weekday
end
def test_next_weekday_on_friday
- assert_equal date_time_init(2015,1,5,0,0,0), date_time_init(2015,1,2,0,0,0).next_weekday
- assert_equal date_time_init(2015,1,5,15,15,10), date_time_init(2015,1,2,15,15,10).next_weekday
+ assert_equal date_time_init(2015, 1, 5, 0, 0, 0), date_time_init(2015, 1, 2, 0, 0, 0).next_weekday
+ assert_equal date_time_init(2015, 1, 5, 15, 15, 10), date_time_init(2015, 1, 2, 15, 15, 10).next_weekday
end
def test_next_weekday_on_saturday
- assert_equal date_time_init(2015,1,5,0,0,0), date_time_init(2015,1,3,0,0,0).next_weekday
- assert_equal date_time_init(2015,1,5,15,15,10), date_time_init(2015,1,3,15,15,10).next_weekday
+ assert_equal date_time_init(2015, 1, 5, 0, 0, 0), date_time_init(2015, 1, 3, 0, 0, 0).next_weekday
+ assert_equal date_time_init(2015, 1, 5, 15, 15, 10), date_time_init(2015, 1, 3, 15, 15, 10).next_weekday
end
def test_next_month_on_31st
- assert_equal date_time_init(2005,9,30,15,15,10), date_time_init(2005,8,31,15,15,10).next_month
+ assert_equal date_time_init(2005, 9, 30, 15, 15, 10), date_time_init(2005, 8, 31, 15, 15, 10).next_month
end
def test_next_quarter_on_31st
- assert_equal date_time_init(2005,11,30,15,15,10), date_time_init(2005,8,31,15,15,10).next_quarter
+ assert_equal date_time_init(2005, 11, 30, 15, 15, 10), date_time_init(2005, 8, 31, 15, 15, 10).next_quarter
end
def test_next_year
- assert_equal date_time_init(2006,6,5,10,10,10), date_time_init(2005,6,5,10,10,10).next_year
+ assert_equal date_time_init(2006, 6, 5, 10, 10, 10), date_time_init(2005, 6, 5, 10, 10, 10).next_year
end
def test_prev_week
- assert_equal date_time_init(2005,2,21,0,0,0), date_time_init(2005,3,1,15,15,10).prev_week
- assert_equal date_time_init(2005,2,22,0,0,0), date_time_init(2005,3,1,15,15,10).prev_week(:tuesday)
- assert_equal date_time_init(2005,2,25,0,0,0), date_time_init(2005,3,1,15,15,10).prev_week(:friday)
- assert_equal date_time_init(2006,10,30,0,0,0), date_time_init(2006,11,6,0,0,0).prev_week
- assert_equal date_time_init(2006,11,15,0,0,0), date_time_init(2006,11,23,0,0,0).prev_week(:wednesday)
+ assert_equal date_time_init(2005, 2, 21, 0, 0, 0), date_time_init(2005, 3, 1, 15, 15, 10).prev_week
+ assert_equal date_time_init(2005, 2, 22, 0, 0, 0), date_time_init(2005, 3, 1, 15, 15, 10).prev_week(:tuesday)
+ assert_equal date_time_init(2005, 2, 25, 0, 0, 0), date_time_init(2005, 3, 1, 15, 15, 10).prev_week(:friday)
+ assert_equal date_time_init(2006, 10, 30, 0, 0, 0), date_time_init(2006, 11, 6, 0, 0, 0).prev_week
+ assert_equal date_time_init(2006, 11, 15, 0, 0, 0), date_time_init(2006, 11, 23, 0, 0, 0).prev_week(:wednesday)
end
def test_prev_week_with_default_beginning_of_week
@@ -177,138 +179,138 @@ module DateAndTimeBehavior
end
def test_prev_week_at_same_time
- assert_equal date_time_init(2005,2,21,15,15,10), date_time_init(2005,3,1,15,15,10).prev_week(:monday, same_time: true)
- assert_equal date_time_init(2005,2,22,15,15,10), date_time_init(2005,3,1,15,15,10).prev_week(:tuesday, same_time: true)
- assert_equal date_time_init(2005,2,25,15,15,10), date_time_init(2005,3,1,15,15,10).prev_week(:friday, same_time: true)
- assert_equal date_time_init(2006,10,30,0,0,0), date_time_init(2006,11,6,0,0,0).prev_week(:monday, same_time: true)
- assert_equal date_time_init(2006,11,15,0,0,0), date_time_init(2006,11,23,0,0,0).prev_week(:wednesday, same_time: true)
+ assert_equal date_time_init(2005, 2, 21, 15, 15, 10), date_time_init(2005, 3, 1, 15, 15, 10).prev_week(:monday, same_time: true)
+ assert_equal date_time_init(2005, 2, 22, 15, 15, 10), date_time_init(2005, 3, 1, 15, 15, 10).prev_week(:tuesday, same_time: true)
+ assert_equal date_time_init(2005, 2, 25, 15, 15, 10), date_time_init(2005, 3, 1, 15, 15, 10).prev_week(:friday, same_time: true)
+ assert_equal date_time_init(2006, 10, 30, 0, 0, 0), date_time_init(2006, 11, 6, 0, 0, 0).prev_week(:monday, same_time: true)
+ assert_equal date_time_init(2006, 11, 15, 0, 0, 0), date_time_init(2006, 11, 23, 0, 0, 0).prev_week(:wednesday, same_time: true)
end
def test_prev_weekday_on_wednesday
- assert_equal date_time_init(2015,1,6,0,0,0), date_time_init(2015,1,7,0,0,0).prev_weekday
- assert_equal date_time_init(2015,1,6,15,15,10), date_time_init(2015,1,7,15,15,10).prev_weekday
+ assert_equal date_time_init(2015, 1, 6, 0, 0, 0), date_time_init(2015, 1, 7, 0, 0, 0).prev_weekday
+ assert_equal date_time_init(2015, 1, 6, 15, 15, 10), date_time_init(2015, 1, 7, 15, 15, 10).prev_weekday
end
def test_prev_weekday_on_monday
- assert_equal date_time_init(2015,1,2,0,0,0), date_time_init(2015,1,5,0,0,0).prev_weekday
- assert_equal date_time_init(2015,1,2,15,15,10), date_time_init(2015,1,5,15,15,10).prev_weekday
+ assert_equal date_time_init(2015, 1, 2, 0, 0, 0), date_time_init(2015, 1, 5, 0, 0, 0).prev_weekday
+ assert_equal date_time_init(2015, 1, 2, 15, 15, 10), date_time_init(2015, 1, 5, 15, 15, 10).prev_weekday
end
def test_prev_weekday_on_sunday
- assert_equal date_time_init(2015,1,2,0,0,0), date_time_init(2015,1,4,0,0,0).prev_weekday
- assert_equal date_time_init(2015,1,2,15,15,10), date_time_init(2015,1,4,15,15,10).prev_weekday
+ assert_equal date_time_init(2015, 1, 2, 0, 0, 0), date_time_init(2015, 1, 4, 0, 0, 0).prev_weekday
+ assert_equal date_time_init(2015, 1, 2, 15, 15, 10), date_time_init(2015, 1, 4, 15, 15, 10).prev_weekday
end
def test_prev_month_on_31st
- assert_equal date_time_init(2004,2,29,10,10,10), date_time_init(2004,3,31,10,10,10).prev_month
+ assert_equal date_time_init(2004, 2, 29, 10, 10, 10), date_time_init(2004, 3, 31, 10, 10, 10).prev_month
end
def test_prev_quarter_on_31st
- assert_equal date_time_init(2004,2,29,10,10,10), date_time_init(2004,5,31,10,10,10).prev_quarter
+ assert_equal date_time_init(2004, 2, 29, 10, 10, 10), date_time_init(2004, 5, 31, 10, 10, 10).prev_quarter
end
def test_prev_year
- assert_equal date_time_init(2004,6,5,10,10,10), date_time_init(2005,6,5,10,10,10).prev_year
+ assert_equal date_time_init(2004, 6, 5, 10, 10, 10), date_time_init(2005, 6, 5, 10, 10, 10).prev_year
end
def test_days_to_week_start
- assert_equal 0, date_time_init(2011,11,01,0,0,0).days_to_week_start(:tuesday)
- assert_equal 1, date_time_init(2011,11,02,0,0,0).days_to_week_start(:tuesday)
- assert_equal 2, date_time_init(2011,11,03,0,0,0).days_to_week_start(:tuesday)
- assert_equal 3, date_time_init(2011,11,04,0,0,0).days_to_week_start(:tuesday)
- assert_equal 4, date_time_init(2011,11,05,0,0,0).days_to_week_start(:tuesday)
- assert_equal 5, date_time_init(2011,11,06,0,0,0).days_to_week_start(:tuesday)
- assert_equal 6, date_time_init(2011,11,07,0,0,0).days_to_week_start(:tuesday)
-
- assert_equal 3, date_time_init(2011,11,03,0,0,0).days_to_week_start(:monday)
- assert_equal 3, date_time_init(2011,11,04,0,0,0).days_to_week_start(:tuesday)
- assert_equal 3, date_time_init(2011,11,05,0,0,0).days_to_week_start(:wednesday)
- assert_equal 3, date_time_init(2011,11,06,0,0,0).days_to_week_start(:thursday)
- assert_equal 3, date_time_init(2011,11,07,0,0,0).days_to_week_start(:friday)
- assert_equal 3, date_time_init(2011,11,8,0,0,0).days_to_week_start(:saturday)
- assert_equal 3, date_time_init(2011,11,9,0,0,0).days_to_week_start(:sunday)
+ assert_equal 0, date_time_init(2011, 11, 01, 0, 0, 0).days_to_week_start(:tuesday)
+ assert_equal 1, date_time_init(2011, 11, 02, 0, 0, 0).days_to_week_start(:tuesday)
+ assert_equal 2, date_time_init(2011, 11, 03, 0, 0, 0).days_to_week_start(:tuesday)
+ assert_equal 3, date_time_init(2011, 11, 04, 0, 0, 0).days_to_week_start(:tuesday)
+ assert_equal 4, date_time_init(2011, 11, 05, 0, 0, 0).days_to_week_start(:tuesday)
+ assert_equal 5, date_time_init(2011, 11, 06, 0, 0, 0).days_to_week_start(:tuesday)
+ assert_equal 6, date_time_init(2011, 11, 07, 0, 0, 0).days_to_week_start(:tuesday)
+
+ assert_equal 3, date_time_init(2011, 11, 03, 0, 0, 0).days_to_week_start(:monday)
+ assert_equal 3, date_time_init(2011, 11, 04, 0, 0, 0).days_to_week_start(:tuesday)
+ assert_equal 3, date_time_init(2011, 11, 05, 0, 0, 0).days_to_week_start(:wednesday)
+ assert_equal 3, date_time_init(2011, 11, 06, 0, 0, 0).days_to_week_start(:thursday)
+ assert_equal 3, date_time_init(2011, 11, 07, 0, 0, 0).days_to_week_start(:friday)
+ assert_equal 3, date_time_init(2011, 11, 8, 0, 0, 0).days_to_week_start(:saturday)
+ assert_equal 3, date_time_init(2011, 11, 9, 0, 0, 0).days_to_week_start(:sunday)
end
def test_days_to_week_start_with_default_set
with_bw_default(:friday) do
- assert_equal 6, Time.local(2012,03,8,0,0,0).days_to_week_start
- assert_equal 5, Time.local(2012,03,7,0,0,0).days_to_week_start
- assert_equal 4, Time.local(2012,03,6,0,0,0).days_to_week_start
- assert_equal 3, Time.local(2012,03,5,0,0,0).days_to_week_start
- assert_equal 2, Time.local(2012,03,4,0,0,0).days_to_week_start
- assert_equal 1, Time.local(2012,03,3,0,0,0).days_to_week_start
- assert_equal 0, Time.local(2012,03,2,0,0,0).days_to_week_start
+ assert_equal 6, Time.local(2012, 03, 8, 0, 0, 0).days_to_week_start
+ assert_equal 5, Time.local(2012, 03, 7, 0, 0, 0).days_to_week_start
+ assert_equal 4, Time.local(2012, 03, 6, 0, 0, 0).days_to_week_start
+ assert_equal 3, Time.local(2012, 03, 5, 0, 0, 0).days_to_week_start
+ assert_equal 2, Time.local(2012, 03, 4, 0, 0, 0).days_to_week_start
+ assert_equal 1, Time.local(2012, 03, 3, 0, 0, 0).days_to_week_start
+ assert_equal 0, Time.local(2012, 03, 2, 0, 0, 0).days_to_week_start
end
end
def test_beginning_of_week
- assert_equal date_time_init(2005,1,31,0,0,0), date_time_init(2005,2,4,10,10,10).beginning_of_week
- assert_equal date_time_init(2005,11,28,0,0,0), date_time_init(2005,11,28,0,0,0).beginning_of_week #monday
- assert_equal date_time_init(2005,11,28,0,0,0), date_time_init(2005,11,29,0,0,0).beginning_of_week #tuesday
- assert_equal date_time_init(2005,11,28,0,0,0), date_time_init(2005,11,30,0,0,0).beginning_of_week #wednesday
- assert_equal date_time_init(2005,11,28,0,0,0), date_time_init(2005,12,01,0,0,0).beginning_of_week #thursday
- assert_equal date_time_init(2005,11,28,0,0,0), date_time_init(2005,12,02,0,0,0).beginning_of_week #friday
- assert_equal date_time_init(2005,11,28,0,0,0), date_time_init(2005,12,03,0,0,0).beginning_of_week #saturday
- assert_equal date_time_init(2005,11,28,0,0,0), date_time_init(2005,12,04,0,0,0).beginning_of_week #sunday
+ assert_equal date_time_init(2005, 1, 31, 0, 0, 0), date_time_init(2005, 2, 4, 10, 10, 10).beginning_of_week
+ assert_equal date_time_init(2005, 11, 28, 0, 0, 0), date_time_init(2005, 11, 28, 0, 0, 0).beginning_of_week #monday
+ assert_equal date_time_init(2005, 11, 28, 0, 0, 0), date_time_init(2005, 11, 29, 0, 0, 0).beginning_of_week #tuesday
+ assert_equal date_time_init(2005, 11, 28, 0, 0, 0), date_time_init(2005, 11, 30, 0, 0, 0).beginning_of_week #wednesday
+ assert_equal date_time_init(2005, 11, 28, 0, 0, 0), date_time_init(2005, 12, 01, 0, 0, 0).beginning_of_week #thursday
+ assert_equal date_time_init(2005, 11, 28, 0, 0, 0), date_time_init(2005, 12, 02, 0, 0, 0).beginning_of_week #friday
+ assert_equal date_time_init(2005, 11, 28, 0, 0, 0), date_time_init(2005, 12, 03, 0, 0, 0).beginning_of_week #saturday
+ assert_equal date_time_init(2005, 11, 28, 0, 0, 0), date_time_init(2005, 12, 04, 0, 0, 0).beginning_of_week #sunday
end
def test_end_of_week
- assert_equal date_time_init(2008,1,6,23,59,59,Rational(999999999, 1000)), date_time_init(2007,12,31,10,10,10).end_of_week
- assert_equal date_time_init(2007,9,2,23,59,59,Rational(999999999, 1000)), date_time_init(2007,8,27,0,0,0).end_of_week #monday
- assert_equal date_time_init(2007,9,2,23,59,59,Rational(999999999, 1000)), date_time_init(2007,8,28,0,0,0).end_of_week #tuesday
- assert_equal date_time_init(2007,9,2,23,59,59,Rational(999999999, 1000)), date_time_init(2007,8,29,0,0,0).end_of_week #wednesday
- assert_equal date_time_init(2007,9,2,23,59,59,Rational(999999999, 1000)), date_time_init(2007,8,30,0,0,0).end_of_week #thursday
- assert_equal date_time_init(2007,9,2,23,59,59,Rational(999999999, 1000)), date_time_init(2007,8,31,0,0,0).end_of_week #friday
- assert_equal date_time_init(2007,9,2,23,59,59,Rational(999999999, 1000)), date_time_init(2007,9,01,0,0,0).end_of_week #saturday
- assert_equal date_time_init(2007,9,2,23,59,59,Rational(999999999, 1000)), date_time_init(2007,9,02,0,0,0).end_of_week #sunday
+ assert_equal date_time_init(2008, 1, 6, 23, 59, 59, Rational(999999999, 1000)), date_time_init(2007, 12, 31, 10, 10, 10).end_of_week
+ assert_equal date_time_init(2007, 9, 2, 23, 59, 59, Rational(999999999, 1000)), date_time_init(2007, 8, 27, 0, 0, 0).end_of_week #monday
+ assert_equal date_time_init(2007, 9, 2, 23, 59, 59, Rational(999999999, 1000)), date_time_init(2007, 8, 28, 0, 0, 0).end_of_week #tuesday
+ assert_equal date_time_init(2007, 9, 2, 23, 59, 59, Rational(999999999, 1000)), date_time_init(2007, 8, 29, 0, 0, 0).end_of_week #wednesday
+ assert_equal date_time_init(2007, 9, 2, 23, 59, 59, Rational(999999999, 1000)), date_time_init(2007, 8, 30, 0, 0, 0).end_of_week #thursday
+ assert_equal date_time_init(2007, 9, 2, 23, 59, 59, Rational(999999999, 1000)), date_time_init(2007, 8, 31, 0, 0, 0).end_of_week #friday
+ assert_equal date_time_init(2007, 9, 2, 23, 59, 59, Rational(999999999, 1000)), date_time_init(2007, 9, 01, 0, 0, 0).end_of_week #saturday
+ assert_equal date_time_init(2007, 9, 2, 23, 59, 59, Rational(999999999, 1000)), date_time_init(2007, 9, 02, 0, 0, 0).end_of_week #sunday
end
def test_end_of_month
- assert_equal date_time_init(2005,3,31,23,59,59,Rational(999999999, 1000)), date_time_init(2005,3,20,10,10,10).end_of_month
- assert_equal date_time_init(2005,2,28,23,59,59,Rational(999999999, 1000)), date_time_init(2005,2,20,10,10,10).end_of_month
- assert_equal date_time_init(2005,4,30,23,59,59,Rational(999999999, 1000)), date_time_init(2005,4,20,10,10,10).end_of_month
+ assert_equal date_time_init(2005, 3, 31, 23, 59, 59, Rational(999999999, 1000)), date_time_init(2005, 3, 20, 10, 10, 10).end_of_month
+ assert_equal date_time_init(2005, 2, 28, 23, 59, 59, Rational(999999999, 1000)), date_time_init(2005, 2, 20, 10, 10, 10).end_of_month
+ assert_equal date_time_init(2005, 4, 30, 23, 59, 59, Rational(999999999, 1000)), date_time_init(2005, 4, 20, 10, 10, 10).end_of_month
end
def test_end_of_year
- assert_equal date_time_init(2007,12,31,23,59,59,Rational(999999999, 1000)), date_time_init(2007,2,22,10,10,10).end_of_year
- assert_equal date_time_init(2007,12,31,23,59,59,Rational(999999999, 1000)), date_time_init(2007,12,31,10,10,10).end_of_year
+ assert_equal date_time_init(2007, 12, 31, 23, 59, 59, Rational(999999999, 1000)), date_time_init(2007, 2, 22, 10, 10, 10).end_of_year
+ assert_equal date_time_init(2007, 12, 31, 23, 59, 59, Rational(999999999, 1000)), date_time_init(2007, 12, 31, 10, 10, 10).end_of_year
end
def test_monday_with_default_beginning_of_week_set
with_bw_default(:saturday) do
- assert_equal date_time_init(2012,9,17,0,0,0), date_time_init(2012,9,18,0,0,0).monday
+ assert_equal date_time_init(2012, 9, 17, 0, 0, 0), date_time_init(2012, 9, 18, 0, 0, 0).monday
end
end
def test_sunday_with_default_beginning_of_week_set
with_bw_default(:wednesday) do
- assert_equal date_time_init(2012,9,23,23,59,59, Rational(999999999, 1000)), date_time_init(2012,9,19,0,0,0).sunday
+ assert_equal date_time_init(2012, 9, 23, 23, 59, 59, Rational(999999999, 1000)), date_time_init(2012, 9, 19, 0, 0, 0).sunday
end
end
def test_on_weekend_on_saturday
- assert date_time_init(2015,1,3,0,0,0).on_weekend?
- assert date_time_init(2015,1,3,15,15,10).on_weekend?
+ assert date_time_init(2015, 1, 3, 0, 0, 0).on_weekend?
+ assert date_time_init(2015, 1, 3, 15, 15, 10).on_weekend?
end
def test_on_weekend_on_sunday
- assert date_time_init(2015,1,4,0,0,0).on_weekend?
- assert date_time_init(2015,1,4,15,15,10).on_weekend?
+ assert date_time_init(2015, 1, 4, 0, 0, 0).on_weekend?
+ assert date_time_init(2015, 1, 4, 15, 15, 10).on_weekend?
end
def test_on_weekend_on_monday
- assert_not date_time_init(2015,1,5,0,0,0).on_weekend?
- assert_not date_time_init(2015,1,5,15,15,10).on_weekend?
+ assert_not date_time_init(2015, 1, 5, 0, 0, 0).on_weekend?
+ assert_not date_time_init(2015, 1, 5, 15, 15, 10).on_weekend?
end
def test_on_weekday_on_sunday
- assert_not date_time_init(2015,1,4,0,0,0).on_weekday?
- assert_not date_time_init(2015,1,4,15,15,10).on_weekday?
+ assert_not date_time_init(2015, 1, 4, 0, 0, 0).on_weekday?
+ assert_not date_time_init(2015, 1, 4, 15, 15, 10).on_weekday?
end
def test_on_weekday_on_monday
- assert date_time_init(2015,1,5,0,0,0).on_weekday?
- assert date_time_init(2015,1,5,15,15,10).on_weekday?
+ assert date_time_init(2015, 1, 5, 0, 0, 0).on_weekday?
+ assert date_time_init(2015, 1, 5, 15, 15, 10).on_weekday?
end
def with_bw_default(bw = :monday)
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 180b3e12aa..4c90460032 100644
--- a/activesupport/test/core_ext/date_and_time_compatibility_test.rb
+++ b/activesupport/test/core_ext/date_and_time_compatibility_test.rb
@@ -40,7 +40,7 @@ class DateAndTimeCompatibilityTest < ActiveSupport::TestCase
def test_datetime_to_time_preserves_timezone
with_preserve_timezone(true) do
with_env_tz "US/Eastern" do
- time = DateTime.new(2016, 4, 23, 15, 11, 12, Rational(1,24)).to_time
+ time = DateTime.new(2016, 4, 23, 15, 11, 12, Rational(1, 24)).to_time
assert_instance_of Time, time
assert_equal @utc_time, time.getutc
@@ -52,7 +52,7 @@ class DateAndTimeCompatibilityTest < ActiveSupport::TestCase
def test_datetime_to_time_does_not_preserve_time_zone
with_preserve_timezone(false) do
with_env_tz "US/Eastern" do
- time = DateTime.new(2016, 4, 23, 15, 11, 12, Rational(1,24)).to_time
+ time = DateTime.new(2016, 4, 23, 15, 11, 12, Rational(1, 24)).to_time
assert_instance_of Time, time
assert_equal @utc_time, time.getutc
diff --git a/activesupport/test/core_ext/date_ext_test.rb b/activesupport/test/core_ext/date_ext_test.rb
index 0655197335..5a90210bb8 100644
--- a/activesupport/test/core_ext/date_ext_test.rb
+++ b/activesupport/test/core_ext/date_ext_test.rb
@@ -4,19 +4,19 @@ require "core_ext/date_and_time_behavior"
require "time_zone_test_helpers"
class DateExtCalculationsTest < ActiveSupport::TestCase
- def date_time_init(year,month,day,*args)
- Date.new(year,month,day)
+ def date_time_init(year, month, day, *args)
+ Date.new(year, month, day)
end
include DateAndTimeBehavior
include TimeZoneTestHelpers
def test_yesterday_in_calendar_reform
- assert_equal Date.new(1582,10,4), Date.new(1582,10,15).yesterday
+ assert_equal Date.new(1582, 10, 4), Date.new(1582, 10, 15).yesterday
end
def test_tomorrow_in_calendar_reform
- assert_equal Date.new(1582,10,15), Date.new(1582,10,4).tomorrow
+ assert_equal Date.new(1582, 10, 15), Date.new(1582, 10, 4).tomorrow
end
def test_to_s
@@ -83,70 +83,70 @@ class DateExtCalculationsTest < ActiveSupport::TestCase
def test_change
assert_equal Date.new(2005, 2, 21), Date.new(2005, 2, 11).change(day: 21)
assert_equal Date.new(2007, 5, 11), Date.new(2005, 2, 11).change(year: 2007, month: 5)
- assert_equal Date.new(2006,2,22), Date.new(2005,2,22).change(year: 2006)
- assert_equal Date.new(2005,6,22), Date.new(2005,2,22).change(month: 6)
+ assert_equal Date.new(2006, 2, 22), Date.new(2005, 2, 22).change(year: 2006)
+ assert_equal Date.new(2005, 6, 22), Date.new(2005, 2, 22).change(month: 6)
end
def test_sunday
- assert_equal Date.new(2008,3,2), Date.new(2008,3,02).sunday
- assert_equal Date.new(2008,3,2), Date.new(2008,2,29).sunday
+ assert_equal Date.new(2008, 3, 2), Date.new(2008, 3, 02).sunday
+ assert_equal Date.new(2008, 3, 2), Date.new(2008, 2, 29).sunday
end
def test_beginning_of_week_in_calendar_reform
- assert_equal Date.new(1582,10,1), Date.new(1582,10,15).beginning_of_week #friday
+ assert_equal Date.new(1582, 10, 1), Date.new(1582, 10, 15).beginning_of_week #friday
end
def test_end_of_week_in_calendar_reform
- assert_equal Date.new(1582,10,17), Date.new(1582,10,4).end_of_week #thursday
+ assert_equal Date.new(1582, 10, 17), Date.new(1582, 10, 4).end_of_week #thursday
end
def test_end_of_year
- assert_equal Date.new(2008,12,31).to_s, Date.new(2008,2,22).end_of_year.to_s
+ assert_equal Date.new(2008, 12, 31).to_s, Date.new(2008, 2, 22).end_of_year.to_s
end
def test_end_of_month
- assert_equal Date.new(2005,3,31), Date.new(2005,3,20).end_of_month
- assert_equal Date.new(2005,2,28), Date.new(2005,2,20).end_of_month
- assert_equal Date.new(2005,4,30), Date.new(2005,4,20).end_of_month
+ assert_equal Date.new(2005, 3, 31), Date.new(2005, 3, 20).end_of_month
+ assert_equal Date.new(2005, 2, 28), Date.new(2005, 2, 20).end_of_month
+ assert_equal Date.new(2005, 4, 30), Date.new(2005, 4, 20).end_of_month
end
def test_prev_year_in_leap_years
- assert_equal Date.new(1999,2,28), Date.new(2000,2,29).prev_year
+ assert_equal Date.new(1999, 2, 28), Date.new(2000, 2, 29).prev_year
end
def test_prev_year_in_calendar_reform
- assert_equal Date.new(1582,10,4), Date.new(1583,10,14).prev_year
+ assert_equal Date.new(1582, 10, 4), Date.new(1583, 10, 14).prev_year
end
def test_last_year
- assert_equal Date.new(2004,6,5), Date.new(2005,6,5).last_year
+ assert_equal Date.new(2004, 6, 5), Date.new(2005, 6, 5).last_year
end
def test_last_year_in_leap_years
- assert_equal Date.new(1999,2,28), Date.new(2000,2,29).last_year
+ assert_equal Date.new(1999, 2, 28), Date.new(2000, 2, 29).last_year
end
def test_last_year_in_calendar_reform
- assert_equal Date.new(1582,10,4), Date.new(1583,10,14).last_year
+ assert_equal Date.new(1582, 10, 4), Date.new(1583, 10, 14).last_year
end
def test_next_year_in_leap_years
- assert_equal Date.new(2001,2,28), Date.new(2000,2,29).next_year
+ assert_equal Date.new(2001, 2, 28), Date.new(2000, 2, 29).next_year
end
def test_next_year_in_calendar_reform
- assert_equal Date.new(1582,10,4), Date.new(1581,10,10).next_year
+ assert_equal Date.new(1582, 10, 4), Date.new(1581, 10, 10).next_year
end
def test_advance
- assert_equal Date.new(2006,2,28), Date.new(2005,2,28).advance(years: 1)
- assert_equal Date.new(2005,6,28), Date.new(2005,2,28).advance(months: 4)
- assert_equal Date.new(2005,3,21), Date.new(2005,2,28).advance(weeks: 3)
- assert_equal Date.new(2005,3,5), Date.new(2005,2,28).advance(days: 5)
- assert_equal Date.new(2012,9,28), Date.new(2005,2,28).advance(years: 7, months: 7)
- assert_equal Date.new(2013,10,3), Date.new(2005,2,28).advance(years: 7, months: 19, days: 5)
- assert_equal Date.new(2013,10,17), Date.new(2005,2,28).advance(years: 7, months: 19, weeks: 2, days: 5)
- assert_equal Date.new(2005,2,28), Date.new(2004,2,29).advance(years: 1) #leap day plus one year
+ assert_equal Date.new(2006, 2, 28), Date.new(2005, 2, 28).advance(years: 1)
+ assert_equal Date.new(2005, 6, 28), Date.new(2005, 2, 28).advance(months: 4)
+ assert_equal Date.new(2005, 3, 21), Date.new(2005, 2, 28).advance(weeks: 3)
+ assert_equal Date.new(2005, 3, 5), Date.new(2005, 2, 28).advance(days: 5)
+ assert_equal Date.new(2012, 9, 28), Date.new(2005, 2, 28).advance(years: 7, months: 7)
+ assert_equal Date.new(2013, 10, 3), Date.new(2005, 2, 28).advance(years: 7, months: 19, days: 5)
+ assert_equal Date.new(2013, 10, 17), Date.new(2005, 2, 28).advance(years: 7, months: 19, weeks: 2, days: 5)
+ assert_equal Date.new(2005, 2, 28), Date.new(2004, 2, 29).advance(years: 1) #leap day plus one year
end
def test_advance_does_first_years_and_then_days
@@ -160,27 +160,27 @@ class DateExtCalculationsTest < ActiveSupport::TestCase
end
def test_advance_in_calendar_reform
- assert_equal Date.new(1582,10,15), Date.new(1582,10,4).advance(days: 1)
- assert_equal Date.new(1582,10,4), Date.new(1582,10,15).advance(days: -1)
+ assert_equal Date.new(1582, 10, 15), Date.new(1582, 10, 4).advance(days: 1)
+ assert_equal Date.new(1582, 10, 4), Date.new(1582, 10, 15).advance(days: -1)
5.upto(14) do |day|
- assert_equal Date.new(1582,10,4), Date.new(1582,9,day).advance(months: 1)
- assert_equal Date.new(1582,10,4), Date.new(1582,11,day).advance(months: -1)
- assert_equal Date.new(1582,10,4), Date.new(1581,10,day).advance(years: 1)
- assert_equal Date.new(1582,10,4), Date.new(1583,10,day).advance(years: -1)
+ assert_equal Date.new(1582, 10, 4), Date.new(1582, 9, day).advance(months: 1)
+ assert_equal Date.new(1582, 10, 4), Date.new(1582, 11, day).advance(months: -1)
+ assert_equal Date.new(1582, 10, 4), Date.new(1581, 10, day).advance(years: 1)
+ assert_equal Date.new(1582, 10, 4), Date.new(1583, 10, day).advance(years: -1)
end
end
def test_last_week
- assert_equal Date.new(2005,5,9), Date.new(2005,5,17).last_week
- assert_equal Date.new(2006,12,25), Date.new(2007,1,7).last_week
- assert_equal Date.new(2010,2,12), Date.new(2010,2,19).last_week(:friday)
- assert_equal Date.new(2010,2,13), Date.new(2010,2,19).last_week(:saturday)
- assert_equal Date.new(2010,2,27), Date.new(2010,3,4).last_week(:saturday)
+ assert_equal Date.new(2005, 5, 9), Date.new(2005, 5, 17).last_week
+ assert_equal Date.new(2006, 12, 25), Date.new(2007, 1, 7).last_week
+ assert_equal Date.new(2010, 2, 12), Date.new(2010, 2, 19).last_week(:friday)
+ assert_equal Date.new(2010, 2, 13), Date.new(2010, 2, 19).last_week(:saturday)
+ assert_equal Date.new(2010, 2, 27), Date.new(2010, 3, 4).last_week(:saturday)
end
def test_next_week_in_calendar_reform
- assert_equal Date.new(1582,10,15), Date.new(1582,9,30).next_week(:friday)
- assert_equal Date.new(1582,10,18), Date.new(1582,10,4).next_week
+ assert_equal Date.new(1582, 10, 15), Date.new(1582, 9, 30).next_week(:friday)
+ assert_equal Date.new(1582, 10, 18), Date.new(1582, 10, 4).next_week
end
def test_last_month_on_31st
@@ -236,97 +236,97 @@ class DateExtCalculationsTest < ActiveSupport::TestCase
end
def test_since
- assert_equal Time.local(2005,2,21,0,0,45), Date.new(2005,2,21).since(45)
+ assert_equal Time.local(2005, 2, 21, 0, 0, 45), Date.new(2005, 2, 21).since(45)
end
def test_since_when_zone_is_set
zone = ActiveSupport::TimeZone["Eastern Time (US & Canada)"]
with_env_tz "UTC" do
with_tz_default zone do
- assert_equal zone.local(2005,2,21,0,0,45), Date.new(2005,2,21).since(45)
- assert_equal zone, Date.new(2005,2,21).since(45).time_zone
+ assert_equal zone.local(2005, 2, 21, 0, 0, 45), Date.new(2005, 2, 21).since(45)
+ assert_equal zone, Date.new(2005, 2, 21).since(45).time_zone
end
end
end
def test_ago
- assert_equal Time.local(2005,2,20,23,59,15), Date.new(2005,2,21).ago(45)
+ assert_equal Time.local(2005, 2, 20, 23, 59, 15), Date.new(2005, 2, 21).ago(45)
end
def test_ago_when_zone_is_set
zone = ActiveSupport::TimeZone["Eastern Time (US & Canada)"]
with_env_tz "UTC" do
with_tz_default zone do
- assert_equal zone.local(2005,2,20,23,59,15), Date.new(2005,2,21).ago(45)
- assert_equal zone, Date.new(2005,2,21).ago(45).time_zone
+ assert_equal zone.local(2005, 2, 20, 23, 59, 15), Date.new(2005, 2, 21).ago(45)
+ assert_equal zone, Date.new(2005, 2, 21).ago(45).time_zone
end
end
end
def test_beginning_of_day
- assert_equal Time.local(2005,2,21,0,0,0), Date.new(2005,2,21).beginning_of_day
+ assert_equal Time.local(2005, 2, 21, 0, 0, 0), Date.new(2005, 2, 21).beginning_of_day
end
def test_middle_of_day
- assert_equal Time.local(2005,2,21,12,0,0), Date.new(2005,2,21).middle_of_day
+ assert_equal Time.local(2005, 2, 21, 12, 0, 0), Date.new(2005, 2, 21).middle_of_day
end
def test_beginning_of_day_when_zone_is_set
zone = ActiveSupport::TimeZone["Eastern Time (US & Canada)"]
with_env_tz "UTC" do
with_tz_default zone do
- assert_equal zone.local(2005,2,21,0,0,0), Date.new(2005,2,21).beginning_of_day
- assert_equal zone, Date.new(2005,2,21).beginning_of_day.time_zone
+ assert_equal zone.local(2005, 2, 21, 0, 0, 0), Date.new(2005, 2, 21).beginning_of_day
+ assert_equal zone, Date.new(2005, 2, 21).beginning_of_day.time_zone
end
end
end
def test_end_of_day
- assert_equal Time.local(2005,2,21,23,59,59,Rational(999999999, 1000)), Date.new(2005,2,21).end_of_day
+ assert_equal Time.local(2005, 2, 21, 23, 59, 59, Rational(999999999, 1000)), Date.new(2005, 2, 21).end_of_day
end
def test_end_of_day_when_zone_is_set
zone = ActiveSupport::TimeZone["Eastern Time (US & Canada)"]
with_env_tz "UTC" do
with_tz_default zone do
- assert_equal zone.local(2005,2,21,23,59,59,Rational(999999999, 1000)), Date.new(2005,2,21).end_of_day
- assert_equal zone, Date.new(2005,2,21).end_of_day.time_zone
+ assert_equal zone.local(2005, 2, 21, 23, 59, 59, Rational(999999999, 1000)), Date.new(2005, 2, 21).end_of_day
+ assert_equal zone, Date.new(2005, 2, 21).end_of_day.time_zone
end
end
end
def test_all_day
- beginning_of_day = Time.local(2011,6,7,0,0,0)
- end_of_day = Time.local(2011,6,7,23,59,59,Rational(999999999, 1000))
- assert_equal beginning_of_day..end_of_day, Date.new(2011,6,7).all_day
+ beginning_of_day = Time.local(2011, 6, 7, 0, 0, 0)
+ end_of_day = Time.local(2011, 6, 7, 23, 59, 59, Rational(999999999, 1000))
+ assert_equal beginning_of_day..end_of_day, Date.new(2011, 6, 7).all_day
end
def test_all_day_when_zone_is_set
zone = ActiveSupport::TimeZone["Hawaii"]
with_env_tz "UTC" do
with_tz_default zone do
- beginning_of_day = zone.local(2011,6,7,0,0,0)
- end_of_day = zone.local(2011,6,7,23,59,59,Rational(999999999, 1000))
- assert_equal beginning_of_day..end_of_day, Date.new(2011,6,7).all_day
+ beginning_of_day = zone.local(2011, 6, 7, 0, 0, 0)
+ end_of_day = zone.local(2011, 6, 7, 23, 59, 59, Rational(999999999, 1000))
+ assert_equal beginning_of_day..end_of_day, Date.new(2011, 6, 7).all_day
end
end
end
def test_all_week
- assert_equal Date.new(2011,6,6)..Date.new(2011,6,12), Date.new(2011,6,7).all_week
- assert_equal Date.new(2011,6,5)..Date.new(2011,6,11), Date.new(2011,6,7).all_week(:sunday)
+ assert_equal Date.new(2011, 6, 6)..Date.new(2011, 6, 12), Date.new(2011, 6, 7).all_week
+ assert_equal Date.new(2011, 6, 5)..Date.new(2011, 6, 11), Date.new(2011, 6, 7).all_week(:sunday)
end
def test_all_month
- assert_equal Date.new(2011,6,1)..Date.new(2011,6,30), Date.new(2011,6,7).all_month
+ assert_equal Date.new(2011, 6, 1)..Date.new(2011, 6, 30), Date.new(2011, 6, 7).all_month
end
def test_all_quarter
- assert_equal Date.new(2011,4,1)..Date.new(2011,6,30), Date.new(2011,6,7).all_quarter
+ assert_equal Date.new(2011, 4, 1)..Date.new(2011, 6, 30), Date.new(2011, 6, 7).all_quarter
end
def test_all_year
- assert_equal Date.new(2011,1,1)..Date.new(2011,12,31), Date.new(2011,6,7).all_year
+ assert_equal Date.new(2011, 1, 1)..Date.new(2011, 12, 31), Date.new(2011, 6, 7).all_year
end
def test_xmlschema
@@ -353,16 +353,16 @@ class DateExtCalculationsTest < ActiveSupport::TestCase
def test_past
Date.stub(:current, Date.new(2000, 1, 1)) do
assert_equal true, Date.new(1999, 12, 31).past?
- assert_equal false, Date.new(2000,1,1).past?
- assert_equal false, Date.new(2000,1,2).past?
+ assert_equal false, Date.new(2000, 1, 1).past?
+ assert_equal false, Date.new(2000, 1, 2).past?
end
end
def test_future
Date.stub(:current, Date.new(2000, 1, 1)) do
assert_equal false, Date.new(1999, 12, 31).future?
- assert_equal false, Date.new(2000,1,1).future?
- assert_equal true, Date.new(2000,1,2).future?
+ assert_equal false, Date.new(2000, 1, 1).future?
+ assert_equal true, Date.new(2000, 1, 2).future?
end
end
@@ -385,7 +385,7 @@ class DateExtCalculationsTest < ActiveSupport::TestCase
def test_date_advance_should_not_change_passed_options_hash
options = { years: 3, months: 11, days: 2 }
- Date.new(2005,2,28).advance(options)
+ Date.new(2005, 2, 28).advance(options)
assert_equal({ years: 3, months: 11, days: 2 }, options)
end
end
diff --git a/activesupport/test/core_ext/date_time_ext_test.rb b/activesupport/test/core_ext/date_time_ext_test.rb
index f2a50f4693..e9be181749 100644
--- a/activesupport/test/core_ext/date_time_ext_test.rb
+++ b/activesupport/test/core_ext/date_time_ext_test.rb
@@ -4,8 +4,8 @@ require "core_ext/date_and_time_behavior"
require "time_zone_test_helpers"
class DateTimeExtCalculationsTest < ActiveSupport::TestCase
- def date_time_init(year,month,day,hour,minute,second,*args)
- DateTime.civil(year,month,day,hour,minute,second)
+ def date_time_init(year, month, day, hour, minute, second, *args)
+ DateTime.civil(year, month, day, hour, minute, second)
end
include DateAndTimeBehavior
@@ -45,7 +45,7 @@ class DateTimeExtCalculationsTest < ActiveSupport::TestCase
assert_instance_of Time, DateTime.new(2016, 3, 11, 15, 11, 12, 0).localtime
assert_equal Time.local(2016, 3, 11, 10, 11, 12), DateTime.new(2016, 3, 11, 15, 11, 12, 0).localtime
assert_equal Time.local(2016, 3, 21, 11, 11, 12), DateTime.new(2016, 3, 21, 15, 11, 12, 0).localtime
- assert_equal Time.local(2016, 4, 1, 11, 11, 12), DateTime.new(2016, 4, 1, 16, 11, 12, Rational(1,24)).localtime
+ assert_equal Time.local(2016, 4, 1, 11, 11, 12), DateTime.new(2016, 4, 1, 16, 11, 12, Rational(1, 24)).localtime
end
end
@@ -54,7 +54,7 @@ class DateTimeExtCalculationsTest < ActiveSupport::TestCase
assert_instance_of Time, DateTime.new(2016, 3, 11, 15, 11, 12, 0).getlocal
assert_equal Time.local(2016, 3, 11, 10, 11, 12), DateTime.new(2016, 3, 11, 15, 11, 12, 0).getlocal
assert_equal Time.local(2016, 3, 21, 11, 11, 12), DateTime.new(2016, 3, 21, 15, 11, 12, 0).getlocal
- assert_equal Time.local(2016, 4, 1, 11, 11, 12), DateTime.new(2016, 4, 1, 16, 11, 12, Rational(1,24)).getlocal
+ assert_equal Time.local(2016, 4, 1, 11, 11, 12), DateTime.new(2016, 4, 1, 16, 11, 12, Rational(1, 24)).getlocal
end
end
@@ -90,108 +90,108 @@ class DateTimeExtCalculationsTest < ActiveSupport::TestCase
end
def test_seconds_since_midnight
- assert_equal 1,DateTime.civil(2005,1,1,0,0,1).seconds_since_midnight
- assert_equal 60,DateTime.civil(2005,1,1,0,1,0).seconds_since_midnight
- assert_equal 3660,DateTime.civil(2005,1,1,1,1,0).seconds_since_midnight
- assert_equal 86399,DateTime.civil(2005,1,1,23,59,59).seconds_since_midnight
+ assert_equal 1, DateTime.civil(2005, 1, 1, 0, 0, 1).seconds_since_midnight
+ assert_equal 60, DateTime.civil(2005, 1, 1, 0, 1, 0).seconds_since_midnight
+ assert_equal 3660, DateTime.civil(2005, 1, 1, 1, 1, 0).seconds_since_midnight
+ assert_equal 86399, DateTime.civil(2005, 1, 1, 23, 59, 59).seconds_since_midnight
end
def test_seconds_until_end_of_day
- assert_equal 0, DateTime.civil(2005,1,1,23,59,59).seconds_until_end_of_day
- assert_equal 1, DateTime.civil(2005,1,1,23,59,58).seconds_until_end_of_day
- assert_equal 60, DateTime.civil(2005,1,1,23,58,59).seconds_until_end_of_day
- assert_equal 3660, DateTime.civil(2005,1,1,22,58,59).seconds_until_end_of_day
- assert_equal 86399, DateTime.civil(2005,1,1,0,0,0).seconds_until_end_of_day
+ assert_equal 0, DateTime.civil(2005, 1, 1, 23, 59, 59).seconds_until_end_of_day
+ assert_equal 1, DateTime.civil(2005, 1, 1, 23, 59, 58).seconds_until_end_of_day
+ assert_equal 60, DateTime.civil(2005, 1, 1, 23, 58, 59).seconds_until_end_of_day
+ assert_equal 3660, DateTime.civil(2005, 1, 1, 22, 58, 59).seconds_until_end_of_day
+ assert_equal 86399, DateTime.civil(2005, 1, 1, 0, 0, 0).seconds_until_end_of_day
end
def test_beginning_of_day
- assert_equal DateTime.civil(2005,2,4,0,0,0), DateTime.civil(2005,2,4,10,10,10).beginning_of_day
+ assert_equal DateTime.civil(2005, 2, 4, 0, 0, 0), DateTime.civil(2005, 2, 4, 10, 10, 10).beginning_of_day
end
def test_middle_of_day
- assert_equal DateTime.civil(2005,2,4,12,0,0), DateTime.civil(2005,2,4,10,10,10).middle_of_day
+ assert_equal DateTime.civil(2005, 2, 4, 12, 0, 0), DateTime.civil(2005, 2, 4, 10, 10, 10).middle_of_day
end
def test_end_of_day
- assert_equal DateTime.civil(2005,2,4,23,59,59), DateTime.civil(2005,2,4,10,10,10).end_of_day
+ assert_equal DateTime.civil(2005, 2, 4, 23, 59, 59), DateTime.civil(2005, 2, 4, 10, 10, 10).end_of_day
end
def test_beginning_of_hour
- assert_equal DateTime.civil(2005,2,4,19,0,0), DateTime.civil(2005,2,4,19,30,10).beginning_of_hour
+ assert_equal DateTime.civil(2005, 2, 4, 19, 0, 0), DateTime.civil(2005, 2, 4, 19, 30, 10).beginning_of_hour
end
def test_end_of_hour
- assert_equal DateTime.civil(2005,2,4,19,59,59), DateTime.civil(2005,2,4,19,30,10).end_of_hour
+ assert_equal DateTime.civil(2005, 2, 4, 19, 59, 59), DateTime.civil(2005, 2, 4, 19, 30, 10).end_of_hour
end
def test_beginning_of_minute
- assert_equal DateTime.civil(2005,2,4,19,30,0), DateTime.civil(2005,2,4,19,30,10).beginning_of_minute
+ assert_equal DateTime.civil(2005, 2, 4, 19, 30, 0), DateTime.civil(2005, 2, 4, 19, 30, 10).beginning_of_minute
end
def test_end_of_minute
- assert_equal DateTime.civil(2005,2,4,19,30,59), DateTime.civil(2005,2,4,19,30,10).end_of_minute
+ assert_equal DateTime.civil(2005, 2, 4, 19, 30, 59), DateTime.civil(2005, 2, 4, 19, 30, 10).end_of_minute
end
def test_end_of_month
- assert_equal DateTime.civil(2005,3,31,23,59,59), DateTime.civil(2005,3,20,10,10,10).end_of_month
- assert_equal DateTime.civil(2005,2,28,23,59,59), DateTime.civil(2005,2,20,10,10,10).end_of_month
- assert_equal DateTime.civil(2005,4,30,23,59,59), DateTime.civil(2005,4,20,10,10,10).end_of_month
+ assert_equal DateTime.civil(2005, 3, 31, 23, 59, 59), DateTime.civil(2005, 3, 20, 10, 10, 10).end_of_month
+ assert_equal DateTime.civil(2005, 2, 28, 23, 59, 59), DateTime.civil(2005, 2, 20, 10, 10, 10).end_of_month
+ assert_equal DateTime.civil(2005, 4, 30, 23, 59, 59), DateTime.civil(2005, 4, 20, 10, 10, 10).end_of_month
end
def test_last_year
- assert_equal DateTime.civil(2004,6,5,10), DateTime.civil(2005,6,5,10,0,0).last_year
+ assert_equal DateTime.civil(2004, 6, 5, 10), DateTime.civil(2005, 6, 5, 10, 0, 0).last_year
end
def test_ago
- assert_equal DateTime.civil(2005,2,22,10,10,9), DateTime.civil(2005,2,22,10,10,10).ago(1)
- assert_equal DateTime.civil(2005,2,22,9,10,10), DateTime.civil(2005,2,22,10,10,10).ago(3600)
- assert_equal DateTime.civil(2005,2,20,10,10,10), DateTime.civil(2005,2,22,10,10,10).ago(86400*2)
- assert_equal DateTime.civil(2005,2,20,9,9,45), DateTime.civil(2005,2,22,10,10,10).ago(86400*2 + 3600 + 25)
+ assert_equal DateTime.civil(2005, 2, 22, 10, 10, 9), DateTime.civil(2005, 2, 22, 10, 10, 10).ago(1)
+ assert_equal DateTime.civil(2005, 2, 22, 9, 10, 10), DateTime.civil(2005, 2, 22, 10, 10, 10).ago(3600)
+ assert_equal DateTime.civil(2005, 2, 20, 10, 10, 10), DateTime.civil(2005, 2, 22, 10, 10, 10).ago(86400 * 2)
+ assert_equal DateTime.civil(2005, 2, 20, 9, 9, 45), DateTime.civil(2005, 2, 22, 10, 10, 10).ago(86400 * 2 + 3600 + 25)
end
def test_since
- assert_equal DateTime.civil(2005,2,22,10,10,11), DateTime.civil(2005,2,22,10,10,10).since(1)
- assert_equal DateTime.civil(2005,2,22,11,10,10), DateTime.civil(2005,2,22,10,10,10).since(3600)
- assert_equal DateTime.civil(2005,2,24,10,10,10), DateTime.civil(2005,2,22,10,10,10).since(86400*2)
- assert_equal DateTime.civil(2005,2,24,11,10,35), DateTime.civil(2005,2,22,10,10,10).since(86400*2 + 3600 + 25)
- assert_equal DateTime.civil(2005,2,22,10,10,11), DateTime.civil(2005,2,22,10,10,10).since(1.333)
- assert_equal DateTime.civil(2005,2,22,10,10,12), DateTime.civil(2005,2,22,10,10,10).since(1.667)
+ assert_equal DateTime.civil(2005, 2, 22, 10, 10, 11), DateTime.civil(2005, 2, 22, 10, 10, 10).since(1)
+ assert_equal DateTime.civil(2005, 2, 22, 11, 10, 10), DateTime.civil(2005, 2, 22, 10, 10, 10).since(3600)
+ assert_equal DateTime.civil(2005, 2, 24, 10, 10, 10), DateTime.civil(2005, 2, 22, 10, 10, 10).since(86400 * 2)
+ assert_equal DateTime.civil(2005, 2, 24, 11, 10, 35), DateTime.civil(2005, 2, 22, 10, 10, 10).since(86400 * 2 + 3600 + 25)
+ assert_equal DateTime.civil(2005, 2, 22, 10, 10, 11), DateTime.civil(2005, 2, 22, 10, 10, 10).since(1.333)
+ assert_equal DateTime.civil(2005, 2, 22, 10, 10, 12), DateTime.civil(2005, 2, 22, 10, 10, 10).since(1.667)
end
def test_change
- assert_equal DateTime.civil(2006,2,22,15,15,10), DateTime.civil(2005,2,22,15,15,10).change(year: 2006)
- assert_equal DateTime.civil(2005,6,22,15,15,10), DateTime.civil(2005,2,22,15,15,10).change(month: 6)
- assert_equal DateTime.civil(2012,9,22,15,15,10), DateTime.civil(2005,2,22,15,15,10).change(year: 2012, month: 9)
- assert_equal DateTime.civil(2005,2,22,16), DateTime.civil(2005,2,22,15,15,10).change(hour: 16)
- assert_equal DateTime.civil(2005,2,22,16,45), DateTime.civil(2005,2,22,15,15,10).change(hour: 16, min: 45)
- assert_equal DateTime.civil(2005,2,22,15,45), DateTime.civil(2005,2,22,15,15,10).change(min: 45)
+ assert_equal DateTime.civil(2006, 2, 22, 15, 15, 10), DateTime.civil(2005, 2, 22, 15, 15, 10).change(year: 2006)
+ assert_equal DateTime.civil(2005, 6, 22, 15, 15, 10), DateTime.civil(2005, 2, 22, 15, 15, 10).change(month: 6)
+ assert_equal DateTime.civil(2012, 9, 22, 15, 15, 10), DateTime.civil(2005, 2, 22, 15, 15, 10).change(year: 2012, month: 9)
+ assert_equal DateTime.civil(2005, 2, 22, 16), DateTime.civil(2005, 2, 22, 15, 15, 10).change(hour: 16)
+ assert_equal DateTime.civil(2005, 2, 22, 16, 45), DateTime.civil(2005, 2, 22, 15, 15, 10).change(hour: 16, min: 45)
+ assert_equal DateTime.civil(2005, 2, 22, 15, 45), DateTime.civil(2005, 2, 22, 15, 15, 10).change(min: 45)
# datetime with fractions of a second
- assert_equal DateTime.civil(2005,2,1,15,15,10.7), DateTime.civil(2005,2,22,15,15,10.7).change(day: 1)
+ assert_equal DateTime.civil(2005, 2, 1, 15, 15, 10.7), DateTime.civil(2005, 2, 22, 15, 15, 10.7).change(day: 1)
end
def test_advance
- assert_equal DateTime.civil(2006,2,28,15,15,10), DateTime.civil(2005,2,28,15,15,10).advance(years: 1)
- assert_equal DateTime.civil(2005,6,28,15,15,10), DateTime.civil(2005,2,28,15,15,10).advance(months: 4)
- assert_equal DateTime.civil(2005,3,21,15,15,10), DateTime.civil(2005,2,28,15,15,10).advance(weeks: 3)
- assert_equal DateTime.civil(2005,3,5,15,15,10), DateTime.civil(2005,2,28,15,15,10).advance(days: 5)
- assert_equal DateTime.civil(2012,9,28,15,15,10), DateTime.civil(2005,2,28,15,15,10).advance(years: 7, months: 7)
- assert_equal DateTime.civil(2013,10,3,15,15,10), DateTime.civil(2005,2,28,15,15,10).advance(years: 7, months: 19, days: 5)
- assert_equal DateTime.civil(2013,10,17,15,15,10), DateTime.civil(2005,2,28,15,15,10).advance(years: 7, months: 19, weeks: 2, days: 5)
- assert_equal DateTime.civil(2001,12,27,15,15,10), DateTime.civil(2005,2,28,15,15,10).advance(years: -3, months: -2, days: -1)
- assert_equal DateTime.civil(2005,2,28,15,15,10), DateTime.civil(2004,2,29,15,15,10).advance(years: 1) #leap day plus one year
- assert_equal DateTime.civil(2005,2,28,20,15,10), DateTime.civil(2005,2,28,15,15,10).advance(hours: 5)
- assert_equal DateTime.civil(2005,2,28,15,22,10), DateTime.civil(2005,2,28,15,15,10).advance(minutes: 7)
- assert_equal DateTime.civil(2005,2,28,15,15,19), DateTime.civil(2005,2,28,15,15,10).advance(seconds: 9)
- assert_equal DateTime.civil(2005,2,28,20,22,19), DateTime.civil(2005,2,28,15,15,10).advance(hours: 5, minutes: 7, seconds: 9)
- assert_equal DateTime.civil(2005,2,28,10,8,1), DateTime.civil(2005,2,28,15,15,10).advance(hours: -5, minutes: -7, seconds: -9)
- assert_equal DateTime.civil(2013,10,17,20,22,19), DateTime.civil(2005,2,28,15,15,10).advance(years: 7, months: 19, weeks: 2, days: 5, hours: 5, minutes: 7, seconds: 9)
+ assert_equal DateTime.civil(2006, 2, 28, 15, 15, 10), DateTime.civil(2005, 2, 28, 15, 15, 10).advance(years: 1)
+ assert_equal DateTime.civil(2005, 6, 28, 15, 15, 10), DateTime.civil(2005, 2, 28, 15, 15, 10).advance(months: 4)
+ assert_equal DateTime.civil(2005, 3, 21, 15, 15, 10), DateTime.civil(2005, 2, 28, 15, 15, 10).advance(weeks: 3)
+ assert_equal DateTime.civil(2005, 3, 5, 15, 15, 10), DateTime.civil(2005, 2, 28, 15, 15, 10).advance(days: 5)
+ assert_equal DateTime.civil(2012, 9, 28, 15, 15, 10), DateTime.civil(2005, 2, 28, 15, 15, 10).advance(years: 7, months: 7)
+ assert_equal DateTime.civil(2013, 10, 3, 15, 15, 10), DateTime.civil(2005, 2, 28, 15, 15, 10).advance(years: 7, months: 19, days: 5)
+ assert_equal DateTime.civil(2013, 10, 17, 15, 15, 10), DateTime.civil(2005, 2, 28, 15, 15, 10).advance(years: 7, months: 19, weeks: 2, days: 5)
+ assert_equal DateTime.civil(2001, 12, 27, 15, 15, 10), DateTime.civil(2005, 2, 28, 15, 15, 10).advance(years: -3, months: -2, days: -1)
+ assert_equal DateTime.civil(2005, 2, 28, 15, 15, 10), DateTime.civil(2004, 2, 29, 15, 15, 10).advance(years: 1) #leap day plus one year
+ assert_equal DateTime.civil(2005, 2, 28, 20, 15, 10), DateTime.civil(2005, 2, 28, 15, 15, 10).advance(hours: 5)
+ assert_equal DateTime.civil(2005, 2, 28, 15, 22, 10), DateTime.civil(2005, 2, 28, 15, 15, 10).advance(minutes: 7)
+ assert_equal DateTime.civil(2005, 2, 28, 15, 15, 19), DateTime.civil(2005, 2, 28, 15, 15, 10).advance(seconds: 9)
+ assert_equal DateTime.civil(2005, 2, 28, 20, 22, 19), DateTime.civil(2005, 2, 28, 15, 15, 10).advance(hours: 5, minutes: 7, seconds: 9)
+ assert_equal DateTime.civil(2005, 2, 28, 10, 8, 1), DateTime.civil(2005, 2, 28, 15, 15, 10).advance(hours: -5, minutes: -7, seconds: -9)
+ assert_equal DateTime.civil(2013, 10, 17, 20, 22, 19), DateTime.civil(2005, 2, 28, 15, 15, 10).advance(years: 7, months: 19, weeks: 2, days: 5, hours: 5, minutes: 7, seconds: 9)
end
def test_advance_partial_days
- assert_equal DateTime.civil(2012,9,29,13,15,10), DateTime.civil(2012,9,28,1,15,10).advance(days: 1.5)
- assert_equal DateTime.civil(2012,9,28,13,15,10), DateTime.civil(2012,9,28,1,15,10).advance(days: 0.5)
- assert_equal DateTime.civil(2012,10,29,13,15,10), DateTime.civil(2012,9,28,1,15,10).advance(days: 1.5, months: 1)
+ assert_equal DateTime.civil(2012, 9, 29, 13, 15, 10), DateTime.civil(2012, 9, 28, 1, 15, 10).advance(days: 1.5)
+ assert_equal DateTime.civil(2012, 9, 28, 13, 15, 10), DateTime.civil(2012, 9, 28, 1, 15, 10).advance(days: 0.5)
+ assert_equal DateTime.civil(2012, 10, 29, 13, 15, 10), DateTime.civil(2012, 9, 28, 1, 15, 10).advance(days: 1.5, months: 1)
end
def test_advanced_processes_first_the_date_deltas_and_then_the_time_deltas
@@ -203,11 +203,11 @@ class DateTimeExtCalculationsTest < ActiveSupport::TestCase
end
def test_last_week
- assert_equal DateTime.civil(2005,2,21), DateTime.civil(2005,3,1,15,15,10).last_week
- assert_equal DateTime.civil(2005,2,22), DateTime.civil(2005,3,1,15,15,10).last_week(:tuesday)
- assert_equal DateTime.civil(2005,2,25), DateTime.civil(2005,3,1,15,15,10).last_week(:friday)
- assert_equal DateTime.civil(2006,10,30), DateTime.civil(2006,11,6,0,0,0).last_week
- assert_equal DateTime.civil(2006,11,15), DateTime.civil(2006,11,23,0,0,0).last_week(:wednesday)
+ assert_equal DateTime.civil(2005, 2, 21), DateTime.civil(2005, 3, 1, 15, 15, 10).last_week
+ assert_equal DateTime.civil(2005, 2, 22), DateTime.civil(2005, 3, 1, 15, 15, 10).last_week(:tuesday)
+ assert_equal DateTime.civil(2005, 2, 25), DateTime.civil(2005, 3, 1, 15, 15, 10).last_week(:friday)
+ assert_equal DateTime.civil(2006, 10, 30), DateTime.civil(2006, 11, 6, 0, 0, 0).last_week
+ assert_equal DateTime.civil(2006, 11, 15), DateTime.civil(2006, 11, 23, 0, 0, 0).last_week(:wednesday)
end
def test_date_time_should_have_correct_last_week_for_leap_year
@@ -233,51 +233,51 @@ class DateTimeExtCalculationsTest < ActiveSupport::TestCase
def test_today_with_offset
Date.stub(:current, Date.new(2000, 1, 1)) do
- assert_equal false, DateTime.civil(1999,12,31,23,59,59, Rational(-18000, 86400)).today?
- assert_equal true, DateTime.civil(2000,1,1,0,0,0, Rational(-18000, 86400)).today?
- assert_equal true, DateTime.civil(2000,1,1,23,59,59, Rational(-18000, 86400)).today?
- assert_equal false, DateTime.civil(2000,1,2,0,0,0, Rational(-18000, 86400)).today?
+ assert_equal false, DateTime.civil(1999, 12, 31, 23, 59, 59, Rational(-18000, 86400)).today?
+ assert_equal true, DateTime.civil(2000, 1, 1, 0, 0, 0, Rational(-18000, 86400)).today?
+ assert_equal true, DateTime.civil(2000, 1, 1, 23, 59, 59, Rational(-18000, 86400)).today?
+ assert_equal false, DateTime.civil(2000, 1, 2, 0, 0, 0, Rational(-18000, 86400)).today?
end
end
def test_today_without_offset
Date.stub(:current, Date.new(2000, 1, 1)) do
- assert_equal false, DateTime.civil(1999,12,31,23,59,59).today?
- assert_equal true, DateTime.civil(2000,1,1,0).today?
- assert_equal true, DateTime.civil(2000,1,1,23,59,59).today?
- assert_equal false, DateTime.civil(2000,1,2,0).today?
+ assert_equal false, DateTime.civil(1999, 12, 31, 23, 59, 59).today?
+ assert_equal true, DateTime.civil(2000, 1, 1, 0).today?
+ assert_equal true, DateTime.civil(2000, 1, 1, 23, 59, 59).today?
+ assert_equal false, DateTime.civil(2000, 1, 2, 0).today?
end
end
def test_past_with_offset
- DateTime.stub(:current, DateTime.civil(2005,2,10,15,30,45, Rational(-18000, 86400))) do
- assert_equal true, DateTime.civil(2005,2,10,15,30,44, Rational(-18000, 86400)).past?
- assert_equal false, DateTime.civil(2005,2,10,15,30,45, Rational(-18000, 86400)).past?
- assert_equal false, DateTime.civil(2005,2,10,15,30,46, Rational(-18000, 86400)).past?
+ DateTime.stub(:current, DateTime.civil(2005, 2, 10, 15, 30, 45, Rational(-18000, 86400))) do
+ assert_equal true, DateTime.civil(2005, 2, 10, 15, 30, 44, Rational(-18000, 86400)).past?
+ assert_equal false, DateTime.civil(2005, 2, 10, 15, 30, 45, Rational(-18000, 86400)).past?
+ assert_equal false, DateTime.civil(2005, 2, 10, 15, 30, 46, Rational(-18000, 86400)).past?
end
end
def test_past_without_offset
- DateTime.stub(:current, DateTime.civil(2005,2,10,15,30,45, Rational(-18000, 86400))) do
- assert_equal true, DateTime.civil(2005,2,10,20,30,44).past?
- assert_equal false, DateTime.civil(2005,2,10,20,30,45).past?
- assert_equal false, DateTime.civil(2005,2,10,20,30,46).past?
+ DateTime.stub(:current, DateTime.civil(2005, 2, 10, 15, 30, 45, Rational(-18000, 86400))) do
+ assert_equal true, DateTime.civil(2005, 2, 10, 20, 30, 44).past?
+ assert_equal false, DateTime.civil(2005, 2, 10, 20, 30, 45).past?
+ assert_equal false, DateTime.civil(2005, 2, 10, 20, 30, 46).past?
end
end
def test_future_with_offset
- DateTime.stub(:current, DateTime.civil(2005,2,10,15,30,45, Rational(-18000, 86400))) do
- assert_equal false, DateTime.civil(2005,2,10,15,30,44, Rational(-18000, 86400)).future?
- assert_equal false, DateTime.civil(2005,2,10,15,30,45, Rational(-18000, 86400)).future?
- assert_equal true, DateTime.civil(2005,2,10,15,30,46, Rational(-18000, 86400)).future?
+ DateTime.stub(:current, DateTime.civil(2005, 2, 10, 15, 30, 45, Rational(-18000, 86400))) do
+ assert_equal false, DateTime.civil(2005, 2, 10, 15, 30, 44, Rational(-18000, 86400)).future?
+ assert_equal false, DateTime.civil(2005, 2, 10, 15, 30, 45, Rational(-18000, 86400)).future?
+ assert_equal true, DateTime.civil(2005, 2, 10, 15, 30, 46, Rational(-18000, 86400)).future?
end
end
def test_future_without_offset
- DateTime.stub(:current, DateTime.civil(2005,2,10,15,30,45, Rational(-18000, 86400))) do
- assert_equal false, DateTime.civil(2005,2,10,20,30,44).future?
- assert_equal false, DateTime.civil(2005,2,10,20,30,45).future?
- assert_equal true, DateTime.civil(2005,2,10,20,30,46).future?
+ DateTime.stub(:current, DateTime.civil(2005, 2, 10, 15, 30, 45, Rational(-18000, 86400))) do
+ assert_equal false, DateTime.civil(2005, 2, 10, 20, 30, 44).future?
+ assert_equal false, DateTime.civil(2005, 2, 10, 20, 30, 45).future?
+ assert_equal true, DateTime.civil(2005, 2, 10, 20, 30, 46).future?
end
end
@@ -329,8 +329,8 @@ class DateTimeExtCalculationsTest < ActiveSupport::TestCase
assert_equal 0, DateTime.civil(2005, 2, 21, 10, 11, 12).utc_offset
assert_equal 0, DateTime.civil(2005, 2, 21, 10, 11, 12, 0).utc_offset
assert_equal 21600, DateTime.civil(2005, 2, 21, 10, 11, 12, 0.25).utc_offset
- assert_equal( -21600, DateTime.civil(2005, 2, 21, 10, 11, 12, -0.25).utc_offset )
- assert_equal( -18000, DateTime.civil(2005, 2, 21, 10, 11, 12, Rational(-5, 24)).utc_offset )
+ assert_equal(-21600, DateTime.civil(2005, 2, 21, 10, 11, 12, -0.25).utc_offset)
+ assert_equal(-18000, DateTime.civil(2005, 2, 21, 10, 11, 12, Rational(-5, 24)).utc_offset)
end
def test_utc
@@ -367,16 +367,16 @@ class DateTimeExtCalculationsTest < ActiveSupport::TestCase
end
def test_compare_with_time_with_zone
- assert_equal 1, DateTime.civil(2000) <=> ActiveSupport::TimeWithZone.new( Time.utc(1999, 12, 31, 23, 59, 59), ActiveSupport::TimeZone["UTC"] )
- assert_equal 0, DateTime.civil(2000) <=> ActiveSupport::TimeWithZone.new( Time.utc(2000, 1, 1, 0, 0, 0), ActiveSupport::TimeZone["UTC"] )
- assert_equal(-1, DateTime.civil(2000) <=> ActiveSupport::TimeWithZone.new( Time.utc(2000, 1, 1, 0, 0, 1), ActiveSupport::TimeZone["UTC"] ))
+ assert_equal 1, DateTime.civil(2000) <=> ActiveSupport::TimeWithZone.new(Time.utc(1999, 12, 31, 23, 59, 59), ActiveSupport::TimeZone["UTC"])
+ assert_equal 0, DateTime.civil(2000) <=> ActiveSupport::TimeWithZone.new(Time.utc(2000, 1, 1, 0, 0, 0), ActiveSupport::TimeZone["UTC"])
+ assert_equal(-1, DateTime.civil(2000) <=> ActiveSupport::TimeWithZone.new(Time.utc(2000, 1, 1, 0, 0, 1), ActiveSupport::TimeZone["UTC"]))
end
def test_compare_with_string
assert_equal 1, DateTime.civil(2000) <=> Time.utc(1999, 12, 31, 23, 59, 59).to_s
assert_equal 0, DateTime.civil(2000) <=> Time.utc(2000, 1, 1, 0, 0, 0).to_s
- assert_equal( -1, DateTime.civil(2000) <=> Time.utc(2000, 1, 1, 0, 0, 1).to_s)
- assert_equal nil, DateTime.civil(2000) <=> "Invalid as Time"
+ assert_equal(-1, DateTime.civil(2000) <=> Time.utc(2000, 1, 1, 0, 0, 1).to_s)
+ assert_nil DateTime.civil(2000) <=> "Invalid as Time"
end
def test_compare_with_integer
@@ -399,27 +399,27 @@ class DateTimeExtCalculationsTest < ActiveSupport::TestCase
def test_to_f
assert_equal 946684800.0, DateTime.civil(2000).to_f
- assert_equal 946684800.0, DateTime.civil(1999,12,31,19,0,0,Rational(-5,24)).to_f
- assert_equal 946684800.5, DateTime.civil(1999,12,31,19,0,0.5,Rational(-5,24)).to_f
+ assert_equal 946684800.0, DateTime.civil(1999, 12, 31, 19, 0, 0, Rational(-5, 24)).to_f
+ assert_equal 946684800.5, DateTime.civil(1999, 12, 31, 19, 0, 0.5, Rational(-5, 24)).to_f
end
def test_to_i
assert_equal 946684800, DateTime.civil(2000).to_i
- assert_equal 946684800, DateTime.civil(1999,12,31,19,0,0,Rational(-5,24)).to_i
+ assert_equal 946684800, DateTime.civil(1999, 12, 31, 19, 0, 0, Rational(-5, 24)).to_i
end
def test_usec
assert_equal 0, DateTime.civil(2000).usec
- assert_equal 500000, DateTime.civil(2000, 1, 1, 0, 0, Rational(1,2)).usec
+ assert_equal 500000, DateTime.civil(2000, 1, 1, 0, 0, Rational(1, 2)).usec
end
def test_nsec
assert_equal 0, DateTime.civil(2000).nsec
- assert_equal 500000000, DateTime.civil(2000, 1, 1, 0, 0, Rational(1,2)).nsec
+ assert_equal 500000000, DateTime.civil(2000, 1, 1, 0, 0, Rational(1, 2)).nsec
end
def test_subsec
assert_equal 0, DateTime.civil(2000).subsec
- assert_equal Rational(1,2), DateTime.civil(2000, 1, 1, 0, 0, Rational(1,2)).subsec
+ assert_equal Rational(1, 2), DateTime.civil(2000, 1, 1, 0, 0, Rational(1, 2)).subsec
end
end
diff --git a/activesupport/test/core_ext/duration_test.rb b/activesupport/test/core_ext/duration_test.rb
index a881768470..fc0dd41d0e 100644
--- a/activesupport/test/core_ext/duration_test.rb
+++ b/activesupport/test/core_ext/duration_test.rb
@@ -21,7 +21,7 @@ class DurationTest < ActiveSupport::TestCase
end
def test_instance_of
- assert 1.minute.instance_of?(Fixnum)
+ assert 1.minute.instance_of?(1.class)
assert 2.days.instance_of?(ActiveSupport::Duration)
assert !3.second.instance_of?(Numeric)
end
@@ -75,7 +75,7 @@ class DurationTest < ActiveSupport::TestCase
current_locale = I18n.default_locale
I18n.default_locale = :de
I18n.backend.store_translations(:de, support: { array: { last_word_connector: " und " } })
- assert_equal "10 years, 1 month und 1 day", (10.years + 1.month + 1.day).inspect
+ assert_equal "10 years, 1 month und 1 day", (10.years + 1.month + 1.day).inspect
ensure
I18n.default_locale = current_locale
end
@@ -89,7 +89,7 @@ class DurationTest < ActiveSupport::TestCase
end
def test_time_plus_duration_returns_same_time_datatype
- twz = ActiveSupport::TimeWithZone.new(nil, ActiveSupport::TimeZone["Moscow"] , Time.utc(2016,4,28,00,45))
+ twz = ActiveSupport::TimeWithZone.new(nil, ActiveSupport::TimeZone["Moscow"] , Time.utc(2016, 4, 28, 00, 45))
now = Time.now.utc
%w( second minute hour day week month year ).each do |unit|
assert_equal((now + 1.send(unit)).class, Time, "Time + 1.#{unit} must be Time")
@@ -153,10 +153,10 @@ class DurationTest < ActiveSupport::TestCase
Time.stub(:now, Time.local(2000)) do
# since
assert_not_instance_of ActiveSupport::TimeWithZone, 5.seconds.since
- assert_equal Time.local(2000,1,1,0,0,5), 5.seconds.since
+ assert_equal Time.local(2000, 1, 1, 0, 0, 5), 5.seconds.since
# ago
assert_not_instance_of ActiveSupport::TimeWithZone, 5.seconds.ago
- assert_equal Time.local(1999,12,31,23,59,55), 5.seconds.ago
+ assert_equal Time.local(1999, 12, 31, 23, 59, 55), 5.seconds.ago
end
end
end
@@ -167,11 +167,11 @@ class DurationTest < ActiveSupport::TestCase
Time.stub(:now, Time.local(2000)) do
# since
assert_instance_of ActiveSupport::TimeWithZone, 5.seconds.since
- assert_equal Time.utc(2000,1,1,0,0,5), 5.seconds.since.time
+ assert_equal Time.utc(2000, 1, 1, 0, 0, 5), 5.seconds.since.time
assert_equal "Eastern Time (US & Canada)", 5.seconds.since.time_zone.name
# ago
assert_instance_of ActiveSupport::TimeWithZone, 5.seconds.ago
- assert_equal Time.utc(1999,12,31,23,59,55), 5.seconds.ago.time
+ assert_equal Time.utc(1999, 12, 31, 23, 59, 55), 5.seconds.ago.time
assert_equal "Eastern Time (US & Canada)", 5.seconds.ago.time_zone.name
end
end
@@ -181,13 +181,13 @@ class DurationTest < ActiveSupport::TestCase
def test_adding_hours_across_dst_boundary
with_env_tz "CET" do
- assert_equal Time.local(2009,3,29,0,0,0) + 24.hours, Time.local(2009,3,30,1,0,0)
+ assert_equal Time.local(2009, 3, 29, 0, 0, 0) + 24.hours, Time.local(2009, 3, 30, 1, 0, 0)
end
end
def test_adding_day_across_dst_boundary
with_env_tz "CET" do
- assert_equal Time.local(2009,3,29,0,0,0) + 1.day, Time.local(2009,3,30,0,0,0)
+ assert_equal Time.local(2009, 3, 29, 0, 0, 0) + 1.day, Time.local(2009, 3, 30, 0, 0, 0)
end
end
@@ -196,7 +196,7 @@ class DurationTest < ActiveSupport::TestCase
assert_nothing_raised do
1.minute.times { counter += 1 }
end
- assert_equal counter, 60
+ assert_equal 60, counter
end
def test_as_json
@@ -213,7 +213,7 @@ class DurationTest < ActiveSupport::TestCase
when 1.day
"ok"
end
- assert_equal cased, "ok"
+ assert_equal "ok", cased
end
def test_respond_to
@@ -292,11 +292,11 @@ class DurationTest < ActiveSupport::TestCase
def test_iso8601_output_precision
expectations = [
- [nil, "P1Y1MT5.55S", 1.year + 1.month + (5.55).seconds ],
- [0, "P1Y1MT6S", 1.year + 1.month + (5.55).seconds ],
- [1, "P1Y1MT5.5S", 1.year + 1.month + (5.55).seconds ],
- [2, "P1Y1MT5.55S", 1.year + 1.month + (5.55).seconds ],
- [3, "P1Y1MT5.550S", 1.year + 1.month + (5.55).seconds ],
+ [nil, "P1Y1MT8.55S", 1.year + 1.month + (8.55).seconds ],
+ [0, "P1Y1MT9S", 1.year + 1.month + (8.55).seconds ],
+ [1, "P1Y1MT8.6S", 1.year + 1.month + (8.55).seconds ],
+ [2, "P1Y1MT8.55S", 1.year + 1.month + (8.55).seconds ],
+ [3, "P1Y1MT8.550S", 1.year + 1.month + (8.55).seconds ],
[nil, "PT1S", 1.second ],
[2, "PT1.00S", 1.second ],
[nil, "PT1.4S", (1.4).seconds ],
@@ -319,7 +319,49 @@ class DurationTest < ActiveSupport::TestCase
time = Time.current
patterns.each do |pattern|
duration = ActiveSupport::Duration.parse(pattern)
- assert_equal time+duration, time+ActiveSupport::Duration.parse(duration.iso8601), pattern.inspect
+ assert_equal time + duration, time + ActiveSupport::Duration.parse(duration.iso8601), pattern.inspect
end
end
+
+ def test_iso8601_parsing_across_spring_dst_boundary
+ with_env_tz eastern_time_zone do
+ with_tz_default "Eastern Time (US & Canada)" do
+ travel_to Time.utc(2016, 3, 11) do
+ assert_equal 604800, ActiveSupport::Duration.parse("P7D").to_i
+ assert_equal 604800, ActiveSupport::Duration.parse("P1W").to_i
+ end
+ end
+ end
+ end
+
+ def test_iso8601_parsing_across_autumn_dst_boundary
+ with_env_tz eastern_time_zone do
+ with_tz_default "Eastern Time (US & Canada)" do
+ travel_to Time.utc(2016, 11, 4) do
+ assert_equal 604800, ActiveSupport::Duration.parse("P7D").to_i
+ assert_equal 604800, ActiveSupport::Duration.parse("P1W").to_i
+ end
+ end
+ end
+ end
+
+ def test_adding_durations_do_not_hold_prior_states
+ time = Time.parse("Nov 29, 2016")
+ # If the implementation adds and subtracts 3 months, the
+ # resulting date would have been in February so the day will
+ # change to the 29th.
+ d1 = 3.months - 3.months
+ d2 = 2.months - 2.months
+
+ assert_equal time + d1, time + d2
+ end
+
+ private
+ def eastern_time_zone
+ if Gem.win_platform?
+ "EST5EDT"
+ else
+ "America/New_York"
+ end
+ end
end
diff --git a/activesupport/test/core_ext/enumerable_test.rb b/activesupport/test/core_ext/enumerable_test.rb
index 9072957e0e..4f1ab993b8 100644
--- a/activesupport/test/core_ext/enumerable_test.rb
+++ b/activesupport/test/core_ext/enumerable_test.rb
@@ -22,7 +22,7 @@ class EnumerableTests < ActiveSupport::TestCase
end
end
- def assert_typed_equal(e, v, cls, msg=nil)
+ def assert_typed_equal(e, v, cls, msg = nil)
assert_kind_of(cls, v, msg)
assert_equal(e, v, msg)
end
@@ -70,7 +70,7 @@ class EnumerableTests < ActiveSupport::TestCase
sum = GenericEnumerable.new([1.quo(2), 1.quo(3)]).sum
assert_typed_equal(5.quo(6), sum, Rational)
- sum = GenericEnumerable.new([2.0, 3.0*Complex::I]).sum
+ sum = GenericEnumerable.new([2.0, 3.0 * Complex::I]).sum
assert_typed_equal(Complex(2.0, 3.0), sum, Complex)
assert_typed_equal(2.0, sum.real, Float)
assert_typed_equal(3.0, sum.imag, Float)
@@ -157,7 +157,7 @@ class EnumerableTests < ActiveSupport::TestCase
sum = [1.quo(2), 1.quo(3)].sum
assert_typed_equal(5.quo(6), sum, Rational)
- sum = [2.0, 3.0*Complex::I].sum
+ sum = [2.0, 3.0 * Complex::I].sum
assert_typed_equal(Complex(2.0, 3.0), sum, Complex)
assert_typed_equal(2.0, sum.real, Float)
assert_typed_equal(3.0, sum.imag, Float)
@@ -172,7 +172,7 @@ class EnumerableTests < ActiveSupport::TestCase
payments.index_by(&:price))
assert_equal Enumerator, payments.index_by.class
if Enumerator.method_defined? :size
- assert_equal nil, payments.index_by.size
+ assert_nil payments.index_by.size
assert_equal 42, (1..42).index_by.size
end
assert_equal({ 5 => Payment.new(5), 15 => Payment.new(15), 10 => Payment.new(10) },
@@ -180,18 +180,18 @@ class EnumerableTests < ActiveSupport::TestCase
end
def test_many
- assert_equal false, GenericEnumerable.new([] ).many?
- assert_equal false, GenericEnumerable.new([ 1 ] ).many?
- assert_equal true, GenericEnumerable.new([ 1, 2 ] ).many?
+ assert_equal false, GenericEnumerable.new([]).many?
+ assert_equal false, GenericEnumerable.new([ 1 ]).many?
+ assert_equal true, GenericEnumerable.new([ 1, 2 ]).many?
- assert_equal false, GenericEnumerable.new([] ).many? { |x| x > 1 }
- assert_equal false, GenericEnumerable.new([ 2 ] ).many? { |x| x > 1 }
- assert_equal false, GenericEnumerable.new([ 1, 2 ] ).many? { |x| x > 1 }
+ assert_equal false, GenericEnumerable.new([]).many? { |x| x > 1 }
+ assert_equal false, GenericEnumerable.new([ 2 ]).many? { |x| x > 1 }
+ assert_equal false, GenericEnumerable.new([ 1, 2 ]).many? { |x| x > 1 }
assert_equal true, GenericEnumerable.new([ 1, 2, 2 ]).many? { |x| x > 1 }
end
def test_many_iterates_only_on_what_is_needed
- infinity = 1.0/0.0
+ infinity = 1.0 / 0.0
very_long_enum = 0..infinity
assert_equal true, very_long_enum.many?
assert_equal true, very_long_enum.many? { |x| x > 100 }
diff --git a/activesupport/test/core_ext/hash_ext_test.rb b/activesupport/test/core_ext/hash_ext_test.rb
index ae35adb19b..bea0a84b53 100644
--- a/activesupport/test/core_ext/hash_ext_test.rb
+++ b/activesupport/test/core_ext/hash_ext_test.rb
@@ -111,7 +111,7 @@ class HashExtTest < ActiveSupport::TestCase
transformed_hash = @mixed.dup
transformed_hash.transform_keys! { |key| key.to_s.upcase }
assert_equal @upcase_strings, transformed_hash
- assert_equal @mixed, :a => 1, "b" => 2
+ assert_equal({ :a => 1, "b" => 2 }, @mixed)
end
def test_deep_transform_keys!
@@ -127,7 +127,7 @@ class HashExtTest < ActiveSupport::TestCase
transformed_hash = @nested_mixed.deep_dup
transformed_hash.deep_transform_keys! { |key| key.to_s.upcase }
assert_equal @nested_upcase_strings, transformed_hash
- assert_equal @nested_mixed, "a" => { b: { "c" => 3 } }
+ assert_equal({ "a" => { b: { "c" => 3 } } }, @nested_mixed)
end
def test_symbolize_keys
@@ -167,7 +167,7 @@ class HashExtTest < ActiveSupport::TestCase
transformed_hash = @mixed.dup
transformed_hash.deep_symbolize_keys!
assert_equal @symbols, transformed_hash
- assert_equal @mixed, :a => 1, "b" => 2
+ assert_equal({ :a => 1, "b" => 2 }, @mixed)
end
def test_deep_symbolize_keys!
@@ -183,7 +183,7 @@ class HashExtTest < ActiveSupport::TestCase
transformed_hash = @nested_mixed.deep_dup
transformed_hash.deep_symbolize_keys!
assert_equal @nested_symbols, transformed_hash
- assert_equal @nested_mixed, "a" => { b: { "c" => 3 } }
+ assert_equal({ "a" => { b: { "c" => 3 } } }, @nested_mixed)
end
def test_symbolize_keys_preserves_keys_that_cant_be_symbolized
@@ -243,7 +243,7 @@ class HashExtTest < ActiveSupport::TestCase
transformed_hash = @mixed.dup
transformed_hash.stringify_keys!
assert_equal @strings, transformed_hash
- assert_equal @mixed, :a => 1, "b" => 2
+ assert_equal({ :a => 1, "b" => 2 }, @mixed)
end
def test_deep_stringify_keys!
@@ -259,7 +259,7 @@ class HashExtTest < ActiveSupport::TestCase
transformed_hash = @nested_mixed.deep_dup
transformed_hash.deep_stringify_keys!
assert_equal @nested_strings, transformed_hash
- assert_equal @nested_mixed, "a" => { b: { "c" => 3 } }
+ assert_equal({ "a" => { b: { "c" => 3 } } }, @nested_mixed)
end
def test_symbolize_keys_for_hash_with_indifferent_access
@@ -390,8 +390,8 @@ class HashExtTest < ActiveSupport::TestCase
assert_equal 1, hash[:a]
assert_equal true, hash[:b]
assert_equal false, hash[:c]
- assert_equal nil, hash[:d]
- assert_equal nil, hash[:e]
+ assert_nil hash[:d]
+ assert_nil hash[:e]
end
def test_indifferent_reading_with_nonnil_default
@@ -404,7 +404,7 @@ class HashExtTest < ActiveSupport::TestCase
assert_equal 1, hash[:a]
assert_equal true, hash[:b]
assert_equal false, hash[:c]
- assert_equal nil, hash[:d]
+ assert_nil hash[:d]
assert_equal 1, hash[:e]
end
@@ -414,11 +414,11 @@ class HashExtTest < ActiveSupport::TestCase
hash["b"] = 2
hash[3] = 3
- assert_equal hash["a"], 1
- assert_equal hash["b"], 2
- assert_equal hash[:a], 1
- assert_equal hash[:b], 2
- assert_equal hash[3], 3
+ assert_equal 1, hash["a"]
+ assert_equal 2, hash["b"]
+ assert_equal 1, hash[:a]
+ assert_equal 2, hash[:b]
+ assert_equal 3, hash[3]
end
def test_indifferent_update
@@ -430,16 +430,16 @@ class HashExtTest < ActiveSupport::TestCase
updated_with_symbols = hash.update(@symbols)
updated_with_mixed = hash.update(@mixed)
- assert_equal updated_with_strings[:a], 1
- assert_equal updated_with_strings["a"], 1
- assert_equal updated_with_strings["b"], 2
+ assert_equal 1, updated_with_strings[:a]
+ assert_equal 1, updated_with_strings["a"]
+ assert_equal 2, updated_with_strings["b"]
- assert_equal updated_with_symbols[:a], 1
- assert_equal updated_with_symbols["b"], 2
- assert_equal updated_with_symbols[:b], 2
+ assert_equal 1, updated_with_symbols[:a]
+ assert_equal 2, updated_with_symbols["b"]
+ assert_equal 2, updated_with_symbols[:b]
- assert_equal updated_with_mixed[:a], 1
- assert_equal updated_with_mixed["b"], 2
+ assert_equal 1, updated_with_mixed[:a]
+ assert_equal 2, updated_with_mixed["b"]
assert [updated_with_strings, updated_with_symbols, updated_with_mixed].all? { |h| h.keys.size == 2 }
end
@@ -447,7 +447,7 @@ class HashExtTest < ActiveSupport::TestCase
def test_update_with_to_hash_conversion
hash = HashWithIndifferentAccess.new
hash.update HashByConversion.new(a: 1)
- assert_equal hash["a"], 1
+ assert_equal 1, hash["a"]
end
def test_indifferent_merging
@@ -472,7 +472,7 @@ class HashExtTest < ActiveSupport::TestCase
def test_merge_with_to_hash_conversion
hash = HashWithIndifferentAccess.new
merged = hash.merge HashByConversion.new(a: 1)
- assert_equal merged["a"], 1
+ assert_equal 1, merged["a"]
end
def test_indifferent_replace
@@ -536,15 +536,15 @@ class HashExtTest < ActiveSupport::TestCase
def test_indifferent_deleting
get_hash = proc { { a: "foo" }.with_indifferent_access }
hash = get_hash.call
- assert_equal hash.delete(:a), "foo"
- assert_equal hash.delete(:a), nil
+ assert_equal "foo", hash.delete(:a)
+ assert_nil hash.delete(:a)
hash = get_hash.call
- assert_equal hash.delete("a"), "foo"
- assert_equal hash.delete("a"), nil
+ assert_equal "foo", hash.delete("a")
+ assert_nil hash.delete("a")
end
def test_indifferent_select
- hash = ActiveSupport::HashWithIndifferentAccess.new(@strings).select { |k,v| v == 1 }
+ hash = ActiveSupport::HashWithIndifferentAccess.new(@strings).select { |k, v| v == 1 }
assert_equal({ "a" => 1 }, hash)
assert_instance_of ActiveSupport::HashWithIndifferentAccess, hash
@@ -556,21 +556,21 @@ class HashExtTest < ActiveSupport::TestCase
end
def test_indifferent_select_returns_a_hash_when_unchanged
- hash = ActiveSupport::HashWithIndifferentAccess.new(@strings).select { |k,v| true }
+ hash = ActiveSupport::HashWithIndifferentAccess.new(@strings).select { |k, v| true }
assert_instance_of ActiveSupport::HashWithIndifferentAccess, hash
end
def test_indifferent_select_bang
indifferent_strings = ActiveSupport::HashWithIndifferentAccess.new(@strings)
- indifferent_strings.select! { |k,v| v == 1 }
+ indifferent_strings.select! { |k, v| v == 1 }
assert_equal({ "a" => 1 }, indifferent_strings)
assert_instance_of ActiveSupport::HashWithIndifferentAccess, indifferent_strings
end
def test_indifferent_reject
- hash = ActiveSupport::HashWithIndifferentAccess.new(@strings).reject { |k,v| v != 1 }
+ hash = ActiveSupport::HashWithIndifferentAccess.new(@strings).reject { |k, v| v != 1 }
assert_equal({ "a" => 1 }, hash)
assert_instance_of ActiveSupport::HashWithIndifferentAccess, hash
@@ -583,7 +583,7 @@ class HashExtTest < ActiveSupport::TestCase
def test_indifferent_reject_bang
indifferent_strings = ActiveSupport::HashWithIndifferentAccess.new(@strings)
- indifferent_strings.reject! { |k,v| v != 1 }
+ indifferent_strings.reject! { |k, v| v != 1 }
assert_equal({ "a" => 1 }, indifferent_strings)
assert_instance_of ActiveSupport::HashWithIndifferentAccess, indifferent_strings
@@ -759,9 +759,9 @@ class HashExtTest < ActiveSupport::TestCase
hash_1 = { a: "a", b: "b", c: { c1: "c1", c2: "c2", c3: { d1: "d1" } } }
hash_2 = { a: 1, c: { c1: 2, c3: { d2: "d2" } } }
expected = { a: [:a, "a", 1], b: "b", c: { c1: [:c1, "c1", 2], c2: "c2", c3: { d1: "d1", d2: "d2" } } }
- assert_equal(expected, hash_1.deep_merge(hash_2) { |k,o,n| [k, o, n] })
+ assert_equal(expected, hash_1.deep_merge(hash_2) { |k, o, n| [k, o, n] })
- hash_1.deep_merge!(hash_2) { |k,o,n| [k, o, n] }
+ hash_1.deep_merge!(hash_2) { |k, o, n| [k, o, n] }
assert_equal expected, hash_1
end
@@ -933,8 +933,8 @@ class HashExtTest < ActiveSupport::TestCase
extracted = original.extract!(:a, :x)
assert_equal expected, extracted
- assert_equal nil, extracted[:a]
- assert_equal nil, extracted[:x]
+ assert_nil extracted[:a]
+ assert_nil extracted[:x]
end
def test_indifferent_extract
@@ -1017,7 +1017,7 @@ class HashExtTest < ActiveSupport::TestCase
assert_equal({}, h)
h = @symbols.dup
- assert_equal(nil, h.compact!)
+ assert_nil(h.compact!)
assert_equal(@symbols, h)
end
@@ -1051,13 +1051,6 @@ class HashExtTest < ActiveSupport::TestCase
assert_nothing_raised { hash.to_hash }
end
- def test_new_from_hash_copying_default_should_not_raise_when_default_proc_does
- hash = Hash.new
- hash.default_proc = proc { |h, k| raise "walrus" }
-
- assert_deprecated { HashWithIndifferentAccess.new_from_hash_copying_default(hash) }
- end
-
def test_new_with_to_hash_conversion_copies_default
normal_hash = Hash.new(3)
normal_hash[:a] = 1
@@ -1227,8 +1220,8 @@ class HashToXmlTest < ActiveSupport::TestCase
def test_timezoned_attributes
xml = {
- created_at: Time.utc(1999,2,2),
- local_created_at: Time.utc(1999,2,2).in_time_zone("Eastern Time (US & Canada)")
+ created_at: Time.utc(1999, 2, 2),
+ local_created_at: Time.utc(1999, 2, 2).in_time_zone("Eastern Time (US & Canada)")
}.to_xml(@xml_options)
assert_match %r{<created-at type=\"dateTime\">1999-02-02T00:00:00Z</created-at>}, xml
assert_match %r{<local-created-at type=\"dateTime\">1999-02-01T19:00:00-05:00</local-created-at>}, xml
@@ -1537,7 +1530,7 @@ class HashToXmlTest < ActiveSupport::TestCase
weight: 0.5,
chunky: true,
price: BigDecimal("12.50"),
- expires_at: Time.utc(2007,12,25,12,34,56),
+ expires_at: Time.utc(2007, 12, 25, 12, 34, 56),
notes: "",
illustration: "babe.png",
caption: "That'll do, pig."
@@ -1596,13 +1589,13 @@ class HashToXmlTest < ActiveSupport::TestCase
end
def test_should_use_default_proc_for_unknown_key
- hash_wia = HashWithIndifferentAccess.new { 1 + 2 }
+ hash_wia = HashWithIndifferentAccess.new { 1 + 2 }
assert_equal 3, hash_wia[:new_key]
end
def test_should_return_nil_if_no_key_is_supplied
- hash_wia = HashWithIndifferentAccess.new { 1 + 2 }
- assert_equal nil, hash_wia.default
+ hash_wia = HashWithIndifferentAccess.new { 1 + 2 }
+ assert_nil hash_wia.default
end
def test_should_use_default_value_for_unknown_key
diff --git a/activesupport/test/core_ext/load_error_test.rb b/activesupport/test/core_ext/load_error_test.rb
index b50a35b2cb..44ff6bb051 100644
--- a/activesupport/test/core_ext/load_error_test.rb
+++ b/activesupport/test/core_ext/load_error_test.rb
@@ -1,14 +1,6 @@
require "abstract_unit"
require "active_support/core_ext/load_error"
-class TestMissingSourceFile < ActiveSupport::TestCase
- def test_it_is_deprecated
- assert_deprecated do
- MissingSourceFile.new
- end
- end
-end
-
class TestLoadError < ActiveSupport::TestCase
def test_with_require
assert_raise(LoadError) { require 'no_this_file_don\'t_exist' }
diff --git a/activesupport/test/core_ext/module/attribute_accessor_per_thread_test.rb b/activesupport/test/core_ext/module/attribute_accessor_per_thread_test.rb
index b816fa50e3..af240bc38d 100644
--- a/activesupport/test/core_ext/module/attribute_accessor_per_thread_test.rb
+++ b/activesupport/test/core_ext/module/attribute_accessor_per_thread_test.rb
@@ -121,11 +121,11 @@ class ModuleAttributeAccessorPerThreadTest < ActiveSupport::TestCase
def test_should_not_affect_superclass_if_subclass_set_value
@class.foo = "super"
- assert_equal @class.foo, "super"
+ assert_equal "super", @class.foo
assert_nil @subclass.foo
@subclass.foo = "sub"
- assert_equal @class.foo, "super"
- assert_equal @subclass.foo, "sub"
+ assert_equal "super", @class.foo
+ assert_equal "sub", @subclass.foo
end
end
diff --git a/activesupport/test/core_ext/module/qualified_const_test.rb b/activesupport/test/core_ext/module/qualified_const_test.rb
deleted file mode 100644
index 418bc80ab9..0000000000
--- a/activesupport/test/core_ext/module/qualified_const_test.rb
+++ /dev/null
@@ -1,118 +0,0 @@
-require "abstract_unit"
-require "active_support/core_ext/module/qualified_const"
-
-module QualifiedConstTestMod
- X = false
-
- module M
- X = 1
-
- class C
- X = 2
- end
- end
-
- module N
- include M
- end
-end
-
-class QualifiedConstTest < ActiveSupport::TestCase
- test "Object.qualified_const_defined?" do
- assert_deprecated do
- assert Object.qualified_const_defined?("QualifiedConstTestMod")
- assert !Object.qualified_const_defined?("NonExistingQualifiedConstTestMod")
-
- assert Object.qualified_const_defined?("QualifiedConstTestMod::X")
- assert !Object.qualified_const_defined?("QualifiedConstTestMod::Y")
-
- assert Object.qualified_const_defined?("QualifiedConstTestMod::M::X")
- assert !Object.qualified_const_defined?("QualifiedConstTestMod::M::Y")
-
- if Module.method(:const_defined?).arity == 1
- assert !Object.qualified_const_defined?("QualifiedConstTestMod::N::X")
- else
- assert Object.qualified_const_defined?("QualifiedConstTestMod::N::X")
- assert !Object.qualified_const_defined?("QualifiedConstTestMod::N::X", false)
- assert Object.qualified_const_defined?("QualifiedConstTestMod::N::X", true)
- end
- end
- end
-
- test "mod.qualified_const_defined?" do
- assert_deprecated do
- assert QualifiedConstTestMod.qualified_const_defined?("M")
- assert !QualifiedConstTestMod.qualified_const_defined?("NonExistingM")
-
- assert QualifiedConstTestMod.qualified_const_defined?("M::X")
- assert !QualifiedConstTestMod.qualified_const_defined?("M::Y")
-
- assert QualifiedConstTestMod.qualified_const_defined?("M::C::X")
- assert !QualifiedConstTestMod.qualified_const_defined?("M::C::Y")
-
- if Module.method(:const_defined?).arity == 1
- assert !QualifiedConstTestMod.qualified_const_defined?("QualifiedConstTestMod::N::X")
- else
- assert QualifiedConstTestMod.qualified_const_defined?("N::X")
- assert !QualifiedConstTestMod.qualified_const_defined?("N::X", false)
- assert QualifiedConstTestMod.qualified_const_defined?("N::X", true)
- end
- end
- end
-
- test "qualified_const_get" do
- assert_deprecated do
- assert_equal false, Object.qualified_const_get("QualifiedConstTestMod::X")
- assert_equal false, QualifiedConstTestMod.qualified_const_get("X")
- assert_equal 1, QualifiedConstTestMod.qualified_const_get("M::X")
- assert_equal 1, QualifiedConstTestMod.qualified_const_get("N::X")
- assert_equal 2, QualifiedConstTestMod.qualified_const_get("M::C::X")
-
- assert_raise(NameError) { QualifiedConstTestMod.qualified_const_get("M::C::Y") }
- end
- end
-
- test "qualified_const_set" do
- assert_deprecated do
- begin
- m = Module.new
- assert_equal m, Object.qualified_const_set("QualifiedConstTestMod2", m)
- assert_equal m, ::QualifiedConstTestMod2
-
- # We are going to assign to existing constants on purpose, so silence warnings.
- silence_warnings do
- assert_equal true, QualifiedConstTestMod.qualified_const_set("QualifiedConstTestMod::X", true)
- assert_equal true, QualifiedConstTestMod::X
-
- assert_equal 10, QualifiedConstTestMod::M.qualified_const_set("X", 10)
- assert_equal 10, QualifiedConstTestMod::M::X
- end
- ensure
- silence_warnings do
- QualifiedConstTestMod.qualified_const_set("QualifiedConstTestMod::X", false)
- QualifiedConstTestMod::M.qualified_const_set("X", 1)
- end
- end
- end
- end
-
- test "reject absolute paths" do
- assert_deprecated do
- assert_raise_with_message(NameError, "wrong constant name ::X") { Object.qualified_const_defined?("::X") }
- assert_raise_with_message(NameError, "wrong constant name ::X") { Object.qualified_const_defined?("::X::Y") }
-
- assert_raise_with_message(NameError, "wrong constant name ::X") { Object.qualified_const_get("::X") }
- assert_raise_with_message(NameError, "wrong constant name ::X") { Object.qualified_const_get("::X::Y") }
-
- assert_raise_with_message(NameError, "wrong constant name ::X") { Object.qualified_const_set("::X", nil) }
- assert_raise_with_message(NameError, "wrong constant name ::X") { Object.qualified_const_set("::X::Y", nil) }
- end
- end
-
- private
-
- def assert_raise_with_message(expected_exception, expected_message, &block)
- exception = assert_raise(expected_exception, &block)
- assert_equal expected_message, exception.message
- end
-end
diff --git a/activesupport/test/core_ext/module_test.rb b/activesupport/test/core_ext/module_test.rb
index 36073b28b7..a4515d1956 100644
--- a/activesupport/test/core_ext/module_test.rb
+++ b/activesupport/test/core_ext/module_test.rb
@@ -48,12 +48,12 @@ class Someone < Struct.new(:name, :place)
end
end
-Invoice = Struct.new(:client) do
+Invoice = Struct.new(:client) do
delegate :street, :city, :name, to: :client, prefix: true
delegate :street, :city, :name, to: :client, prefix: :customer
end
-Project = Struct.new(:description, :person) do
+Project = Struct.new(:description, :person) do
delegate :name, to: :person, allow_nil: true
delegate :to_f, to: :description, allow_nil: true
end
@@ -210,21 +210,21 @@ class ModuleTest < ActiveSupport::TestCase
def test_delegation_prefix
invoice = Invoice.new(@david)
- assert_equal invoice.client_name, "David"
- assert_equal invoice.client_street, "Paulina"
- assert_equal invoice.client_city, "Chicago"
+ assert_equal "David", invoice.client_name
+ assert_equal "Paulina", invoice.client_street
+ assert_equal "Chicago", invoice.client_city
end
def test_delegation_custom_prefix
invoice = Invoice.new(@david)
- assert_equal invoice.customer_name, "David"
- assert_equal invoice.customer_street, "Paulina"
- assert_equal invoice.customer_city, "Chicago"
+ assert_equal "David", invoice.customer_name
+ assert_equal "Paulina", invoice.customer_street
+ assert_equal "Chicago", invoice.customer_city
end
def test_delegation_prefix_with_nil_or_false
- assert_equal Developer.new(@david).name, "David"
- assert_equal Tester.new(@david).name, "David"
+ assert_equal "David", Developer.new(@david).name
+ assert_equal "David", Tester.new(@david).name
end
def test_delegation_prefix_with_instance_variable
@@ -240,7 +240,7 @@ class ModuleTest < ActiveSupport::TestCase
def test_delegation_with_allow_nil
rails = Project.new("Rails", Someone.new("David"))
- assert_equal rails.name, "David"
+ assert_equal "David", rails.name
end
def test_delegation_with_allow_nil_and_nil_value
@@ -386,231 +386,6 @@ class ModuleTest < ActiveSupport::TestCase
assert_equal [Yz, Object], Yz::Zy.parents
end
- def test_local_constants
- ActiveSupport::Deprecation.silence do
- assert_equal %w(Constant1 Constant3), Ab.local_constants.sort.map(&:to_s)
- end
- end
-
- def test_local_constants_is_deprecated
- assert_deprecated { Ab.local_constants.sort.map(&:to_s) }
- end
-end
-
-module BarMethodAliaser
- def self.included(foo_class)
- foo_class.class_eval do
- include BarMethods
- alias_method_chain :bar, :baz
- end
- end
-end
-
-module BarMethods
- def bar_with_baz
- bar_without_baz << "_with_baz"
- end
-
- def quux_with_baz!
- quux_without_baz! << "_with_baz"
- end
-
- def quux_with_baz?
- false
- end
-
- def quux_with_baz=(v)
- send(:quux_without_baz=, v) << "_with_baz"
- end
-
- def duck_with_orange
- duck_without_orange << "_with_orange"
- end
-end
-
-class MethodAliasingTest < ActiveSupport::TestCase
- def setup
- Object.const_set :FooClassWithBarMethod, Class.new { def bar() "bar" end }
- @instance = FooClassWithBarMethod.new
- end
-
- def teardown
- Object.instance_eval { remove_const :FooClassWithBarMethod }
- end
-
- def test_alias_method_chain_deprecated
- assert_deprecated(/alias_method_chain/) do
- Module.new do
- def base
- end
-
- def base_with_deprecated
- end
-
- alias_method_chain :base, :deprecated
- end
- end
- end
-
- def test_alias_method_chain
- assert_deprecated(/alias_method_chain/) do
- assert @instance.respond_to?(:bar)
- feature_aliases = [:bar_with_baz, :bar_without_baz]
-
- feature_aliases.each do |method|
- assert !@instance.respond_to?(method)
- end
-
- assert_equal "bar", @instance.bar
-
- FooClassWithBarMethod.class_eval { include BarMethodAliaser }
-
- feature_aliases.each do |method|
- assert_respond_to @instance, method
- end
-
- assert_equal "bar_with_baz", @instance.bar
- assert_equal "bar", @instance.bar_without_baz
- end
- end
-
- def test_alias_method_chain_with_punctuation_method
- assert_deprecated(/alias_method_chain/) do
- FooClassWithBarMethod.class_eval do
- def quux!; "quux" end
- end
-
- assert !@instance.respond_to?(:quux_with_baz!)
- FooClassWithBarMethod.class_eval do
- include BarMethodAliaser
- alias_method_chain :quux!, :baz
- end
- assert_respond_to @instance, :quux_with_baz!
-
- assert_equal "quux_with_baz", @instance.quux!
- assert_equal "quux", @instance.quux_without_baz!
- end
- end
-
- def test_alias_method_chain_with_same_names_between_predicates_and_bang_methods
- assert_deprecated(/alias_method_chain/) do
- FooClassWithBarMethod.class_eval do
- def quux!; "quux!" end
- def quux?; true end
- def quux=(v); "quux=" end
- end
-
- assert !@instance.respond_to?(:quux_with_baz!)
- assert !@instance.respond_to?(:quux_with_baz?)
- assert !@instance.respond_to?(:quux_with_baz=)
-
- FooClassWithBarMethod.class_eval { include BarMethodAliaser }
- assert_respond_to @instance, :quux_with_baz!
- assert_respond_to @instance, :quux_with_baz?
- assert_respond_to @instance, :quux_with_baz=
-
- FooClassWithBarMethod.alias_method_chain :quux!, :baz
- assert_equal "quux!_with_baz", @instance.quux!
- assert_equal "quux!", @instance.quux_without_baz!
-
- FooClassWithBarMethod.alias_method_chain :quux?, :baz
- assert_equal false, @instance.quux?
- assert_equal true, @instance.quux_without_baz?
-
- FooClassWithBarMethod.alias_method_chain :quux=, :baz
- assert_equal "quux=_with_baz", @instance.send(:quux=, 1234)
- assert_equal "quux=", @instance.send(:quux_without_baz=, 1234)
- end
- end
-
- def test_alias_method_chain_with_feature_punctuation
- assert_deprecated(/alias_method_chain/) do
- FooClassWithBarMethod.class_eval do
- def quux; "quux" end
- def quux?; "quux?" end
- include BarMethodAliaser
- alias_method_chain :quux, :baz!
- end
-
- assert_nothing_raised do
- assert_equal "quux_with_baz", @instance.quux_with_baz!
- end
-
- assert_raise(NameError) do
- FooClassWithBarMethod.alias_method_chain :quux?, :baz!
- end
- end
- end
-
- def test_alias_method_chain_yields_target_and_punctuation
- assert_deprecated(/alias_method_chain/) do
- args = nil
-
- FooClassWithBarMethod.class_eval do
- def quux?; end
- include BarMethods
-
- FooClassWithBarMethod.alias_method_chain :quux?, :baz do |target, punctuation|
- args = [target, punctuation]
- end
- end
-
- assert_not_nil args
- assert_equal "quux", args[0]
- assert_equal "?", args[1]
- end
- end
-
- def test_alias_method_chain_preserves_private_method_status
- assert_deprecated(/alias_method_chain/) do
- FooClassWithBarMethod.class_eval do
- def duck; "duck" end
- include BarMethodAliaser
- private :duck
- alias_method_chain :duck, :orange
- end
-
- assert_raise NoMethodError do
- @instance.duck
- end
-
- assert_equal "duck_with_orange", @instance.instance_eval { duck }
- assert FooClassWithBarMethod.private_method_defined?(:duck)
- end
- end
-
- def test_alias_method_chain_preserves_protected_method_status
- assert_deprecated(/alias_method_chain/) do
- FooClassWithBarMethod.class_eval do
- def duck; "duck" end
- include BarMethodAliaser
- protected :duck
- alias_method_chain :duck, :orange
- end
-
- assert_raise NoMethodError do
- @instance.duck
- end
-
- assert_equal "duck_with_orange", @instance.instance_eval { duck }
- assert FooClassWithBarMethod.protected_method_defined?(:duck)
- end
- end
-
- def test_alias_method_chain_preserves_public_method_status
- assert_deprecated(/alias_method_chain/) do
- FooClassWithBarMethod.class_eval do
- def duck; "duck" end
- include BarMethodAliaser
- public :duck
- alias_method_chain :duck, :orange
- end
-
- assert_equal "duck_with_orange", @instance.duck
- assert FooClassWithBarMethod.public_method_defined?(:duck)
- end
- end
-
def test_delegate_with_case
event = Event.new(Tester.new)
assert_equal 1, event.foo
diff --git a/activesupport/test/core_ext/numeric_ext_test.rb b/activesupport/test/core_ext/numeric_ext_test.rb
index 5e824747ed..5361b7b708 100644
--- a/activesupport/test/core_ext/numeric_ext_test.rb
+++ b/activesupport/test/core_ext/numeric_ext_test.rb
@@ -5,8 +5,8 @@ require "active_support/core_ext/integer"
class NumericExtTimeAndDateTimeTest < ActiveSupport::TestCase
def setup
- @now = Time.local(2005,2,10,15,30,45)
- @dtnow = DateTime.civil(2005,2,10,15,30,45)
+ @now = Time.local(2005, 2, 10, 15, 30, 45)
+ @dtnow = DateTime.civil(2005, 2, 10, 15, 30, 45)
@seconds = {
1.minute => 60,
10.minutes => 600,
@@ -68,8 +68,8 @@ class NumericExtTimeAndDateTimeTest < ActiveSupport::TestCase
end
def test_add_one_year_to_leap_day
- assert_equal Time.utc(2005,2,28,15,15,10), Time.utc(2004,2,29,15,15,10) + 1.year
- assert_equal DateTime.civil(2005,2,28,15,15,10), DateTime.civil(2004,2,29,15,15,10) + 1.year
+ assert_equal Time.utc(2005, 2, 28, 15, 15, 10), Time.utc(2004, 2, 29, 15, 15, 10) + 1.year
+ assert_equal DateTime.civil(2005, 2, 28, 15, 15, 10), DateTime.civil(2004, 2, 29, 15, 15, 10) + 1.year
end
end
@@ -83,7 +83,7 @@ class NumericExtDateTest < ActiveSupport::TestCase
assert_equal @today >> 1, @today + 1.month
assert_equal @today.to_time.since(1), @today + 1.second
assert_equal @today.to_time.since(60), @today + 1.minute
- assert_equal @today.to_time.since(60*60), @today + 1.hour
+ assert_equal @today.to_time.since(60 * 60), @today + 1.hour
end
def test_chaining_duration_operations
@@ -92,7 +92,7 @@ class NumericExtDateTest < ActiveSupport::TestCase
end
def test_add_one_year_to_leap_day
- assert_equal Date.new(2005,2,28), Date.new(2004,2,29) + 1.year
+ assert_equal Date.new(2005, 2, 28), Date.new(2004, 2, 29) + 1.year
end
end
@@ -102,12 +102,12 @@ class NumericExtSizeTest < ActiveSupport::TestCase
assert_equal 1024.kilobytes, 1.megabyte
assert_equal 3584.0.kilobytes, 3.5.megabytes
assert_equal 3584.0.megabytes, 3.5.gigabytes
- assert_equal 1.kilobyte ** 4, 1.terabyte
+ assert_equal 1.kilobyte**4, 1.terabyte
assert_equal 1024.kilobytes + 2.megabytes, 3.megabytes
assert_equal 2.gigabytes / 4, 512.megabytes
assert_equal 256.megabytes * 20 + 5.gigabytes, 10.gigabytes
- assert_equal 1.kilobyte ** 5, 1.petabyte
- assert_equal 1.kilobyte ** 6, 1.exabyte
+ assert_equal 1.kilobyte**5, 1.petabyte
+ assert_equal 1.kilobyte**6, 1.exabyte
end
def test_units_as_bytes_independently
@@ -228,37 +228,37 @@ class NumericExtFormattingTest < ActiveSupport::TestCase
def test_to_s__rounded__with_significant_digits
assert_equal "124000", 123987.to_s(:rounded, precision: 3, significant: true)
- assert_equal "120000000", 123987876.to_s(:rounded, precision: 2, significant: true )
- assert_equal "9775", 9775.to_s(:rounded, precision: 4, significant: true )
- assert_equal "5.4", 5.3923.to_s(:rounded, precision: 2, significant: true )
- assert_equal "5", 5.3923.to_s(:rounded, precision: 1, significant: true )
- assert_equal "1", 1.232.to_s(:rounded, precision: 1, significant: true )
- assert_equal "7", 7.to_s(:rounded, precision: 1, significant: true )
- assert_equal "1", 1.to_s(:rounded, precision: 1, significant: true )
- assert_equal "53", 52.7923.to_s(:rounded, precision: 2, significant: true )
- assert_equal "9775.00", 9775.to_s(:rounded, precision: 6, significant: true )
- assert_equal "5.392900", 5.3929.to_s(:rounded, precision: 7, significant: true )
- assert_equal "0.0", 0.to_s(:rounded, precision: 2, significant: true )
- assert_equal "0", 0.to_s(:rounded, precision: 1, significant: true )
- assert_equal "0.0001", 0.0001.to_s(:rounded, precision: 1, significant: true )
- assert_equal "0.000100", 0.0001.to_s(:rounded, precision: 3, significant: true )
- assert_equal "0.0001", 0.0001111.to_s(:rounded, precision: 1, significant: true )
+ assert_equal "120000000", 123987876.to_s(:rounded, precision: 2, significant: true)
+ assert_equal "9775", 9775.to_s(:rounded, precision: 4, significant: true)
+ assert_equal "5.4", 5.3923.to_s(:rounded, precision: 2, significant: true)
+ assert_equal "5", 5.3923.to_s(:rounded, precision: 1, significant: true)
+ assert_equal "1", 1.232.to_s(:rounded, precision: 1, significant: true)
+ assert_equal "7", 7.to_s(:rounded, precision: 1, significant: true)
+ assert_equal "1", 1.to_s(:rounded, precision: 1, significant: true)
+ assert_equal "53", 52.7923.to_s(:rounded, precision: 2, significant: true)
+ assert_equal "9775.00", 9775.to_s(:rounded, precision: 6, significant: true)
+ assert_equal "5.392900", 5.3929.to_s(:rounded, precision: 7, significant: true)
+ assert_equal "0.0", 0.to_s(:rounded, precision: 2, significant: true)
+ assert_equal "0", 0.to_s(:rounded, precision: 1, significant: true)
+ assert_equal "0.0001", 0.0001.to_s(:rounded, precision: 1, significant: true)
+ assert_equal "0.000100", 0.0001.to_s(:rounded, precision: 3, significant: true)
+ assert_equal "0.0001", 0.0001111.to_s(:rounded, precision: 1, significant: true)
assert_equal "10.0", 9.995.to_s(:rounded, precision: 3, significant: true)
assert_equal "9.99", 9.994.to_s(:rounded, precision: 3, significant: true)
assert_equal "11.0", 10.995.to_s(:rounded, precision: 3, significant: true)
end
def test_to_s__rounded__with_strip_insignificant_zeros
- assert_equal "9775.43", 9775.43.to_s(:rounded, precision: 4, strip_insignificant_zeros: true )
- assert_equal "9775.2", 9775.2.to_s(:rounded, precision: 6, significant: true, strip_insignificant_zeros: true )
- assert_equal "0", 0.to_s(:rounded, precision: 6, significant: true, strip_insignificant_zeros: true )
+ assert_equal "9775.43", 9775.43.to_s(:rounded, precision: 4, strip_insignificant_zeros: true)
+ assert_equal "9775.2", 9775.2.to_s(:rounded, precision: 6, significant: true, strip_insignificant_zeros: true)
+ assert_equal "0", 0.to_s(:rounded, precision: 6, significant: true, strip_insignificant_zeros: true)
end
def test_to_s__rounded__with_significant_true_and_zero_precision
# Zero precision with significant is a mistake (would always return zero),
# so we treat it as if significant was false (increases backwards compatibility for number_to_human_size)
assert_equal "124", 123.987.to_s(:rounded, precision: 0, significant: true)
- assert_equal "12", 12.to_s(:rounded, precision: 0, significant: true )
+ assert_equal "12", 12.to_s(:rounded, precision: 0, significant: true)
end
def test_to_s__human_size
@@ -287,21 +287,6 @@ class NumericExtFormattingTest < ActiveSupport::TestCase
assert_equal "10 Bytes", 10.to_s(:human_size)
end
- def test_to_s__human_size_with_si_prefix
- assert_deprecated do
- assert_equal "3 Bytes", 3.14159265.to_s(:human_size, prefix: :si)
- assert_equal "123 Bytes", 123.0.to_s(:human_size, prefix: :si)
- assert_equal "123 Bytes", 123.to_s(:human_size, prefix: :si)
- assert_equal "1.23 KB", 1234.to_s(:human_size, prefix: :si)
- assert_equal "12.3 KB", 12345.to_s(:human_size, prefix: :si)
- assert_equal "1.23 MB", 1234567.to_s(:human_size, prefix: :si)
- assert_equal "1.23 GB", 1234567890.to_s(:human_size, prefix: :si)
- assert_equal "1.23 TB", 1234567890123.to_s(:human_size, prefix: :si)
- assert_equal "1.23 PB", 1234567890123456.to_s(:human_size, prefix: :si)
- assert_equal "1.23 EB", 1234567890123456789.to_s(:human_size, prefix: :si)
- end
- end
-
def test_to_s__human_size_with_options_hash
assert_equal "1.2 MB", 1234567.to_s(:human_size, precision: 2)
assert_equal "3 Bytes", 3.14159265.to_s(:human_size, precision: 4)
@@ -391,12 +376,6 @@ class NumericExtFormattingTest < ActiveSupport::TestCase
assert_equal "1 Million", BigDecimal("1000010").to_s(:human)
end
- def test_to_formatted_s_is_deprecated
- assert_deprecated do
- 5551234.to_formatted_s(:phone)
- end
- end
-
def test_to_s_with_invalid_formatter
assert_equal "123", 123.to_s(:invalid)
assert_equal "2.5", 2.5.to_s(:invalid)
@@ -423,7 +402,6 @@ class NumericExtFormattingTest < ActiveSupport::TestCase
# TODO: Remove positive and negative tests when we drop support to ruby < 2.3
b = 2**64
- b *= b until Bignum === b
T_ZERO = b.coerce(0).first
T_ONE = b.coerce(1).first
@@ -453,8 +431,8 @@ class NumericExtFormattingTest < ActiveSupport::TestCase
end.new
assert_not_predicate(a, :positive?)
- assert_predicate(1/2r, :positive?)
- assert_not_predicate(-1/2r, :positive?)
+ assert_predicate(1 / 2r, :positive?)
+ assert_not_predicate(-1 / 2r, :positive?)
assert_predicate(T_ONE, :positive?)
assert_not_predicate(T_MONE, :positive?)
@@ -491,8 +469,8 @@ class NumericExtFormattingTest < ActiveSupport::TestCase
end.new
assert_not_predicate(a, :negative?)
- assert_predicate(-1/2r, :negative?)
- assert_not_predicate(1/2r, :negative?)
+ assert_predicate(-1 / 2r, :negative?)
+ assert_not_predicate(1 / 2r, :negative?)
assert_not_predicate(T_ONE, :negative?)
assert_predicate(T_MONE, :negative?)
diff --git a/activesupport/test/core_ext/object/blank_test.rb b/activesupport/test/core_ext/object/blank_test.rb
index ab0676524e..1bedc76320 100644
--- a/activesupport/test/core_ext/object/blank_test.rb
+++ b/activesupport/test/core_ext/object/blank_test.rb
@@ -28,7 +28,7 @@ class BlankTest < ActiveSupport::TestCase
end
def test_presence
- BLANK.each { |v| assert_equal nil, v.presence, "#{v.inspect}.presence should return nil" }
+ BLANK.each { |v| assert_nil v.presence, "#{v.inspect}.presence should return nil" }
NOT.each { |v| assert_equal v, v.presence, "#{v.inspect}.presence should return self" }
end
end
diff --git a/activesupport/test/core_ext/object/deep_dup_test.rb b/activesupport/test/core_ext/object/deep_dup_test.rb
index e335ec1b40..f247ee16de 100644
--- a/activesupport/test/core_ext/object/deep_dup_test.rb
+++ b/activesupport/test/core_ext/object/deep_dup_test.rb
@@ -6,7 +6,7 @@ class DeepDupTest < ActiveSupport::TestCase
array = [1, [2, 3]]
dup = array.deep_dup
dup[1][2] = 4
- assert_equal nil, array[1][2]
+ assert_nil array[1][2]
assert_equal 4, dup[1][2]
end
@@ -14,7 +14,7 @@ class DeepDupTest < ActiveSupport::TestCase
hash = { a: { b: "b" } }
dup = hash.deep_dup
dup[:a][:c] = "c"
- assert_equal nil, hash[:a][:c]
+ assert_nil hash[:a][:c]
assert_equal "c", dup[:a][:c]
end
@@ -22,7 +22,7 @@ class DeepDupTest < ActiveSupport::TestCase
array = [1, { a: 2, b: 3 } ]
dup = array.deep_dup
dup[1][:c] = 4
- assert_equal nil, array[1][:c]
+ assert_nil array[1][:c]
assert_equal 4, dup[1][:c]
end
@@ -30,7 +30,7 @@ class DeepDupTest < ActiveSupport::TestCase
hash = { a: [1, 2] }
dup = hash.deep_dup
dup[:a][2] = "c"
- assert_equal nil, hash[:a][2]
+ assert_nil hash[:a][2]
assert_equal "c", dup[:a][2]
end
diff --git a/activesupport/test/core_ext/object/duplicable_test.rb b/activesupport/test/core_ext/object/duplicable_test.rb
index 2cbfefe235..f78a5f8496 100644
--- a/activesupport/test/core_ext/object/duplicable_test.rb
+++ b/activesupport/test/core_ext/object/duplicable_test.rb
@@ -4,9 +4,13 @@ require "active_support/core_ext/object/duplicable"
require "active_support/core_ext/numeric/time"
class DuplicableTest < ActiveSupport::TestCase
- RAISE_DUP = [nil, false, true, :symbol, 1, 2.3, method(:puts)]
- ALLOW_DUP = ["1", Object.new, /foo/, [], {}, Time.now, Class.new, Module.new]
- ALLOW_DUP << BigDecimal.new("4.56")
+ if RUBY_VERSION >= "2.4.0"
+ RAISE_DUP = [method(:puts), Complex(1), Rational(1)]
+ ALLOW_DUP = ["1", Object.new, /foo/, [], {}, Time.now, Class.new, Module.new, BigDecimal.new("4.56"), nil, false, true, :symbol, 1, 2.3]
+ else
+ RAISE_DUP = [nil, false, true, :symbol, 1, 2.3, method(:puts), Complex(1), Rational(1)]
+ ALLOW_DUP = ["1", Object.new, /foo/, [], {}, Time.now, Class.new, Module.new, BigDecimal.new("4.56")]
+ end
def test_duplicable
rubinius_skip "* Method#dup is allowed at the moment on Rubinius\n" \
diff --git a/activesupport/test/core_ext/object/inclusion_test.rb b/activesupport/test/core_ext/object/inclusion_test.rb
index be211ec7dc..955686d6aa 100644
--- a/activesupport/test/core_ext/object/inclusion_test.rb
+++ b/activesupport/test/core_ext/object/inclusion_test.rb
@@ -3,8 +3,8 @@ require "active_support/core_ext/object/inclusion"
class InTest < ActiveSupport::TestCase
def test_in_array
- assert 1.in?([1,2])
- assert !3.in?([1,2])
+ assert 1.in?([1, 2])
+ assert !3.in?([1, 2])
end
def test_in_hash
@@ -25,7 +25,7 @@ class InTest < ActiveSupport::TestCase
end
def test_in_set
- s = Set.new([1,2])
+ s = Set.new([1, 2])
assert 1.in?(s)
assert !3.in?(s)
end
diff --git a/activesupport/test/core_ext/range_ext_test.rb b/activesupport/test/core_ext/range_ext_test.rb
index fc0e72e09a..d166c7309c 100644
--- a/activesupport/test/core_ext/range_ext_test.rb
+++ b/activesupport/test/core_ext/range_ext_test.rb
@@ -99,21 +99,21 @@ class RangeTest < ActiveSupport::TestCase
end
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))
+ 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 {}
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))
+ 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) {}
end
end
def test_include_on_time_with_zone
- twz = ActiveSupport::TimeWithZone.new(nil, ActiveSupport::TimeZone["Eastern Time (US & Canada)"] , Time.utc(2006,11,28,10,30))
+ 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).include?(twz)
end
diff --git a/activesupport/test/core_ext/string_ext_test.rb b/activesupport/test/core_ext/string_ext_test.rb
index 4b40503d22..00685cd952 100644
--- a/activesupport/test/core_ext/string_ext_test.rb
+++ b/activesupport/test/core_ext/string_ext_test.rb
@@ -152,53 +152,37 @@ class StringInflectionsTest < ActiveSupport::TestCase
def test_string_parameterized_normal
StringToParameterized.each do |normal, slugged|
- assert_equal(normal.parameterize, slugged)
+ assert_equal(slugged, normal.parameterize)
end
end
def test_string_parameterized_normal_preserve_case
StringToParameterizedPreserveCase.each do |normal, slugged|
- assert_equal(normal.parameterize(preserve_case: true), slugged)
+ assert_equal(slugged, normal.parameterize(preserve_case: true))
end
end
def test_string_parameterized_no_separator
StringToParameterizeWithNoSeparator.each do |normal, slugged|
- assert_equal(normal.parameterize(separator: ""), slugged)
- end
- end
-
- def test_string_parameterized_no_separator_deprecated
- StringToParameterizeWithNoSeparator.each do |normal, slugged|
- assert_deprecated(/Passing the separator argument as a positional parameter is deprecated and will soon be removed. Use `separator: ''` instead./i) do
- assert_equal(normal.parameterize(""), slugged)
- end
+ assert_equal(slugged, normal.parameterize(separator: ""))
end
end
def test_string_parameterized_no_separator_preserve_case
StringToParameterizePreserveCaseWithNoSeparator.each do |normal, slugged|
- assert_equal(normal.parameterize(separator: "", preserve_case: true), slugged)
+ assert_equal(slugged, normal.parameterize(separator: "", preserve_case: true))
end
end
def test_string_parameterized_underscore
StringToParameterizeWithUnderscore.each do |normal, slugged|
- assert_equal(normal.parameterize(separator: "_"), slugged)
- end
- end
-
- def test_string_parameterized_underscore_deprecated
- StringToParameterizeWithUnderscore.each do |normal, slugged|
- assert_deprecated(/Passing the separator argument as a positional parameter is deprecated and will soon be removed. Use `separator: '_'` instead./i) do
- assert_equal(normal.parameterize("_"), slugged)
- end
+ assert_equal(slugged, normal.parameterize(separator: "_"))
end
end
def test_string_parameterized_underscore_preserve_case
StringToParameterizePreserceCaseWithUnderscore.each do |normal, slugged|
- assert_equal(normal.parameterize(separator: "_", preserve_case: true), slugged)
+ assert_equal(slugged, normal.parameterize(separator: "_", preserve_case: true))
end
end
@@ -299,7 +283,7 @@ class StringInflectionsTest < ActiveSupport::TestCase
def test_truncate_words_with_complex_string
Timeout.timeout(10) do
complex_string = "aa aa aaa aa aaa aaa aaa aa aaa aaa aaa aaa aaa aaa aaa aaa aaa aaa aaaa aaaaa aaaaa aaaaaa aa aa aa aaa aa aaa aa aa aa aa a aaa aaa \n a aaa <<s"
- assert_equal complex_string.truncate_words(80), complex_string
+ assert_equal complex_string, complex_string.truncate_words(80)
end
rescue Timeout::Error
assert false
@@ -355,7 +339,7 @@ class StringAccessTest < ActiveSupport::TestCase
test "#at with Regex, returns the matching portion of the string" do
assert_equal "lo", "hello".at(/lo/)
- assert_equal nil, "hello".at(/nonexisting/)
+ assert_nil "hello".at(/nonexisting/)
end
test "#from with positive Integer, returns substring from the given position to the end" do
@@ -726,14 +710,14 @@ class OutputSafetyTest < ActiveSupport::TestCase
test "Prepending safe onto unsafe yields unsafe" do
@string.prepend "other".html_safe
assert !@string.html_safe?
- assert_equal @string, "otherhello"
+ assert_equal "otherhello", @string
end
test "Prepending unsafe onto safe yields escaped safe" do
other = "other".html_safe
other.prepend "<foo>"
assert other.html_safe?
- assert_equal other, "&lt;foo&gt;other"
+ assert_equal "&lt;foo&gt;other", other
end
test "Concatting safe onto unsafe yields unsafe" do
diff --git a/activesupport/test/core_ext/time_ext_test.rb b/activesupport/test/core_ext/time_ext_test.rb
index a6c9a7d5a8..a399e36dc9 100644
--- a/activesupport/test/core_ext/time_ext_test.rb
+++ b/activesupport/test/core_ext/time_ext_test.rb
@@ -4,32 +4,32 @@ require "core_ext/date_and_time_behavior"
require "time_zone_test_helpers"
class TimeExtCalculationsTest < ActiveSupport::TestCase
- def date_time_init(year,month,day,hour,minute,second,usec=0)
- Time.local(year,month,day,hour,minute,second,usec)
+ def date_time_init(year, month, day, hour, minute, second, usec = 0)
+ Time.local(year, month, day, hour, minute, second, usec)
end
include DateAndTimeBehavior
include TimeZoneTestHelpers
def test_seconds_since_midnight
- assert_equal 1,Time.local(2005,1,1,0,0,1).seconds_since_midnight
- assert_equal 60,Time.local(2005,1,1,0,1,0).seconds_since_midnight
- assert_equal 3660,Time.local(2005,1,1,1,1,0).seconds_since_midnight
- assert_equal 86399,Time.local(2005,1,1,23,59,59).seconds_since_midnight
- assert_equal 60.00001,Time.local(2005,1,1,0,1,0,10).seconds_since_midnight
+ assert_equal 1, Time.local(2005, 1, 1, 0, 0, 1).seconds_since_midnight
+ assert_equal 60, Time.local(2005, 1, 1, 0, 1, 0).seconds_since_midnight
+ assert_equal 3660, Time.local(2005, 1, 1, 1, 1, 0).seconds_since_midnight
+ assert_equal 86399, Time.local(2005, 1, 1, 23, 59, 59).seconds_since_midnight
+ assert_equal 60.00001, Time.local(2005, 1, 1, 0, 1, 0, 10).seconds_since_midnight
end
def test_seconds_since_midnight_at_daylight_savings_time_start
with_env_tz "US/Eastern" do
# dt: US: 2005 April 3rd 2:00am ST => April 3rd 3:00am DT
- assert_equal 2*3600-1, Time.local(2005,4,3,1,59,59).seconds_since_midnight, "just before DST start"
- assert_equal 2*3600+1, Time.local(2005,4,3,3, 0, 1).seconds_since_midnight, "just after DST start"
+ assert_equal 2 * 3600 - 1, Time.local(2005, 4, 3, 1, 59, 59).seconds_since_midnight, "just before DST start"
+ assert_equal 2 * 3600 + 1, Time.local(2005, 4, 3, 3, 0, 1).seconds_since_midnight, "just after DST start"
end
with_env_tz "NZ" do
# dt: New Zealand: 2006 October 1st 2:00am ST => October 1st 3:00am DT
- assert_equal 2*3600-1, Time.local(2006,10,1,1,59,59).seconds_since_midnight, "just before DST start"
- assert_equal 2*3600+1, Time.local(2006,10,1,3, 0, 1).seconds_since_midnight, "just after DST start"
+ assert_equal 2 * 3600 - 1, Time.local(2006, 10, 1, 1, 59, 59).seconds_since_midnight, "just before DST start"
+ assert_equal 2 * 3600 + 1, Time.local(2006, 10, 1, 3, 0, 1).seconds_since_midnight, "just after DST start"
end
end
@@ -37,47 +37,47 @@ class TimeExtCalculationsTest < ActiveSupport::TestCase
with_env_tz "US/Eastern" do
# st: US: 2005 October 30th 2:00am DT => October 30th 1:00am ST
# avoid setting a time between 1:00 and 2:00 since that requires specifying whether DST is active
- assert_equal 1*3600-1, Time.local(2005,10,30,0,59,59).seconds_since_midnight, "just before DST end"
- assert_equal 3*3600+1, Time.local(2005,10,30,2, 0, 1).seconds_since_midnight, "just after DST end"
+ assert_equal 1 * 3600 - 1, Time.local(2005, 10, 30, 0, 59, 59).seconds_since_midnight, "just before DST end"
+ assert_equal 3 * 3600 + 1, Time.local(2005, 10, 30, 2, 0, 1).seconds_since_midnight, "just after DST end"
# now set a time between 1:00 and 2:00 by specifying whether DST is active
# uses: Time.local( sec, min, hour, day, month, year, wday, yday, isdst, tz )
- assert_equal 1*3600+30*60, Time.local(0,30,1,30,10,2005,0,0,true,ENV["TZ"]).seconds_since_midnight, "before DST end"
- assert_equal 2*3600+30*60, Time.local(0,30,1,30,10,2005,0,0,false,ENV["TZ"]).seconds_since_midnight, "after DST end"
+ assert_equal 1 * 3600 + 30 * 60, Time.local(0, 30, 1, 30, 10, 2005, 0, 0, true, ENV["TZ"]).seconds_since_midnight, "before DST end"
+ assert_equal 2 * 3600 + 30 * 60, Time.local(0, 30, 1, 30, 10, 2005, 0, 0, false, ENV["TZ"]).seconds_since_midnight, "after DST end"
end
with_env_tz "NZ" do
# st: New Zealand: 2006 March 19th 3:00am DT => March 19th 2:00am ST
# avoid setting a time between 2:00 and 3:00 since that requires specifying whether DST is active
- assert_equal 2*3600-1, Time.local(2006,3,19,1,59,59).seconds_since_midnight, "just before DST end"
- assert_equal 4*3600+1, Time.local(2006,3,19,3, 0, 1).seconds_since_midnight, "just after DST end"
+ assert_equal 2 * 3600 - 1, Time.local(2006, 3, 19, 1, 59, 59).seconds_since_midnight, "just before DST end"
+ assert_equal 4 * 3600 + 1, Time.local(2006, 3, 19, 3, 0, 1).seconds_since_midnight, "just after DST end"
# now set a time between 2:00 and 3:00 by specifying whether DST is active
# uses: Time.local( sec, min, hour, day, month, year, wday, yday, isdst, tz )
- assert_equal 2*3600+30*60, Time.local(0,30,2,19,3,2006,0,0,true, ENV["TZ"]).seconds_since_midnight, "before DST end"
- assert_equal 3*3600+30*60, Time.local(0,30,2,19,3,2006,0,0,false,ENV["TZ"]).seconds_since_midnight, "after DST end"
+ assert_equal 2 * 3600 + 30 * 60, Time.local(0, 30, 2, 19, 3, 2006, 0, 0, true, ENV["TZ"]).seconds_since_midnight, "before DST end"
+ assert_equal 3 * 3600 + 30 * 60, Time.local(0, 30, 2, 19, 3, 2006, 0, 0, false, ENV["TZ"]).seconds_since_midnight, "after DST end"
end
end
def test_seconds_until_end_of_day
- assert_equal 0, Time.local(2005,1,1,23,59,59).seconds_until_end_of_day
- assert_equal 1, Time.local(2005,1,1,23,59,58).seconds_until_end_of_day
- assert_equal 60, Time.local(2005,1,1,23,58,59).seconds_until_end_of_day
- assert_equal 3660, Time.local(2005,1,1,22,58,59).seconds_until_end_of_day
- assert_equal 86399, Time.local(2005,1,1,0,0,0).seconds_until_end_of_day
+ assert_equal 0, Time.local(2005, 1, 1, 23, 59, 59).seconds_until_end_of_day
+ assert_equal 1, Time.local(2005, 1, 1, 23, 59, 58).seconds_until_end_of_day
+ assert_equal 60, Time.local(2005, 1, 1, 23, 58, 59).seconds_until_end_of_day
+ assert_equal 3660, Time.local(2005, 1, 1, 22, 58, 59).seconds_until_end_of_day
+ assert_equal 86399, Time.local(2005, 1, 1, 0, 0, 0).seconds_until_end_of_day
end
def test_seconds_until_end_of_day_at_daylight_savings_time_start
with_env_tz "US/Eastern" do
# dt: US: 2005 April 3rd 2:00am ST => April 3rd 3:00am DT
- assert_equal 21*3600, Time.local(2005,4,3,1,59,59).seconds_until_end_of_day, "just before DST start"
- assert_equal 21*3600-2, Time.local(2005,4,3,3,0,1).seconds_until_end_of_day, "just after DST start"
+ assert_equal 21 * 3600, Time.local(2005, 4, 3, 1, 59, 59).seconds_until_end_of_day, "just before DST start"
+ assert_equal 21 * 3600 - 2, Time.local(2005, 4, 3, 3, 0, 1).seconds_until_end_of_day, "just after DST start"
end
with_env_tz "NZ" do
# dt: New Zealand: 2006 October 1st 2:00am ST => October 1st 3:00am DT
- assert_equal 21*3600, Time.local(2006,10,1,1,59,59).seconds_until_end_of_day, "just before DST start"
- assert_equal 21*3600-2, Time.local(2006,10,1,3,0,1).seconds_until_end_of_day, "just after DST start"
+ assert_equal 21 * 3600, Time.local(2006, 10, 1, 1, 59, 59).seconds_until_end_of_day, "just before DST start"
+ assert_equal 21 * 3600 - 2, Time.local(2006, 10, 1, 3, 0, 1).seconds_until_end_of_day, "just after DST start"
end
end
@@ -85,83 +85,83 @@ class TimeExtCalculationsTest < ActiveSupport::TestCase
with_env_tz "US/Eastern" do
# st: US: 2005 October 30th 2:00am DT => October 30th 1:00am ST
# avoid setting a time between 1:00 and 2:00 since that requires specifying whether DST is active
- assert_equal 24*3600, Time.local(2005,10,30,0,59,59).seconds_until_end_of_day, "just before DST end"
- assert_equal 22*3600-2, Time.local(2005,10,30,2,0,1).seconds_until_end_of_day, "just after DST end"
+ assert_equal 24 * 3600, Time.local(2005, 10, 30, 0, 59, 59).seconds_until_end_of_day, "just before DST end"
+ assert_equal 22 * 3600 - 2, Time.local(2005, 10, 30, 2, 0, 1).seconds_until_end_of_day, "just after DST end"
# now set a time between 1:00 and 2:00 by specifying whether DST is active
# uses: Time.local( sec, min, hour, day, month, year, wday, yday, isdst, tz )
- assert_equal 24*3600-30*60-1, Time.local(0,30,1,30,10,2005,0,0,true,ENV["TZ"]).seconds_until_end_of_day, "before DST end"
- assert_equal 23*3600-30*60-1, Time.local(0,30,1,30,10,2005,0,0,false,ENV["TZ"]).seconds_until_end_of_day, "after DST end"
+ assert_equal 24 * 3600 - 30 * 60 - 1, Time.local(0, 30, 1, 30, 10, 2005, 0, 0, true, ENV["TZ"]).seconds_until_end_of_day, "before DST end"
+ assert_equal 23 * 3600 - 30 * 60 - 1, Time.local(0, 30, 1, 30, 10, 2005, 0, 0, false, ENV["TZ"]).seconds_until_end_of_day, "after DST end"
end
with_env_tz "NZ" do
# st: New Zealand: 2006 March 19th 3:00am DT => March 19th 2:00am ST
# avoid setting a time between 2:00 and 3:00 since that requires specifying whether DST is active
- assert_equal 23*3600, Time.local(2006,3,19,1,59,59).seconds_until_end_of_day, "just before DST end"
- assert_equal 21*3600-2, Time.local(2006,3,19,3,0,1).seconds_until_end_of_day, "just after DST end"
+ assert_equal 23 * 3600, Time.local(2006, 3, 19, 1, 59, 59).seconds_until_end_of_day, "just before DST end"
+ assert_equal 21 * 3600 - 2, Time.local(2006, 3, 19, 3, 0, 1).seconds_until_end_of_day, "just after DST end"
# now set a time between 2:00 and 3:00 by specifying whether DST is active
# uses: Time.local( sec, min, hour, day, month, year, wday, yday, isdst, tz )
- assert_equal 23*3600-30*60-1, Time.local(0,30,2,19,3,2006,0,0,true, ENV["TZ"]).seconds_until_end_of_day, "before DST end"
- assert_equal 22*3600-30*60-1, Time.local(0,30,2,19,3,2006,0,0,false,ENV["TZ"]).seconds_until_end_of_day, "after DST end"
+ assert_equal 23 * 3600 - 30 * 60 - 1, Time.local(0, 30, 2, 19, 3, 2006, 0, 0, true, ENV["TZ"]).seconds_until_end_of_day, "before DST end"
+ assert_equal 22 * 3600 - 30 * 60 - 1, Time.local(0, 30, 2, 19, 3, 2006, 0, 0, false, ENV["TZ"]).seconds_until_end_of_day, "after DST end"
end
end
def test_sec_fraction
- time = Time.utc(2016, 4, 23, 0, 0, Rational(1,10000000000))
- assert_equal Rational(1,10000000000), time.sec_fraction
+ time = Time.utc(2016, 4, 23, 0, 0, Rational(1, 10000000000))
+ assert_equal Rational(1, 10000000000), time.sec_fraction
time = Time.utc(2016, 4, 23, 0, 0, 0.0000000001)
assert_equal 0.0000000001.to_r, time.sec_fraction
- time = Time.utc(2016, 4, 23, 0, 0, 0, Rational(1,10000))
- assert_equal Rational(1,10000000000), time.sec_fraction
+ time = Time.utc(2016, 4, 23, 0, 0, 0, Rational(1, 10000))
+ assert_equal Rational(1, 10000000000), time.sec_fraction
time = Time.utc(2016, 4, 23, 0, 0, 0, 0.0001)
assert_equal 0.0001.to_r / 1000000, time.sec_fraction
end
def test_beginning_of_day
- assert_equal Time.local(2005,2,4,0,0,0), Time.local(2005,2,4,10,10,10).beginning_of_day
+ assert_equal Time.local(2005, 2, 4, 0, 0, 0), Time.local(2005, 2, 4, 10, 10, 10).beginning_of_day
with_env_tz "US/Eastern" do
- assert_equal Time.local(2006,4,2,0,0,0), Time.local(2006,4,2,10,10,10).beginning_of_day, "start DST"
- assert_equal Time.local(2006,10,29,0,0,0), Time.local(2006,10,29,10,10,10).beginning_of_day, "ends DST"
+ assert_equal Time.local(2006, 4, 2, 0, 0, 0), Time.local(2006, 4, 2, 10, 10, 10).beginning_of_day, "start DST"
+ assert_equal Time.local(2006, 10, 29, 0, 0, 0), Time.local(2006, 10, 29, 10, 10, 10).beginning_of_day, "ends DST"
end
with_env_tz "NZ" do
- assert_equal Time.local(2006,3,19,0,0,0), Time.local(2006,3,19,10,10,10).beginning_of_day, "ends DST"
- assert_equal Time.local(2006,10,1,0,0,0), Time.local(2006,10,1,10,10,10).beginning_of_day, "start DST"
+ assert_equal Time.local(2006, 3, 19, 0, 0, 0), Time.local(2006, 3, 19, 10, 10, 10).beginning_of_day, "ends DST"
+ assert_equal Time.local(2006, 10, 1, 0, 0, 0), Time.local(2006, 10, 1, 10, 10, 10).beginning_of_day, "start DST"
end
end
def test_middle_of_day
- assert_equal Time.local(2005,2,4,12,0,0), Time.local(2005,2,4,10,10,10).middle_of_day
+ assert_equal Time.local(2005, 2, 4, 12, 0, 0), Time.local(2005, 2, 4, 10, 10, 10).middle_of_day
with_env_tz "US/Eastern" do
- assert_equal Time.local(2006,4,2,12,0,0), Time.local(2006,4,2,10,10,10).middle_of_day, "start DST"
- assert_equal Time.local(2006,10,29,12,0,0), Time.local(2006,10,29,10,10,10).middle_of_day, "ends DST"
+ assert_equal Time.local(2006, 4, 2, 12, 0, 0), Time.local(2006, 4, 2, 10, 10, 10).middle_of_day, "start DST"
+ assert_equal Time.local(2006, 10, 29, 12, 0, 0), Time.local(2006, 10, 29, 10, 10, 10).middle_of_day, "ends DST"
end
with_env_tz "NZ" do
- assert_equal Time.local(2006,3,19,12,0,0), Time.local(2006,3,19,10,10,10).middle_of_day, "ends DST"
- assert_equal Time.local(2006,10,1,12,0,0), Time.local(2006,10,1,10,10,10).middle_of_day, "start DST"
+ assert_equal Time.local(2006, 3, 19, 12, 0, 0), Time.local(2006, 3, 19, 10, 10, 10).middle_of_day, "ends DST"
+ assert_equal Time.local(2006, 10, 1, 12, 0, 0), Time.local(2006, 10, 1, 10, 10, 10).middle_of_day, "start DST"
end
end
def test_beginning_of_hour
- assert_equal Time.local(2005,2,4,19,0,0), Time.local(2005,2,4,19,30,10).beginning_of_hour
+ assert_equal Time.local(2005, 2, 4, 19, 0, 0), Time.local(2005, 2, 4, 19, 30, 10).beginning_of_hour
end
def test_beginning_of_minute
- assert_equal Time.local(2005,2,4,19,30,0), Time.local(2005,2,4,19,30,10).beginning_of_minute
+ assert_equal Time.local(2005, 2, 4, 19, 30, 0), Time.local(2005, 2, 4, 19, 30, 10).beginning_of_minute
end
def test_end_of_day
- assert_equal Time.local(2007,8,12,23,59,59,Rational(999999999, 1000)), Time.local(2007,8,12,10,10,10).end_of_day
+ assert_equal Time.local(2007, 8, 12, 23, 59, 59, Rational(999999999, 1000)), Time.local(2007, 8, 12, 10, 10, 10).end_of_day
with_env_tz "US/Eastern" do
- assert_equal Time.local(2007,4,2,23,59,59,Rational(999999999, 1000)), Time.local(2007,4,2,10,10,10).end_of_day, "start DST"
- assert_equal Time.local(2007,10,29,23,59,59,Rational(999999999, 1000)), Time.local(2007,10,29,10,10,10).end_of_day, "ends DST"
+ assert_equal Time.local(2007, 4, 2, 23, 59, 59, Rational(999999999, 1000)), Time.local(2007, 4, 2, 10, 10, 10).end_of_day, "start DST"
+ assert_equal Time.local(2007, 10, 29, 23, 59, 59, Rational(999999999, 1000)), Time.local(2007, 10, 29, 10, 10, 10).end_of_day, "ends DST"
end
with_env_tz "NZ" do
- assert_equal Time.local(2006,3,19,23,59,59,Rational(999999999, 1000)), Time.local(2006,3,19,10,10,10).end_of_day, "ends DST"
- assert_equal Time.local(2006,10,1,23,59,59,Rational(999999999, 1000)), Time.local(2006,10,1,10,10,10).end_of_day, "start DST"
+ assert_equal Time.local(2006, 3, 19, 23, 59, 59, Rational(999999999, 1000)), Time.local(2006, 3, 19, 10, 10, 10).end_of_day, "ends DST"
+ assert_equal Time.local(2006, 10, 1, 23, 59, 59, Rational(999999999, 1000)), Time.local(2006, 10, 1, 10, 10, 10).end_of_day, "start DST"
end
with_env_tz "Asia/Yekaterinburg" do
assert_equal Time.local(2015, 2, 8, 23, 59, 59, Rational(999999999, 1000)), Time.new(2015, 2, 8, 8, 0, 0, "+05:00").end_of_day
@@ -169,334 +169,334 @@ class TimeExtCalculationsTest < ActiveSupport::TestCase
end
def test_end_of_hour
- assert_equal Time.local(2005,2,4,19,59,59,Rational(999999999, 1000)), Time.local(2005,2,4,19,30,10).end_of_hour
+ assert_equal Time.local(2005, 2, 4, 19, 59, 59, Rational(999999999, 1000)), Time.local(2005, 2, 4, 19, 30, 10).end_of_hour
end
def test_end_of_minute
- assert_equal Time.local(2005,2,4,19,30,59,Rational(999999999, 1000)), Time.local(2005,2,4,19,30,10).end_of_minute
+ assert_equal Time.local(2005, 2, 4, 19, 30, 59, Rational(999999999, 1000)), Time.local(2005, 2, 4, 19, 30, 10).end_of_minute
end
def test_last_year
- assert_equal Time.local(2004,6,5,10), Time.local(2005,6,5,10,0,0).last_year
+ assert_equal Time.local(2004, 6, 5, 10), Time.local(2005, 6, 5, 10, 0, 0).last_year
end
def test_ago
- assert_equal Time.local(2005,2,22,10,10,9), Time.local(2005,2,22,10,10,10).ago(1)
- assert_equal Time.local(2005,2,22,9,10,10), Time.local(2005,2,22,10,10,10).ago(3600)
- assert_equal Time.local(2005,2,20,10,10,10), Time.local(2005,2,22,10,10,10).ago(86400*2)
- assert_equal Time.local(2005,2,20,9,9,45), Time.local(2005,2,22,10,10,10).ago(86400*2 + 3600 + 25)
+ assert_equal Time.local(2005, 2, 22, 10, 10, 9), Time.local(2005, 2, 22, 10, 10, 10).ago(1)
+ assert_equal Time.local(2005, 2, 22, 9, 10, 10), Time.local(2005, 2, 22, 10, 10, 10).ago(3600)
+ assert_equal Time.local(2005, 2, 20, 10, 10, 10), Time.local(2005, 2, 22, 10, 10, 10).ago(86400 * 2)
+ assert_equal Time.local(2005, 2, 20, 9, 9, 45), Time.local(2005, 2, 22, 10, 10, 10).ago(86400 * 2 + 3600 + 25)
end
def test_daylight_savings_time_crossings_backward_start
with_env_tz "US/Eastern" do
# dt: US: 2005 April 3rd 4:18am
- assert_equal Time.local(2005,4,2,3,18,0), Time.local(2005,4,3,4,18,0).ago(24.hours), "dt-24.hours=>st"
- assert_equal Time.local(2005,4,2,3,18,0), Time.local(2005,4,3,4,18,0).ago(86400), "dt-86400=>st"
- assert_equal Time.local(2005,4,2,3,18,0), Time.local(2005,4,3,4,18,0).ago(86400.seconds), "dt-86400.seconds=>st"
+ assert_equal Time.local(2005, 4, 2, 3, 18, 0), Time.local(2005, 4, 3, 4, 18, 0).ago(24.hours), "dt-24.hours=>st"
+ assert_equal Time.local(2005, 4, 2, 3, 18, 0), Time.local(2005, 4, 3, 4, 18, 0).ago(86400), "dt-86400=>st"
+ assert_equal Time.local(2005, 4, 2, 3, 18, 0), Time.local(2005, 4, 3, 4, 18, 0).ago(86400.seconds), "dt-86400.seconds=>st"
- assert_equal Time.local(2005,4,1,4,18,0), Time.local(2005,4,2,4,18,0).ago(24.hours), "st-24.hours=>st"
- assert_equal Time.local(2005,4,1,4,18,0), Time.local(2005,4,2,4,18,0).ago(86400), "st-86400=>st"
- assert_equal Time.local(2005,4,1,4,18,0), Time.local(2005,4,2,4,18,0).ago(86400.seconds), "st-86400.seconds=>st"
+ assert_equal Time.local(2005, 4, 1, 4, 18, 0), Time.local(2005, 4, 2, 4, 18, 0).ago(24.hours), "st-24.hours=>st"
+ assert_equal Time.local(2005, 4, 1, 4, 18, 0), Time.local(2005, 4, 2, 4, 18, 0).ago(86400), "st-86400=>st"
+ assert_equal Time.local(2005, 4, 1, 4, 18, 0), Time.local(2005, 4, 2, 4, 18, 0).ago(86400.seconds), "st-86400.seconds=>st"
end
with_env_tz "NZ" do
# dt: New Zealand: 2006 October 1st 4:18am
- assert_equal Time.local(2006,9,30,3,18,0), Time.local(2006,10,1,4,18,0).ago(24.hours), "dt-24.hours=>st"
- assert_equal Time.local(2006,9,30,3,18,0), Time.local(2006,10,1,4,18,0).ago(86400), "dt-86400=>st"
- assert_equal Time.local(2006,9,30,3,18,0), Time.local(2006,10,1,4,18,0).ago(86400.seconds), "dt-86400.seconds=>st"
+ assert_equal Time.local(2006, 9, 30, 3, 18, 0), Time.local(2006, 10, 1, 4, 18, 0).ago(24.hours), "dt-24.hours=>st"
+ assert_equal Time.local(2006, 9, 30, 3, 18, 0), Time.local(2006, 10, 1, 4, 18, 0).ago(86400), "dt-86400=>st"
+ assert_equal Time.local(2006, 9, 30, 3, 18, 0), Time.local(2006, 10, 1, 4, 18, 0).ago(86400.seconds), "dt-86400.seconds=>st"
- assert_equal Time.local(2006,9,29,4,18,0), Time.local(2006,9,30,4,18,0).ago(24.hours), "st-24.hours=>st"
- assert_equal Time.local(2006,9,29,4,18,0), Time.local(2006,9,30,4,18,0).ago(86400), "st-86400=>st"
- assert_equal Time.local(2006,9,29,4,18,0), Time.local(2006,9,30,4,18,0).ago(86400.seconds), "st-86400.seconds=>st"
+ assert_equal Time.local(2006, 9, 29, 4, 18, 0), Time.local(2006, 9, 30, 4, 18, 0).ago(24.hours), "st-24.hours=>st"
+ assert_equal Time.local(2006, 9, 29, 4, 18, 0), Time.local(2006, 9, 30, 4, 18, 0).ago(86400), "st-86400=>st"
+ assert_equal Time.local(2006, 9, 29, 4, 18, 0), Time.local(2006, 9, 30, 4, 18, 0).ago(86400.seconds), "st-86400.seconds=>st"
end
end
def test_daylight_savings_time_crossings_backward_end
with_env_tz "US/Eastern" do
# st: US: 2005 October 30th 4:03am
- assert_equal Time.local(2005,10,29,5,3), Time.local(2005,10,30,4,3,0).ago(24.hours), "st-24.hours=>dt"
- assert_equal Time.local(2005,10,29,5,3), Time.local(2005,10,30,4,3,0).ago(86400), "st-86400=>dt"
- assert_equal Time.local(2005,10,29,5,3), Time.local(2005,10,30,4,3,0).ago(86400.seconds), "st-86400.seconds=>dt"
+ assert_equal Time.local(2005, 10, 29, 5, 3), Time.local(2005, 10, 30, 4, 3, 0).ago(24.hours), "st-24.hours=>dt"
+ assert_equal Time.local(2005, 10, 29, 5, 3), Time.local(2005, 10, 30, 4, 3, 0).ago(86400), "st-86400=>dt"
+ assert_equal Time.local(2005, 10, 29, 5, 3), Time.local(2005, 10, 30, 4, 3, 0).ago(86400.seconds), "st-86400.seconds=>dt"
- assert_equal Time.local(2005,10,28,4,3), Time.local(2005,10,29,4,3,0).ago(24.hours), "dt-24.hours=>dt"
- assert_equal Time.local(2005,10,28,4,3), Time.local(2005,10,29,4,3,0).ago(86400), "dt-86400=>dt"
- assert_equal Time.local(2005,10,28,4,3), Time.local(2005,10,29,4,3,0).ago(86400.seconds), "dt-86400.seconds=>dt"
+ assert_equal Time.local(2005, 10, 28, 4, 3), Time.local(2005, 10, 29, 4, 3, 0).ago(24.hours), "dt-24.hours=>dt"
+ assert_equal Time.local(2005, 10, 28, 4, 3), Time.local(2005, 10, 29, 4, 3, 0).ago(86400), "dt-86400=>dt"
+ assert_equal Time.local(2005, 10, 28, 4, 3), Time.local(2005, 10, 29, 4, 3, 0).ago(86400.seconds), "dt-86400.seconds=>dt"
end
with_env_tz "NZ" do
# st: New Zealand: 2006 March 19th 4:03am
- assert_equal Time.local(2006,3,18,5,3), Time.local(2006,3,19,4,3,0).ago(24.hours), "st-24.hours=>dt"
- assert_equal Time.local(2006,3,18,5,3), Time.local(2006,3,19,4,3,0).ago(86400), "st-86400=>dt"
- assert_equal Time.local(2006,3,18,5,3), Time.local(2006,3,19,4,3,0).ago(86400.seconds), "st-86400.seconds=>dt"
+ assert_equal Time.local(2006, 3, 18, 5, 3), Time.local(2006, 3, 19, 4, 3, 0).ago(24.hours), "st-24.hours=>dt"
+ assert_equal Time.local(2006, 3, 18, 5, 3), Time.local(2006, 3, 19, 4, 3, 0).ago(86400), "st-86400=>dt"
+ assert_equal Time.local(2006, 3, 18, 5, 3), Time.local(2006, 3, 19, 4, 3, 0).ago(86400.seconds), "st-86400.seconds=>dt"
- assert_equal Time.local(2006,3,17,4,3), Time.local(2006,3,18,4,3,0).ago(24.hours), "dt-24.hours=>dt"
- assert_equal Time.local(2006,3,17,4,3), Time.local(2006,3,18,4,3,0).ago(86400), "dt-86400=>dt"
- assert_equal Time.local(2006,3,17,4,3), Time.local(2006,3,18,4,3,0).ago(86400.seconds), "dt-86400.seconds=>dt"
+ assert_equal Time.local(2006, 3, 17, 4, 3), Time.local(2006, 3, 18, 4, 3, 0).ago(24.hours), "dt-24.hours=>dt"
+ assert_equal Time.local(2006, 3, 17, 4, 3), Time.local(2006, 3, 18, 4, 3, 0).ago(86400), "dt-86400=>dt"
+ assert_equal Time.local(2006, 3, 17, 4, 3), Time.local(2006, 3, 18, 4, 3, 0).ago(86400.seconds), "dt-86400.seconds=>dt"
end
end
def test_daylight_savings_time_crossings_backward_start_1day
with_env_tz "US/Eastern" do
# dt: US: 2005 April 3rd 4:18am
- assert_equal Time.local(2005,4,2,4,18,0), Time.local(2005,4,3,4,18,0).ago(1.day), "dt-1.day=>st"
- assert_equal Time.local(2005,4,1,4,18,0), Time.local(2005,4,2,4,18,0).ago(1.day), "st-1.day=>st"
+ assert_equal Time.local(2005, 4, 2, 4, 18, 0), Time.local(2005, 4, 3, 4, 18, 0).ago(1.day), "dt-1.day=>st"
+ assert_equal Time.local(2005, 4, 1, 4, 18, 0), Time.local(2005, 4, 2, 4, 18, 0).ago(1.day), "st-1.day=>st"
end
with_env_tz "NZ" do
# dt: New Zealand: 2006 October 1st 4:18am
- assert_equal Time.local(2006,9,30,4,18,0), Time.local(2006,10,1,4,18,0).ago(1.day), "dt-1.day=>st"
- assert_equal Time.local(2006,9,29,4,18,0), Time.local(2006,9,30,4,18,0).ago(1.day), "st-1.day=>st"
+ assert_equal Time.local(2006, 9, 30, 4, 18, 0), Time.local(2006, 10, 1, 4, 18, 0).ago(1.day), "dt-1.day=>st"
+ assert_equal Time.local(2006, 9, 29, 4, 18, 0), Time.local(2006, 9, 30, 4, 18, 0).ago(1.day), "st-1.day=>st"
end
end
def test_daylight_savings_time_crossings_backward_end_1day
with_env_tz "US/Eastern" do
# st: US: 2005 October 30th 4:03am
- assert_equal Time.local(2005,10,29,4,3), Time.local(2005,10,30,4,3,0).ago(1.day), "st-1.day=>dt"
- assert_equal Time.local(2005,10,28,4,3), Time.local(2005,10,29,4,3,0).ago(1.day), "dt-1.day=>dt"
+ assert_equal Time.local(2005, 10, 29, 4, 3), Time.local(2005, 10, 30, 4, 3, 0).ago(1.day), "st-1.day=>dt"
+ assert_equal Time.local(2005, 10, 28, 4, 3), Time.local(2005, 10, 29, 4, 3, 0).ago(1.day), "dt-1.day=>dt"
end
with_env_tz "NZ" do
# st: New Zealand: 2006 March 19th 4:03am
- assert_equal Time.local(2006,3,18,4,3), Time.local(2006,3,19,4,3,0).ago(1.day), "st-1.day=>dt"
- assert_equal Time.local(2006,3,17,4,3), Time.local(2006,3,18,4,3,0).ago(1.day), "dt-1.day=>dt"
+ assert_equal Time.local(2006, 3, 18, 4, 3), Time.local(2006, 3, 19, 4, 3, 0).ago(1.day), "st-1.day=>dt"
+ assert_equal Time.local(2006, 3, 17, 4, 3), Time.local(2006, 3, 18, 4, 3, 0).ago(1.day), "dt-1.day=>dt"
end
end
def test_since
- assert_equal Time.local(2005,2,22,10,10,11), Time.local(2005,2,22,10,10,10).since(1)
- assert_equal Time.local(2005,2,22,11,10,10), Time.local(2005,2,22,10,10,10).since(3600)
- assert_equal Time.local(2005,2,24,10,10,10), Time.local(2005,2,22,10,10,10).since(86400*2)
- assert_equal Time.local(2005,2,24,11,10,35), Time.local(2005,2,22,10,10,10).since(86400*2 + 3600 + 25)
+ assert_equal Time.local(2005, 2, 22, 10, 10, 11), Time.local(2005, 2, 22, 10, 10, 10).since(1)
+ assert_equal Time.local(2005, 2, 22, 11, 10, 10), Time.local(2005, 2, 22, 10, 10, 10).since(3600)
+ assert_equal Time.local(2005, 2, 24, 10, 10, 10), Time.local(2005, 2, 22, 10, 10, 10).since(86400 * 2)
+ assert_equal Time.local(2005, 2, 24, 11, 10, 35), Time.local(2005, 2, 22, 10, 10, 10).since(86400 * 2 + 3600 + 25)
# when out of range of Time, returns a DateTime
- assert_equal DateTime.civil(2038,1,20,11,59,59), Time.utc(2038,1,18,11,59,59).since(86400*2)
+ assert_equal DateTime.civil(2038, 1, 20, 11, 59, 59), Time.utc(2038, 1, 18, 11, 59, 59).since(86400 * 2)
end
def test_daylight_savings_time_crossings_forward_start
with_env_tz "US/Eastern" do
# st: US: 2005 April 2nd 7:27pm
- assert_equal Time.local(2005,4,3,20,27,0), Time.local(2005,4,2,19,27,0).since(24.hours), "st+24.hours=>dt"
- assert_equal Time.local(2005,4,3,20,27,0), Time.local(2005,4,2,19,27,0).since(86400), "st+86400=>dt"
- assert_equal Time.local(2005,4,3,20,27,0), Time.local(2005,4,2,19,27,0).since(86400.seconds), "st+86400.seconds=>dt"
+ assert_equal Time.local(2005, 4, 3, 20, 27, 0), Time.local(2005, 4, 2, 19, 27, 0).since(24.hours), "st+24.hours=>dt"
+ assert_equal Time.local(2005, 4, 3, 20, 27, 0), Time.local(2005, 4, 2, 19, 27, 0).since(86400), "st+86400=>dt"
+ assert_equal Time.local(2005, 4, 3, 20, 27, 0), Time.local(2005, 4, 2, 19, 27, 0).since(86400.seconds), "st+86400.seconds=>dt"
- assert_equal Time.local(2005,4,4,19,27,0), Time.local(2005,4,3,19,27,0).since(24.hours), "dt+24.hours=>dt"
- assert_equal Time.local(2005,4,4,19,27,0), Time.local(2005,4,3,19,27,0).since(86400), "dt+86400=>dt"
- assert_equal Time.local(2005,4,4,19,27,0), Time.local(2005,4,3,19,27,0).since(86400.seconds), "dt+86400.seconds=>dt"
+ assert_equal Time.local(2005, 4, 4, 19, 27, 0), Time.local(2005, 4, 3, 19, 27, 0).since(24.hours), "dt+24.hours=>dt"
+ assert_equal Time.local(2005, 4, 4, 19, 27, 0), Time.local(2005, 4, 3, 19, 27, 0).since(86400), "dt+86400=>dt"
+ assert_equal Time.local(2005, 4, 4, 19, 27, 0), Time.local(2005, 4, 3, 19, 27, 0).since(86400.seconds), "dt+86400.seconds=>dt"
end
with_env_tz "NZ" do
# st: New Zealand: 2006 September 30th 7:27pm
- assert_equal Time.local(2006,10,1,20,27,0), Time.local(2006,9,30,19,27,0).since(24.hours), "st+24.hours=>dt"
- assert_equal Time.local(2006,10,1,20,27,0), Time.local(2006,9,30,19,27,0).since(86400), "st+86400=>dt"
- assert_equal Time.local(2006,10,1,20,27,0), Time.local(2006,9,30,19,27,0).since(86400.seconds), "st+86400.seconds=>dt"
+ assert_equal Time.local(2006, 10, 1, 20, 27, 0), Time.local(2006, 9, 30, 19, 27, 0).since(24.hours), "st+24.hours=>dt"
+ assert_equal Time.local(2006, 10, 1, 20, 27, 0), Time.local(2006, 9, 30, 19, 27, 0).since(86400), "st+86400=>dt"
+ assert_equal Time.local(2006, 10, 1, 20, 27, 0), Time.local(2006, 9, 30, 19, 27, 0).since(86400.seconds), "st+86400.seconds=>dt"
- assert_equal Time.local(2006,10,2,19,27,0), Time.local(2006,10,1,19,27,0).since(24.hours), "dt+24.hours=>dt"
- assert_equal Time.local(2006,10,2,19,27,0), Time.local(2006,10,1,19,27,0).since(86400), "dt+86400=>dt"
- assert_equal Time.local(2006,10,2,19,27,0), Time.local(2006,10,1,19,27,0).since(86400.seconds), "dt+86400.seconds=>dt"
+ assert_equal Time.local(2006, 10, 2, 19, 27, 0), Time.local(2006, 10, 1, 19, 27, 0).since(24.hours), "dt+24.hours=>dt"
+ assert_equal Time.local(2006, 10, 2, 19, 27, 0), Time.local(2006, 10, 1, 19, 27, 0).since(86400), "dt+86400=>dt"
+ assert_equal Time.local(2006, 10, 2, 19, 27, 0), Time.local(2006, 10, 1, 19, 27, 0).since(86400.seconds), "dt+86400.seconds=>dt"
end
end
def test_daylight_savings_time_crossings_forward_start_1day
with_env_tz "US/Eastern" do
# st: US: 2005 April 2nd 7:27pm
- assert_equal Time.local(2005,4,3,19,27,0), Time.local(2005,4,2,19,27,0).since(1.day), "st+1.day=>dt"
- assert_equal Time.local(2005,4,4,19,27,0), Time.local(2005,4,3,19,27,0).since(1.day), "dt+1.day=>dt"
+ assert_equal Time.local(2005, 4, 3, 19, 27, 0), Time.local(2005, 4, 2, 19, 27, 0).since(1.day), "st+1.day=>dt"
+ assert_equal Time.local(2005, 4, 4, 19, 27, 0), Time.local(2005, 4, 3, 19, 27, 0).since(1.day), "dt+1.day=>dt"
end
with_env_tz "NZ" do
# st: New Zealand: 2006 September 30th 7:27pm
- assert_equal Time.local(2006,10,1,19,27,0), Time.local(2006,9,30,19,27,0).since(1.day), "st+1.day=>dt"
- assert_equal Time.local(2006,10,2,19,27,0), Time.local(2006,10,1,19,27,0).since(1.day), "dt+1.day=>dt"
+ assert_equal Time.local(2006, 10, 1, 19, 27, 0), Time.local(2006, 9, 30, 19, 27, 0).since(1.day), "st+1.day=>dt"
+ assert_equal Time.local(2006, 10, 2, 19, 27, 0), Time.local(2006, 10, 1, 19, 27, 0).since(1.day), "dt+1.day=>dt"
end
end
def test_daylight_savings_time_crossings_forward_start_tomorrow
with_env_tz "US/Eastern" do
# st: US: 2005 April 2nd 7:27pm
- assert_equal Time.local(2005,4,3,19,27,0), Time.local(2005,4,2,19,27,0).tomorrow, "st+1.day=>dt"
- assert_equal Time.local(2005,4,4,19,27,0), Time.local(2005,4,3,19,27,0).tomorrow, "dt+1.day=>dt"
+ assert_equal Time.local(2005, 4, 3, 19, 27, 0), Time.local(2005, 4, 2, 19, 27, 0).tomorrow, "st+1.day=>dt"
+ assert_equal Time.local(2005, 4, 4, 19, 27, 0), Time.local(2005, 4, 3, 19, 27, 0).tomorrow, "dt+1.day=>dt"
end
with_env_tz "NZ" do
# st: New Zealand: 2006 September 30th 7:27pm
- assert_equal Time.local(2006,10,1,19,27,0), Time.local(2006,9,30,19,27,0).tomorrow, "st+1.day=>dt"
- assert_equal Time.local(2006,10,2,19,27,0), Time.local(2006,10,1,19,27,0).tomorrow, "dt+1.day=>dt"
+ assert_equal Time.local(2006, 10, 1, 19, 27, 0), Time.local(2006, 9, 30, 19, 27, 0).tomorrow, "st+1.day=>dt"
+ assert_equal Time.local(2006, 10, 2, 19, 27, 0), Time.local(2006, 10, 1, 19, 27, 0).tomorrow, "dt+1.day=>dt"
end
end
def test_daylight_savings_time_crossings_backward_start_yesterday
with_env_tz "US/Eastern" do
# st: US: 2005 April 2nd 7:27pm
- assert_equal Time.local(2005,4,2,19,27,0), Time.local(2005,4,3,19,27,0).yesterday, "dt-1.day=>st"
- assert_equal Time.local(2005,4,3,19,27,0), Time.local(2005,4,4,19,27,0).yesterday, "dt-1.day=>dt"
+ assert_equal Time.local(2005, 4, 2, 19, 27, 0), Time.local(2005, 4, 3, 19, 27, 0).yesterday, "dt-1.day=>st"
+ assert_equal Time.local(2005, 4, 3, 19, 27, 0), Time.local(2005, 4, 4, 19, 27, 0).yesterday, "dt-1.day=>dt"
end
with_env_tz "NZ" do
# st: New Zealand: 2006 September 30th 7:27pm
- assert_equal Time.local(2006,9,30,19,27,0), Time.local(2006,10,1,19,27,0).yesterday, "dt-1.day=>st"
- assert_equal Time.local(2006,10,1,19,27,0), Time.local(2006,10,2,19,27,0).yesterday, "dt-1.day=>dt"
+ assert_equal Time.local(2006, 9, 30, 19, 27, 0), Time.local(2006, 10, 1, 19, 27, 0).yesterday, "dt-1.day=>st"
+ assert_equal Time.local(2006, 10, 1, 19, 27, 0), Time.local(2006, 10, 2, 19, 27, 0).yesterday, "dt-1.day=>dt"
end
end
def test_daylight_savings_time_crossings_forward_end
with_env_tz "US/Eastern" do
# dt: US: 2005 October 30th 12:45am
- assert_equal Time.local(2005,10,30,23,45,0), Time.local(2005,10,30,0,45,0).since(24.hours), "dt+24.hours=>st"
- assert_equal Time.local(2005,10,30,23,45,0), Time.local(2005,10,30,0,45,0).since(86400), "dt+86400=>st"
- assert_equal Time.local(2005,10,30,23,45,0), Time.local(2005,10,30,0,45,0).since(86400.seconds), "dt+86400.seconds=>st"
+ assert_equal Time.local(2005, 10, 30, 23, 45, 0), Time.local(2005, 10, 30, 0, 45, 0).since(24.hours), "dt+24.hours=>st"
+ assert_equal Time.local(2005, 10, 30, 23, 45, 0), Time.local(2005, 10, 30, 0, 45, 0).since(86400), "dt+86400=>st"
+ assert_equal Time.local(2005, 10, 30, 23, 45, 0), Time.local(2005, 10, 30, 0, 45, 0).since(86400.seconds), "dt+86400.seconds=>st"
- assert_equal Time.local(2005,11, 1,0,45,0), Time.local(2005,10,31,0,45,0).since(24.hours), "st+24.hours=>st"
- assert_equal Time.local(2005,11, 1,0,45,0), Time.local(2005,10,31,0,45,0).since(86400), "st+86400=>st"
- assert_equal Time.local(2005,11, 1,0,45,0), Time.local(2005,10,31,0,45,0).since(86400.seconds), "st+86400.seconds=>st"
+ assert_equal Time.local(2005, 11, 1, 0, 45, 0), Time.local(2005, 10, 31, 0, 45, 0).since(24.hours), "st+24.hours=>st"
+ assert_equal Time.local(2005, 11, 1, 0, 45, 0), Time.local(2005, 10, 31, 0, 45, 0).since(86400), "st+86400=>st"
+ assert_equal Time.local(2005, 11, 1, 0, 45, 0), Time.local(2005, 10, 31, 0, 45, 0).since(86400.seconds), "st+86400.seconds=>st"
end
with_env_tz "NZ" do
# dt: New Zealand: 2006 March 19th 1:45am
- assert_equal Time.local(2006,3,20,0,45,0), Time.local(2006,3,19,1,45,0).since(24.hours), "dt+24.hours=>st"
- assert_equal Time.local(2006,3,20,0,45,0), Time.local(2006,3,19,1,45,0).since(86400), "dt+86400=>st"
- assert_equal Time.local(2006,3,20,0,45,0), Time.local(2006,3,19,1,45,0).since(86400.seconds), "dt+86400.seconds=>st"
+ assert_equal Time.local(2006, 3, 20, 0, 45, 0), Time.local(2006, 3, 19, 1, 45, 0).since(24.hours), "dt+24.hours=>st"
+ assert_equal Time.local(2006, 3, 20, 0, 45, 0), Time.local(2006, 3, 19, 1, 45, 0).since(86400), "dt+86400=>st"
+ assert_equal Time.local(2006, 3, 20, 0, 45, 0), Time.local(2006, 3, 19, 1, 45, 0).since(86400.seconds), "dt+86400.seconds=>st"
- assert_equal Time.local(2006,3,21,1,45,0), Time.local(2006,3,20,1,45,0).since(24.hours), "st+24.hours=>st"
- assert_equal Time.local(2006,3,21,1,45,0), Time.local(2006,3,20,1,45,0).since(86400), "st+86400=>st"
- assert_equal Time.local(2006,3,21,1,45,0), Time.local(2006,3,20,1,45,0).since(86400.seconds), "st+86400.seconds=>st"
+ assert_equal Time.local(2006, 3, 21, 1, 45, 0), Time.local(2006, 3, 20, 1, 45, 0).since(24.hours), "st+24.hours=>st"
+ assert_equal Time.local(2006, 3, 21, 1, 45, 0), Time.local(2006, 3, 20, 1, 45, 0).since(86400), "st+86400=>st"
+ assert_equal Time.local(2006, 3, 21, 1, 45, 0), Time.local(2006, 3, 20, 1, 45, 0).since(86400.seconds), "st+86400.seconds=>st"
end
end
def test_daylight_savings_time_crossings_forward_end_1day
with_env_tz "US/Eastern" do
# dt: US: 2005 October 30th 12:45am
- assert_equal Time.local(2005,10,31,0,45,0), Time.local(2005,10,30,0,45,0).since(1.day), "dt+1.day=>st"
- assert_equal Time.local(2005,11, 1,0,45,0), Time.local(2005,10,31,0,45,0).since(1.day), "st+1.day=>st"
+ assert_equal Time.local(2005, 10, 31, 0, 45, 0), Time.local(2005, 10, 30, 0, 45, 0).since(1.day), "dt+1.day=>st"
+ assert_equal Time.local(2005, 11, 1, 0, 45, 0), Time.local(2005, 10, 31, 0, 45, 0).since(1.day), "st+1.day=>st"
end
with_env_tz "NZ" do
# dt: New Zealand: 2006 March 19th 1:45am
- assert_equal Time.local(2006,3,20,1,45,0), Time.local(2006,3,19,1,45,0).since(1.day), "dt+1.day=>st"
- assert_equal Time.local(2006,3,21,1,45,0), Time.local(2006,3,20,1,45,0).since(1.day), "st+1.day=>st"
+ assert_equal Time.local(2006, 3, 20, 1, 45, 0), Time.local(2006, 3, 19, 1, 45, 0).since(1.day), "dt+1.day=>st"
+ assert_equal Time.local(2006, 3, 21, 1, 45, 0), Time.local(2006, 3, 20, 1, 45, 0).since(1.day), "st+1.day=>st"
end
end
def test_daylight_savings_time_crossings_forward_end_tomorrow
with_env_tz "US/Eastern" do
# dt: US: 2005 October 30th 12:45am
- assert_equal Time.local(2005,10,31,0,45,0), Time.local(2005,10,30,0,45,0).tomorrow, "dt+1.day=>st"
- assert_equal Time.local(2005,11, 1,0,45,0), Time.local(2005,10,31,0,45,0).tomorrow, "st+1.day=>st"
+ assert_equal Time.local(2005, 10, 31, 0, 45, 0), Time.local(2005, 10, 30, 0, 45, 0).tomorrow, "dt+1.day=>st"
+ assert_equal Time.local(2005, 11, 1, 0, 45, 0), Time.local(2005, 10, 31, 0, 45, 0).tomorrow, "st+1.day=>st"
end
with_env_tz "NZ" do
# dt: New Zealand: 2006 March 19th 1:45am
- assert_equal Time.local(2006,3,20,1,45,0), Time.local(2006,3,19,1,45,0).tomorrow, "dt+1.day=>st"
- assert_equal Time.local(2006,3,21,1,45,0), Time.local(2006,3,20,1,45,0).tomorrow, "st+1.day=>st"
+ assert_equal Time.local(2006, 3, 20, 1, 45, 0), Time.local(2006, 3, 19, 1, 45, 0).tomorrow, "dt+1.day=>st"
+ assert_equal Time.local(2006, 3, 21, 1, 45, 0), Time.local(2006, 3, 20, 1, 45, 0).tomorrow, "st+1.day=>st"
end
end
def test_daylight_savings_time_crossings_backward_end_yesterday
with_env_tz "US/Eastern" do
# dt: US: 2005 October 30th 12:45am
- assert_equal Time.local(2005,10,30,0,45,0), Time.local(2005,10,31,0,45,0).yesterday, "st-1.day=>dt"
- assert_equal Time.local(2005,10, 31,0,45,0), Time.local(2005,11,1,0,45,0).yesterday, "st-1.day=>st"
+ assert_equal Time.local(2005, 10, 30, 0, 45, 0), Time.local(2005, 10, 31, 0, 45, 0).yesterday, "st-1.day=>dt"
+ assert_equal Time.local(2005, 10, 31, 0, 45, 0), Time.local(2005, 11, 1, 0, 45, 0).yesterday, "st-1.day=>st"
end
with_env_tz "NZ" do
# dt: New Zealand: 2006 March 19th 1:45am
- assert_equal Time.local(2006,3,19,1,45,0), Time.local(2006,3,20,1,45,0).yesterday, "st-1.day=>dt"
- assert_equal Time.local(2006,3,20,1,45,0), Time.local(2006,3,21,1,45,0).yesterday, "st-1.day=>st"
+ assert_equal Time.local(2006, 3, 19, 1, 45, 0), Time.local(2006, 3, 20, 1, 45, 0).yesterday, "st-1.day=>dt"
+ assert_equal Time.local(2006, 3, 20, 1, 45, 0), Time.local(2006, 3, 21, 1, 45, 0).yesterday, "st-1.day=>st"
end
end
def test_change
- assert_equal Time.local(2006,2,22,15,15,10), Time.local(2005,2,22,15,15,10).change(year: 2006)
- assert_equal Time.local(2005,6,22,15,15,10), Time.local(2005,2,22,15,15,10).change(month: 6)
- assert_equal Time.local(2012,9,22,15,15,10), Time.local(2005,2,22,15,15,10).change(year: 2012, month: 9)
- assert_equal Time.local(2005,2,22,16), Time.local(2005,2,22,15,15,10).change(hour: 16)
- assert_equal Time.local(2005,2,22,16,45), Time.local(2005,2,22,15,15,10).change(hour: 16, min: 45)
- assert_equal Time.local(2005,2,22,15,45), Time.local(2005,2,22,15,15,10).change(min: 45)
-
- assert_equal Time.local(2005,1,2, 5, 0, 0, 0), Time.local(2005,1,2,11,22,33,44).change(hour: 5)
- assert_equal Time.local(2005,1,2,11, 6, 0, 0), Time.local(2005,1,2,11,22,33,44).change(min: 6)
- assert_equal Time.local(2005,1,2,11,22, 7, 0), Time.local(2005,1,2,11,22,33,44).change(sec: 7)
- assert_equal Time.local(2005,1,2,11,22,33, 8), Time.local(2005,1,2,11,22,33,44).change(usec: 8)
- assert_equal Time.local(2005,1,2,11,22,33, 8), Time.local(2005,1,2,11,22,33,2).change(nsec: 8000)
- assert_raise(ArgumentError) { Time.local(2005,1,2,11,22,33, 8).change(usec: 1, nsec: 1) }
+ assert_equal Time.local(2006, 2, 22, 15, 15, 10), Time.local(2005, 2, 22, 15, 15, 10).change(year: 2006)
+ assert_equal Time.local(2005, 6, 22, 15, 15, 10), Time.local(2005, 2, 22, 15, 15, 10).change(month: 6)
+ assert_equal Time.local(2012, 9, 22, 15, 15, 10), Time.local(2005, 2, 22, 15, 15, 10).change(year: 2012, month: 9)
+ assert_equal Time.local(2005, 2, 22, 16), Time.local(2005, 2, 22, 15, 15, 10).change(hour: 16)
+ assert_equal Time.local(2005, 2, 22, 16, 45), Time.local(2005, 2, 22, 15, 15, 10).change(hour: 16, min: 45)
+ assert_equal Time.local(2005, 2, 22, 15, 45), Time.local(2005, 2, 22, 15, 15, 10).change(min: 45)
+
+ assert_equal Time.local(2005, 1, 2, 5, 0, 0, 0), Time.local(2005, 1, 2, 11, 22, 33, 44).change(hour: 5)
+ assert_equal Time.local(2005, 1, 2, 11, 6, 0, 0), Time.local(2005, 1, 2, 11, 22, 33, 44).change(min: 6)
+ assert_equal Time.local(2005, 1, 2, 11, 22, 7, 0), Time.local(2005, 1, 2, 11, 22, 33, 44).change(sec: 7)
+ assert_equal Time.local(2005, 1, 2, 11, 22, 33, 8), Time.local(2005, 1, 2, 11, 22, 33, 44).change(usec: 8)
+ assert_equal Time.local(2005, 1, 2, 11, 22, 33, 8), Time.local(2005, 1, 2, 11, 22, 33, 2).change(nsec: 8000)
+ assert_raise(ArgumentError) { Time.local(2005, 1, 2, 11, 22, 33, 8).change(usec: 1, nsec: 1) }
assert_nothing_raised { Time.new(2015, 5, 9, 10, 00, 00, "+03:00").change(nsec: 999999999) }
end
def test_utc_change
- assert_equal Time.utc(2006,2,22,15,15,10), Time.utc(2005,2,22,15,15,10).change(year: 2006)
- assert_equal Time.utc(2005,6,22,15,15,10), Time.utc(2005,2,22,15,15,10).change(month: 6)
- assert_equal Time.utc(2012,9,22,15,15,10), Time.utc(2005,2,22,15,15,10).change(year: 2012, month: 9)
- assert_equal Time.utc(2005,2,22,16), Time.utc(2005,2,22,15,15,10).change(hour: 16)
- assert_equal Time.utc(2005,2,22,16,45), Time.utc(2005,2,22,15,15,10).change(hour: 16, min: 45)
- assert_equal Time.utc(2005,2,22,15,45), Time.utc(2005,2,22,15,15,10).change(min: 45)
- assert_equal Time.utc(2005,1,2,11,22,33,8), Time.utc(2005,1,2,11,22,33,2).change(nsec: 8000)
+ assert_equal Time.utc(2006, 2, 22, 15, 15, 10), Time.utc(2005, 2, 22, 15, 15, 10).change(year: 2006)
+ assert_equal Time.utc(2005, 6, 22, 15, 15, 10), Time.utc(2005, 2, 22, 15, 15, 10).change(month: 6)
+ assert_equal Time.utc(2012, 9, 22, 15, 15, 10), Time.utc(2005, 2, 22, 15, 15, 10).change(year: 2012, month: 9)
+ assert_equal Time.utc(2005, 2, 22, 16), Time.utc(2005, 2, 22, 15, 15, 10).change(hour: 16)
+ assert_equal Time.utc(2005, 2, 22, 16, 45), Time.utc(2005, 2, 22, 15, 15, 10).change(hour: 16, min: 45)
+ assert_equal Time.utc(2005, 2, 22, 15, 45), Time.utc(2005, 2, 22, 15, 15, 10).change(min: 45)
+ assert_equal Time.utc(2005, 1, 2, 11, 22, 33, 8), Time.utc(2005, 1, 2, 11, 22, 33, 2).change(nsec: 8000)
end
def test_offset_change
- assert_equal Time.new(2006,2,22,15,15,10,"-08:00"), Time.new(2005,2,22,15,15,10,"-08:00").change(year: 2006)
- assert_equal Time.new(2005,6,22,15,15,10,"-08:00"), Time.new(2005,2,22,15,15,10,"-08:00").change(month: 6)
- assert_equal Time.new(2012,9,22,15,15,10,"-08:00"), Time.new(2005,2,22,15,15,10,"-08:00").change(year: 2012, month: 9)
- assert_equal Time.new(2005,2,22,16,0,0,"-08:00"), Time.new(2005,2,22,15,15,10,"-08:00").change(hour: 16)
- assert_equal Time.new(2005,2,22,16,45,0,"-08:00"), Time.new(2005,2,22,15,15,10,"-08:00").change(hour: 16, min: 45)
- assert_equal Time.new(2005,2,22,15,45,0,"-08:00"), Time.new(2005,2,22,15,15,10,"-08:00").change(min: 45)
- assert_equal Time.new(2005,2,22,15,15,10,"-08:00"), Time.new(2005,2,22,15,15,0,"-08:00").change(sec: 10)
- assert_equal 10, Time.new(2005,2,22,15,15,0,"-08:00").change(usec: 10).usec
- assert_equal 10, Time.new(2005,2,22,15,15,0,"-08:00").change(nsec: 10).nsec
+ assert_equal Time.new(2006, 2, 22, 15, 15, 10, "-08:00"), Time.new(2005, 2, 22, 15, 15, 10, "-08:00").change(year: 2006)
+ assert_equal Time.new(2005, 6, 22, 15, 15, 10, "-08:00"), Time.new(2005, 2, 22, 15, 15, 10, "-08:00").change(month: 6)
+ assert_equal Time.new(2012, 9, 22, 15, 15, 10, "-08:00"), Time.new(2005, 2, 22, 15, 15, 10, "-08:00").change(year: 2012, month: 9)
+ assert_equal Time.new(2005, 2, 22, 16, 0, 0, "-08:00"), Time.new(2005, 2, 22, 15, 15, 10, "-08:00").change(hour: 16)
+ assert_equal Time.new(2005, 2, 22, 16, 45, 0, "-08:00"), Time.new(2005, 2, 22, 15, 15, 10, "-08:00").change(hour: 16, min: 45)
+ assert_equal Time.new(2005, 2, 22, 15, 45, 0, "-08:00"), Time.new(2005, 2, 22, 15, 15, 10, "-08:00").change(min: 45)
+ assert_equal Time.new(2005, 2, 22, 15, 15, 10, "-08:00"), Time.new(2005, 2, 22, 15, 15, 0, "-08:00").change(sec: 10)
+ assert_equal 10, Time.new(2005, 2, 22, 15, 15, 0, "-08:00").change(usec: 10).usec
+ assert_equal 10, Time.new(2005, 2, 22, 15, 15, 0, "-08:00").change(nsec: 10).nsec
assert_raise(ArgumentError) { Time.new(2005, 2, 22, 15, 15, 45, "-08:00").change(usec: 1000000) }
assert_raise(ArgumentError) { Time.new(2005, 2, 22, 15, 15, 45, "-08:00").change(nsec: 1000000000) }
end
def test_advance
- assert_equal Time.local(2006,2,28,15,15,10), Time.local(2005,2,28,15,15,10).advance(years: 1)
- assert_equal Time.local(2005,6,28,15,15,10), Time.local(2005,2,28,15,15,10).advance(months: 4)
- assert_equal Time.local(2005,3,21,15,15,10), Time.local(2005,2,28,15,15,10).advance(weeks: 3)
- assert_equal Time.local(2005,3,25,3,15,10), Time.local(2005,2,28,15,15,10).advance(weeks: 3.5)
- assert_in_delta Time.local(2005,3,26,12,51,10), Time.local(2005,2,28,15,15,10).advance(weeks: 3.7), 1
- assert_equal Time.local(2005,3,5,15,15,10), Time.local(2005,2,28,15,15,10).advance(days: 5)
- assert_equal Time.local(2005,3,6,3,15,10), Time.local(2005,2,28,15,15,10).advance(days: 5.5)
- assert_in_delta Time.local(2005,3,6,8,3,10), Time.local(2005,2,28,15,15,10).advance(days: 5.7), 1
- assert_equal Time.local(2012,9,28,15,15,10), Time.local(2005,2,28,15,15,10).advance(years: 7, months: 7)
- assert_equal Time.local(2013,10,3,15,15,10), Time.local(2005,2,28,15,15,10).advance(years: 7, months: 19, days: 5)
- assert_equal Time.local(2013,10,17,15,15,10), Time.local(2005,2,28,15,15,10).advance(years: 7, months: 19, weeks: 2, days: 5)
- assert_equal Time.local(2001,12,27,15,15,10), Time.local(2005,2,28,15,15,10).advance(years: -3, months: -2, days: -1)
- assert_equal Time.local(2005,2,28,15,15,10), Time.local(2004,2,29,15,15,10).advance(years: 1) #leap day plus one year
- assert_equal Time.local(2005,2,28,20,15,10), Time.local(2005,2,28,15,15,10).advance(hours: 5)
- assert_equal Time.local(2005,2,28,15,22,10), Time.local(2005,2,28,15,15,10).advance(minutes: 7)
- assert_equal Time.local(2005,2,28,15,15,19), Time.local(2005,2,28,15,15,10).advance(seconds: 9)
- assert_equal Time.local(2005,2,28,20,22,19), Time.local(2005,2,28,15,15,10).advance(hours: 5, minutes: 7, seconds: 9)
- assert_equal Time.local(2005,2,28,10,8,1), Time.local(2005,2,28,15,15,10).advance(hours: -5, minutes: -7, seconds: -9)
- assert_equal Time.local(2013,10,17,20,22,19), Time.local(2005,2,28,15,15,10).advance(years: 7, months: 19, weeks: 2, days: 5, hours: 5, minutes: 7, seconds: 9)
+ assert_equal Time.local(2006, 2, 28, 15, 15, 10), Time.local(2005, 2, 28, 15, 15, 10).advance(years: 1)
+ assert_equal Time.local(2005, 6, 28, 15, 15, 10), Time.local(2005, 2, 28, 15, 15, 10).advance(months: 4)
+ assert_equal Time.local(2005, 3, 21, 15, 15, 10), Time.local(2005, 2, 28, 15, 15, 10).advance(weeks: 3)
+ assert_equal Time.local(2005, 3, 25, 3, 15, 10), Time.local(2005, 2, 28, 15, 15, 10).advance(weeks: 3.5)
+ assert_in_delta Time.local(2005, 3, 26, 12, 51, 10), Time.local(2005, 2, 28, 15, 15, 10).advance(weeks: 3.7), 1
+ assert_equal Time.local(2005, 3, 5, 15, 15, 10), Time.local(2005, 2, 28, 15, 15, 10).advance(days: 5)
+ assert_equal Time.local(2005, 3, 6, 3, 15, 10), Time.local(2005, 2, 28, 15, 15, 10).advance(days: 5.5)
+ assert_in_delta Time.local(2005, 3, 6, 8, 3, 10), Time.local(2005, 2, 28, 15, 15, 10).advance(days: 5.7), 1
+ assert_equal Time.local(2012, 9, 28, 15, 15, 10), Time.local(2005, 2, 28, 15, 15, 10).advance(years: 7, months: 7)
+ assert_equal Time.local(2013, 10, 3, 15, 15, 10), Time.local(2005, 2, 28, 15, 15, 10).advance(years: 7, months: 19, days: 5)
+ assert_equal Time.local(2013, 10, 17, 15, 15, 10), Time.local(2005, 2, 28, 15, 15, 10).advance(years: 7, months: 19, weeks: 2, days: 5)
+ assert_equal Time.local(2001, 12, 27, 15, 15, 10), Time.local(2005, 2, 28, 15, 15, 10).advance(years: -3, months: -2, days: -1)
+ assert_equal Time.local(2005, 2, 28, 15, 15, 10), Time.local(2004, 2, 29, 15, 15, 10).advance(years: 1) #leap day plus one year
+ assert_equal Time.local(2005, 2, 28, 20, 15, 10), Time.local(2005, 2, 28, 15, 15, 10).advance(hours: 5)
+ assert_equal Time.local(2005, 2, 28, 15, 22, 10), Time.local(2005, 2, 28, 15, 15, 10).advance(minutes: 7)
+ assert_equal Time.local(2005, 2, 28, 15, 15, 19), Time.local(2005, 2, 28, 15, 15, 10).advance(seconds: 9)
+ assert_equal Time.local(2005, 2, 28, 20, 22, 19), Time.local(2005, 2, 28, 15, 15, 10).advance(hours: 5, minutes: 7, seconds: 9)
+ assert_equal Time.local(2005, 2, 28, 10, 8, 1), Time.local(2005, 2, 28, 15, 15, 10).advance(hours: -5, minutes: -7, seconds: -9)
+ assert_equal Time.local(2013, 10, 17, 20, 22, 19), Time.local(2005, 2, 28, 15, 15, 10).advance(years: 7, months: 19, weeks: 2, days: 5, hours: 5, minutes: 7, seconds: 9)
end
def test_utc_advance
- assert_equal Time.utc(2006,2,22,15,15,10), Time.utc(2005,2,22,15,15,10).advance(years: 1)
- assert_equal Time.utc(2005,6,22,15,15,10), Time.utc(2005,2,22,15,15,10).advance(months: 4)
- assert_equal Time.utc(2005,3,21,15,15,10), Time.utc(2005,2,28,15,15,10).advance(weeks: 3)
- assert_equal Time.utc(2005,3,25,3,15,10), Time.utc(2005,2,28,15,15,10).advance(weeks: 3.5)
- assert_in_delta Time.utc(2005,3,26,12,51,10), Time.utc(2005,2,28,15,15,10).advance(weeks: 3.7), 1
- assert_equal Time.utc(2005,3,5,15,15,10), Time.utc(2005,2,28,15,15,10).advance(days: 5)
- assert_equal Time.utc(2005,3,6,3,15,10), Time.utc(2005,2,28,15,15,10).advance(days: 5.5)
- assert_in_delta Time.utc(2005,3,6,8,3,10), Time.utc(2005,2,28,15,15,10).advance(days: 5.7), 1
- assert_equal Time.utc(2012,9,22,15,15,10), Time.utc(2005,2,22,15,15,10).advance(years: 7, months: 7)
- assert_equal Time.utc(2013,10,3,15,15,10), Time.utc(2005,2,22,15,15,10).advance(years: 7, months: 19, days: 11)
- assert_equal Time.utc(2013,10,17,15,15,10), Time.utc(2005,2,28,15,15,10).advance(years: 7, months: 19, weeks: 2, days: 5)
- assert_equal Time.utc(2001,12,27,15,15,10), Time.utc(2005,2,28,15,15,10).advance(years: -3, months: -2, days: -1)
- assert_equal Time.utc(2005,2,28,15,15,10), Time.utc(2004,2,29,15,15,10).advance(years: 1) #leap day plus one year
- assert_equal Time.utc(2005,2,28,20,15,10), Time.utc(2005,2,28,15,15,10).advance(hours: 5)
- assert_equal Time.utc(2005,2,28,15,22,10), Time.utc(2005,2,28,15,15,10).advance(minutes: 7)
- assert_equal Time.utc(2005,2,28,15,15,19), Time.utc(2005,2,28,15,15,10).advance(seconds: 9)
- assert_equal Time.utc(2005,2,28,20,22,19), Time.utc(2005,2,28,15,15,10).advance(hours: 5, minutes: 7, seconds: 9)
- assert_equal Time.utc(2005,2,28,10,8,1), Time.utc(2005,2,28,15,15,10).advance(hours: -5, minutes: -7, seconds: -9)
- assert_equal Time.utc(2013,10,17,20,22,19), Time.utc(2005,2,28,15,15,10).advance(years: 7, months: 19, weeks: 2, days: 5, hours: 5, minutes: 7, seconds: 9)
+ assert_equal Time.utc(2006, 2, 22, 15, 15, 10), Time.utc(2005, 2, 22, 15, 15, 10).advance(years: 1)
+ assert_equal Time.utc(2005, 6, 22, 15, 15, 10), Time.utc(2005, 2, 22, 15, 15, 10).advance(months: 4)
+ assert_equal Time.utc(2005, 3, 21, 15, 15, 10), Time.utc(2005, 2, 28, 15, 15, 10).advance(weeks: 3)
+ assert_equal Time.utc(2005, 3, 25, 3, 15, 10), Time.utc(2005, 2, 28, 15, 15, 10).advance(weeks: 3.5)
+ assert_in_delta Time.utc(2005, 3, 26, 12, 51, 10), Time.utc(2005, 2, 28, 15, 15, 10).advance(weeks: 3.7), 1
+ assert_equal Time.utc(2005, 3, 5, 15, 15, 10), Time.utc(2005, 2, 28, 15, 15, 10).advance(days: 5)
+ assert_equal Time.utc(2005, 3, 6, 3, 15, 10), Time.utc(2005, 2, 28, 15, 15, 10).advance(days: 5.5)
+ assert_in_delta Time.utc(2005, 3, 6, 8, 3, 10), Time.utc(2005, 2, 28, 15, 15, 10).advance(days: 5.7), 1
+ assert_equal Time.utc(2012, 9, 22, 15, 15, 10), Time.utc(2005, 2, 22, 15, 15, 10).advance(years: 7, months: 7)
+ assert_equal Time.utc(2013, 10, 3, 15, 15, 10), Time.utc(2005, 2, 22, 15, 15, 10).advance(years: 7, months: 19, days: 11)
+ assert_equal Time.utc(2013, 10, 17, 15, 15, 10), Time.utc(2005, 2, 28, 15, 15, 10).advance(years: 7, months: 19, weeks: 2, days: 5)
+ assert_equal Time.utc(2001, 12, 27, 15, 15, 10), Time.utc(2005, 2, 28, 15, 15, 10).advance(years: -3, months: -2, days: -1)
+ assert_equal Time.utc(2005, 2, 28, 15, 15, 10), Time.utc(2004, 2, 29, 15, 15, 10).advance(years: 1) #leap day plus one year
+ assert_equal Time.utc(2005, 2, 28, 20, 15, 10), Time.utc(2005, 2, 28, 15, 15, 10).advance(hours: 5)
+ assert_equal Time.utc(2005, 2, 28, 15, 22, 10), Time.utc(2005, 2, 28, 15, 15, 10).advance(minutes: 7)
+ assert_equal Time.utc(2005, 2, 28, 15, 15, 19), Time.utc(2005, 2, 28, 15, 15, 10).advance(seconds: 9)
+ assert_equal Time.utc(2005, 2, 28, 20, 22, 19), Time.utc(2005, 2, 28, 15, 15, 10).advance(hours: 5, minutes: 7, seconds: 9)
+ assert_equal Time.utc(2005, 2, 28, 10, 8, 1), Time.utc(2005, 2, 28, 15, 15, 10).advance(hours: -5, minutes: -7, seconds: -9)
+ assert_equal Time.utc(2013, 10, 17, 20, 22, 19), Time.utc(2005, 2, 28, 15, 15, 10).advance(years: 7, months: 19, weeks: 2, days: 5, hours: 5, minutes: 7, seconds: 9)
end
def test_offset_advance
- assert_equal Time.new(2006,2,22,15,15,10,"-08:00"), Time.new(2005,2,22,15,15,10,"-08:00").advance(years: 1)
- assert_equal Time.new(2005,6,22,15,15,10,"-08:00"), Time.new(2005,2,22,15,15,10,"-08:00").advance(months: 4)
- assert_equal Time.new(2005,3,21,15,15,10,"-08:00"), Time.new(2005,2,28,15,15,10,"-08:00").advance(weeks: 3)
- assert_equal Time.new(2005,3,25,3,15,10,"-08:00"), Time.new(2005,2,28,15,15,10,"-08:00").advance(weeks: 3.5)
- assert_in_delta Time.new(2005,3,26,12,51,10,"-08:00"), Time.new(2005,2,28,15,15,10,"-08:00").advance(weeks: 3.7), 1
- assert_equal Time.new(2005,3,5,15,15,10,"-08:00"), Time.new(2005,2,28,15,15,10,"-08:00").advance(days: 5)
- assert_equal Time.new(2005,3,6,3,15,10,"-08:00"), Time.new(2005,2,28,15,15,10,"-08:00").advance(days: 5.5)
- assert_in_delta Time.new(2005,3,6,8,3,10,"-08:00"), Time.new(2005,2,28,15,15,10,"-08:00").advance(days: 5.7), 1
- assert_equal Time.new(2012,9,22,15,15,10,"-08:00"), Time.new(2005,2,22,15,15,10,"-08:00").advance(years: 7, months: 7)
- assert_equal Time.new(2013,10,3,15,15,10,"-08:00"), Time.new(2005,2,22,15,15,10,"-08:00").advance(years: 7, months: 19, days: 11)
- assert_equal Time.new(2013,10,17,15,15,10,"-08:00"), Time.new(2005,2,28,15,15,10,"-08:00").advance(years: 7, months: 19, weeks: 2, days: 5)
- assert_equal Time.new(2001,12,27,15,15,10,"-08:00"), Time.new(2005,2,28,15,15,10,"-08:00").advance(years: -3, months: -2, days: -1)
- assert_equal Time.new(2005,2,28,15,15,10,"-08:00"), Time.new(2004,2,29,15,15,10,"-08:00").advance(years: 1) #leap day plus one year
- assert_equal Time.new(2005,2,28,20,15,10,"-08:00"), Time.new(2005,2,28,15,15,10,"-08:00").advance(hours: 5)
- assert_equal Time.new(2005,2,28,15,22,10,"-08:00"), Time.new(2005,2,28,15,15,10,"-08:00").advance(minutes: 7)
- assert_equal Time.new(2005,2,28,15,15,19,"-08:00"), Time.new(2005,2,28,15,15,10,"-08:00").advance(seconds: 9)
- assert_equal Time.new(2005,2,28,20,22,19,"-08:00"), Time.new(2005,2,28,15,15,10,"-08:00").advance(hours: 5, minutes: 7, seconds: 9)
- assert_equal Time.new(2005,2,28,10,8,1,"-08:00"), Time.new(2005,2,28,15,15,10,"-08:00").advance(hours: -5, minutes: -7, seconds: -9)
- assert_equal Time.new(2013,10,17,20,22,19,"-08:00"), Time.new(2005,2,28,15,15,10,"-08:00").advance(years: 7, months: 19, weeks: 2, days: 5, hours: 5, minutes: 7, seconds: 9)
+ assert_equal Time.new(2006, 2, 22, 15, 15, 10, "-08:00"), Time.new(2005, 2, 22, 15, 15, 10, "-08:00").advance(years: 1)
+ assert_equal Time.new(2005, 6, 22, 15, 15, 10, "-08:00"), Time.new(2005, 2, 22, 15, 15, 10, "-08:00").advance(months: 4)
+ assert_equal Time.new(2005, 3, 21, 15, 15, 10, "-08:00"), Time.new(2005, 2, 28, 15, 15, 10, "-08:00").advance(weeks: 3)
+ assert_equal Time.new(2005, 3, 25, 3, 15, 10, "-08:00"), Time.new(2005, 2, 28, 15, 15, 10, "-08:00").advance(weeks: 3.5)
+ assert_in_delta Time.new(2005, 3, 26, 12, 51, 10, "-08:00"), Time.new(2005, 2, 28, 15, 15, 10, "-08:00").advance(weeks: 3.7), 1
+ assert_equal Time.new(2005, 3, 5, 15, 15, 10, "-08:00"), Time.new(2005, 2, 28, 15, 15, 10, "-08:00").advance(days: 5)
+ assert_equal Time.new(2005, 3, 6, 3, 15, 10, "-08:00"), Time.new(2005, 2, 28, 15, 15, 10, "-08:00").advance(days: 5.5)
+ assert_in_delta Time.new(2005, 3, 6, 8, 3, 10, "-08:00"), Time.new(2005, 2, 28, 15, 15, 10, "-08:00").advance(days: 5.7), 1
+ assert_equal Time.new(2012, 9, 22, 15, 15, 10, "-08:00"), Time.new(2005, 2, 22, 15, 15, 10, "-08:00").advance(years: 7, months: 7)
+ assert_equal Time.new(2013, 10, 3, 15, 15, 10, "-08:00"), Time.new(2005, 2, 22, 15, 15, 10, "-08:00").advance(years: 7, months: 19, days: 11)
+ assert_equal Time.new(2013, 10, 17, 15, 15, 10, "-08:00"), Time.new(2005, 2, 28, 15, 15, 10, "-08:00").advance(years: 7, months: 19, weeks: 2, days: 5)
+ assert_equal Time.new(2001, 12, 27, 15, 15, 10, "-08:00"), Time.new(2005, 2, 28, 15, 15, 10, "-08:00").advance(years: -3, months: -2, days: -1)
+ assert_equal Time.new(2005, 2, 28, 15, 15, 10, "-08:00"), Time.new(2004, 2, 29, 15, 15, 10, "-08:00").advance(years: 1) #leap day plus one year
+ assert_equal Time.new(2005, 2, 28, 20, 15, 10, "-08:00"), Time.new(2005, 2, 28, 15, 15, 10, "-08:00").advance(hours: 5)
+ assert_equal Time.new(2005, 2, 28, 15, 22, 10, "-08:00"), Time.new(2005, 2, 28, 15, 15, 10, "-08:00").advance(minutes: 7)
+ assert_equal Time.new(2005, 2, 28, 15, 15, 19, "-08:00"), Time.new(2005, 2, 28, 15, 15, 10, "-08:00").advance(seconds: 9)
+ assert_equal Time.new(2005, 2, 28, 20, 22, 19, "-08:00"), Time.new(2005, 2, 28, 15, 15, 10, "-08:00").advance(hours: 5, minutes: 7, seconds: 9)
+ assert_equal Time.new(2005, 2, 28, 10, 8, 1, "-08:00"), Time.new(2005, 2, 28, 15, 15, 10, "-08:00").advance(hours: -5, minutes: -7, seconds: -9)
+ assert_equal Time.new(2013, 10, 17, 20, 22, 19, "-08:00"), Time.new(2005, 2, 28, 15, 15, 10, "-08:00").advance(years: 7, months: 19, weeks: 2, days: 5, hours: 5, minutes: 7, seconds: 9)
end
def test_advance_with_nsec
@@ -505,37 +505,37 @@ class TimeExtCalculationsTest < ActiveSupport::TestCase
end
def test_advance_gregorian_proleptic
- assert_equal Time.local(1582,10,14,15,15,10), Time.local(1582,10,15,15,15,10).advance(days: -1)
- assert_equal Time.local(1582,10,15,15,15,10), Time.local(1582,10,14,15,15,10).advance(days: 1)
- assert_equal Time.local(1582,10,5,15,15,10), Time.local(1582,10,4,15,15,10).advance(days: 1)
- assert_equal Time.local(1582,10,4,15,15,10), Time.local(1582,10,5,15,15,10).advance(days: -1)
+ assert_equal Time.local(1582, 10, 14, 15, 15, 10), Time.local(1582, 10, 15, 15, 15, 10).advance(days: -1)
+ assert_equal Time.local(1582, 10, 15, 15, 15, 10), Time.local(1582, 10, 14, 15, 15, 10).advance(days: 1)
+ assert_equal Time.local(1582, 10, 5, 15, 15, 10), Time.local(1582, 10, 4, 15, 15, 10).advance(days: 1)
+ assert_equal Time.local(1582, 10, 4, 15, 15, 10), Time.local(1582, 10, 5, 15, 15, 10).advance(days: -1)
end
def test_last_week
with_env_tz "US/Eastern" do
- assert_equal Time.local(2005,2,21), Time.local(2005,3,1,15,15,10).last_week
- assert_equal Time.local(2005,2,22), Time.local(2005,3,1,15,15,10).last_week(:tuesday)
- assert_equal Time.local(2005,2,25), Time.local(2005,3,1,15,15,10).last_week(:friday)
- assert_equal Time.local(2006,10,30), Time.local(2006,11,6,0,0,0).last_week
- assert_equal Time.local(2006,11,15), Time.local(2006,11,23,0,0,0).last_week(:wednesday)
+ assert_equal Time.local(2005, 2, 21), Time.local(2005, 3, 1, 15, 15, 10).last_week
+ assert_equal Time.local(2005, 2, 22), Time.local(2005, 3, 1, 15, 15, 10).last_week(:tuesday)
+ assert_equal Time.local(2005, 2, 25), Time.local(2005, 3, 1, 15, 15, 10).last_week(:friday)
+ assert_equal Time.local(2006, 10, 30), Time.local(2006, 11, 6, 0, 0, 0).last_week
+ assert_equal Time.local(2006, 11, 15), Time.local(2006, 11, 23, 0, 0, 0).last_week(:wednesday)
end
end
def test_next_week_near_daylight_start
with_env_tz "US/Eastern" do
- assert_equal Time.local(2006,4,3), Time.local(2006,4,2,23,1,0).next_week, "just crossed standard => daylight"
+ assert_equal Time.local(2006, 4, 3), Time.local(2006, 4, 2, 23, 1, 0).next_week, "just crossed standard => daylight"
end
with_env_tz "NZ" do
- assert_equal Time.local(2006,10,2), Time.local(2006,10,1,23,1,0).next_week, "just crossed standard => daylight"
+ assert_equal Time.local(2006, 10, 2), Time.local(2006, 10, 1, 23, 1, 0).next_week, "just crossed standard => daylight"
end
end
def test_next_week_near_daylight_end
with_env_tz "US/Eastern" do
- assert_equal Time.local(2006,10,30), Time.local(2006,10,29,23,1,0).next_week, "just crossed daylight => standard"
+ assert_equal Time.local(2006, 10, 30), Time.local(2006, 10, 29, 23, 1, 0).next_week, "just crossed daylight => standard"
end
with_env_tz "NZ" do
- assert_equal Time.local(2006,3,20), Time.local(2006,3,19,23,1,0).next_week, "just crossed daylight => standard"
+ assert_equal Time.local(2006, 3, 20), Time.local(2006, 3, 19, 23, 1, 0).next_week, "just crossed daylight => standard"
end
end
@@ -660,72 +660,72 @@ class TimeExtCalculationsTest < ActiveSupport::TestCase
def test_today_with_time_local
Date.stub(:current, Date.new(2000, 1, 1)) do
- assert_equal false, Time.local(1999,12,31,23,59,59).today?
- assert_equal true, Time.local(2000,1,1,0).today?
- assert_equal true, Time.local(2000,1,1,23,59,59).today?
- assert_equal false, Time.local(2000,1,2,0).today?
+ assert_equal false, Time.local(1999, 12, 31, 23, 59, 59).today?
+ assert_equal true, Time.local(2000, 1, 1, 0).today?
+ assert_equal true, Time.local(2000, 1, 1, 23, 59, 59).today?
+ assert_equal false, Time.local(2000, 1, 2, 0).today?
end
end
def test_today_with_time_utc
Date.stub(:current, Date.new(2000, 1, 1)) do
- assert_equal false, Time.utc(1999,12,31,23,59,59).today?
- assert_equal true, Time.utc(2000,1,1,0).today?
- assert_equal true, Time.utc(2000,1,1,23,59,59).today?
- assert_equal false, Time.utc(2000,1,2,0).today?
+ assert_equal false, Time.utc(1999, 12, 31, 23, 59, 59).today?
+ assert_equal true, Time.utc(2000, 1, 1, 0).today?
+ assert_equal true, Time.utc(2000, 1, 1, 23, 59, 59).today?
+ assert_equal false, Time.utc(2000, 1, 2, 0).today?
end
end
def test_past_with_time_current_as_time_local
with_env_tz "US/Eastern" do
- Time.stub(:current, Time.local(2005,2,10,15,30,45)) do
- assert_equal true, Time.local(2005,2,10,15,30,44).past?
- assert_equal false, Time.local(2005,2,10,15,30,45).past?
- assert_equal false, Time.local(2005,2,10,15,30,46).past?
- assert_equal true, Time.utc(2005,2,10,20,30,44).past?
- assert_equal false, Time.utc(2005,2,10,20,30,45).past?
- assert_equal false, Time.utc(2005,2,10,20,30,46).past?
+ Time.stub(:current, Time.local(2005, 2, 10, 15, 30, 45)) do
+ assert_equal true, Time.local(2005, 2, 10, 15, 30, 44).past?
+ assert_equal false, Time.local(2005, 2, 10, 15, 30, 45).past?
+ assert_equal false, Time.local(2005, 2, 10, 15, 30, 46).past?
+ assert_equal true, Time.utc(2005, 2, 10, 20, 30, 44).past?
+ assert_equal false, Time.utc(2005, 2, 10, 20, 30, 45).past?
+ assert_equal false, Time.utc(2005, 2, 10, 20, 30, 46).past?
end
end
end
def test_past_with_time_current_as_time_with_zone
with_env_tz "US/Eastern" do
- twz = Time.utc(2005,2,10,15,30,45).in_time_zone("Central Time (US & Canada)")
+ twz = Time.utc(2005, 2, 10, 15, 30, 45).in_time_zone("Central Time (US & Canada)")
Time.stub(:current, twz) do
- assert_equal true, Time.local(2005,2,10,10,30,44).past?
- assert_equal false, Time.local(2005,2,10,10,30,45).past?
- assert_equal false, Time.local(2005,2,10,10,30,46).past?
- assert_equal true, Time.utc(2005,2,10,15,30,44).past?
- assert_equal false, Time.utc(2005,2,10,15,30,45).past?
- assert_equal false, Time.utc(2005,2,10,15,30,46).past?
+ assert_equal true, Time.local(2005, 2, 10, 10, 30, 44).past?
+ assert_equal false, Time.local(2005, 2, 10, 10, 30, 45).past?
+ assert_equal false, Time.local(2005, 2, 10, 10, 30, 46).past?
+ assert_equal true, Time.utc(2005, 2, 10, 15, 30, 44).past?
+ assert_equal false, Time.utc(2005, 2, 10, 15, 30, 45).past?
+ assert_equal false, Time.utc(2005, 2, 10, 15, 30, 46).past?
end
end
end
def test_future_with_time_current_as_time_local
with_env_tz "US/Eastern" do
- Time.stub(:current, Time.local(2005,2,10,15,30,45)) do
- assert_equal false, Time.local(2005,2,10,15,30,44).future?
- assert_equal false, Time.local(2005,2,10,15,30,45).future?
- assert_equal true, Time.local(2005,2,10,15,30,46).future?
- assert_equal false, Time.utc(2005,2,10,20,30,44).future?
- assert_equal false, Time.utc(2005,2,10,20,30,45).future?
- assert_equal true, Time.utc(2005,2,10,20,30,46).future?
+ Time.stub(:current, Time.local(2005, 2, 10, 15, 30, 45)) do
+ assert_equal false, Time.local(2005, 2, 10, 15, 30, 44).future?
+ assert_equal false, Time.local(2005, 2, 10, 15, 30, 45).future?
+ assert_equal true, Time.local(2005, 2, 10, 15, 30, 46).future?
+ assert_equal false, Time.utc(2005, 2, 10, 20, 30, 44).future?
+ assert_equal false, Time.utc(2005, 2, 10, 20, 30, 45).future?
+ assert_equal true, Time.utc(2005, 2, 10, 20, 30, 46).future?
end
end
end
def test_future_with_time_current_as_time_with_zone
with_env_tz "US/Eastern" do
- twz = Time.utc(2005,2,10,15,30,45).in_time_zone("Central Time (US & Canada)")
+ twz = Time.utc(2005, 2, 10, 15, 30, 45).in_time_zone("Central Time (US & Canada)")
Time.stub(:current, twz) do
- assert_equal false, Time.local(2005,2,10,10,30,44).future?
- assert_equal false, Time.local(2005,2,10,10,30,45).future?
- assert_equal true, Time.local(2005,2,10,10,30,46).future?
- assert_equal false, Time.utc(2005,2,10,15,30,44).future?
- assert_equal false, Time.utc(2005,2,10,15,30,45).future?
- assert_equal true, Time.utc(2005,2,10,15,30,46).future?
+ assert_equal false, Time.local(2005, 2, 10, 10, 30, 44).future?
+ assert_equal false, Time.local(2005, 2, 10, 10, 30, 45).future?
+ assert_equal true, Time.local(2005, 2, 10, 10, 30, 46).future?
+ assert_equal false, Time.utc(2005, 2, 10, 15, 30, 44).future?
+ assert_equal false, Time.utc(2005, 2, 10, 15, 30, 45).future?
+ assert_equal true, Time.utc(2005, 2, 10, 15, 30, 46).future?
end
end
end
@@ -762,16 +762,16 @@ class TimeExtCalculationsTest < ActiveSupport::TestCase
end
def test_compare_with_time_with_zone
- assert_equal 1, Time.utc(2000) <=> ActiveSupport::TimeWithZone.new( Time.utc(1999, 12, 31, 23, 59, 59), ActiveSupport::TimeZone["UTC"] )
- assert_equal 0, Time.utc(2000) <=> ActiveSupport::TimeWithZone.new( Time.utc(2000, 1, 1, 0, 0, 0), ActiveSupport::TimeZone["UTC"] )
- assert_equal(-1, Time.utc(2000) <=> ActiveSupport::TimeWithZone.new( Time.utc(2000, 1, 1, 0, 0, 1), ActiveSupport::TimeZone["UTC"] ))
+ assert_equal 1, Time.utc(2000) <=> ActiveSupport::TimeWithZone.new(Time.utc(1999, 12, 31, 23, 59, 59), ActiveSupport::TimeZone["UTC"])
+ assert_equal 0, Time.utc(2000) <=> ActiveSupport::TimeWithZone.new(Time.utc(2000, 1, 1, 0, 0, 0), ActiveSupport::TimeZone["UTC"])
+ assert_equal(-1, Time.utc(2000) <=> ActiveSupport::TimeWithZone.new(Time.utc(2000, 1, 1, 0, 0, 1), ActiveSupport::TimeZone["UTC"]))
end
def test_compare_with_string
assert_equal 1, Time.utc(2000) <=> Time.utc(1999, 12, 31, 23, 59, 59, 999).to_s
assert_equal 0, Time.utc(2000) <=> Time.utc(2000, 1, 1, 0, 0, 0).to_s
- assert_equal( -1, Time.utc(2000) <=> Time.utc(2000, 1, 1, 0, 0, 1, 0).to_s)
- assert_equal nil, Time.utc(2000) <=> "Invalid as Time"
+ assert_equal(-1, Time.utc(2000) <=> Time.utc(2000, 1, 1, 0, 0, 1, 0).to_s)
+ assert_nil Time.utc(2000) <=> "Invalid as Time"
end
def test_at_with_datetime
@@ -851,13 +851,13 @@ class TimeExtCalculationsTest < ActiveSupport::TestCase
end
def test_eql?
- assert_equal true, Time.utc(2000).eql?( ActiveSupport::TimeWithZone.new(Time.utc(2000), ActiveSupport::TimeZone["UTC"]) )
- assert_equal true, Time.utc(2000).eql?( ActiveSupport::TimeWithZone.new(Time.utc(2000), ActiveSupport::TimeZone["Hawaii"]) )
- assert_equal false,Time.utc(2000, 1, 1, 0, 0, 1).eql?( ActiveSupport::TimeWithZone.new(Time.utc(2000), ActiveSupport::TimeZone["UTC"]) )
+ assert_equal true, Time.utc(2000).eql?(ActiveSupport::TimeWithZone.new(Time.utc(2000), ActiveSupport::TimeZone["UTC"]))
+ assert_equal true, Time.utc(2000).eql?(ActiveSupport::TimeWithZone.new(Time.utc(2000), ActiveSupport::TimeZone["Hawaii"]))
+ assert_equal false, Time.utc(2000, 1, 1, 0, 0, 1).eql?(ActiveSupport::TimeWithZone.new(Time.utc(2000), ActiveSupport::TimeZone["UTC"]))
end
def test_minus_with_time_with_zone
- assert_equal 86_400.0, Time.utc(2000, 1, 2) - ActiveSupport::TimeWithZone.new( Time.utc(2000, 1, 1), ActiveSupport::TimeZone["UTC"] )
+ assert_equal 86_400.0, Time.utc(2000, 1, 2) - ActiveSupport::TimeWithZone.new(Time.utc(2000, 1, 1), ActiveSupport::TimeZone["UTC"])
end
def test_minus_with_datetime
@@ -883,32 +883,32 @@ class TimeExtCalculationsTest < ActiveSupport::TestCase
end
def test_all_day
- assert_equal Time.local(2011,6,7,0,0,0)..Time.local(2011,6,7,23,59,59,Rational(999999999, 1000)), Time.local(2011,6,7,10,10,10).all_day
+ assert_equal Time.local(2011, 6, 7, 0, 0, 0)..Time.local(2011, 6, 7, 23, 59, 59, Rational(999999999, 1000)), Time.local(2011, 6, 7, 10, 10, 10).all_day
end
def test_all_day_with_timezone
- beginning_of_day = ActiveSupport::TimeWithZone.new(nil, ActiveSupport::TimeZone["Hawaii"], Time.local(2011,6,7,0,0,0))
- end_of_day = ActiveSupport::TimeWithZone.new(nil, ActiveSupport::TimeZone["Hawaii"], Time.local(2011,6,7,23,59,59,Rational(999999999, 1000)))
+ beginning_of_day = ActiveSupport::TimeWithZone.new(nil, ActiveSupport::TimeZone["Hawaii"], Time.local(2011, 6, 7, 0, 0, 0))
+ end_of_day = ActiveSupport::TimeWithZone.new(nil, ActiveSupport::TimeZone["Hawaii"], Time.local(2011, 6, 7, 23, 59, 59, Rational(999999999, 1000)))
- assert_equal beginning_of_day, ActiveSupport::TimeWithZone.new(Time.local(2011,6,7,10,10,10), ActiveSupport::TimeZone["Hawaii"]).all_day.begin
- assert_equal end_of_day, ActiveSupport::TimeWithZone.new(Time.local(2011,6,7,10,10,10), ActiveSupport::TimeZone["Hawaii"]).all_day.end
+ assert_equal beginning_of_day, ActiveSupport::TimeWithZone.new(Time.local(2011, 6, 7, 10, 10, 10), ActiveSupport::TimeZone["Hawaii"]).all_day.begin
+ assert_equal end_of_day, ActiveSupport::TimeWithZone.new(Time.local(2011, 6, 7, 10, 10, 10), ActiveSupport::TimeZone["Hawaii"]).all_day.end
end
def test_all_week
- assert_equal Time.local(2011,6,6,0,0,0)..Time.local(2011,6,12,23,59,59,Rational(999999999, 1000)), Time.local(2011,6,7,10,10,10).all_week
- assert_equal Time.local(2011,6,5,0,0,0)..Time.local(2011,6,11,23,59,59,Rational(999999999, 1000)), Time.local(2011,6,7,10,10,10).all_week(:sunday)
+ assert_equal Time.local(2011, 6, 6, 0, 0, 0)..Time.local(2011, 6, 12, 23, 59, 59, Rational(999999999, 1000)), Time.local(2011, 6, 7, 10, 10, 10).all_week
+ assert_equal Time.local(2011, 6, 5, 0, 0, 0)..Time.local(2011, 6, 11, 23, 59, 59, Rational(999999999, 1000)), Time.local(2011, 6, 7, 10, 10, 10).all_week(:sunday)
end
def test_all_month
- assert_equal Time.local(2011,6,1,0,0,0)..Time.local(2011,6,30,23,59,59,Rational(999999999, 1000)), Time.local(2011,6,7,10,10,10).all_month
+ assert_equal Time.local(2011, 6, 1, 0, 0, 0)..Time.local(2011, 6, 30, 23, 59, 59, Rational(999999999, 1000)), Time.local(2011, 6, 7, 10, 10, 10).all_month
end
def test_all_quarter
- assert_equal Time.local(2011,4,1,0,0,0)..Time.local(2011,6,30,23,59,59,Rational(999999999, 1000)), Time.local(2011,6,7,10,10,10).all_quarter
+ assert_equal Time.local(2011, 4, 1, 0, 0, 0)..Time.local(2011, 6, 30, 23, 59, 59, Rational(999999999, 1000)), Time.local(2011, 6, 7, 10, 10, 10).all_quarter
end
def test_all_year
- assert_equal Time.local(2011,1,1,0,0,0)..Time.local(2011,12,31,23,59,59,Rational(999999999, 1000)), Time.local(2011,6,7,10,10,10).all_year
+ assert_equal Time.local(2011, 1, 1, 0, 0, 0)..Time.local(2011, 12, 31, 23, 59, 59, Rational(999999999, 1000)), Time.local(2011, 6, 7, 10, 10, 10).all_year
end
end
diff --git a/activesupport/test/core_ext/time_with_zone_test.rb b/activesupport/test/core_ext/time_with_zone_test.rb
index e35aa6e154..629666947f 100644
--- a/activesupport/test/core_ext/time_with_zone_test.rb
+++ b/activesupport/test/core_ext/time_with_zone_test.rb
@@ -90,7 +90,7 @@ class TimeWithZoneTest < ActiveSupport::TestCase
end
def test_nsec
- local = Time.local(2011,6,7,23,59,59,Rational(999999999, 1000))
+ local = Time.local(2011, 6, 7, 23, 59, 59, Rational(999999999, 1000))
with_zone = ActiveSupport::TimeWithZone.new(nil, ActiveSupport::TimeZone["Hawaii"], local)
assert_equal local.nsec, with_zone.nsec
@@ -215,69 +215,69 @@ class TimeWithZoneTest < ActiveSupport::TestCase
end
def test_compare_with_time_with_zone
- assert_equal 1, @twz <=> ActiveSupport::TimeWithZone.new( Time.utc(1999, 12, 31, 23, 59, 59), ActiveSupport::TimeZone["UTC"] )
- assert_equal 0, @twz <=> ActiveSupport::TimeWithZone.new( Time.utc(2000, 1, 1, 0, 0, 0), ActiveSupport::TimeZone["UTC"] )
- assert_equal(-1, @twz <=> ActiveSupport::TimeWithZone.new( Time.utc(2000, 1, 1, 0, 0, 1), ActiveSupport::TimeZone["UTC"] ))
+ assert_equal 1, @twz <=> ActiveSupport::TimeWithZone.new(Time.utc(1999, 12, 31, 23, 59, 59), ActiveSupport::TimeZone["UTC"])
+ assert_equal 0, @twz <=> ActiveSupport::TimeWithZone.new(Time.utc(2000, 1, 1, 0, 0, 0), ActiveSupport::TimeZone["UTC"])
+ assert_equal(-1, @twz <=> ActiveSupport::TimeWithZone.new(Time.utc(2000, 1, 1, 0, 0, 1), ActiveSupport::TimeZone["UTC"]))
end
def test_between?
- assert @twz.between?(Time.utc(1999,12,31,23,59,59), Time.utc(2000,1,1,0,0,1))
- assert_equal false, @twz.between?(Time.utc(2000,1,1,0,0,1), Time.utc(2000,1,1,0,0,2))
+ assert @twz.between?(Time.utc(1999, 12, 31, 23, 59, 59), Time.utc(2000, 1, 1, 0, 0, 1))
+ assert_equal false, @twz.between?(Time.utc(2000, 1, 1, 0, 0, 1), Time.utc(2000, 1, 1, 0, 0, 2))
end
def test_today
Date.stub(:current, Date.new(2000, 1, 1)) do
- assert_equal false, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.utc(1999,12,31,23,59,59) ).today?
- assert_equal true, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.utc(2000,1,1,0) ).today?
- assert_equal true, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.utc(2000,1,1,23,59,59) ).today?
- assert_equal false, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.utc(2000,1,2,0) ).today?
+ assert_equal false, ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(1999, 12, 31, 23, 59, 59)).today?
+ assert_equal true, ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2000, 1, 1, 0)).today?
+ assert_equal true, ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2000, 1, 1, 23, 59, 59)).today?
+ assert_equal false, ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2000, 1, 2, 0)).today?
end
end
def test_past_with_time_current_as_time_local
with_env_tz "US/Eastern" do
- Time.stub(:current, Time.local(2005,2,10,15,30,45)) do
- assert_equal true, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,44)).past?
- assert_equal false, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,45)).past?
- assert_equal false, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,46)).past?
+ Time.stub(:current, Time.local(2005, 2, 10, 15, 30, 45)) do
+ assert_equal true, ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.local(2005, 2, 10, 15, 30, 44)).past?
+ assert_equal false, ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.local(2005, 2, 10, 15, 30, 45)).past?
+ assert_equal false, ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.local(2005, 2, 10, 15, 30, 46)).past?
end
end
end
def test_past_with_time_current_as_time_with_zone
- twz = ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,45) )
+ twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.local(2005, 2, 10, 15, 30, 45))
Time.stub(:current, twz) do
- assert_equal true, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,44)).past?
- assert_equal false, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,45)).past?
- assert_equal false, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,46)).past?
+ assert_equal true, ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.local(2005, 2, 10, 15, 30, 44)).past?
+ assert_equal false, ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.local(2005, 2, 10, 15, 30, 45)).past?
+ assert_equal false, ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.local(2005, 2, 10, 15, 30, 46)).past?
end
end
def test_future_with_time_current_as_time_local
with_env_tz "US/Eastern" do
- Time.stub(:current, Time.local(2005,2,10,15,30,45)) do
- assert_equal false, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,44)).future?
- assert_equal false, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,45)).future?
- assert_equal true, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,46)).future?
+ Time.stub(:current, Time.local(2005, 2, 10, 15, 30, 45)) do
+ assert_equal false, ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.local(2005, 2, 10, 15, 30, 44)).future?
+ assert_equal false, ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.local(2005, 2, 10, 15, 30, 45)).future?
+ assert_equal true, ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.local(2005, 2, 10, 15, 30, 46)).future?
end
end
end
def test_future_with_time_current_as_time_with_zone
- twz = ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,45) )
+ twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.local(2005, 2, 10, 15, 30, 45))
Time.stub(:current, twz) do
- assert_equal false, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,44)).future?
- assert_equal false, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,45)).future?
- assert_equal true, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,46)).future?
+ assert_equal false, ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.local(2005, 2, 10, 15, 30, 44)).future?
+ assert_equal false, ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.local(2005, 2, 10, 15, 30, 45)).future?
+ assert_equal true, ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.local(2005, 2, 10, 15, 30, 46)).future?
end
end
def test_eql?
assert_equal true, @twz.eql?(@twz.dup)
assert_equal true, @twz.eql?(Time.utc(2000))
- assert_equal true, @twz.eql?( ActiveSupport::TimeWithZone.new(Time.utc(2000), ActiveSupport::TimeZone["Hawaii"]) )
- assert_equal false, @twz.eql?( Time.utc(2000, 1, 1, 0, 0, 1) )
- assert_equal false, @twz.eql?( DateTime.civil(1999, 12, 31, 23, 59, 59) )
+ assert_equal true, @twz.eql?(ActiveSupport::TimeWithZone.new(Time.utc(2000), ActiveSupport::TimeZone["Hawaii"]))
+ assert_equal false, @twz.eql?(Time.utc(2000, 1, 1, 0, 0, 1))
+ assert_equal false, @twz.eql?(DateTime.civil(1999, 12, 31, 23, 59, 59))
other_twz = ActiveSupport::TimeWithZone.new(DateTime.now.utc, @time_zone)
assert_equal true, other_twz.eql?(other_twz.dup)
@@ -289,117 +289,117 @@ class TimeWithZoneTest < ActiveSupport::TestCase
end
def test_plus_with_integer
- assert_equal Time.utc(1999, 12, 31, 19, 0 ,5), (@twz + 5).time
+ assert_equal Time.utc(1999, 12, 31, 19, 0 , 5), (@twz + 5).time
end
def test_plus_with_integer_when_self_wraps_datetime
datetime = DateTime.civil(2000, 1, 1, 0)
twz = ActiveSupport::TimeWithZone.new(datetime, @time_zone)
- assert_equal DateTime.civil(1999, 12, 31, 19, 0 ,5), (twz + 5).time
+ assert_equal DateTime.civil(1999, 12, 31, 19, 0 , 5), (twz + 5).time
end
def test_plus_when_crossing_time_class_limit
twz = ActiveSupport::TimeWithZone.new(Time.utc(2038, 1, 19), @time_zone)
- assert_equal [0, 0, 19, 19, 1, 2038], (twz + 86_400).to_a[0,6]
+ assert_equal [0, 0, 19, 19, 1, 2038], (twz + 86_400).to_a[0, 6]
end
def test_plus_with_duration
- assert_equal Time.utc(2000, 1, 5, 19, 0 ,0), (@twz + 5.days).time
+ assert_equal Time.utc(2000, 1, 5, 19, 0 , 0), (@twz + 5.days).time
end
def test_minus_with_integer
- assert_equal Time.utc(1999, 12, 31, 18, 59 ,55), (@twz - 5).time
+ assert_equal Time.utc(1999, 12, 31, 18, 59 , 55), (@twz - 5).time
end
def test_minus_with_integer_when_self_wraps_datetime
datetime = DateTime.civil(2000, 1, 1, 0)
twz = ActiveSupport::TimeWithZone.new(datetime, @time_zone)
- assert_equal DateTime.civil(1999, 12, 31, 18, 59 ,55), (twz - 5).time
+ assert_equal DateTime.civil(1999, 12, 31, 18, 59 , 55), (twz - 5).time
end
def test_minus_with_duration
- assert_equal Time.utc(1999, 12, 26, 19, 0 ,0), (@twz - 5.days).time
+ assert_equal Time.utc(1999, 12, 26, 19, 0 , 0), (@twz - 5.days).time
end
def test_minus_with_time
- assert_equal 86_400.0, ActiveSupport::TimeWithZone.new( Time.utc(2000, 1, 2), ActiveSupport::TimeZone["UTC"] ) - Time.utc(2000, 1, 1)
- assert_equal 86_400.0, ActiveSupport::TimeWithZone.new( Time.utc(2000, 1, 2), ActiveSupport::TimeZone["Hawaii"] ) - Time.utc(2000, 1, 1)
+ assert_equal 86_400.0, ActiveSupport::TimeWithZone.new(Time.utc(2000, 1, 2), ActiveSupport::TimeZone["UTC"]) - Time.utc(2000, 1, 1)
+ assert_equal 86_400.0, ActiveSupport::TimeWithZone.new(Time.utc(2000, 1, 2), ActiveSupport::TimeZone["Hawaii"]) - Time.utc(2000, 1, 1)
end
def test_minus_with_time_precision
- assert_equal 86_399.999999998, ActiveSupport::TimeWithZone.new( Time.utc(2000, 1, 2, 23, 59, 59, Rational(999999999, 1000)), ActiveSupport::TimeZone["UTC"] ) - Time.utc(2000, 1, 2, 0, 0, 0, Rational(1, 1000))
- assert_equal 86_399.999999998, ActiveSupport::TimeWithZone.new( Time.utc(2000, 1, 2, 23, 59, 59, Rational(999999999, 1000)), ActiveSupport::TimeZone["Hawaii"] ) - Time.utc(2000, 1, 2, 0, 0, 0, Rational(1, 1000))
+ assert_equal 86_399.999999998, ActiveSupport::TimeWithZone.new(Time.utc(2000, 1, 2, 23, 59, 59, Rational(999999999, 1000)), ActiveSupport::TimeZone["UTC"]) - Time.utc(2000, 1, 2, 0, 0, 0, Rational(1, 1000))
+ assert_equal 86_399.999999998, ActiveSupport::TimeWithZone.new(Time.utc(2000, 1, 2, 23, 59, 59, Rational(999999999, 1000)), ActiveSupport::TimeZone["Hawaii"]) - Time.utc(2000, 1, 2, 0, 0, 0, Rational(1, 1000))
end
def test_minus_with_time_with_zone
- twz1 = ActiveSupport::TimeWithZone.new( Time.utc(2000, 1, 1), ActiveSupport::TimeZone["UTC"] )
- twz2 = ActiveSupport::TimeWithZone.new( Time.utc(2000, 1, 2), ActiveSupport::TimeZone["UTC"] )
- assert_equal 86_400.0, twz2 - twz1
+ twz1 = ActiveSupport::TimeWithZone.new(Time.utc(2000, 1, 1), ActiveSupport::TimeZone["UTC"])
+ twz2 = ActiveSupport::TimeWithZone.new(Time.utc(2000, 1, 2), ActiveSupport::TimeZone["UTC"])
+ assert_equal 86_400.0, twz2 - twz1
end
def test_minus_with_time_with_zone_precision
- twz1 = ActiveSupport::TimeWithZone.new( Time.utc(2000, 1, 1, 0, 0, 0, Rational(1, 1000)), ActiveSupport::TimeZone["UTC"] )
- twz2 = ActiveSupport::TimeWithZone.new( Time.utc(2000, 1, 1, 23, 59, 59, Rational(999999999, 1000)), ActiveSupport::TimeZone["UTC"] )
+ twz1 = ActiveSupport::TimeWithZone.new(Time.utc(2000, 1, 1, 0, 0, 0, Rational(1, 1000)), ActiveSupport::TimeZone["UTC"])
+ twz2 = ActiveSupport::TimeWithZone.new(Time.utc(2000, 1, 1, 23, 59, 59, Rational(999999999, 1000)), ActiveSupport::TimeZone["UTC"])
assert_equal 86_399.999999998, twz2 - twz1
end
def test_minus_with_datetime
- assert_equal 86_400.0, ActiveSupport::TimeWithZone.new( Time.utc(2000, 1, 2), ActiveSupport::TimeZone["UTC"] ) - DateTime.civil(2000, 1, 1)
+ assert_equal 86_400.0, ActiveSupport::TimeWithZone.new(Time.utc(2000, 1, 2), ActiveSupport::TimeZone["UTC"]) - DateTime.civil(2000, 1, 1)
end
def test_minus_with_datetime_precision
- assert_equal 86_399.999999999, ActiveSupport::TimeWithZone.new( Time.utc(2000, 1, 1, 23, 59, 59, Rational(999999999, 1000)), ActiveSupport::TimeZone["UTC"] ) - DateTime.civil(2000, 1, 1)
+ assert_equal 86_399.999999999, ActiveSupport::TimeWithZone.new(Time.utc(2000, 1, 1, 23, 59, 59, Rational(999999999, 1000)), ActiveSupport::TimeZone["UTC"]) - DateTime.civil(2000, 1, 1)
end
def test_minus_with_wrapped_datetime
- assert_equal 86_400.0, ActiveSupport::TimeWithZone.new( DateTime.civil(2000, 1, 2), ActiveSupport::TimeZone["UTC"] ) - Time.utc(2000, 1, 1)
- assert_equal 86_400.0, ActiveSupport::TimeWithZone.new( DateTime.civil(2000, 1, 2), ActiveSupport::TimeZone["UTC"] ) - DateTime.civil(2000, 1, 1)
+ assert_equal 86_400.0, ActiveSupport::TimeWithZone.new(DateTime.civil(2000, 1, 2), ActiveSupport::TimeZone["UTC"]) - Time.utc(2000, 1, 1)
+ assert_equal 86_400.0, ActiveSupport::TimeWithZone.new(DateTime.civil(2000, 1, 2), ActiveSupport::TimeZone["UTC"]) - DateTime.civil(2000, 1, 1)
end
def test_plus_and_minus_enforce_spring_dst_rules
- utc = Time.utc(2006,4,2,6,59,59) # == Apr 2 2006 01:59:59 EST; i.e., 1 second before daylight savings start
+ utc = Time.utc(2006, 4, 2, 6, 59, 59) # == Apr 2 2006 01:59:59 EST; i.e., 1 second before daylight savings start
twz = ActiveSupport::TimeWithZone.new(utc, @time_zone)
- assert_equal Time.utc(2006,4,2,1,59,59), twz.time
+ assert_equal Time.utc(2006, 4, 2, 1, 59, 59), twz.time
assert_equal false, twz.dst?
assert_equal "EST", twz.zone
twz = twz + 1
- assert_equal Time.utc(2006,4,2,3), twz.time # adding 1 sec springs forward to 3:00AM EDT
+ assert_equal Time.utc(2006, 4, 2, 3), twz.time # adding 1 sec springs forward to 3:00AM EDT
assert_equal true, twz.dst?
assert_equal "EDT", twz.zone
twz = twz - 1 # subtracting 1 second takes goes back to 1:59:59AM EST
- assert_equal Time.utc(2006,4,2,1,59,59), twz.time
+ assert_equal Time.utc(2006, 4, 2, 1, 59, 59), twz.time
assert_equal false, twz.dst?
assert_equal "EST", twz.zone
end
def test_plus_and_minus_enforce_fall_dst_rules
- utc = Time.utc(2006,10,29,5,59,59) # == Oct 29 2006 01:59:59 EST; i.e., 1 second before daylight savings end
+ utc = Time.utc(2006, 10, 29, 5, 59, 59) # == Oct 29 2006 01:59:59 EST; i.e., 1 second before daylight savings end
twz = ActiveSupport::TimeWithZone.new(utc, @time_zone)
- assert_equal Time.utc(2006,10,29,1,59,59), twz.time
+ assert_equal Time.utc(2006, 10, 29, 1, 59, 59), twz.time
assert_equal true, twz.dst?
assert_equal "EDT", twz.zone
twz = twz + 1
- assert_equal Time.utc(2006,10,29,1), twz.time # adding 1 sec falls back from 1:59:59 EDT to 1:00AM EST
+ assert_equal Time.utc(2006, 10, 29, 1), twz.time # adding 1 sec falls back from 1:59:59 EDT to 1:00AM EST
assert_equal false, twz.dst?
assert_equal "EST", twz.zone
twz = twz - 1
- assert_equal Time.utc(2006,10,29,1,59,59), twz.time # subtracting 1 sec goes back to 1:59:59AM EDT
+ assert_equal Time.utc(2006, 10, 29, 1, 59, 59), twz.time # subtracting 1 sec goes back to 1:59:59AM EDT
assert_equal true, twz.dst?
assert_equal "EDT", twz.zone
end
def test_to_a
- assert_equal [45, 30, 5, 1, 2, 2000, 2, 32, false, "HST"], ActiveSupport::TimeWithZone.new( Time.utc(2000, 2, 1, 15, 30, 45), ActiveSupport::TimeZone["Hawaii"] ).to_a
+ assert_equal [45, 30, 5, 1, 2, 2000, 2, 32, false, "HST"], ActiveSupport::TimeWithZone.new(Time.utc(2000, 2, 1, 15, 30, 45), ActiveSupport::TimeZone["Hawaii"]).to_a
end
def test_to_f
- result = ActiveSupport::TimeWithZone.new( Time.utc(2000, 1, 1), ActiveSupport::TimeZone["Hawaii"] ).to_f
+ result = ActiveSupport::TimeWithZone.new(Time.utc(2000, 1, 1), ActiveSupport::TimeZone["Hawaii"]).to_f
assert_equal 946684800.0, result
assert_kind_of Float, result
end
def test_to_i
- result = ActiveSupport::TimeWithZone.new( Time.utc(2000, 1, 1), ActiveSupport::TimeZone["Hawaii"] ).to_i
+ result = ActiveSupport::TimeWithZone.new(Time.utc(2000, 1, 1), ActiveSupport::TimeZone["Hawaii"]).to_i
assert_equal 946684800, result
assert_kind_of Integer, result
end
@@ -431,13 +431,13 @@ class TimeWithZoneTest < ActiveSupport::TestCase
def test_to_date
# 1 sec before midnight Jan 1 EST
- assert_equal Date.new(1999, 12, 31), ActiveSupport::TimeWithZone.new( Time.utc(2000, 1, 1, 4, 59, 59), ActiveSupport::TimeZone["Eastern Time (US & Canada)"] ).to_date
+ assert_equal Date.new(1999, 12, 31), ActiveSupport::TimeWithZone.new(Time.utc(2000, 1, 1, 4, 59, 59), ActiveSupport::TimeZone["Eastern Time (US & Canada)"]).to_date
# midnight Jan 1 EST
- assert_equal Date.new(2000, 1, 1), ActiveSupport::TimeWithZone.new( Time.utc(2000, 1, 1, 5, 0, 0), ActiveSupport::TimeZone["Eastern Time (US & Canada)"] ).to_date
+ assert_equal Date.new(2000, 1, 1), ActiveSupport::TimeWithZone.new(Time.utc(2000, 1, 1, 5, 0, 0), ActiveSupport::TimeZone["Eastern Time (US & Canada)"]).to_date
# 1 sec before midnight Jan 2 EST
- assert_equal Date.new(2000, 1, 1), ActiveSupport::TimeWithZone.new( Time.utc(2000, 1, 2, 4, 59, 59), ActiveSupport::TimeZone["Eastern Time (US & Canada)"] ).to_date
+ assert_equal Date.new(2000, 1, 1), ActiveSupport::TimeWithZone.new(Time.utc(2000, 1, 2, 4, 59, 59), ActiveSupport::TimeZone["Eastern Time (US & Canada)"]).to_date
# midnight Jan 2 EST
- assert_equal Date.new(2000, 1, 2), ActiveSupport::TimeWithZone.new( Time.utc(2000, 1, 2, 5, 0, 0), ActiveSupport::TimeZone["Eastern Time (US & Canada)"] ).to_date
+ assert_equal Date.new(2000, 1, 2), ActiveSupport::TimeWithZone.new(Time.utc(2000, 1, 2, 5, 0, 0), ActiveSupport::TimeZone["Eastern Time (US & Canada)"]).to_date
end
def test_to_datetime
@@ -467,7 +467,7 @@ class TimeWithZoneTest < ActiveSupport::TestCase
def test_method_missing_with_time_return_value
assert_instance_of ActiveSupport::TimeWithZone, @twz.months_since(1)
- assert_equal Time.utc(2000, 1, 31, 19, 0 ,0), @twz.months_since(1).time
+ assert_equal Time.utc(2000, 1, 31, 19, 0 , 0), @twz.months_since(1).time
end
def test_marshal_dump_and_load
@@ -513,7 +513,7 @@ class TimeWithZoneTest < ActiveSupport::TestCase
end
def test_date_part_value_methods
- twz = ActiveSupport::TimeWithZone.new(Time.utc(1999,12,31,19,18,17,500), @time_zone)
+ twz = ActiveSupport::TimeWithZone.new(Time.utc(1999, 12, 31, 19, 18, 17, 500), @time_zone)
assert_not_called(twz, :method_missing) do
assert_equal 1999, twz.year
assert_equal 12, twz.month
@@ -533,12 +533,12 @@ class TimeWithZoneTest < ActiveSupport::TestCase
end
def test_usec_returns_sec_fraction_when_datetime_is_wrapped
- twz = ActiveSupport::TimeWithZone.new(DateTime.civil(2000, 1, 1, 0, 0, Rational(1,2)), @time_zone)
+ twz = ActiveSupport::TimeWithZone.new(DateTime.civil(2000, 1, 1, 0, 0, Rational(1, 2)), @time_zone)
assert_equal 500000, twz.usec
end
def test_nsec_returns_sec_fraction_when_datetime_is_wrapped
- twz = ActiveSupport::TimeWithZone.new(DateTime.civil(2000, 1, 1, 0, 0, Rational(1,2)), @time_zone)
+ twz = ActiveSupport::TimeWithZone.new(DateTime.civil(2000, 1, 1, 0, 0, Rational(1, 2)), @time_zone)
assert_equal 500000000, twz.nsec
end
@@ -554,15 +554,15 @@ class TimeWithZoneTest < ActiveSupport::TestCase
end
def test_instance_created_with_local_time_enforces_spring_dst_rules
- twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2006,4,2,2)) # first second of DST
- assert_equal Time.utc(2006,4,2,3), twz.time # springs forward to 3AM
+ twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2006, 4, 2, 2)) # first second of DST
+ assert_equal Time.utc(2006, 4, 2, 3), twz.time # springs forward to 3AM
assert_equal true, twz.dst?
assert_equal "EDT", twz.zone
end
def test_instance_created_with_local_time_enforces_fall_dst_rules
- twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2006,10,29,1)) # 1AM can be either DST or non-DST; we'll pick DST
- assert_equal Time.utc(2006,10,29,1), twz.time
+ twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2006, 10, 29, 1)) # 1AM can be either DST or non-DST; we'll pick DST
+ assert_equal Time.utc(2006, 10, 29, 1), twz.time
assert_equal true, twz.dst?
assert_equal "EDT", twz.zone
end
@@ -575,11 +575,11 @@ class TimeWithZoneTest < ActiveSupport::TestCase
end
def test_utc_to_local_conversion_with_far_future_datetime
- assert_equal [0,0,19,31,12,2049], ActiveSupport::TimeWithZone.new(DateTime.civil(2050), @time_zone).to_a[0,6]
+ assert_equal [0, 0, 19, 31, 12, 2049], ActiveSupport::TimeWithZone.new(DateTime.civil(2050), @time_zone).to_a[0, 6]
end
def test_local_to_utc_conversion_with_far_future_datetime
- assert_equal DateTime.civil(2050).to_f, ActiveSupport::TimeWithZone.new(nil, @time_zone, DateTime.civil(2049,12,31,19)).to_f
+ assert_equal DateTime.civil(2050).to_f, ActiveSupport::TimeWithZone.new(nil, @time_zone, DateTime.civil(2049, 12, 31, 19)).to_f
end
def test_change
@@ -688,7 +688,7 @@ class TimeWithZoneTest < ActiveSupport::TestCase
end
def test_advance_1_year_from_leap_day
- twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2004,2,29))
+ twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2004, 2, 29))
assert_equal "Mon, 28 Feb 2005 00:00:00 EST -05:00", twz.advance(years: 1).inspect
assert_equal "Mon, 28 Feb 2005 00:00:00 EST -05:00", twz.years_since(1).inspect
assert_equal "Mon, 28 Feb 2005 00:00:00 EST -05:00", twz.since(1.year).inspect
@@ -697,7 +697,7 @@ class TimeWithZoneTest < ActiveSupport::TestCase
end
def test_advance_1_month_from_last_day_of_january
- twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2005,1,31))
+ twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2005, 1, 31))
assert_equal "Mon, 28 Feb 2005 00:00:00 EST -05:00", twz.advance(months: 1).inspect
assert_equal "Mon, 28 Feb 2005 00:00:00 EST -05:00", twz.months_since(1).inspect
assert_equal "Mon, 28 Feb 2005 00:00:00 EST -05:00", twz.since(1.month).inspect
@@ -706,7 +706,7 @@ class TimeWithZoneTest < ActiveSupport::TestCase
end
def test_advance_1_month_from_last_day_of_january_during_leap_year
- twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2000,1,31))
+ twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2000, 1, 31))
assert_equal "Tue, 29 Feb 2000 00:00:00 EST -05:00", twz.advance(months: 1).inspect
assert_equal "Tue, 29 Feb 2000 00:00:00 EST -05:00", twz.months_since(1).inspect
assert_equal "Tue, 29 Feb 2000 00:00:00 EST -05:00", twz.since(1.month).inspect
@@ -715,7 +715,7 @@ class TimeWithZoneTest < ActiveSupport::TestCase
end
def test_advance_1_month_into_spring_dst_gap
- twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2006,3,2,2))
+ twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2006, 3, 2, 2))
assert_equal "Sun, 02 Apr 2006 03:00:00 EDT -04:00", twz.advance(months: 1).inspect
assert_equal "Sun, 02 Apr 2006 03:00:00 EDT -04:00", twz.months_since(1).inspect
assert_equal "Sun, 02 Apr 2006 03:00:00 EDT -04:00", twz.since(1.month).inspect
@@ -724,7 +724,7 @@ class TimeWithZoneTest < ActiveSupport::TestCase
end
def test_advance_1_second_into_spring_dst_gap
- twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2006,4,2,1,59,59))
+ twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2006, 4, 2, 1, 59, 59))
assert_equal "Sun, 02 Apr 2006 03:00:00 EDT -04:00", twz.advance(seconds: 1).inspect
assert_equal "Sun, 02 Apr 2006 03:00:00 EDT -04:00", (twz + 1).inspect
assert_equal "Sun, 02 Apr 2006 03:00:00 EDT -04:00", (twz + 1.second).inspect
@@ -735,7 +735,7 @@ class TimeWithZoneTest < ActiveSupport::TestCase
end
def test_advance_1_day_across_spring_dst_transition
- twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2006,4,1,10,30))
+ twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2006, 4, 1, 10, 30))
# In 2006, spring DST transition occurred Apr 2 at 2AM; this day was only 23 hours long
# When we advance 1 day, we want to end up at the same time on the next day
assert_equal "Sun, 02 Apr 2006 10:30:00 EDT -04:00", twz.advance(days: 1).inspect
@@ -748,7 +748,7 @@ class TimeWithZoneTest < ActiveSupport::TestCase
end
def test_advance_1_day_across_spring_dst_transition_backwards
- twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2006,4,2,10,30))
+ twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2006, 4, 2, 10, 30))
# In 2006, spring DST transition occurred Apr 2 at 2AM; this day was only 23 hours long
# When we advance back 1 day, we want to end up at the same time on the previous day
assert_equal "Sat, 01 Apr 2006 10:30:00 EST -05:00", twz.advance(days: -1).inspect
@@ -758,7 +758,7 @@ class TimeWithZoneTest < ActiveSupport::TestCase
end
def test_advance_1_day_expressed_as_number_of_seconds_minutes_or_hours_across_spring_dst_transition
- twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2006,4,1,10,30))
+ twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2006, 4, 1, 10, 30))
# In 2006, spring DST transition occurred Apr 2 at 2AM; this day was only 23 hours long
# When we advance a specific number of hours, minutes or seconds, we want to advance exactly that amount
assert_equal "Sun, 02 Apr 2006 11:30:00 EDT -04:00", (twz + 86400).inspect
@@ -779,7 +779,7 @@ class TimeWithZoneTest < ActiveSupport::TestCase
end
def test_advance_1_day_expressed_as_number_of_seconds_minutes_or_hours_across_spring_dst_transition_backwards
- twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2006,4,2,11,30))
+ twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2006, 4, 2, 11, 30))
# In 2006, spring DST transition occurred Apr 2 at 2AM; this day was only 23 hours long
# When we advance a specific number of hours, minutes or seconds, we want to advance exactly that amount
assert_equal "Sat, 01 Apr 2006 10:30:00 EST -05:00", (twz - 86400).inspect
@@ -796,7 +796,7 @@ class TimeWithZoneTest < ActiveSupport::TestCase
end
def test_advance_1_day_across_fall_dst_transition
- twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2006,10,28,10,30))
+ twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2006, 10, 28, 10, 30))
# In 2006, fall DST transition occurred Oct 29 at 2AM; this day was 25 hours long
# When we advance 1 day, we want to end up at the same time on the next day
assert_equal "Sun, 29 Oct 2006 10:30:00 EST -05:00", twz.advance(days: 1).inspect
@@ -809,7 +809,7 @@ class TimeWithZoneTest < ActiveSupport::TestCase
end
def test_advance_1_day_across_fall_dst_transition_backwards
- twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2006,10,29,10,30))
+ twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2006, 10, 29, 10, 30))
# In 2006, fall DST transition occurred Oct 29 at 2AM; this day was 25 hours long
# When we advance backwards 1 day, we want to end up at the same time on the previous day
assert_equal "Sat, 28 Oct 2006 10:30:00 EDT -04:00", twz.advance(days: -1).inspect
@@ -819,7 +819,7 @@ class TimeWithZoneTest < ActiveSupport::TestCase
end
def test_advance_1_day_expressed_as_number_of_seconds_minutes_or_hours_across_fall_dst_transition
- twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2006,10,28,10,30))
+ twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2006, 10, 28, 10, 30))
# In 2006, fall DST transition occurred Oct 29 at 2AM; this day was 25 hours long
# When we advance a specific number of hours, minutes or seconds, we want to advance exactly that amount
assert_equal "Sun, 29 Oct 2006 09:30:00 EST -05:00", (twz + 86400).inspect
@@ -840,7 +840,7 @@ class TimeWithZoneTest < ActiveSupport::TestCase
end
def test_advance_1_day_expressed_as_number_of_seconds_minutes_or_hours_across_fall_dst_transition_backwards
- twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2006,10,29,9,30))
+ twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2006, 10, 29, 9, 30))
# In 2006, fall DST transition occurred Oct 29 at 2AM; this day was 25 hours long
# When we advance a specific number of hours, minutes or seconds, we want to advance exactly that amount
assert_equal "Sat, 28 Oct 2006 10:30:00 EDT -04:00", (twz - 86400).inspect
@@ -857,7 +857,7 @@ class TimeWithZoneTest < ActiveSupport::TestCase
end
def test_advance_1_week_across_spring_dst_transition
- twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2006,4,1,10,30))
+ twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2006, 4, 1, 10, 30))
assert_equal "Sat, 08 Apr 2006 10:30:00 EDT -04:00", twz.advance(weeks: 1).inspect
assert_equal "Sat, 08 Apr 2006 10:30:00 EDT -04:00", twz.weeks_since(1).inspect
assert_equal "Sat, 08 Apr 2006 10:30:00 EDT -04:00", twz.since(1.week).inspect
@@ -866,7 +866,7 @@ class TimeWithZoneTest < ActiveSupport::TestCase
end
def test_advance_1_week_across_spring_dst_transition_backwards
- twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2006,4,8,10,30))
+ twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2006, 4, 8, 10, 30))
assert_equal "Sat, 01 Apr 2006 10:30:00 EST -05:00", twz.advance(weeks: -1).inspect
assert_equal "Sat, 01 Apr 2006 10:30:00 EST -05:00", twz.weeks_ago(1).inspect
assert_equal "Sat, 01 Apr 2006 10:30:00 EST -05:00", twz.ago(1.week).inspect
@@ -874,7 +874,7 @@ class TimeWithZoneTest < ActiveSupport::TestCase
end
def test_advance_1_week_across_fall_dst_transition
- twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2006,10,28,10,30))
+ twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2006, 10, 28, 10, 30))
assert_equal "Sat, 04 Nov 2006 10:30:00 EST -05:00", twz.advance(weeks: 1).inspect
assert_equal "Sat, 04 Nov 2006 10:30:00 EST -05:00", twz.weeks_since(1).inspect
assert_equal "Sat, 04 Nov 2006 10:30:00 EST -05:00", twz.since(1.week).inspect
@@ -883,7 +883,7 @@ class TimeWithZoneTest < ActiveSupport::TestCase
end
def test_advance_1_week_across_fall_dst_transition_backwards
- twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2006,11,4,10,30))
+ twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2006, 11, 4, 10, 30))
assert_equal "Sat, 28 Oct 2006 10:30:00 EDT -04:00", twz.advance(weeks: -1).inspect
assert_equal "Sat, 28 Oct 2006 10:30:00 EDT -04:00", twz.weeks_ago(1).inspect
assert_equal "Sat, 28 Oct 2006 10:30:00 EDT -04:00", twz.ago(1.week).inspect
@@ -891,7 +891,7 @@ class TimeWithZoneTest < ActiveSupport::TestCase
end
def test_advance_1_month_across_spring_dst_transition
- twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2006,4,1,10,30))
+ twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2006, 4, 1, 10, 30))
assert_equal "Mon, 01 May 2006 10:30:00 EDT -04:00", twz.advance(months: 1).inspect
assert_equal "Mon, 01 May 2006 10:30:00 EDT -04:00", twz.months_since(1).inspect
assert_equal "Mon, 01 May 2006 10:30:00 EDT -04:00", twz.since(1.month).inspect
@@ -900,7 +900,7 @@ class TimeWithZoneTest < ActiveSupport::TestCase
end
def test_advance_1_month_across_spring_dst_transition_backwards
- twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2006,5,1,10,30))
+ twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2006, 5, 1, 10, 30))
assert_equal "Sat, 01 Apr 2006 10:30:00 EST -05:00", twz.advance(months: -1).inspect
assert_equal "Sat, 01 Apr 2006 10:30:00 EST -05:00", twz.months_ago(1).inspect
assert_equal "Sat, 01 Apr 2006 10:30:00 EST -05:00", twz.ago(1.month).inspect
@@ -908,7 +908,7 @@ class TimeWithZoneTest < ActiveSupport::TestCase
end
def test_advance_1_month_across_fall_dst_transition
- twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2006,10,28,10,30))
+ twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2006, 10, 28, 10, 30))
assert_equal "Tue, 28 Nov 2006 10:30:00 EST -05:00", twz.advance(months: 1).inspect
assert_equal "Tue, 28 Nov 2006 10:30:00 EST -05:00", twz.months_since(1).inspect
assert_equal "Tue, 28 Nov 2006 10:30:00 EST -05:00", twz.since(1.month).inspect
@@ -917,7 +917,7 @@ class TimeWithZoneTest < ActiveSupport::TestCase
end
def test_advance_1_month_across_fall_dst_transition_backwards
- twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2006,11,28,10,30))
+ twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2006, 11, 28, 10, 30))
assert_equal "Sat, 28 Oct 2006 10:30:00 EDT -04:00", twz.advance(months: -1).inspect
assert_equal "Sat, 28 Oct 2006 10:30:00 EDT -04:00", twz.months_ago(1).inspect
assert_equal "Sat, 28 Oct 2006 10:30:00 EDT -04:00", twz.ago(1.month).inspect
@@ -925,7 +925,7 @@ class TimeWithZoneTest < ActiveSupport::TestCase
end
def test_advance_1_year
- twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2008,2,15,10,30))
+ twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2008, 2, 15, 10, 30))
assert_equal "Sun, 15 Feb 2009 10:30:00 EST -05:00", twz.advance(years: 1).inspect
assert_equal "Sun, 15 Feb 2009 10:30:00 EST -05:00", twz.years_since(1).inspect
assert_equal "Sun, 15 Feb 2009 10:30:00 EST -05:00", twz.since(1.year).inspect
@@ -937,7 +937,7 @@ class TimeWithZoneTest < ActiveSupport::TestCase
end
def test_advance_1_year_during_dst
- twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2008,7,15,10,30))
+ twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2008, 7, 15, 10, 30))
assert_equal "Wed, 15 Jul 2009 10:30:00 EDT -04:00", twz.advance(years: 1).inspect
assert_equal "Wed, 15 Jul 2009 10:30:00 EDT -04:00", twz.years_since(1).inspect
assert_equal "Wed, 15 Jul 2009 10:30:00 EDT -04:00", twz.since(1.year).inspect
@@ -1057,7 +1057,7 @@ class TimeWithZoneMethodsForTimeAndDateTimeTest < ActiveSupport::TestCase
Time.zone = -9.hours
assert_equal ActiveSupport::TimeZone["Alaska"], Time.zone
Time.zone = nil
- assert_equal nil, Time.zone
+ assert_nil Time.zone
end
def test_time_zone_getter_and_setter_with_zone_default_set
diff --git a/activesupport/test/dependencies_test.rb b/activesupport/test/dependencies_test.rb
index 54068f5a08..e772d15d53 100644
--- a/activesupport/test/dependencies_test.rb
+++ b/activesupport/test/dependencies_test.rb
@@ -121,7 +121,7 @@ class DependenciesTest < ActiveSupport::TestCase
silence_warnings { require_dependency filename }
assert_equal 2, $check_warnings_load_count
- assert_equal nil, $checked_verbose, "After first load warnings should be left alone."
+ assert_nil $checked_verbose, "After first load warnings should be left alone."
assert_includes ActiveSupport::Dependencies.loaded, expanded
ActiveSupport::Dependencies.clear
@@ -526,8 +526,8 @@ class DependenciesTest < ActiveSupport::TestCase
def test_file_search
with_loading "dependencies" do
root = ActiveSupport::Dependencies.autoload_paths.first
- assert_equal nil, ActiveSupport::Dependencies.search_for_file("service_three")
- assert_equal nil, ActiveSupport::Dependencies.search_for_file("service_three.rb")
+ assert_nil ActiveSupport::Dependencies.search_for_file("service_three")
+ assert_nil ActiveSupport::Dependencies.search_for_file("service_three.rb")
assert_equal root + "/service_one.rb", ActiveSupport::Dependencies.search_for_file("service_one")
assert_equal root + "/service_one.rb", ActiveSupport::Dependencies.search_for_file("service_one.rb")
end
@@ -757,14 +757,14 @@ class DependenciesTest < ActiveSupport::TestCase
def test_new_contants_in_without_constants
assert_equal [], (ActiveSupport::Dependencies.new_constants_in(Object) {})
- assert ActiveSupport::Dependencies.constant_watch_stack.all? { |k,v| v.empty? }
+ assert ActiveSupport::Dependencies.constant_watch_stack.all? { |k, v| v.empty? }
end
def test_new_constants_in_with_a_single_constant
assert_equal ["Hello"], ActiveSupport::Dependencies.new_constants_in(Object) {
Object.const_set :Hello, 10
}.map(&:to_s)
- assert ActiveSupport::Dependencies.constant_watch_stack.all? { |k,v| v.empty? }
+ assert ActiveSupport::Dependencies.constant_watch_stack.all? { |k, v| v.empty? }
ensure
remove_constants(:Hello)
end
@@ -781,7 +781,7 @@ class DependenciesTest < ActiveSupport::TestCase
end
assert_equal ["OuterAfter", "OuterBefore"], outer.sort.map(&:to_s)
- assert ActiveSupport::Dependencies.constant_watch_stack.all? { |k,v| v.empty? }
+ assert ActiveSupport::Dependencies.constant_watch_stack.all? { |k, v| v.empty? }
ensure
remove_constants(:OuterBefore, :Inner, :OuterAfter)
end
@@ -800,7 +800,7 @@ class DependenciesTest < ActiveSupport::TestCase
M.const_set :OuterAfter, 30
end
assert_equal ["M::OuterAfter", "M::OuterBefore"], outer.sort
- assert ActiveSupport::Dependencies.constant_watch_stack.all? { |k,v| v.empty? }
+ assert ActiveSupport::Dependencies.constant_watch_stack.all? { |k, v| v.empty? }
ensure
remove_constants(:M)
end
@@ -818,7 +818,7 @@ class DependenciesTest < ActiveSupport::TestCase
M.const_set :OuterAfter, 30
end
assert_equal ["M::OuterAfter", "M::OuterBefore"], outer.sort
- assert ActiveSupport::Dependencies.constant_watch_stack.all? { |k,v| v.empty? }
+ assert ActiveSupport::Dependencies.constant_watch_stack.all? { |k, v| v.empty? }
ensure
remove_constants(:M)
end
diff --git a/activesupport/test/deprecation_test.rb b/activesupport/test/deprecation_test.rb
index e2a3331cfa..5be93f3a1a 100644
--- a/activesupport/test/deprecation_test.rb
+++ b/activesupport/test/deprecation_test.rb
@@ -16,7 +16,7 @@ class Deprecatee
def not() 2 end
def none() 1 end
def one(a) a end
- def multi(a,b,c) [a,b,c] end
+ def multi(a, b, c) [a, b, c] end
deprecate :none, :one, :multi
def a; end
@@ -73,7 +73,7 @@ class DeprecationTest < ActiveSupport::TestCase
end
assert_deprecated(/multi is deprecated/) do
- assert_equal [1,2,3], @dtc.multi(1,2,3)
+ assert_equal [1, 2, 3], @dtc.multi(1, 2, 3)
end
end
@@ -279,7 +279,7 @@ class DeprecationTest < ActiveSupport::TestCase
def test_deprecated_constant_with_deprecator_given
deprecator = deprecator_with_messages
klass = Class.new
- klass.const_set(:OLD, ActiveSupport::Deprecation::DeprecatedConstantProxy.new("klass::OLD", "Object", deprecator) )
+ klass.const_set(:OLD, ActiveSupport::Deprecation::DeprecatedConstantProxy.new("klass::OLD", "Object", deprecator))
assert_difference("deprecator.messages.size") do
klass::OLD.to_s
end
diff --git a/activesupport/test/descendants_tracker_test_cases.rb b/activesupport/test/descendants_tracker_test_cases.rb
index 09c5ce1f07..cf349d53ee 100644
--- a/activesupport/test/descendants_tracker_test_cases.rb
+++ b/activesupport/test/descendants_tracker_test_cases.rb
@@ -40,7 +40,7 @@ module DescendantsTrackerTestCases
end
end
- protected
+ private
def assert_equal_sets(expected, actual)
assert_equal Set.new(expected), Set.new(actual)
diff --git a/activesupport/test/evented_file_update_checker_test.rb b/activesupport/test/evented_file_update_checker_test.rb
index cc47318974..77d8dcb0f8 100644
--- a/activesupport/test/evented_file_update_checker_test.rb
+++ b/activesupport/test/evented_file_update_checker_test.rb
@@ -36,6 +36,8 @@ class EventedFileUpdateCheckerTest < ActiveSupport::TestCase
end
test "notifies forked processes" do
+ jruby_skip "Forking not available on JRuby"
+
FileUtils.touch(tmpfiles)
checker = new_checker(tmpfiles) {}
diff --git a/activesupport/test/executor_test.rb b/activesupport/test/executor_test.rb
index d409216206..7fefc066b3 100644
--- a/activesupport/test/executor_test.rb
+++ b/activesupport/test/executor_test.rb
@@ -105,7 +105,7 @@ class ExecutorTest < ActiveSupport::TestCase
executor.wrap {}
- assert_equal nil, supplied_state
+ assert_nil supplied_state
end
def test_exception_skips_uninvoked_hook
@@ -192,6 +192,8 @@ class ExecutorTest < ActiveSupport::TestCase
end
def test_class_serial_is_unaffected
+ skip if !defined?(RubyVM)
+
hook = Class.new do
define_method(:run) do
nil
diff --git a/activesupport/test/file_update_checker_shared_tests.rb b/activesupport/test/file_update_checker_shared_tests.rb
index cd6a58e840..48cd387196 100644
--- a/activesupport/test/file_update_checker_shared_tests.rb
+++ b/activesupport/test/file_update_checker_shared_tests.rb
@@ -49,6 +49,7 @@ module FileUpdateCheckerSharedTests
checker = new_checker(tmpfiles) { i += 1 }
touch(tmpfiles)
+ wait
assert checker.execute_if_updated
assert_equal 1, i
@@ -62,6 +63,7 @@ module FileUpdateCheckerSharedTests
checker = new_checker(tmpfiles) { i += 1 }
touch(tmpfiles)
+ wait
assert checker.execute_if_updated
assert_equal 1, i
@@ -75,6 +77,7 @@ module FileUpdateCheckerSharedTests
checker = new_checker(tmpfiles) { i += 1 }
rm_f(tmpfiles)
+ wait
assert checker.execute_if_updated
assert_equal 1, i
@@ -87,6 +90,7 @@ module FileUpdateCheckerSharedTests
assert !checker.updated?
touch(tmpfiles)
+ wait
assert checker.updated?
end
@@ -100,6 +104,7 @@ module FileUpdateCheckerSharedTests
assert !checker.updated?
touch(tmpfiles)
+ wait
assert checker.updated?
end
@@ -113,6 +118,7 @@ module FileUpdateCheckerSharedTests
assert !checker.updated?
rm_f(tmpfiles)
+ wait
assert checker.updated?
end
@@ -129,6 +135,7 @@ module FileUpdateCheckerSharedTests
checker = new_checker(tmpfiles) { i += 1 }
touch(tmpfiles[1..-1])
+ wait
assert checker.execute_if_updated
assert_equal 1, i
@@ -145,6 +152,7 @@ module FileUpdateCheckerSharedTests
checker = new_checker(tmpfiles) { i += 1 }
touch(tmpfiles[1..-1])
+ wait
assert checker.execute_if_updated
assert_equal 1, i
@@ -157,6 +165,7 @@ module FileUpdateCheckerSharedTests
assert !checker.updated?
touch(tmpfiles)
+ wait
assert checker.updated?
checker.execute
@@ -169,6 +178,7 @@ module FileUpdateCheckerSharedTests
checker = new_checker([], tmpdir => :rb) { i += 1 }
touch(tmpfile("foo.rb"))
+ wait
assert checker.execute_if_updated
assert_equal 1, i
@@ -180,11 +190,13 @@ module FileUpdateCheckerSharedTests
checker = new_checker([], tmpdir => [:rb, :txt]) { i += 1 }
touch(tmpfile("foo.rb"))
+ wait
assert checker.execute_if_updated
assert_equal 1, i
touch(tmpfile("foo.txt"))
+ wait
assert checker.execute_if_updated
assert_equal 2, i
@@ -196,6 +208,7 @@ module FileUpdateCheckerSharedTests
checker = new_checker([], tmpdir => :txt) { i += 1 }
touch(tmpfile("foo.rb"))
+ wait
assert !checker.execute_if_updated
assert_equal 0, i
@@ -208,6 +221,7 @@ module FileUpdateCheckerSharedTests
checker = new_checker([non_existing]) { i += 1 }
touch(non_existing)
+ wait
assert checker.execute_if_updated
assert_equal 1, i
@@ -226,6 +240,7 @@ module FileUpdateCheckerSharedTests
assert_equal 0, i
touch(File.join(subdir, "nested.rb"))
+ wait
assert checker.execute_if_updated
assert_equal 1, i
@@ -240,17 +255,20 @@ module FileUpdateCheckerSharedTests
checker = new_checker([], tmpdir => :rb, subdir => :txt) { i += 1 }
touch(tmpfile("new.txt"))
+ wait
assert !checker.execute_if_updated
assert_equal 0, i
# subdir does not look for Ruby files, but its parent tmpdir does.
touch(File.join(subdir, "nested.rb"))
+ wait
assert checker.execute_if_updated
assert_equal 1, i
touch(File.join(subdir, "nested.txt"))
+ wait
assert checker.execute_if_updated
assert_equal 2, i
diff --git a/activesupport/test/gzip_test.rb b/activesupport/test/gzip_test.rb
index d88408b55c..f51d3cdf65 100644
--- a/activesupport/test/gzip_test.rb
+++ b/activesupport/test/gzip_test.rb
@@ -20,7 +20,7 @@ class GzipTest < ActiveSupport::TestCase
end
def test_compress_should_return_gzipped_string_by_compression_level
- source_string = "Hello World"*100
+ source_string = "Hello World" * 100
gzipped_by_speed = ActiveSupport::Gzip.compress(source_string, Zlib::BEST_SPEED)
assert_equal 1, Zlib::GzipReader.new(StringIO.new(gzipped_by_speed)).level
diff --git a/activesupport/test/inflector_test.rb b/activesupport/test/inflector_test.rb
index 1c5a4f378c..d881bd346d 100644
--- a/activesupport/test/inflector_test.rb
+++ b/activesupport/test/inflector_test.rb
@@ -272,15 +272,6 @@ class InflectorTest < ActiveSupport::TestCase
end
end
- def test_parameterize_with_custom_separator_deprecated
- jruby_skip "UTF-8 to UTF8-MAC Converter is unavailable"
- StringToParameterizeWithUnderscore.each do |some_string, parameterized_string|
- assert_deprecated(/Passing the separator argument as a positional parameter is deprecated and will soon be removed. Use `separator: '_'` instead./i) do
- assert_equal(parameterized_string, ActiveSupport::Inflector.parameterize(some_string, "_"))
- end
- end
- end
-
def test_parameterize_with_multi_character_separator
jruby_skip "UTF-8 to UTF8-MAC Converter is unavailable"
StringToParameterized.each do |some_string, parameterized_string|
@@ -288,15 +279,6 @@ class InflectorTest < ActiveSupport::TestCase
end
end
- def test_parameterize_with_multi_character_separator_deprecated
- jruby_skip "UTF-8 to UTF8-MAC Converter is unavailable"
- StringToParameterized.each do |some_string, parameterized_string|
- assert_deprecated(/Passing the separator argument as a positional parameter is deprecated and will soon be removed. Use `separator: '__sep__'` instead./i) do
- assert_equal(parameterized_string.gsub("-", "__sep__"), ActiveSupport::Inflector.parameterize(some_string, "__sep__"))
- end
- end
- end
-
def test_classify
ClassNameToTableName.each do |class_name, table_name|
assert_equal(class_name, ActiveSupport::Inflector.classify(table_name))
diff --git a/activesupport/test/inflector_test_cases.rb b/activesupport/test/inflector_test_cases.rb
index a8283301b3..b660987d92 100644
--- a/activesupport/test/inflector_test_cases.rb
+++ b/activesupport/test/inflector_test_cases.rb
@@ -237,7 +237,7 @@ module InflectorTestCases
"Ops\331" => "opsu",
"Ærøskøbing" => "aeroskobing",
"Aßlar" => "asslar",
- "Japanese: 日本語" => "japanese"
+ "Japanese: 日本語" => "japanese"
}
UnderscoreToHuman = {
diff --git a/activesupport/test/json/decoding_test.rb b/activesupport/test/json/decoding_test.rb
index dd8382754b..6d1d8f1b95 100644
--- a/activesupport/test/json/decoding_test.rb
+++ b/activesupport/test/json/decoding_test.rb
@@ -15,7 +15,7 @@ class TestJSONDecoding < ActiveSupport::TestCase
TESTS = {
%q({"returnTo":{"\/categories":"\/"}}) => { "returnTo" => { "/categories" => "/" } },
%q({"return\\"To\\":":{"\/categories":"\/"}}) => { "return\"To\":" => { "/categories" => "/" } },
- %q({"returnTo":{"\/categories":1}}) => { "returnTo" => { "/categories" => 1 } },
+ %q({"returnTo":{"\/categories":1}}) => { "returnTo" => { "/categories" => 1 } },
%({"returnTo":[1,"a"]}) => { "returnTo" => [1, "a"] },
%({"returnTo":[1,"\\"a\\",", "b"]}) => { "returnTo" => [1, "\"a\",", "b"] },
%({"a": "'", "b": "5,000"}) => { "a" => "'", "b" => "5,000" },
@@ -40,8 +40,8 @@ class TestJSONDecoding < ActiveSupport::TestCase
%({"a": "2007-01-01 : it's your birthday"}) => { "a" => "2007-01-01 : it's your birthday" },
%([]) => [],
%({}) => {},
- %({"a":1}) => { "a" => 1 },
- %({"a": ""}) => { "a" => "" },
+ %({"a":1}) => { "a" => 1 },
+ %({"a": ""}) => { "a" => "" },
%({"a":"\\""}) => { "a" => "\"" },
%({"a": null}) => { "a" => nil },
%({"a": true}) => { "a" => true },
@@ -51,19 +51,19 @@ class TestJSONDecoding < ActiveSupport::TestCase
%q({"a": "\u003cunicode\u0020escape\u003e"}) => { "a" => "<unicode escape>" },
'{"a": "\\\\u0020skip double backslashes"}' => { "a" => "\\u0020skip double backslashes" },
%q({"a": "\u003cbr /\u003e"}) => { "a" => "<br />" },
- %q({"b":["\u003ci\u003e","\u003cb\u003e","\u003cu\u003e"]}) => { "b" => ["<i>","<b>","<u>"] },
+ %q({"b":["\u003ci\u003e","\u003cb\u003e","\u003cu\u003e"]}) => { "b" => ["<i>", "<b>", "<u>"] },
# test combination of dates and escaped or unicode encoded data in arrays
%q([{"d":"1970-01-01", "s":"\u0020escape"},{"d":"1970-01-01", "s":"\u0020escape"}]) =>
- [{ "d" => Date.new(1970, 1, 1), "s" => " escape" },{ "d" => Date.new(1970, 1, 1), "s" => " escape" }],
+ [{ "d" => Date.new(1970, 1, 1), "s" => " escape" }, { "d" => Date.new(1970, 1, 1), "s" => " escape" }],
%q([{"d":"1970-01-01","s":"http:\/\/example.com"},{"d":"1970-01-01","s":"http:\/\/example.com"}]) =>
[{ "d" => Date.new(1970, 1, 1), "s" => "http://example.com" },
{ "d" => Date.new(1970, 1, 1), "s" => "http://example.com" }],
# tests escaping of "\n" char with Yaml backend
- %q({"a":"\n"}) => { "a"=>"\n" },
- %q({"a":"\u000a"}) => { "a"=>"\n" },
- %q({"a":"Line1\u000aLine2"}) => { "a"=>"Line1\nLine2" },
+ %q({"a":"\n"}) => { "a" => "\n" },
+ %q({"a":"\u000a"}) => { "a" => "\n" },
+ %q({"a":"Line1\u000aLine2"}) => { "a" => "Line1\nLine2" },
# prevent json unmarshalling
- '{"json_class":"TestJSONDecoding::Foo"}' => { "json_class"=>"TestJSONDecoding::Foo" },
+ '{"json_class":"TestJSONDecoding::Foo"}' => { "json_class" => "TestJSONDecoding::Foo" },
# json "fragments" - these are invalid JSON, but ActionPack relies on this
'"a string"' => "a string",
"1.1" => 1.1,
diff --git a/activesupport/test/json/encoding_test.rb b/activesupport/test/json/encoding_test.rb
index d125cc939f..6d8f7cfbd0 100644
--- a/activesupport/test/json/encoding_test.rb
+++ b/activesupport/test/json/encoding_test.rb
@@ -49,7 +49,7 @@ class TestJSONEncoding < ActiveSupport::TestCase
def test_hash_encoding
assert_equal %({\"a\":\"b\"}), ActiveSupport::JSON.encode(a: :b)
assert_equal %({\"a\":1}), ActiveSupport::JSON.encode("a" => 1)
- assert_equal %({\"a\":[1,2]}), ActiveSupport::JSON.encode("a" => [1,2])
+ assert_equal %({\"a\":[1,2]}), ActiveSupport::JSON.encode("a" => [1, 2])
assert_equal %({"1":2}), ActiveSupport::JSON.encode(1 => 2)
assert_equal %({\"a\":\"b\",\"c\":\"d\"}), sorted_json(ActiveSupport::JSON.encode(a: :b, c: :d))
@@ -108,7 +108,7 @@ class TestJSONEncoding < ActiveSupport::TestCase
def test_time_to_json_includes_local_offset
with_standard_json_time_format(true) do
with_env_tz "US/Eastern" do
- assert_equal %("2005-02-01T15:15:10.000-05:00"), ActiveSupport::JSON.encode(Time.local(2005,2,1,15,15,10))
+ assert_equal %("2005-02-01T15:15:10.000-05:00"), ActiveSupport::JSON.encode(Time.local(2005, 2, 1, 15, 15, 10))
end
end
end
@@ -135,7 +135,7 @@ class TestJSONEncoding < ActiveSupport::TestCase
h = JSONTest::Hashlike.new
json = h.to_json only: [:foo]
- assert_equal({ "foo"=>"hello" }, JSON.parse(json))
+ assert_equal({ "foo" => "hello" }, JSON.parse(json))
end
def test_object_to_json_with_options
@@ -144,7 +144,7 @@ class TestJSONEncoding < ActiveSupport::TestCase
obj.instance_variable_set :@bar, "world"
json = obj.to_json only: ["foo"]
- assert_equal({ "foo"=>"hello" }, JSON.parse(json))
+ assert_equal({ "foo" => "hello" }, JSON.parse(json))
end
def test_struct_to_json_with_options
@@ -153,7 +153,7 @@ class TestJSONEncoding < ActiveSupport::TestCase
struct.bar = "world"
json = struct.to_json only: [:foo]
- assert_equal({ "foo"=>"hello" }, JSON.parse(json))
+ assert_equal({ "foo" => "hello" }, JSON.parse(json))
end
def test_hash_should_pass_encoding_options_to_children_in_as_json
@@ -256,7 +256,7 @@ class TestJSONEncoding < ActiveSupport::TestCase
class CustomWithOptions
attr_accessor :foo, :bar
- def as_json(options={})
+ def as_json(options = {})
options[:only] = %w(foo bar)
super(options)
end
@@ -268,8 +268,8 @@ class TestJSONEncoding < ActiveSupport::TestCase
f.bar = "world"
hash = { "foo" => f, "other_hash" => { "foo" => "other_foo", "test" => "other_test" } }
- assert_equal({ "foo"=>{ "foo"=>"hello","bar"=>"world" },
- "other_hash" => { "foo"=>"other_foo","test"=>"other_test" } }, ActiveSupport::JSON.decode(hash.to_json))
+ assert_equal({ "foo" => { "foo" => "hello", "bar" => "world" },
+ "other_hash" => { "foo" => "other_foo", "test" => "other_test" } }, ActiveSupport::JSON.decode(hash.to_json))
end
def test_array_to_json_should_not_keep_options_around
@@ -278,8 +278,8 @@ class TestJSONEncoding < ActiveSupport::TestCase
f.bar = "world"
array = [f, { "foo" => "other_foo", "test" => "other_test" }]
- assert_equal([{ "foo"=>"hello","bar"=>"world" },
- { "foo"=>"other_foo","test"=>"other_test" }], ActiveSupport::JSON.decode(array.to_json))
+ assert_equal([{ "foo" => "hello", "bar" => "world" },
+ { "foo" => "other_foo", "test" => "other_test" }], ActiveSupport::JSON.decode(array.to_json))
end
class OptionsTest
@@ -329,7 +329,7 @@ class TestJSONEncoding < ActiveSupport::TestCase
end
def test_nil_true_and_false_represented_as_themselves
- assert_equal nil, nil.as_json
+ assert_nil nil.as_json
assert_equal true, true.as_json
assert_equal false, false.as_json
end
@@ -341,7 +341,7 @@ class TestJSONEncoding < ActiveSupport::TestCase
super
end
- def as_json(options={})
+ def as_json(options = {})
@as_json_called = true
super
end
@@ -432,7 +432,27 @@ EXPECTED
assert_equal '"foo"', ActiveSupport::JSON.encode(exception)
end
- protected
+ class InfiniteNumber
+ def as_json(options = nil)
+ { "number" => Float::INFINITY }
+ end
+ end
+
+ def test_to_json_works_when_as_json_returns_infinite_number
+ assert_equal '{"number":null}', InfiniteNumber.new.to_json
+ end
+
+ class NaNNumber
+ def as_json(options = nil)
+ { "number" => Float::NAN }
+ end
+ end
+
+ def test_to_json_works_when_as_json_returns_NaN_number
+ assert_equal '{"number":null}', NaNNumber.new.to_json
+ end
+
+ private
def object_keys(json_object)
json_object[1..-2].scan(/([^{}:,\s]+):/).flatten.sort
diff --git a/activesupport/test/json/encoding_test_cases.rb b/activesupport/test/json/encoding_test_cases.rb
index ff2ed3a788..8eac246937 100644
--- a/activesupport/test/json/encoding_test_cases.rb
+++ b/activesupport/test/json/encoding_test_cases.rb
@@ -36,10 +36,10 @@ module JSONTest
NilTests = [[ nil, %(null) ]]
NumericTests = [[ 1, %(1) ],
[ 2.5, %(2.5) ],
- [ 0.0/0.0, %(null) ],
- [ 1.0/0.0, %(null) ],
- [ -1.0/0.0, %(null) ],
- [ BigDecimal("0.0")/BigDecimal("0.0"), %(null) ],
+ [ 0.0 / 0.0, %(null) ],
+ [ 1.0 / 0.0, %(null) ],
+ [ -1.0 / 0.0, %(null) ],
+ [ BigDecimal("0.0") / BigDecimal("0.0"), %(null) ],
[ BigDecimal("2.5"), %("#{BigDecimal('2.5')}") ]]
StringTests = [[ "this is the <string>", %("this is the \\u003cstring\\u003e")],
@@ -80,13 +80,13 @@ module JSONTest
PathnameTests = [[ Pathname.new("lib/index.rb"), %("lib/index.rb") ]]
- DateTests = [[ Date.new(2005,2,1), %("2005/02/01") ]]
- TimeTests = [[ Time.utc(2005,2,1,15,15,10), %("2005/02/01 15:15:10 +0000") ]]
- DateTimeTests = [[ DateTime.civil(2005,2,1,15,15,10), %("2005/02/01 15:15:10 +0000") ]]
+ DateTests = [[ Date.new(2005, 2, 1), %("2005/02/01") ]]
+ TimeTests = [[ Time.utc(2005, 2, 1, 15, 15, 10), %("2005/02/01 15:15:10 +0000") ]]
+ DateTimeTests = [[ DateTime.civil(2005, 2, 1, 15, 15, 10), %("2005/02/01 15:15:10 +0000") ]]
- StandardDateTests = [[ Date.new(2005,2,1), %("2005-02-01") ]]
- StandardTimeTests = [[ Time.utc(2005,2,1,15,15,10), %("2005-02-01T15:15:10.000Z") ]]
- StandardDateTimeTests = [[ DateTime.civil(2005,2,1,15,15,10), %("2005-02-01T15:15:10.000+00:00") ]]
+ StandardDateTests = [[ Date.new(2005, 2, 1), %("2005-02-01") ]]
+ StandardTimeTests = [[ Time.utc(2005, 2, 1, 15, 15, 10), %("2005-02-01T15:15:10.000Z") ]]
+ StandardDateTimeTests = [[ DateTime.civil(2005, 2, 1, 15, 15, 10), %("2005-02-01T15:15:10.000+00:00") ]]
StandardStringTests = [[ "this is the <string>", %("this is the <string>")]]
end
end
diff --git a/activesupport/test/message_encryptor_test.rb b/activesupport/test/message_encryptor_test.rb
index f8282c89ca..56a436f751 100644
--- a/activesupport/test/message_encryptor_test.rb
+++ b/activesupport/test/message_encryptor_test.rb
@@ -71,7 +71,7 @@ class MessageEncryptorTest < ActiveSupport::TestCase
def test_message_obeys_strict_encoding
bad_encoding_characters = "\n!@#"
- message, iv = @encryptor.encrypt_and_sign("This is a very \n\nhumble string"+bad_encoding_characters)
+ message, iv = @encryptor.encrypt_and_sign("This is a very \n\nhumble string" + bad_encoding_characters)
assert_not_decrypted("#{::Base64.encode64 message.to_s}--#{::Base64.encode64 iv.to_s}")
assert_not_verified("#{::Base64.encode64 message.to_s}--#{::Base64.encode64 iv.to_s}")
diff --git a/activesupport/test/message_verifier_test.rb b/activesupport/test/message_verifier_test.rb
index d56a46b250..d6109c761d 100644
--- a/activesupport/test/message_verifier_test.rb
+++ b/activesupport/test/message_verifier_test.rb
@@ -80,6 +80,6 @@ class MessageVerifierTest < ActiveSupport::TestCase
exception = assert_raise(ArgumentError) do
ActiveSupport::MessageVerifier.new(nil)
end
- assert_equal exception.message, "Secret should not be nil."
+ assert_equal "Secret should not be nil.", exception.message
end
end
diff --git a/activesupport/test/multibyte_chars_test.rb b/activesupport/test/multibyte_chars_test.rb
index 5ff1543328..68ce42bf72 100644
--- a/activesupport/test/multibyte_chars_test.rb
+++ b/activesupport/test/multibyte_chars_test.rb
@@ -231,7 +231,7 @@ class MultibyteCharsUTF8BehaviourTest < ActiveSupport::TestCase
assert_equal 0, @chars.index("こに")
assert_equal 2, @chars.index("ち")
assert_equal 2, @chars.index("ち", -2)
- assert_equal nil, @chars.index("ち", -1)
+ assert_nil @chars.index("ち", -1)
assert_equal 3, @chars.index("わ")
assert_equal 5, "ééxééx".mb_chars.index("x", 4)
end
@@ -390,11 +390,11 @@ class MultibyteCharsUTF8BehaviourTest < ActiveSupport::TestCase
end
def test_slice_should_take_character_offsets
- assert_equal nil, "".mb_chars.slice(0)
+ assert_nil "".mb_chars.slice(0)
assert_equal "こ", @chars.slice(0)
assert_equal "わ", @chars.slice(3)
- assert_equal nil, "".mb_chars.slice(-1..1)
- assert_equal nil, "".mb_chars.slice(-1, 1)
+ assert_nil "".mb_chars.slice(-1..1)
+ assert_nil "".mb_chars.slice(-1, 1)
assert_equal "", "".mb_chars.slice(0..10)
assert_equal "にちわ", @chars.slice(1..3)
assert_equal "にちわ", @chars.slice(1, 3)
@@ -403,10 +403,10 @@ class MultibyteCharsUTF8BehaviourTest < ActiveSupport::TestCase
assert_equal "", @chars.slice(4..10)
assert_equal "に", @chars.slice(/に/u)
assert_equal "にち", @chars.slice(/に./u)
- assert_equal nil, @chars.slice(/unknown/u)
+ assert_nil @chars.slice(/unknown/u)
assert_equal "にち", @chars.slice(/(にち)/u, 1)
- assert_equal nil, @chars.slice(/(にち)/u, 2)
- assert_equal nil, @chars.slice(7..6)
+ assert_nil @chars.slice(/(にち)/u, 2)
+ assert_nil @chars.slice(7..6)
end
def test_slice_bang_returns_sliced_out_substring
@@ -414,12 +414,12 @@ class MultibyteCharsUTF8BehaviourTest < ActiveSupport::TestCase
end
def test_slice_bang_returns_nil_on_out_of_bound_arguments
- assert_equal nil, @chars.mb_chars.slice!(9..10)
+ assert_nil @chars.mb_chars.slice!(9..10)
end
def test_slice_bang_removes_the_slice_from_the_receiver
chars = "úüù".mb_chars
- chars.slice!(0,2)
+ chars.slice!(0, 2)
assert_equal "ù", chars
end
@@ -512,7 +512,7 @@ class MultibyteCharsExtrasTest < ActiveSupport::TestCase
{ "аБвг аБвг" => "Абвг абвг",
"аБвг АБВГ" => "Абвг абвг",
"АБВГ АБВГ" => "Абвг абвг",
- "" => "" }.each do |f,t|
+ "" => "" }.each do |f, t|
assert_equal t, chars(f).capitalize
end
end
@@ -600,10 +600,10 @@ class MultibyteCharsExtrasTest < ActiveSupport::TestCase
].pack("U*")
assert_equal_codepoints "", chars("").normalize
- assert_equal_codepoints [44,105,106,328,323].pack("U*"), chars(comp_str).normalize(:kc).to_s
- assert_equal_codepoints [44,307,328,323].pack("U*"), chars(comp_str).normalize(:c).to_s
- assert_equal_codepoints [44,307,110,780,78,769].pack("U*"), chars(comp_str).normalize(:d).to_s
- assert_equal_codepoints [44,105,106,110,780,78,769].pack("U*"), chars(comp_str).normalize(:kd).to_s
+ assert_equal_codepoints [44, 105, 106, 328, 323].pack("U*"), chars(comp_str).normalize(:kc).to_s
+ assert_equal_codepoints [44, 307, 328, 323].pack("U*"), chars(comp_str).normalize(:c).to_s
+ assert_equal_codepoints [44, 307, 110, 780, 78, 769].pack("U*"), chars(comp_str).normalize(:d).to_s
+ assert_equal_codepoints [44, 105, 106, 110, 780, 78, 769].pack("U*"), chars(comp_str).normalize(:kd).to_s
end
def test_should_compute_grapheme_length
diff --git a/activesupport/test/multibyte_conformance_test.rb b/activesupport/test/multibyte_conformance_test.rb
index bf004d7924..ef1a26135f 100644
--- a/activesupport/test/multibyte_conformance_test.rb
+++ b/activesupport/test/multibyte_conformance_test.rb
@@ -82,7 +82,7 @@ class MultibyteConformanceTest < ActiveSupport::TestCase
end
end
- protected
+ private
def each_line_of_norm_tests(&block)
File.open(File.join(CACHE_DIR, UNIDATA_FILE), "r") do | f |
until f.eof?
diff --git a/activesupport/test/multibyte_grapheme_break_conformance_test.rb b/activesupport/test/multibyte_grapheme_break_conformance_test.rb
index 04a7d290d9..b3328987ae 100644
--- a/activesupport/test/multibyte_grapheme_break_conformance_test.rb
+++ b/activesupport/test/multibyte_grapheme_break_conformance_test.rb
@@ -28,7 +28,7 @@ class MultibyteGraphemeBreakConformanceTest < ActiveSupport::TestCase
end
end
- protected
+ private
def each_line_of_break_tests(&block)
lines = 0
max_test_lines = 0 # Don't limit below 21, because that's the header of the testfile
diff --git a/activesupport/test/multibyte_normalization_conformance_test.rb b/activesupport/test/multibyte_normalization_conformance_test.rb
index e013bd578f..ebc9f92d23 100644
--- a/activesupport/test/multibyte_normalization_conformance_test.rb
+++ b/activesupport/test/multibyte_normalization_conformance_test.rb
@@ -83,7 +83,7 @@ class MultibyteNormalizationConformanceTest < ActiveSupport::TestCase
end
end
- protected
+ private
def each_line_of_norm_tests(&block)
lines = 0
max_test_lines = 0 # Don't limit below 38, because that's the header of the testfile
diff --git a/activesupport/test/multibyte_test_helpers.rb b/activesupport/test/multibyte_test_helpers.rb
index 22c586c50d..2201860d8a 100644
--- a/activesupport/test/multibyte_test_helpers.rb
+++ b/activesupport/test/multibyte_test_helpers.rb
@@ -33,7 +33,7 @@ module MultibyteTestHelpers
str.to_s.unpack("U*").map { |cp| cp.to_s(16) }.join(" ")
end
- def assert_equal_codepoints(expected, actual, message=nil)
+ def assert_equal_codepoints(expected, actual, message = nil)
assert_equal(inspect_codepoints(expected), inspect_codepoints(actual), message)
end
end
diff --git a/activesupport/test/notifications/evented_notification_test.rb b/activesupport/test/notifications/evented_notification_test.rb
index 688971c858..24c5befec3 100644
--- a/activesupport/test/notifications/evented_notification_test.rb
+++ b/activesupport/test/notifications/evented_notification_test.rb
@@ -7,7 +7,7 @@ module ActiveSupport
attr_reader :events
def initialize
- @events = []
+ @events = []
end
def start(name, id, payload)
diff --git a/activesupport/test/notifications/instrumenter_test.rb b/activesupport/test/notifications/instrumenter_test.rb
index e454e6897b..7eacc5cbe7 100644
--- a/activesupport/test/notifications/instrumenter_test.rb
+++ b/activesupport/test/notifications/instrumenter_test.rb
@@ -22,11 +22,11 @@ module ActiveSupport
super
@notifier = TestNotifier.new
@instrumenter = Instrumenter.new @notifier
- @payload = { foo: Object.new }
+ @payload = { foo: Object.new }
end
def test_instrument
- called = false
+ called = false
instrumenter.instrument("foo", payload) {
called = true
}
diff --git a/activesupport/test/notifications_test.rb b/activesupport/test/notifications_test.rb
index a6f0d82e8a..11f743519f 100644
--- a/activesupport/test/notifications_test.rb
+++ b/activesupport/test/notifications_test.rb
@@ -273,7 +273,7 @@ module Notifications
assert !not_child.parent_of?(parent)
end
- protected
+ private
def random_id
@random_id ||= SecureRandom.hex(10)
end
diff --git a/activesupport/test/number_helper_test.rb b/activesupport/test/number_helper_test.rb
index 1a59210018..dc0c34d4e2 100644
--- a/activesupport/test/number_helper_test.rb
+++ b/activesupport/test/number_helper_test.rb
@@ -150,7 +150,7 @@ module ActiveSupport
assert_equal("111.23460000000000000000", number_helper.number_to_rounded(Rational(1112346, 10000), precision: 20))
assert_equal("111.23460000000000000000", number_helper.number_to_rounded("111.2346", precision: 20))
assert_equal("111.23460000000000000000", number_helper.number_to_rounded(BigDecimal(111.2346, Float::DIG), precision: 20))
- assert_equal("111.2346" + "0"*96, number_helper.number_to_rounded("111.2346", precision: 100))
+ assert_equal("111.2346" + "0" * 96, number_helper.number_to_rounded("111.2346", precision: 100))
assert_equal("111.2346", number_helper.number_to_rounded(Rational(1112346, 10000), precision: 4))
assert_equal("0.00", number_helper.number_to_rounded(Rational(0, 1), precision: 2))
end
@@ -166,42 +166,42 @@ module ActiveSupport
def test_to_rounded_with_significant_digits
[@instance_with_helpers, TestClassWithClassNumberHelpers, ActiveSupport::NumberHelper].each do |number_helper|
assert_equal "124000", number_helper.number_to_rounded(123987, precision: 3, significant: true)
- assert_equal "120000000", number_helper.number_to_rounded(123987876, precision: 2, significant: true )
- assert_equal "40000", number_helper.number_to_rounded("43523", precision: 1, significant: true )
- assert_equal "9775", number_helper.number_to_rounded(9775, precision: 4, significant: true )
- assert_equal "5.4", number_helper.number_to_rounded(5.3923, precision: 2, significant: true )
- assert_equal "5", number_helper.number_to_rounded(5.3923, precision: 1, significant: true )
- assert_equal "1", number_helper.number_to_rounded(1.232, precision: 1, significant: true )
- assert_equal "7", number_helper.number_to_rounded(7, precision: 1, significant: true )
- assert_equal "1", number_helper.number_to_rounded(1, precision: 1, significant: true )
- assert_equal "53", number_helper.number_to_rounded(52.7923, precision: 2, significant: true )
- assert_equal "9775.00", number_helper.number_to_rounded(9775, precision: 6, significant: true )
- assert_equal "5.392900", number_helper.number_to_rounded(5.3929, precision: 7, significant: true )
- assert_equal "0.0", number_helper.number_to_rounded(0, precision: 2, significant: true )
- assert_equal "0", number_helper.number_to_rounded(0, precision: 1, significant: true )
- assert_equal "0.0001", number_helper.number_to_rounded(0.0001, precision: 1, significant: true )
- assert_equal "0.000100", number_helper.number_to_rounded(0.0001, precision: 3, significant: true )
- assert_equal "0.0001", number_helper.number_to_rounded(0.0001111, precision: 1, significant: true )
+ assert_equal "120000000", number_helper.number_to_rounded(123987876, precision: 2, significant: true)
+ assert_equal "40000", number_helper.number_to_rounded("43523", precision: 1, significant: true)
+ assert_equal "9775", number_helper.number_to_rounded(9775, precision: 4, significant: true)
+ assert_equal "5.4", number_helper.number_to_rounded(5.3923, precision: 2, significant: true)
+ assert_equal "5", number_helper.number_to_rounded(5.3923, precision: 1, significant: true)
+ assert_equal "1", number_helper.number_to_rounded(1.232, precision: 1, significant: true)
+ assert_equal "7", number_helper.number_to_rounded(7, precision: 1, significant: true)
+ assert_equal "1", number_helper.number_to_rounded(1, precision: 1, significant: true)
+ assert_equal "53", number_helper.number_to_rounded(52.7923, precision: 2, significant: true)
+ assert_equal "9775.00", number_helper.number_to_rounded(9775, precision: 6, significant: true)
+ assert_equal "5.392900", number_helper.number_to_rounded(5.3929, precision: 7, significant: true)
+ assert_equal "0.0", number_helper.number_to_rounded(0, precision: 2, significant: true)
+ assert_equal "0", number_helper.number_to_rounded(0, precision: 1, significant: true)
+ assert_equal "0.0001", number_helper.number_to_rounded(0.0001, precision: 1, significant: true)
+ assert_equal "0.000100", number_helper.number_to_rounded(0.0001, precision: 3, significant: true)
+ assert_equal "0.0001", number_helper.number_to_rounded(0.0001111, precision: 1, significant: true)
assert_equal "10.0", number_helper.number_to_rounded(9.995, precision: 3, significant: true)
assert_equal "9.99", number_helper.number_to_rounded(9.994, precision: 3, significant: true)
assert_equal "11.0", number_helper.number_to_rounded(10.995, precision: 3, significant: true)
- assert_equal "9775.0000000000000000", number_helper.number_to_rounded(9775, precision: 20, significant: true )
- assert_equal "9775.0000000000000000", number_helper.number_to_rounded(9775.0, precision: 20, significant: true )
- assert_equal "9775.0000000000000000", number_helper.number_to_rounded(Rational(9775, 1), precision: 20, significant: true )
- assert_equal "97.750000000000000000", number_helper.number_to_rounded(Rational(9775, 100), precision: 20, significant: true )
- assert_equal "9775.0000000000000000", number_helper.number_to_rounded(BigDecimal(9775), precision: 20, significant: true )
- assert_equal "9775.0000000000000000", number_helper.number_to_rounded("9775", precision: 20, significant: true )
- assert_equal "9775." + "0"*96, number_helper.number_to_rounded("9775", precision: 100, significant: true )
+ assert_equal "9775.0000000000000000", number_helper.number_to_rounded(9775, precision: 20, significant: true)
+ assert_equal "9775.0000000000000000", number_helper.number_to_rounded(9775.0, precision: 20, significant: true)
+ assert_equal "9775.0000000000000000", number_helper.number_to_rounded(Rational(9775, 1), precision: 20, significant: true)
+ assert_equal "97.750000000000000000", number_helper.number_to_rounded(Rational(9775, 100), precision: 20, significant: true)
+ assert_equal "9775.0000000000000000", number_helper.number_to_rounded(BigDecimal(9775), precision: 20, significant: true)
+ assert_equal "9775.0000000000000000", number_helper.number_to_rounded("9775", precision: 20, significant: true)
+ assert_equal "9775." + "0" * 96, number_helper.number_to_rounded("9775", precision: 100, significant: true)
assert_equal("97.7", number_helper.number_to_rounded(Rational(9772, 100), precision: 3, significant: true))
end
end
def test_to_rounded_with_strip_insignificant_zeros
[@instance_with_helpers, TestClassWithClassNumberHelpers, ActiveSupport::NumberHelper].each do |number_helper|
- assert_equal "9775.43", number_helper.number_to_rounded(9775.43, precision: 4, strip_insignificant_zeros: true )
- assert_equal "9775.2", number_helper.number_to_rounded(9775.2, precision: 6, significant: true, strip_insignificant_zeros: true )
- assert_equal "0", number_helper.number_to_rounded(0, precision: 6, significant: true, strip_insignificant_zeros: true )
+ assert_equal "9775.43", number_helper.number_to_rounded(9775.43, precision: 4, strip_insignificant_zeros: true)
+ assert_equal "9775.2", number_helper.number_to_rounded(9775.2, precision: 6, significant: true, strip_insignificant_zeros: true)
+ assert_equal "0", number_helper.number_to_rounded(0, precision: 6, significant: true, strip_insignificant_zeros: true)
end
end
@@ -210,8 +210,8 @@ module ActiveSupport
# Zero precision with significant is a mistake (would always return zero),
# so we treat it as if significant was false (increases backwards compatibility for number_to_human_size)
assert_equal "124", number_helper.number_to_rounded(123.987, precision: 0, significant: true)
- assert_equal "12", number_helper.number_to_rounded(12, precision: 0, significant: true )
- assert_equal "12", number_helper.number_to_rounded("12.3", precision: 0, significant: true )
+ assert_equal "12", number_helper.number_to_rounded(12, precision: 0, significant: true)
+ assert_equal "12", number_helper.number_to_rounded("12.3", precision: 0, significant: true)
end
end
@@ -244,23 +244,6 @@ module ActiveSupport
end
end
- def test_number_to_human_size_with_si_prefix
- assert_deprecated do
- [@instance_with_helpers, TestClassWithClassNumberHelpers, ActiveSupport::NumberHelper].each do |number_helper|
- assert_equal "3 Bytes", number_helper.number_to_human_size(3.14159265, prefix: :si)
- assert_equal "123 Bytes", number_helper.number_to_human_size(123.0, prefix: :si)
- assert_equal "123 Bytes", number_helper.number_to_human_size(123, prefix: :si)
- assert_equal "1.23 KB", number_helper.number_to_human_size(1234, prefix: :si)
- assert_equal "12.3 KB", number_helper.number_to_human_size(12345, prefix: :si)
- assert_equal "1.23 MB", number_helper.number_to_human_size(1234567, prefix: :si)
- assert_equal "1.23 GB", number_helper.number_to_human_size(1234567890, prefix: :si)
- assert_equal "1.23 TB", number_helper.number_to_human_size(1234567890123, prefix: :si)
- assert_equal "1.23 PB", number_helper.number_to_human_size(1234567890123456, prefix: :si)
- assert_equal "1.23 EB", number_helper.number_to_human_size(1234567890123456789, prefix: :si)
- end
- end
- end
-
def test_number_to_human_size_with_options_hash
[@instance_with_helpers, TestClassWithClassNumberHelpers, ActiveSupport::NumberHelper].each do |number_helper|
assert_equal "1.2 MB", number_helper.number_to_human_size(1234567, precision: 2)
diff --git a/activesupport/test/ordered_hash_test.rb b/activesupport/test/ordered_hash_test.rb
index 86da9f193a..2cefab3832 100644
--- a/activesupport/test/ordered_hash_test.rb
+++ b/activesupport/test/ordered_hash_test.rb
@@ -154,7 +154,7 @@ class OrderedHashTest < ActiveSupport::TestCase
end
def test_merge
- other_hash = ActiveSupport::OrderedHash.new
+ other_hash = ActiveSupport::OrderedHash.new
other_hash["purple"] = "800080"
other_hash["violet"] = "ee82ee"
merged = @ordered_hash.merge other_hash
@@ -211,7 +211,7 @@ class OrderedHashTest < ActiveSupport::TestCase
end
def test_alternate_initialization_with_splat
- alternate = ActiveSupport::OrderedHash[1,2,3,4]
+ alternate = ActiveSupport::OrderedHash[1, 2, 3, 4]
assert_kind_of ActiveSupport::OrderedHash, alternate
assert_equal [1, 3], alternate.keys
end
@@ -230,7 +230,7 @@ class OrderedHashTest < ActiveSupport::TestCase
def test_alternate_initialization_raises_exception_on_odd_length_args
assert_raises ArgumentError do
- ActiveSupport::OrderedHash[1,2,3,4,5]
+ ActiveSupport::OrderedHash[1, 2, 3, 4, 5]
end
end
diff --git a/activesupport/test/rescuable_test.rb b/activesupport/test/rescuable_test.rb
index 7e5c3d1a8f..f7eb047d44 100644
--- a/activesupport/test/rescuable_test.rb
+++ b/activesupport/test/rescuable_test.rb
@@ -137,6 +137,6 @@ class RescuableTest < ActiveSupport::TestCase
def test_rescue_falls_back_to_exception_cause
@stargate.dispatch :fall_back_to_cause
- assert_equal "unhandled RuntimeError with a handleable cause", @stargate.result
+ assert_equal "dex", @stargate.result
end
end
diff --git a/activesupport/test/safe_buffer_test.rb b/activesupport/test/safe_buffer_test.rb
index 4d083ab773..36c068b91f 100644
--- a/activesupport/test/safe_buffer_test.rb
+++ b/activesupport/test/safe_buffer_test.rb
@@ -130,7 +130,7 @@ class SafeBufferTest < ActiveSupport::TestCase
end
test "Should be safe when sliced if original value was safe" do
- new_buffer = @buffer[0,0]
+ new_buffer = @buffer[0, 0]
assert_not_nil new_buffer
assert new_buffer.html_safe?, "should be safe"
end
@@ -175,6 +175,6 @@ class SafeBufferTest < ActiveSupport::TestCase
test "Should not affect frozen objects when accessing characters" do
x = "Hello".html_safe
- assert_equal x[/a/, 1], nil
+ assert_nil x[/a/, 1]
end
end
diff --git a/activesupport/test/test_case_test.rb b/activesupport/test/test_case_test.rb
index d769a8c145..af7fc44d66 100644
--- a/activesupport/test/test_case_test.rb
+++ b/activesupport/test/test_case_test.rb
@@ -257,7 +257,7 @@ class SetupAndTeardownTest < ActiveSupport::TestCase
def teardown
end
- protected
+ private
def reset_callback_record
@called_back = []
@@ -282,7 +282,7 @@ class SubclassSetupAndTeardownTest < SetupAndTeardownTest
assert_equal [:foo, :sentinel, :bar], self.class._teardown_callbacks.map(&:raw_filter)
end
- protected
+ private
def bar
@called_back << :bar
end
diff --git a/activesupport/test/time_zone_test.rb b/activesupport/test/time_zone_test.rb
index 76fee1fdd4..4794b55742 100644
--- a/activesupport/test/time_zone_test.rb
+++ b/activesupport/test/time_zone_test.rb
@@ -55,7 +55,7 @@ class TimeZoneTest < ActiveSupport::TestCase
zone = ActiveSupport::TimeZone["Eastern Time (US & Canada)"].dup
def zone.time_now; Time.local(2000); end
assert_instance_of ActiveSupport::TimeWithZone, zone.now
- assert_equal Time.utc(2000,1,1,5), zone.now.utc
+ assert_equal Time.utc(2000, 1, 1, 5), zone.now.utc
assert_equal Time.utc(2000), zone.now.time
assert_equal zone, zone.now.time_zone
end
@@ -65,10 +65,10 @@ class TimeZoneTest < ActiveSupport::TestCase
with_env_tz "US/Eastern" do
zone = ActiveSupport::TimeZone["Eastern Time (US & Canada)"].dup
def zone.time_now
- Time.local(2006,4,2,2) # 2AM springs forward to 3AM
+ Time.local(2006, 4, 2, 2) # 2AM springs forward to 3AM
end
- assert_equal Time.utc(2006,4,2,3), zone.now.time
+ assert_equal Time.utc(2006, 4, 2, 3), zone.now.time
assert_equal true, zone.now.dst?
end
end
@@ -79,7 +79,7 @@ class TimeZoneTest < ActiveSupport::TestCase
def zone.time_now
Time.at(1162098000) # equivalent to 1AM DST
end
- assert_equal Time.utc(2006,10,29,1), zone.now.time
+ assert_equal Time.utc(2006, 10, 29, 1), zone.now.time
assert_equal true, zone.now.dst?
end
end
@@ -162,25 +162,25 @@ class TimeZoneTest < ActiveSupport::TestCase
def test_local_with_old_date
time = ActiveSupport::TimeZone["Hawaii"].local(1850, 2, 5, 15, 30, 45)
- assert_equal [45,30,15,5,2,1850], time.to_a[0,6]
+ assert_equal [45, 30, 15, 5, 2, 1850], time.to_a[0, 6]
assert_equal ActiveSupport::TimeZone["Hawaii"], time.time_zone
end
def test_local_enforces_spring_dst_rules
zone = ActiveSupport::TimeZone["Eastern Time (US & Canada)"]
- twz = zone.local(2006,4,2,1,59,59) # 1 second before DST start
- assert_equal Time.utc(2006,4,2,1,59,59), twz.time
- assert_equal Time.utc(2006,4,2,6,59,59), twz.utc
+ twz = zone.local(2006, 4, 2, 1, 59, 59) # 1 second before DST start
+ assert_equal Time.utc(2006, 4, 2, 1, 59, 59), twz.time
+ assert_equal Time.utc(2006, 4, 2, 6, 59, 59), twz.utc
assert_equal false, twz.dst?
assert_equal "EST", twz.zone
- twz2 = zone.local(2006,4,2,2) # 2AM does not exist because at 2AM, time springs forward to 3AM
- assert_equal Time.utc(2006,4,2,3), twz2.time # twz is created for 3AM
- assert_equal Time.utc(2006,4,2,7), twz2.utc
+ twz2 = zone.local(2006, 4, 2, 2) # 2AM does not exist because at 2AM, time springs forward to 3AM
+ assert_equal Time.utc(2006, 4, 2, 3), twz2.time # twz is created for 3AM
+ assert_equal Time.utc(2006, 4, 2, 7), twz2.utc
assert_equal true, twz2.dst?
assert_equal "EDT", twz2.zone
- twz3 = zone.local(2006,4,2,2,30) # 2:30AM does not exist because at 2AM, time springs forward to 3AM
- assert_equal Time.utc(2006,4,2,3,30), twz3.time # twz is created for 3:30AM
- assert_equal Time.utc(2006,4,2,7,30), twz3.utc
+ twz3 = zone.local(2006, 4, 2, 2, 30) # 2:30AM does not exist because at 2AM, time springs forward to 3AM
+ assert_equal Time.utc(2006, 4, 2, 3, 30), twz3.time # twz is created for 3:30AM
+ assert_equal Time.utc(2006, 4, 2, 7, 30), twz3.utc
assert_equal true, twz3.dst?
assert_equal "EDT", twz3.zone
end
@@ -189,9 +189,9 @@ class TimeZoneTest < ActiveSupport::TestCase
# 1AM during fall DST transition is ambiguous, it could be either DST or non-DST 1AM
# Mirroring Time.local behavior, this method selects the DST time
zone = ActiveSupport::TimeZone["Eastern Time (US & Canada)"]
- twz = zone.local(2006,10,29,1)
- assert_equal Time.utc(2006,10,29,1), twz.time
- assert_equal Time.utc(2006,10,29,5), twz.utc
+ twz = zone.local(2006, 10, 29, 1)
+ assert_equal Time.utc(2006, 10, 29, 1), twz.time
+ assert_equal Time.utc(2006, 10, 29, 5), twz.utc
assert_equal true, twz.dst?
assert_equal "EDT", twz.zone
end
@@ -200,7 +200,7 @@ class TimeZoneTest < ActiveSupport::TestCase
zone = ActiveSupport::TimeZone["Eastern Time (US & Canada)"]
secs = 946684800.0
twz = zone.at(secs)
- assert_equal Time.utc(1999,12,31,19), twz.time
+ assert_equal Time.utc(1999, 12, 31, 19), twz.time
assert_equal Time.utc(2000), twz.utc
assert_equal zone, twz.time_zone
assert_equal secs, twz.to_f
@@ -218,7 +218,7 @@ class TimeZoneTest < ActiveSupport::TestCase
def test_parse
zone = ActiveSupport::TimeZone["Eastern Time (US & Canada)"]
twz = zone.parse("1999-12-31 19:00:00")
- assert_equal Time.utc(1999,12,31,19), twz.time
+ assert_equal Time.utc(1999, 12, 31, 19), twz.time
assert_equal Time.utc(2000), twz.utc
assert_equal zone, twz.time_zone
end
@@ -234,14 +234,14 @@ class TimeZoneTest < ActiveSupport::TestCase
def test_parse_with_old_date
zone = ActiveSupport::TimeZone["Eastern Time (US & Canada)"]
twz = zone.parse("1883-12-31 19:00:00")
- assert_equal [0,0,19,31,12,1883], twz.to_a[0,6]
+ assert_equal [0, 0, 19, 31, 12, 1883], twz.to_a[0, 6]
assert_equal zone, twz.time_zone
end
def test_parse_far_future_date_with_time_zone_offset_in_string
zone = ActiveSupport::TimeZone["Eastern Time (US & Canada)"]
twz = zone.parse("2050-12-31 19:00:00 -10:00") # i.e., 2050-01-01 05:00:00 UTC
- assert_equal [0,0,0,1,1,2051], twz.to_a[0,6]
+ assert_equal [0, 0, 0, 1, 1, 2051], twz.to_a[0, 6]
assert_equal zone, twz.time_zone
end
@@ -253,9 +253,9 @@ class TimeZoneTest < ActiveSupport::TestCase
def test_parse_with_incomplete_date
zone = ActiveSupport::TimeZone["Eastern Time (US & Canada)"]
- zone.stub(:now, zone.local(1999,12,31)) do
+ zone.stub(:now, zone.local(1999, 12, 31)) do
twz = zone.parse("19:00:00")
- assert_equal Time.utc(1999,12,31,19), twz.time
+ assert_equal Time.utc(1999, 12, 31, 19), twz.time
end
end
@@ -272,7 +272,7 @@ class TimeZoneTest < ActiveSupport::TestCase
with_env_tz("EET") do
zone = ActiveSupport::TimeZone["Pacific Time (US & Canada)"]
twz = zone.parse("2012-03-25 03:29:00")
- assert_equal [0, 29, 3, 25, 3, 2012], twz.to_a[0,6]
+ assert_equal [0, 29, 3, 25, 3, 2012], twz.to_a[0, 6]
end
end
@@ -280,7 +280,7 @@ class TimeZoneTest < ActiveSupport::TestCase
with_env_tz("EET") do
zone = ActiveSupport::TimeZone["Pacific Time (US & Canada)"]
twz = zone.parse("2012-03-11 02:29:00")
- assert_equal [0, 29, 3, 11, 3, 2012], twz.to_a[0,6]
+ assert_equal [0, 29, 3, 11, 3, 2012], twz.to_a[0, 6]
end
end
@@ -317,9 +317,9 @@ class TimeZoneTest < ActiveSupport::TestCase
def test_strptime
zone = ActiveSupport::TimeZone["Eastern Time (US & Canada)"]
twz = zone.strptime("1999-12-31 12:00:00", "%Y-%m-%d %H:%M:%S")
- assert_equal Time.utc(1999,12,31,17), twz
- assert_equal Time.utc(1999,12,31,12), twz.time
- assert_equal Time.utc(1999,12,31,17), twz.utc
+ assert_equal Time.utc(1999, 12, 31, 17), twz
+ assert_equal Time.utc(1999, 12, 31, 12), twz.time
+ assert_equal Time.utc(1999, 12, 31, 17), twz.utc
assert_equal zone, twz.time_zone
end
@@ -327,9 +327,9 @@ class TimeZoneTest < ActiveSupport::TestCase
with_tz_default ActiveSupport::TimeZone["Pacific Time (US & Canada)"] do
zone = ActiveSupport::TimeZone["Eastern Time (US & Canada)"]
twz = zone.strptime("1999-12-31 12:00:00", "%Y-%m-%d %H:%M:%S")
- assert_equal Time.utc(1999,12,31,17), twz
- assert_equal Time.utc(1999,12,31,12), twz.time
- assert_equal Time.utc(1999,12,31,17), twz.utc
+ assert_equal Time.utc(1999, 12, 31, 17), twz
+ assert_equal Time.utc(1999, 12, 31, 12), twz.time
+ assert_equal Time.utc(1999, 12, 31, 17), twz.utc
assert_equal zone, twz.time_zone
end
end
@@ -337,45 +337,45 @@ class TimeZoneTest < ActiveSupport::TestCase
def test_strptime_with_explicit_time_zone_as_abbrev
zone = ActiveSupport::TimeZone["Eastern Time (US & Canada)"]
twz = zone.strptime("1999-12-31 12:00:00 PST", "%Y-%m-%d %H:%M:%S %Z")
- assert_equal Time.utc(1999,12,31,20), twz
- assert_equal Time.utc(1999,12,31,15), twz.time
- assert_equal Time.utc(1999,12,31,20), twz.utc
+ assert_equal Time.utc(1999, 12, 31, 20), twz
+ assert_equal Time.utc(1999, 12, 31, 15), twz.time
+ assert_equal Time.utc(1999, 12, 31, 20), twz.utc
assert_equal zone, twz.time_zone
end
def test_strptime_with_explicit_time_zone_as_h_offset
zone = ActiveSupport::TimeZone["Eastern Time (US & Canada)"]
twz = zone.strptime("1999-12-31 12:00:00 -08", "%Y-%m-%d %H:%M:%S %:::z")
- assert_equal Time.utc(1999,12,31,20), twz
- assert_equal Time.utc(1999,12,31,15), twz.time
- assert_equal Time.utc(1999,12,31,20), twz.utc
+ assert_equal Time.utc(1999, 12, 31, 20), twz
+ assert_equal Time.utc(1999, 12, 31, 15), twz.time
+ assert_equal Time.utc(1999, 12, 31, 20), twz.utc
assert_equal zone, twz.time_zone
end
def test_strptime_with_explicit_time_zone_as_hm_offset
zone = ActiveSupport::TimeZone["Eastern Time (US & Canada)"]
twz = zone.strptime("1999-12-31 12:00:00 -08:00", "%Y-%m-%d %H:%M:%S %:z")
- assert_equal Time.utc(1999,12,31,20), twz
- assert_equal Time.utc(1999,12,31,15), twz.time
- assert_equal Time.utc(1999,12,31,20), twz.utc
+ assert_equal Time.utc(1999, 12, 31, 20), twz
+ assert_equal Time.utc(1999, 12, 31, 15), twz.time
+ assert_equal Time.utc(1999, 12, 31, 20), twz.utc
assert_equal zone, twz.time_zone
end
def test_strptime_with_explicit_time_zone_as_hms_offset
zone = ActiveSupport::TimeZone["Eastern Time (US & Canada)"]
twz = zone.strptime("1999-12-31 12:00:00 -08:00:00", "%Y-%m-%d %H:%M:%S %::z")
- assert_equal Time.utc(1999,12,31,20), twz
- assert_equal Time.utc(1999,12,31,15), twz.time
- assert_equal Time.utc(1999,12,31,20), twz.utc
+ assert_equal Time.utc(1999, 12, 31, 20), twz
+ assert_equal Time.utc(1999, 12, 31, 15), twz.time
+ assert_equal Time.utc(1999, 12, 31, 20), twz.utc
assert_equal zone, twz.time_zone
end
def test_strptime_with_almost_explicit_time_zone
zone = ActiveSupport::TimeZone["Eastern Time (US & Canada)"]
twz = zone.strptime("1999-12-31 12:00:00 %Z", "%Y-%m-%d %H:%M:%S %%Z")
- assert_equal Time.utc(1999,12,31,17), twz
- assert_equal Time.utc(1999,12,31,12), twz.time
- assert_equal Time.utc(1999,12,31,17), twz.utc
+ assert_equal Time.utc(1999, 12, 31, 17), twz
+ assert_equal Time.utc(1999, 12, 31, 12), twz.time
+ assert_equal Time.utc(1999, 12, 31, 17), twz.utc
assert_equal zone, twz.time_zone
end
@@ -395,10 +395,28 @@ class TimeZoneTest < ActiveSupport::TestCase
end
end
+ def test_strptime_with_timestamp_seconds
+ with_env_tz "US/Eastern" do
+ zone = ActiveSupport::TimeZone["Eastern Time (US & Canada)"]
+ time_str = "1470272280"
+ time = zone.strptime(time_str, "%s")
+ assert_equal Time.at(1470272280), time
+ end
+ end
+
+ def test_strptime_with_timestamp_milliseconds
+ with_env_tz "US/Eastern" do
+ zone = ActiveSupport::TimeZone["Eastern Time (US & Canada)"]
+ time_str = "1470272280000"
+ time = zone.strptime(time_str, "%Q")
+ assert_equal Time.at(1470272280), time
+ end
+ end
+
def test_utc_offset_lazy_loaded_from_tzinfo_when_not_passed_in_to_initialize
tzinfo = TZInfo::Timezone.get("America/New_York")
zone = ActiveSupport::TimeZone.create(tzinfo.name, nil, tzinfo)
- assert_equal nil, zone.instance_variable_get("@utc_offset")
+ assert_nil zone.instance_variable_get("@utc_offset")
assert_equal(-18_000, zone.utc_offset)
end
@@ -478,8 +496,8 @@ class TimeZoneTest < ActiveSupport::TestCase
def test_all_sorted
all = ActiveSupport::TimeZone.all
- 1.upto( all.length-1 ) do |i|
- assert all[i-1] < all[i]
+ 1.upto(all.length - 1) do |i|
+ assert all[i - 1] < all[i]
end
end
diff --git a/activesupport/test/xml_mini/jdom_engine_test.rb b/activesupport/test/xml_mini/jdom_engine_test.rb
index 816d57972c..e783cea67c 100644
--- a/activesupport/test/xml_mini/jdom_engine_test.rb
+++ b/activesupport/test/xml_mini/jdom_engine_test.rb
@@ -1,37 +1,9 @@
-if RUBY_PLATFORM.include?("java")
- require "abstract_unit"
- require "active_support/xml_mini"
- require "active_support/core_ext/hash/conversions"
-
- class JDOMEngineTest < ActiveSupport::TestCase
- include ActiveSupport
+require_relative "xml_mini_engine_test"
+XMLMiniEngineTest.run_with_platform("java") do
+ class JDOMEngineTest < XMLMiniEngineTest
FILES_DIR = File.dirname(__FILE__) + "/../fixtures/xml"
- def setup
- @default_backend = XmlMini.backend
- XmlMini.backend = "JDOM"
- end
-
- def teardown
- XmlMini.backend = @default_backend
- end
-
- def test_file_from_xml
- hash = Hash.from_xml(<<-eoxml)
- <blog>
- <logo type="file" name="logo.png" content_type="image/png">
- </logo>
- </blog>
- eoxml
- assert hash.has_key?("blog")
- assert hash["blog"].has_key?("logo")
-
- file = hash["blog"]["logo"]
- assert_equal "logo.png", file.original_filename
- assert_equal "image/png", file.content_type
- end
-
def test_not_allowed_to_expand_entities_to_files
attack_xml = <<-EOT
<!DOCTYPE member [
@@ -63,121 +35,17 @@ if RUBY_PLATFORM.include?("java")
assert_equal "x", Hash.from_xml(attack_xml)["member"]
end
- def test_exception_thrown_on_expansion_attack
- assert_raise Java::OrgXmlSax::SAXParseException do
- attack_xml = <<-EOT
- <!DOCTYPE member [
- <!ENTITY a "&b;&b;&b;&b;&b;&b;&b;&b;&b;&b;">
- <!ENTITY b "&c;&c;&c;&c;&c;&c;&c;&c;&c;&c;">
- <!ENTITY c "&d;&d;&d;&d;&d;&d;&d;&d;&d;&d;">
- <!ENTITY d "&e;&e;&e;&e;&e;&e;&e;&e;&e;&e;">
- <!ENTITY e "&f;&f;&f;&f;&f;&f;&f;&f;&f;&f;">
- <!ENTITY f "&g;&g;&g;&g;&g;&g;&g;&g;&g;&g;">
- <!ENTITY g "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx">
- ]>
- <member>
- &a;
- </member>
- EOT
- Hash.from_xml(attack_xml)
+ private
+ def engine
+ "JDOM"
end
- end
-
- def test_setting_JDOM_as_backend
- XmlMini.backend = "JDOM"
- assert_equal XmlMini_JDOM, XmlMini.backend
- end
-
- def test_blank_returns_empty_hash
- assert_equal({}, XmlMini.parse(nil))
- assert_equal({}, XmlMini.parse(""))
- end
-
- def test_array_type_makes_an_array
- assert_equal_rexml(<<-eoxml)
- <blog>
- <posts type="array">
- <post>a post</post>
- <post>another post</post>
- </posts>
- </blog>
- eoxml
- end
-
- def test_one_node_document_as_hash
- assert_equal_rexml(<<-eoxml)
- <products/>
- eoxml
- end
-
- def test_one_node_with_attributes_document_as_hash
- assert_equal_rexml(<<-eoxml)
- <products foo="bar"/>
- eoxml
- end
-
- def test_products_node_with_book_node_as_hash
- assert_equal_rexml(<<-eoxml)
- <products>
- <book name="awesome" id="12345" />
- </products>
- eoxml
- end
-
- def test_products_node_with_two_book_nodes_as_hash
- assert_equal_rexml(<<-eoxml)
- <products>
- <book name="awesome" id="12345" />
- <book name="america" id="67890" />
- </products>
- eoxml
- end
- def test_single_node_with_content_as_hash
- assert_equal_rexml(<<-eoxml)
- <products>
- hello world
- </products>
- eoxml
- end
-
- def test_children_with_children
- assert_equal_rexml(<<-eoxml)
- <root>
- <products>
- <book name="america" id="67890" />
- </products>
- </root>
- eoxml
- end
-
- def test_children_with_text
- assert_equal_rexml(<<-eoxml)
- <root>
- <products>
- hello everyone
- </products>
- </root>
- eoxml
- end
-
- def test_children_with_non_adjacent_text
- assert_equal_rexml(<<-eoxml)
- <root>
- good
- <products>
- hello everyone
- </products>
- morning
- </root>
- eoxml
- end
+ def expansion_attack_error
+ Java::OrgXmlSax::SAXParseException
+ end
- private
- def assert_equal_rexml(xml)
- parsed_xml = XmlMini.parse(xml)
- hash = XmlMini.with_backend("REXML") { XmlMini.parse(xml) }
- assert_equal(hash, parsed_xml)
+ def extended_engine?
+ false
end
end
end
diff --git a/activesupport/test/xml_mini/libxml_engine_test.rb b/activesupport/test/xml_mini/libxml_engine_test.rb
index 81b0d3c407..f3394ad7f2 100644
--- a/activesupport/test/xml_mini/libxml_engine_test.rb
+++ b/activesupport/test/xml_mini/libxml_engine_test.rb
@@ -1,203 +1,19 @@
-begin
- require "libxml"
-rescue LoadError
- # Skip libxml tests
-else
- require "abstract_unit"
- require "active_support/xml_mini"
- require "active_support/core_ext/hash/conversions"
-
- class LibxmlEngineTest < ActiveSupport::TestCase
- include ActiveSupport
+require_relative "xml_mini_engine_test"
+XMLMiniEngineTest.run_with_gem("libxml") do
+ class LibxmlEngineTest < XMLMiniEngineTest
def setup
- @default_backend = XmlMini.backend
- XmlMini.backend = "LibXML"
-
+ super
LibXML::XML::Error.set_handler(&lambda { |error| }) #silence libxml, exceptions will do
end
- def teardown
- XmlMini.backend = @default_backend
- end
-
- def test_exception_thrown_on_expansion_attack
- assert_raise LibXML::XML::Error do
- attack_xml = %{<?xml version="1.0" encoding="UTF-8"?>
- <!DOCTYPE member [
- <!ENTITY a "&b;&b;&b;&b;&b;&b;&b;&b;&b;&b;">
- <!ENTITY b "&c;&c;&c;&c;&c;&c;&c;&c;&c;&c;">
- <!ENTITY c "&d;&d;&d;&d;&d;&d;&d;&d;&d;&d;">
- <!ENTITY d "&e;&e;&e;&e;&e;&e;&e;&e;&e;&e;">
- <!ENTITY e "&f;&f;&f;&f;&f;&f;&f;&f;&f;&f;">
- <!ENTITY f "&g;&g;&g;&g;&g;&g;&g;&g;&g;&g;">
- <!ENTITY g "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx">
- ]>
- <member>
- &a;
- </member>
- }
- Hash.from_xml(attack_xml)
+ private
+ def engine
+ "LibXML"
end
- end
-
- def test_setting_libxml_as_backend
- XmlMini.backend = "LibXML"
- assert_equal XmlMini_LibXML, XmlMini.backend
- end
-
- def test_blank_returns_empty_hash
- assert_equal({}, XmlMini.parse(nil))
- assert_equal({}, XmlMini.parse(""))
- end
-
- def test_array_type_makes_an_array
- assert_equal_rexml(<<-eoxml)
- <blog>
- <posts type="array">
- <post>a post</post>
- <post>another post</post>
- </posts>
- </blog>
- eoxml
- end
-
- def test_one_node_document_as_hash
- assert_equal_rexml(<<-eoxml)
- <products/>
- eoxml
- end
-
- def test_one_node_with_attributes_document_as_hash
- assert_equal_rexml(<<-eoxml)
- <products foo="bar"/>
- eoxml
- end
-
- def test_products_node_with_book_node_as_hash
- assert_equal_rexml(<<-eoxml)
- <products>
- <book name="awesome" id="12345" />
- </products>
- eoxml
- end
-
- def test_products_node_with_two_book_nodes_as_hash
- assert_equal_rexml(<<-eoxml)
- <products>
- <book name="awesome" id="12345" />
- <book name="america" id="67890" />
- </products>
- eoxml
- end
-
- def test_single_node_with_content_as_hash
- assert_equal_rexml(<<-eoxml)
- <products>
- hello world
- </products>
- eoxml
- end
-
- def test_children_with_children
- assert_equal_rexml(<<-eoxml)
- <root>
- <products>
- <book name="america" id="67890" />
- </products>
- </root>
- eoxml
- end
-
- def test_children_with_text
- assert_equal_rexml(<<-eoxml)
- <root>
- <products>
- hello everyone
- </products>
- </root>
- eoxml
- end
-
- def test_children_with_non_adjacent_text
- assert_equal_rexml(<<-eoxml)
- <root>
- good
- <products>
- hello everyone
- </products>
- morning
- </root>
- eoxml
- end
-
- def test_parse_from_io
- io = StringIO.new(<<-eoxml)
- <root>
- good
- <products>
- hello everyone
- </products>
- morning
- </root>
- eoxml
- assert_equal_rexml(io)
- end
-
- def test_children_with_simple_cdata
- assert_equal_rexml(<<-eoxml)
- <root>
- <products>
- <![CDATA[cdatablock]]>
- </products>
- </root>
- eoxml
- end
- def test_children_with_multiple_cdata
- assert_equal_rexml(<<-eoxml)
- <root>
- <products>
- <![CDATA[cdatablock1]]><![CDATA[cdatablock2]]>
- </products>
- </root>
- eoxml
- end
-
- def test_children_with_text_and_cdata
- assert_equal_rexml(<<-eoxml)
- <root>
- <products>
- hello <![CDATA[cdatablock]]>
- morning
- </products>
- </root>
- eoxml
- end
-
- def test_children_with_blank_text
- assert_equal_rexml(<<-eoxml)
- <root>
- <products> </products>
- </root>
- eoxml
- end
-
- def test_children_with_blank_text_and_attribute
- assert_equal_rexml(<<-eoxml)
- <root>
- <products type="file"> </products>
- </root>
- eoxml
- end
-
- private
- def assert_equal_rexml(xml)
- parsed_xml = XmlMini.parse(xml)
- xml.rewind if xml.respond_to?(:rewind)
- hash = XmlMini.with_backend("REXML") { XmlMini.parse(xml) }
- assert_equal(hash, parsed_xml)
+ def expansion_attack_error
+ LibXML::XML::Error
end
end
-
end
diff --git a/activesupport/test/xml_mini/libxmlsax_engine_test.rb b/activesupport/test/xml_mini/libxmlsax_engine_test.rb
index e25fa2813c..f457e160d6 100644
--- a/activesupport/test/xml_mini/libxmlsax_engine_test.rb
+++ b/activesupport/test/xml_mini/libxmlsax_engine_test.rb
@@ -1,195 +1,14 @@
-begin
- require "libxml"
-rescue LoadError
- # Skip libxml tests
-else
- require "abstract_unit"
- require "active_support/xml_mini"
- require "active_support/core_ext/hash/conversions"
+require_relative "xml_mini_engine_test"
- class LibXMLSAXEngineTest < ActiveSupport::TestCase
- include ActiveSupport
-
- def setup
- @default_backend = XmlMini.backend
- XmlMini.backend = "LibXMLSAX"
- end
-
- def teardown
- XmlMini.backend = @default_backend
- end
-
- def test_exception_thrown_on_expansion_attack
- assert_raise LibXML::XML::Error do
- attack_xml = <<-EOT
- <?xml version="1.0" encoding="UTF-8"?>
- <!DOCTYPE member [
- <!ENTITY a "&b;&b;&b;&b;&b;&b;&b;&b;&b;&b;">
- <!ENTITY b "&c;&c;&c;&c;&c;&c;&c;&c;&c;&c;">
- <!ENTITY c "&d;&d;&d;&d;&d;&d;&d;&d;&d;&d;">
- <!ENTITY d "&e;&e;&e;&e;&e;&e;&e;&e;&e;&e;">
- <!ENTITY e "&f;&f;&f;&f;&f;&f;&f;&f;&f;&f;">
- <!ENTITY f "&g;&g;&g;&g;&g;&g;&g;&g;&g;&g;">
- <!ENTITY g "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx">
- ]>
- <member>
- &a;
- </member>
- EOT
-
- Hash.from_xml(attack_xml)
+XMLMiniEngineTest.run_with_gem("libxml") do
+ class LibXMLSAXEngineTest < XMLMiniEngineTest
+ private
+ def engine
+ "LibXMLSAX"
end
- end
-
- def test_setting_libxml_as_backend
- XmlMini.backend = "LibXMLSAX"
- assert_equal XmlMini_LibXMLSAX, XmlMini.backend
- end
-
- def test_blank_returns_empty_hash
- assert_equal({}, XmlMini.parse(nil))
- assert_equal({}, XmlMini.parse(""))
- end
-
- def test_array_type_makes_an_array
- assert_equal_rexml(<<-eoxml)
- <blog>
- <posts type="array">
- <post>a post</post>
- <post>another post</post>
- </posts>
- </blog>
- eoxml
- end
-
- def test_one_node_document_as_hash
- assert_equal_rexml(<<-eoxml)
- <products/>
- eoxml
- end
-
- def test_one_node_with_attributes_document_as_hash
- assert_equal_rexml(<<-eoxml)
- <products foo="bar"/>
- eoxml
- end
-
- def test_products_node_with_book_node_as_hash
- assert_equal_rexml(<<-eoxml)
- <products>
- <book name="awesome" id="12345" />
- </products>
- eoxml
- end
-
- def test_products_node_with_two_book_nodes_as_hash
- assert_equal_rexml(<<-eoxml)
- <products>
- <book name="awesome" id="12345" />
- <book name="america" id="67890" />
- </products>
- eoxml
- end
- def test_single_node_with_content_as_hash
- assert_equal_rexml(<<-eoxml)
- <products>
- hello world
- </products>
- eoxml
- end
-
- def test_children_with_children
- assert_equal_rexml(<<-eoxml)
- <root>
- <products>
- <book name="america" id="67890" />
- </products>
- </root>
- eoxml
- end
-
- def test_children_with_text
- assert_equal_rexml(<<-eoxml)
- <root>
- <products>
- hello everyone
- </products>
- </root>
- eoxml
- end
-
- def test_children_with_non_adjacent_text
- assert_equal_rexml(<<-eoxml)
- <root>
- good
- <products>
- hello everyone
- </products>
- morning
- </root>
- eoxml
- end
-
- def test_parse_from_io
- io = StringIO.new(<<-eoxml)
- <root>
- good
- <products>
- hello everyone
- </products>
- morning
- </root>
- eoxml
- assert_equal_rexml(io)
- end
-
- def test_children_with_simple_cdata
- assert_equal_rexml(<<-eoxml)
- <root>
- <products>
- <![CDATA[cdatablock]]>
- </products>
- </root>
- eoxml
- end
-
- def test_children_with_multiple_cdata
- assert_equal_rexml(<<-eoxml)
- <root>
- <products>
- <![CDATA[cdatablock1]]><![CDATA[cdatablock2]]>
- </products>
- </root>
- eoxml
- end
-
- def test_children_with_text_and_cdata
- assert_equal_rexml(<<-eoxml)
- <root>
- <products>
- hello <![CDATA[cdatablock]]>
- morning
- </products>
- </root>
- eoxml
- end
-
- def test_children_with_blank_text
- assert_equal_rexml(<<-eoxml)
- <root>
- <products> </products>
- </root>
- eoxml
- end
-
- private
- def assert_equal_rexml(xml)
- parsed_xml = XmlMini.parse(xml)
- xml.rewind if xml.respond_to?(:rewind)
- hash = XmlMini.with_backend("REXML") { XmlMini.parse(xml) }
- assert_equal(hash, parsed_xml)
+ def expansion_attack_error
+ LibXML::XML::Error
end
end
-
end
diff --git a/activesupport/test/xml_mini/nokogiri_engine_test.rb b/activesupport/test/xml_mini/nokogiri_engine_test.rb
index 44b82da4e4..3151e75fc0 100644
--- a/activesupport/test/xml_mini/nokogiri_engine_test.rb
+++ b/activesupport/test/xml_mini/nokogiri_engine_test.rb
@@ -1,215 +1,14 @@
-begin
- require "nokogiri"
-rescue LoadError
- # Skip nokogiri tests
-else
- require "abstract_unit"
- require "active_support/xml_mini"
- require "active_support/core_ext/hash/conversions"
+require_relative "xml_mini_engine_test"
- class NokogiriEngineTest < ActiveSupport::TestCase
- def setup
- @default_backend = ActiveSupport::XmlMini.backend
- ActiveSupport::XmlMini.backend = "Nokogiri"
- end
-
- def teardown
- ActiveSupport::XmlMini.backend = @default_backend
- end
-
- def test_file_from_xml
- hash = Hash.from_xml(<<-eoxml)
- <blog>
- <logo type="file" name="logo.png" content_type="image/png">
- </logo>
- </blog>
- eoxml
- assert hash.has_key?("blog")
- assert hash["blog"].has_key?("logo")
-
- file = hash["blog"]["logo"]
- assert_equal "logo.png", file.original_filename
- assert_equal "image/png", file.content_type
- end
-
- def test_exception_thrown_on_expansion_attack
- assert_raise Nokogiri::XML::SyntaxError do
- attack_xml = <<-EOT
- <?xml version="1.0" encoding="UTF-8"?>
- <!DOCTYPE member [
- <!ENTITY a "&b;&b;&b;&b;&b;&b;&b;&b;&b;&b;">
- <!ENTITY b "&c;&c;&c;&c;&c;&c;&c;&c;&c;&c;">
- <!ENTITY c "&d;&d;&d;&d;&d;&d;&d;&d;&d;&d;">
- <!ENTITY d "&e;&e;&e;&e;&e;&e;&e;&e;&e;&e;">
- <!ENTITY e "&f;&f;&f;&f;&f;&f;&f;&f;&f;&f;">
- <!ENTITY f "&g;&g;&g;&g;&g;&g;&g;&g;&g;&g;">
- <!ENTITY g "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx">
- ]>
- <member>
- &a;
- </member>
- EOT
- Hash.from_xml(attack_xml)
+XMLMiniEngineTest.run_with_gem("nokogiri") do
+ class NokogiriEngineTest < XMLMiniEngineTest
+ private
+ def engine
+ "Nokogiri"
end
- end
-
- def test_setting_nokogiri_as_backend
- ActiveSupport::XmlMini.backend = "Nokogiri"
- assert_equal ActiveSupport::XmlMini_Nokogiri, ActiveSupport::XmlMini.backend
- end
-
- def test_blank_returns_empty_hash
- assert_equal({}, ActiveSupport::XmlMini.parse(nil))
- assert_equal({}, ActiveSupport::XmlMini.parse(""))
- end
-
- def test_array_type_makes_an_array
- assert_equal_rexml(<<-eoxml)
- <blog>
- <posts type="array">
- <post>a post</post>
- <post>another post</post>
- </posts>
- </blog>
- eoxml
- end
-
- def test_one_node_document_as_hash
- assert_equal_rexml(<<-eoxml)
- <products/>
- eoxml
- end
-
- def test_one_node_with_attributes_document_as_hash
- assert_equal_rexml(<<-eoxml)
- <products foo="bar"/>
- eoxml
- end
-
- def test_products_node_with_book_node_as_hash
- assert_equal_rexml(<<-eoxml)
- <products>
- <book name="awesome" id="12345" />
- </products>
- eoxml
- end
-
- def test_products_node_with_two_book_nodes_as_hash
- assert_equal_rexml(<<-eoxml)
- <products>
- <book name="awesome" id="12345" />
- <book name="america" id="67890" />
- </products>
- eoxml
- end
- def test_single_node_with_content_as_hash
- assert_equal_rexml(<<-eoxml)
- <products>
- hello world
- </products>
- eoxml
- end
-
- def test_children_with_children
- assert_equal_rexml(<<-eoxml)
- <root>
- <products>
- <book name="america" id="67890" />
- </products>
- </root>
- eoxml
- end
-
- def test_children_with_text
- assert_equal_rexml(<<-eoxml)
- <root>
- <products>
- hello everyone
- </products>
- </root>
- eoxml
- end
-
- def test_children_with_non_adjacent_text
- assert_equal_rexml(<<-eoxml)
- <root>
- good
- <products>
- hello everyone
- </products>
- morning
- </root>
- eoxml
- end
-
- def test_parse_from_io
- io = StringIO.new(<<-eoxml)
- <root>
- good
- <products>
- hello everyone
- </products>
- morning
- </root>
- eoxml
- assert_equal_rexml(io)
- end
-
- def test_children_with_simple_cdata
- assert_equal_rexml(<<-eoxml)
- <root>
- <products>
- <![CDATA[cdatablock]]>
- </products>
- </root>
- eoxml
- end
-
- def test_children_with_multiple_cdata
- assert_equal_rexml(<<-eoxml)
- <root>
- <products>
- <![CDATA[cdatablock1]]><![CDATA[cdatablock2]]>
- </products>
- </root>
- eoxml
- end
-
- def test_children_with_text_and_cdata
- assert_equal_rexml(<<-eoxml)
- <root>
- <products>
- hello <![CDATA[cdatablock]]>
- morning
- </products>
- </root>
- eoxml
- end
-
- def test_children_with_blank_text
- assert_equal_rexml(<<-eoxml)
- <root>
- <products> </products>
- </root>
- eoxml
- end
-
- def test_children_with_blank_text_and_attribute
- assert_equal_rexml(<<-eoxml)
- <root>
- <products type="file"> </products>
- </root>
- eoxml
- end
-
- private
- def assert_equal_rexml(xml)
- parsed_xml = ActiveSupport::XmlMini.parse(xml)
- xml.rewind if xml.respond_to?(:rewind)
- hash = ActiveSupport::XmlMini.with_backend("REXML") { ActiveSupport::XmlMini.parse(xml) }
- assert_equal(hash, parsed_xml)
+ def expansion_attack_error
+ Nokogiri::XML::SyntaxError
end
end
-
end
diff --git a/activesupport/test/xml_mini/nokogirisax_engine_test.rb b/activesupport/test/xml_mini/nokogirisax_engine_test.rb
index 24b35cadf6..7dafbdaf48 100644
--- a/activesupport/test/xml_mini/nokogirisax_engine_test.rb
+++ b/activesupport/test/xml_mini/nokogirisax_engine_test.rb
@@ -1,216 +1,14 @@
-begin
- require "nokogiri"
-rescue LoadError
- # Skip nokogiri tests
-else
- require "abstract_unit"
- require "active_support/xml_mini"
- require "active_support/core_ext/hash/conversions"
+require_relative "xml_mini_engine_test"
- class NokogiriSAXEngineTest < ActiveSupport::TestCase
- def setup
- @default_backend = ActiveSupport::XmlMini.backend
- ActiveSupport::XmlMini.backend = "NokogiriSAX"
- end
-
- def teardown
- ActiveSupport::XmlMini.backend = @default_backend
- end
-
- def test_file_from_xml
- hash = Hash.from_xml(<<-eoxml)
- <blog>
- <logo type="file" name="logo.png" content_type="image/png">
- </logo>
- </blog>
- eoxml
- assert hash.has_key?("blog")
- assert hash["blog"].has_key?("logo")
-
- file = hash["blog"]["logo"]
- assert_equal "logo.png", file.original_filename
- assert_equal "image/png", file.content_type
- end
-
- def test_exception_thrown_on_expansion_attack
- assert_raise RuntimeError do
- attack_xml = <<-EOT
- <?xml version="1.0" encoding="UTF-8"?>
- <!DOCTYPE member [
- <!ENTITY a "&b;&b;&b;&b;&b;&b;&b;&b;&b;&b;">
- <!ENTITY b "&c;&c;&c;&c;&c;&c;&c;&c;&c;&c;">
- <!ENTITY c "&d;&d;&d;&d;&d;&d;&d;&d;&d;&d;">
- <!ENTITY d "&e;&e;&e;&e;&e;&e;&e;&e;&e;&e;">
- <!ENTITY e "&f;&f;&f;&f;&f;&f;&f;&f;&f;&f;">
- <!ENTITY f "&g;&g;&g;&g;&g;&g;&g;&g;&g;&g;">
- <!ENTITY g "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx">
- ]>
- <member>
- &a;
- </member>
- EOT
-
- Hash.from_xml(attack_xml)
+XMLMiniEngineTest.run_with_gem("nokogiri") do
+ class NokogiriSAXEngineTest < XMLMiniEngineTest
+ private
+ def engine
+ "NokogiriSAX"
end
- end
-
- def test_setting_nokogirisax_as_backend
- ActiveSupport::XmlMini.backend = "NokogiriSAX"
- assert_equal ActiveSupport::XmlMini_NokogiriSAX, ActiveSupport::XmlMini.backend
- end
-
- def test_blank_returns_empty_hash
- assert_equal({}, ActiveSupport::XmlMini.parse(nil))
- assert_equal({}, ActiveSupport::XmlMini.parse(""))
- end
-
- def test_array_type_makes_an_array
- assert_equal_rexml(<<-eoxml)
- <blog>
- <posts type="array">
- <post>a post</post>
- <post>another post</post>
- </posts>
- </blog>
- eoxml
- end
-
- def test_one_node_document_as_hash
- assert_equal_rexml(<<-eoxml)
- <products/>
- eoxml
- end
-
- def test_one_node_with_attributes_document_as_hash
- assert_equal_rexml(<<-eoxml)
- <products foo="bar"/>
- eoxml
- end
-
- def test_products_node_with_book_node_as_hash
- assert_equal_rexml(<<-eoxml)
- <products>
- <book name="awesome" id="12345" />
- </products>
- eoxml
- end
-
- def test_products_node_with_two_book_nodes_as_hash
- assert_equal_rexml(<<-eoxml)
- <products>
- <book name="awesome" id="12345" />
- <book name="america" id="67890" />
- </products>
- eoxml
- end
- def test_single_node_with_content_as_hash
- assert_equal_rexml(<<-eoxml)
- <products>
- hello world
- </products>
- eoxml
- end
-
- def test_children_with_children
- assert_equal_rexml(<<-eoxml)
- <root>
- <products>
- <book name="america" id="67890" />
- </products>
- </root>
- eoxml
- end
-
- def test_children_with_text
- assert_equal_rexml(<<-eoxml)
- <root>
- <products>
- hello everyone
- </products>
- </root>
- eoxml
- end
-
- def test_children_with_non_adjacent_text
- assert_equal_rexml(<<-eoxml)
- <root>
- good
- <products>
- hello everyone
- </products>
- morning
- </root>
- eoxml
- end
-
- def test_parse_from_io
- io = StringIO.new(<<-eoxml)
- <root>
- good
- <products>
- hello everyone
- </products>
- morning
- </root>
- eoxml
- assert_equal_rexml(io)
- end
-
- def test_children_with_simple_cdata
- assert_equal_rexml(<<-eoxml)
- <root>
- <products>
- <![CDATA[cdatablock]]>
- </products>
- </root>
- eoxml
- end
-
- def test_children_with_multiple_cdata
- assert_equal_rexml(<<-eoxml)
- <root>
- <products>
- <![CDATA[cdatablock1]]><![CDATA[cdatablock2]]>
- </products>
- </root>
- eoxml
- end
-
- def test_children_with_text_and_cdata
- assert_equal_rexml(<<-eoxml)
- <root>
- <products>
- hello <![CDATA[cdatablock]]>
- morning
- </products>
- </root>
- eoxml
- end
-
- def test_children_with_blank_text
- assert_equal_rexml(<<-eoxml)
- <root>
- <products> </products>
- </root>
- eoxml
- end
-
- def test_children_with_blank_text_and_attribute
- assert_equal_rexml(<<-eoxml)
- <root>
- <products type="file"> </products>
- </root>
- eoxml
- end
-
- private
- def assert_equal_rexml(xml)
- parsed_xml = ActiveSupport::XmlMini.parse(xml)
- xml.rewind if xml.respond_to?(:rewind)
- hash = ActiveSupport::XmlMini.with_backend("REXML") { ActiveSupport::XmlMini.parse(xml) }
- assert_equal(hash, parsed_xml)
+ def expansion_attack_error
+ RuntimeError
end
end
-
end
diff --git a/activesupport/test/xml_mini/rexml_engine_test.rb b/activesupport/test/xml_mini/rexml_engine_test.rb
index dc62f3f671..c51f0d3c20 100644
--- a/activesupport/test/xml_mini/rexml_engine_test.rb
+++ b/activesupport/test/xml_mini/rexml_engine_test.rb
@@ -1,44 +1,25 @@
-require "abstract_unit"
-require "active_support/xml_mini"
+require_relative "xml_mini_engine_test"
-class REXMLEngineTest < ActiveSupport::TestCase
+class REXMLEngineTest < XMLMiniEngineTest
def test_default_is_rexml
assert_equal ActiveSupport::XmlMini_REXML, ActiveSupport::XmlMini.backend
end
- def test_set_rexml_as_backend
- ActiveSupport::XmlMini.backend = "REXML"
- assert_equal ActiveSupport::XmlMini_REXML, ActiveSupport::XmlMini.backend
- end
-
- def test_parse_from_io
- ActiveSupport::XmlMini.backend = "REXML"
- io = StringIO.new(<<-eoxml)
- <root>
- good
- <products>
- hello everyone
- </products>
- morning
- </root>
- eoxml
- hash = ActiveSupport::XmlMini.parse(io)
- assert hash.has_key?("root")
- assert hash["root"].has_key?("products")
- assert_match "good", hash["root"]["__content__"]
- products = hash["root"]["products"]
- assert products.has_key?("__content__")
- assert_match "hello everyone", products["__content__"]
- end
-
def test_parse_from_empty_string
- ActiveSupport::XmlMini.backend = "REXML"
assert_equal({}, ActiveSupport::XmlMini.parse(""))
end
def test_parse_from_frozen_string
- ActiveSupport::XmlMini.backend = "REXML"
xml_string = "<root></root>".freeze
assert_equal({ "root" => {} }, ActiveSupport::XmlMini.parse(xml_string))
end
+
+ private
+ def engine
+ "REXML"
+ end
+
+ def expansion_attack_error
+ RuntimeError
+ end
end
diff --git a/activesupport/test/xml_mini/xml_mini_engine_test.rb b/activesupport/test/xml_mini/xml_mini_engine_test.rb
new file mode 100644
index 0000000000..5be9084c9d
--- /dev/null
+++ b/activesupport/test/xml_mini/xml_mini_engine_test.rb
@@ -0,0 +1,257 @@
+require "abstract_unit"
+require "active_support/xml_mini"
+require "active_support/core_ext/hash/conversions"
+
+class XMLMiniEngineTest < ActiveSupport::TestCase
+ def self.run_with_gem(gem_name)
+ require gem_name
+ yield
+ rescue LoadError
+ # Skip tests unless gem is available
+ end
+
+ def self.run_with_platform(platform_name)
+ yield if RUBY_PLATFORM.include?(platform_name)
+ end
+
+ def self.inherited(base)
+ base.include EngineTests
+ super
+ end
+
+ def setup
+ @default_backend = ActiveSupport::XmlMini.backend
+ ActiveSupport::XmlMini.backend = engine
+ super
+ end
+
+ def teardown
+ ActiveSupport::XmlMini.backend = @default_backend
+ super
+ end
+
+ module EngineTests
+ def test_file_from_xml
+ hash = Hash.from_xml(<<-eoxml)
+ <blog>
+ <logo type="file" name="logo.png" content_type="image/png">
+ </logo>
+ </blog>
+ eoxml
+ assert hash.key?("blog")
+ assert hash["blog"].key?("logo")
+
+ file = hash["blog"]["logo"]
+ assert_equal "logo.png", file.original_filename
+ assert_equal "image/png", file.content_type
+ end
+
+ def test_exception_thrown_on_expansion_attack
+ assert_raise expansion_attack_error do
+ Hash.from_xml(<<-eoxml)
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE member [
+ <!ENTITY a "&b;&b;&b;&b;&b;&b;&b;&b;&b;&b;">
+ <!ENTITY b "&c;&c;&c;&c;&c;&c;&c;&c;&c;&c;">
+ <!ENTITY c "&d;&d;&d;&d;&d;&d;&d;&d;&d;&d;">
+ <!ENTITY d "&e;&e;&e;&e;&e;&e;&e;&e;&e;&e;">
+ <!ENTITY e "&f;&f;&f;&f;&f;&f;&f;&f;&f;&f;">
+ <!ENTITY f "&g;&g;&g;&g;&g;&g;&g;&g;&g;&g;">
+ <!ENTITY g "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx">
+ ]>
+ <member>
+ &a;
+ </member>
+ eoxml
+ end
+ end
+
+ def test_setting_backend
+ assert_engine_class ActiveSupport::XmlMini.backend
+ end
+
+ def test_blank_returns_empty_hash
+ assert_equal({}, ActiveSupport::XmlMini.parse(nil))
+ assert_equal({}, ActiveSupport::XmlMini.parse(""))
+ end
+
+ def test_array_type_makes_an_array
+ assert_equal_rexml(<<-eoxml)
+ <blog>
+ <posts type="array">
+ <post>a post</post>
+ <post>another post</post>
+ </posts>
+ </blog>
+ eoxml
+ end
+
+ def test_one_node_document_as_hash
+ assert_equal_rexml(<<-eoxml)
+ <products/>
+ eoxml
+ end
+
+ def test_one_node_with_attributes_document_as_hash
+ assert_equal_rexml(<<-eoxml)
+ <products foo="bar"/>
+ eoxml
+ end
+
+ def test_products_node_with_book_node_as_hash
+ assert_equal_rexml(<<-eoxml)
+ <products>
+ <book name="awesome" id="12345" />
+ </products>
+ eoxml
+ end
+
+ def test_products_node_with_two_book_nodes_as_hash
+ assert_equal_rexml(<<-eoxml)
+ <products>
+ <book name="awesome" id="12345" />
+ <book name="america" id="67890" />
+ </products>
+ eoxml
+ end
+
+ def test_single_node_with_content_as_hash
+ assert_equal_rexml(<<-eoxml)
+ <products>
+ hello world
+ </products>
+ eoxml
+ end
+
+ def test_children_with_children
+ assert_equal_rexml(<<-eoxml)
+ <root>
+ <products>
+ <book name="america" id="67890" />
+ </products>
+ </root>
+ eoxml
+ end
+
+ def test_children_with_text
+ assert_equal_rexml(<<-eoxml)
+ <root>
+ <products>
+ hello everyone
+ </products>
+ </root>
+ eoxml
+ end
+
+ def test_children_with_non_adjacent_text
+ assert_equal_rexml(<<-eoxml)
+ <root>
+ good
+ <products>
+ hello everyone
+ </products>
+ morning
+ </root>
+ eoxml
+ end
+
+ def test_parse_from_io
+ skip_unless_extended_engine
+
+ assert_equal_rexml(StringIO.new(<<-eoxml))
+ <root>
+ good
+ <products>
+ hello everyone
+ </products>
+ morning
+ </root>
+ eoxml
+ end
+
+ def test_children_with_simple_cdata
+ skip_unless_extended_engine
+
+ assert_equal_rexml(<<-eoxml)
+ <root>
+ <products>
+ <![CDATA[cdatablock]]>
+ </products>
+ </root>
+ eoxml
+ end
+
+ def test_children_with_multiple_cdata
+ skip_unless_extended_engine
+
+ assert_equal_rexml(<<-eoxml)
+ <root>
+ <products>
+ <![CDATA[cdatablock1]]><![CDATA[cdatablock2]]>
+ </products>
+ </root>
+ eoxml
+ end
+
+ def test_children_with_text_and_cdata
+ skip_unless_extended_engine
+
+ assert_equal_rexml(<<-eoxml)
+ <root>
+ <products>
+ hello <![CDATA[cdatablock]]>
+ morning
+ </products>
+ </root>
+ eoxml
+ end
+
+ def test_children_with_blank_text
+ skip_unless_extended_engine
+
+ assert_equal_rexml(<<-eoxml)
+ <root>
+ <products> </products>
+ </root>
+ eoxml
+ end
+
+ def test_children_with_blank_text_and_attribute
+ skip_unless_extended_engine
+
+ assert_equal_rexml(<<-eoxml)
+ <root>
+ <products type="file"> </products>
+ </root>
+ eoxml
+ end
+
+ private
+ def engine
+ raise NotImplementedError
+ end
+
+ def assert_engine_class(actual)
+ assert_equal ActiveSupport.const_get("XmlMini_#{engine}"), actual
+ end
+
+ def assert_equal_rexml(xml)
+ parsed_xml = ActiveSupport::XmlMini.parse(xml)
+ xml.rewind if xml.respond_to?(:rewind)
+ hash = ActiveSupport::XmlMini.with_backend("REXML") { ActiveSupport::XmlMini.parse(xml) }
+ assert_equal(hash, parsed_xml)
+ end
+
+ def expansion_attack_error
+ raise NotImplementedError
+ end
+
+ def extended_engine?
+ true
+ end
+
+ def skip_unless_extended_engine
+ skip "#{engine} is not an extended engine" unless extended_engine?
+ end
+ end
+end
diff --git a/activesupport/test/xml_mini_test.rb b/activesupport/test/xml_mini_test.rb
index b15ccfb764..b8caa1d74c 100644
--- a/activesupport/test/xml_mini_test.rb
+++ b/activesupport/test/xml_mini_test.rb
@@ -93,57 +93,57 @@ module XmlMiniTest
test "#to_tag should use the type value in the options hash" do
@xml.to_tag(:b, "blue", @options.merge(type: "color"))
- assert_xml( "<b type=\"color\">blue</b>" )
+ assert_xml("<b type=\"color\">blue</b>")
end
test "#to_tag accepts symbol types" do
@xml.to_tag(:b, :name, @options)
- assert_xml( "<b type=\"symbol\">name</b>" )
+ assert_xml("<b type=\"symbol\">name</b>")
end
test "#to_tag accepts boolean types" do
@xml.to_tag(:b, true, @options)
- assert_xml( "<b type=\"boolean\">true</b>")
+ assert_xml("<b type=\"boolean\">true</b>")
end
test "#to_tag accepts float types" do
@xml.to_tag(:b, 3.14, @options)
- assert_xml( "<b type=\"float\">3.14</b>")
+ assert_xml("<b type=\"float\">3.14</b>")
end
test "#to_tag accepts decimal types" do
@xml.to_tag(:b, ::BigDecimal.new("1.2"), @options)
- assert_xml( "<b type=\"decimal\">1.2</b>")
+ assert_xml("<b type=\"decimal\">1.2</b>")
end
test "#to_tag accepts date types" do
- @xml.to_tag(:b, Date.new(2001,2,3), @options)
- assert_xml( "<b type=\"date\">2001-02-03</b>")
+ @xml.to_tag(:b, Date.new(2001, 2, 3), @options)
+ assert_xml("<b type=\"date\">2001-02-03</b>")
end
test "#to_tag accepts datetime types" do
- @xml.to_tag(:b, DateTime.new(2001,2,3,4,5,6,"+7"), @options)
- assert_xml( "<b type=\"dateTime\">2001-02-03T04:05:06+07:00</b>")
+ @xml.to_tag(:b, DateTime.new(2001, 2, 3, 4, 5, 6, "+7"), @options)
+ assert_xml("<b type=\"dateTime\">2001-02-03T04:05:06+07:00</b>")
end
test "#to_tag accepts time types" do
@xml.to_tag(:b, Time.new(1993, 02, 24, 12, 0, 0, "+09:00"), @options)
- assert_xml( "<b type=\"dateTime\">1993-02-24T12:00:00+09:00</b>")
+ assert_xml("<b type=\"dateTime\">1993-02-24T12:00:00+09:00</b>")
end
test "#to_tag accepts array types" do
@xml.to_tag(:b, ["first_name", "last_name"], @options)
- assert_xml( "<b type=\"array\"><b>first_name</b><b>last_name</b></b>" )
+ assert_xml("<b type=\"array\"><b>first_name</b><b>last_name</b></b>")
end
test "#to_tag accepts hash types" do
@xml.to_tag(:b, { first_name: "Bob", last_name: "Marley" }, @options)
- assert_xml( "<b><first-name>Bob</first-name><last-name>Marley</last-name></b>" )
+ assert_xml("<b><first-name>Bob</first-name><last-name>Marley</last-name></b>")
end
test "#to_tag should not add type when skip types option is set" do
@xml.to_tag(:b, "Bob", @options.merge(skip_types: 1))
- assert_xml( "<b>Bob</b>" )
+ assert_xml("<b>Bob</b>")
end
test "#to_tag should dasherize the space when passed a string with spaces as a key" do
@@ -237,22 +237,22 @@ module XmlMiniTest
assert_equal :symbol, parser.call("symbol")
assert_equal :symbol, parser.call(:symbol)
assert_equal :'123', parser.call(123)
- assert_raises(ArgumentError) { parser.call(Date.new(2013,11,12,02,11)) }
+ assert_raises(ArgumentError) { parser.call(Date.new(2013, 11, 12, 02, 11)) }
end
def test_date
parser = @parsing["date"]
- assert_equal Date.new(2013,11,12), parser.call("2013-11-12T0211Z")
+ assert_equal Date.new(2013, 11, 12), parser.call("2013-11-12T0211Z")
assert_raises(TypeError) { parser.call(1384190018) }
assert_raises(ArgumentError) { parser.call("not really a date") }
end
def test_datetime
parser = @parsing["datetime"]
- assert_equal Time.new(2013,11,12,02,11,00,0), parser.call("2013-11-12T02:11:00Z")
- assert_equal DateTime.new(2013,11,12), parser.call("2013-11-12T0211Z")
- assert_equal DateTime.new(2013,11,12,02,11), parser.call("2013-11-12T02:11Z")
- assert_equal DateTime.new(2013,11,12,02,11), parser.call("2013-11-12T11:11+9")
+ assert_equal Time.new(2013, 11, 12, 02, 11, 00, 0), parser.call("2013-11-12T02:11:00Z")
+ assert_equal DateTime.new(2013, 11, 12), parser.call("2013-11-12T0211Z")
+ assert_equal DateTime.new(2013, 11, 12, 02, 11), parser.call("2013-11-12T02:11Z")
+ assert_equal DateTime.new(2013, 11, 12, 02, 11), parser.call("2013-11-12T11:11+9")
assert_raises(ArgumentError) { parser.call("1384190018") }
end
@@ -262,7 +262,7 @@ module XmlMiniTest
assert_equal 123, parser.call(123.003)
assert_equal 123, parser.call("123")
assert_equal 0, parser.call("")
- assert_raises(ArgumentError) { parser.call(Date.new(2013,11,12,02,11)) }
+ assert_raises(ArgumentError) { parser.call(Date.new(2013, 11, 12, 02, 11)) }
end
def test_float
@@ -273,7 +273,7 @@ module XmlMiniTest
assert_equal 0.0, parser.call("")
assert_equal 123, parser.call(123)
assert_equal 123.05, parser.call(123.05)
- assert_raises(ArgumentError) { parser.call(Date.new(2013,11,12,02,11)) }
+ assert_raises(ArgumentError) { parser.call(Date.new(2013, 11, 12, 02, 11)) }
end
def test_decimal
@@ -284,7 +284,7 @@ module XmlMiniTest
assert_equal 0.0, parser.call("")
assert_equal 123, parser.call(123)
assert_raises(ArgumentError) { parser.call(123.04) }
- assert_raises(ArgumentError) { parser.call(Date.new(2013,11,12,02,11)) }
+ assert_raises(ArgumentError) { parser.call(Date.new(2013, 11, 12, 02, 11)) }
end
def test_boolean
@@ -305,7 +305,7 @@ module XmlMiniTest
assert_equal "[]", parser.call("[]")
assert_equal "[]", parser.call([])
assert_equal "{}", parser.call({})
- assert_raises(ArgumentError) { parser.call(Date.new(2013,11,12,02,11)) }
+ assert_raises(ArgumentError) { parser.call(Date.new(2013, 11, 12, 02, 11)) }
end
def test_yaml
@@ -316,14 +316,14 @@ product:
description : Basketball
YAML
expected = {
- "product"=> [
- { "sku"=>"BL394D", "quantity"=>4, "description"=>"Basketball" }
+ "product" => [
+ { "sku" => "BL394D", "quantity" => 4, "description" => "Basketball" }
]
}
parser = @parsing["yaml"]
assert_equal(expected, parser.call(yaml))
assert_equal({ 1 => "test" }, parser.call(1 => "test"))
- assert_equal({ "1 => 'test'"=>nil }, parser.call("{1 => 'test'}"))
+ assert_equal({ "1 => 'test'" => nil }, parser.call("{1 => 'test'}"))
end
def test_base64Binary_and_binary
@@ -342,11 +342,11 @@ vehemence of any carnal pleasure.
EXPECTED
parser = @parsing["base64Binary"]
- assert_equal expected_base64.gsub(/\n/," ").strip, parser.call(base64)
+ assert_equal expected_base64.gsub(/\n/, " ").strip, parser.call(base64)
parser.call("NON BASE64 INPUT")
parser = @parsing["binary"]
- assert_equal expected_base64.gsub(/\n/," ").strip, parser.call(base64, "encoding" => "base64")
+ assert_equal expected_base64.gsub(/\n/, " ").strip, parser.call(base64, "encoding" => "base64")
assert_equal "IGNORED INPUT", parser.call("IGNORED INPUT", {})
end
end
diff --git a/ci/travis.rb b/ci/travis.rb
index 1d658bae47..c49a87d864 100755
--- a/ci/travis.rb
+++ b/ci/travis.rb
@@ -136,7 +136,7 @@ class Build
end
end
-if ENV["GEM"]=="aj:integration"
+if ENV["GEM"] == "aj:integration"
ENV["QC_DATABASE_URL"] = "postgres://postgres@localhost/active_jobs_qc_int_test"
ENV["QUE_DATABASE_URL"] = "postgres://postgres@localhost/active_jobs_que_int_test"
end
diff --git a/guides/Rakefile b/guides/Rakefile
index 353966fc55..d2591f523c 100644
--- a/guides/Rakefile
+++ b/guides/Rakefile
@@ -13,8 +13,9 @@ namespace :guides do
desc "Generate .mobi file. The kindlegen executable must be in your PATH. You can get it for free from http://www.amazon.com/gp/feature.html?docId=1000765211"
task :kindle do
- unless `kindlerb -v 2> /dev/null` =~ /kindlerb 0.1.1/
- abort "Please `gem install kindlerb` and make sure you have `kindlegen` in your PATH"
+ require "kindlerb"
+ unless Kindlerb.kindlegen_available?
+ abort "Please run `setupkindlerb` to install kindlegen"
end
unless `convert` =~ /convert/
abort "Please install ImageMagick`"
diff --git a/guides/assets/stylesheets/responsive-tables.css b/guides/assets/stylesheets/responsive-tables.css
index f5fbcbf948..f5fbcbf948 100755..100644
--- a/guides/assets/stylesheets/responsive-tables.css
+++ b/guides/assets/stylesheets/responsive-tables.css
diff --git a/guides/bug_report_templates/active_record_migrations_gem.rb b/guides/bug_report_templates/active_record_migrations_gem.rb
index f568a111f6..ba80e6b4ad 100644
--- a/guides/bug_report_templates/active_record_migrations_gem.rb
+++ b/guides/bug_report_templates/active_record_migrations_gem.rb
@@ -62,4 +62,4 @@ class BugTest < Minitest::Test
assert_equal "decimal(10,0)", Payment.columns.last.sql_type
end
-end \ No newline at end of file
+end
diff --git a/guides/bug_report_templates/active_record_migrations_master.rb b/guides/bug_report_templates/active_record_migrations_master.rb
index ef7b42e0a6..13a375d1ba 100644
--- a/guides/bug_report_templates/active_record_migrations_master.rb
+++ b/guides/bug_report_templates/active_record_migrations_master.rb
@@ -61,4 +61,4 @@ class BugTest < Minitest::Test
assert_equal "decimal(10,0)", Payment.columns.last.sql_type
end
-end \ No newline at end of file
+end
diff --git a/guides/bug_report_templates/benchmark.rb b/guides/bug_report_templates/benchmark.rb
new file mode 100644
index 0000000000..54433b34dd
--- /dev/null
+++ b/guides/bug_report_templates/benchmark.rb
@@ -0,0 +1,49 @@
+begin
+ require "bundler/inline"
+rescue LoadError => e
+ $stderr.puts "Bundler version 1.10 or later is required. Please update your Bundler"
+ raise e
+end
+
+gemfile(true) do
+ source "https://rubygems.org"
+ gem "rails", github: "rails/rails"
+ gem "benchmark-ips"
+end
+
+require "active_support"
+require "active_support/core_ext/object/blank"
+
+# Your patch goes here.
+class String
+ def fast_blank?
+ true
+ end
+end
+
+# Enumerate some representative scenarios here.
+#
+# It is very easy to make an optimization that improves performance for a
+# specific scenario you care about but regresses on other common cases.
+# Therefore, you should test your change against a list of representative
+# scenarios. Ideally, they should be based on real-world scenarios extracted
+# from production applications.
+SCENARIOS = {
+ "Empty" => "",
+ "Single Space" => " ",
+ "Two Spaces" => " ",
+ "Mixed Whitspaces" => " \t\r\n",
+ "Very Long String" => " " * 100
+}
+
+SCENARIOS.each_pair do |name, value|
+ puts
+ puts " #{name} ".center(80, "=")
+ puts
+
+ Benchmark.ips do |x|
+ x.report("blank?") { value.blank? }
+ x.report("fast_blank?") { value.fast_blank? }
+ x.compare!
+ end
+end
diff --git a/guides/rails_guides/generator.rb b/guides/rails_guides/generator.rb
index 39a57191eb..a818ca9d72 100644
--- a/guides/rails_guides/generator.rb
+++ b/guides/rails_guides/generator.rb
@@ -68,7 +68,7 @@ module RailsGuides
GUIDES_RE = /\.(?:erb|md)\z/
- def initialize(output=nil)
+ def initialize(output = nil)
set_flags_from_environment
if kindle?
diff --git a/guides/rails_guides/helpers.rb b/guides/rails_guides/helpers.rb
index 888b51b1ef..6f4b0b492c 100644
--- a/guides/rails_guides/helpers.rb
+++ b/guides/rails_guides/helpers.rb
@@ -26,7 +26,7 @@ module RailsGuides
documents.reject { |document| document["work_in_progress"] }
end
- def docs_for_menu(position=nil)
+ def docs_for_menu(position = nil)
if position.nil?
documents_by_section
elsif position == "L"
diff --git a/guides/rails_guides/indexer.rb b/guides/rails_guides/indexer.rb
index 56df6a5842..c58b6b85a2 100644
--- a/guides/rails_guides/indexer.rb
+++ b/guides/rails_guides/indexer.rb
@@ -17,7 +17,7 @@ module RailsGuides
private
- def process(string, current_level=3, counters=[1])
+ def process(string, current_level = 3, counters = [1])
s = StringScanner.new(string)
level_hash = {}
diff --git a/guides/rails_guides/kindle.rb b/guides/rails_guides/kindle.rb
index 6fb8183cb1..9536d0bd3b 100644
--- a/guides/rails_guides/kindle.rb
+++ b/guides/rails_guides/kindle.rb
@@ -1,9 +1,6 @@
#!/usr/bin/env ruby
-unless `which kindlerb`
- abort "Please gem install kindlerb"
-end
-
+require "kindlerb"
require "nokogiri"
require "fileutils"
require "yaml"
@@ -28,10 +25,9 @@ module Kindle
generate_document_metadata(mobi_outfile)
puts "Creating MOBI document with kindlegen. This may take a while."
- cmd = "kindlerb . > #{File.absolute_path logfile} 2>&1"
- puts cmd
- system(cmd)
- puts "MOBI document generated at #{File.expand_path(mobi_outfile, output_dir)}"
+ if Kindlerb.run(output_dir)
+ puts "MOBI document generated at #{File.expand_path(mobi_outfile, output_dir)}"
+ end
end
end
@@ -56,7 +52,7 @@ module Kindle
h2["id"] = h2.inner_text.gsub(/\s/, "-")
end
add_head_section fdoc, "Front Matter"
- File.open("frontmatter.html","w") { |f| f.puts fdoc.to_html }
+ File.open("frontmatter.html", "w") { |f| f.puts fdoc.to_html }
html_pages.unshift "frontmatter.html"
end
@@ -68,7 +64,7 @@ module Kindle
title = doc.at("title").inner_text.gsub("Ruby on Rails Guides: ", "")
title = page.capitalize.gsub(".html", "") if title.strip == ""
File.open("sections/%03d/_section.txt" % section_idx, "w") { |f| f.puts title }
- doc.xpath("//h3[@id]").each_with_index do |h3,item_idx|
+ doc.xpath("//h3[@id]").each_with_index do |h3, item_idx|
subsection = h3.inner_text
content = h3.xpath("./following-sibling::*").take_while { |x| x.name != "h3" }.map(&:to_html)
item = Nokogiri::HTML(h3.to_html + content.join("\n"))
diff --git a/guides/rails_guides/levenshtein.rb b/guides/rails_guides/levenshtein.rb
index e947150364..40c6a5c372 100644
--- a/guides/rails_guides/levenshtein.rb
+++ b/guides/rails_guides/levenshtein.rb
@@ -20,12 +20,12 @@ module RailsGuides
str2_codepoint_enumerable = str2.each_codepoint
str1.each_codepoint.with_index do |char1, i|
- e = i+1
+ e = i + 1
str2_codepoint_enumerable.with_index do |char2, j|
cost = (char1 == char2) ? 0 : 1
x = [
- d[j+1] + 1, # insertion
+ d[j + 1] + 1, # insertion
e + 1, # deletion
d[j] + cost # substitution
].min
diff --git a/guides/rails_guides/markdown.rb b/guides/rails_guides/markdown.rb
index 33563d669c..009e5aff99 100644
--- a/guides/rails_guides/markdown.rb
+++ b/guides/rails_guides/markdown.rb
@@ -158,7 +158,7 @@ module RailsGuides
@view.content_for(:header_section) { @header }
@view.content_for(:page_title) { @title }
@view.content_for(:index_section) { @index }
- @view.render(layout: @layout, text: @body)
+ @view.render(layout: @layout, html: @body.html_safe)
end
end
end
diff --git a/guides/rails_guides/markdown/renderer.rb b/guides/rails_guides/markdown/renderer.rb
index f8e32fc498..deab741023 100644
--- a/guides/rails_guides/markdown/renderer.rb
+++ b/guides/rails_guides/markdown/renderer.rb
@@ -1,7 +1,7 @@
module RailsGuides
class Markdown
class Renderer < Redcarpet::Render::HTML
- def initialize(options={})
+ def initialize(options = {})
super
end
@@ -52,7 +52,7 @@ HTML
"ruby; html-script: true"
when "html"
"xml" # HTML is understood, but there are .xml rules in the CSS
- else
+ else
"plain"
end
end
diff --git a/guides/source/5_0_release_notes.md b/guides/source/5_0_release_notes.md
index cc332cbf97..39753cbd6f 100644
--- a/guides/source/5_0_release_notes.md
+++ b/guides/source/5_0_release_notes.md
@@ -417,7 +417,7 @@ Please refer to the [Changelog][action-pack] for detailed changes.
`ActionDispatch::IntegrationTest` instead.
([commit](https://github.com/rails/rails/commit/4414c5d1795e815b102571425974a8b1d46d932d))
-* Rails will only generate "weak", instead of strong ETags.
+* Rails generates weak ETags by default.
([Pull Request](https://github.com/rails/rails/pull/17573))
* Controller actions without an explicit `render` call and with no
@@ -453,6 +453,9 @@ Please refer to the [Changelog][action-pack] for detailed changes.
`ActionController::Live`.
([More details in this issue](https://github.com/rails/rails/issues/25581))
+* Introduce `Response#strong_etag=` and `#weak_etag=` and analogous
+ options for `fresh_when` and `stale?`.
+ ([Pull Request](https://github.com/rails/rails/pull/24387))
Action View
-------------
@@ -582,7 +585,7 @@ Please refer to the [Changelog][active-record] for detailed changes.
gem. ([Pull Request](https://github.com/rails/rails/pull/21161))
* Removed support for the legacy `mysql` database adapter from core. Most users should
- be able to use `mysql2`. It will be converted to a separate gem when when we find someone
+ be able to use `mysql2`. It will be converted to a separate gem when we find someone
to maintain it. ([Pull Request 1](https://github.com/rails/rails/pull/22642),
[Pull Request 2](https://github.com/rails/rails/pull/22715))
@@ -1000,7 +1003,8 @@ Please refer to the [Changelog][active-support] for detailed changes.
* Added `#on_weekend?`, `#on_weekday?`, `#next_weekday`, `#prev_weekday` methods to `Date`,
`Time`, and `DateTime`.
- ([Pull Request](https://github.com/rails/rails/pull/18335))
+ ([Pull Request](https://github.com/rails/rails/pull/18335),
+ [Pull Request](https://github.com/rails/rails/pull/23687))
* Added `same_time` option to `#next_week` and `#prev_week` for `Date`, `Time`,
and `DateTime`.
@@ -1051,9 +1055,6 @@ Please refer to the [Changelog][active-support] for detailed changes.
* Added `Array#second_to_last` and `Array#third_to_last` methods.
([Pull Request](https://github.com/rails/rails/pull/23583))
-* Added `#on_weekday?` method to `Date`, `Time`, and `DateTime`.
- ([Pull Request](https://github.com/rails/rails/pull/23687))
-
* Publish `ActiveSupport::Executor` and `ActiveSupport::Reloader` APIs to allow
components and libraries to manage, and participate in, the execution of
application code, and the application reloading process.
diff --git a/guides/source/action_cable_overview.md b/guides/source/action_cable_overview.md
index 3716aa0ecb..319277ef68 100644
--- a/guides/source/action_cable_overview.md
+++ b/guides/source/action_cable_overview.md
@@ -62,7 +62,7 @@ module ApplicationCable
self.current_user = find_verified_user
end
- protected
+ private
def find_verified_user
if current_user = User.find_by(id: cookies.signed[:user_id])
current_user
diff --git a/guides/source/action_controller_overview.md b/guides/source/action_controller_overview.md
index 7b1138c7d4..40eb838d32 100644
--- a/guides/source/action_controller_overview.md
+++ b/guides/source/action_controller_overview.md
@@ -258,6 +258,17 @@ scalar values, map the key to an empty array:
params.permit(id: [])
```
+Sometimes it is not possible or convenient to declare the valid keys of
+a hash parameter or its internal structure. Just map to an empty hash:
+
+```ruby
+params.permit(preferences: {})
+```
+
+but be careful because this opens the door to arbitrary input. In this
+case, `permit` ensures values in the returned structure are permitted
+scalars and filters out anything else.
+
To whitelist an entire hash of parameters, the `permit!` method can be
used:
@@ -265,9 +276,10 @@ used:
params.require(:log_entry).permit!
```
-This will mark the `:log_entry` parameters hash and any sub-hash of it as
-permitted. Extreme care should be taken when using `permit!`, as it
-will allow all current and future model attributes to be mass-assigned.
+This marks the `:log_entry` parameters hash and any sub-hash of it as
+permitted and does not check for permitted scalars, anything is accepted.
+Extreme care should be taken when using `permit!`, as it will allow all current
+and future model attributes to be mass-assigned.
#### Nested Parameters
diff --git a/guides/source/action_mailer_basics.md b/guides/source/action_mailer_basics.md
index 34847832fd..0825d54cb7 100644
--- a/guides/source/action_mailer_basics.md
+++ b/guides/source/action_mailer_basics.md
@@ -400,7 +400,7 @@ class UserMailer < ApplicationMailer
mail(to: @user.email,
subject: 'Welcome to My Awesome Site') do |format|
format.html { render 'another_template' }
- format.text { render text: 'Render text' }
+ format.text { render plain: 'Render text' }
end
end
end
diff --git a/guides/source/action_view_overview.md b/guides/source/action_view_overview.md
index ff0127522b..c835adeab6 100644
--- a/guides/source/action_view_overview.md
+++ b/guides/source/action_view_overview.md
@@ -1493,7 +1493,7 @@ strip_links('Blog: <a href="http://myblog.com/">Visit</a>.')
#### strip_tags(html)
Strips all HTML tags from the html, including comments.
-This uses the html-scanner tokenizer and so its HTML parsing ability is limited by that of html-scanner.
+This functionality is powered by the rails-html-sanitizer gem.
```ruby
strip_tags("Strip <i>these</i> tags!")
diff --git a/guides/source/active_record_callbacks.md b/guides/source/active_record_callbacks.md
index 2a1c960887..868daf2435 100644
--- a/guides/source/active_record_callbacks.md
+++ b/guides/source/active_record_callbacks.md
@@ -36,7 +36,7 @@ class User < ApplicationRecord
before_validation :ensure_login_has_a_value
- protected
+ private
def ensure_login_has_a_value
if login.nil?
self.login = email unless email.blank?
@@ -66,7 +66,7 @@ class User < ApplicationRecord
# :on takes an array as well
after_validation :set_location, on: [ :create, :update ]
- protected
+ private
def normalize_name
self.name = name.downcase.titleize
end
@@ -77,7 +77,7 @@ class User < ApplicationRecord
end
```
-It is considered good practice to declare callback methods as protected or private. If left public, they can be called from outside of the model and violate the principle of object encapsulation.
+It is considered good practice to declare callback methods as private. If left public, they can be called from outside of the model and violate the principle of object encapsulation.
Available Callbacks
-------------------
diff --git a/guides/source/active_record_migrations.md b/guides/source/active_record_migrations.md
index a45becf670..6e7e29ed60 100644
--- a/guides/source/active_record_migrations.md
+++ b/guides/source/active_record_migrations.md
@@ -229,7 +229,7 @@ As always, what has been generated for you is just a starting point. You can add
or remove from it as you see fit by editing the
`db/migrate/YYYYMMDDHHMMSS_add_details_to_products.rb` file.
-Also, the generator accepts column type as `references`(also available as
+Also, the generator accepts column type as `references` (also available as
`belongs_to`). For instance:
```bash
@@ -467,6 +467,8 @@ the first time (i.e. on the date the migration is applied).
Some adapters may support additional options; see the adapter specific API docs
for further information.
+NOTE: `null` and `default` cannot be specified via command line.
+
### Foreign Keys
While it's not required you might want to add foreign key constraints to
@@ -956,10 +958,10 @@ ActiveRecord::Schema.define(version: 20080906171750) do
create_table "products", force: true do |t|
t.string "name"
- t.text "description"
+ t.text "description"
t.datetime "created_at"
t.datetime "updated_at"
- t.string "part_number"
+ t.string "part_number"
end
end
```
@@ -1018,10 +1020,10 @@ such features, the `execute` method can be used to execute arbitrary SQL.
Migrations and Seed Data
------------------------
-The main purpose of Rails' migration feature is to issue commands that modify the
-schema using a consistent process. Migrations can also be used
-to add or modify data. This is useful in an existing database that can't be destroyed
-and recreated, such as a production database.
+The main purpose of Rails' migration feature is to issue commands that modify the
+schema using a consistent process. Migrations can also be used
+to add or modify data. This is useful in an existing database that can't be destroyed
+and recreated, such as a production database.
```ruby
class AddInitialProducts < ActiveRecord::Migration[5.0]
@@ -1037,10 +1039,10 @@ class AddInitialProducts < ActiveRecord::Migration[5.0]
end
```
-To add initial data after a database is created, Rails has a built-in
-'seeds' feature that makes the process quick and easy. This is especially
-useful when reloading the database frequently in development and test environments.
-It's easy to get started with this feature: just fill up `db/seeds.rb` with some
+To add initial data after a database is created, Rails has a built-in
+'seeds' feature that makes the process quick and easy. This is especially
+useful when reloading the database frequently in development and test environments.
+It's easy to get started with this feature: just fill up `db/seeds.rb` with some
Ruby code, and run `rails db:seed`:
```ruby
diff --git a/guides/source/active_record_postgresql.md b/guides/source/active_record_postgresql.md
index d7e35490ef..58af2f82b3 100644
--- a/guides/source/active_record_postgresql.md
+++ b/guides/source/active_record_postgresql.md
@@ -422,7 +422,7 @@ device = Device.create
device.id # => "814865cd-5a1d-4771-9306-4268f188fe9e"
```
-NOTE: `uuid_generate_v4()` (from `uuid-ossp`) is assumed if no `:default` option was
+NOTE: `gen_random_uuid()` (from `pgcrypto`) is assumed if no `:default` option was
passed to `create_table`.
Full Text Search
diff --git a/guides/source/active_record_querying.md b/guides/source/active_record_querying.md
index 38b1ffc4c8..31220f9be2 100644
--- a/guides/source/active_record_querying.md
+++ b/guides/source/active_record_querying.md
@@ -953,6 +953,9 @@ class Client < ApplicationRecord
end
```
+NOTE: Please note that the optimistic locking will be ignored if you update the
+locking column's value.
+
### Pessimistic Locking
Pessimistic locking uses a locking mechanism provided by the underlying database. Using `lock` when building a relation obtains an exclusive lock on the selected rows. Relations using `lock` are usually wrapped inside a transaction for preventing deadlock conditions.
diff --git a/guides/source/active_support_core_extensions.md b/guides/source/active_support_core_extensions.md
index 60a6c37f82..6bbc79a326 100644
--- a/guides/source/active_support_core_extensions.md
+++ b/guides/source/active_support_core_extensions.md
@@ -511,56 +511,6 @@ NOTE: Defined in `active_support/core_ext/object/inclusion.rb`.
Extensions to `Module`
----------------------
-### `alias_method_chain`
-
-**This method is deprecated in favour of using Module#prepend.**
-
-Using plain Ruby you can wrap methods with other methods, that's called _alias chaining_.
-
-For example, let's say you'd like params to be strings in functional tests, as they are in real requests, but still want the convenience of assigning integers and other kind of values. To accomplish that you could wrap `ActionDispatch::IntegrationTest#process` this way in `test/test_helper.rb`:
-
-```ruby
-ActionDispatch::IntegrationTest.class_eval do
- # save a reference to the original process method
- alias_method :original_process, :process
-
- # now redefine process and delegate to original_process
- def process('GET', path, params: nil, headers: nil, env: nil, xhr: false)
- params = Hash[*params.map {|k, v| [k, v.to_s]}.flatten]
- original_process('GET', path, params: params)
- end
-end
-```
-
-That's the method `get`, `post`, etc., delegate the work to.
-
-That technique has a risk, it could be the case that `:original_process` was taken. To try to avoid collisions people choose some label that characterizes what the chaining is about:
-
-```ruby
-ActionDispatch::IntegrationTest.class_eval do
- def process_with_stringified_params(...)
- params = Hash[*params.map {|k, v| [k, v.to_s]}.flatten]
- process_without_stringified_params(method, path, params: params)
- end
- alias_method :process_without_stringified_params, :process
- alias_method :process, :process_with_stringified_params
-end
-```
-
-The method `alias_method_chain` provides a shortcut for that pattern:
-
-```ruby
-ActionDispatch::IntegrationTest.class_eval do
- def process_with_stringified_params(...)
- params = Hash[*params.map {|k, v| [k, v.to_s]}.flatten]
- process_without_stringified_params(method, path, params: params)
- end
- alias_method_chain :process, :stringified_params
-end
-```
-
-NOTE: Defined in `active_support/core_ext/module/aliasing.rb`.
-
### Attributes
#### `alias_attribute`
@@ -2661,7 +2611,7 @@ The method `transform_keys` accepts a block and returns a hash that has applied
```ruby
{nil => nil, 1 => 1, a: :a}.transform_keys { |key| key.to_s.upcase }
-# => {"" => nil, "A" => :a, "1" => 1}
+# => {"" => nil, "1" => 1, "A" => :a}
```
In case of key collision, one of the values will be chosen. The chosen value may not always be the same given the same hash:
@@ -2703,7 +2653,7 @@ The method `stringify_keys` returns a hash that has a stringified version of the
```ruby
{nil => nil, 1 => 1, a: :a}.stringify_keys
-# => {"" => nil, "a" => :a, "1" => 1}
+# => {"" => nil, "1" => 1, "a" => :a}
```
In case of key collision, one of the values will be chosen. The chosen value may not always be the same given the same hash:
@@ -2745,7 +2695,7 @@ The method `symbolize_keys` returns a hash that has a symbolized version of the
```ruby
{nil => nil, 1 => 1, "a" => "a"}.symbolize_keys
-# => {1=>1, nil=>nil, :a=>"a"}
+# => {nil=>nil, 1=>1, :a=>"a"}
```
WARNING. Note in the previous example only one key was symbolized.
@@ -2822,7 +2772,7 @@ Ruby has built-in support for taking slices out of strings and arrays. Active Su
```ruby
{a: 1, b: 2, c: 3}.slice(:a, :c)
-# => {:c=>3, :a=>1}
+# => {:a=>1, :c=>3}
{a: 1, b: 2, c: 3}.slice(:b, :X)
# => {:b=>2} # non-existing keys are ignored
diff --git a/guides/source/active_support_instrumentation.md b/guides/source/active_support_instrumentation.md
index 3fc9d9bfa9..03c9183eb3 100644
--- a/guides/source/active_support_instrumentation.md
+++ b/guides/source/active_support_instrumentation.md
@@ -226,6 +226,24 @@ Action View
}
```
+### render_collection.action_view
+
+| Key | Value |
+| ------------- | ------------------------------------- |
+| `:identifier` | Full path to template |
+| `:count` | Size of collection |
+| `:cache_hits` | Number of partials fetched from cache |
+
+`:cache_hits` is only included if the collection is rendered with `cached: true`.
+
+```ruby
+{
+ identifier: "/Users/adam/projects/notifications/app/views/posts/_post.html.erb",
+ count: 3,
+ cache_hits: 0
+}
+```
+
Active Record
------------
diff --git a/guides/source/asset_pipeline.md b/guides/source/asset_pipeline.md
index 41dfeea84d..25717e04e4 100644
--- a/guides/source/asset_pipeline.md
+++ b/guides/source/asset_pipeline.md
@@ -1227,35 +1227,25 @@ Sprockets.
Making Your Library or Gem a Pre-Processor
------------------------------------------
-As Sprockets uses [Tilt](https://github.com/rtomayko/tilt) as a generic
-interface to different templating engines, your gem should just implement the
-Tilt template protocol. Normally, you would subclass `Tilt::Template` and
-reimplement the `prepare` method, which initializes your template, and the
-`evaluate` method, which returns the processed source. The original source is
-stored in `data`. Have a look at
-[`Tilt::Template`](https://github.com/rtomayko/tilt/blob/master/lib/tilt/template.rb)
-sources to learn more.
+Sprockets uses Processors, Transformers, Compressors, and Exporters to extend
+Sprockets functionality. Have a look at
+[Extending Sprockets](https://github.com/rails/sprockets/blob/master/guides/extending_sprockets.md)
+to learn more. Here we registered a preprocessor to add a comment to the end
+of text/css (.css) files.
```ruby
-module BangBang
- class Template < ::Tilt::Template
- def prepare
- # Do any initialization here
- end
-
- # Adds a "!" to original template.
- def evaluate(scope, locals, &block)
- "#{data}!"
- end
+module AddComment
+ def self.call(input)
+ { data: input[:data] + "/* Hello From my sprockets extension */" }
end
end
```
-Now that you have a `Template` class, it's time to associate it with an
-extension for template files:
+Now that you have a module that modifies the input data, it's time to register
+it as a preprocessor for your mime type.
```ruby
-Sprockets.register_engine '.bang', BangBang::Template
+Sprockets.register_preprocessor 'text/css', AddComment
```
Upgrading from Old Versions of Rails
diff --git a/guides/source/association_basics.md b/guides/source/association_basics.md
index 3837cda553..03d3daecc8 100644
--- a/guides/source/association_basics.md
+++ b/guides/source/association_basics.md
@@ -1994,11 +1994,9 @@ The `collection.delete` method removes one or more objects from the collection b
@part.assemblies.delete(@assembly1)
```
-WARNING: This does not trigger callbacks on the join records.
-
##### `collection.destroy(object, ...)`
-The `collection.destroy` method removes one or more objects from the collection by running `destroy` on each record in the join table, including running callbacks. This does not destroy the objects.
+The `collection.destroy` method removes one or more objects from the collection by deleting records in the join table. This does not destroy the objects.
```ruby
@part.assemblies.destroy(@assembly1)
diff --git a/guides/source/configuring.md b/guides/source/configuring.md
index 022886a122..b0334bfe4a 100644
--- a/guides/source/configuring.md
+++ b/guides/source/configuring.md
@@ -110,7 +110,7 @@ numbers. By default, Rails filters out passwords by adding `Rails.application.co
* `config.force_ssl` forces all requests to be served over HTTPS by using the `ActionDispatch::SSL` middleware, and sets `config.action_mailer.default_url_options` to be `{ protocol: 'https' }`. This can be configured by setting `config.ssl_options` - see the [ActionDispatch::SSL documentation](http://edgeapi.rubyonrails.org/classes/ActionDispatch/SSL.html) for details.
-* `config.log_formatter` defines the formatter of the Rails logger. This option defaults to an instance of `ActiveSupport::Logger::SimpleFormatter` for all modes except production, where it defaults to `Logger::Formatter`. If you are setting a value for `config.logger` you must manually pass the value of your formatter to your logger before it is wrapped in an `ActiveSupport::TaggedLogging` instance, Rails will not do it for you.
+* `config.log_formatter` defines the formatter of the Rails logger. This option defaults to an instance of `ActiveSupport::Logger::SimpleFormatter` for all modes. If you are setting a value for `config.logger` you must manually pass the value of your formatter to your logger before it is wrapped in an `ActiveSupport::TaggedLogging` instance, Rails will not do it for you.
* `config.log_level` defines the verbosity of the Rails logger. This option
defaults to `:debug` for all environments. The available log levels are: `:debug`,
@@ -175,10 +175,12 @@ pipeline is enabled. It is set to `true` by default.
* `config.assets.manifest` defines the full path to be used for the asset precompiler's manifest file. Defaults to a file named `manifest-<random>.json` in the `config.assets.prefix` directory within the public folder.
-* `config.assets.digest` enables the use of MD5 fingerprints in asset names. Set to `true` by default.
+* `config.assets.digest` enables the use of SHA256 fingerprints in asset names. Set to `true` by default.
* `config.assets.debug` disables the concatenation and compression of assets. Set to `true` by default in `development.rb`.
+* `config.assets.version` is an option string that is used in SHA256 hash generation. This can be changed to force all files to be recompiled.
+
* `config.assets.compile` is a boolean that can be used to turn on live Sprockets compilation in production.
* `config.assets.logger` accepts a logger conforming to the interface of Log4r or the default Ruby `Logger` class. Defaults to the same configured at `config.logger`. Setting `config.assets.logger` to `false` will turn off served assets logging.
@@ -1181,7 +1183,7 @@ Below is a comprehensive list of all the initializers found in Rails in the orde
* `finisher_hook`: Provides a hook for after the initialization of process of the application is complete, as well as running all the `config.after_initialize` blocks for the application, railties and engines.
-* `set_routes_reloader`: Configures Action Dispatch to reload the routes file using `ActionDispatch::Callbacks.to_prepare`.
+* `set_routes_reloader_hook`: Configures Action Dispatch to reload the routes file using `ActionDispatch::Callbacks.to_prepare`.
* `disable_dependency_loading`: Disables the automatic dependency loading if the `config.eager_load` is set to `true`.
diff --git a/guides/source/contributing_to_ruby_on_rails.md b/guides/source/contributing_to_ruby_on_rails.md
index 4f938f5deb..830a546570 100644
--- a/guides/source/contributing_to_ruby_on_rails.md
+++ b/guides/source/contributing_to_ruby_on_rails.md
@@ -270,33 +270,24 @@ The above are guidelines - please use your best judgment in using them.
### Benchmark Your Code
-If your change has an impact on the performance of Rails, please use the
-[benchmark-ips](https://github.com/evanphx/benchmark-ips) gem to provide
-benchmark results for comparison.
-
-Here's an example of using benchmark-ips:
-
-```ruby
-require 'benchmark/ips'
-
-Benchmark.ips do |x|
- x.report('addition') { 1 + 2 }
- x.report('addition with send') { 1.send(:+, 2) }
-end
-```
-
-This will generate a report with the following information:
-
-```
-Calculating -------------------------------------
- addition 132.013k i/100ms
- addition with send 125.413k i/100ms
--------------------------------------------------
- addition 9.677M (± 1.7%) i/s - 48.449M
- addition with send 6.794M (± 1.1%) i/s - 33.987M
-```
-
-Please see the benchmark/ips [README](https://github.com/evanphx/benchmark-ips/blob/master/README.md) for more information.
+For changes that might have an impact on performance, please benchmark your
+code and measure the impact. Please share the benchmark script you used as well
+as the results. You should consider including this information in your commit
+message, which allows future contributors to easily verify your findings and
+determine if they are still relevant. (For example, future optimizations in the
+Ruby VM might render certain optimizations unnecessary.)
+
+It is very easy to make an optimization that improves performance for a
+specific scenario you care about but regresses on other common cases.
+Therefore, you should test your change against a list of representative
+scenarios. Ideally, they should be based on real-world scenarios extracted
+from production applications.
+
+You can use the [benchmark template](https://github.com/rails/rails/blob/master/guides/bug_report_templates/benchmark.rb)
+as a starting point. It includes the boilerplate code to setup a benchmark
+using the [benchmark-ips](https://github.com/evanphx/benchmark-ips) gem. The
+template is designed for testing relatively self-contained changes that can be
+inlined into the script.
### Running Tests
diff --git a/guides/source/development_dependencies_install.md b/guides/source/development_dependencies_install.md
index 20cd34c182..16c7e782bc 100644
--- a/guides/source/development_dependencies_install.md
+++ b/guides/source/development_dependencies_install.md
@@ -162,6 +162,10 @@ $ cd actionpack
$ bundle exec ruby -Itest path/to/test.rb -n test_name
```
+### Railties Setup
+
+Some Railties tests depend on a JavaScript runtime environment, such as having [Node.js](https://nodejs.org/) installed.
+
### Active Record Setup
Active Record's test suite runs three times: once for SQLite3, once for MySQL, and once for PostgreSQL. We are going to see now how to set up the environment for them.
diff --git a/guides/source/form_helpers.md b/guides/source/form_helpers.md
index 048fe190e8..8ad76ad01e 100644
--- a/guides/source/form_helpers.md
+++ b/guides/source/form_helpers.md
@@ -438,8 +438,6 @@ output:
Whenever Rails sees that the internal value of an option being generated matches this value, it will add the `selected` attribute to that option.
-TIP: The second argument to `options_for_select` must be exactly equal to the desired internal value. In particular if the value is the integer `2` you cannot pass `"2"` to `options_for_select` - you must pass `2`. Be aware of values extracted from the `params` hash as they are all strings.
-
WARNING: When `:include_blank` or `:prompt` are not present, `:include_blank` is forced true if the select attribute `required` is true, display `size` is one and `multiple` is not true.
You can add arbitrary attributes to the options using hashes:
diff --git a/guides/source/generators.md b/guides/source/generators.md
index 32bbdc554a..d0b6cef3fd 100644
--- a/guides/source/generators.md
+++ b/guides/source/generators.md
@@ -208,7 +208,15 @@ $ bin/rails generate scaffold User name:string
Looking at this output, it's easy to understand how generators work in Rails 3.0 and above. The scaffold generator doesn't actually generate anything, it just invokes others to do the work. This allows us to add/replace/remove any of those invocations. For instance, the scaffold generator invokes the scaffold_controller generator, which invokes erb, test_unit and helper generators. Since each generator has a single responsibility, they are easy to reuse, avoiding code duplication.
-Our first customization on the workflow will be to stop generating stylesheet, JavaScript and test fixture files for scaffolds. We can achieve that by changing our configuration to the following:
+If we want to avoid generating the default `app/assets/stylesheets/scaffolds.scss` file when scaffolding a new resource we can disable `scaffold_stylesheet`:
+
+```ruby
+ config.generators do |g|
+ g.scaffold_stylesheet false
+ end
+```
+
+The next customization on the workflow will be to stop generating stylesheet, JavaScript and test fixture files for scaffolds altogether. We can achieve that by changing our configuration to the following:
```ruby
config.generators do |g|
@@ -451,6 +459,26 @@ $ rails new thud -m https://gist.github.com/radar/722911/raw/
Whilst the final section of this guide doesn't cover how to generate the most awesome template known to man, it will take you through the methods available at your disposal so that you can develop it yourself. These same methods are also available for generators.
+Adding Command Line Arguments
+-----------------------------
+Rails generators can be easily modified to accept custom command line arguments. This functionality comes from [Thor](http://www.rubydoc.info/github/erikhuda/thor/master/Thor/Base/ClassMethods#class_option-instance_method):
+
+```
+class_option :scope, type: :string, default: 'read_products'
+```
+
+Now our generator can be invoked as follows:
+
+```bash
+rails generate initializer --scope write_products
+```
+
+The command line arguments are accessed through the `options` method inside the generator class. e.g:
+
+```ruby
+@scope = options['scope']
+```
+
Generator methods
-----------------
diff --git a/guides/source/getting_started.md b/guides/source/getting_started.md
index 31d5c4f71d..8a451ab793 100644
--- a/guides/source/getting_started.md
+++ b/guides/source/getting_started.md
@@ -474,7 +474,7 @@ one here because the `ArticlesController` inherits from `ApplicationController`.
The next part of the message contains `request.formats` which specifies
the format of template to be served in response. It is set to `text/html` as we
requested this page via browser, so Rails is looking for an HTML template.
-`request.variants` specifies what kind of physical devices would be served by
+`request.variant` specifies what kind of physical devices would be served by
the response and helps Rails determine which template to use in the response.
It is empty because no information has been provided.
@@ -827,7 +827,7 @@ NOTE: A frequent practice is to place the standard CRUD actions in each
controller in the following order: `index`, `show`, `new`, `edit`, `create`, `update`
and `destroy`. You may use any order you choose, but keep in mind that these
are public methods; as mentioned earlier in this guide, they must be placed
-before any private or protected method in the controller in order to work.
+before declaring `private` visibility in the controller.
Given that, let's add the `show` action, as follows:
@@ -1157,7 +1157,7 @@ it look as follows:
```html+erb
<h1>Edit article</h1>
-<%= form_for :article, url: article_path(@article), method: :patch do |f| %>
+<%= form_for(@article) do |f| %>
<% if @article.errors.any? %>
<div id="error_explanation">
@@ -1195,14 +1195,15 @@ it look as follows:
This time we point the form to the `update` action, which is not defined yet
but will be very soon.
-The `method: :patch` option tells Rails that we want this form to be submitted
+Passing the article object to the method, will automagically create url for submitting the edited article form.
+This option tells Rails that we want this form to be submitted
via the `PATCH` HTTP method which is the HTTP method you're expected to use to
**update** resources according to the REST protocol.
The first parameter of `form_for` can be an object, say, `@article` which would
cause the helper to fill in the form with the fields of the object. Passing in a
symbol (`:article`) with the same name as the instance variable (`@article`)
-also automagically leads to the same behavior. This is what is happening here.
+also automagically leads to the same behavior.
More details can be found in [form_for documentation]
(http://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html#method-i-form_for).
@@ -1655,8 +1656,8 @@ 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/comment.coffee | CoffeeScript for the controller |
-| app/assets/stylesheets/comment.scss | Cascading style sheet for the controller |
+| 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
reading the article, and once they have added their comment, will be sent back
diff --git a/guides/source/i18n.md b/guides/source/i18n.md
index 887774961a..fd54bca4ff 100644
--- a/guides/source/i18n.md
+++ b/guides/source/i18n.md
@@ -166,7 +166,7 @@ def set_locale
I18n.locale = extract_locale_from_tld || I18n.default_locale
end
-# Get locale from top-level domain or return nil if such locale is not available
+# Get locale from top-level domain or return +nil+ if such locale is not available
# You have to put something like:
# 127.0.0.1 application.com
# 127.0.0.1 application.it
diff --git a/guides/source/initialization.md b/guides/source/initialization.md
index 57ed35d0d8..aa7bbcc19b 100644
--- a/guides/source/initialization.md
+++ b/guides/source/initialization.md
@@ -74,7 +74,7 @@ This file is as follows:
```ruby
#!/usr/bin/env ruby
-APP_PATH = File.expand_path('../../config/application', __FILE__)
+APP_PATH = File.expand_path('../config/application', __dir__)
require_relative '../config/boot'
require 'rails/commands'
```
@@ -86,7 +86,7 @@ The `APP_PATH` constant will be used later in `rails/commands`. The `config/boot
`config/boot.rb` contains:
```ruby
-ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
+ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
require 'bundler/setup' # Set up gems listed in the Gemfile.
```
@@ -131,7 +131,7 @@ Once `config/boot.rb` has finished, the next file that is required is
`ARGV` array simply contains `server` which will be passed over:
```ruby
-ARGV << '--help' if ARGV.empty?
+require "rails/command"
aliases = {
"g" => "generate",
@@ -146,33 +146,37 @@ aliases = {
command = ARGV.shift
command = aliases[command] || command
-require 'rails/commands/commands_tasks'
-
-Rails::CommandsTasks.new(ARGV).run_command!(command)
+Rails::Command.invoke command, ARGV
```
-TIP: As you can see, an empty ARGV list will make Rails show the help
-snippet.
-
If we had used `s` rather than `server`, Rails would have used the `aliases`
defined here to find the matching command.
-### `rails/commands/commands_tasks.rb`
+### `rails/command.rb`
-When one types a valid Rails command, `run_command!` a method of the same name
-is called. If Rails doesn't recognize the command, it tries to run a Rake task
-of the same name.
+When one types a Rails command, `invoke` tries to lookup a command for the given
+namespace and executing the command if found.
-```ruby
-COMMAND_WHITELIST = %w(plugin generate destroy console server dbconsole application runner new version help)
+If Rails doesn't recognize the command, it hands the reins over to Rake
+to run a task of the same name.
-def run_command!(command)
- command = parse_command(command)
+As shown, `Rails::Command` displays the help output automatically if the `args`
+are empty.
- if COMMAND_WHITELIST.include?(command)
- send(command)
- else
- run_rake_task(command)
+```ruby
+module Rails::Command
+ class << self
+ def invoke(namespace, args = [], **config)
+ namespace = namespace.to_s
+ namespace = "help" if namespace.blank? || Thor::HELP_MAPPINGS.include?(namespace)
+ namespace = "version" if %w( -v --version ).include? namespace
+
+ if command = find_by_namespace(namespace)
+ command.perform(namespace, args, config)
+ else
+ find_by_namespace("rake").perform(namespace, args, config)
+ end
+ end
end
end
```
@@ -180,53 +184,39 @@ end
With the `server` command, Rails will further run the following code:
```ruby
-def set_application_directory!
- Dir.chdir(File.expand_path('../../', APP_PATH)) unless File.exist?(File.expand_path("config.ru"))
-end
-
-def server
- set_application_directory!
- require_command!("server")
-
- Rails::Server.new.tap do |server|
- # We need to require application after the server sets environment,
- # otherwise the --environment option given to the server won't propagate.
- require APP_PATH
- Dir.chdir(Rails.application.root)
- server.start
+module Rails
+ module Command
+ class ServerCommand < Base # :nodoc:
+ def perform
+ set_application_directory!
+
+ Rails::Server.new.tap do |server|
+ # Require application after server sets environment to propagate
+ # the --environment option.
+ require APP_PATH
+ Dir.chdir(Rails.application.root)
+ server.start
+ end
+ end
+ end
end
end
-
-def require_command!(command)
- require "rails/commands/#{command}"
-end
```
This file will change into the Rails root directory (a path two directories up
from `APP_PATH` which points at `config/application.rb`), but only if the
-`config.ru` file isn't found. This then requires `rails/commands/server` which
-sets up the `Rails::Server` class.
-
-```ruby
-require 'fileutils'
-require 'optparse'
-require 'action_dispatch'
-require 'rails'
-
-module Rails
- class Server < ::Rack::Server
-```
-
-`fileutils` and `optparse` are standard Ruby libraries which provide helper functions for working with files and parsing options.
+`config.ru` file isn't found. This then starts up the `Rails::Server` class.
### `actionpack/lib/action_dispatch.rb`
Action Dispatch is the routing component of the Rails framework.
It adds functionality like routing, session, and common middlewares.
-### `rails/commands/server.rb`
+### `rails/commands/server/server_command.rb`
-The `Rails::Server` class is defined in this file by inheriting from `Rack::Server`. When `Rails::Server.new` is called, this calls the `initialize` method in `rails/commands/server.rb`:
+The `Rails::Server` class is defined in this file by inheriting from
+`Rack::Server`. When `Rails::Server.new` is called, this calls the `initialize`
+method in `rails/commands/server/server_command.rb`:
```ruby
def initialize(*)
@@ -252,7 +242,10 @@ end
In this case, `options` will be `nil` so nothing happens in this method.
-After `super` has finished in `Rack::Server`, we jump back to `rails/commands/server.rb`. At this point, `set_environment` is called within the context of the `Rails::Server` object and this method doesn't appear to do much at first glance:
+After `super` has finished in `Rack::Server`, we jump back to
+`rails/commands/server/server_command.rb`. At this point, `set_environment`
+is called within the context of the `Rails::Server` object and this method
+doesn't appear to do much at first glance:
```ruby
def set_environment
@@ -289,17 +282,15 @@ With the `default_options` set to this:
```ruby
def default_options
- environment = ENV['RACK_ENV'] || 'development'
- default_host = environment == 'development' ? 'localhost' : '0.0.0.0'
-
- {
- :environment => environment,
- :pid => nil,
- :Port => 9292,
- :Host => default_host,
- :AccessLog => [],
- :config => "config.ru"
- }
+ super.merge(
+ Port: ENV.fetch("PORT", 3000).to_i,
+ Host: ENV.fetch("HOST", "localhost").dup,
+ DoNotReverseLookup: true,
+ environment: (ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development").dup,
+ daemonize: false,
+ caching: nil,
+ pid: Options::DEFAULT_PID_PATH,
+ restart_cmd: restart_command)
end
```
@@ -311,22 +302,25 @@ def opt_parser
end
```
-The class **is** defined in `Rack::Server`, but is overwritten in `Rails::Server` to take different arguments. Its `parse!` method begins like this:
+The class **is** defined in `Rack::Server`, but is overwritten in
+`Rails::Server` to take different arguments. Its `parse!` method looks
+like this:
```ruby
def parse!(args)
args, options = args.dup, {}
- opt_parser = OptionParser.new do |opts|
- opts.banner = "Usage: rails server [puma, thin, etc] [options]"
- opts.on("-p", "--port=port", Integer,
- "Runs Rails on the specified port.", "Default: 3000") { |v| options[:Port] = v }
- ...
+ option_parser(options).parse! args
+
+ options[:log_stdout] = options[:daemonize].blank? && (options[:environment] || Rails.env) == "development"
+ options[:server] = args.shift
+ options
+end
```
This method will set up keys for the `options` which Rails will then be
able to use to determine how its server should run. After `initialize`
-has finished, we jump back into `rails/server` where `APP_PATH` (which was
+has finished, we jump back into the server command where `APP_PATH` (which was
set earlier) is required.
### `config/application`
@@ -345,6 +339,7 @@ def start
print_boot_information
trap(:INT) { exit }
create_tmp_directories
+ setup_dev_caching
log_to_stdout if options[:log_stdout]
super
@@ -352,7 +347,6 @@ def start
end
private
-
def print_boot_information
...
puts "=> Run `rails server -h` for more startup options"
@@ -364,21 +358,30 @@ private
end
end
+ def setup_dev_caching
+ if options[:environment] == "development"
+ Rails::DevCaching.enable_by_argument(options[:caching])
+ end
+ end
+
def log_to_stdout
wrapped_app # touch the app so the logger is set up
-
- console = ActiveSupport::Logger.new($stdout)
+
+ console = ActiveSupport::Logger.new(STDOUT)
console.formatter = Rails.logger.formatter
console.level = Rails.logger.level
-
- Rails.logger.extend(ActiveSupport::Logger.broadcast(console))
+
+ unless ActiveSupport::Logger.logger_outputs_to?(Rails.logger, STDOUT)
+ Rails.logger.extend(ActiveSupport::Logger.broadcast(console))
+ end
end
```
This is where the first output of the Rails initialization happens. This method
creates a trap for `INT` signals, so if you `CTRL-C` the server, it will exit the
process. As we can see from the code here, it will create the `tmp/cache`,
-`tmp/pids`, and `tmp/sockets` directories. It then calls `wrapped_app` which is
+`tmp/pids`, and `tmp/sockets` directories. It then enables caching in development
+if `rails server` is called with `--dev-caching`. Finally, it calls `wrapped_app` which is
responsible for creating the Rack app, before creating and assigning an instance
of `ActiveSupport::Logger`.
@@ -538,7 +541,7 @@ require "rails"
sprockets/railtie
).each do |railtie|
begin
- require "#{railtie}"
+ require railtie
rescue LoadError
end
end
diff --git a/guides/source/layout.html.erb b/guides/source/layout.html.erb
index 943fd3fd7f..bb50761b30 100644
--- a/guides/source/layout.html.erb
+++ b/guides/source/layout.html.erb
@@ -88,7 +88,7 @@
<div id="container">
<div class="wrapper">
<div id="mainCol">
- <%= yield.html_safe %>
+ <%= yield %>
<h3>Feedback</h3>
<p>
diff --git a/guides/source/layouts_and_rendering.md b/guides/source/layouts_and_rendering.md
index 7e4ec5ba7e..c8702f54fc 100644
--- a/guides/source/layouts_and_rendering.md
+++ b/guides/source/layouts_and_rendering.md
@@ -1082,7 +1082,7 @@ definitions for several similar resources:
* `shared/_search_filters.html.erb`
```html+erb
- <%= form_for(@q) do |f| %>
+ <%= form_for(search) do |f| %>
<h1>Search form:</h1>
<fieldset>
<%= yield f %>
@@ -1280,7 +1280,7 @@ When rendering collections it is also possible to use the `:layout` option:
<%= render partial: "product", collection: @products, layout: "special_layout" %>
```
-The layout will be rendered together with the partial for each item in the collection. The current object and object_counter variables will be available in the layout as well, the same way they do within the partial.
+The layout will be rendered together with the partial for each item in the collection. The current object and object_counter variables will be available in the layout as well, the same way they are within the partial.
### Using Nested Layouts
diff --git a/guides/source/rails_on_rack.md b/guides/source/rails_on_rack.md
index ed935e1008..340933c7ee 100644
--- a/guides/source/rails_on_rack.md
+++ b/guides/source/rails_on_rack.md
@@ -181,7 +181,6 @@ $ bin/rails middleware
(in /Users/lifo/Rails/blog)
use ActionDispatch::Static
use #<ActiveSupport::Cache::Strategy::LocalCache::Middleware:0x00000001c304c8>
-use Rack::Runtime
...
run Rails.application.routes
```
diff --git a/guides/source/routing.md b/guides/source/routing.md
index 937e313663..86492a9332 100644
--- a/guides/source/routing.md
+++ b/guides/source/routing.md
@@ -603,6 +603,14 @@ get 'photos/:id', to: 'photos#show', defaults: { format: 'jpg' }
Rails would match `photos/12` to the `show` action of `PhotosController`, and set `params[:format]` to `"jpg"`.
+You can also use `defaults` in a block format to define the defaults for multiple items:
+
+```ruby
+defaults format: :json do
+ resources :photos
+end
+```
+
NOTE: You cannot override defaults via query parameters - this is for security reasons. The only defaults that can be overridden are dynamic segments via substitution in the URL path.
### Naming Routes
diff --git a/guides/source/security.md b/guides/source/security.md
index aea9728c10..a81a782cf2 100644
--- a/guides/source/security.md
+++ b/guides/source/security.md
@@ -377,7 +377,7 @@ In 2007 there was the first tailor-made trojan which stole information from an I
Having one single place in the admin interface or Intranet, where the input has not been sanitized, makes the entire application vulnerable. Possible exploits include stealing the privileged administrator's cookie, injecting an iframe to steal the administrator's password or installing malicious software through browser security holes to take over the administrator's computer.
-Refer to the Injection section for countermeasures against XSS. It is _recommended to use the SafeErb plugin_ also in an Intranet or administration interface.
+Refer to the Injection section for countermeasures against XSS.
**CSRF** Cross-Site Request Forgery (CSRF), also known as Cross-Site Reference Forgery (XSRF), is a gigantic attack method, it allows the attacker to do everything the administrator or Intranet user may do. As you have already seen above how CSRF works, here are a few examples of what attackers can do in the Intranet or admin interface.
@@ -762,7 +762,7 @@ s = sanitize(user_input, tags: tags, attributes: %w(href title))
This allows only the given tags and does a good job, even against all kinds of tricks and malformed tags.
-As a second step, _it is good practice to escape all output of the application_, especially when re-displaying user input, which hasn't been input-filtered (as in the search form example earlier on). _Use `escapeHTML()` (or its alias `h()`) method_ to replace the HTML input characters &amp;, &quot;, &lt;, and &gt; by their uninterpreted representations in HTML (`&amp;`, `&quot;`, `&lt;`, and `&gt;`). However, it can easily happen that the programmer forgets to use it, so _it is recommended to use the SafeErb gem. SafeErb reminds you to escape strings from external sources.
+As a second step, _it is good practice to escape all output of the application_, especially when re-displaying user input, which hasn't been input-filtered (as in the search form example earlier on). _Use `escapeHTML()` (or its alias `h()`) method_ to replace the HTML input characters &amp;, &quot;, &lt;, and &gt; by their uninterpreted representations in HTML (`&amp;`, `&quot;`, `&lt;`, and `&gt;`).
##### Obfuscation and Encoding Injection
diff --git a/guides/source/testing.md b/guides/source/testing.md
index 0ac5121b12..6f783089a9 100644
--- a/guides/source/testing.md
+++ b/guides/source/testing.md
@@ -322,7 +322,6 @@ specify to make your test failure messages clearer.
| `assert_not_operator( obj1, operator, [obj2], [msg] )` | Ensures that `obj1.operator(obj2)` is false.|
| `assert_predicate ( obj, predicate, [msg] )` | Ensures that `obj.predicate` is true, e.g. `assert_predicate str, :empty?`|
| `assert_not_predicate ( obj, predicate, [msg] )` | Ensures that `obj.predicate` is false, e.g. `assert_not_predicate str, :empty?`|
-| `assert_send( array, [msg] )` | Ensures that executing the method listed in `array[1]` on the object in `array[0]` with the parameters of `array[2 and up]` is true, e.g. assert_send [@user, :full_name, 'Sam Smith']. This one is weird eh?|
| `flunk( [msg] )` | Ensures failure. This is useful to explicitly mark a test that isn't finished yet.|
The above are a subset of assertions that minitest supports. For an exhaustive &
@@ -414,7 +413,7 @@ You can also run an entire directory of tests by providing the path to the direc
$ bin/rails test test/controllers # run all tests from specific directory
```
-The test runner provides lot of other features too like failing fast, deferring test output
+The test runner also provides a lot of other features like failing fast, deferring test output
at the end of test run and so on. Check the documentation of the test runner as follows:
```bash
@@ -800,6 +799,13 @@ end
Now you can try running all the tests and they should pass.
+NOTE: If you followed the steps in the Basic Authentication section, you'll need to add the following to the `setup` block to get all the tests passing:
+
+```ruby
+request.headers['Authorization'] = ActionController::HttpAuthentication::Basic.
+ encode_credentials('dhh', 'secret')
+```
+
### Available Request Types for Functional Tests
If you're familiar with the HTTP protocol, you'll know that `get` is a type of request. There are 6 request types supported in Rails functional tests:
@@ -859,7 +865,7 @@ You also have access to three instance variables in your functional tests, after
class ArticlesControllerTest < ActionDispatch::IntegrationTest
test "should get index" do
get articles_url
-
+
assert_equal "index", @controller.action_name
assert_equal "application/x-www-form-urlencoded", @request.media_type
assert_match "Articles", @response.body
diff --git a/guides/source/upgrading_ruby_on_rails.md b/guides/source/upgrading_ruby_on_rails.md
index 2372590cec..8a3b3b84b4 100644
--- a/guides/source/upgrading_ruby_on_rails.md
+++ b/guides/source/upgrading_ruby_on_rails.md
@@ -325,7 +325,7 @@ should support caching.
#### Configure the Output of `db:structure:dump`
-If you're using `schema_search_path` or other PostgreSQL extentions, you can control how the schema is
+If you're using `schema_search_path` or other PostgreSQL extensions, you can control how the schema is
dumped. Set to `:all` to generate all dumps, or to `:schema_search_path` to generate from schema search path.
config.active_record.dump_schemas = :all
@@ -1278,6 +1278,10 @@ Also check your environment settings for `config.action_dispatch.best_standards_
Rails 4.0 removes the `j` alias for `ERB::Util#json_escape` since `j` is already used for `ActionView::Helpers::JavaScriptHelper#escape_javascript`.
+#### Cache
+
+The caching method changed between Rails 3.x and 4.0. You should [change the cache namespace](http://guides.rubyonrails.org/caching_with_rails.html#activesupport-cache-store) and roll out with a cold cache.
+
### Helpers Loading Order
The order in which helpers from more than one directory are loaded has changed in Rails 4.0. Previously, they were gathered and then sorted alphabetically. After upgrading to Rails 4.0, helpers will preserve the order of loaded directories and will be sorted alphabetically only within each directory. Unless you explicitly use the `helpers_path` parameter, this change will only impact the way of loading helpers from engines. If you rely on the ordering, you should check if correct methods are available after upgrade. If you would like to change the order in which engines are loaded, you can use `config.railties_order=` method.
diff --git a/guides/w3c_validator.rb b/guides/w3c_validator.rb
index 2ce27e2e16..c0a32c6b91 100644
--- a/guides/w3c_validator.rb
+++ b/guides/w3c_validator.rb
@@ -81,7 +81,7 @@ module RailsGuides
error_summary += "\n #{name}"
error_detail += "\n\n #{name} has #{errors.size} validation error(s):\n"
errors.each do |error|
- error_detail += "\n "+error.to_s.delete("\n")
+ error_detail += "\n " + error.to_s.delete("\n")
end
end
diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md
index 524d85b8d5..d7d1a66863 100644
--- a/railties/CHANGELOG.md
+++ b/railties/CHANGELOG.md
@@ -2,6 +2,37 @@
*Yuji Yaginuma*
+* Add Webpack support in new apps via the --webpack option, which will delegate to the rails/webpacker gem.
+
+ To generate a new app that has Webpack dependencies configured and binstubs for webpack and webpack-watcher:
+
+ `rails new myapp --webpack`
+
+ To generate a new app that has Webpack + React configured and an example intalled:
+
+ `rails new myapp --webpack=react`
+
+ *DHH*
+
+* Add Yarn support in new apps with a yarn binstub and vendor/package.json. Skippable via --skip-yarn option.
+
+ *Liceth Ovalles*, *Guillermo Iguaran*, *DHH*
+
+* Removed jquery-rails from default stack, instead rails-ujs that is shipped
+ with Action View is included as default UJS adapter.
+
+ *Guillermo Iguaran*
+
+* The config file `secrets.yml` is now loaded in with all keys as symbols.
+ This allows secrets files to contain more complex information without all
+ child keys being strings while parent keys are symbols.
+
+ *Isaac Sloan*
+
+* Add `:skip_sprockets` to `Rails::PluginBuilder::PASSTHROUGH_OPTIONS`
+
+ *Tsukuru Tanimichi*
+
* Allow the use of listen's 3.1.x branch
*Esteban Santana Santana*
diff --git a/railties/Rakefile b/railties/Rakefile
index 202644fb26..680ed03f75 100644
--- a/railties/Rakefile
+++ b/railties/Rakefile
@@ -18,6 +18,7 @@ namespace :test do
"lib",
"#{File.dirname(__FILE__)}/../activesupport/lib",
"#{File.dirname(__FILE__)}/../actionpack/lib",
+ "#{File.dirname(__FILE__)}/../actionview/lib",
"#{File.dirname(__FILE__)}/../activemodel/lib"
]
ruby "-w", "-I#{dash_i.join ':'}", file
diff --git a/railties/lib/rails.rb b/railties/lib/rails.rb
index 5d862e3fec..ee48043a50 100644
--- a/railties/lib/rails.rb
+++ b/railties/lib/rails.rb
@@ -53,7 +53,7 @@ module Rails
end
# Returns a Pathname object of the current Rails project,
- # otherwise it returns nil if there is no project:
+ # otherwise it returns +nil+ if there is no project:
#
# Rails.root
# # => #<Pathname:/Users/someuser/some/path/project>
@@ -100,7 +100,7 @@ module Rails
end
# Returns a Pathname object of the public folder of the current
- # Rails project, otherwise it returns nil if there is no project:
+ # Rails project, otherwise it returns +nil+ if there is no project:
#
# Rails.public_path
# # => #<Pathname:/Users/someuser/some/path/project/public>
diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb
index b01196e3ed..f96432c89f 100644
--- a/railties/lib/rails/application.rb
+++ b/railties/lib/rails/application.rb
@@ -274,7 +274,7 @@ module Rails
# Sends the initializers to the +initializer+ method defined in the
# Rails::Initializable module. Each Rails::Application class has its own
# set of initializers, as defined by the Initializable module.
- def initializer(name, opts={}, &block)
+ def initializer(name, opts = {}, &block)
self.class.initializer(name, opts, &block)
end
@@ -347,7 +347,7 @@ module Rails
# Initialize the application passing the given group. By default, the
# group is :default
- def initialize!(group=:default) #:nodoc:
+ def initialize!(group = :default) #:nodoc:
raise "Application has been already initialized." if @initialized
run_initializers(group, self)
@initialized = true
@@ -394,8 +394,8 @@ module Rails
shared_secrets = all_secrets["shared"]
env_secrets = all_secrets[Rails.env]
- secrets.merge!(shared_secrets.symbolize_keys) if shared_secrets
- secrets.merge!(env_secrets.symbolize_keys) if env_secrets
+ secrets.merge!(shared_secrets.deep_symbolize_keys) if shared_secrets
+ secrets.merge!(env_secrets.deep_symbolize_keys) if env_secrets
end
# Fallback to config.secret_key_base if secrets.secret_key_base isn't set
diff --git a/railties/lib/rails/application_controller.rb b/railties/lib/rails/application_controller.rb
index f8394b8e0a..a98e51fd28 100644
--- a/railties/lib/rails/application_controller.rb
+++ b/railties/lib/rails/application_controller.rb
@@ -2,7 +2,7 @@ class Rails::ApplicationController < ActionController::Base # :nodoc:
self.view_paths = File.expand_path("../templates", __FILE__)
layout "application"
- protected
+ private
def require_local!
unless local_request?
diff --git a/railties/lib/rails/code_statistics.rb b/railties/lib/rails/code_statistics.rb
index b3d88147a5..9c4bd16aad 100644
--- a/railties/lib/rails/code_statistics.rb
+++ b/railties/lib/rails/code_statistics.rb
@@ -106,7 +106,7 @@ class CodeStatistics #:nodoc:
code = calculate_code
tests = calculate_tests
- puts " Code LOC: #{code} Test LOC: #{tests} Code to Test Ratio: 1:#{sprintf("%.1f", tests.to_f/code)}"
+ puts " Code LOC: #{code} Test LOC: #{tests} Code to Test Ratio: 1:#{sprintf("%.1f", tests.to_f / code)}"
puts ""
end
end
diff --git a/railties/lib/rails/command.rb b/railties/lib/rails/command.rb
index 6065e78fd1..ddb953543f 100644
--- a/railties/lib/rails/command.rb
+++ b/railties/lib/rails/command.rb
@@ -82,16 +82,16 @@ module Rails
[[ "rails", rails ]] + groups.sort.to_a
end
- protected
- def command_type
+ private
+ def command_type # :doc:
@command_type ||= "command"
end
- def lookup_paths
+ def lookup_paths # :doc:
@lookup_paths ||= %w( rails/commands commands )
end
- def file_lookup_paths
+ def file_lookup_paths # :doc:
@file_lookup_paths ||= [ "{#{lookup_paths.join(',')}}", "**", "*_command.rb" ]
end
end
diff --git a/railties/lib/rails/command/behavior.rb b/railties/lib/rails/command/behavior.rb
index ce994746a4..4a92f72f16 100644
--- a/railties/lib/rails/command/behavior.rb
+++ b/railties/lib/rails/command/behavior.rb
@@ -16,13 +16,13 @@ module Rails
@subclasses ||= []
end
- protected
+ private
# This code is based directly on the Text gem implementation.
# Copyright (c) 2006-2013 Paul Battley, Michael Neumann, Tim Fletcher.
#
# Returns a value representing the "cost" of transforming str1 into str2.
- def levenshtein_distance(str1, str2)
+ def levenshtein_distance(str1, str2) # :doc:
s = str1
t = str2
n = s.length
@@ -38,12 +38,12 @@ module Rails
str2_codepoint_enumerable = str2.each_codepoint
str1.each_codepoint.with_index do |char1, i|
- e = i+1
+ e = i + 1
str2_codepoint_enumerable.with_index do |char2, j|
cost = (char1 == char2) ? 0 : 1
x = [
- d[j+1] + 1, # insertion
+ d[j + 1] + 1, # insertion
e + 1, # deletion
d[j] + cost # substitution
].min
@@ -58,7 +58,7 @@ module Rails
end
# Prints a list of generators.
- def print_list(base, namespaces) #:nodoc:
+ def print_list(base, namespaces)
return if namespaces.empty?
puts "#{base.camelize}:"
@@ -71,7 +71,7 @@ module Rails
# Receives namespaces in an array and tries to find matching generators
# in the load path.
- def lookup(namespaces) #:nodoc:
+ def lookup(namespaces)
paths = namespaces_to_paths(namespaces)
paths.each do |raw_path|
@@ -91,7 +91,7 @@ module Rails
end
# This will try to load any command in the load path to show in help.
- def lookup! #:nodoc:
+ def lookup!
$LOAD_PATH.each do |base|
Dir[File.join(base, *file_lookup_paths)].each do |path|
begin
@@ -107,7 +107,7 @@ module Rails
# Convert namespaces to paths by replacing ":" for "/" and adding
# an extra lookup. For example, "rails:model" should be searched
# in both: "rails/model/model_generator" and "rails/model_generator".
- def namespaces_to_paths(namespaces) #:nodoc:
+ def namespaces_to_paths(namespaces)
paths = []
namespaces.each do |namespace|
pieces = namespace.split(":")
diff --git a/railties/lib/rails/commands/application/application_command.rb b/railties/lib/rails/commands/application/application_command.rb
index 7e3a2b011d..7675d3b3d1 100644
--- a/railties/lib/rails/commands/application/application_command.rb
+++ b/railties/lib/rails/commands/application/application_command.rb
@@ -13,7 +13,7 @@ module Rails
end
module Command
- class ApplicationCommand < Base
+ class ApplicationCommand < Base # :nodoc:
hide_command!
def help
diff --git a/railties/lib/rails/commands/console/console_command.rb b/railties/lib/rails/commands/console/console_command.rb
index 617066f575..62e3aa19df 100644
--- a/railties/lib/rails/commands/console/console_command.rb
+++ b/railties/lib/rails/commands/console/console_command.rb
@@ -64,7 +64,7 @@ module Rails
end
module Command
- class ConsoleCommand < Base
+ class ConsoleCommand < Base # :nodoc:
include EnvironmentArgument
class_option :sandbox, aliases: "-s", type: :boolean, default: false,
diff --git a/railties/lib/rails/commands/dbconsole/dbconsole_command.rb b/railties/lib/rails/commands/dbconsole/dbconsole_command.rb
index d3c80da89b..588fb06b15 100644
--- a/railties/lib/rails/commands/dbconsole/dbconsole_command.rb
+++ b/railties/lib/rails/commands/dbconsole/dbconsole_command.rb
@@ -1,6 +1,3 @@
-require "erb"
-require "yaml"
-
require "rails/command/environment_argument"
module Rails
@@ -102,14 +99,14 @@ module Rails
Rails.respond_to?(:env) ? Rails.env : Rails::Command.environment
end
- protected
- def configurations
+ private
+ def configurations # :doc:
require APP_PATH
ActiveRecord::Base.configurations = Rails.application.config.database_configuration
ActiveRecord::Base.configurations
end
- def find_cmd_and_exec(commands, *args)
+ def find_cmd_and_exec(commands, *args) # :doc:
commands = Array(commands)
dirs_on_path = ENV["PATH"].to_s.split(File::PATH_SEPARATOR)
@@ -134,7 +131,7 @@ module Rails
end
module Command
- class DbconsoleCommand < Base
+ class DbconsoleCommand < Base # :nodoc:
include EnvironmentArgument
class_option :include_password, aliases: "-p", type: :boolean,
diff --git a/railties/lib/rails/commands/destroy/destroy_command.rb b/railties/lib/rails/commands/destroy/destroy_command.rb
index 5e6b7f9371..5b552b2070 100644
--- a/railties/lib/rails/commands/destroy/destroy_command.rb
+++ b/railties/lib/rails/commands/destroy/destroy_command.rb
@@ -2,8 +2,8 @@ require "rails/generators"
module Rails
module Command
- class DestroyCommand < Base
- def help # :nodoc:
+ class DestroyCommand < Base # :nodoc:
+ def help
Rails::Generators.help self.class.command_name
end
diff --git a/railties/lib/rails/commands/generate/generate_command.rb b/railties/lib/rails/commands/generate/generate_command.rb
index b381ca85b9..aa8dab71b0 100644
--- a/railties/lib/rails/commands/generate/generate_command.rb
+++ b/railties/lib/rails/commands/generate/generate_command.rb
@@ -2,8 +2,8 @@ require "rails/generators"
module Rails
module Command
- class GenerateCommand < Base
- def help # :nodoc:
+ class GenerateCommand < Base # :nodoc:
+ def help
Rails::Generators.help self.class.command_name
end
@@ -14,6 +14,8 @@ module Rails
require_application_and_environment!
load_generators
+ ARGV.shift
+
Rails::Generators.invoke generator, args, behavior: :invoke, destination_root: Rails::Command.root
end
end
diff --git a/railties/lib/rails/commands/help/help_command.rb b/railties/lib/rails/commands/help/help_command.rb
index 5bcc4c8eee..90d37217fc 100644
--- a/railties/lib/rails/commands/help/help_command.rb
+++ b/railties/lib/rails/commands/help/help_command.rb
@@ -1,6 +1,6 @@
module Rails
module Command
- class HelpCommand < Base
+ class HelpCommand < Base # :nodoc:
hide_command!
def help(*)
diff --git a/railties/lib/rails/commands/new/new_command.rb b/railties/lib/rails/commands/new/new_command.rb
index 13eedfc479..74d1fa5021 100644
--- a/railties/lib/rails/commands/new/new_command.rb
+++ b/railties/lib/rails/commands/new/new_command.rb
@@ -1,6 +1,6 @@
module Rails
module Command
- class NewCommand < Base
+ class NewCommand < Base # :nodoc:
def help
Rails::Command.invoke :application, [ "--help" ]
end
diff --git a/railties/lib/rails/commands/plugin/plugin_command.rb b/railties/lib/rails/commands/plugin/plugin_command.rb
index d6d9fe4400..b40ab006af 100644
--- a/railties/lib/rails/commands/plugin/plugin_command.rb
+++ b/railties/lib/rails/commands/plugin/plugin_command.rb
@@ -1,6 +1,6 @@
module Rails
module Command
- class PluginCommand < Base
+ class PluginCommand < Base # :nodoc:
hide_command!
def help
@@ -11,7 +11,7 @@ module Rails
"#{executable} new [options]"
end
- class_option :rc, type: :boolean, default: File.join("~", ".railsrc"),
+ class_option :rc, type: :string, default: File.join("~", ".railsrc"),
desc: "Initialize the plugin command with previous defaults. Uses .railsrc in your home directory by default."
class_option :no_rc, desc: "Skip evaluating .railsrc."
diff --git a/railties/lib/rails/commands/rake/rake_command.rb b/railties/lib/rails/commands/rake/rake_command.rb
index a43c884170..f03dc81117 100644
--- a/railties/lib/rails/commands/rake/rake_command.rb
+++ b/railties/lib/rails/commands/rake/rake_command.rb
@@ -1,6 +1,6 @@
module Rails
module Command
- class RakeCommand < Base
+ class RakeCommand < Base # :nodoc:
extend Rails::Command::Actions
namespace "rake"
diff --git a/railties/lib/rails/commands/runner/USAGE b/railties/lib/rails/commands/runner/USAGE
index dc47a35ff3..b2a6e8493d 100644
--- a/railties/lib/rails/commands/runner/USAGE
+++ b/railties/lib/rails/commands/runner/USAGE
@@ -8,7 +8,7 @@ Run the Ruby file located at `path/to/filename.rb` after loading the app:
<%= executable %> path/to/filename.rb
-<% if RbConfig::CONFIG['host_os'] !~ /mswin|mingw/ %>
+<% unless Gem.win_platform? %>
You can also use the runner command as a shebang line for your executables:
#!/usr/bin/env <%= File.expand_path(executable) %>
diff --git a/railties/lib/rails/commands/runner/runner_command.rb b/railties/lib/rails/commands/runner/runner_command.rb
index 8db6da8759..4989a7837d 100644
--- a/railties/lib/rails/commands/runner/runner_command.rb
+++ b/railties/lib/rails/commands/runner/runner_command.rb
@@ -1,6 +1,6 @@
module Rails
module Command
- class RunnerCommand < Base
+ class RunnerCommand < Base # :nodoc:
class_option :environment, aliases: "-e", type: :string,
default: Rails::Command.environment.dup,
desc: "The environment for the runner to operate under (test/development/production)"
@@ -14,7 +14,7 @@ module Rails
"#{super} [<'Some.ruby(code)'> | <filename.rb>]"
end
- def perform(code_or_file = nil)
+ def perform(code_or_file = nil, *file_argv)
unless code_or_file
help
exit 1
@@ -27,6 +27,7 @@ module Rails
if File.exist?(code_or_file)
$0 = code_or_file
+ ARGV.replace(file_argv)
Kernel.load code_or_file
else
begin
diff --git a/railties/lib/rails/commands/server/server_command.rb b/railties/lib/rails/commands/server/server_command.rb
index 14cf72f483..e9538b804c 100644
--- a/railties/lib/rails/commands/server/server_command.rb
+++ b/railties/lib/rails/commands/server/server_command.rb
@@ -139,8 +139,8 @@ module Rails
end
module Command
- class ServerCommand < Base
- def help # :nodoc:
+ class ServerCommand < Base # :nodoc:
+ def help
puts Rails::Server::Options.new.option_parser(Hash.new)
end
diff --git a/railties/lib/rails/commands/test/test_command.rb b/railties/lib/rails/commands/test/test_command.rb
index 1b2e3af9cc..7bf8f61137 100644
--- a/railties/lib/rails/commands/test/test_command.rb
+++ b/railties/lib/rails/commands/test/test_command.rb
@@ -3,8 +3,8 @@ require "rails/test_unit/minitest_plugin"
module Rails
module Command
- class TestCommand < Base
- def help # :nodoc:
+ class TestCommand < Base # :nodoc:
+ def help
perform # Hand over help printing to minitest.
end
diff --git a/railties/lib/rails/commands/version/version_command.rb b/railties/lib/rails/commands/version/version_command.rb
index 4f3fbfca1b..ac745594ee 100644
--- a/railties/lib/rails/commands/version/version_command.rb
+++ b/railties/lib/rails/commands/version/version_command.rb
@@ -1,6 +1,6 @@
module Rails
module Command
- class VersionCommand < Base
+ class VersionCommand < Base # :nodoc:
def perform
Rails::Command.invoke :application, [ "--version" ]
end
diff --git a/railties/lib/rails/configuration.rb b/railties/lib/rails/configuration.rb
index 7dfab969e8..fc7d4909f6 100644
--- a/railties/lib/rails/configuration.rb
+++ b/railties/lib/rails/configuration.rb
@@ -91,8 +91,8 @@ module Rails
attr_reader :hidden_namespaces
def initialize
- @aliases = Hash.new { |h,k| h[k] = {} }
- @options = Hash.new { |h,k| h[k] = {} }
+ @aliases = Hash.new { |h, k| h[k] = {} }
+ @options = Hash.new { |h, k| h[k] = {} }
@fallbacks = {}
@templates = []
@colorize_logging = true
diff --git a/railties/lib/rails/console/app.rb b/railties/lib/rails/console/app.rb
index 541d5e3dad..affadc8e09 100644
--- a/railties/lib/rails/console/app.rb
+++ b/railties/lib/rails/console/app.rb
@@ -5,7 +5,7 @@ module Rails
module ConsoleMethods
# reference the global "app" instance, created on demand. To recreate the
# instance, pass a non-false value as the parameter.
- def app(create=false)
+ def app(create = false)
@app_integration_instance = nil if create
@app_integration_instance ||= new_session do |sess|
sess.host! "www.example.com"
@@ -27,7 +27,7 @@ module Rails
end
# reloads the environment
- def reload!(print=true)
+ def reload!(print = true)
puts "Reloading..." if print
Rails.application.reloader.reload!
true
diff --git a/railties/lib/rails/engine.rb b/railties/lib/rails/engine.rb
index 90e7c04d7c..2dd1fb3273 100644
--- a/railties/lib/rails/engine.rb
+++ b/railties/lib/rails/engine.rb
@@ -436,7 +436,7 @@ module Rails
# Load console and invoke the registered hooks.
# Check <tt>Rails::Railtie.console</tt> for more info.
- def load_console(app=self)
+ def load_console(app = self)
require "rails/console/app"
require "rails/console/helpers"
run_console_blocks(app)
@@ -445,14 +445,14 @@ module Rails
# Load Rails runner and invoke the registered hooks.
# Check <tt>Rails::Railtie.runner</tt> for more info.
- def load_runner(app=self)
+ def load_runner(app = self)
run_runner_blocks(app)
self
end
# Load Rake, railties tasks and invoke the registered hooks.
# Check <tt>Rails::Railtie.rake_tasks</tt> for more info.
- def load_tasks(app=self)
+ def load_tasks(app = self)
require "rake"
run_tasks_blocks(app)
self
@@ -460,7 +460,7 @@ module Rails
# Load Rails generators and invoke the registered hooks.
# Check <tt>Rails::Railtie.generators</tt> for more info.
- def load_generators(app=self)
+ def load_generators(app = self)
require "rails/generators"
run_generators_blocks(app)
Rails::Generators.configure!(app.config.generators)
@@ -643,22 +643,24 @@ module Rails
protected
- def load_config_initializer(initializer)
- ActiveSupport::Notifications.instrument("load_config_initializer.railties", initializer: initializer) do
- load(initializer)
- end
- end
-
def run_tasks_blocks(*) #:nodoc:
super
paths["lib/tasks"].existent.sort.each { |ext| load(ext) }
end
- def has_migrations? #:nodoc:
+ private
+
+ def load_config_initializer(initializer) # :doc:
+ ActiveSupport::Notifications.instrument("load_config_initializer.railties", initializer: initializer) do
+ load(initializer)
+ end
+ end
+
+ def has_migrations?
paths["db/migrate"].existent.any?
end
- def self.find_root_with_flag(flag, root_path, default=nil) #:nodoc:
+ def self.find_root_with_flag(flag, root_path, default = nil) #:nodoc:
while root_path && File.directory?(root_path) && !File.exist?("#{root_path}/#{flag}")
parent = File.dirname(root_path)
@@ -671,24 +673,22 @@ module Rails
Pathname.new File.realpath root
end
- def default_middleware_stack #:nodoc:
+ def default_middleware_stack
ActionDispatch::MiddlewareStack.new
end
- def _all_autoload_once_paths #:nodoc:
+ def _all_autoload_once_paths
config.autoload_once_paths
end
- def _all_autoload_paths #:nodoc:
+ def _all_autoload_paths
@_all_autoload_paths ||= (config.autoload_paths + config.eager_load_paths + config.autoload_once_paths).uniq
end
- def _all_load_paths #:nodoc:
+ def _all_load_paths
@_all_load_paths ||= (config.paths.load_paths + _all_autoload_paths).uniq
end
- private
-
def build_request(env)
env.merge!(env_config)
req = ActionDispatch::Request.new env
diff --git a/railties/lib/rails/engine/configuration.rb b/railties/lib/rails/engine/configuration.rb
index 147b904679..0c40173c38 100644
--- a/railties/lib/rails/engine/configuration.rb
+++ b/railties/lib/rails/engine/configuration.rb
@@ -7,7 +7,7 @@ module Rails
attr_accessor :middleware
attr_writer :eager_load_paths, :autoload_once_paths, :autoload_paths
- def initialize(root=nil)
+ def initialize(root = nil)
super()
@root = root
@generators = app_generators.dup
diff --git a/railties/lib/rails/generators.rb b/railties/lib/rails/generators.rb
index dd16b44786..e1980a42ad 100644
--- a/railties/lib/rails/generators.rb
+++ b/railties/lib/rails/generators.rb
@@ -67,246 +67,247 @@ module Rails
}
}
- def self.configure!(config) #:nodoc:
- api_only! if config.api_only
- no_color! unless config.colorize_logging
- aliases.deep_merge! config.aliases
- options.deep_merge! config.options
- fallbacks.merge! config.fallbacks
- templates_path.concat config.templates
- templates_path.uniq!
- hide_namespaces(*config.hidden_namespaces)
- end
+ class << self
+ def configure!(config) #:nodoc:
+ api_only! if config.api_only
+ no_color! unless config.colorize_logging
+ aliases.deep_merge! config.aliases
+ options.deep_merge! config.options
+ fallbacks.merge! config.fallbacks
+ templates_path.concat config.templates
+ templates_path.uniq!
+ hide_namespaces(*config.hidden_namespaces)
+ end
- def self.templates_path #:nodoc:
- @templates_path ||= []
- end
+ def templates_path #:nodoc:
+ @templates_path ||= []
+ end
- def self.aliases #:nodoc:
- @aliases ||= DEFAULT_ALIASES.dup
- end
+ def aliases #:nodoc:
+ @aliases ||= DEFAULT_ALIASES.dup
+ end
- def self.options #:nodoc:
- @options ||= DEFAULT_OPTIONS.dup
- end
+ def options #:nodoc:
+ @options ||= DEFAULT_OPTIONS.dup
+ end
- # Hold configured generators fallbacks. If a plugin developer wants a
- # generator group to fallback to another group in case of missing generators,
- # they can add a fallback.
- #
- # For example, shoulda is considered a test_framework and is an extension
- # of test_unit. However, most part of shoulda generators are similar to
- # test_unit ones.
- #
- # Shoulda then can tell generators to search for test_unit generators when
- # some of them are not available by adding a fallback:
- #
- # Rails::Generators.fallbacks[:shoulda] = :test_unit
- def self.fallbacks
- @fallbacks ||= {}
- end
+ # Hold configured generators fallbacks. If a plugin developer wants a
+ # generator group to fallback to another group in case of missing generators,
+ # they can add a fallback.
+ #
+ # For example, shoulda is considered a test_framework and is an extension
+ # of test_unit. However, most part of shoulda generators are similar to
+ # test_unit ones.
+ #
+ # Shoulda then can tell generators to search for test_unit generators when
+ # some of them are not available by adding a fallback:
+ #
+ # Rails::Generators.fallbacks[:shoulda] = :test_unit
+ def fallbacks
+ @fallbacks ||= {}
+ end
- # Configure generators for API only applications. It basically hides
- # everything that is usually browser related, such as assets and session
- # migration generators, and completely disable helpers and assets
- # so generators such as scaffold won't create them.
- def self.api_only!
- hide_namespaces "assets", "helper", "css", "js"
-
- options[:rails].merge!(
- api: true,
- assets: false,
- helper: false,
- template_engine: nil
- )
-
- if ARGV.first == "mailer"
- options[:rails].merge!(template_engine: :erb)
+ # Configure generators for API only applications. It basically hides
+ # everything that is usually browser related, such as assets and session
+ # migration generators, and completely disable helpers and assets
+ # so generators such as scaffold won't create them.
+ def api_only!
+ hide_namespaces "assets", "helper", "css", "js"
+
+ options[:rails].merge!(
+ api: true,
+ assets: false,
+ helper: false,
+ template_engine: nil
+ )
+
+ if ARGV.first == "mailer"
+ options[:rails].merge!(template_engine: :erb)
+ end
end
- end
- # Remove the color from output.
- def self.no_color!
- Thor::Base.shell = Thor::Shell::Basic
- end
+ # Remove the color from output.
+ def no_color!
+ Thor::Base.shell = Thor::Shell::Basic
+ end
- # Returns an array of generator namespaces that are hidden.
- # Generator namespaces may be hidden for a variety of reasons.
- # Some are aliased such as "rails:migration" and can be
- # invoked with the shorter "migration", others are private to other generators
- # such as "css:scaffold".
- def self.hidden_namespaces
- @hidden_namespaces ||= begin
- orm = options[:rails][:orm]
- test = options[:rails][:test_framework]
- template = options[:rails][:template_engine]
- css = options[:rails][:stylesheet_engine]
-
- [
- "rails",
- "resource_route",
- "#{orm}:migration",
- "#{orm}:model",
- "#{test}:controller",
- "#{test}:helper",
- "#{test}:integration",
- "#{test}:mailer",
- "#{test}:model",
- "#{test}:scaffold",
- "#{test}:view",
- "#{test}:job",
- "#{template}:controller",
- "#{template}:scaffold",
- "#{template}:mailer",
- "#{css}:scaffold",
- "#{css}:assets",
- "css:assets",
- "css:scaffold"
- ]
+ # Returns an array of generator namespaces that are hidden.
+ # Generator namespaces may be hidden for a variety of reasons.
+ # Some are aliased such as "rails:migration" and can be
+ # invoked with the shorter "migration", others are private to other generators
+ # such as "css:scaffold".
+ def hidden_namespaces
+ @hidden_namespaces ||= begin
+ orm = options[:rails][:orm]
+ test = options[:rails][:test_framework]
+ template = options[:rails][:template_engine]
+ css = options[:rails][:stylesheet_engine]
+
+ [
+ "rails",
+ "resource_route",
+ "#{orm}:migration",
+ "#{orm}:model",
+ "#{test}:controller",
+ "#{test}:helper",
+ "#{test}:integration",
+ "#{test}:mailer",
+ "#{test}:model",
+ "#{test}:scaffold",
+ "#{test}:view",
+ "#{test}:job",
+ "#{template}:controller",
+ "#{template}:scaffold",
+ "#{template}:mailer",
+ "#{css}:scaffold",
+ "#{css}:assets",
+ "css:assets",
+ "css:scaffold"
+ ]
+ end
end
- end
- class << self
def hide_namespaces(*namespaces)
hidden_namespaces.concat(namespaces)
end
alias hide_namespace hide_namespaces
- end
- # Show help message with available generators.
- def self.help(command = "generate")
- puts "Usage: rails #{command} GENERATOR [args] [options]"
- puts
- puts "General options:"
- puts " -h, [--help] # Print generator's options and usage"
- puts " -p, [--pretend] # Run but do not make any changes"
- puts " -f, [--force] # Overwrite files that already exist"
- puts " -s, [--skip] # Skip files that already exist"
- puts " -q, [--quiet] # Suppress status output"
- puts
- puts "Please choose a generator below."
- puts
-
- print_generators
- end
-
- def self.public_namespaces
- lookup!
- subclasses.map(&:namespace)
- end
-
- def self.print_generators
- sorted_groups.each { |b, n| print_list(b, n) }
- end
+ # Show help message with available generators.
+ def help(command = "generate")
+ puts "Usage: rails #{command} GENERATOR [args] [options]"
+ puts
+ puts "General options:"
+ puts " -h, [--help] # Print generator's options and usage"
+ puts " -p, [--pretend] # Run but do not make any changes"
+ puts " -f, [--force] # Overwrite files that already exist"
+ puts " -s, [--skip] # Skip files that already exist"
+ puts " -q, [--quiet] # Suppress status output"
+ puts
+ puts "Please choose a generator below."
+ puts
+
+ print_generators
+ end
- def self.sorted_groups
- namespaces = public_namespaces
- namespaces.sort!
+ def public_namespaces
+ lookup!
+ subclasses.map(&:namespace)
+ end
- groups = Hash.new { |h,k| h[k] = [] }
- namespaces.each do |namespace|
- base = namespace.split(":").first
- groups[base] << namespace
+ def print_generators
+ sorted_groups.each { |b, n| print_list(b, n) }
end
- rails = groups.delete("rails")
- rails.map! { |n| n.sub(/^rails:/, "") }
- rails.delete("app")
- rails.delete("plugin")
+ def sorted_groups
+ namespaces = public_namespaces
+ namespaces.sort!
- hidden_namespaces.each { |n| groups.delete(n.to_s) }
+ groups = Hash.new { |h, k| h[k] = [] }
+ namespaces.each do |namespace|
+ base = namespace.split(":").first
+ groups[base] << namespace
+ end
- [[ "rails", rails ]] + groups.sort.to_a
- end
+ rails = groups.delete("rails")
+ rails.map! { |n| n.sub(/^rails:/, "") }
+ rails.delete("app")
+ rails.delete("plugin")
- # Rails finds namespaces similar to thor, it only adds one rule:
- #
- # Generators names must end with "_generator.rb". This is required because Rails
- # looks in load paths and loads the generator just before it's going to be used.
- #
- # find_by_namespace :webrat, :rails, :integration
- #
- # Will search for the following generators:
- #
- # "rails:webrat", "webrat:integration", "webrat"
- #
- # Notice that "rails:generators:webrat" could be loaded as well, what
- # Rails looks for is the first and last parts of the namespace.
- def self.find_by_namespace(name, base = nil, context = nil) #:nodoc:
- lookups = []
- lookups << "#{base}:#{name}" if base
- lookups << "#{name}:#{context}" if context
-
- unless base || context
- unless name.to_s.include?(?:)
- lookups << "#{name}:#{name}"
- lookups << "rails:#{name}"
- end
- lookups << "#{name}"
+ hidden_namespaces.each { |n| groups.delete(n.to_s) }
+
+ [[ "rails", rails ]] + groups.sort.to_a
end
- lookup(lookups)
+ # Rails finds namespaces similar to thor, it only adds one rule:
+ #
+ # Generators names must end with "_generator.rb". This is required because Rails
+ # looks in load paths and loads the generator just before it's going to be used.
+ #
+ # find_by_namespace :webrat, :rails, :integration
+ #
+ # Will search for the following generators:
+ #
+ # "rails:webrat", "webrat:integration", "webrat"
+ #
+ # Notice that "rails:generators:webrat" could be loaded as well, what
+ # Rails looks for is the first and last parts of the namespace.
+ def find_by_namespace(name, base = nil, context = nil) #:nodoc:
+ lookups = []
+ lookups << "#{base}:#{name}" if base
+ lookups << "#{name}:#{context}" if context
+
+ unless base || context
+ unless name.to_s.include?(?:)
+ lookups << "#{name}:#{name}"
+ lookups << "rails:#{name}"
+ end
+ lookups << "#{name}"
+ end
- namespaces = Hash[subclasses.map { |klass| [klass.namespace, klass] }]
- lookups.each do |namespace|
+ lookup(lookups)
- klass = namespaces[namespace]
- return klass if klass
- end
+ namespaces = Hash[subclasses.map { |klass| [klass.namespace, klass] }]
+ lookups.each do |namespace|
- invoke_fallbacks_for(name, base) || invoke_fallbacks_for(context, name)
- end
+ klass = namespaces[namespace]
+ return klass if klass
+ end
- # Receives a namespace, arguments and the behavior to invoke the generator.
- # It's used as the default entry point for generate, destroy and update
- # commands.
- def self.invoke(namespace, args=ARGV, config={})
- names = namespace.to_s.split(":")
- if klass = find_by_namespace(names.pop, names.any? && names.join(":"))
- args << "--help" if args.empty? && klass.arguments.any?(&:required?)
- klass.start(args, config)
- else
- options = sorted_groups.flat_map(&:last)
- suggestions = options.sort_by { |suggested| levenshtein_distance(namespace.to_s, suggested) }.first(3)
- msg = "Could not find generator '#{namespace}'. "
- msg << "Maybe you meant #{ suggestions.map { |s| "'#{s}'" }.to_sentence(last_word_connector: " or ", locale: :en) }\n"
- msg << "Run `rails generate --help` for more options."
- puts msg
+ invoke_fallbacks_for(name, base) || invoke_fallbacks_for(context, name)
end
- end
- protected
- def self.print_list(base, namespaces)
- namespaces = namespaces.reject { |n| hidden_namespaces.include?(n) }
- super
+ # Receives a namespace, arguments and the behavior to invoke the generator.
+ # It's used as the default entry point for generate, destroy and update
+ # commands.
+ def invoke(namespace, args = ARGV, config = {})
+ names = namespace.to_s.split(":")
+ if klass = find_by_namespace(names.pop, names.any? && names.join(":"))
+ args << "--help" if args.empty? && klass.arguments.any?(&:required?)
+ klass.start(args, config)
+ else
+ options = sorted_groups.flat_map(&:last)
+ suggestions = options.sort_by { |suggested| levenshtein_distance(namespace.to_s, suggested) }.first(3)
+ msg = "Could not find generator '#{namespace}'. "
+ msg << "Maybe you meant #{ suggestions.map { |s| "'#{s}'" }.to_sentence(last_word_connector: " or ", locale: :en) }\n"
+ msg << "Run `rails generate --help` for more options."
+ puts msg
+ end
end
- # Try fallbacks for the given base.
- def self.invoke_fallbacks_for(name, base) #:nodoc:
- return nil unless base && fallbacks[base.to_sym]
- invoked_fallbacks = []
-
- Array(fallbacks[base.to_sym]).each do |fallback|
- next if invoked_fallbacks.include?(fallback)
- invoked_fallbacks << fallback
+ private
- klass = find_by_namespace(name, fallback)
- return klass if klass
+ def print_list(base, namespaces) # :doc:
+ namespaces = namespaces.reject { |n| hidden_namespaces.include?(n) }
+ super
end
- nil
- end
+ # Try fallbacks for the given base.
+ def invoke_fallbacks_for(name, base)
+ return nil unless base && fallbacks[base.to_sym]
+ invoked_fallbacks = []
- def self.command_type
- @command_type ||= "generator"
- end
+ Array(fallbacks[base.to_sym]).each do |fallback|
+ next if invoked_fallbacks.include?(fallback)
+ invoked_fallbacks << fallback
- def self.lookup_paths
- @lookup_paths ||= %w( rails/generators generators )
- end
+ klass = find_by_namespace(name, fallback)
+ return klass if klass
+ end
- def self.file_lookup_paths
- @file_lookup_paths ||= [ "{#{lookup_paths.join(',')}}", "**", "*_generator.rb" ]
- end
+ nil
+ end
+
+ def command_type # :doc:
+ @command_type ||= "generator"
+ end
+
+ def lookup_paths # :doc:
+ @lookup_paths ||= %w( rails/generators generators )
+ end
+
+ def file_lookup_paths # :doc:
+ @file_lookup_paths ||= [ "{#{lookup_paths.join(',')}}", "**", "*_generator.rb" ]
+ end
+ end
end
end
diff --git a/railties/lib/rails/generators/actions.rb b/railties/lib/rails/generators/actions.rb
index ab9dc019e2..d058d82cea 100644
--- a/railties/lib/rails/generators/actions.rb
+++ b/railties/lib/rails/generators/actions.rb
@@ -68,7 +68,7 @@ module Rails
# add_source "http://gems.github.com/" do
# gem "rspec-rails"
# end
- def add_source(source, options={}, &block)
+ def add_source(source, options = {}, &block)
log :source, source
in_root do
@@ -96,7 +96,7 @@ module Rails
# environment(nil, env: "development") do
# "config.action_controller.asset_host = 'localhost:3000'"
# end
- def environment(data=nil, options={})
+ def environment(data = nil, options = {})
sentinel = /class [a-z_:]+ < Rails::Application/i
env_file_sentinel = /Rails\.application\.configure do/
data = yield if !data && block_given?
@@ -118,7 +118,7 @@ module Rails
# git :init
# git add: "this.file that.rb"
# git add: "onefile.rb", rm: "badfile.cxx"
- def git(commands={})
+ def git(commands = {})
if commands.is_a?(Symbol)
run "git #{commands}"
else
@@ -137,7 +137,7 @@ module Rails
# end
#
# vendor("foreign.rb", "# Foreign code is fun")
- def vendor(filename, data=nil, &block)
+ def vendor(filename, data = nil, &block)
log :vendor, filename
create_file("vendor/#{filename}", data, verbose: false, &block)
end
@@ -150,7 +150,7 @@ module Rails
# end
#
# lib("foreign.rb", "# Foreign code is fun")
- def lib(filename, data=nil, &block)
+ def lib(filename, data = nil, &block)
log :lib, filename
create_file("lib/#{filename}", data, verbose: false, &block)
end
@@ -170,7 +170,7 @@ module Rails
# end
#
# rakefile('seed.rake', 'puts "Planting seeds"')
- def rakefile(filename, data=nil, &block)
+ def rakefile(filename, data = nil, &block)
log :rakefile, filename
create_file("lib/tasks/#{filename}", data, verbose: false, &block)
end
@@ -188,7 +188,7 @@ module Rails
# end
#
# initializer("api.rb", "API_KEY = '123456'")
- def initializer(filename, data=nil, &block)
+ def initializer(filename, data = nil, &block)
log :initializer, filename
create_file("config/initializers/#{filename}", data, verbose: false, &block)
end
@@ -210,7 +210,7 @@ module Rails
# rake("db:migrate")
# rake("db:migrate", env: "production")
# rake("gems:install", sudo: true)
- def rake(command, options={})
+ def rake(command, options = {})
execute_command :rake, command, options
end
@@ -219,7 +219,7 @@ module Rails
# rails("db:migrate")
# rails("db:migrate", env: "production")
# rails("gems:install", sudo: true)
- def rails_command(command, options={})
+ def rails_command(command, options = {})
execute_command :rails, command, options
end
@@ -260,12 +260,12 @@ module Rails
@after_bundle_callbacks << block
end
- protected
+ private
# Define log for backwards compatibility. If just one argument is sent,
# invoke say, otherwise invoke say_status. Differently from say and
# similarly to say_status, this method respects the quiet? option given.
- def log(*args)
+ def log(*args) # :doc:
if args.size == 1
say args.first.to_s unless options.quiet?
else
@@ -276,16 +276,16 @@ module Rails
# Runs the supplied command using either "rake ..." or "rails ..."
# based on the executor parameter provided.
- def execute_command(executor, command, options={})
+ def execute_command(executor, command, options = {}) # :doc:
log executor, command
env = options[:env] || ENV["RAILS_ENV"] || "development"
- sudo = options[:sudo] && RbConfig::CONFIG["host_os"] !~ /mswin|mingw/ ? "sudo " : ""
+ sudo = options[:sudo] && !Gem.win_platform? ? "sudo " : ""
in_root { run("#{sudo}#{extify(executor)} #{command} RAILS_ENV=#{env}", verbose: false) }
end
# Add an extension to the given name based on the platform.
- def extify(name)
- if RbConfig::CONFIG["host_os"] =~ /mswin|mingw/
+ def extify(name) # :doc:
+ if Gem.win_platform?
"#{name}.bat"
else
name
@@ -294,7 +294,7 @@ module Rails
# Surround string with single quotes if there is no quotes.
# Otherwise fall back to double quotes
- def quote(value)
+ def quote(value) # :doc:
return value.inspect unless value.is_a? String
if value.include?("'")
diff --git a/railties/lib/rails/generators/actions/create_migration.rb b/railties/lib/rails/generators/actions/create_migration.rb
index 587c61fd42..f677e545e5 100644
--- a/railties/lib/rails/generators/actions/create_migration.rb
+++ b/railties/lib/rails/generators/actions/create_migration.rb
@@ -37,9 +37,9 @@ module Rails
end
alias :exists? :existing_migration
- protected
+ private
- def on_conflict_behavior
+ def on_conflict_behavior # :doc:
options = base.options.merge(config)
if identical?
say_status :identical, :blue, relative_existing_migration
@@ -60,7 +60,7 @@ module Rails
end
end
- def say_status(status, color, message = relative_destination)
+ def say_status(status, color, message = relative_destination) # :doc:
base.shell.say_status(status, message, color) if config[:verbose]
end
end
diff --git a/railties/lib/rails/generators/active_model.rb b/railties/lib/rails/generators/active_model.rb
index 6183944bb0..2679d06fe4 100644
--- a/railties/lib/rails/generators/active_model.rb
+++ b/railties/lib/rails/generators/active_model.rb
@@ -39,13 +39,13 @@ module Rails
# GET edit
# PATCH/PUT update
# DELETE destroy
- def self.find(klass, params=nil)
+ def self.find(klass, params = nil)
"#{klass}.find(#{params})"
end
# GET new
# POST create
- def self.build(klass, params=nil)
+ def self.build(klass, params = nil)
if params
"#{klass}.new(#{params})"
else
@@ -59,7 +59,7 @@ module Rails
end
# PATCH/PUT update
- def update(params=nil)
+ def update(params = nil)
"#{name}.update(#{params})"
end
diff --git a/railties/lib/rails/generators/app_base.rb b/railties/lib/rails/generators/app_base.rb
index 91342c592c..5c809807a1 100644
--- a/railties/lib/rails/generators/app_base.rb
+++ b/railties/lib/rails/generators/app_base.rb
@@ -30,9 +30,15 @@ module Rails
class_option :database, type: :string, aliases: "-d", default: "sqlite3",
desc: "Preconfigure for selected database (options: #{DATABASES.join('/')})"
- class_option :javascript, type: :string, aliases: "-j", default: "jquery",
+ class_option :javascript, type: :string, aliases: "-j",
desc: "Preconfigure for selected JavaScript library"
+ class_option :webpack, type: :string, default: nil,
+ desc: "Preconfigure for app-like JavaScript with Webpack"
+
+ class_option :skip_yarn, type: :boolean, default: false,
+ desc: "Don't use Yarn for managing JavaScript dependencies"
+
class_option :skip_gemfile, type: :boolean, default: false,
desc: "Don't create a Gemfile"
@@ -67,6 +73,9 @@ 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,
desc: "Skip JavaScript files"
@@ -99,9 +108,9 @@ module Rails
convert_database_option_for_jruby
end
- protected
+ private
- def gemfile_entry(name, *args)
+ def gemfile_entry(name, *args) # :doc:
options = args.extract_options!
version = args.first
github = options[:github]
@@ -117,11 +126,12 @@ module Rails
self
end
- def gemfile_entries
+ def gemfile_entries # :doc:
[rails_gemfile_entry,
database_gemfile_entry,
webserver_gemfile_entry,
assets_gemfile_entry,
+ webpacker_gemfile_entry,
javascript_gemfile_entry,
jbuilder_gemfile_entry,
psych_gemfile_entry,
@@ -129,13 +139,13 @@ module Rails
@extra_entries].flatten.find_all(&@gem_filter)
end
- def add_gem_entry_filter
+ def add_gem_entry_filter # :doc:
@gem_filter = lambda { |next_filter, entry|
yield(entry) && next_filter.call(entry)
}.curry[@gem_filter]
end
- def builder
+ def builder # :doc:
@builder ||= begin
builder_class = get_builder_class
builder_class.include(ActionMethods)
@@ -143,24 +153,24 @@ module Rails
end
end
- def build(meth, *args)
+ def build(meth, *args) # :doc:
builder.send(meth, *args) if builder.respond_to?(meth)
end
- def create_root
+ def create_root # :doc:
valid_const?
empty_directory "."
FileUtils.cd(destination_root) unless options[:pretend]
end
- def apply_rails_template
+ def apply_rails_template # :doc:
apply rails_template if rails_template
rescue Thor::Error, LoadError, Errno::ENOENT => e
raise Error, "The template [#{rails_template}] could not be loaded. Error: #{e}"
end
- def set_default_accessors!
+ def set_default_accessors! # :doc:
self.destination_root = File.expand_path(app_path, destination_root)
self.rails_template = \
case options[:template]
@@ -173,32 +183,32 @@ module Rails
end
end
- def database_gemfile_entry
+ def database_gemfile_entry # :doc:
return [] if options[:skip_active_record]
gem_name, gem_version = gem_for_database
GemfileEntry.version gem_name, gem_version,
"Use #{options[:database]} as the database for Active Record"
end
- def webserver_gemfile_entry
+ def webserver_gemfile_entry # :doc:
return [] if options[:skip_puma]
comment = "Use Puma as the app server"
GemfileEntry.new("puma", "~> 3.0", comment)
end
- def include_all_railties?
+ def include_all_railties? # :doc:
options.values_at(:skip_active_record, :skip_action_mailer, :skip_test, :skip_sprockets, :skip_action_cable).none?
end
- def comment_if(value)
+ def comment_if(value) # :doc:
options[value] ? "# " : ""
end
- def keeps?
+ def keeps? # :doc:
!options[:skip_keeps]
end
- def sqlite3?
+ def sqlite3? # :doc:
!options[:skip_active_record] && options[:database] == "sqlite3"
end
@@ -309,6 +319,13 @@ module Rails
gems
end
+ def webpacker_gemfile_entry
+ return [] unless options[:webpack]
+
+ comment = "Transpile app-like JavaScript. Read more: https://github.com/rails/webpacker"
+ GemfileEntry.github "webpacker", "rails/webpacker", nil, comment
+ end
+
def jbuilder_gemfile_entry
comment = "Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder"
GemfileEntry.new "jbuilder", "~> 2.5", comment, {}, options[:api]
@@ -322,9 +339,13 @@ module Rails
if options[:skip_javascript] || options[:skip_sprockets]
[]
else
- gems = [coffee_gemfile_entry, javascript_runtime_gemfile_entry]
- gems << GemfileEntry.version("#{options[:javascript]}-rails", nil,
- "Use #{options[:javascript]} as the JavaScript library")
+ gems = [javascript_runtime_gemfile_entry]
+ gems << coffee_gemfile_entry unless options[:skip_coffee]
+
+ if options[:javascript]
+ gems << GemfileEntry.version("#{options[:javascript]}-rails", nil,
+ "Use #{options[:javascript]} as the JavaScript library")
+ end
unless options[:skip_turbolinks]
gems << GemfileEntry.version("turbolinks", "~> 5",
@@ -404,6 +425,13 @@ module Rails
bundle_command("install") if bundle_install?
end
+ def run_webpack
+ if !(webpack = options[:webpack]).nil?
+ rails_command "webpacker:install"
+ rails_command "webpacker:install:#{webpack}" unless webpack == "webpack"
+ end
+ end
+
def generate_spring_binstubs
if bundle_install? && spring_install?
bundle_command("exec spring binstub --all")
diff --git a/railties/lib/rails/generators/base.rb b/railties/lib/rails/generators/base.rb
index 1a0420c769..cc6ae3860f 100644
--- a/railties/lib/rails/generators/base.rb
+++ b/railties/lib/rails/generators/base.rb
@@ -20,14 +20,14 @@ module Rails
strict_args_position!
# Returns the source root for this generator using default_source_root as default.
- def self.source_root(path=nil)
+ def self.source_root(path = nil)
@_source_root = path if path
@_source_root ||= default_source_root
end
# Tries to get the description from a USAGE file one folder above the source
# root otherwise uses a default description.
- def self.desc(description=nil)
+ def self.desc(description = nil)
return super if description
@desc ||= if usage_path
@@ -40,7 +40,7 @@ module Rails
# Convenience method to get the namespace from the class name. It's the
# same as Thor default except that the Generator at the end of the class
# is removed.
- def self.namespace(name=nil)
+ def self.namespace(name = nil)
return super if name
@namespace ||= super.sub(/_generator$/, "").sub(/:generators:/, ":")
end
@@ -195,7 +195,7 @@ module Rails
end
# Make class option aware of Rails::Generators.options and Rails::Generators.aliases.
- def self.class_option(name, options={}) #:nodoc:
+ def self.class_option(name, options = {}) #:nodoc:
options[:desc] = "Indicates when to generate #{name.to_s.humanize.downcase}" unless options.key?(:desc)
options[:aliases] = default_aliases_for_option(name, options)
options[:default] = default_value_for_option(name, options)
@@ -239,11 +239,11 @@ module Rails
end
end
- protected
+ private
# Check whether the given class names are already taken by user
# application or Ruby on Rails.
- def class_collisions(*class_names) #:nodoc:
+ def class_collisions(*class_names)
return unless behavior == :invoke
class_names.flatten.each do |class_name|
@@ -264,7 +264,7 @@ module Rails
end
# Takes in an array of nested modules and extracts the last module
- def extract_last_module(nesting)
+ def extract_last_module(nesting) # :doc:
nesting.inject(Object) do |last_module, nest|
break unless last_module.const_defined?(nest, false)
last_module.const_get(nest)
@@ -272,12 +272,12 @@ module Rails
end
# Use Rails default banner.
- def self.banner
- "rails generate #{namespace.sub(/^rails:/,'')} #{arguments.map(&:usage).join(' ')} [options]".gsub(/\s+/, " ")
+ def self.banner # :doc:
+ "rails generate #{namespace.sub(/^rails:/, '')} #{arguments.map(&:usage).join(' ')} [options]".gsub(/\s+/, " ")
end
# Sets the base_name taking into account the current class namespace.
- def self.base_name
+ def self.base_name # :doc:
@base_name ||= begin
if base = name.to_s.split("::").first
base.underscore
@@ -287,7 +287,7 @@ module Rails
# Removes the namespaces and get the generator name. For example,
# Rails::Generators::ModelGenerator will return "model" as generator name.
- def self.generator_name
+ def self.generator_name # :doc:
@generator_name ||= begin
if generator = name.to_s.split("::").last
generator.sub!(/Generator$/, "")
@@ -298,18 +298,18 @@ module Rails
# Returns the default value for the option name given doing a lookup in
# Rails::Generators.options.
- def self.default_value_for_option(name, options)
+ def self.default_value_for_option(name, options) # :doc:
default_for_option(Rails::Generators.options, name, options, options[:default])
end
# Returns default aliases for the option name given doing a lookup in
# Rails::Generators.aliases.
- def self.default_aliases_for_option(name, options)
+ def self.default_aliases_for_option(name, options) # :doc:
default_for_option(Rails::Generators.aliases, name, options, options[:aliases])
end
# Returns default for the option name given doing a lookup in config.
- def self.default_for_option(config, name, options, default)
+ def self.default_for_option(config, name, options, default) # :doc:
if generator_name && (c = config[generator_name.to_sym]) && c.key?(name)
c[name]
elsif base_name && (c = config[base_name.to_sym]) && c.key?(name)
@@ -343,7 +343,7 @@ module Rails
# Small macro to add ruby as an option to the generator with proper
# default value plus an instance helper method called shebang.
- def self.add_shebang_option!
+ def self.add_shebang_option! # :doc:
class_option :ruby, type: :string, aliases: "-r", default: Thor::Util.ruby_command,
desc: "Path to the Ruby binary of your choice", banner: "PATH"
@@ -361,7 +361,7 @@ module Rails
}
end
- def self.usage_path
+ def self.usage_path # :doc:
paths = [
source_root && File.expand_path("../USAGE", source_root),
default_generator_root && File.join(default_generator_root, "USAGE")
@@ -369,7 +369,7 @@ module Rails
paths.compact.detect { |path| File.exist? path }
end
- def self.default_generator_root
+ def self.default_generator_root # :doc:
path = File.expand_path(File.join(base_name, generator_name), base_root)
path if File.exist?(path)
end
diff --git a/railties/lib/rails/generators/erb.rb b/railties/lib/rails/generators/erb.rb
index d01502002f..d5e326d6ee 100644
--- a/railties/lib/rails/generators/erb.rb
+++ b/railties/lib/rails/generators/erb.rb
@@ -3,7 +3,7 @@ require "rails/generators/named_base"
module Erb # :nodoc:
module Generators # :nodoc:
class Base < Rails::Generators::NamedBase #:nodoc:
- protected
+ private
def formats
[format]
diff --git a/railties/lib/rails/generators/erb/mailer/mailer_generator.rb b/railties/lib/rails/generators/erb/mailer/mailer_generator.rb
index f150240908..677f8041ae 100644
--- a/railties/lib/rails/generators/erb/mailer/mailer_generator.rb
+++ b/railties/lib/rails/generators/erb/mailer/mailer_generator.rb
@@ -26,7 +26,7 @@ module Erb # :nodoc:
end
end
- protected
+ private
def formats
[:text, :html]
diff --git a/railties/lib/rails/generators/erb/scaffold/scaffold_generator.rb b/railties/lib/rails/generators/erb/scaffold/scaffold_generator.rb
index 154d85f381..0d77ef21da 100644
--- a/railties/lib/rails/generators/erb/scaffold/scaffold_generator.rb
+++ b/railties/lib/rails/generators/erb/scaffold/scaffold_generator.rb
@@ -21,7 +21,7 @@ module Erb # :nodoc:
end
end
- protected
+ private
def available_views
%w(index edit show new _form)
diff --git a/railties/lib/rails/generators/generated_attribute.rb b/railties/lib/rails/generators/generated_attribute.rb
index 61181b7b97..baed7bf1e3 100644
--- a/railties/lib/rails/generators/generated_attribute.rb
+++ b/railties/lib/rails/generators/generated_attribute.rb
@@ -56,7 +56,7 @@ module Rails
end
end
- def initialize(name, type=nil, index_type=false, attr_options={})
+ def initialize(name, type = nil, index_type = false, attr_options = {})
@name = name
@type = type || :string
@has_index = INDEX_OPTIONS.include?(index_type)
@@ -151,7 +151,7 @@ module Rails
end
def inject_options
- "".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/migration.rb b/railties/lib/rails/generators/migration.rb
index 7290e235a1..0d63b9a5c9 100644
--- a/railties/lib/rails/generators/migration.rb
+++ b/railties/lib/rails/generators/migration.rb
@@ -52,7 +52,7 @@ module Rails
#
# migration_template "migration.rb", "db/migrate/add_foo_to_bar.rb"
def migration_template(source, destination, config = {})
- source = File.expand_path(find_in_source_paths(source.to_s))
+ source = File.expand_path(find_in_source_paths(source.to_s))
set_migration_assigns!(destination)
context = instance_eval("binding")
diff --git a/railties/lib/rails/generators/named_base.rb b/railties/lib/rails/generators/named_base.rb
index c39ea24935..e3660b012a 100644
--- a/railties/lib/rails/generators/named_base.rb
+++ b/railties/lib/rails/generators/named_base.rb
@@ -32,145 +32,145 @@ module Rails
end
end
+ # TODO Change this to private once we've dropped Ruby 2.2 support.
+ # Workaround for Ruby 2.2 "private attribute?" warning.
protected
attr_reader :file_name
+ private
+
# FIXME: We are avoiding to use alias because a bug on thor that make
# this method public and add it to the task list.
- def singular_name
+ def singular_name # :doc:
file_name
end
# Wrap block with namespace of current application
# if namespace exists and is not skipped
- def module_namespacing(&block)
+ def module_namespacing(&block) # :doc:
content = capture(&block)
content = wrap_with_namespace(content) if namespaced?
concat(content)
end
- def indent(content, multiplier = 2)
+ def indent(content, multiplier = 2) # :doc:
spaces = " " * multiplier
content.each_line.map { |line| line.blank? ? line : "#{spaces}#{line}" }.join
end
- def wrap_with_namespace(content)
+ def wrap_with_namespace(content) # :doc:
content = indent(content).chomp
"module #{namespace.name}\n#{content}\nend\n"
end
- def inside_template
+ def inside_template # :doc:
@inside_template = true
yield
ensure
@inside_template = false
end
- def inside_template?
+ def inside_template? # :doc:
@inside_template
end
- def namespace
+ def namespace # :doc:
Rails::Generators.namespace
end
- def namespaced?
+ def namespaced? # :doc:
!options[:skip_namespace] && namespace
end
- def file_path
+ def file_path # :doc:
@file_path ||= (class_path + [file_name]).join("/")
end
- def class_path
+ def class_path # :doc:
inside_template? || !namespaced? ? regular_class_path : namespaced_class_path
end
- def regular_class_path
+ def regular_class_path # :doc:
@class_path
end
- def namespaced_file_path
- @namespaced_file_path ||= namespaced_class_path.join("/")
- end
-
- def namespaced_class_path
+ def namespaced_class_path # :doc:
@namespaced_class_path ||= [namespaced_path] + @class_path
end
- def namespaced_path
+ def namespaced_path # :doc:
@namespaced_path ||= namespace.name.split("::").first.underscore
end
- def class_name
+ def class_name # :doc:
(class_path + [file_name]).map!(&:camelize).join("::")
end
- def human_name
+ def human_name # :doc:
@human_name ||= singular_name.humanize
end
- def plural_name
+ def plural_name # :doc:
@plural_name ||= singular_name.pluralize
end
- def i18n_scope
+ def i18n_scope # :doc:
@i18n_scope ||= file_path.tr("/", ".")
end
- def table_name
+ def table_name # :doc:
@table_name ||= begin
base = pluralize_table_names? ? plural_name : singular_name
(class_path + [base]).join("_")
end
end
- def uncountable?
+ def uncountable? # :doc:
singular_name == plural_name
end
- def index_helper
+ def index_helper # :doc:
uncountable? ? "#{plural_table_name}_index" : plural_table_name
end
- def show_helper
+ def show_helper # :doc:
"#{singular_table_name}_url(@#{singular_table_name})"
end
- def edit_helper
+ def edit_helper # :doc:
"edit_#{show_helper}"
end
- def new_helper
+ def new_helper # :doc:
"new_#{singular_table_name}_url"
end
- def singular_table_name
+ def singular_table_name # :doc:
@singular_table_name ||= (pluralize_table_names? ? table_name.singularize : table_name)
end
- def plural_table_name
+ def plural_table_name # :doc:
@plural_table_name ||= (pluralize_table_names? ? table_name : table_name.pluralize)
end
- def plural_file_name
+ def plural_file_name # :doc:
@plural_file_name ||= file_name.pluralize
end
- def fixture_file_name
+ def fixture_file_name # :doc:
@fixture_file_name ||= (pluralize_table_names? ? plural_file_name : file_name)
end
- def route_url
+ def route_url # :doc:
@route_url ||= class_path.collect { |dname| "/" + dname }.join + "/" + plural_file_name
end
- def url_helper_prefix
+ def url_helper_prefix # :doc:
@url_helper_prefix ||= (class_path + [file_name]).join("_")
end
# Tries to retrieve the application name or simply return application.
- def application_name
+ def application_name # :doc:
if defined?(Rails) && Rails.application
Rails.application.class.name.split("::").first.underscore
else
@@ -178,20 +178,20 @@ module Rails
end
end
- def assign_names!(name) #:nodoc:
+ def assign_names!(name)
@class_path = name.include?("/") ? name.split("/") : name.split("::")
@class_path.map!(&:underscore)
@file_name = @class_path.pop
end
# Convert attributes array into GeneratedAttribute objects.
- def parse_attributes! #:nodoc:
+ def parse_attributes!
self.attributes = (attributes || []).map do |attr|
Rails::Generators::GeneratedAttribute.parse(attr)
end
end
- def attributes_names
+ def attributes_names # :doc:
@attributes_names ||= attributes.each_with_object([]) do |a, names|
names << a.column_name
names << "password_confirmation" if a.password_digest?
@@ -199,11 +199,11 @@ module Rails
end
end
- def pluralize_table_names?
+ def pluralize_table_names? # :doc:
!defined?(ActiveRecord::Base) || ActiveRecord::Base.pluralize_table_names
end
- def mountable_engine?
+ def mountable_engine? # :doc:
defined?(ENGINE_ROOT) && namespaced?
end
@@ -217,7 +217,7 @@ module Rails
# If the generator is invoked with class name Admin, it will check for
# the presence of "AdminDecorator".
#
- def self.check_class_collision(options={})
+ def self.check_class_collision(options = {}) # :doc:
define_method :check_class_collision do
name = if self.respond_to?(:controller_class_name) # for ScaffoldBase
controller_class_name
diff --git a/railties/lib/rails/generators/rails/app/app_generator.rb b/railties/lib/rails/generators/rails/app/app_generator.rb
index 9f9c50ca10..070a6ac124 100644
--- a/railties/lib/rails/generators/rails/app/app_generator.rb
+++ b/railties/lib/rails/generators/rails/app/app_generator.rb
@@ -56,7 +56,7 @@ module Rails
def app
directory "app"
- keep_file "app/assets/images"
+ keep_file "app/assets/images"
empty_directory_with_keep_file "app/assets/javascripts/channels" unless options[:skip_action_cable]
keep_file "app/controllers/concerns"
@@ -151,19 +151,12 @@ module Rails
end
def vendor
- vendor_javascripts
- vendor_stylesheets
- end
+ empty_directory_with_keep_file "vendor"
- def vendor_javascripts
- unless options[:skip_javascript]
- empty_directory_with_keep_file "vendor/assets/javascripts"
+ unless options[:skip_yarn]
+ template "package.json", "vendor/package.json"
end
end
-
- def vendor_stylesheets
- empty_directory_with_keep_file "vendor/assets/stylesheets"
- end
end
module Generators
@@ -193,9 +186,11 @@ module Rails
raise Error, "Invalid value for --database option. Supported for preconfiguration are: #{DATABASES.join(", ")}."
end
- # Force sprockets to be skipped when generating API only apps.
+ # Force sprockets and yarn to be skipped when generating API only apps.
# Can't modify options hash as it's frozen by default.
- self.options = options.merge(skip_sprockets: true, skip_javascript: true).freeze if options[:api]
+ if options[:api]
+ self.options = options.merge(skip_sprockets: true, skip_javascript: true, skip_yarn: true).freeze
+ end
end
public_task :set_default_accessors!
@@ -205,8 +200,8 @@ module Rails
build(:readme)
build(:rakefile)
build(:configru)
- build(:gitignore) unless options[:skip_git]
- build(:gemfile) unless options[:skip_gemfile]
+ build(:gitignore) unless options[:skip_git]
+ build(:gemfile) unless options[:skip_gemfile]
end
def create_app_files
@@ -241,6 +236,7 @@ module Rails
end
def create_db_files
+ return if options[:skip_active_record]
build(:db)
end
@@ -266,6 +262,10 @@ module Rails
def create_vendor_files
build(:vendor)
+
+ if options[:skip_yarn]
+ remove_file "vendor/package.json"
+ end
end
def delete_app_assets_if_api_option
@@ -273,7 +273,6 @@ module Rails
remove_dir "app/assets"
remove_dir "lib/assets"
remove_dir "tmp/cache/assets"
- remove_dir "vendor/assets"
end
end
@@ -349,23 +348,27 @@ module Rails
end
end
+ def delete_bin_yarn_if_api_option
+ remove_file "bin/yarn" if options[:api]
+ end
+
def finish_template
build(:leftovers)
end
public_task :apply_rails_template, :run_bundle
- public_task :generate_spring_binstubs
+ public_task :run_webpack, :generate_spring_binstubs
def run_after_bundle_callbacks
@after_bundle_callbacks.each(&:call)
end
- protected
-
def self.banner
"rails new #{arguments.map(&:usage).join(' ')} [options]"
end
+ private
+
# Define file as an alias to create_file for backwards compatibility.
def file(*args, &block)
create_file(*args, &block)
@@ -422,7 +425,7 @@ module Rails
"/opt/local/var/run/mysql4/mysqld.sock", # mac + darwinports + mysql4
"/opt/local/var/run/mysql5/mysqld.sock", # mac + darwinports + mysql5
"/opt/lampp/var/mysql/mysql.sock" # xampp for linux
- ].find { |f| File.exist?(f) } unless RbConfig::CONFIG["host_os"] =~ /mswin|mingw/
+ ].find { |f| File.exist?(f) } unless Gem.win_platform?
end
def get_builder_class
diff --git a/railties/lib/rails/generators/rails/app/templates/Gemfile b/railties/lib/rails/generators/rails/app/templates/Gemfile
index 422217286c..f1015b16d5 100644
--- a/railties/lib/rails/generators/rails/app/templates/Gemfile
+++ b/railties/lib/rails/generators/rails/app/templates/Gemfile
@@ -1,5 +1,10 @@
source 'https://rubygems.org'
+git_source(:github) do |repo_name|
+ repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include?("/")
+ "https://github.com/#{repo_name}.git"
+end
+
<% gemfile_entries.each do |gem| -%>
<% if gem.comment -%>
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
index c88426ec06..25870f19c8 100644
--- 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
@@ -1,8 +1,8 @@
// 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, vendor/assets/javascripts,
-// or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
+// 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.
@@ -11,9 +11,11 @@
// about supported directives.
//
<% unless options[:skip_javascript] -%>
+<% if options[:javascript] -%>
//= require <%= options[:javascript] %>
-//= require <%= options[:javascript] %>_ujs
-<% if gemfile_entries.any? { |m| m.name == "turbolinks" } -%>
+<% end -%>
+//= require rails-ujs
+<% unless options[:skip_turbolinks] -%>
//= require turbolinks
<% end -%>
<% end -%>
diff --git a/railties/lib/rails/generators/rails/app/templates/app/assets/stylesheets/application.css b/railties/lib/rails/generators/rails/app/templates/app/assets/stylesheets/application.css
index 0ebd7fe829..865300bef9 100644
--- a/railties/lib/rails/generators/rails/app/templates/app/assets/stylesheets/application.css
+++ b/railties/lib/rails/generators/rails/app/templates/app/assets/stylesheets/application.css
@@ -2,8 +2,8 @@
* This is a manifest file that'll be compiled into application.css, which will include all the files
* listed below.
*
- * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
- * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, or any plugin's
+ * vendor/assets/stylesheets directory can be referenced here using a relative path.
*
* You're free to add application-wide styles to this file and they'll appear at the bottom of the
* compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
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 d51f79bd49..5460155b3e 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
@@ -7,7 +7,7 @@
<%- if options[:skip_javascript] -%>
<%%= stylesheet_link_tag 'application', media: 'all' %>
<%- else -%>
- <%- if gemfile_entries.any? { |m| m.name == 'turbolinks' } -%>
+ <%- unless options[:skip_turbolinks] -%>
<%%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
<%%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
<%- else -%>
diff --git a/railties/lib/rails/generators/rails/app/templates/bin/setup b/railties/lib/rails/generators/rails/app/templates/bin/setup.tt
index acae810c1a..c6607dbb2b 100644
--- a/railties/lib/rails/generators/rails/app/templates/bin/setup
+++ b/railties/lib/rails/generators/rails/app/templates/bin/setup.tt
@@ -16,7 +16,12 @@ chdir APP_ROOT do
puts '== Installing dependencies =='
system! 'gem install bundler --conservative'
system('bundle check') || system!('bundle install')
+<% unless options[:skip_yarn] %>
+ # Install JavaScript dependencies if using Yarn
+ # system('bin/yarn')
+<% end %>
+<% unless options.skip_active_record -%>
# puts "\n== Copying sample files =="
# unless File.exist?('config/database.yml')
# cp 'config/database.yml.sample', 'config/database.yml'
@@ -24,6 +29,7 @@ chdir APP_ROOT do
puts "\n== Preparing database =="
system! 'bin/rails db:setup'
+<% end -%>
puts "\n== Removing old logs and tempfiles =="
system! 'bin/rails log:clear tmp:clear'
diff --git a/railties/lib/rails/generators/rails/app/templates/bin/update b/railties/lib/rails/generators/rails/app/templates/bin/update.tt
index 770a605fed..d23af018c7 100644
--- a/railties/lib/rails/generators/rails/app/templates/bin/update
+++ b/railties/lib/rails/generators/rails/app/templates/bin/update.tt
@@ -16,9 +16,10 @@ chdir APP_ROOT do
puts '== Installing dependencies =='
system! 'gem install bundler --conservative'
system('bundle check') || system!('bundle install')
-
+<% unless options.skip_active_record -%>
puts "\n== Updating database =="
system! 'bin/rails db:migrate'
+<% end -%>
puts "\n== Removing old logs and tempfiles =="
system! 'bin/rails log:clear tmp:clear'
diff --git a/railties/lib/rails/generators/rails/app/templates/bin/yarn b/railties/lib/rails/generators/rails/app/templates/bin/yarn
new file mode 100644
index 0000000000..872438cecb
--- /dev/null
+++ b/railties/lib/rails/generators/rails/app/templates/bin/yarn
@@ -0,0 +1,9 @@
+VENDOR_PATH = File.expand_path('../vendor', __dir__)
+Dir.chdir(VENDOR_PATH) do
+ begin
+ exec "yarnpkg #{ARGV.join(" ")}"
+ rescue Errno::ENOENT
+ puts "Yarn executable was not detected in the system."
+ puts "Download Yarn at https://yarnpkg.com/en/docs/install"
+ end
+end
diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/assets.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/initializers/assets.rb.tt
index 2318cf59ff..f5d03fb117 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/initializers/assets.rb.tt
+++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/assets.rb.tt
@@ -3,8 +3,12 @@
# Version of your assets, change this if you want to expire all your assets.
Rails.application.config.assets.version = '1.0'
-# Add additional assets to the asset load path
+# Add additional assets to the asset load path.
# Rails.application.config.assets.paths << Emoji.images_path
+<%- unless options[:skip_yarn] -%>
+# Add Yarn node_modules folder to the asset load path.
+Rails.application.config.assets.paths << Rails.root.join('vendor/node_modules')
+<%- end -%>
# Precompile additional assets.
# application.js, application.css, and all non-JS/CSS in the app/assets
diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults.rb.tt
index 5ad18cc5ad..3ad3eba98a 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults.rb.tt
+++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults.rb.tt
@@ -32,7 +32,9 @@ ActiveSupport.halt_callback_chains_on_return_false = <%= options[:update] ? true
# Configure SSL options to enable HSTS with subdomains. Previous versions had false.
Rails.application.config.ssl_options = { hsts: { subdomains: true } }
<%- end -%>
+<%- unless options[:skip_sprockets] -%>
# Unknown asset fallback will return the path passed in when the given
# asset is not present in the asset pipeline.
Rails.application.config.assets.unknown_asset_fallback = <%= options[:update] ? true : false %>
+<%- end -%>
diff --git a/railties/lib/rails/generators/rails/app/templates/config/puma.rb b/railties/lib/rails/generators/rails/app/templates/config/puma.rb
index 7ee948002e..1e19380dcb 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/puma.rb
+++ b/railties/lib/rails/generators/rails/app/templates/config/puma.rb
@@ -1,13 +1,13 @@
# Puma can serve each request in a thread from an internal thread pool.
-# The `threads` method setting takes two numbers a minimum and maximum.
+# The `threads` method setting takes two numbers: a minimum and maximum.
# Any libraries that use thread pools should be configured to match
# the maximum value specified for Puma. Default is set to 5 threads for minimum
-# and maximum, this matches the default thread size of Active Record.
+# and maximum; this matches the default thread size of Active Record.
#
threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }
threads threads_count, threads_count
-# Specifies the `port` that Puma will listen on to receive requests, default is 3000.
+# Specifies the `port` that Puma will listen on to receive requests; default is 3000.
#
port ENV.fetch("PORT") { 3000 }
@@ -42,9 +42,9 @@ environment ENV.fetch("RAILS_ENV") { "development" }
# The code in the `on_worker_boot` will be called if you are using
# clustered mode by specifying a number of `workers`. After each worker
-# process is booted this block will be run, if you are using `preload_app!`
-# option you will want to use this block to reconnect to any threads
-# or connections that may have been created at application boot, Ruby
+# process is booted, this block will be run. If you are using the `preload_app!`
+# option, you will want to use this block to reconnect to any threads
+# or connections that may have been created at application boot, as Ruby
# cannot share connections between processes.
#
# on_worker_boot do
diff --git a/railties/lib/rails/generators/rails/app/templates/gitignore b/railties/lib/rails/generators/rails/app/templates/gitignore
index 0e66cc4237..709b341387 100644
--- a/railties/lib/rails/generators/rails/app/templates/gitignore
+++ b/railties/lib/rails/generators/rails/app/templates/gitignore
@@ -21,5 +21,8 @@
!/tmp/.keep
<% end -%>
-# Ignore Byebug command history file.
+<% unless options[:skip_yarn] -%>
+/vendor/node_modules
+
+<% end -%>
.byebug_history
diff --git a/railties/lib/rails/generators/rails/app/templates/package.json b/railties/lib/rails/generators/rails/app/templates/package.json
new file mode 100644
index 0000000000..46db57dcbe
--- /dev/null
+++ b/railties/lib/rails/generators/rails/app/templates/package.json
@@ -0,0 +1,5 @@
+{
+ "name": "<%= app_name %>",
+ "private": true,
+ "dependencies": {}
+}
diff --git a/railties/lib/rails/generators/rails/assets/assets_generator.rb b/railties/lib/rails/generators/rails/assets/assets_generator.rb
index 265dada2ca..95d00c2d39 100644
--- a/railties/lib/rails/generators/rails/assets/assets_generator.rb
+++ b/railties/lib/rails/generators/rails/assets/assets_generator.rb
@@ -7,7 +7,7 @@ module Rails
class_option :javascript_engine, desc: "Engine for JavaScripts"
class_option :stylesheet_engine, desc: "Engine for Stylesheets"
- protected
+ private
def asset_name
file_name
diff --git a/railties/lib/rails/generators/rails/controller/controller_generator.rb b/railties/lib/rails/generators/rails/controller/controller_generator.rb
index 213de37cce..06bdb8b5ce 100644
--- a/railties/lib/rails/generators/rails/controller/controller_generator.rb
+++ b/railties/lib/rails/generators/rails/controller/controller_generator.rb
@@ -3,6 +3,8 @@ module Rails
class ControllerGenerator < NamedBase # :nodoc:
argument :actions, type: :array, default: [], banner: "action action"
class_option :skip_routes, type: :boolean, desc: "Don't add routes to config/routes.rb."
+ class_option :helper, type: :boolean
+ class_option :assets, type: :boolean
check_class_collision suffix: "Controller"
@@ -14,7 +16,7 @@ module Rails
unless options[:skip_routes]
actions.reverse_each do |action|
# route prepends two spaces onto the front of the string that is passed, this corrects that.
- route generate_routing_code(action)[2..-1]
+ route indent(generate_routing_code(action), 2)[2..-1]
end
end
end
@@ -32,27 +34,30 @@ module Rails
# end
# end
def generate_routing_code(action)
- depth = regular_class_path.length
+ depth = 0
+ lines = []
+
# Create 'namespace' ladder
# namespace :foo do
# namespace :bar do
- namespace_ladder = regular_class_path.each_with_index.map do |ns, i|
- indent(" namespace :#{ns} do\n", i * 2)
- end.join
+ regular_class_path.each do |ns|
+ lines << indent("namespace :#{ns} do\n", depth * 2)
+ depth += 1
+ end
# Create route
# get 'baz/index'
- route = indent(%{ get '#{file_name}/#{action}'\n}, depth * 2)
+ lines << indent(%{get '#{file_name}/#{action}'\n}, depth * 2)
# Create `end` ladder
# end
# end
- end_ladder = (1..depth).reverse_each.map do |i|
- indent("end\n", i * 2)
- end.join
+ until depth.zero?
+ depth -= 1
+ lines << indent("end\n", depth * 2)
+ end
- # Combine the 3 parts to generate complete route entry
- namespace_ladder + route + end_ladder
+ lines.join
end
end
end
diff --git a/railties/lib/rails/generators/rails/generator/generator_generator.rb b/railties/lib/rails/generators/rails/generator/generator_generator.rb
index 8040ec5e7b..299a7da5f1 100644
--- a/railties/lib/rails/generators/rails/generator/generator_generator.rb
+++ b/railties/lib/rails/generators/rails/generator/generator_generator.rb
@@ -12,7 +12,7 @@ module Rails
hook_for :test_framework
- protected
+ private
def generator_dir
if options[:namespace]
diff --git a/railties/lib/rails/generators/rails/plugin/plugin_generator.rb b/railties/lib/rails/generators/rails/plugin/plugin_generator.rb
index 9ffeab4fbe..4cf4f8cd9a 100644
--- a/railties/lib/rails/generators/rails/plugin/plugin_generator.rb
+++ b/railties/lib/rails/generators/rails/plugin/plugin_generator.rb
@@ -81,7 +81,7 @@ task default: :test
end
PASSTHROUGH_OPTIONS = [
- :skip_active_record, :skip_action_mailer, :skip_javascript, :database,
+ :skip_active_record, :skip_action_mailer, :skip_javascript, :skip_sprockets, :database,
:javascript, :quiet, :pretend, :force, :skip
]
@@ -270,8 +270,8 @@ task default: :test
@name ||= begin
# same as ActiveSupport::Inflector#underscore except not replacing '-'
underscored = original_name.dup
- underscored.gsub!(/([A-Z]+)([A-Z][a-z])/,'\1_\2')
- underscored.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
+ underscored.gsub!(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
+ underscored.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
underscored.downcase!
underscored
@@ -283,10 +283,10 @@ task default: :test
end
def namespaced_name
- @namespaced_name ||= name.gsub("-", "/")
+ @namespaced_name ||= name.tr("-", "/")
end
- protected
+ private
def create_dummy_app(path = nil)
dummy_path(path) if path
diff --git a/railties/lib/rails/generators/resource_helpers.rb b/railties/lib/rails/generators/resource_helpers.rb
index 6d80003271..978b053308 100644
--- a/railties/lib/rails/generators/resource_helpers.rb
+++ b/railties/lib/rails/generators/resource_helpers.rb
@@ -23,10 +23,14 @@ module Rails
assign_controller_names!(controller_name.pluralize)
end
+ # TODO Change this to private once we've dropped Ruby 2.2 support.
+ # Workaround for Ruby 2.2 "private attribute?" warning.
protected
attr_reader :controller_name, :controller_file_name
+ private
+
def controller_class_path
if options[:model_name]
@controller_class_path
@@ -73,7 +77,7 @@ module Rails
end
# Initialize ORM::Generators::ActiveModel to access instance methods.
- def orm_instance(name=singular_table_name)
+ def orm_instance(name = singular_table_name)
@orm_instance ||= orm_class.new(name)
end
end
diff --git a/railties/lib/rails/generators/test_unit/generator/generator_generator.rb b/railties/lib/rails/generators/test_unit/generator/generator_generator.rb
index 59f8d40343..6b6e094453 100644
--- a/railties/lib/rails/generators/test_unit/generator/generator_generator.rb
+++ b/railties/lib/rails/generators/test_unit/generator/generator_generator.rb
@@ -12,7 +12,7 @@ module TestUnit # :nodoc:
template "generator_test.rb", File.join("test/lib/generators", class_path, "#{file_name}_generator_test.rb")
end
- protected
+ private
def generator_path
if options[:namespace]
diff --git a/railties/lib/rails/generators/test_unit/mailer/mailer_generator.rb b/railties/lib/rails/generators/test_unit/mailer/mailer_generator.rb
index 806279788e..67bff8e4f9 100644
--- a/railties/lib/rails/generators/test_unit/mailer/mailer_generator.rb
+++ b/railties/lib/rails/generators/test_unit/mailer/mailer_generator.rb
@@ -17,7 +17,7 @@ module TestUnit # :nodoc:
template "preview.rb", File.join("test/mailers/previews", class_path, "#{file_name}_mailer_preview.rb")
end
- protected
+ private
def file_name
@_file_name ||= super.gsub(/_mailer/i, "")
end
diff --git a/railties/lib/rails/generators/testing/behaviour.rb b/railties/lib/rails/generators/testing/behaviour.rb
index a1e5a233b9..64d641d096 100644
--- a/railties/lib/rails/generators/testing/behaviour.rb
+++ b/railties/lib/rails/generators/testing/behaviour.rb
@@ -82,23 +82,23 @@ module Rails
Rails::Generators::GeneratedAttribute.parse([name, attribute_type, index].compact.join(":"))
end
- protected
+ private
- def destination_root_is_set? # :nodoc:
+ def destination_root_is_set?
raise "You need to configure your Rails::Generators::TestCase destination root." unless destination_root
end
- def ensure_current_path # :nodoc:
+ def ensure_current_path
cd current_path
end
# Clears all files and directories in destination.
- def prepare_destination
+ def prepare_destination # :doc:
rm_rf(destination_root)
mkdir_p(destination_root)
end
- def migration_file_name(relative) # :nodoc:
+ def migration_file_name(relative)
absolute = File.expand_path(relative, destination_root)
dirname, file_name = File.dirname(absolute), File.basename(absolute).sub(/\.rb$/, "")
Dir.glob("#{dirname}/[0-9]*_*.rb").grep(/\d+_#{file_name}.rb$/).first
diff --git a/railties/lib/rails/initializable.rb b/railties/lib/rails/initializable.rb
index 81b1cd8110..a2615d5efd 100644
--- a/railties/lib/rails/initializable.rb
+++ b/railties/lib/rails/initializable.rb
@@ -53,7 +53,7 @@ module Rails
end
end
- def run_initializers(group=:default, *args)
+ def run_initializers(group = :default, *args)
return if instance_variable_defined?(:@ran)
initializers.tsort_each do |initializer|
initializer.run(*args) if initializer.belongs_to?(group)
diff --git a/railties/lib/rails/mailers_controller.rb b/railties/lib/rails/mailers_controller.rb
index 95de998208..000ce40fbc 100644
--- a/railties/lib/rails/mailers_controller.rb
+++ b/railties/lib/rails/mailers_controller.rb
@@ -40,12 +40,12 @@ class Rails::MailersController < Rails::ApplicationController # :nodoc:
end
end
- protected
- def show_previews?
+ private
+ def show_previews? # :doc:
ActionMailer::Base.show_previews
end
- def find_preview
+ def find_preview # :doc:
candidates = []
params[:path].to_s.scan(%r{/|$}) { candidates << $` }
preview = candidates.detect { |candidate| ActionMailer::Preview.exists?(candidate) }
@@ -57,7 +57,7 @@ class Rails::MailersController < Rails::ApplicationController # :nodoc:
end
end
- def find_preferred_part(*formats)
+ def find_preferred_part(*formats) # :doc:
formats.each do |format|
if part = @email.find_first_mime_type(format)
return part
@@ -69,7 +69,7 @@ class Rails::MailersController < Rails::ApplicationController # :nodoc:
end
end
- def find_part(format)
+ def find_part(format) # :doc:
if part = @email.find_first_mime_type(format)
part
elsif @email.mime_type == format
diff --git a/railties/lib/rails/paths.rb b/railties/lib/rails/paths.rb
index 1c1810dde6..10925de8b2 100644
--- a/railties/lib/rails/paths.rb
+++ b/railties/lib/rails/paths.rb
@@ -45,7 +45,6 @@ module Rails
attr_accessor :path
def initialize(path)
- @current = nil
@path = path
@root = {}
end
diff --git a/railties/lib/rails/rack/logger.rb b/railties/lib/rails/rack/logger.rb
index e3fee75603..853fc26051 100644
--- a/railties/lib/rails/rack/logger.rb
+++ b/railties/lib/rails/rack/logger.rb
@@ -27,45 +27,43 @@ module Rails
end
end
- protected
+ private
- def call_app(request, env)
- instrumenter = ActiveSupport::Notifications.instrumenter
- instrumenter.start "request.action_dispatch", request: request
- logger.info { started_request_message(request) }
- resp = @app.call(env)
- resp[2] = ::Rack::BodyProxy.new(resp[2]) { finish(request) }
- resp
- rescue Exception
- finish(request)
- raise
- ensure
- ActiveSupport::LogSubscriber.flush_all!
- end
+ def call_app(request, env) # :doc:
+ instrumenter = ActiveSupport::Notifications.instrumenter
+ instrumenter.start "request.action_dispatch", request: request
+ logger.info { started_request_message(request) }
+ resp = @app.call(env)
+ resp[2] = ::Rack::BodyProxy.new(resp[2]) { finish(request) }
+ resp
+ rescue Exception
+ finish(request)
+ raise
+ ensure
+ ActiveSupport::LogSubscriber.flush_all!
+ end
- # Started GET "/session/new" for 127.0.0.1 at 2012-09-26 14:51:42 -0700
- def started_request_message(request)
- 'Started %s "%s" for %s at %s' % [
- request.request_method,
- request.filtered_path,
- request.ip,
- Time.now.to_default_s ]
- end
+ # Started GET "/session/new" for 127.0.0.1 at 2012-09-26 14:51:42 -0700
+ def started_request_message(request) # :doc:
+ 'Started %s "%s" for %s at %s' % [
+ request.request_method,
+ request.filtered_path,
+ request.ip,
+ Time.now.to_default_s ]
+ end
- def compute_tags(request)
- @taggers.collect do |tag|
- case tag
- when Proc
- tag.call(request)
- when Symbol
- request.send(tag)
- else
- tag
+ def compute_tags(request) # :doc:
+ @taggers.collect do |tag|
+ case tag
+ when Proc
+ tag.call(request)
+ when Symbol
+ request.send(tag)
+ else
+ tag
+ end
end
end
- end
-
- private
def finish(request)
instrumenter = ActiveSupport::Notifications.instrumenter
diff --git a/railties/lib/rails/railtie.rb b/railties/lib/rails/railtie.rb
index eb3f5d4ee9..474118c0a9 100644
--- a/railties/lib/rails/railtie.rb
+++ b/railties/lib/rails/railtie.rb
@@ -132,27 +132,19 @@ module Rails
end
def rake_tasks(&blk)
- @rake_tasks ||= []
- @rake_tasks << blk if blk
- @rake_tasks
+ register_block_for(:rake_tasks, &blk)
end
def console(&blk)
- @load_console ||= []
- @load_console << blk if blk
- @load_console
+ register_block_for(:load_console, &blk)
end
def runner(&blk)
- @load_runner ||= []
- @load_runner << blk if blk
- @load_runner
+ register_block_for(:runner, &blk)
end
def generators(&blk)
- @generators ||= []
- @generators << blk if blk
- @generators
+ register_block_for(:generators, &blk)
end
def abstract_railtie?
@@ -181,8 +173,8 @@ module Rails
instance.configure(&block)
end
- protected
- def generate_railtie_name(string) #:nodoc:
+ private
+ def generate_railtie_name(string)
ActiveSupport::Inflector.underscore(string).tr("/", "_")
end
@@ -195,6 +187,16 @@ module Rails
super
end
end
+
+ # receives an instance variable identifier, set the variable value if is
+ # blank and append given block to value, which will be used later in
+ # `#each_registered_block(type, &block)`
+ def register_block_for(type, &blk)
+ var_name = "@#{type}"
+ blocks = instance_variable_defined?(var_name) ? instance_variable_get(var_name) : instance_variable_set(var_name, [])
+ blocks << blk if blk
+ blocks
+ end
end
delegate :railtie_name, to: :class
@@ -241,6 +243,7 @@ module Rails
private
+ # run `&block` in every registered block in `#register_block_for`
def each_registered_block(type, &block)
klass = self.class
while klass.respond_to?(type)
diff --git a/railties/lib/rails/railtie/configurable.rb b/railties/lib/rails/railtie/configurable.rb
index 39f1f87575..2a8295426e 100644
--- a/railties/lib/rails/railtie/configurable.rb
+++ b/railties/lib/rails/railtie/configurable.rb
@@ -24,7 +24,7 @@ module Rails
class_eval(&block)
end
- protected
+ private
def method_missing(*args, &block)
instance.send(*args, &block)
diff --git a/railties/lib/rails/source_annotation_extractor.rb b/railties/lib/rails/source_annotation_extractor.rb
index f0df76d3f3..3a48c4c496 100644
--- a/railties/lib/rails/source_annotation_extractor.rb
+++ b/railties/lib/rails/source_annotation_extractor.rb
@@ -44,7 +44,7 @@ class SourceAnnotationExtractor
#
# 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={})
+ def to_s(options = {})
s = "[#{line.to_s.rjust(options[:indent])}] "
s << "[#{tag}] " if options[:tag]
s << text
@@ -66,7 +66,7 @@ class SourceAnnotationExtractor
# See <tt>#find_in</tt> for a list of file extensions that will be taken into account.
#
# This class method is the single entry point for the rake tasks.
- def self.enumerate(tag, options={})
+ def self.enumerate(tag, options = {})
extractor = new(tag)
dirs = options.delete(:dirs) || Annotation.directories
extractor.display(extractor.find(dirs), options)
@@ -116,7 +116,7 @@ class SourceAnnotationExtractor
# Otherwise it returns an empty hash.
def extract_annotations_from(file, pattern)
lineno = 0
- result = File.readlines(file).inject([]) do |list, line|
+ result = File.readlines(file, encoding: Encoding::BINARY).inject([]) do |list, line|
lineno += 1
next list unless line =~ pattern
list << Annotation.new(lineno, $1, $2)
@@ -126,7 +126,7 @@ class SourceAnnotationExtractor
# Prints the mapping from filenames to annotations in +results+ ordered by filename.
# The +options+ hash is passed to each annotation's +to_s+.
- def display(results, options={})
+ def display(results, options = {})
options[:indent] = results.flat_map { |f, a| a.map(&:line) }.max.to_s.size
results.keys.sort.each do |file|
puts "#{file}:"
diff --git a/railties/lib/rails/tasks/statistics.rake b/railties/lib/rails/tasks/statistics.rake
index a6cdd1e99c..8265aef10b 100644
--- a/railties/lib/rails/tasks/statistics.rake
+++ b/railties/lib/rails/tasks/statistics.rake
@@ -8,7 +8,7 @@ STATS_DIRECTORIES = [
%w(Models app/models),
%w(Mailers app/mailers),
%w(Channels app/channels),
- %w(Javascripts app/assets/javascripts),
+ %w(JavaScripts app/assets/javascripts),
%w(Libraries lib/),
%w(Tasks lib/tasks),
%w(APIs app/apis),
diff --git a/railties/lib/rails/test_unit/railtie.rb b/railties/lib/rails/test_unit/railtie.rb
index d0fc795515..746120e6a1 100644
--- a/railties/lib/rails/test_unit/railtie.rb
+++ b/railties/lib/rails/test_unit/railtie.rb
@@ -14,7 +14,9 @@ module Rails
end
initializer "test_unit.line_filtering" do
- ActiveSupport::TestCase.extend Rails::LineFiltering
+ ActiveSupport.on_load(:active_support_test_case) {
+ ActiveSupport::TestCase.extend Rails::LineFiltering
+ }
end
rake_tasks do
diff --git a/railties/test/app_loader_test.rb b/railties/test/app_loader_test.rb
index 7fd5c72c1c..85f5502b4d 100644
--- a/railties/test/app_loader_test.rb
+++ b/railties/test/app_loader_test.rb
@@ -17,7 +17,7 @@ class AppLoaderTest < ActiveSupport::TestCase
end
end
- def write(filename, contents=nil)
+ def write(filename, contents = nil)
FileUtils.mkdir_p(File.dirname(filename))
File.write(filename, contents)
end
diff --git a/railties/test/application/assets_test.rb b/railties/test/application/assets_test.rb
index 741ae543e0..f38cacd6da 100644
--- a/railties/test/application/assets_test.rb
+++ b/railties/test/application/assets_test.rb
@@ -384,7 +384,7 @@ module ApplicationTests
get "/assets/demo.js"
assert_match "alert()", last_response.body
- assert_equal nil, last_response.headers["Set-Cookie"]
+ assert_nil last_response.headers["Set-Cookie"]
end
test "files in any assets/ directories are not added to Sprockets" do
@@ -475,9 +475,9 @@ module ApplicationTests
class ::PostsController < ActionController::Base; end
- get "/posts", {}, "HTTPS"=>"off"
+ get "/posts", {}, "HTTPS" => "off"
assert_match('src="http://example.com/assets/application.self.js', last_response.body)
- get "/posts", {}, "HTTPS"=>"on"
+ get "/posts", {}, "HTTPS" => "on"
assert_match('src="https://example.com/assets/application.self.js', last_response.body)
end
diff --git a/railties/test/application/configuration/custom_test.rb b/railties/test/application/configuration/custom_test.rb
index 13fc98f0d6..3c675eec71 100644
--- a/railties/test/application/configuration/custom_test.rb
+++ b/railties/test/application/configuration/custom_test.rb
@@ -28,7 +28,7 @@ module ApplicationTests
assert_equal 3, x.payment_processing.retries
assert_equal true, x.super_debugger
assert_equal false, x.hyper_debugger
- assert_equal nil, x.nil_debugger
+ assert_nil x.nil_debugger
assert_nil x.i_do_not_exist.zomg
end
diff --git a/railties/test/application/configuration_test.rb b/railties/test/application/configuration_test.rb
index b0f5b30174..31c3b5cf52 100644
--- a/railties/test/application/configuration_test.rb
+++ b/railties/test/application/configuration_test.rb
@@ -614,7 +614,7 @@ module ApplicationTests
app "development"
assert_equal "b3c631c314c0bbca50c1b2843150fe33", app.config.secret_token
- assert_equal nil, app.secrets.secret_key_base
+ assert_nil app.secrets.secret_key_base
assert_equal app.key_generator.class, ActiveSupport::LegacyKeyGenerator
end
@@ -630,12 +630,26 @@ module ApplicationTests
app "development"
assert_equal "", app.config.secret_token
- assert_equal nil, app.secrets.secret_key_base
+ assert_nil app.secrets.secret_key_base
assert_raise ArgumentError, /\AA secret is required/ do
app.key_generator
end
end
+ test "that nested keys are symbolized the same as parents for hashes more than one level deep" do
+ app_file "config/secrets.yml", <<-YAML
+ development:
+ smtp_settings:
+ address: "smtp.example.com"
+ user_name: "postmaster@example.com"
+ password: "697361616320736c6f616e2028656c6f7265737429"
+ YAML
+
+ app "development"
+
+ assert_equal "697361616320736c6f616e2028656c6f7265737429", app.secrets.smtp_settings[:password]
+ end
+
test "protect from forgery is the default in a new app" do
make_basic_app
@@ -1043,7 +1057,7 @@ module ApplicationTests
app "development"
- post "/posts", post: { "title" =>"zomg" }
+ post "/posts", post: { "title" => "zomg" }
assert_equal "permitted", last_response.body
end
@@ -1067,7 +1081,7 @@ module ApplicationTests
assert_equal :raise, ActionController::Parameters.action_on_unpermitted_parameters
- post "/posts", post: { "title" =>"zomg" }
+ post "/posts", post: { "title" => "zomg" }
assert_match "We're sorry, but something went wrong", last_response.body
end
@@ -1107,7 +1121,7 @@ module ApplicationTests
assert_equal :raise, ActionController::Parameters.action_on_unpermitted_parameters
- post "/posts", post: { "title" =>"zomg" }, format: "json"
+ post "/posts", post: { "title" => "zomg" }, format: "json"
assert_equal 200, last_response.status
end
@@ -1190,7 +1204,7 @@ module ApplicationTests
application.config.session_store :disabled
end
- assert_equal nil, app.config.session_store
+ assert_nil app.config.session_store
end
test "default session store initializer sets session store to cookie store" do
@@ -1518,5 +1532,24 @@ module ApplicationTests
assert_equal :default, Rails.configuration.debug_exception_response_format
end
+
+ test "controller force_ssl declaration can be used even if session_store is disabled" do
+ make_basic_app do |application|
+ application.config.session_store :disabled
+ end
+
+ class ::OmgController < ActionController::Base
+ force_ssl
+
+ def index
+ render plain: "Yay! You're on Rails!"
+ end
+ end
+
+ get "/"
+
+ assert_equal 301, last_response.status
+ assert_equal "https://example.org/", last_response.location
+ end
end
end
diff --git a/railties/test/application/generators_test.rb b/railties/test/application/generators_test.rb
index 0153f94504..d2ce14f594 100644
--- a/railties/test/application/generators_test.rb
+++ b/railties/test/application/generators_test.rb
@@ -169,5 +169,20 @@ module ApplicationTests
assert File.exist?(File.join(rails_root, "app/views/notifier_mailer/foo.text.erb"))
assert File.exist?(File.join(rails_root, "app/views/notifier_mailer/foo.html.erb"))
end
+
+ test "ARGV is mutated as expected" do
+ require "#{app_path}/config/environment"
+ Rails::Command.const_set("APP_PATH", "rails/all")
+
+ FileUtils.cd(rails_root) do
+ ARGV = ["mailer", "notifier", "foo"]
+ Rails::Command.const_set("ARGV", ARGV)
+ quietly { Rails::Command.invoke :generate, ARGV }
+
+ assert_equal ["notifier", "foo"], ARGV
+ end
+
+ Rails::Command.send(:remove_const, "APP_PATH")
+ end
end
end
diff --git a/railties/test/application/initializers/frameworks_test.rb b/railties/test/application/initializers/frameworks_test.rb
index b2cd339c1a..32bce7d372 100644
--- a/railties/test/application/initializers/frameworks_test.rb
+++ b/railties/test/application/initializers/frameworks_test.rb
@@ -185,7 +185,7 @@ module ApplicationTests
test "if there's no config.active_support.bare, all of ActiveSupport is required" do
use_frameworks []
require "#{app_path}/config/environment"
- assert_nothing_raised { [1,2,3].sample }
+ assert_nothing_raised { [1, 2, 3].sample }
end
test "config.active_support.bare does not require all of ActiveSupport" do
diff --git a/railties/test/application/initializers/hooks_test.rb b/railties/test/application/initializers/hooks_test.rb
index 0309d02730..36926c50ff 100644
--- a/railties/test/application/initializers/hooks_test.rb
+++ b/railties/test/application/initializers/hooks_test.rb
@@ -31,7 +31,7 @@ module ApplicationTests
RUBY
require "#{app_path}/config/environment"
- assert_equal [1,2,3], $initialization_callbacks
+ assert_equal [1, 2, 3], $initialization_callbacks
end
test "hooks block works correctly with eager_load" do
@@ -46,7 +46,7 @@ module ApplicationTests
RUBY
require "#{app_path}/config/environment"
- assert_equal [1,2,3,4], $initialization_callbacks
+ assert_equal [1, 2, 3, 4], $initialization_callbacks
end
test "after_initialize runs after frameworks have been initialized" do
diff --git a/railties/test/application/loading_test.rb b/railties/test/application/loading_test.rb
index 38f96f3ab9..c75a25bc6f 100644
--- a/railties/test/application/loading_test.rb
+++ b/railties/test/application/loading_test.rb
@@ -357,7 +357,7 @@ class LoadingTest < ActiveSupport::TestCase
assert Rails.application.initialized?
end
- protected
+ private
def setup_ar!
ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
diff --git a/railties/test/application/middleware/session_test.rb b/railties/test/application/middleware/session_test.rb
index 0e4acfdcec..959a629ede 100644
--- a/railties/test/application/middleware/session_test.rb
+++ b/railties/test/application/middleware/session_test.rb
@@ -173,7 +173,7 @@ module ApplicationTests
secret = app.key_generator.generate_key("encrypted cookie")
sign_secret = app.key_generator.generate_key("signed encrypted cookie")
- encryptor = ActiveSupport::MessageEncryptor.new(secret, sign_secret)
+ encryptor = ActiveSupport::MessageEncryptor.new(secret[0, ActiveSupport::MessageEncryptor.key_len], sign_secret)
get "/foo/read_raw_cookie"
assert_equal 1, encryptor.decrypt_and_verify(last_response.body)["foo"]
@@ -222,7 +222,7 @@ module ApplicationTests
secret = app.key_generator.generate_key("encrypted cookie")
sign_secret = app.key_generator.generate_key("signed encrypted cookie")
- encryptor = ActiveSupport::MessageEncryptor.new(secret, sign_secret)
+ encryptor = ActiveSupport::MessageEncryptor.new(secret[0, ActiveSupport::MessageEncryptor.key_len], sign_secret)
get "/foo/read_raw_cookie"
assert_equal 1, encryptor.decrypt_and_verify(last_response.body)["foo"]
@@ -281,7 +281,7 @@ module ApplicationTests
secret = app.key_generator.generate_key("encrypted cookie")
sign_secret = app.key_generator.generate_key("signed encrypted cookie")
- encryptor = ActiveSupport::MessageEncryptor.new(secret, sign_secret)
+ encryptor = ActiveSupport::MessageEncryptor.new(secret[0, ActiveSupport::MessageEncryptor.key_len], sign_secret)
get "/foo/read_raw_cookie"
assert_equal 2, encryptor.decrypt_and_verify(last_response.body)["foo"]
diff --git a/railties/test/application/middleware_test.rb b/railties/test/application/middleware_test.rb
index 9baf7360a5..be41dcb299 100644
--- a/railties/test/application/middleware_test.rb
+++ b/railties/test/application/middleware_test.rb
@@ -246,7 +246,7 @@ module ApplicationTests
get "/", {}, "HTTP_IF_NONE_MATCH" => etag
assert_equal 304, last_response.status
assert_equal "", last_response.body
- assert_equal nil, last_response.headers["Content-Type"]
+ assert_nil last_response.headers["Content-Type"]
assert_equal "max-age=0, private, must-revalidate", last_response.headers["Cache-Control"]
assert_equal etag, last_response.headers["Etag"]
@@ -255,7 +255,7 @@ module ApplicationTests
assert_equal "", last_response.body
assert_equal "text/plain; charset=utf-8", last_response.headers["Content-Type"]
assert_equal "no-cache", last_response.headers["Cache-Control"]
- assert_equal nil, last_response.headers["Etag"]
+ assert_nil last_response.headers["Etag"]
end
test "ORIGINAL_FULLPATH is passed to env" do
diff --git a/railties/test/application/rake_test.rb b/railties/test/application/rake_test.rb
index 5fd5507453..d80a45a83f 100644
--- a/railties/test/application/rake_test.rb
+++ b/railties/test/application/rake_test.rb
@@ -159,13 +159,13 @@ module ApplicationTests
end
RUBY
expected_output = [" Prefix Verb URI Pattern Controller#Action",
- " admin_post POST /admin/post(.:format) admin/posts#create",
" new_admin_post GET /admin/post/new(.:format) admin/posts#new",
"edit_admin_post GET /admin/post/edit(.:format) admin/posts#edit",
- " GET /admin/post(.:format) admin/posts#show",
+ " admin_post GET /admin/post(.:format) admin/posts#show",
" PATCH /admin/post(.:format) admin/posts#update",
" PUT /admin/post(.:format) admin/posts#update",
- " DELETE /admin/post(.:format) admin/posts#destroy\n"].join("\n")
+ " DELETE /admin/post(.:format) admin/posts#destroy",
+ " POST /admin/post(.:format) admin/posts#create\n"].join("\n")
output = Dir.chdir(app_path) { `bin/rails routes -c Admin::PostController` }
assert_equal expected_output, output
@@ -174,6 +174,26 @@ module ApplicationTests
assert_equal expected_output, output
end
+ def test_singular_resource_output_in_rake_routes
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ resource :post
+ end
+ RUBY
+
+ expected_output = [" Prefix Verb URI Pattern Controller#Action",
+ " new_post GET /post/new(.:format) posts#new",
+ "edit_post GET /post/edit(.:format) posts#edit",
+ " post GET /post(.:format) posts#show",
+ " PATCH /post(.:format) posts#update",
+ " PUT /post(.:format) posts#update",
+ " DELETE /post(.:format) posts#destroy",
+ " POST /post(.:format) posts#create\n"].join("\n")
+
+ output = Dir.chdir(app_path) { `bin/rails routes -c PostController` }
+ assert_equal expected_output, output
+ end
+
def test_rails_routes_with_global_search_key
app_file "config/routes.rb", <<-RUBY
Rails.application.routes.draw do
@@ -367,14 +387,14 @@ module ApplicationTests
bin/rails generate model product name:string;
bin/rails db:migrate db:schema:cache:dump`
end
- assert File.exist?(File.join(app_path, "db", "schema_cache.dump"))
+ assert File.exist?(File.join(app_path, "db", "schema_cache.yml"))
end
def test_rake_clear_schema_cache
Dir.chdir(app_path) do
`bin/rails db:schema:cache:dump db:schema:cache:clear`
end
- assert !File.exist?(File.join(app_path, "db", "schema_cache.dump"))
+ assert !File.exist?(File.join(app_path, "db", "schema_cache.yml"))
end
def test_copy_templates
diff --git a/railties/test/application/runner_test.rb b/railties/test/application/runner_test.rb
index 77e7a2cca5..7d058f6ee6 100644
--- a/railties/test/application/runner_test.rb
+++ b/railties/test/application/runner_test.rb
@@ -43,6 +43,15 @@ module ApplicationTests
assert_match "42", Dir.chdir(app_path) { `bin/rails runner "bin/count_users.rb"` }
end
+ def test_no_minitest_loaded_in_production_mode
+ app_file "bin/print_features.rb", <<-SCRIPT
+ p $LOADED_FEATURES.grep(/minitest/)
+ SCRIPT
+ assert_match "[]", Dir.chdir(app_path) {
+ `RAILS_ENV=production bin/rails runner "bin/print_features.rb"`
+ }
+ end
+
def test_should_set_dollar_0_to_file
app_file "bin/dollar0.rb", <<-SCRIPT
puts $0
@@ -59,6 +68,14 @@ module ApplicationTests
assert_match "bin/program_name.rb", Dir.chdir(app_path) { `bin/rails runner "bin/program_name.rb"` }
end
+ def test_passes_extra_args_to_file
+ app_file "bin/program_name.rb", <<-SCRIPT
+ p ARGV
+ SCRIPT
+
+ assert_match %w( a b ).to_s, Dir.chdir(app_path) { `bin/rails runner "bin/program_name.rb" a b` }
+ end
+
def test_with_hook
add_to_config <<-RUBY
runner do |app|
diff --git a/railties/test/application/test_runner_test.rb b/railties/test/application/test_runner_test.rb
index b442891769..0939587960 100644
--- a/railties/test/application/test_runner_test.rb
+++ b/railties/test/application/test_runner_test.rb
@@ -495,7 +495,7 @@ module ApplicationTests
create_test_file :models, "post", pass: false
# This specifically verifies TEST for backwards compatibility with rake test
# as bin/rails test already supports running tests from a single file more cleanly.
- output = Dir.chdir(app_path) { `bin/rake test TEST=test/models/post_test.rb` }
+ output = Dir.chdir(app_path) { `bin/rake test TEST=test/models/post_test.rb` }
assert_match "PostTest", output, "passing TEST= should run selected test"
assert_no_match "AccountTest", output, "passing TEST= should only run selected test"
@@ -504,7 +504,7 @@ module ApplicationTests
def test_pass_rake_options
create_test_file :models, "account"
- output = Dir.chdir(app_path) { `bin/rake --rakefile Rakefile --trace=stdout test` }
+ output = Dir.chdir(app_path) { `bin/rake --rakefile Rakefile --trace=stdout test` }
assert_match "1 runs, 1 assertions", output
assert_match "Execute test", output
@@ -512,26 +512,26 @@ module ApplicationTests
def test_rails_db_create_all_restores_db_connection
create_test_file :models, "account"
- output = Dir.chdir(app_path) { `bin/rails db:create:all db:migrate && echo ".tables" | rails dbconsole` }
+ output = Dir.chdir(app_path) { `bin/rails db:create:all db:migrate && echo ".tables" | rails dbconsole` }
assert_match "ar_internal_metadata", output, "tables should be dumped"
end
def test_rails_db_create_all_restores_db_connection_after_drop
create_test_file :models, "account"
Dir.chdir(app_path) { `bin/rails db:create:all` } # create all to avoid warnings
- output = Dir.chdir(app_path) { `bin/rails db:drop:all db:create:all db:migrate && echo ".tables" | rails dbconsole` }
+ output = Dir.chdir(app_path) { `bin/rails db:drop:all db:create:all db:migrate && echo ".tables" | rails dbconsole` }
assert_match "ar_internal_metadata", output, "tables should be dumped"
end
def test_rake_passes_TESTOPTS_to_minitest
create_test_file :models, "account"
- output = Dir.chdir(app_path) { `bin/rake test TESTOPTS=-v` }
+ output = Dir.chdir(app_path) { `bin/rake test TESTOPTS=-v` }
assert_match "AccountTest#test_truth", output, "passing TEST= should run selected test"
end
def test_rake_passes_multiple_TESTOPTS_to_minitest
create_test_file :models, "account"
- output = Dir.chdir(app_path) { `bin/rake test TESTOPTS='-v --seed=1234'` }
+ output = Dir.chdir(app_path) { `bin/rake test TESTOPTS='-v --seed=1234'` }
assert_match "AccountTest#test_truth", output, "passing TEST= should run selected test"
assert_match "seed=1234", output, "passing TEST= should run selected test"
end
diff --git a/railties/test/backtrace_cleaner_test.rb b/railties/test/backtrace_cleaner_test.rb
index 17201d6f77..f71e56f323 100644
--- a/railties/test/backtrace_cleaner_test.rb
+++ b/railties/test/backtrace_cleaner_test.rb
@@ -15,7 +15,7 @@ class BacktraceCleanerTest < ActiveSupport::TestCase
test "should format installed gems not in Gem.default_dir correctly" do
target_dir = Gem.path.detect { |p| p != Gem.default_dir }
# skip this test if default_dir is the only directory on Gem.path
- if @target_dir
+ if target_dir
backtrace = [ "#{target_dir}/gems/nosuchgem-1.2.3/lib/foo.rb" ]
result = @cleaner.clean(backtrace, :all)
assert_equal "nosuchgem (1.2.3) lib/foo.rb", result[0]
diff --git a/railties/test/code_statistics_calculator_test.rb b/railties/test/code_statistics_calculator_test.rb
index 2dba3c6e96..1bd4225f34 100644
--- a/railties/test/code_statistics_calculator_test.rb
+++ b/railties/test/code_statistics_calculator_test.rb
@@ -24,7 +24,7 @@ class CodeStatisticsCalculatorTest < ActiveSupport::TestCase
end
end
- test "count number of methods in MiniTest file" do
+ test "count number of methods in Minitest file" do
code = <<-RUBY
class FooTest < ActionController::TestCase
test 'expectation' do
@@ -52,7 +52,7 @@ class CodeStatisticsCalculatorTest < ActiveSupport::TestCase
assert_equal 3, @code_statistics_calculator.classes
assert_equal 4, @code_statistics_calculator.methods
- code_statistics_calculator_2 = CodeStatisticsCalculator.new(2, 3, 4, 5)
+ code_statistics_calculator_2 = CodeStatisticsCalculator.new(2, 3, 4, 5)
@code_statistics_calculator.add(code_statistics_calculator_2)
assert_equal 3, @code_statistics_calculator.lines
diff --git a/railties/test/commands/dbconsole_test.rb b/railties/test/commands/dbconsole_test.rb
index 2ddb269eae..0f8c5dbb79 100644
--- a/railties/test/commands/dbconsole_test.rb
+++ b/railties/test/commands/dbconsole_test.rb
@@ -15,15 +15,15 @@ class Rails::DBConsoleTest < ActiveSupport::TestCase
def test_config_with_db_config_only
config_sample = {
- "test"=> {
- "adapter"=> "sqlite3",
- "host"=> "localhost",
- "port"=> "9000",
- "database"=> "foo_test",
- "user"=> "foo",
- "password"=> "bar",
- "pool"=> "5",
- "timeout"=> "3000"
+ "test" => {
+ "adapter" => "sqlite3",
+ "host" => "localhost",
+ "port" => "9000",
+ "database" => "foo_test",
+ "user" => "foo",
+ "password" => "bar",
+ "pool" => "5",
+ "timeout" => "3000"
}
}
app_db_config(config_sample) do
diff --git a/railties/test/commands/server_test.rb b/railties/test/commands/server_test.rb
index 391886bf33..2e8076ed2b 100644
--- a/railties/test/commands/server_test.rb
+++ b/railties/test/commands/server_test.rb
@@ -63,7 +63,7 @@ class Rails::ServerTest < ActiveSupport::TestCase
args = []
options = Rails::Server::Options.new.parse!(args)
merged_options = Rails::Server.new.default_options.merge(options)
- assert_equal nil, merged_options[:caching]
+ assert_nil merged_options[:caching]
end
def test_caching_with_option
diff --git a/railties/test/generators/actions_test.rb b/railties/test/generators/actions_test.rb
index 0a26897a4d..360e8e97d7 100644
--- a/railties/test/generators/actions_test.rb
+++ b/railties/test/generators/actions_test.rb
@@ -369,7 +369,7 @@ F
assert_equal("", action(:log, :yes, "YES"))
end
- protected
+ private
def action(*args, &block)
capture(:stdout) { generator.send(*args, &block) }
diff --git a/railties/test/generators/api_app_generator_test.rb b/railties/test/generators/api_app_generator_test.rb
index bbb814ef4e..c54d9cc599 100644
--- a/railties/test/generators/api_app_generator_test.rb
+++ b/railties/test/generators/api_app_generator_test.rb
@@ -35,7 +35,6 @@ class ApiAppGeneratorTest < Rails::Generators::TestCase
assert_file "Gemfile" do |content|
assert_no_match(/gem 'coffee-rails'/, content)
- assert_no_match(/gem 'jquery-rails'/, content)
assert_no_match(/gem 'sass-rails'/, content)
assert_no_match(/gem 'web-console'/, content)
assert_match(/# gem 'jbuilder'/, content)
@@ -106,10 +105,10 @@ class ApiAppGeneratorTest < Rails::Generators::TestCase
%w(app/assets
app/helpers
app/views/layouts/application.html.erb
+ bin/yarn
config/initializers/assets.rb
config/initializers/cookies_serializer.rb
lib/assets
- vendor/assets
test/helpers
tmp/cache/assets
public/404.html
@@ -117,6 +116,8 @@ class ApiAppGeneratorTest < Rails::Generators::TestCase
public/500.html
public/apple-touch-icon-precomposed.png
public/apple-touch-icon.png
- public/favicon.ico)
+ public/favicon.icon
+ vendor/package.json
+ )
end
end
diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb
index 8f7fa1155f..079ee3765c 100644
--- a/railties/test/generators/app_generator_test.rb
+++ b/railties/test/generators/app_generator_test.rb
@@ -43,9 +43,6 @@ DEFAULT_APP_FILES = %w(
test/mailers
test/integration
vendor
- vendor/assets
- vendor/assets/stylesheets
- vendor/assets/javascripts
tmp
tmp/cache
tmp/cache/assets
@@ -356,12 +353,19 @@ class AppGeneratorTest < Rails::Generators::TestCase
def test_generator_if_skip_active_record_is_given
run_generator [destination_root, "--skip-active-record"]
+ assert_no_directory "db/"
assert_no_file "config/database.yml"
assert_no_file "app/models/application_record.rb"
assert_file "config/application.rb", /#\s+require\s+["']active_record\/railtie["']/
assert_file "test/test_helper.rb" do |helper_content|
assert_no_match(/fixtures :all/, helper_content)
end
+ assert_file "bin/setup" do |setup_content|
+ assert_no_match(/db:setup/, setup_content)
+ end
+ assert_file "bin/update" do |update_content|
+ assert_no_match(/db:migrate/, update_content)
+ end
assert_file "config/initializers/new_framework_defaults.rb" do |initializer_content|
assert_no_match(/belongs_to_required_by_default/, initializer_content)
@@ -398,7 +402,6 @@ class AppGeneratorTest < Rails::Generators::TestCase
assert_match(/#\s+require\s+["']sprockets\/railtie["']/, content)
end
assert_file "Gemfile" do |content|
- assert_no_match(/jquery-rails/, content)
assert_no_match(/sass-rails/, content)
assert_no_match(/uglifier/, content)
assert_no_match(/coffee-rails/, content)
@@ -411,6 +414,9 @@ class AppGeneratorTest < Rails::Generators::TestCase
assert_no_match(/config\.assets\.js_compressor = :uglifier/, content)
assert_no_match(/config\.assets\.css_compressor = :sass/, content)
end
+ assert_file "config/initializers/new_framework_defaults.rb" do |content|
+ assert_no_match(/unknown_asset_fallback/, content)
+ end
end
def test_generator_if_skip_action_cable_is_given
@@ -438,29 +444,25 @@ class AppGeneratorTest < Rails::Generators::TestCase
end
end
- def test_jquery_is_the_default_javascript_library
+ def test_rails_ujs_is_the_default_ujs_library
run_generator
assert_file "app/assets/javascripts/application.js" do |contents|
- assert_match %r{^//= require jquery}, contents
- assert_match %r{^//= require jquery_ujs}, contents
+ assert_match %r{^//= require rails-ujs}, contents
end
- assert_gem "jquery-rails"
end
- def test_other_javascript_libraries
- run_generator [destination_root, "-j", "prototype"]
+ def test_inclusion_of_javascript_libraries_if_required
+ run_generator [destination_root, "-j", "jquery"]
assert_file "app/assets/javascripts/application.js" do |contents|
- assert_match %r{^//= require prototype}, contents
- assert_match %r{^//= require prototype_ujs}, contents
+ assert_match %r{^//= require jquery}, contents
end
- assert_gem "prototype-rails"
+ assert_gem "jquery-rails"
end
def test_javascript_is_skipped_if_required
run_generator [destination_root, "--skip-javascript"]
assert_no_file "app/assets/javascripts"
- assert_no_file "vendor/assets/javascripts"
assert_file "app/views/layouts/application.html.erb" do |contents|
assert_match(/stylesheet_link_tag\s+'application', media: 'all' %>/, contents)
@@ -469,7 +471,6 @@ class AppGeneratorTest < Rails::Generators::TestCase
assert_file "Gemfile" do |content|
assert_no_match(/coffee-rails/, content)
- assert_no_match(/jquery-rails/, content)
assert_no_match(/uglifier/, content)
end
@@ -478,6 +479,30 @@ class AppGeneratorTest < Rails::Generators::TestCase
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_generator_for_yarn
+ run_generator([destination_root])
+ assert_file "vendor/package.json", /dependencies/
+ assert_file "config/initializers/assets.rb", /node_modules/
+ end
+
+ def test_generator_for_yarn_skipped
+ run_generator([destination_root, "--skip-yarn"])
+ assert_no_file "vendor/package.json"
+
+ assert_file "config/initializers/assets.rb" do |content|
+ assert_no_match(/node_modules/, content)
+ end
+ end
+
def test_inclusion_of_jbuilder
run_generator
assert_gem "jbuilder"
@@ -716,7 +741,6 @@ class AppGeneratorTest < Rails::Generators::TestCase
test/helpers
test/integration
tmp
- vendor/assets/stylesheets
)
folders_with_keep.each do |folder|
assert_file("#{folder}/.keep")
@@ -764,7 +788,7 @@ class AppGeneratorTest < Rails::Generators::TestCase
assert_equal 3, @sequence_step
end
- protected
+ private
def stub_rails_application(root)
Rails.application.config.root = root
diff --git a/railties/test/generators/controller_generator_test.rb b/railties/test/generators/controller_generator_test.rb
index 9b986a636a..af86a0136f 100644
--- a/railties/test/generators/controller_generator_test.rb
+++ b/railties/test/generators/controller_generator_test.rb
@@ -65,7 +65,7 @@ class ControllerGeneratorTest < Rails::Generators::TestCase
def test_add_routes
run_generator
- assert_file "config/routes.rb", /get 'account\/foo'/, /get 'account\/bar'/
+ assert_file "config/routes.rb", /^ get 'account\/foo'/, /^ get 'account\/bar'/
end
def test_skip_routes
diff --git a/railties/test/generators/generator_test.rb b/railties/test/generators/generator_test.rb
index c4e4747468..904bade658 100644
--- a/railties/test/generators/generator_test.rb
+++ b/railties/test/generators/generator_test.rb
@@ -20,7 +20,7 @@ module Rails
end
def test_construction
- klass = make_builder_class
+ klass = make_builder_class
assert klass.start(["new", "blah"])
end
diff --git a/railties/test/generators/model_generator_test.rb b/railties/test/generators/model_generator_test.rb
index 701d3ceaf2..2b9f3ed7f2 100644
--- a/railties/test/generators/model_generator_test.rb
+++ b/railties/test/generators/model_generator_test.rb
@@ -212,10 +212,10 @@ class ModelGeneratorTest < Rails::Generators::TestCase
def test_migration_without_timestamps
ActiveRecord::Base.timestamped_migrations = false
run_generator ["account"]
- assert_file "db/migrate/001_create_accounts.rb", /class CreateAccounts < ActiveRecord::Migration\[[0-9.]+\]/
+ assert_file "db/migrate/001_create_accounts.rb", /class CreateAccounts < ActiveRecord::Migration\[[0-9.]+\]/
run_generator ["project"]
- assert_file "db/migrate/002_create_projects.rb", /class CreateProjects < ActiveRecord::Migration\[[0-9.]+\]/
+ assert_file "db/migrate/002_create_projects.rb", /class CreateProjects < ActiveRecord::Migration\[[0-9.]+\]/
ensure
ActiveRecord::Base.timestamped_migrations = true
end
@@ -300,7 +300,7 @@ class ModelGeneratorTest < Rails::Generators::TestCase
assert_file "test/fixtures/accounts.yml", /name: MyString/, /age: 1/
assert_generated_fixture("test/fixtures/accounts.yml",
- "one"=>{ "name"=>"MyString", "age"=>1 }, "two"=>{ "name"=>"MyString", "age"=>1 })
+ "one" => { "name" => "MyString", "age" => 1 }, "two" => { "name" => "MyString", "age" => 1 })
end
def test_fixtures_use_the_references_ids
@@ -308,7 +308,7 @@ class ModelGeneratorTest < Rails::Generators::TestCase
assert_file "test/fixtures/line_items.yml", /product: one\n cart: one/
assert_generated_fixture("test/fixtures/line_items.yml",
- "one"=>{ "product"=>"one", "cart"=>"one" }, "two"=>{ "product"=>"two", "cart"=>"two" })
+ "one" => { "product" => "one", "cart" => "one" }, "two" => { "product" => "two", "cart" => "two" })
end
def test_fixtures_use_the_references_ids_and_type
@@ -316,15 +316,15 @@ class ModelGeneratorTest < Rails::Generators::TestCase
assert_file "test/fixtures/line_items.yml", /product: one\n product_type: Product\n cart: one/
assert_generated_fixture("test/fixtures/line_items.yml",
- "one"=>{ "product"=>"one", "product_type"=>"Product", "cart"=>"one" },
- "two"=>{ "product"=>"two", "product_type"=>"Product", "cart"=>"two" })
+ "one" => { "product" => "one", "product_type" => "Product", "cart" => "one" },
+ "two" => { "product" => "two", "product_type" => "Product", "cart" => "two" })
end
def test_fixtures_respect_reserved_yml_keywords
run_generator ["LineItem", "no:integer", "Off:boolean", "ON:boolean"]
assert_generated_fixture("test/fixtures/line_items.yml",
- "one"=>{ "no"=>1, "Off"=>false, "ON"=>false }, "two"=>{ "no"=>1, "Off"=>false, "ON"=>false })
+ "one" => { "no" => 1, "Off" => false, "ON" => false }, "two" => { "no" => 1, "Off" => false, "ON" => false })
end
def test_fixture_is_skipped
@@ -343,7 +343,7 @@ class ModelGeneratorTest < Rails::Generators::TestCase
ActiveRecord::Base.pluralize_table_names = false
run_generator
assert_generated_fixture("test/fixtures/account.yml",
- "one"=>{ "name"=>"MyString", "age"=>1 }, "two"=>{ "name"=>"MyString", "age"=>1 })
+ "one" => { "name" => "MyString", "age" => 1 }, "two" => { "name" => "MyString", "age" => 1 })
ensure
ActiveRecord::Base.pluralize_table_names = original_pluralize_table_name
end
diff --git a/railties/test/generators/named_base_test.rb b/railties/test/generators/named_base_test.rb
index 0258b3b9d7..3015b5363b 100644
--- a/railties/test/generators/named_base_test.rb
+++ b/railties/test/generators/named_base_test.rb
@@ -131,7 +131,7 @@ class NamedBaseTest < Rails::Generators::TestCase
assert_name g, "admin.foos", :controller_i18n_scope
end
- protected
+ private
def assert_name(generator, value, method)
assert_equal value, generator.send(method)
diff --git a/railties/test/generators/plugin_generator_test.rb b/railties/test/generators/plugin_generator_test.rb
index 15079f2735..9921a80342 100644
--- a/railties/test/generators/plugin_generator_test.rb
+++ b/railties/test/generators/plugin_generator_test.rb
@@ -100,6 +100,14 @@ class PluginGeneratorTest < Rails::Generators::TestCase
end
end
+ def test_generating_adds_dummy_app_in_full_mode_without_sprockets
+ run_generator [destination_root, "-S", "--full"]
+
+ assert_file "test/dummy/config/environments/production.rb" do |contents|
+ assert_no_match(/config\.assets/, contents)
+ end
+ end
+
def test_generating_adds_dummy_app_rake_tasks_without_unit_test_files
run_generator [destination_root, "-T", "--mountable", "--dummy-path", "my_dummy_app"]
assert_file "Rakefile", /APP_RAKEFILE/
@@ -499,7 +507,6 @@ class PluginGeneratorTest < Rails::Generators::TestCase
assert_no_match("gemspec", contents)
assert_match(/gem 'rails'/, contents)
assert_match_sqlite3(contents)
- assert_no_match(/# gem "jquery-rails"/, contents)
end
end
@@ -707,7 +714,7 @@ class PluginGeneratorTest < Rails::Generators::TestCase
end
end
- protected
+ private
def action(*args, &block)
silence(:stdout) { generator.send(*args, &block) }
diff --git a/railties/test/generators/plugin_test_runner_test.rb b/railties/test/generators/plugin_test_runner_test.rb
index 7a10a2afa9..0bdf3b2726 100644
--- a/railties/test/generators/plugin_test_runner_test.rb
+++ b/railties/test/generators/plugin_test_runner_test.rb
@@ -88,7 +88,7 @@ class PluginTestRunnerTest < ActiveSupport::TestCase
def test_executed_only_once
create_test_file "foo"
- result = run_test_command("test/foo_test.rb")
+ result = run_test_command("test/foo_test.rb")
assert_equal 1, result.scan(/1 runs, 1 assertions, 0 failures/).length
end
diff --git a/railties/test/generators/shared_generator_tests.rb b/railties/test/generators/shared_generator_tests.rb
index 27b2fc8955..08b0e34fe2 100644
--- a/railties/test/generators/shared_generator_tests.rb
+++ b/railties/test/generators/shared_generator_tests.rb
@@ -28,7 +28,7 @@ module SharedGeneratorTests
def test_plugin_new_generate_pretend
run_generator ["testapp", "--pretend"]
- default_files.each { |path| assert_no_file File.join("testapp",path) }
+ default_files.each { |path| assert_no_file File.join("testapp", path) }
end
def test_invalid_database_option_raises_an_error
diff --git a/railties/test/isolation/abstract_unit.rb b/railties/test/isolation/abstract_unit.rb
index 6880cf306a..1902eac862 100644
--- a/railties/test/isolation/abstract_unit.rb
+++ b/railties/test/isolation/abstract_unit.rb
@@ -104,7 +104,7 @@ module TestHelpers
# Build an application by invoking the generator and going through the whole stack.
def build_app(options = {})
@prev_rails_env = ENV["RAILS_ENV"]
- ENV["RAILS_ENV"] = "development"
+ ENV["RAILS_ENV"] = "development"
ENV["SECRET_KEY_BASE"] ||= SecureRandom.hex(16)
FileUtils.rm_rf(app_path)
@@ -162,7 +162,6 @@ module TestHelpers
require "rails"
require "action_controller/railtie"
require "action_view/railtie"
- require "action_dispatch/middleware/flash"
@app = Class.new(Rails::Application)
@app.config.eager_load = false
diff --git a/railties/test/path_generation_test.rb b/railties/test/path_generation_test.rb
index 579e50ac95..c0b03d0c15 100644
--- a/railties/test/path_generation_test.rb
+++ b/railties/test/path_generation_test.rb
@@ -38,7 +38,7 @@ class PathGenerationTest < ActiveSupport::TestCase
host = uri_or_host.host unless path
path ||= uri_or_host.path
- params = { "PATH_INFO" => path,
+ params = { "PATH_INFO" => path,
"REQUEST_METHOD" => method,
"HTTP_HOST" => host }
diff --git a/railties/test/rails_info_test.rb b/railties/test/rails_info_test.rb
index 59e79de41a..9f4c5bb025 100644
--- a/railties/test/rails_info_test.rb
+++ b/railties/test/rails_info_test.rb
@@ -54,7 +54,7 @@ class InfoTest < ActiveSupport::TestCase
end
end
- protected
+ private
def properties
Rails::Info.properties
end
diff --git a/railties/test/railties/engine_test.rb b/railties/test/railties/engine_test.rb
index 397037a394..52d691b73b 100644
--- a/railties/test/railties/engine_test.rb
+++ b/railties/test/railties/engine_test.rb
@@ -133,7 +133,7 @@ module RailtiesTest
boot_rails
Dir.chdir(app_path) do
- output = `bundle exec rake railties:install:migrations`.split("\n")
+ output = `bundle exec rake railties:install:migrations`.split("\n")
assert_match(/Copied migration \d+_create_users.bukkits.rb from bukkits/, output.first)
assert_match(/Copied migration \d+_create_blogs.blog_engine.rb from blog_engine/, output.last)
@@ -169,7 +169,7 @@ module RailtiesTest
boot_rails
Dir.chdir(app_path) do
- output = `bundle exec rake railties:install:migrations`.split("\n")
+ output = `bundle exec rake railties:install:migrations`.split("\n")
assert_match(/Copied migration \d+_create_users.core_engine.rb from core_engine/, output.first)
assert_match(/Copied migration \d+_create_keys.api_engine.rb from api_engine/, output.last)
@@ -890,7 +890,7 @@ YAML
boot_rails
- assert_equal AppTemplate.railtie_namespace, AppTemplate::Engine
+ assert_equal AppTemplate::Engine, AppTemplate.railtie_namespace
end
test "properly reload routes" do
diff --git a/railties/test/railties/generators_test.rb b/railties/test/railties/generators_test.rb
index 732898d0c0..5c691b9ec4 100644
--- a/railties/test/railties/generators_test.rb
+++ b/railties/test/railties/generators_test.rb
@@ -29,7 +29,7 @@ module RailtiesTests
`#{Gem.ruby} #{RAILS_FRAMEWORK_ROOT}/railties/exe/rails #{cmd}`
end
- def build_engine(is_mountable=false)
+ def build_engine(is_mountable = false)
FileUtils.rm_rf(engine_path)
FileUtils.mkdir_p(engine_path)
diff --git a/tools/profile b/tools/profile
index f7d91e51cf..01e513c67a 100755
--- a/tools/profile
+++ b/tools/profile
@@ -12,7 +12,7 @@ module CodeTools
Error = Class.new(StandardError)
attr_reader :path, :mode
- def initialize(path, mode=nil)
+ def initialize(path, mode = nil)
assert_ruby_file_exists(path)
@path, @mode = path, mode
require "benchmark"
diff --git a/tools/test.rb b/tools/test.rb
index 7819c13ee2..ce546b382d 100644
--- a/tools/test.rb
+++ b/tools/test.rb
@@ -4,6 +4,8 @@ require "bundler"
Bundler.setup
require "rails/test_unit/minitest_plugin"
+require "rails/test_unit/line_filtering"
+require "active_support/test_case"
module Rails
# Necessary to get rerun-snippts working.
@@ -12,4 +14,7 @@ module Rails
end
end
+ActiveSupport::TestCase.extend Rails::LineFiltering
Rails::TestUnitReporter.executable = "bin/test"
+Minitest.run_via[:rails] = true
+require "active_support/testing/autorun"