aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.codeclimate.yml2
-rw-r--r--.rubocop.yml17
-rw-r--r--Gemfile8
-rw-r--r--Gemfile.lock221
-rw-r--r--RAILS_VERSION2
-rw-r--r--RELEASING_RAILS.md2
-rw-r--r--actioncable/CHANGELOG.md153
-rw-r--r--actioncable/lib/action_cable/gem_version.rb4
-rw-r--r--actioncable/lib/action_cable/server/broadcasting.rb2
-rw-r--r--actioncable/lib/action_cable/server/worker.rb1
-rw-r--r--actioncable/lib/action_cable/subscription_adapter/redis.rb4
-rw-r--r--actioncable/package.json2
-rw-r--r--actioncable/test/subscription_adapter/redis_test.rb4
-rw-r--r--actionmailbox/CHANGELOG.md19
-rw-r--r--actionmailbox/app/controllers/rails/conductor/action_mailbox/inbound_emails_controller.rb3
-rw-r--r--actionmailbox/lib/action_mailbox/gem_version.rb4
-rw-r--r--actionmailbox/lib/action_mailbox/router.rb10
-rw-r--r--actionmailbox/lib/action_mailbox/routing.rb4
-rw-r--r--actionmailbox/lib/rails/generators/test_unit/templates/mailbox_test.rb.tt2
-rw-r--r--actionmailbox/test/controllers/rails/action_mailbox/inbound_emails_controller_test.rb5
-rw-r--r--actionmailbox/test/unit/mailbox/routing_test.rb5
-rw-r--r--actionmailbox/test/unit/router_test.rb14
-rw-r--r--actionmailer/CHANGELOG.md80
-rw-r--r--actionmailer/lib/action_mailer/base.rb2
-rw-r--r--actionmailer/lib/action_mailer/gem_version.rb4
-rw-r--r--actionmailer/lib/action_mailer/preview.rb1
-rw-r--r--actionmailer/lib/action_mailer/test_case.rb2
-rw-r--r--actionmailer/lib/action_mailer/test_helper.rb1
-rw-r--r--actionmailer/test/base_test.rb1
-rw-r--r--actionmailer/test/caching_test.rb1
-rw-r--r--actionmailer/test/i18n_with_controller_test.rb1
-rw-r--r--actionmailer/test/legacy_delivery_job_test.rb1
-rw-r--r--actionmailer/test/mail_helper_test.rb1
-rw-r--r--actionmailer/test/mailers/proc_mailer.rb1
-rw-r--r--actionmailer/test/parameterized_test.rb1
-rw-r--r--actionpack/CHANGELOG.md251
-rw-r--r--actionpack/lib/abstract_controller.rb1
-rw-r--r--actionpack/lib/abstract_controller/base.rb1
-rw-r--r--actionpack/lib/abstract_controller/collector.rb1
-rw-r--r--actionpack/lib/abstract_controller/helpers.rb30
-rw-r--r--actionpack/lib/action_controller.rb2
-rw-r--r--actionpack/lib/action_controller/base.rb1
-rw-r--r--actionpack/lib/action_controller/caching.rb1
-rw-r--r--actionpack/lib/action_controller/metal.rb3
-rw-r--r--actionpack/lib/action_controller/metal/content_security_policy.rb1
-rw-r--r--actionpack/lib/action_controller/metal/data_streaming.rb2
-rw-r--r--actionpack/lib/action_controller/metal/feature_policy.rb46
-rw-r--r--actionpack/lib/action_controller/metal/instrumentation.rb1
-rw-r--r--actionpack/lib/action_controller/metal/live.rb4
-rw-r--r--actionpack/lib/action_controller/metal/mime_responds.rb2
-rw-r--r--actionpack/lib/action_controller/metal/params_wrapper.rb1
-rw-r--r--actionpack/lib/action_controller/metal/renderers.rb2
-rw-r--r--actionpack/lib/action_controller/metal/rendering.rb3
-rw-r--r--actionpack/lib/action_controller/metal/request_forgery_protection.rb2
-rw-r--r--actionpack/lib/action_controller/metal/streaming.rb1
-rw-r--r--actionpack/lib/action_controller/metal/strong_parameters.rb70
-rw-r--r--actionpack/lib/action_controller/template_assertions.rb2
-rw-r--r--actionpack/lib/action_controller/test_case.rb3
-rw-r--r--actionpack/lib/action_dispatch.rb2
-rw-r--r--actionpack/lib/action_dispatch/http/cache.rb1
-rw-r--r--actionpack/lib/action_dispatch/http/content_security_policy.rb35
-rw-r--r--actionpack/lib/action_dispatch/http/feature_policy.rb168
-rw-r--r--actionpack/lib/action_dispatch/http/filter_parameters.rb1
-rw-r--r--actionpack/lib/action_dispatch/http/filter_redirect.rb1
-rw-r--r--actionpack/lib/action_dispatch/http/headers.rb1
-rw-r--r--actionpack/lib/action_dispatch/http/mime_negotiation.rb1
-rw-r--r--actionpack/lib/action_dispatch/http/mime_type.rb2
-rw-r--r--actionpack/lib/action_dispatch/http/parameters.rb1
-rw-r--r--actionpack/lib/action_dispatch/http/request.rb1
-rw-r--r--actionpack/lib/action_dispatch/http/response.rb21
-rw-r--r--actionpack/lib/action_dispatch/http/url.rb1
-rw-r--r--actionpack/lib/action_dispatch/journey/formatter.rb3
-rw-r--r--actionpack/lib/action_dispatch/journey/gtg/builder.rb1
-rw-r--r--actionpack/lib/action_dispatch/journey/gtg/transition_table.rb1
-rw-r--r--actionpack/lib/action_dispatch/journey/nfa/transition_table.rb1
-rw-r--r--actionpack/lib/action_dispatch/journey/path/pattern.rb1
-rw-r--r--actionpack/lib/action_dispatch/journey/route.rb12
-rw-r--r--actionpack/lib/action_dispatch/journey/router.rb1
-rw-r--r--actionpack/lib/action_dispatch/journey/routes.rb1
-rw-r--r--actionpack/lib/action_dispatch/journey/scanner.rb1
-rw-r--r--actionpack/lib/action_dispatch/journey/visitors.rb3
-rw-r--r--actionpack/lib/action_dispatch/middleware/actionable_exceptions.rb39
-rw-r--r--actionpack/lib/action_dispatch/middleware/cookies.rb6
-rw-r--r--actionpack/lib/action_dispatch/middleware/debug_exceptions.rb5
-rw-r--r--actionpack/lib/action_dispatch/middleware/debug_view.rb10
-rw-r--r--actionpack/lib/action_dispatch/middleware/exception_wrapper.rb16
-rw-r--r--actionpack/lib/action_dispatch/middleware/host_authorization.rb2
-rw-r--r--actionpack/lib/action_dispatch/middleware/public_exceptions.rb1
-rw-r--r--actionpack/lib/action_dispatch/middleware/remote_ip.rb1
-rw-r--r--actionpack/lib/action_dispatch/middleware/session/abstract_store.rb2
-rw-r--r--actionpack/lib/action_dispatch/middleware/session/cookie_store.rb1
-rw-r--r--actionpack/lib/action_dispatch/middleware/show_exceptions.rb1
-rw-r--r--actionpack/lib/action_dispatch/middleware/stack.rb16
-rw-r--r--actionpack/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb13
-rw-r--r--actionpack/lib/action_dispatch/middleware/templates/rescues/_actions.text.erb0
-rw-r--r--actionpack/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb4
-rw-r--r--actionpack/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb2
-rw-r--r--actionpack/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb8
-rw-r--r--actionpack/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb2
-rw-r--r--actionpack/lib/action_dispatch/middleware/templates/rescues/layout.erb75
-rw-r--r--actionpack/lib/action_dispatch/middleware/templates/routes/_table.html.erb11
-rw-r--r--actionpack/lib/action_dispatch/railtie.rb2
-rw-r--r--actionpack/lib/action_dispatch/request/session.rb1
-rw-r--r--actionpack/lib/action_dispatch/routing/inspector.rb2
-rw-r--r--actionpack/lib/action_dispatch/routing/mapper.rb41
-rw-r--r--actionpack/lib/action_dispatch/routing/polymorphic_routes.rb2
-rw-r--r--actionpack/lib/action_dispatch/routing/route_set.rb7
-rw-r--r--actionpack/lib/action_dispatch/routing/url_for.rb2
-rw-r--r--actionpack/lib/action_dispatch/system_test_case.rb38
-rw-r--r--actionpack/lib/action_dispatch/system_testing/browser.rb23
-rw-r--r--actionpack/lib/action_dispatch/system_testing/driver.rb2
-rw-r--r--actionpack/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb3
-rw-r--r--actionpack/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb18
-rw-r--r--actionpack/lib/action_dispatch/system_testing/test_helpers/undef_methods.rb26
-rw-r--r--actionpack/lib/action_dispatch/testing/assertion_response.rb1
-rw-r--r--actionpack/lib/action_dispatch/testing/assertions.rb2
-rw-r--r--actionpack/lib/action_dispatch/testing/integration.rb12
-rw-r--r--actionpack/lib/action_dispatch/testing/test_response.rb2
-rw-r--r--actionpack/lib/action_pack/gem_version.rb4
-rw-r--r--actionpack/test/abstract_unit.rb2
-rw-r--r--actionpack/test/controller/api/conditional_get_test.rb1
-rw-r--r--actionpack/test/controller/content_type_test.rb29
-rw-r--r--actionpack/test/controller/filters_test.rb3
-rw-r--r--actionpack/test/controller/flash_test.rb1
-rw-r--r--actionpack/test/controller/helper_test.rb4
-rw-r--r--actionpack/test/controller/http_basic_authentication_test.rb2
-rw-r--r--actionpack/test/controller/http_digest_authentication_test.rb2
-rw-r--r--actionpack/test/controller/http_token_authentication_test.rb2
-rw-r--r--actionpack/test/controller/integration_test.rb21
-rw-r--r--actionpack/test/controller/localized_templates_test.rb2
-rw-r--r--actionpack/test/controller/log_subscriber_test.rb22
-rw-r--r--actionpack/test/controller/metal/renderers_test.rb4
-rw-r--r--actionpack/test/controller/mime/accept_format_test.rb1
-rw-r--r--actionpack/test/controller/mime/respond_to_test.rb78
-rw-r--r--actionpack/test/controller/new_base/render_template_test.rb1
-rw-r--r--actionpack/test/controller/new_base/render_test.rb1
-rw-r--r--actionpack/test/controller/parameters/accessors_test.rb33
-rw-r--r--actionpack/test/controller/parameters/log_on_unpermitted_params_test.rb1
-rw-r--r--actionpack/test/controller/parameters/mutators_test.rb27
-rw-r--r--actionpack/test/controller/parameters/nested_parameters_permit_test.rb30
-rw-r--r--actionpack/test/controller/params_parse_test.rb1
-rw-r--r--actionpack/test/controller/params_wrapper_test.rb1
-rw-r--r--actionpack/test/controller/render_js_test.rb3
-rw-r--r--actionpack/test/controller/render_json_test.rb15
-rw-r--r--actionpack/test/controller/render_test.rb25
-rw-r--r--actionpack/test/controller/render_xml_test.rb5
-rw-r--r--actionpack/test/controller/renderers_test.rb4
-rw-r--r--actionpack/test/controller/request_forgery_protection_test.rb1
-rw-r--r--actionpack/test/controller/rescue_test.rb2
-rw-r--r--actionpack/test/controller/resources_test.rb5
-rw-r--r--actionpack/test/controller/show_exceptions_test.rb11
-rw-r--r--actionpack/test/controller/test_case_test.rb1
-rw-r--r--actionpack/test/dispatch/actionable_exceptions_test.rb80
-rw-r--r--actionpack/test/dispatch/callbacks_test.rb1
-rw-r--r--actionpack/test/dispatch/content_security_policy_test.rb80
-rw-r--r--actionpack/test/dispatch/debug_exceptions_test.rb103
-rw-r--r--actionpack/test/dispatch/feature_policy_test.rb142
-rw-r--r--actionpack/test/dispatch/middleware_stack_test.rb6
-rw-r--r--actionpack/test/dispatch/request_id_test.rb2
-rw-r--r--actionpack/test/dispatch/request_test.rb1
-rw-r--r--actionpack/test/dispatch/response_test.rb65
-rw-r--r--actionpack/test/dispatch/routing_test.rb44
-rw-r--r--actionpack/test/dispatch/session/cookie_store_test.rb1
-rw-r--r--actionpack/test/dispatch/static_test.rb1
-rw-r--r--actionpack/test/dispatch/system_testing/driver_test.rb17
-rw-r--r--actionpack/test/dispatch/system_testing/screenshot_helper_test.rb8
-rw-r--r--actionpack/test/dispatch/system_testing/system_test_case_test.rb45
-rw-r--r--actionpack/test/journey/route/definition/scanner_test.rb1
-rw-r--r--actionpack/test/journey/route_test.rb41
-rw-r--r--actionpack/test/journey/router_test.rb1
-rw-r--r--actiontext/.gitignore1
-rw-r--r--actiontext/CHANGELOG.md22
-rw-r--r--actiontext/Rakefile8
-rw-r--r--actiontext/app/helpers/action_text/content_helper.rb23
-rw-r--r--actiontext/app/models/action_text/rich_text.rb2
-rw-r--r--actiontext/lib/action_text/engine.rb11
-rw-r--r--actiontext/lib/action_text/gem_version.rb4
-rw-r--r--actiontext/lib/action_text/system_test_helper.rb48
-rw-r--r--actiontext/lib/templates/installer.rb13
-rw-r--r--actiontext/package.json2
-rw-r--r--actiontext/test/application_system_test_case.rb9
-rw-r--r--actiontext/test/dummy/.babelrc18
-rw-r--r--actiontext/test/dummy/.browserslistrc1
-rw-r--r--actiontext/test/dummy/app/javascript/packs/application.js1
-rw-r--r--actiontext/test/dummy/app/views/messages/_form.html.erb3
-rw-r--r--actiontext/test/dummy/babel.config.js70
-rwxr-xr-xactiontext/test/dummy/bin/bundle2
-rwxr-xr-xactiontext/test/dummy/bin/webpack19
-rwxr-xr-xactiontext/test/dummy/bin/webpack-dev-server19
-rw-r--r--actiontext/test/dummy/config/webpack/development.js2
-rw-r--r--actiontext/test/dummy/config/webpack/production.js2
-rw-r--r--actiontext/test/dummy/config/webpack/test.js2
-rw-r--r--actiontext/test/dummy/config/webpacker.yml32
-rw-r--r--actiontext/test/dummy/package.json5
-rw-r--r--actiontext/test/dummy/postcss.config.js12
-rw-r--r--actiontext/test/dummy/yarn.lock2741
-rw-r--r--actiontext/test/system/system_test_helper_test.rb39
-rw-r--r--actiontext/test/unit/attachment_test.rb5
-rw-r--r--actiontext/test/unit/model_test.rb9
-rw-r--r--actionview/CHANGELOG.md245
-rw-r--r--actionview/app/assets/javascripts/README.md2
-rw-r--r--actionview/app/assets/javascripts/rails-ujs/features/remote.coffee4
-rw-r--r--actionview/lib/action_view.rb1
-rw-r--r--actionview/lib/action_view/base.rb1
-rw-r--r--actionview/lib/action_view/cache_expiry.rb22
-rw-r--r--actionview/lib/action_view/flows.rb1
-rw-r--r--actionview/lib/action_view/gem_version.rb4
-rw-r--r--actionview/lib/action_view/helpers/active_model_helper.rb1
-rw-r--r--actionview/lib/action_view/helpers/asset_tag_helper.rb3
-rw-r--r--actionview/lib/action_view/helpers/asset_url_helper.rb2
-rw-r--r--actionview/lib/action_view/helpers/cache_helper.rb1
-rw-r--r--actionview/lib/action_view/helpers/date_helper.rb1
-rw-r--r--actionview/lib/action_view/helpers/form_tag_helper.rb3
-rw-r--r--actionview/lib/action_view/helpers/number_helper.rb3
-rw-r--r--actionview/lib/action_view/helpers/rendering_helper.rb6
-rw-r--r--actionview/lib/action_view/helpers/tags/base.rb5
-rw-r--r--actionview/lib/action_view/helpers/tags/check_box.rb1
-rw-r--r--actionview/lib/action_view/helpers/tags/collection_check_boxes.rb1
-rw-r--r--actionview/lib/action_view/helpers/tags/collection_helpers.rb1
-rw-r--r--actionview/lib/action_view/helpers/tags/collection_radio_buttons.rb1
-rw-r--r--actionview/lib/action_view/helpers/tags/color_field.rb1
-rw-r--r--actionview/lib/action_view/helpers/tags/date_field.rb1
-rw-r--r--actionview/lib/action_view/helpers/tags/date_select.rb1
-rw-r--r--actionview/lib/action_view/helpers/tags/datetime_field.rb1
-rw-r--r--actionview/lib/action_view/helpers/tags/datetime_local_field.rb1
-rw-r--r--actionview/lib/action_view/helpers/tags/label.rb1
-rw-r--r--actionview/lib/action_view/helpers/tags/month_field.rb1
-rw-r--r--actionview/lib/action_view/helpers/tags/radio_button.rb1
-rw-r--r--actionview/lib/action_view/helpers/tags/select.rb1
-rw-r--r--actionview/lib/action_view/helpers/tags/text_field.rb1
-rw-r--r--actionview/lib/action_view/helpers/tags/time_field.rb1
-rw-r--r--actionview/lib/action_view/helpers/tags/week_field.rb1
-rw-r--r--actionview/lib/action_view/helpers/text_helper.rb1
-rw-r--r--actionview/lib/action_view/helpers/translation_helper.rb2
-rw-r--r--actionview/lib/action_view/helpers/url_helper.rb58
-rw-r--r--actionview/lib/action_view/layouts.rb3
-rw-r--r--actionview/lib/action_view/log_subscriber.rb1
-rw-r--r--actionview/lib/action_view/lookup_context.rb3
-rw-r--r--actionview/lib/action_view/path_set.rb1
-rw-r--r--actionview/lib/action_view/record_identifier.rb1
-rw-r--r--actionview/lib/action_view/renderer/abstract_renderer.rb10
-rw-r--r--actionview/lib/action_view/renderer/partial_renderer.rb11
-rw-r--r--actionview/lib/action_view/renderer/streaming_template_renderer.rb2
-rw-r--r--actionview/lib/action_view/renderer/template_renderer.rb3
-rw-r--r--actionview/lib/action_view/rendering.rb1
-rw-r--r--actionview/lib/action_view/template.rb3
-rw-r--r--actionview/lib/action_view/template/error.rb21
-rw-r--r--actionview/lib/action_view/template/handlers.rb2
-rw-r--r--actionview/lib/action_view/template/handlers/erb.rb1
-rw-r--r--actionview/lib/action_view/template/html.rb2
-rw-r--r--actionview/lib/action_view/template/resolver.rb12
-rw-r--r--actionview/lib/action_view/test_case.rb4
-rw-r--r--actionview/lib/action_view/testing/resolvers.rb39
-rw-r--r--actionview/lib/action_view/unbound_template.rb7
-rw-r--r--actionview/lib/action_view/view_paths.rb1
-rw-r--r--actionview/package.json2
-rw-r--r--actionview/test/abstract_unit.rb2
-rw-r--r--actionview/test/actionpack/abstract/abstract_controller_test.rb2
-rw-r--r--actionview/test/actionpack/abstract/layouts_test.rb3
-rw-r--r--actionview/test/actionpack/controller/render_test.rb25
-rw-r--r--actionview/test/active_record_unit.rb10
-rw-r--r--actionview/test/activerecord/form_helper_activerecord_test.rb1
-rw-r--r--actionview/test/lib/test_component.rb45
-rw-r--r--actionview/test/template/active_model_helper_test.rb10
-rw-r--r--actionview/test/template/compiled_templates_test.rb40
-rw-r--r--actionview/test/template/form_helper_test.rb1
-rw-r--r--actionview/test/template/form_options_helper_test.rb15
-rw-r--r--actionview/test/template/form_tag_helper_test.rb8
-rw-r--r--actionview/test/template/render_test.rb36
-rw-r--r--actionview/test/template/resolver_shared_tests.rb6
-rw-r--r--actionview/test/template/template_error_test.rb16
-rw-r--r--actionview/test/template/testing/fixture_resolver_test.rb22
-rw-r--r--actionview/test/template/translation_helper_test.rb5
-rw-r--r--actionview/test/template/url_helper_test.rb62
-rw-r--r--actionview/test/ujs/public/test/data-remote.js14
-rw-r--r--activejob/CHANGELOG.md140
-rw-r--r--activejob/lib/active_job/arguments.rb1
-rw-r--r--activejob/lib/active_job/exceptions.rb17
-rw-r--r--activejob/lib/active_job/gem_version.rb4
-rw-r--r--activejob/lib/active_job/serializers/date_serializer.rb1
-rw-r--r--activejob/lib/active_job/serializers/date_time_serializer.rb1
-rw-r--r--activejob/lib/active_job/serializers/duration_serializer.rb1
-rw-r--r--activejob/lib/active_job/serializers/object_serializer.rb1
-rw-r--r--activejob/lib/active_job/serializers/symbol_serializer.rb1
-rw-r--r--activejob/lib/active_job/serializers/time_serializer.rb1
-rw-r--r--activejob/lib/active_job/serializers/time_with_zone_serializer.rb1
-rw-r--r--activejob/lib/active_job/test_helper.rb3
-rw-r--r--activejob/test/cases/exceptions_test.rb25
-rw-r--r--activejob/test/cases/serializers_test.rb1
-rw-r--r--activejob/test/cases/test_helper_test.rb36
-rw-r--r--activejob/test/jobs/timezone_dependent_job.rb1
-rw-r--r--activejob/test/support/integration/test_case_helpers.rb1
-rw-r--r--activemodel/CHANGELOG.md149
-rw-r--r--activemodel/lib/active_model.rb1
-rw-r--r--activemodel/lib/active_model/attribute_assignment.rb1
-rw-r--r--activemodel/lib/active_model/attribute_methods.rb9
-rw-r--r--activemodel/lib/active_model/attribute_set.rb2
-rw-r--r--activemodel/lib/active_model/attributes.rb61
-rw-r--r--activemodel/lib/active_model/callbacks.rb1
-rw-r--r--activemodel/lib/active_model/dirty.rb2
-rw-r--r--activemodel/lib/active_model/error.rb165
-rw-r--r--activemodel/lib/active_model/errors.rb440
-rw-r--r--activemodel/lib/active_model/gem_version.rb4
-rw-r--r--activemodel/lib/active_model/lint.rb2
-rw-r--r--activemodel/lib/active_model/naming.rb1
-rw-r--r--activemodel/lib/active_model/nested_error.rb33
-rw-r--r--activemodel/lib/active_model/railtie.rb2
-rw-r--r--activemodel/lib/active_model/serialization.rb1
-rw-r--r--activemodel/lib/active_model/serializers/json.rb7
-rw-r--r--activemodel/lib/active_model/type/big_integer.rb1
-rw-r--r--activemodel/lib/active_model/type/boolean.rb1
-rw-r--r--activemodel/lib/active_model/type/date.rb1
-rw-r--r--activemodel/lib/active_model/type/date_time.rb1
-rw-r--r--activemodel/lib/active_model/type/decimal.rb1
-rw-r--r--activemodel/lib/active_model/type/float.rb1
-rw-r--r--activemodel/lib/active_model/type/helpers/numeric.rb1
-rw-r--r--activemodel/lib/active_model/type/helpers/time_value.rb22
-rw-r--r--activemodel/lib/active_model/type/immutable_string.rb1
-rw-r--r--activemodel/lib/active_model/type/integer.rb5
-rw-r--r--activemodel/lib/active_model/type/string.rb1
-rw-r--r--activemodel/lib/active_model/type/time.rb1
-rw-r--r--activemodel/lib/active_model/type/value.rb3
-rw-r--r--activemodel/lib/active_model/validations.rb1
-rw-r--r--activemodel/lib/active_model/validations/acceptance.rb1
-rw-r--r--activemodel/lib/active_model/validations/callbacks.rb1
-rw-r--r--activemodel/lib/active_model/validations/clusivity.rb1
-rw-r--r--activemodel/lib/active_model/validations/format.rb1
-rw-r--r--activemodel/lib/active_model/validations/numericality.rb1
-rw-r--r--activemodel/lib/active_model/validations/validates.rb1
-rw-r--r--activemodel/lib/active_model/validator.rb1
-rw-r--r--activemodel/test/cases/attribute_methods_test.rb1
-rw-r--r--activemodel/test/cases/attributes_test.rb14
-rw-r--r--activemodel/test/cases/error_test.rb200
-rw-r--r--activemodel/test/cases/errors_test.rb371
-rw-r--r--activemodel/test/cases/nested_error_test.rb54
-rw-r--r--activemodel/test/cases/railtie_test.rb6
-rw-r--r--activemodel/test/cases/type/date_time_test.rb1
-rw-r--r--activemodel/test/cases/validations/acceptance_validation_test.rb1
-rw-r--r--activemodel/test/cases/validations/i18n_validation_test.rb118
-rw-r--r--activemodel/test/cases/validations/numericality_validation_test.rb1
-rw-r--r--activemodel/test/cases/validations/validations_context_test.rb4
-rw-r--r--activemodel/test/cases/validations/with_validation_test.rb8
-rw-r--r--activemodel/test/cases/validations_test.rb14
-rw-r--r--activemodel/test/models/person_with_validator.rb2
-rw-r--r--activemodel/test/models/reply.rb10
-rw-r--r--activemodel/test/validators/email_validator.rb5
-rw-r--r--activerecord/CHANGELOG.md883
-rw-r--r--activerecord/lib/active_record/aggregations.rb1
-rw-r--r--activerecord/lib/active_record/association_relation.rb1
-rw-r--r--activerecord/lib/active_record/associations/alias_tracker.rb1
-rw-r--r--activerecord/lib/active_record/associations/builder/association.rb4
-rw-r--r--activerecord/lib/active_record/associations/builder/belongs_to.rb7
-rw-r--r--activerecord/lib/active_record/associations/builder/collection_association.rb6
-rw-r--r--activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb2
-rw-r--r--activerecord/lib/active_record/associations/builder/has_many.rb2
-rw-r--r--activerecord/lib/active_record/associations/builder/has_one.rb36
-rw-r--r--activerecord/lib/active_record/associations/builder/singular_association.rb2
-rw-r--r--activerecord/lib/active_record/associations/collection_association.rb2
-rw-r--r--activerecord/lib/active_record/associations/collection_proxy.rb5
-rw-r--r--activerecord/lib/active_record/associations/has_many_association.rb1
-rw-r--r--activerecord/lib/active_record/associations/join_dependency.rb19
-rw-r--r--activerecord/lib/active_record/associations/join_dependency/join_association.rb13
-rw-r--r--activerecord/lib/active_record/associations/preloader.rb1
-rw-r--r--activerecord/lib/active_record/associations/preloader/association.rb12
-rw-r--r--activerecord/lib/active_record/attribute_assignment.rb1
-rw-r--r--activerecord/lib/active_record/attribute_decorators.rb2
-rw-r--r--activerecord/lib/active_record/attribute_methods.rb65
-rw-r--r--activerecord/lib/active_record/attribute_methods/before_type_cast.rb4
-rw-r--r--activerecord/lib/active_record/attribute_methods/dirty.rb25
-rw-r--r--activerecord/lib/active_record/attribute_methods/primary_key.rb24
-rw-r--r--activerecord/lib/active_record/attribute_methods/read.rb13
-rw-r--r--activerecord/lib/active_record/attribute_methods/serialization.rb1
-rw-r--r--activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb2
-rw-r--r--activerecord/lib/active_record/attribute_methods/write.rb17
-rw-r--r--activerecord/lib/active_record/attributes.rb1
-rw-r--r--activerecord/lib/active_record/autosave_association.rb31
-rw-r--r--activerecord/lib/active_record/base.rb1
-rw-r--r--activerecord/lib/active_record/callbacks.rb1
-rw-r--r--activerecord/lib/active_record/coders/yaml_column.rb1
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb105
-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.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb7
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/quoting.rb63
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/savepoints.rb6
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_creation.rb1
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb3
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb61
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/transaction.rb72
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_adapter.rb67
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb41
-rw-r--r--activerecord/lib/active_record/connection_adapters/column.rb14
-rw-r--r--activerecord/lib/active_record/connection_adapters/connection_specification.rb3
-rw-r--r--activerecord/lib/active_record/connection_adapters/deduplicable.rb29
-rw-r--r--activerecord/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb4
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql/database_statements.rb9
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb1
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql/quoting.rb51
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql/schema_creation.rb1
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql/schema_dumper.rb4
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql/type_metadata.rb11
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb18
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/column.rb23
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb6
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid/array.rb1
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid/enum.rb1
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid/hstore.rb1
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb1
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid/money.rb4
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid/point.rb1
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid/range.rb1
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid/uuid.rb1
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb41
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/schema_dumper.rb1
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb7
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/type_metadata.rb8
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/utils.rb1
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb10
-rw-r--r--activerecord/lib/active_record/connection_adapters/schema_cache.rb55
-rw-r--r--activerecord/lib/active_record/connection_adapters/sql_type_metadata.rb10
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite3/database_statements.rb12
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite3/quoting.rb41
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb29
-rw-r--r--activerecord/lib/active_record/connection_adapters/statement_pool.rb1
-rw-r--r--activerecord/lib/active_record/connection_handling.rb13
-rw-r--r--activerecord/lib/active_record/core.rb33
-rw-r--r--activerecord/lib/active_record/database_configurations.rb58
-rw-r--r--activerecord/lib/active_record/database_configurations/url_config.rb1
-rw-r--r--activerecord/lib/active_record/dynamic_matchers.rb7
-rw-r--r--activerecord/lib/active_record/enum.rb9
-rw-r--r--activerecord/lib/active_record/errors.rb16
-rw-r--r--activerecord/lib/active_record/explain.rb1
-rw-r--r--activerecord/lib/active_record/fixture_set/table_row.rb1
-rw-r--r--activerecord/lib/active_record/fixture_set/table_rows.rb1
-rw-r--r--activerecord/lib/active_record/fixtures.rb3
-rw-r--r--activerecord/lib/active_record/gem_version.rb4
-rw-r--r--activerecord/lib/active_record/inheritance.rb3
-rw-r--r--activerecord/lib/active_record/insert_all.rb6
-rw-r--r--activerecord/lib/active_record/integration.rb12
-rw-r--r--activerecord/lib/active_record/internal_metadata.rb4
-rw-r--r--activerecord/lib/active_record/locking/optimistic.rb5
-rw-r--r--activerecord/lib/active_record/log_subscriber.rb2
-rw-r--r--activerecord/lib/active_record/middleware/database_selector.rb7
-rw-r--r--activerecord/lib/active_record/middleware/database_selector/resolver.rb5
-rw-r--r--activerecord/lib/active_record/migration.rb59
-rw-r--r--activerecord/lib/active_record/migration/command_recorder.rb1
-rw-r--r--activerecord/lib/active_record/migration/compatibility.rb5
-rw-r--r--activerecord/lib/active_record/migration/join_table.rb1
-rw-r--r--activerecord/lib/active_record/model_schema.rb5
-rw-r--r--activerecord/lib/active_record/nested_attributes.rb3
-rw-r--r--activerecord/lib/active_record/null_relation.rb1
-rw-r--r--activerecord/lib/active_record/persistence.rb49
-rw-r--r--activerecord/lib/active_record/railtie.rb1
-rw-r--r--activerecord/lib/active_record/railties/databases.rake141
-rw-r--r--activerecord/lib/active_record/readonly_attributes.rb4
-rw-r--r--activerecord/lib/active_record/reflection.rb4
-rw-r--r--activerecord/lib/active_record/relation.rb73
-rw-r--r--activerecord/lib/active_record/relation/batches.rb1
-rw-r--r--activerecord/lib/active_record/relation/calculations.rb8
-rw-r--r--activerecord/lib/active_record/relation/delegation.rb7
-rw-r--r--activerecord/lib/active_record/relation/finder_methods.rb7
-rw-r--r--activerecord/lib/active_record/relation/merger.rb9
-rw-r--r--activerecord/lib/active_record/relation/query_methods.rb120
-rw-r--r--activerecord/lib/active_record/relation/spawn_methods.rb1
-rw-r--r--activerecord/lib/active_record/relation/where_clause.rb15
-rw-r--r--activerecord/lib/active_record/result.rb1
-rw-r--r--activerecord/lib/active_record/sanitization.rb32
-rw-r--r--activerecord/lib/active_record/schema.rb2
-rw-r--r--activerecord/lib/active_record/schema_dumper.rb6
-rw-r--r--activerecord/lib/active_record/schema_migration.rb4
-rw-r--r--activerecord/lib/active_record/scoping.rb1
-rw-r--r--activerecord/lib/active_record/scoping/default.rb1
-rw-r--r--activerecord/lib/active_record/scoping/named.rb1
-rw-r--r--activerecord/lib/active_record/table_metadata.rb22
-rw-r--r--activerecord/lib/active_record/tasks/database_tasks.rb67
-rw-r--r--activerecord/lib/active_record/tasks/mysql_database_tasks.rb5
-rw-r--r--activerecord/lib/active_record/tasks/postgresql_database_tasks.rb1
-rw-r--r--activerecord/lib/active_record/tasks/sqlite_database_tasks.rb1
-rw-r--r--activerecord/lib/active_record/test_fixtures.rb1
-rw-r--r--activerecord/lib/active_record/timestamp.rb43
-rw-r--r--activerecord/lib/active_record/touch_later.rb10
-rw-r--r--activerecord/lib/active_record/transactions.rb60
-rw-r--r--activerecord/lib/active_record/type.rb1
-rw-r--r--activerecord/lib/active_record/type/adapter_specific_registry.rb3
-rw-r--r--activerecord/lib/active_record/type/hash_lookup_type_map.rb1
-rw-r--r--activerecord/lib/active_record/type/serialized.rb1
-rw-r--r--activerecord/lib/active_record/type/type_map.rb1
-rw-r--r--activerecord/lib/active_record/type/unsigned_integer.rb1
-rw-r--r--activerecord/lib/active_record/type_caster/connection.rb26
-rw-r--r--activerecord/lib/active_record/validations.rb1
-rw-r--r--activerecord/lib/active_record/validations/associated.rb1
-rw-r--r--activerecord/lib/arel.rb2
-rw-r--r--activerecord/lib/arel/attributes.rb22
-rw-r--r--activerecord/lib/arel/nodes/node.rb8
-rw-r--r--activerecord/lib/arel/predications.rb5
-rw-r--r--activerecord/lib/arel/visitors.rb1
-rw-r--r--activerecord/lib/arel/visitors/depth_first.rb204
-rw-r--r--activerecord/lib/arel/visitors/dot.rb1
-rw-r--r--activerecord/lib/arel/visitors/mssql.rb1
-rw-r--r--activerecord/lib/arel/visitors/oracle.rb49
-rw-r--r--activerecord/lib/arel/visitors/oracle12.rb58
-rw-r--r--activerecord/lib/arel/visitors/postgresql.rb1
-rw-r--r--activerecord/lib/arel/visitors/sqlite.rb1
-rw-r--r--activerecord/lib/arel/visitors/to_sql.rb114
-rw-r--r--activerecord/lib/arel/visitors/visitor.rb15
-rw-r--r--activerecord/lib/arel/visitors/where_sql.rb1
-rw-r--r--activerecord/lib/rails/generators/active_record/application_record/application_record_generator.rb1
-rw-r--r--activerecord/lib/rails/generators/active_record/migration.rb1
-rw-r--r--activerecord/lib/rails/generators/active_record/migration/migration_generator.rb1
-rw-r--r--activerecord/lib/rails/generators/active_record/model/model_generator.rb1
-rw-r--r--activerecord/test/active_record/connection_adapters/fake_adapter.rb3
-rw-r--r--activerecord/test/cases/adapter_test.rb12
-rw-r--r--activerecord/test/cases/adapters/mysql2/annotate_test.rb37
-rw-r--r--activerecord/test/cases/adapters/mysql2/case_sensitivity_test.rb10
-rw-r--r--activerecord/test/cases/adapters/mysql2/connection_test.rb1
-rw-r--r--activerecord/test/cases/adapters/mysql2/enum_test.rb14
-rw-r--r--activerecord/test/cases/adapters/mysql2/mysql2_adapter_test.rb45
-rw-r--r--activerecord/test/cases/adapters/mysql2/schema_migrations_test.rb1
-rw-r--r--activerecord/test/cases/adapters/mysql2/set_test.rb32
-rw-r--r--activerecord/test/cases/adapters/mysql2/table_options_test.rb4
-rw-r--r--activerecord/test/cases/adapters/mysql2/transaction_test.rb6
-rw-r--r--activerecord/test/cases/adapters/postgresql/annotate_test.rb37
-rw-r--r--activerecord/test/cases/adapters/postgresql/connection_test.rb7
-rw-r--r--activerecord/test/cases/adapters/postgresql/extension_migration_test.rb12
-rw-r--r--activerecord/test/cases/adapters/postgresql/geometric_test.rb1
-rw-r--r--activerecord/test/cases/adapters/postgresql/money_test.rb4
-rw-r--r--activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb34
-rw-r--r--activerecord/test/cases/adapters/postgresql/referential_integrity_test.rb5
-rw-r--r--activerecord/test/cases/adapters/postgresql/rename_table_test.rb1
-rw-r--r--activerecord/test/cases/adapters/postgresql/schema_test.rb6
-rw-r--r--activerecord/test/cases/adapters/postgresql/transaction_test.rb1
-rw-r--r--activerecord/test/cases/adapters/postgresql/uuid_test.rb8
-rw-r--r--activerecord/test/cases/adapters/sqlite3/annotate_test.rb37
-rw-r--r--activerecord/test/cases/adapters/sqlite3/bind_parameter_test.rb20
-rw-r--r--activerecord/test/cases/adapters/sqlite3/collation_test.rb9
-rw-r--r--activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb31
-rw-r--r--activerecord/test/cases/annotate_test.rb46
-rw-r--r--activerecord/test/cases/ar_schema_test.rb27
-rw-r--r--activerecord/test/cases/arel/attributes/attribute_test.rb24
-rw-r--r--activerecord/test/cases/arel/attributes_test.rb41
-rw-r--r--activerecord/test/cases/arel/nodes/node_test.rb19
-rw-r--r--activerecord/test/cases/arel/select_manager_test.rb10
-rw-r--r--activerecord/test/cases/arel/support/fake_record.rb4
-rw-r--r--activerecord/test/cases/arel/visitors/depth_first_test.rb276
-rw-r--r--activerecord/test/cases/arel/visitors/dispatch_contamination_test.rb8
-rw-r--r--activerecord/test/cases/arel/visitors/oracle12_test.rb21
-rw-r--r--activerecord/test/cases/arel/visitors/oracle_test.rb21
-rw-r--r--activerecord/test/cases/arel/visitors/to_sql_test.rb10
-rw-r--r--activerecord/test/cases/associations/cascaded_eager_loading_test.rb24
-rw-r--r--activerecord/test/cases/associations/eager_load_nested_include_test.rb1
-rw-r--r--activerecord/test/cases/associations/eager_test.rb62
-rw-r--r--activerecord/test/cases/associations/extension_test.rb7
-rw-r--r--activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb16
-rw-r--r--activerecord/test/cases/associations/has_many_associations_test.rb86
-rw-r--r--activerecord/test/cases/associations/has_many_through_associations_test.rb34
-rw-r--r--activerecord/test/cases/associations/has_one_associations_test.rb44
-rw-r--r--activerecord/test/cases/associations/left_outer_join_association_test.rb4
-rw-r--r--activerecord/test/cases/associations/nested_through_associations_test.rb1
-rw-r--r--activerecord/test/cases/associations/required_test.rb1
-rw-r--r--activerecord/test/cases/attribute_methods_test.rb5
-rw-r--r--activerecord/test/cases/attributes_test.rb6
-rw-r--r--activerecord/test/cases/autosave_association_test.rb79
-rw-r--r--activerecord/test/cases/base_test.rb96
-rw-r--r--activerecord/test/cases/batches_test.rb2
-rw-r--r--activerecord/test/cases/bind_parameter_test.rb2
-rw-r--r--activerecord/test/cases/calculations_test.rb45
-rw-r--r--activerecord/test/cases/collection_cache_key_test.rb34
-rw-r--r--activerecord/test/cases/comment_test.rb25
-rw-r--r--activerecord/test/cases/connection_adapters/connection_handler_test.rb15
-rw-r--r--activerecord/test/cases/connection_adapters/connection_handlers_multi_db_test.rb4
-rw-r--r--activerecord/test/cases/connection_adapters/merge_and_resolve_default_url_config_test.rb90
-rw-r--r--activerecord/test/cases/connection_adapters/mysql_type_lookup_test.rb3
-rw-r--r--activerecord/test/cases/connection_adapters/schema_cache_test.rb4
-rw-r--r--activerecord/test/cases/connection_adapters/type_lookup_test.rb1
-rw-r--r--activerecord/test/cases/connection_pool_test.rb23
-rw-r--r--activerecord/test/cases/database_statements_test.rb1
-rw-r--r--activerecord/test/cases/enum_test.rb22
-rw-r--r--activerecord/test/cases/explain_test.rb1
-rw-r--r--activerecord/test/cases/finder_respond_to_test.rb1
-rw-r--r--activerecord/test/cases/finder_test.rb14
-rw-r--r--activerecord/test/cases/fixtures_test.rb2
-rw-r--r--activerecord/test/cases/helper.rb1
-rw-r--r--activerecord/test/cases/hot_compatibility_test.rb1
-rw-r--r--activerecord/test/cases/inheritance_test.rb4
-rw-r--r--activerecord/test/cases/insert_all_test.rb1
-rw-r--r--activerecord/test/cases/json_serialization_test.rb3
-rw-r--r--activerecord/test/cases/locking_test.rb1
-rw-r--r--activerecord/test/cases/migration/compatibility_test.rb29
-rw-r--r--activerecord/test/cases/migration/create_join_table_test.rb1
-rw-r--r--activerecord/test/cases/migration/helper.rb1
-rw-r--r--activerecord/test/cases/migration/logger_test.rb9
-rw-r--r--activerecord/test/cases/migration/references_statements_test.rb1
-rw-r--r--activerecord/test/cases/migration_test.rb43
-rw-r--r--activerecord/test/cases/migrator_test.rb143
-rw-r--r--activerecord/test/cases/multi_db_migrator_test.rb218
-rw-r--r--activerecord/test/cases/nested_attributes_test.rb1
-rw-r--r--activerecord/test/cases/persistence_test.rb4
-rw-r--r--activerecord/test/cases/pooled_connections_test.rb1
-rw-r--r--activerecord/test/cases/primary_keys_test.rb8
-rw-r--r--activerecord/test/cases/query_cache_test.rb24
-rw-r--r--activerecord/test/cases/relation/where_clause_test.rb3
-rw-r--r--activerecord/test/cases/relation/where_test.rb69
-rw-r--r--activerecord/test/cases/relation_test.rb8
-rw-r--r--activerecord/test/cases/relations_test.rb32
-rw-r--r--activerecord/test/cases/schema_dumper_test.rb1
-rw-r--r--activerecord/test/cases/schema_loading_test.rb1
-rw-r--r--activerecord/test/cases/tasks/database_tasks_test.rb7
-rw-r--r--activerecord/test/cases/tasks/mysql_rake_test.rb14
-rw-r--r--activerecord/test/cases/tasks/postgresql_rake_test.rb3
-rw-r--r--activerecord/test/cases/test_case.rb24
-rw-r--r--activerecord/test/cases/transaction_callbacks_test.rb59
-rw-r--r--activerecord/test/cases/transactions_test.rb60
-rw-r--r--activerecord/test/cases/unsafe_raw_sql_test.rb87
-rw-r--r--activerecord/test/cases/validations/i18n_validation_test.rb16
-rw-r--r--activerecord/test/cases/yaml_serialization_test.rb1
-rw-r--r--activerecord/test/models/author.rb2
-rw-r--r--activerecord/test/models/book.rb6
-rw-r--r--activerecord/test/models/club.rb5
-rw-r--r--activerecord/test/models/company.rb1
-rw-r--r--activerecord/test/models/company_in_module.rb1
-rw-r--r--activerecord/test/models/contact.rb18
-rw-r--r--activerecord/test/models/developer.rb16
-rw-r--r--activerecord/test/models/face.rb2
-rw-r--r--activerecord/test/models/mouse.rb6
-rw-r--r--activerecord/test/models/person.rb1
-rw-r--r--activerecord/test/models/post.rb5
-rw-r--r--activerecord/test/models/rating.rb1
-rw-r--r--activerecord/test/models/reply.rb12
-rw-r--r--activerecord/test/models/ship.rb3
-rw-r--r--activerecord/test/models/squeak.rb6
-rw-r--r--activerecord/test/models/topic.rb1
-rw-r--r--activerecord/test/schema/mysql2_specific_schema.rb4
-rw-r--r--activerecord/test/schema/schema.rb13
-rw-r--r--activerecord/test/support/config.rb1
-rw-r--r--activestorage/CHANGELOG.md195
-rw-r--r--activestorage/app/jobs/active_storage/mirror_job.rb13
-rw-r--r--activestorage/app/models/active_storage/attachment.rb6
-rw-r--r--activestorage/app/models/active_storage/blob.rb3
-rw-r--r--activestorage/app/models/active_storage/variant.rb14
-rw-r--r--activestorage/app/models/active_storage/variation.rb2
-rw-r--r--activestorage/config/routes.rb2
-rw-r--r--activestorage/lib/active_storage.rb19
-rw-r--r--activestorage/lib/active_storage/analyzer/image_analyzer.rb18
-rw-r--r--activestorage/lib/active_storage/attached/model.rb17
-rw-r--r--activestorage/lib/active_storage/attached/one.rb2
-rw-r--r--activestorage/lib/active_storage/engine.rb9
-rw-r--r--activestorage/lib/active_storage/gem_version.rb4
-rw-r--r--activestorage/lib/active_storage/log_subscriber.rb6
-rw-r--r--activestorage/lib/active_storage/service/azure_storage_service.rb4
-rw-r--r--activestorage/lib/active_storage/service/disk_service.rb6
-rw-r--r--activestorage/lib/active_storage/service/mirror_service.rb29
-rw-r--r--activestorage/lib/active_storage/service/s3_service.rb36
-rw-r--r--activestorage/package.json2
-rw-r--r--activestorage/test/analyzer/image_analyzer_test.rb8
-rw-r--r--activestorage/test/analyzer/video_analyzer_test.rb1
-rw-r--r--activestorage/test/controllers/blobs_controller_test.rb25
-rw-r--r--activestorage/test/controllers/representations_controller_test.rb30
-rw-r--r--activestorage/test/fixtures/files/colors.bmpbin0 -> 2810 bytes
-rw-r--r--activestorage/test/models/attached/many_test.rb72
-rw-r--r--activestorage/test/models/attachment_test.rb53
-rw-r--r--activestorage/test/models/variant_test.rb19
-rw-r--r--activestorage/test/service/azure_storage_service_test.rb14
-rw-r--r--activestorage/test/service/disk_service_test.rb10
-rw-r--r--activestorage/test/service/mirror_service_test.rb14
-rw-r--r--activestorage/test/service/s3_service_test.rb22
-rw-r--r--activestorage/test/test_helper.rb4
-rw-r--r--activesupport/CHANGELOG.md465
-rw-r--r--activesupport/activesupport.gemspec2
-rw-r--r--activesupport/lib/active_support.rb1
-rw-r--r--activesupport/lib/active_support/actionable_error.rb48
-rw-r--r--activesupport/lib/active_support/backtrace_cleaner.rb7
-rw-r--r--activesupport/lib/active_support/cache.rb9
-rw-r--r--activesupport/lib/active_support/cache/file_store.rb1
-rw-r--r--activesupport/lib/active_support/cache/memory_store.rb1
-rw-r--r--activesupport/lib/active_support/cache/redis_cache_store.rb12
-rw-r--r--activesupport/lib/active_support/cache/strategy/local_cache.rb6
-rw-r--r--activesupport/lib/active_support/callbacks.rb4
-rw-r--r--activesupport/lib/active_support/concern.rb25
-rw-r--r--activesupport/lib/active_support/concurrency/share_lock.rb1
-rw-r--r--activesupport/lib/active_support/core_ext/date_and_time/calculations.rb30
-rw-r--r--activesupport/lib/active_support/core_ext/date_and_time/zones.rb1
-rw-r--r--activesupport/lib/active_support/core_ext/date_time/conversions.rb1
-rw-r--r--activesupport/lib/active_support/core_ext/digest.rb3
-rw-r--r--activesupport/lib/active_support/core_ext/enumerable.rb46
-rw-r--r--activesupport/lib/active_support/core_ext/hash/deep_transform_values.rb4
-rw-r--r--activesupport/lib/active_support/core_ext/module/delegation.rb31
-rw-r--r--activesupport/lib/active_support/core_ext/object/duplicable.rb124
-rw-r--r--activesupport/lib/active_support/core_ext/range/each.rb1
-rw-r--r--activesupport/lib/active_support/core_ext/string/access.rb5
-rw-r--r--activesupport/lib/active_support/core_ext/string/filters.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/string/output_safety.rb7
-rw-r--r--activesupport/lib/active_support/core_ext/time/calculations.rb30
-rw-r--r--activesupport/lib/active_support/dependencies.rb42
-rw-r--r--activesupport/lib/active_support/dependencies/zeitwerk_integration.rb10
-rw-r--r--activesupport/lib/active_support/deprecation.rb2
-rw-r--r--activesupport/lib/active_support/deprecation/method_wrappers.rb25
-rw-r--r--activesupport/lib/active_support/deprecation/proxy_wrappers.rb29
-rw-r--r--activesupport/lib/active_support/descendants_tracker.rb18
-rw-r--r--activesupport/lib/active_support/duration.rb3
-rw-r--r--activesupport/lib/active_support/duration/iso8601_parser.rb1
-rw-r--r--activesupport/lib/active_support/duration/iso8601_serializer.rb1
-rw-r--r--activesupport/lib/active_support/evented_file_update_checker.rb20
-rw-r--r--activesupport/lib/active_support/file_update_checker.rb1
-rw-r--r--activesupport/lib/active_support/gem_version.rb4
-rw-r--r--activesupport/lib/active_support/hash_with_indifferent_access.rb12
-rw-r--r--activesupport/lib/active_support/i18n_railtie.rb3
-rw-r--r--activesupport/lib/active_support/inflector/inflections.rb2
-rw-r--r--activesupport/lib/active_support/inflector/methods.rb1
-rw-r--r--activesupport/lib/active_support/json/decoding.rb1
-rw-r--r--activesupport/lib/active_support/lazy_load_hooks.rb1
-rw-r--r--activesupport/lib/active_support/log_subscriber.rb4
-rw-r--r--activesupport/lib/active_support/logger_thread_safe_level.rb3
-rw-r--r--activesupport/lib/active_support/message_verifier.rb4
-rw-r--r--activesupport/lib/active_support/messages/rotator.rb9
-rw-r--r--activesupport/lib/active_support/multibyte/chars.rb1
-rw-r--r--activesupport/lib/active_support/multibyte/unicode.rb1
-rw-r--r--activesupport/lib/active_support/notifications.rb35
-rw-r--r--activesupport/lib/active_support/notifications/fanout.rb26
-rw-r--r--activesupport/lib/active_support/notifications/instrumenter.rb11
-rw-r--r--activesupport/lib/active_support/number_helper.rb4
-rw-r--r--activesupport/lib/active_support/number_helper/number_converter.rb1
-rw-r--r--activesupport/lib/active_support/number_helper/number_to_currency_converter.rb1
-rw-r--r--activesupport/lib/active_support/number_helper/number_to_delimited_converter.rb1
-rw-r--r--activesupport/lib/active_support/number_helper/number_to_human_converter.rb1
-rw-r--r--activesupport/lib/active_support/number_helper/number_to_human_size_converter.rb1
-rw-r--r--activesupport/lib/active_support/number_helper/number_to_phone_converter.rb1
-rw-r--r--activesupport/lib/active_support/number_helper/number_to_rounded_converter.rb1
-rw-r--r--activesupport/lib/active_support/parameter_filter.rb8
-rw-r--r--activesupport/lib/active_support/rails.rb3
-rw-r--r--activesupport/lib/active_support/secure_compare_rotator.rb52
-rw-r--r--activesupport/lib/active_support/string_inquirer.rb1
-rw-r--r--activesupport/lib/active_support/testing/parallelization.rb34
-rw-r--r--activesupport/lib/active_support/testing/stream.rb1
-rw-r--r--activesupport/lib/active_support/testing/time_helpers.rb2
-rw-r--r--activesupport/lib/active_support/xml_mini.rb1
-rw-r--r--activesupport/lib/active_support/xml_mini/jdom.rb1
-rw-r--r--activesupport/test/actionable_error_test.rb47
-rw-r--r--activesupport/test/cache/behaviors/cache_store_behavior.rb1
-rw-r--r--activesupport/test/cache/behaviors/local_cache_behavior.rb9
-rw-r--r--activesupport/test/cache/cache_key_test.rb1
-rw-r--r--activesupport/test/cache/stores/mem_cache_store_test.rb1
-rw-r--r--activesupport/test/cache/stores/redis_cache_store_test.rb2
-rw-r--r--activesupport/test/callbacks_test.rb2
-rw-r--r--activesupport/test/core_ext/date_and_time_behavior.rb60
-rw-r--r--activesupport/test/core_ext/date_ext_test.rb16
-rw-r--r--activesupport/test/core_ext/enumerable_test.rb24
-rw-r--r--activesupport/test/core_ext/module_test.rb44
-rw-r--r--activesupport/test/core_ext/object/duplicable_test.rb9
-rw-r--r--activesupport/test/core_ext/string_ext_test.rb5
-rw-r--r--activesupport/test/core_ext/time_ext_test.rb60
-rw-r--r--activesupport/test/dependencies_test.rb24
-rw-r--r--activesupport/test/deprecation/method_wrappers_test.rb10
-rw-r--r--activesupport/test/deprecation_test.rb17
-rw-r--r--activesupport/test/descendants_tracker_test_cases.rb1
-rw-r--r--activesupport/test/evented_file_update_checker_test.rb64
-rw-r--r--activesupport/test/hash_with_indifferent_access_test.rb28
-rw-r--r--activesupport/test/json/decoding_test.rb1
-rw-r--r--activesupport/test/json/encoding_test.rb1
-rw-r--r--activesupport/test/lazy_load_hooks_test.rb1
-rw-r--r--activesupport/test/logger_test.rb44
-rw-r--r--activesupport/test/message_encryptor_test.rb28
-rw-r--r--activesupport/test/multibyte_chars_test.rb1
-rw-r--r--activesupport/test/notifications_test.rb121
-rw-r--r--activesupport/test/parameter_filter_test.rb7
-rw-r--r--activesupport/test/safe_buffer_test.rb8
-rw-r--r--activesupport/test/secure_compare_rotator_test.rb57
-rw-r--r--activesupport/test/share_lock_test.rb2
-rw-r--r--activesupport/test/subscriber_test.rb1
-rw-r--r--activesupport/test/test_case_test.rb1
-rw-r--r--guides/CHANGELOG.md34
-rw-r--r--guides/assets/images/getting_started/unknown_action_create_for_articles.pngbin4808 -> 16331 bytes
-rw-r--r--guides/bug_report_templates/action_controller_gem.rb2
-rw-r--r--guides/bug_report_templates/active_job_gem.rb2
-rw-r--r--guides/bug_report_templates/active_record_gem.rb4
-rw-r--r--guides/bug_report_templates/active_record_migrations_gem.rb6
-rw-r--r--guides/bug_report_templates/generic_gem.rb2
-rw-r--r--guides/rails_guides/generator.rb1
-rw-r--r--guides/rails_guides/indexer.rb1
-rw-r--r--guides/rails_guides/markdown.rb1
-rw-r--r--guides/rails_guides/markdown/renderer.rb1
-rw-r--r--guides/source/2_3_release_notes.md2
-rw-r--r--guides/source/5_1_release_notes.md4
-rw-r--r--guides/source/5_2_release_notes.md2
-rw-r--r--guides/source/6_0_release_notes.md492
-rw-r--r--guides/source/action_text_overview.md4
-rw-r--r--guides/source/action_view_overview.md28
-rw-r--r--guides/source/active_record_callbacks.md8
-rw-r--r--guides/source/active_record_migrations.md2
-rw-r--r--guides/source/active_record_multiple_databases.md288
-rw-r--r--guides/source/active_record_querying.md15
-rw-r--r--guides/source/active_record_validations.md12
-rw-r--r--guides/source/active_storage_overview.md34
-rw-r--r--guides/source/active_support_core_extensions.md69
-rw-r--r--guides/source/active_support_instrumentation.md36
-rw-r--r--guides/source/api_app.md9
-rw-r--r--guides/source/asset_pipeline.md23
-rw-r--r--guides/source/association_basics.md31
-rw-r--r--guides/source/autoloading_and_reloading_constants.md1376
-rw-r--r--guides/source/autoloading_and_reloading_constants_classic_mode.md1351
-rw-r--r--guides/source/command_line.md46
-rw-r--r--guides/source/configuring.md57
-rw-r--r--guides/source/contributing_to_ruby_on_rails.md10
-rw-r--r--guides/source/documents.yaml15
-rw-r--r--guides/source/engines.md59
-rw-r--r--guides/source/getting_started.md14
-rw-r--r--guides/source/i18n.md21
-rw-r--r--guides/source/initialization.md10
-rw-r--r--guides/source/layouts_and_rendering.md48
-rw-r--r--guides/source/rails_application_templates.md4
-rw-r--r--guides/source/routing.md2
-rw-r--r--guides/source/security.md2
-rw-r--r--guides/source/testing.md25
-rw-r--r--guides/source/upgrading_ruby_on_rails.md343
-rw-r--r--railties/CHANGELOG.md380
-rw-r--r--railties/Rakefile20
-rw-r--r--railties/lib/rails.rb1
-rw-r--r--railties/lib/rails/application.rb7
-rw-r--r--railties/lib/rails/application/bootstrap.rb24
-rw-r--r--railties/lib/rails/application/configuration.rb38
-rw-r--r--railties/lib/rails/application/default_middleware_stack.rb3
-rw-r--r--railties/lib/rails/application/dummy_erb_compiler.rb5
-rw-r--r--railties/lib/rails/application/finisher.rb12
-rw-r--r--railties/lib/rails/application/routes_reloader.rb1
-rw-r--r--railties/lib/rails/application_controller.rb1
-rw-r--r--railties/lib/rails/command.rb1
-rw-r--r--railties/lib/rails/command/spellchecker.rb1
-rw-r--r--railties/lib/rails/commands/dev/dev_command.rb6
-rw-r--r--railties/lib/rails/commands/server/server_command.rb11
-rw-r--r--railties/lib/rails/engine.rb17
-rw-r--r--railties/lib/rails/engine/configuration.rb11
-rw-r--r--railties/lib/rails/gem_version.rb4
-rw-r--r--railties/lib/rails/generators.rb5
-rw-r--r--railties/lib/rails/generators/actions.rb27
-rw-r--r--railties/lib/rails/generators/actions/create_migration.rb1
-rw-r--r--railties/lib/rails/generators/app_base.rb5
-rw-r--r--railties/lib/rails/generators/base.rb8
-rw-r--r--railties/lib/rails/generators/erb.rb1
-rw-r--r--railties/lib/rails/generators/erb/mailer/mailer_generator.rb1
-rw-r--r--railties/lib/rails/generators/erb/scaffold/scaffold_generator.rb1
-rw-r--r--railties/lib/rails/generators/erb/scaffold/templates/show.html.erb.tt2
-rw-r--r--railties/lib/rails/generators/generated_attribute.rb11
-rw-r--r--railties/lib/rails/generators/named_base.rb1
-rw-r--r--railties/lib/rails/generators/rails/app/app_generator.rb8
-rw-r--r--railties/lib/rails/generators/rails/app/templates/Gemfile.tt2
-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.tt5
-rw-r--r--railties/lib/rails/generators/rails/app/templates/bin/update.tt33
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/databases/jdbcsqlite3.yml.tt2
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/databases/sqlite3.yml.tt2
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt2
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt2
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/initializers/content_security_policy.rb.tt3
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/initializers/feature_policy.rb.tt11
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/initializers/filter_parameter_logging.rb.tt4
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults_6_0.rb.tt12
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/puma.rb.tt3
-rw-r--r--railties/lib/rails/generators/rails/app/templates/public/robots.txt2
-rw-r--r--railties/lib/rails/generators/rails/assets/assets_generator.rb7
-rw-r--r--railties/lib/rails/generators/rails/controller/controller_generator.rb1
-rw-r--r--railties/lib/rails/generators/rails/generator/generator_generator.rb1
-rw-r--r--railties/lib/rails/generators/rails/plugin/plugin_generator.rb1
-rw-r--r--railties/lib/rails/generators/rails/scaffold_controller/scaffold_controller_generator.rb13
-rw-r--r--railties/lib/rails/generators/test_unit/controller/templates/functional_test.rb.tt2
-rw-r--r--railties/lib/rails/generators/test_unit/generator/generator_generator.rb1
-rw-r--r--railties/lib/rails/generators/test_unit/integration/integration_generator.rb1
-rw-r--r--railties/lib/rails/generators/test_unit/scaffold/scaffold_generator.rb10
-rw-r--r--railties/lib/rails/generators/testing/behaviour.rb1
-rw-r--r--railties/lib/rails/info_controller.rb1
-rw-r--r--railties/lib/rails/mailers_controller.rb7
-rw-r--r--railties/lib/rails/paths.rb12
-rw-r--r--railties/lib/rails/rack/logger.rb1
-rw-r--r--railties/lib/rails/railtie.rb2
-rw-r--r--railties/lib/rails/railtie/configurable.rb1
-rw-r--r--railties/lib/rails/railtie/configuration.rb1
-rw-r--r--railties/lib/rails/source_annotation_extractor.rb10
-rw-r--r--railties/lib/rails/tasks.rb1
-rw-r--r--railties/lib/rails/tasks/zeitwerk.rake66
-rw-r--r--railties/lib/rails/test_unit/runner.rb2
-rw-r--r--railties/test/application/assets_test.rb1
-rw-r--r--railties/test/application/bin_setup_test.rb26
-rw-r--r--railties/test/application/configuration_test.rb88
-rw-r--r--railties/test/application/content_security_policy_test.rb33
-rw-r--r--railties/test/application/feature_policy_test.rb191
-rw-r--r--railties/test/application/generators_test.rb10
-rw-r--r--railties/test/application/initializers/frameworks_test.rb3
-rw-r--r--railties/test/application/loading_test.rb10
-rw-r--r--railties/test/application/mailer_previews_test.rb7
-rw-r--r--railties/test/application/middleware/exceptions_test.rb16
-rw-r--r--railties/test/application/middleware_test.rb4
-rw-r--r--railties/test/application/rake/dbs_test.rb77
-rw-r--r--railties/test/application/rake/multi_dbs_test.rb105
-rw-r--r--railties/test/application/rake/notes_test.rb1
-rw-r--r--railties/test/application/system_test_case_test.rb45
-rw-r--r--railties/test/application/test_runner_test.rb44
-rw-r--r--railties/test/application/zeitwerk_integration_test.rb81
-rw-r--r--railties/test/backtrace_cleaner_test.rb10
-rw-r--r--railties/test/commands/console_test.rb1
-rw-r--r--railties/test/commands/dbconsole_test.rb1
-rw-r--r--railties/test/commands/server_test.rb23
-rw-r--r--railties/test/configuration/middleware_stack_proxy_test.rb1
-rw-r--r--railties/test/env_helpers.rb1
-rw-r--r--railties/test/generators/actions_test.rb93
-rw-r--r--railties/test/generators/api_app_generator_test.rb9
-rw-r--r--railties/test/generators/app_generator_test.rb5
-rw-r--r--railties/test/generators/generated_attribute_test.rb17
-rw-r--r--railties/test/generators/migration_generator_test.rb45
-rw-r--r--railties/test/generators/model_generator_test.rb55
-rw-r--r--railties/test/generators/named_base_test.rb1
-rw-r--r--railties/test/generators/plugin_generator_test.rb1
-rw-r--r--railties/test/generators/scaffold_controller_generator_test.rb9
-rw-r--r--railties/test/generators/scaffold_generator_test.rb30
-rw-r--r--railties/test/generators/shared_generator_tests.rb5
-rw-r--r--railties/test/isolation/abstract_unit.rb46
-rw-r--r--railties/test/railties/engine_test.rb32
-rwxr-xr-xtools/profile1
-rw-r--r--version.rb4
-rw-r--r--yarn.lock12
918 files changed, 13476 insertions, 10338 deletions
diff --git a/.codeclimate.yml b/.codeclimate.yml
index 50e0f7a657..952b330d8c 100644
--- a/.codeclimate.yml
+++ b/.codeclimate.yml
@@ -25,6 +25,6 @@ checks:
plugins:
rubocop:
enabled: true
- channel: rubocop-0-66
+ channel: rubocop-0-71
exclude_patterns: []
diff --git a/.rubocop.yml b/.rubocop.yml
index 3dbd4a27a6..e310351bf5 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -1,3 +1,7 @@
+require:
+ - rubocop-performance
+ - rubocop-rails
+
AllCops:
TargetRubyVersion: 2.5
# RuboCop has a bunch of cops enabled by default. This setting tells RuboCop
@@ -16,9 +20,6 @@ Performance:
Exclude:
- '**/test/**/*'
-Rails:
- Enabled: true
-
# Prefer assert_not over assert !
Rails/AssertNot:
Include:
@@ -60,6 +61,10 @@ Layout/EndAlignment:
Layout/EmptyLineAfterMagicComment:
Enabled: true
+Layout/EmptyLinesAroundAccessModifier:
+ Enabled: true
+ EnforcedStyle: only_before
+
Layout/EmptyLinesAroundBlockBody:
Enabled: true
@@ -75,13 +80,13 @@ Layout/EmptyLinesAroundMethodBody:
Layout/EmptyLinesAroundModuleBody:
Enabled: true
-Layout/FirstParameterIndentation:
- Enabled: true
-
# Use Ruby >= 1.9 syntax for hashes. Prefer { a: :b } over { :a => :b }.
Style/HashSyntax:
Enabled: true
+Layout/IndentFirstArgument:
+ Enabled: true
+
# Method definitions after `private` or `protected` isolated calls need one
# extra level of indentation.
Layout/IndentationConsistency:
diff --git a/Gemfile b/Gemfile
index b5c6312c52..3a7a564a35 100644
--- a/Gemfile
+++ b/Gemfile
@@ -9,8 +9,8 @@ gemspec
# We need a newish Rake since Active Job sets its test tasks' descriptions.
gem "rake", ">= 11.1"
-gem "capybara", ">= 2.15"
-gem "selenium-webdriver", ">= 3.5.0", "< 3.13.0"
+gem "capybara", ">= 3.26"
+gem "selenium-webdriver", ">= 3.141.592"
gem "rack-cache", "~> 1.2"
gem "sass-rails"
@@ -29,6 +29,8 @@ gem "uglifier", ">= 1.3.0", require: false
gem "json", ">= 2.0.0"
gem "rubocop", ">= 0.47", require: false
+gem "rubocop-performance", require: false
+gem "rubocop-rails", require: false
group :doc do
gem "sdoc", "~> 1.0"
@@ -44,7 +46,7 @@ gem "libxml-ruby", platforms: :ruby
gem "connection_pool", require: false
# for railties app_generator_test
-gem "bootsnap", ">= 1.4.2", require: false
+gem "bootsnap", ">= 1.4.4", require: false
# Active Job
group :job do
diff --git a/Gemfile.lock b/Gemfile.lock
index b92603d799..0618bcf7ec 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -17,79 +17,79 @@ GIT
PATH
remote: .
specs:
- actioncable (6.0.0.beta3)
- actionpack (= 6.0.0.beta3)
+ actioncable (6.1.0.alpha)
+ actionpack (= 6.1.0.alpha)
nio4r (~> 2.0)
websocket-driver (>= 0.6.1)
- actionmailbox (6.0.0.beta3)
- actionpack (= 6.0.0.beta3)
- activejob (= 6.0.0.beta3)
- activerecord (= 6.0.0.beta3)
- activestorage (= 6.0.0.beta3)
- activesupport (= 6.0.0.beta3)
+ actionmailbox (6.1.0.alpha)
+ actionpack (= 6.1.0.alpha)
+ activejob (= 6.1.0.alpha)
+ activerecord (= 6.1.0.alpha)
+ activestorage (= 6.1.0.alpha)
+ activesupport (= 6.1.0.alpha)
mail (>= 2.7.1)
- actionmailer (6.0.0.beta3)
- actionpack (= 6.0.0.beta3)
- actionview (= 6.0.0.beta3)
- activejob (= 6.0.0.beta3)
+ actionmailer (6.1.0.alpha)
+ actionpack (= 6.1.0.alpha)
+ actionview (= 6.1.0.alpha)
+ activejob (= 6.1.0.alpha)
mail (~> 2.5, >= 2.5.4)
rails-dom-testing (~> 2.0)
- actionpack (6.0.0.beta3)
- actionview (= 6.0.0.beta3)
- activesupport (= 6.0.0.beta3)
+ actionpack (6.1.0.alpha)
+ actionview (= 6.1.0.alpha)
+ activesupport (= 6.1.0.alpha)
rack (~> 2.0)
rack-test (>= 0.6.3)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.0.2)
- actiontext (6.0.0.beta3)
- actionpack (= 6.0.0.beta3)
- activerecord (= 6.0.0.beta3)
- activestorage (= 6.0.0.beta3)
- activesupport (= 6.0.0.beta3)
+ actiontext (6.1.0.alpha)
+ actionpack (= 6.1.0.alpha)
+ activerecord (= 6.1.0.alpha)
+ activestorage (= 6.1.0.alpha)
+ activesupport (= 6.1.0.alpha)
nokogiri (>= 1.8.5)
- actionview (6.0.0.beta3)
- activesupport (= 6.0.0.beta3)
+ actionview (6.1.0.alpha)
+ activesupport (= 6.1.0.alpha)
builder (~> 3.1)
erubi (~> 1.4)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.0.3)
- activejob (6.0.0.beta3)
- activesupport (= 6.0.0.beta3)
+ activejob (6.1.0.alpha)
+ activesupport (= 6.1.0.alpha)
globalid (>= 0.3.6)
- activemodel (6.0.0.beta3)
- activesupport (= 6.0.0.beta3)
- activerecord (6.0.0.beta3)
- activemodel (= 6.0.0.beta3)
- activesupport (= 6.0.0.beta3)
- activestorage (6.0.0.beta3)
- actionpack (= 6.0.0.beta3)
- activejob (= 6.0.0.beta3)
- activerecord (= 6.0.0.beta3)
+ activemodel (6.1.0.alpha)
+ activesupport (= 6.1.0.alpha)
+ activerecord (6.1.0.alpha)
+ activemodel (= 6.1.0.alpha)
+ activesupport (= 6.1.0.alpha)
+ activestorage (6.1.0.alpha)
+ actionpack (= 6.1.0.alpha)
+ activejob (= 6.1.0.alpha)
+ activerecord (= 6.1.0.alpha)
marcel (~> 0.3.1)
- activesupport (6.0.0.beta3)
+ activesupport (6.1.0.alpha)
concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 0.7, < 2)
minitest (~> 5.1)
tzinfo (~> 1.1)
- zeitwerk (~> 2.1, >= 2.1.2)
- rails (6.0.0.beta3)
- actioncable (= 6.0.0.beta3)
- actionmailbox (= 6.0.0.beta3)
- actionmailer (= 6.0.0.beta3)
- actionpack (= 6.0.0.beta3)
- actiontext (= 6.0.0.beta3)
- actionview (= 6.0.0.beta3)
- activejob (= 6.0.0.beta3)
- activemodel (= 6.0.0.beta3)
- activerecord (= 6.0.0.beta3)
- activestorage (= 6.0.0.beta3)
- activesupport (= 6.0.0.beta3)
+ zeitwerk (~> 2.1, >= 2.1.8)
+ rails (6.1.0.alpha)
+ actioncable (= 6.1.0.alpha)
+ actionmailbox (= 6.1.0.alpha)
+ actionmailer (= 6.1.0.alpha)
+ actionpack (= 6.1.0.alpha)
+ actiontext (= 6.1.0.alpha)
+ actionview (= 6.1.0.alpha)
+ activejob (= 6.1.0.alpha)
+ activemodel (= 6.1.0.alpha)
+ activerecord (= 6.1.0.alpha)
+ activestorage (= 6.1.0.alpha)
+ activesupport (= 6.1.0.alpha)
bundler (>= 1.3.0)
- railties (= 6.0.0.beta3)
+ railties (= 6.1.0.alpha)
sprockets-rails (>= 2.0.0)
- railties (6.0.0.beta3)
- actionpack (= 6.0.0.beta3)
- activesupport (= 6.0.0.beta3)
+ railties (6.1.0.alpha)
+ actionpack (= 6.1.0.alpha)
+ activesupport (= 6.1.0.alpha)
method_source
rake (>= 0.8.7)
thor (>= 0.20.3, < 2.0)
@@ -167,24 +167,24 @@ GEM
childprocess
faraday
selenium-webdriver
- bootsnap (1.4.2)
+ bootsnap (1.4.4)
msgpack (~> 1.0)
- bootsnap (1.4.2-java)
+ bootsnap (1.4.4-java)
msgpack (~> 1.0)
builder (3.2.3)
bunny (2.13.0)
amq-protocol (~> 2.3, >= 2.3.0)
byebug (10.0.2)
- capybara (3.10.1)
+ capybara (3.26.0)
addressable
mini_mime (>= 0.1.3)
nokogiri (~> 1.8)
rack (>= 1.6.0)
rack-test (>= 0.6.3)
- regexp_parser (~> 1.2)
+ regexp_parser (~> 1.5)
xpath (~> 3.2)
- childprocess (0.9.0)
- ffi (~> 1.0, >= 1.0.11)
+ childprocess (1.0.1)
+ rake (< 13.0)
coffee-script (2.4.1)
coffee-script-source
execjs
@@ -216,7 +216,7 @@ GEM
em-socksify (0.3.2)
eventmachine (>= 1.0.0.beta.4)
erubi (1.8.0)
- et-orbi (1.1.6)
+ et-orbi (1.2.1)
tzinfo
event_emitter (0.2.6)
eventmachine (1.2.7)
@@ -236,12 +236,12 @@ GEM
faye-websocket (0.10.7)
eventmachine (>= 0.12.0)
websocket-driver (>= 0.5.1)
- ffi (1.10.0)
- ffi (1.10.0-java)
- ffi (1.10.0-x64-mingw32)
- ffi (1.10.0-x86-mingw32)
- fugit (1.1.6)
- et-orbi (~> 1.1, >= 1.1.6)
+ ffi (1.11.1)
+ ffi (1.11.1-java)
+ ffi (1.11.1-x64-mingw32)
+ ffi (1.11.1-x86-mingw32)
+ fugit (1.2.1)
+ et-orbi (~> 1.1, >= 1.1.8)
raabro (~> 1.1)
globalid (0.4.2)
activesupport (>= 4.2.0)
@@ -279,7 +279,6 @@ GEM
image_processing (1.7.1)
mini_magick (~> 4.0)
ruby-vips (>= 2.0.13, < 3)
- jar-dependencies (0.4.0)
jaro_winkler (1.5.2)
jaro_winkler (1.5.2-java)
jdbc-mysql (5.1.46)
@@ -327,10 +326,10 @@ GEM
minitest-server (1.0.5)
minitest (~> 5.0)
mono_logger (1.1.0)
- msgpack (1.2.9)
- msgpack (1.2.9-java)
- msgpack (1.2.9-x64-mingw32)
- msgpack (1.2.9-x86-mingw32)
+ msgpack (1.2.10)
+ msgpack (1.2.10-java)
+ msgpack (1.2.10-x64-mingw32)
+ msgpack (1.2.10-x86-mingw32)
multi_json (1.13.1)
multipart-post (2.0.0)
mustache (1.1.0)
@@ -338,29 +337,24 @@ GEM
mysql2 (0.5.2)
mysql2 (0.5.2-x64-mingw32)
mysql2 (0.5.2-x86-mingw32)
- net_http_ssl_fix (0.0.10)
nio4r (2.3.1)
nio4r (2.3.1-java)
- nokogiri (1.9.1)
+ nokogiri (1.10.3)
mini_portile2 (~> 2.4.0)
- nokogiri (1.9.1-java)
- nokogiri (1.9.1-x64-mingw32)
+ nokogiri (1.10.3-java)
+ nokogiri (1.10.3-x64-mingw32)
mini_portile2 (~> 2.4.0)
- nokogiri (1.9.1-x86-mingw32)
+ nokogiri (1.10.3-x86-mingw32)
mini_portile2 (~> 2.4.0)
os (1.0.0)
- parallel (1.13.0)
- parser (2.6.2.0)
+ parallel (1.17.0)
+ parser (2.6.3.0)
ast (~> 2.4.0)
path_expander (1.0.3)
pg (1.1.3)
pg (1.1.3-x64-mingw32)
pg (1.1.3-x86-mingw32)
psych (3.1.0)
- psych (3.1.0-java)
- jar-dependencies (>= 0.1.7)
- psych (3.1.0-x64-mingw32)
- psych (3.1.0-x86-mingw32)
public_suffix (3.0.3)
puma (3.12.1)
puma (3.12.1-java)
@@ -370,10 +364,10 @@ GEM
thor
raabro (1.1.6)
racc (1.4.15)
- rack (2.0.6)
+ rack (2.0.7)
rack-cache (1.8.0)
rack (>= 0.4)
- rack-protection (2.0.4)
+ rack-protection (2.0.5)
rack
rack-proxy (0.6.5)
rack
@@ -385,47 +379,51 @@ GEM
rails-html-sanitizer (1.0.4)
loofah (~> 2.2, >= 2.2.2)
rainbow (3.0.0)
- rake (12.3.1)
+ rake (12.3.2)
rb-fsevent (0.10.3)
rb-inotify (0.10.0)
ffi (~> 1.0)
rdoc (6.0.4)
redcarpet (3.2.3)
- redis (4.0.3)
+ redis (4.1.1)
redis-namespace (1.6.0)
redis (>= 3.0.4)
- regexp_parser (1.3.0)
+ regexp_parser (1.6.0)
representable (3.0.4)
declarative (< 0.1.0)
declarative-option (< 0.2.0)
uber (< 0.2.0)
- resque (1.27.4)
+ resque (2.0.0)
mono_logger (~> 1.0)
multi_json (~> 1.0)
- redis-namespace (~> 1.3)
+ redis-namespace (~> 1.6)
sinatra (>= 0.9.2)
vegas (~> 0.1.2)
- resque-scheduler (4.3.1)
+ resque-scheduler (4.4.0)
mono_logger (~> 1.0)
- redis (>= 3.3, < 5)
- resque (~> 1.26)
+ redis (>= 3.3)
+ resque (>= 1.26)
rufus-scheduler (~> 3.2)
retriable (3.1.2)
- rubocop (0.66.0)
+ rubocop (0.71.0)
jaro_winkler (~> 1.5.1)
parallel (~> 1.10)
- parser (>= 2.5, != 2.5.1.1)
- psych (>= 3.1.0)
+ parser (>= 2.6)
rainbow (>= 2.2.2, < 4.0)
ruby-progressbar (~> 1.7)
- unicode-display_width (>= 1.4.0, < 1.6)
- ruby-progressbar (1.10.0)
+ unicode-display_width (>= 1.4.0, < 1.7)
+ rubocop-performance (1.1.0)
+ rubocop (>= 0.67.0)
+ rubocop-rails (2.0.0)
+ rack (>= 2.0)
+ rubocop (>= 0.70.0)
+ ruby-progressbar (1.10.1)
ruby-vips (2.0.13)
ffi (~> 1.9)
ruby_dep (1.5.0)
- rubyzip (1.2.2)
- rufus-scheduler (3.5.2)
- fugit (~> 1.1, >= 1.1.5)
+ rubyzip (1.2.3)
+ rufus-scheduler (3.6.0)
+ fugit (~> 1.1, >= 1.1.6)
safe_yaml (1.0.4)
sass (3.7.2)
sass-listen (~> 4.0.0)
@@ -440,9 +438,9 @@ GEM
tilt (>= 1.1, < 3)
sdoc (1.0.0)
rdoc (>= 5.0)
- selenium-webdriver (3.12.0)
- childprocess (~> 0.5)
- rubyzip (~> 1.2)
+ selenium-webdriver (3.142.3)
+ childprocess (>= 0.5, < 2.0)
+ rubyzip (~> 1.2, >= 1.2.2)
sequel (5.14.0)
serverengine (2.0.7)
sigdump (~> 0.2.2)
@@ -456,10 +454,10 @@ GEM
faraday (~> 0.9)
jwt (>= 1.5, < 3.0)
multi_json (~> 1.10)
- sinatra (2.0.4)
+ sinatra (2.0.5)
mustermann (~> 1.0)
rack (~> 2.0)
- rack-protection (= 2.0.4)
+ rack-protection (= 2.0.5)
tilt (~> 2.0)
sneakers (2.11.0)
bunny (~> 2.12)
@@ -486,7 +484,7 @@ GEM
thor (0.20.3)
thread_safe (0.3.6)
thread_safe (0.3.6-java)
- tilt (2.0.8)
+ tilt (2.0.9)
turbolinks (5.2.0)
turbolinks-source (~> 5.2)
turbolinks-source (5.2.0)
@@ -497,7 +495,7 @@ GEM
uber (0.1.0)
uglifier (4.1.19)
execjs (>= 0.3.0, < 3)
- unicode-display_width (1.5.0)
+ unicode-display_width (1.6.0)
useragent (0.16.10)
vegas (0.1.11)
rack (>= 1.0.0)
@@ -505,8 +503,7 @@ GEM
json (>= 1.8)
nokogiri (~> 1.6)
wdm (0.1.1)
- webdrivers (3.7.0)
- net_http_ssl_fix
+ webdrivers (4.0.0)
nokogiri (~> 1.6)
rubyzip (~> 1.0)
selenium-webdriver (~> 3.0)
@@ -526,7 +523,7 @@ GEM
websocket-extensions (0.1.3)
xpath (3.2.0)
nokogiri (~> 1.8)
- zeitwerk (2.1.2)
+ zeitwerk (2.1.8)
PLATFORMS
java
@@ -546,9 +543,9 @@ DEPENDENCIES
benchmark-ips
blade
blade-sauce_labs_plugin
- bootsnap (>= 1.4.2)
+ bootsnap (>= 1.4.4)
byebug
- capybara (>= 2.15)
+ capybara (>= 3.26)
connection_pool
dalli
delayed_job
@@ -581,9 +578,11 @@ DEPENDENCIES
resque
resque-scheduler
rubocop (>= 0.47)
+ rubocop-performance
+ rubocop-rails
sass-rails
sdoc (~> 1.0)
- selenium-webdriver (>= 3.5.0, < 3.13.0)
+ selenium-webdriver (>= 3.141.592)
sequel
sidekiq
sneakers
diff --git a/RAILS_VERSION b/RAILS_VERSION
index 6b829f5d9b..6e7c238e32 100644
--- a/RAILS_VERSION
+++ b/RAILS_VERSION
@@ -1 +1 @@
-6.0.0.beta3
+6.1.0.alpha
diff --git a/RELEASING_RAILS.md b/RELEASING_RAILS.md
index d2d7a771bc..bc39e2313d 100644
--- a/RELEASING_RAILS.md
+++ b/RELEASING_RAILS.md
@@ -125,7 +125,7 @@ guide. You can generate keys with the GPG suite from here: https://gpgtools.org.
Run `rake changelog:header` to put a header with the new version in every
CHANGELOG. Don't commit this, the release task handles it.
-Run `rake release`. This will populate the gemspecs and NPM package.json with
+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
rubygems.org.
diff --git a/actioncable/CHANGELOG.md b/actioncable/CHANGELOG.md
index 9f312f8806..6fc2f17d32 100644
--- a/actioncable/CHANGELOG.md
+++ b/actioncable/CHANGELOG.md
@@ -1,154 +1,3 @@
-## Rails 6.0.0.beta3 (March 11, 2019) ##
-* No changes.
-
-## Rails 6.0.0.beta2 (February 25, 2019) ##
-
-* PostgreSQL subscription adapters now support `channel_prefix` option in cable.yml
-
- Avoids channel name collisions when multiple apps use the same database for Action Cable.
-
- *Vladimir Dementyev*
-
-* Allow passing custom configuration to `ActionCable::Server::Base`.
-
- You can now create a standalone Action Cable server with a custom configuration
- (e.g. to run it in isolation from the default one):
-
- ```ruby
- config = ActionCable::Server::Configuration.new
- config.cable = { adapter: "redis", channel_prefix: "custom_" }
-
- CUSTOM_CABLE = ActionCable::Server::Base.new(config: config)
- ```
-
- Then you can mount it in the `routes.rb` file:
-
- ```ruby
- Rails.application.routes.draw do
- mount CUSTOM_CABLE => "/custom_cable"
- # ...
- end
- ```
-
- *Vladimir Dementyev*
-
-* Add `:action_cable_connection` and `:action_cable_channel` load hooks.
-
- You can use them to extend `ActionCable::Connection::Base` and `ActionCable::Channel::Base`
- functionality:
-
- ```ruby
- ActiveSupport.on_load(:action_cable_channel) do
- # do something in the context of ActionCable::Channel::Base
- end
- ```
-
- *Vladimir Dementyev*
-
-* Add `Channel::Base#broadcast_to`.
-
- You can now call `broadcast_to` within a channel action, which equals to
- the `self.class.broadcast_to`.
-
- *Vladimir Dementyev*
-
-* Make `Channel::Base.broadcasting_for` a public API.
-
- You can use `.broadcasting_for` to generate a unique stream identifier within
- a channel for the specified target (e.g. Active Record model):
-
- ```ruby
- ChatChannel.broadcasting_for(model) # => "chat:<model.to_gid_param>"
- ```
-
- *Vladimir Dementyev*
-
-
-## Rails 6.0.0.beta1 (January 18, 2019) ##
-
-* [Rename npm package](https://github.com/rails/rails/pull/34905) from
- [`actioncable`](https://www.npmjs.com/package/actioncable) to
- [`@rails/actioncable`](https://www.npmjs.com/package/@rails/actioncable).
-
- *Javan Makhmali*
-
-* Merge [`action-cable-testing`](https://github.com/palkan/action-cable-testing) to Rails.
-
- *Vladimir Dementyev*
-
-* The JavaScript WebSocket client will no longer try to reconnect
- when you call `reject_unauthorized_connection` on the connection.
-
- *Mick Staugaard*
-
-* `ActionCable.Connection#getState` now references the configurable
- `ActionCable.adapters.WebSocket` property rather than the `WebSocket` global
- variable, matching the behavior of `ActionCable.Connection#open`.
-
- *Richard Macklin*
-
-* The ActionCable javascript package has been converted from CoffeeScript
- to ES2015, and we now publish the source code in the npm distribution.
-
- This allows ActionCable users to depend on the javascript source code
- rather than the compiled code, which can produce smaller javascript bundles.
-
- This change includes some breaking changes to optional parts of the
- ActionCable javascript API:
-
- - Configuration of the WebSocket adapter and logger adapter have been moved
- from properties of `ActionCable` to properties of `ActionCable.adapters`.
- If you are currently configuring these adapters you will need to make
- these changes when upgrading:
-
- ```diff
- - ActionCable.WebSocket = MyWebSocket
- + ActionCable.adapters.WebSocket = MyWebSocket
- ```
- ```diff
- - ActionCable.logger = myLogger
- + ActionCable.adapters.logger = myLogger
- ```
-
- - The `ActionCable.startDebugging()` and `ActionCable.stopDebugging()`
- methods have been removed and replaced with the property
- `ActionCable.logger.enabled`. If you are currently using these methods you
- will need to make these changes when upgrading:
-
- ```diff
- - ActionCable.startDebugging()
- + ActionCable.logger.enabled = true
- ```
- ```diff
- - ActionCable.stopDebugging()
- + ActionCable.logger.enabled = false
- ```
-
- *Richard Macklin*
-
-* Add `id` option to redis adapter so now you can distinguish
- ActionCable's redis connections among others. Also, you can set
- custom id in options.
-
- Before:
- ```
- $ redis-cli client list
- id=669 addr=127.0.0.1:46442 fd=8 name= age=18 ...
- ```
-
- After:
- ```
- $ redis-cli client list
- id=673 addr=127.0.0.1:46516 fd=8 name=ActionCable-PID-19413 age=2 ...
- ```
-
- *Ilia Kasianenko*
-
-* Rails 6 requires Ruby 2.5.0 or newer.
-
- *Jeremy Daer*, *Kasper Timm Hansen*
-
-
-Please check [5-2-stable](https://github.com/rails/rails/blob/5-2-stable/actioncable/CHANGELOG.md) for previous changes.
+Please check [6-0-stable](https://github.com/rails/rails/blob/6-0-stable/actioncable/CHANGELOG.md) for previous changes.
diff --git a/actioncable/lib/action_cable/gem_version.rb b/actioncable/lib/action_cable/gem_version.rb
index 2be81736c6..6e7053e32e 100644
--- a/actioncable/lib/action_cable/gem_version.rb
+++ b/actioncable/lib/action_cable/gem_version.rb
@@ -8,9 +8,9 @@ module ActionCable
module VERSION
MAJOR = 6
- MINOR = 0
+ MINOR = 1
TINY = 0
- PRE = "beta3"
+ PRE = "alpha"
STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
end
diff --git a/actioncable/lib/action_cable/server/broadcasting.rb b/actioncable/lib/action_cable/server/broadcasting.rb
index bc54d784b3..b73cfa7d1f 100644
--- a/actioncable/lib/action_cable/server/broadcasting.rb
+++ b/actioncable/lib/action_cable/server/broadcasting.rb
@@ -40,7 +40,7 @@ module ActionCable
end
def broadcast(message)
- server.logger.debug "[ActionCable] Broadcasting to #{broadcasting}: #{message.inspect}"
+ server.logger.debug { "[ActionCable] Broadcasting to #{broadcasting}: #{message.inspect}" }
payload = { broadcasting: broadcasting, message: message, coder: coder }
ActiveSupport::Notifications.instrument("broadcast.action_cable", payload) do
diff --git a/actioncable/lib/action_cable/server/worker.rb b/actioncable/lib/action_cable/server/worker.rb
index 187c8f7939..b918d4ae72 100644
--- a/actioncable/lib/action_cable/server/worker.rb
+++ b/actioncable/lib/action_cable/server/worker.rb
@@ -66,7 +66,6 @@ module ActionCable
end
private
-
def logger
ActionCable.server.logger
end
diff --git a/actioncable/lib/action_cable/subscription_adapter/redis.rb b/actioncable/lib/action_cable/subscription_adapter/redis.rb
index ad8fa52760..b4a9385c87 100644
--- a/actioncable/lib/action_cable/subscription_adapter/redis.rb
+++ b/actioncable/lib/action_cable/subscription_adapter/redis.rb
@@ -5,6 +5,8 @@ require "thread"
gem "redis", ">= 3", "< 5"
require "redis"
+require "active_support/core_ext/hash/except"
+
module ActionCable
module SubscriptionAdapter
class Redis < Base # :nodoc:
@@ -14,7 +16,7 @@ module ActionCable
# This is needed, for example, when using Makara proxies for distributed Redis.
cattr_accessor :redis_connector, default: ->(config) do
config[:id] ||= "ActionCable-PID-#{$$}"
- ::Redis.new(config.slice(:url, :host, :port, :db, :password, :id))
+ ::Redis.new(config.except(:adapter, :channel_prefix))
end
def initialize(*)
diff --git a/actioncable/package.json b/actioncable/package.json
index 4451afd17f..723aa63c81 100644
--- a/actioncable/package.json
+++ b/actioncable/package.json
@@ -1,6 +1,6 @@
{
"name": "@rails/actioncable",
- "version": "6.0.0-beta3",
+ "version": "6.1.0-alpha",
"description": "WebSocket framework for Ruby on Rails.",
"main": "app/assets/javascripts/action_cable.js",
"files": [
diff --git a/actioncable/test/subscription_adapter/redis_test.rb b/actioncable/test/subscription_adapter/redis_test.rb
index 35840a4036..d4eb89719f 100644
--- a/actioncable/test/subscription_adapter/redis_test.rb
+++ b/actioncable/test/subscription_adapter/redis_test.rb
@@ -34,11 +34,11 @@ class RedisAdapterTest::AlternateConfiguration < RedisAdapterTest
end
class RedisAdapterTest::Connector < ActionCable::TestCase
- test "slices url, host, port, db, password and id from config" do
+ test "excludes adapter and channel prefix" do
config = { url: 1, host: 2, port: 3, db: 4, password: 5, id: "Some custom ID" }
assert_called_with ::Redis, :new, [ config ] do
- connect config.merge(other: "unrelated", stuff: "here")
+ connect config.merge(adapter: "redis", channel_prefix: "custom")
end
end
diff --git a/actionmailbox/CHANGELOG.md b/actionmailbox/CHANGELOG.md
index f5fb573090..bca3d1d9ed 100644
--- a/actionmailbox/CHANGELOG.md
+++ b/actionmailbox/CHANGELOG.md
@@ -1,19 +1,6 @@
-## Rails 6.0.0.beta3 (March 11, 2019) ##
+* Add `ApplicationMailbox.mailbox_for` to expose mailbox routing.
-* No changes.
+ *James Dabbs*
-## Rails 6.0.0.beta2 (February 25, 2019) ##
-
-* Allow skipping incineration of processed emails.
-
- This can be done by setting `config.action_mailbox.incinerate` to `false`.
-
- *Pratik Naik*
-
-
-## Rails 6.0.0.beta1 (January 18, 2019) ##
-
-* Added to Rails.
-
- *DHH*
+Please check [6-0-stable](https://github.com/rails/rails/blob/6-0-stable/actionmailbox/CHANGELOG.md) for previous changes.
diff --git a/actionmailbox/app/controllers/rails/conductor/action_mailbox/inbound_emails_controller.rb b/actionmailbox/app/controllers/rails/conductor/action_mailbox/inbound_emails_controller.rb
index d051dfe665..cd8b12dcde 100644
--- a/actionmailbox/app/controllers/rails/conductor/action_mailbox/inbound_emails_controller.rb
+++ b/actionmailbox/app/controllers/rails/conductor/action_mailbox/inbound_emails_controller.rb
@@ -21,8 +21,9 @@ module Rails
private
def new_mail
Mail.new(params.require(:mail).permit(:from, :to, :cc, :bcc, :in_reply_to, :subject, :body).to_h).tap do |mail|
+ mail[:bcc]&.include_in_headers = true
params[:mail][:attachments].to_a.each do |attachment|
- mail.add_file(filename: attachment.path, content: attachment.read)
+ mail.add_file(filename: attachment.original_filename, content: attachment.read)
end
end
end
diff --git a/actionmailbox/lib/action_mailbox/gem_version.rb b/actionmailbox/lib/action_mailbox/gem_version.rb
index a063553471..5ad2400451 100644
--- a/actionmailbox/lib/action_mailbox/gem_version.rb
+++ b/actionmailbox/lib/action_mailbox/gem_version.rb
@@ -8,9 +8,9 @@ module ActionMailbox
module VERSION
MAJOR = 6
- MINOR = 0
+ MINOR = 1
TINY = 0
- PRE = "beta3"
+ PRE = "alpha"
STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
end
diff --git a/actionmailbox/lib/action_mailbox/router.rb b/actionmailbox/lib/action_mailbox/router.rb
index 71370e409d..54982eae21 100644
--- a/actionmailbox/lib/action_mailbox/router.rb
+++ b/actionmailbox/lib/action_mailbox/router.rb
@@ -21,7 +21,7 @@ module ActionMailbox
end
def route(inbound_email)
- if mailbox = match_to_mailbox(inbound_email)
+ if mailbox = mailbox_for(inbound_email)
mailbox.receive(inbound_email)
else
inbound_email.bounced!
@@ -30,12 +30,12 @@ module ActionMailbox
end
end
+ def mailbox_for(inbound_email)
+ routes.detect { |route| route.match?(inbound_email) }.try(:mailbox_class)
+ end
+
private
attr_reader :routes
-
- def match_to_mailbox(inbound_email)
- routes.detect { |route| route.match?(inbound_email) }.try(:mailbox_class)
- end
end
end
diff --git a/actionmailbox/lib/action_mailbox/routing.rb b/actionmailbox/lib/action_mailbox/routing.rb
index 58462a44c6..8391bf9db0 100644
--- a/actionmailbox/lib/action_mailbox/routing.rb
+++ b/actionmailbox/lib/action_mailbox/routing.rb
@@ -17,6 +17,10 @@ module ActionMailbox
def route(inbound_email)
router.route(inbound_email)
end
+
+ def mailbox_for(inbound_email)
+ router.mailbox_for(inbound_email)
+ end
end
end
end
diff --git a/actionmailbox/lib/rails/generators/test_unit/templates/mailbox_test.rb.tt b/actionmailbox/lib/rails/generators/test_unit/templates/mailbox_test.rb.tt
index 0b51f29fe4..3e215b4d0b 100644
--- a/actionmailbox/lib/rails/generators/test_unit/templates/mailbox_test.rb.tt
+++ b/actionmailbox/lib/rails/generators/test_unit/templates/mailbox_test.rb.tt
@@ -1,5 +1,3 @@
-# frozen_string_literal: true
-
require "test_helper"
class <%= class_name %>MailboxTest < ActionMailbox::TestCase
diff --git a/actionmailbox/test/controllers/rails/action_mailbox/inbound_emails_controller_test.rb b/actionmailbox/test/controllers/rails/action_mailbox/inbound_emails_controller_test.rb
index fcd9ad839f..06f5a9d6db 100644
--- a/actionmailbox/test/controllers/rails/action_mailbox/inbound_emails_controller_test.rb
+++ b/actionmailbox/test/controllers/rails/action_mailbox/inbound_emails_controller_test.rb
@@ -10,6 +10,8 @@ class Rails::Conductor::ActionMailbox::InboundEmailsControllerTest < ActionDispa
mail: {
from: "Jason Fried <jason@37signals.com>",
to: "Replies <replies@example.com>",
+ cc: "CC <cc@example.com>",
+ bcc: "Bcc <bcc@example.com>",
in_reply_to: "<4e6e35f5a38b4_479f13bb90078178@small-app-01.mail>",
subject: "Hey there",
body: "How's it going?"
@@ -20,6 +22,8 @@ class Rails::Conductor::ActionMailbox::InboundEmailsControllerTest < ActionDispa
mail = ActionMailbox::InboundEmail.last.mail
assert_equal %w[ jason@37signals.com ], mail.from
assert_equal %w[ replies@example.com ], mail.to
+ assert_equal %w[ cc@example.com ], mail.cc
+ assert_equal %w[ bcc@example.com ], mail.bcc
assert_equal "4e6e35f5a38b4_479f13bb90078178@small-app-01.mail", mail.in_reply_to
assert_equal "Hey there", mail.subject
assert_equal "How's it going?", mail.body.decoded
@@ -43,6 +47,7 @@ class Rails::Conductor::ActionMailbox::InboundEmailsControllerTest < ActionDispa
mail = ActionMailbox::InboundEmail.last.mail
assert_equal "Let's talk about these images:", mail.text_part.decoded
assert_equal 2, mail.attachments.count
+ assert_equal %w[ avatar1.jpeg avatar2.jpeg ], mail.attachments.collect(&:filename)
end
end
diff --git a/actionmailbox/test/unit/mailbox/routing_test.rb b/actionmailbox/test/unit/mailbox/routing_test.rb
index d4ba702ac5..8302b1d5cc 100644
--- a/actionmailbox/test/unit/mailbox/routing_test.rb
+++ b/actionmailbox/test/unit/mailbox/routing_test.rb
@@ -28,4 +28,9 @@ class ActionMailbox::Base::RoutingTest < ActiveSupport::TestCase
assert_equal "Discussion: Let's debate these attachments", $processed
end
end
+
+ test "mailbox_for" do
+ inbound_email = create_inbound_email_from_fixture "welcome.eml", status: :pending
+ assert_equal RepliesMailbox, ApplicationMailbox.mailbox_for(inbound_email)
+ end
end
diff --git a/actionmailbox/test/unit/router_test.rb b/actionmailbox/test/unit/router_test.rb
index c5dce60856..4dd3730604 100644
--- a/actionmailbox/test/unit/router_test.rb
+++ b/actionmailbox/test/unit/router_test.rb
@@ -135,5 +135,19 @@ module ActionMailbox
@router.add_route Array.new, to: :first
end
end
+
+ test "single string mailbox_for" do
+ @router.add_routes("first@example.com" => :first)
+
+ inbound_email = create_inbound_email_from_mail(to: "first@example.com", subject: "This is a reply")
+ assert_equal FirstMailbox, @router.mailbox_for(inbound_email)
+ end
+
+ test "mailbox_for with no matches" do
+ @router.add_routes("first@example.com" => :first)
+
+ inbound_email = create_inbound_email_from_mail(to: "second@example.com", subject: "This is a reply")
+ assert_nil @router.mailbox_for(inbound_email)
+ end
end
end
diff --git a/actionmailer/CHANGELOG.md b/actionmailer/CHANGELOG.md
index 182c43551d..cb22a4e373 100644
--- a/actionmailer/CHANGELOG.md
+++ b/actionmailer/CHANGELOG.md
@@ -1,81 +1,3 @@
-## Rails 6.0.0.beta3 (March 11, 2019) ##
-* No changes.
-
-## Rails 6.0.0.beta2 (February 25, 2019) ##
-
-* No changes.
-
-
-## Rails 6.0.0.beta1 (January 18, 2019) ##
-
-* Deprecate `ActionMailer::Base.receive` in favor of [Action Mailbox](https://github.com/rails/rails/tree/master/actionmailbox).
-
- *George Claghorn*
-
-* Add `MailDeliveryJob` for delivering both regular and parameterized mail. Deprecate using `DeliveryJob` and `Parameterized::DeliveryJob`.
-
- *Gannon McGibbon*
-
-* Fix ActionMailer assertions not working when a Mail defines
- a custom delivery job class
-
- *Edouard Chin*
-
-* Mails with multipart `format` blocks with implicit render now also check for
- a template name in options hash instead of only using the action name.
-
- *Marcus Ilgner*
-
-* `ActionDispatch::IntegrationTest` includes `ActionMailer::TestHelper` module by default.
-
- *Ricardo Díaz*
-
-* Add `perform_deliveries` to a payload of `deliver.action_mailer` notification.
-
- *Yoshiyuki Kinjo*
-
-* Change delivery logging message when `perform_deliveries` is false.
-
- *Yoshiyuki Kinjo*
-
-* Allow call `assert_enqueued_email_with` with no block.
-
- Example:
- ```
- def test_email
- ContactMailer.welcome.deliver_later
- assert_enqueued_email_with ContactMailer, :welcome
- end
-
- def test_email_with_arguments
- ContactMailer.welcome("Hello", "Goodbye").deliver_later
- assert_enqueued_email_with ContactMailer, :welcome, args: ["Hello", "Goodbye"]
- end
- ```
-
- *bogdanvlviv*
-
-* Ensure mail gem is eager autoloaded when eager load is true to prevent thread deadlocks.
-
- *Samuel Cochran*
-
-* Perform email jobs in `assert_emails`.
-
- *Gannon McGibbon*
-
-* Add `Base.unregister_observer`, `Base.unregister_observers`,
- `Base.unregister_interceptor`, `Base.unregister_interceptors`,
- `Base.unregister_preview_interceptor` and `Base.unregister_preview_interceptors`.
- This makes it possible to dynamically add and remove email observers and
- interceptors at runtime in the same way they're registered.
-
- *Claudio Ortolina*, *Kota Miyake*
-
-* Rails 6 requires Ruby 2.5.0 or newer.
-
- *Jeremy Daer*, *Kasper Timm Hansen*
-
-
-Please check [5-2-stable](https://github.com/rails/rails/blob/5-2-stable/actionmailer/CHANGELOG.md) for previous changes.
+Please check [6-0-stable](https://github.com/rails/rails/blob/6-0-stable/actionmailer/CHANGELOG.md) for previous changes.
diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb
index c1ac9c2ad1..ba914c4813 100644
--- a/actionmailer/lib/action_mailer/base.rb
+++ b/actionmailer/lib/action_mailer/base.rb
@@ -591,7 +591,6 @@ module ActionMailer
end
private
-
def set_payload_for_mail(payload, mail)
payload[:mail] = mail.encoded
payload[:mailer] = name
@@ -873,7 +872,6 @@ module ActionMailer
end
private
-
# Used by #mail to set the content type of the message.
#
# It will use the given +user_content_type+, or multipart if the mail
diff --git a/actionmailer/lib/action_mailer/gem_version.rb b/actionmailer/lib/action_mailer/gem_version.rb
index 4efcd966db..78235dee14 100644
--- a/actionmailer/lib/action_mailer/gem_version.rb
+++ b/actionmailer/lib/action_mailer/gem_version.rb
@@ -8,9 +8,9 @@ module ActionMailer
module VERSION
MAJOR = 6
- MINOR = 0
+ MINOR = 1
TINY = 0
- PRE = "beta3"
+ PRE = "alpha"
STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
end
diff --git a/actionmailer/lib/action_mailer/preview.rb b/actionmailer/lib/action_mailer/preview.rb
index 500b3bede0..a763b9776c 100644
--- a/actionmailer/lib/action_mailer/preview.rb
+++ b/actionmailer/lib/action_mailer/preview.rb
@@ -55,7 +55,6 @@ module ActionMailer
end
private
-
def interceptor_class_for(interceptor)
case interceptor
when String, Symbol
diff --git a/actionmailer/lib/action_mailer/test_case.rb b/actionmailer/lib/action_mailer/test_case.rb
index ee5a864847..97afd87840 100644
--- a/actionmailer/lib/action_mailer/test_case.rb
+++ b/actionmailer/lib/action_mailer/test_case.rb
@@ -22,7 +22,6 @@ module ActionMailer
end
private
-
def clear_test_deliveries
if ActionMailer::Base.delivery_method == :test
ActionMailer::Base.deliveries.clear
@@ -76,7 +75,6 @@ module ActionMailer
end
private
-
def initialize_test_deliveries
set_delivery_method :test
@old_perform_deliveries = ActionMailer::Base.perform_deliveries
diff --git a/actionmailer/lib/action_mailer/test_helper.rb b/actionmailer/lib/action_mailer/test_helper.rb
index e222301dff..988db2491c 100644
--- a/actionmailer/lib/action_mailer/test_helper.rb
+++ b/actionmailer/lib/action_mailer/test_helper.rb
@@ -152,7 +152,6 @@ module ActionMailer
end
private
-
def delivery_job_filter(job)
job_class = job.is_a?(Hash) ? job.fetch(:job) : job.class
diff --git a/actionmailer/test/base_test.rb b/actionmailer/test/base_test.rb
index c07fca5b5e..4ea5634cd0 100644
--- a/actionmailer/test/base_test.rb
+++ b/actionmailer/test/base_test.rb
@@ -939,7 +939,6 @@ class BaseTest < ActiveSupport::TestCase
end
private
-
# Execute the block setting the given values and restoring old values after
# the block is executed.
def swap(klass, new_values)
diff --git a/actionmailer/test/caching_test.rb b/actionmailer/test/caching_test.rb
index b658c96ec7..c3a3e418d0 100644
--- a/actionmailer/test/caching_test.rb
+++ b/actionmailer/test/caching_test.rb
@@ -189,7 +189,6 @@ class FunctionalFragmentCachingTest < BaseCachingTest
end
private
-
def template_digest(name, format)
ActionView::Digestor.digest(name: name, format: format, finder: @mailer.lookup_context)
end
diff --git a/actionmailer/test/i18n_with_controller_test.rb b/actionmailer/test/i18n_with_controller_test.rb
index 6e75cff347..eea72e06ba 100644
--- a/actionmailer/test/i18n_with_controller_test.rb
+++ b/actionmailer/test/i18n_with_controller_test.rb
@@ -67,7 +67,6 @@ class ActionMailerI18nWithControllerTest < ActionDispatch::IntegrationTest
end
private
-
def with_translation(locale, data)
I18n.backend.store_translations(locale, data)
yield
diff --git a/actionmailer/test/legacy_delivery_job_test.rb b/actionmailer/test/legacy_delivery_job_test.rb
index 3a3872df47..a9b2a160d4 100644
--- a/actionmailer/test/legacy_delivery_job_test.rb
+++ b/actionmailer/test/legacy_delivery_job_test.rb
@@ -69,7 +69,6 @@ class LegacyDeliveryJobTest < ActiveSupport::TestCase
end
private
-
def with_delivery_job(job)
old_params_delivery_job = ParamsMailer.delivery_job
old_regular_delivery_job = DelayedMailer.delivery_job
diff --git a/actionmailer/test/mail_helper_test.rb b/actionmailer/test/mail_helper_test.rb
index 51d6ccb10f..a8ab19a95c 100644
--- a/actionmailer/test/mail_helper_test.rb
+++ b/actionmailer/test/mail_helper_test.rb
@@ -68,7 +68,6 @@ The second
end
private
-
def mail_with_defaults(&block)
mail(to: "test@localhost", from: "tester@example.com",
subject: "using helpers", &block)
diff --git a/actionmailer/test/mailers/proc_mailer.rb b/actionmailer/test/mailers/proc_mailer.rb
index 76e730bb79..75ee5dd9b6 100644
--- a/actionmailer/test/mailers/proc_mailer.rb
+++ b/actionmailer/test/mailers/proc_mailer.rb
@@ -18,7 +18,6 @@ class ProcMailer < ActionMailer::Base
end
private
-
def give_a_greeting
"Thanks for signing up this afternoon"
end
diff --git a/actionmailer/test/parameterized_test.rb b/actionmailer/test/parameterized_test.rb
index 7dd6156744..62dd671f48 100644
--- a/actionmailer/test/parameterized_test.rb
+++ b/actionmailer/test/parameterized_test.rb
@@ -81,7 +81,6 @@ class ParameterizedTest < ActiveSupport::TestCase
end
private
-
def with_delivery_job(job)
old_delivery_job = ParamsMailer.delivery_job
ParamsMailer.delivery_job = job
diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md
index 79f6320a04..ca7ae6621b 100644
--- a/actionpack/CHANGELOG.md
+++ b/actionpack/CHANGELOG.md
@@ -1,242 +1,79 @@
-* Raise an `ArgumentError` if a resource custom param contains a colon (`:`).
-
- After this change it's not possible anymore to configure routes like this:
-
- ```
- routes.draw do
- resources :users, param: 'name/:sneaky'
- end
- ```
-
- Fixes #30467.
-
- *Josua Schmid*
-
-
-## Rails 6.0.0.beta3 (March 11, 2019) ##
-
-* No changes.
-
-
-## Rails 6.0.0.beta2 (February 25, 2019) ##
-
-* Make debug exceptions works in an environment where ActiveStorage is not loaded.
-
- *Tomoyuki Kurosawa*
-
-* `ActionDispatch::SystemTestCase.driven_by` can now be called with a block
- to define specific browser capabilities.
+* Fix IntegrationTest `follow_redirect!` to follow redirection using the same HTTP verb when following
+ a 307 redirection.
*Edouard Chin*
+* System tests require Capybara 3.26 or newer.
-## Rails 6.0.0.beta1 (January 18, 2019) ##
-
-* Remove deprecated `fragment_cache_key` helper in favor of `combined_fragment_cache_key`.
-
- *Rafael Mendonça França*
-
-* Remove deprecated methods in `ActionDispatch::TestResponse`.
+ *George Claghorn*
- `#success?`, `missing?` and `error?` were deprecated in Rails 5.2 in favor of
- `#successful?`, `not_found?` and `server_error?`.
+* Reduced log noise handling ActionController::RoutingErrors.
- *Rafael Mendonça França*
+ *Alberto Fernández-Capel*
-* Introduce `ActionDispatch::HostAuthorization`.
+* Add DSL for configuring HTTP Feature Policy
- This is a new middleware that guards against DNS rebinding attacks by
- explicitly permitting the hosts a request can be made to.
+ This new DSL provides a way to configure a HTTP Feature Policy at a
+ global or per-controller level. Full details of HTTP Feature Policy
+ specification and guidelines can be found at MDN:
- Each host is checked with the case operator (`#===`) to support `RegExp`,
- `Proc`, `IPAddr` and custom objects as host allowances.
+ https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Feature-Policy
- *Genadi Samokovarov*
-
-* Allow using `parsed_body` in `ActionController::TestCase`.
-
- In addition to `ActionDispatch::IntegrationTest`, allow using
- `parsed_body` in `ActionController::TestCase`:
+ Example global policy
```
- class SomeControllerTest < ActionController::TestCase
- def test_some_action
- post :action, body: { foo: 'bar' }
- assert_equal({ "foo" => "bar" }, response.parsed_body)
- end
+ Rails.application.config.feature_policy do |f|
+ f.camera :none
+ f.gyroscope :none
+ f.microphone :none
+ f.usb :none
+ f.fullscreen :self
+ f.payment :self, "https://secure.example.com"
end
```
- Fixes #34676.
-
- *Tobias Bühlmann*
-
-* Raise an error on root route naming conflicts.
-
- Raises an `ArgumentError` when multiple root routes are defined in the
- same context instead of assigning nil names to subsequent roots.
-
- *Gannon McGibbon*
-
-* Allow rescue from parameter parse errors:
+ Example controller level policy
```
- rescue_from ActionDispatch::Http::Parameters::ParseError do
- head :unauthorized
+ class PagesController < ApplicationController
+ feature_policy do |p|
+ p.geolocation "https://example.com"
+ end
end
```
- *Gannon McGibbon*, *Josh Cheek*
-
-* Reset Capybara sessions if failed system test screenshot raising an exception.
-
- Reset Capybara sessions if `take_failed_screenshot` raise exception
- in system test `after_teardown`.
-
- *Maxim Perepelitsa*
-
-* Use request object for context if there's no controller
-
- There is no controller instance when using a redirect route or a
- mounted rack application so pass the request object as the context
- when resolving dynamic CSP sources in this scenario.
-
- Fixes #34200.
-
- *Andrew White*
-
-* Apply mapping to symbols returned from dynamic CSP sources
-
- Previously if a dynamic source returned a symbol such as :self it
- would be converted to a string implicitly, e.g:
-
- policy.default_src -> { :self }
-
- would generate the header:
-
- Content-Security-Policy: default-src self
-
- and now it generates:
-
- Content-Security-Policy: default-src 'self'
-
- *Andrew White*
-
-* Add `ActionController::Parameters#each_value`.
-
- *Lukáš Zapletal*
-
-* Deprecate `ActionDispatch::Http::ParameterFilter` in favor of `ActiveSupport::ParameterFilter`.
-
- *Yoshiyuki Kinjo*
-
-* Encode Content-Disposition filenames on `send_data` and `send_file`.
- Previously, `send_data 'data', filename: "\u{3042}.txt"` sends
- `"filename=\"\u{3042}.txt\""` as Content-Disposition and it can be
- garbled.
- Now it follows [RFC 2231](https://tools.ietf.org/html/rfc2231) and
- [RFC 5987](https://tools.ietf.org/html/rfc5987) and sends
- `"filename=\"%3F.txt\"; filename*=UTF-8''%E3%81%82.txt"`.
- Most browsers can find filename correctly and old browsers fallback to ASCII
- converted name.
-
- *Fumiaki Matsushima*
-
-* Expose `ActionController::Parameters#each_key` which allows iterating over
- keys without allocating an array.
-
- *Richard Schneeman*
-
-* Purpose metadata for signed/encrypted cookies.
-
- Rails can now thwart attacks that attempt to copy signed/encrypted value
- of a cookie and use it as the value of another cookie.
-
- It does so by stashing the cookie-name in the purpose field which is
- then signed/encrypted along with the cookie value. Then, on a server-side
- read, we verify the cookie-names and discard any attacked cookies.
-
- Enable `action_dispatch.use_cookies_with_metadata` to use this feature, which
- writes cookies with the new purpose and expiry metadata embedded.
-
- *Assain Jaleel*
-
-* Raises `ActionController::RespondToMismatchError` with conflicting `respond_to` invocations.
-
- `respond_to` can match multiple types and lead to undefined behavior when
- multiple invocations are made and the types do not match:
-
- respond_to do |outer_type|
- outer_type.js do
- respond_to do |inner_type|
- inner_type.html { render body: "HTML" }
- end
- end
- end
-
- *Patrick Toomey*
-
-* `ActionDispatch::Http::UploadedFile` now delegates `to_path` to its tempfile.
-
- This allows uploaded file objects to be passed directly to `File.read`
- without raising a `TypeError`:
-
- uploaded_file = ActionDispatch::Http::UploadedFile.new(tempfile: tmp_file)
- File.read(uploaded_file)
-
- *Aaron Kromer*
-
-* Pass along arguments to underlying `get` method in `follow_redirect!`
-
- Now all arguments passed to `follow_redirect!` are passed to the underlying
- `get` method. This for example allows to set custom headers for the
- redirection request to the server.
-
- follow_redirect!(params: { foo: :bar })
-
- *Remo Fritzsche*
-
-* Introduce a new error page to when the implicit render page is accessed in the browser.
-
- Now instead of showing an error page that with exception and backtraces we now show only
- one informative page.
-
- *Vinicius Stock*
-
-* Introduce `ActionDispatch::DebugExceptions.register_interceptor`.
-
- Exception aware plugin authors can use the newly introduced
- `.register_interceptor` method to get the processed exception, instead of
- monkey patching DebugExceptions.
+ *Jacob Bednarz*
- ActionDispatch::DebugExceptions.register_interceptor do |request, exception|
- HypoteticalPlugin.capture_exception(request, exception)
- end
+* Add the ability to set the CSP nonce only to the specified directives.
- *Genadi Samokovarov*
+ Fixes #35137.
-* Output only one Content-Security-Policy nonce header value per request.
+ *Yuji Yaginuma*
- Fixes #32597.
+* Keep part when scope option has value.
- *Andrey Novikov*, *Andrew White*
+ When a route was defined within an optional scope, if that route didn't
+ take parameters the scope was lost when using path helpers. This commit
+ ensures scope is kept both when the route takes parameters or when it
+ doesn't.
-* Move default headers configuration into their own module that can be included in controllers.
+ Fixes #33219.
- *Kevin Deisz*
+ *Alberto Almagro*
-* Add method `dig` to `session`.
+* Added `deep_transform_keys` and `deep_transform_keys!` methods to ActionController::Parameters.
- *claudiob*, *Takumi Shotoku*
+ *Gustavo Gutierrez*
-* Controller level `force_ssl` has been deprecated in favor of
- `config.force_ssl`.
+* Calling `ActionController::Parameters#transform_keys/!` without a block now returns
+ an enumerator for the parameters instead of the underlying hash.
- *Derek Prior*
+ *Eugene Kenny*
-* Rails 6 requires Ruby 2.5.0 or newer.
+* Fix strong parameters blocks all attributes even when only some keys are invalid (non-numerical).
+ It should only block invalid key's values instead.
- *Jeremy Daer*, *Kasper Timm Hansen*
+ *Stan Lo*
-Please check [5-2-stable](https://github.com/rails/rails/blob/5-2-stable/actionpack/CHANGELOG.md) for previous changes.
+Please check [6-0-stable](https://github.com/rails/rails/blob/6-0-stable/actionpack/CHANGELOG.md) for previous changes.
diff --git a/actionpack/lib/abstract_controller.rb b/actionpack/lib/abstract_controller.rb
index 3a98931167..d1ff62a032 100644
--- a/actionpack/lib/abstract_controller.rb
+++ b/actionpack/lib/abstract_controller.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true
require "action_pack"
+require "active_support"
require "active_support/rails"
require "active_support/i18n"
diff --git a/actionpack/lib/abstract_controller/base.rb b/actionpack/lib/abstract_controller/base.rb
index bb42f2e119..3ff922029b 100644
--- a/actionpack/lib/abstract_controller/base.rb
+++ b/actionpack/lib/abstract_controller/base.rb
@@ -176,7 +176,6 @@ module AbstractController
end
private
-
# Returns true if the name can be considered an action because
# it has a method defined in the controller.
#
diff --git a/actionpack/lib/abstract_controller/collector.rb b/actionpack/lib/abstract_controller/collector.rb
index d4a078ab32..0af546cc96 100644
--- a/actionpack/lib/abstract_controller/collector.rb
+++ b/actionpack/lib/abstract_controller/collector.rb
@@ -22,7 +22,6 @@ module AbstractController
end
private
-
def method_missing(symbol, &block)
unless mime_constant = Mime[symbol]
raise NoMethodError, "To respond to a custom format, register it as a MIME type first: " \
diff --git a/actionpack/lib/abstract_controller/helpers.rb b/actionpack/lib/abstract_controller/helpers.rb
index 3913259ecc..abb09456e0 100644
--- a/actionpack/lib/abstract_controller/helpers.rb
+++ b/actionpack/lib/abstract_controller/helpers.rb
@@ -7,7 +7,7 @@ module AbstractController
extend ActiveSupport::Concern
included do
- class_attribute :_helpers, default: Module.new
+ class_attribute :_helpers, default: define_helpers_module(self)
class_attribute :_helper_methods, default: Array.new
end
@@ -31,7 +31,7 @@ module AbstractController
# independently of the child class's.
def inherited(klass)
helpers = _helpers
- klass._helpers = Module.new { include helpers }
+ klass._helpers = define_helpers_module(klass, helpers)
klass.class_eval { default_helper_module! } unless klass.anonymous?
super
end
@@ -61,12 +61,17 @@ module AbstractController
meths.flatten!
self._helper_methods += meths
+ location = caller_locations(1, 1).first
+ file, line = location.path, location.lineno
+
meths.each do |meth|
- _helpers.class_eval <<-ruby_eval, __FILE__, __LINE__ + 1
- def #{meth}(*args, &blk) # def current_user(*args, &blk)
- controller.send(%(#{meth}), *args, &blk) # controller.send(:current_user, *args, &blk)
- end # end
- ruby_eval
+ method_def = [
+ "def #{meth}(*args, &blk)",
+ " controller.send(%(#{meth}), *args, &blk)",
+ "end"
+ ].join(";")
+
+ _helpers.class_eval method_def, file, line
end
end
@@ -170,6 +175,17 @@ module AbstractController
end
private
+ def define_helpers_module(klass, helpers = nil)
+ # In some tests inherited is called explicitly. In that case, just
+ # return the module from the first time it was defined
+ return klass.const_get(:HelperMethods) if klass.const_defined?(:HelperMethods, false)
+
+ mod = Module.new
+ klass.const_set(:HelperMethods, mod)
+ mod.include(helpers) if helpers
+ mod
+ end
+
# Makes all the (instance) methods in the helper module available to templates
# rendered through this controller.
#
diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb
index 29d61c3ceb..22dc229599 100644
--- a/actionpack/lib/action_controller.rb
+++ b/actionpack/lib/action_controller.rb
@@ -1,6 +1,5 @@
# frozen_string_literal: true
-require "active_support/rails"
require "abstract_controller"
require "action_dispatch"
require "action_controller/metal/live"
@@ -28,6 +27,7 @@ module ActionController
autoload :DefaultHeaders
autoload :EtagWithTemplateDigest
autoload :EtagWithFlash
+ autoload :FeaturePolicy
autoload :Flash
autoload :ForceSSL
autoload :Head
diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb
index 2e565d5d44..63c138af55 100644
--- a/actionpack/lib/action_controller/base.rb
+++ b/actionpack/lib/action_controller/base.rb
@@ -226,6 +226,7 @@ module ActionController
FormBuilder,
RequestForgeryProtection,
ContentSecurityPolicy,
+ FeaturePolicy,
ForceSSL,
Streaming,
DataStreaming,
diff --git a/actionpack/lib/action_controller/caching.rb b/actionpack/lib/action_controller/caching.rb
index bf3b00a7b7..83e3e0c37c 100644
--- a/actionpack/lib/action_controller/caching.rb
+++ b/actionpack/lib/action_controller/caching.rb
@@ -30,7 +30,6 @@ module ActionController
end
private
-
def instrument_payload(key)
{
controller: controller_name,
diff --git a/actionpack/lib/action_controller/metal.rb b/actionpack/lib/action_controller/metal.rb
index b9088e6d86..ec2207b8da 100644
--- a/actionpack/lib/action_controller/metal.rb
+++ b/actionpack/lib/action_controller/metal.rb
@@ -35,7 +35,6 @@ module ActionController
end
private
-
INCLUDE = ->(list, action) { list.include? action }
EXCLUDE = ->(list, action) { !list.include? action }
NULL = ->(list, action) { true }
@@ -148,7 +147,7 @@ module ActionController
attr_internal :response, :request
delegate :session, to: "@_request"
delegate :headers, :status=, :location=, :content_type=,
- :status, :location, :content_type, to: "@_response"
+ :status, :location, :content_type, :media_type, to: "@_response"
def initialize
@_request = nil
diff --git a/actionpack/lib/action_controller/metal/content_security_policy.rb b/actionpack/lib/action_controller/metal/content_security_policy.rb
index b8fab4ebe3..ebd90f07c8 100644
--- a/actionpack/lib/action_controller/metal/content_security_policy.rb
+++ b/actionpack/lib/action_controller/metal/content_security_policy.rb
@@ -36,7 +36,6 @@ module ActionController #:nodoc:
end
private
-
def content_security_policy?
request.content_security_policy
end
diff --git a/actionpack/lib/action_controller/metal/data_streaming.rb b/actionpack/lib/action_controller/metal/data_streaming.rb
index 9ef4f50df1..879745a895 100644
--- a/actionpack/lib/action_controller/metal/data_streaming.rb
+++ b/actionpack/lib/action_controller/metal/data_streaming.rb
@@ -53,7 +53,7 @@ module ActionController #:nodoc:
#
# Show a 404 page in the browser:
#
- # send_file '/path/to/404.html', type: 'text/html; charset=utf-8', status: 404
+ # send_file '/path/to/404.html', type: 'text/html; charset=utf-8', disposition: 'inline', status: 404
#
# Read about the other Content-* HTTP headers if you'd like to
# provide the user with more information (such as Content-Description) in
diff --git a/actionpack/lib/action_controller/metal/feature_policy.rb b/actionpack/lib/action_controller/metal/feature_policy.rb
new file mode 100644
index 0000000000..a627eabea6
--- /dev/null
+++ b/actionpack/lib/action_controller/metal/feature_policy.rb
@@ -0,0 +1,46 @@
+# frozen_string_literal: true
+
+module ActionController #:nodoc:
+ # HTTP Feature Policy is a web standard for defining a mechanism to
+ # allow and deny the use of browser features in its own context, and
+ # in content within any <iframe> elements in the document.
+ #
+ # Full details of HTTP Feature Policy specification and guidelines can
+ # be found at MDN:
+ #
+ # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Feature-Policy
+ #
+ # Examples of usage:
+ #
+ # # Global policy
+ # Rails.application.config.feature_policy do |f|
+ # f.camera :none
+ # f.gyroscope :none
+ # f.microphone :none
+ # f.usb :none
+ # f.fullscreen :self
+ # f.payment :self, "https://secure.example.com"
+ # end
+ #
+ # # Controller level policy
+ # class PagesController < ApplicationController
+ # feature_policy do |p|
+ # p.geolocation "https://example.com"
+ # end
+ # end
+ module FeaturePolicy
+ extend ActiveSupport::Concern
+
+ module ClassMethods
+ def feature_policy(**options, &block)
+ before_action(options) do
+ if block_given?
+ policy = request.feature_policy.clone
+ yield policy
+ request.feature_policy = policy
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/actionpack/lib/action_controller/metal/instrumentation.rb b/actionpack/lib/action_controller/metal/instrumentation.rb
index 51fac08749..6f7fc0d624 100644
--- a/actionpack/lib/action_controller/metal/instrumentation.rb
+++ b/actionpack/lib/action_controller/metal/instrumentation.rb
@@ -69,7 +69,6 @@ module ActionController
end
private
-
# A hook invoked every time a before callback is halted.
def halted_callback_hook(filter)
ActiveSupport::Notifications.instrument("halted_callback.action_controller", filter: filter)
diff --git a/actionpack/lib/action_controller/metal/live.rb b/actionpack/lib/action_controller/metal/live.rb
index dd69930e25..4454ba1e3d 100644
--- a/actionpack/lib/action_controller/metal/live.rb
+++ b/actionpack/lib/action_controller/metal/live.rb
@@ -107,7 +107,6 @@ module ActionController
end
private
-
def perform_write(json, options)
current_options = @options.merge(options).stringify_keys
@@ -205,7 +204,6 @@ module ActionController
end
private
-
def each_chunk(&block)
loop do
str = nil
@@ -220,7 +218,6 @@ module ActionController
class Response < ActionDispatch::Response #:nodoc: all
private
-
def before_committed
super
jar = request.cookie_jar
@@ -286,7 +283,6 @@ module ActionController
end
private
-
# Spawn a new thread to serve up the controller in. This is to get
# around the fact that Rack isn't based around IOs and we need to use
# a thread to stream data from the response bodies. Nobody should call
diff --git a/actionpack/lib/action_controller/metal/mime_responds.rb b/actionpack/lib/action_controller/metal/mime_responds.rb
index bf5e7a433f..5c6f7fe396 100644
--- a/actionpack/lib/action_controller/metal/mime_responds.rb
+++ b/actionpack/lib/action_controller/metal/mime_responds.rb
@@ -205,7 +205,7 @@ module ActionController #:nodoc:
yield collector if block_given?
if format = collector.negotiate_format(request)
- if content_type && content_type != format
+ if media_type && media_type != format
raise ActionController::RespondToMismatchError
end
_process_format(format)
diff --git a/actionpack/lib/action_controller/metal/params_wrapper.rb b/actionpack/lib/action_controller/metal/params_wrapper.rb
index e635abec8e..150ae2666c 100644
--- a/actionpack/lib/action_controller/metal/params_wrapper.rb
+++ b/actionpack/lib/action_controller/metal/params_wrapper.rb
@@ -246,7 +246,6 @@ module ActionController
end
private
-
# Returns the wrapper key which will be used to store wrapped parameters.
def _wrapper_key
_wrapper_options.name
diff --git a/actionpack/lib/action_controller/metal/renderers.rb b/actionpack/lib/action_controller/metal/renderers.rb
index b81d3ef539..a251c29d23 100644
--- a/actionpack/lib/action_controller/metal/renderers.rb
+++ b/actionpack/lib/action_controller/metal/renderers.rb
@@ -157,7 +157,7 @@ module ActionController
json = json.to_json(options) unless json.kind_of?(String)
if options[:callback].present?
- if content_type.nil? || content_type == Mime[:json]
+ if media_type.nil? || media_type == Mime[:json]
self.content_type = Mime[:js]
end
diff --git a/actionpack/lib/action_controller/metal/rendering.rb b/actionpack/lib/action_controller/metal/rendering.rb
index 7d0a944381..efa5de313c 100644
--- a/actionpack/lib/action_controller/metal/rendering.rb
+++ b/actionpack/lib/action_controller/metal/rendering.rb
@@ -53,7 +53,6 @@ module ActionController
end
private
-
def _process_variant(options)
if defined?(request) && !request.nil? && request.variant.present?
options[:variant] = request.variant
@@ -73,7 +72,7 @@ module ActionController
end
def _set_rendered_content_type(format)
- if format && !response.content_type
+ if format && !response.media_type
self.content_type = format.to_s
end
end
diff --git a/actionpack/lib/action_controller/metal/request_forgery_protection.rb b/actionpack/lib/action_controller/metal/request_forgery_protection.rb
index 4bf8d90b69..5a5c04234b 100644
--- a/actionpack/lib/action_controller/metal/request_forgery_protection.rb
+++ b/actionpack/lib/action_controller/metal/request_forgery_protection.rb
@@ -151,7 +151,6 @@ module ActionController #:nodoc:
end
private
-
def protection_method_class(name)
ActionController::RequestForgeryProtection::ProtectionMethods.const_get(name.to_s.classify)
rescue NameError
@@ -175,7 +174,6 @@ module ActionController #:nodoc:
end
private
-
class NullSessionHash < Rack::Session::Abstract::SessionHash #:nodoc:
def initialize(req)
super(nil, req)
diff --git a/actionpack/lib/action_controller/metal/streaming.rb b/actionpack/lib/action_controller/metal/streaming.rb
index 8dc01a5eb9..94a62e5cab 100644
--- a/actionpack/lib/action_controller/metal/streaming.rb
+++ b/actionpack/lib/action_controller/metal/streaming.rb
@@ -196,7 +196,6 @@ module ActionController #:nodoc:
extend ActiveSupport::Concern
private
-
# Set proper cache control and transfer encoding when streaming
def _process_options(options)
super
diff --git a/actionpack/lib/action_controller/metal/strong_parameters.rb b/actionpack/lib/action_controller/metal/strong_parameters.rb
index ae774b01f1..920ae52f2b 100644
--- a/actionpack/lib/action_controller/metal/strong_parameters.rb
+++ b/actionpack/lib/action_controller/metal/strong_parameters.rb
@@ -223,6 +223,12 @@ module ActionController
# config.always_permitted_parameters = %w( controller action format )
cattr_accessor :always_permitted_parameters, default: %w( controller action )
+ class << self
+ def nested_attribute?(key, value) # :nodoc:
+ key =~ /\A-?\d+\z/ && (value.is_a?(Hash) || value.is_a?(Parameters))
+ end
+ end
+
# Returns a new instance of <tt>ActionController::Parameters</tt>.
# Also, sets the +permitted+ attribute to the default value of
# <tt>ActionController::Parameters.permit_all_parameters</tt>.
@@ -253,6 +259,11 @@ module ActionController
@parameters == other
end
end
+ alias eql? ==
+
+ def hash
+ [@parameters.hash, @permitted].hash
+ end
# Returns a safe <tt>ActiveSupport::HashWithIndifferentAccess</tt>
# representation of the parameters with all unpermitted keys removed.
@@ -673,22 +684,37 @@ module ActionController
# Returns a new <tt>ActionController::Parameters</tt> instance with the
# results of running +block+ once for every key. The values are unchanged.
def transform_keys(&block)
- if block
- new_instance_with_inherited_permitted_status(
- @parameters.transform_keys(&block)
- )
- else
- @parameters.transform_keys
- end
+ return to_enum(:transform_keys) unless block_given?
+ new_instance_with_inherited_permitted_status(
+ @parameters.transform_keys(&block)
+ )
end
# Performs keys transformation and returns the altered
# <tt>ActionController::Parameters</tt> instance.
def transform_keys!(&block)
+ return to_enum(:transform_keys!) unless block_given?
@parameters.transform_keys!(&block)
self
end
+ # Returns a new <tt>ActionController::Parameters</tt> instance with the
+ # results of running +block+ once for every key. This includes the keys
+ # from the root hash and from all nested hashes and arrays. The values are unchanged.
+ def deep_transform_keys(&block)
+ new_instance_with_inherited_permitted_status(
+ @parameters.deep_transform_keys(&block)
+ )
+ end
+
+ # Returns the <tt>ActionController::Parameters</tt> instance changing its keys.
+ # This includes the keys from the root hash and from all nested hashes and arrays.
+ # The values are unchanged.
+ def deep_transform_keys!(&block)
+ @parameters.deep_transform_keys!(&block)
+ self
+ end
+
# Deletes a key-value pair from +Parameters+ and returns the value. If
# +key+ is not found, returns +nil+ (or, with optional code block, yields
# +key+ and returns the result). Cf. +#extract!+, which returns the
@@ -723,6 +749,18 @@ module ActionController
end
alias_method :delete_if, :reject!
+ # Returns a new instance of <tt>ActionController::Parameters</tt> without the blank values.
+ # Uses Object#blank? for determining if a value is blank.
+ def compact_blank
+ reject { |_k, v| v.blank? }
+ end
+
+ # Removes all blank values in place and returns self.
+ # Uses Object#blank? for determining if a value is blank.
+ def compact_blank!
+ reject! { |_k, v| v.blank? }
+ end
+
# Returns values that were assigned to the given +keys+. Note that all the
# +Hash+ objects will be converted to <tt>ActionController::Parameters</tt>.
def values_at(*keys)
@@ -811,8 +849,14 @@ module ActionController
attr_writer :permitted
- def fields_for_style?
- @parameters.all? { |k, v| k =~ /\A-?\d+\z/ && (v.is_a?(Hash) || v.is_a?(Parameters)) }
+ def nested_attributes?
+ @parameters.any? { |k, v| Parameters.nested_attribute?(k, v) }
+ end
+
+ def each_nested_attribute
+ hash = self.class.new
+ self.each { |k, v| hash[k] = yield v if Parameters.nested_attribute?(k, v) }
+ hash
end
private
@@ -857,15 +901,13 @@ module ActionController
end
end
- def each_element(object)
+ def each_element(object, &block)
case object
when Array
object.grep(Parameters).map { |el| yield el }.compact
when Parameters
- if object.fields_for_style?
- hash = object.class.new
- object.each { |k, v| hash[k] = yield v }
- hash
+ if object.nested_attributes?
+ object.each_nested_attribute(&block)
else
yield object
end
diff --git a/actionpack/lib/action_controller/template_assertions.rb b/actionpack/lib/action_controller/template_assertions.rb
index dd83c1a283..ec44dbe157 100644
--- a/actionpack/lib/action_controller/template_assertions.rb
+++ b/actionpack/lib/action_controller/template_assertions.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module ActionController
- module TemplateAssertions
+ module TemplateAssertions # :nodoc:
def assert_template(options = {}, message = nil)
raise NoMethodError,
"assert_template has been extracted to a gem. To continue using it,
diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb
index 57921f32b7..47e0099f20 100644
--- a/actionpack/lib/action_controller/test_case.rb
+++ b/actionpack/lib/action_controller/test_case.rb
@@ -158,7 +158,6 @@ module ActionController
end.new
private
-
def params_parsers
super.merge @custom_param_parsers
end
@@ -208,7 +207,6 @@ module ActionController
end
private
-
def load!
@id
end
@@ -594,7 +592,6 @@ module ActionController
end
private
-
def scrub_env!(env)
env.delete_if { |k, v| k =~ /^(action_dispatch|rack)\.request/ }
env.delete_if { |k, v| k =~ /^action_dispatch\.rescue/ }
diff --git a/actionpack/lib/action_dispatch.rb b/actionpack/lib/action_dispatch.rb
index 8f39b88d56..67d303a368 100644
--- a/actionpack/lib/action_dispatch.rb
+++ b/actionpack/lib/action_dispatch.rb
@@ -43,6 +43,7 @@ module ActionDispatch
eager_autoload do
autoload_under "http" do
autoload :ContentSecurityPolicy
+ autoload :FeaturePolicy
autoload :Request
autoload :Response
end
@@ -53,6 +54,7 @@ module ActionDispatch
autoload :RequestId
autoload :Callbacks
autoload :Cookies
+ autoload :ActionableExceptions
autoload :DebugExceptions
autoload :DebugLocks
autoload :DebugView
diff --git a/actionpack/lib/action_dispatch/http/cache.rb b/actionpack/lib/action_dispatch/http/cache.rb
index 8cc84ff36c..7be30be77a 100644
--- a/actionpack/lib/action_dispatch/http/cache.rb
+++ b/actionpack/lib/action_dispatch/http/cache.rb
@@ -123,7 +123,6 @@ module ActionDispatch
end
private
-
DATE = "Date"
LAST_MODIFIED = "Last-Modified"
SPECIAL_KEYS = Set.new(%w[extras no-cache max-age public private must-revalidate])
diff --git a/actionpack/lib/action_dispatch/http/content_security_policy.rb b/actionpack/lib/action_dispatch/http/content_security_policy.rb
index b1e5a28be5..9c430b57e3 100644
--- a/actionpack/lib/action_dispatch/http/content_security_policy.rb
+++ b/actionpack/lib/action_dispatch/http/content_security_policy.rb
@@ -22,15 +22,15 @@ module ActionDispatch #:nodoc:
if policy = request.content_security_policy
nonce = request.content_security_policy_nonce
+ nonce_directives = request.content_security_policy_nonce_directives
context = request.controller_instance || request
- headers[header_name(request)] = policy.build(context, nonce)
+ headers[header_name(request)] = policy.build(context, nonce, nonce_directives)
end
response
end
private
-
def html_response?(headers)
if content_type = headers[CONTENT_TYPE]
content_type =~ /html/
@@ -55,6 +55,7 @@ module ActionDispatch #:nodoc:
POLICY_REPORT_ONLY = "action_dispatch.content_security_policy_report_only"
NONCE_GENERATOR = "action_dispatch.content_security_policy_nonce_generator"
NONCE = "action_dispatch.content_security_policy_nonce"
+ NONCE_DIRECTIVES = "action_dispatch.content_security_policy_nonce_directives"
def content_security_policy
get_header(POLICY)
@@ -80,6 +81,14 @@ module ActionDispatch #:nodoc:
set_header(NONCE_GENERATOR, generator)
end
+ def content_security_policy_nonce_directives
+ get_header(NONCE_DIRECTIVES)
+ end
+
+ def content_security_policy_nonce_directives=(generator)
+ set_header(NONCE_DIRECTIVES, generator)
+ end
+
def content_security_policy_nonce
if content_security_policy_nonce_generator
if nonce = get_header(NONCE)
@@ -91,7 +100,6 @@ module ActionDispatch #:nodoc:
end
private
-
def generate_content_security_policy_nonce
content_security_policy_nonce_generator.call(self)
end
@@ -129,13 +137,17 @@ module ActionDispatch #:nodoc:
object_src: "object-src",
prefetch_src: "prefetch-src",
script_src: "script-src",
+ script_src_attr: "script-src-attr",
+ script_src_elem: "script-src-elem",
style_src: "style-src",
+ style_src_attr: "style-src-attr",
+ style_src_elem: "style-src-elem",
worker_src: "worker-src"
}.freeze
- NONCE_DIRECTIVES = %w[script-src style-src].freeze
+ DEFAULT_NONCE_DIRECTIVES = %w[script-src style-src].freeze
- private_constant :MAPPINGS, :DIRECTIVES, :NONCE_DIRECTIVES
+ private_constant :MAPPINGS, :DIRECTIVES, :DEFAULT_NONCE_DIRECTIVES
attr_reader :directives
@@ -204,8 +216,9 @@ module ActionDispatch #:nodoc:
end
end
- def build(context = nil, nonce = nil)
- build_directives(context, nonce).compact.join("; ")
+ def build(context = nil, nonce = nil, nonce_directives = nil)
+ nonce_directives = DEFAULT_NONCE_DIRECTIVES if nonce_directives.nil?
+ build_directives(context, nonce, nonce_directives).compact.join("; ")
end
private
@@ -228,10 +241,10 @@ module ActionDispatch #:nodoc:
end
end
- def build_directives(context, nonce)
+ def build_directives(context, nonce, nonce_directives)
@directives.map do |directive, sources|
if sources.is_a?(Array)
- if nonce && nonce_directive?(directive)
+ if nonce && nonce_directive?(directive, nonce_directives)
"#{directive} #{build_directive(sources, context).join(' ')} 'nonce-#{nonce}'"
else
"#{directive} #{build_directive(sources, context).join(' ')}"
@@ -266,8 +279,8 @@ module ActionDispatch #:nodoc:
end
end
- def nonce_directive?(directive)
- NONCE_DIRECTIVES.include?(directive)
+ def nonce_directive?(directive, nonce_directives)
+ nonce_directives.include?(directive)
end
end
end
diff --git a/actionpack/lib/action_dispatch/http/feature_policy.rb b/actionpack/lib/action_dispatch/http/feature_policy.rb
new file mode 100644
index 0000000000..592b6e4393
--- /dev/null
+++ b/actionpack/lib/action_dispatch/http/feature_policy.rb
@@ -0,0 +1,168 @@
+# frozen_string_literal: true
+
+require "active_support/core_ext/object/deep_dup"
+
+module ActionDispatch #:nodoc:
+ class FeaturePolicy
+ class Middleware
+ CONTENT_TYPE = "Content-Type"
+ POLICY = "Feature-Policy"
+
+ def initialize(app)
+ @app = app
+ end
+
+ def call(env)
+ request = ActionDispatch::Request.new(env)
+ _, headers, _ = response = @app.call(env)
+
+ return response unless html_response?(headers)
+ return response if policy_present?(headers)
+
+ if policy = request.feature_policy
+ headers[POLICY] = policy.build(request.controller_instance)
+ end
+
+ if policy_empty?(policy)
+ headers.delete(POLICY)
+ end
+
+ response
+ end
+
+ private
+ def html_response?(headers)
+ if content_type = headers[CONTENT_TYPE]
+ content_type =~ /html/
+ end
+ end
+
+ def policy_present?(headers)
+ headers[POLICY]
+ end
+
+ def policy_empty?(policy)
+ policy.try(:directives) && policy.directives.empty?
+ end
+ end
+
+ module Request
+ POLICY = "action_dispatch.feature_policy"
+
+ def feature_policy
+ get_header(POLICY)
+ end
+
+ def feature_policy=(policy)
+ set_header(POLICY, policy)
+ end
+ end
+
+ MAPPINGS = {
+ self: "'self'",
+ none: "'none'",
+ }.freeze
+
+ # List of available features can be found at
+ # https://github.com/WICG/feature-policy/blob/master/features.md#policy-controlled-features
+ DIRECTIVES = {
+ accelerometer: "accelerometer",
+ ambient_light_sensor: "ambient-light-sensor",
+ autoplay: "autoplay",
+ camera: "camera",
+ encrypted_media: "encrypted-media",
+ fullscreen: "fullscreen",
+ geolocation: "geolocation",
+ gyroscope: "gyroscope",
+ magnetometer: "magnetometer",
+ microphone: "microphone",
+ midi: "midi",
+ payment: "payment",
+ picture_in_picture: "picture-in-picture",
+ speaker: "speaker",
+ usb: "usb",
+ vibrate: "vibrate",
+ vr: "vr",
+ }.freeze
+
+ private_constant :MAPPINGS, :DIRECTIVES
+
+ attr_reader :directives
+
+ def initialize
+ @directives = {}
+ yield self if block_given?
+ end
+
+ def initialize_copy(other)
+ @directives = other.directives.deep_dup
+ end
+
+ DIRECTIVES.each do |name, directive|
+ define_method(name) do |*sources|
+ if sources.first
+ @directives[directive] = apply_mappings(sources)
+ else
+ @directives.delete(directive)
+ end
+ end
+ end
+
+ def build(context = nil)
+ build_directives(context).compact.join("; ")
+ end
+
+ private
+ def apply_mappings(sources)
+ sources.map do |source|
+ case source
+ when Symbol
+ apply_mapping(source)
+ when String, Proc
+ source
+ else
+ raise ArgumentError, "Invalid HTTP feature policy source: #{source.inspect}"
+ end
+ end
+ end
+
+ def apply_mapping(source)
+ MAPPINGS.fetch(source) do
+ raise ArgumentError, "Unknown HTTP feature policy source mapping: #{source.inspect}"
+ end
+ end
+
+ def build_directives(context)
+ @directives.map do |directive, sources|
+ if sources.is_a?(Array)
+ "#{directive} #{build_directive(sources, context).join(' ')}"
+ elsif sources
+ directive
+ else
+ nil
+ end
+ end
+ end
+
+ def build_directive(sources, context)
+ sources.map { |source| resolve_source(source, context) }
+ end
+
+ def resolve_source(source, context)
+ case source
+ when String
+ source
+ when Symbol
+ source.to_s
+ when Proc
+ if context.nil?
+ raise RuntimeError, "Missing context for the dynamic feature policy source: #{source.inspect}"
+ else
+ context.instance_exec(&source)
+ end
+ else
+ raise RuntimeError, "Unexpected feature policy source: #{source.inspect}"
+ end
+ end
+ end
+end
diff --git a/actionpack/lib/action_dispatch/http/filter_parameters.rb b/actionpack/lib/action_dispatch/http/filter_parameters.rb
index cbb772175c..7a7a493f64 100644
--- a/actionpack/lib/action_dispatch/http/filter_parameters.rb
+++ b/actionpack/lib/action_dispatch/http/filter_parameters.rb
@@ -56,7 +56,6 @@ module ActionDispatch
end
private
-
def parameter_filter # :doc:
parameter_filter_for fetch_header("action_dispatch.parameter_filter") {
return NULL_PARAM_FILTER
diff --git a/actionpack/lib/action_dispatch/http/filter_redirect.rb b/actionpack/lib/action_dispatch/http/filter_redirect.rb
index 8c4e852235..d780d5f793 100644
--- a/actionpack/lib/action_dispatch/http/filter_redirect.rb
+++ b/actionpack/lib/action_dispatch/http/filter_redirect.rb
@@ -14,7 +14,6 @@ module ActionDispatch
end
private
-
def location_filters
if request
request.get_header("action_dispatch.redirect_filter") || []
diff --git a/actionpack/lib/action_dispatch/http/headers.rb b/actionpack/lib/action_dispatch/http/headers.rb
index 6c7d24d2d0..6ab913bfd0 100644
--- a/actionpack/lib/action_dispatch/http/headers.rb
+++ b/actionpack/lib/action_dispatch/http/headers.rb
@@ -116,7 +116,6 @@ module ActionDispatch
def env; @req.env.dup; end
private
-
# Converts an HTTP header name to an environment variable name if it is
# not contained within the headers hash.
def env_name(key)
diff --git a/actionpack/lib/action_dispatch/http/mime_negotiation.rb b/actionpack/lib/action_dispatch/http/mime_negotiation.rb
index 4e81ba12a5..a2cac49082 100644
--- a/actionpack/lib/action_dispatch/http/mime_negotiation.rb
+++ b/actionpack/lib/action_dispatch/http/mime_negotiation.rb
@@ -154,7 +154,6 @@ module ActionDispatch
end
private
-
BROWSER_LIKE_ACCEPTS = /,\s*\*\/\*|\*\/\*\s*,/
def valid_accept_header # :doc:
diff --git a/actionpack/lib/action_dispatch/http/mime_type.rb b/actionpack/lib/action_dispatch/http/mime_type.rb
index 88b3a93211..ed1d50f3b9 100644
--- a/actionpack/lib/action_dispatch/http/mime_type.rb
+++ b/actionpack/lib/action_dispatch/http/mime_type.rb
@@ -290,11 +290,9 @@ module Mime
def all?; false; end
protected
-
attr_reader :string, :synonyms
private
-
def to_ary; end
def to_a; end
diff --git a/actionpack/lib/action_dispatch/http/parameters.rb b/actionpack/lib/action_dispatch/http/parameters.rb
index 13d0963a33..3c16817af3 100644
--- a/actionpack/lib/action_dispatch/http/parameters.rb
+++ b/actionpack/lib/action_dispatch/http/parameters.rb
@@ -85,7 +85,6 @@ module ActionDispatch
end
private
-
def set_binary_encoding(params, controller, action)
return params unless controller && controller.valid_encoding?
diff --git a/actionpack/lib/action_dispatch/http/request.rb b/actionpack/lib/action_dispatch/http/request.rb
index 44f23940d3..4ac7c5c2bd 100644
--- a/actionpack/lib/action_dispatch/http/request.rb
+++ b/actionpack/lib/action_dispatch/http/request.rb
@@ -23,6 +23,7 @@ module ActionDispatch
include ActionDispatch::Http::FilterParameters
include ActionDispatch::Http::URL
include ActionDispatch::ContentSecurityPolicy::Request
+ include ActionDispatch::FeaturePolicy::Request
include Rack::Request::Env
autoload :Session, "action_dispatch/request/session"
diff --git a/actionpack/lib/action_dispatch/http/response.rb b/actionpack/lib/action_dispatch/http/response.rb
index 69798f99e0..ea3692951f 100644
--- a/actionpack/lib/action_dispatch/http/response.rb
+++ b/actionpack/lib/action_dispatch/http/response.rb
@@ -86,6 +86,7 @@ module ActionDispatch # :nodoc:
cattr_accessor :default_charset, default: "utf-8"
cattr_accessor :default_headers
+ cattr_accessor :return_only_media_type_on_content_type, default: false
include Rack::Response::Helpers
# Aliasing these off because AD::Http::Cache::Response defines them.
@@ -143,7 +144,6 @@ module ActionDispatch # :nodoc:
end
private
-
def each_chunk(&block)
@buf.each(&block)
end
@@ -243,8 +243,22 @@ module ActionDispatch # :nodoc:
end
# Content type of response.
- # It returns just MIME type and does NOT contain charset part.
def content_type
+ if self.class.return_only_media_type_on_content_type
+ ActiveSupport::Deprecation.warn(
+ "Rails 6.1 will return Content-Type header without modification." \
+ " If you want just the MIME type, please use `#media_type` instead."
+ )
+
+ content_type = super
+ content_type ? content_type.split(/;\s*charset=/)[0].presence : content_type
+ else
+ super.presence
+ end
+ end
+
+ # Media type of response.
+ def media_type
parsed_content_type_header.mime_type
end
@@ -405,7 +419,6 @@ module ActionDispatch # :nodoc:
end
private
-
ContentTypeHeader = Struct.new :mime_type, :charset
NullContentTypeHeader = ContentTypeHeader.new nil, nil
@@ -458,7 +471,7 @@ module ActionDispatch # :nodoc:
end
def assign_default_content_type_and_charset!
- return if content_type
+ return if media_type
ct = parsed_content_type_header
set_content_type(ct.mime_type || Mime[:html].to_s,
diff --git a/actionpack/lib/action_dispatch/http/url.rb b/actionpack/lib/action_dispatch/http/url.rb
index 8227749986..3b0f6378ea 100644
--- a/actionpack/lib/action_dispatch/http/url.rb
+++ b/actionpack/lib/action_dispatch/http/url.rb
@@ -78,7 +78,6 @@ module ActionDispatch
end
private
-
def add_params(path, params)
params = { params: params } unless params.is_a?(Hash)
params.reject! { |_, v| v.to_param.nil? }
diff --git a/actionpack/lib/action_dispatch/journey/formatter.rb b/actionpack/lib/action_dispatch/journey/formatter.rb
index 52396ec901..a4861719f8 100644
--- a/actionpack/lib/action_dispatch/journey/formatter.rb
+++ b/actionpack/lib/action_dispatch/journey/formatter.rb
@@ -62,12 +62,11 @@ module ActionDispatch
end
private
-
def extract_parameterized_parts(route, options, recall, parameterize = nil)
parameterized_parts = recall.merge(options)
keys_to_keep = route.parts.reverse_each.drop_while { |part|
- !options.key?(part) || (options[part] || recall[part]).nil?
+ !(options.key?(part) || route.scope_options.key?(part)) || (options[part] || recall[part]).nil?
} | route.required_parts
parameterized_parts.delete_if do |bad_key, _|
diff --git a/actionpack/lib/action_dispatch/journey/gtg/builder.rb b/actionpack/lib/action_dispatch/journey/gtg/builder.rb
index 44c31053cb..2600e7fb70 100644
--- a/actionpack/lib/action_dispatch/journey/gtg/builder.rb
+++ b/actionpack/lib/action_dispatch/journey/gtg/builder.rb
@@ -128,7 +128,6 @@ module ActionDispatch
end
private
-
def followpos_table
@followpos ||= build_followpos
end
diff --git a/actionpack/lib/action_dispatch/journey/gtg/transition_table.rb b/actionpack/lib/action_dispatch/journey/gtg/transition_table.rb
index ea647e051a..5003e92f43 100644
--- a/actionpack/lib/action_dispatch/journey/gtg/transition_table.rb
+++ b/actionpack/lib/action_dispatch/journey/gtg/transition_table.rb
@@ -141,7 +141,6 @@ module ActionDispatch
end
private
-
def states_hash_for(sym)
case sym
when String
diff --git a/actionpack/lib/action_dispatch/journey/nfa/transition_table.rb b/actionpack/lib/action_dispatch/journey/nfa/transition_table.rb
index fe55861507..b36003089d 100644
--- a/actionpack/lib/action_dispatch/journey/nfa/transition_table.rb
+++ b/actionpack/lib/action_dispatch/journey/nfa/transition_table.rb
@@ -94,7 +94,6 @@ module ActionDispatch
end
private
-
def inverted
return @inverted if @inverted
diff --git a/actionpack/lib/action_dispatch/journey/path/pattern.rb b/actionpack/lib/action_dispatch/journey/path/pattern.rb
index dee2980eb1..e4ba82ebdd 100644
--- a/actionpack/lib/action_dispatch/journey/path/pattern.rb
+++ b/actionpack/lib/action_dispatch/journey/path/pattern.rb
@@ -174,7 +174,6 @@ module ActionDispatch
end
private
-
def regexp_visitor
@anchored ? AnchoredRegexp : UnanchoredRegexp
end
diff --git a/actionpack/lib/action_dispatch/journey/route.rb b/actionpack/lib/action_dispatch/journey/route.rb
index 8165709a3d..4aee7a6f83 100644
--- a/actionpack/lib/action_dispatch/journey/route.rb
+++ b/actionpack/lib/action_dispatch/journey/route.rb
@@ -4,9 +4,9 @@ module ActionDispatch
# :stopdoc:
module Journey
class Route
- attr_reader :app, :path, :defaults, :name, :precedence
+ attr_reader :app, :path, :defaults, :name, :precedence, :constraints,
+ :internal, :scope_options
- attr_reader :constraints, :internal
alias :conditions :constraints
module VerbMatchers
@@ -49,15 +49,10 @@ module ActionDispatch
end
end
- def self.build(name, app, path, constraints, required_defaults, defaults)
- request_method_match = verb_matcher(constraints.delete(:request_method))
- new name, app, path, constraints, required_defaults, defaults, request_method_match, 0
- end
-
##
# +path+ is a path constraint.
# +constraints+ is a hash of constraints to be applied to this route.
- def initialize(name, app, path, constraints, required_defaults, defaults, request_method_match, precedence, internal = false)
+ def initialize(name:, app: nil, path:, constraints: {}, required_defaults: [], defaults: {}, request_method_match: nil, precedence: 0, scope_options: {}, internal: false)
@name = name
@app = app
@path = path
@@ -72,6 +67,7 @@ module ActionDispatch
@decorated_ast = nil
@precedence = precedence
@path_formatter = @path.build_formatter
+ @scope_options = scope_options
@internal = internal
end
diff --git a/actionpack/lib/action_dispatch/journey/router.rb b/actionpack/lib/action_dispatch/journey/router.rb
index 89a164f968..4a6639af74 100644
--- a/actionpack/lib/action_dispatch/journey/router.rb
+++ b/actionpack/lib/action_dispatch/journey/router.rb
@@ -81,7 +81,6 @@ module ActionDispatch
end
private
-
def partitioned_routes
routes.partition { |r|
r.path.anchored && r.ast.grep(Nodes::Symbol).all? { |n| n.default_regexp? }
diff --git a/actionpack/lib/action_dispatch/journey/routes.rb b/actionpack/lib/action_dispatch/journey/routes.rb
index 3ba8361d77..3f055db66d 100644
--- a/actionpack/lib/action_dispatch/journey/routes.rb
+++ b/actionpack/lib/action_dispatch/journey/routes.rb
@@ -71,7 +71,6 @@ module ActionDispatch
end
private
-
def clear_cache!
@ast = nil
@simulator = nil
diff --git a/actionpack/lib/action_dispatch/journey/scanner.rb b/actionpack/lib/action_dispatch/journey/scanner.rb
index 2a075862e9..eb6fd17aa7 100644
--- a/actionpack/lib/action_dispatch/journey/scanner.rb
+++ b/actionpack/lib/action_dispatch/journey/scanner.rb
@@ -33,7 +33,6 @@ module ActionDispatch
end
private
-
# takes advantage of String @- deduping capabilities in Ruby 2.5 upwards
# see: https://bugs.ruby-lang.org/issues/13077
def dedup_scan(regex)
diff --git a/actionpack/lib/action_dispatch/journey/visitors.rb b/actionpack/lib/action_dispatch/journey/visitors.rb
index d2619cbf3a..ff26c9a3b0 100644
--- a/actionpack/lib/action_dispatch/journey/visitors.rb
+++ b/actionpack/lib/action_dispatch/journey/visitors.rb
@@ -59,7 +59,6 @@ module ActionDispatch
end
private
-
def visit(node)
send(DISPATCH_CACHE[node.type], node)
end
@@ -168,7 +167,6 @@ module ActionDispatch
class String < FunctionalVisitor # :nodoc:
private
-
def binary(node, seed)
visit(node.right, visit(node.left, seed))
end
@@ -214,7 +212,6 @@ module ActionDispatch
end
private
-
def binary(node, seed)
seed.last.concat node.children.map { |c|
"#{node.object_id} -> #{c.object_id};"
diff --git a/actionpack/lib/action_dispatch/middleware/actionable_exceptions.rb b/actionpack/lib/action_dispatch/middleware/actionable_exceptions.rb
new file mode 100644
index 0000000000..e94cc46603
--- /dev/null
+++ b/actionpack/lib/action_dispatch/middleware/actionable_exceptions.rb
@@ -0,0 +1,39 @@
+# frozen_string_literal: true
+
+require "erb"
+require "action_dispatch/http/request"
+require "active_support/actionable_error"
+
+module ActionDispatch
+ class ActionableExceptions # :nodoc:
+ cattr_accessor :endpoint, default: "/rails/actions"
+
+ def initialize(app)
+ @app = app
+ end
+
+ def call(env)
+ request = ActionDispatch::Request.new(env)
+ return @app.call(env) unless actionable_request?(request)
+
+ ActiveSupport::ActionableError.dispatch(request.params[:error].to_s.safe_constantize, request.params[:action])
+
+ redirect_to request.params[:location]
+ end
+
+ private
+ def actionable_request?(request)
+ request.show_exceptions? && request.post? && request.path == endpoint
+ end
+
+ def redirect_to(location)
+ body = "<html><body>You are being <a href=\"#{ERB::Util.unwrapped_html_escape(location)}\">redirected</a>.</body></html>"
+
+ [302, {
+ "Content-Type" => "text/html; charset=#{Response.default_charset}",
+ "Content-Length" => body.bytesize.to_s,
+ "Location" => location,
+ }, [body]]
+ end
+ end
+end
diff --git a/actionpack/lib/action_dispatch/middleware/cookies.rb b/actionpack/lib/action_dispatch/middleware/cookies.rb
index b69bcab05c..642f155085 100644
--- a/actionpack/lib/action_dispatch/middleware/cookies.rb
+++ b/actionpack/lib/action_dispatch/middleware/cookies.rb
@@ -252,7 +252,6 @@ module ActionDispatch
end
private
-
def upgrade_legacy_hmac_aes_cbc_cookies?
request.secret_key_base.present? &&
request.encrypted_signed_cookie_salt.present? &&
@@ -287,8 +286,8 @@ module ActionDispatch
DOMAIN_REGEXP = /[^.]*\.([^.]*|..\...|...\...)$/
def self.build(req, cookies)
- new(req).tap do |hash|
- hash.update(cookies)
+ new(req).tap do |jar|
+ jar.update(cookies)
end
end
@@ -428,7 +427,6 @@ module ActionDispatch
mattr_accessor :always_write_cookie, default: false
private
-
def escape(string)
::Rack::Utils.escape(string)
end
diff --git a/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb b/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb
index 59113e13f4..e546d1c11f 100644
--- a/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb
+++ b/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb
@@ -42,7 +42,6 @@ module ActionDispatch
end
private
-
def invoke_interceptors(request, exception)
backtrace_cleaner = request.get_header("action_dispatch.backtrace_cleaner")
wrapper = ExceptionWrapper.new(backtrace_cleaner, exception)
@@ -138,9 +137,7 @@ module ActionDispatch
return unless logger
exception = wrapper.exception
-
- trace = wrapper.application_trace
- trace = wrapper.framework_trace if trace.empty?
+ trace = wrapper.exception_trace
ActiveSupport::Deprecation.silence do
message = []
diff --git a/actionpack/lib/action_dispatch/middleware/debug_view.rb b/actionpack/lib/action_dispatch/middleware/debug_view.rb
index 43c0a84504..148662a48b 100644
--- a/actionpack/lib/action_dispatch/middleware/debug_view.rb
+++ b/actionpack/lib/action_dispatch/middleware/debug_view.rb
@@ -52,5 +52,15 @@ module ActionDispatch
super
end
end
+
+ def protect_against_forgery?
+ false
+ end
+
+ def params_valid?
+ @request.parameters
+ rescue ActionController::BadRequest
+ false
+ end
end
end
diff --git a/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb b/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb
index 0cc56f5013..e4a2a51c57 100644
--- a/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb
+++ b/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb
@@ -36,18 +36,23 @@ module ActionDispatch
"ActionView::Template::Error"
]
+ cattr_accessor :silent_exceptions, default: [
+ "ActionController::RoutingError"
+ ]
+
attr_reader :backtrace_cleaner, :exception, :wrapped_causes, :line_number, :file
def initialize(backtrace_cleaner, exception)
@backtrace_cleaner = backtrace_cleaner
@exception = exception
+ @exception_class_name = @exception.class.name
@wrapped_causes = wrapped_causes_for(exception, backtrace_cleaner)
expand_backtrace if exception.is_a?(SyntaxError) || exception.cause.is_a?(SyntaxError)
end
def unwrapped_exception
- if wrapper_exceptions.include?(exception.class.to_s)
+ if wrapper_exceptions.include?(@exception_class_name)
exception.cause
else
exception
@@ -55,13 +60,19 @@ module ActionDispatch
end
def rescue_template
- @@rescue_templates[@exception.class.name]
+ @@rescue_templates[@exception_class_name]
end
def status_code
self.class.status_code_for_exception(unwrapped_exception.class.name)
end
+ def exception_trace
+ trace = application_trace
+ trace = framework_trace if trace.empty? && !silent_exceptions.include?(@exception_class_name)
+ trace
+ end
+
def application_trace
clean_backtrace(:silent)
end
@@ -130,7 +141,6 @@ module ActionDispatch
end
private
-
def backtrace
Array(@exception.backtrace)
end
diff --git a/actionpack/lib/action_dispatch/middleware/host_authorization.rb b/actionpack/lib/action_dispatch/middleware/host_authorization.rb
index b7dff1df41..de7739b9b6 100644
--- a/actionpack/lib/action_dispatch/middleware/host_authorization.rb
+++ b/actionpack/lib/action_dispatch/middleware/host_authorization.rb
@@ -30,7 +30,6 @@ module ActionDispatch
end
private
-
def sanitize_hosts(hosts)
Array(hosts).map do |host|
case host
@@ -87,7 +86,6 @@ module ActionDispatch
end
private
-
def authorized?(request)
origin_host = request.get_header("HTTP_HOST").to_s.sub(/:\d+\z/, "")
forwarded_host = request.x_forwarded_host.to_s.split(/,\s?/).last.to_s.sub(/:\d+\z/, "")
diff --git a/actionpack/lib/action_dispatch/middleware/public_exceptions.rb b/actionpack/lib/action_dispatch/middleware/public_exceptions.rb
index a88ad40f21..3a2a1d7334 100644
--- a/actionpack/lib/action_dispatch/middleware/public_exceptions.rb
+++ b/actionpack/lib/action_dispatch/middleware/public_exceptions.rb
@@ -32,7 +32,6 @@ module ActionDispatch
end
private
-
def render(status, content_type, body)
format = "to_#{content_type.to_sym}" if content_type
if format && body.respond_to?(format)
diff --git a/actionpack/lib/action_dispatch/middleware/remote_ip.rb b/actionpack/lib/action_dispatch/middleware/remote_ip.rb
index a5667573f4..c5d4a0bd31 100644
--- a/actionpack/lib/action_dispatch/middleware/remote_ip.rb
+++ b/actionpack/lib/action_dispatch/middleware/remote_ip.rb
@@ -156,7 +156,6 @@ module ActionDispatch
end
private
-
def ips_from(header) # :doc:
return [] unless header
# Split the comma-separated list into an array of strings.
diff --git a/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb b/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb
index 5b0be96223..3815971acb 100644
--- a/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb
+++ b/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb
@@ -30,7 +30,6 @@ module ActionDispatch
end
private
-
def initialize_sid # :doc:
@default_options.delete(:sidbits)
@default_options.delete(:secure_random)
@@ -83,7 +82,6 @@ module ActionDispatch
include SessionObject
private
-
def set_cookie(request, session_id, cookie)
request.cookie_jar[key] = cookie
end
diff --git a/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb b/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb
index 7c43c781c7..892d88803e 100644
--- a/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb
+++ b/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb
@@ -67,7 +67,6 @@ module ActionDispatch
end
private
-
def extract_session_id(req)
stale_session_check! do
unpacked_cookie_data(req)["session_id"]
diff --git a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb
index 767143a368..a35c0da3d9 100644
--- a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb
+++ b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb
@@ -40,7 +40,6 @@ module ActionDispatch
end
private
-
def render_exception(request, exception)
backtrace_cleaner = request.get_header "action_dispatch.backtrace_cleaner"
wrapper = ExceptionWrapper.new(backtrace_cleaner, exception)
diff --git a/actionpack/lib/action_dispatch/middleware/stack.rb b/actionpack/lib/action_dispatch/middleware/stack.rb
index f0c869fba0..775110d95e 100644
--- a/actionpack/lib/action_dispatch/middleware/stack.rb
+++ b/actionpack/lib/action_dispatch/middleware/stack.rb
@@ -34,7 +34,11 @@ module ActionDispatch
end
def build(app)
- InstrumentationProxy.new(klass.new(app, *args, &block), inspect)
+ klass.new(app, *args, &block)
+ end
+
+ def build_instrumented(app)
+ InstrumentationProxy.new(build(app), inspect)
end
end
@@ -119,11 +123,17 @@ module ActionDispatch
end
def build(app = nil, &block)
- middlewares.freeze.reverse.inject(app || block) { |a, e| e.build(a) }
+ instrumenting = ActiveSupport::Notifications.notifier.listening?(InstrumentationProxy::EVENT_NAME)
+ middlewares.freeze.reverse.inject(app || block) do |a, e|
+ if instrumenting
+ e.build_instrumented(a)
+ else
+ e.build(a)
+ end
+ end
end
private
-
def assert_index(index, where)
i = index.is_a?(Integer) ? index : middlewares.index { |m| m.klass == index }
raise "No such middleware to insert #{where}: #{index.inspect}" unless i
diff --git a/actionpack/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb b/actionpack/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb
new file mode 100644
index 0000000000..b6c6d2f50d
--- /dev/null
+++ b/actionpack/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb
@@ -0,0 +1,13 @@
+<% actions = ActiveSupport::ActionableError.actions(exception) %>
+
+<% if actions.any? %>
+ <div class="actions">
+ <% actions.each do |action, _| %>
+ <%= button_to action, ActionDispatch::ActionableExceptions.endpoint, params: {
+ error: exception.class.name,
+ action: action,
+ location: request.path
+ } %>
+ <% end %>
+ </div>
+<% end %>
diff --git a/actionpack/lib/action_dispatch/middleware/templates/rescues/_actions.text.erb b/actionpack/lib/action_dispatch/middleware/templates/rescues/_actions.text.erb
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/actionpack/lib/action_dispatch/middleware/templates/rescues/_actions.text.erb
diff --git a/actionpack/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb b/actionpack/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb
index 49b1e83551..04271d8e8a 100644
--- a/actionpack/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb
+++ b/actionpack/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb
@@ -6,7 +6,9 @@
<% end %>
<h2 style="margin-top: 30px">Request</h2>
-<p><b>Parameters</b>:</p> <pre><%= debug_params(@request.filtered_parameters) %></pre>
+<% if params_valid? %>
+ <p><b>Parameters</b>:</p> <pre><%= debug_params(@request.filtered_parameters) %></pre>
+<% end %>
<div class="details">
<div class="summary"><a href="#" onclick="return toggleSessionDump()">Toggle session dump</a></div>
diff --git a/actionpack/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb b/actionpack/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb
index 396768ecee..ca42a6fa8b 100644
--- a/actionpack/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb
+++ b/actionpack/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb
@@ -1,5 +1,5 @@
<%
- clean_params = @request.filtered_parameters.clone
+ clean_params = params_valid? ? @request.filtered_parameters.clone : {}
clean_params.delete("action")
clean_params.delete("controller")
diff --git a/actionpack/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb b/actionpack/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb
index bde26f46c2..57cdcf9aaf 100644
--- a/actionpack/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb
+++ b/actionpack/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb
@@ -1,14 +1,18 @@
<header>
<h1>
<%= @exception.class.to_s %>
- <% if @request.parameters['controller'] %>
+ <% if params_valid? && @request.parameters['controller'] %>
in <%= @request.parameters['controller'].camelize %>Controller<% if @request.parameters['action'] %>#<%= @request.parameters['action'] %><% end %>
<% end %>
</h1>
</header>
<div id="container">
- <h2><%= h @exception.message %></h2>
+ <h2>
+ <%= h @exception.message %>
+
+ <%= render "rescues/actions", exception: @exception, request: @request %>
+ </h2>
<%= render "rescues/source", source_extracts: @source_extracts, show_source_idx: @show_source_idx, error_index: 0 %>
<%= render "rescues/trace", traces: @traces, trace_to_show: @trace_to_show, error_index: 0 %>
diff --git a/actionpack/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb b/actionpack/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb
index 603de54b8b..d3265563a8 100644
--- a/actionpack/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb
+++ b/actionpack/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb
@@ -1,5 +1,5 @@
<%= @exception.class.to_s %><%
- if @request.parameters['controller']
+ if params_valid? && @request.parameters['controller']
%> in <%= @request.parameters['controller'].camelize %>Controller<% if @request.parameters['action'] %>#<%= @request.parameters['action'] %><% end %>
<% end %>
diff --git a/actionpack/lib/action_dispatch/middleware/templates/rescues/layout.erb b/actionpack/lib/action_dispatch/middleware/templates/rescues/layout.erb
index 39ea25bdfc..f535822ccf 100644
--- a/actionpack/lib/action_dispatch/middleware/templates/rescues/layout.erb
+++ b/actionpack/lib/action_dispatch/middleware/templates/rescues/layout.erb
@@ -2,11 +2,14 @@
<html lang="en">
<head>
<meta charset="utf-8" />
+ <meta name="viewport" content="width=device-width, initial-scale=1">
<title>Action Controller: Exception caught</title>
<style>
body {
background-color: #FAFAFA;
color: #333;
+ color-scheme: light dark;
+ supported-color-schemes: light dark;
margin: 0px;
}
@@ -35,6 +38,7 @@
}
h1 {
+ overflow-wrap: break-word;
margin: 0.2em 0;
line-height: 1.1em;
font-size: 2em;
@@ -50,7 +54,7 @@
border-radius: 4px;
margin: 1em 0px;
display: block;
- width: 978px;
+ max-width: 978px;
}
.summary {
@@ -78,7 +82,7 @@
.source {
border: 1px solid #D9D9D9;
background: #ECECEC;
- width: 978px;
+ max-width: 978px;
}
.source pre {
@@ -114,7 +118,13 @@
}
.line.active {
- background-color: #FFCCCC;
+ background-color: #FCC;
+ }
+
+ .button_to {
+ display: inline-block;
+ margin-top: 0.5em;
+ margin-bottom: 0.5em;
}
.hidden {
@@ -123,10 +133,67 @@
a { color: #980905; }
a:visited { color: #666; }
- a.trace-frames { color: #666; }
+ a.trace-frames {
+ color: #666;
+ overflow-wrap: break-word;
+ }
a:hover { color: #C52F24; }
a.trace-frames.selected { color: #C52F24 }
+ @media (prefers-color-scheme: dark) {
+ body {
+ background-color: #222;
+ color: #ECECEC;
+ }
+
+ .details {
+ border-color: #666;
+ }
+
+ .summary {
+ border-color: #666;
+ }
+
+ .source {
+ border-color: #555;
+ background-color: #333;
+ }
+
+ .source .data {
+ background: #444;
+ }
+
+ .source .data .line_numbers {
+ background: #333;
+ border-color: #222;
+ }
+
+ .line:hover {
+ background: #666;
+ }
+
+ .line.active {
+ background-color: #977;
+ }
+
+ input[type="submit"] {
+ color: #EEE;
+ background-color: #535353;
+ border: none;
+ border-radius: 3px;
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0,0,0,0.15), 0 1px 1px rgba(0,0,0,0.15);
+ padding: 2px 7px;
+ }
+ input[type="submit"]:active {
+ background-color: #777;
+ }
+
+ a { color: #C52F24; }
+ a.trace-frames { color: #999; }
+ a:hover { color: #E9382B; }
+ a.trace-frames.selected { color: #E9382B; }
+ }
+
<%= yield :style %>
</style>
diff --git a/actionpack/lib/action_dispatch/middleware/templates/routes/_table.html.erb b/actionpack/lib/action_dispatch/middleware/templates/routes/_table.html.erb
index 0242b706b2..2fb4650398 100644
--- a/actionpack/lib/action_dispatch/middleware/templates/routes/_table.html.erb
+++ b/actionpack/lib/action_dispatch/middleware/templates/routes/_table.html.erb
@@ -49,6 +49,17 @@
width: 80%;
font-size: inherit;
}
+
+ @media (prefers-color-scheme: dark) {
+ #route_table tbody tr:nth-child(odd) {
+ background: #333;
+ }
+
+ #route_table tbody.exact_matches,
+ #route_table tbody.fuzzy_matches {
+ color: #333;
+ }
+ }
<% end %>
<table id='route_table' class='route_table'>
diff --git a/actionpack/lib/action_dispatch/railtie.rb b/actionpack/lib/action_dispatch/railtie.rb
index efc3988bc3..2e09aed41d 100644
--- a/actionpack/lib/action_dispatch/railtie.rb
+++ b/actionpack/lib/action_dispatch/railtie.rb
@@ -23,6 +23,7 @@ module ActionDispatch
config.action_dispatch.use_authenticated_cookie_encryption = false
config.action_dispatch.use_cookies_with_metadata = false
config.action_dispatch.perform_deep_munge = true
+ config.action_dispatch.return_only_media_type_on_content_type = true
config.action_dispatch.default_headers = {
"X-Frame-Options" => "SAMEORIGIN",
@@ -43,6 +44,7 @@ module ActionDispatch
ActionDispatch::Request::Utils.perform_deep_munge = app.config.action_dispatch.perform_deep_munge
ActionDispatch::Response.default_charset = app.config.action_dispatch.default_charset || app.config.encoding
ActionDispatch::Response.default_headers = app.config.action_dispatch.default_headers
+ ActionDispatch::Response.return_only_media_type_on_content_type = app.config.action_dispatch.return_only_media_type_on_content_type
ActionDispatch::ExceptionWrapper.rescue_responses.merge!(config.action_dispatch.rescue_responses)
ActionDispatch::ExceptionWrapper.rescue_templates.merge!(config.action_dispatch.rescue_templates)
diff --git a/actionpack/lib/action_dispatch/request/session.rb b/actionpack/lib/action_dispatch/request/session.rb
index bc5e0670e0..8faedf15b9 100644
--- a/actionpack/lib/action_dispatch/request/session.rb
+++ b/actionpack/lib/action_dispatch/request/session.rb
@@ -216,7 +216,6 @@ module ActionDispatch
end
private
-
def load_for_read!
load! if !loaded? && exists?
end
diff --git a/actionpack/lib/action_dispatch/routing/inspector.rb b/actionpack/lib/action_dispatch/routing/inspector.rb
index 413e524ef6..6e40a18009 100644
--- a/actionpack/lib/action_dispatch/routing/inspector.rb
+++ b/actionpack/lib/action_dispatch/routing/inspector.rb
@@ -177,7 +177,6 @@ module ActionDispatch
end
private
-
def draw_section(routes)
header_lengths = ["Prefix", "Verb", "URI Pattern"].map(&:length)
name_width, verb_width, path_width = widths(routes).zip(header_lengths).map(&:max)
@@ -210,7 +209,6 @@ module ActionDispatch
end
private
-
def draw_expanded_section(routes)
routes.map.each_with_index do |r, i|
<<~MESSAGE.chomp
diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb
index f29f66990d..d1100089b1 100644
--- a/actionpack/lib/action_dispatch/routing/mapper.rb
+++ b/actionpack/lib/action_dispatch/routing/mapper.rb
@@ -70,17 +70,21 @@ module ActionDispatch
ANCHOR_CHARACTERS_REGEX = %r{\A(\\A|\^)|(\\Z|\\z|\$)\Z}
OPTIONAL_FORMAT_REGEX = %r{(?:\(\.:format\)+|\.:format|/)\Z}
- attr_reader :requirements, :defaults
- attr_reader :to, :default_controller, :default_action
- attr_reader :required_defaults, :ast
+ attr_reader :requirements, :defaults, :to, :default_controller,
+ :default_action, :required_defaults, :ast, :scope_options
def self.build(scope, set, ast, controller, default_action, to, via, formatted, options_constraints, anchor, options)
- options = scope[:options].merge(options) if scope[:options]
-
- defaults = (scope[:defaults] || {}).dup
- scope_constraints = scope[:constraints] || {}
+ scope_params = {
+ blocks: scope[:blocks] || [],
+ constraints: scope[:constraints] || {},
+ defaults: (scope[:defaults] || {}).dup,
+ module: scope[:module],
+ options: scope[:options] || {}
+ }
- new set, ast, defaults, controller, default_action, scope[:module], to, formatted, scope_constraints, scope[:blocks] || [], via, options_constraints, anchor, options
+ new set: set, ast: ast, controller: controller, default_action: default_action,
+ to: to, formatted: formatted, via: via, options_constraints: options_constraints,
+ anchor: anchor, scope_params: scope_params, options: scope_params[:options].merge(options)
end
def self.check_via(via)
@@ -111,10 +115,9 @@ module ActionDispatch
format != false && path !~ OPTIONAL_FORMAT_REGEX
end
- def initialize(set, ast, defaults, controller, default_action, modyoule, to, formatted, scope_constraints, blocks, via, options_constraints, anchor, options)
- @defaults = defaults
- @set = set
-
+ def initialize(set:, ast:, controller:, default_action:, to:, formatted:, via:, options_constraints:, anchor:, scope_params:, options:)
+ @defaults = scope_params[:defaults]
+ @set = set
@to = intern(to)
@default_controller = intern(controller)
@default_action = intern(default_action)
@@ -122,22 +125,23 @@ module ActionDispatch
@anchor = anchor
@via = via
@internal = options.delete(:internal)
+ @scope_options = scope_params[:options]
path_params = ast.find_all(&:symbol?).map(&:to_sym)
options = add_wildcard_options(options, formatted, ast)
- options = normalize_options!(options, path_params, modyoule)
+ options = normalize_options!(options, path_params, scope_params[:module])
split_options = constraints(options, path_params)
- constraints = scope_constraints.merge Hash[split_options[:constraints] || []]
+ constraints = scope_params[:constraints].merge Hash[split_options[:constraints] || []]
if options_constraints.is_a?(Hash)
@defaults = Hash[options_constraints.find_all { |key, default|
URL_OPTIONS.include?(key) && (String === default || Integer === default)
}].merge @defaults
- @blocks = blocks
+ @blocks = scope_params[:blocks]
constraints.merge! options_constraints
else
@blocks = blocks(options_constraints)
@@ -160,8 +164,10 @@ module ActionDispatch
end
def make_route(name, precedence)
- Journey::Route.new(name, application, path, conditions, required_defaults,
- defaults, request_method, precedence, @internal)
+ Journey::Route.new(name: name, app: application, path: path, constraints: conditions,
+ required_defaults: required_defaults, defaults: defaults,
+ request_method_match: request_method, precedence: precedence,
+ scope_options: scope_options, internal: @internal)
end
def application
@@ -1667,7 +1673,6 @@ module ActionDispatch
end
private
-
def parent_resource
@scope[:scope_level_resource]
end
diff --git a/actionpack/lib/action_dispatch/routing/polymorphic_routes.rb b/actionpack/lib/action_dispatch/routing/polymorphic_routes.rb
index 4de5f9e2f7..e3322e99ab 100644
--- a/actionpack/lib/action_dispatch/routing/polymorphic_routes.rb
+++ b/actionpack/lib/action_dispatch/routing/polymorphic_routes.rb
@@ -156,7 +156,6 @@ module ActionDispatch
end
private
-
def polymorphic_url_for_action(action, record_or_hash, options)
polymorphic_url(record_or_hash, options.merge(action: action))
end
@@ -323,7 +322,6 @@ module ActionDispatch
end
private
-
def polymorphic_mapping(target, record)
if record.respond_to?(:to_model)
target._routes.polymorphic_mappings[record.to_model.model_name.name]
diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb
index 4a24c35efb..5b35b68c44 100644
--- a/actionpack/lib/action_dispatch/routing/route_set.rb
+++ b/actionpack/lib/action_dispatch/routing/route_set.rb
@@ -40,7 +40,6 @@ module ActionDispatch
end
private
-
def controller(req)
req.controller_class
rescue NameError => e
@@ -59,7 +58,6 @@ module ActionDispatch
end
private
-
def controller(_); @controller_class; end
end
@@ -215,7 +213,6 @@ module ActionDispatch
end
private
-
def optimized_helper(args)
params = parameterize_args(args) do
raise_generation_error(args)
@@ -591,14 +588,14 @@ module ActionDispatch
if route.segment_keys.include?(:controller)
ActiveSupport::Deprecation.warn(<<-MSG.squish)
Using a dynamic :controller segment in a route is deprecated and
- will be removed in Rails 6.0.
+ will be removed in Rails 6.1.
MSG
end
if route.segment_keys.include?(:action)
ActiveSupport::Deprecation.warn(<<-MSG.squish)
Using a dynamic :action segment in a route is deprecated and
- will be removed in Rails 6.0.
+ will be removed in Rails 6.1.
MSG
end
diff --git a/actionpack/lib/action_dispatch/routing/url_for.rb b/actionpack/lib/action_dispatch/routing/url_for.rb
index fcb8ae296b..e02a6541c1 100644
--- a/actionpack/lib/action_dispatch/routing/url_for.rb
+++ b/actionpack/lib/action_dispatch/routing/url_for.rb
@@ -215,13 +215,11 @@ module ActionDispatch
end
protected
-
def optimize_routes_generation?
_routes.optimize_routes_generation? && default_url_options.empty?
end
private
-
def _with_routes(routes) # :doc:
old_routes, @_routes = @_routes, routes
yield
diff --git a/actionpack/lib/action_dispatch/system_test_case.rb b/actionpack/lib/action_dispatch/system_test_case.rb
index 066daa4a12..aae96975c7 100644
--- a/actionpack/lib/action_dispatch/system_test_case.rb
+++ b/actionpack/lib/action_dispatch/system_test_case.rb
@@ -1,16 +1,16 @@
# frozen_string_literal: true
-gem "capybara", ">= 2.15"
+gem "capybara", ">= 3.26"
require "capybara/dsl"
require "capybara/minitest"
+require "selenium/webdriver"
require "action_controller"
require "action_dispatch/system_testing/driver"
require "action_dispatch/system_testing/browser"
require "action_dispatch/system_testing/server"
require "action_dispatch/system_testing/test_helpers/screenshot_helper"
require "action_dispatch/system_testing/test_helpers/setup_and_teardown"
-require "action_dispatch/system_testing/test_helpers/undef_methods"
module ActionDispatch
# = System Testing
@@ -110,12 +110,11 @@ module ActionDispatch
# Because <tt>ActionDispatch::SystemTestCase</tt> is a shim between Capybara
# and Rails, any driver that is supported by Capybara is supported by system
# tests as long as you include the required gems and files.
- class SystemTestCase < IntegrationTest
+ class SystemTestCase < ActiveSupport::TestCase
include Capybara::DSL
include Capybara::Minitest::Assertions
include SystemTesting::TestHelpers::SetupAndTeardown
include SystemTesting::TestHelpers::ScreenshotHelper
- include SystemTesting::TestHelpers::UndefMethods
def initialize(*) # :nodoc:
super
@@ -160,8 +159,33 @@ module ActionDispatch
driven_by :selenium
- ActiveSupport.run_load_hooks(:action_dispatch_system_test_case, self)
- end
+ private
+ def url_helpers
+ @url_helpers ||=
+ if ActionDispatch.test_app
+ Class.new do
+ include ActionDispatch.test_app.routes.url_helpers
+
+ def url_options
+ default_url_options.reverse_merge(host: Capybara.app_host || Capybara.current_session.server_url)
+ end
+ end.new
+ end
+ end
- SystemTestCase.start_application
+ def method_missing(name, *args, &block)
+ if url_helpers.respond_to?(name)
+ url_helpers.public_send(name, *args, &block)
+ else
+ super
+ end
+ end
+
+ def respond_to_missing?(name, include_private = false)
+ url_helpers.respond_to?(name)
+ end
+ end
end
+
+ActiveSupport.run_load_hooks :action_dispatch_system_test_case, ActionDispatch::SystemTestCase
+ActionDispatch::SystemTestCase.start_application
diff --git a/actionpack/lib/action_dispatch/system_testing/browser.rb b/actionpack/lib/action_dispatch/system_testing/browser.rb
index c34907b6cb..e861e52f09 100644
--- a/actionpack/lib/action_dispatch/system_testing/browser.rb
+++ b/actionpack/lib/action_dispatch/system_testing/browser.rb
@@ -39,6 +39,29 @@ module ActionDispatch
end
end
+ # driver_path can be configured as a proc. The webdrivers gem uses this
+ # proc to update web drivers. Running this proc early allows us to only
+ # update the webdriver once and avoid race conditions when using
+ # parallel tests.
+ def preload
+ case type
+ when :chrome
+ if ::Selenium::WebDriver::Service.respond_to? :driver_path=
+ ::Selenium::WebDriver::Chrome::Service.driver_path.try(:call)
+ else
+ # Selenium <= v3.141.0
+ ::Selenium::WebDriver::Chrome.driver_path
+ end
+ when :firefox
+ if ::Selenium::WebDriver::Service.respond_to? :driver_path=
+ ::Selenium::WebDriver::Firefox::Service.driver_path.try(:call)
+ else
+ # Selenium <= v3.141.0
+ ::Selenium::WebDriver::Firefox.driver_path
+ end
+ end
+ end
+
private
def headless_chrome_browser_options
capabilities.args << "--headless"
diff --git a/actionpack/lib/action_dispatch/system_testing/driver.rb b/actionpack/lib/action_dispatch/system_testing/driver.rb
index 25a09dd918..15943a55ea 100644
--- a/actionpack/lib/action_dispatch/system_testing/driver.rb
+++ b/actionpack/lib/action_dispatch/system_testing/driver.rb
@@ -9,6 +9,8 @@ module ActionDispatch
@screen_size = options[:screen_size]
@options = options[:options]
@capabilities = capabilities
+
+ @browser.preload
end
def use
diff --git a/actionpack/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb b/actionpack/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb
index 79359a0c8b..056ce51a61 100644
--- a/actionpack/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb
+++ b/actionpack/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb
@@ -39,7 +39,8 @@ module ActionDispatch
private
def image_name
- failed? ? "failures_#{method_name}" : method_name
+ name = method_name[0...225]
+ failed? ? "failures_#{name}" : name
end
def image_path
diff --git a/actionpack/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb b/actionpack/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb
index 600e9c733b..30dc21ebb9 100644
--- a/actionpack/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb
+++ b/actionpack/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb
@@ -4,24 +4,22 @@ module ActionDispatch
module SystemTesting
module TestHelpers
module SetupAndTeardown # :nodoc:
- DEFAULT_HOST = "http://127.0.0.1"
-
def host!(host)
- super
+ ActiveSupport::Deprecation.warn \
+ "ActionDispatch::SystemTestCase#host! is deprecated with no replacement. " \
+ "Set Capybara.app_host directly or rely on Capybara's default host."
+
Capybara.app_host = host
end
- def before_setup
- host! DEFAULT_HOST
+ def before_teardown
+ take_failed_screenshot
+ ensure
super
end
def after_teardown
- begin
- take_failed_screenshot
- ensure
- Capybara.reset_sessions!
- end
+ Capybara.reset_sessions!
ensure
super
end
diff --git a/actionpack/lib/action_dispatch/system_testing/test_helpers/undef_methods.rb b/actionpack/lib/action_dispatch/system_testing/test_helpers/undef_methods.rb
deleted file mode 100644
index d64be3b3d9..0000000000
--- a/actionpack/lib/action_dispatch/system_testing/test_helpers/undef_methods.rb
+++ /dev/null
@@ -1,26 +0,0 @@
-# frozen_string_literal: true
-
-module ActionDispatch
- module SystemTesting
- module TestHelpers
- module UndefMethods # :nodoc:
- extend ActiveSupport::Concern
- included do
- METHODS = %i(get post put patch delete).freeze
-
- METHODS.each do |verb|
- undef_method verb
- end
-
- def method_missing(method, *args, &block)
- if METHODS.include?(method)
- raise NoMethodError, "System tests cannot make direct requests via ##{method}; use #visit and #click_on instead. See http://www.rubydoc.info/github/teamcapybara/capybara/master#The_DSL for more information."
- else
- super
- end
- end
- end
- end
- end
- end
-end
diff --git a/actionpack/lib/action_dispatch/testing/assertion_response.rb b/actionpack/lib/action_dispatch/testing/assertion_response.rb
index dc019db6ac..79af372cc1 100644
--- a/actionpack/lib/action_dispatch/testing/assertion_response.rb
+++ b/actionpack/lib/action_dispatch/testing/assertion_response.rb
@@ -35,7 +35,6 @@ module ActionDispatch
end
private
-
def code_from_name(name)
GENERIC_RESPONSE_CODES[name] || Rack::Utils::SYMBOL_TO_STATUS_CODE[name]
end
diff --git a/actionpack/lib/action_dispatch/testing/assertions.rb b/actionpack/lib/action_dispatch/testing/assertions.rb
index 08c2969685..dcaf914ac9 100644
--- a/actionpack/lib/action_dispatch/testing/assertions.rb
+++ b/actionpack/lib/action_dispatch/testing/assertions.rb
@@ -14,7 +14,7 @@ module ActionDispatch
include Rails::Dom::Testing::Assertions
def html_document
- @html_document ||= if @response.content_type.to_s.end_with?("xml")
+ @html_document ||= if @response.media_type.to_s.end_with?("xml")
Nokogiri::XML::Document.parse(@response.body)
else
Nokogiri::HTML::Document.parse(@response.body)
diff --git a/actionpack/lib/action_dispatch/testing/integration.rb b/actionpack/lib/action_dispatch/testing/integration.rb
index bb8b43ad4d..9e7b4301a8 100644
--- a/actionpack/lib/action_dispatch/testing/integration.rb
+++ b/actionpack/lib/action_dispatch/testing/integration.rb
@@ -3,7 +3,6 @@
require "stringio"
require "uri"
require "active_support/core_ext/kernel/singleton_class"
-require "active_support/core_ext/object/try"
require "rack/test"
require "minitest"
@@ -50,11 +49,16 @@ module ActionDispatch
# Follow a single redirect response. If the last response was not a
# redirect, an exception will be raised. Otherwise, the redirect is
- # performed on the location header. Any arguments are passed to the
- # underlying call to `get`.
+ # performed on the location header. If the redirection is a 307 redirect,
+ # the same HTTP verb will be used when redirecting, otherwise a GET request
+ # will be performed. Any arguments are passed to the
+ # underlying request.
def follow_redirect!(**args)
raise "not a redirect! #{status} #{status_message}" unless redirect?
- get(response.location, **args)
+
+ method = response.status == 307 ? request.method.downcase : :get
+ public_send(method, response.location, **args)
+
status
end
end
diff --git a/actionpack/lib/action_dispatch/testing/test_response.rb b/actionpack/lib/action_dispatch/testing/test_response.rb
index 6f7c86fdcf..f1dd4099c5 100644
--- a/actionpack/lib/action_dispatch/testing/test_response.rb
+++ b/actionpack/lib/action_dispatch/testing/test_response.rb
@@ -19,7 +19,7 @@ module ActionDispatch
end
def response_parser
- @response_parser ||= RequestEncoder.parser(content_type)
+ @response_parser ||= RequestEncoder.parser(media_type)
end
end
end
diff --git a/actionpack/lib/action_pack/gem_version.rb b/actionpack/lib/action_pack/gem_version.rb
index 3bbb1734d9..5f8905139d 100644
--- a/actionpack/lib/action_pack/gem_version.rb
+++ b/actionpack/lib/action_pack/gem_version.rb
@@ -8,9 +8,9 @@ module ActionPack
module VERSION
MAJOR = 6
- MINOR = 0
+ MINOR = 1
TINY = 0
- PRE = "beta3"
+ PRE = "alpha"
STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
end
diff --git a/actionpack/test/abstract_unit.rb b/actionpack/test/abstract_unit.rb
index 6460ca6f8d..1decfcee95 100644
--- a/actionpack/test/abstract_unit.rb
+++ b/actionpack/test/abstract_unit.rb
@@ -96,6 +96,7 @@ class ActionDispatch::IntegrationTest < ActiveSupport::TestCase
RoutedRackApp.new(routes || ActionDispatch::Routing::RouteSet.new) do |middleware|
middleware.use ActionDispatch::ShowExceptions, ActionDispatch::PublicExceptions.new("#{FIXTURE_LOAD_PATH}/public")
middleware.use ActionDispatch::DebugExceptions
+ middleware.use ActionDispatch::ActionableExceptions
middleware.use ActionDispatch::Callbacks
middleware.use ActionDispatch::Cookies
middleware.use ActionDispatch::Flash
@@ -334,7 +335,6 @@ module RoutingTestHelpers
end
private
-
def make_request(env)
Request.new super, url_helpers, @block, strict
end
diff --git a/actionpack/test/controller/api/conditional_get_test.rb b/actionpack/test/controller/api/conditional_get_test.rb
index e366ce9532..f1cd9e46f9 100644
--- a/actionpack/test/controller/api/conditional_get_test.rb
+++ b/actionpack/test/controller/api/conditional_get_test.rb
@@ -18,7 +18,6 @@ class ConditionalGetApiController < ActionController::API
end
private
-
def handle_last_modified_and_etags
fresh_when(last_modified: Time.now.utc.beginning_of_day, etag: [ :foo, 123 ])
end
diff --git a/actionpack/test/controller/content_type_test.rb b/actionpack/test/controller/content_type_test.rb
index 636b025f2c..fcf767b706 100644
--- a/actionpack/test/controller/content_type_test.rb
+++ b/actionpack/test/controller/content_type_test.rb
@@ -66,73 +66,72 @@ class ContentTypeTest < ActionController::TestCase
def test_render_defaults
get :render_defaults
assert_equal "utf-8", @response.charset
- assert_equal Mime[:text], @response.content_type
+ assert_equal Mime[:text], @response.media_type
end
def test_render_changed_charset_default
with_default_charset "utf-16" do
get :render_defaults
assert_equal "utf-16", @response.charset
- assert_equal Mime[:text], @response.content_type
+ assert_equal Mime[:text], @response.media_type
end
end
# :ported:
def test_content_type_from_body
get :render_content_type_from_body
- assert_equal Mime[:rss], @response.content_type
+ assert_equal Mime[:rss], @response.media_type
assert_equal "utf-8", @response.charset
end
# :ported:
def test_content_type_from_render
get :render_content_type_from_render
- assert_equal Mime[:rss], @response.content_type
+ assert_equal Mime[:rss], @response.media_type
assert_equal "utf-8", @response.charset
end
# :ported:
def test_charset_from_body
get :render_charset_from_body
- assert_equal Mime[:text], @response.content_type
+ assert_equal Mime[:text], @response.media_type
assert_equal "utf-16", @response.charset
end
# :ported:
def test_nil_charset_from_body
get :render_nil_charset_from_body
- assert_equal Mime[:text], @response.content_type
+ assert_equal Mime[:text], @response.media_type
assert_equal "utf-8", @response.charset, @response.headers.inspect
end
def test_nil_default_for_erb
with_default_charset nil do
get :render_default_for_erb
- assert_equal Mime[:html], @response.content_type
+ assert_equal Mime[:html], @response.media_type
assert_nil @response.charset, @response.headers.inspect
end
end
def test_default_for_erb
get :render_default_for_erb
- assert_equal Mime[:html], @response.content_type
+ assert_equal Mime[:html], @response.media_type
assert_equal "utf-8", @response.charset
end
def test_default_for_builder
get :render_default_for_builder
- assert_equal Mime[:xml], @response.content_type
+ assert_equal Mime[:xml], @response.media_type
assert_equal "utf-8", @response.charset
end
def test_change_for_builder
get :render_change_for_builder
- assert_equal Mime[:html], @response.content_type
+ assert_equal Mime[:html], @response.media_type
assert_equal "utf-8", @response.charset
end
private
-
def with_default_charset(charset)
old_default_charset = ActionDispatch::Response.default_charset
ActionDispatch::Response.default_charset = charset
@@ -148,22 +147,22 @@ class AcceptBasedContentTypeTest < ActionController::TestCase
def test_render_default_content_types_for_respond_to
@request.accept = Mime[:html].to_s
get :render_default_content_types_for_respond_to
- assert_equal Mime[:html], @response.content_type
+ assert_equal Mime[:html], @response.media_type
@request.accept = Mime[:js].to_s
get :render_default_content_types_for_respond_to
- assert_equal Mime[:js], @response.content_type
+ assert_equal Mime[:js], @response.media_type
end
def test_render_default_content_types_for_respond_to_with_template
@request.accept = Mime[:xml].to_s
get :render_default_content_types_for_respond_to
- assert_equal Mime[:xml], @response.content_type
+ assert_equal Mime[:xml], @response.media_type
end
def test_render_default_content_types_for_respond_to_with_overwrite
@request.accept = Mime[:rss].to_s
get :render_default_content_types_for_respond_to
- assert_equal Mime[:xml], @response.content_type
+ assert_equal Mime[:xml], @response.media_type
end
end
diff --git a/actionpack/test/controller/filters_test.rb b/actionpack/test/controller/filters_test.rb
index fcee812ee4..40443a9397 100644
--- a/actionpack/test/controller/filters_test.rb
+++ b/actionpack/test/controller/filters_test.rb
@@ -310,7 +310,6 @@ class FilterTest < ActionController::TestCase
after_action :conditional_in_parent_after, only: [:show, :another_action]
private
-
def conditional_in_parent_before
@ran_filter ||= []
@ran_filter << "conditional_in_parent_before"
@@ -508,7 +507,6 @@ class FilterTest < ActionController::TestCase
end
private
-
def filter_one
@filters ||= []
@filters << "filter_one"
@@ -532,7 +530,6 @@ class FilterTest < ActionController::TestCase
before_action :find_except, except: :edit
private
-
def find_only
@only = "Only"
end
diff --git a/actionpack/test/controller/flash_test.rb b/actionpack/test/controller/flash_test.rb
index bf95c633e5..1f44c7a68e 100644
--- a/actionpack/test/controller/flash_test.rb
+++ b/actionpack/test/controller/flash_test.rb
@@ -361,7 +361,6 @@ class FlashIntegrationTest < ActionDispatch::IntegrationTest
end
private
-
# Overwrite get to send SessionSecret in env hash
def get(path, *args)
args[0] ||= {}
diff --git a/actionpack/test/controller/helper_test.rb b/actionpack/test/controller/helper_test.rb
index de8072a994..93a2ba1071 100644
--- a/actionpack/test/controller/helper_test.rb
+++ b/actionpack/test/controller/helper_test.rb
@@ -150,8 +150,8 @@ class HelperTest < ActiveSupport::TestCase
end
def test_default_helpers_only
- assert_equal [JustMeHelper], JustMeController._helpers.ancestors.reject(&:anonymous?)
- assert_equal [MeTooHelper, JustMeHelper], MeTooController._helpers.ancestors.reject(&:anonymous?)
+ assert_equal %w[JustMeHelper], JustMeController._helpers.ancestors.reject(&:anonymous?).map(&:to_s)
+ assert_equal %w[MeTooController::HelperMethods MeTooHelper JustMeHelper], MeTooController._helpers.ancestors.reject(&:anonymous?).map(&:to_s)
end
def test_base_helper_methods_after_clear_helpers
diff --git a/actionpack/test/controller/http_basic_authentication_test.rb b/actionpack/test/controller/http_basic_authentication_test.rb
index 1544a627ee..73524d0443 100644
--- a/actionpack/test/controller/http_basic_authentication_test.rb
+++ b/actionpack/test/controller/http_basic_authentication_test.rb
@@ -32,7 +32,6 @@ class HttpBasicAuthenticationTest < ActionController::TestCase
end
private
-
def authenticate
authenticate_or_request_with_http_basic do |username, password|
username == "lifo" && password == "world"
@@ -172,7 +171,6 @@ class HttpBasicAuthenticationTest < ActionController::TestCase
end
private
-
def encode_credentials(username, password)
"Basic #{::Base64.encode64("#{username}:#{password}")}"
end
diff --git a/actionpack/test/controller/http_digest_authentication_test.rb b/actionpack/test/controller/http_digest_authentication_test.rb
index dd4ff85d11..a0f543f607 100644
--- a/actionpack/test/controller/http_digest_authentication_test.rb
+++ b/actionpack/test/controller/http_digest_authentication_test.rb
@@ -20,7 +20,6 @@ class HttpDigestAuthenticationTest < ActionController::TestCase
end
private
-
def authenticate
authenticate_or_request_with_http_digest("SuperSecret") do |username|
# Returns the password
@@ -254,7 +253,6 @@ class HttpDigestAuthenticationTest < ActionController::TestCase
end
private
-
def encode_credentials(options)
options.reverse_merge!(nc: "00000001", cnonce: "0a4f113b", password_is_ha1: false)
password = options.delete(:password)
diff --git a/actionpack/test/controller/http_token_authentication_test.rb b/actionpack/test/controller/http_token_authentication_test.rb
index 103123f98c..57b78154bc 100644
--- a/actionpack/test/controller/http_token_authentication_test.rb
+++ b/actionpack/test/controller/http_token_authentication_test.rb
@@ -21,7 +21,6 @@ class HttpTokenAuthenticationTest < ActionController::TestCase
end
private
-
def authenticate
authenticate_or_request_with_http_token do |token, _|
token == "lifo"
@@ -190,7 +189,6 @@ class HttpTokenAuthenticationTest < ActionController::TestCase
end
private
-
def sample_request(token, options = { nonce: "def" })
authorization = options.inject([%{Token token="#{token}"}]) do |arr, (k, v)|
arr << "#{k}=\"#{v}\""
diff --git a/actionpack/test/controller/integration_test.rb b/actionpack/test/controller/integration_test.rb
index 4dddd98f9f..caacd66bd6 100644
--- a/actionpack/test/controller/integration_test.rb
+++ b/actionpack/test/controller/integration_test.rb
@@ -213,6 +213,10 @@ class IntegrationProcessTest < ActionDispatch::IntegrationTest
redirect_to action_url("get")
end
+ def redirect_307
+ redirect_to action_url("post"), status: 307
+ end
+
def remove_header
response.headers.delete params[:header]
head :ok, "c" => "3"
@@ -337,6 +341,15 @@ class IntegrationProcessTest < ActionDispatch::IntegrationTest
end
end
+ def test_307_redirect_uses_the_same_http_verb
+ with_test_route_set do
+ post "/redirect_307"
+ assert_equal 307, status
+ follow_redirect!
+ assert_equal "POST", request.method
+ end
+ end
+
def test_redirect_reset_html_document
with_test_route_set do
get "/redirect"
@@ -522,11 +535,11 @@ class IntegrationProcessTest < ActionDispatch::IntegrationTest
with_test_route_set do
get "/get", headers: { "Accept" => "application/json" }, xhr: true
assert_equal "application/json", request.accept
- assert_equal "application/json", response.content_type
+ assert_equal "application/json", response.media_type
get "/get", headers: { "HTTP_ACCEPT" => "application/json" }, xhr: true
assert_equal "application/json", request.accept
- assert_equal "application/json", response.content_type
+ assert_equal "application/json", response.media_type
end
end
@@ -986,7 +999,7 @@ class IntegrationRequestEncodersTest < ActionDispatch::IntegrationTest
def test_encoding_as_json
post_to_foos as: :json do
assert_response :success
- assert_equal "application/json", request.content_type
+ assert_equal "application/json", request.media_type
assert_equal "application/json", request.accepts.first.to_s
assert_equal :json, request.format.ref
assert_equal({ "foo" => "fighters" }, request.request_parameters)
@@ -1025,7 +1038,7 @@ class IntegrationRequestEncodersTest < ActionDispatch::IntegrationTest
post_to_foos as: :wibble do
assert_response :success
assert_equal "/foos_wibble", request.path
- assert_equal "text/wibble", request.content_type
+ assert_equal "text/wibble", request.media_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.
diff --git a/actionpack/test/controller/localized_templates_test.rb b/actionpack/test/controller/localized_templates_test.rb
index d84a76fb46..5c5cef66d5 100644
--- a/actionpack/test/controller/localized_templates_test.rb
+++ b/actionpack/test/controller/localized_templates_test.rb
@@ -43,6 +43,6 @@ class LocalizedTemplatesTest < ActionController::TestCase
I18n.locale = :it
get :hello_world
assert_equal "Ciao Mondo", @response.body
- assert_equal "text/html", @response.content_type
+ assert_equal "text/html", @response.media_type
end
end
diff --git a/actionpack/test/controller/log_subscriber_test.rb b/actionpack/test/controller/log_subscriber_test.rb
index 0562c16284..1a7e7f6cbb 100644
--- a/actionpack/test/controller/log_subscriber_test.rb
+++ b/actionpack/test/controller/log_subscriber_test.rb
@@ -98,6 +98,7 @@ class ACLogSubscriberTest < ActionController::TestCase
@cache_path = Dir.mktmpdir(%w[tmp cache])
@controller.cache_store = :file_store, @cache_path
+ @controller.config.perform_caching = true
ActionController::LogSubscriber.attach_to :action_controller
end
@@ -249,19 +250,15 @@ class ACLogSubscriberTest < ActionController::TestCase
end
def test_with_fragment_cache
- @controller.config.perform_caching = true
get :with_fragment_cache
wait
assert_equal 4, logs.size
assert_match(/Read fragment views\/foo/, logs[1])
assert_match(/Write fragment views\/foo/, logs[2])
- ensure
- @controller.config.perform_caching = true
end
def test_with_fragment_cache_when_log_disabled
- @controller.config.perform_caching = true
ActionController::Base.enable_fragment_cache_logging = false
get :with_fragment_cache
wait
@@ -269,69 +266,52 @@ class ACLogSubscriberTest < ActionController::TestCase
assert_equal 2, logs.size
assert_equal "Processing by Another::LogSubscribersController#with_fragment_cache as HTML", logs[0]
assert_match(/Completed 200 OK in \d+ms/, logs[1])
- ensure
- @controller.config.perform_caching = true
ActionController::Base.enable_fragment_cache_logging = true
end
def test_with_fragment_cache_if_with_true
- @controller.config.perform_caching = true
get :with_fragment_cache_if_with_true_condition
wait
assert_equal 4, logs.size
assert_match(/Read fragment views\/foo/, logs[1])
assert_match(/Write fragment views\/foo/, logs[2])
- ensure
- @controller.config.perform_caching = true
end
def test_with_fragment_cache_if_with_false
- @controller.config.perform_caching = true
get :with_fragment_cache_if_with_false_condition
wait
assert_equal 2, logs.size
assert_no_match(/Read fragment views\/foo/, logs[1])
assert_no_match(/Write fragment views\/foo/, logs[2])
- ensure
- @controller.config.perform_caching = true
end
def test_with_fragment_cache_unless_with_true
- @controller.config.perform_caching = true
get :with_fragment_cache_unless_with_true_condition
wait
assert_equal 2, logs.size
assert_no_match(/Read fragment views\/foo/, logs[1])
assert_no_match(/Write fragment views\/foo/, logs[2])
- ensure
- @controller.config.perform_caching = true
end
def test_with_fragment_cache_unless_with_false
- @controller.config.perform_caching = true
get :with_fragment_cache_unless_with_false_condition
wait
assert_equal 4, logs.size
assert_match(/Read fragment views\/foo/, logs[1])
assert_match(/Write fragment views\/foo/, logs[2])
- ensure
- @controller.config.perform_caching = true
end
def test_with_fragment_cache_and_percent_in_key
- @controller.config.perform_caching = true
get :with_fragment_cache_and_percent_in_key
wait
assert_equal 4, logs.size
assert_match(/Read fragment views\/foo/, logs[1])
assert_match(/Write fragment views\/foo/, logs[2])
- ensure
- @controller.config.perform_caching = true
end
def test_process_action_with_exception_includes_http_status_code
diff --git a/actionpack/test/controller/metal/renderers_test.rb b/actionpack/test/controller/metal/renderers_test.rb
index 5f0d125128..f6558f1354 100644
--- a/actionpack/test/controller/metal/renderers_test.rb
+++ b/actionpack/test/controller/metal/renderers_test.rb
@@ -38,13 +38,13 @@ class RenderersMetalTest < ActionController::TestCase
get :one
assert_response :success
assert_equal({ a: "b" }.to_json, @response.body)
- assert_equal "application/json", @response.content_type
+ assert_equal "application/json", @response.media_type
end
def test_render_xml
get :two
assert_response :success
assert_equal(" ", @response.body)
- assert_equal "text/plain", @response.content_type
+ assert_equal "text/plain", @response.media_type
end
end
diff --git a/actionpack/test/controller/mime/accept_format_test.rb b/actionpack/test/controller/mime/accept_format_test.rb
index eed671d593..fb038ae158 100644
--- a/actionpack/test/controller/mime/accept_format_test.rb
+++ b/actionpack/test/controller/mime/accept_format_test.rb
@@ -43,7 +43,6 @@ class PostController < AbstractPostController
end
private
-
def with_iphone
request.format = "iphone" if request.env["HTTP_ACCEPT"] == "text/iphone"
yield
diff --git a/actionpack/test/controller/mime/respond_to_test.rb b/actionpack/test/controller/mime/respond_to_test.rb
index 2f8f191828..fc16c639fb 100644
--- a/actionpack/test/controller/mime/respond_to_test.rb
+++ b/actionpack/test/controller/mime/respond_to_test.rb
@@ -423,12 +423,12 @@ class RespondToControllerTest < ActionController::TestCase
def test_using_defaults
@request.accept = "*/*"
get :using_defaults
- assert_equal "text/html", @response.content_type
+ assert_equal "text/html", @response.media_type
assert_equal "Hello world!", @response.body
@request.accept = "application/xml"
get :using_defaults
- assert_equal "application/xml", @response.content_type
+ assert_equal "application/xml", @response.media_type
assert_equal "<p>Hello world!</p>\n", @response.body
end
@@ -449,12 +449,12 @@ class RespondToControllerTest < ActionController::TestCase
def test_using_defaults_with_type_list
@request.accept = "*/*"
get :using_defaults_with_type_list
- assert_equal "text/html", @response.content_type
+ assert_equal "text/html", @response.media_type
assert_equal "Hello world!", @response.body
@request.accept = "application/xml"
get :using_defaults_with_type_list
- assert_equal "application/xml", @response.content_type
+ assert_equal "application/xml", @response.media_type
assert_equal "<p>Hello world!</p>\n", @response.body
end
@@ -468,7 +468,7 @@ class RespondToControllerTest < ActionController::TestCase
def test_using_non_conflicting_nested_js_then_js
@request.accept = "*/*"
get :using_non_conflicting_nested_js_then_js
- assert_equal "text/javascript", @response.content_type
+ assert_equal "text/javascript", @response.media_type
assert_equal "JS", @response.body
end
@@ -499,12 +499,12 @@ class RespondToControllerTest < ActionController::TestCase
def test_custom_types
@request.accept = "application/fancy-xml"
get :custom_type_handling
- assert_equal "application/fancy-xml", @response.content_type
+ assert_equal "application/fancy-xml", @response.media_type
assert_equal "Fancy XML", @response.body
@request.accept = "text/html"
get :custom_type_handling
- assert_equal "text/html", @response.content_type
+ assert_equal "text/html", @response.media_type
assert_equal "HTML", @response.body
end
@@ -595,7 +595,7 @@ class RespondToControllerTest < ActionController::TestCase
@request.accept = "application/json"
get :json_with_callback
assert_equal "/**/alert(JS)", @response.body
- assert_equal "text/javascript", @response.content_type
+ assert_equal "text/javascript", @response.media_type
end
def test_xhr
@@ -605,13 +605,13 @@ class RespondToControllerTest < ActionController::TestCase
def test_custom_constant
get :custom_constant_handling, format: "mobile"
- assert_equal "text/x-mobile", @response.content_type
+ assert_equal "text/x-mobile", @response.media_type
assert_equal "Mobile", @response.body
end
def test_custom_constant_handling_without_block
get :custom_constant_handling_without_block, format: "mobile"
- assert_equal "text/x-mobile", @response.content_type
+ assert_equal "text/x-mobile", @response.media_type
assert_equal "Mobile", @response.body
end
@@ -664,7 +664,7 @@ class RespondToControllerTest < ActionController::TestCase
assert_equal '<html><div id="html">Hello future from Firefox!</div></html>', @response.body
get :iphone_with_html_response_type, format: "iphone"
- assert_equal "text/html", @response.content_type
+ assert_equal "text/html", @response.media_type
assert_equal '<html><div id="iphone">Hello iPhone future from iPhone!</div></html>', @response.body
end
@@ -672,7 +672,7 @@ class RespondToControllerTest < ActionController::TestCase
@request.accept = "text/iphone"
get :iphone_with_html_response_type
assert_equal '<html><div id="iphone">Hello iPhone future from iPhone!</div></html>', @response.body
- assert_equal "text/html", @response.content_type
+ assert_equal "text/html", @response.media_type
end
def test_invalid_format
@@ -702,7 +702,7 @@ class RespondToControllerTest < ActionController::TestCase
def test_variant_with_implicit_template_rendering
get :variant_with_implicit_template_rendering, params: { v: :mobile }
- assert_equal "text/html", @response.content_type
+ assert_equal "text/html", @response.media_type
assert_equal "mobile", @response.body
end
@@ -756,137 +756,137 @@ class RespondToControllerTest < ActionController::TestCase
def test_variant_with_format_and_custom_render
get :variant_with_format_and_custom_render, params: { v: :phone }
- assert_equal "text/html", @response.content_type
+ assert_equal "text/html", @response.media_type
assert_equal "mobile", @response.body
end
def test_multiple_variants_for_format
get :multiple_variants_for_format, params: { v: :tablet }
- assert_equal "text/html", @response.content_type
+ assert_equal "text/html", @response.media_type
assert_equal "tablet", @response.body
end
def test_no_variant_in_variant_setup
get :variant_plus_none_for_format
- assert_equal "text/html", @response.content_type
+ assert_equal "text/html", @response.media_type
assert_equal "none", @response.body
end
def test_variant_inline_syntax
get :variant_inline_syntax
- assert_equal "text/html", @response.content_type
+ assert_equal "text/html", @response.media_type
assert_equal "none", @response.body
get :variant_inline_syntax, params: { v: :phone }
- assert_equal "text/html", @response.content_type
+ assert_equal "text/html", @response.media_type
assert_equal "phone", @response.body
end
def test_variant_inline_syntax_with_format
get :variant_inline_syntax, format: :js
- assert_equal "text/javascript", @response.content_type
+ assert_equal "text/javascript", @response.media_type
assert_equal "js", @response.body
end
def test_variant_inline_syntax_without_block
get :variant_inline_syntax_without_block, params: { v: :phone }
- assert_equal "text/html", @response.content_type
+ assert_equal "text/html", @response.media_type
assert_equal "phone", @response.body
end
def test_variant_any
get :variant_any, params: { v: :phone }
- assert_equal "text/html", @response.content_type
+ assert_equal "text/html", @response.media_type
assert_equal "phone", @response.body
get :variant_any, params: { v: :tablet }
- assert_equal "text/html", @response.content_type
+ assert_equal "text/html", @response.media_type
assert_equal "any", @response.body
get :variant_any, params: { v: :phablet }
- assert_equal "text/html", @response.content_type
+ assert_equal "text/html", @response.media_type
assert_equal "any", @response.body
end
def test_variant_any_any
get :variant_any_any
- assert_equal "text/html", @response.content_type
+ assert_equal "text/html", @response.media_type
assert_equal "any", @response.body
get :variant_any_any, params: { v: :phone }
- assert_equal "text/html", @response.content_type
+ assert_equal "text/html", @response.media_type
assert_equal "phone", @response.body
get :variant_any_any, params: { v: :yolo }
- assert_equal "text/html", @response.content_type
+ assert_equal "text/html", @response.media_type
assert_equal "any", @response.body
end
def test_variant_inline_any
get :variant_any, params: { v: :phone }
- assert_equal "text/html", @response.content_type
+ assert_equal "text/html", @response.media_type
assert_equal "phone", @response.body
get :variant_inline_any, params: { v: :tablet }
- assert_equal "text/html", @response.content_type
+ assert_equal "text/html", @response.media_type
assert_equal "any", @response.body
get :variant_inline_any, params: { v: :phablet }
- assert_equal "text/html", @response.content_type
+ assert_equal "text/html", @response.media_type
assert_equal "any", @response.body
end
def test_variant_inline_any_any
get :variant_inline_any_any, params: { v: :phone }
- assert_equal "text/html", @response.content_type
+ assert_equal "text/html", @response.media_type
assert_equal "phone", @response.body
get :variant_inline_any_any, params: { v: :yolo }
- assert_equal "text/html", @response.content_type
+ assert_equal "text/html", @response.media_type
assert_equal "any", @response.body
end
def test_variant_any_implicit_render
get :variant_any_implicit_render, params: { v: :tablet }
- assert_equal "text/html", @response.content_type
+ assert_equal "text/html", @response.media_type
assert_equal "tablet", @response.body
get :variant_any_implicit_render, params: { v: :phablet }
- assert_equal "text/html", @response.content_type
+ assert_equal "text/html", @response.media_type
assert_equal "phablet", @response.body
end
def test_variant_any_with_none
get :variant_any_with_none
- assert_equal "text/html", @response.content_type
+ assert_equal "text/html", @response.media_type
assert_equal "none or phone", @response.body
get :variant_any_with_none, params: { v: :phone }
- assert_equal "text/html", @response.content_type
+ assert_equal "text/html", @response.media_type
assert_equal "none or phone", @response.body
end
def test_format_any_variant_any
get :format_any_variant_any, format: :js, params: { v: :tablet }
- assert_equal "text/javascript", @response.content_type
+ assert_equal "text/javascript", @response.media_type
assert_equal "tablet", @response.body
end
def test_variant_negotiation_inline_syntax
get :variant_inline_syntax_without_block, params: { v: [:tablet, :phone] }
- assert_equal "text/html", @response.content_type
+ assert_equal "text/html", @response.media_type
assert_equal "phone", @response.body
end
def test_variant_negotiation_block_syntax
get :variant_plus_none_for_format, params: { v: [:tablet, :phone] }
- assert_equal "text/html", @response.content_type
+ assert_equal "text/html", @response.media_type
assert_equal "phone", @response.body
end
def test_variant_negotiation_without_block
get :variant_inline_syntax_without_block, params: { v: [:tablet, :phone] }
- assert_equal "text/html", @response.content_type
+ assert_equal "text/html", @response.media_type
assert_equal "phone", @response.body
end
end
diff --git a/actionpack/test/controller/new_base/render_template_test.rb b/actionpack/test/controller/new_base/render_template_test.rb
index 14dc958475..270f75eb9e 100644
--- a/actionpack/test/controller/new_base/render_template_test.rb
+++ b/actionpack/test/controller/new_base/render_template_test.rb
@@ -67,7 +67,6 @@ module RenderTemplate
end
private
-
def show_detailed_exceptions?
request.local?
end
diff --git a/actionpack/test/controller/new_base/render_test.rb b/actionpack/test/controller/new_base/render_test.rb
index eb29203f59..dfeb2e2b15 100644
--- a/actionpack/test/controller/new_base/render_test.rb
+++ b/actionpack/test/controller/new_base/render_test.rb
@@ -37,7 +37,6 @@ module Render
end
private
-
def secretz
render plain: "FAIL WHALE!"
end
diff --git a/actionpack/test/controller/parameters/accessors_test.rb b/actionpack/test/controller/parameters/accessors_test.rb
index 7789e654d5..3d1538ff64 100644
--- a/actionpack/test/controller/parameters/accessors_test.rb
+++ b/actionpack/test/controller/parameters/accessors_test.rb
@@ -203,6 +203,25 @@ class ParametersAccessorsTest < ActiveSupport::TestCase
assert_not_predicate @params.transform_keys { |k| k }, :permitted?
end
+ test "transform_keys without a block returns an enumerator" do
+ assert_kind_of Enumerator, @params.transform_keys
+ assert_kind_of ActionController::Parameters, @params.transform_keys.each { |k| k }
+ end
+
+ test "transform_keys! without a block returns an enumerator" do
+ assert_kind_of Enumerator, @params.transform_keys!
+ assert_kind_of ActionController::Parameters, @params.transform_keys!.each { |k| k }
+ end
+
+ test "deep_transform_keys retains permitted status" do
+ @params.permit!
+ assert_predicate @params.deep_transform_keys { |k| k }, :permitted?
+ end
+
+ test "deep_transform_keys retains unpermitted status" do
+ assert_not_predicate @params.deep_transform_keys { |k| k }, :permitted?
+ end
+
test "transform_values retains permitted status" do
@params.permit!
assert_predicate @params.transform_values { |v| v }, :permitted?
@@ -219,8 +238,9 @@ class ParametersAccessorsTest < ActiveSupport::TestCase
end
end
- test "transform_values without block yieds an enumerator" do
+ test "transform_values without a block returns an enumerator" do
assert_kind_of Enumerator, @params.transform_values
+ assert_kind_of ActionController::Parameters, @params.transform_values.each { |v| v }
end
test "transform_values! converts hashes to parameters" do
@@ -229,8 +249,9 @@ class ParametersAccessorsTest < ActiveSupport::TestCase
end
end
- test "transform_values! without block yields an enumerator" do
+ test "transform_values! without a block returns an enumerator" do
assert_kind_of Enumerator, @params.transform_values!
+ assert_kind_of ActionController::Parameters, @params.transform_values!.each { |v| v }
end
test "value? returns true if the given value is present in the params" do
@@ -263,12 +284,14 @@ class ParametersAccessorsTest < ActiveSupport::TestCase
params1 = ActionController::Parameters.new(a: 1, b: 2)
params2 = ActionController::Parameters.new(a: 1, b: 2)
assert(params1 == params2)
+ assert(params1.hash == params2.hash)
end
test "is equal to Parameters instance with same permitted params" do
params1 = ActionController::Parameters.new(a: 1, b: 2).permit(:a)
params2 = ActionController::Parameters.new(a: 1, b: 2).permit(:a)
assert(params1 == params2)
+ assert(params1.hash == params2.hash)
end
test "is equal to Parameters instance with same different source params, but same permitted params" do
@@ -276,6 +299,8 @@ class ParametersAccessorsTest < ActiveSupport::TestCase
params2 = ActionController::Parameters.new(a: 1, c: 3).permit(:a)
assert(params1 == params2)
assert(params2 == params1)
+ assert(params1.hash == params2.hash)
+ assert(params2.hash == params1.hash)
end
test "is not equal to an unpermitted Parameters instance with same params" do
@@ -283,6 +308,8 @@ class ParametersAccessorsTest < ActiveSupport::TestCase
params2 = ActionController::Parameters.new(a: 1)
assert(params1 != params2)
assert(params2 != params1)
+ assert(params1.hash != params2.hash)
+ assert(params2.hash != params1.hash)
end
test "is not equal to Parameters instance with different permitted params" do
@@ -290,6 +317,8 @@ class ParametersAccessorsTest < ActiveSupport::TestCase
params2 = ActionController::Parameters.new(a: 1, b: 2).permit(:a)
assert(params1 != params2)
assert(params2 != params1)
+ assert(params1.hash != params2.hash)
+ assert(params2.hash != params1.hash)
end
test "equality with simple types works" do
diff --git a/actionpack/test/controller/parameters/log_on_unpermitted_params_test.rb b/actionpack/test/controller/parameters/log_on_unpermitted_params_test.rb
index fc9229ca1d..4fffcf6b10 100644
--- a/actionpack/test/controller/parameters/log_on_unpermitted_params_test.rb
+++ b/actionpack/test/controller/parameters/log_on_unpermitted_params_test.rb
@@ -52,7 +52,6 @@ class LogOnUnpermittedParamsTest < ActiveSupport::TestCase
end
private
-
def assert_logged(message)
old_logger = ActionController::Base.logger
log = StringIO.new
diff --git a/actionpack/test/controller/parameters/mutators_test.rb b/actionpack/test/controller/parameters/mutators_test.rb
index 312b1e5b27..ffffd2996f 100644
--- a/actionpack/test/controller/parameters/mutators_test.rb
+++ b/actionpack/test/controller/parameters/mutators_test.rb
@@ -118,4 +118,31 @@ class ParametersMutatorsTest < ActiveSupport::TestCase
test "transform_values! retains unpermitted status" do
assert_not_predicate @params.transform_values! { |v| v }, :permitted?
end
+
+ test "deep_transform_keys! retains permitted status" do
+ @params.permit!
+ assert_predicate @params.deep_transform_keys! { |k| k }, :permitted?
+ end
+
+ test "deep_transform_keys! retains unpermitted status" do
+ assert_not_predicate @params.deep_transform_keys! { |k| k }, :permitted?
+ end
+
+ test "compact_blank retains permitted status" do
+ @params.permit!
+ assert_predicate @params.compact_blank, :permitted?
+ end
+
+ test "compact_blank retains unpermitted status" do
+ assert_not_predicate @params.compact_blank, :permitted?
+ end
+
+ test "compact_blank! retains permitted status" do
+ @params.permit!
+ assert_predicate @params.compact_blank!, :permitted?
+ end
+
+ test "compact_blank! retains unpermitted status" do
+ assert_not_predicate @params.compact_blank!, :permitted?
+ end
end
diff --git a/actionpack/test/controller/parameters/nested_parameters_permit_test.rb b/actionpack/test/controller/parameters/nested_parameters_permit_test.rb
index 1403e224c0..6243b5c51b 100644
--- a/actionpack/test/controller/parameters/nested_parameters_permit_test.rb
+++ b/actionpack/test/controller/parameters/nested_parameters_permit_test.rb
@@ -125,7 +125,7 @@ class NestedParametersPermitTest < ActiveSupport::TestCase
assert_nil permitted[:book][:genre]
end
- test "fields_for-style nested params" do
+ test "nested params with numeric keys" do
params = ActionController::Parameters.new(
book: {
authors_attributes: {
@@ -150,7 +150,33 @@ class NestedParametersPermitTest < ActiveSupport::TestCase
assert_filtered_out permitted[:book][:authors_attributes]["0"], :age_of_death
end
- test "fields_for-style nested params with negative numbers" do
+ test "nested params with non_numeric keys" do
+ params = ActionController::Parameters.new(
+ book: {
+ authors_attributes: {
+ '0': { name: "William Shakespeare", age_of_death: "52" },
+ '1': { name: "Unattributed Assistant" },
+ '2': "Not a hash",
+ 'new_record': { name: "Some name" }
+ }
+ })
+ permitted = params.permit book: { authors_attributes: [ :name ] }
+
+ assert_not_nil permitted[:book][:authors_attributes]["0"]
+ assert_not_nil permitted[:book][:authors_attributes]["1"]
+
+ assert_nil permitted[:book][:authors_attributes]["2"]
+ assert_nil permitted[:book][:authors_attributes]["new_record"]
+ assert_equal "William Shakespeare", permitted[:book][:authors_attributes]["0"][:name]
+ assert_equal "Unattributed Assistant", permitted[:book][:authors_attributes]["1"][:name]
+
+ assert_equal(
+ { "book" => { "authors_attributes" => { "0" => { "name" => "William Shakespeare" }, "1" => { "name" => "Unattributed Assistant" } } } },
+ permitted.to_h
+ )
+ end
+
+ test "nested params with negative numeric keys" do
params = ActionController::Parameters.new(
book: {
authors_attributes: {
diff --git a/actionpack/test/controller/params_parse_test.rb b/actionpack/test/controller/params_parse_test.rb
index 440ab06fd7..091b567473 100644
--- a/actionpack/test/controller/params_parse_test.rb
+++ b/actionpack/test/controller/params_parse_test.rb
@@ -24,7 +24,6 @@ class ParamsParseTest < ActionController::TestCase
end
private
-
def capture_log_output
output = StringIO.new
request.set_header "action_dispatch.logger", ActiveSupport::Logger.new(output)
diff --git a/actionpack/test/controller/params_wrapper_test.rb b/actionpack/test/controller/params_wrapper_test.rb
index c4c74e8f2b..894a3824c0 100644
--- a/actionpack/test/controller/params_wrapper_test.rb
+++ b/actionpack/test/controller/params_wrapper_test.rb
@@ -411,7 +411,6 @@ class IrregularInflectionParamsWrapperTest < ActionController::TestCase
end
private
-
def with_dup
original = ActiveSupport::Inflector::Inflections.instance_variable_get(:@__instance__)[:en]
ActiveSupport::Inflector::Inflections.instance_variable_set(:@__instance__, en: original.dup)
diff --git a/actionpack/test/controller/render_js_test.rb b/actionpack/test/controller/render_js_test.rb
index 1efc0b9de1..da8f6e8062 100644
--- a/actionpack/test/controller/render_js_test.rb
+++ b/actionpack/test/controller/render_js_test.rb
@@ -2,7 +2,6 @@
require "abstract_unit"
require "controller/fake_models"
-require "pathname"
class RenderJSTest < ActionController::TestCase
class TestController < ActionController::Base
@@ -26,7 +25,7 @@ class RenderJSTest < ActionController::TestCase
def test_render_vanilla_js
get :render_vanilla_js_hello, xhr: true
assert_equal "alert('hello')", @response.body
- assert_equal "text/javascript", @response.content_type
+ assert_equal "text/javascript", @response.media_type
end
def test_should_render_js_partial
diff --git a/actionpack/test/controller/render_json_test.rb b/actionpack/test/controller/render_json_test.rb
index 82c1ba26cb..82c6aaafe5 100644
--- a/actionpack/test/controller/render_json_test.rb
+++ b/actionpack/test/controller/render_json_test.rb
@@ -3,7 +3,6 @@
require "abstract_unit"
require "controller/fake_models"
require "active_support/logger"
-require "pathname"
class RenderJsonTest < ActionController::TestCase
class JsonRenderable
@@ -80,7 +79,7 @@ class RenderJsonTest < ActionController::TestCase
def test_render_json_nil
get :render_json_nil
assert_equal "null", @response.body
- assert_equal "application/json", @response.content_type
+ assert_equal "application/json", @response.media_type
end
def test_render_json_render_to_string
@@ -91,7 +90,7 @@ class RenderJsonTest < ActionController::TestCase
def test_render_json
get :render_json_hello_world
assert_equal '{"hello":"world"}', @response.body
- assert_equal "application/json", @response.content_type
+ assert_equal "application/json", @response.media_type
end
def test_render_json_with_status
@@ -103,31 +102,31 @@ class RenderJsonTest < ActionController::TestCase
def test_render_json_with_callback
get :render_json_hello_world_with_callback, xhr: true
assert_equal '/**/alert({"hello":"world"})', @response.body
- assert_equal "text/javascript", @response.content_type
+ assert_equal "text/javascript", @response.media_type
end
def test_render_json_with_custom_content_type
get :render_json_with_custom_content_type, xhr: true
assert_equal '{"hello":"world"}', @response.body
- assert_equal "text/javascript", @response.content_type
+ assert_equal "text/javascript", @response.media_type
end
def test_render_symbol_json
get :render_symbol_json
assert_equal '{"hello":"world"}', @response.body
- assert_equal "application/json", @response.content_type
+ assert_equal "application/json", @response.media_type
end
def test_render_json_with_render_to_string
get :render_json_with_render_to_string
assert_equal '{"hello":"partial html"}', @response.body
- assert_equal "application/json", @response.content_type
+ assert_equal "application/json", @response.media_type
end
def test_render_json_forwards_extra_options
get :render_json_with_extra_options
assert_equal '{"a":"b"}', @response.body
- assert_equal "application/json", @response.content_type
+ assert_equal "application/json", @response.media_type
end
def test_render_json_calls_to_json_from_object
diff --git a/actionpack/test/controller/render_test.rb b/actionpack/test/controller/render_test.rb
index 8bb6617eaa..dd76824413 100644
--- a/actionpack/test/controller/render_test.rb
+++ b/actionpack/test/controller/render_test.rb
@@ -4,6 +4,11 @@ require "abstract_unit"
require "controller/fake_models"
class TestControllerWithExtraEtags < ActionController::Base
+ self.view_paths = [ActionView::FixtureResolver.new(
+ "test/with_implicit_template.erb" => "Hello explicitly!",
+ "test/hello_world.erb" => "Hello world!"
+ )]
+
def self.controller_name; "test"; end
def self.controller_path; "test"; end
@@ -37,6 +42,11 @@ class TestControllerWithExtraEtags < ActionController::Base
end
class ImplicitRenderTestController < ActionController::Base
+ self.view_paths = [ActionView::FixtureResolver.new(
+ "implicit_render_test/hello_world.erb" => "Hello world!",
+ "implicit_render_test/empty_action_with_template.html.erb" => "<h1>Empty action rendered this implicitly.</h1>\n"
+ )]
+
def empty_action
end
@@ -46,6 +56,10 @@ end
module Namespaced
class ImplicitRenderTestController < ActionController::Base
+ self.view_paths = [ActionView::FixtureResolver.new(
+ "namespaced/implicit_render_test/hello_world.erb" => "Hello world!"
+ )]
+
def hello_world
fresh_when(etag: "abc")
end
@@ -265,7 +279,6 @@ class TestController < ActionController::Base
end
private
-
def set_variable_for_layout
@variable_for_layout = nil
end
@@ -294,13 +307,15 @@ end
module TemplateModificationHelper
private
def modify_template(name)
- path = File.expand_path("../fixtures/#{name}.erb", __dir__)
- original = File.read(path)
- File.write(path, "#{original} Modified!")
+ hash = @controller.view_paths.first.instance_variable_get(:@hash)
+ key = name + ".erb"
+ original = hash[key]
+ hash[key] = "#{original} Modified!"
ActionView::LookupContext::DetailsKey.clear
yield
ensure
- File.write(path, original)
+ hash[key] = original
+ ActionView::LookupContext::DetailsKey.clear
end
end
diff --git a/actionpack/test/controller/render_xml_test.rb b/actionpack/test/controller/render_xml_test.rb
index a72d14e4bb..28d8e281ab 100644
--- a/actionpack/test/controller/render_xml_test.rb
+++ b/actionpack/test/controller/render_xml_test.rb
@@ -2,7 +2,6 @@
require "abstract_unit"
require "controller/fake_models"
-require "pathname"
class RenderXmlTest < ActionController::TestCase
class XmlRenderable
@@ -92,11 +91,11 @@ class RenderXmlTest < ActionController::TestCase
def test_should_render_xml_but_keep_custom_content_type
get :render_xml_with_custom_content_type
- assert_equal "application/atomsvc+xml", @response.content_type
+ assert_equal "application/atomsvc+xml", @response.media_type
end
def test_should_use_implicit_content_type
get :implicit_content_type, format: "atom"
- assert_equal Mime[:atom], @response.content_type
+ assert_equal Mime[:atom], @response.media_type
end
end
diff --git a/actionpack/test/controller/renderers_test.rb b/actionpack/test/controller/renderers_test.rb
index d92de6f5d5..96cce664a4 100644
--- a/actionpack/test/controller/renderers_test.rb
+++ b/actionpack/test/controller/renderers_test.rb
@@ -73,7 +73,7 @@ class RenderersTest < ActionController::TestCase
assert_raise ActionView::MissingTemplate do
get :respond_to_mime, format: "csv"
end
- assert_equal Mime[:csv], @response.content_type
+ assert_equal Mime[:csv], @response.media_type
assert_equal "", @response.body
end
@@ -83,7 +83,7 @@ class RenderersTest < ActionController::TestCase
end
@request.accept = "text/csv"
get :respond_to_mime, format: "csv"
- assert_equal Mime[:csv], @response.content_type
+ assert_equal Mime[:csv], @response.media_type
assert_equal "c,s,v", @response.body
ensure
ActionController::Renderers.remove :csv
diff --git a/actionpack/test/controller/request_forgery_protection_test.rb b/actionpack/test/controller/request_forgery_protection_test.rb
index ea94a3e048..01250880f5 100644
--- a/actionpack/test/controller/request_forgery_protection_test.rb
+++ b/actionpack/test/controller/request_forgery_protection_test.rb
@@ -112,7 +112,6 @@ class PrependProtectForgeryBaseController < ActionController::Base
end
private
-
def add_called_callback(name)
@called_callbacks ||= []
@called_callbacks << name
diff --git a/actionpack/test/controller/rescue_test.rb b/actionpack/test/controller/rescue_test.rb
index 089b0b94d4..538bc15fc9 100644
--- a/actionpack/test/controller/rescue_test.rb
+++ b/actionpack/test/controller/rescue_test.rb
@@ -304,7 +304,6 @@ class RescueControllerTest < ActionController::TestCase
end
private
-
def capture_log_output
output = StringIO.new
request.set_header "action_dispatch.logger", ActiveSupport::Logger.new(output)
@@ -351,7 +350,6 @@ class RescueTest < ActionDispatch::IntegrationTest
end
private
-
def with_test_routing
with_routing do |set|
set.draw do
diff --git a/actionpack/test/controller/resources_test.rb b/actionpack/test/controller/resources_test.rb
index d2146f12a5..339025ec52 100644
--- a/actionpack/test/controller/resources_test.rb
+++ b/actionpack/test/controller/resources_test.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require "abstract_unit"
-require "active_support/core_ext/object/try"
require "active_support/core_ext/object/with_options"
require "active_support/core_ext/array/extract_options"
@@ -36,7 +35,6 @@ class ResourcesTest < ActionController::TestCase
collection: collection_methods,
member: member_methods,
path_names: path_names do
-
assert_restful_routes_for :messages,
collection: collection_methods,
member: member_methods,
@@ -58,7 +56,6 @@ class ResourcesTest < ActionController::TestCase
collection: collection_methods,
member: member_methods,
path_names: path_names do |options|
-
collection_methods.each_key do |action|
assert_named_route "/messages/#{path_names[action] || action}", "#{action}_messages_path", action: action
end
@@ -1251,7 +1248,7 @@ class ResourcesTest < ActionController::TestCase
shallow_path = "/#{options[:shallow] ? options[:namespace] : options[:path_prefix]}#{path}"
full_path = "/#{options[:path_prefix]}#{path}"
name_prefix = options[:name_prefix]
- shallow_prefix = options[:shallow] ? options[:namespace].try(:gsub, /\//, "_") : options[:name_prefix]
+ shallow_prefix = options[:shallow] ? options[:namespace]&.gsub(/\//, "_") : options[:name_prefix]
new_action = "new"
edit_action = "edit"
diff --git a/actionpack/test/controller/show_exceptions_test.rb b/actionpack/test/controller/show_exceptions_test.rb
index 8724f9bcdb..8e1068fecf 100644
--- a/actionpack/test/controller/show_exceptions_test.rb
+++ b/actionpack/test/controller/show_exceptions_test.rb
@@ -51,7 +51,6 @@ module ShowExceptions
class ShowExceptionsOverriddenController < ShowExceptionsController
private
-
def show_detailed_exceptions?
params["detailed"] == "1"
end
@@ -76,7 +75,7 @@ module ShowExceptions
@app = ShowExceptionsOverriddenController.action(:boom)
get "/", headers: { "HTTP_ACCEPT" => "application/json" }
assert_response :internal_server_error
- assert_equal "application/json", response.content_type.to_s
+ assert_equal "application/json", response.media_type
assert_equal({ status: 500, error: "Internal Server Error" }.to_json, response.body)
end
@@ -84,7 +83,7 @@ module ShowExceptions
@app = ShowExceptionsOverriddenController.action(:boom)
get "/", headers: { "HTTP_ACCEPT" => "application/xml" }
assert_response :internal_server_error
- assert_equal "application/xml", response.content_type.to_s
+ assert_equal "application/xml", response.media_type
assert_equal({ status: 500, error: "Internal Server Error" }.to_xml, response.body)
end
@@ -92,21 +91,21 @@ module ShowExceptions
@app = ShowExceptionsOverriddenController.action(:boom)
get "/", headers: { "HTTP_ACCEPT" => "text/csv" }
assert_response :internal_server_error
- assert_equal "text/html", response.content_type.to_s
+ assert_equal "text/html", response.media_type
end
end
class ShowFailsafeExceptionsTest < ActionDispatch::IntegrationTest
def test_render_failsafe_exception
@app = ShowExceptionsOverriddenController.action(:boom)
- middleware = @app.instance_variable_get(:@middleware)
+ middleware = @app
@exceptions_app = middleware.instance_variable_get(:@exceptions_app)
middleware.instance_variable_set(:@exceptions_app, nil)
$stderr = StringIO.new
get "/", headers: { "HTTP_ACCEPT" => "text/json" }
assert_response :internal_server_error
- assert_equal "text/plain", response.content_type.to_s
+ assert_equal "text/plain", response.media_type
ensure
middleware.instance_variable_set(:@exceptions_app, @exceptions_app)
$stderr = STDERR
diff --git a/actionpack/test/controller/test_case_test.rb b/actionpack/test/controller/test_case_test.rb
index 998a495d0d..635a91507d 100644
--- a/actionpack/test/controller/test_case_test.rb
+++ b/actionpack/test/controller/test_case_test.rb
@@ -165,7 +165,6 @@ XML
end
private
-
def generate_url(opts)
url_for(opts.merge(action: "test_uri"))
end
diff --git a/actionpack/test/dispatch/actionable_exceptions_test.rb b/actionpack/test/dispatch/actionable_exceptions_test.rb
new file mode 100644
index 0000000000..9215a91e9c
--- /dev/null
+++ b/actionpack/test/dispatch/actionable_exceptions_test.rb
@@ -0,0 +1,80 @@
+# frozen_string_literal: true
+
+require "abstract_unit"
+
+class ActionableExceptionsTest < ActionDispatch::IntegrationTest
+ Actions = []
+
+ class ActionError < StandardError
+ include ActiveSupport::ActionableError
+
+ action "Successful action" do
+ Actions << "Action!"
+ end
+
+ action "Failed action" do
+ raise "Inaction!"
+ end
+ end
+
+ Noop = -> env { [200, {}, [""]] }
+
+ setup do
+ @app = ActionDispatch::ActionableExceptions.new(Noop)
+
+ Actions.clear
+ end
+
+ test "dispatches an actionable error" do
+ post ActionDispatch::ActionableExceptions.endpoint, params: {
+ error: ActionError.name,
+ action: "Successful action",
+ location: "/",
+ }
+
+ assert_equal ["Action!"], Actions
+
+ assert_equal 302, response.status
+ assert_equal "/", response.headers["Location"]
+ end
+
+ test "cannot dispatch errors if not allowed" do
+ post ActionDispatch::ActionableExceptions.endpoint, params: {
+ error: ActionError.name,
+ action: "Successful action",
+ location: "/",
+ }, headers: { "action_dispatch.show_exceptions" => false }
+
+ assert_empty Actions
+ end
+
+ test "dispatched action can fail" do
+ assert_raise RuntimeError do
+ post ActionDispatch::ActionableExceptions.endpoint, params: {
+ error: ActionError.name,
+ action: "Failed action",
+ location: "/",
+ }
+ end
+ end
+
+ test "cannot dispatch non-actionable errors" do
+ assert_raise ActiveSupport::ActionableError::NonActionable do
+ post ActionDispatch::ActionableExceptions.endpoint, params: {
+ error: RuntimeError.name,
+ action: "Inexistent action",
+ location: "/",
+ }
+ end
+ end
+
+ test "cannot dispatch Inexistent errors" do
+ assert_raise ActiveSupport::ActionableError::NonActionable do
+ post ActionDispatch::ActionableExceptions.endpoint, params: {
+ error: "",
+ action: "Inexistent action",
+ location: "/",
+ }
+ end
+ end
+end
diff --git a/actionpack/test/dispatch/callbacks_test.rb b/actionpack/test/dispatch/callbacks_test.rb
index fc80191c02..aa8640c506 100644
--- a/actionpack/test/dispatch/callbacks_test.rb
+++ b/actionpack/test/dispatch/callbacks_test.rb
@@ -38,7 +38,6 @@ class DispatcherTest < ActiveSupport::TestCase
end
private
-
def dispatch(&block)
ActionDispatch::Callbacks.new(block || DummyApp.new).call(
"rack.input" => StringIO.new("")
diff --git a/actionpack/test/dispatch/content_security_policy_test.rb b/actionpack/test/dispatch/content_security_policy_test.rb
index c8c885f35c..3d60dc1661 100644
--- a/actionpack/test/dispatch/content_security_policy_test.rb
+++ b/actionpack/test/dispatch/content_security_policy_test.rb
@@ -128,12 +128,36 @@ class ContentSecurityPolicyTest < ActiveSupport::TestCase
@policy.script_src false
assert_no_match %r{script-src}, @policy.build
+ @policy.script_src_attr :self
+ assert_match %r{script-src-attr 'self'}, @policy.build
+
+ @policy.script_src_attr false
+ assert_no_match %r{script-src-attr}, @policy.build
+
+ @policy.script_src_elem :self
+ assert_match %r{script-src-elem 'self'}, @policy.build
+
+ @policy.script_src_elem false
+ assert_no_match %r{script-src-elem}, @policy.build
+
@policy.style_src :self
assert_match %r{style-src 'self'}, @policy.build
@policy.style_src false
assert_no_match %r{style-src}, @policy.build
+ @policy.style_src_attr :self
+ assert_match %r{style-src-attr 'self'}, @policy.build
+
+ @policy.style_src_attr false
+ assert_no_match %r{style-src-attr}, @policy.build
+
+ @policy.style_src_elem :self
+ assert_match %r{style-src-elem 'self'}, @policy.build
+
+ @policy.style_src_elem false
+ assert_no_match %r{style-src-elem}, @policy.build
+
@policy.worker_src :self
assert_match %r{worker-src 'self'}, @policy.build
@@ -307,7 +331,6 @@ class DefaultContentSecurityPolicyIntegrationTest < ActionDispatch::IntegrationT
end
private
-
def assert_policy(expected, report_only: false)
if report_only
expected_header = "Content-Security-Policy-Report-Only"
@@ -470,7 +493,6 @@ class ContentSecurityPolicyIntegrationTest < ActionDispatch::IntegrationTest
end
private
-
def assert_policy(expected, report_only: false)
assert_response :success
@@ -544,3 +566,57 @@ class DisabledContentSecurityPolicyIntegrationTest < ActionDispatch::Integration
assert_equal "default-src https://example.com", response.headers["Content-Security-Policy"]
end
end
+
+class NonceDirectiveContentSecurityPolicyIntegrationTest < ActionDispatch::IntegrationTest
+ class PolicyController < ActionController::Base
+ def index
+ head :ok
+ end
+ end
+
+ ROUTES = ActionDispatch::Routing::RouteSet.new
+ ROUTES.draw do
+ scope module: "nonce_directive_content_security_policy_integration_test" do
+ get "/", to: "policy#index"
+ end
+ end
+
+ POLICY = ActionDispatch::ContentSecurityPolicy.new do |p|
+ p.default_src -> { :self }
+ p.script_src -> { :https }
+ p.style_src -> { :https }
+ end
+
+ class PolicyConfigMiddleware
+ def initialize(app)
+ @app = app
+ end
+
+ def call(env)
+ env["action_dispatch.content_security_policy"] = POLICY
+ env["action_dispatch.content_security_policy_nonce_generator"] = proc { "iyhD0Yc0W+c=" }
+ env["action_dispatch.content_security_policy_report_only"] = false
+ env["action_dispatch.content_security_policy_nonce_directives"] = %w(script-src)
+ env["action_dispatch.show_exceptions"] = false
+
+ @app.call(env)
+ end
+ end
+
+ APP = build_app(ROUTES) do |middleware|
+ middleware.use PolicyConfigMiddleware
+ middleware.use ActionDispatch::ContentSecurityPolicy::Middleware
+ end
+
+ def app
+ APP
+ end
+
+ def test_generate_nonce_only_specified_in_nonce_directives
+ get "/"
+
+ assert_response :success
+ assert_match "script-src https: 'nonce-iyhD0Yc0W+c='", response.headers["Content-Security-Policy"]
+ assert_no_match "style-src https: 'nonce-iyhD0Yc0W+c='", response.headers["Content-Security-Policy"]
+ end
+end
diff --git a/actionpack/test/dispatch/debug_exceptions_test.rb b/actionpack/test/dispatch/debug_exceptions_test.rb
index 2812b1b614..fa629bc761 100644
--- a/actionpack/test/dispatch/debug_exceptions_test.rb
+++ b/actionpack/test/dispatch/debug_exceptions_test.rb
@@ -5,6 +5,18 @@ require "abstract_unit"
class DebugExceptionsTest < ActionDispatch::IntegrationTest
InterceptedErrorInstance = StandardError.new
+ class CustomActionableError < StandardError
+ include ActiveSupport::ActionableError
+
+ action "Action 1" do
+ nil
+ end
+
+ action "Action 2" do
+ nil
+ end
+ end
+
class Boomer
attr_accessor :closed
@@ -92,6 +104,8 @@ class DebugExceptionsTest < ActionDispatch::IntegrationTest
method_that_raises
when "/nested_exceptions"
raise_nested_exceptions
+ when %r{/actionable_error}
+ raise CustomActionableError
else
raise "puke!"
end
@@ -194,7 +208,7 @@ class DebugExceptionsTest < ActionDispatch::IntegrationTest
assert_response 500
assert_no_match(/<header>/, body)
assert_no_match(/<body>/, body)
- assert_equal "text/plain", response.content_type
+ assert_equal "text/plain", response.media_type
assert_match(/RuntimeError\npuke/, body)
Rails.stub :root, Pathname.new(".") do
@@ -208,31 +222,31 @@ class DebugExceptionsTest < ActionDispatch::IntegrationTest
get "/not_found", headers: xhr_request_env
assert_response 404
assert_no_match(/<body>/, body)
- assert_equal "text/plain", response.content_type
+ assert_equal "text/plain", response.media_type
assert_match(/#{AbstractController::ActionNotFound.name}/, body)
get "/method_not_allowed", headers: xhr_request_env
assert_response 405
assert_no_match(/<body>/, body)
- assert_equal "text/plain", response.content_type
+ assert_equal "text/plain", response.media_type
assert_match(/ActionController::MethodNotAllowed/, body)
get "/unknown_http_method", headers: xhr_request_env
assert_response 405
assert_no_match(/<body>/, body)
- assert_equal "text/plain", response.content_type
+ assert_equal "text/plain", response.media_type
assert_match(/ActionController::UnknownHttpMethod/, body)
get "/bad_request", headers: xhr_request_env
assert_response 400
assert_no_match(/<body>/, body)
- assert_equal "text/plain", response.content_type
+ assert_equal "text/plain", response.media_type
assert_match(/ActionController::BadRequest/, body)
get "/parameter_missing", headers: xhr_request_env
assert_response 400
assert_no_match(/<body>/, body)
- assert_equal "text/plain", response.content_type
+ assert_equal "text/plain", response.media_type
assert_match(/ActionController::ParameterMissing/, body)
end
@@ -243,37 +257,37 @@ class DebugExceptionsTest < ActionDispatch::IntegrationTest
assert_response 500
assert_no_match(/<header>/, body)
assert_no_match(/<body>/, body)
- assert_equal "application/json", response.content_type
+ assert_equal "application/json", response.media_type
assert_match(/RuntimeError: puke/, body)
get "/not_found", headers: { "action_dispatch.show_exceptions" => true }, as: :json
assert_response 404
assert_no_match(/<body>/, body)
- assert_equal "application/json", response.content_type
+ assert_equal "application/json", response.media_type
assert_match(/#{AbstractController::ActionNotFound.name}/, body)
get "/method_not_allowed", headers: { "action_dispatch.show_exceptions" => true }, as: :json
assert_response 405
assert_no_match(/<body>/, body)
- assert_equal "application/json", response.content_type
+ assert_equal "application/json", response.media_type
assert_match(/ActionController::MethodNotAllowed/, body)
get "/unknown_http_method", headers: { "action_dispatch.show_exceptions" => true }, as: :json
assert_response 405
assert_no_match(/<body>/, body)
- assert_equal "application/json", response.content_type
+ assert_equal "application/json", response.media_type
assert_match(/ActionController::UnknownHttpMethod/, body)
get "/bad_request", headers: { "action_dispatch.show_exceptions" => true }, as: :json
assert_response 400
assert_no_match(/<body>/, body)
- assert_equal "application/json", response.content_type
+ assert_equal "application/json", response.media_type
assert_match(/ActionController::BadRequest/, body)
get "/parameter_missing", headers: { "action_dispatch.show_exceptions" => true }, as: :json
assert_response 400
assert_no_match(/<body>/, body)
- assert_equal "application/json", response.content_type
+ assert_equal "application/json", response.media_type
assert_match(/ActionController::ParameterMissing/, body)
end
@@ -284,7 +298,7 @@ class DebugExceptionsTest < ActionDispatch::IntegrationTest
assert_response 500
assert_match(/<header>/, body)
assert_match(/<body>/, body)
- assert_equal "text/html", response.content_type
+ assert_equal "text/html", response.media_type
assert_match(/puke/, body)
end
@@ -293,7 +307,7 @@ class DebugExceptionsTest < ActionDispatch::IntegrationTest
get "/index.xml", headers: { "action_dispatch.show_exceptions" => true }
assert_response 500
- assert_equal "application/xml", response.content_type
+ assert_equal "application/xml", response.media_type
assert_match(/RuntimeError: puke/, body)
end
@@ -307,7 +321,7 @@ class DebugExceptionsTest < ActionDispatch::IntegrationTest
get "/index", headers: { "action_dispatch.show_exceptions" => true }, as: :wibble
assert_response 500
- assert_equal "application/json", response.content_type
+ assert_equal "application/json", response.media_type
assert_match(/RuntimeError: puke/, body)
ensure
@@ -452,6 +466,8 @@ class DebugExceptionsTest < ActionDispatch::IntegrationTest
end
test "logs exception backtrace when all lines silenced" do
+ @app = DevelopmentApp
+
output = StringIO.new
backtrace_cleaner = ActiveSupport::BacktraceCleaner.new
backtrace_cleaner.add_silencer { true }
@@ -464,6 +480,27 @@ class DebugExceptionsTest < ActionDispatch::IntegrationTest
assert_operator((output.rewind && output.read).lines.count, :>, 10)
end
+ test "doesn't log the framework backtrace when error type is a routing error" do
+ @app = ProductionApp
+
+ output = StringIO.new
+ backtrace_cleaner = ActiveSupport::BacktraceCleaner.new
+ backtrace_cleaner.add_silencer { true }
+
+ env = { "action_dispatch.show_exceptions" => true,
+ "action_dispatch.logger" => Logger.new(output),
+ "action_dispatch.backtrace_cleaner" => backtrace_cleaner }
+
+ assert_raises ActionController::RoutingError do
+ get "/pass", headers: env
+ end
+
+ log = output.rewind && output.read
+
+ assert_includes log, "ActionController::RoutingError (No route matches [GET] \"/pass\")"
+ assert_equal 3, log.lines.count
+ end
+
test "display backtrace when error type is SyntaxError" do
@app = DevelopmentApp
@@ -589,4 +626,40 @@ class DebugExceptionsTest < ActionDispatch::IntegrationTest
end
end
end
+
+ test "shows a buttons for every action in an actionable error" do
+ @app = DevelopmentApp
+ Rails.stub :root, Pathname.new(".") do
+ cleaner = ActiveSupport::BacktraceCleaner.new.tap do |bc|
+ bc.add_silencer { |line| line !~ %r{test/dispatch/debug_exceptions_test.rb} }
+ end
+
+ get "/actionable_error", headers: { "action_dispatch.backtrace_cleaner" => cleaner }
+
+ # Assert correct error
+ assert_response 500
+
+ assert_select 'input[value="Action 1"]'
+ assert_select 'input[value="Action 2"]'
+ end
+ end
+
+ test "debug exceptions app shows diagnostics when malformed query parameters are provided" do
+ @app = DevelopmentApp
+
+ get "/bad_request?x[y]=1&x[y][][w]=2"
+
+ assert_response 400
+ assert_match "ActionController::BadRequest", body
+ end
+
+ test "debug exceptions app shows diagnostics when malformed query parameters are provided by XHR" do
+ @app = DevelopmentApp
+ xhr_request_env = { "action_dispatch.show_exceptions" => true, "HTTP_X_REQUESTED_WITH" => "XMLHttpRequest" }
+
+ get "/bad_request?x[y]=1&x[y][][w]=2", headers: xhr_request_env
+
+ assert_response 400
+ assert_match "ActionController::BadRequest", body
+ end
end
diff --git a/actionpack/test/dispatch/feature_policy_test.rb b/actionpack/test/dispatch/feature_policy_test.rb
new file mode 100644
index 0000000000..ebcc8a8b6d
--- /dev/null
+++ b/actionpack/test/dispatch/feature_policy_test.rb
@@ -0,0 +1,142 @@
+# frozen_string_literal: true
+
+require "abstract_unit"
+
+class FeaturePolicyTest < ActiveSupport::TestCase
+ def setup
+ @policy = ActionDispatch::FeaturePolicy.new
+ end
+
+ def test_mappings
+ @policy.midi :self
+ assert_equal "midi 'self'", @policy.build
+
+ @policy.midi :none
+ assert_equal "midi 'none'", @policy.build
+ end
+
+ def test_multiple_sources_for_a_single_directive
+ @policy.geolocation :self, "https://example.com"
+ assert_equal "geolocation 'self' https://example.com", @policy.build
+ end
+
+ def test_single_directive_for_multiple_directives
+ @policy.geolocation :self
+ @policy.usb :none
+ assert_equal "geolocation 'self'; usb 'none'", @policy.build
+ end
+
+ def test_multiple_directives_for_multiple_directives
+ @policy.geolocation :self, "https://example.com"
+ @policy.usb :none, "https://example.com"
+ assert_equal "geolocation 'self' https://example.com; usb 'none' https://example.com", @policy.build
+ end
+
+ def test_invalid_directive_source
+ exception = assert_raises(ArgumentError) do
+ @policy.vr [:non_existent]
+ end
+
+ assert_equal "Invalid HTTP feature policy source: [:non_existent]", exception.message
+ end
+end
+
+class FeaturePolicyIntegrationTest < ActionDispatch::IntegrationTest
+ class PolicyController < ActionController::Base
+ feature_policy only: :index do |f|
+ f.gyroscope :none
+ end
+
+ feature_policy only: :sample_controller do |f|
+ f.gyroscope nil
+ f.usb :self
+ end
+
+ feature_policy only: :multiple_directives do |f|
+ f.gyroscope nil
+ f.usb :self
+ f.autoplay "https://example.com"
+ f.payment "https://secure.example.com"
+ end
+
+ def index
+ head :ok
+ end
+
+ def sample_controller
+ head :ok
+ end
+
+ def multiple_directives
+ head :ok
+ end
+ end
+
+ ROUTES = ActionDispatch::Routing::RouteSet.new
+ ROUTES.draw do
+ scope module: "feature_policy_integration_test" do
+ get "/", to: "policy#index"
+ get "/sample_controller", to: "policy#sample_controller"
+ get "/multiple_directives", to: "policy#multiple_directives"
+ end
+ end
+
+ POLICY = ActionDispatch::FeaturePolicy.new do |p|
+ p.gyroscope :self
+ end
+
+ class PolicyConfigMiddleware
+ def initialize(app)
+ @app = app
+ end
+
+ def call(env)
+ env["action_dispatch.feature_policy"] = POLICY
+ env["action_dispatch.show_exceptions"] = false
+
+ @app.call(env)
+ end
+ end
+
+ APP = build_app(ROUTES) do |middleware|
+ middleware.use PolicyConfigMiddleware
+ middleware.use ActionDispatch::FeaturePolicy::Middleware
+ end
+
+ def app
+ APP
+ end
+
+ def test_generates_feature_policy_header
+ get "/"
+ assert_policy "gyroscope 'none'"
+ end
+
+ def test_generates_per_controller_feature_policy_header
+ get "/sample_controller"
+ assert_policy "usb 'self'"
+ end
+
+ def test_generates_multiple_directives_feature_policy_header
+ get "/multiple_directives"
+ assert_policy "usb 'self'; autoplay https://example.com; payment https://secure.example.com"
+ end
+
+ private
+ def env_config
+ Rails.application.env_config
+ end
+
+ def feature_policy
+ env_config["action_dispatch.feature_policy"]
+ end
+
+ def feature_policy=(policy)
+ env_config["action_dispatch.feature_policy"] = policy
+ end
+
+ def assert_policy(expected)
+ assert_response :success
+ assert_equal expected, response.headers["Feature-Policy"]
+ end
+end
diff --git a/actionpack/test/dispatch/middleware_stack_test.rb b/actionpack/test/dispatch/middleware_stack_test.rb
index 90f2eccd19..c534e60c74 100644
--- a/actionpack/test/dispatch/middleware_stack_test.rb
+++ b/actionpack/test/dispatch/middleware_stack_test.rb
@@ -121,9 +121,6 @@ class MiddlewareStackTest < ActiveSupport::TestCase
end
test "instruments the execution of middlewares" do
- app = @stack.build(proc { |env| [200, {}, []] })
- env = {}
-
events = []
subscriber = proc do |*args|
@@ -131,6 +128,9 @@ class MiddlewareStackTest < ActiveSupport::TestCase
end
ActiveSupport::Notifications.subscribed(subscriber, "process_middleware.action_dispatch") do
+ app = @stack.build(proc { |env| [200, {}, []] })
+
+ env = {}
app.call(env)
end
diff --git a/actionpack/test/dispatch/request_id_test.rb b/actionpack/test/dispatch/request_id_test.rb
index 9df4712dab..036180c297 100644
--- a/actionpack/test/dispatch/request_id_test.rb
+++ b/actionpack/test/dispatch/request_id_test.rb
@@ -29,7 +29,6 @@ class RequestIdTest < ActiveSupport::TestCase
end
private
-
def stub_request(env = {})
ActionDispatch::RequestId.new(lambda { |environment| [ 200, environment, [] ] }).call(env)
ActionDispatch::Request.new(env)
@@ -58,7 +57,6 @@ class RequestIdResponseTest < ActionDispatch::IntegrationTest
end
private
-
def with_test_route_set
with_routing do |set|
set.draw do
diff --git a/actionpack/test/dispatch/request_test.rb b/actionpack/test/dispatch/request_test.rb
index eb49396145..0ec8dd25e0 100644
--- a/actionpack/test/dispatch/request_test.rb
+++ b/actionpack/test/dispatch/request_test.rb
@@ -681,7 +681,6 @@ end
class RequestMethod < BaseRequestTest
test "method returns environment's request method when it has not been
overridden by middleware".squish do
-
ActionDispatch::Request::HTTP_METHODS.each do |method|
request = stub_request("REQUEST_METHOD" => method)
diff --git a/actionpack/test/dispatch/response_test.rb b/actionpack/test/dispatch/response_test.rb
index 7758b0406a..ed64d89902 100644
--- a/actionpack/test/dispatch/response_test.rb
+++ b/actionpack/test/dispatch/response_test.rb
@@ -290,8 +290,8 @@ class ResponseTest < ActiveSupport::TestCase
resp.to_a
assert_equal("utf-16", resp.charset)
- assert_equal(Mime[:xml], resp.content_type)
-
+ assert_equal(Mime[:xml], resp.media_type)
+ assert_equal("application/xml; charset=utf-16", resp.content_type)
assert_equal("application/xml; charset=utf-16", resp.headers["Content-Type"])
end
@@ -503,8 +503,8 @@ class ResponseIntegrationTest < ActionDispatch::IntegrationTest
assert_response :success
assert_equal("utf-16", @response.charset)
- assert_equal(Mime[:xml], @response.content_type)
-
+ assert_equal(Mime[:xml], @response.media_type)
+ assert_equal("application/xml; charset=utf-16", @response.content_type)
assert_equal("application/xml; charset=utf-16", @response.headers["Content-Type"])
end
@@ -519,8 +519,8 @@ class ResponseIntegrationTest < ActionDispatch::IntegrationTest
assert_response :success
assert_equal("utf-16", @response.charset)
- assert_equal(Mime[:xml], @response.content_type)
-
+ assert_equal(Mime[:xml], @response.media_type)
+ assert_equal("application/xml; charset=utf-16", @response.content_type)
assert_equal("application/xml; charset=utf-16", @response.headers["Content-Type"])
end
@@ -553,7 +553,26 @@ class ResponseIntegrationTest < ActionDispatch::IntegrationTest
assert_response :success
assert_equal("text/csv; charset=utf-16; header=present", @response.headers["Content-Type"])
- assert_equal("text/csv", @response.content_type)
+ assert_equal("text/csv; charset=utf-16; header=present", @response.content_type)
+ assert_equal("text/csv", @response.media_type)
+ assert_equal("utf-16", @response.charset)
+ end
+
+ test "response Content-Type with optional parameters that set before charset" do
+ @app = lambda { |env|
+ [
+ 200,
+ { "Content-Type" => "text/csv; header=present; charset=utf-16" },
+ ["Hello"]
+ ]
+ }
+
+ get "/"
+ assert_response :success
+
+ assert_equal("text/csv; header=present; charset=utf-16", @response.headers["Content-Type"])
+ assert_equal("text/csv; header=present; charset=utf-16", @response.content_type)
+ assert_equal("text/csv", @response.media_type)
assert_equal("utf-16", @response.charset)
end
@@ -570,7 +589,37 @@ class ResponseIntegrationTest < ActionDispatch::IntegrationTest
assert_response :success
assert_equal('text/csv; header=present; charset="utf-16"', @response.headers["Content-Type"])
- assert_equal("text/csv", @response.content_type)
+ assert_equal('text/csv; header=present; charset="utf-16"', @response.content_type)
+ assert_equal("text/csv", @response.media_type)
assert_equal("utf-16", @response.charset)
end
+
+ test "`content type` returns header that excludes `charset` when specified `return_only_media_type_on_content_type`" do
+ original = ActionDispatch::Response.return_only_media_type_on_content_type
+ ActionDispatch::Response.return_only_media_type_on_content_type = true
+
+ @app = lambda { |env|
+ if env["PATH_INFO"] == "/with_parameters"
+ [200, { "Content-Type" => "text/csv; header=present; charset=utf-16" }, [""]]
+ else
+ [200, { "Content-Type" => "text/csv; charset=utf-16" }, [""]]
+ end
+ }
+
+ get "/"
+ assert_response :success
+
+ assert_deprecated do
+ assert_equal("text/csv", @response.content_type)
+ end
+
+ get "/with_parameters"
+ assert_response :success
+
+ assert_deprecated do
+ assert_equal("text/csv; header=present", @response.content_type)
+ end
+ ensure
+ ActionDispatch::Response.return_only_media_type_on_content_type = original
+ end
end
diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb
index 362488d585..b67b1dd347 100644
--- a/actionpack/test/dispatch/routing_test.rb
+++ b/actionpack/test/dispatch/routing_test.rb
@@ -3810,7 +3810,6 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
end
private
-
def draw(&block)
self.class.stub_controllers do |routes|
routes.default_url_options = { host: "www.example.com" }
@@ -4953,12 +4952,52 @@ class TestPartialDynamicPathSegments < ActionDispatch::IntegrationTest
end
private
-
def assert_params(params)
assert_equal(params, request.path_parameters)
end
end
+class TestOptionalScopesWithOrWithoutParams < ActionDispatch::IntegrationTest
+ Routes = ActionDispatch::Routing::RouteSet.new.tap do |app|
+ app.draw do
+ scope module: "test_optional_scopes_with_or_without_params" do
+ scope "(:locale)", locale: /en|es/ do
+ get "home", to: "home#index"
+ get "with_param/:foo", to: "home#with_param", as: "with_param"
+ get "without_param", to: "home#without_param"
+ end
+ end
+ end
+ end
+
+ class HomeController < ActionController::Base
+ include Routes.url_helpers
+
+ def index
+ render inline: "<%= with_param_path(foo: 'bar') %> | <%= without_param_path %>"
+ end
+
+ def with_param; end
+ def without_param; end
+ end
+
+ APP = build_app Routes
+
+ def app
+ APP
+ end
+
+ def test_stays_unscoped_with_or_without_params
+ get "/home"
+ assert_equal "/with_param/bar | /without_param", response.body
+ end
+
+ def test_preserves_scope_with_or_without_params
+ get "/es/home"
+ assert_equal "/es/with_param/bar | /es/without_param", response.body
+ end
+end
+
class TestPathParameters < ActionDispatch::IntegrationTest
Routes = ActionDispatch::Routing::RouteSet.new.tap do |app|
app.draw do
@@ -5143,7 +5182,6 @@ class TestRecognizePath < ActionDispatch::IntegrationTest
end
private
-
def recognize_path(*args)
Routes.recognize_path(*args)
end
diff --git a/actionpack/test/dispatch/session/cookie_store_test.rb b/actionpack/test/dispatch/session/cookie_store_test.rb
index e34426a471..b6f83f4062 100644
--- a/actionpack/test/dispatch/session/cookie_store_test.rb
+++ b/actionpack/test/dispatch/session/cookie_store_test.rb
@@ -379,7 +379,6 @@ class CookieStoreTest < ActionDispatch::IntegrationTest
end
private
-
# Overwrite get to send SessionSecret in env hash
def get(path, *args)
args[0] ||= {}
diff --git a/actionpack/test/dispatch/static_test.rb b/actionpack/test/dispatch/static_test.rb
index d44aa00122..1f93d594a6 100644
--- a/actionpack/test/dispatch/static_test.rb
+++ b/actionpack/test/dispatch/static_test.rb
@@ -232,7 +232,6 @@ module StaticTests
end
private
-
def assert_gzip(file_name, response)
expected = File.read("#{FIXTURE_LOAD_PATH}/#{public_path}" + file_name)
actual = ActiveSupport::Gzip.decompress(response.body)
diff --git a/actionpack/test/dispatch/system_testing/driver_test.rb b/actionpack/test/dispatch/system_testing/driver_test.rb
index 0d08f17af3..d3b16d0328 100644
--- a/actionpack/test/dispatch/system_testing/driver_test.rb
+++ b/actionpack/test/dispatch/system_testing/driver_test.rb
@@ -66,7 +66,7 @@ class DriverTest < ActiveSupport::TestCase
end
driver.use
- expected = { args: ["start-maximized"], mobileEmulation: { deviceName: "iphone 6" }, prefs: { detach: true } }
+ expected = { "goog:chromeOptions" => { args: ["start-maximized"], mobileEmulation: { deviceName: "iphone 6" }, prefs: { detach: true } } }
assert_equal expected, driver_option.as_json
end
@@ -81,7 +81,7 @@ class DriverTest < ActiveSupport::TestCase
end
driver.use
- expected = { args: ["start-maximized"], mobileEmulation: { deviceName: "iphone 6" }, prefs: { detach: true } }
+ expected = { "goog:chromeOptions" => { args: ["start-maximized"], mobileEmulation: { deviceName: "iphone 6" }, prefs: { detach: true } } }
assert_equal expected, driver_option.as_json
end
@@ -120,4 +120,17 @@ class DriverTest < ActiveSupport::TestCase
driver.use
end
end
+
+ test "preloads browser's driver_path" do
+ called = false
+
+ original_driver_path = ::Selenium::WebDriver::Chrome::Service.driver_path
+ ::Selenium::WebDriver::Chrome::Service.driver_path = -> { called = true }
+
+ ActionDispatch::SystemTesting::Driver.new(:selenium, screen_size: [1400, 1400], using: :chrome)
+
+ assert called
+ ensure
+ ::Selenium::WebDriver::Chrome::Service.driver_path = original_driver_path
+ end
end
diff --git a/actionpack/test/dispatch/system_testing/screenshot_helper_test.rb b/actionpack/test/dispatch/system_testing/screenshot_helper_test.rb
index b756b91379..b0b36f9d74 100644
--- a/actionpack/test/dispatch/system_testing/screenshot_helper_test.rb
+++ b/actionpack/test/dispatch/system_testing/screenshot_helper_test.rb
@@ -36,6 +36,14 @@ class ScreenshotHelperTest < ActiveSupport::TestCase
end
end
+ test "image name truncates names over 225 characters" do
+ new_test = DrivenBySeleniumWithChrome.new("x" * 400)
+
+ Rails.stub :root, Pathname.getwd do
+ assert_equal Rails.root.join("tmp/screenshots/#{"x" * 225}.png").to_s, new_test.send(:image_path)
+ end
+ end
+
test "defaults to simple output for the screenshot" do
new_test = DrivenBySeleniumWithChrome.new("x")
assert_equal "simple", new_test.send(:output_type)
diff --git a/actionpack/test/dispatch/system_testing/system_test_case_test.rb b/actionpack/test/dispatch/system_testing/system_test_case_test.rb
index 847b09dcfe..d235f7ad89 100644
--- a/actionpack/test/dispatch/system_testing/system_test_case_test.rb
+++ b/actionpack/test/dispatch/system_testing/system_test_case_test.rb
@@ -36,50 +36,11 @@ class SetDriverToSeleniumHeadlessFirefoxTest < DrivenBySeleniumWithHeadlessFiref
end
class SetHostTest < DrivenByRackTest
- test "sets default host" do
- assert_equal "http://127.0.0.1", Capybara.app_host
- end
-
test "overrides host" do
- host! "http://example.com"
-
- assert_equal "http://example.com", Capybara.app_host
- end
-end
-
-class UndefMethodsTest < DrivenBySeleniumWithChrome
- test "get" do
- exception = assert_raise NoMethodError do
- get "http://example.com"
+ assert_deprecated do
+ host! "http://example.com"
end
- assert_equal "System tests cannot make direct requests via #get; use #visit and #click_on instead. See http://www.rubydoc.info/github/teamcapybara/capybara/master#The_DSL for more information.", exception.message
- end
- test "post" do
- exception = assert_raise NoMethodError do
- post "http://example.com"
- end
- assert_equal "System tests cannot make direct requests via #post; use #visit and #click_on instead. See http://www.rubydoc.info/github/teamcapybara/capybara/master#The_DSL for more information.", exception.message
- end
-
- test "put" do
- exception = assert_raise NoMethodError do
- put "http://example.com"
- end
- assert_equal "System tests cannot make direct requests via #put; use #visit and #click_on instead. See http://www.rubydoc.info/github/teamcapybara/capybara/master#The_DSL for more information.", exception.message
- end
-
- test "patch" do
- exception = assert_raise NoMethodError do
- patch "http://example.com"
- end
- assert_equal "System tests cannot make direct requests via #patch; use #visit and #click_on instead. See http://www.rubydoc.info/github/teamcapybara/capybara/master#The_DSL for more information.", exception.message
- end
-
- test "delete" do
- exception = assert_raise NoMethodError do
- delete "http://example.com"
- end
- assert_equal "System tests cannot make direct requests via #delete; use #visit and #click_on instead. See http://www.rubydoc.info/github/teamcapybara/capybara/master#The_DSL for more information.", exception.message
+ assert_equal "http://example.com", Capybara.app_host
end
end
diff --git a/actionpack/test/journey/route/definition/scanner_test.rb b/actionpack/test/journey/route/definition/scanner_test.rb
index 092177d315..e55ed92cc8 100644
--- a/actionpack/test/journey/route/definition/scanner_test.rb
+++ b/actionpack/test/journey/route/definition/scanner_test.rb
@@ -66,7 +66,6 @@ module ActionDispatch
end
private
-
def assert_tokens(expected_tokens, scanner, pattern)
actual_tokens = []
while token = scanner.next_token
diff --git a/actionpack/test/journey/route_test.rb b/actionpack/test/journey/route_test.rb
index a8bf4a11e2..8828201e4f 100644
--- a/actionpack/test/journey/route_test.rb
+++ b/actionpack/test/journey/route_test.rb
@@ -9,7 +9,7 @@ module ActionDispatch
app = Object.new
path = Path::Pattern.from_string "/:controller(/:action(/:id(.:format)))"
defaults = {}
- route = Route.build("name", app, path, {}, [], defaults)
+ route = Route.new(name: "name", app: app, path: path, defaults: defaults)
assert_equal app, route.app
assert_equal path, route.path
@@ -17,10 +17,9 @@ module ActionDispatch
end
def test_route_adds_itself_as_memo
- app = Object.new
- path = Path::Pattern.from_string "/:controller(/:action(/:id(.:format)))"
- defaults = {}
- route = Route.build("name", app, path, {}, [], defaults)
+ app = Object.new
+ path = Path::Pattern.from_string "/:controller(/:action(/:id(.:format)))"
+ route = Route.new(name: "name", app: app, path: path)
route.ast.grep(Nodes::Terminal).each do |node|
assert_equal route, node.memo
@@ -28,30 +27,30 @@ module ActionDispatch
end
def test_path_requirements_override_defaults
- path = Path::Pattern.build(":name", { name: /love/ }, "/", true)
- defaults = { name: "tender" }
- route = Route.build("name", nil, path, {}, [], defaults)
+ path = Path::Pattern.build(":name", { name: /love/ }, "/", true)
+ defaults = { name: "tender" }
+ route = Route.new(name: "name", path: path, defaults: defaults)
assert_equal(/love/, route.requirements[:name])
end
def test_ip_address
path = Path::Pattern.from_string "/messages/:id(.:format)"
- route = Route.build("name", nil, path, { ip: "192.168.1.1" }, [],
- controller: "foo", action: "bar")
+ route = Route.new(name: "name", path: path, constraints: { ip: "192.168.1.1" },
+ defaults: { controller: "foo", action: "bar" })
assert_equal "192.168.1.1", route.ip
end
def test_default_ip
path = Path::Pattern.from_string "/messages/:id(.:format)"
- route = Route.build("name", nil, path, {}, [],
- controller: "foo", action: "bar")
+ route = Route.new(name: "name", path: path,
+ defaults: { controller: "foo", action: "bar" })
assert_equal(//, route.ip)
end
def test_format_with_star
path = Path::Pattern.from_string "/:controller/*extra"
- route = Route.build("name", nil, path, {}, [],
- controller: "foo", action: "bar")
+ route = Route.new(name: "name", path: path,
+ defaults: { controller: "foo", action: "bar" })
assert_equal "/foo/himom", route.format(
controller: "foo",
extra: "himom")
@@ -59,7 +58,8 @@ module ActionDispatch
def test_connects_all_match
path = Path::Pattern.from_string "/:controller(/:action(/:id(.:format)))"
- route = Route.build("name", nil, path, { action: "bar" }, [], controller: "foo")
+ route = Route.new(name: "name", path: path, constraints: { action: "bar" },
+ defaults: { controller: "foo" })
assert_equal "/foo/bar/10", route.format(
controller: "foo",
@@ -69,34 +69,33 @@ module ActionDispatch
def test_extras_are_not_included_if_optional
path = Path::Pattern.from_string "/page/:id(/:action)"
- route = Route.build("name", nil, path, {}, [], action: "show")
+ route = Route.new(name: "name", path: path, defaults: { action: "show" })
assert_equal "/page/10", route.format(id: 10)
end
def test_extras_are_not_included_if_optional_with_parameter
path = Path::Pattern.from_string "(/sections/:section)/pages/:id"
- route = Route.build("name", nil, path, {}, [], action: "show")
+ route = Route.new(name: "name", path: path, defaults: { action: "show" })
assert_equal "/pages/10", route.format(id: 10)
end
def test_extras_are_not_included_if_optional_parameter_is_nil
path = Path::Pattern.from_string "(/sections/:section)/pages/:id"
- route = Route.build("name", nil, path, {}, [], action: "show")
+ route = Route.new(name: "name", path: path, defaults: { action: "show" })
assert_equal "/pages/10", route.format(id: 10, section: nil)
end
def test_score
- constraints = {}
defaults = { controller: "pages", action: "show" }
path = Path::Pattern.from_string "/page/:id(/:action)(.:format)"
- specific = Route.build "name", nil, path, constraints, [:controller, :action], defaults
+ specific = Route.new name: "name", path: path, required_defaults: [:controller, :action], defaults: defaults
path = Path::Pattern.from_string "/:controller(/:action(/:id))(.:format)"
- generic = Route.build "name", nil, path, constraints, [], {}
+ generic = Route.new name: "name", path: path
knowledge = { "id" => true, "controller" => true, "action" => true }
diff --git a/actionpack/test/journey/router_test.rb b/actionpack/test/journey/router_test.rb
index f8d89def6a..fe0e3a975b 100644
--- a/actionpack/test/journey/router_test.rb
+++ b/actionpack/test/journey/router_test.rb
@@ -503,7 +503,6 @@ module ActionDispatch
end
private
-
def get(*args)
ActiveSupport::Deprecation.silence do
mapper.get(*args)
diff --git a/actiontext/.gitignore b/actiontext/.gitignore
index 739e00a2cb..9a67d93d26 100644
--- a/actiontext/.gitignore
+++ b/actiontext/.gitignore
@@ -1,5 +1,6 @@
/test/dummy/db/*.sqlite3
/test/dummy/db/*.sqlite3-journal
/test/dummy/log/*.log
+/test/dummy/public/packs-test
/test/dummy/tmp/
/tmp/
diff --git a/actiontext/CHANGELOG.md b/actiontext/CHANGELOG.md
index b79ff612fb..d28799279f 100644
--- a/actiontext/CHANGELOG.md
+++ b/actiontext/CHANGELOG.md
@@ -1,15 +1,21 @@
-## Rails 6.0.0.beta3 (March 11, 2019) ##
+* The `fill_in_rich_text_area` system test helper locates a Trix editor and fills it in with the given HTML:
-* No changes.
+ ```ruby
+ # <trix-editor id="message_content" ...></trix-editor>
+ fill_in_rich_text_area "message_content", with: "Hello <em>world!</em>"
+ # <trix-editor placeholder="Your message here" ...></trix-editor>
+ fill_in_rich_text_area "Your message here", with: "Hello <em>world!</em>"
-## Rails 6.0.0.beta2 (February 25, 2019) ##
+ # <trix-editor aria-label="Message content" ...></trix-editor>
+ fill_in_rich_text_area "Message content", with: "Hello <em>world!</em>"
-* No changes.
+ # <input id="trix_input_1" name="message[content]" type="hidden">
+ # <trix-editor input="trix_input_1"></trix-editor>
+ fill_in_rich_text_area "message[content]", with: "Hello <em>world!</em>"
+ ```
+ *George Claghorn*
-## Rails 6.0.0.beta1 (January 18, 2019) ##
-* Added to Rails.
-
- *DHH*
+Please check [6-0-stable](https://github.com/rails/rails/blob/6-0-stable/actiontext/CHANGELOG.md) for previous changes.
diff --git a/actiontext/Rakefile b/actiontext/Rakefile
index 36aed17282..a759a22190 100644
--- a/actiontext/Rakefile
+++ b/actiontext/Rakefile
@@ -8,7 +8,13 @@ task :package
Rake::TestTask.new do |t|
t.libs << "test"
- t.pattern = "test/**/*_test.rb"
+ t.test_files = FileList["test/**/*_test.rb"].exclude("test/system/**/*", "test/dummy/**/*")
+ t.verbose = true
+end
+
+Rake::TestTask.new "test:system" do |t|
+ t.libs << "test"
+ t.test_files = FileList["test/system/**/*_test.rb"]
t.verbose = true
end
diff --git a/actiontext/app/helpers/action_text/content_helper.rb b/actiontext/app/helpers/action_text/content_helper.rb
index 2005033d5c..ed2887d865 100644
--- a/actiontext/app/helpers/action_text/content_helper.rb
+++ b/actiontext/app/helpers/action_text/content_helper.rb
@@ -4,20 +4,27 @@ require "rails-html-sanitizer"
module ActionText
module ContentHelper
- SANITIZER = Rails::Html::Sanitizer.white_list_sanitizer
- ALLOWED_TAGS = SANITIZER.allowed_tags + [ ActionText::Attachment::TAG_NAME, "figure", "figcaption" ]
- ALLOWED_ATTRIBUTES = SANITIZER.allowed_attributes + ActionText::Attachment::ATTRIBUTES
+ mattr_accessor(:sanitizer) { Rails::Html::Sanitizer.white_list_sanitizer.new }
+ mattr_accessor(:allowed_tags) { sanitizer.class.allowed_tags + [ ActionText::Attachment::TAG_NAME, "figure", "figcaption" ] }
+ mattr_accessor(:allowed_attributes) { sanitizer.class.allowed_attributes + ActionText::Attachment::ATTRIBUTES }
+ mattr_accessor(:scrubber)
def render_action_text_content(content)
- content = content.render_attachments do |attachment|
+ sanitize_action_text_content(render_action_text_attachments(content))
+ end
+
+ def sanitize_action_text_content(content)
+ sanitizer.sanitize(content.to_html, tags: allowed_tags, attributes: allowed_attributes, scrubber: scrubber).html_safe
+ end
+
+ def render_action_text_attachments(content)
+ content.render_attachments do |attachment|
unless attachment.in?(content.gallery_attachments)
attachment.node.tap do |node|
node.inner_html = render(attachment, in_gallery: false).chomp
end
end
- end
-
- content = content.render_attachment_galleries do |attachment_gallery|
+ end.render_attachment_galleries do |attachment_gallery|
render(layout: attachment_gallery, object: attachment_gallery) do
attachment_gallery.attachments.map do |attachment|
attachment.node.inner_html = render(attachment, in_gallery: true).chomp
@@ -25,8 +32,6 @@ module ActionText
end.join("").html_safe
end.chomp
end
-
- sanitize content.to_html, tags: ALLOWED_TAGS, attributes: ALLOWED_ATTRIBUTES
end
end
end
diff --git a/actiontext/app/models/action_text/rich_text.rb b/actiontext/app/models/action_text/rich_text.rb
index 19fa3e030e..1a3ffdfa27 100644
--- a/actiontext/app/models/action_text/rich_text.rb
+++ b/actiontext/app/models/action_text/rich_text.rb
@@ -15,7 +15,7 @@ module ActionText
has_many_attached :embeds
before_save do
- self.embeds = body.attachables.grep(ActiveStorage::Blob) if body.present?
+ self.embeds = body.attachables.grep(ActiveStorage::Blob).uniq if body.present?
end
def to_plain_text
diff --git a/actiontext/lib/action_text/engine.rb b/actiontext/lib/action_text/engine.rb
index 51ff5b575b..0f55d460a1 100644
--- a/actiontext/lib/action_text/engine.rb
+++ b/actiontext/lib/action_text/engine.rb
@@ -25,6 +25,10 @@ module ActionText
def previewable_attachable?
representable?
end
+
+ def attachable_plain_text_representation(caption = nil)
+ "[#{caption || filename}]"
+ end
end
end
@@ -46,5 +50,12 @@ module ActionText
before_action { ActionText::Content.renderer = ApplicationController.renderer.new(request.env) }
end
end
+
+ initializer "action_text.system_test_helper" do
+ ActiveSupport.on_load(:action_dispatch_system_test_case) do
+ require "action_text/system_test_helper"
+ include ActionText::SystemTestHelper
+ end
+ end
end
end
diff --git a/actiontext/lib/action_text/gem_version.rb b/actiontext/lib/action_text/gem_version.rb
index ecd32d5f69..8b4103712c 100644
--- a/actiontext/lib/action_text/gem_version.rb
+++ b/actiontext/lib/action_text/gem_version.rb
@@ -8,9 +8,9 @@ module ActionText
module VERSION
MAJOR = 6
- MINOR = 0
+ MINOR = 1
TINY = 0
- PRE = "beta3"
+ PRE = "alpha"
STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
end
diff --git a/actiontext/lib/action_text/system_test_helper.rb b/actiontext/lib/action_text/system_test_helper.rb
new file mode 100644
index 0000000000..77fc9eb50b
--- /dev/null
+++ b/actiontext/lib/action_text/system_test_helper.rb
@@ -0,0 +1,48 @@
+# frozen_string_literal: true
+
+module ActionText
+ module SystemTestHelper
+ # Locates a Trix editor and fills it in with the given HTML.
+ #
+ # The editor can be found by:
+ # * its +id+
+ # * its +placeholder+
+ # * its +aria-label+
+ # * the +name+ of its input
+ #
+ # Examples:
+ #
+ # # <trix-editor id="message_content" ...></trix-editor>
+ # fill_in_rich_text_area "message_content", with: "Hello <em>world!</em>"
+ #
+ # # <trix-editor placeholder="Your message here" ...></trix-editor>
+ # fill_in_rich_text_area "Your message here", with: "Hello <em>world!</em>"
+ #
+ # # <trix-editor aria-label="Message content" ...></trix-editor>
+ # fill_in_rich_text_area "Message content", with: "Hello <em>world!</em>"
+ #
+ # # <input id="trix_input_1" name="message[content]" type="hidden">
+ # # <trix-editor input="trix_input_1"></trix-editor>
+ # fill_in_rich_text_area "message[content]", with: "Hello <em>world!</em>"
+ def fill_in_rich_text_area(locator = nil, with:)
+ find(:rich_text_area, locator).execute_script("this.editor.loadHTML(arguments[0])", with.to_s)
+ end
+ end
+end
+
+Capybara.add_selector :rich_text_area do
+ label "rich-text area"
+ xpath do |locator|
+ if locator.nil?
+ XPath.descendant(:"trix-editor")
+ else
+ input_located_by_name = XPath.anywhere(:input).where(XPath.attr(:name) == locator).attr(:id)
+
+ XPath.descendant(:"trix-editor").where \
+ XPath.attr(:id).equals(locator) |
+ XPath.attr(:placeholder).equals(locator) |
+ XPath.attr(:"aria-label").equals(locator) |
+ XPath.attr(:input).equals(input_located_by_name)
+ end
+ end
+end
diff --git a/actiontext/lib/templates/installer.rb b/actiontext/lib/templates/installer.rb
index a8000eb9fc..a15ada92bb 100644
--- a/actiontext/lib/templates/installer.rb
+++ b/actiontext/lib/templates/installer.rb
@@ -29,4 +29,17 @@ if APPLICATION_PACK_PATH.exist?
append_to_file APPLICATION_PACK_PATH, "\n#{line}"
end
end
+else
+ warn <<~WARNING
+ WARNING: Action Text can't locate your JavaScript bundle to add its package dependencies.
+
+ Add these lines to any bundles:
+
+ require("trix")
+ require("@rails/actiontext")
+
+ Alternatively, install and setup the webpacker gem then rerun `bin/rails action_text:install`
+ to have these dependencies added automatically.
+
+ WARNING
end
diff --git a/actiontext/package.json b/actiontext/package.json
index f67fea1642..594cfe16eb 100644
--- a/actiontext/package.json
+++ b/actiontext/package.json
@@ -1,6 +1,6 @@
{
"name": "@rails/actiontext",
- "version": "6.0.0-beta3",
+ "version": "6.1.0-alpha",
"description": "Edit and display rich text in Rails applications",
"main": "app/javascript/actiontext/index.js",
"files": [
diff --git a/actiontext/test/application_system_test_case.rb b/actiontext/test/application_system_test_case.rb
new file mode 100644
index 0000000000..a24f473598
--- /dev/null
+++ b/actiontext/test/application_system_test_case.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+require "test_helper"
+
+class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
+ driven_by :selenium, using: :headless_chrome
+end
+
+Capybara.server = :puma, { Silent: true }
diff --git a/actiontext/test/dummy/.babelrc b/actiontext/test/dummy/.babelrc
deleted file mode 100644
index ded31c0d80..0000000000
--- a/actiontext/test/dummy/.babelrc
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "presets": [
- ["env", {
- "modules": false,
- "targets": {
- "browsers": "> 1%",
- "uglify": true
- },
- "useBuiltIns": true
- }]
- ],
-
- "plugins": [
- "syntax-dynamic-import",
- "transform-object-rest-spread",
- ["transform-class-properties", { "spec": true }]
- ]
-}
diff --git a/actiontext/test/dummy/.browserslistrc b/actiontext/test/dummy/.browserslistrc
new file mode 100644
index 0000000000..e94f8140cc
--- /dev/null
+++ b/actiontext/test/dummy/.browserslistrc
@@ -0,0 +1 @@
+defaults
diff --git a/actiontext/test/dummy/app/javascript/packs/application.js b/actiontext/test/dummy/app/javascript/packs/application.js
index 13ac17ed58..091bb5839e 100644
--- a/actiontext/test/dummy/app/javascript/packs/application.js
+++ b/actiontext/test/dummy/app/javascript/packs/application.js
@@ -1 +1,2 @@
+import "trix"
import "@rails/actiontext"
diff --git a/actiontext/test/dummy/app/views/messages/_form.html.erb b/actiontext/test/dummy/app/views/messages/_form.html.erb
index 3b8a174884..cbaf675fff 100644
--- a/actiontext/test/dummy/app/views/messages/_form.html.erb
+++ b/actiontext/test/dummy/app/views/messages/_form.html.erb
@@ -18,7 +18,8 @@
<div class="field">
<%= form.label :content %>
- <%= form.rich_text_area :content, class: "trix-content" %>
+ <%= form.rich_text_area :content, class: "trix-content",
+ placeholder: "Your message here", aria: { label: "Message content" } %>
</div>
<div class="actions">
diff --git a/actiontext/test/dummy/babel.config.js b/actiontext/test/dummy/babel.config.js
new file mode 100644
index 0000000000..f930f3e0a0
--- /dev/null
+++ b/actiontext/test/dummy/babel.config.js
@@ -0,0 +1,70 @@
+module.exports = function(api) {
+ var validEnv = ['development', 'test', 'production']
+ var currentEnv = api.env()
+ var isDevelopmentEnv = api.env('development')
+ var isProductionEnv = api.env('production')
+ var isTestEnv = api.env('test')
+
+ if (!validEnv.includes(currentEnv)) {
+ throw new Error(
+ 'Please specify a valid `NODE_ENV` or ' +
+ '`BABEL_ENV` environment variables. Valid values are "development", ' +
+ '"test", and "production". Instead, received: ' +
+ JSON.stringify(currentEnv) +
+ '.'
+ )
+ }
+
+ return {
+ presets: [
+ isTestEnv && [
+ require('@babel/preset-env').default,
+ {
+ targets: {
+ node: 'current'
+ }
+ }
+ ],
+ (isProductionEnv || isDevelopmentEnv) && [
+ require('@babel/preset-env').default,
+ {
+ forceAllTransforms: true,
+ useBuiltIns: 'entry',
+ modules: false,
+ exclude: ['transform-typeof-symbol']
+ }
+ ]
+ ].filter(Boolean),
+ plugins: [
+ require('babel-plugin-macros'),
+ require('@babel/plugin-syntax-dynamic-import').default,
+ isTestEnv && require('babel-plugin-dynamic-import-node'),
+ require('@babel/plugin-transform-destructuring').default,
+ [
+ require('@babel/plugin-proposal-class-properties').default,
+ {
+ loose: true
+ }
+ ],
+ [
+ require('@babel/plugin-proposal-object-rest-spread').default,
+ {
+ useBuiltIns: true
+ }
+ ],
+ [
+ require('@babel/plugin-transform-runtime').default,
+ {
+ helpers: false,
+ regenerator: true
+ }
+ ],
+ [
+ require('@babel/plugin-transform-regenerator').default,
+ {
+ async: false
+ }
+ ]
+ ].filter(Boolean)
+ }
+}
diff --git a/actiontext/test/dummy/bin/bundle b/actiontext/test/dummy/bin/bundle
index f19acf5b5c..0962d6a66f 100755
--- a/actiontext/test/dummy/bin/bundle
+++ b/actiontext/test/dummy/bin/bundle
@@ -1,3 +1,3 @@
#!/usr/bin/env ruby
-ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
+ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../../../Gemfile', __dir__)
load Gem.bin_path('bundler', 'bundle')
diff --git a/actiontext/test/dummy/bin/webpack b/actiontext/test/dummy/bin/webpack
new file mode 100755
index 0000000000..008ecb22f7
--- /dev/null
+++ b/actiontext/test/dummy/bin/webpack
@@ -0,0 +1,19 @@
+#!/usr/bin/env ruby
+
+ENV["RAILS_ENV"] ||= ENV["RACK_ENV"] || "development"
+ENV["NODE_ENV"] ||= "development"
+
+require "pathname"
+ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
+ Pathname.new(__FILE__).realpath)
+
+require "rubygems"
+require "bundler/setup"
+
+require "webpacker"
+require "webpacker/webpack_runner"
+
+APP_ROOT = File.expand_path("..", __dir__)
+Dir.chdir(APP_ROOT) do
+ Webpacker::WebpackRunner.run(ARGV)
+end
diff --git a/actiontext/test/dummy/bin/webpack-dev-server b/actiontext/test/dummy/bin/webpack-dev-server
new file mode 100755
index 0000000000..a931a9b7fc
--- /dev/null
+++ b/actiontext/test/dummy/bin/webpack-dev-server
@@ -0,0 +1,19 @@
+#!/usr/bin/env ruby
+
+ENV["RAILS_ENV"] ||= ENV["RACK_ENV"] || "development"
+ENV["NODE_ENV"] ||= "development"
+
+require "pathname"
+ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
+ Pathname.new(__FILE__).realpath)
+
+require "rubygems"
+require "bundler/setup"
+
+require "webpacker"
+require "webpacker/dev_server_runner"
+
+APP_ROOT = File.expand_path("..", __dir__)
+Dir.chdir(APP_ROOT) do
+ Webpacker::DevServerRunner.run(ARGV)
+end
diff --git a/actiontext/test/dummy/config/webpack/development.js b/actiontext/test/dummy/config/webpack/development.js
index 81269f6513..c5edff94ad 100644
--- a/actiontext/test/dummy/config/webpack/development.js
+++ b/actiontext/test/dummy/config/webpack/development.js
@@ -1,3 +1,5 @@
+process.env.NODE_ENV = process.env.NODE_ENV || 'development'
+
const environment = require('./environment')
module.exports = environment.toWebpackConfig()
diff --git a/actiontext/test/dummy/config/webpack/production.js b/actiontext/test/dummy/config/webpack/production.js
index 81269f6513..be0f53aacf 100644
--- a/actiontext/test/dummy/config/webpack/production.js
+++ b/actiontext/test/dummy/config/webpack/production.js
@@ -1,3 +1,5 @@
+process.env.NODE_ENV = process.env.NODE_ENV || 'production'
+
const environment = require('./environment')
module.exports = environment.toWebpackConfig()
diff --git a/actiontext/test/dummy/config/webpack/test.js b/actiontext/test/dummy/config/webpack/test.js
index 81269f6513..c5edff94ad 100644
--- a/actiontext/test/dummy/config/webpack/test.js
+++ b/actiontext/test/dummy/config/webpack/test.js
@@ -1,3 +1,5 @@
+process.env.NODE_ENV = process.env.NODE_ENV || 'development'
+
const environment = require('./environment')
module.exports = environment.toWebpackConfig()
diff --git a/actiontext/test/dummy/config/webpacker.yml b/actiontext/test/dummy/config/webpacker.yml
index d3f24e1b4b..6fd531213f 100644
--- a/actiontext/test/dummy/config/webpacker.yml
+++ b/actiontext/test/dummy/config/webpacker.yml
@@ -3,8 +3,11 @@
default: &default
source_path: app/javascript
source_entry_path: packs
+ public_root_path: public
public_output_path: packs
cache_path: tmp/cache/webpacker
+ check_yarn_integrity: false
+ webpack_compile_output: false
# Additional paths webpack should lookup modules
# ['app/assets', 'engine/foo/app/assets']
@@ -13,11 +16,32 @@ default: &default
# Reload manifest.json on all requests so we reload latest compiled packs
cache_manifest: false
+ # Extract and emit a css file
+ extract_css: false
+
+ static_assets_extensions:
+ - .jpg
+ - .jpeg
+ - .png
+ - .gif
+ - .tiff
+ - .ico
+ - .svg
+ - .eot
+ - .otf
+ - .ttf
+ - .woff
+ - .woff2
+
extensions:
+ - .mjs
- .js
- .sass
- .scss
- .css
+ - .module.sass
+ - .module.scss
+ - .module.css
- .png
- .svg
- .gif
@@ -28,6 +52,9 @@ development:
<<: *default
compile: true
+ # Verifies that versions and hashed value of the package contents in the project's package.json
+ check_yarn_integrity: true
+
# Reference: https://webpack.js.org/configuration/dev-server/
dev_server:
https: false
@@ -45,7 +72,7 @@ development:
headers:
'Access-Control-Allow-Origin': '*'
watch_options:
- ignored: /node_modules/
+ ignored: '**/node_modules/**'
test:
@@ -61,5 +88,8 @@ production:
# Production depends on precompilation of packs prior to booting for performance.
compile: false
+ # Extract and emit a css file
+ extract_css: true
+
# Cache manifest.json for performance
cache_manifest: true
diff --git a/actiontext/test/dummy/package.json b/actiontext/test/dummy/package.json
index 177a97c1e6..cb3bbbddf2 100644
--- a/actiontext/test/dummy/package.json
+++ b/actiontext/test/dummy/package.json
@@ -3,9 +3,10 @@
"private": true,
"dependencies": {
"@rails/webpacker": "^4.0.2",
- "actiontext": "file:../.."
+ "actiontext": "file:../..",
+ "trix": "^1.1.1"
},
"devDependencies": {
- "webpack-dev-server": "^3.2.1"
+ "webpack-dev-server": "^3.3.1"
}
}
diff --git a/actiontext/test/dummy/postcss.config.js b/actiontext/test/dummy/postcss.config.js
new file mode 100644
index 0000000000..aa5998a809
--- /dev/null
+++ b/actiontext/test/dummy/postcss.config.js
@@ -0,0 +1,12 @@
+module.exports = {
+ plugins: [
+ require('postcss-import'),
+ require('postcss-flexbugs-fixes'),
+ require('postcss-preset-env')({
+ autoprefixer: {
+ flexbox: 'no-2009'
+ },
+ stage: 3
+ })
+ ]
+}
diff --git a/actiontext/test/dummy/yarn.lock b/actiontext/test/dummy/yarn.lock
index 6cd01debdc..891f6e7856 100644
--- a/actiontext/test/dummy/yarn.lock
+++ b/actiontext/test/dummy/yarn.lock
@@ -10,17 +10,17 @@
"@babel/highlight" "^7.0.0"
"@babel/core@^7.3.4":
- version "7.3.4"
- resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.3.4.tgz#921a5a13746c21e32445bf0798680e9d11a6530b"
- integrity sha512-jRsuseXBo9pN197KnDwhhaaBzyZr2oIcLHHTt2oDdQrej5Qp57dCCJafWx5ivU8/alEYDpssYqv1MUqcxwQlrA==
+ version "7.4.4"
+ resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.4.4.tgz#84055750b05fcd50f9915a826b44fa347a825250"
+ integrity sha512-lQgGX3FPRgbz2SKmhMtYgJvVzGZrmjaF4apZ2bLwofAKiSjxU0drPh4S/VasyYXwaTs+A1gvQ45BN8SQJzHsQQ==
dependencies:
"@babel/code-frame" "^7.0.0"
- "@babel/generator" "^7.3.4"
- "@babel/helpers" "^7.2.0"
- "@babel/parser" "^7.3.4"
- "@babel/template" "^7.2.2"
- "@babel/traverse" "^7.3.4"
- "@babel/types" "^7.3.4"
+ "@babel/generator" "^7.4.4"
+ "@babel/helpers" "^7.4.4"
+ "@babel/parser" "^7.4.4"
+ "@babel/template" "^7.4.4"
+ "@babel/traverse" "^7.4.4"
+ "@babel/types" "^7.4.4"
convert-source-map "^1.1.0"
debug "^4.1.0"
json5 "^2.1.0"
@@ -29,12 +29,12 @@
semver "^5.4.1"
source-map "^0.5.0"
-"@babel/generator@^7.3.4":
- version "7.3.4"
- resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.3.4.tgz#9aa48c1989257877a9d971296e5b73bfe72e446e"
- integrity sha512-8EXhHRFqlVVWXPezBW5keTiQi/rJMQTg/Y9uVCEZ0CAF3PKtCCaVRnp64Ii1ujhkoDhhF1fVsImoN4yJ2uz4Wg==
+"@babel/generator@^7.4.4":
+ version "7.4.4"
+ resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.4.4.tgz#174a215eb843fc392c7edcaabeaa873de6e8f041"
+ integrity sha512-53UOLK6TVNqKxf7RUh8NE851EHRxOOeVXKbK2bivdb+iziMyk03Sr4eaE9OELCbyZAAafAKPDwF2TPUES5QbxQ==
dependencies:
- "@babel/types" "^7.3.4"
+ "@babel/types" "^7.4.4"
jsesc "^2.5.1"
lodash "^4.17.11"
source-map "^0.5.0"
@@ -55,35 +55,35 @@
"@babel/helper-explode-assignable-expression" "^7.1.0"
"@babel/types" "^7.0.0"
-"@babel/helper-call-delegate@^7.1.0":
- version "7.1.0"
- resolved "https://registry.yarnpkg.com/@babel/helper-call-delegate/-/helper-call-delegate-7.1.0.tgz#6a957f105f37755e8645343d3038a22e1449cc4a"
- integrity sha512-YEtYZrw3GUK6emQHKthltKNZwszBcHK58Ygcis+gVUrF4/FmTVr5CCqQNSfmvg2y+YDEANyYoaLz/SHsnusCwQ==
+"@babel/helper-call-delegate@^7.4.4":
+ version "7.4.4"
+ resolved "https://registry.yarnpkg.com/@babel/helper-call-delegate/-/helper-call-delegate-7.4.4.tgz#87c1f8ca19ad552a736a7a27b1c1fcf8b1ff1f43"
+ integrity sha512-l79boDFJ8S1c5hvQvG+rc+wHw6IuH7YldmRKsYtpbawsxURu/paVy57FZMomGK22/JckepaikOkY0MoAmdyOlQ==
dependencies:
- "@babel/helper-hoist-variables" "^7.0.0"
- "@babel/traverse" "^7.1.0"
- "@babel/types" "^7.0.0"
+ "@babel/helper-hoist-variables" "^7.4.4"
+ "@babel/traverse" "^7.4.4"
+ "@babel/types" "^7.4.4"
-"@babel/helper-create-class-features-plugin@^7.3.4":
- version "7.3.4"
- resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.3.4.tgz#092711a7a3ad8ea34de3e541644c2ce6af1f6f0c"
- integrity sha512-uFpzw6L2omjibjxa8VGZsJUPL5wJH0zzGKpoz0ccBkzIa6C8kWNUbiBmQ0rgOKWlHJ6qzmfa6lTiGchiV8SC+g==
+"@babel/helper-create-class-features-plugin@^7.4.4":
+ version "7.4.4"
+ resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.4.4.tgz#fc3d690af6554cc9efc607364a82d48f58736dba"
+ integrity sha512-UbBHIa2qeAGgyiNR9RszVF7bUHEdgS4JAUNT8SiqrAN6YJVxlOxeLr5pBzb5kan302dejJ9nla4RyKcR1XT6XA==
dependencies:
"@babel/helper-function-name" "^7.1.0"
"@babel/helper-member-expression-to-functions" "^7.0.0"
"@babel/helper-optimise-call-expression" "^7.0.0"
"@babel/helper-plugin-utils" "^7.0.0"
- "@babel/helper-replace-supers" "^7.3.4"
- "@babel/helper-split-export-declaration" "^7.0.0"
+ "@babel/helper-replace-supers" "^7.4.4"
+ "@babel/helper-split-export-declaration" "^7.4.4"
-"@babel/helper-define-map@^7.1.0":
- version "7.1.0"
- resolved "https://registry.yarnpkg.com/@babel/helper-define-map/-/helper-define-map-7.1.0.tgz#3b74caec329b3c80c116290887c0dd9ae468c20c"
- integrity sha512-yPPcW8dc3gZLN+U1mhYV91QU3n5uTbx7DUdf8NnPbjS0RMwBuHi9Xt2MUgppmNz7CJxTBWsGczTiEp1CSOTPRg==
+"@babel/helper-define-map@^7.4.4":
+ version "7.4.4"
+ resolved "https://registry.yarnpkg.com/@babel/helper-define-map/-/helper-define-map-7.4.4.tgz#6969d1f570b46bdc900d1eba8e5d59c48ba2c12a"
+ integrity sha512-IX3Ln8gLhZpSuqHJSnTNBWGDE9kdkTEWl21A/K7PQ00tseBwbqCHTvNLHSBd9M0R5rER4h5Rsvj9vw0R5SieBg==
dependencies:
"@babel/helper-function-name" "^7.1.0"
- "@babel/types" "^7.0.0"
- lodash "^4.17.10"
+ "@babel/types" "^7.4.4"
+ lodash "^4.17.11"
"@babel/helper-explode-assignable-expression@^7.1.0":
version "7.1.0"
@@ -109,12 +109,12 @@
dependencies:
"@babel/types" "^7.0.0"
-"@babel/helper-hoist-variables@^7.0.0":
- version "7.0.0"
- resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.0.0.tgz#46adc4c5e758645ae7a45deb92bab0918c23bb88"
- integrity sha512-Ggv5sldXUeSKsuzLkddtyhyHe2YantsxWKNi7A+7LeD12ExRDWTRk29JCXpaHPAbMaIPZSil7n+lq78WY2VY7w==
+"@babel/helper-hoist-variables@^7.4.4":
+ version "7.4.4"
+ resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.4.4.tgz#0298b5f25c8c09c53102d52ac4a98f773eb2850a"
+ integrity sha512-VYk2/H/BnYbZDDg39hr3t2kKyifAm1W6zHRfhx8jGjIHpQEBv9dry7oQ2f3+J703TLu69nYdxsovl0XYfcnK4w==
dependencies:
- "@babel/types" "^7.0.0"
+ "@babel/types" "^7.4.4"
"@babel/helper-member-expression-to-functions@^7.0.0":
version "7.0.0"
@@ -130,17 +130,17 @@
dependencies:
"@babel/types" "^7.0.0"
-"@babel/helper-module-transforms@^7.1.0":
- version "7.2.2"
- resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.2.2.tgz#ab2f8e8d231409f8370c883d20c335190284b963"
- integrity sha512-YRD7I6Wsv+IHuTPkAmAS4HhY0dkPobgLftHp0cRGZSdrRvmZY8rFvae/GVu3bD00qscuvK3WPHB3YdNpBXUqrA==
+"@babel/helper-module-transforms@^7.1.0", "@babel/helper-module-transforms@^7.4.4":
+ version "7.4.4"
+ resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.4.4.tgz#96115ea42a2f139e619e98ed46df6019b94414b8"
+ integrity sha512-3Z1yp8TVQf+B4ynN7WoHPKS8EkdTbgAEy0nU0rs/1Kw4pDgmvYH3rz3aI11KgxKCba2cn7N+tqzV1mY2HMN96w==
dependencies:
"@babel/helper-module-imports" "^7.0.0"
"@babel/helper-simple-access" "^7.1.0"
- "@babel/helper-split-export-declaration" "^7.0.0"
- "@babel/template" "^7.2.2"
- "@babel/types" "^7.2.2"
- lodash "^4.17.10"
+ "@babel/helper-split-export-declaration" "^7.4.4"
+ "@babel/template" "^7.4.4"
+ "@babel/types" "^7.4.4"
+ lodash "^4.17.11"
"@babel/helper-optimise-call-expression@^7.0.0":
version "7.0.0"
@@ -154,12 +154,12 @@
resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.0.0.tgz#bbb3fbee98661c569034237cc03967ba99b4f250"
integrity sha512-CYAOUCARwExnEixLdB6sDm2dIJ/YgEAKDM1MOeMeZu9Ld/bDgVo8aiWrXwcY7OBh+1Ea2uUcVRcxKk0GJvW7QA==
-"@babel/helper-regex@^7.0.0":
- version "7.0.0"
- resolved "https://registry.yarnpkg.com/@babel/helper-regex/-/helper-regex-7.0.0.tgz#2c1718923b57f9bbe64705ffe5640ac64d9bdb27"
- integrity sha512-TR0/N0NDCcUIUEbqV6dCO+LptmmSQFQ7q70lfcEB4URsjD0E1HzicrwUH+ap6BAQ2jhCX9Q4UqZy4wilujWlkg==
+"@babel/helper-regex@^7.0.0", "@babel/helper-regex@^7.4.4":
+ version "7.4.4"
+ resolved "https://registry.yarnpkg.com/@babel/helper-regex/-/helper-regex-7.4.4.tgz#a47e02bc91fb259d2e6727c2a30013e3ac13c4a2"
+ integrity sha512-Y5nuB/kESmR3tKjU8Nkn1wMGEx1tjJX076HBMeL3XLQCu6vA/YRzuTW0bbb+qRnXvQGn+d6Rx953yffl8vEy7Q==
dependencies:
- lodash "^4.17.10"
+ lodash "^4.17.11"
"@babel/helper-remap-async-to-generator@^7.1.0":
version "7.1.0"
@@ -172,15 +172,15 @@
"@babel/traverse" "^7.1.0"
"@babel/types" "^7.0.0"
-"@babel/helper-replace-supers@^7.1.0", "@babel/helper-replace-supers@^7.3.4":
- version "7.3.4"
- resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.3.4.tgz#a795208e9b911a6eeb08e5891faacf06e7013e13"
- integrity sha512-pvObL9WVf2ADs+ePg0jrqlhHoxRXlOa+SHRHzAXIz2xkYuOHfGl+fKxPMaS4Fq+uje8JQPobnertBBvyrWnQ1A==
+"@babel/helper-replace-supers@^7.1.0", "@babel/helper-replace-supers@^7.4.4":
+ version "7.4.4"
+ resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.4.4.tgz#aee41783ebe4f2d3ab3ae775e1cc6f1a90cefa27"
+ integrity sha512-04xGEnd+s01nY1l15EuMS1rfKktNF+1CkKmHoErDppjAAZL+IUBZpzT748x262HF7fibaQPhbvWUl5HeSt1EXg==
dependencies:
"@babel/helper-member-expression-to-functions" "^7.0.0"
"@babel/helper-optimise-call-expression" "^7.0.0"
- "@babel/traverse" "^7.3.4"
- "@babel/types" "^7.3.4"
+ "@babel/traverse" "^7.4.4"
+ "@babel/types" "^7.4.4"
"@babel/helper-simple-access@^7.1.0":
version "7.1.0"
@@ -190,12 +190,12 @@
"@babel/template" "^7.1.0"
"@babel/types" "^7.0.0"
-"@babel/helper-split-export-declaration@^7.0.0":
- version "7.0.0"
- resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.0.0.tgz#3aae285c0311c2ab095d997b8c9a94cad547d813"
- integrity sha512-MXkOJqva62dfC0w85mEf/LucPPS/1+04nmmRMPEBUB++hiiThQ2zPtX/mEWQ3mtzCEjIJvPY8nuwxXtQeQwUag==
+"@babel/helper-split-export-declaration@^7.4.4":
+ version "7.4.4"
+ resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.4.4.tgz#ff94894a340be78f53f06af038b205c49d993677"
+ integrity sha512-Ro/XkzLf3JFITkW6b+hNxzZ1n5OQ80NvIUdmHspih1XAhtN3vPTuUFT4eQnela+2MaZ5ulH+iyP513KJrxbN7Q==
dependencies:
- "@babel/types" "^7.0.0"
+ "@babel/types" "^7.4.4"
"@babel/helper-wrap-function@^7.1.0":
version "7.2.0"
@@ -207,14 +207,14 @@
"@babel/traverse" "^7.1.0"
"@babel/types" "^7.2.0"
-"@babel/helpers@^7.2.0":
- version "7.3.1"
- resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.3.1.tgz#949eec9ea4b45d3210feb7dc1c22db664c9e44b9"
- integrity sha512-Q82R3jKsVpUV99mgX50gOPCWwco9Ec5Iln/8Vyu4osNIOQgSrd9RFrQeUvmvddFNoLwMyOUWU+5ckioEKpDoGA==
+"@babel/helpers@^7.4.4":
+ version "7.4.4"
+ resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.4.4.tgz#868b0ef59c1dd4e78744562d5ce1b59c89f2f2a5"
+ integrity sha512-igczbR/0SeuPR8RFfC7tGrbdTbFL3QTvH6D+Z6zNxnTe//GyqmtHmDkzrqDmyZ3eSwPqB/LhyKoU5DXsp+Vp2A==
dependencies:
- "@babel/template" "^7.1.2"
- "@babel/traverse" "^7.1.5"
- "@babel/types" "^7.3.0"
+ "@babel/template" "^7.4.4"
+ "@babel/traverse" "^7.4.4"
+ "@babel/types" "^7.4.4"
"@babel/highlight@^7.0.0":
version "7.0.0"
@@ -225,10 +225,10 @@
esutils "^2.0.2"
js-tokens "^4.0.0"
-"@babel/parser@^7.2.2", "@babel/parser@^7.3.4":
- version "7.3.4"
- resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.3.4.tgz#a43357e4bbf4b92a437fb9e465c192848287f27c"
- integrity sha512-tXZCqWtlOOP4wgCp6RjRvLmfuhnqTLy9VHwRochJBCP2nDm27JnnuFEnXFASVyQNHk36jD1tAammsCEEqgscIQ==
+"@babel/parser@^7.4.4":
+ version "7.4.4"
+ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.4.4.tgz#5977129431b8fe33471730d255ce8654ae1250b6"
+ integrity sha512-5pCS4mOsL+ANsFZGdvNLybx4wtqAZJ0MJjMHxvzI3bvIsz6sQvzW8XX92EYIkiPtIvcfG3Aj+Ir5VNyjnZhP7w==
"@babel/plugin-proposal-async-generator-functions@^7.2.0":
version "7.2.0"
@@ -240,11 +240,11 @@
"@babel/plugin-syntax-async-generators" "^7.2.0"
"@babel/plugin-proposal-class-properties@^7.3.4":
- version "7.3.4"
- resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.3.4.tgz#410f5173b3dc45939f9ab30ca26684d72901405e"
- integrity sha512-lUf8D3HLs4yYlAo8zjuneLvfxN7qfKv1Yzbj5vjqaqMJxgJA3Ipwp4VUJ+OrOdz53Wbww6ahwB8UhB2HQyLotA==
+ version "7.4.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.4.4.tgz#93a6486eed86d53452ab9bab35e368e9461198ce"
+ integrity sha512-WjKTI8g8d5w1Bc9zgwSz2nfrsNQsXcCf9J9cdCvrJV6RF56yztwm4TmJC0MgJ9tvwO9gUA/mcYe89bLdGfiXFg==
dependencies:
- "@babel/helper-create-class-features-plugin" "^7.3.4"
+ "@babel/helper-create-class-features-plugin" "^7.4.4"
"@babel/helper-plugin-utils" "^7.0.0"
"@babel/plugin-proposal-json-strings@^7.2.0":
@@ -255,10 +255,10 @@
"@babel/helper-plugin-utils" "^7.0.0"
"@babel/plugin-syntax-json-strings" "^7.2.0"
-"@babel/plugin-proposal-object-rest-spread@^7.3.4":
- version "7.3.4"
- resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.3.4.tgz#47f73cf7f2a721aad5c0261205405c642e424654"
- integrity sha512-j7VQmbbkA+qrzNqbKHrBsW3ddFnOeva6wzSe/zB7T+xaxGc+RCpwo44wCmRixAIGRoIpmVgvzFzNJqQcO3/9RA==
+"@babel/plugin-proposal-object-rest-spread@^7.3.4", "@babel/plugin-proposal-object-rest-spread@^7.4.4":
+ version "7.4.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.4.4.tgz#1ef173fcf24b3e2df92a678f027673b55e7e3005"
+ integrity sha512-dMBG6cSPBbHeEBdFXeQ2QLc5gUpg4Vkaz8octD4aoW/ISO+jBOcsuxYL7bsb5WSu8RLP6boxrBIALEHgoHtO9g==
dependencies:
"@babel/helper-plugin-utils" "^7.0.0"
"@babel/plugin-syntax-object-rest-spread" "^7.2.0"
@@ -271,14 +271,14 @@
"@babel/helper-plugin-utils" "^7.0.0"
"@babel/plugin-syntax-optional-catch-binding" "^7.2.0"
-"@babel/plugin-proposal-unicode-property-regex@^7.2.0":
- version "7.2.0"
- resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.2.0.tgz#abe7281fe46c95ddc143a65e5358647792039520"
- integrity sha512-LvRVYb7kikuOtIoUeWTkOxQEV1kYvL5B6U3iWEGCzPNRus1MzJweFqORTj+0jkxozkTSYNJozPOddxmqdqsRpw==
+"@babel/plugin-proposal-unicode-property-regex@^7.4.4":
+ version "7.4.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.4.4.tgz#501ffd9826c0b91da22690720722ac7cb1ca9c78"
+ integrity sha512-j1NwnOqMG9mFUOH58JTFsA/+ZYzQLUZ/drqWUqxCYLGeu2JFZL8YrNC9hBxKmWtAuOCHPcRpgv7fhap09Fb4kA==
dependencies:
"@babel/helper-plugin-utils" "^7.0.0"
- "@babel/helper-regex" "^7.0.0"
- regexpu-core "^4.2.0"
+ "@babel/helper-regex" "^7.4.4"
+ regexpu-core "^4.5.4"
"@babel/plugin-syntax-async-generators@^7.2.0":
version "7.2.0"
@@ -322,10 +322,10 @@
dependencies:
"@babel/helper-plugin-utils" "^7.0.0"
-"@babel/plugin-transform-async-to-generator@^7.3.4":
- version "7.3.4"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.3.4.tgz#4e45408d3c3da231c0e7b823f407a53a7eb3048c"
- integrity sha512-Y7nCzv2fw/jEZ9f678MuKdMo99MFDJMT/PvD9LisrR5JDFcJH6vYeH6RnjVt3p5tceyGRvTtEN0VOlU+rgHZjA==
+"@babel/plugin-transform-async-to-generator@^7.4.4":
+ version "7.4.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.4.4.tgz#a3f1d01f2f21cadab20b33a82133116f14fb5894"
+ integrity sha512-YiqW2Li8TXmzgbXw+STsSqPBPFnGviiaSp6CYOq55X8GQ2SGVLrXB6pNid8HkqkZAzOH6knbai3snhP7v0fNwA==
dependencies:
"@babel/helper-module-imports" "^7.0.0"
"@babel/helper-plugin-utils" "^7.0.0"
@@ -338,26 +338,26 @@
dependencies:
"@babel/helper-plugin-utils" "^7.0.0"
-"@babel/plugin-transform-block-scoping@^7.3.4":
- version "7.3.4"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.3.4.tgz#5c22c339de234076eee96c8783b2fed61202c5c4"
- integrity sha512-blRr2O8IOZLAOJklXLV4WhcEzpYafYQKSGT3+R26lWG41u/FODJuBggehtOwilVAcFu393v3OFj+HmaE6tVjhA==
+"@babel/plugin-transform-block-scoping@^7.4.4":
+ version "7.4.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.4.4.tgz#c13279fabf6b916661531841a23c4b7dae29646d"
+ integrity sha512-jkTUyWZcTrwxu5DD4rWz6rDB5Cjdmgz6z7M7RLXOJyCUkFBawssDGcGh8M/0FTSB87avyJI1HsTwUXp9nKA1PA==
dependencies:
"@babel/helper-plugin-utils" "^7.0.0"
lodash "^4.17.11"
-"@babel/plugin-transform-classes@^7.3.4":
- version "7.3.4"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.3.4.tgz#dc173cb999c6c5297e0b5f2277fdaaec3739d0cc"
- integrity sha512-J9fAvCFBkXEvBimgYxCjvaVDzL6thk0j0dBvCeZmIUDBwyt+nv6HfbImsSrWsYXfDNDivyANgJlFXDUWRTZBuA==
+"@babel/plugin-transform-classes@^7.4.4":
+ version "7.4.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.4.4.tgz#0ce4094cdafd709721076d3b9c38ad31ca715eb6"
+ integrity sha512-/e44eFLImEGIpL9qPxSRat13I5QNRgBLu2hOQJCF7VLy/otSM/sypV1+XaIw5+502RX/+6YaSAPmldk+nhHDPw==
dependencies:
"@babel/helper-annotate-as-pure" "^7.0.0"
- "@babel/helper-define-map" "^7.1.0"
+ "@babel/helper-define-map" "^7.4.4"
"@babel/helper-function-name" "^7.1.0"
"@babel/helper-optimise-call-expression" "^7.0.0"
"@babel/helper-plugin-utils" "^7.0.0"
- "@babel/helper-replace-supers" "^7.3.4"
- "@babel/helper-split-export-declaration" "^7.0.0"
+ "@babel/helper-replace-supers" "^7.4.4"
+ "@babel/helper-split-export-declaration" "^7.4.4"
globals "^11.1.0"
"@babel/plugin-transform-computed-properties@^7.2.0":
@@ -367,21 +367,21 @@
dependencies:
"@babel/helper-plugin-utils" "^7.0.0"
-"@babel/plugin-transform-destructuring@^7.2.0", "@babel/plugin-transform-destructuring@^7.3.2":
- version "7.3.2"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.3.2.tgz#f2f5520be055ba1c38c41c0e094d8a461dd78f2d"
- integrity sha512-Lrj/u53Ufqxl/sGxyjsJ2XNtNuEjDyjpqdhMNh5aZ+XFOdThL46KBj27Uem4ggoezSYBxKWAil6Hu8HtwqesYw==
+"@babel/plugin-transform-destructuring@^7.3.2", "@babel/plugin-transform-destructuring@^7.4.4":
+ version "7.4.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.4.4.tgz#9d964717829cc9e4b601fc82a26a71a4d8faf20f"
+ integrity sha512-/aOx+nW0w8eHiEHm+BTERB2oJn5D127iye/SUQl7NjHy0lf+j7h4MKMMSOwdazGq9OxgiNADncE+SRJkCxjZpQ==
dependencies:
"@babel/helper-plugin-utils" "^7.0.0"
-"@babel/plugin-transform-dotall-regex@^7.2.0":
- version "7.2.0"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.2.0.tgz#f0aabb93d120a8ac61e925ea0ba440812dbe0e49"
- integrity sha512-sKxnyHfizweTgKZf7XsXu/CNupKhzijptfTM+bozonIuyVrLWVUvYjE2bhuSBML8VQeMxq4Mm63Q9qvcvUcciQ==
+"@babel/plugin-transform-dotall-regex@^7.4.4":
+ version "7.4.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.4.4.tgz#361a148bc951444312c69446d76ed1ea8e4450c3"
+ integrity sha512-P05YEhRc2h53lZDjRPk/OektxCVevFzZs2Gfjd545Wde3k+yFDbXORgl2e0xpbq8mLcKJ7Idss4fAg0zORN/zg==
dependencies:
"@babel/helper-plugin-utils" "^7.0.0"
- "@babel/helper-regex" "^7.0.0"
- regexpu-core "^4.1.3"
+ "@babel/helper-regex" "^7.4.4"
+ regexpu-core "^4.5.4"
"@babel/plugin-transform-duplicate-keys@^7.2.0":
version "7.2.0"
@@ -398,17 +398,17 @@
"@babel/helper-builder-binary-assignment-operator-visitor" "^7.1.0"
"@babel/helper-plugin-utils" "^7.0.0"
-"@babel/plugin-transform-for-of@^7.2.0":
- version "7.2.0"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.2.0.tgz#ab7468befa80f764bb03d3cb5eef8cc998e1cad9"
- integrity sha512-Kz7Mt0SsV2tQk6jG5bBv5phVbkd0gd27SgYD4hH1aLMJRchM0dzHaXvrWhVZ+WxAlDoAKZ7Uy3jVTW2mKXQ1WQ==
+"@babel/plugin-transform-for-of@^7.4.4":
+ version "7.4.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.4.4.tgz#0267fc735e24c808ba173866c6c4d1440fc3c556"
+ integrity sha512-9T/5Dlr14Z9TIEXLXkt8T1DU7F24cbhwhMNUziN3hB1AXoZcdzPcTiKGRn/6iOymDqtTKWnr/BtRKN9JwbKtdQ==
dependencies:
"@babel/helper-plugin-utils" "^7.0.0"
-"@babel/plugin-transform-function-name@^7.2.0":
- version "7.2.0"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.2.0.tgz#f7930362829ff99a3174c39f0afcc024ef59731a"
- integrity sha512-kWgksow9lHdvBC2Z4mxTsvc7YdY7w/V6B2vy9cTIPtLEE9NhwoWivaxdNM/S37elu5bqlLP/qOY906LukO9lkQ==
+"@babel/plugin-transform-function-name@^7.4.4":
+ version "7.4.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.4.4.tgz#e1436116abb0610c2259094848754ac5230922ad"
+ integrity sha512-iU9pv7U+2jC9ANQkKeNF6DrPy4GBa4NWQtl6dHB4Pb3izX2JOEvDTFarlNsBj/63ZEzNNIAMs3Qw4fNCcSOXJA==
dependencies:
"@babel/helper-function-name" "^7.1.0"
"@babel/helper-plugin-utils" "^7.0.0"
@@ -420,6 +420,13 @@
dependencies:
"@babel/helper-plugin-utils" "^7.0.0"
+"@babel/plugin-transform-member-expression-literals@^7.2.0":
+ version "7.2.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.2.0.tgz#fa10aa5c58a2cb6afcf2c9ffa8cb4d8b3d489a2d"
+ integrity sha512-HiU3zKkSU6scTidmnFJ0bMX8hz5ixC93b4MHMiYebmk2lUVNGOboPsqQvx5LzooihijUoLR/v7Nc1rbBtnc7FA==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.0.0"
+
"@babel/plugin-transform-modules-amd@^7.2.0":
version "7.2.0"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.2.0.tgz#82a9bce45b95441f617a24011dc89d12da7f4ee6"
@@ -428,21 +435,21 @@
"@babel/helper-module-transforms" "^7.1.0"
"@babel/helper-plugin-utils" "^7.0.0"
-"@babel/plugin-transform-modules-commonjs@^7.2.0":
- version "7.2.0"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.2.0.tgz#c4f1933f5991d5145e9cfad1dfd848ea1727f404"
- integrity sha512-V6y0uaUQrQPXUrmj+hgnks8va2L0zcZymeU7TtWEgdRLNkceafKXEduv7QzgQAE4lT+suwooG9dC7LFhdRAbVQ==
+"@babel/plugin-transform-modules-commonjs@^7.4.4":
+ version "7.4.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.4.4.tgz#0bef4713d30f1d78c2e59b3d6db40e60192cac1e"
+ integrity sha512-4sfBOJt58sEo9a2BQXnZq+Q3ZTSAUXyK3E30o36BOGnJ+tvJ6YSxF0PG6kERvbeISgProodWuI9UVG3/FMY6iw==
dependencies:
- "@babel/helper-module-transforms" "^7.1.0"
+ "@babel/helper-module-transforms" "^7.4.4"
"@babel/helper-plugin-utils" "^7.0.0"
"@babel/helper-simple-access" "^7.1.0"
-"@babel/plugin-transform-modules-systemjs@^7.3.4":
- version "7.3.4"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.3.4.tgz#813b34cd9acb6ba70a84939f3680be0eb2e58861"
- integrity sha512-VZ4+jlGOF36S7TjKs8g4ojp4MEI+ebCQZdswWb/T9I4X84j8OtFAyjXjt/M16iIm5RIZn0UMQgg/VgIwo/87vw==
+"@babel/plugin-transform-modules-systemjs@^7.4.4":
+ version "7.4.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.4.4.tgz#dc83c5665b07d6c2a7b224c00ac63659ea36a405"
+ integrity sha512-MSiModfILQc3/oqnG7NrP1jHaSPryO6tA2kOMmAQApz5dayPxWiHqmq4sWH2xF5LcQK56LlbKByCd8Aah/OIkQ==
dependencies:
- "@babel/helper-hoist-variables" "^7.0.0"
+ "@babel/helper-hoist-variables" "^7.4.4"
"@babel/helper-plugin-utils" "^7.0.0"
"@babel/plugin-transform-modules-umd@^7.2.0":
@@ -453,17 +460,17 @@
"@babel/helper-module-transforms" "^7.1.0"
"@babel/helper-plugin-utils" "^7.0.0"
-"@babel/plugin-transform-named-capturing-groups-regex@^7.3.0":
- version "7.3.0"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.3.0.tgz#140b52985b2d6ef0cb092ef3b29502b990f9cd50"
- integrity sha512-NxIoNVhk9ZxS+9lSoAQ/LM0V2UEvARLttEHUrRDGKFaAxOYQcrkN/nLRE+BbbicCAvZPl7wMP0X60HsHE5DtQw==
+"@babel/plugin-transform-named-capturing-groups-regex@^7.4.4":
+ version "7.4.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.4.4.tgz#5611d96d987dfc4a3a81c4383bb173361037d68d"
+ integrity sha512-Ki+Y9nXBlKfhD+LXaRS7v95TtTGYRAf9Y1rTDiE75zf8YQz4GDaWRXosMfJBXxnk88mGFjWdCRIeqDbon7spYA==
dependencies:
regexp-tree "^0.1.0"
-"@babel/plugin-transform-new-target@^7.0.0":
- version "7.0.0"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.0.0.tgz#ae8fbd89517fa7892d20e6564e641e8770c3aa4a"
- integrity sha512-yin069FYjah+LbqfGeTfzIBODex/e++Yfa0rH0fpfam9uTbuEeEOx5GLGr210ggOV77mVRNoeqSYqeuaqSzVSw==
+"@babel/plugin-transform-new-target@^7.4.4":
+ version "7.4.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.4.4.tgz#18d120438b0cc9ee95a47f2c72bc9768fbed60a5"
+ integrity sha512-r1z3T2DNGQwwe2vPGZMBNjioT2scgWzK9BCnDEh+46z8EEwXBq24uRzd65I7pjtugzPSj921aM15RpESgzsSuA==
dependencies:
"@babel/helper-plugin-utils" "^7.0.0"
@@ -475,26 +482,40 @@
"@babel/helper-plugin-utils" "^7.0.0"
"@babel/helper-replace-supers" "^7.1.0"
-"@babel/plugin-transform-parameters@^7.2.0":
- version "7.3.3"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.3.3.tgz#3a873e07114e1a5bee17d04815662c8317f10e30"
- integrity sha512-IrIP25VvXWu/VlBWTpsjGptpomtIkYrN/3aDp4UKm7xK6UxZY88kcJ1UwETbzHAlwN21MnNfwlar0u8y3KpiXw==
+"@babel/plugin-transform-parameters@^7.4.4":
+ version "7.4.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.4.4.tgz#7556cf03f318bd2719fe4c922d2d808be5571e16"
+ integrity sha512-oMh5DUO1V63nZcu/ZVLQFqiihBGo4OpxJxR1otF50GMeCLiRx5nUdtokd+u9SuVJrvvuIh9OosRFPP4pIPnwmw==
dependencies:
- "@babel/helper-call-delegate" "^7.1.0"
+ "@babel/helper-call-delegate" "^7.4.4"
"@babel/helper-get-function-arity" "^7.0.0"
"@babel/helper-plugin-utils" "^7.0.0"
-"@babel/plugin-transform-regenerator@^7.3.4":
- version "7.3.4"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.3.4.tgz#1601655c362f5b38eead6a52631f5106b29fa46a"
- integrity sha512-hvJg8EReQvXT6G9H2MvNPXkv9zK36Vxa1+csAVTpE1J3j0zlHplw76uudEbJxgvqZzAq9Yh45FLD4pk5mKRFQA==
+"@babel/plugin-transform-property-literals@^7.2.0":
+ version "7.2.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.2.0.tgz#03e33f653f5b25c4eb572c98b9485055b389e905"
+ integrity sha512-9q7Dbk4RhgcLp8ebduOpCbtjh7C0itoLYHXd9ueASKAG/is5PQtMR5VJGka9NKqGhYEGn5ITahd4h9QeBMylWQ==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.0.0"
+
+"@babel/plugin-transform-regenerator@^7.3.4", "@babel/plugin-transform-regenerator@^7.4.4":
+ version "7.4.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.4.4.tgz#5b4da4df79391895fca9e28f99e87e22cfc02072"
+ integrity sha512-Zz3w+pX1SI0KMIiqshFZkwnVGUhDZzpX2vtPzfJBKQQq8WsP/Xy9DNdELWivxcKOCX/Pywge4SiEaPaLtoDT4g==
dependencies:
regenerator-transform "^0.13.4"
+"@babel/plugin-transform-reserved-words@^7.2.0":
+ version "7.2.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.2.0.tgz#4792af87c998a49367597d07fedf02636d2e1634"
+ integrity sha512-fz43fqW8E1tAB3DKF19/vxbpib1fuyCwSPE418ge5ZxILnBhWyhtPgz8eh1RCGGJlwvksHkyxMxh0eenFi+kFw==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.0.0"
+
"@babel/plugin-transform-runtime@^7.3.4":
- version "7.3.4"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.3.4.tgz#57805ac8c1798d102ecd75c03b024a5b3ea9b431"
- integrity sha512-PaoARuztAdd5MgeVjAxnIDAIUet5KpogqaefQvPOmPYCxYoaPhautxDh3aO8a4xHsKgT/b9gSxR0BKK1MIewPA==
+ version "7.4.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.4.4.tgz#a50f5d16e9c3a4ac18a1a9f9803c107c380bce08"
+ integrity sha512-aMVojEjPszvau3NRg+TIH14ynZLvPewH4xhlCW1w6A3rkxTS1m4uwzRclYR9oS+rl/dr+kT+pzbfHuAWP/lc7Q==
dependencies:
"@babel/helper-module-imports" "^7.0.0"
"@babel/helper-plugin-utils" "^7.0.0"
@@ -523,10 +544,10 @@
"@babel/helper-plugin-utils" "^7.0.0"
"@babel/helper-regex" "^7.0.0"
-"@babel/plugin-transform-template-literals@^7.2.0":
- version "7.2.0"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.2.0.tgz#d87ed01b8eaac7a92473f608c97c089de2ba1e5b"
- integrity sha512-FkPix00J9A/XWXv4VoKJBMeSkyY9x/TqIh76wzcdfl57RJJcf8CehQ08uwfhCDNtRQYtHQKBTwKZDEyjE13Lwg==
+"@babel/plugin-transform-template-literals@^7.4.4":
+ version "7.4.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.4.4.tgz#9d28fea7bbce637fb7612a0750989d8321d4bcb0"
+ integrity sha512-mQrEC4TWkhLN0z8ygIvEL9ZEToPhG5K7KDW3pzGqOfIGZ28Jb0POUkeWcoz8HnHvhFy6dwAT1j8OzqN8s804+g==
dependencies:
"@babel/helper-annotate-as-pure" "^7.0.0"
"@babel/helper-plugin-utils" "^7.0.0"
@@ -538,107 +559,112 @@
dependencies:
"@babel/helper-plugin-utils" "^7.0.0"
-"@babel/plugin-transform-unicode-regex@^7.2.0":
- version "7.2.0"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.2.0.tgz#4eb8db16f972f8abb5062c161b8b115546ade08b"
- integrity sha512-m48Y0lMhrbXEJnVUaYly29jRXbQ3ksxPrS1Tg8t+MHqzXhtBYAvI51euOBaoAlZLPHsieY9XPVMf80a5x0cPcA==
+"@babel/plugin-transform-unicode-regex@^7.4.4":
+ version "7.4.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.4.4.tgz#ab4634bb4f14d36728bf5978322b35587787970f"
+ integrity sha512-il+/XdNw01i93+M9J9u4T7/e/Ue/vWfNZE4IRUQjplu2Mqb/AFTDimkw2tdEdSH50wuQXZAbXSql0UphQke+vA==
dependencies:
"@babel/helper-plugin-utils" "^7.0.0"
- "@babel/helper-regex" "^7.0.0"
- regexpu-core "^4.1.3"
+ "@babel/helper-regex" "^7.4.4"
+ regexpu-core "^4.5.4"
"@babel/polyfill@^7.2.5":
- version "7.2.5"
- resolved "https://registry.yarnpkg.com/@babel/polyfill/-/polyfill-7.2.5.tgz#6c54b964f71ad27edddc567d065e57e87ed7fa7d"
- integrity sha512-8Y/t3MWThtMLYr0YNC/Q76tqN1w30+b0uQMeFUYauG2UGTR19zyUtFrAzT23zNtBxPp+LbE5E/nwV/q/r3y6ug==
+ version "7.4.4"
+ resolved "https://registry.yarnpkg.com/@babel/polyfill/-/polyfill-7.4.4.tgz#78801cf3dbe657844eeabf31c1cae3828051e893"
+ integrity sha512-WlthFLfhQQhh+A2Gn5NSFl0Huxz36x86Jn+E9OW7ibK8edKPq+KLy4apM1yDpQ8kJOVi1OVjpP4vSDLdrI04dg==
dependencies:
- core-js "^2.5.7"
- regenerator-runtime "^0.12.0"
+ core-js "^2.6.5"
+ regenerator-runtime "^0.13.2"
"@babel/preset-env@^7.3.4":
- version "7.3.4"
- resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.3.4.tgz#887cf38b6d23c82f19b5135298bdb160062e33e1"
- integrity sha512-2mwqfYMK8weA0g0uBKOt4FE3iEodiHy9/CW0b+nWXcbL+pGzLx8ESYc+j9IIxr6LTDHWKgPm71i9smo02bw+gA==
+ version "7.4.4"
+ resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.4.4.tgz#b6f6825bfb27b3e1394ca3de4f926482722c1d6f"
+ integrity sha512-FU1H+ACWqZZqfw1x2G1tgtSSYSfxJLkpaUQL37CenULFARDo+h4xJoVHzRoHbK+85ViLciuI7ME4WTIhFRBBlw==
dependencies:
"@babel/helper-module-imports" "^7.0.0"
"@babel/helper-plugin-utils" "^7.0.0"
"@babel/plugin-proposal-async-generator-functions" "^7.2.0"
"@babel/plugin-proposal-json-strings" "^7.2.0"
- "@babel/plugin-proposal-object-rest-spread" "^7.3.4"
+ "@babel/plugin-proposal-object-rest-spread" "^7.4.4"
"@babel/plugin-proposal-optional-catch-binding" "^7.2.0"
- "@babel/plugin-proposal-unicode-property-regex" "^7.2.0"
+ "@babel/plugin-proposal-unicode-property-regex" "^7.4.4"
"@babel/plugin-syntax-async-generators" "^7.2.0"
"@babel/plugin-syntax-json-strings" "^7.2.0"
"@babel/plugin-syntax-object-rest-spread" "^7.2.0"
"@babel/plugin-syntax-optional-catch-binding" "^7.2.0"
"@babel/plugin-transform-arrow-functions" "^7.2.0"
- "@babel/plugin-transform-async-to-generator" "^7.3.4"
+ "@babel/plugin-transform-async-to-generator" "^7.4.4"
"@babel/plugin-transform-block-scoped-functions" "^7.2.0"
- "@babel/plugin-transform-block-scoping" "^7.3.4"
- "@babel/plugin-transform-classes" "^7.3.4"
+ "@babel/plugin-transform-block-scoping" "^7.4.4"
+ "@babel/plugin-transform-classes" "^7.4.4"
"@babel/plugin-transform-computed-properties" "^7.2.0"
- "@babel/plugin-transform-destructuring" "^7.2.0"
- "@babel/plugin-transform-dotall-regex" "^7.2.0"
+ "@babel/plugin-transform-destructuring" "^7.4.4"
+ "@babel/plugin-transform-dotall-regex" "^7.4.4"
"@babel/plugin-transform-duplicate-keys" "^7.2.0"
"@babel/plugin-transform-exponentiation-operator" "^7.2.0"
- "@babel/plugin-transform-for-of" "^7.2.0"
- "@babel/plugin-transform-function-name" "^7.2.0"
+ "@babel/plugin-transform-for-of" "^7.4.4"
+ "@babel/plugin-transform-function-name" "^7.4.4"
"@babel/plugin-transform-literals" "^7.2.0"
+ "@babel/plugin-transform-member-expression-literals" "^7.2.0"
"@babel/plugin-transform-modules-amd" "^7.2.0"
- "@babel/plugin-transform-modules-commonjs" "^7.2.0"
- "@babel/plugin-transform-modules-systemjs" "^7.3.4"
+ "@babel/plugin-transform-modules-commonjs" "^7.4.4"
+ "@babel/plugin-transform-modules-systemjs" "^7.4.4"
"@babel/plugin-transform-modules-umd" "^7.2.0"
- "@babel/plugin-transform-named-capturing-groups-regex" "^7.3.0"
- "@babel/plugin-transform-new-target" "^7.0.0"
+ "@babel/plugin-transform-named-capturing-groups-regex" "^7.4.4"
+ "@babel/plugin-transform-new-target" "^7.4.4"
"@babel/plugin-transform-object-super" "^7.2.0"
- "@babel/plugin-transform-parameters" "^7.2.0"
- "@babel/plugin-transform-regenerator" "^7.3.4"
+ "@babel/plugin-transform-parameters" "^7.4.4"
+ "@babel/plugin-transform-property-literals" "^7.2.0"
+ "@babel/plugin-transform-regenerator" "^7.4.4"
+ "@babel/plugin-transform-reserved-words" "^7.2.0"
"@babel/plugin-transform-shorthand-properties" "^7.2.0"
"@babel/plugin-transform-spread" "^7.2.0"
"@babel/plugin-transform-sticky-regex" "^7.2.0"
- "@babel/plugin-transform-template-literals" "^7.2.0"
+ "@babel/plugin-transform-template-literals" "^7.4.4"
"@babel/plugin-transform-typeof-symbol" "^7.2.0"
- "@babel/plugin-transform-unicode-regex" "^7.2.0"
- browserslist "^4.3.4"
+ "@babel/plugin-transform-unicode-regex" "^7.4.4"
+ "@babel/types" "^7.4.4"
+ browserslist "^4.5.2"
+ core-js-compat "^3.0.0"
invariant "^2.2.2"
js-levenshtein "^1.1.3"
- semver "^5.3.0"
+ semver "^5.5.0"
-"@babel/runtime@^7.3.4":
- version "7.3.4"
- resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.3.4.tgz#73d12ba819e365fcf7fd152aed56d6df97d21c83"
- integrity sha512-IvfvnMdSaLBateu0jfsYIpZTxAc2cKEXEMiezGGN75QcBcecDUKd3PgLAncT0oOgxKy8dd8hrJKj9MfzgfZd6g==
+"@babel/runtime@^7.3.4", "@babel/runtime@^7.4.2":
+ version "7.4.4"
+ resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.4.4.tgz#dc2e34982eb236803aa27a07fea6857af1b9171d"
+ integrity sha512-w0+uT71b6Yi7i5SE0co4NioIpSYS6lLiXvCzWzGSKvpK5vdQtCbICHMj+gbAKAOtxiV6HsVh/MBdaF9EQ6faSg==
dependencies:
- regenerator-runtime "^0.12.0"
+ regenerator-runtime "^0.13.2"
-"@babel/template@^7.1.0", "@babel/template@^7.1.2", "@babel/template@^7.2.2":
- version "7.2.2"
- resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.2.2.tgz#005b3fdf0ed96e88041330379e0da9a708eb2907"
- integrity sha512-zRL0IMM02AUDwghf5LMSSDEz7sBCO2YnNmpg3uWTZj/v1rcG2BmQUvaGU8GhU8BvfMh1k2KIAYZ7Ji9KXPUg7g==
+"@babel/template@^7.1.0", "@babel/template@^7.4.4":
+ version "7.4.4"
+ resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.4.4.tgz#f4b88d1225689a08f5bc3a17483545be9e4ed237"
+ integrity sha512-CiGzLN9KgAvgZsnivND7rkA+AeJ9JB0ciPOD4U59GKbQP2iQl+olF1l76kJOupqidozfZ32ghwBEJDhnk9MEcw==
dependencies:
"@babel/code-frame" "^7.0.0"
- "@babel/parser" "^7.2.2"
- "@babel/types" "^7.2.2"
+ "@babel/parser" "^7.4.4"
+ "@babel/types" "^7.4.4"
-"@babel/traverse@^7.1.0", "@babel/traverse@^7.1.5", "@babel/traverse@^7.3.4":
- version "7.3.4"
- resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.3.4.tgz#1330aab72234f8dea091b08c4f8b9d05c7119e06"
- integrity sha512-TvTHKp6471OYEcE/91uWmhR6PrrYywQntCHSaZ8CM8Vmp+pjAusal4nGB2WCCQd0rvI7nOMKn9GnbcvTUz3/ZQ==
+"@babel/traverse@^7.1.0", "@babel/traverse@^7.4.4":
+ version "7.4.4"
+ resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.4.4.tgz#0776f038f6d78361860b6823887d4f3937133fe8"
+ integrity sha512-Gw6qqkw/e6AGzlyj9KnkabJX7VcubqPtkUQVAwkc0wUMldr3A/hezNB3Rc5eIvId95iSGkGIOe5hh1kMKf951A==
dependencies:
"@babel/code-frame" "^7.0.0"
- "@babel/generator" "^7.3.4"
+ "@babel/generator" "^7.4.4"
"@babel/helper-function-name" "^7.1.0"
- "@babel/helper-split-export-declaration" "^7.0.0"
- "@babel/parser" "^7.3.4"
- "@babel/types" "^7.3.4"
+ "@babel/helper-split-export-declaration" "^7.4.4"
+ "@babel/parser" "^7.4.4"
+ "@babel/types" "^7.4.4"
debug "^4.1.0"
globals "^11.1.0"
lodash "^4.17.11"
-"@babel/types@^7.0.0", "@babel/types@^7.2.0", "@babel/types@^7.2.2", "@babel/types@^7.3.0", "@babel/types@^7.3.4":
- version "7.3.4"
- resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.3.4.tgz#bf482eaeaffb367a28abbf9357a94963235d90ed"
- integrity sha512-WEkp8MsLftM7O/ty580wAmZzN1nDmCACc5+jFzUt+GUFNNIi3LdRlueYz0YIlmJhlZx1QYDMZL5vdWCL0fNjFQ==
+"@babel/types@^7.0.0", "@babel/types@^7.2.0", "@babel/types@^7.4.4":
+ version "7.4.4"
+ resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.4.4.tgz#8db9e9a629bb7c29370009b4b779ed93fe57d5f0"
+ integrity sha512-dOllgYdnEFOebhkKCjzSVFqw/PmmB8pH6RGOWkY4GsboQNd47b1fBThBSwlHAq9alF9vc1M3+6oqR47R50L0tQ==
dependencies:
esutils "^2.0.2"
lodash "^4.17.11"
@@ -699,10 +725,34 @@
webpack-cli "^3.2.3"
webpack-sources "^1.3.0"
+"@types/events@*":
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/@types/events/-/events-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7"
+ integrity sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==
+
+"@types/glob@^7.1.1":
+ version "7.1.1"
+ resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.1.tgz#aa59a1c6e3fbc421e07ccd31a944c30eba521575"
+ integrity sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w==
+ dependencies:
+ "@types/events" "*"
+ "@types/minimatch" "*"
+ "@types/node" "*"
+
+"@types/minimatch@*":
+ version "3.0.3"
+ resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d"
+ integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==
+
+"@types/node@*":
+ version "12.0.0"
+ resolved "https://registry.yarnpkg.com/@types/node/-/node-12.0.0.tgz#d11813b9c0ff8aaca29f04cbc12817f4c7d656e5"
+ integrity sha512-Jrb/x3HT4PTJp6a4avhmJCDEVrPdqLfl3e8GGMbpkGGdwAV5UGlIs4vVEfsHHfylZVOKZWpOqmqFH8CbfOZ6kg==
+
"@types/q@^1.5.1":
- version "1.5.1"
- resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.1.tgz#48fd98c1561fe718b61733daed46ff115b496e18"
- integrity sha512-eqz8c/0kwNi/OEHQfvIuJVLTst3in0e7uTKeuY+WL/zfKn0xVujOTp42bS/vUUokhK5P2BppLd9JXMOMHcgbjA==
+ version "1.5.2"
+ resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.2.tgz#690a1475b84f2a884fd07cd797c00f5f31356ea8"
+ integrity sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw==
"@webassemblyjs/ast@1.8.5":
version "1.8.5"
@@ -865,13 +915,13 @@ abbrev@1:
resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8"
integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==
-accepts@~1.3.4:
- version "1.3.4"
- resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.4.tgz#86246758c7dd6d21a6474ff084a4740ec05eb21f"
- integrity sha1-hiRnWMfdbSGmR0/whKR0DsBesh8=
+accepts@~1.3.4, accepts@~1.3.5:
+ version "1.3.7"
+ resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd"
+ integrity sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==
dependencies:
- mime-types "~2.1.16"
- negotiator "0.6.1"
+ mime-types "~2.1.24"
+ negotiator "0.6.2"
acorn-dynamic-import@^4.0.0:
version "4.0.0"
@@ -884,7 +934,7 @@ acorn@^6.0.5:
integrity sha512-jPTiwtOxaHNaAPg/dmrJ/beuzLRnXtB0kQPQ8JpotKJgTB6rX6c8mlf315941pyjBSaPg8NHXS9fhP4u17DpGA==
"actiontext@file:../..":
- version "6.0.0-beta2"
+ version "6.1.0-alpha"
dependencies:
"@rails/activestorage" "^6.0.0-alpha"
@@ -894,28 +944,11 @@ ajv-errors@^1.0.0:
integrity sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==
ajv-keywords@^3.1.0:
- version "3.1.0"
- resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.1.0.tgz#ac2b27939c543e95d2c06e7f7f5c27be4aa543be"
- integrity sha1-rCsnk5xUPpXSwG5/f1wnvkqlQ74=
-
-ajv@^4.9.1:
- version "4.11.8"
- resolved "https://registry.yarnpkg.com/ajv/-/ajv-4.11.8.tgz#82ffb02b29e662ae53bdc20af15947706739c536"
- integrity sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=
- dependencies:
- co "^4.6.0"
- json-stable-stringify "^1.0.1"
+ version "3.4.0"
+ resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.4.0.tgz#4b831e7b531415a7cc518cd404e73f6193c6349d"
+ integrity sha512-aUjdRFISbuFOl0EIZc+9e4FfZp0bDZgAdOOf30bJmw8VM9v84SHyVyxDfbWxpGYbdZD/9XoKxfHVNmxPkhwyGw==
-ajv@^6.1.0:
- version "6.1.1"
- resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.1.1.tgz#978d597fbc2b7d0e5a5c3ddeb149a682f2abfa0e"
- integrity sha1-l41Zf7wrfQ5aXD3esUmmgvKr+g4=
- dependencies:
- fast-deep-equal "^1.0.0"
- fast-json-stable-stringify "^2.0.0"
- json-schema-traverse "^0.3.0"
-
-ajv@^6.5.5:
+ajv@^6.1.0, ajv@^6.5.5:
version "6.10.0"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.10.0.tgz#90d0d54439da587cd7e843bfb7045f50bd22bdf1"
integrity sha512-nffhOpkymDECQyR0mnsUtoCE8RlX38G0rYP+wgLWFyZuUyuuojSSvi/+euOiQBIn63whYwYVIIH1TvE3tu4OEg==
@@ -981,17 +1014,17 @@ aproba@^1.0.3, aproba@^1.1.1:
integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==
are-we-there-yet@~1.1.2:
- version "1.1.4"
- resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz#bb5dca382bb94f05e15194373d16fd3ba1ca110d"
- integrity sha1-u13KOCu5TwXhUZQ3PRb9O6HKEQ0=
+ version "1.1.5"
+ resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21"
+ integrity sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==
dependencies:
delegates "^1.0.0"
readable-stream "^2.0.6"
argparse@^1.0.7:
- version "1.0.9"
- resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.9.tgz#73d83bc263f86e97f8cc4f6bae1b0e90a7d22c86"
- integrity sha1-c9g7wmP4bpf4zE9rrhsOkKfSLIY=
+ version "1.0.10"
+ resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911"
+ integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==
dependencies:
sprintf-js "~1.0.2"
@@ -1021,9 +1054,9 @@ array-flatten@1.1.1:
integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=
array-flatten@^2.1.0:
- version "2.1.1"
- resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-2.1.1.tgz#426bb9da84090c1838d812c8150af20a8331e296"
- integrity sha1-Qmu52oQJDBg42BLIFQryCoMx4pY=
+ version "2.1.2"
+ resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-2.1.2.tgz#24ef80a28c1a893617e2149b0c6d0d788293b099"
+ integrity sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==
array-union@^1.0.1:
version "1.0.2"
@@ -1043,34 +1076,32 @@ array-unique@^0.3.2:
integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=
asn1.js@^4.0.0:
- version "4.9.2"
- resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.9.2.tgz#8117ef4f7ed87cd8f89044b5bff97ac243a16c9a"
- integrity sha512-b/OsSjvWEo8Pi8H0zsDd2P6Uqo2TK2pH8gNLSJtNLM2Db0v2QaAZ0pBQJXVjAn4gBuugeVDr7s63ZogpUIwWDg==
+ version "4.10.1"
+ resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.10.1.tgz#b9c2bf5805f1e64aadeed6df3a2bfafb5a73f5a0"
+ integrity sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==
dependencies:
bn.js "^4.0.0"
inherits "^2.0.1"
minimalistic-assert "^1.0.0"
asn1@~0.2.3:
- version "0.2.3"
- resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.3.tgz#dac8787713c9966849fc8180777ebe9c1ddf3b86"
- integrity sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=
+ version "0.2.4"
+ resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136"
+ integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==
+ dependencies:
+ safer-buffer "~2.1.0"
assert-plus@1.0.0, assert-plus@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525"
integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=
-assert-plus@^0.2.0:
- version "0.2.0"
- resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-0.2.0.tgz#d74e1b87e7affc0db8aadb7021f3fe48101ab234"
- integrity sha1-104bh+ev/A24qttwIfP+SBAasjQ=
-
assert@^1.1.1:
- version "1.4.1"
- resolved "https://registry.yarnpkg.com/assert/-/assert-1.4.1.tgz#99912d591836b5a6f5b345c0f07eefc08fc65d91"
- integrity sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE=
+ version "1.5.0"
+ resolved "https://registry.yarnpkg.com/assert/-/assert-1.5.0.tgz#55c109aaf6e0aefdb3dc4b71240c70bf574b18eb"
+ integrity sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==
dependencies:
+ object-assign "^4.1.1"
util "0.10.3"
assign-symbols@^1.0.0:
@@ -1078,10 +1109,10 @@ assign-symbols@^1.0.0:
resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367"
integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=
-async-each@^1.0.0, async-each@^1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d"
- integrity sha1-GdOGodntxufByF04iu28xW0zYC0=
+async-each@^1.0.1:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf"
+ integrity sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==
async-foreach@^0.1.3:
version "0.1.3"
@@ -1098,38 +1129,28 @@ asynckit@^0.4.0:
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
integrity sha1-x57Zf380y48robyXkLzDZkdLS3k=
-atob@^2.0.0:
- version "2.0.3"
- resolved "https://registry.yarnpkg.com/atob/-/atob-2.0.3.tgz#19c7a760473774468f20b2d2d03372ad7d4cbf5d"
- integrity sha1-GcenYEc3dEaPILLS0DNyrX1Mv10=
+atob@^2.1.1:
+ version "2.1.2"
+ resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9"
+ integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==
autoprefixer@^9.4.9:
- version "9.4.10"
- resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.4.10.tgz#e1be61fc728bacac8f4252ed242711ec0dcc6a7b"
- integrity sha512-XR8XZ09tUrrSzgSlys4+hy5r2/z4Jp7Ag3pHm31U4g/CTccYPOVe19AkaJ4ey/vRd1sfj+5TtuD6I0PXtutjvQ==
+ version "9.5.1"
+ resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.5.1.tgz#243b1267b67e7e947f28919d786b50d3bb0fb357"
+ integrity sha512-KJSzkStUl3wP0D5sdMlP82Q52JLy5+atf2MHAre48+ckWkXgixmfHyWmA77wFDy6jTHU6mIgXv6hAQ2mf1PjJQ==
dependencies:
- browserslist "^4.4.2"
- caniuse-lite "^1.0.30000940"
+ browserslist "^4.5.4"
+ caniuse-lite "^1.0.30000957"
normalize-range "^0.1.2"
num2fraction "^1.2.2"
postcss "^7.0.14"
postcss-value-parser "^3.3.1"
-aws-sign2@~0.6.0:
- version "0.6.0"
- resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.6.0.tgz#14342dd38dbcc94d0e5b87d763cd63612c0e794f"
- integrity sha1-FDQt0428yU0OW4fXY81jYSwOeU8=
-
aws-sign2@~0.7.0:
version "0.7.0"
resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8"
integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=
-aws4@^1.2.1:
- version "1.6.0"
- resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.6.0.tgz#83ef5ca860b2b32e4a0deedee8c771b9db57471e"
- integrity sha1-g+9cqGCysy5KDe7e6MdxudtXRx4=
-
aws4@^1.8.0:
version "1.8.0"
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.8.0.tgz#f0e003d9ca9e7f59c7a508945d7b2ef9a04a542f"
@@ -1153,12 +1174,13 @@ babel-plugin-dynamic-import-node@^2.2.0:
object.assign "^4.1.0"
babel-plugin-macros@^2.5.0:
- version "2.5.0"
- resolved "https://registry.yarnpkg.com/babel-plugin-macros/-/babel-plugin-macros-2.5.0.tgz#01f4d3b50ed567a67b80a30b9da066e94f4097b6"
- integrity sha512-BWw0lD0kVZAXRD3Od1kMrdmfudqzDzYv2qrN3l2ISR1HVp1EgLKfbOrYV9xmY5k3qx3RIu5uPAUZZZHpo0o5Iw==
+ version "2.5.1"
+ resolved "https://registry.yarnpkg.com/babel-plugin-macros/-/babel-plugin-macros-2.5.1.tgz#4a119ac2c2e19b458c259b9accd7ee34fd57ec6f"
+ integrity sha512-xN3KhAxPzsJ6OQTktCanNpIFnnMsCV+t8OloKxIL72D6+SUZYFn9qfklPgef5HyyDtzYZqqb+fs1S12+gQY82Q==
dependencies:
- cosmiconfig "^5.0.5"
- resolve "^1.8.1"
+ "@babel/runtime" "^7.4.2"
+ cosmiconfig "^5.2.0"
+ resolve "^1.10.0"
balanced-match@^1.0.0:
version "1.0.0"
@@ -1166,9 +1188,9 @@ balanced-match@^1.0.0:
integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
base64-js@^1.0.2:
- version "1.2.1"
- resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.2.1.tgz#a91947da1f4a516ea38e5b4ec0ec3773675e0886"
- integrity sha512-dwVUVIXsBZXwTuwnXI9RK8sBmgq09NDHzyR9SAph9eqk76gKK2JSQmZARC2zRC81JC2QTtxD0ARU5qTS25gIGw==
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.0.tgz#cab1e6118f051095e58b5281aea8c1cd22bfc0e3"
+ integrity sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==
base@^0.11.1:
version "0.11.2"
@@ -1189,26 +1211,21 @@ batch@0.6.1:
integrity sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=
bcrypt-pbkdf@^1.0.0:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz#63bc5dcb61331b92bc05fd528953c33462a06f8d"
- integrity sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e"
+ integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=
dependencies:
tweetnacl "^0.14.3"
-big.js@^3.1.3:
- version "3.2.0"
- resolved "https://registry.yarnpkg.com/big.js/-/big.js-3.2.0.tgz#a5fc298b81b9e0dca2e458824784b65c52ba588e"
- integrity sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==
-
big.js@^5.2.2:
version "5.2.2"
resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328"
integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==
binary-extensions@^1.0.0:
- version "1.11.0"
- resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.11.0.tgz#46aa1751fb6a2f93ee5e689bb1087d4b14c6c205"
- integrity sha1-RqoXUftqL5PuXmibsQh9SxTGwgU=
+ version "1.13.1"
+ resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65"
+ integrity sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==
block-stream@*:
version "0.0.9"
@@ -1218,30 +1235,30 @@ block-stream@*:
inherits "~2.0.0"
bluebird@^3.5.3:
- version "3.5.3"
- resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.3.tgz#7d01c6f9616c9a51ab0f8c549a79dfe6ec33efa7"
- integrity sha512-/qKPUQlaW1OyR51WeCPBvRnAlnZFUJkCSG5HzGnuIqhgyJtF+T94lFnn33eiazjRm2LAHVy2guNnaq48X9SJuw==
+ version "3.5.4"
+ resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.4.tgz#d6cc661595de30d5b3af5fcedd3c0b3ef6ec5714"
+ integrity sha512-FG+nFEZChJrbQ9tIccIfZJBz3J7mLrAhxakAbnrJWn8d7aKOC+LWifa0G+p4ZqKp4y13T7juYvdhq9NzKdsrjw==
bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0:
version "4.11.8"
resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f"
integrity sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==
-body-parser@1.18.2:
- version "1.18.2"
- resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.18.2.tgz#87678a19d84b47d859b83199bd59bce222b10454"
- integrity sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=
+body-parser@1.18.3:
+ version "1.18.3"
+ resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.18.3.tgz#5b292198ffdd553b3a0f20ded0592b956955c8b4"
+ integrity sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=
dependencies:
bytes "3.0.0"
content-type "~1.0.4"
debug "2.6.9"
- depd "~1.1.1"
- http-errors "~1.6.2"
- iconv-lite "0.4.19"
+ depd "~1.1.2"
+ http-errors "~1.6.3"
+ iconv-lite "0.4.23"
on-finished "~2.3.0"
- qs "6.5.1"
- raw-body "2.3.2"
- type-is "~1.6.15"
+ qs "6.5.2"
+ raw-body "2.3.3"
+ type-is "~1.6.16"
bonjour@^3.5.0:
version "3.5.0"
@@ -1260,13 +1277,6 @@ boolbase@^1.0.0, boolbase@~1.0.0:
resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e"
integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24=
-boom@2.x.x:
- version "2.10.1"
- resolved "https://registry.yarnpkg.com/boom/-/boom-2.10.1.tgz#39c8918ceff5799f83f9492a848f625add0c766f"
- integrity sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=
- dependencies:
- hoek "2.x.x"
-
brace-expansion@^1.1.7:
version "1.1.11"
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
@@ -1275,23 +1285,6 @@ brace-expansion@^1.1.7:
balanced-match "^1.0.0"
concat-map "0.0.1"
-braces@^2.3.0:
- version "2.3.0"
- resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.0.tgz#a46941cb5fb492156b3d6a656e06c35364e3e66e"
- integrity sha512-P4O8UQRdGiMLWSizsApmXVQDBS6KCt7dSexgLKBmH5Hr1CZq7vsnscFh8oR1sP1ab1Zj0uCHCEzZeV6SfUf3rA==
- dependencies:
- arr-flatten "^1.1.0"
- array-unique "^0.3.2"
- define-property "^1.0.0"
- extend-shallow "^2.0.1"
- fill-range "^4.0.0"
- isobject "^3.0.1"
- repeat-element "^1.1.2"
- snapdragon "^0.8.1"
- snapdragon-node "^2.0.1"
- split-string "^3.0.2"
- to-regex "^3.0.1"
-
braces@^2.3.1, braces@^2.3.2:
version "2.3.2"
resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729"
@@ -1314,9 +1307,9 @@ brorand@^1.0.1:
integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=
browserify-aes@^1.0.0, browserify-aes@^1.0.4:
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.1.1.tgz#38b7ab55edb806ff2dcda1a7f1620773a477c49f"
- integrity sha512-UGnTYAnB2a3YuYKIRy1/4FB2HdM866E0qC46JXvVTYKlBlZlnvfpSfY6OKfXZAkv70eJ2a1SqzpAo5CRhZGDFg==
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48"
+ integrity sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==
dependencies:
buffer-xor "^1.0.3"
cipher-base "^1.0.0"
@@ -1326,22 +1319,23 @@ browserify-aes@^1.0.0, browserify-aes@^1.0.4:
safe-buffer "^5.0.1"
browserify-cipher@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/browserify-cipher/-/browserify-cipher-1.0.0.tgz#9988244874bf5ed4e28da95666dcd66ac8fc363a"
- integrity sha1-mYgkSHS/XtTijalWZtzWasj8Njo=
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/browserify-cipher/-/browserify-cipher-1.0.1.tgz#8d6474c1b870bfdabcd3bcfcc1934a10e94f15f0"
+ integrity sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==
dependencies:
browserify-aes "^1.0.4"
browserify-des "^1.0.0"
evp_bytestokey "^1.0.0"
browserify-des@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/browserify-des/-/browserify-des-1.0.0.tgz#daa277717470922ed2fe18594118a175439721dd"
- integrity sha1-2qJ3cXRwki7S/hhZQRihdUOXId0=
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/browserify-des/-/browserify-des-1.0.2.tgz#3af4f1f59839403572f1c66204375f7a7f703e9c"
+ integrity sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==
dependencies:
cipher-base "^1.0.1"
des.js "^1.0.0"
inherits "^2.0.1"
+ safe-buffer "^5.1.2"
browserify-rsa@^4.0.0:
version "4.0.1"
@@ -1371,14 +1365,14 @@ browserify-zlib@^0.2.0:
dependencies:
pako "~1.0.5"
-browserslist@^4.0.0, browserslist@^4.3.4, browserslist@^4.4.2:
- version "4.4.2"
- resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.4.2.tgz#6ea8a74d6464bb0bd549105f659b41197d8f0ba2"
- integrity sha512-ISS/AIAiHERJ3d45Fz0AVYKkgcy+F/eJHzKEvv1j0wwKGKD9T3BrwKr/5g45L+Y4XIK5PlTqefHciRFcfE1Jxg==
+browserslist@^4.0.0, browserslist@^4.4.2, browserslist@^4.5.2, browserslist@^4.5.4:
+ version "4.5.6"
+ resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.5.6.tgz#ea42e8581ca2513fa7f371d4dd66da763938163d"
+ integrity sha512-o/hPOtbU9oX507lIqon+UvPYqpx3mHc8cV3QemSBTXwkG8gSQSK6UKvXcE/DcleU3+A59XTUHyCvZ5qGy8xVAg==
dependencies:
- caniuse-lite "^1.0.30000939"
- electron-to-chromium "^1.3.113"
- node-releases "^1.1.8"
+ caniuse-lite "^1.0.30000963"
+ electron-to-chromium "^1.3.127"
+ node-releases "^1.1.17"
buffer-from@^1.0.0:
version "1.1.1"
@@ -1404,11 +1398,6 @@ buffer@^4.3.0:
ieee754 "^1.1.4"
isarray "^1.0.0"
-builtin-modules@^1.0.0:
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f"
- integrity sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=
-
builtin-status-codes@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8"
@@ -1491,15 +1480,10 @@ camelcase@^3.0.0:
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a"
integrity sha1-MvxLn82vhF/N9+c7uXysImHwqwo=
-camelcase@^4.1.0:
- version "4.1.0"
- resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd"
- integrity sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=
-
camelcase@^5.0.0, camelcase@^5.2.0:
- version "5.2.0"
- resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.2.0.tgz#e7522abda5ed94cc0489e1b8466610e88404cf45"
- integrity sha512-IXFsBS2pC+X0j0N/GE7Dm7j3bsEBp+oTpb7F50dwEVX7rf3IgwO9XatnegTsDtniKCUtEJH4fSU6Asw7uoVLfQ==
+ version "5.3.1"
+ resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320"
+ integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==
caniuse-api@^3.0.0:
version "3.0.0"
@@ -1511,15 +1495,10 @@ caniuse-api@^3.0.0:
lodash.memoize "^4.1.2"
lodash.uniq "^4.5.0"
-caniuse-lite@^1.0.0:
- version "1.0.30000808"
- resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000808.tgz#7d759b5518529ea08b6705a19e70dbf401628ffc"
- integrity sha512-vT0JLmHdvq1UVbYXioxCXHYdNw55tyvi+IUWyX0Zeh1OFQi2IllYtm38IJnSgHWCv/zUnX1hdhy3vMJvuTNSqw==
-
-caniuse-lite@^1.0.30000939, caniuse-lite@^1.0.30000940:
- version "1.0.30000942"
- resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000942.tgz#454139b28274bce70bfe1d50c30970df7430c6e4"
- integrity sha512-wLf+IhZUy2rfz48tc40OH7jHjXjnvDFEYqBHluINs/6MgzoNLPf25zhE4NOVzqxLKndf+hau81sAW0RcGHIaBQ==
+caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000939, caniuse-lite@^1.0.30000957, caniuse-lite@^1.0.30000963:
+ version "1.0.30000967"
+ resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000967.tgz#a5039577806fccee80a04aaafb2c0890b1ee2f73"
+ integrity sha512-rUBIbap+VJfxTzrM4akJ00lkvVb5/n5v3EGXfWzSH5zT8aJmGzjA8HWhJ4U6kCpzxozUSnB+yvAYDRPY6mRpgQ==
case-sensitive-paths-webpack-plugin@^2.2.0:
version "2.2.0"
@@ -1551,29 +1530,10 @@ chalk@^2.0, chalk@^2.0.0, chalk@^2.4.1, chalk@^2.4.2:
escape-string-regexp "^1.0.5"
supports-color "^5.3.0"
-chokidar@^2.0.0:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.0.1.tgz#6e67e9998fe10e8f651e975ca62460456ff8e297"
- integrity sha512-rv5iP8ENhpqvDWr677rAXcB+SMoPQ1urd4ch79+PhM4lQwbATdJUQK69t0lJIKNB+VXpqxt5V1gvqs59XEPKnw==
- dependencies:
- anymatch "^2.0.0"
- async-each "^1.0.0"
- braces "^2.3.0"
- glob-parent "^3.1.0"
- inherits "^2.0.1"
- is-binary-path "^1.0.0"
- is-glob "^4.0.0"
- normalize-path "^2.1.1"
- path-is-absolute "^1.0.0"
- readdirp "^2.0.0"
- upath "1.0.0"
- optionalDependencies:
- fsevents "^1.0.0"
-
-chokidar@^2.0.2:
- version "2.1.2"
- resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.2.tgz#9c23ea40b01638439e0513864d362aeacc5ad058"
- integrity sha512-IwXUx0FXc5ibYmPC2XeEj5mpXoV66sR+t3jqu2NS2GYwCktt3KF1/Qqjws/NkegajBA4RbZ5+DDwlOiJsxDHEg==
+chokidar@^2.0.2, chokidar@^2.1.5:
+ version "2.1.5"
+ resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.5.tgz#0ae8434d962281a5f56c72869e79cb6d9d86ad4d"
+ integrity sha512-i0TprVWp+Kj4WRPtInjexJ8Q+BqTE909VpH8xVhXrJkoc5QC8VO9TryGOqTr+2hljzc1sC62t22h5tZePodM/A==
dependencies:
anymatch "^2.0.0"
async-each "^1.0.1"
@@ -1585,7 +1545,7 @@ chokidar@^2.0.2:
normalize-path "^3.0.0"
path-is-absolute "^1.0.0"
readdirp "^2.2.1"
- upath "^1.1.0"
+ upath "^1.1.1"
optionalDependencies:
fsevents "^1.2.7"
@@ -1647,11 +1607,6 @@ clone-deep@^2.0.1:
kind-of "^6.0.0"
shallow-clone "^1.0.0"
-co@^4.6.0:
- version "4.6.0"
- resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184"
- integrity sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=
-
coa@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/coa/-/coa-2.0.2.tgz#43f6c21151b4ef2bf57187db0d73de229e3e7ec3"
@@ -1675,40 +1630,38 @@ collection-visit@^1.0.0:
object-visit "^1.0.0"
color-convert@^1.9.0, color-convert@^1.9.1:
- version "1.9.1"
- resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.1.tgz#c1261107aeb2f294ebffec9ed9ecad529a6097ed"
- integrity sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==
+ version "1.9.3"
+ resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
+ integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==
dependencies:
- color-name "^1.1.1"
+ color-name "1.1.3"
-color-name@^1.0.0, color-name@^1.1.1:
+color-name@1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=
+color-name@^1.0.0:
+ version "1.1.4"
+ resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
+ integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
+
color-string@^1.5.2:
- version "1.5.2"
- resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.5.2.tgz#26e45814bc3c9a7cbd6751648a41434514a773a9"
- integrity sha1-JuRYFLw8mny9Z1FkikFDRRSnc6k=
+ version "1.5.3"
+ resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.5.3.tgz#c9bbc5f01b58b5492f3d6857459cb6590ce204cc"
+ integrity sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw==
dependencies:
color-name "^1.0.0"
simple-swizzle "^0.2.2"
color@^3.0.0:
- version "3.1.0"
- resolved "https://registry.yarnpkg.com/color/-/color-3.1.0.tgz#d8e9fb096732875774c84bf922815df0308d0ffc"
- integrity sha512-CwyopLkuRYO5ei2EpzpIh6LqJMt6Mt+jZhO5VI5f/wJLZriXQE32/SSqzmrh+QB+AZT81Cj8yv+7zwToW8ahZg==
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/color/-/color-3.1.1.tgz#7abf5c0d38e89378284e873c207ae2172dcc8a61"
+ integrity sha512-PvUltIXRjehRKPSy89VnDWFKY58xyhTLyxIg21vwQBI6qLwZNPmC8k3C1uytIgFKEpOIzN4y32iPm8231zFHIg==
dependencies:
color-convert "^1.9.1"
color-string "^1.5.2"
-combined-stream@^1.0.5, combined-stream@~1.0.5:
- version "1.0.5"
- resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.5.tgz#938370a57b4a51dea2c77c15d5c5fdf895164009"
- integrity sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk=
- dependencies:
- delayed-stream "~1.0.0"
-
combined-stream@^1.0.6, combined-stream@~1.0.6:
version "1.0.7"
resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.7.tgz#2d1d24317afb8abe95d6d2c0b07b57813539d828"
@@ -1716,10 +1669,10 @@ combined-stream@^1.0.6, combined-stream@~1.0.6:
dependencies:
delayed-stream "~1.0.0"
-commander@~2.17.1:
- version "2.17.1"
- resolved "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf"
- integrity sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==
+commander@^2.19.0:
+ version "2.20.0"
+ resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.0.tgz#d58bb2b5c1ee8f87b0d340027e9e94e222c5a422"
+ integrity sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==
commondir@^1.0.1:
version "1.0.1"
@@ -1727,16 +1680,16 @@ commondir@^1.0.1:
integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=
component-emitter@^1.2.1:
- version "1.2.1"
- resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6"
- integrity sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0"
+ integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==
-compressible@~2.0.11:
- version "2.0.12"
- resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.12.tgz#c59a5c99db76767e9876500e271ef63b3493bd66"
- integrity sha1-xZpcmdt2dn6YdlAOJx72OzSTvWY=
+compressible@~2.0.16:
+ version "2.0.17"
+ resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.17.tgz#6e8c108a16ad58384a977f3a482ca20bff2f38c1"
+ integrity sha512-BGHeLCK1GV7j1bSmQQAi26X+GgWcTjLr/0tzSvMCl3LH1w1IJ4PFSPoV5316b30cneTziC+B1a+3OjoSUcQYmw==
dependencies:
- mime-db ">= 1.30.0 < 2"
+ mime-db ">= 1.40.0 < 2"
compression-webpack-plugin@^2.0.0:
version "2.0.0"
@@ -1750,17 +1703,17 @@ compression-webpack-plugin@^2.0.0:
serialize-javascript "^1.4.0"
webpack-sources "^1.0.1"
-compression@^1.5.2:
- version "1.7.1"
- resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.1.tgz#eff2603efc2e22cf86f35d2eb93589f9875373db"
- integrity sha1-7/JgPvwuIs+G810uuTWJ+YdTc9s=
+compression@^1.7.4:
+ version "1.7.4"
+ resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.4.tgz#95523eff170ca57c29a0ca41e6fe131f41e5bb8f"
+ integrity sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==
dependencies:
- accepts "~1.3.4"
+ accepts "~1.3.5"
bytes "3.0.0"
- compressible "~2.0.11"
+ compressible "~2.0.16"
debug "2.6.9"
- on-headers "~1.0.1"
- safe-buffer "5.1.1"
+ on-headers "~1.0.2"
+ safe-buffer "5.1.2"
vary "~1.1.2"
concat-map@0.0.1:
@@ -1769,18 +1722,19 @@ concat-map@0.0.1:
integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
concat-stream@^1.5.0:
- version "1.6.0"
- resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.0.tgz#0aac662fd52be78964d5532f694784e70110acf7"
- integrity sha1-CqxmL9Ur54lk1VMvaUeE5wEQrPc=
+ version "1.6.2"
+ resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34"
+ integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==
dependencies:
+ buffer-from "^1.0.0"
inherits "^2.0.3"
readable-stream "^2.2.2"
typedarray "^0.0.6"
-connect-history-api-fallback@^1.3.0:
- version "1.5.0"
- resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-1.5.0.tgz#b06873934bc5e344fef611a196a6faae0aee015a"
- integrity sha1-sGhzk0vF40T+9hGhlqb6rgruAVo=
+connect-history-api-fallback@^1.6.0:
+ version "1.6.0"
+ resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz#8b32089359308d111115d81cad3fceab888f97bc"
+ integrity sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==
console-browserify@^1.1.0:
version "1.1.0"
@@ -1843,7 +1797,27 @@ copy-descriptor@^0.1.0:
resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d"
integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=
-core-js@^2.5.7:
+core-js-compat@^3.0.0:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.0.1.tgz#bff73ba31ca8687431b9c88f78d3362646fb76f0"
+ integrity sha512-2pC3e+Ht/1/gD7Sim/sqzvRplMiRnFQVlPpDVaHtY9l7zZP7knamr3VRD6NyGfHd84MrDC0tAM9ulNxYMW0T3g==
+ dependencies:
+ browserslist "^4.5.4"
+ core-js "3.0.1"
+ core-js-pure "3.0.1"
+ semver "^6.0.0"
+
+core-js-pure@3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.0.1.tgz#37358fb0d024e6b86d443d794f4e37e949098cbe"
+ integrity sha512-mSxeQ6IghKW3MoyF4cz19GJ1cMm7761ON+WObSyLfTu/Jn3x7w4NwNFnrZxgl4MTSvYYepVLNuRtlB4loMwJ5g==
+
+core-js@3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.0.1.tgz#1343182634298f7f38622f95e73f54e48ddf4738"
+ integrity sha512-sco40rF+2KlE0ROMvydjkrVMMG1vYilP2ALoRXcYR4obqbYIuV3Bg+51GEDW+HF8n7NRA+iaA4qD0nD9lo9mew==
+
+core-js@^2.6.5:
version "2.6.5"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.5.tgz#44bc8d249e7fb2ff5d00e0341a7ffb94fbf67895"
integrity sha512-klh/kDpwX8hryYL14M9w/xei6vrv6sE8gTHDG7/T/+SEovB/G4ejwcfE/CBzO6Edsu+OETZMZ3wcX/EjUkrl5A==
@@ -1863,39 +1837,39 @@ cosmiconfig@^4.0.0:
parse-json "^4.0.0"
require-from-string "^2.0.1"
-cosmiconfig@^5.0.0, cosmiconfig@^5.0.5:
- version "5.1.0"
- resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.1.0.tgz#6c5c35e97f37f985061cdf653f114784231185cf"
- integrity sha512-kCNPvthka8gvLtzAxQXvWo4FxqRB+ftRZyPZNuab5ngvM9Y7yw7hbEysglptLgpkGX9nAOKTBVkHUAe8xtYR6Q==
+cosmiconfig@^5.0.0, cosmiconfig@^5.2.0:
+ version "5.2.0"
+ resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.2.0.tgz#45038e4d28a7fe787203aede9c25bca4a08b12c8"
+ integrity sha512-nxt+Nfc3JAqf4WIWd0jXLjTJZmsPLrA9DDc4nRw2KFJQJK7DNooqSXrNI7tzLG50CF8axczly5UV929tBmh/7g==
dependencies:
import-fresh "^2.0.0"
is-directory "^0.3.1"
- js-yaml "^3.9.0"
- lodash.get "^4.4.2"
+ js-yaml "^3.13.0"
parse-json "^4.0.0"
create-ecdh@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.0.tgz#888c723596cdf7612f6498233eebd7a35301737d"
- integrity sha1-iIxyNZbN92EvZJgjPuvXo1MBc30=
+ version "4.0.3"
+ resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.3.tgz#c9111b6f33045c4697f144787f9254cdc77c45ff"
+ integrity sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw==
dependencies:
bn.js "^4.1.0"
elliptic "^6.0.0"
create-hash@^1.1.0, create-hash@^1.1.2:
- version "1.1.3"
- resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.1.3.tgz#606042ac8b9262750f483caddab0f5819172d8fd"
- integrity sha1-YGBCrIuSYnUPSDyt2rD1gZFy2P0=
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196"
+ integrity sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==
dependencies:
cipher-base "^1.0.1"
inherits "^2.0.1"
- ripemd160 "^2.0.0"
+ md5.js "^1.3.4"
+ ripemd160 "^2.0.1"
sha.js "^2.4.0"
create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4:
- version "1.1.6"
- resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.6.tgz#acb9e221a4e17bdb076e90657c42b93e3726cf06"
- integrity sha1-rLniIaThe9sHbpBlfEK5PjcmzwY=
+ version "1.1.7"
+ resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.7.tgz#69170c78b3ab957147b2b8b04572e47ead2243ff"
+ integrity sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==
dependencies:
cipher-base "^1.0.3"
create-hash "^1.1.0"
@@ -1923,13 +1897,6 @@ cross-spawn@^6.0.0, cross-spawn@^6.0.5:
shebang-command "^1.2.0"
which "^1.2.9"
-cryptiles@2.x.x:
- version "2.0.5"
- resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-2.0.5.tgz#3bdfecdc608147c1c67202fa291e7dca59eaa3b8"
- integrity sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=
- dependencies:
- boom "2.x.x"
-
crypto-browserify@^3.11.0:
version "3.12.0"
resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec"
@@ -2159,7 +2126,7 @@ date-now@^0.1.4:
resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b"
integrity sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=
-debug@2.6.9, debug@^2.1.2, debug@^2.2.0, debug@^2.3.3:
+debug@2.6.9, debug@^2.2.0, debug@^2.3.3:
version "2.6.9"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
@@ -2185,13 +2152,6 @@ decamelize@^1.1.1, decamelize@^1.1.2, decamelize@^1.2.0:
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=
-decamelize@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-2.0.0.tgz#656d7bbc8094c4c788ea53c5840908c9c7d063c7"
- integrity sha512-Ikpp5scV3MSYxY39ymh45ZLEecsTdv/Xj2CaQfI8RLMuwi7XvjX9H/fhraiSuU+C5w5NTDu4ZU72xNiZnurBPg==
- dependencies:
- xregexp "4.0.0"
-
decode-uri-component@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545"
@@ -2207,12 +2167,7 @@ deep-extend@^0.6.0:
resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac"
integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==
-deep-extend@~0.4.0:
- version "0.4.2"
- resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.4.2.tgz#48b699c27e334bf89f10892be432f6e4c7d34a7f"
- integrity sha1-SLaZwn4zS/ifEIkr5DL25MfTSn8=
-
-default-gateway@^4.0.1:
+default-gateway@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/default-gateway/-/default-gateway-4.2.0.tgz#167104c7500c2115f6dd69b0a536bb8ed720552b"
integrity sha512-h6sMrVB1VMWVrW13mSc6ia/DwYYw5MN6+exNu1OaJeFac5aSAvwM7lZ0NVfTABuSkQelr4h5oebg3KB1XPdjgA==
@@ -2220,15 +2175,7 @@ default-gateway@^4.0.1:
execa "^1.0.0"
ip-regex "^2.1.0"
-define-properties@^1.1.2:
- version "1.1.2"
- resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.2.tgz#83a73f2fea569898fb737193c8f873caf6d45c94"
- integrity sha1-g6c/L+pWmJj7c3GTyPhzyvbUXJQ=
- dependencies:
- foreach "^2.0.5"
- object-keys "^1.0.8"
-
-define-properties@^1.1.3:
+define-properties@^1.1.2, define-properties@^1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1"
integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==
@@ -2257,17 +2204,18 @@ define-property@^2.0.2:
is-descriptor "^1.0.2"
isobject "^3.0.1"
-del@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/del/-/del-3.0.0.tgz#53ecf699ffcbcb39637691ab13baf160819766e5"
- integrity sha1-U+z2mf/LyzljdpGrE7rxYIGXZuU=
+del@^4.1.0:
+ version "4.1.1"
+ resolved "https://registry.yarnpkg.com/del/-/del-4.1.1.tgz#9e8f117222ea44a31ff3a156c049b99052a9f0b4"
+ integrity sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ==
dependencies:
+ "@types/glob" "^7.1.1"
globby "^6.1.0"
- is-path-cwd "^1.0.0"
- is-path-in-cwd "^1.0.0"
- p-map "^1.1.1"
- pify "^3.0.0"
- rimraf "^2.2.8"
+ is-path-cwd "^2.0.0"
+ is-path-in-cwd "^2.0.0"
+ p-map "^2.0.0"
+ pify "^4.0.1"
+ rimraf "^2.6.3"
delayed-stream@~1.0.0:
version "1.0.0"
@@ -2279,12 +2227,7 @@ delegates@^1.0.0:
resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a"
integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=
-depd@1.1.1:
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.1.tgz#5783b4e1c459f06fa5ca27f991f3d06e7a310359"
- integrity sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k=
-
-depd@~1.1.1:
+depd@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9"
integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=
@@ -2318,9 +2261,9 @@ detect-node@^2.0.4:
integrity sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw==
diffie-hellman@^5.0.0:
- version "5.0.2"
- resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.2.tgz#b5835739270cfe26acf632099fded2a07f209e5e"
- integrity sha1-tYNXOScM/ias9jIJn97SoH8gnl4=
+ version "5.0.3"
+ resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875"
+ integrity sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==
dependencies:
bn.js "^4.1.0"
miller-rabin "^4.0.0"
@@ -2379,10 +2322,10 @@ dot-prop@^4.1.1:
dependencies:
is-obj "^1.0.0"
-duplexify@^3.4.2, duplexify@^3.5.3:
- version "3.5.3"
- resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.5.3.tgz#8b5818800df92fd0125b27ab896491912858243e"
- integrity sha512-g8ID9OroF9hKt2POf8YLayy+9594PzmM3scI00/uBXocX3TWNgoB67hjzkFe9ITAbQOne/lLdBxHXvYUM4ZgGA==
+duplexify@^3.4.2, duplexify@^3.6.0:
+ version "3.7.1"
+ resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.7.1.tgz#2a4df5317f6ccfd91f86d6fd25d8d8a103b88309"
+ integrity sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==
dependencies:
end-of-stream "^1.0.0"
inherits "^2.0.1"
@@ -2390,26 +2333,27 @@ duplexify@^3.4.2, duplexify@^3.5.3:
stream-shift "^1.0.0"
ecc-jsbn@~0.1.1:
- version "0.1.1"
- resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz#0fc73a9ed5f0d53c38193398523ef7e543777505"
- integrity sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=
+ version "0.1.2"
+ resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9"
+ integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=
dependencies:
jsbn "~0.1.0"
+ safer-buffer "^2.1.0"
ee-first@1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=
-electron-to-chromium@^1.3.113:
- version "1.3.113"
- resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.113.tgz#b1ccf619df7295aea17bc6951dc689632629e4a9"
- integrity sha512-De+lPAxEcpxvqPTyZAXELNpRZXABRxf+uL/rSykstQhzj/B0l1150G/ExIIxKc16lI89Hgz81J0BHAcbTqK49g==
+electron-to-chromium@^1.3.127:
+ version "1.3.133"
+ resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.133.tgz#c47639c19b91feee3e22fad69f5556142007008c"
+ integrity sha512-lyoC8aoqbbDqsprb6aPdt9n3DpOZZzdz/T4IZKsR0/dkZIxnJVUjjcpOSwA66jPRIOyDAamCTAUqweU05kKNSg==
elliptic@^6.0.0:
- version "6.4.0"
- resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.4.0.tgz#cac9af8762c85836187003c8dfe193e5e2eae5df"
- integrity sha1-ysmvh2LIWDYYcAPI3+GT5eLq5d8=
+ version "6.4.1"
+ resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.4.1.tgz#c2d0b7776911b86722c632c3c06c60f2f819939a"
+ integrity sha512-BsXLz5sqX8OHcsh7CqBMztyXARmGQ3LWPtGjJi6DiJHq5C/qvi9P3OqgswKSDftbu8+IoI/QDTAm2fFnQ9SZSQ==
dependencies:
bn.js "^4.4.0"
brorand "^1.0.1"
@@ -2424,7 +2368,7 @@ emojis-list@^2.0.0:
resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389"
integrity sha1-TapNnbAPmBmIDHn6RXrlsJof04k=
-encodeurl@~1.0.1:
+encodeurl@~1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=
@@ -2450,21 +2394,14 @@ entities@^1.1.1:
resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.2.tgz#bdfa735299664dfafd34529ed4f8522a275fea56"
integrity sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==
-errno@^0.1.3, errno@^0.1.4:
- version "0.1.6"
- resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.6.tgz#c386ce8a6283f14fc09563b71560908c9bf53026"
- integrity sha512-IsORQDpaaSwcDP4ZZnHxgE85werpo34VYn1Ud3mq+eUsF593faR8oCZNXrROVkpFu2TsbrNhHin0aUrTsQ9vNw==
+errno@^0.1.3, errno@~0.1.7:
+ version "0.1.7"
+ resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.7.tgz#4684d71779ad39af177e3f007996f7c67c852618"
+ integrity sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==
dependencies:
prr "~1.0.1"
-error-ex@^1.2.0:
- version "1.3.1"
- resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.1.tgz#f855a86ce61adc4e8621c3cda21e7a7612c3a8dc"
- integrity sha1-+FWobOYa3E6GIcPNoh56dhLDqNw=
- dependencies:
- is-arrayish "^0.2.1"
-
-error-ex@^1.3.1:
+error-ex@^1.2.0, error-ex@^1.3.1:
version "1.3.2"
resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf"
integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==
@@ -2503,25 +2440,24 @@ escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5:
integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=
eslint-scope@^4.0.0:
- version "4.0.2"
- resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.2.tgz#5f10cd6cabb1965bf479fa65745673439e21cb0e"
- integrity sha512-5q1+B/ogmHl8+paxtOKx38Z8LtWkVGuNt3+GQNErqwLl6ViNp/gdJGMCjZNxZ8j/VYjDNZ2Fo+eQc1TAVPIzbg==
+ version "4.0.3"
+ resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.3.tgz#ca03833310f6889a3264781aa82e63eb9cfe7848"
+ integrity sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==
dependencies:
esrecurse "^4.1.0"
estraverse "^4.1.1"
esprima@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.0.tgz#4499eddcd1110e0b218bacf2fa7f7f59f55ca804"
- integrity sha512-oftTcaMu/EGrEIu904mWteKIv8vMuOgGYo7EhVJJN00R/EED9DCua/xxHRdYnKtcECzVg7xOWhflvJMnqcFZjw==
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71"
+ integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==
esrecurse@^4.1.0:
- version "4.2.0"
- resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.0.tgz#fa9568d98d3823f9a41d91e902dcab9ea6e5b163"
- integrity sha1-+pVo2Y04I/mkHZHpAtyrnqblsWM=
+ version "4.2.1"
+ resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.1.tgz#007a3b9fdbc2b3bb87e4879ea19c92fdbd3942cf"
+ integrity sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==
dependencies:
estraverse "^4.1.0"
- object-assign "^4.0.1"
estraverse@^4.1.0, estraverse@^4.1.1:
version "4.2.0"
@@ -2539,14 +2475,14 @@ etag@~1.8.1:
integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=
eventemitter3@^3.0.0:
- version "3.1.0"
- resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-3.1.0.tgz#090b4d6cdbd645ed10bf750d4b5407942d7ba163"
- integrity sha512-ivIvhpq/Y0uSjcHDcOIccjmYjGLcP09MFGE7ysAwkAvkXfpZlC985pH2/ui64DKazbTW/4kN3yqozUxlXzI6cA==
+ version "3.1.2"
+ resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-3.1.2.tgz#2d3d48f9c346698fce83a85d7d664e98535df6e7"
+ integrity sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==
-events@^1.0.0:
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924"
- integrity sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=
+events@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/events/-/events-3.0.0.tgz#9a0a0dfaf62893d92b875b8f2698ca4114973e88"
+ integrity sha512-Dc381HFWJzEOhQ+d8pkNon++bk9h6cdAoAj4iE6Q4y6xgTzySWXlKn05/TVNpjnfRqi/X0EpJEJohPjNI3zpVA==
eventsource@^1.0.7:
version "1.0.7"
@@ -2596,39 +2532,39 @@ expand-tilde@^2.0.0, expand-tilde@^2.0.2:
dependencies:
homedir-polyfill "^1.0.1"
-express@^4.16.2:
- version "4.16.2"
- resolved "https://registry.yarnpkg.com/express/-/express-4.16.2.tgz#e35c6dfe2d64b7dca0a5cd4f21781be3299e076c"
- integrity sha1-41xt/i1kt9ygpc1PIXgb4ymeB2w=
+express@^4.16.4:
+ version "4.16.4"
+ resolved "https://registry.yarnpkg.com/express/-/express-4.16.4.tgz#fddef61926109e24c515ea97fd2f1bdbf62df12e"
+ integrity sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg==
dependencies:
- accepts "~1.3.4"
+ accepts "~1.3.5"
array-flatten "1.1.1"
- body-parser "1.18.2"
+ body-parser "1.18.3"
content-disposition "0.5.2"
content-type "~1.0.4"
cookie "0.3.1"
cookie-signature "1.0.6"
debug "2.6.9"
- depd "~1.1.1"
- encodeurl "~1.0.1"
+ depd "~1.1.2"
+ encodeurl "~1.0.2"
escape-html "~1.0.3"
etag "~1.8.1"
- finalhandler "1.1.0"
+ finalhandler "1.1.1"
fresh "0.5.2"
merge-descriptors "1.0.1"
methods "~1.1.2"
on-finished "~2.3.0"
parseurl "~1.3.2"
path-to-regexp "0.1.7"
- proxy-addr "~2.0.2"
- qs "6.5.1"
+ proxy-addr "~2.0.4"
+ qs "6.5.2"
range-parser "~1.2.0"
- safe-buffer "5.1.1"
- send "0.16.1"
- serve-static "1.13.1"
+ safe-buffer "5.1.2"
+ send "0.16.2"
+ serve-static "1.13.2"
setprototypeof "1.1.0"
- statuses "~1.3.1"
- type-is "~1.6.15"
+ statuses "~1.4.0"
+ type-is "~1.6.16"
utils-merge "1.0.1"
vary "~1.1.2"
@@ -2647,17 +2583,12 @@ extend-shallow@^3.0.0, extend-shallow@^3.0.2:
assign-symbols "^1.0.0"
is-extendable "^1.0.1"
-extend@~3.0.0:
- version "3.0.1"
- resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444"
- integrity sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=
-
extend@~3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa"
integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==
-extglob@^2.0.2, extglob@^2.0.4:
+extglob@^2.0.4:
version "2.0.4"
resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543"
integrity sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==
@@ -2681,11 +2612,6 @@ extsprintf@^1.2.0:
resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f"
integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8=
-fast-deep-equal@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz#96256a3bc975595eb36d82e9929d060d893439ff"
- integrity sha1-liVqO8l1WV6zbYLpkp0GDYk0Of8=
-
fast-deep-equal@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49"
@@ -2733,26 +2659,26 @@ fill-range@^4.0.0:
repeat-string "^1.6.1"
to-regex-range "^2.1.0"
-finalhandler@1.1.0:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.0.tgz#ce0b6855b45853e791b2fcc680046d88253dd7f5"
- integrity sha1-zgtoVbRYU+eRsvzGgARtiCU91/U=
+finalhandler@1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.1.tgz#eebf4ed840079c83f4249038c9d703008301b105"
+ integrity sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==
dependencies:
debug "2.6.9"
- encodeurl "~1.0.1"
+ encodeurl "~1.0.2"
escape-html "~1.0.3"
on-finished "~2.3.0"
parseurl "~1.3.2"
- statuses "~1.3.1"
+ statuses "~1.4.0"
unpipe "~1.0.0"
find-cache-dir@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-2.0.0.tgz#4c1faed59f45184530fb9d7fa123a4d04a98472d"
- integrity sha512-LDUY6V1Xs5eFskUVYtIwatojt6+9xC9Chnlk/jYOOvn3FAFfSaWddxahDGyNHh0b2dMXa6YW2m0tk8TdVaXHlA==
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-2.1.0.tgz#8d0f94cd13fe43c6c7c261a0d86115ca918c05f7"
+ integrity sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==
dependencies:
commondir "^1.0.1"
- make-dir "^1.0.0"
+ make-dir "^2.0.0"
pkg-dir "^3.0.0"
find-up@^1.0.0:
@@ -2791,12 +2717,12 @@ flatten@^1.0.2:
integrity sha1-2uRqnXj74lKSJYzB54CkHZXAN4I=
flush-write-stream@^1.0.0:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.0.2.tgz#c81b90d8746766f1a609a46809946c45dd8ae417"
- integrity sha1-yBuQ2HRnZvGmCaRoCZRsRd2K5Bc=
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.1.1.tgz#8dd7d873a1babc207d94ead0c2e0e44276ebf2e8"
+ integrity sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==
dependencies:
- inherits "^2.0.1"
- readable-stream "^2.0.4"
+ inherits "^2.0.3"
+ readable-stream "^2.3.6"
follow-redirects@^1.0.0:
version "1.7.0"
@@ -2822,25 +2748,11 @@ for-own@^1.0.0:
dependencies:
for-in "^1.0.1"
-foreach@^2.0.5:
- version "2.0.5"
- resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99"
- integrity sha1-C+4AUBiusmDQo6865ljdATbsG5k=
-
forever-agent@~0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91"
integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=
-form-data@~2.1.1:
- version "2.1.4"
- resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.1.4.tgz#33c183acf193276ecaa98143a69e94bfee1750d1"
- integrity sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE=
- dependencies:
- asynckit "^0.4.0"
- combined-stream "^1.0.5"
- mime-types "^2.1.12"
-
form-data@~2.3.2:
version "2.3.3"
resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6"
@@ -2897,32 +2809,15 @@ fs.realpath@^1.0.0:
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8=
-fsevents@^1.0.0:
- version "1.1.3"
- resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.1.3.tgz#11f82318f5fe7bb2cd22965a108e9306208216d8"
- integrity sha512-WIr7iDkdmdbxu/Gh6eKEZJL6KPE74/5MEsf2whTOFNxbIoIixogroLdKYqB6FDav4Wavh/lZdzzd3b2KxIXC5Q==
- dependencies:
- nan "^2.3.0"
- node-pre-gyp "^0.6.39"
-
fsevents@^1.2.7:
- version "1.2.7"
- resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.7.tgz#4851b664a3783e52003b3c66eb0eee1074933aa4"
- integrity sha512-Pxm6sI2MeBD7RdD12RYsqaP0nMiwx8eZBXCa6z2L+mRHm2DYrOYwihmhjpkdjUHwQhslWQjRpEgNq4XvBmaAuw==
+ version "1.2.9"
+ resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.9.tgz#3f5ed66583ccd6f400b5a00db6f7e861363e388f"
+ integrity sha512-oeyj2H3EjjonWcFjD5NvZNE9Rqe4UW+nQBU2HNeKw0koVLEFIhtyETyAakeAM3de7Z/SW5kcA+fZUait9EApnw==
dependencies:
- nan "^2.9.2"
- node-pre-gyp "^0.10.0"
-
-fstream-ignore@^1.0.5:
- version "1.0.5"
- resolved "https://registry.yarnpkg.com/fstream-ignore/-/fstream-ignore-1.0.5.tgz#9c31dae34767018fe1d249b24dada67d092da105"
- integrity sha1-nDHa40dnAY/h0kmyTa2mfQktoQU=
- dependencies:
- fstream "^1.0.0"
- inherits "2"
- minimatch "^3.0.0"
+ nan "^2.12.1"
+ node-pre-gyp "^0.12.0"
-fstream@^1.0.0, fstream@^1.0.10, fstream@^1.0.2:
+fstream@^1.0.0, fstream@^1.0.2:
version "1.0.11"
resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.11.tgz#5c1fb1f117477114f0632a0eb4b71b3cb0fd3171"
integrity sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=
@@ -2932,7 +2827,7 @@ fstream@^1.0.0, fstream@^1.0.10, fstream@^1.0.2:
mkdirp ">=0.5 0"
rimraf "2"
-function-bind@^1.0.2, function-bind@^1.1.1:
+function-bind@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
@@ -2952,16 +2847,16 @@ gauge@~2.7.3:
wide-align "^1.1.0"
gaze@^1.0.0:
- version "1.1.2"
- resolved "https://registry.yarnpkg.com/gaze/-/gaze-1.1.2.tgz#847224677adb8870d679257ed3388fdb61e40105"
- integrity sha1-hHIkZ3rbiHDWeSV+0ziP22HkAQU=
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/gaze/-/gaze-1.1.3.tgz#c441733e13b927ac8c0ff0b4c3b033f28812924a"
+ integrity sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g==
dependencies:
globule "^1.0.0"
get-caller-file@^1.0.1:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.2.tgz#f702e63127e7e231c160a80c1554acb70d5047e5"
- integrity sha1-9wLmMSfn4jHBYKgMFVSstw1QR+U=
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.3.tgz#f978fa4c90d1dfe7ff2d6beda2a515e713bdcf4a"
+ integrity sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==
get-stdin@^4.0.1:
version "4.0.1"
@@ -2995,33 +2890,10 @@ glob-parent@^3.1.0:
is-glob "^3.1.0"
path-dirname "^1.0.0"
-glob@^6.0.4:
- version "6.0.4"
- resolved "https://registry.yarnpkg.com/glob/-/glob-6.0.4.tgz#0f08860f6a155127b2fadd4f9ce24b1aab6e4d22"
- integrity sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI=
- dependencies:
- inflight "^1.0.4"
- inherits "2"
- minimatch "2 || 3"
- once "^1.3.0"
- path-is-absolute "^1.0.0"
-
-glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@~7.1.1:
- version "7.1.2"
- resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15"
- integrity sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==
- dependencies:
- fs.realpath "^1.0.0"
- inflight "^1.0.4"
- inherits "2"
- minimatch "^3.0.4"
- once "^1.3.0"
- path-is-absolute "^1.0.0"
-
-glob@^7.1.3:
- version "7.1.3"
- resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1"
- integrity sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==
+glob@^7.0.0, glob@^7.0.3, glob@^7.1.2, glob@^7.1.3, glob@~7.1.1:
+ version "7.1.4"
+ resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.4.tgz#aa608a2f6c577ad357e1ae5a5c26d9a8d1969255"
+ integrity sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==
dependencies:
fs.realpath "^1.0.0"
inflight "^1.0.4"
@@ -3051,9 +2923,9 @@ global-prefix@^1.0.1:
which "^1.2.14"
globals@^11.1.0:
- version "11.11.0"
- resolved "https://registry.yarnpkg.com/globals/-/globals-11.11.0.tgz#dcf93757fa2de5486fbeed7118538adf789e9c2e"
- integrity sha512-WHq43gS+6ufNOEqlrDBxVEbb8ntfXrfAUU2ZOpCxrBdGKW3gyv8mCxAfIBD0DroPKGrJ2eSsXsLtY9MPntsyTw==
+ version "11.12.0"
+ resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e"
+ integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==
globby@^6.1.0:
version "6.1.0"
@@ -3067,20 +2939,15 @@ globby@^6.1.0:
pinkie-promise "^2.0.0"
globule@^1.0.0:
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/globule/-/globule-1.2.0.tgz#1dc49c6822dd9e8a2fa00ba2a295006e8664bd09"
- integrity sha1-HcScaCLdnoovoAuiopUAboZkvQk=
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/globule/-/globule-1.2.1.tgz#5dffb1b191f22d20797a9369b49eab4e9839696d"
+ integrity sha512-g7QtgWF4uYSL5/dn71WxubOrS7JVGCnFPEnoeChJmBnyR9Mw8nGoEwOgJL/RC2Te0WhbsEUCejfH8SZNJ+adYQ==
dependencies:
glob "~7.1.1"
- lodash "~4.17.4"
+ lodash "~4.17.10"
minimatch "~3.0.2"
-graceful-fs@^4.1.11, graceful-fs@^4.1.2:
- version "4.1.11"
- resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658"
- integrity sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=
-
-graceful-fs@^4.1.15:
+graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2:
version "4.1.15"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.15.tgz#ffb703e1066e8a0eeaa4c8b80ba9253eeefbfb00"
integrity sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==
@@ -3090,24 +2957,11 @@ handle-thing@^2.0.0:
resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-2.0.0.tgz#0e039695ff50c93fc288557d696f3c1dc6776754"
integrity sha512-d4sze1JNC454Wdo2fkuyzCr6aHcbL6PGGuFAz0Li/NcOm1tCHGnWDRmJP85dh9IhQErTc2svWFEX5xHIOo//kQ==
-har-schema@^1.0.5:
- version "1.0.5"
- resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-1.0.5.tgz#d263135f43307c02c602afc8fe95970c0151369e"
- integrity sha1-0mMTX0MwfALGAq/I/pWXDAFRNp4=
-
har-schema@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92"
integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=
-har-validator@~4.2.1:
- version "4.2.1"
- resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-4.2.1.tgz#33481d0f1bbff600dd203d75812a6a5fba002e2a"
- integrity sha1-M0gdDxu/9gDdID11gSpqX7oALio=
- dependencies:
- ajv "^4.9.1"
- har-schema "^1.0.5"
-
har-validator@~5.1.0:
version "5.1.3"
resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.3.tgz#1ef89ebd3e4996557675eed9893110dc350fa080"
@@ -3169,27 +3023,13 @@ has-values@^1.0.0:
is-number "^3.0.0"
kind-of "^4.0.0"
-has@^1.0.0, has@^1.0.3:
+has@^1.0.0, has@^1.0.1, has@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796"
integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==
dependencies:
function-bind "^1.1.1"
-has@^1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/has/-/has-1.0.1.tgz#8461733f538b0837c9361e39a9ab9e9704dc2f28"
- integrity sha1-hGFzP1OLCDfJNh45qauelwTcLyg=
- dependencies:
- function-bind "^1.0.2"
-
-hash-base@^2.0.0:
- version "2.0.2"
- resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-2.0.2.tgz#66ea1d856db4e8a5470cadf6fce23ae5244ef2e1"
- integrity sha1-ZuodhW206KVHDK32/OI65SRO8uE=
- dependencies:
- inherits "^2.0.1"
-
hash-base@^3.0.0:
version "3.0.4"
resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.0.4.tgz#5fc8686847ecd73499403319a6b0a3f3f6ae4918"
@@ -3199,22 +3039,12 @@ hash-base@^3.0.0:
safe-buffer "^5.0.1"
hash.js@^1.0.0, hash.js@^1.0.3:
- version "1.1.3"
- resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.3.tgz#340dedbe6290187151c1ea1d777a3448935df846"
- integrity sha512-/UETyP0W22QILqS+6HowevwhEFJ3MBJnwTf75Qob9Wz9t0DPuisL8kW8YZMK62dHAKE1c1p+gY1TtOLY+USEHA==
+ version "1.1.7"
+ resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42"
+ integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==
dependencies:
inherits "^2.0.3"
- minimalistic-assert "^1.0.0"
-
-hawk@3.1.3, hawk@~3.1.3:
- version "3.1.3"
- resolved "https://registry.yarnpkg.com/hawk/-/hawk-3.1.3.tgz#078444bd7c1640b0fe540d2c9b73d59678e8e1c4"
- integrity sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=
- dependencies:
- boom "2.x.x"
- cryptiles "2.x.x"
- hoek "2.x.x"
- sntp "1.x.x"
+ minimalistic-assert "^1.0.1"
hex-color-regex@^1.1.0:
version "1.1.0"
@@ -3230,11 +3060,6 @@ hmac-drbg@^1.0.0:
minimalistic-assert "^1.0.0"
minimalistic-crypto-utils "^1.0.1"
-hoek@2.x.x:
- version "2.16.3"
- resolved "https://registry.yarnpkg.com/hoek/-/hoek-2.16.3.tgz#20bb7403d3cea398e91dc4710a8ff1b8274a25ed"
- integrity sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=
-
homedir-polyfill@^1.0.1:
version "1.0.3"
resolved "https://registry.yarnpkg.com/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz#743298cef4e5af3e194161fbadcc2151d3a058e8"
@@ -3243,9 +3068,9 @@ homedir-polyfill@^1.0.1:
parse-passwd "^1.0.0"
hosted-git-info@^2.1.4:
- version "2.5.0"
- resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.5.0.tgz#6d60e34b3abbc8313062c3b798ef8d901a07af3c"
- integrity sha512-pNgbURSuab90KbTqvRPsseaTxOJCZBD0a7t+haSN33piP9cCM4l0CqdzAif2hUqm716UovKB2ROmiabGAKVXyg==
+ version "2.7.1"
+ resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.7.1.tgz#97f236977bd6e125408930ff6de3eec6281ec047"
+ integrity sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==
hpack.js@^2.1.6:
version "2.1.6"
@@ -3268,11 +3093,11 @@ hsla-regex@^1.0.0:
integrity sha1-wc56MWjIxmFAM6S194d/OyJfnDg=
html-comment-regex@^1.1.0:
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/html-comment-regex/-/html-comment-regex-1.1.1.tgz#668b93776eaae55ebde8f3ad464b307a4963625e"
- integrity sha1-ZouTd26q5V696POtRkswekljYl4=
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/html-comment-regex/-/html-comment-regex-1.1.2.tgz#97d4688aeb5c81886a364faa0cad1dda14d433a7"
+ integrity sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ==
-html-entities@^1.2.0:
+html-entities@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.2.1.tgz#0df29351f0721163515dfb9e5543e5f6eed5162f"
integrity sha1-DfKTUfByEWNRXfueVUPl9u7VFi8=
@@ -3282,20 +3107,20 @@ http-deceiver@^1.2.7:
resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87"
integrity sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=
-http-errors@1.6.2, http-errors@~1.6.2:
- version "1.6.2"
- resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.2.tgz#0a002cc85707192a7e7946ceedc11155f60ec736"
- integrity sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=
+http-errors@1.6.3, http-errors@~1.6.2, http-errors@~1.6.3:
+ version "1.6.3"
+ resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d"
+ integrity sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=
dependencies:
- depd "1.1.1"
+ depd "~1.1.2"
inherits "2.0.3"
- setprototypeof "1.0.3"
- statuses ">= 1.3.1 < 2"
+ setprototypeof "1.1.0"
+ statuses ">= 1.4.0 < 2"
http-parser-js@>=0.4.0:
- version "0.4.10"
- resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.4.10.tgz#92c9c1374c35085f75db359ec56cc257cbb93fa4"
- integrity sha1-ksnBN0w1CF912zWexWzCV8u5P6Q=
+ version "0.5.0"
+ resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.5.0.tgz#d65edbede84349d0dc30320815a15d39cc3cbbd8"
+ integrity sha512-cZdEF7r4gfRIq7ezX9J0T+kQmJNOub71dWbgAXVHDct80TKP4MCETtZQ31xyv38UwgzkWPYF/Xc0ge55dW9Z9w==
http-proxy-middleware@^0.19.1:
version "0.19.1"
@@ -3316,15 +3141,6 @@ http-proxy@^1.17.0:
follow-redirects "^1.0.0"
requires-port "^1.0.0"
-http-signature@~1.1.0:
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.1.1.tgz#df72e267066cd0ac67fb76adf8e134a8fbcf91bf"
- integrity sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=
- dependencies:
- assert-plus "^0.2.0"
- jsprim "^1.2.2"
- sshpk "^1.7.0"
-
http-signature@~1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1"
@@ -3339,10 +3155,12 @@ https-browserify@^1.0.0:
resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73"
integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=
-iconv-lite@0.4.19:
- version "0.4.19"
- resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b"
- integrity sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==
+iconv-lite@0.4.23:
+ version "0.4.23"
+ resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.23.tgz#297871f63be507adcfbfca715d0cd0eed84e9a63"
+ integrity sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==
+ dependencies:
+ safer-buffer ">= 2.1.2 < 3"
iconv-lite@^0.4.4:
version "0.4.24"
@@ -3364,9 +3182,9 @@ icss-utils@^4.1.0:
postcss "^7.0.14"
ieee754@^1.1.4:
- version "1.1.8"
- resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.8.tgz#be33d40ac10ef1926701f6f08a2d86fbfd1ad3e4"
- integrity sha1-vjPUCsEO8ZJnAfbwii2G+/0a0+Q=
+ version "1.1.13"
+ resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84"
+ integrity sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==
iferr@^0.1.5:
version "0.1.5"
@@ -3461,11 +3279,11 @@ ini@^1.3.4, ini@~1.3.0:
integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==
internal-ip@^4.2.0:
- version "4.2.0"
- resolved "https://registry.yarnpkg.com/internal-ip/-/internal-ip-4.2.0.tgz#46e81b638d84c338e5c67e42b1a17db67d0814fa"
- integrity sha512-ZY8Rk+hlvFeuMmG5uH1MXhhdeMntmIaxaInvAmzMq/SHV8rv4Kh+6GiQNNDQd0wZFrcO+FiTBo8lui/osKOyJw==
+ version "4.3.0"
+ resolved "https://registry.yarnpkg.com/internal-ip/-/internal-ip-4.3.0.tgz#845452baad9d2ca3b69c635a137acb9a0dad0907"
+ integrity sha512-S1zBo1D6zcsyuC6PMmY5+55YMILQ9av8lotMx447Bq6SAgo/sDK6y6uUKmuYhW7eacnIhFfsPmCNYdDzsnnDCg==
dependencies:
- default-gateway "^4.0.1"
+ default-gateway "^4.2.0"
ipaddr.js "^1.9.0"
interpret@^1.1.0:
@@ -3474,9 +3292,9 @@ interpret@^1.1.0:
integrity sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw==
invariant@^2.2.2:
- version "2.2.2"
- resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.2.tgz#9e1f56ac0acdb6bf303306f338be3b204ae60360"
- integrity sha1-nh9WrArNtr8wMwbzOL47IErmA2A=
+ version "2.2.4"
+ resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6"
+ integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==
dependencies:
loose-envify "^1.0.0"
@@ -3500,12 +3318,7 @@ ip@^1.1.0, ip@^1.1.5:
resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a"
integrity sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=
-ipaddr.js@1.5.2:
- version "1.5.2"
- resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.5.2.tgz#d4b505bde9946987ccf0fc58d9010ff9607e3fa0"
- integrity sha1-1LUFvemUaYfM8PxY2QEP+WB+P6A=
-
-ipaddr.js@^1.9.0:
+ipaddr.js@1.9.0, ipaddr.js@^1.9.0:
version "1.9.0"
resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.0.tgz#37df74e430a0e47550fe54a2defe30d8acd95f65"
integrity sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA==
@@ -3535,9 +3348,9 @@ is-arrayish@^0.2.1:
integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=
is-arrayish@^0.3.1:
- version "0.3.1"
- resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.1.tgz#c2dfc386abaa0c3e33c48db3fe87059e69065efd"
- integrity sha1-wt/DhquqDD4zxI2z/ocFnmkGXv0=
+ version "0.3.2"
+ resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03"
+ integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==
is-binary-path@^1.0.0:
version "1.0.1"
@@ -3551,13 +3364,6 @@ is-buffer@^1.1.5:
resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be"
integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==
-is-builtin-module@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-1.0.0.tgz#540572d34f7ac3119f8f76c30cbc1b1e037affbe"
- integrity sha1-VAVy0096wxGfj3bDDLwbHgN6/74=
- dependencies:
- builtin-modules "^1.0.0"
-
is-callable@^1.1.4:
version "1.1.4"
resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.4.tgz#1e1adf219e1eeb684d691f9d6a05ff0d30a24d75"
@@ -3661,9 +3467,9 @@ is-glob@^3.1.0:
is-extglob "^2.1.0"
is-glob@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.0.tgz#9521c76845cc2610a85203ddf080a958c2ffabc0"
- integrity sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A=
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc"
+ integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==
dependencies:
is-extglob "^2.1.1"
@@ -3679,31 +3485,24 @@ is-obj@^1.0.0:
resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f"
integrity sha1-PkcprB9f3gJc19g6iW2rn09n2w8=
-is-odd@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/is-odd/-/is-odd-1.0.0.tgz#3b8a932eb028b3775c39bb09e91767accdb69088"
- integrity sha1-O4qTLrAos3dcObsJ6RdnrM22kIg=
- dependencies:
- is-number "^3.0.0"
-
-is-path-cwd@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-1.0.0.tgz#d225ec23132e89edd38fda767472e62e65f1106d"
- integrity sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=
+is-path-cwd@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-2.1.0.tgz#2e0c7e463ff5b7a0eb60852d851a6809347a124c"
+ integrity sha512-Sc5j3/YnM8tDeyCsVeKlm/0p95075DyLmDEIkSgQ7mXkrOX+uTCtmQFm0CYzVyJwcCCmO3k8qfJt17SxQwB5Zw==
-is-path-in-cwd@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/is-path-in-cwd/-/is-path-in-cwd-1.0.0.tgz#6477582b8214d602346094567003be8a9eac04dc"
- integrity sha1-ZHdYK4IU1gI0YJRWcAO+ip6sBNw=
+is-path-in-cwd@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz#bfe2dca26c69f397265a4009963602935a053acb"
+ integrity sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ==
dependencies:
- is-path-inside "^1.0.0"
+ is-path-inside "^2.1.0"
-is-path-inside@^1.0.0:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-1.0.1.tgz#8ef5b7de50437a3fdca6b4e865ef7aa55cb48036"
- integrity sha1-jvW33lBDej/cprToZe96pVy0gDY=
+is-path-inside@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-2.1.0.tgz#7c9810587d659a40d27bcdb4d5616eab059494b2"
+ integrity sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg==
dependencies:
- path-is-inside "^1.0.1"
+ path-is-inside "^1.0.2"
is-plain-object@^2.0.1, is-plain-object@^2.0.3, is-plain-object@^2.0.4:
version "2.0.4"
@@ -3791,29 +3590,24 @@ isstream@~0.1.2:
integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=
js-base64@^2.1.8:
- version "2.4.3"
- resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.4.3.tgz#2e545ec2b0f2957f41356510205214e98fad6582"
- integrity sha512-H7ErYLM34CvDMto3GbD6xD0JLUGYXR3QTcH6B/tr4Hi/QpSThnCsIp+Sy5FRTw3B0d6py4HcNkW7nO/wdtGWEw==
+ version "2.5.1"
+ resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.5.1.tgz#1efa39ef2c5f7980bb1784ade4a8af2de3291121"
+ integrity sha512-M7kLczedRMYX4L8Mdh4MzyAMM9O5osx+4FcOQuTvr3A9F2D9S5JXheN0ewNbrvK2UatkTRhL5ejGmGSjNMiZuw==
js-levenshtein@^1.1.3:
version "1.1.6"
resolved "https://registry.yarnpkg.com/js-levenshtein/-/js-levenshtein-1.1.6.tgz#c6cee58eb3550372df8deb85fad5ce66ce01d59d"
integrity sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==
-js-tokens@^3.0.0:
- version "3.0.2"
- resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b"
- integrity sha1-mGbfOVECEw449/mWvOtlRDIJwls=
-
-js-tokens@^4.0.0:
+"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
-js-yaml@^3.12.0, js-yaml@^3.12.2, js-yaml@^3.9.0:
- version "3.12.2"
- resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.12.2.tgz#ef1d067c5a9d9cb65bd72f285b5d8105c77f14fc"
- integrity sha512-QHn/Lh/7HhZ/Twc7vJYQTkjuCa0kaCcDcjK5Zlk2rvnUpy7DxMJ23+Jc2dcyvltwQVg1nygAVlB2oRDFHoRS5Q==
+js-yaml@^3.12.2, js-yaml@^3.13.0, js-yaml@^3.13.1, js-yaml@^3.9.0:
+ version "3.13.1"
+ resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847"
+ integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==
dependencies:
argparse "^1.0.7"
esprima "^4.0.0"
@@ -3838,11 +3632,6 @@ json-parse-better-errors@^1.0.1, json-parse-better-errors@^1.0.2:
resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9"
integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==
-json-schema-traverse@^0.3.0:
- version "0.3.1"
- resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz#349a6d44c53a51de89b40805c5d5e59b417d3340"
- integrity sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=
-
json-schema-traverse@^0.4.1:
version "0.4.1"
resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
@@ -3853,13 +3642,6 @@ json-schema@0.2.3:
resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13"
integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=
-json-stable-stringify@^1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af"
- integrity sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=
- dependencies:
- jsonify "~0.0.0"
-
json-stringify-safe@~5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
@@ -3870,11 +3652,6 @@ json3@^3.3.2:
resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.2.tgz#3c0434743df93e2f5c42aee7b19bcb483575f4e1"
integrity sha1-PAQ0dD35Pi9cQq7nsZvLSDV19OE=
-json5@^0.5.0:
- version "0.5.1"
- resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821"
- integrity sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=
-
json5@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe"
@@ -3889,11 +3666,6 @@ json5@^2.1.0:
dependencies:
minimist "^1.2.0"
-jsonify@~0.0.0:
- version "0.0.0"
- resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73"
- integrity sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=
-
jsprim@^1.2.2:
version "1.4.1"
resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2"
@@ -3904,10 +3676,10 @@ jsprim@^1.2.2:
json-schema "0.2.3"
verror "1.10.0"
-killable@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/killable/-/killable-1.0.0.tgz#da8b84bd47de5395878f95d64d02f2449fe05e6b"
- integrity sha1-2ouEvUfeU5WHj5XWTQLyRJ/gXms=
+killable@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/killable/-/killable-1.0.1.tgz#4c8ce441187a061c7474fb87ca08e2a638194892"
+ integrity sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg==
kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0:
version "3.2.2"
@@ -3923,7 +3695,7 @@ kind-of@^4.0.0:
dependencies:
is-buffer "^1.1.5"
-kind-of@^5.0.0, kind-of@^5.0.2:
+kind-of@^5.0.0:
version "5.1.0"
resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d"
integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==
@@ -3941,13 +3713,6 @@ last-call-webpack-plugin@^3.0.0:
lodash "^4.17.5"
webpack-sources "^1.1.0"
-lazy-cache@^2.0.2:
- version "2.0.2"
- resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-2.0.2.tgz#b9190a4f913354694840859f8a8f7084d8822264"
- integrity sha1-uRkKT5EzVGlIQIWfio9whNiCImQ=
- dependencies:
- set-getter "^0.1.0"
-
lcid@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835"
@@ -3974,20 +3739,11 @@ load-json-file@^1.0.0:
strip-bom "^2.0.0"
loader-runner@^2.3.0:
- version "2.3.0"
- resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.3.0.tgz#f482aea82d543e07921700d5a46ef26fdac6b8a2"
- integrity sha1-9IKuqC1UPgeSFwDVpG7yb9rGuKI=
-
-loader-utils@^1.0.1, loader-utils@^1.0.2, loader-utils@^1.1.0:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.1.0.tgz#c98aef488bcceda2ffb5e2de646d6a754429f5cd"
- integrity sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=
- dependencies:
- big.js "^3.1.3"
- emojis-list "^2.0.0"
- json5 "^0.5.0"
+ version "2.4.0"
+ resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.4.0.tgz#ed47066bfe534d7e84c4c7b9998c2a75607d9357"
+ integrity sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==
-loader-utils@^1.2.3:
+loader-utils@^1.0.1, loader-utils@^1.0.2, loader-utils@^1.1.0, loader-utils@^1.2.3:
version "1.2.3"
resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.2.3.tgz#1ff5dc6911c9f0a062531a4c04b609406108c2c7"
integrity sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==
@@ -4009,17 +3765,7 @@ lodash._reinterpolate@~3.0.0:
resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d"
integrity sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=
-lodash.assign@^4.2.0:
- version "4.2.0"
- resolved "https://registry.yarnpkg.com/lodash.assign/-/lodash.assign-4.2.0.tgz#0d99f3ccd7a6d261d19bdaeb9245005d285808e7"
- integrity sha1-DZnzzNem0mHRm9rrkkUAXShYCOc=
-
-lodash.clonedeep@^4.3.2:
- version "4.5.0"
- resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef"
- integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=
-
-lodash.get@^4.0, lodash.get@^4.4.2:
+lodash.get@^4.0:
version "4.4.2"
resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99"
integrity sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=
@@ -4034,11 +3780,6 @@ lodash.memoize@^4.1.2:
resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe"
integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=
-lodash.mergewith@^4.6.0:
- version "4.6.1"
- resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.1.tgz#639057e726c3afbdb3e7d42741caa8d6e4335927"
- integrity sha512-eWw5r+PYICtEBgrBE5hhlT6aAa75f411bgDz/ZL2KZqYV03USvucsxcHUIlGTDTECs1eunpI7HOV7U+WLDvNdQ==
-
lodash.tail@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/lodash.tail/-/lodash.tail-4.1.1.tgz#d2333a36d9e7717c8ad2f7cacafec7c32b444664"
@@ -4064,32 +3805,22 @@ lodash.uniq@^4.5.0:
resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=
-lodash@3.x:
- version "3.10.1"
- resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6"
- integrity sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=
-
-lodash@^4.0.0, lodash@~4.17.4:
- version "4.17.5"
- resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.5.tgz#99a92d65c0272debe8c96b6057bc8fbfa3bed511"
- integrity sha512-svL3uiZf1RwhH+cWrfZn3A4+U58wbP0tGVTLQPbjplZxZ8ROD9VLuNgsRniTlLe7OlSqR79RUehXgpBW/s0IQw==
-
-lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.5:
+lodash@^4.0.0, lodash@^4.17.11, lodash@^4.17.5, lodash@~4.17.10:
version "4.17.11"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d"
integrity sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==
-loglevel@^1.4.1:
+loglevel@^1.6.1:
version "1.6.1"
resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.6.1.tgz#e0fc95133b6ef276cdc8887cdaf24aa6f156f8fa"
integrity sha1-4PyVEztu8nbNyIh82vJKpvFW+Po=
loose-envify@^1.0.0:
- version "1.3.1"
- resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.3.1.tgz#d1a8ad33fa9ce0e713d65fdd0ac8b748d478c848"
- integrity sha1-0aitM/qc4OcT1l/dCsi3SNR4yEg=
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
+ integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
dependencies:
- js-tokens "^3.0.0"
+ js-tokens "^3.0.0 || ^4.0.0"
loud-rejection@^1.0.0:
version "1.6.0"
@@ -4100,9 +3831,9 @@ loud-rejection@^1.0.0:
signal-exit "^3.0.0"
lru-cache@^4.0.1:
- version "4.1.1"
- resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.1.tgz#622e32e82488b49279114a4f9ecf45e7cd6bba55"
- integrity sha512-q4spe4KTfsAS1SUHLO0wz8Qiyf1+vMIAgpRYioFYDMNqKfHQbg+AVDH3i4fvpl71/P1L0dBl+fQi+P37UYf0ew==
+ version "4.1.5"
+ resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd"
+ integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==
dependencies:
pseudomap "^1.0.2"
yallist "^2.1.2"
@@ -4114,12 +3845,13 @@ lru-cache@^5.1.1:
dependencies:
yallist "^3.0.2"
-make-dir@^1.0.0:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.1.0.tgz#19b4369fe48c116f53c2af95ad102c0e39e85d51"
- integrity sha512-0Pkui4wLJ7rxvmfUvs87skoEaxmu0hCUApF8nonzpl7q//FWp9zu8W61Scz4sd/kUiqDxvUhtoam2efDyiBzcA==
+make-dir@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5"
+ integrity sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==
dependencies:
- pify "^3.0.0"
+ pify "^4.0.1"
+ semver "^5.6.0"
mamacro@^0.0.3:
version "0.0.3"
@@ -4151,12 +3883,13 @@ map-visit@^1.0.0:
object-visit "^1.0.0"
md5.js@^1.3.4:
- version "1.3.4"
- resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.4.tgz#e9bdbde94a20a5ac18b04340fc5764d5b09d901d"
- integrity sha1-6b296UogpawYsENA/Fdk1bCdkB0=
+ version "1.3.5"
+ resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f"
+ integrity sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==
dependencies:
hash-base "^3.0.0"
inherits "^2.0.1"
+ safe-buffer "^5.1.2"
mdn-data@~1.1.0:
version "1.1.4"
@@ -4169,12 +3902,12 @@ media-typer@0.3.0:
integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=
mem@^4.0.0:
- version "4.1.0"
- resolved "https://registry.yarnpkg.com/mem/-/mem-4.1.0.tgz#aeb9be2d21f47e78af29e4ac5978e8afa2ca5b8a"
- integrity sha512-I5u6Q1x7wxO0kdOpYBB28xueHADYps5uty/zg936CiG8NTe5sJL8EjrCuLneuDW3PlMdZBGDIn8BirEVdovZvg==
+ version "4.3.0"
+ resolved "https://registry.yarnpkg.com/mem/-/mem-4.3.0.tgz#461af497bc4ae09608cdb2e60eefb69bff744178"
+ integrity sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==
dependencies:
map-age-cleaner "^0.1.1"
- mimic-fn "^1.0.0"
+ mimic-fn "^2.0.0"
p-is-promise "^2.0.0"
memory-fs@^0.4.0, memory-fs@^0.4.1, memory-fs@~0.4.1:
@@ -4211,7 +3944,7 @@ methods@~1.1.2:
resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=
-micromatch@^3.0.4, micromatch@^3.1.10, micromatch@^3.1.8:
+micromatch@^3.0.4, micromatch@^3.1.10, micromatch@^3.1.4, micromatch@^3.1.8:
version "3.1.10"
resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23"
integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==
@@ -4230,25 +3963,6 @@ micromatch@^3.0.4, micromatch@^3.1.10, micromatch@^3.1.8:
snapdragon "^0.8.1"
to-regex "^3.0.2"
-micromatch@^3.1.4:
- version "3.1.5"
- resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.5.tgz#d05e168c206472dfbca985bfef4f57797b4cd4ba"
- integrity sha512-ykttrLPQrz1PUJcXjwsTUjGoPJ64StIGNE2lGVD1c9CuguJ+L7/navsE8IcDNndOoCMvYV0qc/exfVbMHkUhvA==
- dependencies:
- arr-diff "^4.0.0"
- array-unique "^0.3.2"
- braces "^2.3.0"
- define-property "^1.0.0"
- extend-shallow "^2.0.1"
- extglob "^2.0.2"
- fragment-cache "^0.2.1"
- kind-of "^6.0.0"
- nanomatch "^1.2.5"
- object.pick "^1.3.0"
- regex-not "^1.0.0"
- snapdragon "^0.8.1"
- to-regex "^3.0.1"
-
miller-rabin@^4.0.0:
version "4.0.1"
resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d"
@@ -4257,34 +3971,17 @@ miller-rabin@^4.0.0:
bn.js "^4.0.0"
brorand "^1.0.1"
-"mime-db@>= 1.30.0 < 2":
- version "1.32.0"
- resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.32.0.tgz#485b3848b01a3cda5f968b4882c0771e58e09414"
- integrity sha512-+ZWo/xZN40Tt6S+HyakUxnSOgff+JEdaneLWIm0Z6LmpCn5DMcZntLyUY5c/rTDog28LhXLKOUZKoTxTCAdBVw==
-
-mime-db@~1.30.0:
- version "1.30.0"
- resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.30.0.tgz#74c643da2dd9d6a45399963465b26d5ca7d71f01"
- integrity sha1-dMZD2i3Z1qRTmZY0ZbJtXKfXHwE=
-
-mime-db@~1.38.0:
- version "1.38.0"
- resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.38.0.tgz#1a2aab16da9eb167b49c6e4df2d9c68d63d8e2ad"
- integrity sha512-bqVioMFFzc2awcdJZIzR3HjZFX20QhilVS7hytkKrv7xFAn8bM1gzc/FOX2awLISvWe0PV8ptFKcon+wZ5qYkg==
-
-mime-types@^2.1.12, mime-types@~2.1.15, mime-types@~2.1.16, mime-types@~2.1.17, mime-types@~2.1.7:
- version "2.1.17"
- resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.17.tgz#09d7a393f03e995a79f8af857b70a9e0ab16557a"
- integrity sha1-Cdejk/A+mVp5+K+Fe3Cp4KsWVXo=
- dependencies:
- mime-db "~1.30.0"
+mime-db@1.40.0, "mime-db@>= 1.40.0 < 2":
+ version "1.40.0"
+ resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.40.0.tgz#a65057e998db090f732a68f6c276d387d4126c32"
+ integrity sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==
-mime-types@~2.1.19:
- version "2.1.22"
- resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.22.tgz#fe6b355a190926ab7698c9a0556a11199b2199bd"
- integrity sha512-aGl6TZGnhm/li6F7yx82bJiBZwgiEa4Hf6CNr8YO+r5UHr53tSTYZb102zyU50DOWWKeOv0uQLRL0/9EiKWCog==
+mime-types@^2.1.12, mime-types@~2.1.17, mime-types@~2.1.19, mime-types@~2.1.24:
+ version "2.1.24"
+ resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.24.tgz#b6f8d0b3e951efb77dedeca194cff6d16f676f81"
+ integrity sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==
dependencies:
- mime-db "~1.38.0"
+ mime-db "1.40.0"
mime@1.4.1:
version "1.4.1"
@@ -4292,14 +3989,14 @@ mime@1.4.1:
integrity sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==
mime@^2.3.1:
- version "2.4.0"
- resolved "https://registry.yarnpkg.com/mime/-/mime-2.4.0.tgz#e051fd881358585f3279df333fe694da0bcffdd6"
- integrity sha512-ikBcWwyqXQSHKtciCcctu9YfPbFYZ4+gbHEmE0Q8jzcTYQg5dHCr3g2wwAZjPoJfQVXZq6KXAjpXOTf5/cjT7w==
+ version "2.4.2"
+ resolved "https://registry.yarnpkg.com/mime/-/mime-2.4.2.tgz#ce5229a5e99ffc313abac806b482c10e7ba6ac78"
+ integrity sha512-zJBfZDkwRu+j3Pdd2aHsR5GfH2jIWhmL1ZzBoc+X+3JEti2hbArWcyJ+1laC1D2/U/W1a/+Cegj0/OnEU2ybjg==
-mimic-fn@^1.0.0:
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022"
- integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==
+mimic-fn@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
+ integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==
mini-css-extract-plugin@^0.5.0:
version "0.5.0"
@@ -4310,17 +4007,17 @@ mini-css-extract-plugin@^0.5.0:
schema-utils "^1.0.0"
webpack-sources "^1.1.0"
-minimalistic-assert@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz#702be2dda6b37f4836bcb3f5db56641b64a1d3d3"
- integrity sha1-cCvi3aazf0g2vLP121ZkG2Sh09M=
+minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7"
+ integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==
minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a"
integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=
-"minimatch@2 || 3", minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.4, minimatch@~3.0.2:
+minimatch@^3.0.4, minimatch@~3.0.2:
version "3.0.4"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
@@ -4426,32 +4123,10 @@ multicast-dns@^6.0.1:
dns-packet "^1.3.1"
thunky "^1.0.2"
-nan@^2.10.0, nan@^2.9.2:
- version "2.12.1"
- resolved "https://registry.yarnpkg.com/nan/-/nan-2.12.1.tgz#7b1aa193e9aa86057e3c7bbd0ac448e770925552"
- integrity sha512-JY7V6lRkStKcKTvHO5NVSQRv+RV+FIL5pvDoLiAtSL9pKlC5x9PKQcZDsq7m4FO4d57mkhC6Z+QhAh3Jdk5JFw==
-
-nan@^2.3.0:
- version "2.8.0"
- resolved "https://registry.yarnpkg.com/nan/-/nan-2.8.0.tgz#ed715f3fe9de02b57a5e6252d90a96675e1f085a"
- integrity sha1-7XFfP+neArV6XmJS2QqWZ14fCFo=
-
-nanomatch@^1.2.5:
- version "1.2.7"
- resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.7.tgz#53cd4aa109ff68b7f869591fdc9d10daeeea3e79"
- integrity sha512-/5ldsnyurvEw7wNpxLFgjVvBLMta43niEYOy0CJ4ntcYSbx6bugRUTQeFb4BR/WanEL1o3aQgHuVLHQaB6tOqg==
- dependencies:
- arr-diff "^4.0.0"
- array-unique "^0.3.2"
- define-property "^1.0.0"
- extend-shallow "^2.0.1"
- fragment-cache "^0.2.1"
- is-odd "^1.0.0"
- kind-of "^5.0.2"
- object.pick "^1.3.0"
- regex-not "^1.0.0"
- snapdragon "^0.8.1"
- to-regex "^3.0.1"
+nan@^2.12.1, nan@^2.13.2:
+ version "2.13.2"
+ resolved "https://registry.yarnpkg.com/nan/-/nan-2.13.2.tgz#f51dc7ae66ba7d5d55e1e6d4d8092e802c9aefe7"
+ integrity sha512-TghvYc72wlMGMVMluVo9WRJc0mB8KxxF/gZ4YYFy7V2ZQX9l7rgbPg7vjS9mt6U5HXODVFVI2bOduCzwOMv/lw==
nanomatch@^1.2.9:
version "1.2.13"
@@ -4471,18 +4146,18 @@ nanomatch@^1.2.9:
to-regex "^3.0.1"
needle@^2.2.1:
- version "2.2.4"
- resolved "https://registry.yarnpkg.com/needle/-/needle-2.2.4.tgz#51931bff82533b1928b7d1d69e01f1b00ffd2a4e"
- integrity sha512-HyoqEb4wr/rsoaIDfTH2aVL9nWtQqba2/HvMv+++m8u0dz808MaagKILxtfeSN7QU7nvbQ79zk3vYOJp9zsNEA==
+ version "2.3.1"
+ resolved "https://registry.yarnpkg.com/needle/-/needle-2.3.1.tgz#d272f2f4034afb9c4c9ab1379aabc17fc85c9388"
+ integrity sha512-CaLXV3W8Vnbps8ZANqDGz7j4x7Yj1LW4TWF/TQuDfj7Cfx4nAPTvw98qgTevtto1oHDrh3pQkaODbqupXlsWTg==
dependencies:
- debug "^2.1.2"
+ debug "^4.1.0"
iconv-lite "^0.4.4"
sax "^1.2.4"
-negotiator@0.6.1:
- version "0.6.1"
- resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9"
- integrity sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=
+negotiator@0.6.2:
+ version "0.6.2"
+ resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb"
+ integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==
neo-async@^2.5.0:
version "2.6.0"
@@ -4494,10 +4169,10 @@ nice-try@^1.0.4:
resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366"
integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==
-node-forge@0.7.1:
- version "0.7.1"
- resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.7.1.tgz#9da611ea08982f4b94206b3beb4cc9665f20c300"
- integrity sha1-naYR6giYL0uUIGs760zJZl8gwwA=
+node-forge@0.7.5:
+ version "0.7.5"
+ resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.7.5.tgz#6c152c345ce11c52f465c2abd957e8639cd674df"
+ integrity sha512-MmbQJ2MTESTjt3Gi/3yG1wGpIMhUfcIypUCGtTizFR9IiccFwxSpfp0vtIZlkFclEqERemxfnSdZEMR9VqqEFQ==
node-gyp@^3.8.0:
version "3.8.0"
@@ -4518,9 +4193,9 @@ node-gyp@^3.8.0:
which "1"
node-libs-browser@^2.0.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-2.1.0.tgz#5f94263d404f6e44767d726901fff05478d600df"
- integrity sha512-5AzFzdoIMb89hBGMZglEegffzgRg+ZFoUmisQ8HI4j1KDdpx13J0taNp2y9xPbur6W61gepGDDotGBVQ7mfUCg==
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-2.2.0.tgz#c72f60d9d46de08a940dedbb25f3ffa2f9bbaa77"
+ integrity sha512-5MQunG/oyOaBdttrL40dA7bUfPORLRWMUJLQtMg7nluxUvk5XwnLdL9twQHFAjRx/y7mIMkLKT9++qPbbk6BZA==
dependencies:
assert "^1.1.1"
browserify-zlib "^0.2.0"
@@ -4529,7 +4204,7 @@ node-libs-browser@^2.0.0:
constants-browserify "^1.0.0"
crypto-browserify "^3.11.0"
domain-browser "^1.1.1"
- events "^1.0.0"
+ events "^3.0.0"
https-browserify "^1.0.0"
os-browserify "^0.3.0"
path-browserify "0.0.0"
@@ -4543,13 +4218,13 @@ node-libs-browser@^2.0.0:
timers-browserify "^2.0.4"
tty-browserify "0.0.0"
url "^0.11.0"
- util "^0.10.3"
+ util "^0.11.0"
vm-browserify "0.0.4"
-node-pre-gyp@^0.10.0:
- version "0.10.3"
- resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.10.3.tgz#3070040716afdc778747b61b6887bf78880b80fc"
- integrity sha512-d1xFs+C/IPS8Id0qPTZ4bUT8wWryfR/OzzAFxweG+uLN85oPzyo2Iw6bVlLQ/JOdgNonXLCoRyqDzDWq4iw72A==
+node-pre-gyp@^0.12.0:
+ version "0.12.0"
+ resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.12.0.tgz#39ba4bb1439da030295f899e3b520b7785766149"
+ integrity sha512-4KghwV8vH5k+g2ylT+sLTjy5wmUOb9vPhnM8NHvRf9dHmnW/CndrFXy2aRPaPST6dugXSdHXfeaHQm77PIz/1A==
dependencies:
detect-libc "^1.0.2"
mkdirp "^0.5.1"
@@ -4562,34 +4237,17 @@ node-pre-gyp@^0.10.0:
semver "^5.3.0"
tar "^4"
-node-pre-gyp@^0.6.39:
- version "0.6.39"
- resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.6.39.tgz#c00e96860b23c0e1420ac7befc5044e1d78d8649"
- integrity sha512-OsJV74qxnvz/AMGgcfZoDaeDXKD3oY3QVIbBmwszTFkRisTSXbMQyn4UWzUMOtA5SVhrBZOTp0wcoSBgfMfMmQ==
- dependencies:
- detect-libc "^1.0.2"
- hawk "3.1.3"
- mkdirp "^0.5.1"
- nopt "^4.0.1"
- npmlog "^4.0.2"
- rc "^1.1.7"
- request "2.81.0"
- rimraf "^2.6.1"
- semver "^5.3.0"
- tar "^2.2.1"
- tar-pack "^3.4.0"
-
-node-releases@^1.1.8:
- version "1.1.9"
- resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.9.tgz#70d0985ec4bf7de9f08fc481f5dae111889ca482"
- integrity sha512-oic3GT4OtbWWKfRolz5Syw0Xus0KRFxeorLNj0s93ofX6PWyuzKjsiGxsCtWktBwwmTF6DdRRf2KreGqeOk5KA==
+node-releases@^1.1.17:
+ version "1.1.18"
+ resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.18.tgz#cc98fd75598a324a77188ebddf6650e9cbd8b1d5"
+ integrity sha512-/mnVgm6u/8OwlIsoyRXtTI0RfQcxZoAZbdwyXap0EeWwcOpDDymyCHM2/aR9XKmHXrvizHoPAOs0pcbiJ6RUaA==
dependencies:
semver "^5.3.0"
node-sass@^4.11.0:
- version "4.11.0"
- resolved "https://registry.yarnpkg.com/node-sass/-/node-sass-4.11.0.tgz#183faec398e9cbe93ba43362e2768ca988a6369a"
- integrity sha512-bHUdHTphgQJZaF1LASx0kAviPH7sGlcyNhWade4eVIpFp6tsn7SV8xNMTbsQFpEV9VXpnwTTnNYlfsZXgGgmkA==
+ version "4.12.0"
+ resolved "https://registry.yarnpkg.com/node-sass/-/node-sass-4.12.0.tgz#0914f531932380114a30cc5fa4fa63233a25f017"
+ integrity sha512-A1Iv4oN+Iel6EPv77/HddXErL2a+gZ4uBeZUy+a8O35CFYTXhgA8MgLCWBtwpGZdCvTvQ9d+bQxX/QC36GDPpQ==
dependencies:
async-foreach "^0.1.3"
chalk "^1.1.1"
@@ -4598,12 +4256,10 @@ node-sass@^4.11.0:
get-stdin "^4.0.1"
glob "^7.0.3"
in-publish "^2.0.0"
- lodash.assign "^4.2.0"
- lodash.clonedeep "^4.3.2"
- lodash.mergewith "^4.6.0"
+ lodash "^4.17.11"
meow "^3.7.0"
mkdirp "^0.5.1"
- nan "^2.10.0"
+ nan "^2.13.2"
node-gyp "^3.8.0"
npmlog "^4.0.0"
request "^2.88.0"
@@ -4627,12 +4283,12 @@ nopt@^4.0.1:
osenv "^0.1.4"
normalize-package-data@^2.3.2, normalize-package-data@^2.3.4:
- version "2.4.0"
- resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.4.0.tgz#12f95a307d58352075a04907b84ac8be98ac012f"
- integrity sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==
+ version "2.5.0"
+ resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8"
+ integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==
dependencies:
hosted-git-info "^2.1.4"
- is-builtin-module "^1.0.0"
+ resolve "^1.10.0"
semver "2 || 3 || 4 || 5"
validate-npm-package-license "^3.0.1"
@@ -4705,17 +4361,12 @@ number-is-nan@^1.0.0:
resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d"
integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=
-oauth-sign@~0.8.1:
- version "0.8.2"
- resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43"
- integrity sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=
-
oauth-sign@~0.9.0:
version "0.9.0"
resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455"
integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==
-object-assign@^4.0.1, object-assign@^4.1.0:
+object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
@@ -4730,14 +4381,9 @@ object-copy@^0.1.0:
kind-of "^3.0.3"
object-keys@^1.0.11, object-keys@^1.0.12:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.0.tgz#11bd22348dd2e096a045ab06f6c85bcc340fa032"
- integrity sha512-6OO5X1+2tYkNyNEx6TsCxEqFfRWaqx6EtMiSbGrw8Ob8v9Ne+Hl8rBAgLBZn5wjEz3s/s6U1WXFUFOcxxAwUpg==
-
-object-keys@^1.0.8:
- version "1.0.11"
- resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.0.11.tgz#c54601778ad560f1142ce0e01bcca8b56d13426d"
- integrity sha1-xUYBd4rVYPEULODgG8yotW0TQm0=
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e"
+ integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==
object-visit@^1.0.0:
version "1.0.1"
@@ -4781,12 +4427,7 @@ object.values@^1.1.0:
function-bind "^1.1.1"
has "^1.0.3"
-obuf@^1.0.0:
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.1.tgz#104124b6c602c6796881a042541d36db43a5264e"
- integrity sha1-EEEktsYCxnlogaBCVB0220OlJk4=
-
-obuf@^1.1.2:
+obuf@^1.0.0, obuf@^1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e"
integrity sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==
@@ -4798,22 +4439,22 @@ on-finished@~2.3.0:
dependencies:
ee-first "1.1.1"
-on-headers@~1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.1.tgz#928f5d0f470d49342651ea6794b0857c100693f7"
- integrity sha1-ko9dD0cNSTQmUepnlLCFfBAGk/c=
+on-headers@~1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f"
+ integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==
-once@^1.3.0, once@^1.3.1, once@^1.3.3, once@^1.4.0:
+once@^1.3.0, once@^1.3.1, once@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E=
dependencies:
wrappy "1"
-opn@^5.1.0:
- version "5.2.0"
- resolved "https://registry.yarnpkg.com/opn/-/opn-5.2.0.tgz#71fdf934d6827d676cecbea1531f95d354641225"
- integrity sha512-Jd/GpzPyHF4P2/aNOVmS3lfMSWV9J7cOhCG1s08XCEAsPkB7lp6ddiU0J7XzyQRDUh8BqJ7PchfINjR8jyofRQ==
+opn@^5.5.0:
+ version "5.5.0"
+ resolved "https://registry.yarnpkg.com/opn/-/opn-5.5.0.tgz#fc7164fab56d235904c51c3b27da6758ca3b9bfc"
+ integrity sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA==
dependencies:
is-wsl "^1.1.0"
@@ -4864,9 +4505,9 @@ os-tmpdir@^1.0.0:
integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=
osenv@0, osenv@^0.1.4:
- version "0.1.4"
- resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.4.tgz#42fe6d5953df06c8064be6f176c3d05aaaa34644"
- integrity sha1-Qv5tWVPfBsgGS+bxdsPQWqqjRkQ=
+ version "0.1.5"
+ resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410"
+ integrity sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==
dependencies:
os-homedir "^1.0.0"
os-tmpdir "^1.0.0"
@@ -4882,9 +4523,9 @@ p-finally@^1.0.0:
integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=
p-is-promise@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-2.0.0.tgz#7554e3d572109a87e1f3f53f6a7d85d1b194f4c5"
- integrity sha512-pzQPhYMCAgLAKPWD2jC3Se9fEfrD9npNos0y150EeqZll7akhEgGhTW/slB6lHku8AvYGiJ+YJ5hfHKePPgFWg==
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-2.1.0.tgz#918cebaea248a62cf7ffab8e3bca8c5f882fc42e"
+ integrity sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==
p-limit@^2.0.0:
version "2.2.0"
@@ -4900,20 +4541,20 @@ p-locate@^3.0.0:
dependencies:
p-limit "^2.0.0"
-p-map@^1.1.1:
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/p-map/-/p-map-1.2.0.tgz#e4e94f311eabbc8633a1e79908165fca26241b6b"
- integrity sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA==
+p-map@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/p-map/-/p-map-2.1.0.tgz#310928feef9c9ecc65b68b17693018a665cea175"
+ integrity sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==
p-try@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.0.0.tgz#85080bb87c64688fa47996fe8f7dfbe8211760b1"
- integrity sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ==
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6"
+ integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==
pako@~1.0.5:
- version "1.0.6"
- resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.6.tgz#0101211baa70c4bca4a0f63f2206e97b7dfaf258"
- integrity sha512-lQe48YPsMJAig+yngZ87Lus+NF+3mtu7DVOBu6b/gHO1YpKwIj5AWjZ/TOS7i46HD/UixzWb1zeWDZfGZ3iYcg==
+ version "1.0.10"
+ resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.10.tgz#4328badb5086a426aa90f541977d4955da5c9732"
+ integrity sha512-0DTvPVU3ed8+HNXOu5Bs+o//Mbdj9VNQMUOe9oKCwh8l0GNwpTDMKCWbRjgtD291AWnkAgkqA/LOnQS8AmS1tw==
parallel-transform@^1.1.0:
version "1.1.0"
@@ -4925,15 +4566,16 @@ parallel-transform@^1.1.0:
readable-stream "^2.1.5"
parse-asn1@^5.0.0:
- version "5.1.0"
- resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.0.tgz#37c4f9b7ed3ab65c74817b5f2480937fbf97c712"
- integrity sha1-N8T5t+06tlx0gXtfJICTf7+XxxI=
+ version "5.1.4"
+ resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.4.tgz#37f6628f823fbdeb2273b4d540434a22f3ef1fcc"
+ integrity sha512-Qs5duJcuvNExRfFZ99HDD3z4mAi3r9Wl/FOjEOijlxwCZs7E7mW2vjTpgQ4J8LpTF8x5v+1Vn5UQFejmWT11aw==
dependencies:
asn1.js "^4.0.0"
browserify-aes "^1.0.0"
create-hash "^1.1.0"
evp_bytestokey "^1.0.0"
pbkdf2 "^3.0.3"
+ safe-buffer "^5.1.1"
parse-json@^2.2.0:
version "2.2.0"
@@ -4956,9 +4598,9 @@ parse-passwd@^1.0.0:
integrity sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=
parseurl@~1.3.2:
- version "1.3.2"
- resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.2.tgz#fc289d4ed8993119460c156253262cdc8de65bf3"
- integrity sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=
+ version "1.3.3"
+ resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4"
+ integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==
pascalcase@^0.1.1:
version "0.1.1"
@@ -4997,7 +4639,7 @@ path-is-absolute@^1.0.0:
resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18=
-path-is-inside@^1.0.1:
+path-is-inside@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53"
integrity sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=
@@ -5007,11 +4649,6 @@ path-key@^2.0.0, path-key@^2.0.1:
resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40"
integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=
-path-parse@^1.0.5:
- version "1.0.5"
- resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.5.tgz#3c1adf871ea9cd6c9431b6ea2bd74a0ff055c4c1"
- integrity sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=
-
path-parse@^1.0.6:
version "1.0.6"
resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c"
@@ -5032,9 +4669,9 @@ path-type@^1.0.0:
pinkie-promise "^2.0.0"
pbkdf2@^3.0.3:
- version "3.0.14"
- resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.14.tgz#a35e13c64799b06ce15320f459c230e68e73bade"
- integrity sha512-gjsZW9O34fm0R7PaLHRJmLLVfSoesxztjPjE9o6R+qtVJij90ltg1joIovN9GKrRW3t1PzhDDG3UMEMFfZ+1wA==
+ version "3.0.17"
+ resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.17.tgz#976c206530617b14ebb32114239f7b09336e93a6"
+ integrity sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA==
dependencies:
create-hash "^1.1.2"
create-hmac "^1.1.4"
@@ -5042,11 +4679,6 @@ pbkdf2@^3.0.3:
safe-buffer "^5.0.1"
sha.js "^2.4.8"
-performance-now@^0.2.0:
- version "0.2.0"
- resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-0.2.0.tgz#33ef30c5c77d4ea21c5a53869d91b56d8f2555e5"
- integrity sha1-M+8wxcd9TqIcWlOGnZG1bY8lVeU=
-
performance-now@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
@@ -5062,6 +4694,11 @@ pify@^3.0.0:
resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176"
integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=
+pify@^4.0.1:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231"
+ integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==
+
pinkie-promise@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa"
@@ -5082,16 +4719,16 @@ pkg-dir@^3.0.0:
find-up "^3.0.0"
pnp-webpack-plugin@^1.3.1:
- version "1.4.1"
- resolved "https://registry.yarnpkg.com/pnp-webpack-plugin/-/pnp-webpack-plugin-1.4.1.tgz#e8f8c683b496a71c0d200e664c4bb399a9c9585e"
- integrity sha512-S4kz+5rvWvD0w1O63eTJeXIxW4JHK0wPRMO7GmPhbZXJnTePcfrWZlni4BoglIf7pLSY18xtqo3MSnVkoAFXKg==
+ version "1.4.3"
+ resolved "https://registry.yarnpkg.com/pnp-webpack-plugin/-/pnp-webpack-plugin-1.4.3.tgz#0a100b63f4a1d09cee6ee55a87393b69f03ab5c7"
+ integrity sha512-ExrNwuFH3DudHwWY2uRMqyiCOBEDdhQYHIAsqW/CM6hIZlSgXC/ma/p08FoNOUhVyh9hl1NGnMpR94T5i3SHaQ==
dependencies:
- ts-pnp "^1.0.0"
+ ts-pnp "^1.1.2"
-portfinder@^1.0.9:
- version "1.0.13"
- resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.13.tgz#bb32ecd87c27104ae6ee44b5a3ccbf0ebb1aede9"
- integrity sha1-uzLs2HwnEErm7kS1o8y/Drsa7ek=
+portfinder@^1.0.20:
+ version "1.0.20"
+ resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.20.tgz#bea68632e54b2e13ab7b0c4775e9b41bf270e44a"
+ integrity sha512-Yxe4mTyDzTd59PZJY4ojZR8F+E5e97iq2ZOHPz3HDgSvYC5siNad2tLooQ5y5QHyQhc3xVqvyk/eNA3wuoa7Sw==
dependencies:
async "^1.5.2"
debug "^2.2.0"
@@ -5138,12 +4775,12 @@ postcss-color-gray@^5.0.0:
postcss-values-parser "^2.0.0"
postcss-color-hex-alpha@^5.0.2:
- version "5.0.2"
- resolved "https://registry.yarnpkg.com/postcss-color-hex-alpha/-/postcss-color-hex-alpha-5.0.2.tgz#e9b1886bb038daed33f6394168c210b40bb4fdb6"
- integrity sha512-8bIOzQMGdZVifoBQUJdw+yIY00omBd2EwkJXepQo9cjp1UOHHHoeRDeSzTP6vakEpaRc6GAIOfvcQR7jBYaG5Q==
+ version "5.0.3"
+ resolved "https://registry.yarnpkg.com/postcss-color-hex-alpha/-/postcss-color-hex-alpha-5.0.3.tgz#a8d9ca4c39d497c9661e374b9c51899ef0f87388"
+ integrity sha512-PF4GDel8q3kkreVXKLAGNpHKilXsZ6xuu+mOQMHWHLPNyjiUBOr75sp5ZKJfmv1MCus5/DWUGcK9hm6qHEnXYw==
dependencies:
- postcss "^7.0.2"
- postcss-values-parser "^2.0.0"
+ postcss "^7.0.14"
+ postcss-values-parser "^2.0.1"
postcss-color-mod-function@^3.0.3:
version "3.0.3"
@@ -5182,19 +4819,19 @@ postcss-convert-values@^4.0.1:
postcss-value-parser "^3.0.0"
postcss-custom-media@^7.0.7:
- version "7.0.7"
- resolved "https://registry.yarnpkg.com/postcss-custom-media/-/postcss-custom-media-7.0.7.tgz#bbc698ed3089ded61aad0f5bfb1fb48bf6969e73"
- integrity sha512-bWPCdZKdH60wKOTG4HKEgxWnZVjAIVNOJDvi3lkuTa90xo/K0YHa2ZnlKLC5e2qF8qCcMQXt0yzQITBp8d0OFA==
+ version "7.0.8"
+ resolved "https://registry.yarnpkg.com/postcss-custom-media/-/postcss-custom-media-7.0.8.tgz#fffd13ffeffad73621be5f387076a28b00294e0c"
+ integrity sha512-c9s5iX0Ge15o00HKbuRuTqNndsJUbaXdiNsksnVH8H4gdc+zbLzr/UasOwNG6CTDpLFekVY4672eWdiiWu2GUg==
dependencies:
- postcss "^7.0.5"
+ postcss "^7.0.14"
postcss-custom-properties@^8.0.9:
- version "8.0.9"
- resolved "https://registry.yarnpkg.com/postcss-custom-properties/-/postcss-custom-properties-8.0.9.tgz#8943870528a6eae4c8e8d285b6ccc9fd1f97e69c"
- integrity sha512-/Lbn5GP2JkKhgUO2elMs4NnbUJcvHX4AaF5nuJDaNkd2chYW1KA5qtOGGgdkBEWcXtKSQfHXzT7C6grEVyb13w==
+ version "8.0.10"
+ resolved "https://registry.yarnpkg.com/postcss-custom-properties/-/postcss-custom-properties-8.0.10.tgz#e8dc969e1e15c555f0b836b7f278ef47e3cdeaff"
+ integrity sha512-GDL0dyd7++goDR4SSasYdRNNvp4Gqy1XMzcCnTijiph7VB27XXpJ8bW/AI0i2VSBZ55TpdGhMr37kMSpRfYD0Q==
dependencies:
- postcss "^7.0.5"
- postcss-values-parser "^2.0.0"
+ postcss "^7.0.14"
+ postcss-values-parser "^2.0.1"
postcss-custom-selectors@^5.1.2:
version "5.1.2"
@@ -5718,17 +5355,12 @@ postcss-unique-selectors@^4.0.1:
postcss "^7.0.0"
uniqs "^2.0.0"
-postcss-value-parser@^3.0.0, postcss-value-parser@^3.3.1:
+postcss-value-parser@^3.0.0, postcss-value-parser@^3.2.3, postcss-value-parser@^3.3.0, postcss-value-parser@^3.3.1:
version "3.3.1"
resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz#9ff822547e2893213cf1c30efa51ac5fd1ba8281"
integrity sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==
-postcss-value-parser@^3.2.3, postcss-value-parser@^3.3.0:
- version "3.3.0"
- resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.0.tgz#87f38f9f18f774a4ab4c8a232f5c5ce8872a9d15"
- integrity sha1-h/OPnxj3dKSrTIojL1xc6IcqnRU=
-
-postcss-values-parser@^2.0.0:
+postcss-values-parser@^2.0.0, postcss-values-parser@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/postcss-values-parser/-/postcss-values-parser-2.0.1.tgz#da8b472d901da1e205b47bdc98637b9e9e550e5f"
integrity sha512-2tLuBsA6P4rYTNKCXYG/71C7j1pU6pK503suYOmn4xYrQIzW+opD+7FAFNuGSdZC/3Qfy334QbeMu7MEb8gOxg==
@@ -5738,9 +5370,9 @@ postcss-values-parser@^2.0.0:
uniq "^1.0.1"
postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.14, postcss@^7.0.2, postcss@^7.0.5, postcss@^7.0.6:
- version "7.0.14"
- resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.14.tgz#4527ed6b1ca0d82c53ce5ec1a2041c2346bbd6e5"
- integrity sha512-NsbD6XUUMZvBxtQAJuWDJeeC4QFsmWsfozWxCJPWf3M55K9iu2iMDaKqyoOdTJ1R4usBXuxlVFAIo8rZPQD4Bg==
+ version "7.0.16"
+ resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.16.tgz#48f64f1b4b558cb8b52c88987724359acb010da2"
+ integrity sha512-MOo8zNSlIqh22Uaa3drkdIAgUGEL+AD1ESiSdmElLUmE2uVDo1QloiT/IfW9qRw8Gw+Y/w69UVMGwbufMSftxA==
dependencies:
chalk "^2.4.2"
source-map "^0.6.1"
@@ -5766,13 +5398,13 @@ promise-inflight@^1.0.1:
resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3"
integrity sha1-mEcocL8igTL8vdhoEputEsPAKeM=
-proxy-addr@~2.0.2:
- version "2.0.2"
- resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.2.tgz#6571504f47bb988ec8180253f85dd7e14952bdec"
- integrity sha1-ZXFQT0e7mI7IGAJT+F3X4UlSvew=
+proxy-addr@~2.0.4:
+ version "2.0.5"
+ resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.5.tgz#34cbd64a2d81f4b1fd21e76f9f06c8a45299ee34"
+ integrity sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ==
dependencies:
forwarded "~0.1.2"
- ipaddr.js "1.5.2"
+ ipaddr.js "1.9.0"
prr@~1.0.1:
version "1.0.1"
@@ -5790,15 +5422,16 @@ psl@^1.1.24:
integrity sha512-/6pt4+C+T+wZUieKR620OpzN/LlnNKuWjy1iFLQ/UG35JqHlR/89MP1d96dUfkf6Dne3TuLQzOYEYshJ+Hx8mw==
public-encrypt@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.0.tgz#39f699f3a46560dd5ebacbca693caf7c65c18cc6"
- integrity sha1-OfaZ86RlYN1eusvKaTyvfGXBjMY=
+ version "4.0.3"
+ resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.3.tgz#4fcc9d77a07e48ba7527e7cbe0de33d0701331e0"
+ integrity sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==
dependencies:
bn.js "^4.1.0"
browserify-rsa "^4.0.0"
create-hash "^1.1.0"
parse-asn1 "^5.0.0"
randombytes "^2.0.1"
+ safe-buffer "^5.1.2"
pump@^2.0.0:
version "2.0.1"
@@ -5817,11 +5450,11 @@ pump@^3.0.0:
once "^1.3.1"
pumpify@^1.3.3:
- version "1.4.0"
- resolved "https://registry.yarnpkg.com/pumpify/-/pumpify-1.4.0.tgz#80b7c5df7e24153d03f0e7ac8a05a5d068bd07fb"
- integrity sha512-2kmNR9ry+Pf45opRVirpNuIFotsxUGLaYqxIwuR77AYrYRMuFCz9eryHBS52L360O+NcR383CL4QYlMKPq4zYA==
+ version "1.5.1"
+ resolved "https://registry.yarnpkg.com/pumpify/-/pumpify-1.5.1.tgz#36513be246ab27570b1a374a5ce278bfd74370ce"
+ integrity sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==
dependencies:
- duplexify "^3.5.3"
+ duplexify "^3.6.0"
inherits "^2.0.3"
pump "^2.0.0"
@@ -5845,17 +5478,7 @@ q@^1.1.2:
resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7"
integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=
-qs@6.5.1:
- version "6.5.1"
- resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.1.tgz#349cdf6eef89ec45c12d7d5eb3fc0c870343a6d8"
- integrity sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==
-
-qs@~6.4.0:
- version "6.4.0"
- resolved "https://registry.yarnpkg.com/qs/-/qs-6.4.0.tgz#13e26d28ad6b0ffaa91312cd3bf708ed351e7233"
- integrity sha1-E+JtKK1rD/qpExLNO/cI7TUecjM=
-
-qs@~6.5.2:
+qs@6.5.2, qs@~6.5.2:
version "6.5.2"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"
integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==
@@ -5870,51 +5493,41 @@ querystring@0.2.0:
resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620"
integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=
-querystringify@^2.0.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.1.0.tgz#7ded8dfbf7879dcc60d0a644ac6754b283ad17ef"
- integrity sha512-sluvZZ1YiTLD5jsqZcDmFyV2EwToyXZBfpoVOmktMmW+VEnhgakFHnasVph65fOjGPTWN0Nw3+XQaSeMayr0kg==
+querystringify@^2.1.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.1.1.tgz#60e5a5fd64a7f8bfa4d2ab2ed6fdf4c85bad154e"
+ integrity sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA==
randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5:
- version "2.0.6"
- resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.0.6.tgz#d302c522948588848a8d300c932b44c24231da80"
- integrity sha512-CIQ5OFxf4Jou6uOKe9t1AOgqpeU5fd70A8NPdHSGeYXqXsPe6peOwI0cUl88RWZ6sP1vPMV3avd/R6cZ5/sP1A==
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a"
+ integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==
dependencies:
safe-buffer "^5.1.0"
randomfill@^1.0.3:
- version "1.0.3"
- resolved "https://registry.yarnpkg.com/randomfill/-/randomfill-1.0.3.tgz#b96b7df587f01dd91726c418f30553b1418e3d62"
- integrity sha512-YL6GrhrWoic0Eq8rXVbMptH7dAxCs0J+mh5Y0euNekPPYaxEmdVGim6GdoxoRzKW2yJoU8tueifS7mYxvcFDEQ==
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/randomfill/-/randomfill-1.0.4.tgz#c92196fc86ab42be983f1bf31778224931d61458"
+ integrity sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==
dependencies:
randombytes "^2.0.5"
safe-buffer "^5.1.0"
range-parser@^1.0.3, range-parser@~1.2.0:
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e"
- integrity sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031"
+ integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==
-raw-body@2.3.2:
- version "2.3.2"
- resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.3.2.tgz#bcd60c77d3eb93cde0050295c3f379389bc88f89"
- integrity sha1-vNYMd9Prk83gBQKVw/N5OJvIj4k=
+raw-body@2.3.3:
+ version "2.3.3"
+ resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.3.3.tgz#1b324ece6b5706e153855bc1148c65bb7f6ea0c3"
+ integrity sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==
dependencies:
bytes "3.0.0"
- http-errors "1.6.2"
- iconv-lite "0.4.19"
+ http-errors "1.6.3"
+ iconv-lite "0.4.23"
unpipe "1.0.0"
-rc@^1.1.7:
- version "1.2.5"
- resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.5.tgz#275cd687f6e3b36cc756baa26dfee80a790301fd"
- integrity sha1-J1zWh/bjs2zHVrqibf7oCnkDAf0=
- dependencies:
- deep-extend "~0.4.0"
- ini "~1.3.0"
- minimist "^1.2.0"
- strip-json-comments "~2.0.1"
-
rc@^1.2.7:
version "1.2.8"
resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed"
@@ -5949,38 +5562,28 @@ read-pkg@^1.0.0:
normalize-package-data "^2.3.2"
path-type "^1.0.0"
-"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.4, readable-stream@^2.0.6, readable-stream@^2.1.4, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3:
- version "2.3.4"
- resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.4.tgz#c946c3f47fa7d8eabc0b6150f4a12f69a4574071"
- integrity sha512-vuYxeWYM+fde14+rajzqgeohAI7YoJcHE7kXDAc4Nk0EbuKnJfqtY9YtRkLo/tqkuF7MsBQRhPnPeyjYITp3ZQ==
+"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.6, readable-stream@~2.3.6:
+ version "2.3.6"
+ resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf"
+ integrity sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==
dependencies:
core-util-is "~1.0.0"
inherits "~2.0.3"
isarray "~1.0.0"
process-nextick-args "~2.0.0"
safe-buffer "~5.1.1"
- string_decoder "~1.0.3"
+ string_decoder "~1.1.1"
util-deprecate "~1.0.1"
readable-stream@^3.0.6:
- version "3.2.0"
- resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.2.0.tgz#de17f229864c120a9f56945756e4f32c4045245d"
- integrity sha512-RV20kLjdmpZuTF1INEb9IA3L68Nmi+Ri7ppZqo78wj//Pn62fCoJyV9zalccNzDD/OuJpMG4f+pfMl8+L6QdGw==
+ version "3.3.0"
+ resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.3.0.tgz#cb8011aad002eb717bf040291feba8569c986fb9"
+ integrity sha512-EsI+s3k3XsW+fU8fQACLN59ky34AZ14LoeVZpYwmZvldCFo0r0gnelwF2TcMjLor/BTL5aDJVBMkss0dthToPw==
dependencies:
inherits "^2.0.3"
string_decoder "^1.1.1"
util-deprecate "^1.0.1"
-readdirp@^2.0.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.1.0.tgz#4ed0ad060df3073300c48440373f72d1cc642d78"
- integrity sha1-TtCtBg3zBzMAxIRANz9y0cxkLXg=
- dependencies:
- graceful-fs "^4.1.2"
- minimatch "^3.0.2"
- readable-stream "^2.0.2"
- set-immediate-shim "^1.0.1"
-
readdirp@^2.2.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.2.1.tgz#0e87622a3325aa33e892285caf8b4e846529a525"
@@ -5998,10 +5601,10 @@ redent@^1.0.0:
indent-string "^2.1.0"
strip-indent "^1.0.1"
-regenerate-unicode-properties@^8.0.1:
- version "8.0.1"
- resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-8.0.1.tgz#58a4a74e736380a7ab3c5f7e03f303a941b31289"
- integrity sha512-HTjMafphaH5d5QDHuwW8Me6Hbc/GhXg8luNqTkPVwZ/oCZhnoifjWhGYsu2BzepMELTlbnoVcXvV0f+2uDDvoQ==
+regenerate-unicode-properties@^8.0.2:
+ version "8.0.2"
+ resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-8.0.2.tgz#7b38faa296252376d363558cfbda90c9ce709662"
+ integrity sha512-SbA/iNrBUf6Pv2zU8Ekv1Qbhv92yxL4hiDa2siuxs4KKn4oOoMDHXjAf7+Nz9qinUQ46B1LcWEi/PhJfPWpZWQ==
dependencies:
regenerate "^1.4.0"
@@ -6010,10 +5613,10 @@ regenerate@^1.4.0:
resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.0.tgz#4a856ec4b56e4077c557589cae85e7a4c8869a11"
integrity sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg==
-regenerator-runtime@^0.12.0:
- version "0.12.1"
- resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.12.1.tgz#fa1a71544764c036f8c49b13a08b2594c9f8a0de"
- integrity sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg==
+regenerator-runtime@^0.13.2:
+ version "0.13.2"
+ resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.2.tgz#32e59c9a6fb9b1a4aff09b4930ca2d4477343447"
+ integrity sha512-S/TQAZJO+D3m9xeN1WTI8dLKBBiRgXBlTJvbWjCThHWZj9EvHK70Ff50/tYj2J/fvBY6JtFVwRuazHN2E7M9BA==
regenerator-transform@^0.13.4:
version "0.13.4"
@@ -6022,14 +5625,7 @@ regenerator-transform@^0.13.4:
dependencies:
private "^0.1.6"
-regex-not@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.0.tgz#42f83e39771622df826b02af176525d6a5f157f9"
- integrity sha1-Qvg+OXcWIt+CawKvF2Ul1qXxV/k=
- dependencies:
- extend-shallow "^2.0.1"
-
-regex-not@^1.0.2:
+regex-not@^1.0.0, regex-not@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c"
integrity sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==
@@ -6038,17 +5634,17 @@ regex-not@^1.0.2:
safe-regex "^1.1.0"
regexp-tree@^0.1.0:
- version "0.1.5"
- resolved "https://registry.yarnpkg.com/regexp-tree/-/regexp-tree-0.1.5.tgz#7cd71fca17198d04b4176efd79713f2998009397"
- integrity sha512-nUmxvfJyAODw+0B13hj8CFVAxhe7fDEAgJgaotBu3nnR+IgGgZq59YedJP5VYTlkEfqjuK6TuRpnymKdatLZfQ==
+ version "0.1.6"
+ resolved "https://registry.yarnpkg.com/regexp-tree/-/regexp-tree-0.1.6.tgz#84900fa12fdf428a2ac25f04300382a7c0148479"
+ integrity sha512-LFrA98Dw/heXqDojz7qKFdygZmFoiVlvE1Zp7Cq2cvF+ZA+03Gmhy0k0PQlsC1jvHPiTUSs+pDHEuSWv6+6D7w==
-regexpu-core@^4.1.3, regexpu-core@^4.2.0:
- version "4.5.3"
- resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.5.3.tgz#72f572e03bb8b9f4f4d895a0ccc57e707f4af2e4"
- integrity sha512-LON8666bTAlViVEPXMv65ZqiaR3rMNLz36PIaQ7D+er5snu93k0peR7FSvO0QteYbZ3GOkvfHKbGr/B1xDu9FA==
+regexpu-core@^4.5.4:
+ version "4.5.4"
+ resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.5.4.tgz#080d9d02289aa87fe1667a4f5136bc98a6aebaae"
+ integrity sha512-BtizvGtFQKGPUcTy56o3nk1bGRp4SZOTYrDtGNlqCQufptV5IkkLN6Emw+yunAJjzf+C9FQFtvq7IoA3+oMYHQ==
dependencies:
regenerate "^1.4.0"
- regenerate-unicode-properties "^8.0.1"
+ regenerate-unicode-properties "^8.0.2"
regjsgen "^0.5.0"
regjsparser "^0.6.0"
unicode-match-property-ecmascript "^1.0.4"
@@ -6072,9 +5668,9 @@ remove-trailing-separator@^1.0.1:
integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8=
repeat-element@^1.1.2:
- version "1.1.2"
- resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.2.tgz#ef089a178d1483baae4d93eb98b4f9e4e11d990a"
- integrity sha1-7wiaF40Ug7quTZPrmLT55OEdmQo=
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.3.tgz#782e0d825c0c5a3bb39731f84efee6b742e6b1ce"
+ integrity sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==
repeat-string@^1.6.1:
version "1.6.1"
@@ -6088,34 +5684,6 @@ repeating@^2.0.0:
dependencies:
is-finite "^1.0.0"
-request@2.81.0:
- version "2.81.0"
- resolved "https://registry.yarnpkg.com/request/-/request-2.81.0.tgz#c6928946a0e06c5f8d6f8a9333469ffda46298a0"
- integrity sha1-xpKJRqDgbF+Nb4qTM0af/aRimKA=
- dependencies:
- aws-sign2 "~0.6.0"
- aws4 "^1.2.1"
- caseless "~0.12.0"
- combined-stream "~1.0.5"
- extend "~3.0.0"
- forever-agent "~0.6.1"
- form-data "~2.1.1"
- har-validator "~4.2.1"
- hawk "~3.1.3"
- http-signature "~1.1.0"
- is-typedarray "~1.0.0"
- isstream "~0.1.2"
- json-stringify-safe "~5.0.1"
- mime-types "~2.1.7"
- oauth-sign "~0.8.1"
- performance-now "^0.2.0"
- qs "~6.4.0"
- safe-buffer "^5.0.1"
- stringstream "~0.0.4"
- tough-cookie "~2.3.0"
- tunnel-agent "^0.6.0"
- uuid "^3.0.0"
-
request@^2.87.0, request@^2.88.0:
version "2.88.0"
resolved "https://registry.yarnpkg.com/request/-/request-2.88.0.tgz#9c2fca4f7d35b592efe57c7f0a55e81052124fef"
@@ -6187,17 +5755,10 @@ resolve-url@^0.2.1:
resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a"
integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=
-resolve@^1.1.7:
- version "1.5.0"
- resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.5.0.tgz#1f09acce796c9a762579f31b2c1cc4c3cddf9f36"
- integrity sha512-hgoSGrc3pjzAPHNBg+KnFcK2HwlHTs/YrAGUr6qgTVUZmXv1UEXXl0bZNBKMA9fud6lRYFdPGz0xXxycPzmmiw==
- dependencies:
- path-parse "^1.0.5"
-
-resolve@^1.3.2, resolve@^1.8.1:
- version "1.10.0"
- resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.10.0.tgz#3bdaaeaf45cc07f375656dfd2e54ed0810b101ba"
- integrity sha512-3sUr9aq5OfSg2S9pNtPA9hL1FVEAjvfOC4leW0SNf/mpnaakz2a9femSd6LqAww2RaFctwyf1lCqnTHuF1rxDg==
+resolve@^1.1.7, resolve@^1.10.0, resolve@^1.3.2, resolve@^1.8.1:
+ version "1.10.1"
+ resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.10.1.tgz#664842ac960795bbe758221cdccda61fb64b5f18"
+ integrity sha512-KuIe4mf++td/eFb6wkaPbMDnP6kObCaEtIDuHOUED6MNUo4K670KZUHuuvYPZDxNF0WVLw49n06M2m2dXphEzA==
dependencies:
path-parse "^1.0.6"
@@ -6216,14 +5777,7 @@ rgba-regex@^1.0.0:
resolved "https://registry.yarnpkg.com/rgba-regex/-/rgba-regex-1.0.0.tgz#43374e2e2ca0968b0ef1523460b7d730ff22eeb3"
integrity sha1-QzdOLiyglosO8VI0YLfXMP8i7rM=
-rimraf@2, rimraf@^2.2.8, rimraf@^2.5.1, rimraf@^2.5.4, rimraf@^2.6.1:
- version "2.6.2"
- resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.2.tgz#2ed8150d24a16ea8651e6d6ef0f47c4158ce7a36"
- integrity sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==
- dependencies:
- glob "^7.0.5"
-
-rimraf@^2.6.2:
+rimraf@2, rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.2, rimraf@^2.6.3:
version "2.6.3"
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab"
integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==
@@ -6231,11 +5785,11 @@ rimraf@^2.6.2:
glob "^7.1.3"
ripemd160@^2.0.0, ripemd160@^2.0.1:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.1.tgz#0f4584295c53a3628af7e6d79aca21ce57d1c6e7"
- integrity sha1-D0WEKVxTo2KK9+bXmsohzlfRxuc=
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c"
+ integrity sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==
dependencies:
- hash-base "^2.0.0"
+ hash-base "^3.0.0"
inherits "^2.0.1"
run-queue@^1.0.0, run-queue@^1.0.3:
@@ -6245,12 +5799,7 @@ run-queue@^1.0.0, run-queue@^1.0.3:
dependencies:
aproba "^1.1.1"
-safe-buffer@5.1.1, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
- version "5.1.1"
- resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853"
- integrity sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==
-
-safe-buffer@^5.1.2:
+safe-buffer@5.1.2, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
version "5.1.2"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
@@ -6262,7 +5811,7 @@ safe-regex@^1.1.0:
dependencies:
ret "~0.1.10"
-"safer-buffer@>= 2.1.2 < 3":
+"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0:
version "2.1.2"
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
@@ -6316,37 +5865,37 @@ select-hose@^2.0.0:
resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca"
integrity sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=
-selfsigned@^1.9.1:
- version "1.10.2"
- resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-1.10.2.tgz#b4449580d99929b65b10a48389301a6592088758"
- integrity sha1-tESVgNmZKbZbEKSDiTAaZZIIh1g=
+selfsigned@^1.10.4:
+ version "1.10.4"
+ resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-1.10.4.tgz#cdd7eccfca4ed7635d47a08bf2d5d3074092e2cd"
+ integrity sha512-9AukTiDmHXGXWtWjembZ5NDmVvP2695EtpgbCsxCa68w3c88B+alqbmZ4O3hZ4VWGXeGWzEVdvqgAJD8DQPCDw==
dependencies:
- node-forge "0.7.1"
+ node-forge "0.7.5"
-"semver@2 || 3 || 4 || 5", semver@^5.3.0:
- version "5.5.0"
- resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab"
- integrity sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==
+"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0:
+ version "5.7.0"
+ resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.0.tgz#790a7cf6fea5459bac96110b29b60412dc8ff96b"
+ integrity sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==
-semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0:
- version "5.6.0"
- resolved "https://registry.yarnpkg.com/semver/-/semver-5.6.0.tgz#7e74256fbaa49c75aa7c7a205cc22799cac80004"
- integrity sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==
+semver@^6.0.0:
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/semver/-/semver-6.0.0.tgz#05e359ee571e5ad7ed641a6eec1e547ba52dea65"
+ integrity sha512-0UewU+9rFapKFnlbirLi3byoOuhrSsli/z/ihNnvM24vgF+8sNBiI1LZPBSH9wJKUwaUbw+s3hToDLCXkrghrQ==
semver@~5.3.0:
version "5.3.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f"
integrity sha1-myzl094C0XxgEq0yaqa00M9U+U8=
-send@0.16.1:
- version "0.16.1"
- resolved "https://registry.yarnpkg.com/send/-/send-0.16.1.tgz#a70e1ca21d1382c11d0d9f6231deb281080d7ab3"
- integrity sha512-ElCLJdJIKPk6ux/Hocwhk7NFHpI3pVm/IZOYWqUmoxcgeyM+MpxHHKhb8QmlJDX1pU6WrgaHBkVNm73Sv7uc2A==
+send@0.16.2:
+ version "0.16.2"
+ resolved "https://registry.yarnpkg.com/send/-/send-0.16.2.tgz#6ecca1e0f8c156d141597559848df64730a6bbc1"
+ integrity sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==
dependencies:
debug "2.6.9"
- depd "~1.1.1"
+ depd "~1.1.2"
destroy "~1.0.4"
- encodeurl "~1.0.1"
+ encodeurl "~1.0.2"
escape-html "~1.0.3"
etag "~1.8.1"
fresh "0.5.2"
@@ -6355,14 +5904,14 @@ send@0.16.1:
ms "2.0.0"
on-finished "~2.3.0"
range-parser "~1.2.0"
- statuses "~1.3.1"
+ statuses "~1.4.0"
serialize-javascript@^1.4.0:
- version "1.4.0"
- resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-1.4.0.tgz#7c958514db6ac2443a8abc062dc9f7886a7f6005"
- integrity sha1-fJWFFNtqwkQ6irwGLcn3iGp/YAU=
+ version "1.7.0"
+ resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-1.7.0.tgz#d6e0dfb2a3832a8c94468e6eb1db97e55a192a65"
+ integrity sha512-ke8UG8ulpFOxO8f8gRYabHQe/ZntKlcig2Mp+8+URDP1D8vJZ0KUt7LYo07q25Z/+JVSgpr/cui9PIp5H6/+nA==
-serve-index@^1.7.2:
+serve-index@^1.9.1:
version "1.9.1"
resolved "https://registry.yarnpkg.com/serve-index/-/serve-index-1.9.1.tgz#d3768d69b1e7d82e5ce050fff5b453bea12a9239"
integrity sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=
@@ -6375,33 +5924,21 @@ serve-index@^1.7.2:
mime-types "~2.1.17"
parseurl "~1.3.2"
-serve-static@1.13.1:
- version "1.13.1"
- resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.13.1.tgz#4c57d53404a761d8f2e7c1e8a18a47dbf278a719"
- integrity sha512-hSMUZrsPa/I09VYFJwa627JJkNs0NrfL1Uzuup+GqHfToR2KcsXFymXSV90hoyw3M+msjFuQly+YzIH/q0MGlQ==
+serve-static@1.13.2:
+ version "1.13.2"
+ resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.13.2.tgz#095e8472fd5b46237db50ce486a43f4b86c6cec1"
+ integrity sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==
dependencies:
- encodeurl "~1.0.1"
+ encodeurl "~1.0.2"
escape-html "~1.0.3"
parseurl "~1.3.2"
- send "0.16.1"
+ send "0.16.2"
set-blocking@^2.0.0, set-blocking@~2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc=
-set-getter@^0.1.0:
- version "0.1.0"
- resolved "https://registry.yarnpkg.com/set-getter/-/set-getter-0.1.0.tgz#d769c182c9d5a51f409145f2fba82e5e86e80376"
- integrity sha1-12nBgsnVpR9AkUXy+6guXoboA3Y=
- dependencies:
- to-object-path "^0.3.0"
-
-set-immediate-shim@^1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61"
- integrity sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=
-
set-value@^0.4.3:
version "0.4.3"
resolved "https://registry.yarnpkg.com/set-value/-/set-value-0.4.3.tgz#7db08f9d3d22dc7f78e53af3c3bf4666ecdfccf1"
@@ -6427,20 +5964,15 @@ setimmediate@^1.0.4:
resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285"
integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=
-setprototypeof@1.0.3:
- version "1.0.3"
- resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.0.3.tgz#66567e37043eeb4f04d91bd658c0cbefb55b8e04"
- integrity sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ=
-
setprototypeof@1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656"
integrity sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==
sha.js@^2.4.0, sha.js@^2.4.8:
- version "2.4.10"
- resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.10.tgz#b1fde5cd7d11a5626638a07c604ab909cfa31f9b"
- integrity sha512-vnwmrFDlOExK4Nm16J2KMWHLrp14lBrjxMxBJpu++EnsuBmpiYaM/MEs46Vxxm/4FvdP5yTwuCTO9it5FSjrqA==
+ version "2.4.11"
+ resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7"
+ integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==
dependencies:
inherits "^2.0.1"
safe-buffer "^5.0.1"
@@ -6495,9 +6027,9 @@ snapdragon-util@^3.0.1:
kind-of "^3.2.0"
snapdragon@^0.8.1:
- version "0.8.1"
- resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.1.tgz#e12b5487faded3e3dea0ac91e9400bf75b401370"
- integrity sha1-4StUh/re0+PeoKyR6UAL91tAE3A=
+ version "0.8.2"
+ resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d"
+ integrity sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==
dependencies:
base "^0.11.1"
debug "^2.2.0"
@@ -6506,14 +6038,7 @@ snapdragon@^0.8.1:
map-cache "^0.2.2"
source-map "^0.5.6"
source-map-resolve "^0.5.0"
- use "^2.0.0"
-
-sntp@1.x.x:
- version "1.0.9"
- resolved "https://registry.yarnpkg.com/sntp/-/sntp-1.0.9.tgz#6541184cc90aeea6c6e7b35e2659082443c66198"
- integrity sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=
- dependencies:
- hoek "2.x.x"
+ use "^3.1.0"
sockjs-client@1.3.0:
version "1.3.0"
@@ -6536,25 +6061,25 @@ sockjs@0.3.19:
uuid "^3.0.1"
source-list-map@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.0.tgz#aaa47403f7b245a92fbc97ea08f250d6087ed085"
- integrity sha512-I2UmuJSRr/T8jisiROLU3A3ltr+swpniSmNPI4Ml3ZCX6tVnDsuZzK7F2hl5jTqbZBWCEKlj5HRQiPExXLgE8A==
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34"
+ integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==
source-map-resolve@^0.5.0:
- version "0.5.1"
- resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.1.tgz#7ad0f593f2281598e854df80f19aae4b92d7a11a"
- integrity sha512-0KW2wvzfxm8NCTb30z0LMNyPqWCdDGE2viwzUaucqJdkTRXtZiSY3I+2A6nVAjmdOy0I4gU8DwnVVGsk9jvP2A==
+ version "0.5.2"
+ resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.2.tgz#72e2cc34095543e43b2c62b2c4c10d4a9054f259"
+ integrity sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==
dependencies:
- atob "^2.0.0"
+ atob "^2.1.1"
decode-uri-component "^0.2.0"
resolve-url "^0.2.1"
source-map-url "^0.4.0"
urix "^0.1.0"
-source-map-support@~0.5.9:
- version "0.5.10"
- resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.10.tgz#2214080bc9d51832511ee2bab96e3c2f9353120c"
- integrity sha512-YfQ3tQFTK/yzlGJuX8pTwa4tifQj4QS2Mj7UegOu8jAz59MqIiMGPXxQhVQiIMNzayuUSF/jEuVnfFF5JqybmQ==
+source-map-support@~0.5.10:
+ version "0.5.12"
+ resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.12.tgz#b4f3b10d51857a5af0138d3ce8003b201613d599"
+ integrity sha512-4h2Pbvyy15EE02G+JOZpUCmqWJuqrs+sEkzewTm++BPi7Hvn/HwcqLAcNxYAyI0x13CpPPn+kMjl+hplXMHITQ==
dependencies:
buffer-from "^1.0.0"
source-map "^0.6.0"
@@ -6586,22 +6111,31 @@ spark-md5@^3.0.0:
resolved "https://registry.yarnpkg.com/spark-md5/-/spark-md5-3.0.0.tgz#3722227c54e2faf24b1dc6d933cc144e6f71bfef"
integrity sha1-NyIifFTi+vJLHcbZM8wUTm9xv+8=
-spdx-correct@~1.0.0:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-1.0.2.tgz#4b3073d933ff51f3912f03ac5519498a4150db40"
- integrity sha1-SzBz2TP/UfORLwOsVRlJikFQ20A=
+spdx-correct@^3.0.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.0.tgz#fb83e504445268f154b074e218c87c003cd31df4"
+ integrity sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==
dependencies:
- spdx-license-ids "^1.0.2"
+ spdx-expression-parse "^3.0.0"
+ spdx-license-ids "^3.0.0"
-spdx-expression-parse@~1.0.0:
- version "1.0.4"
- resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-1.0.4.tgz#9bdf2f20e1f40ed447fbe273266191fced51626c"
- integrity sha1-m98vIOH0DtRH++JzJmGR/O1RYmw=
+spdx-exceptions@^2.1.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz#2ea450aee74f2a89bfb94519c07fcd6f41322977"
+ integrity sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==
-spdx-license-ids@^1.0.2:
- version "1.2.2"
- resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-1.2.2.tgz#c9df7a3424594ade6bd11900d596696dc06bac57"
- integrity sha1-yd96NCRZSt5r0RkA1ZZpbcBrrFc=
+spdx-expression-parse@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz#99e119b7a5da00e05491c9fa338b7904823b41d0"
+ integrity sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==
+ dependencies:
+ spdx-exceptions "^2.1.0"
+ spdx-license-ids "^3.0.0"
+
+spdx-license-ids@^3.0.0:
+ version "3.0.4"
+ resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.4.tgz#75ecd1a88de8c184ef015eafb51b5b48bfd11bb1"
+ integrity sha512-7j8LYJLeY/Yb6ACbQ7F76qy5jHkp0U6jgBfJsk97bwWlVUnUWsAgpyaCvo17h0/RQGnQ036tVDomiwoI4pDkQA==
spdy-transport@^3.0.0:
version "3.0.0"
@@ -6639,18 +6173,18 @@ sprintf-js@~1.0.2:
integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=
sshpk@^1.7.0:
- version "1.13.1"
- resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.13.1.tgz#512df6da6287144316dc4c18fe1cf1d940739be3"
- integrity sha1-US322mKHFEMW3EwY/hzx2UBzm+M=
+ version "1.16.1"
+ resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877"
+ integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==
dependencies:
asn1 "~0.2.3"
assert-plus "^1.0.0"
- dashdash "^1.12.0"
- getpass "^0.1.1"
- optionalDependencies:
bcrypt-pbkdf "^1.0.0"
+ dashdash "^1.12.0"
ecc-jsbn "~0.1.1"
+ getpass "^0.1.1"
jsbn "~0.1.0"
+ safer-buffer "^2.0.2"
tweetnacl "~0.14.0"
ssri@^6.0.1:
@@ -6673,47 +6207,47 @@ static-extend@^0.1.1:
define-property "^0.2.5"
object-copy "^0.1.0"
-"statuses@>= 1.3.1 < 2":
+"statuses@>= 1.4.0 < 2":
+ version "1.5.0"
+ resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c"
+ integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=
+
+statuses@~1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.4.0.tgz#bb73d446da2796106efcc1b601a253d6c46bd087"
integrity sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==
-statuses@~1.3.1:
- version "1.3.1"
- resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.3.1.tgz#faf51b9eb74aaef3b3acf4ad5f61abf24cb7b93e"
- integrity sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4=
-
stdout-stream@^1.4.0:
- version "1.4.0"
- resolved "https://registry.yarnpkg.com/stdout-stream/-/stdout-stream-1.4.0.tgz#a2c7c8587e54d9427ea9edb3ac3f2cd522df378b"
- integrity sha1-osfIWH5U2UJ+qe2zrD8s1SLfN4s=
+ version "1.4.1"
+ resolved "https://registry.yarnpkg.com/stdout-stream/-/stdout-stream-1.4.1.tgz#5ac174cdd5cd726104aa0c0b2bd83815d8d535de"
+ integrity sha512-j4emi03KXqJWcIeF8eIXkjMFN1Cmb8gUlDYGeBALLPo5qdyTfA9bOtl8m33lRoC+vFMkP3gl0WsDr6+gzxbbTA==
dependencies:
readable-stream "^2.0.1"
stream-browserify@^2.0.1:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.1.tgz#66266ee5f9bdb9940a4e4514cafb43bb71e5c9db"
- integrity sha1-ZiZu5fm9uZQKTkUUyvtDu3Hlyds=
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.2.tgz#87521d38a44aa7ee91ce1cd2a47df0cb49dd660b"
+ integrity sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==
dependencies:
inherits "~2.0.1"
readable-stream "^2.0.2"
stream-each@^1.1.0:
- version "1.2.2"
- resolved "https://registry.yarnpkg.com/stream-each/-/stream-each-1.2.2.tgz#8e8c463f91da8991778765873fe4d960d8f616bd"
- integrity sha512-mc1dbFhGBxvTM3bIWmAAINbqiuAk9TATcfIQC8P+/+HJefgaiTlMn2dHvkX8qlI12KeYKSQ1Ua9RrIqrn1VPoA==
+ version "1.2.3"
+ resolved "https://registry.yarnpkg.com/stream-each/-/stream-each-1.2.3.tgz#ebe27a0c389b04fbcc233642952e10731afa9bae"
+ integrity sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw==
dependencies:
end-of-stream "^1.1.0"
stream-shift "^1.0.0"
stream-http@^2.7.2:
- version "2.8.0"
- resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.8.0.tgz#fd86546dac9b1c91aff8fc5d287b98fafb41bc10"
- integrity sha512-sZOFxI/5xw058XIRHl4dU3dZ+TTOIGJR78Dvo0oEAejIt4ou27k+3ne1zYmCV+v7UucbxIFQuOgnkTVHh8YPnw==
+ version "2.8.3"
+ resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.8.3.tgz#b2d242469288a5a27ec4fe8933acf623de6514fc"
+ integrity sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==
dependencies:
builtin-status-codes "^3.0.0"
inherits "^2.0.1"
- readable-stream "^2.3.3"
+ readable-stream "^2.3.6"
to-arraybuffer "^1.0.0"
xtend "^4.0.0"
@@ -6731,7 +6265,7 @@ string-width@^1.0.1, string-width@^1.0.2:
is-fullwidth-code-point "^1.0.0"
strip-ansi "^3.0.0"
-string-width@^2.0.0, string-width@^2.1.1:
+"string-width@^1.0.2 || 2", string-width@^2.0.0, string-width@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e"
integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==
@@ -6739,24 +6273,19 @@ string-width@^2.0.0, string-width@^2.1.1:
is-fullwidth-code-point "^2.0.0"
strip-ansi "^4.0.0"
-string_decoder@^1.0.0, string_decoder@~1.0.3:
- version "1.0.3"
- resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.0.3.tgz#0fc67d7c141825de94282dd536bec6b9bce860ab"
- integrity sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==
- dependencies:
- safe-buffer "~5.1.0"
-
-string_decoder@^1.1.1:
+string_decoder@^1.0.0, string_decoder@^1.1.1:
version "1.2.0"
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.2.0.tgz#fe86e738b19544afe70469243b2a1ee9240eae8d"
integrity sha512-6YqyX6ZWEYguAxgZzHGL7SsCeGx3V2TtOTqZz1xSTSWnqsbWwbptafNyvf/ACquZUXV3DANr5BDIwNYe1mN42w==
dependencies:
safe-buffer "~5.1.0"
-stringstream@~0.0.4:
- version "0.0.5"
- resolved "https://registry.yarnpkg.com/stringstream/-/stringstream-0.0.5.tgz#4e484cd4de5a0bbbee18e46307710a8a81621878"
- integrity sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=
+string_decoder@~1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8"
+ integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==
+ dependencies:
+ safe-buffer "~5.1.0"
strip-ansi@^3.0.0, strip-ansi@^3.0.1:
version "3.0.1"
@@ -6833,9 +6362,9 @@ supports-color@^6.1.0:
has-flag "^3.0.0"
svgo@^1.0.0:
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/svgo/-/svgo-1.2.0.tgz#305a8fc0f4f9710828c65039bb93d5793225ffc3"
- integrity sha512-xBfxJxfk4UeVN8asec9jNxHiv3UAMv/ujwBWGYvQhhMb2u3YTGKkiybPcLFDLq7GLLWE9wa73e0/m8L5nTzQbw==
+ version "1.2.2"
+ resolved "https://registry.yarnpkg.com/svgo/-/svgo-1.2.2.tgz#0253d34eccf2aed4ad4f283e11ee75198f9d7316"
+ integrity sha512-rAfulcwp2D9jjdGu+0CuqlrAUin6bBWrpoqXWwKDZZZJfXcUXQSxLJOFJCQCSA0x0pP2U0TxSlJu2ROq5Bq6qA==
dependencies:
chalk "^2.4.1"
coa "^2.0.2"
@@ -6844,7 +6373,7 @@ svgo@^1.0.0:
css-tree "1.0.0-alpha.28"
css-url-regex "^1.1.0"
csso "^3.5.1"
- js-yaml "^3.12.0"
+ js-yaml "^3.13.1"
mkdirp "~0.5.1"
object.values "^1.1.0"
sax "~1.2.4"
@@ -6853,25 +6382,11 @@ svgo@^1.0.0:
util.promisify "~1.0.0"
tapable@^1.0.0, tapable@^1.1.0:
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.1.tgz#4d297923c5a72a42360de2ab52dadfaaec00018e"
- integrity sha512-9I2ydhj8Z9veORCw5PRm4u9uebCn0mcCa6scWoNcbZ6dAtoo2618u9UUzxgmsCOreJpqDDuv61LvwofW7hLcBA==
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2"
+ integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==
-tar-pack@^3.4.0:
- version "3.4.1"
- resolved "https://registry.yarnpkg.com/tar-pack/-/tar-pack-3.4.1.tgz#e1dbc03a9b9d3ba07e896ad027317eb679a10a1f"
- integrity sha512-PPRybI9+jM5tjtCbN2cxmmRU7YmqT3Zv/UDy48tAh2XRkLa9bAORtSWLkVc13+GJF+cdTh1yEnHEk3cpTaL5Kg==
- dependencies:
- debug "^2.2.0"
- fstream "^1.0.10"
- fstream-ignore "^1.0.5"
- once "^1.3.3"
- readable-stream "^2.1.4"
- rimraf "^2.5.1"
- tar "^2.2.1"
- uid-number "^0.0.6"
-
-tar@^2.0.0, tar@^2.2.1:
+tar@^2.0.0:
version "2.2.1"
resolved "https://registry.yarnpkg.com/tar/-/tar-2.2.1.tgz#8e4d2a256c0e2185c6b18ad694aec968b83cb1d1"
integrity sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE=
@@ -6908,31 +6423,31 @@ terser-webpack-plugin@^1.1.0, terser-webpack-plugin@^1.2.3:
worker-farm "^1.5.2"
terser@^3.16.1:
- version "3.16.1"
- resolved "https://registry.yarnpkg.com/terser/-/terser-3.16.1.tgz#5b0dd4fa1ffd0b0b43c2493b2c364fd179160493"
- integrity sha512-JDJjgleBROeek2iBcSNzOHLKsB/MdDf+E/BOAJ0Tk9r7p9/fVobfv7LMJ/g/k3v9SXdmjZnIlFd5nfn/Rt0Xow==
+ version "3.17.0"
+ resolved "https://registry.yarnpkg.com/terser/-/terser-3.17.0.tgz#f88ffbeda0deb5637f9d24b0da66f4e15ab10cb2"
+ integrity sha512-/FQzzPJmCpjAH9Xvk2paiWrFq+5M6aVOf+2KRbwhByISDX/EujxsK+BAvrhb6H+2rtrLCHK9N01wO014vrIwVQ==
dependencies:
- commander "~2.17.1"
+ commander "^2.19.0"
source-map "~0.6.1"
- source-map-support "~0.5.9"
+ source-map-support "~0.5.10"
through2@^2.0.0:
- version "2.0.3"
- resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.3.tgz#0004569b37c7c74ba39c43f3ced78d1ad94140be"
- integrity sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=
+ version "2.0.5"
+ resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd"
+ integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==
dependencies:
- readable-stream "^2.1.5"
+ readable-stream "~2.3.6"
xtend "~4.0.1"
thunky@^1.0.2:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.0.2.tgz#a862e018e3fb1ea2ec3fce5d55605cf57f247371"
- integrity sha1-qGLgGOP7HqLsP85dVWBc9X8kc3E=
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.0.3.tgz#f5df732453407b09191dae73e2a8cc73f381a826"
+ integrity sha512-YwT8pjmNcAXBZqrubu22P4FYsh2D4dxRmnWBOL8Jk8bUcRUtc5326kx32tuTmFDAZtLOGEVNl8POAR8j896Iow==
timers-browserify@^2.0.4:
- version "2.0.6"
- resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.6.tgz#241e76927d9ca05f4d959819022f5b3664b64bae"
- integrity sha512-HQ3nbYRAowdVd0ckGFvmJPPCOH/CHleFN/Y0YQCX1DVaB7t+KFvisuyN09fuP8Jtp1CpfSh8O8bMkHbdbPe6Pw==
+ version "2.0.10"
+ resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.10.tgz#1d28e3d2aadf1d5a5996c4e9f95601cd053480ae"
+ integrity sha512-YvC1SV1XdOUaL6gx5CoGroT3Gu49pK9+TZ38ErPldOWW4j49GI1HKs9DV+KGq/w6y+LZ72W1c8cKz2vzY+qpzg==
dependencies:
setimmediate "^1.0.4"
@@ -6966,16 +6481,7 @@ to-regex-range@^2.1.0:
is-number "^3.0.0"
repeat-string "^1.6.1"
-to-regex@^3.0.1:
- version "3.0.1"
- resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.1.tgz#15358bee4a2c83bd76377ba1dc049d0f18837aae"
- integrity sha1-FTWL7kosg712N3uh3ASdDxiDeq4=
- dependencies:
- define-property "^0.2.5"
- extend-shallow "^2.0.1"
- regex-not "^1.0.0"
-
-to-regex@^3.0.2:
+to-regex@^3.0.1, to-regex@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce"
integrity sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==
@@ -6985,13 +6491,6 @@ to-regex@^3.0.2:
regex-not "^1.0.2"
safe-regex "^1.1.0"
-tough-cookie@~2.3.0:
- version "2.3.3"
- resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.3.tgz#0b618a5565b6dea90bf3425d04d55edc475a7561"
- integrity sha1-C2GKVWW23qkL80JdBNVe3EdadWE=
- dependencies:
- punycode "^1.4.1"
-
tough-cookie@~2.4.3:
version "2.4.3"
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.4.3.tgz#53f36da3f47783b0925afa06ff9f3b165280f781"
@@ -7010,17 +6509,22 @@ trim-right@^1.0.1:
resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003"
integrity sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=
+trix@^1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/trix/-/trix-1.1.1.tgz#ca8a3fab4d23d960a17e88441d601cb22a86f5d5"
+ integrity sha512-dNHTcryRK0EmwCyJDOBrG6OznL8HNEVVlecq/xzxLj3M9Eht5DV4yl+eCYX8RKyfrtBcNki+mpKIxESgXaGGZA==
+
"true-case-path@^1.0.2":
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/true-case-path/-/true-case-path-1.0.2.tgz#7ec91130924766c7f573be3020c34f8fdfd00d62"
- integrity sha1-fskRMJJHZsf1c74wIMNPj9/QDWI=
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/true-case-path/-/true-case-path-1.0.3.tgz#f813b5a8c86b40da59606722b144e3225799f47d"
+ integrity sha512-m6s2OdQe5wgpFMC+pAJ+q9djG82O2jcHPOI6RNg1yy9rCYR+WD6Nbpl32fDpfC56nirdRy+opFa/Vk7HYhqaew==
dependencies:
- glob "^6.0.4"
+ glob "^7.1.2"
-ts-pnp@^1.0.0:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/ts-pnp/-/ts-pnp-1.0.1.tgz#fde74a6371676a167abaeda1ffc0fdb423520098"
- integrity sha512-Zzg9XH0anaqhNSlDRibNC8Kp+B9KNM0uRIpLpGkGyrgRIttA7zZBhotTSEoEyuDrz3QW2LGtu2dxuk34HzIGnQ==
+ts-pnp@^1.1.2:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/ts-pnp/-/ts-pnp-1.1.2.tgz#be8e4bfce5d00f0f58e0666a82260c34a57af552"
+ integrity sha512-f5Knjh7XCyRIzoC/z1Su1yLLRrPrFCgtUAh/9fCSP6NKbATwpOL1+idQVXQokK9GRFURn/jYPGPfegIctwunoA==
tslib@^1.9.0:
version "1.9.3"
@@ -7044,29 +6548,19 @@ tweetnacl@^0.14.3, tweetnacl@~0.14.0:
resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64"
integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=
-type-is@~1.6.15:
- version "1.6.15"
- resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.15.tgz#cab10fb4909e441c82842eafe1ad646c81804410"
- integrity sha1-yrEPtJCeRByChC6v4a1kbIGARBA=
+type-is@~1.6.16:
+ version "1.6.18"
+ resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131"
+ integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==
dependencies:
media-typer "0.3.0"
- mime-types "~2.1.15"
+ mime-types "~2.1.24"
typedarray@^0.0.6:
version "0.0.6"
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
-uid-number@^0.0.6:
- version "0.0.6"
- resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81"
- integrity sha1-DqEOgDXo61uOREnwbaHHMGY7qoE=
-
-underscore.string@2.3.x:
- version "2.3.3"
- resolved "https://registry.yarnpkg.com/underscore.string/-/underscore.string-2.3.3.tgz#71c08bf6b428b1133f37e78fa3a21c82f7329b0d"
- integrity sha1-ccCL9rQosRM/N+ePo6Icgvcymw0=
-
unicode-canonical-property-names-ecmascript@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818"
@@ -7118,9 +6612,9 @@ unique-filename@^1.1.1:
unique-slug "^2.0.0"
unique-slug@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-2.0.0.tgz#db6676e7c7cc0629878ff196097c78855ae9f4ab"
- integrity sha1-22Z258fMBimHj/GWCXx4hVrp9Ks=
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-2.0.1.tgz#5e9edc6d1ce8fb264db18a507ef9bd8544451ca6"
+ integrity sha512-n9cU6+gITaVu7VGj1Z8feKMmfAjEAQGhwD9fE3zvpRRa0wEIx8ODYkVGfSc94M2OX00tUFV8wH3zYbm1I8mxFg==
dependencies:
imurmurhash "^0.1.4"
@@ -7142,18 +6636,10 @@ unset-value@^1.0.0:
has-value "^0.3.1"
isobject "^3.0.0"
-upath@1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/upath/-/upath-1.0.0.tgz#b4706b9461ca8473adf89133d235689ca17f3656"
- integrity sha1-tHBrlGHKhHOt+JEz0jVonKF/NlY=
- dependencies:
- lodash "3.x"
- underscore.string "2.3.x"
-
-upath@^1.1.0:
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/upath/-/upath-1.1.1.tgz#497f7c1090b0818f310bbfb06783586a68d28014"
- integrity sha512-D0yetkpIOKiZQquxjM2Syvy48Y1DbZ0SWxgsZiwd9GCWRpc75vN8ytzem14WDSg+oiX6+Qt31FpiS/ExODCrLg==
+upath@^1.1.1:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/upath/-/upath-1.1.2.tgz#3db658600edaeeccbe6db5e684d67ee8c2acd068"
+ integrity sha512-kXpym8nmDmlCBr7nKdIx8P2jNBa+pBpIUFRnKJ4dr8htyYGJFokkr2ZvERRtUN+9SY+JqXouNgUPtv6JQva/2Q==
uri-js@^4.2.2:
version "4.2.2"
@@ -7168,11 +6654,11 @@ urix@^0.1.0:
integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=
url-parse@^1.4.3:
- version "1.4.4"
- resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.4.4.tgz#cac1556e95faa0303691fec5cf9d5a1bc34648f8"
- integrity sha512-/92DTTorg4JjktLNLe6GPS2/RvAd/RGr6LuktmWSMLEOa6rjnlrFXNgSbSmkNvCoL2T028A0a1JaJLzRMlFoHg==
+ version "1.4.7"
+ resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.4.7.tgz#a8a83535e8c00a316e403a5db4ac1b9b853ae278"
+ integrity sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg==
dependencies:
- querystringify "^2.0.0"
+ querystringify "^2.1.1"
requires-port "^1.0.0"
url@^0.11.0:
@@ -7183,14 +6669,10 @@ url@^0.11.0:
punycode "1.3.2"
querystring "0.2.0"
-use@^2.0.0:
- version "2.0.2"
- resolved "https://registry.yarnpkg.com/use/-/use-2.0.2.tgz#ae28a0d72f93bf22422a18a2e379993112dec8e8"
- integrity sha1-riig1y+TvyJCKhii43mZMRLeyOg=
- dependencies:
- define-property "^0.2.5"
- isobject "^3.0.0"
- lazy-cache "^2.0.2"
+use@^3.1.0:
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f"
+ integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==
util-deprecate@^1.0.1, util-deprecate@~1.0.1:
version "1.0.2"
@@ -7205,40 +6687,42 @@ util.promisify@^1.0.0, util.promisify@~1.0.0:
define-properties "^1.1.2"
object.getownpropertydescriptors "^2.0.3"
-util@0.10.3, util@^0.10.3:
+util@0.10.3:
version "0.10.3"
resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9"
integrity sha1-evsa/lCAUkZInj23/g7TeTNqwPk=
dependencies:
inherits "2.0.1"
+util@^0.11.0:
+ version "0.11.1"
+ resolved "https://registry.yarnpkg.com/util/-/util-0.11.1.tgz#3236733720ec64bb27f6e26f421aaa2e1b588d61"
+ integrity sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==
+ dependencies:
+ inherits "2.0.3"
+
utils-merge@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=
-uuid@^3.0.0, uuid@^3.0.1:
- version "3.2.1"
- resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.2.1.tgz#12c528bb9d58d0b9265d9a2f6f0fe8be17ff1f14"
- integrity sha512-jZnMwlb9Iku/O3smGWvZhauCf6cvvpKi4BKRiliS3cxnI+Gz9j5MEpTz2UFuXiKPJocb7gnsLHwiS05ige5BEA==
-
-uuid@^3.3.2:
+uuid@^3.0.1, uuid@^3.3.2:
version "3.3.2"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131"
integrity sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==
v8-compile-cache@^2.0.2:
- version "2.0.2"
- resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.0.2.tgz#a428b28bb26790734c4fc8bc9fa106fccebf6a6c"
- integrity sha512-1wFuMUIM16MDJRCrpbpuEPTUGmM5QMUg0cr3KFwra2XgOgFcPGDQHDh3CszSCD2Zewc/dh/pamNEW8CbfDebUw==
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.0.3.tgz#00f7494d2ae2b688cfe2899df6ed2c54bef91dbe"
+ integrity sha512-CNmdbwQMBjwr9Gsmohvm0pbL954tJrNzf6gWL3K+QMQf00PF7ERGrEiLgjuU3mKreLC2MeGhUsNV9ybTbLgd3w==
validate-npm-package-license@^3.0.1:
- version "3.0.1"
- resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz#2804babe712ad3379459acfbe24746ab2c303fbc"
- integrity sha1-KAS6vnEq0zeUWaz74kdGqywwP7w=
+ version "3.0.4"
+ resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a"
+ integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==
dependencies:
- spdx-correct "~1.0.0"
- spdx-expression-parse "~1.0.0"
+ spdx-correct "^3.0.0"
+ spdx-expression-parse "^3.0.0"
vary@~1.1.2:
version "1.1.2"
@@ -7246,9 +6730,9 @@ vary@~1.1.2:
integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=
vendors@^1.0.0:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/vendors/-/vendors-1.0.1.tgz#37ad73c8ee417fb3d580e785312307d274847f22"
- integrity sha1-N61zyO5Bf7PVgOeFMSMH0nSEfyI=
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/vendors/-/vendors-1.0.3.tgz#a6467781abd366217c050f8202e7e50cc9eef8c0"
+ integrity sha512-fOi47nsJP5Wqefa43kyWSg80qF+Q3XA6MUkgi7Hp1HQaKDQW4cQrK2D0P7mmbFtsV1N89am55Yru/nyEwRubcw==
verror@1.10.0:
version "1.10.0"
@@ -7275,14 +6759,7 @@ watchpack@^1.5.0:
graceful-fs "^4.1.2"
neo-async "^2.5.0"
-wbuf@^1.1.0:
- version "1.7.2"
- resolved "https://registry.yarnpkg.com/wbuf/-/wbuf-1.7.2.tgz#d697b99f1f59512df2751be42769c1580b5801fe"
- integrity sha1-1pe5nx9ZUS3ydRvkJ2nBWAtYAf4=
- dependencies:
- minimalistic-assert "^1.0.0"
-
-wbuf@^1.7.3:
+wbuf@^1.1.0, wbuf@^1.7.3:
version "1.7.3"
resolved "https://registry.yarnpkg.com/wbuf/-/wbuf-1.7.3.tgz#c1d8d149316d3ea852848895cb6a0bfe887b87df"
integrity sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==
@@ -7303,9 +6780,9 @@ webpack-assets-manifest@^3.1.1:
webpack-sources "^1.0.0"
webpack-cli@^3.2.3:
- version "3.2.3"
- resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-3.2.3.tgz#13653549adfd8ccd920ad7be1ef868bacc22e346"
- integrity sha512-Ik3SjV6uJtWIAN5jp5ZuBMWEAaP5E4V78XJ2nI+paFPh8v4HPSwo/myN0r29Xc/6ZKnd2IdrAlpSgNOu2CDQ6Q==
+ version "3.3.2"
+ resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-3.3.2.tgz#aed2437b0db0a7faa2ad28484e166a5360014a91"
+ integrity sha512-FLkobnaJJ+03j5eplxlI0TUxhGCOdfewspIGuvDVtpOlrAuKMFC57K42Ukxqs1tn8947/PM6tP95gQc0DCzRYA==
dependencies:
chalk "^2.4.1"
cross-spawn "^6.0.5"
@@ -7317,53 +6794,53 @@ webpack-cli@^3.2.3:
loader-utils "^1.1.0"
supports-color "^5.5.0"
v8-compile-cache "^2.0.2"
- yargs "^12.0.4"
+ yargs "^12.0.5"
-webpack-dev-middleware@^3.5.1:
- version "3.6.1"
- resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-3.6.1.tgz#91f2531218a633a99189f7de36045a331a4b9cd4"
- integrity sha512-XQmemun8QJexMEvNFbD2BIg4eSKrmSIMrTfnl2nql2Sc6OGAYFyb8rwuYrCjl/IiEYYuyTEiimMscu7EXji/Dw==
+webpack-dev-middleware@^3.6.2:
+ version "3.6.2"
+ resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-3.6.2.tgz#f37a27ad7c09cd7dc67cd97655413abaa1f55942"
+ integrity sha512-A47I5SX60IkHrMmZUlB0ZKSWi29TZTcPz7cha1Z75yYOsgWh/1AcPmQEbC8ZIbU3A1ytSv1PMU0PyPz2Lmz2jg==
dependencies:
memory-fs "^0.4.1"
mime "^2.3.1"
range-parser "^1.0.3"
webpack-log "^2.0.0"
-webpack-dev-server@^3.2.1:
- version "3.2.1"
- resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-3.2.1.tgz#1b45ce3ecfc55b6ebe5e36dab2777c02bc508c4e"
- integrity sha512-sjuE4mnmx6JOh9kvSbPYw3u/6uxCLHNWfhWaIPwcXWsvWOPN+nc5baq4i9jui3oOBRXGonK9+OI0jVkaz6/rCw==
+webpack-dev-server@^3.3.1:
+ version "3.3.1"
+ resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-3.3.1.tgz#7046e49ded5c1255a82c5d942bcdda552b72a62d"
+ integrity sha512-jY09LikOyGZrxVTXK0mgIq9y2IhCoJ05848dKZqX1gAGLU1YDqgpOT71+W53JH/wI4v6ky4hm+KvSyW14JEs5A==
dependencies:
ansi-html "0.0.7"
bonjour "^3.5.0"
- chokidar "^2.0.0"
- compression "^1.5.2"
- connect-history-api-fallback "^1.3.0"
+ chokidar "^2.1.5"
+ compression "^1.7.4"
+ connect-history-api-fallback "^1.6.0"
debug "^4.1.1"
- del "^3.0.0"
- express "^4.16.2"
- html-entities "^1.2.0"
+ del "^4.1.0"
+ express "^4.16.4"
+ html-entities "^1.2.1"
http-proxy-middleware "^0.19.1"
import-local "^2.0.0"
internal-ip "^4.2.0"
ip "^1.1.5"
- killable "^1.0.0"
- loglevel "^1.4.1"
- opn "^5.1.0"
- portfinder "^1.0.9"
+ killable "^1.0.1"
+ loglevel "^1.6.1"
+ opn "^5.5.0"
+ portfinder "^1.0.20"
schema-utils "^1.0.0"
- selfsigned "^1.9.1"
- semver "^5.6.0"
- serve-index "^1.7.2"
+ selfsigned "^1.10.4"
+ semver "^6.0.0"
+ serve-index "^1.9.1"
sockjs "0.3.19"
sockjs-client "1.3.0"
spdy "^4.0.0"
- strip-ansi "^3.0.0"
+ strip-ansi "^3.0.1"
supports-color "^6.1.0"
url "^0.11.0"
- webpack-dev-middleware "^3.5.1"
+ webpack-dev-middleware "^3.6.2"
webpack-log "^2.0.0"
- yargs "12.0.2"
+ yargs "12.0.5"
webpack-log@^2.0.0:
version "2.0.0"
@@ -7373,7 +6850,7 @@ webpack-log@^2.0.0:
ansi-colors "^3.0.0"
uuid "^3.3.2"
-webpack-sources@^1.0.0, webpack-sources@^1.3.0:
+webpack-sources@^1.0.0, webpack-sources@^1.0.1, webpack-sources@^1.1.0, webpack-sources@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.3.0.tgz#2a28dcb9f1f45fe960d8f1493252b5ee6530fa85"
integrity sha512-OiVgSrbGu7NEnEvQJJgdSFPl2qWKkWq5lHMhgiToIiN9w34EBnjYzSYs+VbL5KoYiLNtFFa7BZIKxRED3I32pA==
@@ -7381,18 +6858,10 @@ webpack-sources@^1.0.0, webpack-sources@^1.3.0:
source-list-map "^2.0.0"
source-map "~0.6.1"
-webpack-sources@^1.0.1, webpack-sources@^1.1.0:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.1.0.tgz#a101ebae59d6507354d71d8013950a3a8b7a5a54"
- integrity sha512-aqYp18kPphgoO5c/+NaUvEeACtZjMESmDChuD3NBciVpah3XpMEU9VAAtIaB1BsfJWWTSdv8Vv1m3T0aRk2dUw==
- dependencies:
- source-list-map "^2.0.0"
- source-map "~0.6.1"
-
webpack@^4.29.6:
- version "4.29.6"
- resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.29.6.tgz#66bf0ec8beee4d469f8b598d3988ff9d8d90e955"
- integrity sha512-MwBwpiE1BQpMDkbnUUaW6K8RFZjljJHArC6tWQJoFm0oQtfoSebtg4Y7/QHnJ/SddtjYLHaKGX64CFjG5rehJw==
+ version "4.31.0"
+ resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.31.0.tgz#ae201d45f0571336e42d1c2b5c8ab56c4d3b0c63"
+ integrity sha512-n6RVO3X0LbbipoE62akME9K/JI7qYrwwufs20VvgNNpqUoH4860KkaxJTbGq5bgkVZF9FqyyTG/0WPLH3PVNJA==
dependencies:
"@webassemblyjs/ast" "1.8.5"
"@webassemblyjs/helper-module-context" "1.8.5"
@@ -7442,14 +6911,7 @@ which-module@^2.0.0:
resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a"
integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=
-which@1, which@^1.2.9:
- version "1.3.0"
- resolved "https://registry.yarnpkg.com/which/-/which-1.3.0.tgz#ff04bdfc010ee547d780bec38e1ac1c2777d253a"
- integrity sha512-xcJpopdamTuY5duC/KnTTNBraPK54YwpenP4lzxU8H91GudWpFv38u0CKjclE1Wi2EH2EDz5LRcHcKbCIzqGyg==
- dependencies:
- isexe "^2.0.0"
-
-which@^1.2.14:
+which@1, which@^1.2.14, which@^1.2.9:
version "1.3.1"
resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"
integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==
@@ -7457,19 +6919,18 @@ which@^1.2.14:
isexe "^2.0.0"
wide-align@^1.1.0:
- version "1.1.2"
- resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.2.tgz#571e0f1b0604636ebc0dfc21b0339bbe31341710"
- integrity sha512-ijDLlyQ7s6x1JgCLur53osjm/UXUYD9+0PbYKrBsYisYXzCxN+HC3mYDNy/dWdmf3AwqwU3CXwDCvsNgGK1S0w==
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457"
+ integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==
dependencies:
- string-width "^1.0.2"
+ string-width "^1.0.2 || 2"
worker-farm@^1.5.2:
- version "1.5.2"
- resolved "https://registry.yarnpkg.com/worker-farm/-/worker-farm-1.5.2.tgz#32b312e5dc3d5d45d79ef44acc2587491cd729ae"
- integrity sha512-XxiQ9kZN5n6mmnW+mFJ+wXjNNI/Nx4DIdaAKLX1Bn6LYBWlN/zaBhu34DQYPZ1AJobQuu67S2OfDdNSVULvXkQ==
+ version "1.7.0"
+ resolved "https://registry.yarnpkg.com/worker-farm/-/worker-farm-1.7.0.tgz#26a94c5391bbca926152002f69b84a4bf772e5a8"
+ integrity sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==
dependencies:
- errno "^0.1.4"
- xtend "^4.0.1"
+ errno "~0.1.7"
wrap-ansi@^2.0.0:
version "2.1.0"
@@ -7484,12 +6945,7 @@ wrappy@1:
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
-xregexp@4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/xregexp/-/xregexp-4.0.0.tgz#e698189de49dd2a18cc5687b05e17c8e43943020"
- integrity sha512-PHyM+sQouu7xspQQwELlGwwd05mXUFqwFYfqPO0cC7x4fxyHnnuetmQr6CjJiafIDoH4MogHb9dOoJzR/Y4rFg==
-
-xtend@^4.0.0, xtend@^4.0.1, xtend@~4.0.1:
+xtend@^4.0.0, xtend@~4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af"
integrity sha1-pcbVMr5lbiPbgg77lDofBJmNY68=
@@ -7514,13 +6970,6 @@ yallist@^3.0.0, yallist@^3.0.2:
resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.0.3.tgz#b4b049e314be545e3ce802236d6cd22cd91c3de9"
integrity sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==
-yargs-parser@^10.1.0:
- version "10.1.0"
- resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-10.1.0.tgz#7202265b89f7e9e9f2e5765e0fe735a905edbaa8"
- integrity sha512-VCIyR1wJoEBZUqk5PA+oOBF6ypbwh5aNB3I50guxAL/quggdfs4TtNHQrSazFA3fYZ+tEqfs0zIGlv0c/rgjbQ==
- dependencies:
- camelcase "^4.1.0"
-
yargs-parser@^11.1.1:
version "11.1.1"
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-11.1.1.tgz#879a0865973bca9f6bab5cbdf3b1c67ec7d3bcf4"
@@ -7536,25 +6985,7 @@ yargs-parser@^5.0.0:
dependencies:
camelcase "^3.0.0"
-yargs@12.0.2:
- version "12.0.2"
- resolved "https://registry.yarnpkg.com/yargs/-/yargs-12.0.2.tgz#fe58234369392af33ecbef53819171eff0f5aadc"
- integrity sha512-e7SkEx6N6SIZ5c5H22RTZae61qtn3PYUE8JYbBFlK9sYmh3DMQ6E5ygtaG/2BW0JZi4WGgTR2IV5ChqlqrDGVQ==
- dependencies:
- cliui "^4.0.0"
- decamelize "^2.0.0"
- find-up "^3.0.0"
- get-caller-file "^1.0.1"
- os-locale "^3.0.0"
- require-directory "^2.1.1"
- require-main-filename "^1.0.1"
- set-blocking "^2.0.0"
- string-width "^2.0.0"
- which-module "^2.0.0"
- y18n "^3.2.1 || ^4.0.0"
- yargs-parser "^10.1.0"
-
-yargs@^12.0.4:
+yargs@12.0.5, yargs@^12.0.5:
version "12.0.5"
resolved "https://registry.yarnpkg.com/yargs/-/yargs-12.0.5.tgz#05f5997b609647b64f66b81e3b4b10a368e7ad13"
integrity sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==
diff --git a/actiontext/test/system/system_test_helper_test.rb b/actiontext/test/system/system_test_helper_test.rb
new file mode 100644
index 0000000000..2a57fa8d88
--- /dev/null
+++ b/actiontext/test/system/system_test_helper_test.rb
@@ -0,0 +1,39 @@
+# frozen_string_literal: true
+
+require "application_system_test_case"
+
+class ActionText::SystemTestHelperTest < ApplicationSystemTestCase
+ test "filling in a rich-text area by ID" do
+ visit new_message_url
+ assert_selector "trix-editor#message_content"
+ fill_in_rich_text_area "message_content", with: "Hello world!"
+ assert_selector :field, "message[content]", with: /Hello world!/, type: "hidden"
+ end
+
+ test "filling in a rich-text area by placeholder" do
+ visit new_message_url
+ assert_selector "trix-editor[placeholder='Your message here']"
+ fill_in_rich_text_area "Your message here", with: "Hello world!"
+ assert_selector :field, "message[content]", with: /Hello world!/, type: "hidden"
+ end
+
+ test "filling in a rich-text area by aria-label" do
+ visit new_message_url
+ assert_selector "trix-editor[aria-label='Message content']"
+ fill_in_rich_text_area "Message content", with: "Hello world!"
+ assert_selector :field, "message[content]", with: /Hello world!/, type: "hidden"
+ end
+
+ test "filling in a rich-text area by input name" do
+ visit new_message_url
+ assert_selector "trix-editor[input]"
+ fill_in_rich_text_area "message[content]", with: "Hello world!"
+ assert_selector :field, "message[content]", with: /Hello world!/, type: "hidden"
+ end
+
+ test "filling in the only rich-text area" do
+ visit new_message_url
+ fill_in_rich_text_area with: "Hello world!"
+ assert_selector :field, "message[content]", with: /Hello world!/, type: "hidden"
+ end
+end
diff --git a/actiontext/test/unit/attachment_test.rb b/actiontext/test/unit/attachment_test.rb
index 026078dcec..54831a0271 100644
--- a/actiontext/test/unit/attachment_test.rb
+++ b/actiontext/test/unit/attachment_test.rb
@@ -50,6 +50,11 @@ class ActionText::AttachmentTest < ActiveSupport::TestCase
assert_not_nil trix_attachment.attributes["content"]
end
+ test "converts to plain text" do
+ assert_equal "[Vroom vroom]", ActionText::Attachment.from_attachable(attachable, caption: "Vroom vroom").to_plain_text
+ assert_equal "[racecar.jpg]", ActionText::Attachment.from_attachable(attachable).to_plain_text
+ end
+
test "defaults trix partial to model partial" do
attachable = Page.create! title: "Homepage"
assert_equal "pages/page", attachable.to_trix_content_attachment_partial_path
diff --git a/actiontext/test/unit/model_test.rb b/actiontext/test/unit/model_test.rb
index af53f88caa..c2c3ccaaec 100644
--- a/actiontext/test/unit/model_test.rb
+++ b/actiontext/test/unit/model_test.rb
@@ -44,6 +44,15 @@ class ActionText::ModelTest < ActiveSupport::TestCase
assert_equal [ActiveStorage::Attachment], message.content.embeds.map(&:class)
end
+ test "embed extraction deduplicates file attachments" do
+ blob = create_file_blob(filename: "racecar.jpg", content_type: "image/jpg")
+ content = ActionText::Content.new("Hello world").append_attachables([ blob, blob ])
+
+ assert_nothing_raised do
+ Message.create!(subject: "Greetings", content: content)
+ end
+ end
+
test "saving content" do
message = Message.create!(subject: "Greetings", content: "<h1>Hello world</h1>")
assert_equal "Hello world", message.content.to_plain_text
diff --git a/actionview/CHANGELOG.md b/actionview/CHANGELOG.md
index be67aff543..504b1d3e98 100644
--- a/actionview/CHANGELOG.md
+++ b/actionview/CHANGELOG.md
@@ -1,244 +1,19 @@
-* Only clear ActionView cache in development on file changes
+* annotated_source_code returns an empty array so TemplateErrors without a
+ template in the backtrace are surfaced properly by DebugExceptions.
- To speed up development mode, view caches are only cleared when files in
- the view paths have changed. Applications which have implemented custom
- `ActionView::Resolver` subclasses may need to add their own cache clearing.
+ *Guilherme Mansur*, *Kasper Timm Hansen*
- *John Hawthorn*
+* Add autoload for SyntaxErrorInTemplate so syntax errors are correctly raised by DebugExceptions.
-* Fix `ActionView::FixtureResolver` so that it handles template variants correctly.
+ *Guilherme Mansur*, *Gannon McGibbon*
- *Edward Rudd*
+* `RenderingHelper` supports rendering objects that `respond_to?` `:render_in`
+ *Joel Hawksley*, *Natasha Umer*, *Aaron Patterson*, *Shawn Allen*, *Emily Plummer*, *Diana Mounter*, *John Hawthorn*, *Nathan Herald*, *Zaid Zawaideh*, *Zach Ahn*
-## Rails 6.0.0.beta3 (March 11, 2019) ##
+* Fix `select_tag` so that it doesn't change `options` when `include_blank` is present.
-* Only accept formats from registered mime types
+ *Younes SERRAJ*
- A lack of filtering on mime types could allow an attacker to read
- arbitrary files on the target server or to perform a denial of service
- attack.
- Fixes CVE-2019-5418
- Fixes CVE-2019-5419
-
- *John Hawthorn*, *Eileen M. Uchitelle*, *Aaron Patterson*
-
-
-## Rails 6.0.0.beta2 (February 25, 2019) ##
-
-* `ActionView::Template.finalize_compiled_template_methods` is deprecated with
- no replacement.
-
- *tenderlove*
-
-* `config.action_view.finalize_compiled_template_methods` is deprecated with
- no replacement.
-
- *tenderlove*
-
-* Ensure unique DOM IDs for collection inputs with float values.
-
- Fixes #34974.
-
- *Mark Edmondson*
-
-
-## Rails 6.0.0.beta1 (January 18, 2019) ##
-
-* [Rename npm package](https://github.com/rails/rails/pull/34905) from
- [`rails-ujs`](https://www.npmjs.com/package/rails-ujs) to
- [`@rails/ujs`](https://www.npmjs.com/package/@rails/ujs).
-
- *Javan Makhmali*
-
-* Remove deprecated `image_alt` helper.
-
- *Rafael Mendonça França*
-
-* Fix the need of `#protect_against_forgery?` method defined in
- `ActionView::Base` subclasses. This prevents the use of forms and buttons.
-
- *Genadi Samokovarov*
-
-* Fix UJS permanently showing disabled text in a[data-remote][data-disable-with] elements within forms.
-
- Fixes #33889.
-
- *Wolfgang Hobmaier*
-
-* Prevent non-primary mouse keys from triggering Rails UJS click handlers.
- Firefox fires click events even if the click was triggered by non-primary mouse keys such as right- or scroll-wheel-clicks.
- For example, right-clicking a link such as the one described below (with an underlying ajax request registered on click) should not cause that request to occur.
-
- ```
- <%= link_to 'Remote', remote_path, class: 'remote', remote: true, data: { type: :json } %>
- ```
-
- Fixes #34541.
-
- *Wolfgang Hobmaier*
-
-* Prevent `ActionView::TextHelper#word_wrap` from unexpectedly stripping white space from the _left_ side of lines.
-
- For example, given input like this:
-
- ```
- This is a paragraph with an initial indent,
- followed by additional lines that are not indented,
- and finally terminated with a blockquote:
- "A pithy saying"
- ```
-
- Calling `word_wrap` should not trim the indents on the first and last lines.
-
- Fixes #34487.
-
- *Lyle Mullican*
-
-* Add allocations to template rendering instrumentation.
-
- Adds the allocations for template and partial rendering to the server output on render.
-
- ```
- Rendered posts/_form.html.erb (Duration: 7.1ms | Allocations: 6004)
- Rendered posts/new.html.erb within layouts/application (Duration: 8.3ms | Allocations: 6654)
- Completed 200 OK in 858ms (Views: 848.4ms | ActiveRecord: 0.4ms | Allocations: 1539564)
- ```
-
- *Eileen M. Uchitelle*, *Aaron Patterson*
-
-* Respect the `only_path` option passed to `url_for` when the options are passed in as an array
-
- Fixes #33237.
-
- *Joel Ambass*
-
-* Deprecate calling private model methods from view helpers.
-
- For example, in methods like `options_from_collection_for_select`
- and `collection_select` it is possible to call private methods from
- the objects used.
-
- Fixes #33546.
-
- *Ana María Martínez Gómez*
-
-* Fix issue with `button_to`'s `to_form_params`
-
- `button_to` was throwing exception when invoked with `params` hash that
- contains symbol and string keys. The reason for the exception was that
- `to_form_params` was comparing the given symbol and string keys.
-
- The issue is fixed by turning all keys to strings inside
- `to_form_params` before comparing them.
-
- *Georgi Georgiev*
-
-* Mark arrays of translations as trusted safe by using the `_html` suffix.
-
- Example:
-
- en:
- foo_html:
- - "One"
- - "<strong>Two</strong>"
- - "Three &#128075; &#128578;"
-
- *Juan Broullon*
-
-* Add `year_format` option to date_select tag. This option makes it possible to customize year
- names. Lambda should be passed to use this option.
-
- Example:
-
- date_select('user_birthday', '', start_year: 1998, end_year: 2000, year_format: ->year { "Heisei #{year - 1988}" })
-
- The HTML produced:
-
- <select id="user_birthday__1i" name="user_birthday[(1i)]">
- <option value="1998">Heisei 10</option>
- <option value="1999">Heisei 11</option>
- <option value="2000">Heisei 12</option>
- </select>
- /* The rest is omitted */
-
- *Koki Ryu*
-
-* Fix JavaScript views rendering does not work with Firefox when using
- Content Security Policy.
-
- Fixes #32577.
-
- *Yuji Yaginuma*
-
-* Add the `nonce: true` option for `javascript_include_tag` helper to
- support automatic nonce generation for Content Security Policy.
- Works the same way as `javascript_tag nonce: true` does.
-
- *Yaroslav Markin*
-
-* Remove `ActionView::Helpers::RecordTagHelper`.
-
- *Yoshiyuki Hirano*
-
-* Disable `ActionView::Template` finalizers in test environment.
-
- Template finalization can be expensive in large view test suites.
- Add a configuration option,
- `action_view.finalize_compiled_template_methods`, and turn it off in
- the test environment.
-
- *Simon Coffey*
-
-* Extract the `confirm` call in its own, overridable method in `rails_ujs`.
-
- Example:
-
- Rails.confirm = function(message, element) {
- return (my_bootstrap_modal_confirm(message));
- }
-
- *Mathieu Mahé*
-
-* Enable select tag helper to mark `prompt` option as `selected` and/or `disabled` for `required`
- field.
-
- Example:
-
- select :post,
- :category,
- ["lifestyle", "programming", "spiritual"],
- { selected: "", disabled: "", prompt: "Choose one" },
- { required: true }
-
- Placeholder option would be selected and disabled.
-
- The HTML produced:
-
- <select required="required" name="post[category]" id="post_category">
- <option disabled="disabled" selected="selected" value="">Choose one</option>
- <option value="lifestyle">lifestyle</option>
- <option value="programming">programming</option>
- <option value="spiritual">spiritual</option></select>
-
- *Sergey Prikhodko*
-
-* Don't enforce UTF-8 by default.
-
- With the disabling of TLS 1.0 by most major websites, continuing to run
- IE8 or lower becomes increasingly difficult so default to not enforcing
- UTF-8 encoding as it's not relevant to other browsers.
-
- *Andrew White*
-
-* Change translation key of `submit_tag` from `module_name_class_name` to `module_name/class_name`.
-
- *Rui Onodera*
-
-* Rails 6 requires Ruby 2.5.0 or newer.
-
- *Jeremy Daer*, *Kasper Timm Hansen*
-
-
-Please check [5-2-stable](https://github.com/rails/rails/blob/5-2-stable/actionview/CHANGELOG.md) for previous changes.
+Please check [6-0-stable](https://github.com/rails/rails/blob/6-0-stable/actionview/CHANGELOG.md) for previous changes.
diff --git a/actionview/app/assets/javascripts/README.md b/actionview/app/assets/javascripts/README.md
index aa167004b6..c9282ba76f 100644
--- a/actionview/app/assets/javascripts/README.md
+++ b/actionview/app/assets/javascripts/README.md
@@ -15,7 +15,7 @@ Note that the `data` attributes this library adds are a feature of HTML5. If you
## Installation
-### NPM
+### npm
npm install @rails/ujs --save
diff --git a/actionview/app/assets/javascripts/rails-ujs/features/remote.coffee b/actionview/app/assets/javascripts/rails-ujs/features/remote.coffee
index a5b61220bb..d1aeef56c7 100644
--- a/actionview/app/assets/javascripts/rails-ujs/features/remote.coffee
+++ b/actionview/app/assets/javascripts/rails-ujs/features/remote.coffee
@@ -88,6 +88,6 @@ Rails.preventInsignificantClick = (e) ->
data = link.getAttribute('data-params')
metaClick = e.metaKey or e.ctrlKey
insignificantMetaClick = metaClick and method is 'GET' and not data
- primaryMouseKey = e.button is 0
- e.stopImmediatePropagation() if not primaryMouseKey or insignificantMetaClick
+ nonPrimaryMouseClick = e.button? and e.button isnt 0
+ e.stopImmediatePropagation() if nonPrimaryMouseClick or insignificantMetaClick
diff --git a/actionview/lib/action_view.rb b/actionview/lib/action_view.rb
index 7f85bf2a5e..11b4563548 100644
--- a/actionview/lib/action_view.rb
+++ b/actionview/lib/action_view.rb
@@ -77,6 +77,7 @@ module ActionView
autoload :ActionViewError
autoload :EncodingError
autoload :TemplateError
+ autoload :SyntaxErrorInTemplate
autoload :WrongEncodingError
end
end
diff --git a/actionview/lib/action_view/base.rb b/actionview/lib/action_view/base.rb
index 5253ef7b0c..40d5ed36a1 100644
--- a/actionview/lib/action_view/base.rb
+++ b/actionview/lib/action_view/base.rb
@@ -3,7 +3,6 @@
require "active_support/core_ext/module/attr_internal"
require "active_support/core_ext/module/attribute_accessors"
require "active_support/ordered_options"
-require "active_support/deprecation"
require "action_view/log_subscriber"
require "action_view/helpers"
require "action_view/context"
diff --git a/actionview/lib/action_view/cache_expiry.rb b/actionview/lib/action_view/cache_expiry.rb
index 820afc093d..27690561b7 100644
--- a/actionview/lib/action_view/cache_expiry.rb
+++ b/actionview/lib/action_view/cache_expiry.rb
@@ -16,18 +16,23 @@ module ActionView
@watched_dirs = nil
@watcher_class = watcher
@watcher = nil
+ @mutex = Mutex.new
end
def clear_cache_if_necessary
- watched_dirs = dirs_to_watch
- if watched_dirs != @watched_dirs
- @watched_dirs = watched_dirs
- @watcher = @watcher_class.new([], watched_dirs) do
- clear_cache
+ @mutex.synchronize do
+ watched_dirs = dirs_to_watch
+ return if watched_dirs.empty?
+
+ if watched_dirs != @watched_dirs
+ @watched_dirs = watched_dirs
+ @watcher = @watcher_class.new([], watched_dirs) do
+ clear_cache
+ end
+ @watcher.execute
+ else
+ @watcher.execute_if_updated
end
- @watcher.execute
- else
- @watcher.execute_if_updated
end
end
@@ -36,7 +41,6 @@ module ActionView
end
private
-
def dirs_to_watch
fs_paths = all_view_paths.grep(FileSystemResolver)
fs_paths.map(&:path).sort.uniq
diff --git a/actionview/lib/action_view/flows.rb b/actionview/lib/action_view/flows.rb
index ff44fa6619..1609777182 100644
--- a/actionview/lib/action_view/flows.rb
+++ b/actionview/lib/action_view/flows.rb
@@ -68,7 +68,6 @@ module ActionView
end
private
-
def inside_fiber?
Fiber.current.object_id != @root
end
diff --git a/actionview/lib/action_view/gem_version.rb b/actionview/lib/action_view/gem_version.rb
index 5bed37583e..011662d8d1 100644
--- a/actionview/lib/action_view/gem_version.rb
+++ b/actionview/lib/action_view/gem_version.rb
@@ -8,9 +8,9 @@ module ActionView
module VERSION
MAJOR = 6
- MINOR = 0
+ MINOR = 1
TINY = 0
- PRE = "beta3"
+ PRE = "alpha"
STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
end
diff --git a/actionview/lib/action_view/helpers/active_model_helper.rb b/actionview/lib/action_view/helpers/active_model_helper.rb
index e41a95d2ce..e8f1d4fee5 100644
--- a/actionview/lib/action_view/helpers/active_model_helper.rb
+++ b/actionview/lib/action_view/helpers/active_model_helper.rb
@@ -38,7 +38,6 @@ module ActionView
end
private
-
def object_has_errors?
object.respond_to?(:errors) && object.errors.respond_to?(:[]) && error_message.present?
end
diff --git a/actionview/lib/action_view/helpers/asset_tag_helper.rb b/actionview/lib/action_view/helpers/asset_tag_helper.rb
index 59d70a1dc4..1e1d97fe75 100644
--- a/actionview/lib/action_view/helpers/asset_tag_helper.rb
+++ b/actionview/lib/action_view/helpers/asset_tag_helper.rb
@@ -3,7 +3,6 @@
require "active_support/core_ext/array/extract_options"
require "active_support/core_ext/hash/keys"
require "active_support/core_ext/object/inclusion"
-require "active_support/core_ext/object/try"
require "action_view/helpers/asset_url_helper"
require "action_view/helpers/tag_helper"
@@ -268,7 +267,7 @@ module ActionView
def preload_link_tag(source, options = {})
href = asset_path(source, skip_pipeline: options.delete(:skip_pipeline))
extname = File.extname(source).downcase.delete(".")
- mime_type = options.delete(:type) || Template::Types[extname].try(:to_s)
+ mime_type = options.delete(:type) || Template::Types[extname]&.to_s
as_type = options.delete(:as) || resolve_link_as(extname, mime_type)
crossorigin = options.delete(:crossorigin)
crossorigin = "anonymous" if crossorigin == true || (crossorigin.blank? && as_type == "font")
diff --git a/actionview/lib/action_view/helpers/asset_url_helper.rb b/actionview/lib/action_view/helpers/asset_url_helper.rb
index cc62783d60..295f945325 100644
--- a/actionview/lib/action_view/helpers/asset_url_helper.rb
+++ b/actionview/lib/action_view/helpers/asset_url_helper.rb
@@ -133,6 +133,8 @@ module ActionView
# which is implemented by sprockets-rails.
#
# asset_path("application.js") # => "/assets/application-60aa4fdc5cea14baf5400fba1abf4f2a46a5166bad4772b1effe341570f07de9.js"
+ # asset_path('application.js', host: 'example.com') # => "//example.com/assets/application.js"
+ # asset_path("application.js", host: 'example.com', protocol: 'https') # => "https://example.com/assets/application.js"
#
# === Without the asset pipeline (<tt>skip_pipeline: true</tt>)
#
diff --git a/actionview/lib/action_view/helpers/cache_helper.rb b/actionview/lib/action_view/helpers/cache_helper.rb
index 020aebeea3..5a05f8706a 100644
--- a/actionview/lib/action_view/helpers/cache_helper.rb
+++ b/actionview/lib/action_view/helpers/cache_helper.rb
@@ -227,7 +227,6 @@ module ActionView
end
private
-
def fragment_name_with_digest(name, virtual_path, digest_path)
virtual_path ||= @virtual_path
diff --git a/actionview/lib/action_view/helpers/date_helper.rb b/actionview/lib/action_view/helpers/date_helper.rb
index 9d5e5eaba3..f1d9fdc678 100644
--- a/actionview/lib/action_view/helpers/date_helper.rb
+++ b/actionview/lib/action_view/helpers/date_helper.rb
@@ -688,7 +688,6 @@ module ActionView
end
private
-
def normalize_distance_of_time_argument_to_time(value)
if value.is_a?(Numeric)
Time.at(value)
diff --git a/actionview/lib/action_view/helpers/form_tag_helper.rb b/actionview/lib/action_view/helpers/form_tag_helper.rb
index 5d4ff36425..c93ead9653 100644
--- a/actionview/lib/action_view/helpers/form_tag_helper.rb
+++ b/actionview/lib/action_view/helpers/form_tag_helper.rb
@@ -137,7 +137,8 @@ module ActionView
html_name = (options[:multiple] == true && !name.to_s.ends_with?("[]")) ? "#{name}[]" : name
if options.include?(:include_blank)
- include_blank = options.delete(:include_blank)
+ include_blank = options[:include_blank]
+ options = options.except(:include_blank)
options_for_blank_options_tag = { value: "" }
if include_blank == true
diff --git a/actionview/lib/action_view/helpers/number_helper.rb b/actionview/lib/action_view/helpers/number_helper.rb
index 35206b7e48..827e0bd02c 100644
--- a/actionview/lib/action_view/helpers/number_helper.rb
+++ b/actionview/lib/action_view/helpers/number_helper.rb
@@ -251,7 +251,7 @@ module ActionView
end
# Formats the bytes in +number+ into a more understandable
- # representation (e.g., giving it 1500 yields 1.5 KB). This
+ # representation (e.g., giving it 1500 yields 1.46 KB). This
# method is useful for reporting file sizes to users. You can
# customize the format in the +options+ hash.
#
@@ -403,7 +403,6 @@ module ActionView
end
private
-
def delegate_number_helper_method(method, number, options)
return unless number
options = escape_unsafe_options(options.symbolize_keys)
diff --git a/actionview/lib/action_view/helpers/rendering_helper.rb b/actionview/lib/action_view/helpers/rendering_helper.rb
index 7ead691113..4be23b68f3 100644
--- a/actionview/lib/action_view/helpers/rendering_helper.rb
+++ b/actionview/lib/action_view/helpers/rendering_helper.rb
@@ -35,7 +35,11 @@ module ActionView
end
end
else
- view_renderer.render_partial(self, partial: options, locals: locals, &block)
+ if options.respond_to?(:render_in)
+ options.render_in(self, &block)
+ else
+ view_renderer.render_partial(self, partial: options, locals: locals, &block)
+ end
end
end
diff --git a/actionview/lib/action_view/helpers/tags/base.rb b/actionview/lib/action_view/helpers/tags/base.rb
index b58e1a6680..4ce1f507f3 100644
--- a/actionview/lib/action_view/helpers/tags/base.rb
+++ b/actionview/lib/action_view/helpers/tags/base.rb
@@ -34,7 +34,6 @@ module ActionView
end
private
-
def value
if @allow_method_names_outside_object
object.public_send @method_name if object && object.respond_to?(@method_name)
@@ -106,7 +105,7 @@ module ActionView
end
def tag_name(multiple = false, index = nil)
- # a little duplication to construct less strings
+ # a little duplication to construct fewer strings
case
when @object_name.empty?
"#{sanitized_method_name}#{multiple ? "[]" : ""}"
@@ -118,7 +117,7 @@ module ActionView
end
def tag_id(index = nil)
- # a little duplication to construct less strings
+ # a little duplication to construct fewer strings
case
when @object_name.empty?
sanitized_method_name.dup
diff --git a/actionview/lib/action_view/helpers/tags/check_box.rb b/actionview/lib/action_view/helpers/tags/check_box.rb
index 4327e07cae..dbf020e514 100644
--- a/actionview/lib/action_view/helpers/tags/check_box.rb
+++ b/actionview/lib/action_view/helpers/tags/check_box.rb
@@ -39,7 +39,6 @@ module ActionView
end
private
-
def checked?(value)
case value
when TrueClass, FalseClass
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 455442178e..d058eb2c72 100644
--- a/actionview/lib/action_view/helpers/tags/collection_check_boxes.rb
+++ b/actionview/lib/action_view/helpers/tags/collection_check_boxes.rb
@@ -22,7 +22,6 @@ module ActionView
end
private
-
def render_component(builder)
builder.check_box + builder.label
end
diff --git a/actionview/lib/action_view/helpers/tags/collection_helpers.rb b/actionview/lib/action_view/helpers/tags/collection_helpers.rb
index e1ad11bff8..62060547de 100644
--- a/actionview/lib/action_view/helpers/tags/collection_helpers.rb
+++ b/actionview/lib/action_view/helpers/tags/collection_helpers.rb
@@ -37,7 +37,6 @@ module ActionView
end
private
-
def instantiate_builder(builder_class, item, value, text, html_options)
builder_class.new(@template_object, @object_name, @method_name, item,
sanitize_attribute_name(value), text, value, html_options)
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 16d37134e5..0c3b899f53 100644
--- a/actionview/lib/action_view/helpers/tags/collection_radio_buttons.rb
+++ b/actionview/lib/action_view/helpers/tags/collection_radio_buttons.rb
@@ -21,7 +21,6 @@ module ActionView
end
private
-
def render_component(builder)
builder.radio_button + builder.label
end
diff --git a/actionview/lib/action_view/helpers/tags/color_field.rb b/actionview/lib/action_view/helpers/tags/color_field.rb
index 39ab1285c3..69b190809f 100644
--- a/actionview/lib/action_view/helpers/tags/color_field.rb
+++ b/actionview/lib/action_view/helpers/tags/color_field.rb
@@ -12,7 +12,6 @@ module ActionView
end
private
-
def validate_color_string(string)
regex = /#[0-9a-fA-F]{6}/
if regex.match?(string)
diff --git a/actionview/lib/action_view/helpers/tags/date_field.rb b/actionview/lib/action_view/helpers/tags/date_field.rb
index b17a907651..ceaabfa99c 100644
--- a/actionview/lib/action_view/helpers/tags/date_field.rb
+++ b/actionview/lib/action_view/helpers/tags/date_field.rb
@@ -5,7 +5,6 @@ module ActionView
module Tags # :nodoc:
class DateField < DatetimeField # :nodoc:
private
-
def format_date(value)
value.try(:strftime, "%Y-%m-%d")
end
diff --git a/actionview/lib/action_view/helpers/tags/date_select.rb b/actionview/lib/action_view/helpers/tags/date_select.rb
index fe4e3914d7..f222bf2075 100644
--- a/actionview/lib/action_view/helpers/tags/date_select.rb
+++ b/actionview/lib/action_view/helpers/tags/date_select.rb
@@ -23,7 +23,6 @@ module ActionView
end
private
-
def select_type
self.class.select_type
end
diff --git a/actionview/lib/action_view/helpers/tags/datetime_field.rb b/actionview/lib/action_view/helpers/tags/datetime_field.rb
index 5d9b639b1b..e2dbf408c8 100644
--- a/actionview/lib/action_view/helpers/tags/datetime_field.rb
+++ b/actionview/lib/action_view/helpers/tags/datetime_field.rb
@@ -14,7 +14,6 @@ module ActionView
end
private
-
def format_date(value)
raise NotImplementedError
end
diff --git a/actionview/lib/action_view/helpers/tags/datetime_local_field.rb b/actionview/lib/action_view/helpers/tags/datetime_local_field.rb
index d8f8fd00d1..8908bf9948 100644
--- a/actionview/lib/action_view/helpers/tags/datetime_local_field.rb
+++ b/actionview/lib/action_view/helpers/tags/datetime_local_field.rb
@@ -11,7 +11,6 @@ module ActionView
end
private
-
def format_date(value)
value.try(:strftime, "%Y-%m-%dT%T")
end
diff --git a/actionview/lib/action_view/helpers/tags/label.rb b/actionview/lib/action_view/helpers/tags/label.rb
index 02bd099784..436c4cbda3 100644
--- a/actionview/lib/action_view/helpers/tags/label.rb
+++ b/actionview/lib/action_view/helpers/tags/label.rb
@@ -71,7 +71,6 @@ module ActionView
end
private
-
def render_component(builder)
builder.translation
end
diff --git a/actionview/lib/action_view/helpers/tags/month_field.rb b/actionview/lib/action_view/helpers/tags/month_field.rb
index 93b2bf11f0..463866a181 100644
--- a/actionview/lib/action_view/helpers/tags/month_field.rb
+++ b/actionview/lib/action_view/helpers/tags/month_field.rb
@@ -5,7 +5,6 @@ module ActionView
module Tags # :nodoc:
class MonthField < DatetimeField # :nodoc:
private
-
def format_date(value)
value.try(:strftime, "%Y-%m")
end
diff --git a/actionview/lib/action_view/helpers/tags/radio_button.rb b/actionview/lib/action_view/helpers/tags/radio_button.rb
index 621db2b1b5..4ce6c9f6bc 100644
--- a/actionview/lib/action_view/helpers/tags/radio_button.rb
+++ b/actionview/lib/action_view/helpers/tags/radio_button.rb
@@ -23,7 +23,6 @@ module ActionView
end
private
-
def checked?(value)
value.to_s == @tag_value.to_s
end
diff --git a/actionview/lib/action_view/helpers/tags/select.rb b/actionview/lib/action_view/helpers/tags/select.rb
index 790721a0b7..172e3a9226 100644
--- a/actionview/lib/action_view/helpers/tags/select.rb
+++ b/actionview/lib/action_view/helpers/tags/select.rb
@@ -29,7 +29,6 @@ module ActionView
end
private
-
# Grouped choices look like this:
#
# [nil, []]
diff --git a/actionview/lib/action_view/helpers/tags/text_field.rb b/actionview/lib/action_view/helpers/tags/text_field.rb
index d92967e212..c579e9e79f 100644
--- a/actionview/lib/action_view/helpers/tags/text_field.rb
+++ b/actionview/lib/action_view/helpers/tags/text_field.rb
@@ -24,7 +24,6 @@ module ActionView
end
private
-
def field_type
self.class.field_type
end
diff --git a/actionview/lib/action_view/helpers/tags/time_field.rb b/actionview/lib/action_view/helpers/tags/time_field.rb
index 9384a83a3e..e74c578db9 100644
--- a/actionview/lib/action_view/helpers/tags/time_field.rb
+++ b/actionview/lib/action_view/helpers/tags/time_field.rb
@@ -5,7 +5,6 @@ module ActionView
module Tags # :nodoc:
class TimeField < DatetimeField # :nodoc:
private
-
def format_date(value)
value.try(:strftime, "%T.%L")
end
diff --git a/actionview/lib/action_view/helpers/tags/week_field.rb b/actionview/lib/action_view/helpers/tags/week_field.rb
index 572535d1d6..5a403ed91d 100644
--- a/actionview/lib/action_view/helpers/tags/week_field.rb
+++ b/actionview/lib/action_view/helpers/tags/week_field.rb
@@ -5,7 +5,6 @@ module ActionView
module Tags # :nodoc:
class WeekField < DatetimeField # :nodoc:
private
-
def format_date(value)
value.try(:strftime, "%Y-W%V")
end
diff --git a/actionview/lib/action_view/helpers/text_helper.rb b/actionview/lib/action_view/helpers/text_helper.rb
index c282505e13..8203a43239 100644
--- a/actionview/lib/action_view/helpers/text_helper.rb
+++ b/actionview/lib/action_view/helpers/text_helper.rb
@@ -426,7 +426,6 @@ module ActionView
end
private
-
def next_index
step_index(1)
end
diff --git a/actionview/lib/action_view/helpers/translation_helper.rb b/actionview/lib/action_view/helpers/translation_helper.rb
index d5b0a9263f..baa337c62f 100644
--- a/actionview/lib/action_view/helpers/translation_helper.rb
+++ b/actionview/lib/action_view/helpers/translation_helper.rb
@@ -60,7 +60,7 @@ module ActionView
def translate(key, options = {})
options = options.dup
if options.has_key?(:default)
- remaining_defaults = Array(options.delete(:default)).compact
+ remaining_defaults = Array.wrap(options.delete(:default)).compact
options[:default] = remaining_defaults unless remaining_defaults.first.kind_of?(Symbol)
end
diff --git a/actionview/lib/action_view/helpers/url_helper.rb b/actionview/lib/action_view/helpers/url_helper.rb
index df83dff681..85fd549177 100644
--- a/actionview/lib/action_view/helpers/url_helper.rb
+++ b/actionview/lib/action_view/helpers/url_helper.rb
@@ -60,8 +60,8 @@ module ActionView
# Creates an anchor element of the given +name+ using a URL created by the set of +options+.
# See the valid options in the documentation for +url_for+. It's also possible to
- # pass a String instead of an options hash, which generates an anchor element that uses the
- # value of the String as the href for the link. Using a <tt>:back</tt> Symbol instead
+ # pass a \String instead of an options hash, which generates an anchor element that uses the
+ # value of the \String as the href for the link. Using a <tt>:back</tt> \Symbol instead
# of an options hash will generate a link to the referrer (a JavaScript back link
# will be used in place of a referrer if none exists). If +nil+ is passed as the name
# the value of the link itself will become the name.
@@ -226,7 +226,7 @@ module ActionView
# The +options+ hash accepts the same options as +url_for+.
#
# There are a few special +html_options+:
- # * <tt>:method</tt> - Symbol of HTTP verb. Supported verbs are <tt>:post</tt>, <tt>:get</tt>,
+ # * <tt>:method</tt> - \Symbol of HTTP verb. Supported verbs are <tt>:post</tt>, <tt>:get</tt>,
# <tt>:delete</tt>, <tt>:patch</tt>, and <tt>:put</tt>. By default it will be <tt>:post</tt>.
# * <tt>:disabled</tt> - If set to true, it will generate a disabled button.
# * <tt>:data</tt> - This option can be used to add custom data attributes.
@@ -235,7 +235,7 @@ module ActionView
# * <tt>:form</tt> - This hash will be form attributes
# * <tt>:form_class</tt> - This controls the class of the form within which the submit button will
# be placed
- # * <tt>:params</tt> - Hash of parameters to be rendered as hidden fields within the form.
+ # * <tt>:params</tt> - \Hash of parameters to be rendered as hidden fields within the form.
#
# ==== Data attributes
#
@@ -253,7 +253,7 @@ module ActionView
# # <input value="New" type="submit" />
# # </form>"
#
- # <%= button_to "New", new_articles_path %>
+ # <%= button_to "New", new_article_path %>
# # => "<form method="post" action="/articles/new" class="button_to">
# # <input value="New" type="submit" />
# # </form>"
@@ -571,6 +571,54 @@ module ActionView
end
end
+ # Creates an SMS anchor link tag to the specified +phone_number+, which is
+ # also used as the name of the link unless +name+ is specified. Additional
+ # HTML attributes for the link can be passed in +html_options+.
+ #
+ # When clicked, an SMS message is prepopulated with the passed phone number
+ # and optional +body+ value.
+ #
+ # +sms_to+ has a +body+ option for customizing the SMS message itself by
+ # passing special keys to +html_options+.
+ #
+ # ==== Options
+ # * <tt>:body</tt> - Preset the body of the message.
+ #
+ # ==== Examples
+ # sms_to "5155555785"
+ # # => <a href="sms:5155555785;">5155555785</a>
+ #
+ # sms_to "5155555785", "Text me"
+ # # => <a href="sms:5155555785;">Text me</a>
+ #
+ # sms_to "5155555785", "Text me",
+ # body: "Hello Jim I have a question about your product."
+ # # => <a href="sms:5155555785;?body=Hello%20Jim%20I%20have%20a%20question%20about%20your%20product">Text me</a>
+ #
+ # You can use a block as well if your link target is hard to fit into the name parameter. \ERB example:
+ #
+ # <%= sms_to "5155555785" do %>
+ # <strong>Text me:</strong>
+ # <% end %>
+ # # => <a href="sms:5155555785;">
+ # <strong>Text me:</strong>
+ # </a>
+ def sms_to(phone_number, name = nil, html_options = {}, &block)
+ html_options, name = name, nil if block_given?
+ html_options = (html_options || {}).stringify_keys
+
+ extras = %w{ body }.map! { |item|
+ option = html_options.delete(item).presence || next
+ "#{item.dasherize}=#{ERB::Util.url_encode(option)}"
+ }.compact
+ extras = extras.empty? ? "" : "?&" + extras.join("&")
+
+ encoded_phone_number = ERB::Util.url_encode(phone_number)
+ html_options["href"] = "sms:#{encoded_phone_number};#{extras}"
+
+ content_tag("a", name || phone_number, html_options, &block)
+ end
+
private
def convert_options_to_data_attributes(options, html_options)
if html_options
diff --git a/actionview/lib/action_view/layouts.rb b/actionview/lib/action_view/layouts.rb
index 08f66bf435..be21ff0e5d 100644
--- a/actionview/lib/action_view/layouts.rb
+++ b/actionview/lib/action_view/layouts.rb
@@ -224,7 +224,6 @@ module ActionView
# that if no layout conditions are used, this method is not used
module LayoutConditions # :nodoc:
private
-
# Determines whether the current action has a layout definition by
# checking the action name against the :only and :except conditions
# set by the <tt>layout</tt> method.
@@ -334,7 +333,6 @@ module ActionView
end
private
-
# If no layout is supplied, look for a template named the return
# value of this method.
#
@@ -372,7 +370,6 @@ module ActionView
end
private
-
def _conditional_layout?
true
end
diff --git a/actionview/lib/action_view/log_subscriber.rb b/actionview/lib/action_view/log_subscriber.rb
index 227f025385..02ac0ce477 100644
--- a/actionview/lib/action_view/log_subscriber.rb
+++ b/actionview/lib/action_view/log_subscriber.rb
@@ -54,7 +54,6 @@ module ActionView
end
private
-
EMPTY = ""
def from_rails_root(string) # :doc:
string = string.sub(rails_root, EMPTY)
diff --git a/actionview/lib/action_view/lookup_context.rb b/actionview/lib/action_view/lookup_context.rb
index b9a8cc129c..211fbc8e6c 100644
--- a/actionview/lib/action_view/lookup_context.rb
+++ b/actionview/lib/action_view/lookup_context.rb
@@ -3,7 +3,6 @@
require "concurrent/map"
require "active_support/core_ext/module/remove_method"
require "active_support/core_ext/module/attribute_accessors"
-require "active_support/deprecation"
require "action_view/template/resolver"
module ActionView
@@ -112,7 +111,6 @@ module ActionView
end
private
-
def _set_detail(key, value) # :doc:
@details = @details.dup if @digest_cache || @details_key
@digest_cache = nil
@@ -171,7 +169,6 @@ module ActionView
end
private
-
# Whenever setting view paths, makes a copy so that we can manipulate them in
# instance objects as we wish.
def build_view_paths(paths)
diff --git a/actionview/lib/action_view/path_set.rb b/actionview/lib/action_view/path_set.rb
index d54749d8e3..a6401323db 100644
--- a/actionview/lib/action_view/path_set.rb
+++ b/actionview/lib/action_view/path_set.rb
@@ -69,7 +69,6 @@ module ActionView #:nodoc:
end
private
-
def _find_all(path, prefixes, args)
prefixes = [prefixes] if String === prefixes
prefixes.each do |prefix|
diff --git a/actionview/lib/action_view/record_identifier.rb b/actionview/lib/action_view/record_identifier.rb
index ee39b6050d..7491dbe15a 100644
--- a/actionview/lib/action_view/record_identifier.rb
+++ b/actionview/lib/action_view/record_identifier.rb
@@ -95,7 +95,6 @@ module ActionView
end
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.
# If you need to read back a key from a dom_id in order to query for the underlying database record,
diff --git a/actionview/lib/action_view/renderer/abstract_renderer.rb b/actionview/lib/action_view/renderer/abstract_renderer.rb
index 475452f1bb..a52e5d3aca 100644
--- a/actionview/lib/action_view/renderer/abstract_renderer.rb
+++ b/actionview/lib/action_view/renderer/abstract_renderer.rb
@@ -59,11 +59,10 @@ module ActionView
end
class RenderedTemplate # :nodoc:
- attr_reader :body, :layout, :template
+ attr_reader :body, :template
- def initialize(body, layout, template)
+ def initialize(body, template)
@body = body
- @layout = layout
@template = template
end
@@ -75,7 +74,6 @@ module ActionView
end
private
-
def extract_details(options) # :doc:
@lookup_context.registered_details.each_with_object({}) do |key, details|
value = options[key]
@@ -97,8 +95,8 @@ module ActionView
@lookup_context.formats = formats | @lookup_context.formats
end
- def build_rendered_template(content, template, layout = nil)
- RenderedTemplate.new content, layout, template
+ def build_rendered_template(content, template)
+ RenderedTemplate.new content, template
end
def build_rendered_collection(templates, spacer)
diff --git a/actionview/lib/action_view/renderer/partial_renderer.rb b/actionview/lib/action_view/renderer/partial_renderer.rb
index ed8d5cf54e..46bc4c9d5f 100644
--- a/actionview/lib/action_view/renderer/partial_renderer.rb
+++ b/actionview/lib/action_view/renderer/partial_renderer.rb
@@ -305,7 +305,7 @@ module ActionView
else
@template_keys = @locals.keys
end
- template = find_partial(@path, @template_keys)
+ template = find_template(@path, @template_keys)
@variable ||= template.variable
else
if options[:cached]
@@ -322,7 +322,6 @@ module ActionView
end
private
-
def render_collection(view, template)
identifier = (template && template.identifier) || @path
instrument(:collection, identifier: identifier, count: @collection.size) do |payload|
@@ -364,7 +363,7 @@ module ActionView
content = layout.render(view, locals) { content } if layout
payload[:cache_hit] = view.view_renderer.cache_hits[template.virtual_path]
- build_rendered_template(content, template, layout)
+ build_rendered_template(content, template)
end
end
@@ -428,10 +427,6 @@ module ActionView
@object.to_ary if @object.respond_to?(:to_ary)
end
- def find_partial(path, template_keys)
- find_template(path, template_keys)
- end
-
def find_template(path, locals)
prefixes = path.include?(?/) ? [] : @lookup_context.prefixes
@lookup_context.find_template(path, prefixes, true, locals, @details)
@@ -455,7 +450,7 @@ module ActionView
content = template.render(view, locals)
content = layout.render(view, locals) { content } if layout
partial_iteration.iterate!
- build_rendered_template(content, template, layout)
+ build_rendered_template(content, template)
end
end
diff --git a/actionview/lib/action_view/renderer/streaming_template_renderer.rb b/actionview/lib/action_view/renderer/streaming_template_renderer.rb
index 19fa399e2c..08a280c7ee 100644
--- a/actionview/lib/action_view/renderer/streaming_template_renderer.rb
+++ b/actionview/lib/action_view/renderer/streaming_template_renderer.rb
@@ -27,7 +27,6 @@ module ActionView
end
private
-
# This is the same logging logic as in ShowExceptions middleware.
def log_error(exception)
logger = ActionView::Base.logger
@@ -55,7 +54,6 @@ module ActionView
end
private
-
def delayed_render(buffer, template, layout, view, locals)
# Wrap the given buffer in the StreamingBuffer and pass it to the
# underlying template handler. Now, every time something is concatenated
diff --git a/actionview/lib/action_view/renderer/template_renderer.rb b/actionview/lib/action_view/renderer/template_renderer.rb
index 4aab3f913f..b2d7332572 100644
--- a/actionview/lib/action_view/renderer/template_renderer.rb
+++ b/actionview/lib/action_view/renderer/template_renderer.rb
@@ -14,7 +14,6 @@ module ActionView
end
private
-
# Determine the template to be rendered using the given options.
def determine_template(options)
keys = options.has_key?(:locals) ? options[:locals].keys : []
@@ -71,7 +70,7 @@ module ActionView
else
content
end
- build_rendered_template(body, template, layout)
+ build_rendered_template(body, template)
end
# This is the method which actually finds the layout using details in the lookup
diff --git a/actionview/lib/action_view/rendering.rb b/actionview/lib/action_view/rendering.rb
index 5a06bd9da6..7247598ac2 100644
--- a/actionview/lib/action_view/rendering.rb
+++ b/actionview/lib/action_view/rendering.rb
@@ -104,7 +104,6 @@ module ActionView
end
private
-
# Find and render a template based on the options given.
def _render_template(options)
variant = options.delete(:variant)
diff --git a/actionview/lib/action_view/template.rb b/actionview/lib/action_view/template.rb
index b80bf56c1b..3877108fef 100644
--- a/actionview/lib/action_view/template.rb
+++ b/actionview/lib/action_view/template.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
-require "active_support/core_ext/object/try"
require "active_support/core_ext/kernel/singleton_class"
-require "active_support/deprecation"
require "thread"
require "delegate"
@@ -264,7 +262,6 @@ module ActionView
end
private
-
# Compile a template. This method ensures a template is compiled
# just once and removes the source after it is compiled.
def compile!(view)
diff --git a/actionview/lib/action_view/template/error.rb b/actionview/lib/action_view/template/error.rb
index d0ea03e228..7fc74a5502 100644
--- a/actionview/lib/action_view/template/error.rb
+++ b/actionview/lib/action_view/template/error.rb
@@ -81,8 +81,8 @@ module ActionView
end
end
- def source_extract(indentation = 0, output = :console)
- return unless num = line_number
+ def source_extract(indentation = 0)
+ return [] unless num = line_number
num = num.to_i
source_code = @template.source.split("\n")
@@ -91,9 +91,9 @@ module ActionView
end_on_line = [ num + SOURCE_CODE_RADIUS - 1, source_code.length].min
indent = end_on_line.to_s.size + indentation
- return unless source_code = source_code[start_on_line..end_on_line]
+ return [] unless source_code = source_code[start_on_line..end_on_line]
- formatted_code_for(source_code, start_on_line, indent, output)
+ formatted_code_for(source_code, start_on_line, indent)
end
def sub_template_of(template_path)
@@ -114,7 +114,6 @@ module ActionView
end
private
-
def source_location
if line_number
"on line ##{line_number} of "
@@ -123,15 +122,11 @@ module ActionView
end + file_name
end
- def formatted_code_for(source_code, line_counter, indent, output)
- start_value = (output == :html) ? {} : []
- source_code.inject(start_value) do |result, line|
+ def formatted_code_for(source_code, line_counter, indent)
+ indent_template = "%#{indent}s: %s"
+ source_code.map do |line|
line_counter += 1
- if output == :html
- result.update(line_counter.to_s => "%#{indent}s %s\n" % ["", line])
- else
- result << "%#{indent}s: %s" % [line_counter, line]
- end
+ indent_template % [line_counter, line]
end
end
end
diff --git a/actionview/lib/action_view/template/handlers.rb b/actionview/lib/action_view/template/handlers.rb
index 6450513003..c7ef456125 100644
--- a/actionview/lib/action_view/template/handlers.rb
+++ b/actionview/lib/action_view/template/handlers.rb
@@ -1,7 +1,5 @@
# frozen_string_literal: true
-require "active_support/deprecation"
-
module ActionView #:nodoc:
# = Action View Template Handlers
class Template #:nodoc:
diff --git a/actionview/lib/action_view/template/handlers/erb.rb b/actionview/lib/action_view/template/handlers/erb.rb
index b6314a5bc3..69a5a0b1b9 100644
--- a/actionview/lib/action_view/template/handlers/erb.rb
+++ b/actionview/lib/action_view/template/handlers/erb.rb
@@ -63,7 +63,6 @@ module ActionView
end
private
-
def valid_encoding(string, encoding)
# If a magic encoding comment was found, tag the
# String with this encoding. This is for a case
diff --git a/actionview/lib/action_view/template/html.rb b/actionview/lib/action_view/template/html.rb
index ecd1c31e79..563bffd333 100644
--- a/actionview/lib/action_view/template/html.rb
+++ b/actionview/lib/action_view/template/html.rb
@@ -1,7 +1,5 @@
# frozen_string_literal: true
-require "active_support/deprecation"
-
module ActionView #:nodoc:
# = Action View HTML Template
class Template #:nodoc:
diff --git a/actionview/lib/action_view/template/resolver.rb b/actionview/lib/action_view/template/resolver.rb
index ce53eb046d..bfac4c08a0 100644
--- a/actionview/lib/action_view/template/resolver.rb
+++ b/actionview/lib/action_view/template/resolver.rb
@@ -93,7 +93,6 @@ module ActionView
end
private
-
def canonical_no_templates(templates)
templates.empty? ? NO_TEMPLATES : templates
end
@@ -130,7 +129,6 @@ module ActionView
end
private
-
def _find_all(name, prefix, partial, details, key, locals)
find_templates(name, prefix, partial, details, locals)
end
@@ -183,7 +181,6 @@ module ActionView
end
private
-
def _find_all(name, prefix, partial, details, key, locals)
path = Path.build(name, prefix, partial)
query(path, details, details[:formats], locals, cache: !!key)
@@ -282,12 +279,8 @@ module ActionView
format, variant = pieces.last.split(EXTENSIONS[:variants], 2) if pieces.last
format = if format
Template::Types[format]&.ref
- else
- if handler.respond_to?(:default_format) # default_format can return nil
- handler.default_format
- else
- nil
- end
+ elsif handler.respond_to?(:default_format) # default_format can return nil
+ handler.default_format
end
# Template::Types[format] and handler.default_format can return nil
@@ -323,7 +316,6 @@ module ActionView
end
private
-
def find_template_paths_from_details(path, details)
# Instead of checking for every possible path, as our other globs would
# do, scan the directory for files with the right prefix.
diff --git a/actionview/lib/action_view/test_case.rb b/actionview/lib/action_view/test_case.rb
index e14f7aaec7..b08f8b2dfb 100644
--- a/actionview/lib/action_view/test_case.rb
+++ b/actionview/lib/action_view/test_case.rb
@@ -93,7 +93,6 @@ module ActionView
end
private
-
def include_helper_modules!
helper(helper_class) if helper_class
include _helpers
@@ -163,7 +162,6 @@ module ActionView
end
private
-
# Need to experiment if this priority is the best one: rendered => output_buffer
def document_root_element
Nokogiri::HTML::Document.parse(@rendered.blank? ? @output_buffer : @rendered).root
@@ -284,7 +282,7 @@ module ActionView
def respond_to_missing?(name, include_private = false)
begin
- routes = @controller.respond_to?(:_routes) && @controller._routes
+ routes = defined?(@controller) && @controller.respond_to?(:_routes) && @controller._routes
rescue
# Don't call routes, if there is an error on _routes call
end
diff --git a/actionview/lib/action_view/testing/resolvers.rb b/actionview/lib/action_view/testing/resolvers.rb
index 1bedf44934..03eac29bb4 100644
--- a/actionview/lib/action_view/testing/resolvers.rb
+++ b/actionview/lib/action_view/testing/resolvers.rb
@@ -7,10 +7,15 @@ module ActionView #:nodoc:
# file system. This is used internally by Rails' own test suite, and is
# useful for testing extensions that have no way of knowing what the file
# system will look like at runtime.
- class FixtureResolver < PathResolver
+ class FixtureResolver < OptimizedFileSystemResolver
def initialize(hash = {}, pattern = nil)
- super(pattern)
+ super("")
+ if pattern
+ ActiveSupport::Deprecation.warn "Specifying a custom path for #{self.class} is deprecated. Implement a custom Resolver subclass instead."
+ @pattern = pattern
+ end
@hash = hash
+ @path = ""
end
def data
@@ -22,27 +27,33 @@ module ActionView #:nodoc:
end
private
-
def query(path, exts, _, locals, cache:)
- query = +""
- EXTENSIONS.each do |ext, prefix|
- query << "(" << exts[ext].map { |e| e && Regexp.escape("#{prefix}#{e}") }.join("|") << "|)"
- end
- query = /^(#{Regexp.escape(path)})#{query}$/
+ regex = build_regex(path, exts)
- templates = []
- @hash.each do |_path, source|
- next unless query.match?(_path)
+ @hash.select do |_path, _|
+ ("/" + _path).match?(regex)
+ end.map do |_path, source|
handler, format, variant = extract_handler_and_format_and_variant(_path)
- templates << Template.new(source, _path, handler,
+
+ Template.new(source, _path, handler,
virtual_path: path.virtual,
format: format,
variant: variant,
locals: locals
)
+ end.sort_by do |t|
+ match = ("/" + t.identifier).match(regex)
+ EXTENSIONS.keys.reverse.map do |ext|
+ if ext == :variants && exts[ext] == :any
+ match[ext].nil? ? 0 : 1
+ elsif match[ext].nil?
+ exts[ext].length
+ else
+ found = match[ext].to_sym
+ exts[ext].index(found)
+ end
+ end
end
-
- templates.sort_by { |t| -t.identifier.match(/^#{query}$/).captures.reject(&:blank?).size }
end
end
diff --git a/actionview/lib/action_view/unbound_template.rb b/actionview/lib/action_view/unbound_template.rb
index db69b6d016..3d4434b2e9 100644
--- a/actionview/lib/action_view/unbound_template.rb
+++ b/actionview/lib/action_view/unbound_template.rb
@@ -4,9 +4,9 @@ require "concurrent/map"
module ActionView
class UnboundTemplate
- def initialize(source, identifer, handler, options)
+ def initialize(source, identifier, handler, options)
@source = source
- @identifer = identifer
+ @identifier = identifier
@handler = handler
@options = options
@@ -18,12 +18,11 @@ module ActionView
end
private
-
def build_template(locals)
options = @options.merge(locals: locals)
Template.new(
@source,
- @identifer,
+ @identifier,
@handler,
options
)
diff --git a/actionview/lib/action_view/view_paths.rb b/actionview/lib/action_view/view_paths.rb
index 3ca5aedc14..7e05f15648 100644
--- a/actionview/lib/action_view/view_paths.rb
+++ b/actionview/lib/action_view/view_paths.rb
@@ -29,7 +29,6 @@ module ActionView
end
private
-
# Override this method in your controller if you want to change paths prefixes for finding views.
# Prefixes defined here will still be added to parents' <tt>._prefixes</tt>.
def local_prefixes
diff --git a/actionview/package.json b/actionview/package.json
index e5c8b9bb7d..74cdcfec43 100644
--- a/actionview/package.json
+++ b/actionview/package.json
@@ -1,6 +1,6 @@
{
"name": "@rails/ujs",
- "version": "6.0.0-beta3",
+ "version": "6.1.0-alpha",
"description": "Ruby on Rails unobtrusive scripting adapter",
"main": "lib/assets/compiled/rails-ujs.js",
"files": [
diff --git a/actionview/test/abstract_unit.rb b/actionview/test/abstract_unit.rb
index 2c1b277b41..b652f2e6cb 100644
--- a/actionview/test/abstract_unit.rb
+++ b/actionview/test/abstract_unit.rb
@@ -192,6 +192,8 @@ module ActionDispatch
end
class ActiveSupport::TestCase
+ parallelize
+
include ActiveSupport::Testing::MethodCallAssertions
private
diff --git a/actionview/test/actionpack/abstract/abstract_controller_test.rb b/actionview/test/actionpack/abstract/abstract_controller_test.rb
index eecc19d413..2de81596b5 100644
--- a/actionview/test/actionpack/abstract/abstract_controller_test.rb
+++ b/actionview/test/actionpack/abstract/abstract_controller_test.rb
@@ -230,6 +230,7 @@ module AbstractController
class ActionMissingRespondToActionController < AbstractController::Base
# No actions
+
private
def action_missing(action_name)
self.response_body = "success"
@@ -242,7 +243,6 @@ module AbstractController
def fail() self.response_body = "fail" end
private
-
def method_for_action(action_name)
action_name.to_s != "fail" && action_name
end
diff --git a/actionview/test/actionpack/abstract/layouts_test.rb b/actionview/test/actionpack/abstract/layouts_test.rb
index 1146e6f64b..15643c161d 100644
--- a/actionview/test/actionpack/abstract/layouts_test.rb
+++ b/actionview/test/actionpack/abstract/layouts_test.rb
@@ -140,6 +140,7 @@ module AbstractControllerTests
def index
render template: ActionView::Template::Text.new("Hello symbol!")
end
+
private
def hello
"overwrite"
@@ -295,7 +296,7 @@ module AbstractControllerTests
10.times do |x|
controller = WithString.new
controller.define_singleton_method :index do
- render template: ActionView::Template::Text.new("Hello string!"), locals: { :"x#{x}" => :omg }
+ render template: ActionView::Template::Text.new("Hello string!"), locals: { "x#{x}": :omg }
end
controller.process(:index)
end
diff --git a/actionview/test/actionpack/controller/render_test.rb b/actionview/test/actionpack/controller/render_test.rb
index c8ce7366d1..64070a9542 100644
--- a/actionview/test/actionpack/controller/render_test.rb
+++ b/actionview/test/actionpack/controller/render_test.rb
@@ -607,7 +607,6 @@ class TestController < ActionController::Base
end
private
-
def set_variable_for_layout
@variable_for_layout = nil
end
@@ -1003,14 +1002,14 @@ class RenderTest < ActionController::TestCase
def test_render_xml
get :render_xml_hello
assert_equal "<html>\n <p>Hello David</p>\n<p>This is grand!</p>\n</html>\n", @response.body
- assert_equal "application/xml", @response.content_type
+ assert_equal "application/xml", @response.media_type
end
# :ported:
def test_render_xml_as_string_template
get :render_xml_hello_as_string_template
assert_equal "<html>\n <p>Hello David</p>\n<p>This is grand!</p>\n</html>\n", @response.body
- assert_equal "application/xml", @response.content_type
+ assert_equal "application/xml", @response.media_type
end
# :ported:
@@ -1039,7 +1038,7 @@ class RenderTest < ActionController::TestCase
def test_rendered_format_without_format
get :inline_rendered_format_without_format
assert_equal "test", @response.body
- assert_equal "text/html", @response.content_type
+ assert_equal "text/html", @response.media_type
end
def test_partials_list
@@ -1077,7 +1076,7 @@ class RenderTest < ActionController::TestCase
def test_accessing_local_assigns_in_inline_template
get :accessing_local_assigns_in_inline_template, params: { local_name: "Local David" }
assert_equal "Goodbye, Local David", @response.body
- assert_equal "text/html", @response.content_type
+ assert_equal "text/html", @response.media_type
end
def test_should_implicitly_render_html_template_from_xhr_request
@@ -1264,13 +1263,13 @@ class RenderTest < ActionController::TestCase
def test_partial_only
get :partial_only
assert_equal "only partial", @response.body
- assert_equal "text/html", @response.content_type
+ assert_equal "text/html", @response.media_type
end
def test_should_render_html_formatted_partial
get :partial
assert_equal "partial html", @response.body
- assert_equal "text/html", @response.content_type
+ assert_equal "text/html", @response.media_type
end
def test_render_html_formatted_partial_even_with_other_mime_time_in_accept
@@ -1279,20 +1278,20 @@ class RenderTest < ActionController::TestCase
get :partial_html_erb
assert_equal "partial.html.erb", @response.body.strip
- assert_equal "text/html", @response.content_type
+ assert_equal "text/html", @response.media_type
end
def test_should_render_html_partial_with_formats
get :partial_formats_html
assert_equal "partial html", @response.body
- assert_equal "text/html", @response.content_type
+ assert_equal "text/html", @response.media_type
end
def test_render_to_string_partial
get :render_to_string_with_partial
assert_equal "only partial", @controller.instance_variable_get(:@partial_only)
assert_equal "Hello: david", @controller.instance_variable_get(:@partial_with_locals)
- assert_equal "text/html", @response.content_type
+ assert_equal "text/html", @response.media_type
end
def test_render_to_string_with_template_and_html_partial
@@ -1300,21 +1299,21 @@ class RenderTest < ActionController::TestCase
assert_equal "**only partial**\n", @controller.instance_variable_get(:@text)
assert_equal "<strong>only partial</strong>\n", @controller.instance_variable_get(:@html)
assert_equal "<strong>only html partial</strong>\n", @response.body
- assert_equal "text/html", @response.content_type
+ assert_equal "text/html", @response.media_type
end
def test_render_to_string_and_render_with_different_formats
get :render_to_string_and_render_with_different_formats
assert_equal "<strong>only partial</strong>\n", @controller.instance_variable_get(:@html)
assert_equal "**only partial**\n", @response.body
- assert_equal "text/plain", @response.content_type
+ assert_equal "text/plain", @response.media_type
end
def test_render_template_within_a_template_with_other_format
get :render_template_within_a_template_with_other_format
expected = "only html partial<p>This is grand!</p>"
assert_equal expected, @response.body.strip
- assert_equal "text/html", @response.content_type
+ assert_equal "text/html", @response.media_type
end
def test_partial_with_counter
diff --git a/actionview/test/active_record_unit.rb b/actionview/test/active_record_unit.rb
index e4ea6a426d..4efb31a360 100644
--- a/actionview/test/active_record_unit.rb
+++ b/actionview/test/active_record_unit.rb
@@ -42,6 +42,12 @@ class ActiveRecordTestConnector
self.able_to_connect = false
end
+ def reconnect
+ return unless able_to_connect
+ ActiveRecord::Base.connection.reconnect!
+ load_schema
+ end
+
private
def setup_connection
if Object.const_defined?(:ActiveRecord)
@@ -102,3 +108,7 @@ class ActiveRecordTestCase < ActionController::TestCase
end
ActiveRecordTestConnector.setup
+
+ActiveSupport::Testing::Parallelization.after_fork_hook do
+ ActiveRecordTestConnector.reconnect
+end
diff --git a/actionview/test/activerecord/form_helper_activerecord_test.rb b/actionview/test/activerecord/form_helper_activerecord_test.rb
index 34655bfe23..1b196ade6d 100644
--- a/actionview/test/activerecord/form_helper_activerecord_test.rb
+++ b/actionview/test/activerecord/form_helper_activerecord_test.rb
@@ -58,7 +58,6 @@ class FormHelperActiveRecordTest < ActionView::TestCase
end
private
-
def hidden_fields(method = nil)
txt = +%{<input name="utf8" type="hidden" value="&#x2713;" />}
diff --git a/actionview/test/lib/test_component.rb b/actionview/test/lib/test_component.rb
new file mode 100644
index 0000000000..0d45d6e25f
--- /dev/null
+++ b/actionview/test/lib/test_component.rb
@@ -0,0 +1,45 @@
+# frozen_string_literal: true
+
+class TestComponent < ActionView::Base
+ include ActiveModel::Validations
+
+ validates :content, :title, presence: true
+ delegate :render, to: :view_context
+
+ def initialize(title:)
+ @title = title
+ end
+
+ # Entrypoint for rendering. Called by ActionView::RenderingHelper#render.
+ #
+ # Returns ActionView::OutputBuffer.
+ def render_in(view_context, &block)
+ self.class.compile
+ @view_context = view_context
+ @content = view_context.capture(&block) if block_given?
+ validate!
+ rendered_template
+ end
+
+ def self.template
+ <<~'erb'
+ <span title="<%= title %>"><%= content %> (<%= render(plain: "Inline render") %>)</span>
+ erb
+ end
+
+ def self.compile
+ @compiled ||= nil
+ return if @compiled
+
+ class_eval(
+ "def rendered_template; @output_buffer = ActionView::OutputBuffer.new; " +
+ ActionView::Template::Handlers::ERB.erb_implementation.new(template, trim: true).src +
+ "; end"
+ )
+
+ @compiled = true
+ end
+
+private
+ attr_reader :content, :title, :view_context
+end
diff --git a/actionview/test/template/active_model_helper_test.rb b/actionview/test/template/active_model_helper_test.rb
index 36afed6dd6..c6a3c9064f 100644
--- a/actionview/test/template/active_model_helper_test.rb
+++ b/actionview/test/template/active_model_helper_test.rb
@@ -20,11 +20,11 @@ class ActiveModelHelperTest < ActionView::TestCase
super
@post = Post.new
- @post.errors[:author_name] << "can't be empty"
- @post.errors[:body] << "foo"
- @post.errors[:category] << "must exist"
- @post.errors[:published] << "must be accepted"
- @post.errors[:updated_at] << "bar"
+ assert_deprecated { @post.errors[:author_name] << "can't be empty" }
+ assert_deprecated { @post.errors[:body] << "foo" }
+ assert_deprecated { @post.errors[:category] << "must exist" }
+ assert_deprecated { @post.errors[:published] << "must be accepted" }
+ assert_deprecated { @post.errors[:updated_at] << "bar" }
@post.author_name = ""
@post.body = "Back to the hill and over it again!"
diff --git a/actionview/test/template/compiled_templates_test.rb b/actionview/test/template/compiled_templates_test.rb
index d7f2e8ee07..59fa058bed 100644
--- a/actionview/test/template/compiled_templates_test.rb
+++ b/actionview/test/template/compiled_templates_test.rb
@@ -60,46 +60,8 @@ class CompiledTemplatesTest < ActiveSupport::TestCase
assert_equal "two", render(template: "test/render_file_with_locals_and_default", locals: { secret: "two" })
end
- def test_template_changes_are_not_reflected_with_cached_templates
- assert_equal "Hello world!", render(template: "test/hello_world")
- modify_template "test/hello_world.erb", "Goodbye world!" do
- assert_equal "Hello world!", render(template: "test/hello_world")
- end
- assert_equal "Hello world!", render(template: "test/hello_world")
- end
-
- def test_template_changes_are_reflected_with_uncached_templates
- assert_equal "Hello world!", render_without_cache(template: "test/hello_world")
- modify_template "test/hello_world.erb", "Goodbye world!" do
- assert_equal "Goodbye world!", render_without_cache(template: "test/hello_world")
- end
- assert_equal "Hello world!", render_without_cache(template: "test/hello_world")
- end
-
private
def render(*args)
- render_with_cache(*args)
- end
-
- def render_with_cache(*args)
- view_paths = ActionController::Base.view_paths
- view_class.with_view_paths(view_paths, {}).render(*args)
- end
-
- def render_without_cache(*args)
- path = ActionView::FileSystemResolver.new(FIXTURE_LOAD_PATH)
- view_paths = ActionView::PathSet.new([path])
- view_class.with_view_paths(view_paths, {}).render(*args)
- end
-
- def modify_template(template, content)
- filename = "#{FIXTURE_LOAD_PATH}/#{template}"
- old_content = File.read(filename)
- begin
- File.open(filename, "wb+") { |f| f.write(content) }
- yield
- ensure
- File.open(filename, "wb+") { |f| f.write(old_content) }
- end
+ ActionController::Base.render(*args)
end
end
diff --git a/actionview/test/template/form_helper_test.rb b/actionview/test/template/form_helper_test.rb
index 91052e5ae2..83b089397c 100644
--- a/actionview/test/template/form_helper_test.rb
+++ b/actionview/test/template/form_helper_test.rb
@@ -1897,7 +1897,6 @@ class FormHelperTest < ActionView::TestCase
def test_form_tags_do_not_call_private_properties_on_form_object
obj = Class.new do
private
-
def private_property
raise "This method should not be called."
end
diff --git a/actionview/test/template/form_options_helper_test.rb b/actionview/test/template/form_options_helper_test.rb
index 4ccd3ae336..d7a7b95ab3 100644
--- a/actionview/test/template/form_options_helper_test.rb
+++ b/actionview/test/template/form_options_helper_test.rb
@@ -29,10 +29,20 @@ class FormOptionsHelperTest < ActionView::TestCase
end
Continent = Struct.new("Continent", :continent_name, :countries)
Country = Struct.new("Country", :country_id, :country_name)
- Firm = Struct.new("Firm", :time_zone)
Album = Struct.new("Album", :id, :title, :genre)
end
+ class Firm
+ include ActiveModel::Validations
+ extend ActiveModel::Naming
+
+ attr_accessor :time_zone
+
+ def initialize(time_zone = nil)
+ @time_zone = time_zone
+ end
+ end
+
module FakeZones
FakeZone = Struct.new(:name) do
def to_s; name; end
@@ -1294,7 +1304,7 @@ class FormOptionsHelperTest < ActionView::TestCase
def test_time_zone_select_with_priority_zones_and_errors
@firm = Firm.new("D")
@firm.extend ActiveModel::Validations
- @firm.errors[:time_zone] << "invalid"
+ assert_deprecated { @firm.errors[:time_zone] << "invalid" }
zones = [ ActiveSupport::TimeZone.new("A"), ActiveSupport::TimeZone.new("D") ]
html = time_zone_select("firm", "time_zone", zones)
assert_dom_equal "<div class=\"field_with_errors\">" \
@@ -1462,7 +1472,6 @@ class FormOptionsHelperTest < ActionView::TestCase
end
private
-
def dummy_posts
[ Post.new("<Abe> went home", "<Abe>", "To a little house", "shh!"),
Post.new("Babe went home", "Babe", "To a little house", "shh!"),
diff --git a/actionview/test/template/form_tag_helper_test.rb b/actionview/test/template/form_tag_helper_test.rb
index 9ece9f3ad1..80afed96e5 100644
--- a/actionview/test/template/form_tag_helper_test.rb
+++ b/actionview/test/template/form_tag_helper_test.rb
@@ -301,6 +301,13 @@ class FormTagHelperTest < ActionView::TestCase
assert_dom_equal expected, actual
end
+ def test_select_tag_with_include_blank_doesnt_change_options
+ options = { include_blank: true, prompt: "string" }
+ expected_options = options.dup
+ select_tag "places", raw("<option>Home</option><option>Work</option><option>Pub</option>"), options
+ expected_options.each { |k, v| assert_equal v, options[k] }
+ end
+
def test_select_tag_with_include_blank_false
actual = select_tag "places", raw("<option>Home</option><option>Work</option><option>Pub</option>"), include_blank: false
expected = %(<select id="places" name="places"><option>Home</option><option>Work</option><option>Pub</option></select>)
@@ -796,7 +803,6 @@ class FormTagHelperTest < ActionView::TestCase
end
private
-
def root_elem(rendered_content)
Nokogiri::HTML::DocumentFragment.parse(rendered_content).children.first # extract from nodeset
end
diff --git a/actionview/test/template/render_test.rb b/actionview/test/template/render_test.rb
index f0b5d7d95e..c82264a170 100644
--- a/actionview/test/template/render_test.rb
+++ b/actionview/test/template/render_test.rb
@@ -2,6 +2,8 @@
require "abstract_unit"
require "controller/fake_models"
+require "test_component"
+require "active_model/validations"
class TestController < ActionController::Base
end
@@ -33,6 +35,11 @@ module RenderTestCases
assert_equal ORIGINAL_LOCALES, I18n.available_locales.map(&:to_s).sort
end
+ def teardown
+ I18n.reload!
+ ActionController::Base.view_paths.map(&:clear_cache)
+ end
+
def test_implicit_format_comes_from_parent_template
rendered_templates = JSON.parse(@controller_view.render(template: "test/mixing_formats"))
assert_equal({ "format" => "HTML",
@@ -665,6 +672,21 @@ module RenderTestCases
def test_render_throws_exception_when_no_extensions_passed_to_register_template_handler_function_call
assert_raises(ArgumentError) { ActionView::Template.register_template_handler CustomHandler }
end
+
+ def test_render_component
+ assert_equal(
+ %(<span title="my title">Hello, World! (Inline render)</span>),
+ @view.render(TestComponent.new(title: "my title")) { "Hello, World!" }.strip
+ )
+ end
+
+ def test_render_component_with_validation_error
+ error = assert_raises(ActiveModel::ValidationError) do
+ @view.render(TestComponent.new(title: "my title")).strip
+ end
+
+ assert_match "Content can't be blank", error.message
+ end
end
class CachedViewRenderTest < ActiveSupport::TestCase
@@ -677,11 +699,6 @@ class CachedViewRenderTest < ActiveSupport::TestCase
assert_equal ActionView::OptimizedFileSystemResolver, view_paths.first.class
setup_view(view_paths)
end
-
- def teardown
- GC.start
- I18n.reload!
- end
end
class LazyViewRenderTest < ActiveSupport::TestCase
@@ -697,11 +714,6 @@ class LazyViewRenderTest < ActiveSupport::TestCase
setup_view(view_paths)
end
- def teardown
- GC.start
- I18n.reload!
- end
-
def test_render_utf8_template_with_magic_comment
with_external_encoding Encoding::ASCII_8BIT do
result = @view.render(template: "test/utf8_magic", formats: [:html], layouts: "layouts/yield")
@@ -758,10 +770,6 @@ class CachedCollectionViewRenderTest < ActiveSupport::TestCase
setup_view(view_paths)
end
- teardown do
- I18n.reload!
- end
-
test "template body written to cache" do
customer = Customer.new("david", 1)
key = cache_key(customer, "test/_customer")
diff --git a/actionview/test/template/resolver_shared_tests.rb b/actionview/test/template/resolver_shared_tests.rb
index 525b452075..8b47c5bc89 100644
--- a/actionview/test/template/resolver_shared_tests.rb
+++ b/actionview/test/template/resolver_shared_tests.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ResolverSharedTests
attr_reader :tmpdir
@@ -7,7 +9,7 @@ module ResolverSharedTests
end
end
- def with_file(filename, source="File at #{filename}")
+ def with_file(filename, source = "File at #{filename}")
path = File.join(tmpdir, filename)
FileUtils.mkdir_p(File.dirname(path))
File.write(path, source)
@@ -103,7 +105,7 @@ module ResolverSharedTests
c = context.find("hello_world", "test", false, [], {})
# disable_cache should give us a new object
- refute_same a, b
+ assert_not_same a, b
# but it should not clear the cache
assert_same a, c
diff --git a/actionview/test/template/template_error_test.rb b/actionview/test/template/template_error_test.rb
index c4dc88e4aa..643c29602b 100644
--- a/actionview/test/template/template_error_test.rb
+++ b/actionview/test/template/template_error_test.rb
@@ -34,4 +34,20 @@ class TemplateErrorTest < ActiveSupport::TestCase
assert_equal "#<ActionView::Template::Error: original>", error.inspect
end
+
+ def test_annotated_source_code_returns_empty_array_if_source_cant_be_found
+ template = Class.new do
+ def identifier
+ "something"
+ end
+ end.new
+
+ error = begin
+ raise
+ rescue
+ raise ActionView::Template::Error.new(template) rescue $!
+ end
+
+ assert_equal [], error.annotated_source_code
+ end
end
diff --git a/actionview/test/template/testing/fixture_resolver_test.rb b/actionview/test/template/testing/fixture_resolver_test.rb
index 6d0317e0c9..a6066e94c6 100644
--- a/actionview/test/template/testing/fixture_resolver_test.rb
+++ b/actionview/test/template/testing/fixture_resolver_test.rb
@@ -27,4 +27,26 @@ class FixtureResolverTest < ActiveSupport::TestCase
assert_equal :html, templates.first.format
assert_equal "variant", templates.first.variant
end
+
+ def test_should_match_locales
+ resolver = ActionView::FixtureResolver.new("arbitrary/path.erb" => "this text", "arbitrary/path.fr.erb" => "ce texte")
+ en = resolver.find_all("path", "arbitrary", false, locale: [:en], formats: [:html], variants: [], handlers: [:erb])
+ fr = resolver.find_all("path", "arbitrary", false, locale: [:fr], formats: [:html], variants: [], handlers: [:erb])
+
+ assert_equal 1, en.size
+ assert_equal 2, fr.size
+
+ assert_equal "this text", en[0].source
+ assert_equal "ce texte", fr[0].source
+ assert_equal "this text", fr[1].source
+ end
+
+ def test_should_return_all_variants_for_any
+ resolver = ActionView::FixtureResolver.new("arbitrary/path.html.erb" => "this html", "arbitrary/path.html+varient.erb" => "this text")
+ templates = resolver.find_all("path", "arbitrary", false, locale: [], formats: [:html], variants: [], handlers: [:erb])
+ assert_equal 1, templates.size, "expected one template"
+ assert_equal "this html", templates.first.source
+ templates = resolver.find_all("path", "arbitrary", false, locale: [], formats: [:html], variants: :any, handlers: [:erb])
+ assert_equal 2, templates.size, "expected all templates"
+ end
end
diff --git a/actionview/test/template/translation_helper_test.rb b/actionview/test/template/translation_helper_test.rb
index 9afdc3c68f..3475a1dada 100644
--- a/actionview/test/template/translation_helper_test.rb
+++ b/actionview/test/template/translation_helper_test.rb
@@ -121,6 +121,11 @@ class TranslationHelperTest < ActiveSupport::TestCase
I18n.exception_handler = old_exception_handler
end
+ def test_hash_default
+ default = { separator: ".", delimiter: "," }
+ assert_equal default, translate(:'special.number.format', default: default)
+ end
+
def test_translation_returning_an_array
expected = %w(foo bar)
assert_equal expected, translate(:"translations.array")
diff --git a/actionview/test/template/url_helper_test.rb b/actionview/test/template/url_helper_test.rb
index 632b32f09f..bce6e7f370 100644
--- a/actionview/test/template/url_helper_test.rb
+++ b/actionview/test/template/url_helper_test.rb
@@ -708,6 +708,68 @@ class UrlHelperTest < ActiveSupport::TestCase
assert_equal({ class: "special" }, options)
end
+ def test_sms_to
+ assert_dom_equal %{<a href="sms:15155555785;">15155555785</a>}, sms_to("15155555785")
+ assert_dom_equal %{<a href="sms:15155555785;">Jim Jones</a>}, sms_to("15155555785", "Jim Jones")
+ assert_dom_equal(
+ %{<a class="admin" href="sms:15155555785;">Jim Jones</a>},
+ sms_to("15155555785", "Jim Jones", "class" => "admin")
+ )
+ assert_equal sms_to("15155555785", "Jim Jones", "class" => "admin"),
+ sms_to("15155555785", "Jim Jones", class: "admin")
+ end
+
+ def test_sms_to_with_options
+ assert_dom_equal(
+ %{<a class="simple-class" href="sms:15155555785;?&body=Hello%20from%20Jim">Text me</a>},
+ sms_to("15155555785", "Text me", class: "simple-class", body: "Hello from Jim")
+ )
+
+ assert_dom_equal(
+ %{<a href="sms:15155555785;?&body=This%20is%20the%20body%20of%20the%20message.">Text me</a>},
+ sms_to("15155555785", "Text me", body: "This is the body of the message.")
+ )
+ end
+
+ def test_sms_with_img
+ assert_dom_equal %{<a href="sms:15155555785;"><img src="/feedback.png" /></a>},
+ sms_to("15155555785", raw('<img src="/feedback.png" />'))
+ end
+
+ def test_sms_with_html_safe_string
+ assert_dom_equal(
+ %{<a href="sms:1%2B5155555785;">1+5155555785</a>},
+ sms_to(raw("1+5155555785"))
+ )
+ end
+
+ def test_sms_with_nil
+ assert_dom_equal(
+ %{<a href="sms:;"></a>},
+ sms_to(nil)
+ )
+ end
+
+ def test_sms_returns_html_safe_string
+ assert_predicate sms_to("15155555785"), :html_safe?
+ end
+
+ def test_sms_with_block
+ assert_dom_equal %{<a href="sms:15155555785;"><span>Text me</span></a>},
+ sms_to("15155555785") { content_tag(:span, "Text me") }
+ end
+
+ def test_sms_with_block_and_options
+ assert_dom_equal %{<a class="special" href="sms:15155555785;?&body=Hello%20from%20Jim"><span>Text me</span></a>},
+ sms_to("15155555785", body: "Hello from Jim", class: "special") { content_tag(:span, "Text me") }
+ end
+
+ def test_sms_does_not_modify_html_options_hash
+ options = { class: "special" }
+ sms_to "15155555785", "ME!", options
+ assert_equal({ class: "special" }, options)
+ end
+
def protect_against_forgery?
request_forgery
end
diff --git a/actionview/test/ujs/public/test/data-remote.js b/actionview/test/ujs/public/test/data-remote.js
index 9e41067549..16ea114f3b 100644
--- a/actionview/test/ujs/public/test/data-remote.js
+++ b/actionview/test/ujs/public/test/data-remote.js
@@ -82,6 +82,20 @@ asyncTest('right/mouse-wheel-clicking on a link does not fire ajaxyness', 0, fun
setTimeout(function() { start() }, 13)
})
+asyncTest('clicking on a link via a non-mouse Event (such as from js) works', 1, function() {
+ var link = $('a[data-remote]')
+
+ link
+ .removeAttr('data-params')
+ .bindNative('ajax:beforeSend', function() {
+ ok(true, 'ajax should be triggered')
+ })
+
+ Rails.fire(link[0], 'click')
+
+ 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]')
diff --git a/activejob/CHANGELOG.md b/activejob/CHANGELOG.md
index c4e21b48ac..542a4eb2db 100644
--- a/activejob/CHANGELOG.md
+++ b/activejob/CHANGELOG.md
@@ -1,140 +1,6 @@
-* Use individual execution counters when calculating retry delay.
+* `assert_enqueued_with` and `assert_performed_with` can now test jobs with relative delay.
- *Patrik Bóna*
+ *Vlado Cingel*
-* Make job argument assertions with `Time`, `ActiveSupport::TimeWithZone`, and `DateTime` work by dropping microseconds. Microsecond precision is lost during serialization.
- *Gannon McGibbon*
-
-
-## Rails 6.0.0.beta3 (March 11, 2019) ##
-
-* No changes.
-
-
-## Rails 6.0.0.beta2 (February 25, 2019) ##
-
-* No changes.
-
-
-## Rails 6.0.0.beta1 (January 18, 2019) ##
-
-* Return false instead of the job instance when `enqueue` is aborted.
-
- This will be the behavior in Rails 6.1 but it can be controlled now with
- `config.active_job.return_false_on_aborted_enqueue`.
-
- *Kir Shatrov*
-
-* Keep executions for each specific declaration
-
- Each `retry_on` declaration has now its own specific executions counter. Before it was
- shared between all executions of a job.
-
- *Alberto Almagro*
-
-* Allow all assertion helpers that have a `only` and `except` keyword to accept
- Procs.
-
- *Edouard Chin*
-
-* Restore `HashWithIndifferentAccess` support to `ActiveJob::Arguments.deserialize`.
-
- *Gannon McGibbon*
-
-* Include deserialized arguments in job instances returned from
- `assert_enqueued_with` and `assert_performed_with`
-
- *Alan Wu*
-
-* Allow `assert_enqueued_with`/`assert_performed_with` methods to accept
- a proc for the `args` argument. This is useful to check if only a subset of arguments
- matches your expectations.
-
- *Edouard Chin*
-
-* `ActionDispatch::IntegrationTest` includes `ActiveJob::TestHelper` module by default.
-
- *Ricardo Díaz*
-
-* Added `enqueue_retry.active_job`, `retry_stopped.active_job`, and `discard.active_job` hooks.
-
- *steves*
-
-* Allow `assert_performed_with` to be called without a block.
-
- *bogdanvlviv*
-
-* Execution of `assert_performed_jobs`, and `assert_no_performed_jobs`
- without a block should respect passed `:except`, `:only`, and `:queue` options.
-
- *bogdanvlviv*
-
-* Allow `:queue` option to job assertions and helpers.
-
- *bogdanvlviv*
-
-* Allow `perform_enqueued_jobs` to be called without a block.
-
- Performs all of the jobs that have been enqueued up to this point in the test.
-
- *Kevin Deisz*
-
-* Move `enqueue`/`enqueue_at` notifications to an around callback.
-
- Improves timing accuracy over the old after callback by including
- time spent writing to the adapter's IO implementation.
-
- *Zach Kemp*
-
-* Allow call `assert_enqueued_with` with no block.
-
- Example:
- ```
- def test_assert_enqueued_with
- MyJob.perform_later(1,2,3)
- assert_enqueued_with(job: MyJob, args: [1,2,3], queue: 'low')
-
- MyJob.set(wait_until: Date.tomorrow.noon).perform_later
- assert_enqueued_with(job: MyJob, at: Date.tomorrow.noon)
- end
- ```
-
- *bogdanvlviv*
-
-* Allow passing multiple exceptions to `retry_on`, and `discard_on`.
-
- *George Claghorn*
-
-* Pass the error instance as the second parameter of block executed by `discard_on`.
-
- Fixes #32853.
-
- *Yuji Yaginuma*
-
-* Remove support for Qu gem.
-
- Reasons are that the Qu gem wasn't compatible since Rails 5.1,
- gem development was stopped in 2014 and maintainers have
- confirmed its demise. See issue #32273
-
- *Alberto Almagro*
-
-* Add support for timezones to Active Job.
-
- Record what was the current timezone in effect when the job was
- enqueued and then restore when the job is executed in same way
- that the current locale is recorded and restored.
-
- *Andrew White*
-
-* Rails 6 requires Ruby 2.5.0 or newer.
-
- *Jeremy Daer*, *Kasper Timm Hansen*
-
-* Add support to define custom argument serializers.
-
- *Evgenii Pecherkin*, *Rafael Mendonça França*
-
-
-Please check [5-2-stable](https://github.com/rails/rails/blob/5-2-stable/activejob/CHANGELOG.md) for previous changes.
+Please check [6-0-stable](https://github.com/rails/rails/blob/6-0-stable/activejob/CHANGELOG.md) for previous changes.
diff --git a/activejob/lib/active_job/arguments.rb b/activejob/lib/active_job/arguments.rb
index 92eb58aaaf..e08e30040b 100644
--- a/activejob/lib/active_job/arguments.rb
+++ b/activejob/lib/active_job/arguments.rb
@@ -45,7 +45,6 @@ module ActiveJob
end
private
-
# :nodoc:
PERMITTED_TYPES = [ NilClass, String, Integer, Float, BigDecimal, TrueClass, FalseClass ]
# :nodoc:
diff --git a/activejob/lib/active_job/exceptions.rb b/activejob/lib/active_job/exceptions.rb
index 35c1476368..8e83246303 100644
--- a/activejob/lib/active_job/exceptions.rb
+++ b/activejob/lib/active_job/exceptions.rb
@@ -49,12 +49,10 @@ module ActiveJob
# end
def retry_on(*exceptions, wait: 3.seconds, attempts: 5, queue: nil, priority: nil)
rescue_from(*exceptions) do |error|
- # Guard against jobs that were persisted before we started having individual executions counters per retry_on
- self.exception_executions ||= {}
- self.exception_executions[exceptions.to_s] = (exception_executions[exceptions.to_s] || 0) + 1
+ executions = executions_for(exceptions)
- if exception_executions[exceptions.to_s] < attempts
- retry_job wait: determine_delay(seconds_or_duration_or_algorithm: wait, executions: exception_executions[exceptions.to_s]), queue: queue, priority: priority, error: error
+ if executions < attempts
+ retry_job wait: determine_delay(seconds_or_duration_or_algorithm: wait, executions: executions), queue: queue, priority: priority, error: error
else
if block_given?
instrument :retry_stopped, error: error do
@@ -146,5 +144,14 @@ module ActiveJob
ActiveSupport::Notifications.instrument("#{name}.active_job", payload, &block)
end
+
+ def executions_for(exceptions)
+ if exception_executions
+ exception_executions[exceptions.to_s] = (exception_executions[exceptions.to_s] || 0) + 1
+ else
+ # Guard against jobs that were persisted before we started having individual executions counters per retry_on
+ executions
+ end
+ end
end
end
diff --git a/activejob/lib/active_job/gem_version.rb b/activejob/lib/active_job/gem_version.rb
index 5313a4ab9e..224ae9072a 100644
--- a/activejob/lib/active_job/gem_version.rb
+++ b/activejob/lib/active_job/gem_version.rb
@@ -8,9 +8,9 @@ module ActiveJob
module VERSION
MAJOR = 6
- MINOR = 0
+ MINOR = 1
TINY = 0
- PRE = "beta3"
+ PRE = "alpha"
STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
end
diff --git a/activejob/lib/active_job/serializers/date_serializer.rb b/activejob/lib/active_job/serializers/date_serializer.rb
index e995d30faa..3ec587bc54 100644
--- a/activejob/lib/active_job/serializers/date_serializer.rb
+++ b/activejob/lib/active_job/serializers/date_serializer.rb
@@ -12,7 +12,6 @@ module ActiveJob
end
private
-
def klass
Date
end
diff --git a/activejob/lib/active_job/serializers/date_time_serializer.rb b/activejob/lib/active_job/serializers/date_time_serializer.rb
index fe780a1978..42df338b73 100644
--- a/activejob/lib/active_job/serializers/date_time_serializer.rb
+++ b/activejob/lib/active_job/serializers/date_time_serializer.rb
@@ -12,7 +12,6 @@ module ActiveJob
end
private
-
def klass
DateTime
end
diff --git a/activejob/lib/active_job/serializers/duration_serializer.rb b/activejob/lib/active_job/serializers/duration_serializer.rb
index 715fe27a5c..b35eaf5f60 100644
--- a/activejob/lib/active_job/serializers/duration_serializer.rb
+++ b/activejob/lib/active_job/serializers/duration_serializer.rb
@@ -15,7 +15,6 @@ module ActiveJob
end
private
-
def klass
ActiveSupport::Duration
end
diff --git a/activejob/lib/active_job/serializers/object_serializer.rb b/activejob/lib/active_job/serializers/object_serializer.rb
index 6d280969be..be69ddf90c 100644
--- a/activejob/lib/active_job/serializers/object_serializer.rb
+++ b/activejob/lib/active_job/serializers/object_serializer.rb
@@ -44,7 +44,6 @@ module ActiveJob
end
private
-
# The class of the object that will be serialized.
def klass # :doc:
raise NotImplementedError
diff --git a/activejob/lib/active_job/serializers/symbol_serializer.rb b/activejob/lib/active_job/serializers/symbol_serializer.rb
index 7e1f9553a2..3f6ee9101f 100644
--- a/activejob/lib/active_job/serializers/symbol_serializer.rb
+++ b/activejob/lib/active_job/serializers/symbol_serializer.rb
@@ -12,7 +12,6 @@ module ActiveJob
end
private
-
def klass
Symbol
end
diff --git a/activejob/lib/active_job/serializers/time_serializer.rb b/activejob/lib/active_job/serializers/time_serializer.rb
index fe20772f35..d385cca6bb 100644
--- a/activejob/lib/active_job/serializers/time_serializer.rb
+++ b/activejob/lib/active_job/serializers/time_serializer.rb
@@ -12,7 +12,6 @@ module ActiveJob
end
private
-
def klass
Time
end
diff --git a/activejob/lib/active_job/serializers/time_with_zone_serializer.rb b/activejob/lib/active_job/serializers/time_with_zone_serializer.rb
index 43017fc75b..0b29012072 100644
--- a/activejob/lib/active_job/serializers/time_with_zone_serializer.rb
+++ b/activejob/lib/active_job/serializers/time_with_zone_serializer.rb
@@ -12,7 +12,6 @@ module ActiveJob
end
private
-
def klass
ActiveSupport::TimeWithZone
end
diff --git a/activejob/lib/active_job/test_helper.rb b/activejob/lib/active_job/test_helper.rb
index 463020a332..5b1af7da22 100644
--- a/activejob/lib/active_job/test_helper.rb
+++ b/activejob/lib/active_job/test_helper.rb
@@ -630,7 +630,7 @@ module ActiveJob
def prepare_args_for_assertion(args)
args.dup.tap do |arguments|
- arguments[:at] = arguments[:at].to_f if arguments[:at]
+ arguments[:at] = round_time_arguments(arguments[:at]) if arguments[:at]
arguments[:args] = round_time_arguments(arguments[:args]) if arguments[:args]
end
end
@@ -650,6 +650,7 @@ module ActiveJob
def deserialize_args_for_assertion(job)
job.dup.tap do |new_job|
+ new_job[:at] = round_time_arguments(Time.at(new_job[:at])) if new_job[:at]
new_job[:args] = ActiveJob::Arguments.deserialize(new_job[:args]) if new_job[:args]
end
end
diff --git a/activejob/test/cases/exceptions_test.rb b/activejob/test/cases/exceptions_test.rb
index 840f4d40b5..1f07b7b294 100644
--- a/activejob/test/cases/exceptions_test.rb
+++ b/activejob/test/cases/exceptions_test.rb
@@ -179,6 +179,31 @@ class ExceptionsTest < ActiveSupport::TestCase
assert_equal ["Raised ActiveJob::DeserializationError for the 5 time"], JobBuffer.values
end
+ test "running a job enqueued by AJ 5.2" do
+ job = RetryJob.new("DefaultsError", 6)
+ job.exception_executions = nil # This is how jobs from Rails 5.2 will look
+
+ assert_raises DefaultsError do
+ job.enqueue
+ end
+
+ assert_equal 5, JobBuffer.values.count
+ end
+
+ test "running a job enqueued and attempted under AJ 5.2" do
+ job = RetryJob.new("DefaultsError", 6)
+
+ # Fake 4 previous executions under AJ 5.2
+ job.exception_executions = nil
+ job.executions = 4
+
+ assert_raises DefaultsError do
+ job.enqueue
+ end
+
+ assert_equal ["Raised DefaultsError for the 5th time"], JobBuffer.values
+ end
+
private
def adapter_skips_scheduling?(queue_adapter)
[
diff --git a/activejob/test/cases/serializers_test.rb b/activejob/test/cases/serializers_test.rb
index bee0c061bd..2b9ba75ae0 100644
--- a/activejob/test/cases/serializers_test.rb
+++ b/activejob/test/cases/serializers_test.rb
@@ -26,7 +26,6 @@ class SerializersTest < ActiveSupport::TestCase
end
private
-
def klass
DummyValueObject
end
diff --git a/activejob/test/cases/test_helper_test.rb b/activejob/test/cases/test_helper_test.rb
index d6607cb6b6..34dfd8eb56 100644
--- a/activejob/test/cases/test_helper_test.rb
+++ b/activejob/test/cases/test_helper_test.rb
@@ -621,6 +621,12 @@ class EnqueuedJobsTest < ActiveJob::TestCase
end
end
+ def test_assert_enqueued_with_with_relative_at_option
+ assert_enqueued_with(job: HelloJob, at: 5.minutes.from_now) do
+ HelloJob.set(wait: 5.minutes).perform_later
+ end
+ end
+
def test_assert_enqueued_with_with_no_block_with_at_option
HelloJob.set(wait_until: Date.tomorrow.noon).perform_later
assert_enqueued_with(job: HelloJob, at: Date.tomorrow.noon)
@@ -1663,6 +1669,18 @@ class PerformedJobsTest < ActiveJob::TestCase
end
end
+ def test_assert_performed_with_with_relative_at_option
+ assert_performed_with(job: HelloJob, at: 5.minutes.from_now) do
+ HelloJob.set(wait: 5.minutes).perform_later
+ end
+
+ assert_raise ActiveSupport::TestCase::Assertion do
+ assert_performed_with(job: HelloJob, at: 2.minutes.from_now) do
+ HelloJob.set(wait: 1.minute).perform_later
+ end
+ end
+ end
+
def test_assert_performed_with_without_block_with_at_option
HelloJob.set(wait_until: Date.tomorrow.noon).perform_later
@@ -1710,28 +1728,28 @@ class PerformedJobsTest < ActiveJob::TestCase
def test_assert_performed_with_time
now = Time.now
- args = [{ argument1: { now: now } }]
+ args = [{ argument1: { now: now }, argument2: now }]
- assert_enqueued_with(job: MultipleKwargsJob, args: args) do
- MultipleKwargsJob.perform_later(argument1: { now: now })
+ assert_performed_with(job: MultipleKwargsJob, args: args) do
+ MultipleKwargsJob.perform_later(argument1: { now: now }, argument2: now)
end
end
def test_assert_performed_with_date_time
now = DateTime.now
- args = [{ argument1: { now: now } }]
+ args = [{ argument1: { now: now }, argument2: now }]
- assert_enqueued_with(job: MultipleKwargsJob, args: args) do
- MultipleKwargsJob.perform_later(argument1: { now: now })
+ assert_performed_with(job: MultipleKwargsJob, args: args) do
+ MultipleKwargsJob.perform_later(argument1: { now: now }, argument2: now)
end
end
def test_assert_performed_with_time_with_zone
now = Time.now.in_time_zone("Tokyo")
- args = [{ argument1: { now: now } }]
+ args = [{ argument1: { now: now }, argument2: now }]
- assert_enqueued_with(job: MultipleKwargsJob, args: args) do
- MultipleKwargsJob.perform_later(argument1: { now: now })
+ assert_performed_with(job: MultipleKwargsJob, args: args) do
+ MultipleKwargsJob.perform_later(argument1: { now: now }, argument2: now)
end
end
diff --git a/activejob/test/jobs/timezone_dependent_job.rb b/activejob/test/jobs/timezone_dependent_job.rb
index 41f473d533..f63ff95b95 100644
--- a/activejob/test/jobs/timezone_dependent_job.rb
+++ b/activejob/test/jobs/timezone_dependent_job.rb
@@ -15,7 +15,6 @@ class TimezoneDependentJob < ActiveJob::Base
end
private
-
def localtime(*args)
Time.zone ? Time.zone.local(*args) : Time.utc(*args)
end
diff --git a/activejob/test/support/integration/test_case_helpers.rb b/activejob/test/support/integration/test_case_helpers.rb
index 973ee07764..d8931a74ed 100644
--- a/activejob/test/support/integration/test_case_helpers.rb
+++ b/activejob/test/support/integration/test_case_helpers.rb
@@ -19,7 +19,6 @@ module TestCaseHelpers
end
private
-
def jobs_manager
JobsManager.current_manager
end
diff --git a/activemodel/CHANGELOG.md b/activemodel/CHANGELOG.md
index ad87abfa3a..9d77564c61 100644
--- a/activemodel/CHANGELOG.md
+++ b/activemodel/CHANGELOG.md
@@ -1,150 +1,3 @@
-* Type cast falsy boolean symbols on boolean attribute as false.
- Fixes #35676.
- *Ryuta Kamizono*
-
-* Change how validation error translation strings are fetched: The new behavior
- will first try the more specific keys, including doing locale fallback, then try
- the less specific ones.
-
- For example, this is the order in which keys will now be tried for a `blank`
- error on a `product`'s `title` attribute with current locale set to `en-US`:
-
- en-US.activerecord.errors.models.product.attributes.title.blank
- en-US.activerecord.errors.models.product.blank
- en-US.activerecord.errors.messages.blank
-
- en.activerecord.errors.models.product.attributes.title.blank
- en.activerecord.errors.models.product.blank
- en.activerecord.errors.messages.blank
-
- en-US.errors.attributes.title.blank
- en-US.errors.messages.blank
-
- en.errors.attributes.title.blank
- en.errors.messages.blank
-
- *Hugo Vacher*
-
-
-## Rails 6.0.0.beta3 (March 11, 2019) ##
-
-* No changes.
-
-
-## Rails 6.0.0.beta2 (February 25, 2019) ##
-
-* Fix date value when casting a multiparameter date hash to not convert
- from Gregorian date to Julian date.
-
- Before:
-
- Day.new({"day(1i)"=>"1", "day(2i)"=>"1", "day(3i)"=>"1"})
- # => #<Day id: nil, day: "0001-01-03", created_at: nil, updated_at: nil>
-
- After:
-
- Day.new({"day(1i)"=>"1", "day(2i)"=>"1", "day(3i)"=>"1"})
- # => #<Day id: nil, day: "0001-01-01", created_at: nil, updated_at: nil>
-
- Fixes #28521.
-
- *Sayan Chakraborty*
-
-* Fix year value when casting a multiparameter time hash.
-
- When assigning a hash to a time attribute that's missing a year component
- (e.g. a `time_select` with `:ignore_date` set to `true`) then the year
- defaults to 1970 instead of the expected 2000. This results in the attribute
- changing as a result of the save.
-
- Before:
- ```
- event = Event.new(start_time: { 4 => 20, 5 => 30 })
- event.start_time # => 1970-01-01 20:30:00 UTC
- event.save
- event.reload
- event.start_time # => 2000-01-01 20:30:00 UTC
- ```
-
- After:
- ```
- event = Event.new(start_time: { 4 => 20, 5 => 30 })
- event.start_time # => 2000-01-01 20:30:00 UTC
- event.save
- event.reload
- event.start_time # => 2000-01-01 20:30:00 UTC
- ```
-
- *Andrew White*
-
-
-## Rails 6.0.0.beta1 (January 18, 2019) ##
-
-* Add `ActiveModel::Errors#of_kind?`.
-
- *bogdanvlviv*, *Rafael Mendonça França*
-
-* Fix numericality equality validation of `BigDecimal` and `Float`
- by casting to `BigDecimal` on both ends of the validation.
-
- *Gannon McGibbon*
-
-* Add `#slice!` method to `ActiveModel::Errors`.
-
- *Daniel López Prat*
-
-* Fix numericality validator to still use value before type cast except Active Record.
-
- Fixes #33651, #33686.
-
- *Ryuta Kamizono*
-
-* Fix `ActiveModel::Serializers::JSON#as_json` method for timestamps.
-
- Before:
- ```
- contact = Contact.new(created_at: Time.utc(2006, 8, 1))
- contact.as_json["created_at"] # => 2006-08-01 00:00:00 UTC
- ```
-
- After:
- ```
- contact = Contact.new(created_at: Time.utc(2006, 8, 1))
- contact.as_json["created_at"] # => "2006-08-01T00:00:00.000Z"
- ```
-
- *Bogdan Gusiev*
-
-* Allows configurable attribute name for `#has_secure_password`. This
- still defaults to an attribute named 'password', causing no breaking
- change. There is a new method `#authenticate_XXX` where XXX is the
- configured attribute name, making the existing `#authenticate` now an
- alias for this when the attribute is the default 'password'.
-
- Example:
-
- class User < ActiveRecord::Base
- has_secure_password :recovery_password, validations: false
- end
-
- user = User.new()
- user.recovery_password = "42password"
- user.recovery_password_digest # => "$2a$04$iOfhwahFymCs5weB3BNH/uX..."
- user.authenticate_recovery_password('42password') # => user
-
- *Unathi Chonco*
-
-* Add `config.active_model.i18n_customize_full_message` in order to control whether
- the `full_message` error format can be overridden at the attribute or model
- level in the locale files. This is `false` by default.
-
- *Martin Larochelle*
-
-* Rails 6 requires Ruby 2.5.0 or newer.
-
- *Jeremy Daer*, *Kasper Timm Hansen*
-
-
-Please check [5-2-stable](https://github.com/rails/rails/blob/5-2-stable/activemodel/CHANGELOG.md) for previous changes.
+Please check [6-0-stable](https://github.com/rails/rails/blob/6-0-stable/activemodel/CHANGELOG.md) for previous changes.
diff --git a/activemodel/lib/active_model.rb b/activemodel/lib/active_model.rb
index c9140dc582..756473e38d 100644
--- a/activemodel/lib/active_model.rb
+++ b/activemodel/lib/active_model.rb
@@ -53,6 +53,7 @@ module ActiveModel
eager_autoload do
autoload :Errors
+ autoload :Error
autoload :RangeError, "active_model/errors"
autoload :StrictValidationFailed, "active_model/errors"
autoload :UnknownAttributeError, "active_model/errors"
diff --git a/activemodel/lib/active_model/attribute_assignment.rb b/activemodel/lib/active_model/attribute_assignment.rb
index f0e3458f51..9bdec0dfda 100644
--- a/activemodel/lib/active_model/attribute_assignment.rb
+++ b/activemodel/lib/active_model/attribute_assignment.rb
@@ -38,7 +38,6 @@ module ActiveModel
alias attributes= assign_attributes
private
-
def _assign_attributes(attributes)
attributes.each do |k, v|
_assign_attribute(k, v)
diff --git a/activemodel/lib/active_model/attribute_methods.rb b/activemodel/lib/active_model/attribute_methods.rb
index 0075cd3c01..1a4e0b8e59 100644
--- a/activemodel/lib/active_model/attribute_methods.rb
+++ b/activemodel/lib/active_model/attribute_methods.rb
@@ -352,10 +352,7 @@ module ActiveModel
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.
- matchers = attribute_method_matchers.partition(&:plain?).reverse.flatten(1)
- matchers.map { |matcher| matcher.match(method_name) }.compact
+ attribute_method_matchers.map { |matcher| matcher.match(method_name) }.compact
end
end
@@ -405,10 +402,6 @@ module ActiveModel
def method_name(attr_name)
@method_name % attr_name
end
-
- def plain?
- prefix.empty? && suffix.empty?
- end
end
end
diff --git a/activemodel/lib/active_model/attribute_set.rb b/activemodel/lib/active_model/attribute_set.rb
index 4679b33852..2f5437ceda 100644
--- a/activemodel/lib/active_model/attribute_set.rb
+++ b/activemodel/lib/active_model/attribute_set.rb
@@ -94,11 +94,9 @@ module ActiveModel
end
protected
-
attr_reader :attributes
private
-
def initialized_attributes
attributes.select { |_, attr| attr.initialized? }
end
diff --git a/activemodel/lib/active_model/attributes.rb b/activemodel/lib/active_model/attributes.rb
index b7b2f35bcc..c586f52b78 100644
--- a/activemodel/lib/active_model/attributes.rb
+++ b/activemodel/lib/active_model/attributes.rb
@@ -26,8 +26,22 @@ module ActiveModel
define_attribute_method(name)
end
- private
+ # Returns an array of attribute names as strings
+ #
+ # class Person
+ # include ActiveModel::Attributes
+ #
+ # attribute :name, :string
+ # attribute :age, :integer
+ # end
+ #
+ # Person.attribute_names
+ # # => ["name", "age"]
+ def attribute_names
+ attribute_types.keys
+ end
+ private
def define_method_attribute=(name)
ActiveModel::AttributeMethods::AttrNames.define_attribute_accessor_method(
generated_attribute_methods, name, writer: true,
@@ -65,29 +79,52 @@ module ActiveModel
super
end
+ # Returns a hash of all the attributes with their names as keys and the values of the attributes as values.
+ #
+ # class Person
+ # include ActiveModel::Model
+ # include ActiveModel::Attributes
+ #
+ # attribute :name, :string
+ # attribute :age, :integer
+ # end
+ #
+ # person = Person.new(name: 'Francesco', age: 22)
+ # person.attributes
+ # # => {"name"=>"Francesco", "age"=>22}
def attributes
@attributes.to_hash
end
- private
+ # Returns an array of attribute names as strings
+ #
+ # class Person
+ # include ActiveModel::Attributes
+ #
+ # attribute :name, :string
+ # attribute :age, :integer
+ # end
+ #
+ # person = Person.new
+ # person.attribute_names
+ # # => ["name", "age"]
+ def attribute_names
+ @attributes.keys
+ end
+ private
def write_attribute(attr_name, value)
- name = if self.class.attribute_alias?(attr_name)
- self.class.attribute_alias(attr_name).to_s
- else
- attr_name.to_s
- end
+ name = attr_name.to_s
+ name = self.class.attribute_aliases[name] || name
@attributes.write_from_user(name, value)
value
end
def attribute(attr_name)
- name = if self.class.attribute_alias?(attr_name)
- self.class.attribute_alias(attr_name).to_s
- else
- attr_name.to_s
- end
+ name = attr_name.to_s
+ name = self.class.attribute_aliases[name] || name
+
@attributes.fetch_value(name)
end
diff --git a/activemodel/lib/active_model/callbacks.rb b/activemodel/lib/active_model/callbacks.rb
index ea2ed7dff7..7a544395cb 100644
--- a/activemodel/lib/active_model/callbacks.rb
+++ b/activemodel/lib/active_model/callbacks.rb
@@ -126,7 +126,6 @@ module ActiveModel
end
private
-
def _define_before_model_callback(klass, callback)
klass.define_singleton_method("before_#{callback}") do |*args, **options, &block|
options.assert_valid_keys(:if, :unless, :prepend)
diff --git a/activemodel/lib/active_model/dirty.rb b/activemodel/lib/active_model/dirty.rb
index 35a587658c..aaefe00c83 100644
--- a/activemodel/lib/active_model/dirty.rb
+++ b/activemodel/lib/active_model/dirty.rb
@@ -136,7 +136,7 @@ module ActiveModel
@mutations_from_database = nil
end
- # Clears dirty data and moves +changes+ to +previously_changed+ and
+ # Clears dirty data and moves +changes+ to +previous_changes+ and
# +mutations_from_database+ to +mutations_before_last_save+ respectively.
def changes_applied
unless defined?(@attributes)
diff --git a/activemodel/lib/active_model/error.rb b/activemodel/lib/active_model/error.rb
new file mode 100644
index 0000000000..6deab3578d
--- /dev/null
+++ b/activemodel/lib/active_model/error.rb
@@ -0,0 +1,165 @@
+# frozen_string_literal: true
+
+require "active_support/core_ext/class/attribute"
+
+module ActiveModel
+ # == Active \Model \Error
+ #
+ # Represents one single error
+ class Error
+ CALLBACKS_OPTIONS = [:if, :unless, :on, :allow_nil, :allow_blank, :strict]
+ MESSAGE_OPTIONS = [:message]
+
+ class_attribute :i18n_customize_full_message, default: false
+
+ def self.full_message(attribute, message, base_class) # :nodoc:
+ return message if attribute == :base
+ attribute = attribute.to_s
+
+ if i18n_customize_full_message && base_class.respond_to?(:i18n_scope)
+ attribute = attribute.remove(/\[\d\]/)
+ parts = attribute.split(".")
+ attribute_name = parts.pop
+ namespace = parts.join("/") unless parts.empty?
+ attributes_scope = "#{base_class.i18n_scope}.errors.models"
+
+ if namespace
+ defaults = base_class.lookup_ancestors.map do |klass|
+ [
+ :"#{attributes_scope}.#{klass.model_name.i18n_key}/#{namespace}.attributes.#{attribute_name}.format",
+ :"#{attributes_scope}.#{klass.model_name.i18n_key}/#{namespace}.format",
+ ]
+ end
+ else
+ defaults = base_class.lookup_ancestors.map do |klass|
+ [
+ :"#{attributes_scope}.#{klass.model_name.i18n_key}.attributes.#{attribute_name}.format",
+ :"#{attributes_scope}.#{klass.model_name.i18n_key}.format",
+ ]
+ end
+ end
+
+ defaults.flatten!
+ else
+ defaults = []
+ end
+
+ defaults << :"errors.format"
+ defaults << "%{attribute} %{message}"
+
+ attr_name = attribute.tr(".", "_").humanize
+ attr_name = base_class.human_attribute_name(attribute, default: attr_name)
+
+ I18n.t(defaults.shift,
+ default: defaults,
+ attribute: attr_name,
+ message: message)
+ end
+
+ def self.generate_message(attribute, type, base, options) # :nodoc:
+ type = options.delete(:message) if options[:message].is_a?(Symbol)
+ value = (attribute != :base ? base.send(:read_attribute_for_validation, attribute) : nil)
+
+ options = {
+ model: base.model_name.human,
+ attribute: base.class.human_attribute_name(attribute),
+ value: value,
+ object: base
+ }.merge!(options)
+
+ if base.class.respond_to?(:i18n_scope)
+ i18n_scope = base.class.i18n_scope.to_s
+ defaults = base.class.lookup_ancestors.flat_map do |klass|
+ [ :"#{i18n_scope}.errors.models.#{klass.model_name.i18n_key}.attributes.#{attribute}.#{type}",
+ :"#{i18n_scope}.errors.models.#{klass.model_name.i18n_key}.#{type}" ]
+ end
+ defaults << :"#{i18n_scope}.errors.messages.#{type}"
+
+ catch(:exception) do
+ translation = I18n.translate(defaults.first, options.merge(default: defaults.drop(1), throw: true))
+ return translation unless translation.nil?
+ end unless options[:message]
+ else
+ defaults = []
+ end
+
+ defaults << :"errors.attributes.#{attribute}.#{type}"
+ defaults << :"errors.messages.#{type}"
+
+ key = defaults.shift
+ defaults = options.delete(:message) if options[:message]
+ options[:default] = defaults
+
+ I18n.translate(key, options)
+ end
+
+ def initialize(base, attribute, type = :invalid, **options)
+ @base = base
+ @attribute = attribute
+ @raw_type = type
+ @type = type || :invalid
+ @options = options
+ end
+
+ def initialize_dup(other)
+ @attribute = @attribute.dup
+ @raw_type = @raw_type.dup
+ @type = @type.dup
+ @options = @options.deep_dup
+ end
+
+ attr_reader :base, :attribute, :type, :raw_type, :options
+
+ def message
+ case raw_type
+ when Symbol
+ self.class.generate_message(attribute, raw_type, @base, options.except(*CALLBACKS_OPTIONS))
+ else
+ raw_type
+ end
+ end
+
+ def detail
+ { error: raw_type }.merge(options.except(*CALLBACKS_OPTIONS + MESSAGE_OPTIONS))
+ end
+
+ def full_message
+ self.class.full_message(attribute, message, @base.class)
+ end
+
+ # See if error matches provided +attribute+, +type+ and +options+.
+ def match?(attribute, type = nil, **options)
+ if @attribute != attribute || (type && @type != type)
+ return false
+ end
+
+ options.each do |key, value|
+ if @options[key] != value
+ return false
+ end
+ end
+
+ true
+ end
+
+ def strict_match?(attribute, type, **options)
+ return false unless match?(attribute, type)
+
+ options == @options.except(*CALLBACKS_OPTIONS + MESSAGE_OPTIONS)
+ end
+
+ def ==(other)
+ other.is_a?(self.class) && attributes_for_hash == other.attributes_for_hash
+ end
+ alias eql? ==
+
+ def hash
+ attributes_for_hash.hash
+ end
+
+ protected
+ def attributes_for_hash
+ [@base, @attribute, @raw_type, @options.except(*CALLBACKS_OPTIONS)]
+ end
+ end
+end
diff --git a/activemodel/lib/active_model/errors.rb b/activemodel/lib/active_model/errors.rb
index 3a692a3e64..42c004ce31 100644
--- a/activemodel/lib/active_model/errors.rb
+++ b/activemodel/lib/active_model/errors.rb
@@ -4,6 +4,9 @@ require "active_support/core_ext/array/conversions"
require "active_support/core_ext/string/inflections"
require "active_support/core_ext/object/deep_dup"
require "active_support/core_ext/string/filters"
+require "active_model/error"
+require "active_model/nested_error"
+require "forwardable"
module ActiveModel
# == Active \Model \Errors
@@ -59,15 +62,15 @@ module ActiveModel
class Errors
include Enumerable
- CALLBACKS_OPTIONS = [:if, :unless, :on, :allow_nil, :allow_blank, :strict]
- MESSAGE_OPTIONS = [:message]
+ extend Forwardable
+ def_delegators :@errors, :size, :clear, :blank?, :empty?, :uniq!, :any?
+ # TODO: forward all enumerable methods after `each` deprecation is removed.
+ def_delegators :@errors, :count
- class << self
- attr_accessor :i18n_customize_full_message # :nodoc:
- end
- self.i18n_customize_full_message = false
+ LEGACY_ATTRIBUTES = [:messages, :details].freeze
- attr_reader :messages, :details
+ attr_reader :errors
+ alias :objects :errors
# Pass in the instance of the object that is using the errors object.
#
@@ -77,18 +80,17 @@ module ActiveModel
# end
# end
def initialize(base)
- @base = base
- @messages = apply_default_array({})
- @details = apply_default_array({})
+ @base = base
+ @errors = []
end
def initialize_dup(other) # :nodoc:
- @messages = other.messages.dup
- @details = other.details.deep_dup
+ @errors = other.errors.deep_dup
super
end
# Copies the errors from <tt>other</tt>.
+ # For copying errors but keep <tt>@base</tt> as is.
#
# other - The ActiveModel::Errors instance.
#
@@ -96,11 +98,31 @@ module ActiveModel
#
# person.errors.copy!(other)
def copy!(other) # :nodoc:
- @messages = other.messages.dup
- @details = other.details.dup
+ @errors = other.errors.deep_dup
+ @errors.each { |error|
+ error.instance_variable_set("@base", @base)
+ }
+ end
+
+ # Imports one error
+ # Imported errors are wrapped as a NestedError,
+ # providing access to original error object.
+ # If attribute or type needs to be overriden, use `override_options`.
+ #
+ # override_options - Hash
+ # @option override_options [Symbol] :attribute Override the attribute the error belongs to
+ # @option override_options [Symbol] :type Override type of the error.
+ def import(error, override_options = {})
+ [:attribute, :type].each do |key|
+ if override_options.key?(key)
+ override_options[key] = override_options[key].to_sym
+ end
+ end
+ @errors.append(NestedError.new(@base, error, override_options))
end
- # Merges the errors from <tt>other</tt>.
+ # Merges the errors from <tt>other</tt>,
+ # each <tt>Error</tt> wrapped as <tt>NestedError</tt>.
#
# other - The ActiveModel::Errors instance.
#
@@ -108,8 +130,9 @@ module ActiveModel
#
# person.errors.merge!(other)
def merge!(other)
- @messages.merge!(other.messages) { |_, ary1, ary2| ary1 + ary2 }
- @details.merge!(other.details) { |_, ary1, ary2| ary1 + ary2 }
+ other.errors.each { |error|
+ import(error)
+ }
end
# Removes all errors except the given keys. Returns a hash containing the removed errors.
@@ -118,19 +141,31 @@ module ActiveModel
# person.errors.slice!(:age, :gender) # => { :name=>["cannot be nil"], :city=>["cannot be nil"] }
# person.errors.keys # => [:age, :gender]
def slice!(*keys)
+ deprecation_removal_warning(:slice!)
+
keys = keys.map(&:to_sym)
- @details.slice!(*keys)
- @messages.slice!(*keys)
+
+ results = messages.dup.slice!(*keys)
+
+ @errors.keep_if do |error|
+ keys.include?(error.attribute)
+ end
+
+ results
end
- # Clear the error messages.
+ # Search for errors matching +attribute+, +type+ or +options+.
+ #
+ # Only supplied params will be matched.
#
- # person.errors.full_messages # => ["name cannot be nil"]
- # person.errors.clear
- # person.errors.full_messages # => []
- def clear
- messages.clear
- details.clear
+ # person.errors.where(:name) # => all name errors.
+ # person.errors.where(:name, :too_short) # => all name errors being too short
+ # person.errors.where(:name, :too_short, minimum: 2) # => all name errors being too short and minimum is 2
+ def where(attribute, type = nil, **options)
+ attribute, type, options = normalize_arguments(attribute, type, options)
+ @errors.select { |error|
+ error.match?(attribute, type, options)
+ }
end
# Returns +true+ if the error messages include an error for the given key
@@ -140,8 +175,9 @@ 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?
+ @errors.any? { |error|
+ error.match?(attribute.to_sym)
+ }
end
alias :has_key? :include?
alias :key? :include?
@@ -151,10 +187,13 @@ module ActiveModel
# person.errors[:name] # => ["cannot be nil"]
# person.errors.delete(:name) # => ["cannot be nil"]
# person.errors[:name] # => []
- def delete(key)
- attribute = key.to_sym
- details.delete(attribute)
- messages.delete(attribute)
+ def delete(attribute, type = nil, **options)
+ attribute, type, options = normalize_arguments(attribute, type, options)
+ matches = where(attribute, type, options)
+ matches.each do |error|
+ @errors.delete(error)
+ end
+ matches.map(&:message).presence
end
# When passed a symbol or a name of a method, returns an array of errors
@@ -163,7 +202,7 @@ module ActiveModel
# person.errors[:name] # => ["cannot be nil"]
# person.errors['name'] # => ["cannot be nil"]
def [](attribute)
- messages[attribute.to_sym]
+ DeprecationHandlingMessageArray.new(messages_for(attribute), self, attribute)
end
# Iterates through each error key, value pair in the error messages hash.
@@ -180,31 +219,37 @@ module ActiveModel
# # Will yield :name and "can't be blank"
# # then yield :name and "must be specified"
# end
- def each
- messages.each_key do |attribute|
- messages[attribute].each { |error| yield attribute, error }
- end
- end
+ def each(&block)
+ if block.arity <= 1
+ @errors.each(&block)
+ else
+ ActiveSupport::Deprecation.warn(<<~MSG)
+ Enumerating ActiveModel::Errors as a hash has been deprecated.
+ In Rails 6.1, `errors` is an array of Error objects,
+ therefore it should be accessed by a block with a single block
+ parameter like this:
+
+ person.errors.each do |error|
+ error.full_message
+ end
- # Returns the number of error messages.
- #
- # person.errors.add(:name, :blank, message: "can't be blank")
- # person.errors.size # => 1
- # person.errors.add(:name, :not_specified, message: "must be specified")
- # person.errors.size # => 2
- def size
- values.flatten.size
+ You are passing a block expecting two parameters,
+ so the old hash behavior is simulated. As this is deprecated,
+ this will result in an ArgumentError in Rails 6.2.
+ MSG
+ @errors.
+ sort { |a, b| a.attribute <=> b.attribute }.
+ each { |error| yield error.attribute, error.message }
+ end
end
- alias :count :size
# Returns all message values.
#
# person.errors.messages # => {:name=>["cannot be nil", "must be specified"]}
# person.errors.values # => [["cannot be nil", "must be specified"]]
def values
- messages.select do |key, value|
- !value.empty?
- end.values
+ deprecation_removal_warning(:values)
+ @errors.map(&:message).freeze
end
# Returns all message keys.
@@ -212,20 +257,11 @@ module ActiveModel
# person.errors.messages # => {:name=>["cannot be nil", "must be specified"]}
# person.errors.keys # => [:name]
def keys
- messages.select do |key, value|
- !value.empty?
- end.keys
- end
-
- # Returns +true+ if no errors are found, +false+ otherwise.
- # If the error message is a string it can be empty.
- #
- # person.errors.full_messages # => ["name cannot be nil"]
- # person.errors.empty? # => false
- def empty?
- size.zero?
+ deprecation_removal_warning(:keys)
+ keys = @errors.map(&:attribute)
+ keys.uniq!
+ keys.freeze
end
- alias :blank? :empty?
# Returns an xml formatted representation of the Errors hash.
#
@@ -239,6 +275,7 @@ module ActiveModel
# # <error>name must be specified</error>
# # </errors>
def to_xml(options = {})
+ deprecation_removal_warning(:to_xml)
to_a.to_xml({ root: "errors", skip_types: true }.merge!(options))
end
@@ -258,13 +295,38 @@ module ActiveModel
# person.errors.to_hash # => {:name=>["cannot be nil"]}
# person.errors.to_hash(true) # => {:name=>["name cannot be nil"]}
def to_hash(full_messages = false)
- if full_messages
- messages.each_with_object({}) do |(attribute, array), messages|
- messages[attribute] = array.map { |message| full_message(attribute, message) }
- end
- else
- without_default_proc(messages)
+ hash = {}
+ message_method = full_messages ? :full_message : :message
+ group_by_attribute.each do |attribute, errors|
+ hash[attribute] = errors.map(&message_method)
+ end
+ hash
+ end
+
+ def to_h
+ ActiveSupport::Deprecation.warn(<<~EOM)
+ ActiveModel::Errors#to_h is deprecated and will be removed in Rails 6.2
+ Please use `ActiveModel::Errors.to_hash` instead. The values in the hash
+ returned by `ActiveModel::Errors.to_hash` is an array of error messages.
+ EOM
+
+ to_hash.transform_values { |values| values.last }
+ end
+
+ def messages
+ DeprecationHandlingMessageHash.new(self)
+ end
+
+ def details
+ hash = {}
+ group_by_attribute.each do |attribute, errors|
+ hash[attribute] = errors.map(&:detail)
end
+ DeprecationHandlingDetailsHash.new(hash)
+ end
+
+ def group_by_attribute
+ @errors.group_by(&:attribute)
end
# Adds +message+ to the error messages and used validator type to +details+ on +attribute+.
@@ -308,17 +370,20 @@ module ActiveModel
# # => {:base=>["either name or email must be present"]}
# person.errors.details
# # => {:base=>[{error: :name_or_email_blank}]}
- def add(attribute, message = :invalid, options = {})
- message = message.call if message.respond_to?(:call)
- detail = normalize_detail(message, options)
- message = normalize_message(attribute, message, options)
+ def add(attribute, type = :invalid, **options)
+ error = Error.new(
+ @base,
+ *normalize_arguments(attribute, type, options)
+ )
+
if exception = options[:strict]
exception = ActiveModel::StrictValidationFailed if exception == true
- raise exception, full_message(attribute, message)
+ raise exception, error.full_message
end
- details[attribute.to_sym] << detail
- messages[attribute.to_sym] << message
+ @errors.append(error)
+
+ error
end
# Returns +true+ if an error on the attribute with the given message is
@@ -337,13 +402,15 @@ module ActiveModel
# person.errors.added? :name, :too_long, count: 24 # => false
# person.errors.added? :name, :too_long # => false
# person.errors.added? :name, "is too long" # => false
- def added?(attribute, message = :invalid, options = {})
- message = message.call if message.respond_to?(:call)
+ def added?(attribute, type = :invalid, options = {})
+ attribute, type, options = normalize_arguments(attribute, type, options)
- if message.is_a? Symbol
- details[attribute.to_sym].include? normalize_detail(message, options)
+ if type.is_a? Symbol
+ @errors.any? { |error|
+ error.strict_match?(attribute, type, options)
+ }
else
- self[attribute].include? message
+ messages_for(attribute).include?(type)
end
end
@@ -359,12 +426,12 @@ module ActiveModel
# person.errors.of_kind? :name, :not_too_long # => false
# person.errors.of_kind? :name, "is too long" # => false
def of_kind?(attribute, message = :invalid)
- message = message.call if message.respond_to?(:call)
+ attribute, message = normalize_arguments(attribute, message)
if message.is_a? Symbol
- details[attribute.to_sym].map { |e| e[:error] }.include? message
+ !where(attribute, message).empty?
else
- self[attribute].include? message
+ messages_for(attribute).include?(message)
end
end
@@ -379,7 +446,7 @@ module ActiveModel
# person.errors.full_messages
# # => ["Name is too short (minimum is 5 characters)", "Name can't be blank", "Email can't be blank"]
def full_messages
- map { |attribute, message| full_message(attribute, message) }
+ @errors.map(&:full_message)
end
alias :to_a :full_messages
@@ -394,63 +461,18 @@ 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) }
+ where(attribute).map(&:full_message).freeze
+ end
+
+ def messages_for(attribute)
+ where(attribute).map(&:message)
end
# Returns a full message for a given attribute.
#
# person.errors.full_message(:name, 'is invalid') # => "Name is invalid"
- #
- # The `"%{attribute} %{message}"` error format can be overridden with either
- #
- # * <tt>activemodel.errors.models.person/contacts/addresses.attributes.street.format</tt>
- # * <tt>activemodel.errors.models.person/contacts/addresses.format</tt>
- # * <tt>activemodel.errors.models.person.attributes.name.format</tt>
- # * <tt>activemodel.errors.models.person.format</tt>
- # * <tt>errors.format</tt>
def full_message(attribute, message)
- return message if attribute == :base
- attribute = attribute.to_s
-
- if self.class.i18n_customize_full_message && @base.class.respond_to?(:i18n_scope)
- attribute = attribute.remove(/\[\d\]/)
- parts = attribute.split(".")
- attribute_name = parts.pop
- namespace = parts.join("/") unless parts.empty?
- attributes_scope = "#{@base.class.i18n_scope}.errors.models"
-
- if namespace
- defaults = @base.class.lookup_ancestors.map do |klass|
- [
- :"#{attributes_scope}.#{klass.model_name.i18n_key}/#{namespace}.attributes.#{attribute_name}.format",
- :"#{attributes_scope}.#{klass.model_name.i18n_key}/#{namespace}.format",
- ]
- end
- else
- defaults = @base.class.lookup_ancestors.map do |klass|
- [
- :"#{attributes_scope}.#{klass.model_name.i18n_key}.attributes.#{attribute_name}.format",
- :"#{attributes_scope}.#{klass.model_name.i18n_key}.format",
- ]
- end
- end
-
- defaults.flatten!
- else
- defaults = []
- end
-
- defaults << :"errors.format"
- defaults << "%{attribute} %{message}"
-
- attr_name = attribute.tr(".", "_").humanize
- attr_name = @base.class.human_attribute_name(attribute, default: attr_name)
-
- I18n.t(defaults.shift,
- default: defaults,
- attribute: attr_name,
- message: message)
+ Error.full_message(attribute, message, @base.class)
end
# Translates an error message in its default scope
@@ -478,82 +500,112 @@ module ActiveModel
# * <tt>errors.attributes.title.blank</tt>
# * <tt>errors.messages.blank</tt>
def generate_message(attribute, type = :invalid, options = {})
- type = options.delete(:message) if options[:message].is_a?(Symbol)
- value = (attribute != :base ? @base.send(:read_attribute_for_validation, attribute) : nil)
-
- options = {
- model: @base.model_name.human,
- attribute: @base.class.human_attribute_name(attribute),
- value: value,
- object: @base
- }.merge!(options)
-
- if @base.class.respond_to?(:i18n_scope)
- i18n_scope = @base.class.i18n_scope.to_s
- defaults = @base.class.lookup_ancestors.flat_map do |klass|
- [ :"#{i18n_scope}.errors.models.#{klass.model_name.i18n_key}.attributes.#{attribute}.#{type}",
- :"#{i18n_scope}.errors.models.#{klass.model_name.i18n_key}.#{type}" ]
+ Error.generate_message(attribute, type, @base, options)
+ end
+
+ def marshal_load(array) # :nodoc:
+ # Rails 5
+ @errors = []
+ @base = array[0]
+ add_from_legacy_details_hash(array[2])
+ end
+
+ def init_with(coder) # :nodoc:
+ data = coder.map
+
+ data.each { |k, v|
+ next if LEGACY_ATTRIBUTES.include?(k.to_sym)
+ instance_variable_set(:"@#{k}", v)
+ }
+
+ @errors ||= []
+
+ # Legacy support Rails 5.x details hash
+ add_from_legacy_details_hash(data["details"]) if data.key?("details")
+ end
+
+ private
+ def normalize_arguments(attribute, type, **options)
+ # Evaluate proc first
+ if type.respond_to?(:call)
+ type = type.call(@base, options)
end
- defaults << :"#{i18n_scope}.errors.messages.#{type}"
- catch(:exception) do
- translation = I18n.translate(defaults.first, options.merge(default: defaults.drop(1), throw: true))
- return translation unless translation.nil?
- end unless options[:message]
- else
- defaults = []
+ [attribute.to_sym, type, options]
end
- defaults << :"errors.attributes.#{attribute}.#{type}"
- defaults << :"errors.messages.#{type}"
+ def add_from_legacy_details_hash(details)
+ details.each { |attribute, errors|
+ errors.each { |error|
+ type = error.delete(:error)
+ add(attribute, type, error)
+ }
+ }
+ end
- key = defaults.shift
- defaults = options.delete(:message) if options[:message]
- options[:default] = defaults
+ def deprecation_removal_warning(method_name)
+ ActiveSupport::Deprecation.warn("ActiveModel::Errors##{method_name} is deprecated and will be removed in Rails 6.2")
+ end
- I18n.translate(key, options)
- end
+ def deprecation_rename_warning(old_method_name, new_method_name)
+ ActiveSupport::Deprecation.warn("ActiveModel::Errors##{old_method_name} is deprecated. Please call ##{new_method_name} instead.")
+ end
+ end
- def marshal_dump # :nodoc:
- [@base, without_default_proc(@messages), without_default_proc(@details)]
+ class DeprecationHandlingMessageHash < SimpleDelegator
+ def initialize(errors)
+ @errors = errors
+ super(prepare_content)
end
- def marshal_load(array) # :nodoc:
- @base, @messages, @details = array
- apply_default_array(@messages)
- apply_default_array(@details)
- end
+ def []=(attribute, value)
+ ActiveSupport::Deprecation.warn("Calling `[]=` to an ActiveModel::Errors is deprecated. Please call `ActiveModel::Errors#add` instead.")
- def init_with(coder) # :nodoc:
- coder.map.each { |k, v| instance_variable_set(:"@#{k}", v) }
- @details ||= {}
- apply_default_array(@messages)
- apply_default_array(@details)
+ @errors.delete(attribute)
+ Array(value).each do |message|
+ @errors.add(attribute, message)
+ end
+
+ __setobj__ prepare_content
end
- private
- def normalize_message(attribute, message, options)
- case message
- when Symbol
- generate_message(attribute, message, options.except(*CALLBACKS_OPTIONS))
- else
- message
+ private
+ def prepare_content
+ content = @errors.to_hash
+ content.each do |attribute, value|
+ content[attribute] = DeprecationHandlingMessageArray.new(value, @errors, attribute)
+ end
+ content.default_proc = proc do |hash, attribute|
+ hash = hash.dup
+ hash[attribute] = DeprecationHandlingMessageArray.new([], @errors, attribute)
+ __setobj__ hash.freeze
+ hash[attribute]
+ end
+ content.freeze
end
- end
+ end
- def normalize_detail(message, options)
- { error: message }.merge(options.except(*CALLBACKS_OPTIONS + MESSAGE_OPTIONS))
+ class DeprecationHandlingMessageArray < SimpleDelegator
+ def initialize(content, errors, attribute)
+ @errors = errors
+ @attribute = attribute
+ super(content.freeze)
end
- def without_default_proc(hash)
- hash.dup.tap do |new_h|
- new_h.default_proc = nil
- end
+ def <<(message)
+ ActiveSupport::Deprecation.warn("Calling `<<` to an ActiveModel::Errors message array in order to add an error is deprecated. Please call `ActiveModel::Errors#add` instead.")
+
+ @errors.add(@attribute, message)
+ __setobj__ @errors.messages_for(@attribute)
+ self
end
+ end
- def apply_default_array(hash)
- hash.default_proc = proc { |h, key| h[key] = [] }
- hash
+ class DeprecationHandlingDetailsHash < SimpleDelegator
+ def initialize(details)
+ details.default = []
+ details.freeze
+ super(details)
end
end
diff --git a/activemodel/lib/active_model/gem_version.rb b/activemodel/lib/active_model/gem_version.rb
index 1626aac468..5475c1eda7 100644
--- a/activemodel/lib/active_model/gem_version.rb
+++ b/activemodel/lib/active_model/gem_version.rb
@@ -8,9 +8,9 @@ module ActiveModel
module VERSION
MAJOR = 6
- MINOR = 0
+ MINOR = 1
TINY = 0
- PRE = "beta3"
+ PRE = "alpha"
STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
end
diff --git a/activemodel/lib/active_model/lint.rb b/activemodel/lib/active_model/lint.rb
index b7ceabb59a..f9bfed95f1 100644
--- a/activemodel/lib/active_model/lint.rb
+++ b/activemodel/lib/active_model/lint.rb
@@ -101,7 +101,7 @@ module ActiveModel
# locale. If no error is present, the method should return an empty array.
def test_errors_aref
assert_respond_to model, :errors
- assert model.errors[:hello].is_a?(Array), "errors#[] should return an Array"
+ assert_equal [], model.errors[:hello], "errors#[] should return an empty Array"
end
private
diff --git a/activemodel/lib/active_model/naming.rb b/activemodel/lib/active_model/naming.rb
index bf23fa3c05..6a02d5dbf7 100644
--- a/activemodel/lib/active_model/naming.rb
+++ b/activemodel/lib/active_model/naming.rb
@@ -207,7 +207,6 @@ module ActiveModel
end
private
-
def _singularize(string)
ActiveSupport::Inflector.underscore(string).tr("/", "_")
end
diff --git a/activemodel/lib/active_model/nested_error.rb b/activemodel/lib/active_model/nested_error.rb
new file mode 100644
index 0000000000..93348c7771
--- /dev/null
+++ b/activemodel/lib/active_model/nested_error.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+require "active_model/error"
+require "forwardable"
+
+module ActiveModel
+ # Represents one single error
+ # @!attribute [r] base
+ # @return [ActiveModel::Base] the object which the error belongs to
+ # @!attribute [r] attribute
+ # @return [Symbol] attribute of the object which the error belongs to
+ # @!attribute [r] type
+ # @return [Symbol] error's type
+ # @!attribute [r] options
+ # @return [Hash] additional options
+ # @!attribute [r] inner_error
+ # @return [Error] inner error
+ class NestedError < Error
+ def initialize(base, inner_error, override_options = {})
+ @base = base
+ @inner_error = inner_error
+ @attribute = override_options.fetch(:attribute) { inner_error.attribute }
+ @type = override_options.fetch(:type) { inner_error.type }
+ @raw_type = inner_error.raw_type
+ @options = inner_error.options
+ end
+
+ attr_reader :inner_error
+
+ extend Forwardable
+ def_delegators :@inner_error, :message
+ end
+end
diff --git a/activemodel/lib/active_model/railtie.rb b/activemodel/lib/active_model/railtie.rb
index eb7901c7e9..65e20b9791 100644
--- a/activemodel/lib/active_model/railtie.rb
+++ b/activemodel/lib/active_model/railtie.rb
@@ -14,7 +14,7 @@ module ActiveModel
end
initializer "active_model.i18n_customize_full_message" do
- ActiveModel::Errors.i18n_customize_full_message = config.active_model.delete(:i18n_customize_full_message) || false
+ ActiveModel::Error.i18n_customize_full_message = config.active_model.delete(:i18n_customize_full_message) || false
end
end
end
diff --git a/activemodel/lib/active_model/serialization.rb b/activemodel/lib/active_model/serialization.rb
index c4b7b32291..168dfa0dd2 100644
--- a/activemodel/lib/active_model/serialization.rb
+++ b/activemodel/lib/active_model/serialization.rb
@@ -150,7 +150,6 @@ module ActiveModel
end
private
-
# Hook method defining how an attribute value should be retrieved for
# serialization. By default this is assumed to be an instance named after
# the attribute. Override this method in subclasses should you need to
diff --git a/activemodel/lib/active_model/serializers/json.rb b/activemodel/lib/active_model/serializers/json.rb
index f77fb98c32..09dcae889b 100644
--- a/activemodel/lib/active_model/serializers/json.rb
+++ b/activemodel/lib/active_model/serializers/json.rb
@@ -42,6 +42,13 @@ module ActiveModel
# # => { "user" => { "id" => 1, "name" => "Konata Izumi", "age" => 16,
# # "created_at" => "2006-08-01T17:27:13.000Z", "awesome" => true } }
#
+ # If you prefer, <tt>:root</tt> may also be set to a custom string key instead as in:
+ #
+ # user = User.find(1)
+ # user.as_json(root: "author")
+ # # => { "author" => { "id" => 1, "name" => "Konata Izumi", "age" => 16,
+ # # "created_at" => "2006-08-01T17:27:13.000Z", "awesome" => true } }
+ #
# Without any +options+, the returned Hash will include all the model's
# attributes.
#
diff --git a/activemodel/lib/active_model/type/big_integer.rb b/activemodel/lib/active_model/type/big_integer.rb
index 89e43bcc5f..b2c3ee50aa 100644
--- a/activemodel/lib/active_model/type/big_integer.rb
+++ b/activemodel/lib/active_model/type/big_integer.rb
@@ -6,7 +6,6 @@ module ActiveModel
module Type
class BigInteger < Integer # :nodoc:
private
-
def max_value
::Float::INFINITY
end
diff --git a/activemodel/lib/active_model/type/boolean.rb b/activemodel/lib/active_model/type/boolean.rb
index e64d2c793c..1214e9319b 100644
--- a/activemodel/lib/active_model/type/boolean.rb
+++ b/activemodel/lib/active_model/type/boolean.rb
@@ -34,7 +34,6 @@ module ActiveModel
end
private
-
def cast_value(value)
if value == ""
nil
diff --git a/activemodel/lib/active_model/type/date.rb b/activemodel/lib/active_model/type/date.rb
index c5fe926039..0e96d2c8a4 100644
--- a/activemodel/lib/active_model/type/date.rb
+++ b/activemodel/lib/active_model/type/date.rb
@@ -15,7 +15,6 @@ module ActiveModel
end
private
-
def cast_value(value)
if value.is_a?(::String)
return if value.empty?
diff --git a/activemodel/lib/active_model/type/date_time.rb b/activemodel/lib/active_model/type/date_time.rb
index 133410e821..ba705be9b2 100644
--- a/activemodel/lib/active_model/type/date_time.rb
+++ b/activemodel/lib/active_model/type/date_time.rb
@@ -14,7 +14,6 @@ module ActiveModel
end
private
-
def cast_value(value)
return apply_seconds_precision(value) unless value.is_a?(::String)
return if value.empty?
diff --git a/activemodel/lib/active_model/type/decimal.rb b/activemodel/lib/active_model/type/decimal.rb
index e8ee18c00e..6aa51ff2ac 100644
--- a/activemodel/lib/active_model/type/decimal.rb
+++ b/activemodel/lib/active_model/type/decimal.rb
@@ -17,7 +17,6 @@ module ActiveModel
end
private
-
def cast_value(value)
casted_value = \
case value
diff --git a/activemodel/lib/active_model/type/float.rb b/activemodel/lib/active_model/type/float.rb
index ea1987df7c..36c7a5103c 100644
--- a/activemodel/lib/active_model/type/float.rb
+++ b/activemodel/lib/active_model/type/float.rb
@@ -19,7 +19,6 @@ module ActiveModel
end
private
-
def cast_value(value)
case value
when ::Float then value
diff --git a/activemodel/lib/active_model/type/helpers/numeric.rb b/activemodel/lib/active_model/type/helpers/numeric.rb
index 1d8171e25b..074316b559 100644
--- a/activemodel/lib/active_model/type/helpers/numeric.rb
+++ b/activemodel/lib/active_model/type/helpers/numeric.rb
@@ -24,7 +24,6 @@ module ActiveModel
end
private
-
def number_to_non_number?(old_value, new_value_before_type_cast)
old_value != nil && non_numeric_string?(new_value_before_type_cast.to_s)
end
diff --git a/activemodel/lib/active_model/type/helpers/time_value.rb b/activemodel/lib/active_model/type/helpers/time_value.rb
index 735b9a75a6..075e906034 100644
--- a/activemodel/lib/active_model/type/helpers/time_value.rb
+++ b/activemodel/lib/active_model/type/helpers/time_value.rb
@@ -11,10 +11,10 @@ module ActiveModel
value = apply_seconds_precision(value)
if value.acts_like?(:time)
- zone_conversion_method = is_utc? ? :getutc : :getlocal
-
- if value.respond_to?(zone_conversion_method)
- value = value.send(zone_conversion_method)
+ if is_utc?
+ value = value.getutc if value.respond_to?(:getutc) && !value.utc?
+ else
+ value = value.getlocal if value.respond_to?(:getlocal)
end
end
@@ -22,10 +22,17 @@ module ActiveModel
end
def apply_seconds_precision(value)
- return value unless precision && value.respond_to?(:usec)
- number_of_insignificant_digits = 6 - precision
+ return value unless precision && value.respond_to?(:nsec)
+
+ number_of_insignificant_digits = 9 - precision
round_power = 10**number_of_insignificant_digits
- value.change(usec: value.usec - value.usec % round_power)
+ rounded_off_nsec = value.nsec % round_power
+
+ if rounded_off_nsec > 0
+ value.change(nsec: value.nsec - rounded_off_nsec)
+ else
+ value
+ end
end
def type_cast_for_schema(value)
@@ -37,7 +44,6 @@ module ActiveModel
end
private
-
def new_time(year, mon, mday, hour, min, sec, microsec, offset = nil)
# Treat 0000-00-00 00:00:00 as nil.
return if year.nil? || (year == 0 && mon == 0 && mday == 0)
diff --git a/activemodel/lib/active_model/type/immutable_string.rb b/activemodel/lib/active_model/type/immutable_string.rb
index 826bd7038f..18e12c54d1 100644
--- a/activemodel/lib/active_model/type/immutable_string.rb
+++ b/activemodel/lib/active_model/type/immutable_string.rb
@@ -17,7 +17,6 @@ module ActiveModel
end
private
-
def cast_value(value)
result = \
case value
diff --git a/activemodel/lib/active_model/type/integer.rb b/activemodel/lib/active_model/type/integer.rb
index 1e1061ff60..e9bbdf4b7b 100644
--- a/activemodel/lib/active_model/type/integer.rb
+++ b/activemodel/lib/active_model/type/integer.rb
@@ -18,6 +18,11 @@ module ActiveModel
:integer
end
+ def deserialize(value)
+ return if value.blank?
+ value.to_i
+ end
+
def serialize(value)
return if value.is_a?(::String) && non_numeric_string?(value)
ensure_in_range(super)
diff --git a/activemodel/lib/active_model/type/string.rb b/activemodel/lib/active_model/type/string.rb
index a9c9bfadb6..0d9f4a63b4 100644
--- a/activemodel/lib/active_model/type/string.rb
+++ b/activemodel/lib/active_model/type/string.rb
@@ -12,7 +12,6 @@ module ActiveModel
end
private
-
def cast_value(value)
case value
when ::String then ::String.new(value)
diff --git a/activemodel/lib/active_model/type/time.rb b/activemodel/lib/active_model/type/time.rb
index 61847a4ce7..f230bd4257 100644
--- a/activemodel/lib/active_model/type/time.rb
+++ b/activemodel/lib/active_model/type/time.rb
@@ -29,7 +29,6 @@ module ActiveModel
end
private
-
def cast_value(value)
return apply_seconds_precision(value) unless value.is_a?(::String)
return if value.empty?
diff --git a/activemodel/lib/active_model/type/value.rb b/activemodel/lib/active_model/type/value.rb
index b6914dd63c..788ded3e96 100644
--- a/activemodel/lib/active_model/type/value.rb
+++ b/activemodel/lib/active_model/type/value.rb
@@ -110,11 +110,10 @@ module ActiveModel
[self.class, precision, scale, limit].hash
end
- def assert_valid_value(*)
+ def assert_valid_value(_)
end
private
-
# Convenience method for types which do not need separate type casting
# behavior for user and database inputs. Called by Value#cast for
# values except +nil+.
diff --git a/activemodel/lib/active_model/validations.rb b/activemodel/lib/active_model/validations.rb
index f18f9a601a..4a6b464131 100644
--- a/activemodel/lib/active_model/validations.rb
+++ b/activemodel/lib/active_model/validations.rb
@@ -402,7 +402,6 @@ module ActiveModel
alias :read_attribute_for_validation :send
private
-
def run_validations!
_run_validate_callbacks
errors.empty?
diff --git a/activemodel/lib/active_model/validations/acceptance.rb b/activemodel/lib/active_model/validations/acceptance.rb
index 6fd54270f2..1b96575a10 100644
--- a/activemodel/lib/active_model/validations/acceptance.rb
+++ b/activemodel/lib/active_model/validations/acceptance.rb
@@ -15,7 +15,6 @@ module ActiveModel
end
private
-
def setup!(klass)
klass.include(LazilyDefineAttributes.new(AttributeDefinition.new(attributes)))
end
diff --git a/activemodel/lib/active_model/validations/callbacks.rb b/activemodel/lib/active_model/validations/callbacks.rb
index 887d31ae2a..7178ba99e9 100644
--- a/activemodel/lib/active_model/validations/callbacks.rb
+++ b/activemodel/lib/active_model/validations/callbacks.rb
@@ -112,7 +112,6 @@ module ActiveModel
end
private
-
# Overwrite run validations to include callbacks.
def run_validations!
_run_validation_callbacks { super }
diff --git a/activemodel/lib/active_model/validations/clusivity.rb b/activemodel/lib/active_model/validations/clusivity.rb
index bafb8e2106..fb9f48301c 100644
--- a/activemodel/lib/active_model/validations/clusivity.rb
+++ b/activemodel/lib/active_model/validations/clusivity.rb
@@ -15,7 +15,6 @@ module ActiveModel
end
private
-
def include?(record, value)
members = if delimiter.respond_to?(:call)
delimiter.call(record)
diff --git a/activemodel/lib/active_model/validations/format.rb b/activemodel/lib/active_model/validations/format.rb
index 7c3f091473..bea57415b0 100644
--- a/activemodel/lib/active_model/validations/format.rb
+++ b/activemodel/lib/active_model/validations/format.rb
@@ -23,7 +23,6 @@ module ActiveModel
end
private
-
def option_call(record, name)
option = options[name]
option.respond_to?(:call) ? option.call(record) : option
diff --git a/activemodel/lib/active_model/validations/numericality.rb b/activemodel/lib/active_model/validations/numericality.rb
index 51e224d5cd..e7be668e9d 100644
--- a/activemodel/lib/active_model/validations/numericality.rb
+++ b/activemodel/lib/active_model/validations/numericality.rb
@@ -79,7 +79,6 @@ module ActiveModel
end
private
-
def is_number?(raw_value)
!parse_as_number(raw_value).nil?
rescue ArgumentError, TypeError
diff --git a/activemodel/lib/active_model/validations/validates.rb b/activemodel/lib/active_model/validations/validates.rb
index 21c4ce0dfe..97612d474d 100644
--- a/activemodel/lib/active_model/validations/validates.rb
+++ b/activemodel/lib/active_model/validations/validates.rb
@@ -150,7 +150,6 @@ module ActiveModel
end
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
diff --git a/activemodel/lib/active_model/validator.rb b/activemodel/lib/active_model/validator.rb
index 94d53b8dd1..3ba6acea15 100644
--- a/activemodel/lib/active_model/validator.rb
+++ b/activemodel/lib/active_model/validator.rb
@@ -175,7 +175,6 @@ module ActiveModel
end
private
-
def validate_each(record, attribute, value)
@block.call(record, attribute, value)
end
diff --git a/activemodel/test/cases/attribute_methods_test.rb b/activemodel/test/cases/attribute_methods_test.rb
index 4e228032c3..e3b3b15f25 100644
--- a/activemodel/test/cases/attribute_methods_test.rb
+++ b/activemodel/test/cases/attribute_methods_test.rb
@@ -40,7 +40,6 @@ private
end
protected
-
def protected_method
"O_o O_o"
end
diff --git a/activemodel/test/cases/attributes_test.rb b/activemodel/test/cases/attributes_test.rb
index 5483fb101d..af0ddcb92f 100644
--- a/activemodel/test/cases/attributes_test.rb
+++ b/activemodel/test/cases/attributes_test.rb
@@ -67,6 +67,20 @@ module ActiveModel
assert_equal expected_attributes, data.attributes
end
+ test "reading attribute names" do
+ names = [
+ "integer_field",
+ "string_field",
+ "decimal_field",
+ "string_with_default",
+ "date_field",
+ "boolean_field"
+ ]
+
+ assert_equal names, ModelForAttributesTest.attribute_names
+ assert_equal names, ModelForAttributesTest.new.attribute_names
+ end
+
test "nonexistent attribute" do
assert_raise ActiveModel::UnknownAttributeError do
ModelForAttributesTest.new(nonexistent: "nonexistent")
diff --git a/activemodel/test/cases/error_test.rb b/activemodel/test/cases/error_test.rb
new file mode 100644
index 0000000000..d74321fee5
--- /dev/null
+++ b/activemodel/test/cases/error_test.rb
@@ -0,0 +1,200 @@
+# frozen_string_literal: true
+
+require "cases/helper"
+require "active_model/error"
+
+class ErrorTest < ActiveModel::TestCase
+ class Person
+ extend ActiveModel::Naming
+ def initialize
+ @errors = ActiveModel::Errors.new(self)
+ end
+
+ attr_accessor :name, :age
+ attr_reader :errors
+
+ def read_attribute_for_validation(attr)
+ send(attr)
+ end
+
+ def self.human_attribute_name(attr, options = {})
+ attr
+ end
+
+ def self.lookup_ancestors
+ [self]
+ end
+ end
+
+ def test_initialize
+ base = Person.new
+ error = ActiveModel::Error.new(base, :name, :too_long, foo: :bar)
+ assert_equal base, error.base
+ assert_equal :name, error.attribute
+ assert_equal :too_long, error.type
+ assert_equal({ foo: :bar }, error.options)
+ end
+
+ test "initialize without type" do
+ error = ActiveModel::Error.new(Person.new, :name)
+ assert_equal :invalid, error.type
+ assert_equal({}, error.options)
+ end
+
+ test "initialize without type but with options" do
+ options = { message: "bar" }
+ error = ActiveModel::Error.new(Person.new, :name, options)
+ assert_equal(options, error.options)
+ end
+
+ # match?
+
+ test "match? handles mixed condition" do
+ subject = ActiveModel::Error.new(Person.new, :mineral, :not_enough, count: 2)
+ assert_not subject.match?(:mineral, :too_coarse)
+ assert subject.match?(:mineral, :not_enough)
+ assert subject.match?(:mineral, :not_enough, count: 2)
+ assert_not subject.match?(:mineral, :not_enough, count: 1)
+ end
+
+ test "match? handles attribute match" do
+ subject = ActiveModel::Error.new(Person.new, :mineral, :not_enough, count: 2)
+ assert_not subject.match?(:foo)
+ assert subject.match?(:mineral)
+ end
+
+ test "match? handles error type match" do
+ subject = ActiveModel::Error.new(Person.new, :mineral, :not_enough, count: 2)
+ assert_not subject.match?(:mineral, :too_coarse)
+ assert subject.match?(:mineral, :not_enough)
+ end
+
+ test "match? handles extra options match" do
+ subject = ActiveModel::Error.new(Person.new, :mineral, :not_enough, count: 2)
+ assert_not subject.match?(:mineral, :not_enough, count: 1)
+ assert subject.match?(:mineral, :not_enough, count: 2)
+ end
+
+ # message
+
+ test "message with type as a symbol" do
+ error = ActiveModel::Error.new(Person.new, :name, :blank)
+ assert_equal "can't be blank", error.message
+ end
+
+ test "message with custom interpolation" do
+ subject = ActiveModel::Error.new(Person.new, :name, :inclusion, message: "custom message %{value}", value: "name")
+ assert_equal "custom message name", subject.message
+ end
+
+ test "message returns plural interpolation" do
+ subject = ActiveModel::Error.new(Person.new, :name, :too_long, count: 10)
+ assert_equal "is too long (maximum is 10 characters)", subject.message
+ end
+
+ test "message returns singular interpolation" do
+ subject = ActiveModel::Error.new(Person.new, :name, :too_long, count: 1)
+ assert_equal "is too long (maximum is 1 character)", subject.message
+ end
+
+ test "message returns count interpolation" do
+ subject = ActiveModel::Error.new(Person.new, :name, :too_long, message: "custom message %{count}", count: 10)
+ assert_equal "custom message 10", subject.message
+ end
+
+ test "message handles lambda in messages and option values, and i18n interpolation" do
+ subject = ActiveModel::Error.new(Person.new, :name, :invalid,
+ foo: "foo",
+ bar: "bar",
+ baz: Proc.new { "baz" },
+ message: Proc.new { |model, options|
+ "%{attribute} %{foo} #{options[:bar]} %{baz}"
+ }
+ )
+ assert_equal "name foo bar baz", subject.message
+ end
+
+ test "generate_message works without i18n_scope" do
+ person = Person.new
+ error = ActiveModel::Error.new(person, :name, :blank)
+ assert_not_respond_to Person, :i18n_scope
+ assert_nothing_raised {
+ error.message
+ }
+ end
+
+ test "message with type as custom message" do
+ error = ActiveModel::Error.new(Person.new, :name, message: "cannot be blank")
+ assert_equal "cannot be blank", error.message
+ end
+
+ test "message with options[:message] as custom message" do
+ error = ActiveModel::Error.new(Person.new, :name, :blank, message: "cannot be blank")
+ assert_equal "cannot be blank", error.message
+ end
+
+ test "message renders lazily using current locale" do
+ error = nil
+
+ I18n.backend.store_translations(:pl, errors: { messages: { invalid: "jest nieprawidłowe" } })
+
+ I18n.with_locale(:en) { error = ActiveModel::Error.new(Person.new, :name, :invalid) }
+ I18n.with_locale(:pl) {
+ assert_equal "jest nieprawidłowe", error.message
+ }
+ end
+
+ test "message uses current locale" do
+ I18n.backend.store_translations(:en, errors: { messages: { inadequate: "Inadequate %{attribute} found!" } })
+ error = ActiveModel::Error.new(Person.new, :name, :inadequate)
+ assert_equal "Inadequate name found!", error.message
+ end
+
+ # full_message
+
+ test "full_message returns the given message when attribute is :base" do
+ error = ActiveModel::Error.new(Person.new, :base, message: "press the button")
+ assert_equal "press the button", error.full_message
+ end
+
+ test "full_message returns the given message with the attribute name included" do
+ error = ActiveModel::Error.new(Person.new, :name, :blank)
+ assert_equal "name can't be blank", error.full_message
+ end
+
+ test "full_message uses default format" do
+ error = ActiveModel::Error.new(Person.new, :name, message: "can't be blank")
+
+ # Use a locale without errors.format
+ I18n.with_locale(:unknown) {
+ assert_equal "name can't be blank", error.full_message
+ }
+ end
+
+ test "equality by base attribute, type and options" do
+ person = Person.new
+
+ e1 = ActiveModel::Error.new(person, :name, foo: :bar)
+ e2 = ActiveModel::Error.new(person, :name, foo: :bar)
+ e2.instance_variable_set(:@_humanized_attribute, "Name")
+
+ assert_equal(e1, e2)
+ end
+
+ test "inequality" do
+ person = Person.new
+ error = ActiveModel::Error.new(person, :name, foo: :bar)
+
+ assert error != ActiveModel::Error.new(person, :name, foo: :baz)
+ assert error != ActiveModel::Error.new(person, :name)
+ assert error != ActiveModel::Error.new(person, :title, foo: :bar)
+ assert error != ActiveModel::Error.new(Person.new, :name, foo: :bar)
+ end
+
+ test "comparing against different class would not raise error" do
+ person = Person.new
+ error = ActiveModel::Error.new(person, :name, foo: :bar)
+
+ assert error != person
+ end
+end
diff --git a/activemodel/test/cases/errors_test.rb b/activemodel/test/cases/errors_test.rb
index 947f9bf99b..a6cd1da717 100644
--- a/activemodel/test/cases/errors_test.rb
+++ b/activemodel/test/cases/errors_test.rb
@@ -10,7 +10,7 @@ class ErrorsTest < ActiveModel::TestCase
@errors = ActiveModel::Errors.new(self)
end
- attr_accessor :name, :age
+ attr_accessor :name, :age, :gender, :city
attr_reader :errors
def validate!
@@ -31,48 +31,66 @@ class ErrorsTest < ActiveModel::TestCase
end
def test_delete
- errors = ActiveModel::Errors.new(self)
- errors[:foo] << "omg"
- errors.delete("foo")
- assert_empty errors[:foo]
+ errors = ActiveModel::Errors.new(Person.new)
+ errors.add(:name, :blank)
+ errors.delete("name")
+ assert_empty errors[:name]
end
def test_include?
- errors = ActiveModel::Errors.new(self)
- errors[:foo] << "omg"
+ errors = ActiveModel::Errors.new(Person.new)
+ assert_deprecated { errors[:foo] << "omg" }
assert_includes errors, :foo, "errors should include :foo"
assert_includes errors, "foo", "errors should include 'foo' as :foo"
end
+ def test_each_when_arity_is_negative
+ errors = ActiveModel::Errors.new(Person.new)
+ errors.add(:name, :blank)
+ errors.add(:gender, :blank)
+
+ assert_equal([:name, :gender], errors.map(&:attribute))
+ end
+
+ def test_any?
+ errors = ActiveModel::Errors.new(Person.new)
+ errors.add(:name)
+ assert_not_deprecated {
+ assert errors.any?, "any? should return true"
+ }
+ assert_not_deprecated {
+ assert errors.any? { |_| true }, "any? should return true"
+ }
+ end
+
def test_dup
- errors = ActiveModel::Errors.new(self)
- errors[:foo] << "bar"
+ errors = ActiveModel::Errors.new(Person.new)
+ errors.add(:name)
errors_dup = errors.dup
- errors_dup[:bar] << "omg"
- assert_not_same errors_dup.messages, errors.messages
+ assert_not_same errors_dup.errors, errors.errors
end
def test_has_key?
- errors = ActiveModel::Errors.new(self)
- errors[:foo] << "omg"
+ errors = ActiveModel::Errors.new(Person.new)
+ errors.add(: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
- errors = ActiveModel::Errors.new(self)
+ errors = ActiveModel::Errors.new(Person.new)
assert_equal false, errors.has_key?(:name), "errors should not have key :name"
end
def test_key?
- errors = ActiveModel::Errors.new(self)
- errors[:foo] << "omg"
+ errors = ActiveModel::Errors.new(Person.new)
+ errors.add(: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
- errors = ActiveModel::Errors.new(self)
+ errors = ActiveModel::Errors.new(Person.new)
assert_equal false, errors.key?(:name), "errors should not have key :name"
end
@@ -86,42 +104,58 @@ class ErrorsTest < ActiveModel::TestCase
end
test "error access is indifferent" do
- errors = ActiveModel::Errors.new(self)
- errors[:foo] << "omg"
+ errors = ActiveModel::Errors.new(Person.new)
+ errors.add(:name, "omg")
- assert_equal ["omg"], errors["foo"]
+ assert_equal ["omg"], errors["name"]
end
test "values returns an array of messages" do
+ errors = ActiveModel::Errors.new(Person.new)
+ assert_deprecated { errors.messages[:foo] = "omg" }
+ assert_deprecated { errors.messages[:baz] = "zomg" }
+
+ assert_deprecated do
+ assert_equal ["omg", "zomg"], errors.values
+ end
+ end
+
+ test "[]= overrides values" do
errors = ActiveModel::Errors.new(self)
- errors.messages[:foo] = "omg"
- errors.messages[:baz] = "zomg"
+ assert_deprecated { errors.messages[:foo] = "omg" }
+ assert_deprecated { errors.messages[:foo] = "zomg" }
- assert_equal ["omg", "zomg"], errors.values
+ assert_equal ["zomg"], errors[:foo]
end
test "values returns an empty array after try to get a message only" do
- errors = ActiveModel::Errors.new(self)
+ errors = ActiveModel::Errors.new(Person.new)
errors.messages[:foo]
errors.messages[:baz]
- assert_equal [], errors.values
+ assert_deprecated do
+ assert_equal [], errors.values
+ end
end
test "keys returns the error keys" do
- errors = ActiveModel::Errors.new(self)
- errors.messages[:foo] << "omg"
- errors.messages[:baz] << "zomg"
+ errors = ActiveModel::Errors.new(Person.new)
+ assert_deprecated { errors.messages[:foo] << "omg" }
+ assert_deprecated { errors.messages[:baz] << "zomg" }
- assert_equal [:foo, :baz], errors.keys
+ assert_deprecated do
+ assert_equal [:foo, :baz], errors.keys
+ end
end
test "keys returns an empty array after try to get a message only" do
- errors = ActiveModel::Errors.new(self)
+ errors = ActiveModel::Errors.new(Person.new)
errors.messages[:foo]
errors.messages[:baz]
- assert_equal [], errors.keys
+ assert_deprecated do
+ assert_equal [], errors.keys
+ end
end
test "detecting whether there are errors with empty?, blank?, include?" do
@@ -146,32 +180,130 @@ class ErrorsTest < ActiveModel::TestCase
assert_equal ["cannot be nil"], person.errors[:name]
end
- test "add an error message on a specific attribute" do
+ test "add an error message on a specific attribute (deprecated)" do
person = Person.new
person.errors.add(:name, "cannot be blank")
assert_equal ["cannot be blank"], person.errors[:name]
end
- test "add an error message on a specific attribute with a defined type" do
+ test "add an error message on a specific attribute with a defined type (deprecated)" do
person = Person.new
person.errors.add(:name, :blank, message: "cannot be blank")
assert_equal ["cannot be blank"], person.errors[:name]
end
- test "add an error with a symbol" do
+ test "add an error with a symbol (deprecated)" do
person = Person.new
person.errors.add(:name, :blank)
message = person.errors.generate_message(:name, :blank)
assert_equal [message], person.errors[:name]
end
- test "add an error with a proc" do
+ test "add an error with a proc (deprecated)" do
person = Person.new
message = Proc.new { "cannot be blank" }
person.errors.add(:name, message)
assert_equal ["cannot be blank"], person.errors[:name]
end
+ test "add creates an error object and returns it" do
+ person = Person.new
+ error = person.errors.add(:name, :blank)
+
+ assert_equal :name, error.attribute
+ assert_equal :blank, error.type
+ assert_equal error, person.errors.objects.first
+ end
+
+ test "add, with type as symbol" do
+ person = Person.new
+ person.errors.add(:name, :blank)
+
+ assert_equal :blank, person.errors.objects.first.type
+ assert_equal ["can't be blank"], person.errors[:name]
+ end
+
+ test "add, with type as String" do
+ msg = "custom msg"
+
+ person = Person.new
+ person.errors.add(:name, msg)
+
+ assert_equal [msg], person.errors[:name]
+ end
+
+ test "add, with type as nil" do
+ person = Person.new
+ person.errors.add(:name)
+
+ assert_equal :invalid, person.errors.objects.first.type
+ assert_equal ["is invalid"], person.errors[:name]
+ end
+
+ test "add, with type as Proc, which evaluates to String" do
+ msg = "custom msg"
+ type = Proc.new { msg }
+
+ person = Person.new
+ person.errors.add(:name, type)
+
+ assert_equal [msg], person.errors[:name]
+ end
+
+ test "add, type being Proc, which evaluates to Symbol" do
+ type = Proc.new { :blank }
+
+ person = Person.new
+ person.errors.add(:name, type)
+
+ assert_equal :blank, person.errors.objects.first.type
+ assert_equal ["can't be blank"], person.errors[:name]
+ end
+
+ test "initialize options[:message] as Proc, which evaluates to String" do
+ msg = "custom msg"
+ type = Proc.new { msg }
+
+ person = Person.new
+ person.errors.add(:name, :blank, message: type)
+
+ assert_equal :blank, person.errors.objects.first.type
+ assert_equal [msg], person.errors[:name]
+ end
+
+ test "add, with options[:message] as Proc, which evaluates to String, where type is nil" do
+ msg = "custom msg"
+ type = Proc.new { msg }
+
+ person = Person.new
+ person.errors.add(:name, message: type)
+
+ assert_equal :invalid, person.errors.objects.first.type
+ assert_equal [msg], person.errors[:name]
+ end
+
+ test "added? when attribute was added through a collection" do
+ person = Person.new
+ person.errors.add(:"family_members.name", :too_long, count: 25)
+ assert person.errors.added?(:"family_members.name", :too_long, count: 25)
+ assert_not person.errors.added?(:"family_members.name", :too_long)
+ assert_not person.errors.added?(:"family_members.name", :too_long, name: "hello")
+ end
+
+ test "added? ignores callback option" do
+ person = Person.new
+
+ person.errors.add(:name, :too_long, if: -> { true })
+ assert person.errors.added?(:name, :too_long)
+ end
+
+ test "added? ignores message option" do
+ person = Person.new
+
+ person.errors.add(:name, :too_long, message: proc { "foo" })
+ assert person.errors.added?(:name, :too_long)
+ end
+
test "added? detects indifferent if a specific error was added to the object" do
person = Person.new
person.errors.add(:name, "cannot be blank")
@@ -342,6 +474,17 @@ class ErrorsTest < ActiveModel::TestCase
assert_equal ["name cannot be blank", "name cannot be nil"], person.errors.to_a
end
+ test "to_h is deprecated" do
+ person = Person.new
+ person.errors.add(:name, "cannot be blank")
+ person.errors.add(:name, "too long")
+
+ expected_deprecation = "ActiveModel::Errors#to_h is deprecated"
+ assert_deprecated(expected_deprecation) do
+ assert_equal({ name: "too long" }, person.errors.to_h)
+ end
+ end
+
test "to_hash returns the error messages hash" do
person = Person.new
person.errors.add(:name, "cannot be blank")
@@ -358,6 +501,27 @@ class ErrorsTest < ActiveModel::TestCase
assert_nil person.errors.as_json.default_proc
end
+ test "full_messages doesn't require the base object to respond to `:errors" do
+ model = Class.new do
+ def initialize
+ @errors = ActiveModel::Errors.new(self)
+ @errors.add(:name, "bar")
+ end
+
+ def self.human_attribute_name(attr, options = {})
+ "foo"
+ end
+
+ def call
+ error_wrapper = Struct.new(:model_errors)
+
+ error_wrapper.new(@errors)
+ end
+ end
+
+ assert_equal(["foo bar"], model.new.call.model_errors.full_messages)
+ end
+
test "full_messages creates a list of error messages with the attribute name included" do
person = Person.new
person.errors.add(:name, "cannot be blank")
@@ -437,6 +601,32 @@ class ErrorsTest < ActiveModel::TestCase
assert_equal({ name: [{ error: :invalid }] }, person.errors.details)
end
+ test "details retains original type as error" do
+ errors = ActiveModel::Errors.new(Person.new)
+ errors.add(:name, "cannot be nil")
+ errors.add("foo", "bar")
+ errors.add(:baz, nil)
+ errors.add(:age, :invalid, count: 3, message: "%{count} is too low")
+
+ assert_equal(
+ {
+ name: [{ error: "cannot be nil" }],
+ foo: [{ error: "bar" }],
+ baz: [{ error: nil }],
+ age: [{ error: :invalid, count: 3 }]
+ },
+ errors.details
+ )
+ end
+
+ test "group_by_attribute" do
+ person = Person.new
+ error = person.errors.add(:name, :invalid, message: "is bad")
+ hash = person.errors.group_by_attribute
+
+ assert_equal({ name: [error] }, hash)
+ end
+
test "dup duplicates details" do
errors = ActiveModel::Errors.new(Person.new)
errors.add(:name, :invalid)
@@ -445,11 +635,17 @@ class ErrorsTest < ActiveModel::TestCase
assert_not_equal errors_dup.details, errors.details
end
+ test "delete returns nil when no errors were deleted" do
+ errors = ActiveModel::Errors.new(Person.new)
+
+ assert_nil(errors.delete(:name))
+ end
+
test "delete removes details on given attribute" do
errors = ActiveModel::Errors.new(Person.new)
errors.add(:name, :invalid)
errors.delete(:name)
- assert_empty errors.details[:name]
+ assert_not errors.added?(:name)
end
test "delete returns the deleted messages" do
@@ -467,7 +663,7 @@ class ErrorsTest < ActiveModel::TestCase
assert_empty person.errors.details
end
- test "copy errors" do
+ test "copy errors (deprecated)" do
errors = ActiveModel::Errors.new(Person.new)
errors.add(:name, :invalid)
person = Person.new
@@ -477,7 +673,25 @@ class ErrorsTest < ActiveModel::TestCase
assert_equal [:name], person.errors.details.keys
end
- test "merge errors" do
+ test "details returns empty array when accessed with non-existent attribute" do
+ errors = ActiveModel::Errors.new(Person.new)
+
+ assert_equal [], errors.details[:foo]
+ end
+
+ test "copy errors" do
+ errors = ActiveModel::Errors.new(Person.new)
+ errors.add(:name, :invalid)
+ person = Person.new
+ person.errors.copy!(errors)
+
+ assert person.errors.added?(:name, :invalid)
+ person.errors.each do |error|
+ assert_same person, error.base
+ end
+ end
+
+ test "merge errors (deprecated)" do
errors = ActiveModel::Errors.new(Person.new)
errors.add(:name, :invalid)
@@ -489,6 +703,18 @@ class ErrorsTest < ActiveModel::TestCase
assert_equal({ name: [{ error: :blank }, { error: :invalid }] }, person.errors.details)
end
+ test "merge errors" do
+ errors = ActiveModel::Errors.new(Person.new)
+ errors.add(:name, :invalid)
+
+ person = Person.new
+ person.errors.add(:name, :blank)
+ person.errors.merge!(errors)
+
+ assert(person.errors.added?(:name, :invalid))
+ assert(person.errors.added?(:name, :blank))
+ end
+
test "slice! removes all errors except the given keys" do
person = Person.new
person.errors.add(:name, "cannot be nil")
@@ -496,9 +722,9 @@ class ErrorsTest < ActiveModel::TestCase
person.errors.add(:gender, "cannot be nil")
person.errors.add(:city, "cannot be nil")
- person.errors.slice!(:age, "gender")
+ assert_deprecated { person.errors.slice!(:age, "gender") }
- assert_equal [:age, :gender], person.errors.keys
+ assert_equal [:age, :gender], assert_deprecated { person.errors.keys }
end
test "slice! returns the deleted errors" do
@@ -508,7 +734,7 @@ class ErrorsTest < ActiveModel::TestCase
person.errors.add(:gender, "cannot be nil")
person.errors.add(:city, "cannot be nil")
- removed_errors = person.errors.slice!(:age, "gender")
+ removed_errors = assert_deprecated { person.errors.slice!(:age, "gender") }
assert_equal({ name: ["cannot be nil"], city: ["cannot be nil"] }, removed_errors)
end
@@ -518,10 +744,23 @@ class ErrorsTest < ActiveModel::TestCase
errors.add(:name, :invalid)
serialized = Marshal.load(Marshal.dump(errors))
+ assert_equal Person, serialized.instance_variable_get(:@base).class
assert_equal errors.messages, serialized.messages
assert_equal errors.details, serialized.details
end
+ test "errors are compatible with marshal dumped from Rails 5.x" do
+ # Derived from
+ # errors = ActiveModel::Errors.new(Person.new)
+ # errors.add(:name, :invalid)
+ dump = "\x04\bU:\x18ActiveModel::Errors[\bo:\x17ErrorsTest::Person\x06:\f@errorsU;\x00[\b@\a{\x00{\x00{\x06:\tname[\x06I\"\x0Fis invalid\x06:\x06ET{\x06;\b[\x06{\x06:\nerror:\finvalid"
+ serialized = Marshal.load(dump)
+
+ assert_equal Person, serialized.instance_variable_get(:@base).class
+ assert_equal({ name: ["is invalid"] }, serialized.messages)
+ assert_equal({ name: [{ error: :invalid }] }, serialized.details)
+ end
+
test "errors are backward compatible with the Rails 4.2 format" do
yaml = <<~CODE
--- !ruby/object:ActiveModel::Errors
@@ -541,4 +780,54 @@ class ErrorsTest < ActiveModel::TestCase
assert_equal({}, errors.messages)
assert_equal({}, errors.details)
end
+
+ test "errors are compatible with YAML dumped from Rails 5.x" do
+ yaml = <<~CODE
+ --- !ruby/object:ActiveModel::Errors
+ base: &1 !ruby/object:ErrorsTest::Person
+ errors: !ruby/object:ActiveModel::Errors
+ base: *1
+ messages: {}
+ details: {}
+ messages:
+ :name:
+ - is invalid
+ details:
+ :name:
+ - :error: :invalid
+ CODE
+
+ errors = YAML.load(yaml)
+ 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
+
+ test "errors are compatible with YAML dumped from Rails 6.x" do
+ yaml = <<~CODE
+ --- !ruby/object:ActiveModel::Errors
+ base: &1 !ruby/object:ErrorsTest::Person
+ errors: !ruby/object:ActiveModel::Errors
+ base: *1
+ errors: []
+ errors:
+ - !ruby/object:ActiveModel::Error
+ base: *1
+ attribute: :name
+ type: :invalid
+ raw_type: :invalid
+ options: {}
+ CODE
+
+ errors = YAML.load(yaml)
+ 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/nested_error_test.rb b/activemodel/test/cases/nested_error_test.rb
new file mode 100644
index 0000000000..6c2458ba83
--- /dev/null
+++ b/activemodel/test/cases/nested_error_test.rb
@@ -0,0 +1,54 @@
+# frozen_string_literal: true
+
+require "cases/helper"
+require "active_model/nested_error"
+require "models/topic"
+require "models/reply"
+
+class NestedErrorTest < ActiveModel::TestCase
+ def test_initialize
+ topic = Topic.new
+ inner_error = ActiveModel::Error.new(topic, :title, :not_enough, count: 2)
+ reply = Reply.new
+ error = ActiveModel::NestedError.new(reply, inner_error)
+
+ assert_equal reply, error.base
+ assert_equal inner_error.attribute, error.attribute
+ assert_equal inner_error.type, error.type
+ assert_equal(inner_error.options, error.options)
+ end
+
+ test "initialize with overriding attribute and type" do
+ topic = Topic.new
+ inner_error = ActiveModel::Error.new(topic, :title, :not_enough, count: 2)
+ reply = Reply.new
+ error = ActiveModel::NestedError.new(reply, inner_error, attribute: :parent, type: :foo)
+
+ assert_equal reply, error.base
+ assert_equal :parent, error.attribute
+ assert_equal :foo, error.type
+ assert_equal(inner_error.options, error.options)
+ end
+
+ def test_message
+ topic = Topic.new(author_name: "Bruce")
+ inner_error = ActiveModel::Error.new(topic, :title, :not_enough, message: Proc.new { |model, options|
+ "not good enough for #{model.author_name}"
+ })
+ reply = Reply.new(author_name: "Mark")
+ error = ActiveModel::NestedError.new(reply, inner_error)
+
+ assert_equal "not good enough for Bruce", error.message
+ end
+
+ def test_full_message
+ topic = Topic.new(author_name: "Bruce")
+ inner_error = ActiveModel::Error.new(topic, :title, :not_enough, message: Proc.new { |model, options|
+ "not good enough for #{model.author_name}"
+ })
+ reply = Reply.new(author_name: "Mark")
+ error = ActiveModel::NestedError.new(reply, inner_error)
+
+ assert_equal "Title not good enough for Bruce", error.full_message
+ end
+end
diff --git a/activemodel/test/cases/railtie_test.rb b/activemodel/test/cases/railtie_test.rb
index 95ee7cace3..f5ff1a3fd7 100644
--- a/activemodel/test/cases/railtie_test.rb
+++ b/activemodel/test/cases/railtie_test.rb
@@ -35,20 +35,20 @@ class RailtieTest < ActiveModel::TestCase
test "i18n customize full message defaults to false" do
@app.initialize!
- assert_equal false, ActiveModel::Errors.i18n_customize_full_message
+ assert_equal false, ActiveModel::Error.i18n_customize_full_message
end
test "i18n customize full message can be disabled" do
@app.config.active_model.i18n_customize_full_message = false
@app.initialize!
- assert_equal false, ActiveModel::Errors.i18n_customize_full_message
+ assert_equal false, ActiveModel::Error.i18n_customize_full_message
end
test "i18n customize full message can be enabled" do
@app.config.active_model.i18n_customize_full_message = true
@app.initialize!
- assert_equal true, ActiveModel::Errors.i18n_customize_full_message
+ assert_equal true, ActiveModel::Error.i18n_customize_full_message
end
end
diff --git a/activemodel/test/cases/type/date_time_test.rb b/activemodel/test/cases/type/date_time_test.rb
index 74b47d1b4d..4a63eee0cf 100644
--- a/activemodel/test/cases/type/date_time_test.rb
+++ b/activemodel/test/cases/type/date_time_test.rb
@@ -37,7 +37,6 @@ module ActiveModel
end
private
-
def with_timezone_config(default:)
old_zone_default = ::Time.zone_default
::Time.zone_default = ::Time.find_zone(default)
diff --git a/activemodel/test/cases/validations/acceptance_validation_test.rb b/activemodel/test/cases/validations/acceptance_validation_test.rb
index 72baf6e7a7..6bd3d292f8 100644
--- a/activemodel/test/cases/validations/acceptance_validation_test.rb
+++ b/activemodel/test/cases/validations/acceptance_validation_test.rb
@@ -92,7 +92,6 @@ class AcceptanceValidationTest < ActiveModel::TestCase
end
private
-
# Acceptance validator includes anonymous module into class, which cannot
# be cleared, so to avoid multiple inclusions we use a named subclass which
# we can remove in teardown.
diff --git a/activemodel/test/cases/validations/i18n_validation_test.rb b/activemodel/test/cases/validations/i18n_validation_test.rb
index 35bb918f26..c81649f493 100644
--- a/activemodel/test/cases/validations/i18n_validation_test.rb
+++ b/activemodel/test/cases/validations/i18n_validation_test.rb
@@ -13,8 +13,8 @@ class I18nValidationTest < ActiveModel::TestCase
I18n.backend = I18n::Backend::Simple.new
I18n.backend.store_translations("en", errors: { messages: { custom: nil } })
- @original_i18n_customize_full_message = ActiveModel::Errors.i18n_customize_full_message
- ActiveModel::Errors.i18n_customize_full_message = true
+ @original_i18n_customize_full_message = ActiveModel::Error.i18n_customize_full_message
+ ActiveModel::Error.i18n_customize_full_message = true
end
def teardown
@@ -24,7 +24,7 @@ class I18nValidationTest < ActiveModel::TestCase
I18n.load_path.replace @old_load_path
I18n.backend = @old_backend
I18n.backend.reload!
- ActiveModel::Errors.i18n_customize_full_message = @original_i18n_customize_full_message
+ ActiveModel::Error.i18n_customize_full_message = @original_i18n_customize_full_message
end
def test_full_message_encoding
@@ -49,7 +49,7 @@ class I18nValidationTest < ActiveModel::TestCase
end
def test_errors_full_messages_doesnt_use_attribute_format_without_config
- ActiveModel::Errors.i18n_customize_full_message = false
+ ActiveModel::Error.i18n_customize_full_message = false
I18n.backend.store_translations("en", activemodel: {
errors: { models: { person: { attributes: { name: { format: "%{message}" } } } } } })
@@ -59,8 +59,21 @@ class I18nValidationTest < ActiveModel::TestCase
assert_equal "Name test cannot be blank", person.errors.full_message(:name_test, "cannot be blank")
end
+ def test_errors_full_messages_on_nested_error_uses_attribute_format
+ ActiveModel::Error.i18n_customize_full_message = true
+ I18n.backend.store_translations("en", activemodel: {
+ errors: { models: { person: { attributes: { gender: "Gender" } } } },
+ attributes: { "person/contacts": { gender: "Gender" } }
+ })
+
+ person = person_class.new
+ error = ActiveModel::Error.new(person, :gender, "can't be blank")
+ person.errors.import(error, attribute: "person[0].contacts.gender")
+ assert_equal ["Gender can't be blank"], person.errors.full_messages
+ end
+
def test_errors_full_messages_uses_attribute_format
- ActiveModel::Errors.i18n_customize_full_message = true
+ ActiveModel::Error.i18n_customize_full_message = true
I18n.backend.store_translations("en", activemodel: {
errors: { models: { person: { attributes: { name: { format: "%{message}" } } } } } })
@@ -71,7 +84,7 @@ class I18nValidationTest < ActiveModel::TestCase
end
def test_errors_full_messages_uses_model_format
- ActiveModel::Errors.i18n_customize_full_message = true
+ ActiveModel::Error.i18n_customize_full_message = true
I18n.backend.store_translations("en", activemodel: {
errors: { models: { person: { format: "%{message}" } } } })
@@ -82,7 +95,7 @@ class I18nValidationTest < ActiveModel::TestCase
end
def test_errors_full_messages_uses_deeply_nested_model_attributes_format
- ActiveModel::Errors.i18n_customize_full_message = true
+ ActiveModel::Error.i18n_customize_full_message = true
I18n.backend.store_translations("en", activemodel: {
errors: { models: { 'person/contacts/addresses': { attributes: { street: { format: "%{message}" } } } } } })
@@ -93,7 +106,7 @@ class I18nValidationTest < ActiveModel::TestCase
end
def test_errors_full_messages_uses_deeply_nested_model_model_format
- ActiveModel::Errors.i18n_customize_full_message = true
+ ActiveModel::Error.i18n_customize_full_message = true
I18n.backend.store_translations("en", activemodel: {
errors: { models: { 'person/contacts/addresses': { format: "%{message}" } } } })
@@ -104,7 +117,7 @@ class I18nValidationTest < ActiveModel::TestCase
end
def test_errors_full_messages_with_indexed_deeply_nested_attributes_and_attributes_format
- ActiveModel::Errors.i18n_customize_full_message = true
+ ActiveModel::Error.i18n_customize_full_message = true
I18n.backend.store_translations("en", activemodel: {
errors: { models: { 'person/contacts/addresses': { attributes: { street: { format: "%{message}" } } } } } })
@@ -115,7 +128,7 @@ class I18nValidationTest < ActiveModel::TestCase
end
def test_errors_full_messages_with_indexed_deeply_nested_attributes_and_model_format
- ActiveModel::Errors.i18n_customize_full_message = true
+ ActiveModel::Error.i18n_customize_full_message = true
I18n.backend.store_translations("en", activemodel: {
errors: { models: { 'person/contacts/addresses': { format: "%{message}" } } } })
@@ -126,7 +139,7 @@ class I18nValidationTest < ActiveModel::TestCase
end
def test_errors_full_messages_with_indexed_deeply_nested_attributes_and_i18n_attribute_name
- ActiveModel::Errors.i18n_customize_full_message = true
+ ActiveModel::Error.i18n_customize_full_message = true
I18n.backend.store_translations("en", activemodel: {
attributes: { 'person/contacts/addresses': { country: "Country" } }
@@ -138,7 +151,7 @@ class I18nValidationTest < ActiveModel::TestCase
end
def test_errors_full_messages_with_indexed_deeply_nested_attributes_without_i18n_config
- ActiveModel::Errors.i18n_customize_full_message = false
+ ActiveModel::Error.i18n_customize_full_message = false
I18n.backend.store_translations("en", activemodel: {
errors: { models: { 'person/contacts/addresses': { attributes: { street: { format: "%{message}" } } } } } })
@@ -149,7 +162,7 @@ class I18nValidationTest < ActiveModel::TestCase
end
def test_errors_full_messages_with_i18n_attribute_name_without_i18n_config
- ActiveModel::Errors.i18n_customize_full_message = false
+ ActiveModel::Error.i18n_customize_full_message = false
I18n.backend.store_translations("en", activemodel: {
attributes: { 'person/contacts[0]/addresses[0]': { country: "Country" } }
@@ -169,8 +182,8 @@ class I18nValidationTest < ActiveModel::TestCase
# [ case, validation_options, generate_message_options]
[ "given no options", {}, {}],
[ "given custom message", { message: "custom" }, { message: "custom" }],
- [ "given if condition", { if: lambda { true } }, {}],
- [ "given unless condition", { unless: lambda { false } }, {}],
+ [ "given if condition", { if: lambda { true } }, {}],
+ [ "given unless condition", { unless: lambda { false } }, {}],
[ "given option that is not reserved", { format: "jpg" }, { format: "jpg" }]
]
@@ -178,9 +191,10 @@ class I18nValidationTest < ActiveModel::TestCase
test "validates_confirmation_of on generated message #{name}" do
person_class.validates_confirmation_of :title, validation_options
@person.title_confirmation = "foo"
- call = [:title_confirmation, :confirmation, generate_message_options.merge(attribute: "Title")]
- assert_called_with(@person.errors, :generate_message, call) do
+ call = [:title_confirmation, :confirmation, @person, generate_message_options.merge(attribute: "Title")]
+ assert_called_with(ActiveModel::Error, :generate_message, call) do
@person.valid?
+ @person.errors.messages
end
end
end
@@ -188,9 +202,10 @@ class I18nValidationTest < ActiveModel::TestCase
COMMON_CASES.each do |name, validation_options, generate_message_options|
test "validates_acceptance_of on generated message #{name}" do
person_class.validates_acceptance_of :title, validation_options.merge(allow_nil: false)
- call = [:title, :accepted, generate_message_options]
- assert_called_with(@person.errors, :generate_message, call) do
+ call = [:title, :accepted, @person, generate_message_options]
+ assert_called_with(ActiveModel::Error, :generate_message, call) do
@person.valid?
+ @person.errors.messages
end
end
end
@@ -198,9 +213,10 @@ class I18nValidationTest < ActiveModel::TestCase
COMMON_CASES.each do |name, validation_options, generate_message_options|
test "validates_presence_of on generated message #{name}" do
person_class.validates_presence_of :title, validation_options
- call = [:title, :blank, generate_message_options]
- assert_called_with(@person.errors, :generate_message, call) do
+ call = [:title, :blank, @person, generate_message_options]
+ assert_called_with(ActiveModel::Error, :generate_message, call) do
@person.valid?
+ @person.errors.messages
end
end
end
@@ -208,9 +224,10 @@ class I18nValidationTest < ActiveModel::TestCase
COMMON_CASES.each do |name, validation_options, generate_message_options|
test "validates_length_of for :within on generated message when too short #{name}" do
person_class.validates_length_of :title, validation_options.merge(within: 3..5)
- call = [:title, :too_short, generate_message_options.merge(count: 3)]
- assert_called_with(@person.errors, :generate_message, call) do
+ call = [:title, :too_short, @person, generate_message_options.merge(count: 3)]
+ assert_called_with(ActiveModel::Error, :generate_message, call) do
@person.valid?
+ @person.errors.messages
end
end
end
@@ -219,9 +236,10 @@ class I18nValidationTest < ActiveModel::TestCase
test "validates_length_of for :too_long generated message #{name}" do
person_class.validates_length_of :title, validation_options.merge(within: 3..5)
@person.title = "this title is too long"
- call = [:title, :too_long, generate_message_options.merge(count: 5)]
- assert_called_with(@person.errors, :generate_message, call) do
+ call = [:title, :too_long, @person, generate_message_options.merge(count: 5)]
+ assert_called_with(ActiveModel::Error, :generate_message, call) do
@person.valid?
+ @person.errors.messages
end
end
end
@@ -229,9 +247,10 @@ class I18nValidationTest < ActiveModel::TestCase
COMMON_CASES.each do |name, validation_options, generate_message_options|
test "validates_length_of for :is on generated message #{name}" do
person_class.validates_length_of :title, validation_options.merge(is: 5)
- call = [:title, :wrong_length, generate_message_options.merge(count: 5)]
- assert_called_with(@person.errors, :generate_message, call) do
+ call = [:title, :wrong_length, @person, generate_message_options.merge(count: 5)]
+ assert_called_with(ActiveModel::Error, :generate_message, call) do
@person.valid?
+ @person.errors.messages
end
end
end
@@ -240,9 +259,10 @@ class I18nValidationTest < ActiveModel::TestCase
test "validates_format_of on generated message #{name}" do
person_class.validates_format_of :title, validation_options.merge(with: /\A[1-9][0-9]*\z/)
@person.title = "72x"
- call = [:title, :invalid, generate_message_options.merge(value: "72x")]
- assert_called_with(@person.errors, :generate_message, call) do
+ call = [:title, :invalid, @person, generate_message_options.merge(value: "72x")]
+ assert_called_with(ActiveModel::Error, :generate_message, call) do
@person.valid?
+ @person.errors.messages
end
end
end
@@ -251,9 +271,10 @@ class I18nValidationTest < ActiveModel::TestCase
test "validates_inclusion_of on generated message #{name}" do
person_class.validates_inclusion_of :title, validation_options.merge(in: %w(a b c))
@person.title = "z"
- call = [:title, :inclusion, generate_message_options.merge(value: "z")]
- assert_called_with(@person.errors, :generate_message, call) do
+ call = [:title, :inclusion, @person, generate_message_options.merge(value: "z")]
+ assert_called_with(ActiveModel::Error, :generate_message, call) do
@person.valid?
+ @person.errors.messages
end
end
end
@@ -262,9 +283,10 @@ class I18nValidationTest < ActiveModel::TestCase
test "validates_inclusion_of using :within on generated message #{name}" do
person_class.validates_inclusion_of :title, validation_options.merge(within: %w(a b c))
@person.title = "z"
- call = [:title, :inclusion, generate_message_options.merge(value: "z")]
- assert_called_with(@person.errors, :generate_message, call) do
+ call = [:title, :inclusion, @person, generate_message_options.merge(value: "z")]
+ assert_called_with(ActiveModel::Error, :generate_message, call) do
@person.valid?
+ @person.errors.messages
end
end
end
@@ -273,9 +295,10 @@ class I18nValidationTest < ActiveModel::TestCase
test "validates_exclusion_of generated message #{name}" do
person_class.validates_exclusion_of :title, validation_options.merge(in: %w(a b c))
@person.title = "a"
- call = [:title, :exclusion, generate_message_options.merge(value: "a")]
- assert_called_with(@person.errors, :generate_message, call) do
+ call = [:title, :exclusion, @person, generate_message_options.merge(value: "a")]
+ assert_called_with(ActiveModel::Error, :generate_message, call) do
@person.valid?
+ @person.errors.messages
end
end
end
@@ -284,9 +307,10 @@ class I18nValidationTest < ActiveModel::TestCase
test "validates_exclusion_of using :within generated message #{name}" do
person_class.validates_exclusion_of :title, validation_options.merge(within: %w(a b c))
@person.title = "a"
- call = [:title, :exclusion, generate_message_options.merge(value: "a")]
- assert_called_with(@person.errors, :generate_message, call) do
+ call = [:title, :exclusion, @person, generate_message_options.merge(value: "a")]
+ assert_called_with(ActiveModel::Error, :generate_message, call) do
@person.valid?
+ @person.errors.messages
end
end
end
@@ -295,9 +319,10 @@ class I18nValidationTest < ActiveModel::TestCase
test "validates_numericality_of generated message #{name}" do
person_class.validates_numericality_of :title, validation_options
@person.title = "a"
- call = [:title, :not_a_number, generate_message_options.merge(value: "a")]
- assert_called_with(@person.errors, :generate_message, call) do
+ call = [:title, :not_a_number, @person, generate_message_options.merge(value: "a")]
+ assert_called_with(ActiveModel::Error, :generate_message, call) do
@person.valid?
+ @person.errors.messages
end
end
end
@@ -306,9 +331,10 @@ class I18nValidationTest < ActiveModel::TestCase
test "validates_numericality_of for :only_integer on generated message #{name}" do
person_class.validates_numericality_of :title, validation_options.merge(only_integer: true)
@person.title = "0.0"
- call = [:title, :not_an_integer, generate_message_options.merge(value: "0.0")]
- assert_called_with(@person.errors, :generate_message, call) do
+ call = [:title, :not_an_integer, @person, generate_message_options.merge(value: "0.0")]
+ assert_called_with(ActiveModel::Error, :generate_message, call) do
@person.valid?
+ @person.errors.messages
end
end
end
@@ -317,9 +343,10 @@ class I18nValidationTest < ActiveModel::TestCase
test "validates_numericality_of for :odd on generated message #{name}" do
person_class.validates_numericality_of :title, validation_options.merge(only_integer: true, odd: true)
@person.title = 0
- call = [:title, :odd, generate_message_options.merge(value: 0)]
- assert_called_with(@person.errors, :generate_message, call) do
+ call = [:title, :odd, @person, generate_message_options.merge(value: 0)]
+ assert_called_with(ActiveModel::Error, :generate_message, call) do
@person.valid?
+ @person.errors.messages
end
end
end
@@ -328,9 +355,10 @@ class I18nValidationTest < ActiveModel::TestCase
test "validates_numericality_of for :less_than on generated message #{name}" do
person_class.validates_numericality_of :title, validation_options.merge(only_integer: true, less_than: 0)
@person.title = 1
- call = [:title, :less_than, generate_message_options.merge(value: 1, count: 0)]
- assert_called_with(@person.errors, :generate_message, call) do
+ call = [:title, :less_than, @person, generate_message_options.merge(value: 1, count: 0)]
+ assert_called_with(ActiveModel::Error, :generate_message, call) do
@person.valid?
+ @person.errors.messages
end
end
end
diff --git a/activemodel/test/cases/validations/numericality_validation_test.rb b/activemodel/test/cases/validations/numericality_validation_test.rb
index 16c44762cb..191af033df 100644
--- a/activemodel/test/cases/validations/numericality_validation_test.rb
+++ b/activemodel/test/cases/validations/numericality_validation_test.rb
@@ -310,7 +310,6 @@ class NumericalityValidationTest < ActiveModel::TestCase
end
private
-
def invalid!(values, error = nil)
with_each_topic_approved_value(values) do |topic, value|
assert topic.invalid?, "#{value.inspect} not rejected as a number"
diff --git a/activemodel/test/cases/validations/validations_context_test.rb b/activemodel/test/cases/validations/validations_context_test.rb
index 024eb1882f..3d2dea9828 100644
--- a/activemodel/test/cases/validations/validations_context_test.rb
+++ b/activemodel/test/cases/validations/validations_context_test.rb
@@ -14,13 +14,13 @@ class ValidationsContextTest < ActiveModel::TestCase
class ValidatorThatAddsErrors < ActiveModel::Validator
def validate(record)
- record.errors[:base] << ERROR_MESSAGE
+ record.errors.add(:base, ERROR_MESSAGE)
end
end
class AnotherValidatorThatAddsErrors < ActiveModel::Validator
def validate(record)
- record.errors[:base] << ANOTHER_ERROR_MESSAGE
+ record.errors.add(:base, ANOTHER_ERROR_MESSAGE)
end
end
diff --git a/activemodel/test/cases/validations/with_validation_test.rb b/activemodel/test/cases/validations/with_validation_test.rb
index 8239792c79..e6ae6603f2 100644
--- a/activemodel/test/cases/validations/with_validation_test.rb
+++ b/activemodel/test/cases/validations/with_validation_test.rb
@@ -14,13 +14,13 @@ class ValidatesWithTest < ActiveModel::TestCase
class ValidatorThatAddsErrors < ActiveModel::Validator
def validate(record)
- record.errors[:base] << ERROR_MESSAGE
+ record.errors.add(:base, message: ERROR_MESSAGE)
end
end
class OtherValidatorThatAddsErrors < ActiveModel::Validator
def validate(record)
- record.errors[:base] << OTHER_ERROR_MESSAGE
+ record.errors.add(:base, message: OTHER_ERROR_MESSAGE)
end
end
@@ -32,14 +32,14 @@ class ValidatesWithTest < ActiveModel::TestCase
class ValidatorThatValidatesOptions < ActiveModel::Validator
def validate(record)
if options[:field] == :first_name
- record.errors[:base] << ERROR_MESSAGE
+ record.errors.add(:base, message: ERROR_MESSAGE)
end
end
end
class ValidatorPerEachAttribute < ActiveModel::EachValidator
def validate_each(record, attribute, value)
- record.errors[attribute] << "Value is #{value}"
+ record.errors.add(attribute, message: "Value is #{value}")
end
end
diff --git a/activemodel/test/cases/validations_test.rb b/activemodel/test/cases/validations_test.rb
index 7776233db5..0b9e1b7005 100644
--- a/activemodel/test/cases/validations_test.rb
+++ b/activemodel/test/cases/validations_test.rb
@@ -53,7 +53,7 @@ class ValidationsTest < ActiveModel::TestCase
r = Reply.new
r.valid?
- errors = r.errors.collect { |attr, messages| [attr.to_s, messages] }
+ errors = assert_deprecated { r.errors.collect { |attr, messages| [attr.to_s, messages] } }
assert_includes errors, ["title", "is Empty"]
assert_includes errors, ["content", "is Empty"]
@@ -74,7 +74,7 @@ class ValidationsTest < ActiveModel::TestCase
def test_errors_on_nested_attributes_expands_name
t = Topic.new
- t.errors["replies.name"] << "can't be blank"
+ assert_deprecated { t.errors["replies.name"] << "can't be blank" }
assert_equal ["Replies name can't be blank"], t.errors.full_messages
end
@@ -216,7 +216,7 @@ class ValidationsTest < ActiveModel::TestCase
t = Topic.new
assert_predicate t, :invalid?
- xml = t.errors.to_xml
+ xml = assert_deprecated { t.errors.to_xml }
assert_match %r{<errors>}, xml
assert_match %r{<error>Title can't be blank</error>}, xml
assert_match %r{<error>Content can't be blank</error>}, xml
@@ -241,14 +241,14 @@ class ValidationsTest < ActiveModel::TestCase
t = Topic.new title: ""
assert_predicate t, :invalid?
- assert_equal :title, key = t.errors.keys[0]
+ assert_equal :title, key = assert_deprecated { t.errors.keys[0] }
assert_equal "can't be blank", t.errors[key][0]
assert_equal "is too short (minimum is 2 characters)", t.errors[key][1]
- assert_equal :author_name, key = t.errors.keys[1]
+ assert_equal :author_name, key = assert_deprecated { t.errors.keys[1] }
assert_equal "can't be blank", t.errors[key][0]
- assert_equal :author_email_address, key = t.errors.keys[2]
+ assert_equal :author_email_address, key = assert_deprecated { t.errors.keys[2] }
assert_equal "will never be valid", t.errors[key][0]
- assert_equal :content, key = t.errors.keys[3]
+ assert_equal :content, key = assert_deprecated { t.errors.keys[3] }
assert_equal "is too short (minimum is 2 characters)", t.errors[key][0]
end
diff --git a/activemodel/test/models/person_with_validator.rb b/activemodel/test/models/person_with_validator.rb
index 44e78cbc29..fbb28d2a0f 100644
--- a/activemodel/test/models/person_with_validator.rb
+++ b/activemodel/test/models/person_with_validator.rb
@@ -5,7 +5,7 @@ class PersonWithValidator
class PresenceValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
- record.errors[attribute] << "Local validator#{options[:custom]}" if value.blank?
+ record.errors.add(attribute, message: "Local validator#{options[:custom]}") if value.blank?
end
end
diff --git a/activemodel/test/models/reply.rb b/activemodel/test/models/reply.rb
index 6bb18f95fe..f340b6fb14 100644
--- a/activemodel/test/models/reply.rb
+++ b/activemodel/test/models/reply.rb
@@ -11,24 +11,24 @@ class Reply < Topic
validate :check_wrong_update, on: :update
def check_empty_title
- errors[:title] << "is Empty" unless title && title.size > 0
+ errors.add(:title, "is Empty") unless title && title.size > 0
end
def errors_on_empty_content
- errors[:content] << "is Empty" unless content && content.size > 0
+ errors.add(:content, "is Empty") unless content && content.size > 0
end
def check_content_mismatch
if title && content && content == "Mismatch"
- errors[:title] << "is Content Mismatch"
+ errors.add(:title, "is Content Mismatch")
end
end
def title_is_wrong_create
- errors[:title] << "is Wrong Create" if title && title == "Wrong Create"
+ errors.add(:title, "is Wrong Create") if title && title == "Wrong Create"
end
def check_wrong_update
- errors[:title] << "is Wrong Update" if title && title == "Wrong Update"
+ errors.add(:title, "is Wrong Update") if title && title == "Wrong Update"
end
end
diff --git a/activemodel/test/validators/email_validator.rb b/activemodel/test/validators/email_validator.rb
index 0c634d8659..774a10b2ba 100644
--- a/activemodel/test/validators/email_validator.rb
+++ b/activemodel/test/validators/email_validator.rb
@@ -2,7 +2,8 @@
class EmailValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
- record.errors[attribute] << (options[:message] || "is not an email") unless
- /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i.match?(value)
+ unless /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i.match?(value)
+ record.errors.add(attribute, message: options[:message] || "is not an email")
+ end
end
end
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index 8daa6c0ce5..2af48f99db 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,885 +1,60 @@
-* Fix dirty tracking for `touch` to track saved changes.
+* Add a warning for enum elements with 'not_' prefix.
- Fixes #33429.
-
- *Ryuta Kamzono*
-
-* `change_column_comment` and `change_table_comment` are invertible only if
- `to` and `from` options are specified.
-
- *Yoshiyuki Kinjo*
-
-* Don't call commit/rollback callbacks when a record isn't saved.
-
- Fixes #29747.
-
- *Ryuta Kamizono*
-
-* Fix circular `autosave: true` causes invalid records to be saved.
-
- Prior to the fix, when there was a circular series of `autosave: true`
- associations, the callback for a `has_many` association was run while
- another instance of the same callback on the same association hadn't
- finished running. When control returned to the first instance of the
- callback, the instance variable had changed, and subsequent associated
- records weren't saved correctly. Specifically, the ID field for the
- `belongs_to` corresponding to the `has_many` was `nil`.
-
- Fixes #28080.
-
- *Larry Reid*
-
-* Raise `ArgumentError` for invalid `:limit` and `:precision` like as other options.
-
- Before:
-
- ```ruby
- add_column :items, :attr1, :binary, size: 10 # => ArgumentError
- add_column :items, :attr2, :decimal, scale: 10 # => ArgumentError
- add_column :items, :attr3, :integer, limit: 10 # => ActiveRecordError
- add_column :items, :attr4, :datetime, precision: 10 # => ActiveRecordError
- ```
-
- After:
-
- ```ruby
- add_column :items, :attr1, :binary, size: 10 # => ArgumentError
- add_column :items, :attr2, :decimal, scale: 10 # => ArgumentError
- add_column :items, :attr3, :integer, limit: 10 # => ArgumentError
- add_column :items, :attr4, :datetime, precision: 10 # => ArgumentError
- ```
-
- *Ryuta Kamizono*
-
-* Association loading isn't to be affected by scoping consistently
- whether preloaded / eager loaded or not, with the exception of `unscoped`.
-
- Before:
-
- ```ruby
- Post.where("1=0").scoping do
- Comment.find(1).post # => nil
- Comment.preload(:post).find(1).post # => #<Post id: 1, ...>
- Comment.eager_load(:post).find(1).post # => #<Post id: 1, ...>
- end
- ```
-
- After:
-
- ```ruby
- Post.where("1=0").scoping do
- Comment.find(1).post # => #<Post id: 1, ...>
- Comment.preload(:post).find(1).post # => #<Post id: 1, ...>
- Comment.eager_load(:post).find(1).post # => #<Post id: 1, ...>
- end
- ```
-
- Fixes #34638, #35398.
-
- *Ryuta Kamizono*
-
-* Add `rails db:prepare` to migrate or setup a database.
-
- Runs `db:migrate` if the database exists or `db:setup` if it doesn't.
-
- *Roberto Miranda*
-
-* Add `after_save_commit` callback as shortcut for `after_commit :hook, on: [ :create, :update ]`.
-
- *DHH*
-
-* Assign all attributes before calling `build` to ensure the child record is visible in
- `before_add` and `after_add` callbacks for `has_many :through` associations.
-
- Fixes #33249.
-
- *Ryan H. Kerr*
-
-* Add `ActiveRecord::Relation#extract_associated` for extracting associated records from a relation.
-
- ```
- account.memberships.extract_associated(:user)
- # => Returns collection of User records
- ```
-
- *DHH*
-
-* Add `ActiveRecord::Relation#annotate` for adding SQL comments to its queries.
-
- For example:
-
- ```
- Post.where(id: 123).annotate("this is a comment").to_sql
- # SELECT "posts".* FROM "posts" WHERE "posts"."id" = 123 /* this is a comment */
- ```
-
- This can be useful in instrumentation or other analysis of issued queries.
-
- *Matt Yoho*
-
-* Support Optimizer Hints.
-
- In most databases, a way to control the optimizer is by using optimizer hints,
- which can be specified within individual statements.
-
- Example (for MySQL):
-
- Topic.optimizer_hints("MAX_EXECUTION_TIME(50000)", "NO_INDEX_MERGE(topics)")
- # SELECT /*+ MAX_EXECUTION_TIME(50000) NO_INDEX_MERGE(topics) */ `topics`.* FROM `topics`
-
- Example (for PostgreSQL with pg_hint_plan):
-
- Topic.optimizer_hints("SeqScan(topics)", "Parallel(topics 8)")
- # SELECT /*+ SeqScan(topics) Parallel(topics 8) */ "topics".* FROM "topics"
-
- See also:
-
- * https://dev.mysql.com/doc/refman/8.0/en/optimizer-hints.html
- * https://pghintplan.osdn.jp/pg_hint_plan.html
- * https://docs.oracle.com/en/database/oracle/oracle-database/12.2/tgsql/influencing-the-optimizer.html
- * https://docs.microsoft.com/en-us/sql/t-sql/queries/hints-transact-sql-query?view=sql-server-2017
- * https://www.ibm.com/support/knowledgecenter/en/SSEPGG_11.1.0/com.ibm.db2.luw.admin.perf.doc/doc/c0070117.html
-
- *Ryuta Kamizono*
-
-* Fix query attribute method on user-defined attribute to be aware of typecasted value.
-
- For example, the following code no longer return false as casted non-empty string:
-
- ```
- class Post < ActiveRecord::Base
- attribute :user_defined_text, :text
- end
-
- Post.new(user_defined_text: "false").user_defined_text? # => true
- ```
-
- *Yuji Kamijima*
-
-* Quote empty ranges like other empty enumerables.
-
- *Patrick Rebsch*
-
-* Add `insert_all`/`insert_all!`/`upsert_all` methods to `ActiveRecord::Persistence`,
- allowing bulk inserts akin to the bulk updates provided by `update_all` and
- bulk deletes by `delete_all`.
-
- Supports skipping or upserting duplicates through the `ON CONFLICT` syntax
- for PostgreSQL (9.5+) and SQLite (3.24+) and `ON DUPLICATE KEY UPDATE` syntax
- for MySQL.
-
- *Bob Lail*
-
-* Add `rails db:seed:replant` that truncates tables of each database
- for current environment and loads the seeds.
-
- *bogdanvlviv*, *DHH*
-
-* Add `ActiveRecord::Base.connection.truncate` for SQLite3 adapter.
-
- *bogdanvlviv*
-
-* Deprecate mismatched collation comparison for uniqueness validator.
-
- Uniqueness validator will no longer enforce case sensitive comparison in Rails 6.1.
- To continue case sensitive comparison on the case insensitive column,
- pass `case_sensitive: true` option explicitly to the uniqueness validator.
-
- *Ryuta Kamizono*
-
-* Add `reselect` method. This is a short-hand for `unscope(:select).select(fields)`.
-
- Fixes #27340.
-
- *Willian Gustavo Veiga*
-
-* Add negative scopes for all enum values.
-
- Example:
-
- class Post < ActiveRecord::Base
- enum status: %i[ drafted active trashed ]
- end
-
- Post.not_drafted # => where.not(status: :drafted)
- Post.not_active # => where.not(status: :active)
- Post.not_trashed # => where.not(status: :trashed)
-
- *DHH*
-
-* Fix different `count` calculation when using `size` with manual `select` with DISTINCT.
-
- Fixes #35214.
-
- *Juani Villarejo*
-
-
-## Rails 6.0.0.beta3 (March 11, 2019) ##
-
-* No changes.
-
-
-## Rails 6.0.0.beta2 (February 25, 2019) ##
-
-* Fix prepared statements caching to be enabled even when query caching is enabled.
-
- *Ryuta Kamizono*
-
-* Ensure `update_all` series cares about optimistic locking.
-
- *Ryuta Kamizono*
-
-* Don't allow `where` with non numeric string matches to 0 values.
-
- *Ryuta Kamizono*
-
-* Introduce `ActiveRecord::Relation#destroy_by` and `ActiveRecord::Relation#delete_by`.
-
- `destroy_by` allows relation to find all the records matching the condition and perform
- `destroy_all` on the matched records.
-
- Example:
-
- Person.destroy_by(name: 'David')
- Person.destroy_by(name: 'David', rating: 4)
-
- david = Person.find_by(name: 'David')
- david.posts.destroy_by(id: [1, 2, 3])
-
- `delete_by` allows relation to find all the records matching the condition and perform
- `delete_all` on the matched records.
-
- Example:
-
- Person.delete_by(name: 'David')
- Person.delete_by(name: 'David', rating: 4)
-
- david = Person.find_by(name: 'David')
- david.posts.delete_by(id: [1, 2, 3])
-
- *Abhay Nikam*
-
-* Don't allow `where` with invalid value matches to nil values.
-
- Fixes #33624.
-
- *Ryuta Kamizono*
-
-* SQLite3: Implement `add_foreign_key` and `remove_foreign_key`.
-
- *Ryuta Kamizono*
-
-* Deprecate using class level querying methods if the receiver scope
- regarded as leaked. Use `klass.unscoped` to avoid the leaking scope.
-
- *Ryuta Kamizono*
-
-* Allow applications to automatically switch connections.
-
- Adds a middleware and configuration options that can be used in your
- application to automatically switch between the writing and reading
- database connections.
-
- `GET` and `HEAD` requests will read from the replica unless there was
- a write in the last 2 seconds, otherwise they will read from the primary.
- Non-get requests will always write to the primary. The middleware accepts
- an argument for a Resolver class and a Operations class where you are able
- to change how the auto-switcher works to be most beneficial for your
- application.
-
- To use the middleware in your application you can use the following
- configuration options:
-
- ```
- config.active_record.database_selector = { delay: 2.seconds }
- config.active_record.database_resolver = ActiveRecord::Middleware::DatabaseSelector::Resolver
- config.active_record.database_resolver_context = ActiveRecord::Middleware::DatabaseSelector::Resolver::Session
- ```
-
- To change the database selection strategy, pass a custom class to the
- configuration options:
-
- ```
- config.active_record.database_selector = { delay: 10.seconds }
- config.active_record.database_resolver = MyResolver
- config.active_record.database_resolver_context = MyResolver::MyCookies
- ```
-
- *Eileen M. Uchitelle*
-
-* MySQL: Support `:size` option to change text and blob size.
-
- *Ryuta Kamizono*
-
-* Make `t.timestamps` with precision by default.
-
- *Ryuta Kamizono*
-
-
-## Rails 6.0.0.beta1 (January 18, 2019) ##
-
-* Remove deprecated `#set_state` from the transaction object.
-
- *Rafael Mendonça França*
-
-* Remove deprecated `#supports_statement_cache?` from the database adapters.
-
- *Rafael Mendonça França*
-
-* Remove deprecated `#insert_fixtures` from the database adapters.
-
- *Rafael Mendonça França*
-
-* Remove deprecated `ActiveRecord::ConnectionAdapters::SQLite3Adapter#valid_alter_table_type?`.
-
- *Rafael Mendonça França*
-
-* Do not allow passing the column name to `sum` when a block is passed.
-
- *Rafael Mendonça França*
-
-* Do not allow passing the column name to `count` when a block is passed.
-
- *Rafael Mendonça França*
-
-* Remove delegation of missing methods in a relation to arel.
-
- *Rafael Mendonça França*
-
-* Remove delegation of missing methods in a relation to private methods of the class.
-
- *Rafael Mendonça França*
-
-* Deprecate `config.activerecord.sqlite3.represent_boolean_as_integer`.
-
- *Rafael Mendonça França*
-
-* Change `SQLite3Adapter` to always represent boolean values as integers.
-
- *Rafael Mendonça França*
-
-* Remove ability to specify a timestamp name for `#cache_key`.
-
- *Rafael Mendonça França*
-
-* Remove deprecated `ActiveRecord::Migrator.migrations_path=`.
-
- *Rafael Mendonça França*
-
-* Remove deprecated `expand_hash_conditions_for_aggregates`.
-
- *Rafael Mendonça França*
-
-* Set polymorphic type column to NULL on `dependent: :nullify` strategy.
-
- On polymorphic associations both the foreign key and the foreign type columns will be set to NULL.
-
- *Laerti Papa*
-
-* Allow permitted instance of `ActionController::Parameters` as argument of `ActiveRecord::Relation#exists?`.
-
- *Gannon McGibbon*
-
-* Add support for endless ranges introduces in Ruby 2.6.
-
- *Greg Navis*
-
-* Deprecate passing `migrations_paths` to `connection.assume_migrated_upto_version`.
-
- *Ryuta Kamizono*
-
-* MySQL: `ROW_FORMAT=DYNAMIC` create table option by default.
-
- Since MySQL 5.7.9, the `innodb_default_row_format` option defines the default row
- format for InnoDB tables. The default setting is `DYNAMIC`.
- The row format is required for indexing on `varchar(255)` with `utf8mb4` columns.
-
- *Ryuta Kamizono*
-
-* Fix join table column quoting with SQLite.
-
- *Gannon McGibbon*
-
-* Allow disabling scopes generated by `ActiveRecord.enum`.
-
- *Alfred Dominic*
-
-* Ensure that `delete_all` on collection proxy returns affected count.
-
- *Ryuta Kamizono*
-
-* Reset scope after delete on collection association to clear stale offsets of removed records.
-
- *Gannon McGibbon*
-
-* Add the ability to prevent writes to a database for the duration of a block.
-
- Allows the application to prevent writes to a database. This can be useful when
- you're building out multiple databases and want to make sure you're not sending
- writes when you want a read.
-
- If `while_preventing_writes` is called and the query is considered a write
- query the database will raise an exception regardless of whether the database
- user is able to write.
-
- This is not meant to be a catch-all for write queries but rather a way to enforce
- read-only queries without opening a second connection. One purpose of this is to
- catch accidental writes, not all writes.
-
- *Eileen M. Uchitelle*
-
-* Allow aliased attributes to be used in `#update_columns` and `#update`.
-
- *Gannon McGibbon*
-
-* Allow spaces in postgres table names.
-
- Fixes issue where "user post" is misinterpreted as "\"user\".\"post\"" when quoting table names with the postgres adapter.
-
- *Gannon McGibbon*
-
-* Cached `columns_hash` fields should be excluded from `ResultSet#column_types`.
-
- PR #34528 addresses the inconsistent behaviour when attribute is defined for an ignored column. The following test
- was passing for SQLite and MySQL, but failed for PostgreSQL:
-
- ```ruby
- class DeveloperName < ActiveRecord::Type::String
- def deserialize(value)
- "Developer: #{value}"
- end
- end
-
- class AttributedDeveloper < ActiveRecord::Base
- self.table_name = "developers"
-
- attribute :name, DeveloperName.new
-
- self.ignored_columns += ["name"]
- end
-
- developer = AttributedDeveloper.create
- developer.update_column :name, "name"
-
- loaded_developer = AttributedDeveloper.where(id: developer.id).select("*").first
- puts loaded_developer.name # should be "Developer: name" but it's just "name"
- ```
-
- *Dmitry Tsepelev*
-
-* Make the implicit order column configurable.
-
- When calling ordered finder methods such as `first` or `last` without an
- explicit order clause, ActiveRecord sorts records by primary key. This can
- result in unpredictable and surprising behaviour when the primary key is
- not an auto-incrementing integer, for example when it's a UUID. This change
- makes it possible to override the column used for implicit ordering such
- that `first` and `last` will return more predictable results.
-
- Example:
-
- class Project < ActiveRecord::Base
- self.implicit_order_column = "created_at"
- end
-
- *Tekin Suleyman*
-
-* Bump minimum PostgreSQL version to 9.3.
-
- *Yasuo Honda*
-
-* Values of enum are frozen, raising an error when attempting to modify them.
-
- *Emmanuel Byrd*
-
-* Move `ActiveRecord::StatementInvalid` SQL to error property and include binds as separate error property.
-
- `ActiveRecord::ConnectionAdapters::AbstractAdapter#translate_exception_class` now requires `binds` to be passed as the last argument.
-
- `ActiveRecord::ConnectionAdapters::AbstractAdapter#translate_exception` now requires `message`, `sql`, and `binds` to be passed as keyword arguments.
-
- Subclasses of `ActiveRecord::StatementInvalid` must now provide `sql:` and `binds:` arguments to `super`.
-
- Example:
-
- ```
- class MySubclassedError < ActiveRecord::StatementInvalid
- def initialize(message, sql:, binds:)
- super(message, sql: sql, binds: binds)
- end
- end
- ```
-
- *Gannon McGibbon*
-
-* Add an `:if_not_exists` option to `create_table`.
-
- Example:
-
- create_table :posts, if_not_exists: true do |t|
- t.string :title
- end
-
- That would execute:
-
- CREATE TABLE IF NOT EXISTS posts (
- ...
- )
-
- If the table already exists, `if_not_exists: false` (the default) raises an
- exception whereas `if_not_exists: true` does nothing.
-
- *fatkodima*, *Stefan Kanev*
-
-* Defining an Enum as a Hash with blank key, or as an Array with a blank value, now raises an `ArgumentError`.
-
- *Christophe Maximin*
-
-* Adds support for multiple databases to `rails db:schema:cache:dump` and `rails db:schema:cache:clear`.
-
- *Gannon McGibbon*
-
-* `update_columns` now correctly raises `ActiveModel::MissingAttributeError`
- if the attribute does not exist.
-
- *Sean Griffin*
-
-* Add support for hash and URL configs in database hash of `ActiveRecord::Base.connected_to`.
-
- ````
- User.connected_to(database: { writing: "postgres://foo" }) do
- User.create!(name: "Gannon")
- end
-
- config = { "adapter" => "sqlite3", "database" => "db/readonly.sqlite3" }
- User.connected_to(database: { reading: config }) do
- User.count
- end
- ````
-
- *Gannon McGibbon*
-
-* Support default expression for MySQL.
-
- MySQL 8.0.13 and higher supports default value to be a function or expression.
-
- https://dev.mysql.com/doc/refman/8.0/en/create-table.html
-
- *Ryuta Kamizono*
-
-* Support expression indexes for MySQL.
-
- MySQL 8.0.13 and higher supports functional key parts that index
- expression values rather than column or column prefix values.
-
- https://dev.mysql.com/doc/refman/8.0/en/create-index.html
-
- *Ryuta Kamizono*
-
-* Fix collection cache key with limit and custom select to avoid ambiguous timestamp column error.
-
- Fixes #33056.
-
- *Federico Martinez*
-
-* Add basic API for connection switching to support multiple databases.
-
- 1) Adds a `connects_to` method for models to connect to multiple databases. Example:
-
- ```
- class AnimalsModel < ApplicationRecord
- self.abstract_class = true
-
- connects_to database: { writing: :animals_primary, reading: :animals_replica }
- end
-
- class Dog < AnimalsModel
- # connected to both the animals_primary db for writing and the animals_replica for reading
- end
- ```
-
- 2) Adds a `connected_to` block method for switching connection roles or connecting to
- a database that the model didn't connect to. Connecting to the database in this block is
- useful when you have another defined connection, for example `slow_replica` that you don't
- want to connect to by default but need in the console, or a specific code block.
-
- ```
- ActiveRecord::Base.connected_to(role: :reading) do
- Dog.first # finds dog from replica connected to AnimalsBase
- Book.first # doesn't have a reading connection, will raise an error
- end
- ```
-
- ```
- ActiveRecord::Base.connected_to(database: :slow_replica) do
- SlowReplicaModel.first # if the db config has a slow_replica configuration this will be used to do the lookup, otherwise this will throw an exception
- end
- ```
-
- *Eileen M. Uchitelle*
-
-* Enum raises on invalid definition values
-
- When defining a Hash enum it can be easy to use `[]` instead of `{}`. This
- commit checks that only valid definition values are provided, those can
- be a Hash, an array of Symbols or an array of Strings. Otherwise it
- raises an `ArgumentError`.
-
- Fixes #33961
-
- *Alberto Almagro*
-
-* Reloading associations now clears the Query Cache like `Persistence#reload` does.
-
- ```
- class Post < ActiveRecord::Base
- has_one :category
- belongs_to :author
- has_many :comments
- end
-
- # Each of the following will now clear the query cache.
- post.reload_category
- post.reload_author
- post.comments.reload
- ```
-
- *Christophe Maximin*
-
-* Added `index` option for `change_table` migration helpers.
- With this change you can create indexes while adding new
- columns into the existing tables.
-
- Example:
-
- change_table(:languages) do |t|
- t.string :country_code, index: true
+ class Foo
+ enum status: [:sent, :not_sent]
end
- *Mehmet Emin İNAÇ*
-
-* Fix `transaction` reverting for migrations.
-
- Before: Commands inside a `transaction` in a reverted migration ran uninverted.
- Now: This change fixes that by reverting commands inside `transaction` block.
+ *Edu Depetris*
- *fatkodima*, *David Verhasselt*
+* Make currency symbols optional for money column type in PostgreSQL
-* Raise an error instead of scanning the filesystem root when `fixture_path` is blank.
+ *Joel Schneider*
- *Gannon McGibbon*, *Max Albrecht*
+* Add support for beginless ranges, introduced in Ruby 2.7.
-* Allow `ActiveRecord::Base.configurations=` to be set with a symbolized hash.
+ *Josh Goodall*
- *Gannon McGibbon*
+* Add database_exists? method to connection adapters to check if a database exists.
-* Don't update counter cache unless the record is actually saved.
+ *Guilherme Mansur*
- Fixes #31493, #33113, #33117.
+* Loading the schema for a model that has no `table_name` raises a `TableNotSpecified` error.
- *Ryuta Kamizono*
-
-* Deprecate `ActiveRecord::Result#to_hash` in favor of `ActiveRecord::Result#to_a`.
-
- *Gannon McGibbon*, *Kevin Cheng*
-
-* SQLite3 adapter supports expression indexes.
-
- ```
- create_table :users do |t|
- t.string :email
- end
-
- add_index :users, 'lower(email)', name: 'index_users_on_email', unique: true
- ```
-
- *Gray Kemmey*
-
-* Allow subclasses to redefine autosave callbacks for associated records.
-
- Fixes #33305.
-
- *Andrey Subbota*
-
-* Bump minimum MySQL version to 5.5.8.
+ *Guilherme Mansur*, *Eugene Kenny*
- *Yasuo Honda*
+* PostgreSQL: Fix GROUP BY with ORDER BY virtual count attribute.
-* Use MySQL utf8mb4 character set by default.
-
- `utf8mb4` character set with 4-Byte encoding supports supplementary characters including emoji.
- The previous default 3-Byte encoding character set `utf8` is not enough to support them.
-
- *Yasuo Honda*
-
-* Fix duplicated record creation when using nested attributes with `create_with`.
-
- *Darwin Wu*
-
-* Configuration item `config.filter_parameters` could also filter out
- sensitive values of database columns when call `#inspect`.
- We also added `ActiveRecord::Base::filter_attributes`/`=` in order to
- specify sensitive attributes to specific model.
-
- ```
- Rails.application.config.filter_parameters += [:credit_card_number, /phone/]
- Account.last.inspect # => #<Account id: 123, name: "DHH", credit_card_number: [FILTERED], telephone_number: [FILTERED] ...>
- SecureAccount.filter_attributes += [:name]
- SecureAccount.last.inspect # => #<SecureAccount id: 42, name: [FILTERED], credit_card_number: [FILTERED] ...>
- ```
-
- *Zhang Kang*, *Yoshiyuki Kinjo*
-
-* Deprecate `column_name_length`, `table_name_length`, `columns_per_table`,
- `indexes_per_table`, `columns_per_multicolumn_index`, `sql_query_length`,
- and `joins_per_query` methods in `DatabaseLimits`.
+ Fixes #36022.
*Ryuta Kamizono*
-* `ActiveRecord::Base.configurations` now returns an object.
-
- `ActiveRecord::Base.configurations` used to return a hash, but this
- is an inflexible data model. In order to improve multiple-database
- handling in Rails, we've changed this to return an object. Some methods
- are provided to make the object behave hash-like in order to ease the
- transition process. Since most applications don't manipulate the hash
- we've decided to add backwards-compatible functionality that will throw
- a deprecation warning if used, however calling `ActiveRecord::Base.configurations`
- will use the new version internally and externally.
-
- For example, the following `database.yml`:
-
- ```
- development:
- adapter: sqlite3
- database: db/development.sqlite3
- ```
-
- Used to become a hash:
-
- ```
- { "development" => { "adapter" => "sqlite3", "database" => "db/development.sqlite3" } }
- ```
-
- Is now converted into the following object:
-
- ```
- #<ActiveRecord::DatabaseConfigurations:0x00007fd1acbdf800 @configurations=[
- #<ActiveRecord::DatabaseConfigurations::HashConfig:0x00007fd1acbded10 @env_name="development",
- @spec_name="primary", @config={"adapter"=>"sqlite3", "database"=>"db/development.sqlite3"}>
- ]
- ```
-
- Iterating over the database configurations has also changed. Instead of
- calling hash methods on the `configurations` hash directly, a new method `configs_for` has
- been provided that allows you to select the correct configuration. `env_name` and
- `spec_name` arguments are optional. For example, these return an array of
- database config objects for the requested environment and a single database config object
- will be returned for the requested environment and specification name respectively.
-
- ```
- ActiveRecord::Base.configurations.configs_for(env_name: "development")
- ActiveRecord::Base.configurations.configs_for(env_name: "development", spec_name: "primary")
- ```
-
- *Eileen M. Uchitelle*, *Aaron Patterson*
-
-* Add database configuration to disable advisory locks.
-
- ```
- production:
- adapter: postgresql
- advisory_locks: false
- ```
-
- *Guo Xiang*
-
-* SQLite3 adapter `alter_table` method restores foreign keys.
-
- *Yasuo Honda*
-
-* Allow `:to_table` option to `invert_remove_foreign_key`.
-
- Example:
-
- remove_foreign_key :accounts, to_table: :owners
-
- *Nikolay Epifanov*, *Rich Chen*
-
-* Add environment & load_config dependency to `bin/rake db:seed` to enable
- seed load in environments without Rails and custom DB configuration
-
- *Tobias Bielohlawek*
-
-* Fix default value for mysql time types with specified precision.
-
- *Nikolay Kondratyev*
-
-* Fix `touch` option to behave consistently with `Persistence#touch` method.
-
- *Ryuta Kamizono*
-
-* Migrations raise when duplicate column definition.
-
- Fixes #33024.
-
- *Federico Martinez*
-
-* Bump minimum SQLite version to 3.8
-
- *Yasuo Honda*
-
-* Fix parent record should not get saved with duplicate children records.
-
- Fixes #32940.
-
- *Santosh Wadghule*
-
-* Fix logic on disabling commit callbacks so they are not called unexpectedly when errors occur.
-
- *Brian Durand*
-
-* Ensure `Associations::CollectionAssociation#size` and `Associations::CollectionAssociation#empty?`
- use loaded association ids if present.
-
- *Graham Turner*
-
-* Add support to preload associations of polymorphic associations when not all the records have the requested associations.
-
- *Dana Sherson*
-
-* Add `touch_all` method to `ActiveRecord::Relation`.
-
- Example:
-
- Person.where(name: "David").touch_all(time: Time.new(2020, 5, 16, 0, 0, 0))
+* Make ActiveRecord `ConnectionPool.connections` method thread-safe.
- *fatkodima*, *duggiefresh*
+ Fixes #36465.
-* Add `ActiveRecord::Base.base_class?` predicate.
+ *Jeff Doering*
- *Bogdan Gusiev*
+* Add support for multiple databases to `rails db:abort_if_pending_migrations`.
-* Add custom prefix/suffix options to `ActiveRecord::Store.store_accessor`.
+ *Mark Lee*
- *Tan Huynh*, *Yukio Mizuta*
+* Fix sqlite3 collation parsing when using decimal columns.
-* Rails 6 requires Ruby 2.5.0 or newer.
+ *Martin R. Schuster*
- *Jeremy Daer*, *Kasper Timm Hansen*
+* Fix invalid schema when primary key column has a comment.
-* Deprecate `update_attributes`/`!` in favor of `update`/`!`.
+ Fixes #29966.
- *Eddie Lebow*
+ *Guilherme Goettems Schneider*
-* Add `ActiveRecord::Base.create_or_find_by`/`!` to deal with the SELECT/INSERT race condition in
- `ActiveRecord::Base.find_or_create_by`/`!` by leaning on unique constraints in the database.
+* Fix table comment also being applied to the primary key column.
- *DHH*
+ *Guilherme Goettems Schneider*
-* Add `Relation#pick` as short-hand for single-value plucks.
+* Allow generated `create_table` migrations to include or skip timestamps.
- *DHH*
+ *Michael Duchemin*
-Please check [5-2-stable](https://github.com/rails/rails/blob/5-2-stable/activerecord/CHANGELOG.md) for previous changes.
+Please check [6-0-stable](https://github.com/rails/rails/blob/6-0-stable/activerecord/CHANGELOG.md) for previous changes.
diff --git a/activerecord/lib/active_record/aggregations.rb b/activerecord/lib/active_record/aggregations.rb
index 3250e29b82..aa08124158 100644
--- a/activerecord/lib/active_record/aggregations.rb
+++ b/activerecord/lib/active_record/aggregations.rb
@@ -14,7 +14,6 @@ module ActiveRecord
end
private
-
def clear_aggregation_cache
@aggregation_cache.clear if persisted?
end
diff --git a/activerecord/lib/active_record/association_relation.rb b/activerecord/lib/active_record/association_relation.rb
index 4c538ef2bd..de9892e48d 100644
--- a/activerecord/lib/active_record/association_relation.rb
+++ b/activerecord/lib/active_record/association_relation.rb
@@ -29,7 +29,6 @@ module ActiveRecord
end
private
-
def exec_queries
super do |record|
@association.set_inverse_instance_from_queries(record)
diff --git a/activerecord/lib/active_record/associations/alias_tracker.rb b/activerecord/lib/active_record/associations/alias_tracker.rb
index 272eede824..ac90ba0137 100644
--- a/activerecord/lib/active_record/associations/alias_tracker.rb
+++ b/activerecord/lib/active_record/associations/alias_tracker.rb
@@ -72,7 +72,6 @@ module ActiveRecord
attr_reader :aliases
private
-
def truncate(name)
name.slice(0, @connection.table_alias_length - 2)
end
diff --git a/activerecord/lib/active_record/associations/builder/association.rb b/activerecord/lib/active_record/associations/builder/association.rb
index 3b4b243148..0c61094d6c 100644
--- a/activerecord/lib/active_record/associations/builder/association.rb
+++ b/activerecord/lib/active_record/associations/builder/association.rb
@@ -128,5 +128,9 @@ module ActiveRecord::Associations::Builder # :nodoc:
name = reflection.name
model.before_destroy lambda { |o| o.association(name).handle_dependency }
end
+
+ private_class_method :build_scope, :macro, :valid_options, :validate_options, :define_extensions,
+ :define_callbacks, :define_accessors, :define_readers, :define_writers, :define_validations,
+ :valid_dependent_options, :check_dependent_options, :add_destroy_callbacks
end
end
diff --git a/activerecord/lib/active_record/associations/builder/belongs_to.rb b/activerecord/lib/active_record/associations/builder/belongs_to.rb
index fc00f1e900..321ccba918 100644
--- a/activerecord/lib/active_record/associations/builder/belongs_to.rb
+++ b/activerecord/lib/active_record/associations/builder/belongs_to.rb
@@ -74,11 +74,11 @@ module ActiveRecord::Associations::Builder # :nodoc:
def self.add_touch_callbacks(model, reflection)
foreign_key = reflection.foreign_key
- n = reflection.name
+ name = reflection.name
touch = reflection.options[:touch]
callback = lambda { |changes_method| lambda { |record|
- BelongsTo.touch_record(record, record.send(changes_method), foreign_key, n, touch, belongs_to_touch_method)
+ BelongsTo.touch_record(record, record.send(changes_method), foreign_key, name, touch, belongs_to_touch_method)
}}
if reflection.counter_cache_column
@@ -123,5 +123,8 @@ module ActiveRecord::Associations::Builder # :nodoc:
model.validates_presence_of reflection.name, message: :required
end
end
+
+ private_class_method :macro, :valid_options, :valid_dependent_options, :define_callbacks, :define_validations,
+ :add_counter_cache_callbacks, :add_touch_callbacks, :add_default_callbacks, :add_destroy_callbacks
end
end
diff --git a/activerecord/lib/active_record/associations/builder/collection_association.rb b/activerecord/lib/active_record/associations/builder/collection_association.rb
index 9fccfcce0c..e78d25441b 100644
--- a/activerecord/lib/active_record/associations/builder/collection_association.rb
+++ b/activerecord/lib/active_record/associations/builder/collection_association.rb
@@ -22,9 +22,9 @@ module ActiveRecord::Associations::Builder # :nodoc:
def self.define_extensions(model, name, &block)
if block_given?
- extension_module_name = "#{model.name.demodulize}#{name.to_s.camelize}AssociationExtension"
+ extension_module_name = "#{name.to_s.camelize}AssociationExtension"
extension = Module.new(&block)
- model.module_parent.const_set(extension_module_name, extension)
+ model.const_set(extension_module_name, extension)
end
end
@@ -66,5 +66,7 @@ module ActiveRecord::Associations::Builder # :nodoc:
end
CODE
end
+
+ private_class_method :valid_options, :define_callback, :define_extensions, :define_readers, :define_writers
end
end
diff --git a/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb b/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb
index 0140aa15c8..6ad4c75fb5 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
@@ -46,7 +46,6 @@ module ActiveRecord::Associations::Builder # :nodoc:
end
private
-
def self.suppress_composite_primary_key(pk)
pk unless pk.is_a?(Array)
end
@@ -73,7 +72,6 @@ module ActiveRecord::Associations::Builder # :nodoc:
end
private
-
def middle_options(join_model)
middle_options = {}
middle_options[:class_name] = "#{lhs_model.name}::#{join_model.name}"
diff --git a/activerecord/lib/active_record/associations/builder/has_many.rb b/activerecord/lib/active_record/associations/builder/has_many.rb
index 5b9617bc6d..556e2988f5 100644
--- a/activerecord/lib/active_record/associations/builder/has_many.rb
+++ b/activerecord/lib/active_record/associations/builder/has_many.rb
@@ -13,5 +13,7 @@ module ActiveRecord::Associations::Builder # :nodoc:
def self.valid_dependent_options
[:destroy, :delete_all, :nullify, :restrict_with_error, :restrict_with_exception]
end
+
+ private_class_method :macro, :valid_options, :valid_dependent_options
end
end
diff --git a/activerecord/lib/active_record/associations/builder/has_one.rb b/activerecord/lib/active_record/associations/builder/has_one.rb
index bfb37d6eee..27ebe8cb71 100644
--- a/activerecord/lib/active_record/associations/builder/has_one.rb
+++ b/activerecord/lib/active_record/associations/builder/has_one.rb
@@ -7,7 +7,7 @@ module ActiveRecord::Associations::Builder # :nodoc:
end
def self.valid_options(options)
- valid = super + [:as]
+ valid = super + [:as, :touch]
valid += [:through, :source, :source_type] if options[:through]
valid
end
@@ -16,6 +16,11 @@ module ActiveRecord::Associations::Builder # :nodoc:
[:destroy, :delete, :nullify, :restrict_with_error, :restrict_with_exception]
end
+ def self.define_callbacks(model, reflection)
+ super
+ add_touch_callbacks(model, reflection) if reflection.options[:touch]
+ end
+
def self.add_destroy_callbacks(model, reflection)
super unless reflection.options[:through]
end
@@ -26,5 +31,34 @@ module ActiveRecord::Associations::Builder # :nodoc:
model.validates_presence_of reflection.name, message: :required
end
end
+
+ def self.touch_record(o, name, touch)
+ record = o.send name
+
+ return unless record && record.persisted?
+
+ if touch != true
+ record.touch(touch)
+ else
+ record.touch
+ end
+ end
+
+ def self.add_touch_callbacks(model, reflection)
+ name = reflection.name
+ touch = reflection.options[:touch]
+
+ callback = lambda { |record|
+ HasOne.touch_record(record, name, touch)
+ }
+
+ model.after_create callback, if: :saved_changes?
+ model.after_update callback, if: :saved_changes?
+ model.after_destroy callback
+ model.after_touch callback
+ end
+
+ private_class_method :macro, :valid_options, :valid_dependent_options, :add_destroy_callbacks,
+ :define_callbacks, :define_validations, :add_touch_callbacks
end
end
diff --git a/activerecord/lib/active_record/associations/builder/singular_association.rb b/activerecord/lib/active_record/associations/builder/singular_association.rb
index 0a02ef4cc1..0e22563b41 100644
--- a/activerecord/lib/active_record/associations/builder/singular_association.rb
+++ b/activerecord/lib/active_record/associations/builder/singular_association.rb
@@ -38,5 +38,7 @@ module ActiveRecord::Associations::Builder # :nodoc:
end
CODE
end
+
+ private_class_method :valid_options, :define_accessors, :define_constructors
end
end
diff --git a/activerecord/lib/active_record/associations/collection_association.rb b/activerecord/lib/active_record/associations/collection_association.rb
index c3d4eab562..891b50d160 100644
--- a/activerecord/lib/active_record/associations/collection_association.rb
+++ b/activerecord/lib/active_record/associations/collection_association.rb
@@ -56,7 +56,7 @@ module ActiveRecord
def ids_writer(ids)
primary_key = reflection.association_primary_key
pk_type = klass.type_for_attribute(primary_key)
- ids = Array(ids).reject(&:blank?)
+ ids = Array(ids).compact_blank
ids.map! { |i| pk_type.cast(i) }
records = klass.where(primary_key => ids).index_by do |r|
diff --git a/activerecord/lib/active_record/associations/collection_proxy.rb b/activerecord/lib/active_record/associations/collection_proxy.rb
index edcb44f0fc..0db0ad8595 100644
--- a/activerecord/lib/active_record/associations/collection_proxy.rb
+++ b/activerecord/lib/active_record/associations/collection_proxy.rb
@@ -1002,7 +1002,7 @@ module ActiveRecord
end
# Adds one or more +records+ to the collection by setting their foreign keys
- # to the association's primary key. Since +<<+ flattens its argument list and
+ # to the association's primary key. Since <tt><<</tt> flattens its argument list and
# inserts each record, +push+ and +concat+ behave identically. Returns +self+
# so several appends may be chained together.
#
@@ -1029,7 +1029,7 @@ module ActiveRecord
alias_method :append, :<<
alias_method :concat, :<<
- def prepend(*args)
+ def prepend(*args) # :nodoc:
raise NoMethodError, "prepend on association is not defined. Please use <<, push or append"
end
@@ -1101,7 +1101,6 @@ module ActiveRecord
delegate(*delegate_methods, to: :scope)
private
-
def find_nth_with_limit(index, limit)
load_target if find_from_target?
super
diff --git a/activerecord/lib/active_record/associations/has_many_association.rb b/activerecord/lib/active_record/associations/has_many_association.rb
index 5972846940..dd2ed55279 100644
--- a/activerecord/lib/active_record/associations/has_many_association.rb
+++ b/activerecord/lib/active_record/associations/has_many_association.rb
@@ -37,7 +37,6 @@ module ActiveRecord
end
private
-
# Returns the number of records in this collection.
#
# If the association has a counter cache it gets that value. Otherwise
diff --git a/activerecord/lib/active_record/associations/join_dependency.rb b/activerecord/lib/active_record/associations/join_dependency.rb
index b76005b587..f35a40fb2f 100644
--- a/activerecord/lib/active_record/associations/join_dependency.rb
+++ b/activerecord/lib/active_record/associations/join_dependency.rb
@@ -64,16 +64,17 @@ module ActiveRecord
end
end
- def initialize(base, table, associations)
+ def initialize(base, table, associations, join_type)
tree = self.class.make_tree associations
@join_root = JoinBase.new(base, table, build(tree, base))
+ @join_type = join_type
end
def reflections
join_root.drop(1).map!(&:reflection)
end
- def join_constraints(joins_to_add, join_type, alias_tracker)
+ def join_constraints(joins_to_add, alias_tracker)
@alias_tracker = alias_tracker
construct_tables!(join_root)
@@ -82,9 +83,9 @@ module ActiveRecord
joins.concat joins_to_add.flat_map { |oj|
construct_tables!(oj.join_root)
if join_root.match? oj.join_root
- walk join_root, oj.join_root
+ walk(join_root, oj.join_root, oj.join_type)
else
- make_join_constraints(oj.join_root, join_type)
+ make_join_constraints(oj.join_root, oj.join_type)
end
}
end
@@ -125,7 +126,7 @@ module ActiveRecord
end
protected
- attr_reader :join_root
+ attr_reader :join_root, :join_type
private
attr_reader :alias_tracker
@@ -151,7 +152,7 @@ module ActiveRecord
end
end
- def make_constraints(parent, child, join_type = Arel::Nodes::OuterJoin)
+ def make_constraints(parent, child, join_type)
foreign_table = parent.table
foreign_klass = parent.base_klass
joins = child.join_constraints(foreign_table, foreign_klass, join_type, alias_tracker)
@@ -173,13 +174,13 @@ module ActiveRecord
join ? "#{name}_join" : name
end
- def walk(left, right)
+ def walk(left, right, join_type)
intersection, missing = right.children.map { |node1|
[left.children.find { |node2| node1.match? node2 }, node1]
}.partition(&:first)
- joins = intersection.flat_map { |l, r| r.table = l.table; walk(l, r) }
- joins.concat missing.flat_map { |_, n| make_constraints(left, n) }
+ joins = intersection.flat_map { |l, r| r.table = l.table; walk(l, r, join_type) }
+ joins.concat missing.flat_map { |_, n| make_constraints(left, n, join_type) }
end
def find_reflection(klass, name)
diff --git a/activerecord/lib/active_record/associations/join_dependency/join_association.rb b/activerecord/lib/active_record/associations/join_dependency/join_association.rb
index ca0305abbb..6a7e92dc28 100644
--- a/activerecord/lib/active_record/associations/join_dependency/join_association.rb
+++ b/activerecord/lib/active_record/associations/join_dependency/join_association.rb
@@ -44,8 +44,7 @@ module ActiveRecord
unless others.empty?
joins.concat arel.join_sources
- right = joins.last.right
- right.expr.children.concat(others)
+ append_constraints(joins.last, others)
end
# The current table in this iteration becomes the foreign table in the next
@@ -65,6 +64,16 @@ module ActiveRecord
@readonly = reflection.scope && reflection.scope_for(base_klass.unscoped).readonly_value
end
+
+ private
+ def append_constraints(join, constraints)
+ if join.is_a?(Arel::Nodes::StringJoin)
+ join_string = table.create_and(constraints.unshift(join.left))
+ join.left = Arel.sql(base_klass.connection.visitor.compile(join_string))
+ else
+ join.right.expr.children.concat(constraints)
+ end
+ end
end
end
end
diff --git a/activerecord/lib/active_record/associations/preloader.rb b/activerecord/lib/active_record/associations/preloader.rb
index 6b57e5093a..d4e8b364e1 100644
--- a/activerecord/lib/active_record/associations/preloader.rb
+++ b/activerecord/lib/active_record/associations/preloader.rb
@@ -95,7 +95,6 @@ module ActiveRecord
end
private
-
# Loads all the given data into +records+ for the +association+.
def preloaders_on(association, records, scope, polymorphic_parent = false)
case association
diff --git a/activerecord/lib/active_record/associations/preloader/association.rb b/activerecord/lib/active_record/associations/preloader/association.rb
index 46532f651e..4c7b0e6f07 100644
--- a/activerecord/lib/active_record/associations/preloader/association.rb
+++ b/activerecord/lib/active_record/associations/preloader/association.rb
@@ -27,7 +27,9 @@ module ActiveRecord
end
def records_by_owner
- @records_by_owner ||= preloaded_records.each_with_object({}) do |record, result|
+ # owners can be duplicated when a relation has a collection association join
+ # #compare_by_identity makes such owners different hash keys
+ @records_by_owner ||= preloaded_records.each_with_object({}.compare_by_identity) do |record, result|
owners_by_key[convert_key(record[association_key_name])].each do |owner|
(result[owner] ||= []) << record
end
@@ -36,13 +38,7 @@ module ActiveRecord
def preloaded_records
return @preloaded_records if defined?(@preloaded_records)
- return [] if owner_keys.empty?
- # Some databases impose a limit on the number of ids in a list (in Oracle it's 1000)
- # Make several smaller queries if necessary or make one query if the adapter supports it
- slices = owner_keys.each_slice(klass.connection.in_clause_length || owner_keys.size)
- @preloaded_records = slices.flat_map do |slice|
- records_for(slice)
- end
+ @preloaded_records = owner_keys.empty? ? [] : records_for(owner_keys)
end
private
diff --git a/activerecord/lib/active_record/attribute_assignment.rb b/activerecord/lib/active_record/attribute_assignment.rb
index 929045f29b..acb8ba7e5a 100644
--- a/activerecord/lib/active_record/attribute_assignment.rb
+++ b/activerecord/lib/active_record/attribute_assignment.rb
@@ -7,7 +7,6 @@ module ActiveRecord
include ActiveModel::AttributeAssignment
private
-
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 98b7805c0a..0b66043d2a 100644
--- a/activerecord/lib/active_record/attribute_decorators.rb
+++ b/activerecord/lib/active_record/attribute_decorators.rb
@@ -46,7 +46,6 @@ module ActiveRecord
end
private
-
def load_schema!
super
attribute_types.each do |name, type|
@@ -75,7 +74,6 @@ module ActiveRecord
end
private
-
def decorators_for(name, type)
matching(name, type).map(&:last)
end
diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb
index af7e46e649..21f72bb6c7 100644
--- a/activerecord/lib/active_record/attribute_methods.rb
+++ b/activerecord/lib/active_record/attribute_methods.rb
@@ -24,7 +24,7 @@ module ActiveRecord
RESTRICTED_CLASS_METHODS = %w(private public protected allocate new name parent superclass)
- class GeneratedAttributeMethodsBuilder < Module #:nodoc:
+ class GeneratedAttributeMethods < Module #:nodoc:
include Mutex_m
end
@@ -35,7 +35,7 @@ module ActiveRecord
end
def initialize_generated_modules # :nodoc:
- @generated_attribute_methods = const_set(:GeneratedAttributeMethods, GeneratedAttributeMethodsBuilder.new)
+ @generated_attribute_methods = const_set(:GeneratedAttributeMethods, GeneratedAttributeMethods.new)
private_constant :GeneratedAttributeMethods
@attribute_methods_generated = false
include @generated_attribute_methods
@@ -89,7 +89,7 @@ module ActiveRecord
# If ThisClass < ... < SomeSuperClass < ... < Base and SomeSuperClass
# defines its own attribute method, then we don't want to overwrite that.
defined = method_defined_within?(method_name, superclass, Base) &&
- ! superclass.instance_method(method_name).owner.is_a?(GeneratedAttributeMethodsBuilder)
+ ! superclass.instance_method(method_name).owner.is_a?(GeneratedAttributeMethods)
defined || super
end
end
@@ -159,57 +159,6 @@ module ActiveRecord
end
end
- # Regexp for column names (with or without a table name prefix). Matches
- # the following:
- # "#{table_name}.#{column_name}"
- # "#{column_name}"
- COLUMN_NAME = /\A(?:\w+\.)?\w+\z/i
-
- # Regexp for column names with order (with or without a table name
- # prefix, with or without various order modifiers). Matches the following:
- # "#{table_name}.#{column_name}"
- # "#{table_name}.#{column_name} #{direction}"
- # "#{table_name}.#{column_name} #{direction} NULLS FIRST"
- # "#{table_name}.#{column_name} NULLS LAST"
- # "#{column_name}"
- # "#{column_name} #{direction}"
- # "#{column_name} #{direction} NULLS FIRST"
- # "#{column_name} NULLS LAST"
- COLUMN_NAME_WITH_ORDER = /
- \A
- (?:\w+\.)?
- \w+
- (?:\s+asc|\s+desc)?
- (?:\s+nulls\s+(?:first|last))?
- \z
- /ix
-
- def disallow_raw_sql!(args, permit: COLUMN_NAME) # :nodoc:
- unexpected = args.reject do |arg|
- Arel.arel_node?(arg) ||
- arg.to_s.split(/\s*,\s*/).all? { |part| permit.match?(part) }
- end
-
- return if unexpected.none?
-
- if allow_unsafe_raw_sql == :deprecated
- ActiveSupport::Deprecation.warn(
- "Dangerous query method (method whose arguments are used as raw " \
- "SQL) called with non-attribute argument(s): " \
- "#{unexpected.map(&:inspect).join(", ")}. Non-attribute " \
- "arguments will be disallowed in Rails 6.0. This method should " \
- "not be called with user-provided values, such as request " \
- "parameters or model attributes. Known-safe values can be passed " \
- "by wrapping them in Arel.sql()."
- )
- else
- raise(ActiveRecord::UnknownAttributeReference,
- "Query method called with non-attribute argument(s): " +
- unexpected.map(&:inspect).join(", ")
- )
- end
- end
-
# Returns true if the given attribute exists, otherwise false.
#
# class Person < ActiveRecord::Base
@@ -437,7 +386,7 @@ module ActiveRecord
def attributes_for_update(attribute_names)
attribute_names &= self.class.column_names
attribute_names.delete_if do |name|
- readonly_attribute?(name)
+ self.class.readonly_attribute?(name)
end
end
@@ -460,12 +409,8 @@ module ActiveRecord
end
end
- def readonly_attribute?(name)
- self.class.readonly_attributes.include?(name)
- end
-
def pk_attribute?(name)
- name == self.class.primary_key
+ name == @primary_key
end
end
end
diff --git a/activerecord/lib/active_record/attribute_methods/before_type_cast.rb b/activerecord/lib/active_record/attribute_methods/before_type_cast.rb
index dc239ff9ea..4a7b6c60e5 100644
--- a/activerecord/lib/active_record/attribute_methods/before_type_cast.rb
+++ b/activerecord/lib/active_record/attribute_methods/before_type_cast.rb
@@ -46,6 +46,7 @@ module ActiveRecord
# task.read_attribute_before_type_cast('completed_on') # => "2012-10-21"
# task.read_attribute_before_type_cast(:completed_on) # => "2012-10-21"
def read_attribute_before_type_cast(attr_name)
+ sync_with_transaction_state if @transaction_state&.finalized?
@attributes[attr_name.to_s].value_before_type_cast
end
@@ -60,17 +61,18 @@ module ActiveRecord
# task.attributes_before_type_cast
# # => {"id"=>nil, "title"=>nil, "is_done"=>true, "completed_on"=>"2012-10-21", "created_at"=>nil, "updated_at"=>nil}
def attributes_before_type_cast
+ sync_with_transaction_state if @transaction_state&.finalized?
@attributes.values_before_type_cast
end
private
-
# Dispatch target for <tt>*_before_type_cast</tt> attribute methods.
def attribute_before_type_cast(attribute_name)
read_attribute_before_type_cast(attribute_name)
end
def attribute_came_from_user?(attribute_name)
+ sync_with_transaction_state if @transaction_state&.finalized?
@attributes[attribute_name].came_from_user?
end
end
diff --git a/activerecord/lib/active_record/attribute_methods/dirty.rb b/activerecord/lib/active_record/attribute_methods/dirty.rb
index 68ac8475b0..45341765c1 100644
--- a/activerecord/lib/active_record/attribute_methods/dirty.rb
+++ b/activerecord/lib/active_record/attribute_methods/dirty.rb
@@ -156,13 +156,19 @@ module ActiveRecord
end
private
+ def mutations_from_database
+ sync_with_transaction_state if @transaction_state&.finalized?
+ super
+ end
+
+ def mutations_before_last_save
+ sync_with_transaction_state if @transaction_state&.finalized?
+ super
+ end
+
def write_attribute_without_type_cast(attr_name, value)
- name = attr_name.to_s
- if self.class.attribute_alias?(name)
- name = self.class.attribute_alias(name)
- end
- result = super(name, value)
- clear_attribute_change(name)
+ result = super
+ clear_attribute_change(attr_name)
result
end
@@ -171,6 +177,11 @@ module ActiveRecord
affected_rows = super
+ if @_skip_dirty_tracking ||= false
+ clear_attribute_changes(@_touch_attr_names)
+ return affected_rows
+ end
+
changes = {}
@attributes.keys.each do |attr_name|
next if @_touch_attr_names.include?(attr_name)
@@ -187,7 +198,7 @@ module ActiveRecord
affected_rows
ensure
- @_touch_attr_names = nil
+ @_touch_attr_names, @_skip_dirty_tracking = nil, nil
end
def _update_record(attribute_names = attribute_names_for_partial_writes)
diff --git a/activerecord/lib/active_record/attribute_methods/primary_key.rb b/activerecord/lib/active_record/attribute_methods/primary_key.rb
index 6af5346fa7..768c5f8c05 100644
--- a/activerecord/lib/active_record/attribute_methods/primary_key.rb
+++ b/activerecord/lib/active_record/attribute_methods/primary_key.rb
@@ -16,44 +16,35 @@ module ActiveRecord
# Returns the primary key column's value.
def id
- sync_with_transaction_state
- primary_key = self.class.primary_key
- _read_attribute(primary_key) if primary_key
+ _read_attribute(@primary_key)
end
# Sets the primary key column's value.
def id=(value)
- sync_with_transaction_state
- primary_key = self.class.primary_key
- _write_attribute(primary_key, value) if primary_key
+ _write_attribute(@primary_key, value)
end
# Queries the primary key column's value.
def id?
- sync_with_transaction_state
- query_attribute(self.class.primary_key)
+ query_attribute(@primary_key)
end
# Returns the primary key column's value before type cast.
def id_before_type_cast
- sync_with_transaction_state
- read_attribute_before_type_cast(self.class.primary_key)
+ read_attribute_before_type_cast(@primary_key)
end
# Returns the primary key column's previous value.
def id_was
- sync_with_transaction_state
- attribute_was(self.class.primary_key)
+ attribute_was(@primary_key)
end
# Returns the primary key column's value from the database.
def id_in_database
- sync_with_transaction_state
- attribute_in_database(self.class.primary_key)
+ attribute_in_database(@primary_key)
end
private
-
def attribute_method?(attr_name)
attr_name == "id" || super
end
@@ -122,13 +113,12 @@ module ActiveRecord
#
# Project.primary_key # => "foo_id"
def primary_key=(value)
- @primary_key = value && value.to_s
+ @primary_key = value && -value.to_s
@quoted_primary_key = nil
@attributes_builder = nil
end
private
-
def suppress_composite_primary_key(pk)
return pk unless pk.is_a?(Array)
diff --git a/activerecord/lib/active_record/attribute_methods/read.rb b/activerecord/lib/active_record/attribute_methods/read.rb
index ffac5313ad..0f0e721b24 100644
--- a/activerecord/lib/active_record/attribute_methods/read.rb
+++ b/activerecord/lib/active_record/attribute_methods/read.rb
@@ -7,16 +7,12 @@ module ActiveRecord
module ClassMethods # :nodoc:
private
-
def define_method_attribute(name)
- sync_with_transaction_state = "sync_with_transaction_state" if name == primary_key
-
ActiveModel::AttributeMethods::AttrNames.define_attribute_accessor_method(
generated_attribute_methods, name
) do |temp_method_name, attr_name_expr|
generated_attribute_methods.module_eval <<-RUBY, __FILE__, __LINE__ + 1
def #{temp_method_name}
- #{sync_with_transaction_state}
name = #{attr_name_expr}
_read_attribute(name) { |n| missing_attribute(n, caller) }
end
@@ -30,19 +26,16 @@ module ActiveRecord
# to a date object, like Date.new(2004, 12, 12)).
def read_attribute(attr_name, &block)
name = attr_name.to_s
- if self.class.attribute_alias?(name)
- name = self.class.attribute_alias(name)
- end
+ name = self.class.attribute_aliases[name] || name
- primary_key = self.class.primary_key
- name = primary_key if name == "id" && primary_key
- sync_with_transaction_state if name == primary_key
+ name = @primary_key if name == "id" && @primary_key
_read_attribute(name, &block)
end
# This method exists to avoid the expensive primary_key check internally, without
# breaking compatibility with the read_attribute API
def _read_attribute(attr_name, &block) # :nodoc
+ sync_with_transaction_state if @transaction_state&.finalized?
@attributes.fetch_value(attr_name.to_s, &block)
end
diff --git a/activerecord/lib/active_record/attribute_methods/serialization.rb b/activerecord/lib/active_record/attribute_methods/serialization.rb
index 6e0e90f39c..7bc03b9eed 100644
--- a/activerecord/lib/active_record/attribute_methods/serialization.rb
+++ b/activerecord/lib/active_record/attribute_methods/serialization.rb
@@ -79,7 +79,6 @@ module ActiveRecord
end
private
-
def type_incompatible_with_serialize?(type, class_name)
type.is_a?(ActiveRecord::Type::Json) && class_name == ::JSON ||
type.respond_to?(:type_cast_array, true) && class_name == ::Array
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 294a3dc32c..fb44232dff 100644
--- a/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
+++ b/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
@@ -25,7 +25,6 @@ module ActiveRecord
end
private
-
def convert_time_to_time_zone(value)
return if value.nil?
@@ -64,7 +63,6 @@ module ActiveRecord
module ClassMethods # :nodoc:
private
-
def inherited(subclass)
super
# We need to apply this decorator here, rather than on module inclusion. The closure
diff --git a/activerecord/lib/active_record/attribute_methods/write.rb b/activerecord/lib/active_record/attribute_methods/write.rb
index d5ba2f42cb..66536a8ddf 100644
--- a/activerecord/lib/active_record/attribute_methods/write.rb
+++ b/activerecord/lib/active_record/attribute_methods/write.rb
@@ -11,17 +11,13 @@ module ActiveRecord
module ClassMethods # :nodoc:
private
-
def define_method_attribute=(name)
- sync_with_transaction_state = "sync_with_transaction_state" if name == primary_key
-
ActiveModel::AttributeMethods::AttrNames.define_attribute_accessor_method(
generated_attribute_methods, name, writer: true,
) do |temp_method_name, attr_name_expr|
generated_attribute_methods.module_eval <<-RUBY, __FILE__, __LINE__ + 1
def #{temp_method_name}(value)
name = #{attr_name_expr}
- #{sync_with_transaction_state}
_write_attribute(name, value)
end
RUBY
@@ -34,27 +30,24 @@ module ActiveRecord
# turned into +nil+.
def write_attribute(attr_name, value)
name = attr_name.to_s
- if self.class.attribute_alias?(name)
- name = self.class.attribute_alias(name)
- end
+ name = self.class.attribute_aliases[name] || name
- primary_key = self.class.primary_key
- name = primary_key if name == "id" && primary_key
- sync_with_transaction_state if name == primary_key
+ name = @primary_key if name == "id" && @primary_key
_write_attribute(name, value)
end
# This method exists to avoid the expensive primary_key check internally, without
# breaking compatibility with the write_attribute API
def _write_attribute(attr_name, value) # :nodoc:
+ sync_with_transaction_state if @transaction_state&.finalized?
@attributes.write_from_user(attr_name.to_s, value)
value
end
private
def write_attribute_without_type_cast(attr_name, value)
- name = attr_name.to_s
- @attributes.write_cast_value(name, value)
+ sync_with_transaction_state if @transaction_state&.finalized?
+ @attributes.write_cast_value(attr_name.to_s, value)
value
end
diff --git a/activerecord/lib/active_record/attributes.rb b/activerecord/lib/active_record/attributes.rb
index 7cf421c184..c7846dbe7a 100644
--- a/activerecord/lib/active_record/attributes.rb
+++ b/activerecord/lib/active_record/attributes.rb
@@ -255,7 +255,6 @@ module ActiveRecord
end
private
-
NO_DEFAULT_PROVIDED = Object.new # :nodoc:
private_constant :NO_DEFAULT_PROVIDED
diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb
index 50f29a81a6..734ebb45ae 100644
--- a/activerecord/lib/active_record/autosave_association.rb
+++ b/activerecord/lib/active_record/autosave_association.rb
@@ -147,7 +147,6 @@ module ActiveRecord
module ClassMethods # :nodoc:
private
-
def define_non_cyclic_method(name, &block)
return if instance_methods(false).include?(name)
define_method(name) do |*args|
@@ -267,7 +266,6 @@ module ActiveRecord
end
private
-
# Returns the record for an association collection that should be validated
# or saved. If +autosave+ is +false+ only new records will be returned,
# unless the parent is/was a new record itself.
@@ -304,7 +302,7 @@ module ActiveRecord
def validate_single_association(reflection)
association = association_instance_get(reflection.name)
record = association && association.reader
- association_valid?(reflection, record) if record
+ association_valid?(reflection, record) if record && record.changed_for_autosave?
end
# Validate the associated records if <tt>:validate</tt> or
@@ -330,21 +328,16 @@ module ActiveRecord
if reflection.options[:autosave]
indexed_attribute = !index.nil? && (reflection.options[:index_errors] || ActiveRecord::Base.index_nested_attribute_errors)
- record.errors.each do |attribute, message|
+ record.errors.group_by_attribute.each { |attribute, errors|
attribute = normalize_reflection_attribute(indexed_attribute, reflection, index, attribute)
- errors[attribute] << message
- errors[attribute].uniq!
- end
-
- record.errors.details.each_key do |attribute|
- reflection_attribute =
- normalize_reflection_attribute(indexed_attribute, reflection, index, attribute).to_sym
- record.errors.details[attribute].each do |error|
- errors.details[reflection_attribute] << error
- errors.details[reflection_attribute].uniq!
- end
- end
+ errors.each { |error|
+ self.errors.import(
+ error,
+ attribute: attribute
+ )
+ }
+ }
else
errors.add(reflection.name)
end
@@ -416,7 +409,7 @@ module ActiveRecord
saved = record.save(validate: false)
end
- raise ActiveRecord::Rollback unless saved
+ raise(RecordInvalid.new(association.owner)) unless saved
end
end
end
@@ -500,9 +493,7 @@ module ActiveRecord
end
def _ensure_no_duplicate_errors
- errors.messages.each_key do |attribute|
- errors[attribute].uniq!
- end
+ errors.uniq!
end
end
end
diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb
index 2af6d09b53..282c9fcf30 100644
--- a/activerecord/lib/active_record/base.rb
+++ b/activerecord/lib/active_record/base.rb
@@ -12,7 +12,6 @@ require "active_support/core_ext/hash/slice"
require "active_support/core_ext/string/behavior"
require "active_support/core_ext/kernel/singleton_class"
require "active_support/core_ext/module/introspection"
-require "active_support/core_ext/object/duplicable"
require "active_support/core_ext/class/subclasses"
require "active_record/attribute_decorators"
require "active_record/define_callbacks"
diff --git a/activerecord/lib/active_record/callbacks.rb b/activerecord/lib/active_record/callbacks.rb
index ef5444dfc3..a9ab9ab7a9 100644
--- a/activerecord/lib/active_record/callbacks.rb
+++ b/activerecord/lib/active_record/callbacks.rb
@@ -323,7 +323,6 @@ module ActiveRecord
end
private
-
def create_or_update(**)
_run_save_callbacks { super }
end
diff --git a/activerecord/lib/active_record/coders/yaml_column.rb b/activerecord/lib/active_record/coders/yaml_column.rb
index 11559141c7..881f0bcdb0 100644
--- a/activerecord/lib/active_record/coders/yaml_column.rb
+++ b/activerecord/lib/active_record/coders/yaml_column.rb
@@ -39,7 +39,6 @@ module ActiveRecord
end
private
-
def check_arity_of_constructor
load(nil)
rescue ArgumentError
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 68498b5dc5..36001efdd5 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
@@ -3,6 +3,7 @@
require "thread"
require "concurrent/map"
require "monitor"
+require "weakref"
module ActiveRecord
# Raised when a connection could not be obtained within the connection
@@ -19,6 +20,26 @@ module ActiveRecord
end
module ConnectionAdapters
+ module AbstractPool # :nodoc:
+ def get_schema_cache(connection)
+ @schema_cache ||= SchemaCache.new(connection)
+ @schema_cache.connection = connection
+ @schema_cache
+ end
+
+ def set_schema_cache(cache)
+ @schema_cache = cache
+ end
+ end
+
+ class NullPool # :nodoc:
+ include ConnectionAdapters::AbstractPool
+
+ def initialize
+ @schema_cache = nil
+ end
+ end
+
# Connection pool base class for managing Active Record database
# connections.
#
@@ -146,7 +167,6 @@ module ActiveRecord
end
private
-
def internal_poll(timeout)
no_wait_poll || (timeout && wait_poll(timeout))
end
@@ -294,23 +314,50 @@ module ActiveRecord
@frequency = frequency
end
+ @mutex = Mutex.new
+ @pools = {}
+
+ class << self
+ def register_pool(pool, frequency) # :nodoc:
+ @mutex.synchronize do
+ unless @pools.key?(frequency)
+ @pools[frequency] = []
+ spawn_thread(frequency)
+ end
+ @pools[frequency] << WeakRef.new(pool)
+ end
+ end
+
+ private
+ def spawn_thread(frequency)
+ Thread.new(frequency) do |t|
+ loop do
+ sleep t
+ @mutex.synchronize do
+ @pools[frequency].select!(&:weakref_alive?)
+ @pools[frequency].each do |p|
+ p.reap
+ p.flush
+ rescue WeakRef::RefError
+ end
+ end
+ end
+ end
+ end
+ end
+
def run
return unless frequency && frequency > 0
- Thread.new(frequency, pool) { |t, p|
- loop do
- sleep t
- p.reap
- p.flush
- end
- }
+ self.class.register_pool(pool, frequency)
end
end
include MonitorMixin
include QueryCache::ConnectionPoolConfiguration
+ include ConnectionAdapters::AbstractPool
attr_accessor :automatic_reconnect, :checkout_timeout, :schema_cache
- attr_reader :spec, :connections, :size, :reaper
+ attr_reader :spec, :size, :reaper
# Creates a new ConnectionPool object. +spec+ is a ConnectionSpecification
# object which describes database connection information (e.g. adapter,
@@ -379,7 +426,7 @@ module ActiveRecord
# #connection can be called any number of times; the connection is
# held in a cache keyed by a thread.
def connection
- @thread_cached_conns[connection_cache_key(@lock_thread || Thread.current)] ||= checkout
+ @thread_cached_conns[connection_cache_key(current_thread)] ||= checkout
end
# Returns true if there is an open connection being used for the current thread.
@@ -388,7 +435,7 @@ module ActiveRecord
# #connection or #with_connection methods. Connections obtained through
# #checkout will not be detected by #active_connection?
def active_connection?
- @thread_cached_conns[connection_cache_key(Thread.current)]
+ @thread_cached_conns[connection_cache_key(current_thread)]
end
# Signal that the thread is finished with the current connection.
@@ -423,6 +470,21 @@ module ActiveRecord
synchronize { @connections.any? }
end
+ # Returns an array containing the connections currently in the pool.
+ # Access to the array does not require synchronization on the pool because
+ # the array is newly created and not retained by the pool.
+ #
+ # However; this method bypasses the ConnectionPool's thread-safe connection
+ # access pattern. A returned connection may be owned by another thread,
+ # unowned, or by happen-stance owned by the calling thread.
+ #
+ # Calling methods on a connection without ownership is subject to the
+ # thread-safety guarantees of the underlying method. Many of the methods
+ # on connection adapter classes are inherently multi-thread unsafe.
+ def connections
+ synchronize { @connections.dup }
+ end
+
# Disconnects all connections in the pool, and clears the pool.
#
# Raises:
@@ -668,6 +730,10 @@ module ActiveRecord
thread
end
+ def current_thread
+ @lock_thread || Thread.current
+ end
+
# Take control of all existing connections so a "group" action such as
# reload/disconnect can be performed safely. It is no longer enough to
# wrap it in +synchronize+ because some pool's actions are allowed
@@ -809,7 +875,6 @@ module ActiveRecord
def new_connection
Base.send(spec.adapter_method, spec.config).tap do |conn|
- conn.schema_cache = schema_cache.dup if schema_cache
conn.check_version
end
end
@@ -938,15 +1003,30 @@ module ActiveRecord
end
end
+ attr_reader :prevent_writes
+
def initialize
# These caches are keyed by spec.name (ConnectionSpecification#name).
@owner_to_pool = ConnectionHandler.create_owner_to_pool
+ @prevent_writes = false
# Backup finalizer: if the forked child never needed a pool, the above
# early discard has not occurred
ObjectSpace.define_finalizer self, ConnectionHandler.unowned_pool_finalizer(@owner_to_pool)
end
+ # Prevent writing to the database regardless of role.
+ #
+ # In some cases you may want to prevent writes to the database
+ # even if you are on a database that can write. `while_preventing_writes`
+ # will prevent writes to the database for the duration of the block.
+ def while_preventing_writes
+ original, @prevent_writes = @prevent_writes, true
+ yield
+ ensure
+ @prevent_writes = original
+ end
+
def connection_pool_list
owner_to_pool.values.compact
end
@@ -1064,7 +1144,6 @@ module ActiveRecord
end
private
-
def owner_to_pool
@owner_to_pool[Process.pid]
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 75e959045e..d932f068f2 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/database_limits.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/database_limits.rb
@@ -1,7 +1,5 @@
# frozen_string_literal: true
-require "active_support/deprecation"
-
module ActiveRecord
module ConnectionAdapters # :nodoc:
module DatabaseLimits
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 ef19538447..044272ea51 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
@@ -205,8 +205,6 @@ module ActiveRecord
# In order to get around this problem, #transaction will emulate the effect
# of nested transactions, by using savepoints:
# https://dev.mysql.com/doc/refman/5.7/en/savepoint.html
- # Savepoints are supported by MySQL and PostgreSQL. SQLite3 version >= '3.6.8'
- # supports savepoints.
#
# It is safe to call this method if a database transaction is already open,
# i.e. if #transaction is called within another #transaction block. In case
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 a7753e3e9c..768122b4d2 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb
@@ -33,17 +33,17 @@ module ActiveRecord
end
def enable_query_cache!
- @query_cache_enabled[connection_cache_key(Thread.current)] = true
+ @query_cache_enabled[connection_cache_key(current_thread)] = true
connection.enable_query_cache! if active_connection?
end
def disable_query_cache!
- @query_cache_enabled.delete connection_cache_key(Thread.current)
+ @query_cache_enabled.delete connection_cache_key(current_thread)
connection.disable_query_cache! if active_connection?
end
def query_cache_enabled
- @query_cache_enabled[connection_cache_key(Thread.current)]
+ @query_cache_enabled[connection_cache_key(current_thread)]
end
end
@@ -109,7 +109,6 @@ module ActiveRecord
end
private
-
def cache_sql(sql, name, binds)
@lock.synchronize do
result =
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
index 2877530917..93273f6cf6 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
@@ -114,16 +114,16 @@ module ActiveRecord
# if the value is a Time responding to usec.
def quoted_date(value)
if value.acts_like?(:time)
- zone_conversion_method = ActiveRecord::Base.default_timezone == :utc ? :getutc : :getlocal
-
- if value.respond_to?(zone_conversion_method)
- value = value.send(zone_conversion_method)
+ if ActiveRecord::Base.default_timezone == :utc
+ value = value.getutc if value.respond_to?(:getutc) && !value.utc?
+ else
+ value = value.getlocal if value.respond_to?(:getlocal)
end
end
result = value.to_s(:db)
if value.respond_to?(:usec) && value.usec > 0
- "#{result}.#{sprintf("%06d", value.usec)}"
+ result << "." << sprintf("%06d", value.usec)
else
result
end
@@ -142,6 +142,59 @@ module ActiveRecord
value.to_s.gsub(%r{ (/ (?: | \g<1>) \*) \+? \s* | \s* (\* (?: | \g<2>) /) }x, "")
end
+ def column_name_matcher # :nodoc:
+ COLUMN_NAME
+ end
+
+ def column_name_with_order_matcher # :nodoc:
+ COLUMN_NAME_WITH_ORDER
+ end
+
+ # Regexp for column names (with or without a table name prefix).
+ # Matches the following:
+ #
+ # "#{table_name}.#{column_name}"
+ # "#{column_name}"
+ COLUMN_NAME = /
+ \A
+ (
+ (?:
+ # table_name.column_name | function(one or no argument)
+ ((?:\w+\.)?\w+) | \w+\((?:|\g<2>)\)
+ )
+ (?:(?:\s+AS)?\s+\w+)?
+ )
+ (?:\s*,\s*\g<1>)*
+ \z
+ /ix
+
+ # Regexp for column names with order (with or without a table name prefix,
+ # with or without various order modifiers). Matches the following:
+ #
+ # "#{table_name}.#{column_name}"
+ # "#{table_name}.#{column_name} #{direction}"
+ # "#{table_name}.#{column_name} #{direction} NULLS FIRST"
+ # "#{table_name}.#{column_name} NULLS LAST"
+ # "#{column_name}"
+ # "#{column_name} #{direction}"
+ # "#{column_name} #{direction} NULLS FIRST"
+ # "#{column_name} NULLS LAST"
+ COLUMN_NAME_WITH_ORDER = /
+ \A
+ (
+ (?:
+ # table_name.column_name | function(one or no argument)
+ ((?:\w+\.)?\w+) | \w+\((?:|\g<2>)\)
+ )
+ (?:\s+ASC|\s+DESC)?
+ (?:\s+NULLS\s+(?:FIRST|LAST))?
+ )
+ (?:\s*,\s*\g<1>)*
+ \z
+ /ix
+
+ private_constant :COLUMN_NAME, :COLUMN_NAME_WITH_ORDER
+
private
def type_casted_binds(binds)
if binds.first.is_a?(Array)
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/savepoints.rb b/activerecord/lib/active_record/connection_adapters/abstract/savepoints.rb
index 52a796b926..d6dbef3fc8 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/savepoints.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/savepoints.rb
@@ -8,15 +8,15 @@ module ActiveRecord
end
def create_savepoint(name = current_savepoint_name)
- execute("SAVEPOINT #{name}")
+ execute("SAVEPOINT #{name}", "TRANSACTION")
end
def exec_rollback_to_savepoint(name = current_savepoint_name)
- execute("ROLLBACK TO SAVEPOINT #{name}")
+ execute("ROLLBACK TO SAVEPOINT #{name}", "TRANSACTION")
end
def release_savepoint(name = current_savepoint_name)
- execute("RELEASE SAVEPOINT #{name}")
+ execute("RELEASE SAVEPOINT #{name}", "TRANSACTION")
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_creation.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_creation.rb
index 7d20825a75..23c993cfc3 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_creation.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_creation.rb
@@ -19,7 +19,6 @@ module ActiveRecord
to: :@conn, private: true
private
-
def visit_AlterTable(o)
sql = +"ALTER TABLE #{quote_table_name(o.name)} "
sql << o.adds.map { |col| accept col }.join(" ")
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 688eea75e8..dbd533b4b3 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
@@ -264,8 +264,7 @@ module ActiveRecord
if_not_exists: false,
options: nil,
as: nil,
- comment: nil,
- **
+ comment: nil
)
@conn = conn
@columns_hash = {}
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 622e00fffb..fb56e712be 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb
@@ -15,7 +15,7 @@ module ActiveRecord
def column_spec_for_primary_key(column)
return {} if default_primary_key?(column)
spec = { id: schema_type(column).inspect }
- spec.merge!(prepare_column_options(column).except!(:null))
+ spec.merge!(prepare_column_options(column).except!(:null, :comment))
spec[:default] ||= "nil" if explicit_primary_key_default?(column)
spec
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
index 7041d92586..88367c79a1 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
@@ -2,7 +2,6 @@
require "active_record/migration/join_table"
require "active_support/core_ext/string/access"
-require "active_support/deprecation"
require "digest/sha2"
module ActiveRecord
@@ -101,7 +100,7 @@ module ActiveRecord
def index_exists?(table_name, column_name, options = {})
column_names = Array(column_name).map(&:to_s)
checks = []
- checks << lambda { |i| i.columns == column_names }
+ checks << lambda { |i| Array(i.columns) == column_names }
checks << lambda { |i| i.unique } if options[:unique]
checks << lambda { |i| i.name == options[:name].to_s } if options[:name]
@@ -291,25 +290,27 @@ module ActiveRecord
# SELECT * FROM orders INNER JOIN line_items ON order_id=orders.id
#
# See also TableDefinition#column for details on how to create columns.
- def create_table(table_name, **options)
- td = create_table_definition(table_name, options)
+ def create_table(table_name, id: :primary_key, primary_key: nil, force: nil, **options)
+ td = create_table_definition(
+ table_name, options.extract!(:temporary, :if_not_exists, :options, :as, :comment)
+ )
- if options[:id] != false && !options[:as]
- pk = options.fetch(:primary_key) do
- Base.get_primary_key table_name.to_s.singularize
- end
+ if id && !td.as
+ pk = primary_key || Base.get_primary_key(table_name.to_s.singularize)
if pk.is_a?(Array)
td.primary_keys pk
else
- td.primary_key pk, options.fetch(:id, :primary_key), options
+ td.primary_key pk, id, options
end
end
yield td if block_given?
- if options[:force]
- drop_table(table_name, options.merge(if_exists: true))
+ if force
+ drop_table(table_name, force: force, if_exists: true)
+ else
+ schema_cache.clear_data_source_cache!(table_name.to_s)
end
result = execute schema_creation.accept td
@@ -321,7 +322,7 @@ module ActiveRecord
end
if supports_comments? && !supports_comments_in_create?
- if table_comment = options[:comment].presence
+ if table_comment = td.comment.presence
change_table_comment(table_name, table_comment)
end
@@ -499,6 +500,7 @@ module ActiveRecord
# it can be helpful to provide these in a migration's +change+ method so it can be reverted.
# In that case, +options+ and the block will be used by #create_table.
def drop_table(table_name, options = {})
+ schema_cache.clear_data_source_cache!(table_name.to_s)
execute "DROP TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}"
end
@@ -518,14 +520,15 @@ module ActiveRecord
# Available options are (none of these exists by default):
# * <tt>:limit</tt> -
# Requests a maximum column length. This is the number of characters for a <tt>:string</tt> column
- # and number of bytes for <tt>:text</tt>, <tt>:binary</tt> and <tt>:integer</tt> columns.
+ # and number of bytes for <tt>:text</tt>, <tt>:binary</tt>, and <tt>:integer</tt> columns.
# This option is ignored by some backends.
# * <tt>:default</tt> -
# The column's default value. Use +nil+ for +NULL+.
# * <tt>:null</tt> -
# Allows or disallows +NULL+ values in the column.
# * <tt>:precision</tt> -
- # Specifies the precision for the <tt>:decimal</tt> and <tt>:numeric</tt> columns.
+ # Specifies the precision for the <tt>:decimal</tt>, <tt>:numeric</tt>,
+ # <tt>:datetime</tt>, and <tt>:time</tt> columns.
# * <tt>:scale</tt> -
# Specifies the scale for the <tt>:decimal</tt> and <tt>:numeric</tt> columns.
# * <tt>:collation</tt> -
@@ -735,7 +738,7 @@ module ActiveRecord
#
# CREATE UNIQUE INDEX index_accounts_on_branch_id_and_party_id ON accounts(branch_id, party_id) WHERE active
#
- # Note: Partial indexes are only supported for PostgreSQL and SQLite 3.8.0+.
+ # Note: Partial indexes are only supported for PostgreSQL and SQLite.
#
# ====== Creating an index with a specific method
#
@@ -770,6 +773,17 @@ module ActiveRecord
# CREATE FULLTEXT INDEX index_developers_on_name ON developers (name) -- MySQL
#
# Note: only supported by MySQL.
+ #
+ # ====== Creating an index with a specific algorithm
+ #
+ # add_index(:developers, :name, algorithm: :concurrently)
+ # # CREATE INDEX CONCURRENTLY developers_on_name on developers (name)
+ #
+ # Note: only supported by PostgreSQL.
+ #
+ # Concurrently adding an index is not supported in a transaction.
+ #
+ # For more information see the {"Transactional Migrations" section}[rdoc-ref:Migration].
def add_index(table_name, column_name, options = {})
index_name, index_type, index_columns, index_options = add_index_options(table_name, column_name, options)
execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} (#{index_columns})#{index_options}"
@@ -793,6 +807,15 @@ module ActiveRecord
#
# remove_index :accounts, name: :by_branch_party
#
+ # Removes the index named +by_branch_party+ in the +accounts+ table +concurrently+.
+ #
+ # remove_index :accounts, name: :by_branch_party, algorithm: :concurrently
+ #
+ # Note: only supported by PostgreSQL.
+ #
+ # Concurrently removing an index is not supported in a transaction.
+ #
+ # For more information see the {"Transactional Migrations" section}[rdoc-ref:Migration].
def remove_index(table_name, options = {})
index_name = index_name_for_remove(table_name, options)
execute "DROP INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)}"
@@ -1040,8 +1063,8 @@ module ActiveRecord
options
end
- def dump_schema_information #:nodoc:
- versions = ActiveRecord::SchemaMigration.all_versions
+ def dump_schema_information # :nodoc:
+ versions = schema_migration.all_versions
insert_versions_sql(versions) if versions.any?
end
@@ -1057,7 +1080,7 @@ module ActiveRecord
end
version = version.to_i
- sm_table = quote_table_name(ActiveRecord::SchemaMigration.table_name)
+ sm_table = quote_table_name(schema_migration.table_name)
migrated = migration_context.get_all_versions
versions = migration_context.migrations.map(&:version)
@@ -1430,7 +1453,7 @@ module ActiveRecord
end
def insert_versions_sql(versions)
- sm_table = quote_table_name(ActiveRecord::SchemaMigration.table_name)
+ sm_table = quote_table_name(schema_migration.table_name)
if versions.is_a?(Array)
sql = +"INSERT INTO #{sm_table} (version) VALUES\n"
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb b/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb
index c9e84e48cc..53ce8df491 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb
@@ -5,10 +5,11 @@ module ActiveRecord
class TransactionState
def initialize(state = nil)
@state = state
- @children = []
+ @children = nil
end
def add_child(state)
+ @children ||= []
@children << state
end
@@ -41,12 +42,12 @@ module ActiveRecord
end
def rollback!
- @children.each { |c| c.rollback! }
+ @children&.each { |c| c.rollback! }
@state = :rolledback
end
def full_rollback!
- @children.each { |c| c.rollback! }
+ @children&.each { |c| c.rollback! }
@state = :fully_rolledback
end
@@ -75,18 +76,19 @@ module ActiveRecord
class Transaction #:nodoc:
attr_reader :connection, :state, :records, :savepoint_name, :isolation_level
- def initialize(connection, options, run_commit_callbacks: false)
+ def initialize(connection, isolation: nil, joinable: true, run_commit_callbacks: false)
@connection = connection
@state = TransactionState.new
- @records = []
- @isolation_level = options[:isolation]
+ @records = nil
+ @isolation_level = isolation
@materialized = false
- @joinable = options.fetch(:joinable, true)
+ @joinable = joinable
@run_commit_callbacks = run_commit_callbacks
end
def add_record(record)
- records << record
+ @records ||= []
+ @records << record
end
def materialize!
@@ -98,32 +100,42 @@ module ActiveRecord
end
def rollback_records
- ite = records.uniq
+ return unless records
+ ite = records.uniq(&:object_id)
+ already_run_callbacks = {}
while record = ite.shift
- record.rolledback!(force_restore_state: full_rollback?)
+ trigger_callbacks = record.trigger_transactional_callbacks?
+ should_run_callbacks = !already_run_callbacks[record] && trigger_callbacks
+ already_run_callbacks[record] ||= trigger_callbacks
+ record.rolledback!(force_restore_state: full_rollback?, should_run_callbacks: should_run_callbacks)
end
ensure
- ite.each do |i|
+ ite&.each do |i|
i.rolledback!(force_restore_state: full_rollback?, should_run_callbacks: false)
end
end
def before_commit_records
- records.uniq.each(&:before_committed!) if @run_commit_callbacks
+ records.uniq.each(&:before_committed!) if records && @run_commit_callbacks
end
def commit_records
- ite = records.uniq
+ return unless records
+ ite = records.uniq(&:object_id)
+ already_run_callbacks = {}
while record = ite.shift
if @run_commit_callbacks
- record.committed!
+ trigger_callbacks = record.trigger_transactional_callbacks?
+ should_run_callbacks = !already_run_callbacks[record] && trigger_callbacks
+ already_run_callbacks[record] ||= trigger_callbacks
+ record.committed!(should_run_callbacks: should_run_callbacks)
else
# if not running callbacks, only adds the record to the parent transaction
connection.add_transaction_record(record)
end
end
ensure
- ite.each { |i| i.committed!(should_run_callbacks: false) }
+ ite&.each { |i| i.committed!(should_run_callbacks: false) }
end
def full_rollback?; true; end
@@ -133,8 +145,8 @@ module ActiveRecord
end
class SavepointTransaction < Transaction
- def initialize(connection, savepoint_name, parent_transaction, *args)
- super(connection, *args)
+ def initialize(connection, savepoint_name, parent_transaction, **options)
+ super(connection, options)
parent_transaction.state.add_child(@state)
@@ -194,18 +206,29 @@ module ActiveRecord
@lazy_transactions_enabled = true
end
- def begin_transaction(options = {})
+ def begin_transaction(isolation: nil, joinable: true, _lazy: true)
@connection.lock.synchronize do
run_commit_callbacks = !current_transaction.joinable?
transaction =
if @stack.empty?
- RealTransaction.new(@connection, options, run_commit_callbacks: run_commit_callbacks)
+ RealTransaction.new(
+ @connection,
+ isolation: isolation,
+ joinable: joinable,
+ run_commit_callbacks: run_commit_callbacks
+ )
else
- SavepointTransaction.new(@connection, "active_record_#{@stack.size}", @stack.last, options,
- run_commit_callbacks: run_commit_callbacks)
+ SavepointTransaction.new(
+ @connection,
+ "active_record_#{@stack.size}",
+ @stack.last,
+ isolation: isolation,
+ joinable: joinable,
+ run_commit_callbacks: run_commit_callbacks
+ )
end
- if @connection.supports_lazy_transactions? && lazy_transactions_enabled? && options[:_lazy] != false
+ if @connection.supports_lazy_transactions? && lazy_transactions_enabled? && _lazy
@has_unmaterialized_transactions = true
else
transaction.materialize!
@@ -266,9 +289,9 @@ module ActiveRecord
end
end
- def within_new_transaction(options = {})
+ def within_new_transaction(isolation: nil, joinable: true)
@connection.lock.synchronize do
- transaction = begin_transaction options
+ transaction = begin_transaction(isolation: isolation, joinable: joinable)
yield
rescue Exception => error
if transaction
@@ -301,7 +324,6 @@ module ActiveRecord
end
private
-
NULL_TRANSACTION = NullTransaction.new
# Deallocate invalidated prepared statements outside of the transaction
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
index 200184c2f9..dc970c384b 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
@@ -6,7 +6,6 @@ require "active_record/connection_adapters/sql_type_metadata"
require "active_record/connection_adapters/abstract/schema_dumper"
require "active_record/connection_adapters/abstract/schema_creation"
require "active_support/concurrency/load_interlock_aware_monitor"
-require "active_support/deprecation"
require "arel/collectors/bind"
require "arel/collectors/composite"
require "arel/collectors/sql_string"
@@ -78,7 +77,7 @@ module ActiveRecord
SIMPLE_INT = /\A\d+\z/
attr_accessor :pool
- attr_reader :schema_cache, :visitor, :owner, :logger, :lock, :prepared_statements, :prevent_writes
+ attr_reader :visitor, :owner, :logger, :lock, :prepared_statements
alias :in_use? :owner
set_callback :checkin, :after, :enable_lazy_transactions!
@@ -106,6 +105,14 @@ module ActiveRecord
Regexp.union(*parts)
end
+ def self.quoted_column_names # :nodoc:
+ @quoted_column_names ||= {}
+ end
+
+ def self.quoted_table_names # :nodoc:
+ @quoted_table_names ||= {}
+ end
+
def initialize(connection, logger = nil, config = {}) # :nodoc:
super()
@@ -114,11 +121,8 @@ module ActiveRecord
@instrumenter = ActiveSupport::Notifications.instrumenter
@logger = logger
@config = config
- @pool = nil
+ @pool = ActiveRecord::ConnectionAdapters::NullPool.new
@idle_since = Concurrent.monotonic_time
- @schema_cache = SchemaCache.new self
- @quoted_column_names, @quoted_table_names = {}, {}
- @prevent_writes = false
@visitor = arel_visitor
@statements = build_statement_pool
@lock = ActiveSupport::Concurrency::LoadInterlockAwareMonitor.new
@@ -144,19 +148,7 @@ module ActiveRecord
# Returns true if the connection is a replica, or if +prevent_writes+
# is set to true.
def preventing_writes?
- replica? || prevent_writes
- end
-
- # Prevent writing to the database regardless of role.
- #
- # In some cases you may want to prevent writes to the database
- # even if you are on a database that can write. `while_preventing_writes`
- # will prevent writes to the database for the duration of the block.
- def while_preventing_writes
- original, @prevent_writes = @prevent_writes, true
- yield
- ensure
- @prevent_writes = original
+ replica? || ActiveRecord::Base.connection_handler.prevent_writes
end
def migrations_paths # :nodoc:
@@ -164,14 +156,32 @@ module ActiveRecord
end
def migration_context # :nodoc:
- MigrationContext.new(migrations_paths)
+ MigrationContext.new(migrations_paths, schema_migration)
+ end
+
+ def schema_migration # :nodoc:
+ @schema_migration ||= begin
+ conn = self
+ spec_name = conn.pool.spec.name
+ name = "#{spec_name}::SchemaMigration"
+
+ Class.new(ActiveRecord::SchemaMigration) do
+ define_singleton_method(:name) { name }
+ define_singleton_method(:to_s) { name }
+
+ self.connection_specification_name = spec_name
+ end
+ end
end
class Version
include Comparable
- def initialize(version_string)
+ attr_reader :full_version_string
+
+ def initialize(version_string, full_version_string = nil)
@version = version_string.split(".").map(&:to_i)
+ @full_version_string = full_version_string
end
def <=>(version_string)
@@ -203,9 +213,13 @@ module ActiveRecord
@owner = Thread.current
end
+ def schema_cache
+ @pool.get_schema_cache(self)
+ end
+
def schema_cache=(cache)
cache.connection = self
- @schema_cache = cache
+ @pool.set_schema_cache(cache)
end
# this method must only be called while holding connection pool's mutex
@@ -256,6 +270,11 @@ module ActiveRecord
self.class::ADAPTER_NAME
end
+ # Does the database for this adapter exist?
+ def self.database_exists?(config)
+ raise NotImplementedError
+ end
+
# Does this adapter support DDL rollbacks in transactions? That is, would
# CREATE TABLE or ALTER TABLE get rolled back by a transaction?
def supports_ddl_transactions?
@@ -484,6 +503,9 @@ module ActiveRecord
#
# Prevent @connection's finalizer from touching the socket, or
# otherwise communicating with its server, when it is collected.
+ if schema_cache.connection == self
+ schema_cache.connection = nil
+ end
end
# Reset the state of this connection, directing the DBMS to clear
@@ -584,7 +606,6 @@ module ActiveRecord
end
private
-
def type_map
@type_map ||= Type::TypeMap.new.tap do |mapping|
initialize_type_map(mapping)
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 8b907759c6..ef1eef6b69 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
@@ -45,7 +45,6 @@ module ActiveRecord
class StatementPool < ConnectionAdapters::StatementPool # :nodoc:
private
-
def dealloc(stmt)
stmt.close
end
@@ -56,7 +55,9 @@ module ActiveRecord
end
def get_database_version #:nodoc:
- Version.new(version_string)
+ full_version_string = get_full_version
+ version_string = version_string(full_version_string)
+ Version.new(version_string, full_version_string)
end
def mariadb? # :nodoc:
@@ -174,15 +175,6 @@ module ActiveRecord
# DATABASE STATEMENTS ======================================
#++
- def explain(arel, binds = [])
- sql = "EXPLAIN #{to_sql(arel, binds)}"
- start = Concurrent.monotonic_time
- result = exec_query(sql, "EXPLAIN", binds)
- elapsed = Concurrent.monotonic_time - start
-
- MySQL::ExplainPrettyPrinter.new.pp(result, elapsed)
- end
-
# Executes the SQL statement in the context of this connection.
def execute(sql, name = nil)
materialize_transactions
@@ -202,7 +194,7 @@ module ActiveRecord
end
def begin_db_transaction
- execute "BEGIN"
+ execute("BEGIN", "TRANSACTION")
end
def begin_isolated_db_transaction(isolation)
@@ -211,11 +203,11 @@ module ActiveRecord
end
def commit_db_transaction #:nodoc:
- execute "COMMIT"
+ execute("COMMIT", "TRANSACTION")
end
def exec_rollback_db_transaction #:nodoc:
- execute "ROLLBACK"
+ execute("ROLLBACK", "TRANSACTION")
end
def empty_insert_statement_value(primary_key = nil)
@@ -296,6 +288,8 @@ module ActiveRecord
# Example:
# rename_table('octopuses', 'octopi')
def rename_table(table_name, new_name)
+ schema_cache.clear_data_source_cache!(table_name.to_s)
+ schema_cache.clear_data_source_cache!(new_name.to_s)
execute "RENAME TABLE #{quote_table_name(table_name)} TO #{quote_table_name(new_name)}"
rename_table_indexes(table_name, new_name)
end
@@ -316,6 +310,7 @@ module ActiveRecord
# it can be helpful to provide these in a migration's +change+ method so it can be reverted.
# In that case, +options+ and the block will be used by create_table.
def drop_table(table_name, options = {})
+ schema_cache.clear_data_source_cache!(table_name.to_s)
execute "DROP#{' TEMPORARY' if options[:temporary]} TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}#{' CASCADE' if options[:force] == :cascade}"
end
@@ -481,12 +476,12 @@ module ActiveRecord
# distinct queries, and requires that the ORDER BY include the distinct column.
# See https://dev.mysql.com/doc/refman/5.7/en/group-by-handling.html
def columns_for_distinct(columns, orders) # :nodoc:
- order_columns = orders.reject(&:blank?).map { |s|
+ order_columns = orders.compact_blank.map { |s|
# Convert Arel node to string
s = s.to_sql unless s.is_a?(String)
# Remove any ASC/DESC modifiers
s.gsub(/\s+(?:ASC|DESC)\b/i, "")
- }.reject(&:blank?).map.with_index { |column, i| "#{column} AS alias_#{i}" }
+ }.compact_blank.map.with_index { |column, i| "#{column} AS alias_#{i}" }
(order_columns << super).join(", ")
end
@@ -520,7 +515,6 @@ module ActiveRecord
end
private
-
def initialize_type_map(m = type_map)
super
@@ -625,7 +619,11 @@ module ActiveRecord
when ER_QUERY_INTERRUPTED
QueryCanceled.new(message, sql: sql, binds: binds)
else
- super
+ if exception.is_a?(Mysql2::Error::TimeoutError)
+ ActiveRecord::AdapterTimeout.new(message, sql: sql, binds: binds)
+ else
+ super
+ end
end
end
@@ -743,7 +741,7 @@ module ActiveRecord
end.compact.join(", ")
# ...and send them all in one query
- execute "SET #{encoding} #{sql_mode_assignment} #{variable_assignments}"
+ execute("SET #{encoding} #{sql_mode_assignment} #{variable_assignments}", "SCHEMA")
end
def column_definitions(table_name) # :nodoc:
@@ -788,8 +786,8 @@ module ActiveRecord
MismatchedForeignKey.new(options)
end
- def version_string
- full_version.match(/^(?:5\.5\.5-)?(\d+\.\d+\.\d+)/)[1]
+ def version_string(full_version_string)
+ full_version_string.match(/^(?:5\.5\.5-)?(\d+\.\d+\.\d+)/)[1]
end
class MysqlString < Type::String # :nodoc:
@@ -802,7 +800,6 @@ module ActiveRecord
end
private
-
def cast_value(value)
case value
when true then "1"
diff --git a/activerecord/lib/active_record/connection_adapters/column.rb b/activerecord/lib/active_record/connection_adapters/column.rb
index 279d0b9e84..2708d2756b 100644
--- a/activerecord/lib/active_record/connection_adapters/column.rb
+++ b/activerecord/lib/active_record/connection_adapters/column.rb
@@ -5,6 +5,8 @@ module ActiveRecord
module ConnectionAdapters
# An abstract definition of a column in a table.
class Column
+ include Deduplicable
+
attr_reader :name, :default, :sql_type_metadata, :null, :default_function, :collation, :comment
delegate :precision, :scale, :limit, :type, :sql_type, to: :sql_type_metadata, allow_nil: true
@@ -76,6 +78,7 @@ module ActiveRecord
def hash
Column.hash ^
name.hash ^
+ name.encoding.hash ^
default.hash ^
sql_type_metadata.hash ^
null.hash ^
@@ -83,6 +86,17 @@ module ActiveRecord
collation.hash ^
comment.hash
end
+
+ private
+ def deduplicated
+ @name = -name
+ @sql_type_metadata = sql_type_metadata.deduplicate if sql_type_metadata
+ @default = -default if default
+ @default_function = -default_function if default_function
+ @collation = -collation if collation
+ @comment = -comment if comment
+ super
+ end
end
class NullColumn < Column
diff --git a/activerecord/lib/active_record/connection_adapters/connection_specification.rb b/activerecord/lib/active_record/connection_adapters/connection_specification.rb
index 9eaf9d9a89..0732b69f81 100644
--- a/activerecord/lib/active_record/connection_adapters/connection_specification.rb
+++ b/activerecord/lib/active_record/connection_adapters/connection_specification.rb
@@ -50,13 +50,12 @@ module ActiveRecord
# Converts the given URL to a full connection hash.
def to_hash
- config = raw_config.reject { |_, value| value.blank? }
+ config = raw_config.compact_blank
config.map { |key, value| config[key] = uri_parser.unescape(value) if value.is_a? String }
config
end
private
-
attr_reader :uri
def uri_parser
diff --git a/activerecord/lib/active_record/connection_adapters/deduplicable.rb b/activerecord/lib/active_record/connection_adapters/deduplicable.rb
new file mode 100644
index 0000000000..fb2fd60bbc
--- /dev/null
+++ b/activerecord/lib/active_record/connection_adapters/deduplicable.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+module ActiveRecord
+ module ConnectionAdapters # :nodoc:
+ module Deduplicable
+ extend ActiveSupport::Concern
+
+ module ClassMethods
+ def registry
+ @registry ||= {}
+ end
+
+ def new(*)
+ super.deduplicate
+ end
+ end
+
+ def deduplicate
+ self.class.registry[self] ||= deduplicated
+ end
+ alias :-@ :deduplicate
+
+ private
+ def deduplicated
+ freeze
+ end
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb b/activerecord/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb
index 1df4dea2d8..97d74df529 100644
--- a/activerecord/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb
+++ b/activerecord/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb
@@ -5,7 +5,7 @@ module ActiveRecord
module DetermineIfPreparableVisitor
attr_accessor :preparable
- def accept(*)
+ def accept(object, collector)
@preparable = true
super
end
@@ -20,7 +20,7 @@ module ActiveRecord
super
end
- def visit_Arel_Nodes_SqlLiteral(*)
+ def visit_Arel_Nodes_SqlLiteral(o, collector)
@preparable = false
super
end
diff --git a/activerecord/lib/active_record/connection_adapters/mysql/database_statements.rb b/activerecord/lib/active_record/connection_adapters/mysql/database_statements.rb
index 2132e5d248..bbcdc96cdc 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql/database_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql/database_statements.rb
@@ -26,6 +26,15 @@ module ActiveRecord
!READ_QUERY.match?(sql)
end
+ def explain(arel, binds = [])
+ sql = "EXPLAIN #{to_sql(arel, binds)}"
+ start = Concurrent.monotonic_time
+ result = exec_query(sql, "EXPLAIN", binds)
+ elapsed = Concurrent.monotonic_time - start
+
+ MySQL::ExplainPrettyPrinter.new.pp(result, elapsed)
+ end
+
# Executes the SQL statement in the context of this connection.
def execute(sql, name = nil)
if preventing_writes? && write_query?(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 20c3c83664..edd5ea0542 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
@@ -37,7 +37,6 @@ module ActiveRecord
end
private
-
def compute_column_widths(result)
[].tap do |widths|
result.columns.each_with_index do |column, i|
diff --git a/activerecord/lib/active_record/connection_adapters/mysql/quoting.rb b/activerecord/lib/active_record/connection_adapters/mysql/quoting.rb
index 75564a61d6..0069f5871c 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql/quoting.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql/quoting.rb
@@ -5,11 +5,11 @@ module ActiveRecord
module MySQL
module Quoting # :nodoc:
def quote_column_name(name)
- @quoted_column_names[name] ||= "`#{super.gsub('`', '``')}`"
+ self.class.quoted_column_names[name] ||= "`#{super.gsub('`', '``')}`"
end
def quote_table_name(name)
- @quoted_table_names[name] ||= super.gsub(".", "`.`").freeze
+ self.class.quoted_table_names[name] ||= super.gsub(".", "`.`").freeze
end
def unquoted_true
@@ -32,12 +32,49 @@ module ActiveRecord
"x'#{value.hex}'"
end
- def _type_cast(value)
- case value
- when Date, Time then value
- else super
- end
+ def column_name_matcher
+ COLUMN_NAME
+ end
+
+ def column_name_with_order_matcher
+ COLUMN_NAME_WITH_ORDER
end
+
+ COLUMN_NAME = /
+ \A
+ (
+ (?:
+ # `table_name`.`column_name` | function(one or no argument)
+ ((?:\w+\.|`\w+`\.)?(?:\w+|`\w+`)) | \w+\((?:|\g<2>)\)
+ )
+ (?:(?:\s+AS)?\s+(?:\w+|`\w+`))?
+ )
+ (?:\s*,\s*\g<1>)*
+ \z
+ /ix
+
+ COLUMN_NAME_WITH_ORDER = /
+ \A
+ (
+ (?:
+ # `table_name`.`column_name` | function(one or no argument)
+ ((?:\w+\.|`\w+`\.)?(?:\w+|`\w+`)) | \w+\((?:|\g<2>)\)
+ )
+ (?:\s+ASC|\s+DESC)?
+ )
+ (?:\s*,\s*\g<1>)*
+ \z
+ /ix
+
+ private_constant :COLUMN_NAME, :COLUMN_NAME_WITH_ORDER
+
+ private
+ def _type_cast(value)
+ case value
+ when Date, Time then value
+ else super
+ end
+ end
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/mysql/schema_creation.rb b/activerecord/lib/active_record/connection_adapters/mysql/schema_creation.rb
index 82ed320617..0f5ab7562a 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql/schema_creation.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql/schema_creation.rb
@@ -7,7 +7,6 @@ module ActiveRecord
delegate :add_sql_comment!, :mariadb?, to: :@conn, private: true
private
-
def visit_DropForeignKey(name)
"DROP FOREIGN KEY #{name}"
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 234fb25fdf..bcd300f3db 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql/schema_dumper.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql/schema_dumper.rb
@@ -41,13 +41,15 @@ module ActiveRecord
case column.sql_type
when /\Atimestamp\b/
:timestamp
+ when /\A(?:enum|set)\b/
+ column.sql_type
else
super
end
end
def schema_limit(column)
- super unless /\A(?:tiny|medium|long)?(?:text|blob)/.match?(column.sql_type)
+ super unless /\A(?:enum|set|(?:tiny|medium|long)?(?:text|blob))\b/.match?(column.sql_type)
end
def schema_precision(column)
diff --git a/activerecord/lib/active_record/connection_adapters/mysql/type_metadata.rb b/activerecord/lib/active_record/connection_adapters/mysql/type_metadata.rb
index 9167593064..a7232fa249 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql/type_metadata.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql/type_metadata.rb
@@ -6,9 +6,11 @@ module ActiveRecord
class TypeMetadata < DelegateClass(SqlTypeMetadata) # :nodoc:
undef to_yaml if method_defined?(:to_yaml)
+ include Deduplicable
+
attr_reader :extra
- def initialize(type_metadata, extra: "")
+ def initialize(type_metadata, extra: nil)
super(type_metadata)
@extra = extra
end
@@ -25,6 +27,13 @@ module ActiveRecord
__getobj__.hash ^
extra.hash
end
+
+ private
+ def deduplicated
+ __setobj__(__getobj__.deduplicate)
+ @extra = -extra if extra
+ super
+ end
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb
index 0dc880c731..1df9ac32c9 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb
@@ -8,6 +8,8 @@ require "mysql2"
module ActiveRecord
module ConnectionHandling # :nodoc:
+ ER_BAD_DB_ERROR = 1049
+
# Establishes a connection to the database that's used by all Active Record objects.
def mysql2_connection(config)
config = config.symbolize_keys
@@ -22,7 +24,7 @@ module ActiveRecord
client = Mysql2::Client.new(config)
ConnectionAdapters::Mysql2Adapter.new(client, logger, nil, config)
rescue Mysql2::Error => error
- if error.message.include?("Unknown database")
+ if error.error_number == ER_BAD_DB_ERROR
raise ActiveRecord::NoDatabaseError
else
raise
@@ -42,6 +44,12 @@ module ActiveRecord
configure_connection
end
+ def self.database_exists?(config)
+ !!ActiveRecord::Base.mysql2_connection(config)
+ rescue ActiveRecord::NoDatabaseError
+ false
+ end
+
def supports_json?
!mariadb? && database_version >= "5.7.8"
end
@@ -109,12 +117,12 @@ module ActiveRecord
end
def discard! # :nodoc:
+ super
@connection.automatic_close = false
@connection = nil
end
private
-
def connect
@connection = Mysql2::Client.new(@config)
configure_connection
@@ -126,7 +134,11 @@ module ActiveRecord
end
def full_version
- @full_version ||= @connection.server_info[:version]
+ schema_cache.database_version.full_version_string
+ end
+
+ def get_full_version
+ @connection.server_info[:version]
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/column.rb b/activerecord/lib/active_record/connection_adapters/postgresql/column.rb
index ec25bb1e19..f1ecf6df30 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/column.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/column.rb
@@ -23,6 +23,29 @@ module ActiveRecord
def sql_type
super.sub(/\[\]\z/, "")
end
+
+ def init_with(coder)
+ @serial = coder["serial"]
+ super
+ end
+
+ def encode_with(coder)
+ coder["serial"] = @serial
+ super
+ end
+
+ def ==(other)
+ other.is_a?(Column) &&
+ super &&
+ serial? == other.serial?
+ end
+ alias :eql? :==
+
+ def hash
+ Column.hash ^
+ super.hash ^
+ serial?.hash
+ end
end
end
PostgreSQLColumn = PostgreSQL::Column # :nodoc:
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 d872bd662f..45ec79ca78 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb
@@ -145,7 +145,7 @@ module ActiveRecord
# Begins a transaction.
def begin_db_transaction
- execute "BEGIN"
+ execute("BEGIN", "TRANSACTION")
end
def begin_isolated_db_transaction(isolation)
@@ -155,12 +155,12 @@ module ActiveRecord
# Commits a transaction.
def commit_db_transaction
- execute "COMMIT"
+ execute("COMMIT", "TRANSACTION")
end
# Aborts a transaction.
def exec_rollback_db_transaction
- execute "ROLLBACK"
+ execute("ROLLBACK", "TRANSACTION")
end
private
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 b1dfbde86e..0bbe98145a 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/array.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/array.rb
@@ -77,7 +77,6 @@ module ActiveRecord
end
private
-
def type_cast_array(value, method)
if value.is_a?(::Array)
value.map { |item| type_cast_array(item, method) }
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/enum.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/enum.rb
index f70f09ad95..bae34472e1 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/enum.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/enum.rb
@@ -10,7 +10,6 @@ module ActiveRecord
end
private
-
def cast_value(value)
value.to_s
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/hstore.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/hstore.rb
index 7b42677101..8d4dacbd64 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/hstore.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/hstore.rb
@@ -46,7 +46,6 @@ module ActiveRecord
end
private
-
HstorePair = begin
quoted_string = /"[^"\\]*(?:\\.[^"\\]*)*"/
unquoted_string = /(?:\\.|[^\s,])[^\s=,\\]*(?:\\.[^\s=,\\]*|=[^,>])*/
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb
index 7f6adc351c..e52d4385ef 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb
@@ -34,7 +34,6 @@ module ActiveRecord
end
private
-
def number_for_point(number)
number.to_s.gsub(/\.0$/, "")
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/money.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/money.rb
index 6434377b57..357493dfc0 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/money.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/money.rb
@@ -26,9 +26,9 @@ module ActiveRecord
value = value.sub(/^\((.+)\)$/, '-\1') # (4)
case value
- when /^-?\D+[\d,]+\.\d{2}$/ # (1)
+ when /^-?\D*[\d,]+\.\d{2}$/ # (1)
value.gsub!(/[^-\d.]/, "")
- when /^-?\D+[\d.]+,\d{2}$/ # (2)
+ when /^-?\D*[\d.]+,\d{2}$/ # (2)
value.gsub!(/[^-\d,]/, "").sub!(/,/, ".")
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/point.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/point.rb
index 8c74cecc4d..e81e18ff70 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/point.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/point.rb
@@ -50,7 +50,6 @@ module ActiveRecord
end
private
-
def number_for_point(number)
number.to_s.gsub(/\.0$/, "")
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/range.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/range.rb
index aa7701e038..d19f1f9cf8 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/range.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/range.rb
@@ -58,7 +58,6 @@ module ActiveRecord
end
private
-
def type_cast_single(value)
infinity?(value) ? value : @subtype.deserialize(value)
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/uuid.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/uuid.rb
index 28abdbd073..74a28eef58 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/uuid.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/uuid.rb
@@ -14,7 +14,6 @@ module ActiveRecord
end
private
-
def cast_value(value)
casted = value.to_s
casted if casted.match?(ACCEPTABLE_UUID)
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb b/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb
index d40e0ef1f0..07b66de366 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb
@@ -30,7 +30,7 @@ module ActiveRecord
# - "schema.name".table_name
# - "schema.name"."table.name"
def quote_table_name(name) # :nodoc:
- @quoted_table_names[name] ||= Utils.extract_schema_qualified_name(name.to_s).quoted.freeze
+ self.class.quoted_table_names[name] ||= Utils.extract_schema_qualified_name(name.to_s).quoted.freeze
end
# Quotes schema names for use in SQL queries.
@@ -44,7 +44,7 @@ module ActiveRecord
# Quotes column names for use in SQL queries.
def quote_column_name(name) # :nodoc:
- @quoted_column_names[name] ||= PG::Connection.quote_ident(super).freeze
+ self.class.quoted_column_names[name] ||= PG::Connection.quote_ident(super).freeze
end
# Quote date/time values for use in SQL input.
@@ -78,6 +78,43 @@ module ActiveRecord
type_map.lookup(column.oid, column.fmod, column.sql_type)
end
+ def column_name_matcher
+ COLUMN_NAME
+ end
+
+ def column_name_with_order_matcher
+ COLUMN_NAME_WITH_ORDER
+ end
+
+ COLUMN_NAME = /
+ \A
+ (
+ (?:
+ # "table_name"."column_name"::type_name | function(one or no argument)::type_name
+ ((?:\w+\.|"\w+"\.)?(?:\w+|"\w+")(?:::\w+)?) | \w+\((?:|\g<2>)\)(?:::\w+)?
+ )
+ (?:(?:\s+AS)?\s+(?:\w+|"\w+"))?
+ )
+ (?:\s*,\s*\g<1>)*
+ \z
+ /ix
+
+ COLUMN_NAME_WITH_ORDER = /
+ \A
+ (
+ (?:
+ # "table_name"."column_name"::type_name | function(one or no argument)::type_name
+ ((?:\w+\.|"\w+"\.)?(?:\w+|"\w+")(?:::\w+)?) | \w+\((?:|\g<2>)\)(?:::\w+)?
+ )
+ (?:\s+ASC|\s+DESC)?
+ (?:\s+NULLS\s+(?:FIRST|LAST))?
+ )
+ (?:\s*,\s*\g<1>)*
+ \z
+ /ix
+
+ private_constant :COLUMN_NAME, :COLUMN_NAME_WITH_ORDER
+
private
def lookup_cast_type(sql_type)
super(query_value("SELECT #{quote(sql_type)}::regtype::oid", "SCHEMA").to_i)
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 84643d20da..d201e40190 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_dumper.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_dumper.rb
@@ -5,7 +5,6 @@ module ActiveRecord
module PostgreSQL
class SchemaDumper < ConnectionAdapters::SchemaDumper # :nodoc:
private
-
def extensions(stream)
extensions = @connection.extensions
if extensions.any?
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 40c5e51d92..628a609521 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
@@ -55,6 +55,7 @@ module ActiveRecord
end
def drop_table(table_name, options = {}) # :nodoc:
+ schema_cache.clear_data_source_cache!(table_name.to_s)
execute "DROP TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}#{' CASCADE' if options[:force] == :cascade}"
end
@@ -376,6 +377,8 @@ module ActiveRecord
# rename_table('octopuses', 'octopi')
def rename_table(table_name, new_name)
clear_cache!
+ schema_cache.clear_data_source_cache!(table_name.to_s)
+ schema_cache.clear_data_source_cache!(new_name.to_s)
execute "ALTER TABLE #{quote_table_name(table_name)} RENAME TO #{quote_table_name(new_name)}"
pk, seq = pk_and_sequence_for(new_name)
if pk
@@ -552,13 +555,13 @@ module ActiveRecord
# PostgreSQL requires the ORDER BY columns in the select list for distinct queries, and
# requires that the ORDER BY include the distinct column.
def columns_for_distinct(columns, orders) #:nodoc:
- order_columns = orders.reject(&:blank?).map { |s|
+ order_columns = orders.compact_blank.map { |s|
# Convert Arel node to string
s = s.to_sql unless s.is_a?(String)
# Remove any ASC/DESC modifiers
s.gsub(/\s+(?:ASC|DESC)\b/i, "")
.gsub(/\s+NULLS\s+(?:FIRST|LAST)\b/i, "")
- }.reject(&:blank?).map.with_index { |column, i| "#{column} AS alias_#{i}" }
+ }.compact_blank.map.with_index { |column, i| "#{column} AS alias_#{i}" }
(order_columns << super).join(", ")
end
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 8bdec623af..b7f6479357 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/type_metadata.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/type_metadata.rb
@@ -7,6 +7,8 @@ module ActiveRecord
class TypeMetadata < DelegateClass(SqlTypeMetadata)
undef to_yaml if method_defined?(:to_yaml)
+ include Deduplicable
+
attr_reader :oid, :fmod
def initialize(type_metadata, oid: nil, fmod: nil)
@@ -29,6 +31,12 @@ module ActiveRecord
oid.hash ^
fmod.hash
end
+
+ private
+ def deduplicated
+ __setobj__(__getobj__.deduplicate)
+ super
+ end
end
end
PostgreSQLTypeMetadata = PostgreSQL::TypeMetadata
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/utils.rb b/activerecord/lib/active_record/connection_adapters/postgresql/utils.rb
index f2f4701500..e8caeb8132 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/utils.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/utils.rb
@@ -37,7 +37,6 @@ module ActiveRecord
end
protected
-
def parts
@parts ||= [@schema, @identifier].compact
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
index 91318a0af1..0a7c6d8ac4 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -46,7 +46,7 @@ module ActiveRecord
conn = PG.connect(conn_params)
ConnectionAdapters::PostgreSQLAdapter.new(conn, logger, conn_params, config)
rescue ::PG::Error => error
- if error.message.include?("does not exist")
+ if error.message.include?(conn_params[:dbname])
raise ActiveRecord::NoDatabaseError
else
raise
@@ -259,6 +259,12 @@ module ActiveRecord
@use_insert_returning = @config.key?(:insert_returning) ? self.class.type_cast_config_to_boolean(@config[:insert_returning]) : true
end
+ def self.database_exists?(config)
+ !!ActiveRecord::Base.postgresql_connection(config)
+ rescue ActiveRecord::NoDatabaseError
+ false
+ end
+
# Is this connection alive and ready for queries?
def active?
@lock.synchronize do
@@ -302,6 +308,7 @@ module ActiveRecord
end
def discard! # :nodoc:
+ super
@connection.socket_io.reopen(IO::NULL) rescue nil
@connection = nil
end
@@ -452,7 +459,6 @@ module ActiveRecord
end
private
-
# See https://www.postgresql.org/docs/current/static/errcodes-appendix.html
VALUE_LIMIT_VIOLATION = "22001"
NUMERIC_VALUE_OUT_OF_RANGE = "22003"
diff --git a/activerecord/lib/active_record/connection_adapters/schema_cache.rb b/activerecord/lib/active_record/connection_adapters/schema_cache.rb
index dbfe1e4a34..5e30304864 100644
--- a/activerecord/lib/active_record/connection_adapters/schema_cache.rb
+++ b/activerecord/lib/active_record/connection_adapters/schema_cache.rb
@@ -27,7 +27,6 @@ module ActiveRecord
def encode_with(coder)
coder["columns"] = @columns
- coder["columns_hash"] = @columns_hash
coder["primary_keys"] = @primary_keys
coder["data_sources"] = @data_sources
coder["indexes"] = @indexes
@@ -37,16 +36,21 @@ module ActiveRecord
def init_with(coder)
@columns = coder["columns"]
- @columns_hash = coder["columns_hash"]
@primary_keys = coder["primary_keys"]
@data_sources = coder["data_sources"]
@indexes = coder["indexes"] || {}
@version = coder["version"]
@database_version = coder["database_version"]
+
+ derive_columns_hash_and_deduplicate_values
end
def primary_keys(table_name)
- @primary_keys[table_name] ||= data_source_exists?(table_name) ? connection.primary_key(table_name) : nil
+ @primary_keys.fetch(table_name) do
+ if data_source_exists?(table_name)
+ @primary_keys[deep_deduplicate(table_name)] = deep_deduplicate(connection.primary_key(table_name))
+ end
+ end
end
# A cached lookup for table existence.
@@ -54,7 +58,7 @@ module ActiveRecord
prepare_data_sources if @data_sources.empty?
return @data_sources[name] if @data_sources.key? name
- @data_sources[name] = connection.data_source_exists?(name)
+ @data_sources[deep_deduplicate(name)] = connection.data_source_exists?(name)
end
# Add internal cache for table with +table_name+.
@@ -73,15 +77,17 @@ module ActiveRecord
# Get the columns for a table
def columns(table_name)
- @columns[table_name] ||= connection.columns(table_name)
+ @columns.fetch(table_name) do
+ @columns[deep_deduplicate(table_name)] = deep_deduplicate(connection.columns(table_name))
+ end
end
# Get the columns for a table as a hash, key is the column name
# value is the column object.
def columns_hash(table_name)
- @columns_hash[table_name] ||= Hash[columns(table_name).map { |col|
- [col.name, col]
- }]
+ @columns_hash.fetch(table_name) do
+ @columns_hash[deep_deduplicate(table_name)] = columns(table_name).index_by(&:name)
+ end
end
# Checks whether the columns hash is already cached for a table.
@@ -90,7 +96,9 @@ module ActiveRecord
end
def indexes(table_name)
- @indexes[table_name] ||= connection.indexes(table_name)
+ @indexes.fetch(table_name) do
+ @indexes[deep_deduplicate(table_name)] = deep_deduplicate(connection.indexes(table_name))
+ end
end
def database_version # :nodoc:
@@ -124,15 +132,38 @@ module ActiveRecord
def marshal_dump
# if we get current version during initialization, it happens stack over flow.
@version = connection.migration_context.current_version
- [@version, @columns, @columns_hash, @primary_keys, @data_sources, @indexes, database_version]
+ [@version, @columns, {}, @primary_keys, @data_sources, @indexes, database_version]
end
def marshal_load(array)
- @version, @columns, @columns_hash, @primary_keys, @data_sources, @indexes, @database_version = array
- @indexes = @indexes || {}
+ @version, @columns, _columns_hash, @primary_keys, @data_sources, @indexes, @database_version = array
+ @indexes ||= {}
+
+ derive_columns_hash_and_deduplicate_values
end
private
+ def derive_columns_hash_and_deduplicate_values
+ @columns = deep_deduplicate(@columns)
+ @columns_hash = @columns.transform_values { |columns| columns.index_by(&:name) }
+ @primary_keys = deep_deduplicate(@primary_keys)
+ @data_sources = deep_deduplicate(@data_sources)
+ @indexes = deep_deduplicate(@indexes)
+ end
+
+ def deep_deduplicate(value)
+ case value
+ when Hash
+ value.transform_keys { |k| deep_deduplicate(k) }.transform_values { |v| deep_deduplicate(v) }
+ when Array
+ value.map { |i| deep_deduplicate(i) }
+ when String, Deduplicable
+ -value
+ else
+ value
+ end
+ end
+
def prepare_data_sources
connection.data_sources.each { |source| @data_sources[source] = true }
end
diff --git a/activerecord/lib/active_record/connection_adapters/sql_type_metadata.rb b/activerecord/lib/active_record/connection_adapters/sql_type_metadata.rb
index df28df7a7c..969867e70f 100644
--- a/activerecord/lib/active_record/connection_adapters/sql_type_metadata.rb
+++ b/activerecord/lib/active_record/connection_adapters/sql_type_metadata.rb
@@ -1,9 +1,13 @@
# frozen_string_literal: true
+require "active_record/connection_adapters/deduplicable"
+
module ActiveRecord
# :stopdoc:
module ConnectionAdapters
class SqlTypeMetadata
+ include Deduplicable
+
attr_reader :sql_type, :type, :limit, :precision, :scale
def initialize(sql_type: nil, type: nil, limit: nil, precision: nil, scale: nil)
@@ -32,6 +36,12 @@ module ActiveRecord
precision.hash >> 1 ^
scale.hash >> 2
end
+
+ private
+ def deduplicated
+ @sql_type = -sql_type
+ super
+ end
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3/database_statements.rb b/activerecord/lib/active_record/connection_adapters/sqlite3/database_statements.rb
index 46ce1a15b5..85053acf91 100644
--- a/activerecord/lib/active_record/connection_adapters/sqlite3/database_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/sqlite3/database_statements.rb
@@ -11,6 +11,11 @@ module ActiveRecord
!READ_QUERY.match?(sql)
end
+ def explain(arel, binds = [])
+ sql = "EXPLAIN QUERY PLAN #{to_sql(arel, binds)}"
+ SQLite3::ExplainPrettyPrinter.new.pp(exec_query(sql, "EXPLAIN", []))
+ end
+
def execute(sql, name = nil) #:nodoc:
if preventing_writes? && write_query?(sql)
raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
@@ -68,18 +73,17 @@ module ActiveRecord
alias :exec_update :exec_delete
def begin_db_transaction #:nodoc:
- log("begin transaction", nil) { @connection.transaction }
+ log("begin transaction", "TRANSACTION") { @connection.transaction }
end
def commit_db_transaction #:nodoc:
- log("commit transaction", nil) { @connection.commit }
+ log("commit transaction", "TRANSACTION") { @connection.commit }
end
def exec_rollback_db_transaction #:nodoc:
- log("rollback transaction", nil) { @connection.rollback }
+ log("rollback transaction", "TRANSACTION") { @connection.rollback }
end
-
private
def execute_batch(sql, name = nil)
if preventing_writes? && write_query?(sql)
diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3/quoting.rb b/activerecord/lib/active_record/connection_adapters/sqlite3/quoting.rb
index cb9d32a577..9b74a774e5 100644
--- a/activerecord/lib/active_record/connection_adapters/sqlite3/quoting.rb
+++ b/activerecord/lib/active_record/connection_adapters/sqlite3/quoting.rb
@@ -13,11 +13,11 @@ module ActiveRecord
end
def quote_table_name(name)
- @quoted_table_names[name] ||= super.gsub(".", "\".\"").freeze
+ self.class.quoted_table_names[name] ||= super.gsub(".", "\".\"").freeze
end
def quote_column_name(name)
- @quoted_column_names[name] ||= %Q("#{super.gsub('"', '""')}")
+ self.class.quoted_column_names[name] ||= %Q("#{super.gsub('"', '""')}")
end
def quoted_time(value)
@@ -45,8 +45,43 @@ module ActiveRecord
0
end
- private
+ def column_name_matcher
+ COLUMN_NAME
+ end
+
+ def column_name_with_order_matcher
+ COLUMN_NAME_WITH_ORDER
+ end
+ COLUMN_NAME = /
+ \A
+ (
+ (?:
+ # "table_name"."column_name" | function(one or no argument)
+ ((?:\w+\.|"\w+"\.)?(?:\w+|"\w+")) | \w+\((?:|\g<2>)\)
+ )
+ (?:(?:\s+AS)?\s+(?:\w+|"\w+"))?
+ )
+ (?:\s*,\s*\g<1>)*
+ \z
+ /ix
+
+ COLUMN_NAME_WITH_ORDER = /
+ \A
+ (
+ (?:
+ # "table_name"."column_name" | function(one or no argument)
+ ((?:\w+\.|"\w+"\.)?(?:\w+|"\w+")) | \w+\((?:|\g<2>)\)
+ )
+ (?:\s+ASC|\s+DESC)?
+ )
+ (?:\s*,\s*\g<1>)*
+ \z
+ /ix
+
+ private_constant :COLUMN_NAME, :COLUMN_NAME_WITH_ORDER
+
+ private
def _type_cast(value)
case value
when BigDecimal
diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
index f5f5827d04..f4847eb6c0 100644
--- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
@@ -48,8 +48,8 @@ module ActiveRecord
end
module ConnectionAdapters #:nodoc:
- # The SQLite3 adapter works SQLite 3.6.16 or newer
- # with the sqlite3-ruby drivers (available as gem from https://rubygems.org/gems/sqlite3).
+ # The SQLite3 adapter works with the sqlite3-ruby drivers
+ # (available as gem from https://rubygems.org/gems/sqlite3).
#
# Options:
#
@@ -98,6 +98,16 @@ module ActiveRecord
configure_connection
end
+ def self.database_exists?(config)
+ config = config.symbolize_keys
+ if config[:database] == ":memory:"
+ return true
+ else
+ database_file = defined?(Rails.root) ? File.expand_path(config[:database], Rails.root) : config[:database]
+ File.exist?(database_file)
+ end
+ end
+
def supports_ddl_transactions?
true
end
@@ -201,14 +211,6 @@ module ActiveRecord
end
end
- #--
- # DATABASE STATEMENTS ======================================
- #++
- def explain(arel, binds = [])
- sql = "EXPLAIN QUERY PLAN #{to_sql(arel, binds)}"
- SQLite3::ExplainPrettyPrinter.new.pp(exec_query(sql, "EXPLAIN", []))
- end
-
# SCHEMA STATEMENTS ========================================
def primary_keys(table_name) # :nodoc:
@@ -226,6 +228,8 @@ module ActiveRecord
# Example:
# rename_table('octopuses', 'octopi')
def rename_table(table_name, new_name)
+ schema_cache.clear_data_source_cache!(table_name.to_s)
+ schema_cache.clear_data_source_cache!(new_name.to_s)
exec_query "ALTER TABLE #{quote_table_name(table_name)} RENAME TO #{quote_table_name(new_name)}"
rename_table_indexes(table_name, new_name)
end
@@ -389,6 +393,7 @@ module ActiveRecord
if from_primary_key.is_a?(Array)
@definition.primary_keys from_primary_key
end
+
columns(from).each do |column|
column_name = options[:rename] ?
(options[:rename][column.name] ||
@@ -485,9 +490,9 @@ module ActiveRecord
result = exec_query(sql, "SCHEMA").first
if result
- # Splitting with left parentheses and picking up last will return all
+ # Splitting with left parentheses and discarding the first part will return all
# columns separated with comma(,).
- columns_string = result["sql"].split("(").last
+ columns_string = result["sql"].split("(", 2).last
columns_string.split(",").each do |column_string|
# This regex will match the column name and collation type and will save
diff --git a/activerecord/lib/active_record/connection_adapters/statement_pool.rb b/activerecord/lib/active_record/connection_adapters/statement_pool.rb
index 46bd831da7..0960feed84 100644
--- a/activerecord/lib/active_record/connection_adapters/statement_pool.rb
+++ b/activerecord/lib/active_record/connection_adapters/statement_pool.rb
@@ -48,7 +48,6 @@ module ActiveRecord
end
private
-
def cache
@cache[Process.pid]
end
diff --git a/activerecord/lib/active_record/connection_handling.rb b/activerecord/lib/active_record/connection_handling.rb
index 6782833c5a..c8cefa9906 100644
--- a/activerecord/lib/active_record/connection_handling.rb
+++ b/activerecord/lib/active_record/connection_handling.rb
@@ -109,8 +109,8 @@ module ActiveRecord
# a role. If you would like to use a different role you can pass a hash to database:
#
# ActiveRecord::Base.connected_to(database: { readonly_slow: :animals_slow_replica }) do
- # Dog.run_a_long_query # runs a long query while connected to the +animals_slow_replica+
- # using the readonly_slow role.
+ # # runs a long query while connected to the +animals_slow_replica+ using the readonly_slow role.
+ # Dog.run_a_long_query
# end
#
# When using the database key a new connection will be established every time.
@@ -173,7 +173,7 @@ module ActiveRecord
raise "Anonymous class is not allowed." unless name
config_or_env ||= DEFAULT_ENV.call.to_sym
- pool_name = self == Base ? "primary" : name
+ pool_name = primary_class? ? "primary" : name
self.connection_specification_name = pool_name
resolver = ConnectionAdapters::ConnectionSpecification::Resolver.new(Base.configurations)
@@ -204,11 +204,15 @@ module ActiveRecord
# Return the specification name from the current class or its parent.
def connection_specification_name
if !defined?(@connection_specification_name) || @connection_specification_name.nil?
- return self == Base ? "primary" : superclass.connection_specification_name
+ return primary_class? ? "primary" : superclass.connection_specification_name
end
@connection_specification_name
end
+ def primary_class? # :nodoc:
+ self == Base || defined?(ApplicationRecord) && self == ApplicationRecord
+ end
+
# Returns the configuration of the associated connection as a hash:
#
# ActiveRecord::Base.connection_config
@@ -252,7 +256,6 @@ module ActiveRecord
:clear_all_connections!, :flush_idle_connections!, to: :connection_handler
private
-
def swap_connection_handler(handler, &blk) # :nodoc:
old_handler, ActiveRecord::Base.connection_handler = ActiveRecord::Base.connection_handler, handler
yield
diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb
index 6fed3e5c19..595ef4ee25 100644
--- a/activerecord/lib/active_record/core.rb
+++ b/activerecord/lib/active_record/core.rb
@@ -101,7 +101,6 @@ module ActiveRecord
# environment where dumping schema is rarely needed.
mattr_accessor :dump_schema_after_migration, instance_writer: false, default: true
- mattr_accessor :database_selector, instance_writer: false
##
# :singleton-method:
# Specifies which database schemas to dump when calling db:structure:dump.
@@ -175,8 +174,7 @@ module ActiveRecord
record = statement.execute([id], connection)&.first
unless record
- raise RecordNotFound.new("Couldn't find #{name} with '#{primary_key}'=#{id}",
- name, primary_key, id)
+ raise RecordNotFound.new("Couldn't find #{name} with '#{key}'=#{id}", name, key, id)
end
record
end
@@ -270,7 +268,8 @@ module ActiveRecord
end
def arel_attribute(name, table = arel_table) # :nodoc:
- name = attribute_alias(name) if attribute_alias?(name)
+ name = name.to_s
+ name = attribute_aliases[name] || name
table[name]
end
@@ -287,7 +286,6 @@ module ActiveRecord
end
private
-
def cached_find_by_statement(key, &block)
cache = @find_by_statement_cache[connection.prepared_statements]
cache.compute_if_absent(key) { StatementCache.create(connection, &block) }
@@ -318,7 +316,7 @@ module ActiveRecord
# # Instantiates a single new object
# User.new(first_name: 'Jamie')
def initialize(attributes = nil)
- self.class.define_attribute_methods
+ @new_record = true
@attributes = self.class._default_attributes.deep_dup
init_internals
@@ -355,12 +353,10 @@ module ActiveRecord
# +attributes+ should be an attributes object, and unlike the
# `initialize` method, no assignment calls are made per attribute.
def init_with_attributes(attributes, new_record = false) # :nodoc:
- init_internals
-
@new_record = new_record
@attributes = attributes
- self.class.define_attribute_methods
+ init_internals
yield self if block_given?
@@ -399,13 +395,13 @@ module ActiveRecord
##
def initialize_dup(other) # :nodoc:
@attributes = @attributes.deep_dup
- @attributes.reset(self.class.primary_key)
+ @attributes.reset(@primary_key)
_run_initialize_callbacks
@new_record = true
@destroyed = false
- @_start_transaction_state = {}
+ @_start_transaction_state = nil
@transaction_state = nil
super
@@ -466,7 +462,7 @@ module ActiveRecord
# Returns +true+ if the attributes hash has been frozen.
def frozen?
- sync_with_transaction_state
+ sync_with_transaction_state if @transaction_state&.finalized?
@attributes.frozen?
end
@@ -557,7 +553,6 @@ module ActiveRecord
end
private
-
# +Array#flatten+ will call +#to_ary+ (recursively) on each of the elements of
# the array, and then rescues from the possible +NoMethodError+. If those elements are
# +ActiveRecord::Base+'s, then this triggers the various +method_missing+'s that we have,
@@ -571,22 +566,18 @@ module ActiveRecord
end
def init_internals
+ @primary_key = self.class.primary_key
@readonly = false
@destroyed = false
@marked_for_destruction = false
@destroyed_by_association = nil
- @new_record = true
- @_start_transaction_state = {}
+ @_start_transaction_state = nil
@transaction_state = nil
- end
- def initialize_internals_callback
+ self.class.define_attribute_methods
end
- def thaw
- if @attributes.frozen?
- @attributes = @attributes.dup
- end
+ def initialize_internals_callback
end
def custom_inspect_method_defined?
diff --git a/activerecord/lib/active_record/database_configurations.rb b/activerecord/lib/active_record/database_configurations.rb
index 44b5cfc738..8baa0f5af6 100644
--- a/activerecord/lib/active_record/database_configurations.rb
+++ b/activerecord/lib/active_record/database_configurations.rb
@@ -104,18 +104,30 @@ module ActiveRecord
return configs.configurations if configs.is_a?(DatabaseConfigurations)
return configs if configs.is_a?(Array)
- build_db_config = configs.each_pair.flat_map do |env_name, config|
- walk_configs(env_name.to_s, "primary", config)
- end.flatten.compact
+ db_configs = configs.flat_map do |env_name, config|
+ if config.is_a?(Hash) && config.all? { |_, v| v.is_a?(Hash) }
+ walk_configs(env_name.to_s, config)
+ else
+ build_db_config_from_raw_config(env_name.to_s, "primary", config)
+ end
+ end
- if url = ENV["DATABASE_URL"]
- build_url_config(url, build_db_config)
- else
- build_db_config
+ current_env = ActiveRecord::ConnectionHandling::DEFAULT_ENV.call.to_s
+
+ unless db_configs.find(&:for_current_env?)
+ db_configs << environment_url_config(current_env, "primary", {})
+ end
+
+ merge_db_environment_variables(current_env, db_configs.compact)
+ end
+
+ def walk_configs(env_name, config)
+ config.map do |spec_name, sub_config|
+ build_db_config_from_raw_config(env_name, spec_name.to_s, sub_config)
end
end
- def walk_configs(env_name, spec_name, config)
+ def build_db_config_from_raw_config(env_name, spec_name, config)
case config
when String
build_db_config_from_string(env_name, spec_name, config)
@@ -141,31 +153,27 @@ module ActiveRecord
config_without_url.delete "url"
ActiveRecord::DatabaseConfigurations::UrlConfig.new(env_name, spec_name, url, config_without_url)
- elsif config["database"] || (config.size == 1 && config.values.all? { |v| v.is_a? String })
- ActiveRecord::DatabaseConfigurations::HashConfig.new(env_name, spec_name, config)
else
- config.each_pair.map do |sub_spec_name, sub_config|
- walk_configs(env_name, sub_spec_name, sub_config)
- end
+ ActiveRecord::DatabaseConfigurations::HashConfig.new(env_name, spec_name, config)
end
end
- def build_url_config(url, configs)
- env = ActiveRecord::ConnectionHandling::DEFAULT_ENV.call.to_s
+ def merge_db_environment_variables(current_env, configs)
+ configs.map do |config|
+ next config if config.url_config? || config.env_name != current_env
- if original_config = configs.find(&:for_current_env?)
- if original_config.url_config?
- configs
- else
- configs.map do |config|
- ActiveRecord::DatabaseConfigurations::UrlConfig.new(config.env_name, config.spec_name, url, config.config)
- end
- end
- else
- configs + [ActiveRecord::DatabaseConfigurations::UrlConfig.new(env, "primary", url)]
+ url_config = environment_url_config(current_env, config.spec_name, config.config)
+ url_config || config
end
end
+ def environment_url_config(env, spec_name, config)
+ url = ENV["DATABASE_URL"]
+ return unless url
+
+ ActiveRecord::DatabaseConfigurations::UrlConfig.new(env, spec_name, url, config)
+ end
+
def method_missing(method, *args, &blk)
case method
when :each, :first
diff --git a/activerecord/lib/active_record/database_configurations/url_config.rb b/activerecord/lib/active_record/database_configurations/url_config.rb
index e2d30ae416..e6b4acc647 100644
--- a/activerecord/lib/active_record/database_configurations/url_config.rb
+++ b/activerecord/lib/active_record/database_configurations/url_config.rb
@@ -56,7 +56,6 @@ module ActiveRecord
end
private
-
def build_url_hash(url)
if url.nil? || /^jdbc:/.match?(url)
{ "url" => url }
diff --git a/activerecord/lib/active_record/dynamic_matchers.rb b/activerecord/lib/active_record/dynamic_matchers.rb
index 3bb8c6f4e3..7d9e221faa 100644
--- a/activerecord/lib/active_record/dynamic_matchers.rb
+++ b/activerecord/lib/active_record/dynamic_matchers.rb
@@ -49,11 +49,11 @@ module ActiveRecord
attr_reader :model, :name, :attribute_names
- def initialize(model, name)
+ def initialize(model, method_name)
@model = model
- @name = name.to_s
+ @name = method_name.to_s
@attribute_names = @name.match(self.class.pattern)[1].split("_and_")
- @attribute_names.map! { |n| @model.attribute_aliases[n] || n }
+ @attribute_names.map! { |name| @model.attribute_aliases[name] || name }
end
def valid?
@@ -69,7 +69,6 @@ module ActiveRecord
end
private
-
def body
"#{finder}(#{attributes_hash})"
end
diff --git a/activerecord/lib/active_record/enum.rb b/activerecord/lib/active_record/enum.rb
index 8077630aeb..fc49f752aa 100644
--- a/activerecord/lib/active_record/enum.rb
+++ b/activerecord/lib/active_record/enum.rb
@@ -200,6 +200,8 @@ module ActiveRecord
# scope :active, -> { where(status: 0) }
# scope :not_active, -> { where.not(status: 0) }
if enum_scopes != false
+ klass.send(:detect_negative_condition!, value_method_name)
+
klass.send(:detect_enum_conflict!, name, value_method_name, true)
klass.scope value_method_name, -> { where(attr => value) }
@@ -261,5 +263,12 @@ module ActiveRecord
source: source
}
end
+
+ def detect_negative_condition!(method_name)
+ if method_name.start_with?("not_") && logger
+ logger.warn "An enum element in #{self.name} uses the prefix 'not_'." \
+ " This will cause a conflict with auto generated negative scopes."
+ end
+ end
end
end
diff --git a/activerecord/lib/active_record/errors.rb b/activerecord/lib/active_record/errors.rb
index 60cf9818c1..20cc987d6e 100644
--- a/activerecord/lib/active_record/errors.rb
+++ b/activerecord/lib/active_record/errors.rb
@@ -38,6 +38,10 @@ module ActiveRecord
class AdapterNotSpecified < ActiveRecordError
end
+ # Raised when a model makes a query but it has not specified an associated table.
+ class TableNotSpecified < ActiveRecordError
+ end
+
# Raised when Active Record cannot find database adapter specified in
# +config/database.yml+ or programmatically.
class AdapterNotFound < ActiveRecordError
@@ -349,16 +353,24 @@ module ActiveRecord
class IrreversibleOrderError < ActiveRecordError
end
+ # Superclass for errors that have been aborted (either by client or server).
+ class QueryAborted < StatementInvalid
+ end
+
# LockWaitTimeout will be raised when lock wait timeout exceeded.
class LockWaitTimeout < StatementInvalid
end
# StatementTimeout will be raised when statement timeout exceeded.
- class StatementTimeout < StatementInvalid
+ class StatementTimeout < QueryAborted
end
# QueryCanceled will be raised when canceling statement due to user request.
- class QueryCanceled < StatementInvalid
+ class QueryCanceled < QueryAborted
+ end
+
+ # AdapterTimeout will be raised when database clients times out while waiting from the server.
+ class AdapterTimeout < QueryAborted
end
# UnknownAttributeReference is raised when an unknown and potentially unsafe
diff --git a/activerecord/lib/active_record/explain.rb b/activerecord/lib/active_record/explain.rb
index 919e96cd7a..5dca75c539 100644
--- a/activerecord/lib/active_record/explain.rb
+++ b/activerecord/lib/active_record/explain.rb
@@ -36,7 +36,6 @@ module ActiveRecord
end
private
-
def render_bind(attr)
value = if attr.type.binary? && attr.value
"<#{attr.value_for_database.to_s.bytesize} bytes of binary data>"
diff --git a/activerecord/lib/active_record/fixture_set/table_row.rb b/activerecord/lib/active_record/fixture_set/table_row.rb
index cb4726f1ee..f65329f91d 100644
--- a/activerecord/lib/active_record/fixture_set/table_row.rb
+++ b/activerecord/lib/active_record/fixture_set/table_row.rb
@@ -48,7 +48,6 @@ module ActiveRecord
end
private
-
def model_metadata
@table_rows.model_metadata
end
diff --git a/activerecord/lib/active_record/fixture_set/table_rows.rb b/activerecord/lib/active_record/fixture_set/table_rows.rb
index 23814b6cb5..df1cd63963 100644
--- a/activerecord/lib/active_record/fixture_set/table_rows.rb
+++ b/activerecord/lib/active_record/fixture_set/table_rows.rb
@@ -29,7 +29,6 @@ module ActiveRecord
end
private
-
def build_table_rows_from(table_name, fixtures, config)
now = config.default_timezone == :utc ? Time.now.utc : Time.now
diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb
index 327121a2a2..046ed0e95c 100644
--- a/activerecord/lib/active_record/fixtures.rb
+++ b/activerecord/lib/active_record/fixtures.rb
@@ -464,7 +464,6 @@ module ActiveRecord
end
private
-
def insert_class(class_names, name, klass)
# We only want to deal with AR objects.
if klass && klass < ActiveRecord::Base
@@ -570,7 +569,6 @@ module ActiveRecord
end
private
-
def read_and_insert(fixtures_directory, fixture_files, class_names, connection) # :nodoc:
fixtures_map = {}
fixture_sets = fixture_files.map do |fixture_set_name|
@@ -661,7 +659,6 @@ module ActiveRecord
end
private
-
def model_class=(class_name)
if class_name.is_a?(Class) # TODO: Should be an AR::Base type class, or any?
@model_class = class_name
diff --git a/activerecord/lib/active_record/gem_version.rb b/activerecord/lib/active_record/gem_version.rb
index f77bc2e3c1..7f92174f87 100644
--- a/activerecord/lib/active_record/gem_version.rb
+++ b/activerecord/lib/active_record/gem_version.rb
@@ -8,9 +8,9 @@ module ActiveRecord
module VERSION
MAJOR = 6
- MINOR = 0
+ MINOR = 1
TINY = 0
- PRE = "beta3"
+ PRE = "alpha"
STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
end
diff --git a/activerecord/lib/active_record/inheritance.rb b/activerecord/lib/active_record/inheritance.rb
index 9570bc6f86..5ca48fa18c 100644
--- a/activerecord/lib/active_record/inheritance.rb
+++ b/activerecord/lib/active_record/inheritance.rb
@@ -176,7 +176,6 @@ module ActiveRecord
end
protected
-
# Returns the class type of the record using the current module as a prefix. So descendants of
# MyApp::Business::Account would appear as MyApp::Business::AccountSubclass.
def compute_type(type_name)
@@ -208,7 +207,6 @@ module ActiveRecord
end
private
-
# Called by +instantiate+ to decide which class to use for a new
# record instance. For single-table inheritance, we check the record
# for a +type+ column and return the corresponding class.
@@ -272,7 +270,6 @@ module ActiveRecord
end
private
-
def initialize_internals_callback
super
ensure_proper_type
diff --git a/activerecord/lib/active_record/insert_all.rb b/activerecord/lib/active_record/insert_all.rb
index 959e5bd4d7..f6577dcbc4 100644
--- a/activerecord/lib/active_record/insert_all.rb
+++ b/activerecord/lib/active_record/insert_all.rb
@@ -21,9 +21,9 @@ module ActiveRecord
end
def execute
- message = "#{model} "
- message += "Bulk " if inserts.many?
- message += (on_duplicate == :update ? "Upsert" : "Insert")
+ message = +"#{model} "
+ message << "Bulk " if inserts.many?
+ message << (on_duplicate == :update ? "Upsert" : "Insert")
connection.exec_query to_sql, message
end
diff --git a/activerecord/lib/active_record/integration.rb b/activerecord/lib/active_record/integration.rb
index b769541e95..4a97061731 100644
--- a/activerecord/lib/active_record/integration.rb
+++ b/activerecord/lib/active_record/integration.rb
@@ -22,6 +22,14 @@ module ActiveRecord
#
# This is +true+, by default on Rails 5.2 and above.
class_attribute :cache_versioning, instance_writer: false, default: false
+
+ ##
+ # :singleton-method:
+ # Indicates whether to use a stable #cache_key method that is accompanied
+ # by a changing version in the #cache_version method on collections.
+ #
+ # This is +false+, by default until Rails 6.1.
+ class_attribute :collection_cache_versioning, instance_writer: false, default: false
end
# Returns a +String+, which Action Pack uses for constructing a URL to this
@@ -85,7 +93,7 @@ module ActiveRecord
# cache_version, but this method can be overwritten to return something else.
#
# Note, this method will return nil if ActiveRecord::Base.cache_versioning is set to
- # +false+ (which it is by default until Rails 6.0).
+ # +false+.
def cache_version
return unless cache_versioning
@@ -154,7 +162,7 @@ module ActiveRecord
end
def collection_cache_key(collection = all, timestamp_column = :updated_at) # :nodoc:
- collection.compute_cache_key(timestamp_column)
+ collection.send(:compute_cache_key, timestamp_column)
end
end
diff --git a/activerecord/lib/active_record/internal_metadata.rb b/activerecord/lib/active_record/internal_metadata.rb
index e6166581f1..8f3c6d0ee3 100644
--- a/activerecord/lib/active_record/internal_metadata.rb
+++ b/activerecord/lib/active_record/internal_metadata.rb
@@ -28,10 +28,6 @@ module ActiveRecord
where(key: key).pluck(:value).first
end
- def table_exists?
- connection.table_exists?(table_name)
- end
-
# Creates an internal metadata table with columns +key+ and +value+
def create_table
unless table_exists?
diff --git a/activerecord/lib/active_record/locking/optimistic.rb b/activerecord/lib/active_record/locking/optimistic.rb
index b7eecda59e..c2a083bf3b 100644
--- a/activerecord/lib/active_record/locking/optimistic.rb
+++ b/activerecord/lib/active_record/locking/optimistic.rb
@@ -87,7 +87,7 @@ module ActiveRecord
affected_rows = self.class._update_record(
attributes_with_values(attribute_names),
- self.class.primary_key => id_in_database,
+ @primary_key => id_in_database,
locking_column => previous_lock_value
)
@@ -110,7 +110,7 @@ module ActiveRecord
locking_column = self.class.locking_column
affected_rows = self.class._delete_record(
- self.class.primary_key => id_in_database,
+ @primary_key => id_in_database,
locking_column => read_attribute_before_type_cast(locking_column)
)
@@ -156,7 +156,6 @@ module ActiveRecord
end
private
-
# We need to apply this decorator here, rather than on module inclusion. The closure
# created by the matcher would otherwise evaluate for `ActiveRecord::Base`, not the
# sub class being decorated. As such, changes to `lock_optimistically`, or
diff --git a/activerecord/lib/active_record/log_subscriber.rb b/activerecord/lib/active_record/log_subscriber.rb
index 6b84431343..6248c2f578 100644
--- a/activerecord/lib/active_record/log_subscriber.rb
+++ b/activerecord/lib/active_record/log_subscriber.rb
@@ -110,7 +110,7 @@ module ActiveRecord
end
def extract_query_source_location(locations)
- backtrace_cleaner.clean(locations).first
+ backtrace_cleaner.clean(locations.lazy).first
end
end
end
diff --git a/activerecord/lib/active_record/middleware/database_selector.rb b/activerecord/lib/active_record/middleware/database_selector.rb
index b5b5df074c..7374107048 100644
--- a/activerecord/lib/active_record/middleware/database_selector.rb
+++ b/activerecord/lib/active_record/middleware/database_selector.rb
@@ -35,10 +35,10 @@ module ActiveRecord
# config.active_record.database_resolver = MyResolver
# config.active_record.database_resolver_context = MyResolver::MySession
class DatabaseSelector
- def initialize(app, resolver_klass = Resolver, context_klass = Resolver::Session, options = {})
+ def initialize(app, resolver_klass = nil, context_klass = nil, options = {})
@app = app
- @resolver_klass = resolver_klass
- @context_klass = context_klass
+ @resolver_klass = resolver_klass || Resolver
+ @context_klass = context_klass || Resolver::Session
@options = options
end
@@ -55,7 +55,6 @@ module ActiveRecord
end
private
-
def select_database(request, &blk)
context = context_klass.call(request)
resolver = resolver_klass.call(context, options)
diff --git a/activerecord/lib/active_record/middleware/database_selector/resolver.rb b/activerecord/lib/active_record/middleware/database_selector/resolver.rb
index 80b8cd7cae..3eb1039c50 100644
--- a/activerecord/lib/active_record/middleware/database_selector/resolver.rb
+++ b/activerecord/lib/active_record/middleware/database_selector/resolver.rb
@@ -44,10 +44,9 @@ module ActiveRecord
end
private
-
def read_from_primary(&blk)
- ActiveRecord::Base.connection.while_preventing_writes do
- ActiveRecord::Base.connected_to(role: ActiveRecord::Base.writing_role) do
+ ActiveRecord::Base.connected_to(role: ActiveRecord::Base.writing_role) do
+ ActiveRecord::Base.connection_handler.while_preventing_writes do
instrumenter.instrument("database_selector.active_record.read_from_primary") do
yield
end
diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb
index ed0c6d48b8..7edfec9903 100644
--- a/activerecord/lib/active_record/migration.rb
+++ b/activerecord/lib/active_record/migration.rb
@@ -4,6 +4,7 @@ require "benchmark"
require "set"
require "zlib"
require "active_support/core_ext/module/attribute_accessors"
+require "active_support/actionable_error"
module ActiveRecord
class MigrationError < ActiveRecordError #:nodoc:
@@ -128,6 +129,12 @@ module ActiveRecord
end
class PendingMigrationError < MigrationError #:nodoc:
+ include ActiveSupport::ActionableError
+
+ action "Run pending migrations" do
+ ActiveRecord::Tasks::DatabaseTasks.migrate
+ end
+
def initialize(message = nil)
if !message && defined?(Rails.env)
super("Migrations are pending. To resolve this issue, run:\n\n rails db:migrate RAILS_ENV=#{::Rails.env}")
@@ -487,9 +494,9 @@ module ActiveRecord
# This migration will create the horses table for you on the way up, and
# automatically figure out how to drop the table on the way down.
#
- # Some commands like +remove_column+ cannot be reversed. If you care to
- # define how to move up and down in these cases, you should define the +up+
- # and +down+ methods as before.
+ # Some commands cannot be reversed. If you care to define how to move up
+ # and down in these cases, you should define the +up+ and +down+ methods
+ # as before.
#
# If a command cannot be reversed, an
# <tt>ActiveRecord::IrreversibleMigration</tt> exception will be raised when
@@ -561,7 +568,6 @@ module ActiveRecord
end
private
-
def connection
ActiveRecord::Base.connection
end
@@ -878,13 +884,14 @@ module ActiveRecord
def copy(destination, sources, options = {})
copied = []
+ schema_migration = options[:schema_migration] || ActiveRecord::SchemaMigration
FileUtils.mkdir_p(destination) unless File.exist?(destination)
- destination_migrations = ActiveRecord::MigrationContext.new(destination).migrations
+ destination_migrations = ActiveRecord::MigrationContext.new(destination, schema_migration).migrations
last = destination_migrations.last
sources.each do |scope, path|
- source_migrations = ActiveRecord::MigrationContext.new(path).migrations
+ source_migrations = ActiveRecord::MigrationContext.new(path, schema_migration).migrations
source_migrations.each do |migration|
source = File.binread(migration.filename)
@@ -985,7 +992,6 @@ module ActiveRecord
delegate :migrate, :announce, :write, :disable_ddl_transaction, to: :migration
private
-
def migration
@migration ||= load_migration
end
@@ -1007,10 +1013,11 @@ module ActiveRecord
end
class MigrationContext #:nodoc:
- attr_reader :migrations_paths
+ attr_reader :migrations_paths, :schema_migration
- def initialize(migrations_paths)
+ def initialize(migrations_paths, schema_migration)
@migrations_paths = migrations_paths
+ @schema_migration = schema_migration
end
def migrate(target_version = nil, &block)
@@ -1041,7 +1048,7 @@ module ActiveRecord
migrations
end
- Migrator.new(:up, selected_migrations, target_version).migrate
+ Migrator.new(:up, selected_migrations, schema_migration, target_version).migrate
end
def down(target_version = nil)
@@ -1051,20 +1058,20 @@ module ActiveRecord
migrations
end
- Migrator.new(:down, selected_migrations, target_version).migrate
+ Migrator.new(:down, selected_migrations, schema_migration, target_version).migrate
end
def run(direction, target_version)
- Migrator.new(direction, migrations, target_version).run
+ Migrator.new(direction, migrations, schema_migration, target_version).run
end
def open
- Migrator.new(:up, migrations, nil)
+ Migrator.new(:up, migrations, schema_migration)
end
def get_all_versions
- if SchemaMigration.table_exists?
- SchemaMigration.all_versions.map(&:to_i)
+ if schema_migration.table_exists?
+ schema_migration.all_versions.map(&:to_i)
else
[]
end
@@ -1101,12 +1108,12 @@ module ActiveRecord
end
def migrations_status
- db_list = ActiveRecord::SchemaMigration.normalized_versions
+ db_list = schema_migration.normalized_versions
file_list = migration_files.map do |file|
version, name, scope = parse_migration_filename(file)
raise IllegalMigrationNameError.new(file) unless version
- version = ActiveRecord::SchemaMigration.normalize_migration_number(version)
+ version = schema_migration.normalize_migration_number(version)
status = db_list.delete(version) ? "up" : "down"
[status, version, (name + scope).humanize]
end.compact
@@ -1146,7 +1153,7 @@ module ActiveRecord
end
def move(direction, steps)
- migrator = Migrator.new(direction, migrations)
+ migrator = Migrator.new(direction, migrations, schema_migration)
if current_version != 0 && !migrator.current_migration
raise UnknownMigrationVersionError.new(current_version)
@@ -1165,27 +1172,28 @@ module ActiveRecord
end
end
- class Migrator #:nodoc:
+ class Migrator # :nodoc:
class << self
attr_accessor :migrations_paths
# For cases where a table doesn't exist like loading from schema cache
def current_version
- MigrationContext.new(migrations_paths).current_version
+ MigrationContext.new(migrations_paths, SchemaMigration).current_version
end
end
self.migrations_paths = ["db/migrate"]
- def initialize(direction, migrations, target_version = nil)
+ def initialize(direction, migrations, schema_migration, target_version = nil)
@direction = direction
@target_version = target_version
@migrated_versions = nil
@migrations = migrations
+ @schema_migration = schema_migration
validate(@migrations)
- ActiveRecord::SchemaMigration.create_table
+ @schema_migration.create_table
ActiveRecord::InternalMetadata.create_table
end
@@ -1239,11 +1247,10 @@ module ActiveRecord
end
def load_migrated
- @migrated_versions = Set.new(Base.connection.migration_context.get_all_versions)
+ @migrated_versions = Set.new(@schema_migration.all_versions.map(&:to_i))
end
private
-
# Used for running a specific migration.
def run_without_lock
migration = migrations.detect { |m| m.version == @target_version }
@@ -1323,10 +1330,10 @@ module ActiveRecord
def record_version_state_after_migrating(version)
if down?
migrated.delete(version)
- ActiveRecord::SchemaMigration.delete_by(version: version.to_s)
+ @schema_migration.delete_by(version: version.to_s)
else
migrated << version
- ActiveRecord::SchemaMigration.create!(version: version.to_s)
+ @schema_migration.create!(version: version.to_s)
end
end
diff --git a/activerecord/lib/active_record/migration/command_recorder.rb b/activerecord/lib/active_record/migration/command_recorder.rb
index efed4b0e26..67172ef395 100644
--- a/activerecord/lib/active_record/migration/command_recorder.rb
+++ b/activerecord/lib/active_record/migration/command_recorder.rb
@@ -118,7 +118,6 @@ module ActiveRecord
end
private
-
module StraightReversions # :nodoc:
private
{
diff --git a/activerecord/lib/active_record/migration/compatibility.rb b/activerecord/lib/active_record/migration/compatibility.rb
index ff91218696..ef78a9161e 100644
--- a/activerecord/lib/active_record/migration/compatibility.rb
+++ b/activerecord/lib/active_record/migration/compatibility.rb
@@ -13,7 +13,10 @@ module ActiveRecord
const_get(name)
end
- V6_0 = Current
+ V6_1 = Current
+
+ class V6_0 < V6_1
+ end
class V5_2 < V6_0
module TableDefinition
diff --git a/activerecord/lib/active_record/migration/join_table.rb b/activerecord/lib/active_record/migration/join_table.rb
index 9abb289bb0..45169617c1 100644
--- a/activerecord/lib/active_record/migration/join_table.rb
+++ b/activerecord/lib/active_record/migration/join_table.rb
@@ -4,7 +4,6 @@ module ActiveRecord
class Migration
module JoinTable #:nodoc:
private
-
def find_join_table_name(table_1, table_2, options = {})
options.delete(:table_name) || join_table_name(table_1, table_2)
end
diff --git a/activerecord/lib/active_record/model_schema.rb b/activerecord/lib/active_record/model_schema.rb
index 55fc58e339..18f19af6be 100644
--- a/activerecord/lib/active_record/model_schema.rb
+++ b/activerecord/lib/active_record/model_schema.rb
@@ -456,13 +456,11 @@ module ActiveRecord
end
protected
-
def initialize_load_schema_monitor
@load_schema_monitor = Monitor.new
end
private
-
def inherited(child_class)
super
child_class.initialize_load_schema_monitor
@@ -484,6 +482,9 @@ module ActiveRecord
end
def load_schema!
+ unless table_name
+ raise ActiveRecord::TableNotSpecified, "#{self} has no table configured. Set one with #{self}.table_name="
+ end
@columns_hash = connection.schema_cache.columns_hash(table_name).except(*ignored_columns)
@columns_hash.each do |name, column|
define_attribute(
diff --git a/activerecord/lib/active_record/nested_attributes.rb b/activerecord/lib/active_record/nested_attributes.rb
index 8b9098df6c..ab107742ed 100644
--- a/activerecord/lib/active_record/nested_attributes.rb
+++ b/activerecord/lib/active_record/nested_attributes.rb
@@ -2,7 +2,6 @@
require "active_support/core_ext/hash/except"
require "active_support/core_ext/module/redefine_method"
-require "active_support/core_ext/object/try"
require "active_support/core_ext/hash/indifferent_access"
module ActiveRecord
@@ -354,7 +353,6 @@ module ActiveRecord
end
private
-
# Generates a writer method for this association. Serves as a point for
# accessing the objects in the association. For example, this method
# could generate the following:
@@ -386,7 +384,6 @@ module ActiveRecord
end
private
-
# Attribute hash keys that should not be assigned as normal attributes.
# These hash keys are nested attributes implementation details.
UNASSIGNABLE_KEYS = %w( id _destroy )
diff --git a/activerecord/lib/active_record/null_relation.rb b/activerecord/lib/active_record/null_relation.rb
index cf0de0fdeb..bee5b5f24a 100644
--- a/activerecord/lib/active_record/null_relation.rb
+++ b/activerecord/lib/active_record/null_relation.rb
@@ -60,7 +60,6 @@ module ActiveRecord
end
private
-
def exec_queries
@records = [].freeze
end
diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb
index 8bade8cd28..323b01ab2d 100644
--- a/activerecord/lib/active_record/persistence.rb
+++ b/activerecord/lib/active_record/persistence.rb
@@ -353,6 +353,7 @@ module ActiveRecord
end
def _insert_record(values) # :nodoc:
+ primary_key = self.primary_key
primary_key_value = nil
if primary_key && Hash === values
@@ -423,20 +424,20 @@ module ActiveRecord
# Returns true if this object hasn't been saved yet -- that is, a record
# for the object doesn't exist in the database yet; otherwise, returns false.
def new_record?
- sync_with_transaction_state
+ sync_with_transaction_state if @transaction_state&.finalized?
@new_record
end
# Returns true if this object has been destroyed, otherwise returns false.
def destroyed?
- sync_with_transaction_state
+ sync_with_transaction_state if @transaction_state&.finalized?
@destroyed
end
# Returns true if the record is persisted, i.e. it's not a new record and it was
# not destroyed, otherwise returns false.
def persisted?
- sync_with_transaction_state
+ sync_with_transaction_state if @transaction_state&.finalized?
!(@new_record || @destroyed)
end
@@ -663,8 +664,13 @@ module ActiveRecord
raise ActiveRecordError, "cannot update a new record" if new_record?
raise ActiveRecordError, "cannot update a destroyed record" if destroyed?
+ attributes = attributes.transform_keys do |key|
+ name = key.to_s
+ self.class.attribute_aliases[name] || name
+ end
+
attributes.each_key do |key|
- verify_readonly_attribute(key.to_s)
+ verify_readonly_attribute(key)
end
id_in_database = self.id_in_database
@@ -674,7 +680,7 @@ module ActiveRecord
affected_rows = self.class._update_record(
attributes,
- self.class.primary_key => id_in_database
+ @primary_key => id_in_database
)
affected_rows == 1
@@ -843,16 +849,11 @@ module ActiveRecord
# ball.touch(:updated_at) # => raises ActiveRecordError
#
def touch(*names, time: nil)
- unless persisted?
- raise ActiveRecordError, <<-MSG.squish
- cannot touch on a new or destroyed record object. Consider using
- persisted?, new_record?, or destroyed? before touching
- MSG
- end
+ _raise_record_not_touched_error unless persisted?
attribute_names = timestamp_attributes_for_update_in_model
attribute_names |= names.map!(&:to_s).map! { |name|
- self.class.attribute_alias?(name) ? self.class.attribute_alias(name) : name
+ self.class.attribute_aliases[name] || name
}
unless attribute_names.empty?
@@ -864,7 +865,6 @@ module ActiveRecord
end
private
-
# A hook to be overridden by association modules.
def destroy_associations
end
@@ -874,7 +874,7 @@ module ActiveRecord
end
def _delete_row
- self.class._delete_record(self.class.primary_key => id_in_database)
+ self.class._delete_record(@primary_key => id_in_database)
end
def _touch_row(attribute_names, time)
@@ -890,7 +890,7 @@ module ActiveRecord
def _update_row(attribute_names, attempted_action = "update")
self.class._update_record(
attributes_with_values(attribute_names),
- self.class.primary_key => id_in_database
+ @primary_key => id_in_database
)
end
@@ -928,7 +928,7 @@ module ActiveRecord
attributes_with_values(attribute_names)
)
- self.id ||= new_id if self.class.primary_key
+ self.id ||= new_id if @primary_key
@new_record = false
@@ -938,7 +938,7 @@ module ActiveRecord
end
def verify_readonly_attribute(name)
- raise ActiveRecordError, "#{name} is marked as readonly" if self.class.readonly_attributes.include?(name)
+ raise ActiveRecordError, "#{name} is marked as readonly" if self.class.readonly_attribute?(name)
end
def _raise_record_not_destroyed
@@ -948,14 +948,21 @@ module ActiveRecord
@_association_destroy_exception = nil
end
+ def _raise_readonly_record_error
+ raise ReadOnlyRecord, "#{self.class} is marked as readonly"
+ end
+
+ def _raise_record_not_touched_error
+ raise ActiveRecordError, <<~MSG.squish
+ Cannot touch on a new or destroyed record object. Consider using
+ persisted?, new_record?, or destroyed? before touching.
+ MSG
+ end
+
# The name of the method used to touch a +belongs_to+ association when the
# +:touch+ option is used.
def belongs_to_touch_method
:touch
end
-
- def _raise_readonly_record_error
- raise ReadOnlyRecord, "#{self.class} is marked as readonly"
- end
end
end
diff --git a/activerecord/lib/active_record/railtie.rb b/activerecord/lib/active_record/railtie.rb
index a1d7c893bf..d5375390c7 100644
--- a/activerecord/lib/active_record/railtie.rb
+++ b/activerecord/lib/active_record/railtie.rb
@@ -134,7 +134,6 @@ end_error
cache = YAML.load(File.read(filename))
if cache.version == current_version
- connection.schema_cache = cache
connection_pool.schema_cache = cache.dup
else
warn "Ignoring db/schema_cache.yml because it has expired. The current schema version is #{current_version}, but the one in the cache is #{cache.version}."
diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake
index 447def8d77..4d9acc911b 100644
--- a/activerecord/lib/active_record/railties/databases.rake
+++ b/activerecord/lib/active_record/railties/databases.rake
@@ -2,6 +2,8 @@
require "active_record"
+databases = ActiveRecord::Tasks::DatabaseTasks.setup_initial_database_yaml
+
db_namespace = namespace :db do
desc "Set the environment value for the database"
task "environment:set" => :load_config do
@@ -23,7 +25,7 @@ db_namespace = namespace :db do
ActiveRecord::Tasks::DatabaseTasks.create_all
end
- ActiveRecord::Tasks::DatabaseTasks.for_each do |spec_name|
+ ActiveRecord::Tasks::DatabaseTasks.for_each(databases) do |spec_name|
desc "Create #{spec_name} database for current environment"
task spec_name => :load_config do
db_config = ActiveRecord::Base.configurations.configs_for(env_name: Rails.env, spec_name: spec_name)
@@ -42,7 +44,7 @@ db_namespace = namespace :db do
ActiveRecord::Tasks::DatabaseTasks.drop_all
end
- ActiveRecord::Tasks::DatabaseTasks.for_each do |spec_name|
+ ActiveRecord::Tasks::DatabaseTasks.for_each(databases) do |spec_name|
desc "Drop #{spec_name} database for current environment"
task spec_name => [:load_config, :check_protected_environments] do
db_config = ActiveRecord::Base.configurations.configs_for(env_name: Rails.env, spec_name: spec_name)
@@ -78,7 +80,7 @@ db_namespace = namespace :db do
desc "Migrate the database (options: VERSION=x, VERBOSE=false, SCOPE=blog)."
task migrate: :load_config do
- ActiveRecord::Base.configurations.configs_for(env_name: Rails.env).each do |db_config|
+ ActiveRecord::Base.configurations.configs_for(env_name: ActiveRecord::Tasks::DatabaseTasks.env).each do |db_config|
ActiveRecord::Base.establish_connection(db_config.config)
ActiveRecord::Tasks::DatabaseTasks.migrate
end
@@ -101,7 +103,7 @@ db_namespace = namespace :db do
end
namespace :migrate do
- ActiveRecord::Tasks::DatabaseTasks.for_each do |spec_name|
+ ActiveRecord::Tasks::DatabaseTasks.for_each(databases) do |spec_name|
desc "Migrate #{spec_name} database for current environment"
task spec_name => :load_config do
db_config = ActiveRecord::Base.configurations.configs_for(env_name: Rails.env, spec_name: spec_name)
@@ -110,7 +112,7 @@ db_namespace = namespace :db do
end
end
- # desc 'Rollbacks the database one migration and re migrate up (options: STEP=x, VERSION=x).'
+ desc "Rolls back the database one migration and re-migrates up (options: STEP=x, VERSION=x)."
task redo: :load_config do
raise "Empty VERSION provided" if ENV["VERSION"] && ENV["VERSION"].empty?
@@ -126,8 +128,10 @@ db_namespace = namespace :db do
# desc 'Resets your database using your migrations for the current environment'
task reset: ["db:drop", "db:create", "db:migrate"]
- # desc 'Runs the "up" for a given migration VERSION.'
+ desc 'Runs the "up" for a given migration VERSION.'
task up: :load_config do
+ ActiveRecord::Tasks::DatabaseTasks.raise_for_multi_db(command: "db:migrate:up")
+
raise "VERSION is required" if !ENV["VERSION"] || ENV["VERSION"].empty?
ActiveRecord::Tasks::DatabaseTasks.check_target_version
@@ -139,8 +143,29 @@ db_namespace = namespace :db do
db_namespace["_dump"].invoke
end
- # desc 'Runs the "down" for a given migration VERSION.'
+ namespace :up do
+ ActiveRecord::Tasks::DatabaseTasks.for_each(databases) do |spec_name|
+ task spec_name => :load_config do
+ raise "VERSION is required" if !ENV["VERSION"] || ENV["VERSION"].empty?
+
+ db_config = ActiveRecord::Base.configurations.configs_for(env_name: Rails.env, spec_name: spec_name)
+
+ ActiveRecord::Base.establish_connection(db_config.config)
+ ActiveRecord::Tasks::DatabaseTasks.check_target_version
+ ActiveRecord::Base.connection.migration_context.run(
+ :up,
+ ActiveRecord::Tasks::DatabaseTasks.target_version
+ )
+
+ db_namespace["_dump"].invoke
+ end
+ end
+ end
+
+ desc 'Runs the "down" for a given migration VERSION.'
task down: :load_config do
+ ActiveRecord::Tasks::DatabaseTasks.raise_for_multi_db(command: "db:migrate:down")
+
raise "VERSION is required - To go down one migration, use db:rollback" if !ENV["VERSION"] || ENV["VERSION"].empty?
ActiveRecord::Tasks::DatabaseTasks.check_target_version
@@ -152,16 +177,35 @@ db_namespace = namespace :db do
db_namespace["_dump"].invoke
end
+ namespace :down do
+ ActiveRecord::Tasks::DatabaseTasks.for_each(databases) do |spec_name|
+ task spec_name => :load_config do
+ raise "VERSION is required" if !ENV["VERSION"] || ENV["VERSION"].empty?
+
+ db_config = ActiveRecord::Base.configurations.configs_for(env_name: Rails.env, spec_name: spec_name)
+
+ ActiveRecord::Base.establish_connection(db_config.config)
+ ActiveRecord::Tasks::DatabaseTasks.check_target_version
+ ActiveRecord::Base.connection.migration_context.run(
+ :down,
+ ActiveRecord::Tasks::DatabaseTasks.target_version
+ )
+
+ db_namespace["_dump"].invoke
+ end
+ end
+ end
+
desc "Display status of migrations"
task status: :load_config do
- ActiveRecord::Base.configurations.configs_for(env_name: Rails.env).each do |db_config|
+ ActiveRecord::Base.configurations.configs_for(env_name: ActiveRecord::Tasks::DatabaseTasks.env).each do |db_config|
ActiveRecord::Base.establish_connection(db_config.config)
ActiveRecord::Tasks::DatabaseTasks.migrate_status
end
end
namespace :status do
- ActiveRecord::Tasks::DatabaseTasks.for_each do |spec_name|
+ ActiveRecord::Tasks::DatabaseTasks.for_each(databases) do |spec_name|
desc "Display status of migrations for #{spec_name} database"
task spec_name => :load_config do
db_config = ActiveRecord::Base.configurations.configs_for(env_name: Rails.env, spec_name: spec_name)
@@ -186,7 +230,7 @@ db_namespace = namespace :db do
db_namespace["_dump"].invoke
end
- # desc 'Drops and recreates the database from db/schema.rb for the current environment and loads the seeds.'
+ desc "Drops and recreates the database from db/schema.rb for the current environment and loads the seeds."
task reset: [ "db:drop", "db:setup" ]
# desc "Retrieves the charset for the current environment's database"
@@ -208,7 +252,11 @@ db_namespace = namespace :db do
# desc "Raises an error if there are pending migrations"
task abort_if_pending_migrations: :load_config do
- pending_migrations = ActiveRecord::Base.connection.migration_context.open.pending_migrations
+ pending_migrations = ActiveRecord::Base.configurations.configs_for(env_name: ActiveRecord::Tasks::DatabaseTasks.env).flat_map do |db_config|
+ ActiveRecord::Base.establish_connection(db_config.config)
+
+ ActiveRecord::Base.connection.migration_context.open.pending_migrations
+ end
if pending_migrations.any?
puts "You have #{pending_migrations.size} pending #{pending_migrations.size > 1 ? 'migrations:' : 'migration:'}"
@@ -219,17 +267,57 @@ db_namespace = namespace :db do
end
end
+ namespace :abort_if_pending_migrations do
+ ActiveRecord::Tasks::DatabaseTasks.for_each(databases) do |spec_name|
+ # desc "Raises an error if there are pending migrations for #{spec_name} database"
+ task spec_name => :load_config do
+ db_config = ActiveRecord::Base.configurations.configs_for(env_name: Rails.env, spec_name: spec_name)
+ ActiveRecord::Base.establish_connection(db_config.config)
+
+ pending_migrations = ActiveRecord::Base.connection.migration_context.open.pending_migrations
+
+ if pending_migrations.any?
+ puts "You have #{pending_migrations.size} pending #{pending_migrations.size > 1 ? 'migrations:' : 'migration:'}"
+ pending_migrations.each do |pending_migration|
+ puts " %4d %s" % [pending_migration.version, pending_migration.name]
+ end
+ abort %{Run `rails db:migrate:#{spec_name}` to update your database then try again.}
+ end
+ end
+ end
+ end
+
desc "Creates the database, loads the schema, and initializes with the seed data (use db:reset to also drop the database first)"
task setup: ["db:schema:load_if_ruby", "db:structure:load_if_sql", :seed]
desc "Runs setup if database does not exist, or runs migrations if it does"
task prepare: :load_config do
- ActiveRecord::Base.configurations.configs_for(env_name: Rails.env).each do |db_config|
+ seed = false
+
+ ActiveRecord::Base.configurations.configs_for(env_name: ActiveRecord::Tasks::DatabaseTasks.env).each do |db_config|
ActiveRecord::Base.establish_connection(db_config.config)
- db_namespace["migrate"].invoke
+
+ # Skipped when no database
+ ActiveRecord::Tasks::DatabaseTasks.migrate
+ if ActiveRecord::Base.dump_schema_after_migration
+ ActiveRecord::Tasks::DatabaseTasks.dump_schema(db_config.config, ActiveRecord::Base.schema_format, db_config.spec_name)
+ end
+
rescue ActiveRecord::NoDatabaseError
- db_namespace["setup"].invoke
+ ActiveRecord::Tasks::DatabaseTasks.create_current(db_config.env_name, db_config.spec_name)
+ ActiveRecord::Tasks::DatabaseTasks.load_schema(
+ db_config.config,
+ ActiveRecord::Base.schema_format,
+ nil,
+ db_config.env_name,
+ db_config.spec_name
+ )
+
+ seed = true
end
+
+ ActiveRecord::Base.establish_connection
+ ActiveRecord::Tasks::DatabaseTasks.load_seed if seed
end
desc "Loads the seed data from db/seeds.rb"
@@ -294,13 +382,9 @@ db_namespace = namespace :db do
namespace :schema do
desc "Creates a db/schema.rb file that is portable against any DB supported by Active Record"
task dump: :load_config do
- require "active_record/schema_dumper"
- ActiveRecord::Base.configurations.configs_for(env_name: Rails.env).each do |db_config|
- filename = ActiveRecord::Tasks::DatabaseTasks.dump_filename(db_config.spec_name, :ruby)
- File.open(filename, "w:utf-8") do |file|
- ActiveRecord::Base.establish_connection(db_config.config)
- ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, file)
- end
+ ActiveRecord::Base.configurations.configs_for(env_name: ActiveRecord::Tasks::DatabaseTasks.env).each do |db_config|
+ ActiveRecord::Base.establish_connection(db_config.config)
+ ActiveRecord::Tasks::DatabaseTasks.dump_schema(db_config.config, :ruby, db_config.spec_name)
end
db_namespace["schema:dump"].reenable
@@ -318,7 +402,7 @@ db_namespace = namespace :db do
namespace :cache do
desc "Creates a db/schema_cache.yml file."
task dump: :load_config do
- ActiveRecord::Base.configurations.configs_for(env_name: Rails.env).each do |db_config|
+ ActiveRecord::Base.configurations.configs_for(env_name: ActiveRecord::Tasks::DatabaseTasks.env).each do |db_config|
ActiveRecord::Base.establish_connection(db_config.config)
filename = ActiveRecord::Tasks::DatabaseTasks.cache_dump_filename(db_config.spec_name)
ActiveRecord::Tasks::DatabaseTasks.dump_schema_cache(
@@ -330,7 +414,7 @@ db_namespace = namespace :db do
desc "Clears a db/schema_cache.yml file."
task clear: :load_config do
- ActiveRecord::Base.configurations.configs_for(env_name: Rails.env).each do |db_config|
+ ActiveRecord::Base.configurations.configs_for(env_name: ActiveRecord::Tasks::DatabaseTasks.env).each do |db_config|
filename = ActiveRecord::Tasks::DatabaseTasks.cache_dump_filename(db_config.spec_name)
rm_f filename, verbose: false
end
@@ -341,16 +425,9 @@ db_namespace = namespace :db do
namespace :structure do
desc "Dumps the database structure to db/structure.sql. Specify another file with SCHEMA=db/my_structure.sql"
task dump: :load_config do
- ActiveRecord::Base.configurations.configs_for(env_name: Rails.env).each do |db_config|
+ ActiveRecord::Base.configurations.configs_for(env_name: ActiveRecord::Tasks::DatabaseTasks.env).each do |db_config|
ActiveRecord::Base.establish_connection(db_config.config)
- filename = ActiveRecord::Tasks::DatabaseTasks.dump_filename(db_config.spec_name, :sql)
- ActiveRecord::Tasks::DatabaseTasks.structure_dump(db_config.config, filename)
- if ActiveRecord::SchemaMigration.table_exists?
- File.open(filename, "a") do |f|
- f.puts ActiveRecord::Base.connection.dump_schema_information
- f.print "\n"
- end
- end
+ ActiveRecord::Tasks::DatabaseTasks.dump_schema(db_config.config, :sql, db_config.spec_name)
end
db_namespace["structure:dump"].reenable
diff --git a/activerecord/lib/active_record/readonly_attributes.rb b/activerecord/lib/active_record/readonly_attributes.rb
index 7bc26993d5..c851ed52c3 100644
--- a/activerecord/lib/active_record/readonly_attributes.rb
+++ b/activerecord/lib/active_record/readonly_attributes.rb
@@ -19,6 +19,10 @@ module ActiveRecord
def readonly_attributes
_attr_readonly
end
+
+ def readonly_attribute?(name) # :nodoc:
+ _attr_readonly.include?(name)
+ end
end
end
end
diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb
index 1312bf6f91..cbfa60d4d9 100644
--- a/activerecord/lib/active_record/reflection.rb
+++ b/activerecord/lib/active_record/reflection.rb
@@ -477,7 +477,7 @@ module ActiveRecord
def check_preloadable!
return unless scope
- if scope.arity > 0
+ unless scope.arity == 0
raise ArgumentError, <<-MSG.squish
The association scope '#{name}' is instance dependent (the scope
block takes an argument). Preloading instance dependent scopes is
@@ -590,7 +590,6 @@ module ActiveRecord
end
private
-
def calculate_constructable(macro, options)
true
end
@@ -704,7 +703,6 @@ module ActiveRecord
end
private
-
def calculate_constructable(macro, options)
!options[:through]
end
diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb
index cd62b0b881..ea8f44752b 100644
--- a/activerecord/lib/active_record/relation.rb
+++ b/activerecord/lib/active_record/relation.rb
@@ -291,36 +291,58 @@ module ActiveRecord
limit_value ? records.many? : size > 1
end
- # Returns a cache key that can be used to identify the records fetched by
- # this query. The cache key is built with a fingerprint of the sql query,
- # the number of records matched by the query and a timestamp of the last
- # updated record. When a new record comes to match the query, or any of
- # the existing records is updated or deleted, the cache key changes.
+ # Returns a stable cache key that can be used to identify this query.
+ # The cache key is built with a fingerprint of the SQL query.
#
- # Product.where("name like ?", "%Cosmic Encounter%").cache_key
- # # => "products/query-1850ab3d302391b85b8693e941286659-1-20150714212553907087000"
+ # Product.where("name like ?", "%Cosmic Encounter%").cache_key
+ # # => "products/query-1850ab3d302391b85b8693e941286659"
#
- # If the collection is loaded, the method will iterate through the records
- # to generate the timestamp, otherwise it will trigger one SQL query like:
+ # If ActiveRecord::Base.collection_cache_versioning is turned off, as it was
+ # in Rails 6.0 and earlier, the cache key will also include a version.
#
- # SELECT COUNT(*), MAX("products"."updated_at") FROM "products" WHERE (name like '%Cosmic Encounter%')
+ # ActiveRecord::Base.collection_cache_versioning = false
+ # Product.where("name like ?", "%Cosmic Encounter%").cache_key
+ # # => "products/query-1850ab3d302391b85b8693e941286659-1-20150714212553907087000"
#
# You can also pass a custom timestamp column to fetch the timestamp of the
# last updated record.
#
# Product.where("name like ?", "%Game%").cache_key(:last_reviewed_at)
- #
- # You can customize the strategy to generate the key on a per model basis
- # overriding ActiveRecord::Base#collection_cache_key.
def cache_key(timestamp_column = :updated_at)
@cache_keys ||= {}
- @cache_keys[timestamp_column] ||= @klass.collection_cache_key(self, timestamp_column)
+ @cache_keys[timestamp_column] ||= klass.collection_cache_key(self, timestamp_column)
end
def compute_cache_key(timestamp_column = :updated_at) # :nodoc:
query_signature = ActiveSupport::Digest.hexdigest(to_sql)
key = "#{klass.model_name.cache_key}/query-#{query_signature}"
+ if cache_version(timestamp_column)
+ key
+ else
+ "#{key}-#{compute_cache_version(timestamp_column)}"
+ end
+ end
+ private :compute_cache_key
+
+ # Returns a cache version that can be used together with the cache key to form
+ # a recyclable caching scheme. The cache version is built with the number of records
+ # matching the query, and the timestamp of the last updated record. When a new record
+ # comes to match the query, or any of the existing records is updated or deleted,
+ # the cache version changes.
+ #
+ # If the collection is loaded, the method will iterate through the records
+ # to generate the timestamp, otherwise it will trigger one SQL query like:
+ #
+ # SELECT COUNT(*), MAX("products"."updated_at") FROM "products" WHERE (name like '%Cosmic Encounter%')
+ def cache_version(timestamp_column = :updated_at)
+ if collection_cache_versioning
+ @cache_versions ||= {}
+ @cache_versions[timestamp_column] ||= compute_cache_version(timestamp_column)
+ end
+ end
+
+ def compute_cache_version(timestamp_column) # :nodoc:
if loaded? || distinct_value
size = records.size
if size > 0
@@ -356,11 +378,12 @@ module ActiveRecord
end
if timestamp
- "#{key}-#{size}-#{timestamp.utc.to_s(cache_timestamp_format)}"
+ "#{size}-#{timestamp.utc.to_s(cache_timestamp_format)}"
else
- "#{key}-#{size}"
+ "#{size}"
end
end
+ private :compute_cache_version
# Scope all queries to the current scope.
#
@@ -445,7 +468,19 @@ module ActiveRecord
end
end
- def update_counters(counters) # :nodoc:
+ # Updates the counters of the records in the current relation.
+ #
+ # === Parameters
+ #
+ # * +counter+ - A Hash containing the names of the fields to update as keys and the amount to update as values.
+ # * <tt>:touch</tt> option - Touch the timestamp columns when updating.
+ # * If attributes names are passed, they are updated along with update_at/on attributes.
+ #
+ # === Examples
+ #
+ # # For Posts by a given author increment the comment_count by 1.
+ # Post.where(author_id: author.id).update_counters(comment_count: 1)
+ def update_counters(counters)
touch = counters.delete(:touch)
updates = {}
@@ -530,8 +565,8 @@ module ActiveRecord
# # => ActiveRecord::ActiveRecordError: delete_all doesn't support distinct
def delete_all
invalid_methods = INVALID_METHODS_FOR_DELETE_ALL.select do |method|
- value = get_value(method)
- SINGLE_VALUE_METHODS.include?(method) ? value : value.any?
+ value = @values[method]
+ method == :distinct ? value : value&.any?
end
if invalid_methods.any?
raise ActiveRecordError.new("delete_all doesn't support #{invalid_methods.join(', ')}")
diff --git a/activerecord/lib/active_record/relation/batches.rb b/activerecord/lib/active_record/relation/batches.rb
index 9c579843b1..30b8edd0bd 100644
--- a/activerecord/lib/active_record/relation/batches.rb
+++ b/activerecord/lib/active_record/relation/batches.rb
@@ -258,7 +258,6 @@ module ActiveRecord
end
private
-
def apply_limits(relation, start, finish)
relation = apply_start_limit(relation, start) if start
relation = apply_finish_limit(relation, finish) if finish
diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb
index 801e312658..0a14a33c1d 100644
--- a/activerecord/lib/active_record/relation/calculations.rb
+++ b/activerecord/lib/active_record/relation/calculations.rb
@@ -260,10 +260,8 @@ module ActiveRecord
def aggregate_column(column_name)
return column_name if Arel::Expressions === column_name
- if @klass.has_attribute?(column_name) || @klass.attribute_alias?(column_name)
- @klass.arel_attribute(column_name)
- else
- Arel.sql(column_name == :all ? "*" : column_name.to_s)
+ arel_column(column_name.to_s) do |name|
+ Arel.sql(column_name == :all ? "*" : name)
end
end
@@ -342,7 +340,7 @@ module ActiveRecord
}
relation = except(:group).distinct!(false)
- relation.group_values = group_aliases
+ relation.group_values = group_fields
relation.select_values = select_values
calculated_data = skip_query_cache_if_necessary { @klass.connection.select_all(relation.arel, nil) }
diff --git a/activerecord/lib/active_record/relation/delegation.rb b/activerecord/lib/active_record/relation/delegation.rb
index 7a53a9d1c7..2f61c05eca 100644
--- a/activerecord/lib/active_record/relation/delegation.rb
+++ b/activerecord/lib/active_record/relation/delegation.rb
@@ -45,7 +45,10 @@ module ActiveRecord
private
def generated_relation_methods
- @generated_relation_methods ||= GeneratedRelationMethods.new
+ @generated_relation_methods ||= GeneratedRelationMethods.new.tap do |mod|
+ const_set(:GeneratedRelationMethods, mod)
+ private_constant :GeneratedRelationMethods
+ end
end
end
@@ -96,7 +99,6 @@ module ActiveRecord
end
private
-
def method_missing(method, *args, &block)
if @klass.respond_to?(method)
@klass.generate_relation_method(method)
@@ -113,7 +115,6 @@ module ActiveRecord
end
private
-
def relation_class_for(klass)
klass.relation_delegate_class(self)
end
diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb
index 9450e4d3c5..1dbf4808fd 100644
--- a/activerecord/lib/active_record/relation/finder_methods.rb
+++ b/activerecord/lib/active_record/relation/finder_methods.rb
@@ -346,7 +346,6 @@ module ActiveRecord
end
private
-
def offset_index
offset_value || 0
end
@@ -355,7 +354,7 @@ module ActiveRecord
conditions = sanitize_forbidden_attributes(conditions)
if distinct_value && offset_value
- relation = limit(1)
+ relation = except(:order).limit!(1)
else
relation = except(:select, :distinct, :order)._select!(ONE_AS_ONE).limit!(1)
end
@@ -371,7 +370,9 @@ module ActiveRecord
end
def apply_join_dependency(eager_loading: group_values.empty?)
- join_dependency = construct_join_dependency(eager_load_values + includes_values)
+ join_dependency = construct_join_dependency(
+ eager_load_values + includes_values, Arel::Nodes::OuterJoin
+ )
relation = except(:includes, :eager_load, :preload).joins!(join_dependency)
if eager_loading && !using_limitable_reflections?(join_dependency.reflections)
diff --git a/activerecord/lib/active_record/relation/merger.rb b/activerecord/lib/active_record/relation/merger.rb
index 6bb77b355c..e1735c0522 100644
--- a/activerecord/lib/active_record/relation/merger.rb
+++ b/activerecord/lib/active_record/relation/merger.rb
@@ -89,7 +89,6 @@ module ActiveRecord
end
private
-
def merge_preloads
return if other.preload_values.empty? && other.includes_values.empty?
@@ -123,7 +122,9 @@ module ActiveRecord
end
end
- join_dependency = other.construct_join_dependency(associations)
+ join_dependency = other.construct_join_dependency(
+ associations, Arel::Nodes::InnerJoin
+ )
relation.joins!(join_dependency, *others)
end
end
@@ -135,7 +136,9 @@ module ActiveRecord
relation.left_outer_joins!(*other.left_outer_joins_values)
else
associations = other.left_outer_joins_values
- join_dependency = other.construct_join_dependency(associations)
+ join_dependency = other.construct_join_dependency(
+ associations, Arel::Nodes::OuterJoin
+ )
relation.joins!(join_dependency)
end
end
diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb
index 90b5e9a118..6957ba052b 100644
--- a/activerecord/lib/active_record/relation/query_methods.rb
+++ b/activerecord/lib/active_record/relation/query_methods.rb
@@ -41,18 +41,31 @@ module ActiveRecord
#
# User.where.not(name: %w(Ko1 Nobu))
# # SELECT * FROM users WHERE name NOT IN ('Ko1', 'Nobu')
- #
- # User.where.not(name: "Jon", role: "admin")
- # # SELECT * FROM users WHERE name != 'Jon' AND role != 'admin'
def not(opts, *rest)
opts = sanitize_forbidden_attributes(opts)
where_clause = @scope.send(:where_clause_factory).build(opts, rest)
@scope.references!(PredicateBuilder.references(opts)) if Hash === opts
- @scope.where_clause += where_clause.invert
+
+ if not_behaves_as_nor?(opts)
+ ActiveSupport::Deprecation.warn(<<~MSG.squish)
+ NOT conditions will no longer behave as NOR in Rails 6.1.
+ To continue using NOR conditions, NOT each conditions manually
+ (`#{ opts.keys.map { |key| ".where.not(#{key.inspect} => ...)" }.join }`).
+ MSG
+ @scope.where_clause += where_clause.invert(:nor)
+ else
+ @scope.where_clause += where_clause.invert
+ end
+
@scope
end
+
+ private
+ def not_behaves_as_nor?(opts)
+ opts.is_a?(Hash) && opts.size > 1
+ end
end
FROZEN_EMPTY_ARRAY = [].freeze
@@ -67,11 +80,13 @@ module ActiveRecord
end
class_eval <<-CODE, __FILE__, __LINE__ + 1
def #{method_name} # def includes_values
- get_value(#{name.inspect}) # get_value(:includes)
+ default = DEFAULT_VALUES[:#{name}] # default = DEFAULT_VALUES[:includes]
+ @values.fetch(:#{name}, default) # @values.fetch(:includes, default)
end # end
def #{method_name}=(value) # def includes_values=(value)
- set_value(#{name.inspect}, value) # set_value(:includes, value)
+ assert_mutability! # assert_mutability!
+ @values[:#{name}] = value # @values[:includes] = value
end # end
CODE
end
@@ -123,7 +138,7 @@ module ActiveRecord
end
def includes!(*args) # :nodoc:
- args.reject!(&:blank?)
+ args.compact_blank!
args.flatten!
self.includes_values |= args
@@ -250,7 +265,7 @@ module ActiveRecord
end
def _select!(*fields) # :nodoc:
- fields.reject!(&:blank?)
+ fields.compact_blank!
fields.flatten!
self.select_values += fields
self
@@ -417,7 +432,8 @@ module ActiveRecord
if !VALID_UNSCOPING_VALUES.include?(scope)
raise ArgumentError, "Called unscope() with invalid unscoping argument ':#{scope}'. Valid arguments are :#{VALID_UNSCOPING_VALUES.to_a.join(", :")}."
end
- set_value(scope, DEFAULT_VALUES[scope])
+ assert_mutability!
+ @values[scope] = DEFAULT_VALUES[scope]
when Hash
scope.each do |key, target_value|
if key != :where
@@ -936,7 +952,7 @@ module ActiveRecord
def optimizer_hints!(*args) # :nodoc:
args.flatten!
- self.optimizer_hints_values += args
+ self.optimizer_hints_values |= args
self
end
@@ -949,7 +965,7 @@ module ActiveRecord
def reverse_order! # :nodoc:
orders = order_values.uniq
- orders.reject!(&:blank?)
+ orders.compact_blank!
self.order_values = reverse_sql_order(orders)
self
end
@@ -989,9 +1005,9 @@ module ActiveRecord
@arel ||= build_arel(aliases)
end
- def construct_join_dependency(associations) # :nodoc:
+ def construct_join_dependency(associations, join_type) # :nodoc:
ActiveRecord::Associations::JoinDependency.new(
- klass, table, associations
+ klass, table, associations, join_type
)
end
@@ -1005,17 +1021,6 @@ module ActiveRecord
end
private
- # Returns a relation value with a given name
- def get_value(name)
- @values.fetch(name, DEFAULT_VALUES[name])
- end
-
- # Sets the relation value with the given name
- def set_value(name, value)
- assert_mutability!
- @values[name] = value
- end
-
def assert_mutability!
raise ImmutableRelation if @loaded
raise ImmutableRelation if defined?(@arel) && @arel
@@ -1048,7 +1053,7 @@ module ActiveRecord
)
arel.skip(Arel::Nodes::BindParam.new(offset_attribute))
end
- arel.group(*arel_columns(group_values.uniq.reject(&:blank?))) unless group_values.empty?
+ arel.group(*arel_columns(group_values.uniq.compact_blank)) unless group_values.empty?
build_order(arel)
@@ -1097,7 +1102,7 @@ module ActiveRecord
def build_joins(manager, joins, aliases)
unless left_outer_joins_values.empty?
left_joins = valid_association_list(left_outer_joins_values.flatten)
- joins << construct_join_dependency(left_joins)
+ joins.unshift construct_join_dependency(left_joins, Arel::Nodes::OuterJoin)
end
buckets = joins.group_by do |join|
@@ -1123,27 +1128,21 @@ module ActiveRecord
association_joins = buckets[:association_join]
stashed_joins = buckets[:stashed_join]
- join_nodes = buckets[:join_node].uniq
- string_joins = buckets[:string_join].map(&:strip).uniq
+ join_nodes = buckets[:join_node].tap(&:uniq!)
+ string_joins = buckets[:string_join].compact_blank!.map!(&:strip).tap(&:uniq!)
- join_list = join_nodes + convert_join_strings_to_ast(string_joins)
- alias_tracker = alias_tracker(join_list, aliases)
+ string_joins.map! { |join| table.create_string_join(Arel.sql(join)) }
- join_dependency = construct_join_dependency(association_joins)
+ join_sources = manager.join_sources
+ join_sources.concat(join_nodes) unless join_nodes.empty?
- joins = join_dependency.join_constraints(stashed_joins, join_type, alias_tracker)
- joins.each { |join| manager.from(join) }
-
- manager.join_sources.concat(join_list)
-
- alias_tracker.aliases
- end
+ unless association_joins.empty? && stashed_joins.empty?
+ alias_tracker = alias_tracker(join_nodes + string_joins, aliases)
+ join_dependency = construct_join_dependency(association_joins, join_type)
+ join_sources.concat(join_dependency.join_constraints(stashed_joins, alias_tracker))
+ end
- def convert_join_strings_to_ast(joins)
- joins
- .flatten
- .reject(&:blank?)
- .map { |join| table.create_string_join(Arel.sql(join)) }
+ join_sources.concat(string_joins) unless string_joins.empty?
end
def build_select(arel)
@@ -1160,8 +1159,9 @@ module ActiveRecord
columns.flat_map do |field|
case field
when Symbol
- field = field.to_s
- arel_column(field, &connection.method(:quote_table_name))
+ arel_column(field.to_s) do |attr_name|
+ connection.quote_table_name(attr_name)
+ end
when String
arel_column(field, &:itself)
when Proc
@@ -1173,7 +1173,7 @@ module ActiveRecord
end
def arel_column(field)
- field = klass.attribute_alias(field) if klass.attribute_alias?(field)
+ field = klass.attribute_aliases[field] || field
from = from_clause.name || from_clause.value
if klass.columns_hash.key?(field) && (!from || table_name_matches?(from))
@@ -1227,7 +1227,7 @@ module ActiveRecord
def build_order(arel)
orders = order_values.uniq
- orders.reject!(&:blank?)
+ orders.compact_blank!
arel.order(*orders) unless orders.empty?
end
@@ -1248,6 +1248,7 @@ module ActiveRecord
end
def preprocess_order_args(order_args)
+ order_args.reject!(&:blank?)
order_args.map! do |arg|
klass.sanitize_sql_for_order(arg)
end
@@ -1255,7 +1256,7 @@ module ActiveRecord
@klass.disallow_raw_sql!(
order_args.flat_map { |a| a.is_a?(Hash) ? a.keys : a },
- permit: AttributeMethods::ClassMethods::COLUMN_NAME_WITH_ORDER
+ permit: connection.column_name_with_order_matcher
)
validate_order_args(order_args)
@@ -1268,20 +1269,14 @@ module ActiveRecord
order_args.map! do |arg|
case arg
when Symbol
- arg = arg.to_s
- arel_column(arg) {
- Arel.sql(connection.quote_table_name(arg))
- }.asc
+ order_column(arg.to_s).asc
when Hash
arg.map { |field, dir|
case field
when Arel::Nodes::SqlLiteral
field.send(dir.downcase)
else
- field = field.to_s
- arel_column(field) {
- Arel.sql(connection.quote_table_name(field))
- }.send(dir.downcase)
+ order_column(field.to_s).send(dir.downcase)
end
}
else
@@ -1290,6 +1285,16 @@ module ActiveRecord
end.flatten!
end
+ def order_column(field)
+ arel_column(field) do |attr_name|
+ if attr_name == "count" && !group_values.empty?
+ arel_attribute(attr_name)
+ else
+ Arel.sql(connection.quote_table_name(attr_name))
+ end
+ end
+ end
+
# Checks to make sure that the arguments are not blank. Note that if some
# blank-like object were initially passed into the query method, then this
# method will not raise an error.
@@ -1316,7 +1321,8 @@ module ActiveRecord
def structurally_incompatible_values_for_or(other)
values = other.values
STRUCTURAL_OR_METHODS.reject do |method|
- get_value(method) == values.fetch(method, DEFAULT_VALUES[method])
+ default = DEFAULT_VALUES[method]
+ @values.fetch(method, default) == values.fetch(method, default)
end
end
diff --git a/activerecord/lib/active_record/relation/spawn_methods.rb b/activerecord/lib/active_record/relation/spawn_methods.rb
index efc4b447aa..3f6dd50139 100644
--- a/activerecord/lib/active_record/relation/spawn_methods.rb
+++ b/activerecord/lib/active_record/relation/spawn_methods.rb
@@ -67,7 +67,6 @@ module ActiveRecord
end
private
-
def relation_with(values)
result = Relation.create(klass, values: values)
result.extend(*extending_values) if extending_values.any?
diff --git a/activerecord/lib/active_record/relation/where_clause.rb b/activerecord/lib/active_record/relation/where_clause.rb
index 47728aac30..8fae380b0a 100644
--- a/activerecord/lib/active_record/relation/where_clause.rb
+++ b/activerecord/lib/active_record/relation/where_clause.rb
@@ -70,7 +70,15 @@ module ActiveRecord
predicates == other.predicates
end
- def invert
+ def invert(as = :nand)
+ if predicates.size == 1
+ inverted_predicates = [ invert_predicate(predicates.first) ]
+ elsif as == :nor
+ inverted_predicates = predicates.map { |node| invert_predicate(node) }
+ else
+ inverted_predicates = [ Arel::Nodes::Not.new(ast) ]
+ end
+
WhereClause.new(inverted_predicates)
end
@@ -79,7 +87,6 @@ module ActiveRecord
end
protected
-
attr_reader :predicates
def referenced_columns
@@ -115,10 +122,6 @@ module ActiveRecord
node.respond_to?(:operator) && node.operator == :==
end
- def inverted_predicates
- predicates.map { |node| invert_predicate(node) }
- end
-
def invert_predicate(node)
case node
when NilClass
diff --git a/activerecord/lib/active_record/result.rb b/activerecord/lib/active_record/result.rb
index da6d10b6ec..3b615f29a3 100644
--- a/activerecord/lib/active_record/result.rb
+++ b/activerecord/lib/active_record/result.rb
@@ -132,7 +132,6 @@ module ActiveRecord
end
private
-
def column_type(name, type_overrides = {})
type_overrides.fetch(name) do
column_types.fetch(name, Type.default_value)
diff --git a/activerecord/lib/active_record/sanitization.rb b/activerecord/lib/active_record/sanitization.rb
index 750766714d..b16cbb0f84 100644
--- a/activerecord/lib/active_record/sanitization.rb
+++ b/activerecord/lib/active_record/sanitization.rb
@@ -61,8 +61,9 @@ module ActiveRecord
# # => "id ASC"
def sanitize_sql_for_order(condition)
if condition.is_a?(Array) && condition.first.to_s.include?("?")
- disallow_raw_sql!([condition.first],
- permit: AttributeMethods::ClassMethods::COLUMN_NAME_WITH_ORDER
+ disallow_raw_sql!(
+ [condition.first],
+ permit: connection.column_name_with_order_matcher
)
# Ensure we aren't dealing with a subclass of String that might
@@ -133,6 +134,33 @@ module ActiveRecord
end
end
+ def disallow_raw_sql!(args, permit: connection.column_name_matcher) # :nodoc:
+ unexpected = nil
+ args.each do |arg|
+ next if arg.is_a?(Symbol) || Arel.arel_node?(arg) || permit.match?(arg.to_s)
+ (unexpected ||= []) << arg
+ end
+
+ return unless unexpected
+
+ if allow_unsafe_raw_sql == :deprecated
+ ActiveSupport::Deprecation.warn(
+ "Dangerous query method (method whose arguments are used as raw " \
+ "SQL) called with non-attribute argument(s): " \
+ "#{unexpected.map(&:inspect).join(", ")}. Non-attribute " \
+ "arguments will be disallowed in Rails 6.1. This method should " \
+ "not be called with user-provided values, such as request " \
+ "parameters or model attributes. Known-safe values can be passed " \
+ "by wrapping them in Arel.sql()."
+ )
+ else
+ raise(ActiveRecord::UnknownAttributeReference,
+ "Query method called with non-attribute argument(s): " +
+ unexpected.map(&:inspect).join(", ")
+ )
+ end
+ end
+
private
def replace_bind_variables(statement, values)
raise_if_bind_arity_mismatch(statement, statement.count("?"), values.size)
diff --git a/activerecord/lib/active_record/schema.rb b/activerecord/lib/active_record/schema.rb
index 76bf53387d..aba25fb375 100644
--- a/activerecord/lib/active_record/schema.rb
+++ b/activerecord/lib/active_record/schema.rb
@@ -50,7 +50,7 @@ module ActiveRecord
instance_eval(&block)
if info[:version].present?
- ActiveRecord::SchemaMigration.create_table
+ connection.schema_migration.create_table
connection.assume_migrated_upto_version(info[:version])
end
diff --git a/activerecord/lib/active_record/schema_dumper.rb b/activerecord/lib/active_record/schema_dumper.rb
index 2f7cc07221..f4b1f536b3 100644
--- a/activerecord/lib/active_record/schema_dumper.rb
+++ b/activerecord/lib/active_record/schema_dumper.rb
@@ -146,7 +146,11 @@ HEADER
raise StandardError, "Unknown type '#{column.sql_type}' for column '#{column.name}'" unless @connection.valid_type?(column.type)
next if column.name == pk
type, colspec = column_spec(column)
- tbl.print " t.#{type} #{column.name.inspect}"
+ if type.is_a?(Symbol)
+ tbl.print " t.#{type} #{column.name.inspect}"
+ else
+ tbl.print " t.column #{column.name.inspect}, #{type.inspect}"
+ end
tbl.print ", #{format_colspec(colspec)}" if colspec.present?
tbl.puts
end
diff --git a/activerecord/lib/active_record/schema_migration.rb b/activerecord/lib/active_record/schema_migration.rb
index 74547de862..dec7fee986 100644
--- a/activerecord/lib/active_record/schema_migration.rb
+++ b/activerecord/lib/active_record/schema_migration.rb
@@ -22,10 +22,6 @@ module ActiveRecord
"#{table_name_prefix}#{schema_migrations_table_name}#{table_name_suffix}"
end
- def table_exists?
- connection.table_exists?(table_name)
- end
-
def create_table
unless table_exists?
version_options = connection.internal_string_options_for_primary_key
diff --git a/activerecord/lib/active_record/scoping.rb b/activerecord/lib/active_record/scoping.rb
index 35e9dcbffc..62c7988bd8 100644
--- a/activerecord/lib/active_record/scoping.rb
+++ b/activerecord/lib/active_record/scoping.rb
@@ -95,7 +95,6 @@ module ActiveRecord
end
private
-
def raise_invalid_scope_type!(scope_type)
if !VALID_SCOPE_TYPES.include?(scope_type)
raise ArgumentError, "Invalid scope type '#{scope_type}' sent to the registry. Scope types must be included in VALID_SCOPE_TYPES"
diff --git a/activerecord/lib/active_record/scoping/default.rb b/activerecord/lib/active_record/scoping/default.rb
index 87bcfd5181..151eef362b 100644
--- a/activerecord/lib/active_record/scoping/default.rb
+++ b/activerecord/lib/active_record/scoping/default.rb
@@ -44,7 +44,6 @@ module ActiveRecord
end
private
-
# Use this macro in your model to set a default scope for all operations on
# the model.
#
diff --git a/activerecord/lib/active_record/scoping/named.rb b/activerecord/lib/active_record/scoping/named.rb
index cd9801b7a0..7baef99e83 100644
--- a/activerecord/lib/active_record/scoping/named.rb
+++ b/activerecord/lib/active_record/scoping/named.rb
@@ -204,7 +204,6 @@ module ActiveRecord
end
private
-
def valid_scope_name?(name)
if respond_to?(name, true) && logger
logger.warn "Creating scope :#{name}. " \
diff --git a/activerecord/lib/active_record/table_metadata.rb b/activerecord/lib/active_record/table_metadata.rb
index b67479fb6a..9a1176db6a 100644
--- a/activerecord/lib/active_record/table_metadata.rb
+++ b/activerecord/lib/active_record/table_metadata.rb
@@ -4,17 +4,18 @@ module ActiveRecord
class TableMetadata # :nodoc:
delegate :foreign_type, :foreign_key, :join_primary_key, :join_foreign_key, to: :association, prefix: true
- def initialize(klass, arel_table, association = nil)
+ def initialize(klass, arel_table, association = nil, types = klass)
@klass = klass
+ @types = types
@arel_table = arel_table
@association = association
end
def resolve_column_aliases(hash)
new_hash = hash.dup
- hash.each do |key, _|
- if (key.is_a?(Symbol)) && klass.attribute_alias?(key)
- new_hash[klass.attribute_alias(key)] = new_hash.delete(key)
+ hash.each_key do |key|
+ if key.is_a?(Symbol) && new_key = klass.attribute_aliases[key.to_s]
+ new_hash[new_key] = new_hash.delete(key)
end
end
new_hash
@@ -29,11 +30,7 @@ module ActiveRecord
end
def type(column_name)
- if klass
- klass.type_for_attribute(column_name)
- else
- Type.default_value
- end
+ types.type_for_attribute(column_name)
end
def has_column?(column_name)
@@ -52,13 +49,12 @@ module ActiveRecord
elsif association && !association.polymorphic?
association_klass = association.klass
arel_table = association_klass.arel_table.alias(table_name)
+ TableMetadata.new(association_klass, arel_table, association)
else
type_caster = TypeCaster::Connection.new(klass, table_name)
- association_klass = nil
arel_table = Arel::Table.new(table_name, type_caster: type_caster)
+ TableMetadata.new(nil, arel_table, association, type_caster)
end
-
- TableMetadata.new(association_klass, arel_table, association)
end
def polymorphic_association?
@@ -74,6 +70,6 @@ module ActiveRecord
end
private
- attr_reader :klass, :arel_table, :association
+ attr_reader :klass, :types, :arel_table, :association
end
end
diff --git a/activerecord/lib/active_record/tasks/database_tasks.rb b/activerecord/lib/active_record/tasks/database_tasks.rb
index 7285c15477..5d1ce19829 100644
--- a/activerecord/lib/active_record/tasks/database_tasks.rb
+++ b/activerecord/lib/active_record/tasks/database_tasks.rb
@@ -141,8 +141,21 @@ module ActiveRecord
end
end
- def for_each
- databases = Rails.application.config.load_database_yaml
+ def setup_initial_database_yaml
+ return {} unless defined?(Rails)
+
+ begin
+ Rails.application.config.load_database_yaml
+ rescue
+ $stderr.puts "Rails couldn't infer whether you are using multiple databases from your database.yml and can't generate the tasks for the non-primary databases. If you'd like to use this feature, please simplify your ERB."
+
+ {}
+ end
+ end
+
+ def for_each(databases)
+ return {} unless defined?(Rails)
+
database_configs = ActiveRecord::DatabaseConfigurations.new(databases).configs_for(env_name: Rails.env)
# if this is a single database application we don't want tasks for each primary database
@@ -153,8 +166,22 @@ module ActiveRecord
end
end
- def create_current(environment = env)
- each_current_configuration(environment) { |configuration|
+ def raise_for_multi_db(environment = env, command:)
+ db_configs = ActiveRecord::Base.configurations.configs_for(env_name: environment)
+
+ if db_configs.count > 1
+ dbs_list = []
+
+ db_configs.each do |db|
+ dbs_list << "#{command}:#{db.spec_name}"
+ end
+
+ raise "You're using a multiple database application. To use `#{command}` you must run the namespaced task with a VERSION. Available tasks are #{dbs_list.to_sentence}."
+ end
+ end
+
+ def create_current(environment = env, spec_name = nil)
+ each_current_configuration(environment, spec_name) { |configuration|
create configuration
}
ActiveRecord::Base.establish_connection(environment.to_sym)
@@ -184,9 +211,10 @@ module ActiveRecord
def truncate_tables(configuration)
ActiveRecord::Base.connected_to(database: { truncation: configuration }) do
- table_names = ActiveRecord::Base.connection.tables
+ conn = ActiveRecord::Base.connection
+ table_names = conn.tables
table_names -= [
- SchemaMigration.table_name,
+ conn.schema_migration.table_name,
InternalMetadata.table_name
]
@@ -217,7 +245,7 @@ module ActiveRecord
end
def migrate_status
- unless ActiveRecord::SchemaMigration.table_exists?
+ unless ActiveRecord::Base.connection.schema_migration.table_exists?
Kernel.abort "Schema migrations table does not exist yet."
end
@@ -309,6 +337,27 @@ module ActiveRecord
Migration.verbose = verbose_was
end
+ def dump_schema(configuration, format = ActiveRecord::Base.schema_format, spec_name = "primary") # :nodoc:
+ require "active_record/schema_dumper"
+ filename = dump_filename(spec_name, format)
+ connection = ActiveRecord::Base.connection
+
+ case format
+ when :ruby
+ File.open(filename, "w:utf-8") do |file|
+ ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, file)
+ end
+ when :sql
+ structure_dump(configuration, filename)
+ if connection.schema_migration.table_exists?
+ File.open(filename, "a") do |f|
+ f.puts connection.dump_schema_information
+ f.print "\n"
+ end
+ end
+ end
+ end
+
def schema_file(format = ActiveRecord::Base.schema_format)
File.join(db_dir, schema_file_type(format))
end
@@ -390,12 +439,14 @@ module ActiveRecord
task.is_a?(String) ? task.constantize : task
end
- def each_current_configuration(environment)
+ def each_current_configuration(environment, spec_name = nil)
environments = [environment]
environments << "test" if environment == "development"
environments.each do |env|
ActiveRecord::Base.configurations.configs_for(env_name: env).each do |db_config|
+ next if spec_name && spec_name != db_config.spec_name
+
yield db_config.config, db_config.spec_name, env
end
end
diff --git a/activerecord/lib/active_record/tasks/mysql_database_tasks.rb b/activerecord/lib/active_record/tasks/mysql_database_tasks.rb
index 1c1b29b5e1..e3efeb75b5 100644
--- a/activerecord/lib/active_record/tasks/mysql_database_tasks.rb
+++ b/activerecord/lib/active_record/tasks/mysql_database_tasks.rb
@@ -3,6 +3,8 @@
module ActiveRecord
module Tasks # :nodoc:
class MySQLDatabaseTasks # :nodoc:
+ ER_DB_CREATE_EXISTS = 1007
+
delegate :connection, :establish_connection, to: ActiveRecord::Base
def initialize(configuration)
@@ -14,7 +16,7 @@ module ActiveRecord
connection.create_database configuration["database"], creation_options
establish_connection configuration
rescue ActiveRecord::StatementInvalid => error
- if error.message.include?("database exists")
+ if connection.error_number(error.cause) == ER_DB_CREATE_EXISTS
raise DatabaseAlreadyExists
else
raise
@@ -67,7 +69,6 @@ module ActiveRecord
end
private
-
attr_reader :configuration
def configuration_without_database
diff --git a/activerecord/lib/active_record/tasks/postgresql_database_tasks.rb b/activerecord/lib/active_record/tasks/postgresql_database_tasks.rb
index 8acb11f75f..626ffdfdf9 100644
--- a/activerecord/lib/active_record/tasks/postgresql_database_tasks.rb
+++ b/activerecord/lib/active_record/tasks/postgresql_database_tasks.rb
@@ -89,7 +89,6 @@ module ActiveRecord
end
private
-
attr_reader :configuration
def encoding
diff --git a/activerecord/lib/active_record/tasks/sqlite_database_tasks.rb b/activerecord/lib/active_record/tasks/sqlite_database_tasks.rb
index a82cea80ca..f67a3498b6 100644
--- a/activerecord/lib/active_record/tasks/sqlite_database_tasks.rb
+++ b/activerecord/lib/active_record/tasks/sqlite_database_tasks.rb
@@ -59,7 +59,6 @@ module ActiveRecord
end
private
-
attr_reader :configuration, :root
def run_cmd(cmd, args, out)
diff --git a/activerecord/lib/active_record/test_fixtures.rb b/activerecord/lib/active_record/test_fixtures.rb
index 8c60d71669..1d6fef1eb9 100644
--- a/activerecord/lib/active_record/test_fixtures.rb
+++ b/activerecord/lib/active_record/test_fixtures.rb
@@ -179,7 +179,6 @@ module ActiveRecord
end
private
-
# Shares the writing connection pool with connections on
# other handlers.
#
diff --git a/activerecord/lib/active_record/timestamp.rb b/activerecord/lib/active_record/timestamp.rb
index 04a1c03474..c883d368b5 100644
--- a/activerecord/lib/active_record/timestamp.rb
+++ b/activerecord/lib/active_record/timestamp.rb
@@ -59,19 +59,26 @@ module ActiveRecord
attribute_names.index_with(time || current_time_from_proper_timezone)
end
- private
- def timestamp_attributes_for_create_in_model
- timestamp_attributes_for_create.select { |c| column_names.include?(c) }
- end
+ def timestamp_attributes_for_create_in_model
+ @timestamp_attributes_for_create_in_model ||=
+ (timestamp_attributes_for_create & column_names).freeze
+ end
- def timestamp_attributes_for_update_in_model
- timestamp_attributes_for_update.select { |c| column_names.include?(c) }
- end
+ def timestamp_attributes_for_update_in_model
+ @timestamp_attributes_for_update_in_model ||=
+ (timestamp_attributes_for_update & column_names).freeze
+ end
- def all_timestamp_attributes_in_model
- timestamp_attributes_for_create_in_model + timestamp_attributes_for_update_in_model
- end
+ def all_timestamp_attributes_in_model
+ @all_timestamp_attributes_in_model ||=
+ (timestamp_attributes_for_create_in_model + timestamp_attributes_for_update_in_model).freeze
+ end
+
+ def current_time_from_proper_timezone
+ default_timezone == :utc ? Time.now.utc : Time.now
+ end
+ private
def timestamp_attributes_for_create
["created_at", "created_on"]
end
@@ -80,13 +87,15 @@ module ActiveRecord
["updated_at", "updated_on"]
end
- def current_time_from_proper_timezone
- default_timezone == :utc ? Time.now.utc : Time.now
+ def reload_schema_from_cache
+ @timestamp_attributes_for_create_in_model = nil
+ @timestamp_attributes_for_update_in_model = nil
+ @all_timestamp_attributes_in_model = nil
+ super
end
end
private
-
def _create_record
if record_timestamps
current_time = current_time_from_proper_timezone
@@ -124,19 +133,19 @@ module ActiveRecord
end
def timestamp_attributes_for_create_in_model
- self.class.send(:timestamp_attributes_for_create_in_model)
+ self.class.timestamp_attributes_for_create_in_model
end
def timestamp_attributes_for_update_in_model
- self.class.send(:timestamp_attributes_for_update_in_model)
+ self.class.timestamp_attributes_for_update_in_model
end
def all_timestamp_attributes_in_model
- self.class.send(:all_timestamp_attributes_in_model)
+ self.class.all_timestamp_attributes_in_model
end
def current_time_from_proper_timezone
- self.class.send(:current_time_from_proper_timezone)
+ self.class.current_time_from_proper_timezone
end
def max_updated_column_timestamp
diff --git a/activerecord/lib/active_record/touch_later.rb b/activerecord/lib/active_record/touch_later.rb
index 980e42664b..3981bd46ad 100644
--- a/activerecord/lib/active_record/touch_later.rb
+++ b/activerecord/lib/active_record/touch_later.rb
@@ -10,12 +10,7 @@ module ActiveRecord
end
def touch_later(*names) # :nodoc:
- unless persisted?
- raise ActiveRecordError, <<-MSG.squish
- cannot touch on a new or destroyed record object. Consider using
- persisted?, new_record?, or destroyed? before touching
- MSG
- end
+ _raise_record_not_touched_error unless persisted?
@_defer_touch_attrs ||= timestamp_attributes_for_update_in_model
@_defer_touch_attrs |= names
@@ -23,6 +18,7 @@ module ActiveRecord
surreptitiously_touch @_defer_touch_attrs
add_to_transaction
+ @_new_record_before_last_commit ||= false
# touch the parents as we are not calling the after_save callbacks
self.class.reflect_on_all_associations(:belongs_to).each do |r|
@@ -40,7 +36,6 @@ module ActiveRecord
end
private
-
def surreptitiously_touch(attrs)
attrs.each { |attr| write_attribute attr, @_touch_time }
clear_attribute_changes attrs
@@ -48,6 +43,7 @@ module ActiveRecord
def touch_deferred_attributes
if has_defer_touch_attrs? && persisted?
+ @_skip_dirty_tracking = true
touch(*@_defer_touch_attrs, time: @_touch_time)
@_defer_touch_attrs, @_touch_time = nil, nil
end
diff --git a/activerecord/lib/active_record/transactions.rb b/activerecord/lib/active_record/transactions.rb
index 03a373f0af..5113e08e8e 100644
--- a/activerecord/lib/active_record/transactions.rb
+++ b/activerecord/lib/active_record/transactions.rb
@@ -164,12 +164,12 @@ module ActiveRecord
# end
# end
#
- # only "Kotori" is created. This works on MySQL and PostgreSQL. SQLite3 version >= '3.6.8' also supports it.
+ # only "Kotori" is created.
#
# Most databases don't support true nested transactions. At the time of
# writing, the only database that we're aware of that supports true nested
# transactions, is MS-SQL. Because of this, Active Record emulates nested
- # transactions by using savepoints on MySQL and PostgreSQL. See
+ # transactions by using savepoints. See
# https://dev.mysql.com/doc/refman/5.7/en/savepoint.html
# for more information about savepoints.
#
@@ -282,7 +282,6 @@ module ActiveRecord
end
private
-
def set_options_for_callbacks!(args, enforced_options = {})
options = args.extract_options!.merge!(enforced_options)
args << options
@@ -333,7 +332,7 @@ module ActiveRecord
# Ensure that it is not called if the object was never persisted (failed create),
# but call it after the commit of a destroyed object.
def committed!(should_run_callbacks: true) #:nodoc:
- if should_run_callbacks && (destroyed? || persisted?)
+ if should_run_callbacks
@_committed_already_called = true
_run_commit_without_transaction_enrollment_callbacks
_run_commit_callbacks
@@ -364,39 +363,40 @@ module ActiveRecord
def with_transaction_returning_status
status = nil
self.class.transaction do
- unless has_transactional_callbacks?
- sync_with_transaction_state
+ if has_transactional_callbacks?
+ add_to_transaction
+ else
+ sync_with_transaction_state if @transaction_state&.finalized?
@transaction_state = self.class.connection.transaction_state
end
remember_transaction_record_state
status = yield
raise ActiveRecord::Rollback unless status
- ensure
- if has_transactional_callbacks? &&
- (@_new_record_before_last_commit && !new_record? || _trigger_update_callback || _trigger_destroy_callback)
- add_to_transaction
- end
end
status
end
+ def trigger_transactional_callbacks? # :nodoc:
+ (@_new_record_before_last_commit || _trigger_update_callback) && persisted? ||
+ _trigger_destroy_callback && destroyed?
+ end
+
private
attr_reader :_committed_already_called, :_trigger_update_callback, :_trigger_destroy_callback
# Save the new record state and id of a record so it can be restored later if a transaction fails.
def remember_transaction_record_state
- @_start_transaction_state.reverse_merge!(
+ @_start_transaction_state ||= {
id: id,
new_record: @new_record,
destroyed: @destroyed,
+ attributes: @attributes,
frozen?: frozen?,
- )
- @_start_transaction_state[:level] = (@_start_transaction_state[:level] || 0) + 1
- remember_new_record_before_last_commit
- end
+ level: 0
+ }
+ @_start_transaction_state[:level] += 1
- def remember_new_record_before_last_commit
if _committed_already_called
@_new_record_before_last_commit = false
else
@@ -406,28 +406,32 @@ module ActiveRecord
# Clear the new record state and id of a record.
def clear_transaction_record_state
- @_start_transaction_state[:level] = (@_start_transaction_state[:level] || 0) - 1
+ return unless @_start_transaction_state
+ @_start_transaction_state[:level] -= 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
- @_start_transaction_state.clear
+ @_start_transaction_state = nil
@transaction_state = nil
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_restore_state = false)
- unless @_start_transaction_state.empty?
- transaction_level = (@_start_transaction_state[:level] || 0) - 1
- if transaction_level < 1 || force_restore_state
- restore_state = @_start_transaction_state
- thaw
+ if restore_state = @_start_transaction_state
+ if force_restore_state || restore_state[:level] <= 1
@new_record = restore_state[:new_record]
@destroyed = restore_state[:destroyed]
- pk = self.class.primary_key
- if pk && _read_attribute(pk) != restore_state[:id]
- _write_attribute(pk, restore_state[:id])
+ @attributes = restore_state[:attributes].map do |attr|
+ value = @attributes.fetch_value(attr.name)
+ attr = attr.with_value_from_user(value) if attr.value != value
+ attr
+ end
+ @mutations_from_database = nil
+ @mutations_before_last_save = nil
+ if @attributes.fetch_value(@primary_key) != restore_state[:id]
+ @attributes.write_from_user(@primary_key, restore_state[:id])
end
freeze if restore_state[:frozen?]
end
@@ -472,7 +476,7 @@ module ActiveRecord
# the TransactionState, and rolls back or commits the Active Record object
# as appropriate.
def sync_with_transaction_state
- if (transaction_state = @transaction_state)&.finalized?
+ if transaction_state = @transaction_state
if transaction_state.fully_committed?
force_clear_transaction_record_state
elsif transaction_state.committed?
diff --git a/activerecord/lib/active_record/type.rb b/activerecord/lib/active_record/type.rb
index 03d00006b7..4c1ef1a7e4 100644
--- a/activerecord/lib/active_record/type.rb
+++ b/activerecord/lib/active_record/type.rb
@@ -47,7 +47,6 @@ module ActiveRecord
end
private
-
def current_adapter_name
ActiveRecord::Base.connection.adapter_name.downcase.to_sym
end
diff --git a/activerecord/lib/active_record/type/adapter_specific_registry.rb b/activerecord/lib/active_record/type/adapter_specific_registry.rb
index b300fdfa05..c8c16635b1 100644
--- a/activerecord/lib/active_record/type/adapter_specific_registry.rb
+++ b/activerecord/lib/active_record/type/adapter_specific_registry.rb
@@ -11,7 +11,6 @@ module ActiveRecord
end
private
-
def registration_klass
Registration
end
@@ -53,7 +52,6 @@ module ActiveRecord
end
protected
-
attr_reader :name, :block, :adapter, :override
def priority
@@ -72,7 +70,6 @@ module ActiveRecord
end
private
-
def matches_adapter?(adapter: nil, **)
(self.adapter.nil? || adapter == self.adapter)
end
diff --git a/activerecord/lib/active_record/type/hash_lookup_type_map.rb b/activerecord/lib/active_record/type/hash_lookup_type_map.rb
index db9853fbcc..b260464df5 100644
--- a/activerecord/lib/active_record/type/hash_lookup_type_map.rb
+++ b/activerecord/lib/active_record/type/hash_lookup_type_map.rb
@@ -16,7 +16,6 @@ module ActiveRecord
end
private
-
def perform_fetch(type, *args, &block)
@mapping.fetch(type, block).call(type, *args)
end
diff --git a/activerecord/lib/active_record/type/serialized.rb b/activerecord/lib/active_record/type/serialized.rb
index 0a2f6cb9fb..a34b2fe702 100644
--- a/activerecord/lib/active_record/type/serialized.rb
+++ b/activerecord/lib/active_record/type/serialized.rb
@@ -56,7 +56,6 @@ module ActiveRecord
end
private
-
def default_value?(value)
value == coder.load(nil)
end
diff --git a/activerecord/lib/active_record/type/type_map.rb b/activerecord/lib/active_record/type/type_map.rb
index fc40b460f0..58f25ba075 100644
--- a/activerecord/lib/active_record/type/type_map.rb
+++ b/activerecord/lib/active_record/type/type_map.rb
@@ -45,7 +45,6 @@ module ActiveRecord
end
private
-
def perform_fetch(lookup_key, *args)
matching_pair = @mapping.reverse_each.detect do |key, _|
key === lookup_key
diff --git a/activerecord/lib/active_record/type/unsigned_integer.rb b/activerecord/lib/active_record/type/unsigned_integer.rb
index 4619528f81..535369e630 100644
--- a/activerecord/lib/active_record/type/unsigned_integer.rb
+++ b/activerecord/lib/active_record/type/unsigned_integer.rb
@@ -4,7 +4,6 @@ module ActiveRecord
module Type
class UnsignedInteger < ActiveModel::Type::Integer # :nodoc:
private
-
def max_value
super * 2
end
diff --git a/activerecord/lib/active_record/type_caster/connection.rb b/activerecord/lib/active_record/type_caster/connection.rb
index 7cf8181d8e..f43559f4cb 100644
--- a/activerecord/lib/active_record/type_caster/connection.rb
+++ b/activerecord/lib/active_record/type_caster/connection.rb
@@ -8,21 +8,27 @@ module ActiveRecord
@table_name = table_name
end
- def type_cast_for_database(attribute_name, value)
+ def type_cast_for_database(attr_name, value)
return value if value.is_a?(Arel::Nodes::BindParam)
- column = column_for(attribute_name)
- connection.type_cast_from_column(column, value)
+ type = type_for_attribute(attr_name)
+ type.serialize(value)
end
- private
- attr_reader :table_name
- delegate :connection, to: :@klass
+ def type_for_attribute(attr_name)
+ schema_cache = connection.schema_cache
- def column_for(attribute_name)
- if connection.schema_cache.data_source_exists?(table_name)
- connection.schema_cache.columns_hash(table_name)[attribute_name.to_s]
- end
+ if schema_cache.data_source_exists?(table_name)
+ column = schema_cache.columns_hash(table_name)[attr_name.to_s]
+ type = connection.lookup_cast_type_from_column(column) if column
end
+
+ type || Type.default_value
+ end
+
+ delegate :connection, to: :@klass, private: true
+
+ private
+ attr_reader :table_name
end
end
end
diff --git a/activerecord/lib/active_record/validations.rb b/activerecord/lib/active_record/validations.rb
index ca27a3f0ab..23e8d53168 100644
--- a/activerecord/lib/active_record/validations.rb
+++ b/activerecord/lib/active_record/validations.rb
@@ -71,7 +71,6 @@ module ActiveRecord
alias_method :validate, :valid?
private
-
def default_validation_context
new_record? ? :create : :update
end
diff --git a/activerecord/lib/active_record/validations/associated.rb b/activerecord/lib/active_record/validations/associated.rb
index 3538aeec22..dc89df4be7 100644
--- a/activerecord/lib/active_record/validations/associated.rb
+++ b/activerecord/lib/active_record/validations/associated.rb
@@ -10,7 +10,6 @@ module ActiveRecord
end
private
-
def valid_object?(record)
(record.respond_to?(:marked_for_destruction?) && record.marked_for_destruction?) || record.valid?
end
diff --git a/activerecord/lib/arel.rb b/activerecord/lib/arel.rb
index 361cd915cc..0fc07e1ede 100644
--- a/activerecord/lib/arel.rb
+++ b/activerecord/lib/arel.rb
@@ -12,7 +12,7 @@ require "arel/math"
require "arel/alias_predication"
require "arel/order_predications"
require "arel/table"
-require "arel/attributes"
+require "arel/attributes/attribute"
require "arel/visitors"
require "arel/collectors/sql_string"
diff --git a/activerecord/lib/arel/attributes.rb b/activerecord/lib/arel/attributes.rb
deleted file mode 100644
index 35d586c948..0000000000
--- a/activerecord/lib/arel/attributes.rb
+++ /dev/null
@@ -1,22 +0,0 @@
-# frozen_string_literal: true
-
-require "arel/attributes/attribute"
-
-module Arel # :nodoc: all
- module Attributes
- ###
- # Factory method to wrap a raw database +column+ to an Arel Attribute.
- def self.for(column)
- case column.type
- when :string, :text, :binary then String
- when :integer then Integer
- when :float then Float
- when :decimal then Decimal
- when :date, :datetime, :timestamp, :time then Time
- when :boolean then Boolean
- else
- Undefined
- end
- end
- end
-end
diff --git a/activerecord/lib/arel/nodes/node.rb b/activerecord/lib/arel/nodes/node.rb
index 8086102bde..0416ff58de 100644
--- a/activerecord/lib/arel/nodes/node.rb
+++ b/activerecord/lib/arel/nodes/node.rb
@@ -6,7 +6,6 @@ module Arel # :nodoc: all
# Abstract base class for all AST nodes
class Node
include Arel::FactoryMethods
- include Enumerable
###
# Factory method to create a Nodes::Not node that has the recipient of
@@ -38,13 +37,6 @@ module Arel # :nodoc: all
collector = engine.connection.visitor.accept self, collector
collector.value
end
-
- # Iterate through AST, nodes will be yielded depth-first
- def each(&block)
- return enum_for(:each) unless block_given?
-
- ::Arel::Visitors::DepthFirst.new(block).accept self
- end
end
end
end
diff --git a/activerecord/lib/arel/predications.rb b/activerecord/lib/arel/predications.rb
index 7dafde4952..895d394363 100644
--- a/activerecord/lib/arel/predications.rb
+++ b/activerecord/lib/arel/predications.rb
@@ -37,7 +37,7 @@ module Arel # :nodoc: all
def between(other)
if unboundable?(other.begin) == 1 || unboundable?(other.end) == -1
self.in([])
- elsif open_ended?(other.begin)
+ elsif other.begin.nil? || open_ended?(other.begin)
if other.end.nil? || open_ended?(other.end)
not_in([])
elsif other.exclude_end?
@@ -85,7 +85,7 @@ Passing a range to `#in` is deprecated. Call `#between`, instead.
def not_between(other)
if unboundable?(other.begin) == 1 || unboundable?(other.end) == -1
not_in([])
- elsif open_ended?(other.begin)
+ elsif other.begin.nil? || open_ended?(other.begin)
if other.end.nil? || open_ended?(other.end)
self.in([])
elsif other.exclude_end?
@@ -221,7 +221,6 @@ Passing a range to `#not_in` is deprecated. Call `#not_between`, instead.
end
private
-
def grouping_any(method_id, others, *extras)
nodes = others.map { |expr| send(method_id, expr, *extras) }
Nodes::Grouping.new nodes.inject { |memo, node|
diff --git a/activerecord/lib/arel/visitors.rb b/activerecord/lib/arel/visitors.rb
index e350f52e65..a1097f6750 100644
--- a/activerecord/lib/arel/visitors.rb
+++ b/activerecord/lib/arel/visitors.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require "arel/visitors/visitor"
-require "arel/visitors/depth_first"
require "arel/visitors/to_sql"
require "arel/visitors/sqlite"
require "arel/visitors/postgresql"
diff --git a/activerecord/lib/arel/visitors/depth_first.rb b/activerecord/lib/arel/visitors/depth_first.rb
deleted file mode 100644
index d696edc507..0000000000
--- a/activerecord/lib/arel/visitors/depth_first.rb
+++ /dev/null
@@ -1,204 +0,0 @@
-# frozen_string_literal: true
-
-module Arel # :nodoc: all
- module Visitors
- class DepthFirst < Arel::Visitors::Visitor
- def initialize(block = nil)
- @block = block || Proc.new
- super()
- end
-
- private
-
- def visit(o)
- super
- @block.call o
- end
-
- def unary(o)
- visit o.expr
- end
- alias :visit_Arel_Nodes_Else :unary
- alias :visit_Arel_Nodes_Group :unary
- alias :visit_Arel_Nodes_Cube :unary
- alias :visit_Arel_Nodes_RollUp :unary
- alias :visit_Arel_Nodes_GroupingSet :unary
- alias :visit_Arel_Nodes_GroupingElement :unary
- alias :visit_Arel_Nodes_Grouping :unary
- alias :visit_Arel_Nodes_Having :unary
- alias :visit_Arel_Nodes_Lateral :unary
- alias :visit_Arel_Nodes_Limit :unary
- alias :visit_Arel_Nodes_Not :unary
- alias :visit_Arel_Nodes_Offset :unary
- alias :visit_Arel_Nodes_On :unary
- alias :visit_Arel_Nodes_Ordering :unary
- alias :visit_Arel_Nodes_Ascending :unary
- alias :visit_Arel_Nodes_Descending :unary
- alias :visit_Arel_Nodes_UnqualifiedColumn :unary
- alias :visit_Arel_Nodes_OptimizerHints :unary
- alias :visit_Arel_Nodes_ValuesList :unary
-
- def function(o)
- visit o.expressions
- visit o.alias
- visit o.distinct
- end
- alias :visit_Arel_Nodes_Avg :function
- alias :visit_Arel_Nodes_Exists :function
- alias :visit_Arel_Nodes_Max :function
- alias :visit_Arel_Nodes_Min :function
- alias :visit_Arel_Nodes_Sum :function
-
- def visit_Arel_Nodes_NamedFunction(o)
- visit o.name
- visit o.expressions
- visit o.distinct
- visit o.alias
- end
-
- def visit_Arel_Nodes_Count(o)
- visit o.expressions
- visit o.alias
- visit o.distinct
- end
-
- def visit_Arel_Nodes_Case(o)
- visit o.case
- visit o.conditions
- visit o.default
- end
-
- def nary(o)
- o.children.each { |child| visit child }
- end
- alias :visit_Arel_Nodes_And :nary
-
- def binary(o)
- visit o.left
- visit o.right
- end
- alias :visit_Arel_Nodes_As :binary
- alias :visit_Arel_Nodes_Assignment :binary
- alias :visit_Arel_Nodes_Between :binary
- alias :visit_Arel_Nodes_Concat :binary
- alias :visit_Arel_Nodes_DeleteStatement :binary
- alias :visit_Arel_Nodes_DoesNotMatch :binary
- alias :visit_Arel_Nodes_Equality :binary
- alias :visit_Arel_Nodes_FullOuterJoin :binary
- alias :visit_Arel_Nodes_GreaterThan :binary
- alias :visit_Arel_Nodes_GreaterThanOrEqual :binary
- alias :visit_Arel_Nodes_In :binary
- alias :visit_Arel_Nodes_InfixOperation :binary
- alias :visit_Arel_Nodes_JoinSource :binary
- alias :visit_Arel_Nodes_InnerJoin :binary
- alias :visit_Arel_Nodes_LessThan :binary
- alias :visit_Arel_Nodes_LessThanOrEqual :binary
- alias :visit_Arel_Nodes_Matches :binary
- alias :visit_Arel_Nodes_NotEqual :binary
- alias :visit_Arel_Nodes_NotIn :binary
- alias :visit_Arel_Nodes_NotRegexp :binary
- alias :visit_Arel_Nodes_IsNotDistinctFrom :binary
- alias :visit_Arel_Nodes_IsDistinctFrom :binary
- alias :visit_Arel_Nodes_Or :binary
- alias :visit_Arel_Nodes_OuterJoin :binary
- alias :visit_Arel_Nodes_Regexp :binary
- alias :visit_Arel_Nodes_RightOuterJoin :binary
- alias :visit_Arel_Nodes_TableAlias :binary
- alias :visit_Arel_Nodes_When :binary
-
- def visit_Arel_Nodes_StringJoin(o)
- visit o.left
- end
-
- def visit_Arel_Attribute(o)
- visit o.relation
- visit o.name
- end
- alias :visit_Arel_Attributes_Integer :visit_Arel_Attribute
- alias :visit_Arel_Attributes_Float :visit_Arel_Attribute
- alias :visit_Arel_Attributes_String :visit_Arel_Attribute
- alias :visit_Arel_Attributes_Time :visit_Arel_Attribute
- alias :visit_Arel_Attributes_Boolean :visit_Arel_Attribute
- alias :visit_Arel_Attributes_Attribute :visit_Arel_Attribute
- alias :visit_Arel_Attributes_Decimal :visit_Arel_Attribute
-
- def visit_Arel_Table(o)
- visit o.name
- end
-
- def terminal(o)
- end
- alias :visit_ActiveSupport_Multibyte_Chars :terminal
- alias :visit_ActiveSupport_StringInquirer :terminal
- alias :visit_Arel_Nodes_Lock :terminal
- alias :visit_Arel_Nodes_Node :terminal
- alias :visit_Arel_Nodes_SqlLiteral :terminal
- alias :visit_Arel_Nodes_BindParam :terminal
- alias :visit_Arel_Nodes_Window :terminal
- alias :visit_Arel_Nodes_True :terminal
- alias :visit_Arel_Nodes_False :terminal
- alias :visit_BigDecimal :terminal
- alias :visit_Class :terminal
- alias :visit_Date :terminal
- alias :visit_DateTime :terminal
- alias :visit_FalseClass :terminal
- alias :visit_Float :terminal
- alias :visit_Integer :terminal
- alias :visit_NilClass :terminal
- alias :visit_String :terminal
- alias :visit_Symbol :terminal
- alias :visit_Time :terminal
- alias :visit_TrueClass :terminal
-
- def visit_Arel_Nodes_InsertStatement(o)
- visit o.relation
- visit o.columns
- visit o.values
- end
-
- def visit_Arel_Nodes_SelectCore(o)
- visit o.projections
- visit o.source
- visit o.wheres
- visit o.groups
- visit o.windows
- visit o.havings
- end
-
- def visit_Arel_Nodes_SelectStatement(o)
- visit o.cores
- visit o.orders
- visit o.limit
- visit o.lock
- visit o.offset
- end
-
- def visit_Arel_Nodes_UpdateStatement(o)
- visit o.relation
- visit o.values
- visit o.wheres
- visit o.orders
- visit o.limit
- end
-
- def visit_Arel_Nodes_Comment(o)
- visit o.values
- end
-
- def visit_Array(o)
- o.each { |i| visit i }
- end
- alias :visit_Set :visit_Array
-
- def visit_Hash(o)
- o.each { |k, v| visit(k); visit(v) }
- end
-
- DISPATCH = dispatch_cache
-
- def get_dispatch_cache
- DISPATCH
- end
- end
- end
-end
diff --git a/activerecord/lib/arel/visitors/dot.rb b/activerecord/lib/arel/visitors/dot.rb
index ecc386de07..c4ea07bcfe 100644
--- a/activerecord/lib/arel/visitors/dot.rb
+++ b/activerecord/lib/arel/visitors/dot.rb
@@ -31,7 +31,6 @@ module Arel # :nodoc: all
end
private
-
def visit_Arel_Nodes_Ordering(o)
visit_edge o, "expr"
end
diff --git a/activerecord/lib/arel/visitors/mssql.rb b/activerecord/lib/arel/visitors/mssql.rb
index 8475139870..92eb94f802 100644
--- a/activerecord/lib/arel/visitors/mssql.rb
+++ b/activerecord/lib/arel/visitors/mssql.rb
@@ -11,7 +11,6 @@ module Arel # :nodoc: all
end
private
-
def visit_Arel_Nodes_IsNotDistinctFrom(o, collector)
right = o.right
diff --git a/activerecord/lib/arel/visitors/oracle.rb b/activerecord/lib/arel/visitors/oracle.rb
index 500974dff5..aab66301ef 100644
--- a/activerecord/lib/arel/visitors/oracle.rb
+++ b/activerecord/lib/arel/visitors/oracle.rb
@@ -4,7 +4,6 @@ module Arel # :nodoc: all
module Visitors
class Oracle < Arel::Visitors::ToSql
private
-
def visit_Arel_Nodes_SelectStatement(o, collector)
o = order_hacks(o)
@@ -87,50 +86,6 @@ module Arel # :nodoc: all
collector << " )"
end
- def visit_Arel_Nodes_In(o, collector)
- if Array === o.right && !o.right.empty?
- o.right.delete_if { |value| unboundable?(value) }
- end
-
- if Array === o.right && o.right.empty?
- collector << "1=0"
- else
- first = true
- o.right.each_slice(in_clause_length) do |sliced_o_right|
- collector << " OR " unless first
- first = false
-
- collector = visit o.left, collector
- collector << " IN ("
- visit(sliced_o_right, collector)
- collector << ")"
- end
- end
- collector
- end
-
- def visit_Arel_Nodes_NotIn(o, collector)
- if Array === o.right && !o.right.empty?
- o.right.delete_if { |value| unboundable?(value) }
- end
-
- if Array === o.right && o.right.empty?
- collector << "1=1"
- else
- first = true
- o.right.each_slice(in_clause_length) do |sliced_o_right|
- collector << " AND " unless first
- first = false
-
- collector = visit o.left, collector
- collector << " NOT IN ("
- visit(sliced_o_right, collector)
- collector << ")"
- end
- end
- collector
- end
-
def visit_Arel_Nodes_UpdateStatement(o, collector)
# Oracle does not allow ORDER BY/LIMIT in UPDATEs.
if o.orders.any? && o.limit.nil?
@@ -198,10 +153,6 @@ module Arel # :nodoc: all
collector = visit [o.left, o.right, 0, 1], collector
collector << ")"
end
-
- def in_clause_length
- 1000
- end
end
end
end
diff --git a/activerecord/lib/arel/visitors/oracle12.rb b/activerecord/lib/arel/visitors/oracle12.rb
index 8e0f07fca9..36783243b5 100644
--- a/activerecord/lib/arel/visitors/oracle12.rb
+++ b/activerecord/lib/arel/visitors/oracle12.rb
@@ -4,15 +4,13 @@ module Arel # :nodoc: all
module Visitors
class Oracle12 < Arel::Visitors::ToSql
private
-
def visit_Arel_Nodes_SelectStatement(o, collector)
# Oracle does not allow LIMIT clause with select for update
if o.limit && o.lock
- raise ArgumentError, <<-MSG
- 'Combination of limit and lock is not supported.
- because generated SQL statements
- `SELECT FOR UPDATE and FETCH FIRST n ROWS` generates ORA-02014.`
- MSG
+ raise ArgumentError, <<~MSG
+ Combination of limit and lock is not supported. Because generated SQL statements
+ `SELECT FOR UPDATE and FETCH FIRST n ROWS` generates ORA-02014.
+ MSG
end
super
end
@@ -41,50 +39,6 @@ module Arel # :nodoc: all
collector << " )"
end
- def visit_Arel_Nodes_In(o, collector)
- if Array === o.right && !o.right.empty?
- o.right.delete_if { |value| unboundable?(value) }
- end
-
- if Array === o.right && o.right.empty?
- collector << "1=0"
- else
- first = true
- o.right.each_slice(in_clause_length) do |sliced_o_right|
- collector << " OR " unless first
- first = false
-
- collector = visit o.left, collector
- collector << " IN ("
- visit(sliced_o_right, collector)
- collector << ")"
- end
- end
- collector
- end
-
- def visit_Arel_Nodes_NotIn(o, collector)
- if Array === o.right && !o.right.empty?
- o.right.delete_if { |value| unboundable?(value) }
- end
-
- if Array === o.right && o.right.empty?
- collector << "1=1"
- else
- first = true
- o.right.each_slice(in_clause_length) do |sliced_o_right|
- collector << " AND " unless first
- first = false
-
- collector = visit o.left, collector
- collector << " NOT IN ("
- visit(sliced_o_right, collector)
- collector << ")"
- end
- end
- collector
- end
-
def visit_Arel_Nodes_UpdateStatement(o, collector)
# Oracle does not allow ORDER BY/LIMIT in UPDATEs.
if o.orders.any? && o.limit.nil?
@@ -106,10 +60,6 @@ module Arel # :nodoc: all
collector = visit [o.left, o.right, 0, 1], collector
collector << ")"
end
-
- def in_clause_length
- 1000
- end
end
end
end
diff --git a/activerecord/lib/arel/visitors/postgresql.rb b/activerecord/lib/arel/visitors/postgresql.rb
index 8296f1cdc1..d4f21ff93e 100644
--- a/activerecord/lib/arel/visitors/postgresql.rb
+++ b/activerecord/lib/arel/visitors/postgresql.rb
@@ -4,7 +4,6 @@ module Arel # :nodoc: all
module Visitors
class PostgreSQL < Arel::Visitors::ToSql
private
-
def visit_Arel_Nodes_Matches(o, collector)
op = o.case_sensitive ? " LIKE " : " ILIKE "
collector = infix_value o, collector, op
diff --git a/activerecord/lib/arel/visitors/sqlite.rb b/activerecord/lib/arel/visitors/sqlite.rb
index af6f7e856a..62ec74ad82 100644
--- a/activerecord/lib/arel/visitors/sqlite.rb
+++ b/activerecord/lib/arel/visitors/sqlite.rb
@@ -4,7 +4,6 @@ module Arel # :nodoc: all
module Visitors
class SQLite < Arel::Visitors::ToSql
private
-
# Locks are not supported in SQLite
def visit_Arel_Nodes_Lock(o, collector)
collector
diff --git a/activerecord/lib/arel/visitors/to_sql.rb b/activerecord/lib/arel/visitors/to_sql.rb
index 277d553e6c..eff7a0d036 100644
--- a/activerecord/lib/arel/visitors/to_sql.rb
+++ b/activerecord/lib/arel/visitors/to_sql.rb
@@ -19,7 +19,6 @@ module Arel # :nodoc: all
end
private
-
def visit_Arel_Nodes_DeleteStatement(o, collector)
o = prepare_delete_statement(o)
@@ -52,10 +51,14 @@ module Arel # :nodoc: all
def visit_Arel_Nodes_InsertStatement(o, collector)
collector << "INSERT INTO "
collector = visit o.relation, collector
- if o.columns.any?
- collector << " (#{o.columns.map { |x|
- quote_column_name x.name
- }.join ', '})"
+
+ unless o.columns.empty?
+ collector << " ("
+ o.columns.each_with_index do |x, i|
+ collector << ", " unless i == 0
+ collector << quote_column_name(x.name)
+ end
+ collector << ")"
end
if o.values
@@ -97,22 +100,20 @@ module Arel # :nodoc: all
def visit_Arel_Nodes_ValuesList(o, collector)
collector << "VALUES "
- len = o.rows.length - 1
- o.rows.each_with_index { |row, i|
+ o.rows.each_with_index do |row, i|
+ collector << ", " unless i == 0
collector << "("
- row_len = row.length - 1
row.each_with_index do |value, k|
+ collector << ", " unless k == 0
case value
when Nodes::SqlLiteral, Nodes::BindParam
collector = visit(value, collector)
else
collector << quote(value).to_s
end
- collector << ", " unless k == row_len
end
collector << ")"
- collector << ", " unless i == len
- }
+ end
collector
end
@@ -128,11 +129,10 @@ module Arel # :nodoc: all
unless o.orders.empty?
collector << " ORDER BY "
- len = o.orders.length - 1
- o.orders.each_with_index { |x, i|
+ o.orders.each_with_index do |x, i|
+ collector << ", " unless i == 0
collector = visit(x, collector)
- collector << ", " unless len == i
- }
+ end
end
visit_Arel_Nodes_SelectOptions(o, collector)
@@ -506,41 +506,73 @@ module Arel # :nodoc: all
def visit_Arel_Table(o, collector)
if o.table_alias
- collector << "#{quote_table_name o.name} #{quote_table_name o.table_alias}"
+ collector << quote_table_name(o.name) << " " << quote_table_name(o.table_alias)
else
collector << quote_table_name(o.name)
end
end
def visit_Arel_Nodes_In(o, collector)
- if Array === o.right && !o.right.empty?
+ unless Array === o.right
+ return collect_in_clause(o.left, o.right, collector)
+ end
+
+ unless o.right.empty?
o.right.delete_if { |value| unboundable?(value) }
end
- if Array === o.right && o.right.empty?
- collector << "1=0"
+ return collector << "1=0" if o.right.empty?
+
+ in_clause_length = @connection.in_clause_length
+
+ if !in_clause_length || o.right.length <= in_clause_length
+ collect_in_clause(o.left, o.right, collector)
else
- collector = visit o.left, collector
- collector << " IN ("
- visit(o.right, collector) << ")"
+ collector << "("
+ o.right.each_slice(in_clause_length).each_with_index do |right, i|
+ collector << " OR " unless i == 0
+ collect_in_clause(o.left, right, collector)
+ end
+ collector << ")"
end
end
+ def collect_in_clause(left, right, collector)
+ collector = visit left, collector
+ collector << " IN ("
+ visit(right, collector) << ")"
+ end
+
def visit_Arel_Nodes_NotIn(o, collector)
- if Array === o.right && !o.right.empty?
+ unless Array === o.right
+ return collect_not_in_clause(o.left, o.right, collector)
+ end
+
+ unless o.right.empty?
o.right.delete_if { |value| unboundable?(value) }
end
- if Array === o.right && o.right.empty?
- collector << "1=1"
+ return collector << "1=1" if o.right.empty?
+
+ in_clause_length = @connection.in_clause_length
+
+ if !in_clause_length || o.right.length <= in_clause_length
+ collect_not_in_clause(o.left, o.right, collector)
else
- collector = visit o.left, collector
- collector << " NOT IN ("
- collector = visit o.right, collector
- collector << ")"
+ o.right.each_slice(in_clause_length).each_with_index do |right, i|
+ collector << " AND " unless i == 0
+ collect_not_in_clause(o.left, right, collector)
+ end
+ collector
end
end
+ def collect_not_in_clause(left, right, collector)
+ collector = visit left, collector
+ collector << " NOT IN ("
+ visit(right, collector) << ")"
+ end
+
def visit_Arel_Nodes_And(o, collector)
inject_join o.children, collector, " AND "
end
@@ -650,20 +682,13 @@ module Arel # :nodoc: all
end
def visit_Arel_Nodes_UnqualifiedColumn(o, collector)
- collector << "#{quote_column_name o.name}"
- collector
+ collector << quote_column_name(o.name)
end
def visit_Arel_Attributes_Attribute(o, collector)
join_name = o.relation.table_alias || o.relation.name
- collector << "#{quote_table_name join_name}.#{quote_column_name o.name}"
+ collector << quote_table_name(join_name) << "." << quote_column_name(o.name)
end
- alias :visit_Arel_Attributes_Integer :visit_Arel_Attributes_Attribute
- alias :visit_Arel_Attributes_Float :visit_Arel_Attributes_Attribute
- alias :visit_Arel_Attributes_Decimal :visit_Arel_Attributes_Attribute
- alias :visit_Arel_Attributes_String :visit_Arel_Attributes_Attribute
- alias :visit_Arel_Attributes_Time :visit_Arel_Attributes_Attribute
- alias :visit_Arel_Attributes_Boolean :visit_Arel_Attributes_Attribute
def literal(o, collector); collector << o.to_s; end
@@ -753,14 +778,11 @@ module Arel # :nodoc: all
end
def inject_join(list, collector, join_str)
- len = list.length - 1
- list.each_with_index.inject(collector) { |c, (x, i)|
- if i == len
- visit x, c
- else
- visit(x, c) << join_str
- end
- }
+ list.each_with_index do |x, i|
+ collector << join_str unless i == 0
+ collector = visit(x, collector)
+ end
+ collector
end
def unboundable?(value)
diff --git a/activerecord/lib/arel/visitors/visitor.rb b/activerecord/lib/arel/visitors/visitor.rb
index 1c17184e86..9066307aed 100644
--- a/activerecord/lib/arel/visitors/visitor.rb
+++ b/activerecord/lib/arel/visitors/visitor.rb
@@ -7,16 +7,15 @@ module Arel # :nodoc: all
@dispatch = get_dispatch_cache
end
- def accept(object, *args)
- visit object, *args
+ def accept(object, collector = nil)
+ visit object, collector
end
private
-
attr_reader :dispatch
def self.dispatch_cache
- Hash.new do |hash, klass|
+ @dispatch_cache ||= Hash.new do |hash, klass|
hash[klass] = "visit_#{(klass.name || '').gsub('::', '_')}"
end
end
@@ -25,9 +24,13 @@ module Arel # :nodoc: all
self.class.dispatch_cache
end
- def visit(object, *args)
+ def visit(object, collector = nil)
dispatch_method = dispatch[object.class]
- send dispatch_method, object, *args
+ if collector
+ send dispatch_method, object, collector
+ else
+ send dispatch_method, object
+ end
rescue NoMethodError => e
raise e if respond_to?(dispatch_method, true)
superklass = object.class.ancestors.find { |klass|
diff --git a/activerecord/lib/arel/visitors/where_sql.rb b/activerecord/lib/arel/visitors/where_sql.rb
index c6caf5e7c9..8fb299d1c8 100644
--- a/activerecord/lib/arel/visitors/where_sql.rb
+++ b/activerecord/lib/arel/visitors/where_sql.rb
@@ -9,7 +9,6 @@ module Arel # :nodoc: all
end
private
-
def visit_Arel_Nodes_SelectCore(o, collector)
collector << "WHERE "
wheres = o.wheres.map do |where|
diff --git a/activerecord/lib/rails/generators/active_record/application_record/application_record_generator.rb b/activerecord/lib/rails/generators/active_record/application_record/application_record_generator.rb
index 35d5664400..56b9628a92 100644
--- a/activerecord/lib/rails/generators/active_record/application_record/application_record_generator.rb
+++ b/activerecord/lib/rails/generators/active_record/application_record/application_record_generator.rb
@@ -13,7 +13,6 @@ module ActiveRecord
end
private
-
def application_record_file_name
@application_record_file_name ||=
if namespaced?
diff --git a/activerecord/lib/rails/generators/active_record/migration.rb b/activerecord/lib/rails/generators/active_record/migration.rb
index cbb88d571d..af753071a9 100644
--- a/activerecord/lib/rails/generators/active_record/migration.rb
+++ b/activerecord/lib/rails/generators/active_record/migration.rb
@@ -17,7 +17,6 @@ module ActiveRecord
end
private
-
def primary_key_type
key_type = options[:primary_key_type]
", id: :#{key_type}" if key_type
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 cb2c74f1ca..0620a515bd 100644
--- a/activerecord/lib/rails/generators/active_record/migration/migration_generator.rb
+++ b/activerecord/lib/rails/generators/active_record/migration/migration_generator.rb
@@ -7,6 +7,7 @@ module ActiveRecord
class MigrationGenerator < Base # :nodoc:
argument :attributes, type: :array, default: [], banner: "field[:type][:index] field[:type][:index]"
+ class_option :timestamps, type: :boolean
class_option :primary_key_type, type: :string, desc: "The type for primary key"
class_option :database, type: :string, aliases: %i(--db), desc: "The database for your migration. By default, the current environment's primary database is used."
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 c71bbdcab8..d4733f948f 100644
--- a/activerecord/lib/rails/generators/active_record/model/model_generator.rb
+++ b/activerecord/lib/rails/generators/active_record/model/model_generator.rb
@@ -35,7 +35,6 @@ module ActiveRecord
hook_for :test_framework
private
-
def attributes_with_index
attributes.select { |a| !a.reference? && a.has_index? }
end
diff --git a/activerecord/test/active_record/connection_adapters/fake_adapter.rb b/activerecord/test/active_record/connection_adapters/fake_adapter.rb
index f977b2997b..f1f457aedd 100644
--- a/activerecord/test/active_record/connection_adapters/fake_adapter.rb
+++ b/activerecord/test/active_record/connection_adapters/fake_adapter.rb
@@ -32,7 +32,8 @@ module ActiveRecord
name.to_s,
options[:default],
fetch_type_metadata(sql_type),
- options[:null])
+ options[:null],
+ )
end
def columns(table_name)
diff --git a/activerecord/test/cases/adapter_test.rb b/activerecord/test/cases/adapter_test.rb
index ce2ed06c1d..0bc617edbe 100644
--- a/activerecord/test/cases/adapter_test.rb
+++ b/activerecord/test/cases/adapter_test.rb
@@ -12,6 +12,7 @@ module ActiveRecord
def setup
@connection = ActiveRecord::Base.connection
@connection.materialize_transactions
+ @connection_handler = ActiveRecord::Base.connection_handler
end
##
@@ -166,7 +167,7 @@ module ActiveRecord
def test_preventing_writes_predicate
assert_not_predicate @connection, :preventing_writes?
- @connection.while_preventing_writes do
+ @connection_handler.while_preventing_writes do
assert_predicate @connection, :preventing_writes?
end
@@ -176,7 +177,7 @@ module ActiveRecord
def test_errors_when_an_insert_query_is_called_while_preventing_writes
assert_no_queries do
assert_raises(ActiveRecord::ReadOnlyError) do
- @connection.while_preventing_writes do
+ @connection_handler.while_preventing_writes do
@connection.transaction do
@connection.insert("INSERT INTO subscribers(nick) VALUES ('138853948594')", nil, false)
end
@@ -190,7 +191,7 @@ module ActiveRecord
assert_no_queries do
assert_raises(ActiveRecord::ReadOnlyError) do
- @connection.while_preventing_writes do
+ @connection_handler.while_preventing_writes do
@connection.transaction do
@connection.update("UPDATE subscribers SET nick = '9989' WHERE nick = '138853948594'")
end
@@ -204,7 +205,7 @@ module ActiveRecord
assert_no_queries do
assert_raises(ActiveRecord::ReadOnlyError) do
- @connection.while_preventing_writes do
+ @connection_handler.while_preventing_writes do
@connection.transaction do
@connection.delete("DELETE FROM subscribers WHERE nick = '138853948594'")
end
@@ -216,7 +217,7 @@ module ActiveRecord
def test_doesnt_error_when_a_select_query_is_called_while_preventing_writes
@connection.insert("INSERT INTO subscribers(nick) VALUES ('138853948594')")
- @connection.while_preventing_writes do
+ @connection_handler.while_preventing_writes do
result = @connection.select_all("SELECT subscribers.* FROM subscribers WHERE nick = '138853948594'")
assert_equal 1, result.length
end
@@ -559,7 +560,6 @@ module ActiveRecord
end
private
-
def reset_fixtures(*fixture_names)
ActiveRecord::FixtureSet.reset_cache
diff --git a/activerecord/test/cases/adapters/mysql2/annotate_test.rb b/activerecord/test/cases/adapters/mysql2/annotate_test.rb
deleted file mode 100644
index b512540073..0000000000
--- a/activerecord/test/cases/adapters/mysql2/annotate_test.rb
+++ /dev/null
@@ -1,37 +0,0 @@
-# frozen_string_literal: true
-
-require "cases/helper"
-require "models/post"
-
-class Mysql2AnnotateTest < ActiveRecord::Mysql2TestCase
- fixtures :posts
-
- def test_annotate_wraps_content_in_an_inline_comment
- assert_sql(%r{\ASELECT `posts`\.`id` FROM `posts` /\* foo \*/}) do
- posts = Post.select(:id).annotate("foo")
- assert posts.first
- end
- end
-
- def test_annotate_is_sanitized
- assert_sql(%r{\ASELECT `posts`\.`id` FROM `posts` /\* foo \*/}) do
- posts = Post.select(:id).annotate("*/foo/*")
- assert posts.first
- end
-
- assert_sql(%r{\ASELECT `posts`\.`id` FROM `posts` /\* foo \*/}) do
- posts = Post.select(:id).annotate("**//foo//**")
- assert posts.first
- end
-
- assert_sql(%r{\ASELECT `posts`\.`id` FROM `posts` /\* foo \*/ /\* bar \*/}) do
- posts = Post.select(:id).annotate("*/foo/*").annotate("*/bar")
- assert posts.first
- end
-
- assert_sql(%r{\ASELECT `posts`\.`id` FROM `posts` /\* \+ MAX_EXECUTION_TIME\(1\) \*/}) do
- posts = Post.select(:id).annotate("+ MAX_EXECUTION_TIME(1)")
- assert posts.first
- end
- end
-end
diff --git a/activerecord/test/cases/adapters/mysql2/case_sensitivity_test.rb b/activerecord/test/cases/adapters/mysql2/case_sensitivity_test.rb
index c32475c683..3756f74c95 100644
--- a/activerecord/test/cases/adapters/mysql2/case_sensitivity_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/case_sensitivity_test.rb
@@ -22,7 +22,7 @@ class Mysql2CaseSensitivityTest < ActiveRecord::Mysql2TestCase
CollationTest.validates_uniqueness_of(:string_ci_column, case_sensitive: false)
CollationTest.create!(string_ci_column: "A")
invalid = CollationTest.new(string_ci_column: "a")
- queries = assert_sql { invalid.save }
+ queries = capture_sql { invalid.save }
ci_uniqueness_query = queries.detect { |q| q.match(/string_ci_column/) }
assert_no_match(/lower/i, ci_uniqueness_query)
end
@@ -31,7 +31,7 @@ class Mysql2CaseSensitivityTest < ActiveRecord::Mysql2TestCase
CollationTest.validates_uniqueness_of(:string_cs_column, case_sensitive: false)
CollationTest.create!(string_cs_column: "A")
invalid = CollationTest.new(string_cs_column: "a")
- queries = assert_sql { invalid.save }
+ queries = capture_sql { invalid.save }
cs_uniqueness_query = queries.detect { |q| q.match(/string_cs_column/) }
assert_match(/lower/i, cs_uniqueness_query)
end
@@ -40,7 +40,7 @@ class Mysql2CaseSensitivityTest < ActiveRecord::Mysql2TestCase
CollationTest.validates_uniqueness_of(:string_ci_column, case_sensitive: true)
CollationTest.create!(string_ci_column: "A")
invalid = CollationTest.new(string_ci_column: "A")
- queries = assert_sql { invalid.save }
+ queries = capture_sql { invalid.save }
ci_uniqueness_query = queries.detect { |q| q.match(/string_ci_column/) }
assert_match(/binary/i, ci_uniqueness_query)
end
@@ -49,7 +49,7 @@ class Mysql2CaseSensitivityTest < ActiveRecord::Mysql2TestCase
CollationTest.validates_uniqueness_of(:string_cs_column, case_sensitive: true)
CollationTest.create!(string_cs_column: "A")
invalid = CollationTest.new(string_cs_column: "A")
- queries = assert_sql { invalid.save }
+ queries = capture_sql { invalid.save }
cs_uniqueness_query = queries.detect { |q| q.match(/string_cs_column/) }
assert_no_match(/binary/i, cs_uniqueness_query)
end
@@ -58,7 +58,7 @@ class Mysql2CaseSensitivityTest < ActiveRecord::Mysql2TestCase
CollationTest.validates_uniqueness_of(:binary_column, case_sensitive: true)
CollationTest.create!(binary_column: "A")
invalid = CollationTest.new(binary_column: "A")
- queries = assert_sql { invalid.save }
+ queries = capture_sql { invalid.save }
bin_uniqueness_query = queries.detect { |q| q.match(/binary_column/) }
assert_no_match(/\bBINARY\b/, bin_uniqueness_query)
end
diff --git a/activerecord/test/cases/adapters/mysql2/connection_test.rb b/activerecord/test/cases/adapters/mysql2/connection_test.rb
index 9c6566106a..cb7461a8d5 100644
--- a/activerecord/test/cases/adapters/mysql2/connection_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/connection_test.rb
@@ -197,7 +197,6 @@ class Mysql2ConnectionTest < ActiveRecord::Mysql2TestCase
end
private
-
def test_lock_free(lock_name)
@connection.select_value("SELECT IS_FREE_LOCK(#{@connection.quote(lock_name)})") == 1
end
diff --git a/activerecord/test/cases/adapters/mysql2/enum_test.rb b/activerecord/test/cases/adapters/mysql2/enum_test.rb
index 832f5d61d1..1168b3677e 100644
--- a/activerecord/test/cases/adapters/mysql2/enum_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/enum_test.rb
@@ -1,11 +1,20 @@
# frozen_string_literal: true
require "cases/helper"
+require "support/schema_dumping_helper"
class Mysql2EnumTest < ActiveRecord::Mysql2TestCase
+ include SchemaDumpingHelper
+
class EnumTest < ActiveRecord::Base
end
+ def setup
+ EnumTest.connection.create_table :enum_tests, id: false, force: true do |t|
+ t.column :enum_column, "enum('text','blob','tiny','medium','long','unsigned','bigint')"
+ end
+ end
+
def test_enum_limit
column = EnumTest.columns_hash["enum_column"]
assert_equal 8, column.limit
@@ -20,4 +29,9 @@ class Mysql2EnumTest < ActiveRecord::Mysql2TestCase
column = EnumTest.columns_hash["enum_column"]
assert_not_predicate column, :bigint?
end
+
+ def test_schema_dumping
+ schema = dump_table_schema "enum_tests"
+ assert_match %r{t\.column "enum_column", "enum\('text','blob','tiny','medium','long','unsigned','bigint'\)"$}, schema
+ 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 6ade2eec24..cfc1823773 100644
--- a/activerecord/test/cases/adapters/mysql2/mysql2_adapter_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/mysql2_adapter_test.rb
@@ -8,6 +8,7 @@ class Mysql2AdapterTest < ActiveRecord::Mysql2TestCase
def setup
@conn = ActiveRecord::Base.connection
+ @connection_handler = ActiveRecord::Base.connection_handler
end
def test_exec_query_nothing_raises_with_no_result_queries
@@ -19,6 +20,18 @@ class Mysql2AdapterTest < ActiveRecord::Mysql2TestCase
end
end
+ def test_database_exists_returns_false_if_database_does_not_exist
+ config = ActiveRecord::Base.configurations["arunit"].merge(database: "inexistent_activerecord_unittest")
+ assert_not ActiveRecord::ConnectionAdapters::Mysql2Adapter.database_exists?(config),
+ "expected database to not exist"
+ end
+
+ def test_database_exists_returns_true_when_the_database_exists
+ config = ActiveRecord::Base.configurations["arunit"]
+ assert ActiveRecord::ConnectionAdapters::Mysql2Adapter.database_exists?(config),
+ "expected database #{config[:database]} to exist"
+ end
+
def test_columns_for_distinct_zero_orders
assert_equal "posts.id",
@conn.columns_for_distinct("posts.id", [])
@@ -148,7 +161,7 @@ class Mysql2AdapterTest < ActiveRecord::Mysql2TestCase
def test_errors_when_an_insert_query_is_called_while_preventing_writes
assert_raises(ActiveRecord::ReadOnlyError) do
- @conn.while_preventing_writes do
+ @connection_handler.while_preventing_writes do
@conn.insert("INSERT INTO `engines` (`car_id`) VALUES ('138853948594')")
end
end
@@ -158,7 +171,7 @@ class Mysql2AdapterTest < ActiveRecord::Mysql2TestCase
@conn.insert("INSERT INTO `engines` (`car_id`) VALUES ('138853948594')")
assert_raises(ActiveRecord::ReadOnlyError) do
- @conn.while_preventing_writes do
+ @connection_handler.while_preventing_writes do
@conn.update("UPDATE `engines` SET `engines`.`car_id` = '9989' WHERE `engines`.`car_id` = '138853948594'")
end
end
@@ -168,7 +181,7 @@ class Mysql2AdapterTest < ActiveRecord::Mysql2TestCase
@conn.execute("INSERT INTO `engines` (`car_id`) VALUES ('138853948594')")
assert_raises(ActiveRecord::ReadOnlyError) do
- @conn.while_preventing_writes do
+ @connection_handler.while_preventing_writes do
@conn.execute("DELETE FROM `engines` where `engines`.`car_id` = '138853948594'")
end
end
@@ -178,7 +191,7 @@ class Mysql2AdapterTest < ActiveRecord::Mysql2TestCase
@conn.execute("INSERT INTO `engines` (`car_id`) VALUES ('138853948594')")
assert_raises(ActiveRecord::ReadOnlyError) do
- @conn.while_preventing_writes do
+ @connection_handler.while_preventing_writes do
@conn.execute("REPLACE INTO `engines` SET `engines`.`car_id` = '249823948'")
end
end
@@ -187,19 +200,19 @@ class Mysql2AdapterTest < ActiveRecord::Mysql2TestCase
def test_doesnt_error_when_a_select_query_is_called_while_preventing_writes
@conn.execute("INSERT INTO `engines` (`car_id`) VALUES ('138853948594')")
- @conn.while_preventing_writes do
+ @connection_handler.while_preventing_writes do
assert_equal 1, @conn.execute("SELECT `engines`.* FROM `engines` WHERE `engines`.`car_id` = '138853948594'").entries.count
end
end
def test_doesnt_error_when_a_show_query_is_called_while_preventing_writes
- @conn.while_preventing_writes do
+ @connection_handler.while_preventing_writes do
assert_equal 2, @conn.execute("SHOW FULL FIELDS FROM `engines`").entries.count
end
end
def test_doesnt_error_when_a_set_query_is_called_while_preventing_writes
- @conn.while_preventing_writes do
+ @connection_handler.while_preventing_writes do
assert_nil @conn.execute("SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci")
end
end
@@ -207,13 +220,27 @@ class Mysql2AdapterTest < ActiveRecord::Mysql2TestCase
def test_doesnt_error_when_a_read_query_with_leading_chars_is_called_while_preventing_writes
@conn.execute("INSERT INTO `engines` (`car_id`) VALUES ('138853948594')")
- @conn.while_preventing_writes do
+ @connection_handler.while_preventing_writes do
assert_equal 1, @conn.execute("(\n( SELECT `engines`.* FROM `engines` WHERE `engines`.`car_id` = '138853948594' ) )").entries.count
end
end
- private
+ def test_read_timeout_exception
+ ActiveRecord::Base.establish_connection(
+ ActiveRecord::Base.configurations[:arunit].merge("read_timeout" => 1)
+ )
+ error = assert_raises(ActiveRecord::AdapterTimeout) do
+ ActiveRecord::Base.connection.execute("SELECT SLEEP(2)")
+ end
+ assert_kind_of ActiveRecord::QueryAborted, error
+
+ assert_equal Mysql2::Error::TimeoutError, error.cause.class
+ ensure
+ ActiveRecord::Base.establish_connection :arunit
+ end
+
+ private
def with_example_table(definition = "id int auto_increment primary key, number int, data varchar(255)", &block)
super(@conn, "ex", definition, &block)
end
diff --git a/activerecord/test/cases/adapters/mysql2/schema_migrations_test.rb b/activerecord/test/cases/adapters/mysql2/schema_migrations_test.rb
index d7d9a2d732..182d5a3e58 100644
--- a/activerecord/test/cases/adapters/mysql2/schema_migrations_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/schema_migrations_test.rb
@@ -40,7 +40,6 @@ class SchemaMigrationsTest < ActiveRecord::Mysql2TestCase
end
private
-
def with_encoding_utf8mb4
database_name = connection.current_database
database_info = connection.select_one("SELECT * FROM information_schema.schemata WHERE schema_name = '#{database_name}'")
diff --git a/activerecord/test/cases/adapters/mysql2/set_test.rb b/activerecord/test/cases/adapters/mysql2/set_test.rb
new file mode 100644
index 0000000000..89107e142f
--- /dev/null
+++ b/activerecord/test/cases/adapters/mysql2/set_test.rb
@@ -0,0 +1,32 @@
+# frozen_string_literal: true
+
+require "cases/helper"
+require "support/schema_dumping_helper"
+
+class Mysql2SetTest < ActiveRecord::Mysql2TestCase
+ include SchemaDumpingHelper
+
+ class SetTest < ActiveRecord::Base
+ end
+
+ def setup
+ SetTest.connection.create_table :set_tests, id: false, force: true do |t|
+ t.column :set_column, "set('text','blob','tiny','medium','long','unsigned','bigint')"
+ end
+ end
+
+ def test_should_not_be_unsigned
+ column = SetTest.columns_hash["set_column"]
+ assert_not_predicate column, :unsigned?
+ end
+
+ def test_should_not_be_bigint
+ column = SetTest.columns_hash["set_column"]
+ assert_not_predicate column, :bigint?
+ end
+
+ def test_schema_dumping
+ schema = dump_table_schema "set_tests"
+ assert_match %r{t\.column "set_column", "set\('text','blob','tiny','medium','long','unsigned','bigint'\)"$}, schema
+ end
+end
diff --git a/activerecord/test/cases/adapters/mysql2/table_options_test.rb b/activerecord/test/cases/adapters/mysql2/table_options_test.rb
index 1c92df940f..13cf1daa08 100644
--- a/activerecord/test/cases/adapters/mysql2/table_options_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/table_options_test.rb
@@ -73,7 +73,7 @@ class Mysql2DefaultEngineOptionSchemaDumpTest < ActiveRecord::Mysql2TestCase
end
end.new
- ActiveRecord::Migrator.new(:up, [migration]).migrate
+ ActiveRecord::Migrator.new(:up, [migration], ActiveRecord::Base.connection.schema_migration).migrate
output = dump_table_schema("mysql_table_options")
options = %r{create_table "mysql_table_options", options: "(?<options>.*)"}.match(output)[:options]
@@ -112,7 +112,7 @@ class Mysql2DefaultEngineOptionSqlOutputTest < ActiveRecord::Mysql2TestCase
end
end.new
- ActiveRecord::Migrator.new(:up, [migration]).migrate
+ ActiveRecord::Migrator.new(:up, [migration], ActiveRecord::Base.connection.schema_migration).migrate
assert_match %r{ENGINE=InnoDB}, @log.string
end
diff --git a/activerecord/test/cases/adapters/mysql2/transaction_test.rb b/activerecord/test/cases/adapters/mysql2/transaction_test.rb
index 52e283f247..2041cc308f 100644
--- a/activerecord/test/cases/adapters/mysql2/transaction_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/transaction_test.rb
@@ -92,7 +92,7 @@ module ActiveRecord
test "raises StatementTimeout when statement timeout exceeded" do
skip unless ActiveRecord::Base.connection.show_variable("max_execution_time")
- assert_raises(ActiveRecord::StatementTimeout) do
+ error = assert_raises(ActiveRecord::StatementTimeout) do
s = Sample.create!(value: 1)
latch1 = Concurrent::CountDownLatch.new
latch2 = Concurrent::CountDownLatch.new
@@ -117,10 +117,11 @@ module ActiveRecord
thread.join
end
end
+ assert_kind_of ActiveRecord::QueryAborted, error
end
test "raises QueryCanceled when canceling statement due to user request" do
- assert_raises(ActiveRecord::QueryCanceled) do
+ error = assert_raises(ActiveRecord::QueryCanceled) do
s = Sample.create!(value: 1)
latch = Concurrent::CountDownLatch.new
@@ -144,6 +145,7 @@ module ActiveRecord
thread.join
end
end
+ assert_kind_of ActiveRecord::QueryAborted, error
end
end
end
diff --git a/activerecord/test/cases/adapters/postgresql/annotate_test.rb b/activerecord/test/cases/adapters/postgresql/annotate_test.rb
deleted file mode 100644
index 42a2861511..0000000000
--- a/activerecord/test/cases/adapters/postgresql/annotate_test.rb
+++ /dev/null
@@ -1,37 +0,0 @@
-# frozen_string_literal: true
-
-require "cases/helper"
-require "models/post"
-
-class PostgresqlAnnotateTest < ActiveRecord::PostgreSQLTestCase
- fixtures :posts
-
- def test_annotate_wraps_content_in_an_inline_comment
- assert_sql(%r{\ASELECT "posts"\."id" FROM "posts" /\* foo \*/}) do
- posts = Post.select(:id).annotate("foo")
- assert posts.first
- end
- end
-
- def test_annotate_is_sanitized
- assert_sql(%r{\ASELECT "posts"\."id" FROM "posts" /\* foo \*/}) do
- posts = Post.select(:id).annotate("*/foo/*")
- assert posts.first
- end
-
- assert_sql(%r{\ASELECT "posts"\."id" FROM "posts" /\* foo \*/}) do
- posts = Post.select(:id).annotate("**//foo//**")
- assert posts.first
- end
-
- assert_sql(%r{\ASELECT "posts"\."id" FROM "posts" /\* foo \*/ /\* bar \*/}) do
- posts = Post.select(:id).annotate("*/foo/*").annotate("*/bar")
- assert posts.first
- end
-
- assert_sql(%r{\ASELECT "posts"\."id" FROM "posts" /\* \+ MAX_EXECUTION_TIME\(1\) \*/}) do
- posts = Post.select(:id).annotate("+ MAX_EXECUTION_TIME(1)")
- assert posts.first
- end
- end
-end
diff --git a/activerecord/test/cases/adapters/postgresql/connection_test.rb b/activerecord/test/cases/adapters/postgresql/connection_test.rb
index 210758f462..dcee4fd22d 100644
--- a/activerecord/test/cases/adapters/postgresql/connection_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/connection_test.rb
@@ -26,19 +26,19 @@ module ActiveRecord
end
def test_encoding
- assert_queries(1) do
+ assert_queries(1, ignore_none: true) do
assert_not_nil @connection.encoding
end
end
def test_collation
- assert_queries(1) do
+ assert_queries(1, ignore_none: true) do
assert_not_nil @connection.collation
end
end
def test_ctype
- assert_queries(1) do
+ assert_queries(1, ignore_none: true) do
assert_not_nil @connection.ctype
end
end
@@ -239,7 +239,6 @@ module ActiveRecord
end
private
-
def with_warning_suppression
log_level = @connection.client_min_messages
@connection.client_min_messages = "error"
diff --git a/activerecord/test/cases/adapters/postgresql/extension_migration_test.rb b/activerecord/test/cases/adapters/postgresql/extension_migration_test.rb
index 0fd7b2c6ed..16baa8933d 100644
--- a/activerecord/test/cases/adapters/postgresql/extension_migration_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/extension_migration_test.rb
@@ -27,20 +27,20 @@ class PostgresqlExtensionMigrationTest < ActiveRecord::PostgreSQLTestCase
ActiveRecord::Base.table_name_prefix = "p_"
ActiveRecord::Base.table_name_suffix = "_s"
- ActiveRecord::SchemaMigration.reset_table_name
+ @connection.schema_migration.reset_table_name
ActiveRecord::InternalMetadata.reset_table_name
- ActiveRecord::SchemaMigration.delete_all rescue nil
+ @connection.schema_migration.delete_all rescue nil
ActiveRecord::Migration.verbose = false
end
def teardown
- ActiveRecord::SchemaMigration.delete_all rescue nil
+ @connection.schema_migration.delete_all rescue nil
ActiveRecord::Migration.verbose = true
ActiveRecord::Base.table_name_prefix = @old_table_name_prefix
ActiveRecord::Base.table_name_suffix = @old_table_name_suffix
- ActiveRecord::SchemaMigration.reset_table_name
+ @connection.schema_migration.reset_table_name
ActiveRecord::InternalMetadata.reset_table_name
super
@@ -50,7 +50,7 @@ class PostgresqlExtensionMigrationTest < ActiveRecord::PostgreSQLTestCase
@connection.disable_extension("hstore")
migrations = [EnableHstore.new(nil, 1)]
- ActiveRecord::Migrator.new(:up, migrations).migrate
+ ActiveRecord::Migrator.new(:up, migrations, ActiveRecord::Base.connection.schema_migration).migrate
assert @connection.extension_enabled?("hstore"), "extension hstore should be enabled"
end
@@ -58,7 +58,7 @@ class PostgresqlExtensionMigrationTest < ActiveRecord::PostgreSQLTestCase
@connection.enable_extension("hstore")
migrations = [DisableHstore.new(nil, 1)]
- ActiveRecord::Migrator.new(:up, migrations).migrate
+ ActiveRecord::Migrator.new(:up, migrations, ActiveRecord::Base.connection.schema_migration).migrate
assert_not @connection.extension_enabled?("hstore"), "extension hstore should not be enabled"
end
end
diff --git a/activerecord/test/cases/adapters/postgresql/geometric_test.rb b/activerecord/test/cases/adapters/postgresql/geometric_test.rb
index 14c262f4ce..f312b6e23d 100644
--- a/activerecord/test/cases/adapters/postgresql/geometric_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/geometric_test.rb
@@ -361,7 +361,6 @@ class PostgreSQLGeometricTypesTest < ActiveRecord::PostgreSQLTestCase
end
private
-
def assert_column_exists(column_name)
assert connection.column_exists?(table_name, column_name)
end
diff --git a/activerecord/test/cases/adapters/postgresql/money_test.rb b/activerecord/test/cases/adapters/postgresql/money_test.rb
index 1aa0348879..ff2ab22a80 100644
--- a/activerecord/test/cases/adapters/postgresql/money_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/money_test.rb
@@ -54,8 +54,12 @@ class PostgresqlMoneyTest < ActiveRecord::PostgreSQLTestCase
type = PostgresqlMoney.type_for_attribute("wealth")
assert_equal(12345678.12, type.cast(+"$12,345,678.12"))
assert_equal(12345678.12, type.cast(+"$12.345.678,12"))
+ assert_equal(12345678.12, type.cast(+"12,345,678.12"))
+ assert_equal(12345678.12, type.cast(+"12.345.678,12"))
assert_equal(-1.15, type.cast(+"-$1.15"))
assert_equal(-2.25, type.cast(+"($2.25)"))
+ assert_equal(-1.15, type.cast(+"-1.15"))
+ assert_equal(-2.25, type.cast(+"(2.25)"))
end
def test_schema_dumping
diff --git a/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb b/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb
index fbd3cbf90f..830c0892d3 100644
--- a/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb
@@ -13,6 +13,7 @@ module ActiveRecord
def setup
@connection = ActiveRecord::Base.connection
+ @connection_handler = ActiveRecord::Base.connection_handler
end
def test_bad_connection
@@ -23,6 +24,18 @@ module ActiveRecord
end
end
+ def test_database_exists_returns_false_when_the_database_does_not_exist
+ config = { database: "non_extant_database", adapter: "postgresql" }
+ assert_not ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.database_exists?(config),
+ "expected database #{config[:database]} to not exist"
+ end
+
+ def test_database_exists_returns_true_when_the_database_exists
+ config = ActiveRecord::Base.configurations["arunit"]
+ assert ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.database_exists?(config),
+ "expected database #{config[:database]} to exist"
+ end
+
def test_primary_key
with_example_table do
assert_equal "id", @connection.primary_key("ex")
@@ -240,9 +253,11 @@ module ActiveRecord
def test_expression_index
with_example_table do
- @connection.add_index "ex", "mod(id, 10), abs(number)", name: "expression"
+ expr = "mod(id, 10), abs(number)"
+ @connection.add_index "ex", expr, name: "expression"
index = @connection.indexes("ex").find { |idx| idx.name == "expression" }
- assert_equal "mod(id, 10), abs(number)", index.columns
+ assert_equal expr, index.columns
+ assert_equal true, @connection.index_exists?("ex", expr, name: "expression")
end
end
@@ -379,7 +394,7 @@ module ActiveRecord
def test_errors_when_an_insert_query_is_called_while_preventing_writes
with_example_table do
assert_raises(ActiveRecord::ReadOnlyError) do
- @connection.while_preventing_writes do
+ @connection_handler.while_preventing_writes do
@connection.execute("INSERT INTO ex (data) VALUES ('138853948594')")
end
end
@@ -391,7 +406,7 @@ module ActiveRecord
@connection.execute("INSERT INTO ex (data) VALUES ('138853948594')")
assert_raises(ActiveRecord::ReadOnlyError) do
- @connection.while_preventing_writes do
+ @connection_handler.while_preventing_writes do
@connection.execute("UPDATE ex SET data = '9989' WHERE data = '138853948594'")
end
end
@@ -403,7 +418,7 @@ module ActiveRecord
@connection.execute("INSERT INTO ex (data) VALUES ('138853948594')")
assert_raises(ActiveRecord::ReadOnlyError) do
- @connection.while_preventing_writes do
+ @connection_handler.while_preventing_writes do
@connection.execute("DELETE FROM ex where data = '138853948594'")
end
end
@@ -414,20 +429,20 @@ module ActiveRecord
with_example_table do
@connection.execute("INSERT INTO ex (data) VALUES ('138853948594')")
- @connection.while_preventing_writes do
+ @connection_handler.while_preventing_writes do
assert_equal 1, @connection.execute("SELECT * FROM ex WHERE data = '138853948594'").entries.count
end
end
end
def test_doesnt_error_when_a_show_query_is_called_while_preventing_writes
- @connection.while_preventing_writes do
+ @connection_handler.while_preventing_writes do
assert_equal 1, @connection.execute("SHOW TIME ZONE").entries.count
end
end
def test_doesnt_error_when_a_set_query_is_called_while_preventing_writes
- @connection.while_preventing_writes do
+ @connection_handler.while_preventing_writes do
assert_equal [], @connection.execute("SET standard_conforming_strings = on").entries
end
end
@@ -436,14 +451,13 @@ module ActiveRecord
with_example_table do
@connection.execute("INSERT INTO ex (data) VALUES ('138853948594')")
- @connection.while_preventing_writes do
+ @connection_handler.while_preventing_writes do
assert_equal 1, @connection.execute("(\n( SELECT * FROM ex WHERE data = '138853948594' ) )").entries.count
end
end
end
private
-
def with_example_table(definition = "id serial primary key, number integer, data character varying(255)", &block)
super(@connection, "ex", definition, &block)
end
diff --git a/activerecord/test/cases/adapters/postgresql/referential_integrity_test.rb b/activerecord/test/cases/adapters/postgresql/referential_integrity_test.rb
index ba477c63f4..a4f722c063 100644
--- a/activerecord/test/cases/adapters/postgresql/referential_integrity_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/referential_integrity_test.rb
@@ -13,7 +13,7 @@ class PostgreSQLReferentialIntegrityTest < ActiveRecord::PostgreSQLTestCase
end
module MissingSuperuserPrivileges
- def execute(sql)
+ def execute(sql, name = nil)
if IS_REFERENTIAL_INTEGRITY_SQL.call(sql)
super "BROKEN;" rescue nil # put transaction in broken state
raise ActiveRecord::StatementInvalid, "PG::InsufficientPrivilege"
@@ -24,7 +24,7 @@ class PostgreSQLReferentialIntegrityTest < ActiveRecord::PostgreSQLTestCase
end
module ProgrammerMistake
- def execute(sql)
+ def execute(sql, name = nil)
if IS_REFERENTIAL_INTEGRITY_SQL.call(sql)
raise ArgumentError, "something is not right."
else
@@ -106,7 +106,6 @@ class PostgreSQLReferentialIntegrityTest < ActiveRecord::PostgreSQLTestCase
end
private
-
def assert_transaction_is_not_broken
assert_equal 1, @connection.select_value("SELECT 1")
end
diff --git a/activerecord/test/cases/adapters/postgresql/rename_table_test.rb b/activerecord/test/cases/adapters/postgresql/rename_table_test.rb
index 7eccaf4aa2..fae20de086 100644
--- a/activerecord/test/cases/adapters/postgresql/rename_table_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/rename_table_test.rb
@@ -25,7 +25,6 @@ class PostgresqlRenameTableTest < ActiveRecord::PostgreSQLTestCase
end
private
-
def num_indices_named(name)
@connection.execute(<<~SQL).values.length
SELECT 1 FROM "pg_index"
diff --git a/activerecord/test/cases/adapters/postgresql/schema_test.rb b/activerecord/test/cases/adapters/postgresql/schema_test.rb
index 336cec30ca..fe6a3deff4 100644
--- a/activerecord/test/cases/adapters/postgresql/schema_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/schema_test.rb
@@ -104,7 +104,11 @@ class SchemaTest < ActiveRecord::PostgreSQLTestCase
end
def test_schema_names
- assert_equal ["public", "test_schema", "test_schema2"], @connection.schema_names
+ schema_names = @connection.schema_names
+ assert_includes schema_names, "public"
+ assert_includes schema_names, "test_schema"
+ assert_includes schema_names, "test_schema2"
+ assert_includes schema_names, "hint_plan" if @connection.supports_optimizer_hints?
end
def test_create_schema
diff --git a/activerecord/test/cases/adapters/postgresql/transaction_test.rb b/activerecord/test/cases/adapters/postgresql/transaction_test.rb
index 919ff3d158..311863a418 100644
--- a/activerecord/test/cases/adapters/postgresql/transaction_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/transaction_test.rb
@@ -177,7 +177,6 @@ module ActiveRecord
end
private
-
def with_warning_suppression
log_level = ActiveRecord::Base.connection.client_min_messages
ActiveRecord::Base.connection.client_min_messages = "error"
diff --git a/activerecord/test/cases/adapters/postgresql/uuid_test.rb b/activerecord/test/cases/adapters/postgresql/uuid_test.rb
index d2d8ea8042..a1c985fc71 100644
--- a/activerecord/test/cases/adapters/postgresql/uuid_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/uuid_test.rb
@@ -293,14 +293,16 @@ class PostgresqlUUIDGenerationTest < ActiveRecord::PostgreSQLTestCase
create_table("pg_uuids_4", id: :uuid)
end
end.new
- ActiveRecord::Migrator.new(:up, [migration]).migrate
+ ActiveRecord::Migrator.new(:up, [migration], ActiveRecord::Base.connection.schema_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
+ ActiveRecord::Base.connection.schema_migration.delete_all
end
+ uses_transaction :test_schema_dumper_for_uuid_primary_key_default_in_legacy_migration
end
class PostgresqlUUIDTestNilDefault < ActiveRecord::PostgreSQLTestCase
@@ -341,14 +343,16 @@ class PostgresqlUUIDTestNilDefault < ActiveRecord::PostgreSQLTestCase
create_table("pg_uuids_4", id: :uuid, default: nil)
end
end.new
- ActiveRecord::Migrator.new(:up, [migration]).migrate
+ ActiveRecord::Migrator.new(:up, [migration], ActiveRecord::Base.connection.schema_migration).migrate
schema = dump_table_schema "pg_uuids_4"
assert_match(/\bcreate_table "pg_uuids_4", id: :uuid, default: nil/, schema)
ensure
drop_table "pg_uuids_4"
ActiveRecord::Migration.verbose = @verbose_was
+ ActiveRecord::Base.connection.schema_migration.delete_all
end
+ uses_transaction :test_schema_dumper_for_uuid_primary_key_with_default_nil_in_legacy_migration
end
class PostgresqlUUIDTestInverseOf < ActiveRecord::PostgreSQLTestCase
diff --git a/activerecord/test/cases/adapters/sqlite3/annotate_test.rb b/activerecord/test/cases/adapters/sqlite3/annotate_test.rb
deleted file mode 100644
index 6567a5eca3..0000000000
--- a/activerecord/test/cases/adapters/sqlite3/annotate_test.rb
+++ /dev/null
@@ -1,37 +0,0 @@
-# frozen_string_literal: true
-
-require "cases/helper"
-require "models/post"
-
-class SQLite3AnnotateTest < ActiveRecord::SQLite3TestCase
- fixtures :posts
-
- def test_annotate_wraps_content_in_an_inline_comment
- assert_sql(%r{\ASELECT "posts"\."id" FROM "posts" /\* foo \*/}) do
- posts = Post.select(:id).annotate("foo")
- assert posts.first
- end
- end
-
- def test_annotate_is_sanitized
- assert_sql(%r{\ASELECT "posts"\."id" FROM "posts" /\* foo \*/}) do
- posts = Post.select(:id).annotate("*/foo/*")
- assert posts.first
- end
-
- assert_sql(%r{\ASELECT "posts"\."id" FROM "posts" /\* foo \*/}) do
- posts = Post.select(:id).annotate("**//foo//**")
- assert posts.first
- end
-
- assert_sql(%r{\ASELECT "posts"\."id" FROM "posts" /\* foo \*/ /\* bar \*/}) do
- posts = Post.select(:id).annotate("*/foo/*").annotate("*/bar")
- assert posts.first
- end
-
- assert_sql(%r{\ASELECT "posts"\."id" FROM "posts" /\* \+ MAX_EXECUTION_TIME\(1\) \*/}) do
- posts = Post.select(:id).annotate("+ MAX_EXECUTION_TIME(1)")
- assert posts.first
- end
- end
-end
diff --git a/activerecord/test/cases/adapters/sqlite3/bind_parameter_test.rb b/activerecord/test/cases/adapters/sqlite3/bind_parameter_test.rb
deleted file mode 100644
index 93a7dafebd..0000000000
--- a/activerecord/test/cases/adapters/sqlite3/bind_parameter_test.rb
+++ /dev/null
@@ -1,20 +0,0 @@
-# frozen_string_literal: true
-
-require "cases/helper"
-require "models/topic"
-
-module ActiveRecord
- module ConnectionAdapters
- class SQLite3Adapter
- class BindParameterTest < ActiveRecord::SQLite3TestCase
- def test_too_many_binds
- topics = Topic.where(id: (1..999).to_a << 2**63)
- assert_equal Topic.count, topics.count
-
- topics = Topic.where.not(id: (1..999).to_a << 2**63)
- assert_equal 0, topics.count
- end
- end
- end
- end
-end
diff --git a/activerecord/test/cases/adapters/sqlite3/collation_test.rb b/activerecord/test/cases/adapters/sqlite3/collation_test.rb
index 76c8f7d8dd..d938b5ff2f 100644
--- a/activerecord/test/cases/adapters/sqlite3/collation_test.rb
+++ b/activerecord/test/cases/adapters/sqlite3/collation_test.rb
@@ -11,6 +11,10 @@ class SQLite3CollationTest < ActiveRecord::SQLite3TestCase
@connection.create_table :collation_table_sqlite3, force: true do |t|
t.string :string_nocase, collation: "NOCASE"
t.text :text_rtrim, collation: "RTRIM"
+ # The decimal column might interfere with collation parsing.
+ # Thus, add this column type and some other string column afterwards.
+ t.decimal :decimal_col, precision: 6, scale: 2
+ t.string :string_after_decimal_nocase, collation: "NOCASE"
end
end
@@ -22,6 +26,11 @@ class SQLite3CollationTest < ActiveRecord::SQLite3TestCase
column = @connection.columns(:collation_table_sqlite3).find { |c| c.name == "string_nocase" }
assert_equal :string, column.type
assert_equal "NOCASE", column.collation
+
+ # Verify collation of a column behind the decimal column as well.
+ column = @connection.columns(:collation_table_sqlite3).find { |c| c.name == "string_after_decimal_nocase" }
+ assert_equal :string, column.type
+ assert_equal "NOCASE", column.collation
end
test "text column with collation" do
diff --git a/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb b/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb
index 806cfbfc00..b6d72c7bcd 100644
--- a/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb
+++ b/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb
@@ -19,6 +19,8 @@ module ActiveRecord
@conn = Base.sqlite3_connection database: ":memory:",
adapter: "sqlite3",
timeout: 100
+
+ @connection_handler = ActiveRecord::Base.connection_handler
end
def test_bad_connection
@@ -28,6 +30,17 @@ module ActiveRecord
end
end
+ def test_database_exists_returns_false_when_the_database_does_not_exist
+ assert_not SQLite3Adapter.database_exists?(adapter: "sqlite3", database: "non_extant_db"),
+ "expected non_extant_db to not exist"
+ end
+
+ def test_database_exists_returns_true_when_databae_exists
+ config = ActiveRecord::Base.configurations["arunit"]
+ assert SQLite3Adapter.database_exists?(config),
+ "expected #{config[:database]} to exist"
+ end
+
unless in_memory_db?
def test_connect_with_url
original_connection = ActiveRecord::Base.remove_connection
@@ -51,6 +64,11 @@ module ActiveRecord
end
end
+ def test_database_exists_returns_true_for_an_in_memory_db
+ assert SQLite3Adapter.database_exists?(database: ":memory:"),
+ "Expected in memory database to exist"
+ end
+
def test_column_types
owner = Owner.create!(name: "hello".encode("ascii-8bit"))
owner.reload
@@ -572,7 +590,7 @@ module ActiveRecord
def test_errors_when_an_insert_query_is_called_while_preventing_writes
with_example_table "id int, data string" do
assert_raises(ActiveRecord::ReadOnlyError) do
- @conn.while_preventing_writes do
+ @connection_handler.while_preventing_writes do
@conn.execute("INSERT INTO ex (data) VALUES ('138853948594')")
end
end
@@ -584,7 +602,7 @@ module ActiveRecord
@conn.execute("INSERT INTO ex (data) VALUES ('138853948594')")
assert_raises(ActiveRecord::ReadOnlyError) do
- @conn.while_preventing_writes do
+ @connection_handler.while_preventing_writes do
@conn.execute("UPDATE ex SET data = '9989' WHERE data = '138853948594'")
end
end
@@ -596,7 +614,7 @@ module ActiveRecord
@conn.execute("INSERT INTO ex (data) VALUES ('138853948594')")
assert_raises(ActiveRecord::ReadOnlyError) do
- @conn.while_preventing_writes do
+ @connection_handler.while_preventing_writes do
@conn.execute("DELETE FROM ex where data = '138853948594'")
end
end
@@ -608,7 +626,7 @@ module ActiveRecord
@conn.execute("INSERT INTO ex (data) VALUES ('138853948594')")
assert_raises(ActiveRecord::ReadOnlyError) do
- @conn.while_preventing_writes do
+ @connection_handler.while_preventing_writes do
@conn.execute("REPLACE INTO ex (data) VALUES ('249823948')")
end
end
@@ -619,7 +637,7 @@ module ActiveRecord
with_example_table "id int, data string" do
@conn.execute("INSERT INTO ex (data) VALUES ('138853948594')")
- @conn.while_preventing_writes do
+ @connection_handler.while_preventing_writes do
assert_equal 1, @conn.execute("SELECT data from ex WHERE data = '138853948594'").count
end
end
@@ -629,14 +647,13 @@ module ActiveRecord
with_example_table "id int, data string" do
@conn.execute("INSERT INTO ex (data) VALUES ('138853948594')")
- @conn.while_preventing_writes do
+ @connection_handler.while_preventing_writes do
assert_equal 1, @conn.execute(" SELECT data from ex WHERE data = '138853948594'").count
end
end
end
private
-
def assert_logged(logs)
subscriber = SQLSubscriber.new
subscription = ActiveSupport::Notifications.subscribe("sql.active_record", subscriber)
diff --git a/activerecord/test/cases/annotate_test.rb b/activerecord/test/cases/annotate_test.rb
new file mode 100644
index 0000000000..4d71d28f83
--- /dev/null
+++ b/activerecord/test/cases/annotate_test.rb
@@ -0,0 +1,46 @@
+# frozen_string_literal: true
+
+require "cases/helper"
+require "models/post"
+
+class AnnotateTest < ActiveRecord::TestCase
+ fixtures :posts
+
+ def test_annotate_wraps_content_in_an_inline_comment
+ quoted_posts_id, quoted_posts = regexp_escape_table_name("posts.id"), regexp_escape_table_name("posts")
+
+ assert_sql(%r{\ASELECT #{quoted_posts_id} FROM #{quoted_posts} /\* foo \*/}i) do
+ posts = Post.select(:id).annotate("foo")
+ assert posts.first
+ end
+ end
+
+ def test_annotate_is_sanitized
+ quoted_posts_id, quoted_posts = regexp_escape_table_name("posts.id"), regexp_escape_table_name("posts")
+
+ assert_sql(%r{\ASELECT #{quoted_posts_id} FROM #{quoted_posts} /\* foo \*/}i) do
+ posts = Post.select(:id).annotate("*/foo/*")
+ assert posts.first
+ end
+
+ assert_sql(%r{\ASELECT #{quoted_posts_id} FROM #{quoted_posts} /\* foo \*/}i) do
+ posts = Post.select(:id).annotate("**//foo//**")
+ assert posts.first
+ end
+
+ assert_sql(%r{\ASELECT #{quoted_posts_id} FROM #{quoted_posts} /\* foo \*/ /\* bar \*/}i) do
+ posts = Post.select(:id).annotate("*/foo/*").annotate("*/bar")
+ assert posts.first
+ end
+
+ assert_sql(%r{\ASELECT #{quoted_posts_id} FROM #{quoted_posts} /\* \+ MAX_EXECUTION_TIME\(1\) \*/}i) do
+ posts = Post.select(:id).annotate("+ MAX_EXECUTION_TIME(1)")
+ assert posts.first
+ end
+ end
+
+ private
+ def regexp_escape_table_name(name)
+ Regexp.escape(Post.connection.quote_table_name(name))
+ end
+end
diff --git a/activerecord/test/cases/ar_schema_test.rb b/activerecord/test/cases/ar_schema_test.rb
index 7aa6d089c5..2d5a06a4ac 100644
--- a/activerecord/test/cases/ar_schema_test.rb
+++ b/activerecord/test/cases/ar_schema_test.rb
@@ -9,7 +9,8 @@ class ActiveRecordSchemaTest < ActiveRecord::TestCase
@original_verbose = ActiveRecord::Migration.verbose
ActiveRecord::Migration.verbose = false
@connection = ActiveRecord::Base.connection
- ActiveRecord::SchemaMigration.drop_table
+ @schema_migration = @connection.schema_migration
+ @schema_migration.drop_table
end
teardown do
@@ -18,21 +19,21 @@ class ActiveRecordSchemaTest < ActiveRecord::TestCase
@connection.drop_table :nep_schema_migrations rescue nil
@connection.drop_table :has_timestamps rescue nil
@connection.drop_table :multiple_indexes rescue nil
- ActiveRecord::SchemaMigration.delete_all rescue nil
+ @schema_migration.delete_all rescue nil
ActiveRecord::Migration.verbose = @original_verbose
end
def test_has_primary_key
old_primary_key_prefix_type = ActiveRecord::Base.primary_key_prefix_type
ActiveRecord::Base.primary_key_prefix_type = :table_name_with_underscore
- assert_equal "version", ActiveRecord::SchemaMigration.primary_key
+ assert_equal "version", @schema_migration.primary_key
- ActiveRecord::SchemaMigration.create_table
- assert_difference "ActiveRecord::SchemaMigration.count", 1 do
- ActiveRecord::SchemaMigration.create version: 12
+ @schema_migration.create_table
+ assert_difference "@schema_migration.count", 1 do
+ @schema_migration.create version: 12
end
ensure
- ActiveRecord::SchemaMigration.drop_table
+ @schema_migration.drop_table
ActiveRecord::Base.primary_key_prefix_type = old_primary_key_prefix_type
end
@@ -54,7 +55,7 @@ class ActiveRecordSchemaTest < ActiveRecord::TestCase
def test_schema_define_with_table_name_prefix
old_table_name_prefix = ActiveRecord::Base.table_name_prefix
ActiveRecord::Base.table_name_prefix = "nep_"
- ActiveRecord::SchemaMigration.reset_table_name
+ @schema_migration.reset_table_name
ActiveRecord::InternalMetadata.reset_table_name
ActiveRecord::Schema.define(version: 7) do
create_table :fruits do |t|
@@ -67,7 +68,7 @@ class ActiveRecordSchemaTest < ActiveRecord::TestCase
assert_equal 7, @connection.migration_context.current_version
ensure
ActiveRecord::Base.table_name_prefix = old_table_name_prefix
- ActiveRecord::SchemaMigration.reset_table_name
+ @schema_migration.reset_table_name
ActiveRecord::InternalMetadata.reset_table_name
end
@@ -89,10 +90,10 @@ class ActiveRecordSchemaTest < ActiveRecord::TestCase
end
def test_normalize_version
- assert_equal "118", ActiveRecord::SchemaMigration.normalize_migration_number("0000118")
- assert_equal "002", ActiveRecord::SchemaMigration.normalize_migration_number("2")
- assert_equal "017", ActiveRecord::SchemaMigration.normalize_migration_number("0017")
- assert_equal "20131219224947", ActiveRecord::SchemaMigration.normalize_migration_number("20131219224947")
+ assert_equal "118", @schema_migration.normalize_migration_number("0000118")
+ assert_equal "002", @schema_migration.normalize_migration_number("2")
+ assert_equal "017", @schema_migration.normalize_migration_number("0017")
+ assert_equal "20131219224947", @schema_migration.normalize_migration_number("20131219224947")
end
def test_schema_load_with_multiple_indexes_for_column_of_different_names
diff --git a/activerecord/test/cases/arel/attributes/attribute_test.rb b/activerecord/test/cases/arel/attributes/attribute_test.rb
index c7bd0a053b..7ebb90c6fd 100644
--- a/activerecord/test/cases/arel/attributes/attribute_test.rb
+++ b/activerecord/test/cases/arel/attributes/attribute_test.rb
@@ -638,6 +638,18 @@ module Arel
)
end
+ if Gem::Version.new("2.7.0") <= Gem::Version.new(RUBY_VERSION)
+ it "can be constructed with a range implicitly starting at Infinity" do
+ attribute = Attribute.new nil, nil
+ node = attribute.between(eval("..0")) # eval for backwards compatibility
+
+ node.must_equal Nodes::LessThanOrEqual.new(
+ attribute,
+ Nodes::Casted.new(0, attribute)
+ )
+ end
+ end
+
if Gem::Version.new("2.6.0") <= Gem::Version.new(RUBY_VERSION)
it "can be constructed with a range implicitly ending at Infinity" do
attribute = Attribute.new nil, nil
@@ -839,6 +851,18 @@ module Arel
)
end
+ if Gem::Version.new("2.7.0") <= Gem::Version.new(RUBY_VERSION)
+ it "can be constructed with a range implicitly starting at Infinity" do
+ attribute = Attribute.new nil, nil
+ node = attribute.not_between(eval("..0")) # eval for backwards compatibility
+
+ node.must_equal Nodes::GreaterThan.new(
+ attribute,
+ Nodes::Casted.new(0, attribute)
+ )
+ end
+ end
+
if Gem::Version.new("2.6.0") <= Gem::Version.new(RUBY_VERSION)
it "can be constructed with a range implicitly ending at Infinity" do
attribute = Attribute.new nil, nil
diff --git a/activerecord/test/cases/arel/attributes_test.rb b/activerecord/test/cases/arel/attributes_test.rb
index b00af4bd29..1712633ae9 100644
--- a/activerecord/test/cases/arel/attributes_test.rb
+++ b/activerecord/test/cases/arel/attributes_test.rb
@@ -23,46 +23,5 @@ module Arel
assert_equal 2, array.uniq.size
end
end
-
- describe "for" do
- it "deals with unknown column types" do
- column = Struct.new(:type).new :crazy
- Attributes.for(column).must_equal Attributes::Undefined
- end
-
- it "returns the correct constant for strings" do
- [:string, :text, :binary].each do |type|
- column = Struct.new(:type).new type
- Attributes.for(column).must_equal Attributes::String
- end
- end
-
- it "returns the correct constant for ints" do
- column = Struct.new(:type).new :integer
- Attributes.for(column).must_equal Attributes::Integer
- end
-
- it "returns the correct constant for floats" do
- column = Struct.new(:type).new :float
- Attributes.for(column).must_equal Attributes::Float
- end
-
- it "returns the correct constant for decimals" do
- column = Struct.new(:type).new :decimal
- Attributes.for(column).must_equal Attributes::Decimal
- end
-
- it "returns the correct constant for boolean" do
- column = Struct.new(:type).new :boolean
- Attributes.for(column).must_equal Attributes::Boolean
- end
-
- it "returns the correct constant for time" do
- [:date, :datetime, :timestamp, :time].each do |type|
- column = Struct.new(:type).new type
- Attributes.for(column).must_equal Attributes::Time
- end
- end
- end
end
end
diff --git a/activerecord/test/cases/arel/nodes/node_test.rb b/activerecord/test/cases/arel/nodes/node_test.rb
index f4f07ef2c5..f1e0ce1ea9 100644
--- a/activerecord/test/cases/arel/nodes/node_test.rb
+++ b/activerecord/test/cases/arel/nodes/node_test.rb
@@ -18,24 +18,5 @@ module Arel
assert klass.ancestors.include?(Nodes::Node), klass.name
end
end
-
- def test_each
- list = []
- node = Nodes::Node.new
- node.each { |n| list << n }
- assert_equal [node], list
- end
-
- def test_generator
- list = []
- node = Nodes::Node.new
- node.each.each { |n| list << n }
- assert_equal [node], list
- end
-
- def test_enumerable
- node = Nodes::Node.new
- assert_kind_of Enumerable, node
- end
end
end
diff --git a/activerecord/test/cases/arel/select_manager_test.rb b/activerecord/test/cases/arel/select_manager_test.rb
index e6c49cd429..526fe6787a 100644
--- a/activerecord/test/cases/arel/select_manager_test.rb
+++ b/activerecord/test/cases/arel/select_manager_test.rb
@@ -369,16 +369,6 @@ module Arel
mgr = table.from
assert mgr.ast
end
-
- it "should allow orders to work when the ast is grepped" do
- table = Table.new :users
- mgr = table.from
- mgr.project Arel.sql "*"
- mgr.from table
- mgr.orders << Arel::Nodes::Ascending.new(Arel.sql("foo"))
- mgr.ast.grep(Arel::Nodes::OuterJoin)
- mgr.to_sql.must_be_like %{ SELECT * FROM "users" ORDER BY foo ASC }
- end
end
describe "taken" do
diff --git a/activerecord/test/cases/arel/support/fake_record.rb b/activerecord/test/cases/arel/support/fake_record.rb
index 18e6c10c9d..5ebeabd4a3 100644
--- a/activerecord/test/cases/arel/support/fake_record.rb
+++ b/activerecord/test/cases/arel/support/fake_record.rb
@@ -62,6 +62,10 @@ module FakeRecord
comment
end
+ def in_clause_length
+ 3
+ end
+
def schema_cache
self
end
diff --git a/activerecord/test/cases/arel/visitors/depth_first_test.rb b/activerecord/test/cases/arel/visitors/depth_first_test.rb
deleted file mode 100644
index 106be2311d..0000000000
--- a/activerecord/test/cases/arel/visitors/depth_first_test.rb
+++ /dev/null
@@ -1,276 +0,0 @@
-# frozen_string_literal: true
-
-require_relative "../helper"
-
-module Arel
- module Visitors
- class TestDepthFirst < Arel::Test
- Collector = Struct.new(:calls) do
- def call(object)
- calls << object
- end
- end
-
- def setup
- @collector = Collector.new []
- @visitor = Visitors::DepthFirst.new @collector
- end
-
- def test_raises_with_object
- assert_raises(TypeError) do
- @visitor.accept(Object.new)
- end
- end
-
-
- # unary ops
- [
- Arel::Nodes::Not,
- Arel::Nodes::Group,
- Arel::Nodes::On,
- Arel::Nodes::Grouping,
- Arel::Nodes::Offset,
- Arel::Nodes::Ordering,
- Arel::Nodes::StringJoin,
- Arel::Nodes::UnqualifiedColumn,
- Arel::Nodes::ValuesList,
- Arel::Nodes::Limit,
- Arel::Nodes::Else,
- ].each do |klass|
- define_method("test_#{klass.name.gsub('::', '_')}") do
- op = klass.new(:a)
- @visitor.accept op
- assert_equal [:a, op], @collector.calls
- end
- end
-
- # functions
- [
- Arel::Nodes::Exists,
- Arel::Nodes::Avg,
- Arel::Nodes::Min,
- Arel::Nodes::Max,
- Arel::Nodes::Sum,
- ].each do |klass|
- define_method("test_#{klass.name.gsub('::', '_')}") do
- func = klass.new(:a, "b")
- @visitor.accept func
- assert_equal [:a, "b", false, func], @collector.calls
- end
- end
-
- def test_named_function
- func = Arel::Nodes::NamedFunction.new(:a, :b, "c")
- @visitor.accept func
- assert_equal [:a, :b, false, "c", func], @collector.calls
- end
-
- def test_lock
- lock = Nodes::Lock.new true
- @visitor.accept lock
- assert_equal [lock], @collector.calls
- end
-
- def test_count
- count = Nodes::Count.new :a, :b, "c"
- @visitor.accept count
- assert_equal [:a, "c", :b, count], @collector.calls
- end
-
- def test_inner_join
- join = Nodes::InnerJoin.new :a, :b
- @visitor.accept join
- assert_equal [:a, :b, join], @collector.calls
- end
-
- def test_full_outer_join
- join = Nodes::FullOuterJoin.new :a, :b
- @visitor.accept join
- assert_equal [:a, :b, join], @collector.calls
- end
-
- def test_outer_join
- join = Nodes::OuterJoin.new :a, :b
- @visitor.accept join
- assert_equal [:a, :b, join], @collector.calls
- end
-
- def test_right_outer_join
- join = Nodes::RightOuterJoin.new :a, :b
- @visitor.accept join
- assert_equal [:a, :b, join], @collector.calls
- end
-
- def test_comment
- comment = Nodes::Comment.new ["foo"]
- @visitor.accept comment
- assert_equal ["foo", ["foo"], comment], @collector.calls
- end
-
- [
- Arel::Nodes::Assignment,
- Arel::Nodes::Between,
- Arel::Nodes::Concat,
- Arel::Nodes::DoesNotMatch,
- Arel::Nodes::Equality,
- Arel::Nodes::GreaterThan,
- Arel::Nodes::GreaterThanOrEqual,
- Arel::Nodes::In,
- Arel::Nodes::LessThan,
- Arel::Nodes::LessThanOrEqual,
- Arel::Nodes::Matches,
- Arel::Nodes::NotEqual,
- Arel::Nodes::NotIn,
- Arel::Nodes::Or,
- Arel::Nodes::TableAlias,
- Arel::Nodes::As,
- Arel::Nodes::DeleteStatement,
- Arel::Nodes::JoinSource,
- Arel::Nodes::When,
- ].each do |klass|
- define_method("test_#{klass.name.gsub('::', '_')}") do
- binary = klass.new(:a, :b)
- @visitor.accept binary
- assert_equal [:a, :b, binary], @collector.calls
- end
- end
-
- def test_Arel_Nodes_InfixOperation
- binary = Arel::Nodes::InfixOperation.new(:o, :a, :b)
- @visitor.accept binary
- assert_equal [:a, :b, binary], @collector.calls
- end
-
- # N-ary
- [
- Arel::Nodes::And,
- ].each do |klass|
- define_method("test_#{klass.name.gsub('::', '_')}") do
- binary = klass.new([:a, :b, :c])
- @visitor.accept binary
- assert_equal [:a, :b, :c, binary], @collector.calls
- end
- end
-
- [
- Arel::Attributes::Integer,
- Arel::Attributes::Float,
- Arel::Attributes::String,
- Arel::Attributes::Time,
- Arel::Attributes::Boolean,
- Arel::Attributes::Attribute
- ].each do |klass|
- define_method("test_#{klass.name.gsub('::', '_')}") do
- binary = klass.new(:a, :b)
- @visitor.accept binary
- assert_equal [:a, :b, binary], @collector.calls
- end
- end
-
- def test_table
- relation = Arel::Table.new(:users)
- @visitor.accept relation
- assert_equal ["users", relation], @collector.calls
- end
-
- def test_array
- node = Nodes::Or.new(:a, :b)
- list = [node]
- @visitor.accept list
- assert_equal [:a, :b, node, list], @collector.calls
- end
-
- def test_set
- node = Nodes::Or.new(:a, :b)
- set = Set.new([node])
- @visitor.accept set
- assert_equal [:a, :b, node, set], @collector.calls
- end
-
- def test_hash
- node = Nodes::Or.new(:a, :b)
- hash = { node => node }
- @visitor.accept hash
- assert_equal [:a, :b, node, :a, :b, node, hash], @collector.calls
- end
-
- def test_update_statement
- stmt = Nodes::UpdateStatement.new
- stmt.relation = :a
- stmt.values << :b
- stmt.wheres << :c
- stmt.orders << :d
- stmt.limit = :e
-
- @visitor.accept stmt
- assert_equal [:a, :b, stmt.values, :c, stmt.wheres, :d, stmt.orders,
- :e, stmt], @collector.calls
- end
-
- def test_select_core
- core = Nodes::SelectCore.new
- core.projections << :a
- core.froms = :b
- core.wheres << :c
- core.groups << :d
- core.windows << :e
- core.havings << :f
-
- @visitor.accept core
- assert_equal [
- :a, core.projections,
- :b, [],
- core.source,
- :c, core.wheres,
- :d, core.groups,
- :e, core.windows,
- :f, core.havings,
- core], @collector.calls
- end
-
- def test_select_statement
- ss = Nodes::SelectStatement.new
- ss.cores.replace [:a]
- ss.orders << :b
- ss.limit = :c
- ss.lock = :d
- ss.offset = :e
-
- @visitor.accept ss
- assert_equal [
- :a, ss.cores,
- :b, ss.orders,
- :c,
- :d,
- :e,
- ss], @collector.calls
- end
-
- def test_insert_statement
- stmt = Nodes::InsertStatement.new
- stmt.relation = :a
- stmt.columns << :b
- stmt.values = :c
-
- @visitor.accept stmt
- assert_equal [:a, :b, stmt.columns, :c, stmt], @collector.calls
- end
-
- def test_case
- node = Arel::Nodes::Case.new
- node.case = :a
- node.conditions << :b
- node.default = :c
-
- @visitor.accept node
- assert_equal [:a, :b, node.conditions, :c, node], @collector.calls
- end
-
- def test_node
- node = Nodes::Node.new
- @visitor.accept node
- assert_equal [node], @collector.calls
- end
- end
- end
-end
diff --git a/activerecord/test/cases/arel/visitors/dispatch_contamination_test.rb b/activerecord/test/cases/arel/visitors/dispatch_contamination_test.rb
index a07a1a050a..36f9eb49a2 100644
--- a/activerecord/test/cases/arel/visitors/dispatch_contamination_test.rb
+++ b/activerecord/test/cases/arel/visitors/dispatch_contamination_test.rb
@@ -48,7 +48,13 @@ module Arel
node = Nodes::Union.new(Nodes::True.new, Nodes::False.new)
assert_equal "( TRUE UNION FALSE )", node.to_sql
- node.first # from Nodes::Node's Enumerable mixin
+ visitor = Class.new(Visitor) {
+ def visit_Arel_Nodes_Union(o); end
+ alias :visit_Arel_Nodes_True :visit_Arel_Nodes_Union
+ alias :visit_Arel_Nodes_False :visit_Arel_Nodes_Union
+ }.new
+
+ visitor.accept(node)
assert_equal "( TRUE UNION FALSE )", node.to_sql
end
diff --git a/activerecord/test/cases/arel/visitors/oracle12_test.rb b/activerecord/test/cases/arel/visitors/oracle12_test.rb
index ebea12910d..4ce5cab4db 100644
--- a/activerecord/test/cases/arel/visitors/oracle12_test.rb
+++ b/activerecord/test/cases/arel/visitors/oracle12_test.rb
@@ -8,7 +8,6 @@ module Arel
before do
@visitor = Oracle12.new Table.engine.connection
@table = Table.new(:users)
- @attr = @table[:id]
end
def compile(node)
@@ -96,26 +95,6 @@ module Arel
sql.must_be_like %{ "users"."name" IS NOT NULL }
end
end
-
- describe "Nodes::In" do
- it "should know how to visit" do
- ary = (1 .. 1001).to_a
- node = @attr.in ary
- compile(node).must_be_like %{
- "users"."id" IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, 378, 379, 380, 381, 382, 383, 384, 385, 386, 387, 388, 389, 390, 391, 392, 393, 394, 395, 396, 397, 398, 399, 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, 418, 419, 420, 421, 422, 423, 424, 425, 426, 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, 437, 438, 439, 440, 441, 442, 443, 444, 445, 446, 447, 448, 449, 450, 451, 452, 453, 454, 455, 456, 457, 458, 459, 460, 461, 462, 463, 464, 465, 466, 467, 468, 469, 470, 471, 472, 473, 474, 475, 476, 477, 478, 479, 480, 481, 482, 483, 484, 485, 486, 487, 488, 489, 490, 491, 492, 493, 494, 495, 496, 497, 498, 499, 500, 501, 502, 503, 504, 505, 506, 507, 508, 509, 510, 511, 512, 513, 514, 515, 516, 517, 518, 519, 520, 521, 522, 523, 524, 525, 526, 527, 528, 529, 530, 531, 532, 533, 534, 535, 536, 537, 538, 539, 540, 541, 542, 543, 544, 545, 546, 547, 548, 549, 550, 551, 552, 553, 554, 555, 556, 557, 558, 559, 560, 561, 562, 563, 564, 565, 566, 567, 568, 569, 570, 571, 572, 573, 574, 575, 576, 577, 578, 579, 580, 581, 582, 583, 584, 585, 586, 587, 588, 589, 590, 591, 592, 593, 594, 595, 596, 597, 598, 599, 600, 601, 602, 603, 604, 605, 606, 607, 608, 609, 610, 611, 612, 613, 614, 615, 616, 617, 618, 619, 620, 621, 622, 623, 624, 625, 626, 627, 628, 629, 630, 631, 632, 633, 634, 635, 636, 637, 638, 639, 640, 641, 642, 643, 644, 645, 646, 647, 648, 649, 650, 651, 652, 653, 654, 655, 656, 657, 658, 659, 660, 661, 662, 663, 664, 665, 666, 667, 668, 669, 670, 671, 672, 673, 674, 675, 676, 677, 678, 679, 680, 681, 682, 683, 684, 685, 686, 687, 688, 689, 690, 691, 692, 693, 694, 695, 696, 697, 698, 699, 700, 701, 702, 703, 704, 705, 706, 707, 708, 709, 710, 711, 712, 713, 714, 715, 716, 717, 718, 719, 720, 721, 722, 723, 724, 725, 726, 727, 728, 729, 730, 731, 732, 733, 734, 735, 736, 737, 738, 739, 740, 741, 742, 743, 744, 745, 746, 747, 748, 749, 750, 751, 752, 753, 754, 755, 756, 757, 758, 759, 760, 761, 762, 763, 764, 765, 766, 767, 768, 769, 770, 771, 772, 773, 774, 775, 776, 777, 778, 779, 780, 781, 782, 783, 784, 785, 786, 787, 788, 789, 790, 791, 792, 793, 794, 795, 796, 797, 798, 799, 800, 801, 802, 803, 804, 805, 806, 807, 808, 809, 810, 811, 812, 813, 814, 815, 816, 817, 818, 819, 820, 821, 822, 823, 824, 825, 826, 827, 828, 829, 830, 831, 832, 833, 834, 835, 836, 837, 838, 839, 840, 841, 842, 843, 844, 845, 846, 847, 848, 849, 850, 851, 852, 853, 854, 855, 856, 857, 858, 859, 860, 861, 862, 863, 864, 865, 866, 867, 868, 869, 870, 871, 872, 873, 874, 875, 876, 877, 878, 879, 880, 881, 882, 883, 884, 885, 886, 887, 888, 889, 890, 891, 892, 893, 894, 895, 896, 897, 898, 899, 900, 901, 902, 903, 904, 905, 906, 907, 908, 909, 910, 911, 912, 913, 914, 915, 916, 917, 918, 919, 920, 921, 922, 923, 924, 925, 926, 927, 928, 929, 930, 931, 932, 933, 934, 935, 936, 937, 938, 939, 940, 941, 942, 943, 944, 945, 946, 947, 948, 949, 950, 951, 952, 953, 954, 955, 956, 957, 958, 959, 960, 961, 962, 963, 964, 965, 966, 967, 968, 969, 970, 971, 972, 973, 974, 975, 976, 977, 978, 979, 980, 981, 982, 983, 984, 985, 986, 987, 988, 989, 990, 991, 992, 993, 994, 995, 996, 997, 998, 999, 1000) OR \"users\".\"id\" IN (1001)
- }
- end
- end
-
- describe "Nodes::NotIn" do
- it "should know how to visit" do
- ary = (1 .. 1001).to_a
- node = @attr.not_in ary
- compile(node).must_be_like %{
- "users"."id" NOT IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, 378, 379, 380, 381, 382, 383, 384, 385, 386, 387, 388, 389, 390, 391, 392, 393, 394, 395, 396, 397, 398, 399, 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, 418, 419, 420, 421, 422, 423, 424, 425, 426, 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, 437, 438, 439, 440, 441, 442, 443, 444, 445, 446, 447, 448, 449, 450, 451, 452, 453, 454, 455, 456, 457, 458, 459, 460, 461, 462, 463, 464, 465, 466, 467, 468, 469, 470, 471, 472, 473, 474, 475, 476, 477, 478, 479, 480, 481, 482, 483, 484, 485, 486, 487, 488, 489, 490, 491, 492, 493, 494, 495, 496, 497, 498, 499, 500, 501, 502, 503, 504, 505, 506, 507, 508, 509, 510, 511, 512, 513, 514, 515, 516, 517, 518, 519, 520, 521, 522, 523, 524, 525, 526, 527, 528, 529, 530, 531, 532, 533, 534, 535, 536, 537, 538, 539, 540, 541, 542, 543, 544, 545, 546, 547, 548, 549, 550, 551, 552, 553, 554, 555, 556, 557, 558, 559, 560, 561, 562, 563, 564, 565, 566, 567, 568, 569, 570, 571, 572, 573, 574, 575, 576, 577, 578, 579, 580, 581, 582, 583, 584, 585, 586, 587, 588, 589, 590, 591, 592, 593, 594, 595, 596, 597, 598, 599, 600, 601, 602, 603, 604, 605, 606, 607, 608, 609, 610, 611, 612, 613, 614, 615, 616, 617, 618, 619, 620, 621, 622, 623, 624, 625, 626, 627, 628, 629, 630, 631, 632, 633, 634, 635, 636, 637, 638, 639, 640, 641, 642, 643, 644, 645, 646, 647, 648, 649, 650, 651, 652, 653, 654, 655, 656, 657, 658, 659, 660, 661, 662, 663, 664, 665, 666, 667, 668, 669, 670, 671, 672, 673, 674, 675, 676, 677, 678, 679, 680, 681, 682, 683, 684, 685, 686, 687, 688, 689, 690, 691, 692, 693, 694, 695, 696, 697, 698, 699, 700, 701, 702, 703, 704, 705, 706, 707, 708, 709, 710, 711, 712, 713, 714, 715, 716, 717, 718, 719, 720, 721, 722, 723, 724, 725, 726, 727, 728, 729, 730, 731, 732, 733, 734, 735, 736, 737, 738, 739, 740, 741, 742, 743, 744, 745, 746, 747, 748, 749, 750, 751, 752, 753, 754, 755, 756, 757, 758, 759, 760, 761, 762, 763, 764, 765, 766, 767, 768, 769, 770, 771, 772, 773, 774, 775, 776, 777, 778, 779, 780, 781, 782, 783, 784, 785, 786, 787, 788, 789, 790, 791, 792, 793, 794, 795, 796, 797, 798, 799, 800, 801, 802, 803, 804, 805, 806, 807, 808, 809, 810, 811, 812, 813, 814, 815, 816, 817, 818, 819, 820, 821, 822, 823, 824, 825, 826, 827, 828, 829, 830, 831, 832, 833, 834, 835, 836, 837, 838, 839, 840, 841, 842, 843, 844, 845, 846, 847, 848, 849, 850, 851, 852, 853, 854, 855, 856, 857, 858, 859, 860, 861, 862, 863, 864, 865, 866, 867, 868, 869, 870, 871, 872, 873, 874, 875, 876, 877, 878, 879, 880, 881, 882, 883, 884, 885, 886, 887, 888, 889, 890, 891, 892, 893, 894, 895, 896, 897, 898, 899, 900, 901, 902, 903, 904, 905, 906, 907, 908, 909, 910, 911, 912, 913, 914, 915, 916, 917, 918, 919, 920, 921, 922, 923, 924, 925, 926, 927, 928, 929, 930, 931, 932, 933, 934, 935, 936, 937, 938, 939, 940, 941, 942, 943, 944, 945, 946, 947, 948, 949, 950, 951, 952, 953, 954, 955, 956, 957, 958, 959, 960, 961, 962, 963, 964, 965, 966, 967, 968, 969, 970, 971, 972, 973, 974, 975, 976, 977, 978, 979, 980, 981, 982, 983, 984, 985, 986, 987, 988, 989, 990, 991, 992, 993, 994, 995, 996, 997, 998, 999, 1000) AND \"users\".\"id\" NOT IN (1001)
- }
- end
- end
end
end
end
diff --git a/activerecord/test/cases/arel/visitors/oracle_test.rb b/activerecord/test/cases/arel/visitors/oracle_test.rb
index f69b201855..893edc7f74 100644
--- a/activerecord/test/cases/arel/visitors/oracle_test.rb
+++ b/activerecord/test/cases/arel/visitors/oracle_test.rb
@@ -8,7 +8,6 @@ module Arel
before do
@visitor = Oracle.new Table.engine.connection
@table = Table.new(:users)
- @attr = @table[:id]
end
def compile(node)
@@ -232,26 +231,6 @@ module Arel
sql.must_be_like %{ "users"."name" IS NOT NULL }
end
end
-
- describe "Nodes::In" do
- it "should know how to visit" do
- ary = (1 .. 1001).to_a
- node = @attr.in ary
- compile(node).must_be_like %{
- "users"."id" IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, 378, 379, 380, 381, 382, 383, 384, 385, 386, 387, 388, 389, 390, 391, 392, 393, 394, 395, 396, 397, 398, 399, 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, 418, 419, 420, 421, 422, 423, 424, 425, 426, 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, 437, 438, 439, 440, 441, 442, 443, 444, 445, 446, 447, 448, 449, 450, 451, 452, 453, 454, 455, 456, 457, 458, 459, 460, 461, 462, 463, 464, 465, 466, 467, 468, 469, 470, 471, 472, 473, 474, 475, 476, 477, 478, 479, 480, 481, 482, 483, 484, 485, 486, 487, 488, 489, 490, 491, 492, 493, 494, 495, 496, 497, 498, 499, 500, 501, 502, 503, 504, 505, 506, 507, 508, 509, 510, 511, 512, 513, 514, 515, 516, 517, 518, 519, 520, 521, 522, 523, 524, 525, 526, 527, 528, 529, 530, 531, 532, 533, 534, 535, 536, 537, 538, 539, 540, 541, 542, 543, 544, 545, 546, 547, 548, 549, 550, 551, 552, 553, 554, 555, 556, 557, 558, 559, 560, 561, 562, 563, 564, 565, 566, 567, 568, 569, 570, 571, 572, 573, 574, 575, 576, 577, 578, 579, 580, 581, 582, 583, 584, 585, 586, 587, 588, 589, 590, 591, 592, 593, 594, 595, 596, 597, 598, 599, 600, 601, 602, 603, 604, 605, 606, 607, 608, 609, 610, 611, 612, 613, 614, 615, 616, 617, 618, 619, 620, 621, 622, 623, 624, 625, 626, 627, 628, 629, 630, 631, 632, 633, 634, 635, 636, 637, 638, 639, 640, 641, 642, 643, 644, 645, 646, 647, 648, 649, 650, 651, 652, 653, 654, 655, 656, 657, 658, 659, 660, 661, 662, 663, 664, 665, 666, 667, 668, 669, 670, 671, 672, 673, 674, 675, 676, 677, 678, 679, 680, 681, 682, 683, 684, 685, 686, 687, 688, 689, 690, 691, 692, 693, 694, 695, 696, 697, 698, 699, 700, 701, 702, 703, 704, 705, 706, 707, 708, 709, 710, 711, 712, 713, 714, 715, 716, 717, 718, 719, 720, 721, 722, 723, 724, 725, 726, 727, 728, 729, 730, 731, 732, 733, 734, 735, 736, 737, 738, 739, 740, 741, 742, 743, 744, 745, 746, 747, 748, 749, 750, 751, 752, 753, 754, 755, 756, 757, 758, 759, 760, 761, 762, 763, 764, 765, 766, 767, 768, 769, 770, 771, 772, 773, 774, 775, 776, 777, 778, 779, 780, 781, 782, 783, 784, 785, 786, 787, 788, 789, 790, 791, 792, 793, 794, 795, 796, 797, 798, 799, 800, 801, 802, 803, 804, 805, 806, 807, 808, 809, 810, 811, 812, 813, 814, 815, 816, 817, 818, 819, 820, 821, 822, 823, 824, 825, 826, 827, 828, 829, 830, 831, 832, 833, 834, 835, 836, 837, 838, 839, 840, 841, 842, 843, 844, 845, 846, 847, 848, 849, 850, 851, 852, 853, 854, 855, 856, 857, 858, 859, 860, 861, 862, 863, 864, 865, 866, 867, 868, 869, 870, 871, 872, 873, 874, 875, 876, 877, 878, 879, 880, 881, 882, 883, 884, 885, 886, 887, 888, 889, 890, 891, 892, 893, 894, 895, 896, 897, 898, 899, 900, 901, 902, 903, 904, 905, 906, 907, 908, 909, 910, 911, 912, 913, 914, 915, 916, 917, 918, 919, 920, 921, 922, 923, 924, 925, 926, 927, 928, 929, 930, 931, 932, 933, 934, 935, 936, 937, 938, 939, 940, 941, 942, 943, 944, 945, 946, 947, 948, 949, 950, 951, 952, 953, 954, 955, 956, 957, 958, 959, 960, 961, 962, 963, 964, 965, 966, 967, 968, 969, 970, 971, 972, 973, 974, 975, 976, 977, 978, 979, 980, 981, 982, 983, 984, 985, 986, 987, 988, 989, 990, 991, 992, 993, 994, 995, 996, 997, 998, 999, 1000) OR \"users\".\"id\" IN (1001)
- }
- end
- end
-
- describe "Nodes::NotIn" do
- it "should know how to visit" do
- ary = (1 .. 1001).to_a
- node = @attr.not_in ary
- compile(node).must_be_like %{
- "users"."id" NOT IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, 378, 379, 380, 381, 382, 383, 384, 385, 386, 387, 388, 389, 390, 391, 392, 393, 394, 395, 396, 397, 398, 399, 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, 418, 419, 420, 421, 422, 423, 424, 425, 426, 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, 437, 438, 439, 440, 441, 442, 443, 444, 445, 446, 447, 448, 449, 450, 451, 452, 453, 454, 455, 456, 457, 458, 459, 460, 461, 462, 463, 464, 465, 466, 467, 468, 469, 470, 471, 472, 473, 474, 475, 476, 477, 478, 479, 480, 481, 482, 483, 484, 485, 486, 487, 488, 489, 490, 491, 492, 493, 494, 495, 496, 497, 498, 499, 500, 501, 502, 503, 504, 505, 506, 507, 508, 509, 510, 511, 512, 513, 514, 515, 516, 517, 518, 519, 520, 521, 522, 523, 524, 525, 526, 527, 528, 529, 530, 531, 532, 533, 534, 535, 536, 537, 538, 539, 540, 541, 542, 543, 544, 545, 546, 547, 548, 549, 550, 551, 552, 553, 554, 555, 556, 557, 558, 559, 560, 561, 562, 563, 564, 565, 566, 567, 568, 569, 570, 571, 572, 573, 574, 575, 576, 577, 578, 579, 580, 581, 582, 583, 584, 585, 586, 587, 588, 589, 590, 591, 592, 593, 594, 595, 596, 597, 598, 599, 600, 601, 602, 603, 604, 605, 606, 607, 608, 609, 610, 611, 612, 613, 614, 615, 616, 617, 618, 619, 620, 621, 622, 623, 624, 625, 626, 627, 628, 629, 630, 631, 632, 633, 634, 635, 636, 637, 638, 639, 640, 641, 642, 643, 644, 645, 646, 647, 648, 649, 650, 651, 652, 653, 654, 655, 656, 657, 658, 659, 660, 661, 662, 663, 664, 665, 666, 667, 668, 669, 670, 671, 672, 673, 674, 675, 676, 677, 678, 679, 680, 681, 682, 683, 684, 685, 686, 687, 688, 689, 690, 691, 692, 693, 694, 695, 696, 697, 698, 699, 700, 701, 702, 703, 704, 705, 706, 707, 708, 709, 710, 711, 712, 713, 714, 715, 716, 717, 718, 719, 720, 721, 722, 723, 724, 725, 726, 727, 728, 729, 730, 731, 732, 733, 734, 735, 736, 737, 738, 739, 740, 741, 742, 743, 744, 745, 746, 747, 748, 749, 750, 751, 752, 753, 754, 755, 756, 757, 758, 759, 760, 761, 762, 763, 764, 765, 766, 767, 768, 769, 770, 771, 772, 773, 774, 775, 776, 777, 778, 779, 780, 781, 782, 783, 784, 785, 786, 787, 788, 789, 790, 791, 792, 793, 794, 795, 796, 797, 798, 799, 800, 801, 802, 803, 804, 805, 806, 807, 808, 809, 810, 811, 812, 813, 814, 815, 816, 817, 818, 819, 820, 821, 822, 823, 824, 825, 826, 827, 828, 829, 830, 831, 832, 833, 834, 835, 836, 837, 838, 839, 840, 841, 842, 843, 844, 845, 846, 847, 848, 849, 850, 851, 852, 853, 854, 855, 856, 857, 858, 859, 860, 861, 862, 863, 864, 865, 866, 867, 868, 869, 870, 871, 872, 873, 874, 875, 876, 877, 878, 879, 880, 881, 882, 883, 884, 885, 886, 887, 888, 889, 890, 891, 892, 893, 894, 895, 896, 897, 898, 899, 900, 901, 902, 903, 904, 905, 906, 907, 908, 909, 910, 911, 912, 913, 914, 915, 916, 917, 918, 919, 920, 921, 922, 923, 924, 925, 926, 927, 928, 929, 930, 931, 932, 933, 934, 935, 936, 937, 938, 939, 940, 941, 942, 943, 944, 945, 946, 947, 948, 949, 950, 951, 952, 953, 954, 955, 956, 957, 958, 959, 960, 961, 962, 963, 964, 965, 966, 967, 968, 969, 970, 971, 972, 973, 974, 975, 976, 977, 978, 979, 980, 981, 982, 983, 984, 985, 986, 987, 988, 989, 990, 991, 992, 993, 994, 995, 996, 997, 998, 999, 1000) AND \"users\".\"id\" NOT IN (1001)
- }
- end
- end
end
end
end
diff --git a/activerecord/test/cases/arel/visitors/to_sql_test.rb b/activerecord/test/cases/arel/visitors/to_sql_test.rb
index 625e37f1c0..fd19574876 100644
--- a/activerecord/test/cases/arel/visitors/to_sql_test.rb
+++ b/activerecord/test/cases/arel/visitors/to_sql_test.rb
@@ -395,6 +395,11 @@ module Arel
compile(node).must_be_like %{
"users"."id" IN (1, 2, 3)
}
+
+ node = @attr.in [1, 2, 3, 4, 5]
+ compile(node).must_be_like %{
+ ("users"."id" IN (1, 2, 3) OR "users"."id" IN (4, 5))
+ }
end
it "should return 1=0 when empty right which is always false" do
@@ -545,6 +550,11 @@ module Arel
compile(node).must_be_like %{
"users"."id" NOT IN (1, 2, 3)
}
+
+ node = @attr.not_in [1, 2, 3, 4, 5]
+ compile(node).must_be_like %{
+ "users"."id" NOT IN (1, 2, 3) AND "users"."id" NOT IN (4, 5)
+ }
end
it "should return 1=1 when empty right which is always true" do
diff --git a/activerecord/test/cases/associations/cascaded_eager_loading_test.rb b/activerecord/test/cases/associations/cascaded_eager_loading_test.rb
index 49f754be63..cbe48a374f 100644
--- a/activerecord/test/cases/associations/cascaded_eager_loading_test.rb
+++ b/activerecord/test/cases/associations/cascaded_eager_loading_test.rb
@@ -37,8 +37,8 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase
def test_eager_association_loading_with_hmt_does_not_table_name_collide_when_joining_associations
authors = Author.joins(:posts).eager_load(:comments).where(posts: { tags_count: 1 }).order(:id).to_a
- assert_equal 3, assert_no_queries { authors.size }
- assert_equal 10, assert_no_queries { authors[0].comments.size }
+ assert_equal 3, assert_queries(0) { authors.size }
+ assert_equal 10, assert_queries(0) { authors[0].comments.size }
end
def test_eager_association_loading_grafts_stashed_associations_to_correct_parent
@@ -103,14 +103,14 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase
firms = Firm.all.merge!(includes: { account: { firm: :account } }, order: "companies.id").to_a
assert_equal 2, firms.size
assert_equal firms.first.account, firms.first.account.firm.account
- assert_equal companies(:first_firm).account, assert_no_queries { firms.first.account.firm.account }
- assert_equal companies(:first_firm).account.firm.account, assert_no_queries { firms.first.account.firm.account }
+ assert_equal companies(:first_firm).account, assert_queries(0) { firms.first.account.firm.account }
+ assert_equal companies(:first_firm).account.firm.account, assert_queries(0) { firms.first.account.firm.account }
end
def test_eager_association_loading_with_has_many_sti
topics = Topic.all.merge!(includes: :replies, order: "topics.id").to_a
first, second, = topics(:first).replies.size, topics(:second).replies.size
- assert_no_queries do
+ assert_queries(0) do
assert_equal first, topics[0].replies.size
assert_equal second, topics[1].replies.size
end
@@ -131,13 +131,13 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase
replies = Reply.all.merge!(includes: :topic, order: "topics.id").to_a
assert_includes replies, topics(:second)
assert_not_includes replies, topics(:first)
- assert_equal topics(:first), assert_no_queries { replies.first.topic }
+ assert_equal topics(:first), assert_queries(0) { replies.first.topic }
end
def test_eager_association_loading_with_multiple_stis_and_order
author = Author.all.merge!(includes: { posts: [ :special_comments, :very_special_comment ] }, order: ["authors.name", "comments.body", "very_special_comments_posts.body"], where: "posts.id = 4").first
assert_equal authors(:david), author
- assert_no_queries do
+ assert_queries(0) do
author.posts.first.special_comments
author.posts.first.very_special_comment
end
@@ -146,7 +146,7 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase
def test_eager_association_loading_of_stis_with_multiple_references
authors = Author.all.merge!(includes: { posts: { special_comments: { post: [ :special_comments, :very_special_comment ] } } }, order: "comments.body, very_special_comments_posts.body", where: "posts.id = 4").to_a
assert_equal [authors(:david)], authors
- assert_no_queries do
+ assert_queries(0) do
authors.first.posts.first.special_comments.first.post.special_comments
authors.first.posts.first.special_comments.first.post.very_special_comment
end
@@ -155,14 +155,14 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase
def test_eager_association_loading_where_first_level_returns_nil
authors = Author.all.merge!(includes: { post_about_thinking: :comments }, order: "authors.id DESC").to_a
assert_equal [authors(:bob), authors(:mary), authors(:david)], authors
- assert_no_queries do
+ assert_queries(0) do
authors[2].post_about_thinking.comments.first
end
end
def test_preload_through_missing_records
post = Post.where.not(author_id: Author.select(:id)).preload(author: { comments: :post }).first!
- assert_no_queries { assert_nil post.author }
+ assert_queries(0) { assert_nil post.author }
end
def test_eager_association_loading_with_missing_first_record
@@ -172,12 +172,12 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase
def test_eager_association_loading_with_recursive_cascading_four_levels_has_many_through
source = Vertex.all.merge!(includes: { sinks: { sinks: { sinks: :sinks } } }, order: "vertices.id").first
- assert_equal vertices(:vertex_4), assert_no_queries { source.sinks.first.sinks.first.sinks.first }
+ assert_equal vertices(:vertex_4), assert_queries(0) { source.sinks.first.sinks.first.sinks.first }
end
def test_eager_association_loading_with_recursive_cascading_four_levels_has_and_belongs_to_many
sink = Vertex.all.merge!(includes: { sources: { sources: { sources: :sources } } }, order: "vertices.id DESC").first
- assert_equal vertices(:vertex_1), assert_no_queries { sink.sources.first.sources.first.sources.first.sources.first }
+ assert_equal vertices(:vertex_1), assert_queries(0) { sink.sources.first.sources.first.sources.first.sources.first }
end
def test_eager_association_loading_with_cascaded_interdependent_one_level_and_two_levels
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 849939de75..9be21b23db 100644
--- a/activerecord/test/cases/associations/eager_load_nested_include_test.rb
+++ b/activerecord/test/cases/associations/eager_load_nested_include_test.rb
@@ -14,6 +14,7 @@ module Remembered
included do
after_create :remember
+
private
def remember; self.class.remembered << self; end
end
diff --git a/activerecord/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb
index 594d161fa3..cb46f9e053 100644
--- a/activerecord/test/cases/associations/eager_test.rb
+++ b/activerecord/test/cases/associations/eager_test.rb
@@ -101,6 +101,17 @@ class EagerAssociationTest < ActiveRecord::TestCase
assert_equal [taggings(:normal_comment_rating)], rating.taggings_without_tag
end
+ def test_loading_association_with_string_joins
+ rating = Rating.first
+ assert_equal [taggings(:normal_comment_rating)], rating.taggings_with_no_tag
+
+ rating = Rating.preload(:taggings_with_no_tag).first
+ assert_equal [taggings(:normal_comment_rating)], rating.taggings_with_no_tag
+
+ rating = Rating.eager_load(:taggings_with_no_tag).first
+ assert_equal [taggings(:normal_comment_rating)], rating.taggings_with_no_tag
+ end
+
def test_loading_with_scope_including_joins
member = Member.first
assert_equal members(:groucho), member
@@ -240,7 +251,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
end
def test_load_associated_records_in_one_query_when_adapter_has_no_limit
- assert_called(Comment.connection, :in_clause_length, returns: nil) do
+ assert_not_called(Comment.connection, :in_clause_length) do
post = posts(:welcome)
assert_queries(2) do
Post.includes(:comments).where(id: post.id).to_a
@@ -249,16 +260,16 @@ class EagerAssociationTest < ActiveRecord::TestCase
end
def test_load_associated_records_in_several_queries_when_many_ids_passed
- assert_called(Comment.connection, :in_clause_length, returns: 1) do
+ assert_called(Comment.connection, :in_clause_length, times: 2, returns: 1) do
post1, post2 = posts(:welcome), posts(:thinking)
- assert_queries(3) do
+ assert_queries(2) do
Post.includes(:comments).where(id: [post1.id, post2.id]).to_a
end
end
end
def test_load_associated_records_in_one_query_when_a_few_ids_passed
- assert_called(Comment.connection, :in_clause_length, returns: 3) do
+ assert_not_called(Comment.connection, :in_clause_length) do
post = posts(:welcome)
assert_queries(2) do
Post.includes(:comments).where(id: post.id).to_a
@@ -512,7 +523,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
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")
assert_nothing_raised do
- Comment.includes(:post).references(:posts).order(Arel.sql(quoted_posts_id))
+ Comment.includes(:post).references(:posts).order(quoted_posts_id)
end
end
@@ -778,7 +789,6 @@ class EagerAssociationTest < ActiveRecord::TestCase
.where("comments.body like 'Normal%' OR comments.#{QUOTED_TYPE}= 'SpecialComment'")
.references(:comments)
.scoping do
-
posts = authors(:david).posts.limit(2).to_a
assert_equal 2, posts.size
end
@@ -787,7 +797,6 @@ class EagerAssociationTest < ActiveRecord::TestCase
.where("authors.name = 'David' AND (comments.body like 'Normal%' OR comments.#{QUOTED_TYPE}= 'SpecialComment')")
.references(:authors, :comments)
.scoping do
-
count = Post.limit(2).count
assert_equal count, posts.size
end
@@ -959,14 +968,14 @@ class EagerAssociationTest < ActiveRecord::TestCase
posts(:thinking, :sti_comments),
Post.all.merge!(
includes: [:author, :comments], where: { "authors.name" => "David" },
- order: Arel.sql("UPPER(posts.title)"), limit: 2, offset: 1
+ order: "UPPER(posts.title)", limit: 2, offset: 1
).to_a
)
assert_equal(
posts(:sti_post_and_comments, :sti_comments),
Post.all.merge!(
includes: [:author, :comments], where: { "authors.name" => "David" },
- order: Arel.sql("UPPER(posts.title) DESC"), limit: 2, offset: 1
+ order: "UPPER(posts.title) DESC", limit: 2, offset: 1
).to_a
)
end
@@ -976,14 +985,14 @@ class EagerAssociationTest < ActiveRecord::TestCase
posts(:thinking, :sti_comments),
Post.all.merge!(
includes: [:author, :comments], where: { "authors.name" => "David" },
- order: [Arel.sql("UPPER(posts.title)"), "posts.id"], limit: 2, offset: 1
+ order: ["UPPER(posts.title)", "posts.id"], limit: 2, offset: 1
).to_a
)
assert_equal(
posts(:sti_post_and_comments, :sti_comments),
Post.all.merge!(
includes: [:author, :comments], where: { "authors.name" => "David" },
- order: [Arel.sql("UPPER(posts.title) DESC"), "posts.id"], limit: 2, offset: 1
+ order: ["UPPER(posts.title) DESC", "posts.id"], limit: 2, offset: 1
).to_a
)
end
@@ -1234,7 +1243,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
Post.all.merge!(select: "posts.*, authors.name as author_name", includes: :comments, joins: :author, order: "posts.id").to_a
end
assert_equal "David", posts[0].author_name
- assert_equal posts(:welcome).comments, assert_no_queries { posts[0].comments }
+ assert_equal posts(:welcome).comments.sort_by(&:id), assert_no_queries { posts[0].comments.sort_by(&:id) }
end
def test_eager_loading_with_conditions_on_join_model_preloads
@@ -1246,8 +1255,8 @@ class EagerAssociationTest < ActiveRecord::TestCase
end
def test_preload_belongs_to_uses_exclusive_scope
- people = Person.males.merge(includes: :primary_contact).to_a
- assert_not_equal people.length, 0
+ people = Person.males.includes(:primary_contact).to_a
+ assert_equal 2, people.length
people.each do |person|
assert_no_queries { assert_not_nil person.primary_contact }
assert_equal Person.find(person.id).primary_contact, person.primary_contact
@@ -1256,16 +1265,17 @@ class EagerAssociationTest < ActiveRecord::TestCase
def test_preload_has_many_uses_exclusive_scope
people = Person.males.includes(:agents).to_a
+ assert_equal 2, people.length
people.each do |person|
- assert_equal Person.find(person.id).agents, person.agents
+ assert_equal Person.find(person.id).agents.sort_by(&:id), person.agents.sort_by(&:id)
end
end
def test_preload_has_many_using_primary_key
- expected = Firm.first.clients_using_primary_key.to_a
+ expected = Firm.first.clients_using_primary_key.sort_by(&:id)
firm = Firm.includes(:clients_using_primary_key).first
assert_no_queries do
- assert_equal expected, firm.clients_using_primary_key
+ assert_equal expected, firm.clients_using_primary_key.sort_by(&:id)
end
end
@@ -1516,6 +1526,24 @@ class EagerAssociationTest < ActiveRecord::TestCase
assert_match message, error.message
end
+ test "preloading and eager loading of optional instance dependent associations is not supported" do
+ message = "association scope 'posts_mentioning_author' is"
+ error = assert_raises(ArgumentError) do
+ Author.includes(:posts_mentioning_author).to_a
+ end
+ assert_match message, error.message
+
+ error = assert_raises(ArgumentError) do
+ Author.preload(:posts_mentioning_author).to_a
+ end
+ assert_match message, error.message
+
+ error = assert_raises(ArgumentError) do
+ Author.eager_load(:posts_mentioning_author).to_a
+ end
+ assert_match message, error.message
+ end
+
test "preload with invalid argument" do
exception = assert_raises(ArgumentError) do
Author.preload(10).to_a
diff --git a/activerecord/test/cases/associations/extension_test.rb b/activerecord/test/cases/associations/extension_test.rb
index aef8f31112..604a52655c 100644
--- a/activerecord/test/cases/associations/extension_test.rb
+++ b/activerecord/test/cases/associations/extension_test.rb
@@ -70,8 +70,8 @@ class AssociationsExtensionsTest < ActiveRecord::TestCase
extend!(Developer)
extend!(MyApplication::Business::Developer)
- assert Object.const_get "DeveloperAssociationNameAssociationExtension"
- assert MyApplication::Business.const_get "DeveloperAssociationNameAssociationExtension"
+ assert Developer.const_get "AssociationNameAssociationExtension"
+ assert MyApplication::Business::Developer.const_get "AssociationNameAssociationExtension"
end
def test_proxy_association_after_scoped
@@ -87,8 +87,7 @@ class AssociationsExtensionsTest < ActiveRecord::TestCase
end
private
-
def extend!(model)
- ActiveRecord::Associations::Builder::HasMany.define_extensions(model, :association_name) { }
+ ActiveRecord::Associations::Builder::HasMany.send(:define_extensions, model, :association_name) { }
end
end
diff --git a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb
index fe8bdd03ba..25cfa0a723 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
@@ -313,10 +313,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
def test_build
devel = Developer.find(1)
- # Load schema information so we don't query below if running just this test.
- Project.define_attribute_methods
-
- proj = assert_no_queries { devel.projects.build("name" => "Projekt") }
+ proj = assert_queries(0) { devel.projects.build("name" => "Projekt") }
assert_not_predicate devel.projects, :loaded?
assert_equal devel.projects.last, proj
@@ -332,10 +329,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
def test_new_aliased_to_build
devel = Developer.find(1)
- # Load schema information so we don't query below if running just this test.
- Project.define_attribute_methods
-
- proj = assert_no_queries { devel.projects.new("name" => "Projekt") }
+ proj = assert_queries(0) { devel.projects.new("name" => "Projekt") }
assert_not_predicate devel.projects, :loaded?
assert_equal devel.projects.last, proj
@@ -556,7 +550,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
developer = project.developers.first
- assert_no_queries do
+ assert_queries(0) do
assert_predicate project.developers, :loaded?
assert_includes project.developers, developer
end
@@ -751,7 +745,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
def test_get_ids_for_loaded_associations
developer = developers(:david)
developer.projects.reload
- assert_no_queries do
+ assert_queries(0) do
developer.project_ids
developer.project_ids
end
@@ -879,7 +873,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
def test_has_and_belongs_to_many_associations_on_new_records_use_null_relations
projects = Developer.new.projects
- assert_no_queries do
+ assert_queries(0) do
assert_equal [], projects
assert_equal [], projects.where(title: "omg")
assert_equal [], projects.pluck(:title)
diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb
index 32285f269a..6c54c2f1cd 100644
--- a/activerecord/test/cases/associations/has_many_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_associations_test.rb
@@ -468,10 +468,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
company = companies(:first_firm)
new_clients = []
- # Load schema information so we don't query below if running just this test.
- Client.define_attribute_methods
-
- assert_no_queries do
+ assert_queries(0) do
new_clients << company.clients_of_firm.build(name: "Another Client")
new_clients << company.clients_of_firm.build(name: "Another Client II")
new_clients << company.clients_of_firm.build(name: "Another Client III")
@@ -492,10 +489,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
company = companies(:first_firm)
new_clients = []
- # Load schema information so we don't query below if running just this test.
- Client.define_attribute_methods
-
- assert_no_queries do
+ assert_queries(0) do
new_clients << company.clients_of_firm.build(name: "Another Client")
new_clients << company.clients_of_firm.build(name: "Another Client II")
new_clients << company.clients_of_firm.build(name: "Another Client III")
@@ -1015,11 +1009,8 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_transactions_when_adding_to_new_record
- # Load schema information so we don't query below if running just this test.
- Client.define_attribute_methods
-
firm = Firm.new
- assert_no_queries do
+ assert_queries(0) do
firm.clients_of_firm.concat(Client.new("name" => "Natural Company"))
end
end
@@ -1034,10 +1025,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_new_aliased_to_build
company = companies(:first_firm)
- # Load schema information so we don't query below if running just this test.
- Client.define_attribute_methods
-
- new_client = assert_no_queries { company.clients_of_firm.new("name" => "Another Client") }
+ new_client = assert_queries(0) { company.clients_of_firm.new("name" => "Another Client") }
assert_not_predicate company.clients_of_firm, :loaded?
assert_equal "Another Client", new_client.name
@@ -1048,10 +1036,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_build
company = companies(:first_firm)
- # Load schema information so we don't query below if running just this test.
- Client.define_attribute_methods
-
- new_client = assert_no_queries { company.clients_of_firm.build("name" => "Another Client") }
+ new_client = assert_queries(0) { company.clients_of_firm.build("name" => "Another Client") }
assert_not_predicate company.clients_of_firm, :loaded?
assert_equal "Another Client", new_client.name
@@ -1109,10 +1094,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_build_many
company = companies(:first_firm)
- # Load schema information so we don't query below if running just this test.
- Client.define_attribute_methods
-
- new_clients = assert_no_queries { company.clients_of_firm.build([{ "name" => "Another Client" }, { "name" => "Another Client II" }]) }
+ new_clients = assert_queries(0) { company.clients_of_firm.build([{ "name" => "Another Client" }, { "name" => "Another Client II" }]) }
assert_equal 2, new_clients.size
end
@@ -1127,10 +1109,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_equal 1, first_topic.replies.length
- # Load schema information so we don't query below if running just this test.
- Reply.define_attribute_methods
-
- assert_no_queries do
+ assert_queries(0) do
first_topic.replies.build(title: "Not saved", content: "Superstars")
assert_equal 2, first_topic.replies.size
end
@@ -1141,10 +1120,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_build_via_block
company = companies(:first_firm)
- # Load schema information so we don't query below if running just this test.
- Client.define_attribute_methods
-
- new_client = assert_no_queries { company.clients_of_firm.build { |client| client.name = "Another Client" } }
+ new_client = assert_queries(0) { company.clients_of_firm.build { |client| client.name = "Another Client" } }
assert_not_predicate company.clients_of_firm, :loaded?
assert_equal "Another Client", new_client.name
@@ -1155,10 +1131,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_build_many_via_block
company = companies(:first_firm)
- # Load schema information so we don't query below if running just this test.
- Client.define_attribute_methods
-
- new_clients = assert_no_queries do
+ new_clients = assert_queries(0) do
company.clients_of_firm.build([{ "name" => "Another Client" }, { "name" => "Another Client II" }]) do |client|
client.name = "changed"
end
@@ -1447,11 +1420,8 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_transaction_when_deleting_new_record
- # Load schema information so we don't query below if running just this test.
- Client.define_attribute_methods
-
firm = Firm.new
- assert_no_queries do
+ assert_queries(0) do
client = Client.new("name" => "New Client")
firm.clients_of_firm << client
firm.clients_of_firm.destroy(client)
@@ -1966,11 +1936,8 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_transactions_when_replacing_on_new_record
- # Load schema information so we don't query below if running just this test.
- Client.define_attribute_methods
-
firm = Firm.new
- assert_no_queries do
+ assert_queries(0) do
firm.clients_of_firm = [Client.new("name" => "New Client")]
end
end
@@ -2024,11 +1991,8 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_get_ids_for_association_on_new_record_does_not_try_to_find_records
- # Load schema information so we don't query below if running just this test.
- companies(:first_client).contract_ids
-
company = Company.new
- assert_no_queries do
+ assert_queries(0) do
company.contract_ids
end
@@ -2711,18 +2675,22 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
bulb = Bulb.create!
tyre = Tyre.create!
- car = Car.create! do |c|
+ car = Car.create!(name: "honda") do |c|
c.bulbs << bulb
c.tyres << tyre
end
+ assert_equal [nil, "honda"], car.saved_change_to_name
+
assert_equal 1, car.bulbs.count
assert_equal 1, car.tyres.count
end
test "associations replace in memory when records have the same id" do
bulb = Bulb.create!
- car = Car.create!(bulbs: [bulb])
+ car = Car.create!(name: "honda", bulbs: [bulb])
+
+ assert_equal [nil, "honda"], car.saved_change_to_name
new_bulb = Bulb.find(bulb.id)
new_bulb.name = "foo"
@@ -2733,7 +2701,9 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
test "in memory replacement executes no queries" do
bulb = Bulb.create!
- car = Car.create!(bulbs: [bulb])
+ car = Car.create!(name: "honda", bulbs: [bulb])
+
+ assert_equal [nil, "honda"], car.saved_change_to_name
new_bulb = Bulb.find(bulb.id)
@@ -2765,7 +2735,9 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
test "in memory replacements sets inverse instance" do
bulb = Bulb.create!
- car = Car.create!(bulbs: [bulb])
+ car = Car.create!(name: "honda", bulbs: [bulb])
+
+ assert_equal [nil, "honda"], car.saved_change_to_name
new_bulb = Bulb.find(bulb.id)
car.bulbs = [new_bulb]
@@ -2785,7 +2757,9 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
test "in memory replacement maintains order" do
first_bulb = Bulb.create!
second_bulb = Bulb.create!
- car = Car.create!(bulbs: [first_bulb, second_bulb])
+ car = Car.create!(name: "honda", bulbs: [first_bulb, second_bulb])
+
+ assert_equal [nil, "honda"], car.saved_change_to_name
same_bulb = Bulb.find(first_bulb.id)
car.bulbs = [second_bulb, same_bulb]
@@ -2958,8 +2932,12 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_equal [], reference.ideal_jobs
end
- private
+ def test_has_many_preloading_with_duplicate_records
+ posts = Post.joins(:comments).preload(:comments).to_a
+ assert_equal [1, 2], posts.first.comments.map(&:id)
+ end
+ private
def force_signal37_to_load_all_clients_of_firm
companies(:first_firm).clients_of_firm.load_target
end
diff --git a/activerecord/test/cases/associations/has_many_through_associations_test.rb b/activerecord/test/cases/associations/has_many_through_associations_test.rb
index c13789f7ec..6faa9664f7 100644
--- a/activerecord/test/cases/associations/has_many_through_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb
@@ -58,6 +58,14 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
assert_equal preloaded, Marshal.load(Marshal.dump(preloaded))
end
+ def test_through_association_with_joins
+ assert_equal [comments(:eager_other_comment1)], authors(:mary).comments.merge(Post.joins(:comments))
+ end
+
+ def test_through_association_with_left_joins
+ assert_equal [comments(:eager_other_comment1)], authors(:mary).comments.merge(Post.left_joins(:comments))
+ end
+
def test_preload_with_nested_association
posts = Post.preload(:author, :author_favorites_with_scope).to_a
@@ -299,10 +307,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
assert_queries(1) { posts(:thinking) }
new_person = nil # so block binding catches it
- # Load schema information so we don't query below if running just this test.
- Person.define_attribute_methods
-
- assert_no_queries do
+ assert_queries(0) do
new_person = Person.new first_name: "bob"
end
@@ -322,10 +327,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
def test_associate_new_by_building
assert_queries(1) { posts(:thinking) }
- # Load schema information so we don't query below if running just this test.
- Person.define_attribute_methods
-
- assert_no_queries do
+ assert_queries(0) do
posts(:thinking).people.build(first_name: "Bob")
posts(:thinking).people.new(first_name: "Ted")
end
@@ -749,10 +751,14 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
firm = companies(:first_firm)
lifo = Developer.new(name: "lifo")
- assert_raises(ActiveRecord::RecordInvalid) { firm.developers << lifo }
+ assert_raises(ActiveRecord::RecordInvalid) do
+ assert_deprecated { firm.developers << lifo }
+ end
lifo = Developer.create!(name: "lifo")
- assert_raises(ActiveRecord::RecordInvalid) { firm.developers << lifo }
+ assert_raises(ActiveRecord::RecordInvalid) do
+ assert_deprecated { firm.developers << lifo }
+ end
end
end
@@ -1163,7 +1169,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
def test_create_should_not_raise_exception_when_join_record_has_errors
repair_validations(Categorization) do
Categorization.validate { |r| r.errors[:base] << "Invalid Categorization" }
- Category.create(name: "Fishing", authors: [Author.first])
+ assert_deprecated { Category.create(name: "Fishing", authors: [Author.first]) }
end
end
@@ -1176,7 +1182,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
repair_validations(Categorization) do
Categorization.validate { |r| r.errors[:base] << "Invalid Categorization" }
assert_raises(ActiveRecord::RecordInvalid) do
- Category.create!(name: "Fishing", authors: [Author.first])
+ assert_deprecated { Category.create!(name: "Fishing", authors: [Author.first]) }
end
end
end
@@ -1186,7 +1192,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
Categorization.validate { |r| r.errors[:base] << "Invalid Categorization" }
c = Category.new(name: "Fishing", authors: [Author.first])
assert_raises(ActiveRecord::RecordInvalid) do
- c.save!
+ assert_deprecated { c.save! }
end
end
end
@@ -1195,7 +1201,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
repair_validations(Categorization) do
Categorization.validate { |r| r.errors[:base] << "Invalid Categorization" }
c = Category.new(name: "Fishing", authors: [Author.first])
- assert_not c.save
+ assert_deprecated { assert_not c.save }
end
end
diff --git a/activerecord/test/cases/associations/has_one_associations_test.rb b/activerecord/test/cases/associations/has_one_associations_test.rb
index 7bb629466d..3ef25c7027 100644
--- a/activerecord/test/cases/associations/has_one_associations_test.rb
+++ b/activerecord/test/cases/associations/has_one_associations_test.rb
@@ -15,10 +15,13 @@ require "models/post"
require "models/drink_designer"
require "models/chef"
require "models/department"
+require "models/club"
+require "models/membership"
class HasOneAssociationsTest < ActiveRecord::TestCase
self.use_transactional_tests = false unless supports_savepoints?
- fixtures :accounts, :companies, :developers, :projects, :developers_projects, :ships, :pirates, :authors, :author_addresses
+ fixtures :accounts, :companies, :developers, :projects, :developers_projects,
+ :ships, :pirates, :authors, :author_addresses, :memberships, :clubs
def setup
Account.destroyed_account_ids.clear
@@ -253,11 +256,8 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
end
def test_build_association_dont_create_transaction
- # Load schema information so we don't query below if running just this test.
- Account.define_attribute_methods
-
firm = Firm.new
- assert_no_queries do
+ assert_queries(0) do
firm.build_account
end
end
@@ -706,6 +706,40 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
end
end
+ def test_has_one_with_touch_option_on_create
+ assert_queries(3) {
+ Club.create(name: "1000 Oaks", membership_attributes: { favourite: true })
+ }
+ end
+
+ def test_has_one_with_touch_option_on_update
+ new_club = Club.create(name: "1000 Oaks")
+ new_club.create_membership
+
+ assert_queries(2) { new_club.update(name: "Effingut") }
+ end
+
+ def test_has_one_with_touch_option_on_touch
+ new_club = Club.create(name: "1000 Oaks")
+ new_club.create_membership
+
+ assert_queries(1) { new_club.touch }
+ end
+
+ def test_has_one_with_touch_option_on_destroy
+ new_club = Club.create(name: "1000 Oaks")
+ new_club.create_membership
+
+ assert_queries(2) { new_club.destroy }
+ end
+
+ def test_has_one_with_touch_option_on_empty_update
+ new_club = Club.create(name: "1000 Oaks")
+ new_club.create_membership
+
+ assert_no_queries { new_club.save }
+ end
+
class SpecialBook < ActiveRecord::Base
self.table_name = "books"
belongs_to :author, class_name: "SpecialAuthor"
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 0a8863c35d..d44c6407f5 100644
--- a/activerecord/test/cases/associations/left_outer_join_association_test.rb
+++ b/activerecord/test/cases/associations/left_outer_join_association_test.rb
@@ -32,6 +32,10 @@ class LeftOuterJoinAssociationTest < ActiveRecord::TestCase
assert_equal 17, Post.left_outer_joins(:comments).count
end
+ def test_merging_left_joins_should_be_left_joins
+ assert_equal 5, Author.left_joins(:posts).merge(Post.no_comments).count
+ end
+
def test_left_joins_aliases_left_outer_joins
assert_equal Post.left_outer_joins(:comments).to_sql, Post.left_joins(:comments).to_sql
end
diff --git a/activerecord/test/cases/associations/nested_through_associations_test.rb b/activerecord/test/cases/associations/nested_through_associations_test.rb
index 35da74102d..8d74ae3961 100644
--- a/activerecord/test/cases/associations/nested_through_associations_test.rb
+++ b/activerecord/test/cases/associations/nested_through_associations_test.rb
@@ -626,7 +626,6 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
end
private
-
def assert_includes_and_joins_equal(query, expected, association)
actual = assert_queries(1) { query.joins(association).to_a.uniq }
assert_equal expected, actual
diff --git a/activerecord/test/cases/associations/required_test.rb b/activerecord/test/cases/associations/required_test.rb
index c7a78e6bc4..db7f945a36 100644
--- a/activerecord/test/cases/associations/required_test.rb
+++ b/activerecord/test/cases/associations/required_test.rb
@@ -117,7 +117,6 @@ class RequiredAssociationsTest < ActiveRecord::TestCase
end
private
-
def subclass_of(klass, &block)
subclass = Class.new(klass, &block)
def subclass.name
diff --git a/activerecord/test/cases/attribute_methods_test.rb b/activerecord/test/cases/attribute_methods_test.rb
index 9fd62dcf72..71b5407dcc 100644
--- a/activerecord/test/cases/attribute_methods_test.rb
+++ b/activerecord/test/cases/attribute_methods_test.rb
@@ -1081,13 +1081,12 @@ class AttributeMethodsTest < ActiveRecord::TestCase
assert_equal ["title"], model.accessed_fields
end
- test "generated attribute methods ancestors have correct class" do
+ test "generated attribute methods ancestors have correct module" do
mod = Topic.send(:generated_attribute_methods)
- assert_match %r(Topic::GeneratedAttributeMethods), mod.inspect
+ assert_equal "Topic::GeneratedAttributeMethods", mod.inspect
end
private
-
def new_topic_like_ar_class(&block)
klass = Class.new(ActiveRecord::Base) do
self.table_name = "topics"
diff --git a/activerecord/test/cases/attributes_test.rb b/activerecord/test/cases/attributes_test.rb
index a6fb9f0af7..d6ac5a1057 100644
--- a/activerecord/test/cases/attributes_test.rb
+++ b/activerecord/test/cases/attributes_test.rb
@@ -241,7 +241,7 @@ module ActiveRecord
test "attributes not backed by database columns are always initialized" do
OverloadedType.create!
- model = OverloadedType.first
+ model = OverloadedType.last
assert_nil model.non_existent_decimal
model.non_existent_decimal = "123"
@@ -253,7 +253,7 @@ module ActiveRecord
attribute :non_existent_decimal, :decimal, default: 123
end
child.create!
- model = child.first
+ model = child.last
assert_equal 123, model.non_existent_decimal
end
@@ -264,7 +264,7 @@ module ActiveRecord
attribute :foo, :string, default: "lol"
end
child.create!
- model = child.first
+ model = child.last
assert_equal "lol", model.foo
diff --git a/activerecord/test/cases/autosave_association_test.rb b/activerecord/test/cases/autosave_association_test.rb
index 1a0732c14b..3528ac045f 100644
--- a/activerecord/test/cases/autosave_association_test.rb
+++ b/activerecord/test/cases/autosave_association_test.rb
@@ -2,6 +2,7 @@
require "cases/helper"
require "models/author"
+require "models/book"
require "models/bird"
require "models/post"
require "models/comment"
@@ -12,12 +13,14 @@ require "models/developer"
require "models/computer"
require "models/invoice"
require "models/line_item"
+require "models/mouse"
require "models/order"
require "models/parrot"
require "models/pirate"
require "models/project"
require "models/ship"
require "models/ship_part"
+require "models/squeak"
require "models/tag"
require "models/tagging"
require "models/treasure"
@@ -39,7 +42,6 @@ class TestAutosaveAssociationsInGeneral < ActiveRecord::TestCase
def self.name; "Person"; end
private
-
def should_be_cool
unless first_name == "cool"
errors.add :first_name, "not cool"
@@ -82,7 +84,6 @@ class TestAutosaveAssociationsInGeneral < ActiveRecord::TestCase
end
private
-
def assert_no_difference_when_adding_callbacks_twice_for(model, association_name)
reflection = model.reflect_on_association(association_name)
assert_no_difference "callbacks_for_model(#{model.name}).length" do
@@ -387,6 +388,20 @@ class TestDefaultAutosaveAssociationOnABelongsToAssociation < ActiveRecord::Test
assert_predicate auditlog, :valid?
end
+
+ def test_validation_does_not_validate_non_dirty_association_target
+ mouse = Mouse.create!(name: "Will")
+ Squeak.create!(mouse: mouse)
+
+ mouse.name = nil
+ mouse.save! validate: false
+
+ squeak = Squeak.last
+
+ assert_equal true, squeak.valid?
+ assert_equal true, squeak.mouse.present?
+ assert_equal true, squeak.valid?
+ end
end
class TestDefaultAutosaveAssociationOnAHasManyAssociationWithAcceptsNestedAttributes < ActiveRecord::TestCase
@@ -644,10 +659,7 @@ class TestDefaultAutosaveAssociationOnAHasManyAssociation < ActiveRecord::TestCa
def test_build_before_save
company = companies(:first_firm)
- # Load schema information so we don't query below if running just this test.
- Client.define_attribute_methods
-
- new_client = assert_no_queries { company.clients_of_firm.build("name" => "Another Client") }
+ new_client = assert_queries(0) { company.clients_of_firm.build("name" => "Another Client") }
assert_not_predicate company.clients_of_firm, :loaded?
company.name += "-changed"
@@ -659,10 +671,7 @@ class TestDefaultAutosaveAssociationOnAHasManyAssociation < ActiveRecord::TestCa
def test_build_many_before_save
company = companies(:first_firm)
- # Load schema information so we don't query below if running just this test.
- Client.define_attribute_methods
-
- assert_no_queries { company.clients_of_firm.build([{ "name" => "Another Client" }, { "name" => "Another Client II" }]) }
+ assert_queries(0) { company.clients_of_firm.build([{ "name" => "Another Client" }, { "name" => "Another Client II" }]) }
company.name += "-changed"
assert_queries(3) { assert company.save }
@@ -672,10 +681,7 @@ class TestDefaultAutosaveAssociationOnAHasManyAssociation < ActiveRecord::TestCa
def test_build_via_block_before_save
company = companies(:first_firm)
- # Load schema information so we don't query below if running just this test.
- Client.define_attribute_methods
-
- new_client = assert_no_queries { company.clients_of_firm.build { |client| client.name = "Another Client" } }
+ new_client = assert_queries(0) { company.clients_of_firm.build { |client| client.name = "Another Client" } }
assert_not_predicate company.clients_of_firm, :loaded?
company.name += "-changed"
@@ -687,10 +693,7 @@ class TestDefaultAutosaveAssociationOnAHasManyAssociation < ActiveRecord::TestCa
def test_build_many_via_block_before_save
company = companies(:first_firm)
- # Load schema information so we don't query below if running just this test.
- Client.define_attribute_methods
-
- assert_no_queries do
+ assert_queries(0) do
company.clients_of_firm.build([{ "name" => "Another Client" }, { "name" => "Another Client II" }]) do |client|
client.name = "changed"
end
@@ -1685,6 +1688,10 @@ class TestAutosaveAssociationValidationsOnAHasManyAssociation < ActiveRecord::Te
super
@pirate = Pirate.create(catchphrase: "Don' botharrr talkin' like one, savvy?")
@pirate.birds.create(name: "cookoo")
+
+ @author = Author.new(name: "DHH")
+ @author.published_books.build(name: "Rework", isbn: "1234")
+ @author.published_books.build(name: "Remote", isbn: "1234")
end
test "should automatically validate associations" do
@@ -1693,6 +1700,42 @@ class TestAutosaveAssociationValidationsOnAHasManyAssociation < ActiveRecord::Te
assert_not_predicate @pirate, :valid?
end
+
+ test "rollbacks whole transaction and raises ActiveRecord::RecordInvalid when associations fail to #save! due to uniqueness validation failure" do
+ author_count_before_save = Author.count
+ book_count_before_save = Book.count
+
+ assert_no_difference "Author.count" do
+ assert_no_difference "Book.count" do
+ exception = assert_raises(ActiveRecord::RecordInvalid) do
+ @author.save!
+ end
+
+ assert_equal("Validation failed: Published books is invalid", exception.message)
+ end
+ end
+
+ assert_equal(author_count_before_save, Author.count)
+ assert_equal(book_count_before_save, Book.count)
+ end
+
+ test "rollbacks whole transaction when associations fail to #save due to uniqueness validation failure" do
+ author_count_before_save = Author.count
+ book_count_before_save = Book.count
+
+ assert_no_difference "Author.count" do
+ assert_no_difference "Book.count" do
+ assert_nothing_raised do
+ result = @author.save
+
+ assert_not(result)
+ end
+ end
+ end
+
+ assert_equal(author_count_before_save, Author.count)
+ assert_equal(book_count_before_save, Book.count)
+ end
end
class TestAutosaveAssociationValidationsOnAHasOneAssociation < ActiveRecord::TestCase
diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb
index 99f47cfe37..1324bdf9b8 100644
--- a/activerecord/test/cases/base_test.rb
+++ b/activerecord/test/cases/base_test.rb
@@ -67,6 +67,16 @@ end
class BasicsTest < ActiveRecord::TestCase
fixtures :topics, :companies, :developers, :projects, :computers, :accounts, :minimalistics, "warehouse-things", :authors, :author_addresses, :categorizations, :categories, :posts
+ def test_generated_association_methods_module_name
+ mod = Post.send(:generated_association_methods)
+ assert_equal "Post::GeneratedAssociationMethods", mod.inspect
+ end
+
+ def test_generated_relation_methods_module_name
+ mod = Post.send(:generated_relation_methods)
+ assert_equal "Post::GeneratedRelationMethods", mod.inspect
+ end
+
def test_column_names_are_escaped
conn = ActiveRecord::Base.connection
classname = conn.class.name[/[^:]*$/]
@@ -1131,11 +1141,14 @@ class BasicsTest < ActiveRecord::TestCase
def test_clear_cache!
# preheat cache
c1 = Post.connection.schema_cache.columns("posts")
+ assert_not_equal 0, Post.connection.schema_cache.size
+
ActiveRecord::Base.clear_cache!
+ assert_equal 0, Post.connection.schema_cache.size
+
c2 = Post.connection.schema_cache.columns("posts")
- c1.each_with_index do |v, i|
- assert_not_same v, c2[i]
- end
+ assert_not_equal 0, Post.connection.schema_cache.size
+
assert_equal c1, c2
end
@@ -1205,6 +1218,8 @@ class BasicsTest < ActiveRecord::TestCase
wr.close
assert Marshal.load rd.read
rd.close
+ ensure
+ self.class.send(:remove_const, "Post") if self.class.const_defined?("Post", false)
end
end
@@ -1400,6 +1415,14 @@ class BasicsTest < ActiveRecord::TestCase
assert_not_includes SymbolIgnoredDeveloper.columns_hash.keys, "first_name"
end
+ test ".columns_hash raises an error if the record has an empty table name" do
+ expected_message = "FirstAbstractClass has no table configured. Set one with FirstAbstractClass.table_name="
+ exception = assert_raises(ActiveRecord::TableNotSpecified) do
+ FirstAbstractClass.columns_hash
+ end
+ assert_equal expected_message, exception.message
+ end
+
test "ignored columns have no attribute methods" do
assert_not_respond_to Developer.new, :first_name
assert_not_respond_to Developer.new, :first_name=
@@ -1484,7 +1507,7 @@ class BasicsTest < ActiveRecord::TestCase
test "creating a record raises if preventing writes" do
error = assert_raises ActiveRecord::ReadOnlyError do
- ActiveRecord::Base.connection.while_preventing_writes do
+ ActiveRecord::Base.connection_handler.while_preventing_writes do
Bird.create! name: "Bluejay"
end
end
@@ -1496,7 +1519,7 @@ class BasicsTest < ActiveRecord::TestCase
bird = Bird.create! name: "Bluejay"
error = assert_raises ActiveRecord::ReadOnlyError do
- ActiveRecord::Base.connection.while_preventing_writes do
+ ActiveRecord::Base.connection_handler.while_preventing_writes do
bird.update! name: "Robin"
end
end
@@ -1508,7 +1531,7 @@ class BasicsTest < ActiveRecord::TestCase
bird = Bird.create! name: "Bluejay"
error = assert_raises ActiveRecord::ReadOnlyError do
- ActiveRecord::Base.connection.while_preventing_writes do
+ ActiveRecord::Base.connection_handler.while_preventing_writes do
bird.destroy!
end
end
@@ -1519,7 +1542,7 @@ class BasicsTest < ActiveRecord::TestCase
test "selecting a record does not raise if preventing writes" do
bird = Bird.create! name: "Bluejay"
- ActiveRecord::Base.connection.while_preventing_writes do
+ ActiveRecord::Base.connection_handler.while_preventing_writes do
assert_equal bird, Bird.where(name: "Bluejay").first
end
end
@@ -1527,13 +1550,13 @@ class BasicsTest < ActiveRecord::TestCase
test "an explain query does not raise if preventing writes" do
Bird.create!(name: "Bluejay")
- ActiveRecord::Base.connection.while_preventing_writes do
+ ActiveRecord::Base.connection_handler.while_preventing_writes do
assert_queries(2) { Bird.where(name: "Bluejay").explain }
end
end
test "an empty transaction does not raise if preventing writes" do
- ActiveRecord::Base.connection.while_preventing_writes do
+ ActiveRecord::Base.connection_handler.while_preventing_writes do
assert_queries(2, ignore_none: true) do
Bird.transaction do
ActiveRecord::Base.connection.materialize_transactions
@@ -1541,4 +1564,59 @@ class BasicsTest < ActiveRecord::TestCase
end
end
end
+
+ test "preventing writes applies to all connections on a handler" do
+ conn1_error = assert_raises ActiveRecord::ReadOnlyError do
+ ActiveRecord::Base.connection_handler.while_preventing_writes do
+ assert_equal ActiveRecord::Base.connection, Bird.connection
+ assert_not_equal ARUnit2Model.connection, Bird.connection
+ Bird.create!(name: "Bluejay")
+ end
+ end
+
+ assert_match %r/\AWrite query attempted while in readonly mode: INSERT /, conn1_error.message
+
+ conn2_error = assert_raises ActiveRecord::ReadOnlyError do
+ ActiveRecord::Base.connection_handler.while_preventing_writes do
+ assert_not_equal ActiveRecord::Base.connection, Professor.connection
+ assert_equal ARUnit2Model.connection, Professor.connection
+ Professor.create!(name: "Professor Bluejay")
+ end
+ end
+
+ assert_match %r/\AWrite query attempted while in readonly mode: INSERT /, conn2_error.message
+ end
+
+ unless in_memory_db?
+ test "preventing writes with multiple handlers" do
+ ActiveRecord::Base.connects_to(database: { writing: :arunit, reading: :arunit })
+
+ conn1_error = assert_raises ActiveRecord::ReadOnlyError do
+ ActiveRecord::Base.connected_to(role: :writing) do
+ assert_equal :writing, ActiveRecord::Base.current_role
+
+ ActiveRecord::Base.connection_handler.while_preventing_writes do
+ Bird.create!(name: "Bluejay")
+ end
+ end
+ end
+
+ assert_match %r/\AWrite query attempted while in readonly mode: INSERT /, conn1_error.message
+
+ conn2_error = assert_raises ActiveRecord::ReadOnlyError do
+ ActiveRecord::Base.connected_to(role: :reading) do
+ assert_equal :reading, ActiveRecord::Base.current_role
+
+ ActiveRecord::Base.connection_handler.while_preventing_writes do
+ Bird.create!(name: "Bluejay")
+ end
+ end
+ end
+
+ assert_match %r/\AWrite query attempted while in readonly mode: INSERT /, conn2_error.message
+ ensure
+ ActiveRecord::Base.connection_handlers = { writing: ActiveRecord::Base.default_connection_handler }
+ ActiveRecord::Base.establish_connection(:arunit)
+ end
+ end
end
diff --git a/activerecord/test/cases/batches_test.rb b/activerecord/test/cases/batches_test.rb
index cf6e280898..0d0bf39f79 100644
--- a/activerecord/test/cases/batches_test.rb
+++ b/activerecord/test/cases/batches_test.rb
@@ -146,7 +146,7 @@ class EachTest < ActiveRecord::TestCase
def test_find_in_batches_should_quote_batch_order
c = Post.connection
- assert_sql(/ORDER BY #{c.quote_table_name('posts')}\.#{c.quote_column_name('id')}/) do
+ assert_sql(/ORDER BY #{Regexp.escape(c.quote_table_name("posts.id"))}/i) do
Post.find_in_batches(batch_size: 1) do |batch|
assert_kind_of Array, batch
assert_kind_of Post, batch.first
diff --git a/activerecord/test/cases/bind_parameter_test.rb b/activerecord/test/cases/bind_parameter_test.rb
index 85685d1d00..720446b39d 100644
--- a/activerecord/test/cases/bind_parameter_test.rb
+++ b/activerecord/test/cases/bind_parameter_test.rb
@@ -93,7 +93,7 @@ if ActiveRecord::Base.connection.prepared_statements
def test_statement_cache_with_in_clause
@connection.clear_cache!
- topics = Topic.where(id: [1, 3])
+ topics = Topic.where(id: [1, 3]).order(:id)
assert_equal [1, 3], topics.map(&:id)
assert_not_includes statement_cache, to_sql_key(topics.arel)
end
diff --git a/activerecord/test/cases/calculations_test.rb b/activerecord/test/cases/calculations_test.rb
index 16c2a3661d..dbd1d03c4c 100644
--- a/activerecord/test/cases/calculations_test.rb
+++ b/activerecord/test/cases/calculations_test.rb
@@ -139,6 +139,13 @@ class CalculationsTest < ActiveRecord::TestCase
end
end
+ def test_should_not_use_alias_for_grouped_field
+ assert_sql(/GROUP BY #{Regexp.escape(Account.connection.quote_table_name("accounts.firm_id"))}/i) do
+ c = Account.group(:firm_id).order("accounts_firm_id").sum(:credit_limit)
+ assert_equal [1, 2, 6, 9], c.keys.compact
+ end
+ end
+
def test_should_order_by_grouped_field
c = Account.group(:firm_id).order("firm_id").sum(:credit_limit)
assert_equal [1, 2, 6, 9], c.keys.compact
@@ -185,7 +192,7 @@ class CalculationsTest < ActiveRecord::TestCase
def test_limit_is_kept
return if current_adapter?(:OracleAdapter)
- queries = assert_sql { Account.limit(1).count }
+ queries = capture_sql { Account.limit(1).count }
assert_equal 1, queries.length
assert_match(/LIMIT/, queries.first)
end
@@ -193,7 +200,7 @@ class CalculationsTest < ActiveRecord::TestCase
def test_offset_is_kept
return if current_adapter?(:OracleAdapter)
- queries = assert_sql { Account.offset(1).count }
+ queries = capture_sql { Account.offset(1).count }
assert_equal 1, queries.length
assert_match(/OFFSET/, queries.first)
end
@@ -201,14 +208,14 @@ class CalculationsTest < ActiveRecord::TestCase
def test_limit_with_offset_is_kept
return if current_adapter?(:OracleAdapter)
- queries = assert_sql { Account.limit(1).offset(1).count }
+ queries = capture_sql { Account.limit(1).offset(1).count }
assert_equal 1, queries.length
assert_match(/LIMIT/, queries.first)
assert_match(/OFFSET/, queries.first)
end
def test_no_limit_no_offset
- queries = assert_sql { Account.count }
+ queries = capture_sql { Account.count }
assert_equal 1, queries.length
assert_no_match(/LIMIT/, queries.first)
assert_no_match(/OFFSET/, queries.first)
@@ -224,15 +231,12 @@ class CalculationsTest < ActiveRecord::TestCase
end
def test_apply_distinct_in_count
- queries = assert_sql do
+ queries = capture_sql do
Account.distinct.count
Account.group(:firm_id).distinct.count
end
queries.each do |query|
- # `table_alias_length` in `column_alias_for` would execute
- # "SHOW max_identifier_length" statement in PostgreSQL adapter.
- next if query == "SHOW max_identifier_length"
assert_match %r{\ASELECT(?! DISTINCT) COUNT\(DISTINCT\b}, query
end
end
@@ -464,7 +468,7 @@ class CalculationsTest < ActiveRecord::TestCase
def test_should_not_perform_joined_include_by_default
assert_equal Account.count, Account.includes(:firm).count
- queries = assert_sql { Account.includes(:firm).count }
+ queries = capture_sql { Account.includes(:firm).count }
assert_no_match(/join/i, queries.last)
end
@@ -592,11 +596,7 @@ class CalculationsTest < ActiveRecord::TestCase
end
def test_should_sum_expression
- if current_adapter?(:SQLite3Adapter, :Mysql2Adapter, :PostgreSQLAdapter, :OracleAdapter)
- assert_equal 636, Account.sum("2 * credit_limit")
- else
- assert_equal 636, Account.sum("2 * credit_limit").to_i
- end
+ assert_equal 636, Account.sum("2 * credit_limit")
end
def test_sum_expression_returns_zero_when_no_records_to_sum
@@ -774,6 +774,12 @@ class CalculationsTest < ActiveRecord::TestCase
assert_equal [[2, 2], [4, 4]], Reply.includes(:topic).pluck(:id, :"topics.id")
end
+ def test_group_by_with_order_by_virtual_count_attribute
+ expected = { "SpecialPost" => 1, "StiPost" => 2 }
+ actual = Post.group(:type).order(:count).limit(2).maximum(:comments_count)
+ assert_equal expected, actual
+ end if current_adapter?(:PostgreSQLAdapter)
+
def test_group_by_with_limit
expected = { "Post" => 8, "SpecialPost" => 1 }
actual = Post.includes(:comments).group(:type).order(:type).limit(2).count("comments.id")
@@ -840,7 +846,7 @@ class CalculationsTest < ActiveRecord::TestCase
def test_pluck_columns_with_same_name
expected = [["The First Topic", "The Second Topic of the day"], ["The Third Topic of the day", "The Fourth Topic of the day"]]
- actual = Topic.joins(:replies)
+ actual = Topic.joins(:replies).order(:id)
.pluck("topics.title", "replies_topics.title")
assert_equal expected, actual
end
@@ -860,28 +866,25 @@ class CalculationsTest < ActiveRecord::TestCase
end
def test_pluck_loaded_relation
- Company.attribute_names # Load schema information so we don't query below
companies = Company.order(:id).limit(3).load
- assert_no_queries do
+ assert_queries(0) do
assert_equal ["37signals", "Summit", "Microsoft"], companies.pluck(:name)
end
end
def test_pluck_loaded_relation_multiple_columns
- Company.attribute_names # Load schema information so we don't query below
companies = Company.order(:id).limit(3).load
- assert_no_queries do
+ assert_queries(0) do
assert_equal [[1, "37signals"], [2, "Summit"], [3, "Microsoft"]], companies.pluck(:id, :name)
end
end
def test_pluck_loaded_relation_sql_fragment
- Company.attribute_names # Load schema information so we don't query below
companies = Company.order(:name).limit(3).load
- assert_queries 1 do
+ assert_queries(1) do
assert_equal ["37signals", "Apex", "Ex Nihilo"], companies.pluck(Arel.sql("DISTINCT name"))
end
end
diff --git a/activerecord/test/cases/collection_cache_key_test.rb b/activerecord/test/cases/collection_cache_key_test.rb
index 483383257b..f07f3c42e6 100644
--- a/activerecord/test/cases/collection_cache_key_test.rb
+++ b/activerecord/test/cases/collection_cache_key_test.rb
@@ -171,5 +171,39 @@ module ActiveRecord
assert_match(/\Adevelopers\/query-(\h+)-(\d+)-(\d+)\z/, developers.cache_key)
end
+
+ test "cache_key should be stable when using collection_cache_versioning" do
+ with_collection_cache_versioning do
+ developers = Developer.where(salary: 100000)
+
+ assert_match(/\Adevelopers\/query-(\h+)\z/, developers.cache_key)
+
+ /\Adevelopers\/query-(\h+)\z/ =~ developers.cache_key
+
+ assert_equal ActiveSupport::Digest.hexdigest(developers.to_sql), $1
+ end
+ end
+
+ test "cache_version for relation" do
+ with_collection_cache_versioning do
+ developers = Developer.where(salary: 100000).order(updated_at: :desc)
+ last_developer_timestamp = developers.first.updated_at
+
+ assert_match(/(\d+)-(\d+)\z/, developers.cache_version)
+
+ /(\d+)-(\d+)\z/ =~ developers.cache_version
+
+ assert_equal developers.count.to_s, $1
+ assert_equal last_developer_timestamp.to_s(ActiveRecord::Base.cache_timestamp_format), $2
+ end
+ end
+
+ def with_collection_cache_versioning(value = true)
+ @old_collection_cache_versioning = ActiveRecord::Base.collection_cache_versioning
+ ActiveRecord::Base.collection_cache_versioning = value
+ yield
+ ensure
+ ActiveRecord::Base.collection_cache_versioning = @old_collection_cache_versioning
+ end
end
end
diff --git a/activerecord/test/cases/comment_test.rb b/activerecord/test/cases/comment_test.rb
index 584e03d196..25e2f20676 100644
--- a/activerecord/test/cases/comment_test.rb
+++ b/activerecord/test/cases/comment_test.rb
@@ -14,6 +14,9 @@ if ActiveRecord::Base.connection.supports_comments?
class BlankComment < ActiveRecord::Base
end
+ class PkCommented < ActiveRecord::Base
+ end
+
setup do
@connection = ActiveRecord::Base.connection
@@ -35,8 +38,13 @@ if ActiveRecord::Base.connection.supports_comments?
t.index :absent_comment
end
+ @connection.create_table("pk_commenteds", comment: "Table comment", id: false, force: true) do |t|
+ t.integer :id, comment: "Primary key comment", primary_key: true
+ end
+
Commented.reset_column_information
BlankComment.reset_column_information
+ PkCommented.reset_column_information
end
teardown do
@@ -44,6 +52,11 @@ if ActiveRecord::Base.connection.supports_comments?
@connection.drop_table "blank_comments", if_exists: true
end
+ def test_default_primary_key_comment
+ column = Commented.columns_hash["id"]
+ assert_nil column.comment
+ end
+
def test_column_created_in_block
column = Commented.columns_hash["name"]
assert_equal :string, column.type
@@ -164,5 +177,17 @@ if ActiveRecord::Base.connection.supports_comments?
column = Commented.columns_hash["name"]
assert_nil column.comment
end
+
+ def test_comment_on_primary_key
+ column = PkCommented.columns_hash["id"]
+ assert_equal "Primary key comment", column.comment
+ assert_equal "Table comment", @connection.table_comment("pk_commenteds")
+ end
+
+ def test_schema_dump_with_primary_key_comment
+ output = dump_table_schema "pk_commenteds"
+ assert_match %r[create_table "pk_commenteds",.*\s+comment: "Table comment"], output
+ assert_no_match %r[create_table "pk_commenteds",.*\s+comment: "Primary key comment"], 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 6282759a10..843242a897 100644
--- a/activerecord/test/cases/connection_adapters/connection_handler_test.rb
+++ b/activerecord/test/cases/connection_adapters/connection_handler_test.rb
@@ -29,7 +29,7 @@ module ActiveRecord
def test_establish_connection_uses_spec_name
old_config = ActiveRecord::Base.configurations
- config = { "readonly" => { "adapter" => "sqlite3" } }
+ config = { "readonly" => { "adapter" => "sqlite3", "pool" => "5" } }
ActiveRecord::Base.configurations = config
resolver = ConnectionAdapters::ConnectionSpecification::Resolver.new(ActiveRecord::Base.configurations)
spec = resolver.spec(:readonly)
@@ -367,11 +367,24 @@ module ActiveRecord
assert_same klass2.connection, ActiveRecord::Base.connection
end
+ class ApplicationRecord < ActiveRecord::Base
+ self.abstract_class = true
+ end
+
+ class MyClass < ApplicationRecord
+ end
+
def test_connection_specification_name_should_fallback_to_parent
klassA = Class.new(Base)
klassB = Class.new(klassA)
+ klassC = Class.new(MyClass)
assert_equal klassB.connection_specification_name, klassA.connection_specification_name
+ assert_equal klassC.connection_specification_name, klassA.connection_specification_name
+
+ assert_equal "primary", klassA.connection_specification_name
+ assert_equal "primary", klassC.connection_specification_name
+
klassA.connection_specification_name = "readonly"
assert_equal "readonly", klassB.connection_specification_name
end
diff --git a/activerecord/test/cases/connection_adapters/connection_handlers_multi_db_test.rb b/activerecord/test/cases/connection_adapters/connection_handlers_multi_db_test.rb
index a2d289bf2f..d3184f39f5 100644
--- a/activerecord/test/cases/connection_adapters/connection_handlers_multi_db_test.rb
+++ b/activerecord/test/cases/connection_adapters/connection_handlers_multi_db_test.rb
@@ -209,7 +209,7 @@ module ActiveRecord
config = {
"default_env" => {
"animals" => { adapter: "sqlite3", database: "db/animals.sqlite3" },
- "primary" => { adapter: "sqlite3", database: "db/primary.sqlite3" }
+ "primary" => { adapter: "sqlite3", database: "db/primary.sqlite3" }
}
}
@prev_configs, ActiveRecord::Base.configurations = ActiveRecord::Base.configurations, config
@@ -236,7 +236,7 @@ module ActiveRecord
config = {
"default_env" => {
"animals" => { adapter: "sqlite3", database: "db/animals.sqlite3" },
- "primary" => { adapter: "sqlite3", database: "db/primary.sqlite3" }
+ "primary" => { adapter: "sqlite3", database: "db/primary.sqlite3" }
}
}
@prev_configs, ActiveRecord::Base.configurations = ActiveRecord::Base.configurations, config
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 515bf5df06..95e57f42e3 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
@@ -244,6 +244,25 @@ module ActiveRecord
assert_equal expected, actual
end
+ def test_no_url_sub_key_with_database_url_doesnt_trample_other_envs
+ ENV["DATABASE_URL"] = "postgres://localhost/baz"
+
+ config = { "default_env" => { "database" => "foo" }, "other_env" => { "url" => "postgres://foohost/bardb" } }
+ actual = resolve_config(config)
+ expected = { "default_env" =>
+ { "database" => "baz",
+ "adapter" => "postgresql",
+ "host" => "localhost"
+ },
+ "other_env" =>
+ { "adapter" => "postgresql",
+ "database" => "bardb",
+ "host" => "foohost"
+ }
+ }
+ assert_equal expected, actual
+ end
+
def test_merge_no_conflicts_with_database_url
ENV["DATABASE_URL"] = "postgres://localhost/foo"
@@ -273,6 +292,77 @@ module ActiveRecord
}
assert_equal expected, actual
end
+
+ def test_merge_no_conflicts_with_database_url_and_adapter
+ ENV["DATABASE_URL"] = "postgres://localhost/foo"
+
+ config = { "default_env" => { "adapter" => "postgresql", "pool" => "5" } }
+ actual = resolve_config(config)
+ expected = { "default_env" =>
+ { "adapter" => "postgresql",
+ "database" => "foo",
+ "host" => "localhost",
+ "pool" => "5"
+ }
+ }
+ assert_equal expected, actual
+ end
+
+ def test_merge_no_conflicts_with_database_url_and_numeric_pool
+ ENV["DATABASE_URL"] = "postgres://localhost/foo"
+
+ config = { "default_env" => { "pool" => 5 } }
+ actual = resolve_config(config)
+ expected = { "default_env" =>
+ { "adapter" => "postgresql",
+ "database" => "foo",
+ "host" => "localhost",
+ "pool" => 5
+ }
+ }
+
+ assert_equal expected, actual
+ end
+
+ def test_tiered_configs_with_database_url
+ ENV["DATABASE_URL"] = "postgres://localhost/foo"
+
+ config = {
+ "default_env" => {
+ "primary" => { "pool" => 5 },
+ "animals" => { "pool" => 5 }
+ }
+ }
+
+ expected = {
+ "adapter" => "postgresql",
+ "database" => "foo",
+ "host" => "localhost",
+ "pool" => 5
+ }
+
+ ["primary", "animals"].each do |spec_name|
+ configs = ActiveRecord::DatabaseConfigurations.new(config)
+ actual = configs.configs_for(env_name: "default_env", spec_name: spec_name).config
+ assert_equal expected, actual
+ end
+ end
+
+ def test_does_not_change_other_environments
+ ENV["DATABASE_URL"] = "postgres://localhost/foo"
+ config = { "production" => { "adapter" => "not_postgres", "database" => "not_foo", "host" => "localhost" }, "default_env" => {} }
+
+ actual = resolve_spec(:production, config)
+ assert_equal config["production"].merge("name" => "production"), actual
+
+ actual = resolve_spec(:default_env, config)
+ assert_equal({
+ "host" => "localhost",
+ "database" => "foo",
+ "adapter" => "postgresql",
+ "name" => "default_env"
+ }, actual)
+ end
end
end
end
diff --git a/activerecord/test/cases/connection_adapters/mysql_type_lookup_test.rb b/activerecord/test/cases/connection_adapters/mysql_type_lookup_test.rb
index 38331aa641..774380d7e0 100644
--- a/activerecord/test/cases/connection_adapters/mysql_type_lookup_test.rb
+++ b/activerecord/test/cases/connection_adapters/mysql_type_lookup_test.rb
@@ -40,7 +40,7 @@ if current_adapter?(:Mysql2Adapter)
end
def test_enum_type_with_value_matching_other_type
- assert_lookup_type :string, "ENUM('unicode', '8bit', 'none')"
+ assert_lookup_type :string, "ENUM('unicode', '8bit', 'none', 'time')"
end
def test_binary_types
@@ -58,7 +58,6 @@ if current_adapter?(:Mysql2Adapter)
end
private
-
def assert_lookup_type(type, lookup)
cast_type = @connection.send(:type_map).lookup(lookup)
assert_equal type, cast_type.type
diff --git a/activerecord/test/cases/connection_adapters/schema_cache_test.rb b/activerecord/test/cases/connection_adapters/schema_cache_test.rb
index 89a9c30f9b..28e232b88f 100644
--- a/activerecord/test/cases/connection_adapters/schema_cache_test.rb
+++ b/activerecord/test/cases/connection_adapters/schema_cache_test.rb
@@ -95,6 +95,10 @@ module ActiveRecord
assert_no_queries do
assert_equal @database_version.to_s, @cache.database_version.to_s
+
+ if current_adapter?(:Mysql2Adapter)
+ assert_not_nil @cache.database_version.full_version_string
+ end
end
end
diff --git a/activerecord/test/cases/connection_adapters/type_lookup_test.rb b/activerecord/test/cases/connection_adapters/type_lookup_test.rb
index 1c79d776f0..e92bb40632 100644
--- a/activerecord/test/cases/connection_adapters/type_lookup_test.rb
+++ b/activerecord/test/cases/connection_adapters/type_lookup_test.rb
@@ -109,7 +109,6 @@ unless current_adapter?(:PostgreSQLAdapter) # PostgreSQL does not use type strin
end
private
-
def assert_lookup_type(type, lookup)
cast_type = @connection.send(:type_map).lookup(lookup)
assert_equal type, cast_type.type
diff --git a/activerecord/test/cases/connection_pool_test.rb b/activerecord/test/cases/connection_pool_test.rb
index a15ad9a45b..ccbb6e16cd 100644
--- a/activerecord/test/cases/connection_pool_test.rb
+++ b/activerecord/test/cases/connection_pool_test.rb
@@ -507,7 +507,6 @@ module ActiveRecord
pool.schema_cache = schema_cache
pool.with_connection do |conn|
- assert_not_same pool.schema_cache, conn.schema_cache
assert_equal pool.schema_cache.size, conn.schema_cache.size
assert_same pool.schema_cache.columns(:posts), conn.schema_cache.columns(:posts)
end
@@ -695,6 +694,28 @@ module ActiveRecord
end
end
+ def test_public_connections_access_threadsafe
+ _conn1 = @pool.checkout
+ conn2 = @pool.checkout
+
+ connections = @pool.connections
+ found_conn = nil
+
+ # Without assuming too much about implementation
+ # details make sure that a concurrent change to
+ # the pool is thread-safe.
+ connections.each_index do |idx|
+ if connections[idx] == conn2
+ Thread.new do
+ @pool.remove(conn2)
+ end.join
+ end
+ found_conn = connections[idx]
+ end
+
+ assert_not_nil found_conn
+ end
+
private
def with_single_connection_pool
one_conn_spec = ActiveRecord::Base.connection_pool.spec.dup
diff --git a/activerecord/test/cases/database_statements_test.rb b/activerecord/test/cases/database_statements_test.rb
index 1c934602ec..119d48b85e 100644
--- a/activerecord/test/cases/database_statements_test.rb
+++ b/activerecord/test/cases/database_statements_test.rb
@@ -23,7 +23,6 @@ class DatabaseStatementsTest < ActiveRecord::TestCase
end
private
-
def return_the_inserted_id(method:)
# Oracle adapter uses prefetched primary key values from sequence and passes them to connection adapter insert method
if current_adapter?(:OracleAdapter)
diff --git a/activerecord/test/cases/enum_test.rb b/activerecord/test/cases/enum_test.rb
index ae0ce195b3..8673a99c45 100644
--- a/activerecord/test/cases/enum_test.rb
+++ b/activerecord/test/cases/enum_test.rb
@@ -3,6 +3,7 @@
require "cases/helper"
require "models/author"
require "models/book"
+require "active_support/log_subscriber/test_helper"
class EnumTest < ActiveRecord::TestCase
fixtures :books, :authors, :author_addresses
@@ -565,4 +566,25 @@ class EnumTest < ActiveRecord::TestCase
assert_raises(NoMethodError) { klass.proposed }
end
+
+ test "enums with a negative condition log a warning" do
+ old_logger = ActiveRecord::Base.logger
+ logger = ActiveSupport::LogSubscriber::TestHelper::MockLogger.new
+
+ ActiveRecord::Base.logger = logger
+
+ expected_message = "An enum element in Book uses the prefix 'not_'."\
+ " This will cause a conflict with auto generated negative scopes."
+
+ Class.new(ActiveRecord::Base) do
+ def self.name
+ "Book"
+ end
+ enum status: [:sent, :not_sent]
+ end
+
+ assert_match(expected_message, logger.logged(:warn).first)
+ ensure
+ ActiveRecord::Base.logger = old_logger
+ end
end
diff --git a/activerecord/test/cases/explain_test.rb b/activerecord/test/cases/explain_test.rb
index a0e75f4e89..edd2c768d3 100644
--- a/activerecord/test/cases/explain_test.rb
+++ b/activerecord/test/cases/explain_test.rb
@@ -72,7 +72,6 @@ if ActiveRecord::Base.connection.supports_explain?
end
private
-
def stub_explain_for_query_plans(query_plans = ["query plan foo", "query plan bar"])
explain_called = 0
diff --git a/activerecord/test/cases/finder_respond_to_test.rb b/activerecord/test/cases/finder_respond_to_test.rb
index 66413a98e4..d9e88b3feb 100644
--- a/activerecord/test/cases/finder_respond_to_test.rb
+++ b/activerecord/test/cases/finder_respond_to_test.rb
@@ -54,7 +54,6 @@ class FinderRespondToTest < ActiveRecord::TestCase
end
private
-
def ensure_topic_method_is_not_cached(method_id)
Topic.singleton_class.remove_method method_id if Topic.public_methods.include? method_id
end
diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb
index ca114d468e..1f2058cc0a 100644
--- a/activerecord/test/cases/finder_test.rb
+++ b/activerecord/test/cases/finder_test.rb
@@ -245,7 +245,8 @@ class FinderTest < ActiveRecord::TestCase
end
def test_exists_does_not_select_columns_without_alias
- assert_sql(/SELECT\W+1 AS one FROM ["`]topics["`]/i) do
+ c = Topic.connection
+ assert_sql(/SELECT 1 AS one FROM #{Regexp.escape(c.quote_table_name("topics"))}/i) do
Topic.exists?
end
end
@@ -282,6 +283,11 @@ class FinderTest < ActiveRecord::TestCase
assert_not Post.select(:body).distinct.offset(4).exists?
end
+ def test_exists_with_distinct_and_offset_and_eagerload_and_order
+ assert Post.eager_load(:comments).distinct.offset(10).merge(Comment.order(post_id: :asc)).exists?
+ assert_not Post.eager_load(:comments).distinct.offset(11).merge(Comment.order(post_id: :asc)).exists?
+ end
+
# Ensure +exists?+ runs without an error by excluding distinct value.
# See https://github.com/rails/rails/pull/26981.
def test_exists_with_order_and_distinct
@@ -517,6 +523,7 @@ class FinderTest < ActiveRecord::TestCase
expected.touch # PostgreSQL changes the default order if no order clause is used
assert_equal expected, Topic.first
assert_equal expected, Topic.limit(5).first
+ assert_equal expected, Topic.order(nil).first
end
def test_model_class_responds_to_first_bang
@@ -540,6 +547,7 @@ class FinderTest < ActiveRecord::TestCase
expected.touch # PostgreSQL changes the default order if no order clause is used
assert_equal expected, Topic.second
assert_equal expected, Topic.limit(5).second
+ assert_equal expected, Topic.order(nil).second
end
def test_model_class_responds_to_second_bang
@@ -563,6 +571,7 @@ class FinderTest < ActiveRecord::TestCase
expected.touch # PostgreSQL changes the default order if no order clause is used
assert_equal expected, Topic.third
assert_equal expected, Topic.limit(5).third
+ assert_equal expected, Topic.order(nil).third
end
def test_model_class_responds_to_third_bang
@@ -586,6 +595,7 @@ class FinderTest < ActiveRecord::TestCase
expected.touch # PostgreSQL changes the default order if no order clause is used
assert_equal expected, Topic.fourth
assert_equal expected, Topic.limit(5).fourth
+ assert_equal expected, Topic.order(nil).fourth
end
def test_model_class_responds_to_fourth_bang
@@ -609,6 +619,7 @@ class FinderTest < ActiveRecord::TestCase
expected.touch # PostgreSQL changes the default order if no order clause is used
assert_equal expected, Topic.fifth
assert_equal expected, Topic.limit(5).fifth
+ assert_equal expected, Topic.order(nil).fifth
end
def test_model_class_responds_to_fifth_bang
@@ -777,6 +788,7 @@ class FinderTest < ActiveRecord::TestCase
assert_equal expected, clients.first(2)
assert_equal expected, clients.limit(5).first(2)
+ assert_equal expected, clients.order(nil).first(2)
end
def test_implicit_order_column_is_configurable
diff --git a/activerecord/test/cases/fixtures_test.rb b/activerecord/test/cases/fixtures_test.rb
index 0cb868da6e..a7f01e898e 100644
--- a/activerecord/test/cases/fixtures_test.rb
+++ b/activerecord/test/cases/fixtures_test.rb
@@ -948,7 +948,6 @@ class TransactionalFixturesOnConnectionNotification < ActiveRecord::TestCase
end
private
-
def fire_connection_notification(connection)
assert_called_with(ActiveRecord::Base.connection_handler, :retrieve_connection, ["book"], returns: connection) do
message_bus = ActiveSupport::Notifications.instrumenter
@@ -1367,7 +1366,6 @@ class MultipleDatabaseFixturesTest < ActiveRecord::TestCase
end
private
-
def with_temporary_connection_pool
old_pool = ActiveRecord::Base.connection_handler.retrieve_connection_pool(ActiveRecord::Base.connection_specification_name)
new_pool = ActiveRecord::ConnectionAdapters::ConnectionPool.new ActiveRecord::Base.connection_pool.spec
diff --git a/activerecord/test/cases/helper.rb b/activerecord/test/cases/helper.rb
index 543a0aeb39..56c780c4a6 100644
--- a/activerecord/test/cases/helper.rb
+++ b/activerecord/test/cases/helper.rb
@@ -189,7 +189,6 @@ end
module InTimeZone
private
-
def in_time_zone(zone)
old_zone = Time.zone
old_tz = ActiveRecord::Base.time_zone_aware_attributes
diff --git a/activerecord/test/cases/hot_compatibility_test.rb b/activerecord/test/cases/hot_compatibility_test.rb
index 7b388ebc5e..f41aea6125 100644
--- a/activerecord/test/cases/hot_compatibility_test.rb
+++ b/activerecord/test/cases/hot_compatibility_test.rb
@@ -115,7 +115,6 @@ class HotCompatibilityTest < ActiveRecord::TestCase
end
private
-
def get_prepared_statement_cache(connection)
connection.instance_variable_get(:@statements)
.instance_variable_get(:@cache)[Process.pid]
diff --git a/activerecord/test/cases/inheritance_test.rb b/activerecord/test/cases/inheritance_test.rb
index 629167e9ed..01e4878c3f 100644
--- a/activerecord/test/cases/inheritance_test.rb
+++ b/activerecord/test/cases/inheritance_test.rb
@@ -471,9 +471,9 @@ class InheritanceTest < ActiveRecord::TestCase
end
def test_eager_load_belongs_to_primary_key_quoting
- con = Account.connection
+ c = Account.connection
bind_param = Arel::Nodes::BindParam.new(nil)
- assert_sql(/#{con.quote_table_name('companies')}\.#{con.quote_column_name('id')} = (?:#{Regexp.quote(bind_param.to_sql)}|1)/) do
+ assert_sql(/#{Regexp.escape(c.quote_table_name("companies.id"))} = (?:#{Regexp.escape(bind_param.to_sql)}|1)/i) do
Account.all.merge!(includes: :firm).find(1)
end
end
diff --git a/activerecord/test/cases/insert_all_test.rb b/activerecord/test/cases/insert_all_test.rb
index f24c63031c..d086d77081 100644
--- a/activerecord/test/cases/insert_all_test.rb
+++ b/activerecord/test/cases/insert_all_test.rb
@@ -262,7 +262,6 @@ class InsertAllTest < ActiveRecord::TestCase
end
private
-
def capture_log_output
output = StringIO.new
old_logger, ActiveRecord::Base.logger = ActiveRecord::Base.logger, ActiveSupport::Logger.new(output)
diff --git a/activerecord/test/cases/json_serialization_test.rb b/activerecord/test/cases/json_serialization_test.rb
index 82cf281cff..d68e208617 100644
--- a/activerecord/test/cases/json_serialization_test.rb
+++ b/activerecord/test/cases/json_serialization_test.rb
@@ -10,7 +10,6 @@ require "models/comment"
module JsonSerializationHelpers
private
-
def set_include_root_in_json(value)
original_root_in_json = ActiveRecord::Base.include_root_in_json
ActiveRecord::Base.include_root_in_json = value
@@ -24,7 +23,7 @@ class JsonSerializationTest < ActiveRecord::TestCase
include JsonSerializationHelpers
class NamespacedContact < Contact
- column :name, :string
+ column :name, "string"
end
def setup
diff --git a/activerecord/test/cases/locking_test.rb b/activerecord/test/cases/locking_test.rb
index 04f9b26960..b468da7c76 100644
--- a/activerecord/test/cases/locking_test.rb
+++ b/activerecord/test/cases/locking_test.rb
@@ -593,7 +593,6 @@ class OptimisticLockingWithSchemaChangeTest < ActiveRecord::TestCase
end
private
-
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
diff --git a/activerecord/test/cases/migration/compatibility_test.rb b/activerecord/test/cases/migration/compatibility_test.rb
index 726ccf925e..ff2a694e66 100644
--- a/activerecord/test/cases/migration/compatibility_test.rb
+++ b/activerecord/test/cases/migration/compatibility_test.rb
@@ -12,6 +12,7 @@ module ActiveRecord
def setup
super
@connection = ActiveRecord::Base.connection
+ @schema_migration = @connection.schema_migration
@verbose_was = ActiveRecord::Migration.verbose
ActiveRecord::Migration.verbose = false
@@ -38,7 +39,7 @@ module ActiveRecord
}.new
assert connection.index_exists?(:testings, :foo, name: "custom_index_name")
- assert_raise(StandardError) { ActiveRecord::Migrator.new(:up, [migration]).migrate }
+ assert_raise(StandardError) { ActiveRecord::Migrator.new(:up, [migration], @schema_migration).migrate }
assert connection.index_exists?(:testings, :foo, name: "custom_index_name")
end
@@ -53,7 +54,7 @@ module ActiveRecord
}.new
assert connection.index_exists?(:testings, :bar)
- ActiveRecord::Migrator.new(:up, [migration]).migrate
+ ActiveRecord::Migrator.new(:up, [migration], @schema_migration).migrate
assert_not connection.index_exists?(:testings, :bar)
end
@@ -67,7 +68,7 @@ module ActiveRecord
end
}.new
- ActiveRecord::Migrator.new(:up, [migration]).migrate
+ ActiveRecord::Migrator.new(:up, [migration], @schema_migration).migrate
assert_not connection.index_exists?(:more_testings, :foo_id)
assert_not connection.index_exists?(:more_testings, :bar_id)
@@ -84,7 +85,7 @@ module ActiveRecord
end
}.new
- ActiveRecord::Migrator.new(:up, [migration]).migrate
+ ActiveRecord::Migrator.new(:up, [migration], @schema_migration).migrate
assert connection.column_exists?(:more_testings, :created_at, null: true)
assert connection.column_exists?(:more_testings, :updated_at, null: true)
@@ -101,7 +102,7 @@ module ActiveRecord
end
}.new
- ActiveRecord::Migrator.new(:up, [migration]).migrate
+ ActiveRecord::Migrator.new(:up, [migration], @schema_migration).migrate
assert connection.column_exists?(:testings, :created_at, null: true)
assert connection.column_exists?(:testings, :updated_at, null: true)
@@ -117,7 +118,7 @@ module ActiveRecord
end
}.new
- ActiveRecord::Migrator.new(:up, [migration]).migrate
+ ActiveRecord::Migrator.new(:up, [migration], @schema_migration).migrate
assert connection.column_exists?(:testings, :created_at, null: true)
assert connection.column_exists?(:testings, :updated_at, null: true)
@@ -131,7 +132,7 @@ module ActiveRecord
end
}.new
- ActiveRecord::Migrator.new(:up, [migration]).migrate
+ ActiveRecord::Migrator.new(:up, [migration], @schema_migration).migrate
assert connection.column_exists?(:testings, :created_at, null: true)
assert connection.column_exists?(:testings, :updated_at, null: true)
@@ -146,7 +147,7 @@ module ActiveRecord
end
}.new
- ActiveRecord::Migrator.new(:up, [migration]).migrate
+ ActiveRecord::Migrator.new(:up, [migration], @schema_migration).migrate
assert connection.column_exists?(:more_testings, :created_at, null: false, **precision_implicit_default)
assert connection.column_exists?(:more_testings, :updated_at, null: false, **precision_implicit_default)
@@ -163,7 +164,7 @@ module ActiveRecord
end
}.new
- ActiveRecord::Migrator.new(:up, [migration]).migrate
+ ActiveRecord::Migrator.new(:up, [migration], @schema_migration).migrate
assert connection.column_exists?(:testings, :created_at, null: false, **precision_implicit_default)
assert connection.column_exists?(:testings, :updated_at, null: false, **precision_implicit_default)
@@ -179,7 +180,7 @@ module ActiveRecord
end
}.new
- ActiveRecord::Migrator.new(:up, [migration]).migrate
+ ActiveRecord::Migrator.new(:up, [migration], @schema_migration).migrate
assert connection.column_exists?(:testings, :created_at, null: false, **precision_implicit_default)
assert connection.column_exists?(:testings, :updated_at, null: false, **precision_implicit_default)
@@ -193,7 +194,7 @@ module ActiveRecord
end
}.new
- ActiveRecord::Migrator.new(:up, [migration]).migrate
+ ActiveRecord::Migrator.new(:up, [migration], @schema_migration).migrate
assert connection.column_exists?(:testings, :created_at, null: false, **precision_implicit_default)
assert connection.column_exists?(:testings, :updated_at, null: false, **precision_implicit_default)
@@ -230,7 +231,7 @@ module ActiveRecord
end
}.new
- ActiveRecord::Migrator.new(:up, [migration]).migrate
+ ActiveRecord::Migrator.new(:up, [migration], @schema_migration).migrate
assert connection.column_exists?(:testings, :foo, comment: "comment")
end
@@ -243,7 +244,7 @@ module ActiveRecord
end
}.new
- ActiveRecord::Migrator.new(:up, [migration]).migrate
+ ActiveRecord::Migrator.new(:up, [migration], @schema_migration).migrate
assert_equal "comment", connection.table_comment("testings")
end
@@ -261,7 +262,7 @@ module ActiveRecord
}.new
Testing.create!
- ActiveRecord::Migrator.new(:up, [migration]).migrate
+ ActiveRecord::Migrator.new(:up, [migration], @schema_migration).migrate
assert_equal ["foobar"], Testing.all.map(&:foo)
ensure
ActiveRecord::Base.clear_cache!
diff --git a/activerecord/test/cases/migration/create_join_table_test.rb b/activerecord/test/cases/migration/create_join_table_test.rb
index e0cbb29dcf..0257545330 100644
--- a/activerecord/test/cases/migration/create_join_table_test.rb
+++ b/activerecord/test/cases/migration/create_join_table_test.rb
@@ -151,7 +151,6 @@ module ActiveRecord
end
private
-
def with_table_cleanup
tables_before = connection.data_sources
diff --git a/activerecord/test/cases/migration/helper.rb b/activerecord/test/cases/migration/helper.rb
index c056199140..da8bdc472a 100644
--- a/activerecord/test/cases/migration/helper.rb
+++ b/activerecord/test/cases/migration/helper.rb
@@ -34,7 +34,6 @@ module ActiveRecord
end
private
-
delegate(*CONNECTION_METHODS, to: :connection)
end
end
diff --git a/activerecord/test/cases/migration/logger_test.rb b/activerecord/test/cases/migration/logger_test.rb
index 28f4cc124b..431047f957 100644
--- a/activerecord/test/cases/migration/logger_test.rb
+++ b/activerecord/test/cases/migration/logger_test.rb
@@ -17,19 +17,20 @@ module ActiveRecord
def setup
super
- ActiveRecord::SchemaMigration.create_table
- ActiveRecord::SchemaMigration.delete_all
+ @schema_migration = ActiveRecord::Base.connection.schema_migration
+ @schema_migration.create_table
+ @schema_migration.delete_all
end
teardown do
- ActiveRecord::SchemaMigration.drop_table
+ @schema_migration.drop_table
end
def test_migration_should_be_run_without_logger
previous_logger = ActiveRecord::Base.logger
ActiveRecord::Base.logger = nil
migrations = [Migration.new("a", 1), Migration.new("b", 2), Migration.new("c", 3)]
- ActiveRecord::Migrator.new(:up, migrations).migrate
+ ActiveRecord::Migrator.new(:up, migrations, @schema_migration).migrate
ensure
ActiveRecord::Base.logger = previous_logger
end
diff --git a/activerecord/test/cases/migration/references_statements_test.rb b/activerecord/test/cases/migration/references_statements_test.rb
index 769241ba12..451894fc54 100644
--- a/activerecord/test/cases/migration/references_statements_test.rb
+++ b/activerecord/test/cases/migration/references_statements_test.rb
@@ -126,7 +126,6 @@ module ActiveRecord
end
private
-
def with_polymorphic_column
add_column table_name, :supplier_type, :string
add_index table_name, [:supplier_id, :supplier_type]
diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb
index 8e8ed494d9..20f577b2c5 100644
--- a/activerecord/test/cases/migration_test.rb
+++ b/activerecord/test/cases/migration_test.rb
@@ -38,6 +38,7 @@ class MigrationTest < ActiveRecord::TestCase
end
Reminder.reset_column_information
@verbose_was, ActiveRecord::Migration.verbose = ActiveRecord::Migration.verbose, false
+ @schema_migration = ActiveRecord::Base.connection.schema_migration
ActiveRecord::Base.connection.schema_cache.clear!
end
@@ -84,7 +85,7 @@ class MigrationTest < ActiveRecord::TestCase
def test_migrator_versions
migrations_path = MIGRATIONS_ROOT + "/valid"
- migrator = ActiveRecord::MigrationContext.new(migrations_path)
+ migrator = ActiveRecord::MigrationContext.new(migrations_path, @schema_migration)
migrator.up
assert_equal 3, migrator.current_version
@@ -102,23 +103,23 @@ class MigrationTest < ActiveRecord::TestCase
ActiveRecord::Base.connection.drop_table "schema_migrations", if_exists: true
migrations_path = MIGRATIONS_ROOT + "/valid"
- migrator = ActiveRecord::MigrationContext.new(migrations_path)
+ migrator = ActiveRecord::MigrationContext.new(migrations_path, @schema_migration)
assert_equal true, migrator.needs_migration?
end
def test_any_migrations
- migrator = ActiveRecord::MigrationContext.new(MIGRATIONS_ROOT + "/valid")
+ migrator = ActiveRecord::MigrationContext.new(MIGRATIONS_ROOT + "/valid", @schema_migration)
assert_predicate migrator, :any_migrations?
- migrator_empty = ActiveRecord::MigrationContext.new(MIGRATIONS_ROOT + "/empty")
+ migrator_empty = ActiveRecord::MigrationContext.new(MIGRATIONS_ROOT + "/empty", @schema_migration)
assert_not_predicate migrator_empty, :any_migrations?
end
def test_migration_version
- migrator = ActiveRecord::MigrationContext.new(MIGRATIONS_ROOT + "/version_check")
+ migrator = ActiveRecord::MigrationContext.new(MIGRATIONS_ROOT + "/version_check", @schema_migration)
assert_equal 0, migrator.current_version
migrator.up(20131219224947)
assert_equal 20131219224947, migrator.current_version
@@ -190,6 +191,7 @@ class MigrationTest < ActiveRecord::TestCase
assert_not_predicate BigNumber, :table_exists?
GiveMeBigNumbers.up
+ assert_predicate BigNumber, :table_exists?
BigNumber.reset_column_information
assert BigNumber.create(
@@ -248,7 +250,7 @@ class MigrationTest < ActiveRecord::TestCase
assert_not_predicate Reminder, :table_exists?
name_filter = lambda { |migration| migration.name == "ValidPeopleHaveLastNames" }
- migrator = ActiveRecord::MigrationContext.new(MIGRATIONS_ROOT + "/valid")
+ migrator = ActiveRecord::MigrationContext.new(MIGRATIONS_ROOT + "/valid", @schema_migration)
migrator.up(&name_filter)
assert_column Person, :last_name
@@ -310,7 +312,7 @@ class MigrationTest < ActiveRecord::TestCase
end
}.new
- migrator = ActiveRecord::Migrator.new(:up, [migration], 100)
+ migrator = ActiveRecord::Migrator.new(:up, [migration], @schema_migration, 100)
e = assert_raise(StandardError) { migrator.migrate }
@@ -331,7 +333,7 @@ class MigrationTest < ActiveRecord::TestCase
end
}.new
- migrator = ActiveRecord::Migrator.new(:up, [migration], 100)
+ migrator = ActiveRecord::Migrator.new(:up, [migration], @schema_migration, 100)
e = assert_raise(StandardError) { migrator.run }
@@ -354,7 +356,7 @@ class MigrationTest < ActiveRecord::TestCase
end
}.new
- migrator = ActiveRecord::Migrator.new(:up, [migration], 101)
+ migrator = ActiveRecord::Migrator.new(:up, [migration], @schema_migration, 101)
e = assert_raise(StandardError) { migrator.migrate }
assert_equal "An error has occurred, all later migrations canceled:\n\nSomething broke", e.message
@@ -413,7 +415,7 @@ class MigrationTest < ActiveRecord::TestCase
def test_internal_metadata_stores_environment
current_env = ActiveRecord::ConnectionHandling::DEFAULT_ENV.call
migrations_path = MIGRATIONS_ROOT + "/valid"
- migrator = ActiveRecord::MigrationContext.new(migrations_path)
+ migrator = ActiveRecord::MigrationContext.new(migrations_path, @schema_migration)
migrator.up
assert_equal current_env, ActiveRecord::InternalMetadata[:environment]
@@ -441,7 +443,7 @@ class MigrationTest < ActiveRecord::TestCase
current_env = ActiveRecord::ConnectionHandling::DEFAULT_ENV.call
migrations_path = MIGRATIONS_ROOT + "/valid"
- migrator = ActiveRecord::MigrationContext.new(migrations_path)
+ migrator = ActiveRecord::MigrationContext.new(migrations_path, @schema_migration)
migrator.up
assert_equal current_env, ActiveRecord::InternalMetadata[:environment]
assert_equal "bar", ActiveRecord::InternalMetadata[:foo]
@@ -482,6 +484,7 @@ class MigrationTest < ActiveRecord::TestCase
Thing.reset_table_name
Thing.reset_sequence_name
WeNeedThings.up
+ assert_predicate Thing, :table_exists?
Thing.reset_column_information
assert Thing.create("content" => "hello world")
@@ -502,8 +505,9 @@ class MigrationTest < ActiveRecord::TestCase
ActiveRecord::Base.table_name_suffix = "_suffix"
Reminder.reset_table_name
Reminder.reset_sequence_name
- Reminder.reset_column_information
WeNeedReminders.up
+ assert_predicate Reminder, :table_exists?
+ Reminder.reset_column_information
assert Reminder.create("content" => "hello world", "remind_at" => Time.now)
assert_equal "hello world", Reminder.first.content
@@ -636,7 +640,7 @@ class MigrationTest < ActiveRecord::TestCase
if ActiveRecord::Base.connection.supports_advisory_locks?
def test_migrator_generates_valid_lock_id
migration = Class.new(ActiveRecord::Migration::Current).new
- migrator = ActiveRecord::Migrator.new(:up, [migration], 100)
+ migrator = ActiveRecord::Migrator.new(:up, [migration], @schema_migration, 100)
lock_id = migrator.send(:generate_migrator_advisory_lock_id)
@@ -650,7 +654,7 @@ class MigrationTest < ActiveRecord::TestCase
# It is important we are consistent with how we generate this so that
# exclusive locking works across migrator versions
migration = Class.new(ActiveRecord::Migration::Current).new
- migrator = ActiveRecord::Migrator.new(:up, [migration], 100)
+ migrator = ActiveRecord::Migrator.new(:up, [migration], @schema_migration, 100)
lock_id = migrator.send(:generate_migrator_advisory_lock_id)
@@ -672,7 +676,7 @@ class MigrationTest < ActiveRecord::TestCase
end
}.new
- migrator = ActiveRecord::Migrator.new(:up, [migration], 100)
+ migrator = ActiveRecord::Migrator.new(:up, [migration], @schema_migration, 100)
lock_id = migrator.send(:generate_migrator_advisory_lock_id)
with_another_process_holding_lock(lock_id) do
@@ -693,7 +697,7 @@ class MigrationTest < ActiveRecord::TestCase
end
}.new
- migrator = ActiveRecord::Migrator.new(:up, [migration], 100)
+ migrator = ActiveRecord::Migrator.new(:up, [migration], @schema_migration, 100)
lock_id = migrator.send(:generate_migrator_advisory_lock_id)
with_another_process_holding_lock(lock_id) do
@@ -706,7 +710,7 @@ class MigrationTest < ActiveRecord::TestCase
def test_with_advisory_lock_raises_the_right_error_when_it_fails_to_release_lock
migration = Class.new(ActiveRecord::Migration::Current).new
- migrator = ActiveRecord::Migrator.new(:up, [migration], 100)
+ migrator = ActiveRecord::Migrator.new(:up, [migration], @schema_migration, 100)
lock_id = migrator.send(:generate_migrator_advisory_lock_id)
e = assert_raises(ActiveRecord::ConcurrentMigrationError) do
@@ -854,7 +858,7 @@ if ActiveRecord::Base.connection.supports_bulk_alter?
classname = ActiveRecord::Base.connection.class.name[/[^:]*$/]
expected_query_count = {
- "Mysql2Adapter" => 3, # Adding an index fires a query every time to check if an index already exists or not
+ "Mysql2Adapter" => 1, # mysql2 supports creating two indexes using one statement
"PostgreSQLAdapter" => 2,
}.fetch(classname) {
raise "need an expected query count for #{classname}"
@@ -886,7 +890,7 @@ if ActiveRecord::Base.connection.supports_bulk_alter?
classname = ActiveRecord::Base.connection.class.name[/[^:]*$/]
expected_query_count = {
- "Mysql2Adapter" => 3, # Adding an index fires a query every time to check if an index already exists or not
+ "Mysql2Adapter" => 1, # mysql2 supports dropping and creating two indexes using one statement
"PostgreSQLAdapter" => 2,
}.fetch(classname) {
raise "need an expected query count for #{classname}"
@@ -935,7 +939,6 @@ if ActiveRecord::Base.connection.supports_bulk_alter?
end
private
-
def with_bulk_change_table
# Reset columns/indexes cache as we're changing the table
@columns = @indexes = nil
diff --git a/activerecord/test/cases/migrator_test.rb b/activerecord/test/cases/migrator_test.rb
index 30e199f1c5..aeba8e1d14 100644
--- a/activerecord/test/cases/migrator_test.rb
+++ b/activerecord/test/cases/migrator_test.rb
@@ -23,8 +23,9 @@ class MigratorTest < ActiveRecord::TestCase
def setup
super
- ActiveRecord::SchemaMigration.create_table
- ActiveRecord::SchemaMigration.delete_all rescue nil
+ @schema_migration = ActiveRecord::Base.connection.schema_migration
+ @schema_migration.create_table
+ @schema_migration.delete_all rescue nil
@verbose_was = ActiveRecord::Migration.verbose
ActiveRecord::Migration.message_count = 0
ActiveRecord::Migration.class_eval do
@@ -36,7 +37,7 @@ class MigratorTest < ActiveRecord::TestCase
end
teardown do
- ActiveRecord::SchemaMigration.delete_all rescue nil
+ @schema_migration.delete_all rescue nil
ActiveRecord::Migration.verbose = @verbose_was
ActiveRecord::Migration.class_eval do
undef :puts
@@ -49,7 +50,7 @@ class MigratorTest < ActiveRecord::TestCase
def test_migrator_with_duplicate_names
e = assert_raises(ActiveRecord::DuplicateMigrationNameError) do
list = [ActiveRecord::Migration.new("Chunky"), ActiveRecord::Migration.new("Chunky")]
- ActiveRecord::Migrator.new(:up, list)
+ ActiveRecord::Migrator.new(:up, list, @schema_migration)
end
assert_match(/Multiple migrations have the name Chunky/, e.message)
end
@@ -57,39 +58,40 @@ class MigratorTest < ActiveRecord::TestCase
def test_migrator_with_duplicate_versions
assert_raises(ActiveRecord::DuplicateMigrationVersionError) do
list = [ActiveRecord::Migration.new("Foo", 1), ActiveRecord::Migration.new("Bar", 1)]
- ActiveRecord::Migrator.new(:up, list)
+ ActiveRecord::Migrator.new(:up, list, @schema_migration)
end
end
def test_migrator_with_missing_version_numbers
assert_raises(ActiveRecord::UnknownMigrationVersionError) do
list = [ActiveRecord::Migration.new("Foo", 1), ActiveRecord::Migration.new("Bar", 2)]
- ActiveRecord::Migrator.new(:up, list, 3).run
+ ActiveRecord::Migrator.new(:up, list, @schema_migration, 3).run
end
assert_raises(ActiveRecord::UnknownMigrationVersionError) do
list = [ActiveRecord::Migration.new("Foo", 1), ActiveRecord::Migration.new("Bar", 2)]
- ActiveRecord::Migrator.new(:up, list, -1).run
+ ActiveRecord::Migrator.new(:up, list, @schema_migration, -1).run
end
assert_raises(ActiveRecord::UnknownMigrationVersionError) do
list = [ActiveRecord::Migration.new("Foo", 1), ActiveRecord::Migration.new("Bar", 2)]
- ActiveRecord::Migrator.new(:up, list, 0).run
+ ActiveRecord::Migrator.new(:up, list, @schema_migration, 0).run
end
assert_raises(ActiveRecord::UnknownMigrationVersionError) do
list = [ActiveRecord::Migration.new("Foo", 1), ActiveRecord::Migration.new("Bar", 2)]
- ActiveRecord::Migrator.new(:up, list, 3).migrate
+ ActiveRecord::Migrator.new(:up, list, @schema_migration, 3).migrate
end
assert_raises(ActiveRecord::UnknownMigrationVersionError) do
list = [ActiveRecord::Migration.new("Foo", 1), ActiveRecord::Migration.new("Bar", 2)]
- ActiveRecord::Migrator.new(:up, list, -1).migrate
+ ActiveRecord::Migrator.new(:up, list, @schema_migration, -1).migrate
end
end
def test_finds_migrations
- migrations = ActiveRecord::MigrationContext.new(MIGRATIONS_ROOT + "/valid").migrations
+ schema_migration = ActiveRecord::Base.connection.schema_migration
+ migrations = ActiveRecord::MigrationContext.new(MIGRATIONS_ROOT + "/valid", schema_migration).migrations
[[1, "ValidPeopleHaveLastNames"], [2, "WeNeedReminders"], [3, "InnocentJointable"]].each_with_index do |pair, i|
assert_equal migrations[i].version, pair.first
@@ -98,7 +100,8 @@ class MigratorTest < ActiveRecord::TestCase
end
def test_finds_migrations_in_subdirectories
- migrations = ActiveRecord::MigrationContext.new(MIGRATIONS_ROOT + "/valid_with_subdirectories").migrations
+ schema_migration = ActiveRecord::Base.connection.schema_migration
+ migrations = ActiveRecord::MigrationContext.new(MIGRATIONS_ROOT + "/valid_with_subdirectories", schema_migration).migrations
[[1, "ValidPeopleHaveLastNames"], [2, "WeNeedReminders"], [3, "InnocentJointable"]].each_with_index do |pair, i|
assert_equal migrations[i].version, pair.first
@@ -107,8 +110,9 @@ class MigratorTest < ActiveRecord::TestCase
end
def test_finds_migrations_from_two_directories
+ schema_migration = ActiveRecord::Base.connection.schema_migration
directories = [MIGRATIONS_ROOT + "/valid_with_timestamps", MIGRATIONS_ROOT + "/to_copy_with_timestamps"]
- migrations = ActiveRecord::MigrationContext.new(directories).migrations
+ migrations = ActiveRecord::MigrationContext.new(directories, schema_migration).migrations
[[20090101010101, "PeopleHaveHobbies"],
[20090101010202, "PeopleHaveDescriptions"],
@@ -121,14 +125,16 @@ class MigratorTest < ActiveRecord::TestCase
end
def test_finds_migrations_in_numbered_directory
- migrations = ActiveRecord::MigrationContext.new(MIGRATIONS_ROOT + "/10_urban").migrations
+ schema_migration = ActiveRecord::Base.connection.schema_migration
+ migrations = ActiveRecord::MigrationContext.new(MIGRATIONS_ROOT + "/10_urban", schema_migration).migrations
assert_equal 9, migrations[0].version
assert_equal "AddExpressions", migrations[0].name
end
def test_relative_migrations
+ schema_migration = ActiveRecord::Base.connection.schema_migration
list = Dir.chdir(MIGRATIONS_ROOT) do
- ActiveRecord::MigrationContext.new("valid").migrations
+ ActiveRecord::MigrationContext.new("valid", schema_migration).migrations
end
migration_proxy = list.find { |item|
@@ -138,9 +144,9 @@ class MigratorTest < ActiveRecord::TestCase
end
def test_finds_pending_migrations
- ActiveRecord::SchemaMigration.create!(version: "1")
+ @schema_migration.create!(version: "1")
migration_list = [ActiveRecord::Migration.new("foo", 1), ActiveRecord::Migration.new("bar", 3)]
- migrations = ActiveRecord::Migrator.new(:up, migration_list).pending_migrations
+ migrations = ActiveRecord::Migrator.new(:up, migration_list, @schema_migration).pending_migrations
assert_equal 1, migrations.size
assert_equal migration_list.last, migrations.first
@@ -148,35 +154,38 @@ class MigratorTest < ActiveRecord::TestCase
def test_migrations_status
path = MIGRATIONS_ROOT + "/valid"
+ schema_migration = ActiveRecord::Base.connection.schema_migration
- ActiveRecord::SchemaMigration.create(version: 2)
- ActiveRecord::SchemaMigration.create(version: 10)
+ @schema_migration.create(version: 2)
+ @schema_migration.create(version: 10)
assert_equal [
["down", "001", "Valid people have last names"],
["up", "002", "We need reminders"],
["down", "003", "Innocent jointable"],
["up", "010", "********** NO FILE **********"],
- ], ActiveRecord::MigrationContext.new(path).migrations_status
+ ], ActiveRecord::MigrationContext.new(path, schema_migration).migrations_status
end
def test_migrations_status_in_subdirectories
path = MIGRATIONS_ROOT + "/valid_with_subdirectories"
+ schema_migration = ActiveRecord::Base.connection.schema_migration
- ActiveRecord::SchemaMigration.create(version: 2)
- ActiveRecord::SchemaMigration.create(version: 10)
+ @schema_migration.create(version: 2)
+ @schema_migration.create(version: 10)
assert_equal [
["down", "001", "Valid people have last names"],
["up", "002", "We need reminders"],
["down", "003", "Innocent jointable"],
["up", "010", "********** NO FILE **********"],
- ], ActiveRecord::MigrationContext.new(path).migrations_status
+ ], ActiveRecord::MigrationContext.new(path, schema_migration).migrations_status
end
def test_migrations_status_with_schema_define_in_subdirectories
path = MIGRATIONS_ROOT + "/valid_with_subdirectories"
prev_paths = ActiveRecord::Migrator.migrations_paths
+ schema_migration = ActiveRecord::Base.connection.schema_migration
ActiveRecord::Migrator.migrations_paths = path
ActiveRecord::Schema.define(version: 3) do
@@ -186,16 +195,17 @@ class MigratorTest < ActiveRecord::TestCase
["up", "001", "Valid people have last names"],
["up", "002", "We need reminders"],
["up", "003", "Innocent jointable"],
- ], ActiveRecord::MigrationContext.new(path).migrations_status
+ ], ActiveRecord::MigrationContext.new(path, schema_migration).migrations_status
ensure
ActiveRecord::Migrator.migrations_paths = prev_paths
end
def test_migrations_status_from_two_directories
paths = [MIGRATIONS_ROOT + "/valid_with_timestamps", MIGRATIONS_ROOT + "/to_copy_with_timestamps"]
+ schema_migration = ActiveRecord::Base.connection.schema_migration
- ActiveRecord::SchemaMigration.create(version: "20100101010101")
- ActiveRecord::SchemaMigration.create(version: "20160528010101")
+ @schema_migration.create(version: "20100101010101")
+ @schema_migration.create(version: "20160528010101")
assert_equal [
["down", "20090101010101", "People have hobbies"],
@@ -204,18 +214,18 @@ class MigratorTest < ActiveRecord::TestCase
["down", "20100201010101", "Valid with timestamps we need reminders"],
["down", "20100301010101", "Valid with timestamps innocent jointable"],
["up", "20160528010101", "********** NO FILE **********"],
- ], ActiveRecord::MigrationContext.new(paths).migrations_status
+ ], ActiveRecord::MigrationContext.new(paths, schema_migration).migrations_status
end
def test_migrator_interleaved_migrations
pass_one = [Sensor.new("One", 1)]
- ActiveRecord::Migrator.new(:up, pass_one).migrate
+ ActiveRecord::Migrator.new(:up, pass_one, @schema_migration).migrate
assert pass_one.first.went_up
assert_not pass_one.first.went_down
pass_two = [Sensor.new("One", 1), Sensor.new("Three", 3)]
- ActiveRecord::Migrator.new(:up, pass_two).migrate
+ ActiveRecord::Migrator.new(:up, pass_two, @schema_migration).migrate
assert_not pass_two[0].went_up
assert pass_two[1].went_up
assert pass_two.all? { |x| !x.went_down }
@@ -224,7 +234,7 @@ class MigratorTest < ActiveRecord::TestCase
Sensor.new("Two", 2),
Sensor.new("Three", 3)]
- ActiveRecord::Migrator.new(:down, pass_three).migrate
+ ActiveRecord::Migrator.new(:down, pass_three, @schema_migration).migrate
assert pass_three[0].went_down
assert_not pass_three[1].went_down
assert pass_three[2].went_down
@@ -232,7 +242,7 @@ class MigratorTest < ActiveRecord::TestCase
def test_up_calls_up
migrations = [Sensor.new(nil, 0), Sensor.new(nil, 1), Sensor.new(nil, 2)]
- migrator = ActiveRecord::Migrator.new(:up, migrations)
+ migrator = ActiveRecord::Migrator.new(:up, migrations, @schema_migration)
migrator.migrate
assert migrations.all?(&:went_up)
assert migrations.all? { |m| !m.went_down }
@@ -243,7 +253,7 @@ class MigratorTest < ActiveRecord::TestCase
test_up_calls_up
migrations = [Sensor.new(nil, 0), Sensor.new(nil, 1), Sensor.new(nil, 2)]
- migrator = ActiveRecord::Migrator.new(:down, migrations)
+ migrator = ActiveRecord::Migrator.new(:down, migrations, @schema_migration)
migrator.migrate
assert migrations.all? { |m| !m.went_up }
assert migrations.all?(&:went_down)
@@ -251,30 +261,31 @@ class MigratorTest < ActiveRecord::TestCase
end
def test_current_version
- ActiveRecord::SchemaMigration.create!(version: "1000")
- migrator = ActiveRecord::MigrationContext.new("db/migrate")
+ @schema_migration.create!(version: "1000")
+ schema_migration = ActiveRecord::Base.connection.schema_migration
+ migrator = ActiveRecord::MigrationContext.new("db/migrate", schema_migration)
assert_equal 1000, migrator.current_version
end
def test_migrator_one_up
calls, migrations = sensors(3)
- ActiveRecord::Migrator.new(:up, migrations, 1).migrate
+ ActiveRecord::Migrator.new(:up, migrations, @schema_migration, 1).migrate
assert_equal [[:up, 1]], calls
calls.clear
- ActiveRecord::Migrator.new(:up, migrations, 2).migrate
+ ActiveRecord::Migrator.new(:up, migrations, @schema_migration, 2).migrate
assert_equal [[:up, 2]], calls
end
def test_migrator_one_down
calls, migrations = sensors(3)
- ActiveRecord::Migrator.new(:up, migrations).migrate
+ ActiveRecord::Migrator.new(:up, migrations, @schema_migration).migrate
assert_equal [[:up, 1], [:up, 2], [:up, 3]], calls
calls.clear
- ActiveRecord::Migrator.new(:down, migrations, 1).migrate
+ ActiveRecord::Migrator.new(:down, migrations, @schema_migration, 1).migrate
assert_equal [[:down, 3], [:down, 2]], calls
end
@@ -282,17 +293,17 @@ class MigratorTest < ActiveRecord::TestCase
def test_migrator_one_up_one_down
calls, migrations = sensors(3)
- ActiveRecord::Migrator.new(:up, migrations, 1).migrate
+ ActiveRecord::Migrator.new(:up, migrations, @schema_migration, 1).migrate
assert_equal [[:up, 1]], calls
calls.clear
- ActiveRecord::Migrator.new(:down, migrations, 0).migrate
+ ActiveRecord::Migrator.new(:down, migrations, @schema_migration, 0).migrate
assert_equal [[:down, 1]], calls
end
def test_migrator_double_up
calls, migrations = sensors(3)
- migrator = ActiveRecord::Migrator.new(:up, migrations, 1)
+ migrator = ActiveRecord::Migrator.new(:up, migrations, @schema_migration, 1)
assert_equal(0, migrator.current_version)
migrator.migrate
@@ -305,7 +316,7 @@ class MigratorTest < ActiveRecord::TestCase
def test_migrator_double_down
calls, migrations = sensors(3)
- migrator = ActiveRecord::Migrator.new(:up, migrations, 1)
+ migrator = ActiveRecord::Migrator.new(:up, migrations, @schema_migration, 1)
assert_equal 0, migrator.current_version
@@ -313,7 +324,7 @@ class MigratorTest < ActiveRecord::TestCase
assert_equal [[:up, 1]], calls
calls.clear
- migrator = ActiveRecord::Migrator.new(:down, migrations, 1)
+ migrator = ActiveRecord::Migrator.new(:down, migrations, @schema_migration, 1)
migrator.run
assert_equal [[:down, 1]], calls
calls.clear
@@ -328,12 +339,12 @@ class MigratorTest < ActiveRecord::TestCase
_, migrations = sensors(3)
ActiveRecord::Migration.verbose = true
- ActiveRecord::Migrator.new(:up, migrations, 1).migrate
+ ActiveRecord::Migrator.new(:up, migrations, @schema_migration, 1).migrate
assert_not_equal 0, ActiveRecord::Migration.message_count
ActiveRecord::Migration.message_count = 0
- ActiveRecord::Migrator.new(:down, migrations, 0).migrate
+ ActiveRecord::Migrator.new(:down, migrations, @schema_migration, 0).migrate
assert_not_equal 0, ActiveRecord::Migration.message_count
end
@@ -341,9 +352,9 @@ class MigratorTest < ActiveRecord::TestCase
_, migrations = sensors(3)
ActiveRecord::Migration.verbose = false
- ActiveRecord::Migrator.new(:up, migrations, 1).migrate
+ ActiveRecord::Migrator.new(:up, migrations, @schema_migration, 1).migrate
assert_equal 0, ActiveRecord::Migration.message_count
- ActiveRecord::Migrator.new(:down, migrations, 0).migrate
+ ActiveRecord::Migrator.new(:down, migrations, @schema_migration, 0).migrate
assert_equal 0, ActiveRecord::Migration.message_count
end
@@ -351,23 +362,24 @@ class MigratorTest < ActiveRecord::TestCase
calls, migrations = sensors(3)
# migrate up to 1
- ActiveRecord::Migrator.new(:up, migrations, 1).migrate
+ ActiveRecord::Migrator.new(:up, migrations, @schema_migration, 1).migrate
assert_equal [[:up, 1]], calls
calls.clear
# migrate down to 0
- ActiveRecord::Migrator.new(:down, migrations, 0).migrate
+ ActiveRecord::Migrator.new(:down, migrations, @schema_migration, 0).migrate
assert_equal [[:down, 1]], calls
calls.clear
# migrate down to 0 again
- ActiveRecord::Migrator.new(:down, migrations, 0).migrate
+ ActiveRecord::Migrator.new(:down, migrations, @schema_migration, 0).migrate
assert_equal [], calls
end
def test_migrator_going_down_due_to_version_target
+ schema_migration = ActiveRecord::Base.connection.schema_migration
calls, migrator = migrator_class(3)
- migrator = migrator.new("valid")
+ migrator = migrator.new("valid", schema_migration)
migrator.up(1)
assert_equal [[:up, 1]], calls
@@ -382,8 +394,9 @@ class MigratorTest < ActiveRecord::TestCase
end
def test_migrator_output_when_running_multiple_migrations
+ schema_migration = ActiveRecord::Base.connection.schema_migration
_, migrator = migrator_class(3)
- migrator = migrator.new("valid")
+ migrator = migrator.new("valid", schema_migration)
result = migrator.migrate
assert_equal(3, result.count)
@@ -397,8 +410,9 @@ class MigratorTest < ActiveRecord::TestCase
end
def test_migrator_output_when_running_single_migration
+ schema_migration = ActiveRecord::Base.connection.schema_migration
_, migrator = migrator_class(1)
- migrator = migrator.new("valid")
+ migrator = migrator.new("valid", schema_migration)
result = migrator.run(:up, 1)
@@ -406,8 +420,9 @@ class MigratorTest < ActiveRecord::TestCase
end
def test_migrator_rollback
+ schema_migration = ActiveRecord::Base.connection.schema_migration
_, migrator = migrator_class(3)
- migrator = migrator.new("valid")
+ migrator = migrator.new("valid", schema_migration)
migrator.migrate
assert_equal(3, migrator.current_version)
@@ -426,18 +441,20 @@ class MigratorTest < ActiveRecord::TestCase
end
def test_migrator_db_has_no_schema_migrations_table
+ schema_migration = ActiveRecord::Base.connection.schema_migration
_, migrator = migrator_class(3)
- migrator = migrator.new("valid")
+ migrator = migrator.new("valid", schema_migration)
- ActiveRecord::Base.connection.drop_table "schema_migrations", if_exists: true
- assert_not ActiveRecord::Base.connection.table_exists?("schema_migrations")
+ ActiveRecord::SchemaMigration.drop_table
+ assert_not_predicate ActiveRecord::SchemaMigration, :table_exists?
migrator.migrate(1)
- assert ActiveRecord::Base.connection.table_exists?("schema_migrations")
+ assert_predicate ActiveRecord::SchemaMigration, :table_exists?
end
def test_migrator_forward
+ schema_migration = ActiveRecord::Base.connection.schema_migration
_, migrator = migrator_class(3)
- migrator = migrator.new("/valid")
+ migrator = migrator.new("/valid", schema_migration)
migrator.migrate(1)
assert_equal(1, migrator.current_version)
@@ -450,18 +467,20 @@ class MigratorTest < ActiveRecord::TestCase
def test_only_loads_pending_migrations
# migrate up to 1
- ActiveRecord::SchemaMigration.create!(version: "1")
+ @schema_migration.create!(version: "1")
+ schema_migration = ActiveRecord::Base.connection.schema_migration
calls, migrator = migrator_class(3)
- migrator = migrator.new("valid")
+ migrator = migrator.new("valid", schema_migration)
migrator.migrate
assert_equal [[:up, 2], [:up, 3]], calls
end
def test_get_all_versions
+ schema_migration = ActiveRecord::Base.connection.schema_migration
_, migrator = migrator_class(3)
- migrator = migrator.new("valid")
+ migrator = migrator.new("valid", schema_migration)
migrator.migrate
assert_equal([1, 2, 3], migrator.get_all_versions)
diff --git a/activerecord/test/cases/multi_db_migrator_test.rb b/activerecord/test/cases/multi_db_migrator_test.rb
new file mode 100644
index 0000000000..650b3af6f0
--- /dev/null
+++ b/activerecord/test/cases/multi_db_migrator_test.rb
@@ -0,0 +1,218 @@
+# frozen_string_literal: true
+
+require "cases/helper"
+require "cases/migration/helper"
+
+class MultiDbMigratorTest < ActiveRecord::TestCase
+ self.use_transactional_tests = false
+
+ # Use this class to sense if migrations have gone
+ # up or down.
+ class Sensor < ActiveRecord::Migration::Current
+ attr_reader :went_up, :went_down
+
+ def initialize(name = self.class.name, version = nil)
+ super
+ @went_up = false
+ @went_down = false
+ end
+
+ def up; @went_up = true; end
+ def down; @went_down = true; end
+ end
+
+ def setup
+ super
+ @connection_a = ActiveRecord::Base.connection
+ @connection_b = ARUnit2Model.connection
+
+ @connection_a.schema_migration.create_table
+ @connection_b.schema_migration.create_table
+
+ @connection_a.schema_migration.delete_all rescue nil
+ @connection_b.schema_migration.delete_all rescue nil
+
+ @path_a = MIGRATIONS_ROOT + "/valid"
+ @path_b = MIGRATIONS_ROOT + "/to_copy"
+
+ @schema_migration_a = @connection_a.schema_migration
+ @migrations_a = ActiveRecord::MigrationContext.new(@path_a, @schema_migration_a).migrations
+ @schema_migration_b = @connection_b.schema_migration
+ @migrations_b = ActiveRecord::MigrationContext.new(@path_b, @schema_migration_b).migrations
+
+ @migrations_a_list = [[1, "ValidPeopleHaveLastNames"], [2, "WeNeedReminders"], [3, "InnocentJointable"]]
+ @migrations_b_list = [[1, "PeopleHaveHobbies"], [2, "PeopleHaveDescriptions"]]
+
+ @verbose_was = ActiveRecord::Migration.verbose
+
+ ActiveRecord::Migration.message_count = 0
+ ActiveRecord::Migration.class_eval do
+ undef :puts
+ def puts(*)
+ ActiveRecord::Migration.message_count += 1
+ end
+ end
+ end
+
+ teardown do
+ @connection_a.schema_migration.delete_all rescue nil
+ @connection_b.schema_migration.delete_all rescue nil
+
+ ActiveRecord::Migration.verbose = @verbose_was
+ ActiveRecord::Migration.class_eval do
+ undef :puts
+ def puts(*)
+ super
+ end
+ end
+ end
+
+ def test_finds_migrations
+ @migrations_a_list.each_with_index do |pair, i|
+ assert_equal @migrations_a[i].version, pair.first
+ assert_equal @migrations_a[i].name, pair.last
+ end
+
+ @migrations_b_list.each_with_index do |pair, i|
+ assert_equal @migrations_b[i].version, pair.first
+ assert_equal @migrations_b[i].name, pair.last
+ end
+ end
+
+ def test_migrations_status
+ @schema_migration_a.create(version: 2)
+ @schema_migration_a.create(version: 10)
+
+ assert_equal [
+ ["down", "001", "Valid people have last names"],
+ ["up", "002", "We need reminders"],
+ ["down", "003", "Innocent jointable"],
+ ["up", "010", "********** NO FILE **********"],
+ ], ActiveRecord::MigrationContext.new(@path_a, @schema_migration_a).migrations_status
+
+ @schema_migration_b.create(version: 4)
+
+ assert_equal [
+ ["down", "001", "People have hobbies"],
+ ["down", "002", "People have descriptions"],
+ ["up", "004", "********** NO FILE **********"]
+ ], ActiveRecord::MigrationContext.new(@path_b, @schema_migration_b).migrations_status
+ end
+
+ def test_get_all_versions
+ _, migrator_a = migrator_class(3)
+ migrator_a = migrator_a.new(@path_a, @schema_migration_a)
+
+ migrator_a.migrate
+ assert_equal([1, 2, 3], migrator_a.get_all_versions)
+
+ migrator_a.rollback
+ assert_equal([1, 2], migrator_a.get_all_versions)
+
+ migrator_a.rollback
+ assert_equal([1], migrator_a.get_all_versions)
+
+ migrator_a.rollback
+ assert_equal([], migrator_a.get_all_versions)
+
+ _, migrator_b = migrator_class(2)
+ migrator_b = migrator_b.new(@path_b, @schema_migration_b)
+
+ migrator_b.migrate
+ assert_equal([1, 2], migrator_b.get_all_versions)
+
+ migrator_b.rollback
+ assert_equal([1], migrator_b.get_all_versions)
+
+ migrator_b.rollback
+ assert_equal([], migrator_b.get_all_versions)
+ end
+
+ def test_finds_pending_migrations
+ @schema_migration_a.create!(version: "1")
+ migration_list_a = [ActiveRecord::Migration.new("foo", 1), ActiveRecord::Migration.new("bar", 3)]
+ migrations_a = ActiveRecord::Migrator.new(:up, migration_list_a, @schema_migration_a).pending_migrations
+
+ assert_equal 1, migrations_a.size
+ assert_equal migration_list_a.last, migrations_a.first
+
+ @schema_migration_b.create!(version: "1")
+ migration_list_b = [ActiveRecord::Migration.new("foo", 1), ActiveRecord::Migration.new("bar", 3)]
+ migrations_b = ActiveRecord::Migrator.new(:up, migration_list_b, @schema_migration_b).pending_migrations
+
+ assert_equal 1, migrations_b.size
+ assert_equal migration_list_b.last, migrations_b.first
+ end
+
+ def test_migrator_db_has_no_schema_migrations_table
+ _, migrator = migrator_class(3)
+ migrator = migrator.new(@path_a, @schema_migration_a)
+
+ @schema_migration_a.drop_table
+ assert_not @connection_a.table_exists?("schema_migrations")
+ migrator.migrate(1)
+ assert @connection_a.table_exists?("schema_migrations")
+
+ _, migrator = migrator_class(3)
+ migrator = migrator.new(@path_b, @schema_migration_b)
+
+ @schema_migration_b.drop_table
+ assert_not @connection_b.table_exists?("schema_migrations")
+ migrator.migrate(1)
+ assert @connection_b.table_exists?("schema_migrations")
+ end
+
+ def test_migrator_forward
+ _, migrator = migrator_class(3)
+ migrator = migrator.new(@path_a, @schema_migration_a)
+ migrator.migrate(1)
+ assert_equal(1, migrator.current_version)
+
+ migrator.forward(2)
+ assert_equal(3, migrator.current_version)
+
+ migrator.forward
+ assert_equal(3, migrator.current_version)
+
+ _, migrator_b = migrator_class(3)
+ migrator_b = migrator_b.new(@path_b, @schema_migration_b)
+ migrator_b.migrate(1)
+ assert_equal(1, migrator_b.current_version)
+
+ migrator_b.forward(2)
+ assert_equal(3, migrator_b.current_version)
+
+ migrator_b.forward
+ assert_equal(3, migrator_b.current_version)
+ end
+
+ private
+ def m(name, version)
+ x = Sensor.new name, version
+ x.extend(Module.new {
+ define_method(:up) { yield(:up, x); super() }
+ define_method(:down) { yield(:down, x); super() }
+ }) if block_given?
+ end
+
+ def sensors(count)
+ calls = []
+ migrations = count.times.map { |i|
+ m(nil, i + 1) { |c, migration|
+ calls << [c, migration.version]
+ }
+ }
+ [calls, migrations]
+ end
+
+ def migrator_class(count)
+ calls, migrations = sensors(count)
+
+ migrator = Class.new(ActiveRecord::MigrationContext) {
+ define_method(:migrations) { |*|
+ migrations
+ }
+ }
+ [calls, migrator]
+ end
+end
diff --git a/activerecord/test/cases/nested_attributes_test.rb b/activerecord/test/cases/nested_attributes_test.rb
index bb1c1ea17d..b49e62bee6 100644
--- a/activerecord/test/cases/nested_attributes_test.rb
+++ b/activerecord/test/cases/nested_attributes_test.rb
@@ -851,7 +851,6 @@ module NestedAttributesOnACollectionAssociationTests
end
private
-
def association_setter
@association_setter ||= "#{@association_name}_attributes=".to_sym
end
diff --git a/activerecord/test/cases/persistence_test.rb b/activerecord/test/cases/persistence_test.rb
index d5057ad381..7b7aa7e9b7 100644
--- a/activerecord/test/cases/persistence_test.rb
+++ b/activerecord/test/cases/persistence_test.rb
@@ -183,7 +183,7 @@ class PersistenceTest < ActiveRecord::TestCase
assert_not_predicate company, :valid?
original_errors = company.errors
client = company.becomes(Client)
- assert_equal original_errors.keys, client.errors.keys
+ assert_equal assert_deprecated { original_errors.keys }, assert_deprecated { client.errors.keys }
end
def test_becomes_errors_base
@@ -197,7 +197,7 @@ class PersistenceTest < ActiveRecord::TestCase
admin.errors.add :token, :invalid
child = admin.becomes(child_class)
- assert_equal [:token], child.errors.keys
+ assert_equal [:token], assert_deprecated { child.errors.keys }
assert_nothing_raised do
child.errors.add :foo, :invalid
end
diff --git a/activerecord/test/cases/pooled_connections_test.rb b/activerecord/test/cases/pooled_connections_test.rb
index 080aeb0989..d783b2945d 100644
--- a/activerecord/test/cases/pooled_connections_test.rb
+++ b/activerecord/test/cases/pooled_connections_test.rb
@@ -72,7 +72,6 @@ class PooledConnectionsTest < ActiveRecord::TestCase
end
private
-
def add_record(name)
ActiveRecord::Base.connection_pool.with_connection { Project.create! name: name }
end
diff --git a/activerecord/test/cases/primary_keys_test.rb b/activerecord/test/cases/primary_keys_test.rb
index 4759d3b6b2..511d7fc982 100644
--- a/activerecord/test/cases/primary_keys_test.rb
+++ b/activerecord/test/cases/primary_keys_test.rb
@@ -203,6 +203,14 @@ class PrimaryKeysTest < ActiveRecord::TestCase
assert_queries(3, ignore_none: true) { klass.create! }
end
+ def test_assign_id_raises_error_if_primary_key_doesnt_exist
+ klass = Class.new(ActiveRecord::Base) do
+ self.table_name = "dashboards"
+ end
+ dashboard = klass.new
+ assert_raises(ActiveModel::MissingAttributeError) { dashboard.id = "1" }
+ end
+
if current_adapter?(:PostgreSQLAdapter)
def test_serial_with_quoted_sequence_name
column = MixedCaseMonkey.columns_hash[MixedCaseMonkey.primary_key]
diff --git a/activerecord/test/cases/query_cache_test.rb b/activerecord/test/cases/query_cache_test.rb
index eb32b690aa..79bd6906d1 100644
--- a/activerecord/test/cases/query_cache_test.rb
+++ b/activerecord/test/cases/query_cache_test.rb
@@ -335,11 +335,7 @@ class QueryCacheTest < ActiveRecord::TestCase
def test_cache_does_not_wrap_results_in_arrays
Task.cache do
- if current_adapter?(:SQLite3Adapter, :Mysql2Adapter, :PostgreSQLAdapter, :OracleAdapter)
- assert_equal 2, Task.connection.select_value("SELECT count(*) AS count_all FROM tasks")
- else
- assert_instance_of String, Task.connection.select_value("SELECT count(*) AS count_all FROM tasks")
- end
+ assert_equal 2, Task.connection.select_value("SELECT count(*) AS count_all FROM tasks")
end
end
@@ -540,8 +536,24 @@ class QueryCacheTest < ActiveRecord::TestCase
ActiveRecord::Base.connection_handlers = { writing: ActiveRecord::Base.default_connection_handler }
end
- private
+ test "query cache is enabled in threads with shared connection" do
+ ActiveRecord::Base.connection_pool.lock_thread = true
+
+ assert_cache :off
+ thread_a = Thread.new do
+ middleware { |env|
+ assert_cache :clean
+ [200, {}, nil]
+ }.call({})
+ end
+
+ thread_a.join
+
+ ActiveRecord::Base.connection_pool.lock_thread = false
+ end
+
+ private
def with_temporary_connection_pool
old_pool = ActiveRecord::Base.connection_handler.retrieve_connection_pool(ActiveRecord::Base.connection_specification_name)
new_pool = ActiveRecord::ConnectionAdapters::ConnectionPool.new ActiveRecord::Base.connection_pool.spec
diff --git a/activerecord/test/cases/relation/where_clause_test.rb b/activerecord/test/cases/relation/where_clause_test.rb
index 0b06cec40b..35db3d1175 100644
--- a/activerecord/test/cases/relation/where_clause_test.rb
+++ b/activerecord/test/cases/relation/where_clause_test.rb
@@ -106,7 +106,7 @@ class ActiveRecord::Relation
Arel::Nodes::Not.new(random_object)
])
- assert_equal expected, original.invert
+ assert_equal expected, original.invert(:nor)
end
test "except removes binary predicates referencing a given column" do
@@ -233,7 +233,6 @@ class ActiveRecord::Relation
end
private
-
def table
Arel::Table.new("table")
end
diff --git a/activerecord/test/cases/relation/where_test.rb b/activerecord/test/cases/relation/where_test.rb
index b045184d7d..aad30ddea0 100644
--- a/activerecord/test/cases/relation/where_test.rb
+++ b/activerecord/test/cases/relation/where_test.rb
@@ -18,7 +18,19 @@ require "support/stubs/strong_parameters"
module ActiveRecord
class WhereTest < ActiveRecord::TestCase
- fixtures :posts, :edges, :authors, :author_addresses, :binaries, :essays, :cars, :treasures, :price_estimates, :topics
+ fixtures :posts, :comments, :edges, :authors, :author_addresses, :binaries, :essays, :cars, :treasures, :price_estimates, :topics
+
+ def test_in_clause_is_correctly_sliced
+ assert_called(Author.connection, :in_clause_length, returns: 1) do
+ david = authors(:david)
+ assert_equal [david], Author.where(name: "David", id: [1, 2])
+ end
+ end
+
+ def test_type_casting_nested_joins
+ comment = comments(:eager_other_comment1)
+ assert_equal [comment], Comment.joins(post: :author).where(authors: { id: "2-foo" })
+ end
def test_where_copies_bind_params
author = authors(:david)
@@ -115,13 +127,58 @@ module ActiveRecord
assert_equal expected.to_sql, actual.to_sql
end
- def test_polymorphic_shallow_where_not
- treasure = treasures(:sapphire)
+ def test_where_not_polymorphic_association
+ sapphire = treasures(:sapphire)
- expected = [price_estimates(:diamond), price_estimates(:honda)]
- actual = PriceEstimate.where.not(estimate_of: treasure)
+ all = [treasures(:diamond), sapphire, cars(:honda), sapphire]
+ assert_equal all, PriceEstimate.all.sort_by(&:id).map(&:estimate_of)
- assert_equal expected.sort_by(&:id), actual.sort_by(&:id)
+ actual = PriceEstimate.where.not(estimate_of: sapphire)
+ only = PriceEstimate.where(estimate_of: sapphire)
+
+ expected = all - [sapphire]
+ assert_equal expected, actual.sort_by(&:id).map(&:estimate_of)
+ assert_equal all - expected, only.sort_by(&:id).map(&:estimate_of)
+ end
+
+ def test_where_not_polymorphic_id_and_type_as_nand
+ sapphire = treasures(:sapphire)
+
+ all = [treasures(:diamond), sapphire, cars(:honda), sapphire]
+ assert_equal all, PriceEstimate.all.sort_by(&:id).map(&:estimate_of)
+
+ actual = PriceEstimate.where.yield_self do |where_chain|
+ where_chain.stub(:not_behaves_as_nor?, false) do
+ where_chain.not(estimate_of_type: sapphire.class.polymorphic_name, estimate_of_id: sapphire.id)
+ end
+ end
+ only = PriceEstimate.where(estimate_of_type: sapphire.class.polymorphic_name, estimate_of_id: sapphire.id)
+
+ expected = all - [sapphire]
+ assert_equal expected, actual.sort_by(&:id).map(&:estimate_of)
+ assert_equal all - expected, only.sort_by(&:id).map(&:estimate_of)
+ end
+
+ def test_where_not_polymorphic_id_and_type_as_nor_is_deprecated
+ sapphire = treasures(:sapphire)
+
+ all = [treasures(:diamond), sapphire, cars(:honda), sapphire]
+ assert_equal all, PriceEstimate.all.sort_by(&:id).map(&:estimate_of)
+
+ message = <<~MSG.squish
+ NOT conditions will no longer behave as NOR in Rails 6.1.
+ To continue using NOR conditions, NOT each conditions manually
+ (`.where.not(:estimate_of_type => ...).where.not(:estimate_of_id => ...)`).
+ MSG
+ actual = assert_deprecated(message) do
+ PriceEstimate.where.not(estimate_of_type: sapphire.class.polymorphic_name, estimate_of_id: sapphire.id)
+ end
+ only = PriceEstimate.where(estimate_of_type: sapphire.class.polymorphic_name, estimate_of_id: sapphire.id)
+
+ expected = all - [sapphire]
+ # NOT (estimate_of_type = 'Treasure' OR estimate_of_id = sapphire.id) matches only `cars(:honda)` unfortunately.
+ assert_not_equal expected, actual.sort_by(&:id).map(&:estimate_of)
+ assert_equal all - expected, only.sort_by(&:id).map(&:estimate_of)
end
def test_polymorphic_nested_array_where
diff --git a/activerecord/test/cases/relation_test.rb b/activerecord/test/cases/relation_test.rb
index 3f370e5ede..e74fb1a098 100644
--- a/activerecord/test/cases/relation_test.rb
+++ b/activerecord/test/cases/relation_test.rb
@@ -363,6 +363,13 @@ module ActiveRecord
assert_match %r{/\*\+ BADHINT \*/}, post_with_hint.to_sql
end
+ def test_does_not_duplicate_optimizer_hints_on_merge
+ escaped_table = Post.connection.quote_table_name("posts")
+ expected = "SELECT /*+ OMGHINT */ #{escaped_table}.* FROM #{escaped_table}"
+ query = Post.optimizer_hints("OMGHINT").merge(Post.optimizer_hints("OMGHINT")).to_sql
+ assert_equal expected, query
+ end
+
class EnsureRoundTripTypeCasting < ActiveRecord::Type::Value
def type
:string
@@ -405,7 +412,6 @@ module ActiveRecord
end
private
-
def skip_if_sqlite3_version_includes_quoting_bug
if sqlite3_version_includes_quoting_bug?
skip <<-ERROR.squish
diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb
index 2417775ef1..1a20fe5dc2 100644
--- a/activerecord/test/cases/relations_test.rb
+++ b/activerecord/test/cases/relations_test.rb
@@ -298,7 +298,7 @@ class RelationTest < ActiveRecord::TestCase
end
def test_reverse_order_with_function
- topics = Topic.order(Arel.sql("length(title)")).reverse_order
+ topics = Topic.order("length(title)").reverse_order
assert_equal topics(:second).title, topics.first.title
end
@@ -308,9 +308,9 @@ class RelationTest < ActiveRecord::TestCase
end
def test_reverse_order_with_function_other_predicates
- topics = Topic.order(Arel.sql("author_name, length(title), id")).reverse_order
+ topics = Topic.order("author_name, length(title), id").reverse_order
assert_equal topics(:second).title, topics.first.title
- topics = Topic.order(Arel.sql("length(author_name), id, length(title)")).reverse_order
+ topics = Topic.order("length(author_name), id, length(title)").reverse_order
assert_equal topics(:fifth).title, topics.first.title
end
@@ -337,21 +337,21 @@ class RelationTest < ActiveRecord::TestCase
def test_reverse_order_with_nulls_first_or_last
assert_raises(ActiveRecord::IrreversibleOrderError) do
- Topic.order(Arel.sql("title NULLS FIRST")).reverse_order
+ Topic.order("title NULLS FIRST").reverse_order
end
assert_raises(ActiveRecord::IrreversibleOrderError) do
- Topic.order(Arel.sql("title NULLS FIRST")).reverse_order
+ Topic.order("title NULLS FIRST").reverse_order
end
assert_raises(ActiveRecord::IrreversibleOrderError) do
- Topic.order(Arel.sql("title nulls last")).reverse_order
+ Topic.order("title nulls last").reverse_order
end
assert_raises(ActiveRecord::IrreversibleOrderError) do
- Topic.order(Arel.sql("title NULLS FIRST, author_name")).reverse_order
+ Topic.order("title NULLS FIRST, author_name").reverse_order
end
assert_raises(ActiveRecord::IrreversibleOrderError) do
- Topic.order(Arel.sql("author_name, title nulls last")).reverse_order
+ Topic.order("author_name, title nulls last").reverse_order
end
- end
+ end if current_adapter?(:PostgreSQLAdapter, :OracleAdapter)
def test_default_reverse_order_on_table_without_primary_key
assert_raises(ActiveRecord::IrreversibleOrderError) do
@@ -706,7 +706,7 @@ class RelationTest < ActiveRecord::TestCase
end
def test_to_sql_on_eager_join
- expected = assert_sql {
+ expected = capture_sql {
Post.eager_load(:last_comment).order("comments.id DESC").to_a
}.first
actual = Post.eager_load(:last_comment).order("comments.id DESC").to_sql
@@ -1679,7 +1679,7 @@ class RelationTest < ActiveRecord::TestCase
scope = Post.order("comments.body")
assert_equal ["comments"], scope.references_values
- scope = Post.order(Arel.sql("#{Comment.quoted_table_name}.#{Comment.quoted_primary_key}"))
+ scope = Post.order("#{Comment.quoted_table_name}.#{Comment.quoted_primary_key}")
if current_adapter?(:OracleAdapter)
assert_equal ["COMMENTS"], scope.references_values
else
@@ -1696,7 +1696,7 @@ class RelationTest < ActiveRecord::TestCase
scope = Post.order("comments.body asc")
assert_equal ["comments"], scope.references_values
- scope = Post.order(Arel.sql("foo(comments.body)"))
+ scope = Post.order("foo(comments.body)")
assert_equal [], scope.references_values
end
@@ -1704,7 +1704,7 @@ class RelationTest < ActiveRecord::TestCase
scope = Post.reorder("comments.body")
assert_equal %w(comments), scope.references_values
- scope = Post.reorder(Arel.sql("#{Comment.quoted_table_name}.#{Comment.quoted_primary_key}"))
+ scope = Post.reorder("#{Comment.quoted_table_name}.#{Comment.quoted_primary_key}")
if current_adapter?(:OracleAdapter)
assert_equal ["COMMENTS"], scope.references_values
else
@@ -1721,7 +1721,7 @@ class RelationTest < ActiveRecord::TestCase
scope = Post.reorder("comments.body asc")
assert_equal %w(comments), scope.references_values
- scope = Post.reorder(Arel.sql("foo(comments.body)"))
+ scope = Post.reorder("foo(comments.body)")
assert_equal [], scope.references_values
end
@@ -1955,8 +1955,8 @@ class RelationTest < ActiveRecord::TestCase
test "joins with order by custom attribute" do
companies = Company.create!([{ name: "test1" }, { name: "test2" }])
companies.each { |company| company.contracts.create! }
- assert_equal companies, Company.joins(:contracts).order(:metadata)
- assert_equal companies.reverse, Company.joins(:contracts).order(metadata: :desc)
+ assert_equal companies, Company.joins(:contracts).order(:metadata, :count)
+ assert_equal companies.reverse, Company.joins(:contracts).order(metadata: :desc, count: :desc)
end
test "delegations do not leak to other classes" do
diff --git a/activerecord/test/cases/schema_dumper_test.rb b/activerecord/test/cases/schema_dumper_test.rb
index 49e9be9565..bb7184c5fc 100644
--- a/activerecord/test/cases/schema_dumper_test.rb
+++ b/activerecord/test/cases/schema_dumper_test.rb
@@ -33,6 +33,7 @@ class SchemaDumperTest < ActiveRecord::TestCase
schema_info = ActiveRecord::Base.connection.dump_schema_information
assert_match(/20100201010101.*20100301010101/m, schema_info)
+ assert_includes schema_info, "20100101010101"
ensure
ActiveRecord::SchemaMigration.delete_all
end
diff --git a/activerecord/test/cases/schema_loading_test.rb b/activerecord/test/cases/schema_loading_test.rb
index f539156466..5da2d9e08f 100644
--- a/activerecord/test/cases/schema_loading_test.rb
+++ b/activerecord/test/cases/schema_loading_test.rb
@@ -43,7 +43,6 @@ class SchemaLoadingTest < ActiveRecord::TestCase
end
private
-
def define_model
Class.new(ActiveRecord::Base) do
include SchemaLoadCounter
diff --git a/activerecord/test/cases/tasks/database_tasks_test.rb b/activerecord/test/cases/tasks/database_tasks_test.rb
index dd4a0b0455..6b6861465b 100644
--- a/activerecord/test/cases/tasks/database_tasks_test.rb
+++ b/activerecord/test/cases/tasks/database_tasks_test.rb
@@ -760,7 +760,7 @@ module ActiveRecord
end
class DatabaseTasksMigrateTest < DatabaseTasksMigrationTestCase
- def test_migrate_set_and_unset_verbose_and_version_env_vars
+ def test_can_migrate_from_pending_migration_error_action_dispatch
verbose, version = ENV["VERBOSE"], ENV["VERSION"]
ENV["VERSION"] = "2"
ENV["VERBOSE"] = "false"
@@ -772,7 +772,9 @@ module ActiveRecord
ENV.delete("VERBOSE")
# re-run up migration
- assert_includes capture_migration_output, "migrating"
+ assert_includes(capture(:stdout) do
+ ActiveSupport::ActionableError.dispatch ActiveRecord::PendingMigrationError, "Run pending migrations"
+ end, "migrating")
ensure
ENV["VERBOSE"], ENV["VERSION"] = verbose, version
end
@@ -833,7 +835,6 @@ module ActiveRecord
end
private
-
def capture_migration_status
capture(:stdout) do
ActiveRecord::Tasks::DatabaseTasks.migrate_status
diff --git a/activerecord/test/cases/tasks/mysql_rake_test.rb b/activerecord/test/cases/tasks/mysql_rake_test.rb
index 552e623fd4..ac3c5bc26e 100644
--- a/activerecord/test/cases/tasks/mysql_rake_test.rb
+++ b/activerecord/test/cases/tasks/mysql_rake_test.rb
@@ -7,7 +7,10 @@ if current_adapter?(:Mysql2Adapter)
module ActiveRecord
class MysqlDBCreateTest < ActiveRecord::TestCase
def setup
- @connection = Class.new { def create_database(*); end }.new
+ @connection = Class.new do
+ def create_database(*); end
+ def error_number(_); end
+ end.new
@configuration = {
"adapter" => "mysql2",
"database" => "my-app-db"
@@ -90,9 +93,11 @@ if current_adapter?(:Mysql2Adapter)
with_stubbed_connection_establish_connection do
ActiveRecord::Base.connection.stub(
:create_database,
- proc { raise ActiveRecord::Tasks::DatabaseAlreadyExists }
+ proc { raise ActiveRecord::StatementInvalid }
) do
- ActiveRecord::Tasks::DatabaseTasks.create @configuration
+ ActiveRecord::Base.connection.stub(:error_number, 1007) do
+ ActiveRecord::Tasks::DatabaseTasks.create @configuration
+ end
assert_equal "Database 'my-app-db' already exists\n", $stderr.string
end
@@ -100,7 +105,6 @@ if current_adapter?(:Mysql2Adapter)
end
private
-
def with_stubbed_connection_establish_connection
ActiveRecord::Base.stub(:establish_connection, nil) do
ActiveRecord::Base.stub(:connection, @connection) do
@@ -180,7 +184,6 @@ if current_adapter?(:Mysql2Adapter)
end
private
-
def with_stubbed_connection_establish_connection
ActiveRecord::Base.stub(:establish_connection, nil) do
ActiveRecord::Base.stub(:connection, @connection) do
@@ -233,7 +236,6 @@ if current_adapter?(:Mysql2Adapter)
end
private
-
def with_stubbed_connection_establish_connection
ActiveRecord::Base.stub(:establish_connection, nil) do
ActiveRecord::Base.stub(:connection, @connection) do
diff --git a/activerecord/test/cases/tasks/postgresql_rake_test.rb b/activerecord/test/cases/tasks/postgresql_rake_test.rb
index 065ba7734c..f9df650687 100644
--- a/activerecord/test/cases/tasks/postgresql_rake_test.rb
+++ b/activerecord/test/cases/tasks/postgresql_rake_test.rb
@@ -139,7 +139,6 @@ if current_adapter?(:PostgreSQLAdapter)
end
private
-
def with_stubbed_connection_establish_connection
ActiveRecord::Base.stub(:connection, @connection) do
ActiveRecord::Base.stub(:establish_connection, nil) do
@@ -201,7 +200,6 @@ if current_adapter?(:PostgreSQLAdapter)
end
private
-
def with_stubbed_connection_establish_connection
ActiveRecord::Base.stub(:connection, @connection) do
ActiveRecord::Base.stub(:establish_connection, nil) do
@@ -301,7 +299,6 @@ if current_adapter?(:PostgreSQLAdapter)
end
private
-
def with_stubbed_connection
ActiveRecord::Base.stub(:connection, @connection) do
yield
diff --git a/activerecord/test/cases/test_case.rb b/activerecord/test/cases/test_case.rb
index 5b25432dc0..1b8bad32a4 100644
--- a/activerecord/test/cases/test_case.rb
+++ b/activerecord/test/cases/test_case.rb
@@ -34,7 +34,7 @@ module ActiveRecord
ActiveRecord::Base.connection.materialize_transactions
SQLCounter.clear_log
yield
- SQLCounter.log_all.dup
+ SQLCounter.log.dup
end
def assert_sql(*patterns_to_match)
@@ -107,32 +107,12 @@ module ActiveRecord
clear_log
- self.ignored_sql = [/^PRAGMA/, /^SELECT currval/, /^SELECT CAST/, /^SELECT @@IDENTITY/, /^SELECT @@ROWCOUNT/, /^SAVEPOINT/, /^ROLLBACK TO SAVEPOINT/, /^RELEASE SAVEPOINT/, /^SHOW max_identifier_length/, /^BEGIN/, /^COMMIT/]
-
- # FIXME: this needs to be refactored so specific database can add their own
- # ignored SQL, or better yet, use a different notification for the queries
- # instead examining the SQL content.
- oracle_ignored = [/^select .*nextval/i, /^SAVEPOINT/, /^ROLLBACK TO/, /^\s*select .* from all_triggers/im, /^\s*select .* from all_constraints/im, /^\s*select .* from all_tab_cols/im, /^\s*select .* from all_sequences/im]
- mysql_ignored = [/^SHOW FULL TABLES/i, /^SHOW FULL FIELDS/, /^SHOW CREATE TABLE /i, /^SHOW VARIABLES /, /^\s*SELECT (?:column_name|table_name)\b.*\bFROM information_schema\.(?:key_column_usage|tables)\b/im]
- postgresql_ignored = [/^\s*select\b.*\bfrom\b.*pg_namespace\b/im, /^\s*select tablename\b.*from pg_tables\b/im, /^\s*select\b.*\battname\b.*\bfrom\b.*\bpg_attribute\b/im, /^SHOW search_path/i, /^\s*SELECT\b.*::regtype::oid\b/im]
- sqlite3_ignored = [/^\s*SELECT name\b.*\bFROM sqlite_master/im, /^\s*SELECT sql\b.*\bFROM sqlite_master/im]
-
- [oracle_ignored, mysql_ignored, postgresql_ignored, sqlite3_ignored].each do |db_ignored_sql|
- ignored_sql.concat db_ignored_sql
- end
-
- attr_reader :ignore
-
- def initialize(ignore = Regexp.union(self.class.ignored_sql))
- @ignore = ignore
- end
-
def call(name, start, finish, message_id, values)
return if values[:cached]
sql = values[:sql]
self.class.log_all << sql
- self.class.log << sql unless ignore.match?(sql)
+ self.class.log << sql unless ["SCHEMA", "TRANSACTION"].include? values[:name]
end
end
diff --git a/activerecord/test/cases/transaction_callbacks_test.rb b/activerecord/test/cases/transaction_callbacks_test.rb
index 53fe31e087..19b89ab08c 100644
--- a/activerecord/test/cases/transaction_callbacks_test.rb
+++ b/activerecord/test/cases/transaction_callbacks_test.rb
@@ -36,6 +36,8 @@ class TransactionCallbacksTest < ActiveRecord::TestCase
has_many :replies, class_name: "ReplyWithCallbacks", foreign_key: "parent_id"
+ before_destroy { self.class.find(id).touch if persisted? }
+
before_commit { |record| record.do_before_commit(nil) }
after_commit { |record| record.do_after_commit(nil) }
after_save_commit { |record| record.do_after_commit(:save) }
@@ -458,7 +460,6 @@ class TransactionCallbacksTest < ActiveRecord::TestCase
end
private
-
def add_transaction_execution_blocks(record)
record.after_commit_block(:create) { |r| r.history << :commit_on_create }
record.after_commit_block(:update) { |r| r.history << :commit_on_update }
@@ -549,6 +550,8 @@ class CallbacksOnMultipleActionsTest < ActiveRecord::TestCase
end
class CallbacksOnDestroyUpdateActionRaceTest < ActiveRecord::TestCase
+ self.use_transactional_tests = false
+
class TopicWithHistory < ActiveRecord::Base
self.table_name = :topics
@@ -562,11 +565,22 @@ class CallbacksOnDestroyUpdateActionRaceTest < ActiveRecord::TestCase
end
class TopicWithCallbacksOnDestroy < TopicWithHistory
- after_commit(on: :destroy) { |record| record.class.history << :destroy }
+ after_commit(on: :destroy) { |record| record.class.history << :commit_on_destroy }
+ after_rollback(on: :destroy) { |record| record.class.history << :rollback_on_destroy }
+
+ before_destroy :before_destroy_for_transaction
+
+ private
+ def before_destroy_for_transaction; end
end
class TopicWithCallbacksOnUpdate < TopicWithHistory
- after_commit(on: :update) { |record| record.class.history << :update }
+ after_commit(on: :update) { |record| record.class.history << :commit_on_update }
+
+ before_save :before_save_for_transaction
+
+ private
+ def before_save_for_transaction; end
end
def test_trigger_once_on_multiple_deletions
@@ -574,10 +588,39 @@ class CallbacksOnDestroyUpdateActionRaceTest < ActiveRecord::TestCase
topic = TopicWithCallbacksOnDestroy.new
topic.save
topic_clone = TopicWithCallbacksOnDestroy.find(topic.id)
+
+ topic.define_singleton_method(:before_destroy_for_transaction) do
+ topic_clone.destroy
+ end
+
topic.destroy
- topic_clone.destroy
- assert_equal [:destroy], TopicWithCallbacksOnDestroy.history
+ assert_equal [:commit_on_destroy], TopicWithCallbacksOnDestroy.history
+ end
+
+ def test_rollback_on_multiple_deletions
+ TopicWithCallbacksOnDestroy.clear_history
+ topic = TopicWithCallbacksOnDestroy.new
+ topic.save
+ topic_clone = TopicWithCallbacksOnDestroy.find(topic.id)
+
+ topic.define_singleton_method(:before_destroy_for_transaction) do
+ topic_clone.update!(author_name: "Test Author Clone")
+ topic_clone.destroy
+ end
+
+ TopicWithCallbacksOnDestroy.transaction do
+ topic.update!(author_name: "Test Author")
+ topic.destroy
+ raise ActiveRecord::Rollback
+ end
+
+ assert_not_predicate topic, :destroyed?
+ assert_not_predicate topic_clone, :destroyed?
+ assert_equal [nil, "Test Author"], topic.author_name_change_to_be_saved
+ assert_equal [nil, "Test Author Clone"], topic_clone.author_name_change_to_be_saved
+
+ assert_equal [:rollback_on_destroy], TopicWithCallbacksOnDestroy.history
end
def test_trigger_on_update_where_row_was_deleted
@@ -585,7 +628,11 @@ class CallbacksOnDestroyUpdateActionRaceTest < ActiveRecord::TestCase
topic = TopicWithCallbacksOnUpdate.new
topic.save
topic_clone = TopicWithCallbacksOnUpdate.find(topic.id)
- topic.destroy
+
+ topic_clone.define_singleton_method(:before_save_for_transaction) do
+ topic.destroy
+ end
+
topic_clone.author_name = "Test Author"
topic_clone.save
diff --git a/activerecord/test/cases/transactions_test.rb b/activerecord/test/cases/transactions_test.rb
index 7bad3de343..b5c1cac3d9 100644
--- a/activerecord/test/cases/transactions_test.rb
+++ b/activerecord/test/cases/transactions_test.rb
@@ -18,6 +18,65 @@ class TransactionTest < ActiveRecord::TestCase
@first, @second = Topic.find(1, 2).sort_by(&:id)
end
+ def test_rollback_dirty_changes
+ topic = topics(:fifth)
+
+ ActiveRecord::Base.transaction do
+ topic.update(title: "Ruby on Rails")
+ raise ActiveRecord::Rollback
+ end
+
+ title_change = ["The Fifth Topic of the day", "Ruby on Rails"]
+ assert_equal title_change, topic.changes["title"]
+ end
+
+ def test_rollback_dirty_changes_multiple_saves
+ topic = topics(:fifth)
+
+ ActiveRecord::Base.transaction do
+ topic.update(title: "Ruby on Rails")
+ topic.update(title: "Another Title")
+ raise ActiveRecord::Rollback
+ end
+
+ title_change = ["The Fifth Topic of the day", "Another Title"]
+ assert_equal title_change, topic.changes["title"]
+ end
+
+ def test_rollback_dirty_changes_then_retry_save
+ topic = topics(:fifth)
+
+ ActiveRecord::Base.transaction do
+ topic.update(title: "Ruby on Rails")
+ raise ActiveRecord::Rollback
+ end
+
+ title_change = ["The Fifth Topic of the day", "Ruby on Rails"]
+ assert_equal title_change, topic.changes["title"]
+
+ assert topic.save
+
+ assert_equal title_change, topic.saved_changes["title"]
+ assert_equal topic.title, topic.reload.title
+ end
+
+ def test_rollback_dirty_changes_then_retry_save_on_new_record
+ topic = Topic.new(title: "Ruby on Rails")
+
+ ActiveRecord::Base.transaction do
+ topic.save
+ raise ActiveRecord::Rollback
+ end
+
+ title_change = [nil, "Ruby on Rails"]
+ assert_equal title_change, topic.changes["title"]
+
+ assert topic.save
+
+ assert_equal title_change, topic.saved_changes["title"]
+ assert_equal topic.title, topic.reload.title
+ end
+
def test_persisted_in_a_model_with_custom_primary_key_after_failed_save
movie = Movie.create
assert_not_predicate movie, :persisted?
@@ -1018,7 +1077,6 @@ class TransactionTest < ActiveRecord::TestCase
end
private
-
%w(validation save destroy).each do |filter|
define_method("add_cancelling_before_#{filter}_with_db_side_effect_to_topic") do |topic|
meta = class << topic; self; end
diff --git a/activerecord/test/cases/unsafe_raw_sql_test.rb b/activerecord/test/cases/unsafe_raw_sql_test.rb
index d5d8f2a09a..87edb163f2 100644
--- a/activerecord/test/cases/unsafe_raw_sql_test.rb
+++ b/activerecord/test/cases/unsafe_raw_sql_test.rb
@@ -77,7 +77,7 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase
assert_equal ids_expected, ids_disabled
end
- test "order: allows table and column name" do
+ test "order: allows table and column names" do
ids_expected = Post.order(Arel.sql("title")).pluck(:id)
ids_depr = with_unsafe_raw_sql_deprecated { Post.order("posts.title").pluck(:id) }
@@ -87,6 +87,17 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase
assert_equal ids_expected, ids_disabled
end
+ test "order: allows quoted table and column names" do
+ ids_expected = Post.order(Arel.sql("title")).pluck(:id)
+
+ quoted_title = Post.connection.quote_table_name("posts.title")
+ ids_depr = with_unsafe_raw_sql_deprecated { Post.order(quoted_title).pluck(:id) }
+ ids_disabled = with_unsafe_raw_sql_disabled { Post.order(quoted_title).pluck(:id) }
+
+ assert_equal ids_expected, ids_depr
+ assert_equal ids_expected, ids_disabled
+ end
+
test "order: allows column name and direction in string" do
ids_expected = Post.order(Arel.sql("title desc")).pluck(:id)
@@ -116,10 +127,10 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase
["asc", "desc", ""].each do |direction|
%w(first last).each do |position|
- ids_expected = Post.order(Arel.sql("type #{direction} nulls #{position}")).pluck(:id)
+ ids_expected = Post.order(Arel.sql("type::text #{direction} nulls #{position}")).pluck(:id)
- ids_depr = with_unsafe_raw_sql_deprecated { Post.order("type #{direction} nulls #{position}").pluck(:id) }
- ids_disabled = with_unsafe_raw_sql_disabled { Post.order("type #{direction} nulls #{position}").pluck(:id) }
+ ids_depr = with_unsafe_raw_sql_deprecated { Post.order("type::text #{direction} nulls #{position}").pluck(:id) }
+ ids_disabled = with_unsafe_raw_sql_disabled { Post.order("type::text #{direction} nulls #{position}").pluck(:id) }
assert_equal ids_expected, ids_depr
assert_equal ids_expected, ids_disabled
@@ -130,7 +141,7 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase
test "order: disallows invalid column name" do
with_unsafe_raw_sql_disabled do
assert_raises(ActiveRecord::UnknownAttributeReference) do
- Post.order("len(title) asc").pluck(:id)
+ Post.order("REPLACE(title, 'misc', 'zzzz') asc").pluck(:id)
end
end
end
@@ -146,7 +157,7 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase
test "order: disallows invalid column with direction" do
with_unsafe_raw_sql_disabled do
assert_raises(ActiveRecord::UnknownAttributeReference) do
- Post.order("len(title)" => :asc).pluck(:id)
+ Post.order("REPLACE(title, 'misc', 'zzzz')" => :asc).pluck(:id)
end
end
end
@@ -179,7 +190,7 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase
test "order: disallows invalid Array arguments" do
with_unsafe_raw_sql_disabled do
assert_raises(ActiveRecord::UnknownAttributeReference) do
- Post.order(["author_id", "length(title)"]).pluck(:id)
+ Post.order(["author_id", "REPLACE(title, 'misc', 'zzzz')"]).pluck(:id)
end
end
end
@@ -187,8 +198,8 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase
test "order: allows valid Array arguments" do
ids_expected = Post.order(Arel.sql("author_id, length(title)")).pluck(:id)
- ids_depr = with_unsafe_raw_sql_deprecated { Post.order(["author_id", Arel.sql("length(title)")]).pluck(:id) }
- ids_disabled = with_unsafe_raw_sql_disabled { Post.order(["author_id", Arel.sql("length(title)")]).pluck(:id) }
+ ids_depr = with_unsafe_raw_sql_deprecated { Post.order(["author_id", "length(title)"]).pluck(:id) }
+ ids_disabled = with_unsafe_raw_sql_disabled { Post.order(["author_id", "length(title)"]).pluck(:id) }
assert_equal ids_expected, ids_depr
assert_equal ids_expected, ids_disabled
@@ -197,7 +208,7 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase
test "order: logs deprecation warning for unrecognized column" do
with_unsafe_raw_sql_deprecated do
assert_deprecated(/Dangerous query method/) do
- Post.order("length(title)")
+ Post.order("REPLACE(title, 'misc', 'zzzz')")
end
end
end
@@ -212,6 +223,16 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase
assert_equal titles_expected, titles_disabled
end
+ test "pluck: allows string column name with function and alias" do
+ titles_expected = Post.pluck(Arel.sql("UPPER(title)"))
+
+ titles_depr = with_unsafe_raw_sql_deprecated { Post.pluck("UPPER(title) AS title") }
+ titles_disabled = with_unsafe_raw_sql_disabled { Post.pluck("UPPER(title) AS title") }
+
+ assert_equal titles_expected, titles_depr
+ assert_equal titles_expected, titles_disabled
+ end
+
test "pluck: allows symbol column name" do
titles_expected = Post.pluck(Arel.sql("title"))
@@ -262,10 +283,21 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase
assert_equal titles_expected, titles_disabled
end
+ test "pluck: allows quoted table and column names" do
+ titles_expected = Post.pluck(Arel.sql("title"))
+
+ quoted_title = Post.connection.quote_table_name("posts.title")
+ titles_depr = with_unsafe_raw_sql_deprecated { Post.pluck(quoted_title) }
+ titles_disabled = with_unsafe_raw_sql_disabled { Post.pluck(quoted_title) }
+
+ assert_equal titles_expected, titles_depr
+ assert_equal titles_expected, titles_disabled
+ end
+
test "pluck: disallows invalid column name" do
with_unsafe_raw_sql_disabled do
assert_raises(ActiveRecord::UnknownAttributeReference) do
- Post.pluck("length(title)")
+ Post.pluck("REPLACE(title, 'misc', 'zzzz')")
end
end
end
@@ -273,7 +305,7 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase
test "pluck: disallows invalid column name amongst valid names" do
with_unsafe_raw_sql_disabled do
assert_raises(ActiveRecord::UnknownAttributeReference) do
- Post.pluck(:title, "length(title)")
+ Post.pluck(:title, "REPLACE(title, 'misc', 'zzzz')")
end
end
end
@@ -281,7 +313,7 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase
test "pluck: disallows invalid column names with includes" do
with_unsafe_raw_sql_disabled do
assert_raises(ActiveRecord::UnknownAttributeReference) do
- Post.includes(:comments).pluck(:title, "length(title)")
+ Post.includes(:comments).pluck(:title, "REPLACE(title, 'misc', 'zzzz')")
end
end
end
@@ -296,24 +328,25 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase
test "pluck: logs deprecation warning" do
with_unsafe_raw_sql_deprecated do
assert_deprecated(/Dangerous query method/) do
- Post.includes(:comments).pluck(:title, "length(title)")
+ Post.includes(:comments).pluck(:title, "REPLACE(title, 'misc', 'zzzz')")
end
end
end
- def with_unsafe_raw_sql_disabled(&blk)
- with_config(:disabled, &blk)
- end
+ private
+ def with_unsafe_raw_sql_disabled(&block)
+ with_config(:disabled, &block)
+ end
- def with_unsafe_raw_sql_deprecated(&blk)
- with_config(:deprecated, &blk)
- end
+ def with_unsafe_raw_sql_deprecated(&block)
+ with_config(:deprecated, &block)
+ end
- def with_config(new_value, &blk)
- old_value = ActiveRecord::Base.allow_unsafe_raw_sql
- ActiveRecord::Base.allow_unsafe_raw_sql = new_value
- blk.call
- ensure
- ActiveRecord::Base.allow_unsafe_raw_sql = old_value
- end
+ def with_config(new_value, &block)
+ old_value = ActiveRecord::Base.allow_unsafe_raw_sql
+ ActiveRecord::Base.allow_unsafe_raw_sql = new_value
+ yield
+ ensure
+ ActiveRecord::Base.allow_unsafe_raw_sql = old_value
+ end
end
diff --git a/activerecord/test/cases/validations/i18n_validation_test.rb b/activerecord/test/cases/validations/i18n_validation_test.rb
index b7c52ea18c..4dd8a4a82b 100644
--- a/activerecord/test/cases/validations/i18n_validation_test.rb
+++ b/activerecord/test/cases/validations/i18n_validation_test.rb
@@ -40,19 +40,20 @@ class I18nValidationTest < ActiveRecord::TestCase
COMMON_CASES = [
# [ case, validation_options, generate_message_options]
[ "given no options", {}, {}],
- [ "given custom message", { message: "custom" }, { message: "custom" }],
- [ "given if condition", { if: lambda { true } }, {}],
- [ "given unless condition", { unless: lambda { false } }, {}],
- [ "given option that is not reserved", { format: "jpg" }, { format: "jpg" }],
- [ "given on condition", { on: [:create, :update] }, {}]
+ [ "given custom message", { message: "custom" }, { message: "custom" }],
+ [ "given if condition", { if: lambda { true } }, {}],
+ [ "given unless condition", { unless: lambda { false } }, {}],
+ [ "given option that is not reserved", { format: "jpg" }, { format: "jpg" }],
+ [ "given on condition", { on: [:create, :update] }, {}]
]
COMMON_CASES.each do |name, validation_options, generate_message_options|
test "validates_uniqueness_of on generated message #{name}" do
Topic.validates_uniqueness_of :title, validation_options
@topic.title = unique_topic.title
- assert_called_with(@topic.errors, :generate_message, [:title, :taken, generate_message_options.merge(value: "unique!")]) do
+ assert_called_with(ActiveModel::Error, :generate_message, [:title, :taken, @topic, generate_message_options.merge(value: "unique!")]) do
@topic.valid?
+ @topic.errors.messages
end
end
end
@@ -60,8 +61,9 @@ class I18nValidationTest < ActiveRecord::TestCase
COMMON_CASES.each do |name, validation_options, generate_message_options|
test "validates_associated on generated message #{name}" do
Topic.validates_associated :replies, validation_options
- assert_called_with(replied_topic.errors, :generate_message, [:replies, :invalid, generate_message_options.merge(value: replied_topic.replies)]) do
+ assert_called_with(ActiveModel::Error, :generate_message, [:replies, :invalid, replied_topic, generate_message_options.merge(value: replied_topic.replies)]) do
replied_topic.save
+ replied_topic.errors.messages
end
end
end
diff --git a/activerecord/test/cases/yaml_serialization_test.rb b/activerecord/test/cases/yaml_serialization_test.rb
index 60ebdce178..7003afa33a 100644
--- a/activerecord/test/cases/yaml_serialization_test.rb
+++ b/activerecord/test/cases/yaml_serialization_test.rb
@@ -130,7 +130,6 @@ class YamlSerializationTest < ActiveRecord::TestCase
end
private
-
def yaml_fixture(file_name)
path = File.expand_path(
"../support/yaml_compatibility_fixtures/#{file_name}.yml",
diff --git a/activerecord/test/models/author.rb b/activerecord/test/models/author.rb
index 67be59a1fe..da7e4139b1 100644
--- a/activerecord/test/models/author.rb
+++ b/activerecord/test/models/author.rb
@@ -116,6 +116,7 @@ class Author < ActiveRecord::Base
has_many :tags_with_primary_key, through: :posts
has_many :books
+ has_many :published_books, class_name: "PublishedBook"
has_many :unpublished_books, -> { where(status: [:proposed, :written]) }, class_name: "Book"
has_many :subscriptions, through: :books
has_many :subscribers, -> { order("subscribers.nick") }, through: :subscriptions
@@ -153,6 +154,7 @@ class Author < ActiveRecord::Base
has_many :comments_on_posts_with_default_include, through: :posts_with_default_include, source: :comments
has_many :posts_with_signature, ->(record) { where("posts.title LIKE ?", "%by #{record.name.downcase}%") }, class_name: "Post"
+ has_many :posts_mentioning_author, ->(record = nil) { where("posts.body LIKE ?", "%#{record&.name&.downcase}%") }, class_name: "Post"
has_many :posts_with_extension, -> { order(:title) }, class_name: "Post" do
def extension_method; end
diff --git a/activerecord/test/models/book.rb b/activerecord/test/models/book.rb
index afdda1a81e..43b82e6047 100644
--- a/activerecord/test/models/book.rb
+++ b/activerecord/test/models/book.rb
@@ -24,3 +24,9 @@ class Book < ActiveRecord::Base
"do publish work..."
end
end
+
+class PublishedBook < ActiveRecord::Base
+ self.table_name = "books"
+
+ validates_uniqueness_of :isbn
+end
diff --git a/activerecord/test/models/club.rb b/activerecord/test/models/club.rb
index 13e72e9c50..890e427616 100644
--- a/activerecord/test/models/club.rb
+++ b/activerecord/test/models/club.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
class Club < ActiveRecord::Base
- has_one :membership
+ has_one :membership, touch: true
has_many :memberships, inverse_of: false
has_many :members, through: :memberships
has_one :sponsor
@@ -12,8 +12,9 @@ class Club < ActiveRecord::Base
scope :general, -> { left_joins(:category).where(categories: { name: "General" }).unscope(:limit) }
- private
+ accepts_nested_attributes_for :membership
+ private
def private_method
"I'm sorry sir, this is a *private* club, not a *pirate* club"
end
diff --git a/activerecord/test/models/company.rb b/activerecord/test/models/company.rb
index a0f48d23f1..339b5c8ca8 100644
--- a/activerecord/test/models/company.rb
+++ b/activerecord/test/models/company.rb
@@ -25,7 +25,6 @@ class Company < AbstractCompany
end
private
-
def private_method
"I am Jack's innermost fears and aspirations"
end
diff --git a/activerecord/test/models/company_in_module.rb b/activerecord/test/models/company_in_module.rb
index 52b7e06a63..320b26b950 100644
--- a/activerecord/test/models/company_in_module.rb
+++ b/activerecord/test/models/company_in_module.rb
@@ -91,7 +91,6 @@ module MyApplication
validate :check_empty_credit_limit
private
-
def check_empty_credit_limit
errors.add("credit_card", :blank) if credit_card.blank?
end
diff --git a/activerecord/test/models/contact.rb b/activerecord/test/models/contact.rb
index 6e02ff199b..d5f6f00691 100644
--- a/activerecord/test/models/contact.rb
+++ b/activerecord/test/models/contact.rb
@@ -10,14 +10,14 @@ module ContactFakeColumns
table_name => "id"
}
- column :id, :integer
- column :name, :string
- column :age, :integer
- column :avatar, :binary
- column :created_at, :datetime
- column :awesome, :boolean
- column :preferences, :string
- column :alternative_id, :integer
+ column :id, "integer"
+ column :name, "string"
+ column :age, "integer"
+ column :avatar, "binary"
+ column :created_at, "datetime"
+ column :awesome, "boolean"
+ column :preferences, "string"
+ column :alternative_id, "integer"
serialize :preferences
@@ -37,7 +37,7 @@ end
class ContactSti < ActiveRecord::Base
extend ContactFakeColumns
- column :type, :string
+ column :type, "string"
def type; "ContactSti" end
end
diff --git a/activerecord/test/models/developer.rb b/activerecord/test/models/developer.rb
index c6574cf6e7..92d01ba338 100644
--- a/activerecord/test/models/developer.rb
+++ b/activerecord/test/models/developer.rb
@@ -2,13 +2,13 @@
require "ostruct"
-module DeveloperProjectsAssociationExtension2
- def find_least_recent
- order("id ASC").first
+class Developer < ActiveRecord::Base
+ module ProjectsAssociationExtension2
+ def find_least_recent
+ order("id ASC").first
+ end
end
-end
-class Developer < ActiveRecord::Base
self.ignored_columns = %w(first_name last_name)
has_and_belongs_to_many :projects do
@@ -24,19 +24,19 @@ class Developer < ActiveRecord::Base
has_and_belongs_to_many :shared_computers, class_name: "Computer"
has_and_belongs_to_many :projects_extended_by_name,
- -> { extending(DeveloperProjectsAssociationExtension) },
+ -> { extending(ProjectsAssociationExtension) },
class_name: "Project",
join_table: "developers_projects",
association_foreign_key: "project_id"
has_and_belongs_to_many :projects_extended_by_name_twice,
- -> { extending(DeveloperProjectsAssociationExtension, DeveloperProjectsAssociationExtension2) },
+ -> { extending(ProjectsAssociationExtension, ProjectsAssociationExtension2) },
class_name: "Project",
join_table: "developers_projects",
association_foreign_key: "project_id"
has_and_belongs_to_many :projects_extended_by_name_and_block,
- -> { extending(DeveloperProjectsAssociationExtension) },
+ -> { extending(ProjectsAssociationExtension) },
class_name: "Project",
join_table: "developers_projects",
association_foreign_key: "project_id" do
diff --git a/activerecord/test/models/face.rb b/activerecord/test/models/face.rb
index e900fd40fb..45ccc442ba 100644
--- a/activerecord/test/models/face.rb
+++ b/activerecord/test/models/face.rb
@@ -6,7 +6,7 @@ class Face < ActiveRecord::Base
belongs_to :polymorphic_man, polymorphic: true, inverse_of: :polymorphic_face
# Oracle identifier length is limited to 30 bytes or less, `polymorphic` renamed `poly`
belongs_to :poly_man_without_inverse, polymorphic: true
- # These is a "broken" inverse_of for the purposes of testing
+ # These are "broken" inverse_of associations for the purposes of testing
belongs_to :horrible_man, class_name: "Man", inverse_of: :horrible_face
belongs_to :horrible_polymorphic_man, polymorphic: true, inverse_of: :horrible_polymorphic_face
diff --git a/activerecord/test/models/mouse.rb b/activerecord/test/models/mouse.rb
new file mode 100644
index 0000000000..75a55c125d
--- /dev/null
+++ b/activerecord/test/models/mouse.rb
@@ -0,0 +1,6 @@
+# frozen_string_literal: true
+
+class Mouse < ActiveRecord::Base
+ has_many :squeaks, autosave: true
+ validates :name, presence: true
+end
diff --git a/activerecord/test/models/person.rb b/activerecord/test/models/person.rb
index c3d15a571a..0dfd29e45e 100644
--- a/activerecord/test/models/person.rb
+++ b/activerecord/test/models/person.rb
@@ -101,7 +101,6 @@ class RichPerson < ActiveRecord::Base
before_validation :run_before_validation
private
-
def run_before_create
self.first_name = first_name.to_s + "run_before_create"
end
diff --git a/activerecord/test/models/post.rb b/activerecord/test/models/post.rb
index c34968590f..50c0dddcf2 100644
--- a/activerecord/test/models/post.rb
+++ b/activerecord/test/models/post.rb
@@ -43,6 +43,7 @@ class Post < ActiveRecord::Base
has_one :first_comment, -> { order("id ASC") }, class_name: "Comment"
has_one :last_comment, -> { order("id desc") }, class_name: "Comment"
+ scope :no_comments, -> { left_joins(:comments).where(comments: { id: nil }) }
scope :with_special_comments, -> { joins(:comments).where(comments: { type: "SpecialComment" }) }
scope :with_very_special_comments, -> { joins(:comments).where(comments: { type: "VerySpecialComment" }) }
scope :with_post, ->(post_id) { joins(:comments).where(comments: { post_id: post_id }) }
@@ -323,8 +324,8 @@ class FakeKlass
"posts"
end
- def attribute_alias?(name)
- false
+ def attribute_aliases
+ {}
end
def sanitize_sql(sql)
diff --git a/activerecord/test/models/rating.rb b/activerecord/test/models/rating.rb
index 49aa38285f..2a18ea45ac 100644
--- a/activerecord/test/models/rating.rb
+++ b/activerecord/test/models/rating.rb
@@ -4,4 +4,5 @@ class Rating < ActiveRecord::Base
belongs_to :comment
has_many :taggings, as: :taggable
has_many :taggings_without_tag, -> { left_joins(:tag).where("tags.id": nil) }, as: :taggable, class_name: "Tagging"
+ has_many :taggings_with_no_tag, -> { joins("LEFT OUTER JOIN tags ON tags.id = taggings.tag_id").where("tags.id": nil) }, as: :taggable, class_name: "Tagging"
end
diff --git a/activerecord/test/models/reply.rb b/activerecord/test/models/reply.rb
index b35623a344..f6ab9c8a8f 100644
--- a/activerecord/test/models/reply.rb
+++ b/activerecord/test/models/reply.rb
@@ -34,29 +34,29 @@ class WrongReply < Reply
validate :check_author_name_is_secret, on: :special_case
def check_empty_title
- errors[:title] << "Empty" unless attribute_present?("title")
+ errors.add(:title, "Empty") unless attribute_present?("title")
end
def errors_on_empty_content
- errors[:content] << "Empty" unless attribute_present?("content")
+ errors.add(:content, "Empty") unless attribute_present?("content")
end
def check_content_mismatch
if attribute_present?("title") && attribute_present?("content") && content == "Mismatch"
- errors[:title] << "is Content Mismatch"
+ errors.add(:title, "is Content Mismatch")
end
end
def title_is_wrong_create
- errors[:title] << "is Wrong Create" if attribute_present?("title") && title == "Wrong Create"
+ errors.add(:title, "is Wrong Create") if attribute_present?("title") && title == "Wrong Create"
end
def check_wrong_update
- errors[:title] << "is Wrong Update" if attribute_present?("title") && title == "Wrong Update"
+ errors.add(:title, "is Wrong Update") if attribute_present?("title") && title == "Wrong Update"
end
def check_author_name_is_secret
- errors[:author_name] << "Invalid" unless author_name == "secret"
+ errors.add(:author_name, "Invalid") unless author_name == "secret"
end
end
diff --git a/activerecord/test/models/ship.rb b/activerecord/test/models/ship.rb
index 7973219a79..6bab7a1eb9 100644
--- a/activerecord/test/models/ship.rb
+++ b/activerecord/test/models/ship.rb
@@ -27,7 +27,8 @@ class ShipWithoutNestedAttributes < ActiveRecord::Base
has_many :prisoners, inverse_of: :ship, foreign_key: :ship_id
has_many :parts, class_name: "ShipPart", foreign_key: :ship_id
- validates :name, presence: true
+ validates :name, presence: true, if: -> { true }
+ validates :name, presence: true, if: -> { true }
end
class Prisoner < ActiveRecord::Base
diff --git a/activerecord/test/models/squeak.rb b/activerecord/test/models/squeak.rb
new file mode 100644
index 0000000000..e0a643c238
--- /dev/null
+++ b/activerecord/test/models/squeak.rb
@@ -0,0 +1,6 @@
+# frozen_string_literal: true
+
+class Squeak < ActiveRecord::Base
+ belongs_to :mouse
+ accepts_nested_attributes_for :mouse
+end
diff --git a/activerecord/test/models/topic.rb b/activerecord/test/models/topic.rb
index 77101090f2..7a864c728c 100644
--- a/activerecord/test/models/topic.rb
+++ b/activerecord/test/models/topic.rb
@@ -93,7 +93,6 @@ class Topic < ActiveRecord::Base
end
private
-
def default_written_on
self.written_on = Time.now unless attribute_present?("written_on")
end
diff --git a/activerecord/test/schema/mysql2_specific_schema.rb b/activerecord/test/schema/mysql2_specific_schema.rb
index b143035213..911ac808c6 100644
--- a/activerecord/test/schema/mysql2_specific_schema.rb
+++ b/activerecord/test/schema/mysql2_specific_schema.rb
@@ -62,10 +62,6 @@ ActiveRecord::Schema.define do
t.binary :binary_column, limit: 1
end
- create_table :enum_tests, id: false, force: true do |t|
- t.column :enum_column, "ENUM('text','blob','tiny','medium','long','unsigned','bigint')"
- end
-
execute "DROP PROCEDURE IF EXISTS ten"
execute <<~SQL
diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb
index 7d9b8afeb6..dd0ff759b6 100644
--- a/activerecord/test/schema/schema.rb
+++ b/activerecord/test/schema/schema.rb
@@ -115,7 +115,7 @@ ActiveRecord::Schema.define do
t.column :font_size, :integer, **default_zero
t.column :difficulty, :integer, **default_zero
t.column :cover, :string, default: "hard"
- t.string :isbn
+ t.string :isbn, **case_sensitive_options
t.datetime :published_on
t.index [:author_id, :name], unique: true
t.index :isbn, where: "published_on IS NOT NULL", unique: true
@@ -261,6 +261,7 @@ ActiveRecord::Schema.define do
t.references :developer, index: false
t.references :company, index: false
t.string :metadata
+ t.integer :count
end
create_table :customers, force: true do |t|
@@ -524,6 +525,8 @@ ActiveRecord::Schema.define do
t.integer :club_id, :member_id
t.boolean :favourite, default: false
t.integer :type
+ t.datetime :created_at
+ t.datetime :updated_at
end
create_table :member_types, force: true do |t|
@@ -560,6 +563,10 @@ ActiveRecord::Schema.define do
t.string :type
end
+ create_table :mice, force: true do |t|
+ t.string :name
+ end
+
create_table :movies, force: true, id: false do |t|
t.primary_key :movieid
t.string :name
@@ -840,6 +847,10 @@ ActiveRecord::Schema.define do
end
end
+ create_table :squeaks, force: true do |t|
+ t.integer :mouse_id
+ end
+
create_table :prisoners, force: true do |t|
t.belongs_to :ship
end
diff --git a/activerecord/test/support/config.rb b/activerecord/test/support/config.rb
index de0d90a18f..66ae57b382 100644
--- a/activerecord/test/support/config.rb
+++ b/activerecord/test/support/config.rb
@@ -12,7 +12,6 @@ module ARTest
end
private
-
def config_file
Pathname.new(ENV["ARCONFIG"] || TEST_ROOT + "/config.yml")
end
diff --git a/activestorage/CHANGELOG.md b/activestorage/CHANGELOG.md
index 54fc949172..1475a7a786 100644
--- a/activestorage/CHANGELOG.md
+++ b/activestorage/CHANGELOG.md
@@ -1,175 +1,64 @@
-## Rails 6.0.0.beta3 (March 11, 2019) ##
+* Add `config.active_storage.draw_routes` to disable Active Storage routes.
-* No changes.
+ *Gannon McGibbon*
+* Image analysis is skipped if ImageMagick returns an error.
-## Rails 6.0.0.beta2 (February 25, 2019) ##
-
-* No changes.
-
-
-## Rails 6.0.0.beta1 (January 18, 2019) ##
-
-* [Rename npm package](https://github.com/rails/rails/pull/34905) from
- [`activestorage`](https://www.npmjs.com/package/activestorage) to
- [`@rails/activestorage`](https://www.npmjs.com/package/@rails/activestorage).
-
- *Javan Makhmali*
-
-* Replace `config.active_storage.queue` with two options that indicate which
- queues analysis and purge jobs should use, respectively:
-
- * `config.active_storage.queues.analysis`
- * `config.active_storage.queues.purge`
-
- `config.active_storage.queue` is preferred over the new options when it's
- set, but it is deprecated and will be removed in Rails 6.1.
-
- *George Claghorn*
-
-* Permit generating variants of TIFF images.
-
- *Luciano Sousa*
-
-* Use base36 (all lowercase) for all new Blob keys to prevent
- collisions and undefined behavior with case-insensitive filesystems and
- database indices.
-
- *Julik Tarkhanov*
-
-* It doesn’t include an `X-CSRF-Token` header if a meta tag is not found on
- the page. It previously included one with a value of `undefined`.
-
- *Cameron Bothner*
-
-* Fix `ArgumentError` when uploading to amazon s3
-
- *Hiroki Sanpei*
-
-* Add progressive JPG to default list of variable content types
-
- *Maurice Kühlborn*
-
-* Add `ActiveStorage.routes_prefix` for configuring generated routes.
-
- *Chris Bisnett*
-
-* `ActiveStorage::Service::AzureStorageService` only handles specifically
- relevant types of `Azure::Core::Http::HTTPError`. It previously obscured
- other types of `HTTPError`, which is the azure-storage gem’s catch-all
- exception class.
-
- *Cameron Bothner*
-
-* `ActiveStorage::DiskController#show` generates a 404 Not Found response when
- the requested file is missing from the disk service. It previously raised
- `Errno::ENOENT`.
-
- *Cameron Bothner*
-
-* `ActiveStorage::Blob#download` and `ActiveStorage::Blob#open` raise
- `ActiveStorage::FileNotFoundError` when the corresponding file is missing
- from the storage service. Services translate service-specific missing object
- exceptions (e.g. `Google::Cloud::NotFoundError` for the GCS service and
- `Errno::ENOENT` for the disk service) into
- `ActiveStorage::FileNotFoundError`.
-
- *Cameron Bothner*
-
-* Added the `ActiveStorage::SetCurrent` concern for custom Active Storage
- controllers that can't inherit from `ActiveStorage::BaseController`.
+ `ActiveStorage::Analyzer::ImageAnalyzer#metadata` would previously raise a
+ `MiniMagick::Error`, which caused persistent `ActiveStorage::AnalyzeJob`
+ failures. It now logs the error and returns `{}`, resulting in no metadata
+ being added to the offending image blob.
*George Claghorn*
-* Active Storage error classes like `ActiveStorage::IntegrityError` and
- `ActiveStorage::UnrepresentableError` now inherit from `ActiveStorage::Error`
- instead of `StandardError`. This permits rescuing `ActiveStorage::Error` to
- handle all Active Storage errors.
+* Method calls on singular attachments return `nil` when no file is attached.
- *Andrei Makarov*, *George Claghorn*
-
-* Uploaded files assigned to a record are persisted to storage when the record
- is saved instead of immediately.
-
- In Rails 5.2, the following causes an uploaded file in `params[:avatar]` to
- be stored:
+ Previously, assuming the following User model, `user.avatar.filename` would
+ raise a `Module::DelegationError` if no avatar was attached:
```ruby
- @user.avatar = params[:avatar]
+ class User < ApplicationRecord
+ has_one_attached :avatar
+ end
```
- In Rails 6, the uploaded file is stored when `@user` is successfully saved.
+ They now return `nil`.
- *George Claghorn*
-
-* Add the ability to reflect on defined attachments using the existing
- ActiveRecord reflection mechanism.
-
- *Kevin Deisz*
-
-* Variant arguments of `false` or `nil` will no longer be passed to the
- processor. For example, the following will not have the monochrome
- variation applied:
-
- ```ruby
- avatar.variant(monochrome: false)
- ```
+ *Matthew Tanous*
- *Jacob Smith*
+* The mirror service supports direct uploads.
-* Generated attachment getter and setter methods are created
- within the model's `GeneratedAssociationMethods` module to
- allow overriding and composition using `super`.
+ New files are directly uploaded to the primary service. When a
+ directly-uploaded file is attached to a record, a background job is enqueued
+ to copy it to each secondary service.
- *Josh Susser*, *Jamon Douglas*
+ Configure the queue used to process mirroring jobs by setting
+ `config.active_storage.queues.mirror`. The default is `:active_storage_mirror`.
-* Add `ActiveStorage::Blob#open`, which downloads a blob to a tempfile on disk
- and yields the tempfile. Deprecate `ActiveStorage::Downloading`.
-
- *David Robertson*, *George Claghorn*
-
-* Pass in `identify: false` as an argument when providing a `content_type` for
- `ActiveStorage::Attached::{One,Many}#attach` to bypass automatic content
- type inference. For example:
+ *George Claghorn*
- ```ruby
- @message.image.attach(
- io: File.open('/path/to/file'),
- filename: 'file.pdf',
- content_type: 'application/pdf',
- identify: false
- )
+* The S3 service now permits uploading files larger than 5 gigabytes.
+
+ When uploading a file greater than 100 megabytes in size, the service
+ transparently switches to [multipart uploads](https://docs.aws.amazon.com/AmazonS3/latest/dev/mpuoverview.html)
+ using a part size computed from the file's total size and S3's part count limit.
+
+ No application changes are necessary to take advantage of this feature. You
+ can customize the default 100 MB multipart upload threshold in your S3
+ service's configuration:
+
+ ```yaml
+ production:
+ service: s3
+ access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %>
+ secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %>
+ region: us-east-1
+ bucket: my-bucket
+ upload:
+ multipart_threshold: <%= 250.megabytes %>
```
- *Ryan Davidson*
-
-* The Google Cloud Storage service properly supports streaming downloads.
- It now requires version 1.11 or newer of the google-cloud-storage gem.
-
*George Claghorn*
-* Use the [ImageProcessing](https://github.com/janko-m/image_processing) gem
- for Active Storage variants, and deprecate the MiniMagick backend.
-
- This means that variants are now automatically oriented if the original
- image was rotated. Also, in addition to the existing ImageMagick
- operations, variants can now use `:resize_to_fit`, `:resize_to_fill`, and
- other ImageProcessing macros. These are now recommended over raw `:resize`,
- as they also sharpen the thumbnail after resizing.
-
- The ImageProcessing gem also comes with a backend implemented on
- [libvips](http://jcupitt.github.io/libvips/), an alternative to
- ImageMagick which has significantly better performance than
- ImageMagick in most cases, both in terms of speed and memory usage. In
- Active Storage it's now possible to switch to the libvips backend by
- changing `Rails.application.config.active_storage.variant_processor` to
- `:vips`.
-
- *Janko Marohnić*
-
-* Rails 6 requires Ruby 2.5.0 or newer.
-
- *Jeremy Daer*, *Kasper Timm Hansen*
-
-Please check [5-2-stable](https://github.com/rails/rails/blob/5-2-stable/activestorage/CHANGELOG.md) for previous changes.
+Please check [6-0-stable](https://github.com/rails/rails/blob/6-0-stable/activestorage/CHANGELOG.md) for previous changes.
diff --git a/activestorage/app/jobs/active_storage/mirror_job.rb b/activestorage/app/jobs/active_storage/mirror_job.rb
new file mode 100644
index 0000000000..e34faedb56
--- /dev/null
+++ b/activestorage/app/jobs/active_storage/mirror_job.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+# Provides asynchronous mirroring of directly-uploaded blobs.
+class ActiveStorage::MirrorJob < ActiveStorage::BaseJob
+ queue_as { ActiveStorage.queues[:mirror] }
+
+ discard_on ActiveStorage::FileNotFoundError
+ retry_on ActiveStorage::IntegrityError, attempts: 10, wait: :exponentially_longer
+
+ def perform(key, checksum:)
+ ActiveStorage::Blob.service.try(:mirror, key, checksum: checksum)
+ end
+end
diff --git a/activestorage/app/models/active_storage/attachment.rb b/activestorage/app/models/active_storage/attachment.rb
index 874ba80ca8..1ee43b1cd5 100644
--- a/activestorage/app/models/active_storage/attachment.rb
+++ b/activestorage/app/models/active_storage/attachment.rb
@@ -13,7 +13,7 @@ class ActiveStorage::Attachment < ActiveRecord::Base
delegate_missing_to :blob
- after_create_commit :analyze_blob_later, :identify_blob
+ after_create_commit :mirror_blob_later, :analyze_blob_later, :identify_blob
after_destroy_commit :purge_dependent_blob_later
# Synchronously deletes the attachment and {purges the blob}[rdoc-ref:ActiveStorage::Blob#purge].
@@ -37,6 +37,10 @@ class ActiveStorage::Attachment < ActiveRecord::Base
blob.analyze_later unless blob.analyzed?
end
+ def mirror_blob_later
+ blob.mirror_later
+ end
+
def purge_dependent_blob_later
blob&.purge_later if dependent == :purge_later
end
diff --git a/activestorage/app/models/active_storage/blob.rb b/activestorage/app/models/active_storage/blob.rb
index c9fbafad1f..6a486dd899 100644
--- a/activestorage/app/models/active_storage/blob.rb
+++ b/activestorage/app/models/active_storage/blob.rb
@@ -207,6 +207,9 @@ class ActiveStorage::Blob < ActiveRecord::Base
name: [ "ActiveStorage-#{id}-", filename.extension_with_delimiter ], tmpdir: tmpdir, &block
end
+ def mirror_later #:nodoc:
+ ActiveStorage::MirrorJob.perform_later(key, checksum: checksum) if service.respond_to?(:mirror)
+ end
# Deletes the files on the service associated with the blob. This should only be done if the blob is going to be
# deleted as well or you will essentially have a dead reference. It's recommended to use #purge and #purge_later
diff --git a/activestorage/app/models/active_storage/variant.rb b/activestorage/app/models/active_storage/variant.rb
index bc0058967a..1859b8482e 100644
--- a/activestorage/app/models/active_storage/variant.rb
+++ b/activestorage/app/models/active_storage/variant.rb
@@ -96,19 +96,13 @@ class ActiveStorage::Variant
end
def process
- blob.open do |image|
- transform(image) { |output| upload(output) }
+ blob.open do |input|
+ variation.transform(input, format: format) do |output|
+ service.upload(key, output)
+ end
end
end
- def transform(image, &block)
- variation.transform(image, format: format, &block)
- end
-
- def upload(file)
- service.upload(key, file)
- end
-
def specification
@specification ||=
diff --git a/activestorage/app/models/active_storage/variation.rb b/activestorage/app/models/active_storage/variation.rb
index 41b5a45f53..45ae71e0ca 100644
--- a/activestorage/app/models/active_storage/variation.rb
+++ b/activestorage/app/models/active_storage/variation.rb
@@ -40,7 +40,7 @@ class ActiveStorage::Variation
end
def initialize(transformations)
- @transformations = transformations
+ @transformations = transformations.deep_symbolize_keys
end
# Accepts a File object, performs the +transformations+ against it, and
diff --git a/activestorage/config/routes.rb b/activestorage/config/routes.rb
index 3af7361cff..bde53e72f3 100644
--- a/activestorage/config/routes.rb
+++ b/activestorage/config/routes.rb
@@ -29,4 +29,4 @@ Rails.application.routes.draw do
resolve("ActiveStorage::Blob") { |blob, options| route_for(:rails_blob, blob, options) }
resolve("ActiveStorage::Attachment") { |attachment, options| route_for(:rails_blob, attachment.blob, options) }
-end
+end if ActiveStorage.draw_routes
diff --git a/activestorage/lib/active_storage.rb b/activestorage/lib/active_storage.rb
index 5c5da551ae..c35a9920d6 100644
--- a/activestorage/lib/active_storage.rb
+++ b/activestorage/lib/active_storage.rb
@@ -43,17 +43,26 @@ module ActiveStorage
mattr_accessor :logger
mattr_accessor :verifier
+ mattr_accessor :variant_processor, default: :mini_magick
+
mattr_accessor :queues, default: {}
+
mattr_accessor :previewers, default: []
- mattr_accessor :analyzers, default: []
- mattr_accessor :variant_processor, default: :mini_magick
+ mattr_accessor :analyzers, default: []
+
mattr_accessor :paths, default: {}
- mattr_accessor :variable_content_types, default: []
+
+ mattr_accessor :variable_content_types, default: []
+ mattr_accessor :binary_content_type, default: "application/octet-stream"
mattr_accessor :content_types_to_serve_as_binary, default: []
- mattr_accessor :content_types_allowed_inline, default: []
- mattr_accessor :binary_content_type, default: "application/octet-stream"
+ mattr_accessor :content_types_allowed_inline, default: []
+
mattr_accessor :service_urls_expire_in, default: 5.minutes
+
mattr_accessor :routes_prefix, default: "/rails/active_storage"
+ mattr_accessor :draw_routes, default: true
+
+ mattr_accessor :replace_on_assign_to_many, default: false
module Transformers
extend ActiveSupport::Autoload
diff --git a/activestorage/lib/active_storage/analyzer/image_analyzer.rb b/activestorage/lib/active_storage/analyzer/image_analyzer.rb
index 3b39de91be..bd1bef3076 100644
--- a/activestorage/lib/active_storage/analyzer/image_analyzer.rb
+++ b/activestorage/lib/active_storage/analyzer/image_analyzer.rb
@@ -25,17 +25,27 @@ module ActiveStorage
{ width: image.width, height: image.height }
end
end
- rescue LoadError
- logger.info "Skipping image analysis because the mini_magick gem isn't installed"
- {}
end
private
def read_image
download_blob_to_tempfile do |file|
require "mini_magick"
- yield MiniMagick::Image.new(file.path)
+ image = MiniMagick::Image.new(file.path)
+
+ if image.valid?
+ yield image
+ else
+ logger.info "Skipping image analysis because ImageMagick doesn't support the file"
+ {}
+ end
end
+ rescue LoadError
+ logger.info "Skipping image analysis because the mini_magick gem isn't installed"
+ {}
+ rescue MiniMagick::Error => error
+ logger.error "Skipping image analysis due to an ImageMagick error: #{error.message}"
+ {}
end
def rotated_image?(image)
diff --git a/activestorage/lib/active_storage/attached/model.rb b/activestorage/lib/active_storage/attached/model.rb
index ae7f0685f2..06864a846f 100644
--- a/activestorage/lib/active_storage/attached/model.rb
+++ b/activestorage/lib/active_storage/attached/model.rb
@@ -93,12 +93,19 @@ module ActiveStorage
end
def #{name}=(attachables)
- attachment_changes["#{name}"] =
- if attachables.nil? || Array(attachables).none?
- ActiveStorage::Attached::Changes::DeleteMany.new("#{name}", self)
- else
- ActiveStorage::Attached::Changes::CreateMany.new("#{name}", self, attachables)
+ if ActiveStorage.replace_on_assign_to_many
+ attachment_changes["#{name}"] =
+ if attachables.nil? || Array(attachables).none?
+ ActiveStorage::Attached::Changes::DeleteMany.new("#{name}", self)
+ else
+ ActiveStorage::Attached::Changes::CreateMany.new("#{name}", self, attachables)
+ end
+ else
+ if !attachables.nil? || Array(attachables).any?
+ attachment_changes["#{name}"] =
+ ActiveStorage::Attached::Changes::CreateMany.new("#{name}", self, #{name}.blobs + attachables)
end
+ end
end
CODE
diff --git a/activestorage/lib/active_storage/attached/one.rb b/activestorage/lib/active_storage/attached/one.rb
index c039226fcd..003be1cb43 100644
--- a/activestorage/lib/active_storage/attached/one.rb
+++ b/activestorage/lib/active_storage/attached/one.rb
@@ -3,7 +3,7 @@
module ActiveStorage
# Representation of a single attachment to a model.
class Attached::One < Attached
- delegate_missing_to :attachment
+ delegate_missing_to :attachment, allow_nil: true
# Returns the associated attachment record.
#
diff --git a/activestorage/lib/active_storage/engine.rb b/activestorage/lib/active_storage/engine.rb
index fc75a8f816..9d9cd02d12 100644
--- a/activestorage/lib/active_storage/engine.rb
+++ b/activestorage/lib/active_storage/engine.rb
@@ -24,7 +24,7 @@ module ActiveStorage
config.active_storage.previewers = [ ActiveStorage::Previewer::PopplerPDFPreviewer, ActiveStorage::Previewer::MuPDFPreviewer, ActiveStorage::Previewer::VideoPreviewer ]
config.active_storage.analyzers = [ ActiveStorage::Analyzer::ImageAnalyzer, ActiveStorage::Analyzer::VideoAnalyzer ]
config.active_storage.paths = ActiveSupport::OrderedOptions.new
- config.active_storage.queues = ActiveSupport::OrderedOptions.new
+ config.active_storage.queues = ActiveSupport::InheritableOptions.new(mirror: :active_storage_mirror)
config.active_storage.variable_content_types = %w(
image/png
@@ -33,6 +33,7 @@ module ActiveStorage
image/jpeg
image/pjpeg
image/tiff
+ image/bmp
image/vnd.adobe.photoshop
image/vnd.microsoft.icon
)
@@ -56,6 +57,7 @@ module ActiveStorage
image/jpg
image/jpeg
image/tiff
+ image/bmp
image/vnd.adobe.photoshop
image/vnd.microsoft.icon
application/pdf
@@ -71,12 +73,15 @@ module ActiveStorage
ActiveStorage.analyzers = app.config.active_storage.analyzers || []
ActiveStorage.paths = app.config.active_storage.paths || {}
ActiveStorage.routes_prefix = app.config.active_storage.routes_prefix || "/rails/active_storage"
+ ActiveStorage.draw_routes = app.config.active_storage.draw_routes != false
ActiveStorage.variable_content_types = app.config.active_storage.variable_content_types || []
ActiveStorage.content_types_to_serve_as_binary = app.config.active_storage.content_types_to_serve_as_binary || []
ActiveStorage.service_urls_expire_in = app.config.active_storage.service_urls_expire_in || 5.minutes
ActiveStorage.content_types_allowed_inline = app.config.active_storage.content_types_allowed_inline || []
ActiveStorage.binary_content_type = app.config.active_storage.binary_content_type || "application/octet-stream"
+
+ ActiveStorage.replace_on_assign_to_many = app.config.active_storage.replace_on_assign_to_many || false
end
end
@@ -128,7 +133,7 @@ module ActiveStorage
"config.active_storage.queue is deprecated and will be removed in Rails 6.1. " \
"Set config.active_storage.queues.purge and config.active_storage.queues.analysis instead."
- ActiveStorage.queues = { purge: queue, analysis: queue }
+ ActiveStorage.queues = { purge: queue, analysis: queue, mirror: queue }
else
ActiveStorage.queues = app.config.active_storage.queues || {}
end
diff --git a/activestorage/lib/active_storage/gem_version.rb b/activestorage/lib/active_storage/gem_version.rb
index d8f8577cc3..5ef5d09f28 100644
--- a/activestorage/lib/active_storage/gem_version.rb
+++ b/activestorage/lib/active_storage/gem_version.rb
@@ -8,9 +8,9 @@ module ActiveStorage
module VERSION
MAJOR = 6
- MINOR = 0
+ MINOR = 1
TINY = 0
- PRE = "beta3"
+ PRE = "alpha"
STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
end
diff --git a/activestorage/lib/active_storage/log_subscriber.rb b/activestorage/lib/active_storage/log_subscriber.rb
index 6c0b4c30e7..f823f31093 100644
--- a/activestorage/lib/active_storage/log_subscriber.rb
+++ b/activestorage/lib/active_storage/log_subscriber.rb
@@ -32,6 +32,12 @@ module ActiveStorage
debug event, color("Generated URL for file at key: #{key_in(event)} (#{event.payload[:url]})", BLUE)
end
+ def service_mirror(event)
+ message = "Mirrored file at key: #{key_in(event)}"
+ message += " (checksum: #{event.payload[:checksum]})" if event.payload[:checksum]
+ debug event, color(message, GREEN)
+ end
+
def logger
ActiveStorage.logger
end
diff --git a/activestorage/lib/active_storage/service/azure_storage_service.rb b/activestorage/lib/active_storage/service/azure_storage_service.rb
index 993cc0e5f7..8d77e9b20f 100644
--- a/activestorage/lib/active_storage/service/azure_storage_service.rb
+++ b/activestorage/lib/active_storage/service/azure_storage_service.rb
@@ -17,10 +17,10 @@ module ActiveStorage
@container = container
end
- def upload(key, io, checksum: nil, **)
+ def upload(key, io, checksum: nil, content_type: nil, **)
instrument :upload, key: key, checksum: checksum do
handle_errors do
- blobs.create_block_blob(container, key, IO.try_convert(io) || io, content_md5: checksum)
+ blobs.create_block_blob(container, key, IO.try_convert(io) || io, content_md5: checksum, content_type: content_type)
end
end
end
diff --git a/activestorage/lib/active_storage/service/disk_service.rb b/activestorage/lib/active_storage/service/disk_service.rb
index 67892d43b2..764a447c69 100644
--- a/activestorage/lib/active_storage/service/disk_service.rb
+++ b/activestorage/lib/active_storage/service/disk_service.rb
@@ -84,8 +84,12 @@ module ActiveStorage
purpose: :blob_key }
)
+ current_uri = URI.parse(current_host)
+
generated_url = url_helpers.rails_disk_service_url(verified_key_with_expiration,
- host: current_host,
+ protocol: current_uri.scheme,
+ host: current_uri.host,
+ port: current_uri.port,
disposition: content_disposition,
content_type: content_type,
filename: filename
diff --git a/activestorage/lib/active_storage/service/mirror_service.rb b/activestorage/lib/active_storage/service/mirror_service.rb
index aa41df304e..c44bd1f360 100644
--- a/activestorage/lib/active_storage/service/mirror_service.rb
+++ b/activestorage/lib/active_storage/service/mirror_service.rb
@@ -4,12 +4,17 @@ require "active_support/core_ext/module/delegation"
module ActiveStorage
# Wraps a set of mirror services and provides a single ActiveStorage::Service object that will all
- # have the files uploaded to them. A +primary+ service is designated to answer calls to +download+, +exists?+,
- # and +url+.
+ # have the files uploaded to them. A +primary+ service is designated to answer calls to:
+ # * +download+
+ # * +exists?+
+ # * +url+
+ # * +url_for_direct_upload+
+ # * +headers_for_direct_upload+
class Service::MirrorService < Service
attr_reader :primary, :mirrors
- delegate :download, :download_chunk, :exist?, :url, :path_for, to: :primary
+ delegate :download, :download_chunk, :exist?, :url,
+ :url_for_direct_upload, :headers_for_direct_upload, :path_for, to: :primary
# Stitch together from named services.
def self.build(primary:, mirrors:, configurator:, **options) #:nodoc:
@@ -26,7 +31,8 @@ module ActiveStorage
# ensure a match when the upload has completed or raise an ActiveStorage::IntegrityError.
def upload(key, io, checksum: nil, **options)
each_service.collect do |service|
- service.upload key, io.tap(&:rewind), checksum: checksum, **options
+ io.rewind
+ service.upload key, io, checksum: checksum, **options
end
end
@@ -40,6 +46,21 @@ module ActiveStorage
perform_across_services :delete_prefixed, prefix
end
+
+ # Copy the file at the +key+ from the primary service to each of the mirrors where it doesn't already exist.
+ def mirror(key, checksum:)
+ instrument :mirror, key: key, checksum: checksum do
+ if (mirrors_in_need_of_mirroring = mirrors.select { |service| !service.exist?(key) }).any?
+ primary.open(key, checksum: checksum) do |io|
+ mirrors_in_need_of_mirroring.each do |service|
+ io.rewind
+ service.upload key, io, checksum: checksum
+ end
+ end
+ end
+ end
+ end
+
private
def each_service(&block)
[ primary, *mirrors ].each(&block)
diff --git a/activestorage/lib/active_storage/service/s3_service.rb b/activestorage/lib/active_storage/service/s3_service.rb
index bf94f3f49e..e4bd57048a 100644
--- a/activestorage/lib/active_storage/service/s3_service.rb
+++ b/activestorage/lib/active_storage/service/s3_service.rb
@@ -1,5 +1,7 @@
# frozen_string_literal: true
+gem "aws-sdk-s3", "~> 1.14"
+
require "aws-sdk-s3"
require "active_support/core_ext/numeric/bytes"
@@ -7,20 +9,24 @@ module ActiveStorage
# Wraps the Amazon Simple Storage Service (S3) as an Active Storage service.
# See ActiveStorage::Service for the generic API documentation that applies to all services.
class Service::S3Service < Service
- attr_reader :client, :bucket, :upload_options
+ attr_reader :client, :bucket
+ attr_reader :multipart_upload_threshold, :upload_options
def initialize(bucket:, upload: {}, **options)
@client = Aws::S3::Resource.new(**options)
@bucket = @client.bucket(bucket)
+ @multipart_upload_threshold = upload.fetch(:multipart_threshold, 100.megabytes)
@upload_options = upload
end
def upload(key, io, checksum: nil, content_type: nil, **)
instrument :upload, key: key, checksum: checksum do
- object_for(key).put(upload_options.merge(body: io, content_md5: checksum, content_type: content_type))
- rescue Aws::S3::Errors::BadDigest
- raise ActiveStorage::IntegrityError
+ if io.size < multipart_upload_threshold
+ upload_with_single_part key, io, checksum: checksum, content_type: content_type
+ else
+ upload_with_multipart key, io, content_type: content_type
+ end
end
end
@@ -40,7 +46,7 @@ module ActiveStorage
def download_chunk(key, range)
instrument :download_chunk, key: key, range: range do
- object_for(key).get(range: "bytes=#{range.begin}-#{range.exclude_end? ? range.end - 1 : range.end}").body.read.force_encoding(Encoding::BINARY)
+ object_for(key).get(range: "bytes=#{range.begin}-#{range.exclude_end? ? range.end - 1 : range.end}").body.string.force_encoding(Encoding::BINARY)
rescue Aws::S3::Errors::NoSuchKey
raise ActiveStorage::FileNotFoundError
end
@@ -94,6 +100,24 @@ module ActiveStorage
end
private
+ MAXIMUM_UPLOAD_PARTS_COUNT = 10000
+ MINIMUM_UPLOAD_PART_SIZE = 5.megabytes
+
+ def upload_with_single_part(key, io, checksum: nil, content_type: nil)
+ object_for(key).put(body: io, content_md5: checksum, content_type: content_type, **upload_options)
+ rescue Aws::S3::Errors::BadDigest
+ raise ActiveStorage::IntegrityError
+ end
+
+ def upload_with_multipart(key, io, content_type: nil)
+ part_size = [ io.size.fdiv(MAXIMUM_UPLOAD_PARTS_COUNT).ceil, MINIMUM_UPLOAD_PART_SIZE ].max
+
+ object_for(key).upload_stream(content_type: content_type, part_size: part_size, **upload_options) do |out|
+ IO.copy_stream(io, out)
+ end
+ end
+
+
def object_for(key)
bucket.object(key)
end
@@ -108,7 +132,7 @@ module ActiveStorage
raise ActiveStorage::FileNotFoundError unless object.exists?
while offset < object.content_length
- yield object.get(range: "bytes=#{offset}-#{offset + chunk_size - 1}").body.read.force_encoding(Encoding::BINARY)
+ yield object.get(range: "bytes=#{offset}-#{offset + chunk_size - 1}").body.string.force_encoding(Encoding::BINARY)
offset += chunk_size
end
end
diff --git a/activestorage/package.json b/activestorage/package.json
index c363acebde..39a1ea1383 100644
--- a/activestorage/package.json
+++ b/activestorage/package.json
@@ -1,6 +1,6 @@
{
"name": "@rails/activestorage",
- "version": "6.0.0-beta3",
+ "version": "6.1.0-alpha",
"description": "Attach cloud and local files in Rails applications",
"main": "app/assets/javascripts/activestorage.js",
"files": [
diff --git a/activestorage/test/analyzer/image_analyzer_test.rb b/activestorage/test/analyzer/image_analyzer_test.rb
index 55bb5e7280..73438c15ab 100644
--- a/activestorage/test/analyzer/image_analyzer_test.rb
+++ b/activestorage/test/analyzer/image_analyzer_test.rb
@@ -29,4 +29,12 @@ class ActiveStorage::Analyzer::ImageAnalyzerTest < ActiveSupport::TestCase
assert_equal 792, metadata[:width]
assert_equal 584, metadata[:height]
end
+
+ test "analyzing an unsupported image type" do
+ blob = create_blob(data: "bad", filename: "bad_file.bad", content_type: "image/bad_type")
+ metadata = extract_metadata_from(blob)
+
+ assert_nil metadata[:width]
+ assert_nil metadata[:heigh]
+ end
end
diff --git a/activestorage/test/analyzer/video_analyzer_test.rb b/activestorage/test/analyzer/video_analyzer_test.rb
index d30f49315a..172a2f0aae 100644
--- a/activestorage/test/analyzer/video_analyzer_test.rb
+++ b/activestorage/test/analyzer/video_analyzer_test.rb
@@ -24,7 +24,6 @@ class ActiveStorage::Analyzer::VideoAnalyzerTest < ActiveSupport::TestCase
assert_equal 480, metadata[:width]
assert_equal 640, metadata[:height]
assert_equal [4, 3], metadata[:display_aspect_ratio]
- assert_equal 5.227975, metadata[:duration]
assert_equal 90, metadata[:angle]
end
diff --git a/activestorage/test/controllers/blobs_controller_test.rb b/activestorage/test/controllers/blobs_controller_test.rb
index 9bf2641de6..9c811df895 100644
--- a/activestorage/test/controllers/blobs_controller_test.rb
+++ b/activestorage/test/controllers/blobs_controller_test.rb
@@ -20,28 +20,3 @@ class ActiveStorage::BlobsControllerTest < ActionDispatch::IntegrationTest
assert_equal "max-age=300, private", @response.headers["Cache-Control"]
end
end
-
-if SERVICE_CONFIGURATIONS[:s3] && SERVICE_CONFIGURATIONS[:s3][:access_key_id].present?
- class ActiveStorage::S3BlobsControllerTest < ActionDispatch::IntegrationTest
- setup do
- @old_service = ActiveStorage::Blob.service
- ActiveStorage::Blob.service = ActiveStorage::Service.configure(:s3, SERVICE_CONFIGURATIONS)
- end
-
- teardown do
- ActiveStorage::Blob.service = @old_service
- end
-
- test "allow redirection to the different host" do
- blob = create_file_blob filename: "racecar.jpg"
-
- assert_nothing_raised { get rails_blob_url(blob) }
- assert_response :redirect
- assert_no_match @request.host, @response.headers["Location"]
- ensure
- blob.purge
- end
- end
-else
- puts "Skipping S3 redirection tests because no S3 configuration was supplied"
-end
diff --git a/activestorage/test/controllers/representations_controller_test.rb b/activestorage/test/controllers/representations_controller_test.rb
index 4ae0ff877e..2662cc5283 100644
--- a/activestorage/test/controllers/representations_controller_test.rb
+++ b/activestorage/test/controllers/representations_controller_test.rb
@@ -59,33 +59,3 @@ class ActiveStorage::RepresentationsControllerWithPreviewsTest < ActionDispatch:
assert_response :not_found
end
end
-
-if SERVICE_CONFIGURATIONS[:s3] && SERVICE_CONFIGURATIONS[:s3][:access_key_id].present?
- class ActiveStorage::S3RepresentationsControllerWithVariantsTest < ActionDispatch::IntegrationTest
- setup do
- @old_service = ActiveStorage::Blob.service
- ActiveStorage::Blob.service = ActiveStorage::Service.configure(:s3, SERVICE_CONFIGURATIONS)
- end
-
- teardown do
- ActiveStorage::Blob.service = @old_service
- end
-
- test "allow redirection to the different host" do
- blob = create_file_blob filename: "racecar.jpg"
-
- assert_nothing_raised do
- get rails_blob_representation_url(
- filename: blob.filename,
- signed_blob_id: blob.signed_id,
- variation_key: ActiveStorage::Variation.encode(resize: "100x100"))
- end
- assert_response :redirect
- assert_no_match @request.host, @response.headers["Location"]
- ensure
- blob.purge
- end
- end
-else
- puts "Skipping S3 redirection tests because no S3 configuration was supplied"
-end
diff --git a/activestorage/test/fixtures/files/colors.bmp b/activestorage/test/fixtures/files/colors.bmp
new file mode 100644
index 0000000000..3cc1e8764d
--- /dev/null
+++ b/activestorage/test/fixtures/files/colors.bmp
Binary files differ
diff --git a/activestorage/test/models/attached/many_test.rb b/activestorage/test/models/attached/many_test.rb
index e826109874..39ddecb041 100644
--- a/activestorage/test/models/attached/many_test.rb
+++ b/activestorage/test/models/attached/many_test.rb
@@ -269,44 +269,22 @@ class ActiveStorage::ManyAttachedTest < ActiveSupport::TestCase
end
end
- test "analyzing a new blob from an uploaded file after attaching it to an existing record" do
- perform_enqueued_jobs do
- @user.highlights.attach fixture_file_upload("racecar.jpg")
- end
-
- assert @user.highlights.reload.first.analyzed?
- assert_equal 4104, @user.highlights.first.metadata[:width]
- assert_equal 2736, @user.highlights.first.metadata[:height]
- end
-
- test "analyzing a new blob from an uploaded file after attaching it to an existing record via update" do
- perform_enqueued_jobs do
- @user.update! highlights: [ fixture_file_upload("racecar.jpg") ]
- end
-
- assert @user.highlights.reload.first.analyzed?
- assert_equal 4104, @user.highlights.first.metadata[:width]
- assert_equal 2736, @user.highlights.first.metadata[:height]
- end
+ test "updating an existing record with attachments when appending on assign" do
+ append_on_assign do
+ @user.highlights.attach create_blob(filename: "funky.jpg"), create_blob(filename: "town.jpg")
- test "analyzing a directly-uploaded blob after attaching it to an existing record" do
- perform_enqueued_jobs do
- @user.highlights.attach directly_upload_file_blob(filename: "racecar.jpg")
- end
+ assert_difference -> { @user.reload.highlights.count }, +2 do
+ @user.update! highlights: [ create_blob(filename: "whenever.jpg"), create_blob(filename: "wherever.jpg") ]
+ end
- assert @user.highlights.reload.first.analyzed?
- assert_equal 4104, @user.highlights.first.metadata[:width]
- assert_equal 2736, @user.highlights.first.metadata[:height]
- end
+ assert_no_difference -> { @user.reload.highlights.count } do
+ @user.update! highlights: [ ]
+ end
- test "analyzing a directly-uploaded blob after attaching it to an existing record via update" do
- perform_enqueued_jobs do
- @user.update! highlights: [ directly_upload_file_blob(filename: "racecar.jpg") ]
+ assert_no_difference -> { @user.reload.highlights.count } do
+ @user.update! highlights: nil
+ end
end
-
- assert @user.highlights.reload.first.analyzed?
- assert_equal 4104, @user.highlights.first.metadata[:width]
- assert_equal 2736, @user.highlights.first.metadata[:height]
end
test "attaching existing blobs to a new record" do
@@ -422,24 +400,6 @@ class ActiveStorage::ManyAttachedTest < ActiveSupport::TestCase
assert_equal "Could not find or build blob: expected attachable, got :foo", error.message
end
- test "analyzing a new blob from an uploaded file after attaching it to a new record" do
- perform_enqueued_jobs do
- user = User.create!(name: "Jason", highlights: [ fixture_file_upload("racecar.jpg") ])
- assert user.highlights.reload.first.analyzed?
- assert_equal 4104, user.highlights.first.metadata[:width]
- assert_equal 2736, user.highlights.first.metadata[:height]
- end
- end
-
- test "analyzing a directly-uploaded blob after attaching it to a new record" do
- perform_enqueued_jobs do
- user = User.create!(name: "Jason", highlights: [ directly_upload_file_blob(filename: "racecar.jpg") ])
- assert user.highlights.reload.first.analyzed?
- assert_equal 4104, user.highlights.first.metadata[:width]
- assert_equal 2736, user.highlights.first.metadata[:height]
- end
- end
-
test "detaching" do
[ create_blob(filename: "funky.jpg"), create_blob(filename: "town.jpg") ].tap do |blobs|
@user.highlights.attach blobs
@@ -596,4 +556,12 @@ class ActiveStorage::ManyAttachedTest < ActiveSupport::TestCase
User.remove_method :highlights
end
end
+
+ private
+ def append_on_assign
+ ActiveStorage.replace_on_assign_to_many, previous = false, ActiveStorage.replace_on_assign_to_many
+ yield
+ ensure
+ ActiveStorage.replace_on_assign_to_many = previous
+ end
end
diff --git a/activestorage/test/models/attachment_test.rb b/activestorage/test/models/attachment_test.rb
new file mode 100644
index 0000000000..94f354d116
--- /dev/null
+++ b/activestorage/test/models/attachment_test.rb
@@ -0,0 +1,53 @@
+# frozen_string_literal: true
+
+require "test_helper"
+require "database/setup"
+
+class ActiveStorage::AttachmentTest < ActiveSupport::TestCase
+ include ActiveJob::TestHelper
+
+ setup do
+ @user = User.create!(name: "Josh")
+ end
+
+ teardown { ActiveStorage::Blob.all.each(&:delete) }
+
+ test "analyzing a directly-uploaded blob after attaching it" do
+ blob = directly_upload_file_blob(filename: "racecar.jpg")
+ assert_not blob.analyzed?
+
+ perform_enqueued_jobs do
+ @user.highlights.attach(blob)
+ end
+
+ assert blob.reload.analyzed?
+ assert_equal 4104, blob.metadata[:width]
+ assert_equal 2736, blob.metadata[:height]
+ end
+
+ test "mirroring a directly-uploaded blob after attaching it" do
+ previous_service, ActiveStorage::Blob.service = ActiveStorage::Blob.service, build_mirror_service
+
+ blob = directly_upload_file_blob
+ assert_not ActiveStorage::Blob.service.mirrors.second.exist?(blob.key)
+
+ perform_enqueued_jobs do
+ @user.highlights.attach(blob)
+ end
+
+ assert ActiveStorage::Blob.service.mirrors.second.exist?(blob.key)
+ ensure
+ ActiveStorage::Blob.service = previous_service
+ end
+
+ private
+ def build_mirror_service
+ ActiveStorage::Service::MirrorService.new \
+ primary: build_disk_service("primary"),
+ mirrors: 3.times.collect { |i| build_disk_service("mirror_#{i + 1}") }
+ end
+
+ def build_disk_service(purpose)
+ ActiveStorage::Service::DiskService.new(root: Dir.mktmpdir("active_storage_tests_#{purpose}"))
+ end
+end
diff --git a/activestorage/test/models/variant_test.rb b/activestorage/test/models/variant_test.rb
index d98935eb9f..6b43923159 100644
--- a/activestorage/test/models/variant_test.rb
+++ b/activestorage/test/models/variant_test.rb
@@ -4,6 +4,14 @@ require "test_helper"
require "database/setup"
class ActiveStorage::VariantTest < ActiveSupport::TestCase
+ test "variations have the same key for different types of the same transformation" do
+ blob = create_file_blob(filename: "racecar.jpg")
+ variant_a = blob.variant(resize: "100x100")
+ variant_b = blob.variant("resize" => "100x100")
+
+ assert_equal variant_a.key, variant_b.key
+ end
+
test "resized variation of JPEG blob" do
blob = create_file_blob(filename: "racecar.jpg")
variant = blob.variant(resize: "100x100").processed
@@ -144,6 +152,17 @@ class ActiveStorage::VariantTest < ActiveSupport::TestCase
assert_equal 33, image.height
end
+ test "resized variation of BMP blob" do
+ blob = create_file_blob(filename: "colors.bmp")
+ variant = blob.variant(resize: "15x15").processed
+ assert_match(/colors\.bmp/, variant.service_url)
+
+ image = read_image(variant)
+ assert_equal "BMP", image.type
+ assert_equal 15, image.width
+ assert_equal 8, image.height
+ end
+
test "optimized variation of GIF blob" do
blob = create_file_blob(filename: "image.gif", content_type: "image/gif")
diff --git a/activestorage/test/service/azure_storage_service_test.rb b/activestorage/test/service/azure_storage_service_test.rb
index 2b07902d07..fc7b86ccb0 100644
--- a/activestorage/test/service/azure_storage_service_test.rb
+++ b/activestorage/test/service/azure_storage_service_test.rb
@@ -9,6 +9,20 @@ if SERVICE_CONFIGURATIONS[:azure]
include ActiveStorage::Service::SharedServiceTests
+ test "upload with content_type" do
+ key = SecureRandom.base58(24)
+ data = "Foobar"
+
+ @service.upload(key, StringIO.new(data), checksum: Digest::MD5.base64digest(data), filename: ActiveStorage::Filename.new("test.txt"), content_type: "text/plain")
+
+ url = @service.url(key, expires_in: 2.minutes, disposition: :attachment, content_type: nil, filename: ActiveStorage::Filename.new("test.html"))
+ response = Net::HTTP.get_response(URI(url))
+ assert_equal "text/plain", response.content_type
+ assert_match(/attachment;.*test\.html/, response["Content-Disposition"])
+ ensure
+ @service.delete key
+ end
+
test "signed URL generation" do
url = @service.url(@key, expires_in: 5.minutes,
disposition: :inline, filename: ActiveStorage::Filename.new("avatar.png"), content_type: "image/png")
diff --git a/activestorage/test/service/disk_service_test.rb b/activestorage/test/service/disk_service_test.rb
index f3c4dd26bd..b766cc3f56 100644
--- a/activestorage/test/service/disk_service_test.rb
+++ b/activestorage/test/service/disk_service_test.rb
@@ -8,8 +8,14 @@ class ActiveStorage::Service::DiskServiceTest < ActiveSupport::TestCase
include ActiveStorage::Service::SharedServiceTests
test "URL generation" do
- assert_match(/^https:\/\/example.com\/rails\/active_storage\/disk\/.*\/avatar\.png\?content_type=image%2Fpng&disposition=inline/,
- @service.url(@key, expires_in: 5.minutes, disposition: :inline, filename: ActiveStorage::Filename.new("avatar.png"), content_type: "image/png"))
+ original_url_options = Rails.application.routes.default_url_options.dup
+ Rails.application.routes.default_url_options.merge!(protocol: "http", host: "test.example.com", port: 3001)
+ begin
+ assert_match(/^https:\/\/example.com\/rails\/active_storage\/disk\/.*\/avatar\.png\?content_type=image%2Fpng&disposition=inline/,
+ @service.url(@key, expires_in: 5.minutes, disposition: :inline, filename: ActiveStorage::Filename.new("avatar.png"), content_type: "image/png"))
+ ensure
+ Rails.application.routes.default_url_options = original_url_options
+ end
end
test "headers_for_direct_upload generation" do
diff --git a/activestorage/test/service/mirror_service_test.rb b/activestorage/test/service/mirror_service_test.rb
index 249a5652fb..aa4d610753 100644
--- a/activestorage/test/service/mirror_service_test.rb
+++ b/activestorage/test/service/mirror_service_test.rb
@@ -53,6 +53,20 @@ class ActiveStorage::Service::MirrorServiceTest < ActiveSupport::TestCase
end
end
+ test "mirroring a file from the primary service to secondary services where it doesn't exist" do
+ key = SecureRandom.base58(24)
+ data = "Something else entirely!"
+ checksum = Digest::MD5.base64digest(data)
+
+ @service.primary.upload key, StringIO.new(data), checksum: checksum
+ @service.mirrors.third.upload key, StringIO.new("Surprise!")
+
+ @service.mirror key, checksum: checksum
+ assert_equal data, @service.mirrors.first.download(key)
+ assert_equal data, @service.mirrors.second.download(key)
+ assert_equal "Surprise!", @service.mirrors.third.download(key)
+ end
+
test "URL generation in primary service" do
filename = ActiveStorage::Filename.new("test.txt")
diff --git a/activestorage/test/service/s3_service_test.rb b/activestorage/test/service/s3_service_test.rb
index 74c0aa0405..b9120770e6 100644
--- a/activestorage/test/service/s3_service_test.rb
+++ b/activestorage/test/service/s3_service_test.rb
@@ -46,8 +46,7 @@ if SERVICE_CONFIGURATIONS[:s3]
end
test "uploading with server-side encryption" do
- config = SERVICE_CONFIGURATIONS.deep_merge(s3: { upload: { server_side_encryption: "AES256" } })
- service = ActiveStorage::Service.configure(:s3, config)
+ service = build_service(upload: { server_side_encryption: "AES256" })
begin
key = SecureRandom.base58(24)
@@ -77,6 +76,25 @@ if SERVICE_CONFIGURATIONS[:s3]
ensure
@service.delete key
end
+
+ test "uploading a large object in multiple parts" do
+ service = build_service(upload: { multipart_threshold: 5.megabytes })
+
+ begin
+ key = SecureRandom.base58(24)
+ data = SecureRandom.bytes(8.megabytes)
+
+ service.upload key, StringIO.new(data), checksum: Digest::MD5.base64digest(data)
+ assert data == service.download(key)
+ ensure
+ service.delete key
+ end
+ end
+
+ private
+ def build_service(configuration)
+ ActiveStorage::Service.configure :s3, SERVICE_CONFIGURATIONS.deep_merge(s3: configuration)
+ end
end
else
puts "Skipping S3 Service tests because no S3 configuration was supplied"
diff --git a/activestorage/test/test_helper.rb b/activestorage/test/test_helper.rb
index b34d0d64bb..ac38b9362c 100644
--- a/activestorage/test/test_helper.rb
+++ b/activestorage/test/test_helper.rb
@@ -7,6 +7,7 @@ require "bundler/setup"
require "active_support"
require "active_support/test_case"
require "active_support/testing/autorun"
+require "active_storage/service/mirror_service"
require "image_processing/mini_magick"
begin
@@ -67,7 +68,8 @@ class ActiveSupport::TestCase
checksum = Digest::MD5.file(file).base64digest
create_blob_before_direct_upload(filename: filename, byte_size: byte_size, checksum: checksum, content_type: content_type).tap do |blob|
- ActiveStorage::Blob.service.upload(blob.key, file.open)
+ service = ActiveStorage::Blob.service.try(:primary) || ActiveStorage::Blob.service
+ service.upload(blob.key, file.open)
end
end
diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md
index 57e03b5e12..f20c7c92e6 100644
--- a/activesupport/CHANGELOG.md
+++ b/activesupport/CHANGELOG.md
@@ -1,446 +1,83 @@
-* The Zeitwerk compatibility interface for `ActiveSupport::Dependencies` no
- longer implements `autoloaded_constants` or `autoloaded?` (undocumented,
- anyway). Experience shows introspection does not have many use cases, and
- troubleshooting is done by logging. With this design trade-off we are able
- to use even less memory in all environments.
+* Add `compact_blank` for those times when you want to remove #blank? values from
+ an Enumerable (also `compact_blank!` on Hash, Array, ActionController::Parameters)
- *Xavier Noria*
+ *Dana Sherson*
-* Depends on Zeitwerk 2, which stores less metadata if reloading is disabled
- and hence uses less memory when `config.cache_classes` is `true`, a standard
- setup in production.
+* Make ActiveSupport::Logger Fiber-safe. Fixes #36752.
- *Xavier Noria*
+ Use `Fiber.current.__id__` in `ActiveSupport::Logger#local_level=` in order
+ to make log level local to Ruby Fibers in addition to Threads.
-* In `:zeitwerk` mode, eager load directories in engines and applications only
- if present in their respective `config.eager_load_paths`.
+ Example:
- A common use case for this is adding `lib` to `config.autoload_paths`, but
- not to `config.eager_load_paths`. In that configuration, for example, files
- in the `lib` directory should not be eager loaded.
+ logger = ActiveSupport::Logger.new(STDOUT)
+ logger.level = 1
+ p "Main is debug? #{logger.debug?}"
- *Xavier Noria*
+ Fiber.new {
+ logger.local_level = 0
+ p "Thread is debug? #{logger.debug?}"
+ }.resume
-* Fix bug in Range comparisons when comparing to an excluded-end Range
+ p "Main is debug? #{logger.debug?}"
Before:
- (1..10).cover?(1...11) # => false
+ Main is debug? false
+ Thread is debug? true
+ Main is debug? true
After:
- (1..10).cover?(1...11) # => true
+ Main is debug? false
+ Thread is debug? true
+ Main is debug? false
- With the same change for `Range#include?` and `Range#===`.
+ *Alexander Varnin*
- *Owen Stephens*
-
-* Use weak references in descendants tracker to allow anonymous subclasses to
- be garbage collected.
-
- *Edgars Beigarts*
-
-* Update `ActiveSupport::Notifications::Instrumenter#instrument` to make
- passing a block optional. This will let users use
- `ActiveSupport::Notifications` messaging features outside of
- instrumentation.
-
- *Ali Ibrahim*
-
-* Fix `Time#advance` to work with dates before 1001-03-07
+* Allow the `on_rotation` proc used when decrypting/verifying a message to be
+ passed at the constructor level.
Before:
- Time.utc(1001, 3, 6).advance(years: -1) # => 1000-03-05 00:00:00 UTC
-
- After
-
- Time.utc(1001, 3, 6).advance(years: -1) # => 1000-03-06 00:00:00 UTC
-
- Note that this doesn't affect `DateTime#advance` as that doesn't use a proleptic calendar.
-
- *Andrew White*
-
-* In Zeitwerk mode, engines are now managed by the `main` autoloader. Engines may reference application constants, if the application is reloaded and we do not reload engines, they won't use the reloaded application code.
-
- *Xavier Noria*
-
-* Add support for supplying `locale` to `transliterate` and `parameterize`.
-
- I18n.backend.store_translations(:de, i18n: { transliterate: { rule: { "ü" => "ue" } } })
-
- ActiveSupport::Inflector.transliterate("ü", locale: :de) # => "ue"
- "Fünf autos".parameterize(locale: :de) # => "fuenf-autos"
- ActiveSupport::Inflector.parameterize("Fünf autos", locale: :de) # => "fuenf-autos"
-
- *Kaan Ozkan*, *Sharang Dashputre*
-
-* Allow `Array#excluding` and `Enumerable#excluding` to deal with a passed array gracefully.
-
- [ 1, 2, 3, 4, 5 ].excluding([4, 5]) # => [ 1, 2, 3 ]
-
- *DHH*
-
-* Renamed `Array#without` and `Enumerable#without` to `Array#excluding` and `Enumerable#excluding`, to create parity with
- `Array#including` and `Enumerable#including`. Retained the old names as aliases.
-
- *DHH*
-
-* Added `Array#including` and `Enumerable#including` to conveniently enlarge a collection with more members using a method rather than an operator:
-
- [ 1, 2, 3 ].including(4, 5) # => [ 1, 2, 3, 4, 5 ]
- post.authors.including(Current.person) # => All the authors plus the current person!
-
- *DHH*
-
-
-## Rails 6.0.0.beta3 (March 11, 2019) ##
-
-* No changes.
-
-
-## Rails 6.0.0.beta2 (February 25, 2019) ##
-
-* New autoloading based on [Zeitwerk](https://github.com/fxn/zeitwerk).
-
- *Xavier Noria*
-
-* Revise `ActiveSupport::Notifications.unsubscribe` to correctly handle Regex or other multiple-pattern subscribers.
-
- *Zach Kemp*
-
-* Add `before_reset` callback to `CurrentAttributes` and define `after_reset` as an alias of `resets` for symmetry.
-
- *Rosa Gutierrez*
-
-* Remove the `` Kernel#` `` override that suppresses ENOENT and accidentally returns nil on Unix systems.
-
- *Akinori Musha*
-
-* Add `ActiveSupport::HashWithIndifferentAccess#assoc`.
-
- `assoc` can now be called with either a string or a symbol.
-
- *Stefan Schüßler*
+ crypt = ActiveSupport::MessageEncryptor.new('long_secret')
+ crypt.decrypt_and_verify(encrypted_message, on_rotation: proc { ... })
+ crypt.decrypt_and_verify(another_encrypted_message, on_rotation: proc { ... })
-* Add `Hash#deep_transform_values`, and `Hash#deep_transform_values!`.
-
- *Guillermo Iguaran*
-
-
-## Rails 6.0.0.beta1 (January 18, 2019) ##
-
-* Remove deprecated `Module#reachable?` method.
-
- *Rafael Mendonça França*
-
-* Remove deprecated `#acronym_regex` method from `Inflections`.
-
- *Rafael Mendonça França*
-
-* Fix `String#safe_constantize` throwing a `LoadError` for incorrectly cased constant references.
-
- *Keenan Brock*
-
-* Preserve key order passed to `ActiveSupport::CacheStore#fetch_multi`.
-
- `fetch_multi(*names)` now returns its results in the same order as the `*names` requested, rather than returning cache hits followed by cache misses.
-
- *Gannon McGibbon*
-
-* If the same block is `included` multiple times for a Concern, an exception is no longer raised.
-
- *Mark J. Titorenko*, *Vlad Bokov*
-
-* Fix bug where `#to_options` for `ActiveSupport::HashWithIndifferentAccess`
- would not act as alias for `#symbolize_keys`.
-
- *Nick Weiland*
-
-* Improve the logic that detects non-autoloaded constants.
-
- *Jan Habermann*, *Xavier Noria*
-
-* Deprecate `ActiveSupport::Multibyte::Unicode#pack_graphemes(array)` and `ActiveSuppport::Multibyte::Unicode#unpack_graphemes(string)`
- in favor of `array.flatten.pack("U*")` and `string.scan(/\X/).map(&:codepoints)`, respectively.
-
- *Francesco Rodríguez*
-
-* Deprecate `ActiveSupport::Multibyte::Chars.consumes?` in favor of `String#is_utf8?`.
-
- *Francesco Rodríguez*
-
-* Fix duration being rounded to a full second.
- ```
- time = DateTime.parse("2018-1-1")
- time += 0.51.seconds
- ```
- Will now correctly add 0.51 second and not 1 full second.
-
- *Edouard Chin*
-
-* Deprecate `ActiveSupport::Multibyte::Unicode#normalize` and `ActiveSuppport::Multibyte::Chars#normalize`
- in favor of `String#unicode_normalize`
-
- *Francesco Rodríguez*
-
-* Deprecate `ActiveSupport::Multibyte::Unicode#downcase/upcase/swapcase` in favor of
- `String#downcase/upcase/swapcase`.
-
- *Francesco Rodríguez*
-
-* Add `ActiveSupport::ParameterFilter`.
-
- *Yoshiyuki Kinjo*
-
-* Rename `Module#parent`, `Module#parents`, and `Module#parent_name` to
- `module_parent`, `module_parents`, and `module_parent_name`.
-
- *Gannon McGibbon*
+ After:
-* Deprecate the use of `LoggerSilence` in favor of `ActiveSupport::LoggerSilence`
+ crypt = ActiveSupport::MessageEncryptor.new('long_secret', on_rotation: proc { ... })
+ crypt.decrypt_and_verify(encrypted_message)
+ crypt.decrypt_and_verify(another_encrypted_message)
*Edouard Chin*
-* Deprecate using negative limits in `String#first` and `String#last`.
-
- *Gannon McGibbon*, *Eric Turner*
-
-* Fix bug where `#without` for `ActiveSupport::HashWithIndifferentAccess` would fail
- with symbol arguments
-
- *Abraham Chan*
-
-* Treat `#delete_prefix`, `#delete_suffix` and `#unicode_normalize` results as non-`html_safe`.
- Ensure safety of arguments for `#insert`, `#[]=` and `#replace` calls on `html_safe` Strings.
-
- *Janosch Müller*
+* `delegate_missing_to` would raise a `DelegationError` if the object
+ delegated to was `nil`. Now the `allow_nil` option has been added to enable
+ the user to specify they want `nil` returned in this case.
-* Changed `ActiveSupport::TaggedLogging.new` to return a new logger instance instead
- of mutating the one received as parameter.
+ *Matthew Tanous*
- *Thierry Joyal*
+* `truncate` would return the original string if it was too short to be truncated
+ and a frozen string if it were long enough to be truncated. Now truncate will
+ consistently return an unfrozen string regardless. This behavior is consistent
+ with `gsub` and `strip`.
-* Define `unfreeze_time` as an alias of `travel_back` in `ActiveSupport::Testing::TimeHelpers`.
-
- The alias is provided for symmetry with `freeze_time`.
-
- *Ryan Davidson*
-
-* Add support for tracing constant autoloads. Just throw
-
- ActiveSupport::Dependencies.logger = Rails.logger
- ActiveSupport::Dependencies.verbose = true
-
- in an initializer.
-
- *Xavier Noria*
-
-* Maintain `html_safe?` on html_safe strings when sliced.
-
- string = "<div>test</div>".html_safe
- string[-1..1].html_safe? # => true
-
- *Elom Gomez*, *Yumin Wong*
-
-* Add `Array#extract!`.
-
- The method removes and returns the elements for which the block returns a true value.
- If no block is given, an Enumerator is returned instead.
-
- numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
- odd_numbers = numbers.extract! { |number| number.odd? } # => [1, 3, 5, 7, 9]
- numbers # => [0, 2, 4, 6, 8]
-
- *bogdanvlviv*
-
-* Support not to cache `nil` for `ActiveSupport::Cache#fetch`.
-
- cache.fetch('bar', skip_nil: true) { nil }
- cache.exist?('bar') # => false
-
- *Martin Hong*
-
-* Add "event object" support to the notification system.
- Before this change, end users were forced to create hand made artisanal
- event objects on their own, like this:
-
- ActiveSupport::Notifications.subscribe('wait') do |*args|
- @event = ActiveSupport::Notifications::Event.new(*args)
- end
-
- ActiveSupport::Notifications.instrument('wait') do
- sleep 1
- end
-
- @event.duration # => 1000.138
-
- After this change, if the block passed to `subscribe` only takes one
- parameter, the framework will yield an event object to the block. Now
- end users are no longer required to make their own:
-
- ActiveSupport::Notifications.subscribe('wait') do |event|
- @event = event
- end
-
- ActiveSupport::Notifications.instrument('wait') do
- sleep 1
- end
-
- p @event.allocations # => 7
- p @event.cpu_time # => 0.256
- p @event.idle_time # => 1003.2399
-
- Now you can enjoy event objects without making them yourself. Neat!
-
- *Aaron "t.lo" Patterson*
-
-* Add cpu_time, idle_time, and allocations to Event.
-
- *Eileen M. Uchitelle*, *Aaron Patterson*
-
-* RedisCacheStore: support key expiry in increment/decrement.
-
- Pass `:expires_in` to `#increment` and `#decrement` to set a Redis EXPIRE on the key.
-
- If the key is already set to expire, RedisCacheStore won't extend its expiry.
-
- Rails.cache.increment("some_key", 1, expires_in: 2.minutes)
-
- *Jason Lee*
-
-* Allow `Range#===` and `Range#cover?` on Range.
-
- `Range#cover?` can now accept a range argument like `Range#include?` and
- `Range#===`. `Range#===` works correctly on Ruby 2.6. `Range#include?` is moved
- into a new file, with these two methods.
-
- *Requiring active_support/core_ext/range/include_range is now deprecated.*
- *Use `require "active_support/core_ext/range/compare_range"` instead.*
-
- *utilum*
-
-* Add `index_with` to Enumerable.
-
- Allows creating a hash from an enumerable with the value from a passed block
- or a default argument.
-
- %i( title body ).index_with { |attr| post.public_send(attr) }
- # => { title: "hey", body: "what's up?" }
-
- %i( title body ).index_with(nil)
- # => { title: nil, body: nil }
-
- Closely linked with `index_by`, which creates a hash where the keys are extracted from a block.
-
- *Kasper Timm Hansen*
-
-* Fix bug where `ActiveSupport::TimeZone.all` would fail when tzinfo data for
- any timezone defined in `ActiveSupport::TimeZone::MAPPING` is missing.
-
- *Dominik Sander*
-
-* Redis cache store: `delete_matched` no longer blocks the Redis server.
- (Switches from evaled Lua to a batched SCAN + DEL loop.)
-
- *Gleb Mazovetskiy*
-
-* Fix bug where `ActiveSupport::Cache` will massively inflate the storage
- size when compression is enabled (which is true by default). This patch
- does not attempt to repair existing data: please manually flush the cache
- to clear out the problematic entries.
-
- *Godfrey Chan*
-
-* Fix bug where `URI.unescape` would fail with mixed Unicode/escaped character input:
-
- URI.unescape("\xe3\x83\x90") # => "バ"
- URI.unescape("%E3%83%90") # => "バ"
- URI.unescape("\xe3\x83\x90%E3%83%90") # => Encoding::CompatibilityError
-
- *Ashe Connor*, *Aaron Patterson*
-
-* Add `before?` and `after?` methods to `Date`, `DateTime`,
- `Time`, and `TimeWithZone`.
-
- *Nick Holden*
-
-* `ActiveSupport::Inflector#ordinal` and `ActiveSupport::Inflector#ordinalize` now support
- translations through I18n.
-
- # locale/fr.rb
-
- {
- fr: {
- number: {
- nth: {
- ordinals: lambda do |_key, number:, **_options|
- if number.to_i.abs == 1
- 'er'
- else
- 'e'
- end
- end,
-
- ordinalized: lambda do |_key, number:, **_options|
- "#{number}#{ActiveSupport::Inflector.ordinal(number)}"
- end
- }
- }
- }
- }
-
-
- *Christian Blais*
-
-* Add `:private` option to ActiveSupport's `Module#delegate`
- in order to delegate methods as private:
-
- class User < ActiveRecord::Base
- has_one :profile
- delegate :date_of_birth, to: :profile, private: true
-
- def age
- Date.today.year - date_of_birth.year
- end
- end
-
- # User.new.age # => 29
- # User.new.date_of_birth
- # => NoMethodError: private method `date_of_birth' called for #<User:0x00000008221340>
-
- *Tomas Valent*
-
-* `String#truncate_bytes` to truncate a string to a maximum bytesize without
- breaking multibyte characters or grapheme clusters like 👩‍👩‍👦‍👦.
-
- *Jeremy Daer*
-
-* `String#strip_heredoc` preserves frozenness.
-
- "foo".freeze.strip_heredoc.frozen? # => true
-
- Fixes that frozen string literals would inadvertently become unfrozen:
-
- # frozen_string_literal: true
-
- foo = <<-MSG.strip_heredoc
- la la la
- MSG
-
- foo.frozen? # => false !??
-
- *Jeremy Daer*
-
-* Rails 6 requires Ruby 2.5.0 or newer.
+ Before:
- *Jeremy Daer*, *Kasper Timm Hansen*
+ 'foobar'.truncate(5).frozen?
+ # => true
+ 'foobar'.truncate(6).frozen?
+ # => false
-* Adds parallel testing to Rails.
+ After:
- Parallelize your test suite with forked processes or threads.
+ 'foobar'.truncate(5).frozen?
+ # => false
+ 'foobar'.truncate(6).frozen?
+ # => false
- *Eileen M. Uchitelle*, *Aaron Patterson*
+ *Jordan Thomas*
-Please check [5-2-stable](https://github.com/rails/rails/blob/5-2-stable/activesupport/CHANGELOG.md) for previous changes.
+Please check [6-0-stable](https://github.com/rails/rails/blob/6-0-stable/activesupport/CHANGELOG.md) for previous changes.
diff --git a/activesupport/activesupport.gemspec b/activesupport/activesupport.gemspec
index dd2340cdd3..da295a0d35 100644
--- a/activesupport/activesupport.gemspec
+++ b/activesupport/activesupport.gemspec
@@ -34,5 +34,5 @@ Gem::Specification.new do |s|
s.add_dependency "tzinfo", "~> 1.1"
s.add_dependency "minitest", "~> 5.1"
s.add_dependency "concurrent-ruby", "~> 1.0", ">= 1.0.2"
- s.add_dependency "zeitwerk", "~> 2.1", ">= 2.1.2"
+ s.add_dependency "zeitwerk", "~> 2.1", ">= 2.1.8"
end
diff --git a/activesupport/lib/active_support.rb b/activesupport/lib/active_support.rb
index 5589c71281..9e242ddeaa 100644
--- a/activesupport/lib/active_support.rb
+++ b/activesupport/lib/active_support.rb
@@ -34,6 +34,7 @@ module ActiveSupport
extend ActiveSupport::Autoload
autoload :Concern
+ autoload :ActionableError
autoload :CurrentAttributes
autoload :Dependencies
autoload :DescendantsTracker
diff --git a/activesupport/lib/active_support/actionable_error.rb b/activesupport/lib/active_support/actionable_error.rb
new file mode 100644
index 0000000000..7db14cd178
--- /dev/null
+++ b/activesupport/lib/active_support/actionable_error.rb
@@ -0,0 +1,48 @@
+# frozen_string_literal: true
+
+module ActiveSupport
+ # Actionable errors let's you define actions to resolve an error.
+ #
+ # To make an error actionable, include the <tt>ActiveSupport::ActionableError</tt>
+ # module and invoke the +action+ class macro to define the action. An action
+ # needs a name and a block to execute.
+ module ActionableError
+ extend Concern
+
+ class NonActionable < StandardError; end
+
+ included do
+ class_attribute :_actions, default: {}
+ end
+
+ def self.actions(error) # :nodoc:
+ case error
+ when ActionableError, -> it { Class === it && it < ActionableError }
+ error._actions
+ else
+ {}
+ end
+ end
+
+ def self.dispatch(error, name) # :nodoc:
+ actions(error).fetch(name).call
+ rescue KeyError
+ raise NonActionable, "Cannot find action \"#{name}\""
+ end
+
+ module ClassMethods
+ # Defines an action that can resolve the error.
+ #
+ # class PendingMigrationError < MigrationError
+ # include ActiveSupport::ActionableError
+ #
+ # action "Run pending migrations" do
+ # ActiveRecord::Tasks::DatabaseTasks.migrate
+ # end
+ # end
+ def action(name, &block)
+ _actions[name] = block
+ end
+ end
+ end
+end
diff --git a/activesupport/lib/active_support/backtrace_cleaner.rb b/activesupport/lib/active_support/backtrace_cleaner.rb
index 62973eca58..f55e821e10 100644
--- a/activesupport/lib/active_support/backtrace_cleaner.rb
+++ b/activesupport/lib/active_support/backtrace_cleaner.rb
@@ -85,7 +85,6 @@ module ActiveSupport
end
private
-
FORMATTED_GEMS_PATTERN = /\A[^\/]+ \([\w.]+\) /
def add_gem_filter
@@ -122,7 +121,11 @@ module ActiveSupport
end
def noise(backtrace)
- backtrace - silence(backtrace)
+ backtrace.select do |line|
+ @silencers.any? do |s|
+ s.call(line)
+ end
+ end
end
end
end
diff --git a/activesupport/lib/active_support/cache.rb b/activesupport/lib/active_support/cache.rb
index e055135bb4..a5063d0784 100644
--- a/activesupport/lib/active_support/cache.rb
+++ b/activesupport/lib/active_support/cache.rb
@@ -678,18 +678,15 @@ module ActiveSupport
end
def instrument(operation, key, options = nil)
- log { "Cache #{operation}: #{normalize_key(key, options)}#{options.blank? ? "" : " (#{options.inspect})"}" }
+ if logger && logger.debug? && !silence?
+ logger.debug "Cache #{operation}: #{normalize_key(key, options)}#{options.blank? ? "" : " (#{options.inspect})"}"
+ end
payload = { key: key }
payload.merge!(options) if options.is_a?(Hash)
ActiveSupport::Notifications.instrument("cache_#{operation}.active_support", payload) { yield(payload) }
end
- def log
- return unless logger && logger.debug? && !silence?
- logger.debug(yield)
- end
-
def handle_expired_entry(entry, key, options)
if entry && entry.expired?
race_ttl = options[:race_condition_ttl].to_i
diff --git a/activesupport/lib/active_support/cache/file_store.rb b/activesupport/lib/active_support/cache/file_store.rb
index f43894a1ea..925440a23f 100644
--- a/activesupport/lib/active_support/cache/file_store.rb
+++ b/activesupport/lib/active_support/cache/file_store.rb
@@ -72,7 +72,6 @@ module ActiveSupport
end
private
-
def read_entry(key, options)
if File.exist?(key)
File.open(key) { |f| Marshal.load(f) }
diff --git a/activesupport/lib/active_support/cache/memory_store.rb b/activesupport/lib/active_support/cache/memory_store.rb
index 629eb2dd70..fa24da91b4 100644
--- a/activesupport/lib/active_support/cache/memory_store.rb
+++ b/activesupport/lib/active_support/cache/memory_store.rb
@@ -114,7 +114,6 @@ module ActiveSupport
end
private
-
PER_ENTRY_OVERHEAD = 240
def cached_size(key, entry)
diff --git a/activesupport/lib/active_support/cache/redis_cache_store.rb b/activesupport/lib/active_support/cache/redis_cache_store.rb
index 87f9aa5346..bb092fcde9 100644
--- a/activesupport/lib/active_support/cache/redis_cache_store.rb
+++ b/activesupport/lib/active_support/cache/redis_cache_store.rb
@@ -152,12 +152,14 @@ module ActiveSupport
# Creates a new Redis cache store.
#
- # Handles three options: block provided to instantiate, single URL
- # provided, and multiple URLs provided.
+ # Handles four options: :redis block, :redis instance, single :url
+ # string, and multiple :url strings.
#
- # :redis Proc -> options[:redis].call
- # :url String -> Redis.new(url: …)
- # :url Array -> Redis::Distributed.new([{ url: … }, { url: … }, …])
+ # Option Class Result
+ # :redis Proc -> options[:redis].call
+ # :redis Object -> options[:redis]
+ # :url String -> Redis.new(url: …)
+ # :url Array -> Redis::Distributed.new([{ url: … }, { url: … }, …])
#
# No namespace is set by default. Provide one if the Redis cache
# server is shared with other apps: <tt>namespace: 'myapp-cache'</tt>.
diff --git a/activesupport/lib/active_support/cache/strategy/local_cache.rb b/activesupport/lib/active_support/cache/strategy/local_cache.rb
index 39b32fc7f6..8e80946fbb 100644
--- a/activesupport/lib/active_support/cache/strategy/local_cache.rb
+++ b/activesupport/lib/active_support/cache/strategy/local_cache.rb
@@ -1,6 +1,5 @@
# frozen_string_literal: true
-require "active_support/core_ext/object/duplicable"
require "active_support/core_ext/string/inflections"
require "active_support/per_thread_registry"
@@ -75,7 +74,10 @@ module ActiveSupport
end
def fetch_entry(key, options = nil) # :nodoc:
- @data.fetch(key) { @data[key] = yield }
+ entry = @data.fetch(key) { @data[key] = yield }
+ dup_entry = entry.dup
+ dup_entry&.dup_value!
+ dup_entry
end
end
diff --git a/activesupport/lib/active_support/callbacks.rb b/activesupport/lib/active_support/callbacks.rb
index d0644a0f7e..daf98c9528 100644
--- a/activesupport/lib/active_support/callbacks.rb
+++ b/activesupport/lib/active_support/callbacks.rb
@@ -7,7 +7,6 @@ require "active_support/core_ext/class/attribute"
require "active_support/core_ext/kernel/reporting"
require "active_support/core_ext/kernel/singleton_class"
require "active_support/core_ext/string/filters"
-require "active_support/deprecation"
require "thread"
module ActiveSupport
@@ -142,7 +141,6 @@ module ActiveSupport
end
private
-
# A hook invoked every time a before callback is halted.
# This can be overridden in ActiveSupport::Callbacks implementors in order
# to provide better debugging/logging.
@@ -582,7 +580,6 @@ module ActiveSupport
attr_reader :chain
private
-
def append_one(callback)
@callbacks = nil
remove_duplicates(callback)
@@ -843,7 +840,6 @@ module ActiveSupport
end
protected
-
def get_callbacks(name) # :nodoc:
__callbacks[name.to_sym]
end
diff --git a/activesupport/lib/active_support/concern.rb b/activesupport/lib/active_support/concern.rb
index 5d356a0ab6..708c445031 100644
--- a/activesupport/lib/active_support/concern.rb
+++ b/activesupport/lib/active_support/concern.rb
@@ -110,7 +110,7 @@ module ActiveSupport
base.instance_variable_set(:@_dependencies, [])
end
- def append_features(base)
+ def append_features(base) #:nodoc:
if base.instance_variable_defined?(:@_dependencies)
base.instance_variable_get(:@_dependencies) << self
false
@@ -123,6 +123,9 @@ module ActiveSupport
end
end
+ # Evaluate given block in context of base class,
+ # so that you can write class macros here.
+ # When you define more than one +included+ block, it raises an exception.
def included(base = nil, &block)
if base.nil?
if instance_variable_defined?(:@_included_block)
@@ -137,6 +140,26 @@ module ActiveSupport
end
end
+ # Define class methods from given block.
+ # You can define private class methods as well.
+ #
+ # module Example
+ # extend ActiveSupport::Concern
+ #
+ # class_methods do
+ # def foo; puts 'foo'; end
+ #
+ # private
+ # def bar; puts 'bar'; end
+ # end
+ # end
+ #
+ # class Buzz
+ # include Example
+ # end
+ #
+ # Buzz.foo # => "foo"
+ # Buzz.bar # => private method 'bar' called for Buzz:Class(NoMethodError)
def class_methods(&class_methods_module_definition)
mod = const_defined?(:ClassMethods, false) ?
const_get(:ClassMethods) :
diff --git a/activesupport/lib/active_support/concurrency/share_lock.rb b/activesupport/lib/active_support/concurrency/share_lock.rb
index f18ccf1c88..eae7d4469f 100644
--- a/activesupport/lib/active_support/concurrency/share_lock.rb
+++ b/activesupport/lib/active_support/concurrency/share_lock.rb
@@ -200,7 +200,6 @@ module ActiveSupport
end
private
-
# Must be called within synchronize
def busy_for_exclusive?(purpose)
busy_for_sharing?(purpose) ||
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 e2e11545e2..c7a2378e41 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
@@ -20,21 +20,11 @@ module DateAndTime
advance(days: -1)
end
- # Returns a new date/time the specified number of days ago.
- def prev_day(days = 1)
- advance(days: -days)
- end
-
# Returns a new date/time representing tomorrow.
def tomorrow
advance(days: 1)
end
- # Returns a new date/time the specified number of days in the future.
- def next_day(days = 1)
- advance(days: days)
- end
-
# Returns true if the date/time is today.
def today?
to_date == ::Date.current
@@ -198,21 +188,11 @@ module DateAndTime
end
end
- # Returns a new date/time the specified number of months in the future.
- def next_month(months = 1)
- advance(months: months)
- end
-
# Short-hand for months_since(3)
def next_quarter
months_since(3)
end
- # Returns a new date/time the specified number of years in the future.
- def next_year(years = 1)
- advance(years: years)
- end
-
# Returns a new date/time representing the given day in the previous week.
# Week is assumed to start on +start_day+, default is
# +Date.beginning_of_week+ or +config.beginning_of_week+ when set.
@@ -233,11 +213,6 @@ module DateAndTime
end
alias_method :last_weekday, :prev_weekday
- # Returns a new date/time the specified number of months ago.
- def prev_month(months = 1)
- advance(months: -months)
- end
-
# Short-hand for months_ago(1).
def last_month
months_ago(1)
@@ -249,11 +224,6 @@ module DateAndTime
end
alias_method :last_quarter, :prev_quarter
- # Returns a new date/time the specified number of years ago.
- def prev_year(years = 1)
- advance(years: -years)
- end
-
# Short-hand for years_ago(1).
def last_year
years_ago(1)
diff --git a/activesupport/lib/active_support/core_ext/date_and_time/zones.rb b/activesupport/lib/active_support/core_ext/date_and_time/zones.rb
index 894fd9b76d..fb6a27cb27 100644
--- a/activesupport/lib/active_support/core_ext/date_and_time/zones.rb
+++ b/activesupport/lib/active_support/core_ext/date_and_time/zones.rb
@@ -29,7 +29,6 @@ module DateAndTime
end
private
-
def time_with_zone(time, zone)
if time
ActiveSupport::TimeWithZone.new(time.utc? ? time : time.getutc, zone)
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 29725c89f7..231bf870a2 100644
--- a/activesupport/lib/active_support/core_ext/date_time/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/date_time/conversions.rb
@@ -96,7 +96,6 @@ class DateTime
end
private
-
def offset_in_seconds
(offset * 86400).to_i
end
diff --git a/activesupport/lib/active_support/core_ext/digest.rb b/activesupport/lib/active_support/core_ext/digest.rb
new file mode 100644
index 0000000000..ce1427e13a
--- /dev/null
+++ b/activesupport/lib/active_support/core_ext/digest.rb
@@ -0,0 +1,3 @@
+# frozen_string_literal: true
+
+require "active_support/core_ext/digest/uuid"
diff --git a/activesupport/lib/active_support/core_ext/enumerable.rb b/activesupport/lib/active_support/core_ext/enumerable.rb
index 4675c41936..728d332306 100644
--- a/activesupport/lib/active_support/core_ext/enumerable.rb
+++ b/activesupport/lib/active_support/core_ext/enumerable.rb
@@ -148,6 +148,41 @@ module Enumerable
map { |element| element[keys.first] }
end
end
+
+ # Returns a new +Array+ without the blank items.
+ # Uses Object#blank? for determining if an item is blank.
+ #
+ # [1, "", nil, 2, " ", [], {}, false, true].compact_blank
+ # # => [1, 2, true]
+ #
+ # Set.new([nil, "", 1, 2])
+ # # => [2, 1] (or [1, 2])
+ #
+ # When called on a +Hash+, returns a new +Hash+ without the blank values.
+ #
+ # { a: "", b: 1, c: nil, d: [], e: false, f: true }.compact_blank
+ # #=> { b: 1, f: true }
+ def compact_blank
+ reject(&:blank?)
+ end
+end
+
+class Hash
+ # Hash#reject has its own definition, so this needs one too.
+ def compact_blank #:nodoc:
+ reject { |_k, v| v.blank? }
+ end
+
+ # Removes all blank values from the +Hash+ in place and returns self.
+ # Uses Object#blank? for determining if a value is blank.
+ #
+ # h = { a: "", b: 1, c: nil, d: [], e: false, f: true }
+ # h.compact_blank!
+ # # => { b: 1, f: true }
+ def compact_blank!
+ # use delete_if rather than reject! because it always returns self even if nothing changed
+ delete_if { |_k, v| v.blank? }
+ end
end
class Range #:nodoc:
@@ -185,4 +220,15 @@ class Array #:nodoc:
super
end
end
+
+ # Removes all blank elements from the +Array+ in place and returns self.
+ # Uses Object#blank? for determining if an item is blank.
+ #
+ # a = [1, "", nil, 2, " ", [], {}, false, true]
+ # a.compact_blank!
+ # # => [1, 2, true]
+ def compact_blank!
+ # use delete_if rather than reject! because it always returns self even if nothing changed
+ delete_if(&:blank?)
+ end
end
diff --git a/activesupport/lib/active_support/core_ext/hash/deep_transform_values.rb b/activesupport/lib/active_support/core_ext/hash/deep_transform_values.rb
index 720a1f67c8..18acb1e70c 100644
--- a/activesupport/lib/active_support/core_ext/hash/deep_transform_values.rb
+++ b/activesupport/lib/active_support/core_ext/hash/deep_transform_values.rb
@@ -1,8 +1,8 @@
# frozen_string_literal: true
class Hash
- # Returns a new hash with all keys converted by the block operation.
- # This includes the keys from the root hash and from all
+ # Returns a new hash with all values converted by the block operation.
+ # This includes the values from the root hash and from all
# nested hashes and arrays.
#
# hash = { person: { name: 'Rob', age: '28' } }
diff --git a/activesupport/lib/active_support/core_ext/module/delegation.rb b/activesupport/lib/active_support/core_ext/module/delegation.rb
index 5652f2d1cc..14d7f0c484 100644
--- a/activesupport/lib/active_support/core_ext/module/delegation.rb
+++ b/activesupport/lib/active_support/core_ext/module/delegation.rb
@@ -170,7 +170,7 @@ class Module
# The target method must be public, otherwise it will raise +NoMethodError+.
def delegate(*methods, to: nil, prefix: nil, allow_nil: nil, private: nil)
unless to
- raise ArgumentError, "Delegation needs a target. Supply an options hash with a :to key as the last argument (e.g. delegate :hello, to: :greeter)."
+ raise ArgumentError, "Delegation needs a target. Supply a keyword argument 'to' (e.g. delegate :hello, to: :greeter)."
end
if prefix == true && /^[^a-z_]/.match?(to)
@@ -205,18 +205,18 @@ class Module
if allow_nil
method_def = [
"def #{method_prefix}#{method}(#{definition})",
- "_ = #{to}",
- "if !_.nil? || nil.respond_to?(:#{method})",
- " _.#{method}(#{definition})",
- "end",
- "end"
+ " _ = #{to}",
+ " if !_.nil? || nil.respond_to?(:#{method})",
+ " _.#{method}(#{definition})",
+ " end",
+ "end"
].join ";"
else
exception = %(raise DelegationError, "#{self}##{method_prefix}#{method} delegated to #{to}.#{method}, but #{to} is nil: \#{self.inspect}")
method_def = [
"def #{method_prefix}#{method}(#{definition})",
- " _ = #{to}",
+ " _ = #{to}",
" _.#{method}(#{definition})",
"rescue NoMethodError => e",
" if _.nil? && e.name == :#{method}",
@@ -274,8 +274,14 @@ class Module
# variables, methods, constants, etc.
#
# The delegated method must be public on the target, otherwise it will
- # raise +NoMethodError+.
- def delegate_missing_to(target)
+ # raise +DelegationError+. If you wish to instead return +nil+,
+ # use the <tt>:allow_nil</tt> option.
+ #
+ # The <tt>marshal_dump</tt> and <tt>_dump</tt> methods are exempt from
+ # delegation due to possible interference when calling
+ # <tt>Marshal.dump(object)</tt>, should the delegation target method
+ # of <tt>object</tt> add or remove instance variables.
+ def delegate_missing_to(target, allow_nil: nil)
target = target.to_s
target = "self.#{target}" if DELEGATION_RESERVED_METHOD_NAMES.include?(target)
@@ -284,6 +290,7 @@ class Module
# It may look like an oversight, but we deliberately do not pass
# +include_private+, because they do not get delegated.
+ return false if name == :marshal_dump || name == :_dump
#{target}.respond_to?(name) || super
end
@@ -295,7 +302,11 @@ class Module
super
rescue NoMethodError
if #{target}.nil?
- raise DelegationError, "\#{method} delegated to #{target}, but #{target} is nil"
+ if #{allow_nil == true}
+ nil
+ else
+ raise DelegationError, "\#{method} delegated to #{target}, but #{target} is nil"
+ end
else
raise
end
diff --git a/activesupport/lib/active_support/core_ext/object/duplicable.rb b/activesupport/lib/active_support/core_ext/object/duplicable.rb
index c78ee6bbfc..3ebcdca02b 100644
--- a/activesupport/lib/active_support/core_ext/object/duplicable.rb
+++ b/activesupport/lib/active_support/core_ext/object/duplicable.rb
@@ -28,96 +28,6 @@ class Object
end
end
-class NilClass
- 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
- 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
- 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
- begin
- :symbol.dup
-
- # Some symbols couldn't be duped in Ruby 2.4.0 only, due to a bug.
- # This feature check catches any regression.
- "symbol_from_string".to_sym.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
- 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
-
-require "bigdecimal"
-class BigDecimal
- # BigDecimals are duplicable:
- #
- # BigDecimal("1.2").duplicable? # => true
- # BigDecimal("1.2").dup # => #<BigDecimal:...,'0.12E1',18(18)>
- def duplicable?
- true
- end
-end
-
class Method
# Methods are not duplicable:
#
@@ -128,32 +38,12 @@ class Method
end
end
-class Complex
- begin
- Complex(1).dup
- rescue TypeError
-
- # Complexes are not duplicable:
- #
- # Complex(1).duplicable? # => false
- # Complex(1).dup # => TypeError: can't copy Complex
- def duplicable?
- false
- end
- end
-end
-
-class Rational
- begin
- Rational(1).dup
- rescue TypeError
-
- # Rationals are not duplicable:
- #
- # Rational(1).duplicable? # => false
- # Rational(1).dup # => TypeError: can't copy Rational
- def duplicable?
- false
- end
+class UnboundMethod
+ # Unbound methods are not duplicable:
+ #
+ # method(:puts).unbind.duplicable? # => false
+ # method(:puts).unbind.dup # => TypeError: allocator undefined for UnboundMethod
+ def duplicable?
+ false
end
end
diff --git a/activesupport/lib/active_support/core_ext/range/each.rb b/activesupport/lib/active_support/core_ext/range/each.rb
index 2f22cd0e92..2d86997edf 100644
--- a/activesupport/lib/active_support/core_ext/range/each.rb
+++ b/activesupport/lib/active_support/core_ext/range/each.rb
@@ -15,7 +15,6 @@ module ActiveSupport
end
private
-
def ensure_iteration_allowed
raise TypeError, "can't iterate from #{first.class}" if first.is_a?(TimeWithZone)
end
diff --git a/activesupport/lib/active_support/core_ext/string/access.rb b/activesupport/lib/active_support/core_ext/string/access.rb
index 4ca24028b0..4894193665 100644
--- a/activesupport/lib/active_support/core_ext/string/access.rb
+++ b/activesupport/lib/active_support/core_ext/string/access.rb
@@ -44,7 +44,7 @@ class String
# str.from(0).to(-1) # => "hello"
# str.from(1).to(-2) # => "ell"
def from(position)
- self[position..-1]
+ self[position, length]
end
# Returns a substring from the beginning of the string to the given position.
@@ -61,7 +61,8 @@ class String
# str.from(0).to(-1) # => "hello"
# str.from(1).to(-2) # => "ell"
def to(position)
- self[0..position]
+ position = [position + length, -1].max if position < 0
+ self[0, position + 1]
end
# Returns the first character. If a limit is supplied, returns a substring
diff --git a/activesupport/lib/active_support/core_ext/string/filters.rb b/activesupport/lib/active_support/core_ext/string/filters.rb
index df0e79afa8..7f28bd52f2 100644
--- a/activesupport/lib/active_support/core_ext/string/filters.rb
+++ b/activesupport/lib/active_support/core_ext/string/filters.rb
@@ -75,7 +75,7 @@ class String
length_with_room_for_omission
end
- "#{self[0, stop]}#{omission}"
+ +"#{self[0, stop]}#{omission}"
end
# Truncates +text+ to at most <tt>bytesize</tt> bytes in length without
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 638152626b..f48ea56326 100644
--- a/activesupport/lib/active_support/core_ext/string/output_safety.rb
+++ b/activesupport/lib/active_support/core_ext/string/output_safety.rb
@@ -213,6 +213,12 @@ module ActiveSupport #:nodoc:
dup.concat(other)
end
+ def *(*)
+ new_safe_buffer = super
+ new_safe_buffer.instance_variable_set(:@html_safe, @html_safe)
+ new_safe_buffer
+ end
+
def %(args)
case args
when Hash
@@ -285,7 +291,6 @@ module ActiveSupport #:nodoc:
end
private
-
def html_escape_interpolated_argument(arg)
(!html_safe? || arg.html_safe?) ? arg : CGI.escapeHTML(arg.to_s)
end
diff --git a/activesupport/lib/active_support/core_ext/time/calculations.rb b/activesupport/lib/active_support/core_ext/time/calculations.rb
index f09a6271ad..eed34965e3 100644
--- a/activesupport/lib/active_support/core_ext/time/calculations.rb
+++ b/activesupport/lib/active_support/core_ext/time/calculations.rb
@@ -311,4 +311,34 @@ class Time
end
alias_method :eql_without_coercion, :eql?
alias_method :eql?, :eql_with_coercion
+
+ # Returns a new time the specified number of days ago.
+ def prev_day(days = 1)
+ advance(days: -days)
+ end
+
+ # Returns a new time the specified number of days in the future.
+ def next_day(days = 1)
+ advance(days: days)
+ end
+
+ # Returns a new time the specified number of months ago.
+ def prev_month(months = 1)
+ advance(months: -months)
+ end
+
+ # Returns a new time the specified number of months in the future.
+ def next_month(months = 1)
+ advance(months: months)
+ end
+
+ # Returns a new time the specified number of years ago.
+ def prev_year(years = 1)
+ advance(years: -years)
+ end
+
+ # Returns a new time the specified number of years in the future.
+ def next_year(years = 1)
+ advance(years: years)
+ end
end
diff --git a/activesupport/lib/active_support/dependencies.rb b/activesupport/lib/active_support/dependencies.rb
index 82f07c085e..923d6ad228 100644
--- a/activesupport/lib/active_support/dependencies.rb
+++ b/activesupport/lib/active_support/dependencies.rb
@@ -20,6 +20,9 @@ module ActiveSupport #:nodoc:
module Dependencies #:nodoc:
extend self
+ UNBOUND_METHOD_MODULE_NAME = Module.instance_method(:name)
+ private_constant :UNBOUND_METHOD_MODULE_NAME
+
mattr_accessor :interlock, default: Interlock.new
# :doc:
@@ -201,6 +204,11 @@ module ActiveSupport #:nodoc:
end
end
+ def self.include_into(base)
+ base.include(self)
+ append_features(base)
+ end
+
def const_missing(const_name)
from_mod = anonymous? ? guess_for_anonymous(const_name) : self
Dependencies.load_missing_constant(from_mod, const_name)
@@ -230,6 +238,21 @@ module ActiveSupport #:nodoc:
base.class_eval do
define_method(:load, Kernel.instance_method(:load))
private :load
+
+ define_method(:require, Kernel.instance_method(:require))
+ private :require
+ end
+ end
+
+ def self.include_into(base)
+ base.include(self)
+
+ if base.instance_method(:load).owner == base
+ base.remove_method(:load)
+ end
+
+ if base.instance_method(:require).owner == base
+ base.remove_method(:require)
end
end
@@ -290,7 +313,6 @@ module ActiveSupport #:nodoc:
end
private
-
def load(file, wrap = false)
result = false
load_dependency(file) { result = super }
@@ -326,9 +348,9 @@ module ActiveSupport #:nodoc:
end
def hook!
- Object.class_eval { include Loadable }
- Module.class_eval { include ModuleConstMissing }
- Exception.class_eval { include Blamable }
+ Loadable.include_into(Object)
+ ModuleConstMissing.include_into(Module)
+ Exception.include(Blamable)
end
def unhook!
@@ -639,7 +661,7 @@ module ActiveSupport #:nodoc:
# Determine if the given constant has been automatically loaded.
def autoloaded?(desc)
- return false if desc.is_a?(Module) && desc.anonymous?
+ return false if desc.is_a?(Module) && real_mod_name(desc).nil?
name = to_constant_name desc
return false unless qualified_const_defined?(name)
autoloaded_constants.include?(name)
@@ -695,7 +717,7 @@ module ActiveSupport #:nodoc:
when String then desc.sub(/^::/, "")
when Symbol then desc.to_s
when Module
- desc.name ||
+ real_mod_name(desc) ||
raise(ArgumentError, "Anonymous modules have no name to be referenced by")
else raise TypeError, "Not a valid constant descriptor: #{desc.inspect}"
end
@@ -769,6 +791,14 @@ module ActiveSupport #:nodoc:
def log(message)
logger.debug("autoloading: #{message}") if logger && verbose
end
+
+ private
+
+ # Returns the original name of a class or module even if `name` has been
+ # overridden.
+ def real_mod_name(mod)
+ UNBOUND_METHOD_MODULE_NAME.bind(mod).call
+ end
end
end
diff --git a/activesupport/lib/active_support/dependencies/zeitwerk_integration.rb b/activesupport/lib/active_support/dependencies/zeitwerk_integration.rb
index d5dc7c2ff4..f75083a05a 100644
--- a/activesupport/lib/active_support/dependencies/zeitwerk_integration.rb
+++ b/activesupport/lib/active_support/dependencies/zeitwerk_integration.rb
@@ -23,8 +23,13 @@ module ActiveSupport
ActiveSupport::Inflector.safe_constantize(cpath)
end
- def to_unload?(cpath)
- Rails.autoloaders.main.to_unload?(cpath)
+ def autoloaded_constants
+ Rails.autoloaders.main.unloadable_cpaths
+ end
+
+ def autoloaded?(object)
+ cpath = object.is_a?(Module) ? real_mod_name(object) : object.to_s
+ Rails.autoloaders.main.unloadable_cpath?(cpath)
end
def verbose=(verbose)
@@ -51,7 +56,6 @@ module ActiveSupport
end
private
-
def setup_autoloaders(enable_reloading)
Dependencies.autoload_paths.each do |autoload_path|
# Zeitwerk only accepts existing directories in `push_dir` to
diff --git a/activesupport/lib/active_support/deprecation.rb b/activesupport/lib/active_support/deprecation.rb
index 7271ab565b..16db547016 100644
--- a/activesupport/lib/active_support/deprecation.rb
+++ b/activesupport/lib/active_support/deprecation.rb
@@ -35,7 +35,7 @@ module ActiveSupport
# and the second is a library name.
#
# ActiveSupport::Deprecation.new('2.0', 'MyLibrary')
- def initialize(deprecation_horizon = "6.1", gem_name = "Rails")
+ def initialize(deprecation_horizon = "6.2", gem_name = "Rails")
self.gem_name = gem_name
self.deprecation_horizon = deprecation_horizon
# By default, warnings are not silenced and debugging is off.
diff --git a/activesupport/lib/active_support/deprecation/method_wrappers.rb b/activesupport/lib/active_support/deprecation/method_wrappers.rb
index d99571790f..7c0a54a1d0 100644
--- a/activesupport/lib/active_support/deprecation/method_wrappers.rb
+++ b/activesupport/lib/active_support/deprecation/method_wrappers.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true
require "active_support/core_ext/array/extract_options"
+require "active_support/core_ext/module/redefine_method"
module ActiveSupport
class Deprecation
@@ -52,29 +53,17 @@ module ActiveSupport
options = method_names.extract_options!
deprecator = options.delete(:deprecator) || self
method_names += options.keys
- mod = Module.new
+ mod = nil
method_names.each do |method_name|
if target_module.method_defined?(method_name) || target_module.private_method_defined?(method_name)
- aliased_method, punctuation = method_name.to_s.sub(/([?!=])$/, ""), $1
- with_method = "#{aliased_method}_with_deprecation#{punctuation}"
- without_method = "#{aliased_method}_without_deprecation#{punctuation}"
-
- target_module.define_method(with_method) do |*args, &block|
+ method = target_module.instance_method(method_name)
+ target_module.redefine_method(method_name) do |*args, &block|
deprecator.deprecation_warning(method_name, options[method_name])
- send(without_method, *args, &block)
- end
-
- target_module.alias_method(without_method, method_name)
- target_module.alias_method(method_name, with_method)
-
- case
- when target_module.protected_method_defined?(without_method)
- target_module.send(:protected, method_name)
- when target_module.private_method_defined?(without_method)
- target_module.send(:private, method_name)
+ method.bind(self).call(*args, &block)
end
else
+ mod ||= Module.new
mod.define_method(method_name) do |*args, &block|
deprecator.deprecation_warning(method_name, options[method_name])
super(*args, &block)
@@ -82,7 +71,7 @@ module ActiveSupport
end
end
- target_module.prepend(mod) unless mod.instance_methods(false).empty?
+ target_module.prepend(mod) if mod
end
end
end
diff --git a/activesupport/lib/active_support/deprecation/proxy_wrappers.rb b/activesupport/lib/active_support/deprecation/proxy_wrappers.rb
index 56f1e23136..d7d3c30b97 100644
--- a/activesupport/lib/active_support/deprecation/proxy_wrappers.rb
+++ b/activesupport/lib/active_support/deprecation/proxy_wrappers.rb
@@ -120,9 +120,16 @@ module ActiveSupport
# # => DEPRECATION WARNING: PLANETS is deprecated! Use PLANETS_POST_2006 instead.
# (Backtrace information…)
# ["Mercury", "Venus", "Earth", "Mars", "Jupiter", "Saturn", "Uranus", "Neptune"]
- class DeprecatedConstantProxy < DeprecationProxy
+ class DeprecatedConstantProxy < Module
+ def self.new(*args, &block)
+ object = args.first
+
+ return object unless object
+ super
+ end
+
def initialize(old_const, new_const, deprecator = ActiveSupport::Deprecation.instance, message: "#{old_const} is deprecated! Use #{new_const} instead.")
- require "active_support/inflector/methods"
+ Kernel.require "active_support/inflector/methods"
@old_const = old_const
@new_const = new_const
@@ -130,6 +137,14 @@ module ActiveSupport
@message = message
end
+ instance_methods.each { |m| undef_method m unless /^__|^object_id$/.match?(m) }
+
+ # Don't give a deprecation warning on inspect since test/unit and error
+ # logs rely on it for diagnostics.
+ def inspect
+ target.inspect
+ end
+
# Returns the class of the new constant.
#
# PLANETS_POST_2006 = %w(mercury venus earth mars jupiter saturn uranus neptune)
@@ -144,8 +159,14 @@ module ActiveSupport
ActiveSupport::Inflector.constantize(@new_const.to_s)
end
- def warn(callstack, called, args)
- @deprecator.warn(@message, callstack)
+ def const_missing(name)
+ @deprecator.warn(@message, caller_locations)
+ target.const_get(name)
+ end
+
+ def method_missing(called, *args, &block)
+ @deprecator.warn(@message, caller_locations)
+ target.__send__(called, *args, &block)
end
end
end
diff --git a/activesupport/lib/active_support/descendants_tracker.rb b/activesupport/lib/active_support/descendants_tracker.rb
index fe0c6991aa..b14842bf67 100644
--- a/activesupport/lib/active_support/descendants_tracker.rb
+++ b/activesupport/lib/active_support/descendants_tracker.rb
@@ -22,18 +22,11 @@ module ActiveSupport
def clear
if defined? ActiveSupport::Dependencies
- # to_unload? is only defined in Zeitwerk mode.
- to_unload = if Dependencies.respond_to?(:to_unload?)
- ->(klass) { Dependencies.to_unload?(klass.name) }
- else
- ->(klass) { Dependencies.autoloaded?(klass) }
- end
-
@@direct_descendants.each do |klass, descendants|
- if to_unload[klass]
+ if Dependencies.autoloaded?(klass)
@@direct_descendants.delete(klass)
else
- descendants.reject! { |v| to_unload[v] }
+ descendants.reject! { |v| Dependencies.autoloaded?(v) }
end
end
else
@@ -48,7 +41,6 @@ module ActiveSupport
end
private
-
def accumulate_descendants(klass, acc)
if direct_descendants = @@direct_descendants[klass]
direct_descendants.each do |direct_descendant|
@@ -85,15 +77,17 @@ module ActiveSupport
end
def <<(klass)
- cleanup!
@refs << WeakRef.new(klass)
end
def each
- @refs.each do |ref|
+ @refs.reject! do |ref|
yield ref.__getobj__
+ false
rescue WeakRef::RefError
+ true
end
+ self
end
def refs_size
diff --git a/activesupport/lib/active_support/duration.rb b/activesupport/lib/active_support/duration.rb
index 97b4634d7b..2b4f1288f1 100644
--- a/activesupport/lib/active_support/duration.rb
+++ b/activesupport/lib/active_support/duration.rb
@@ -4,7 +4,6 @@ require "active_support/core_ext/array/conversions"
require "active_support/core_ext/module/delegation"
require "active_support/core_ext/object/acts_like"
require "active_support/core_ext/string/filters"
-require "active_support/deprecation"
module ActiveSupport
# Provides accurate date and time measurements using Date#advance and
@@ -199,7 +198,6 @@ module ActiveSupport
end
private
-
def calculate_total_seconds(parts)
parts.inject(0) do |total, (part, value)|
total + value * PARTS_IN_SECONDS[part]
@@ -400,7 +398,6 @@ module ActiveSupport
end
private
-
def sum(sign, time = ::Time.current)
parts.inject(time) do |t, (type, number)|
if t.acts_like?(:time) || t.acts_like?(:date)
diff --git a/activesupport/lib/active_support/duration/iso8601_parser.rb b/activesupport/lib/active_support/duration/iso8601_parser.rb
index d3233e6111..83f3b28602 100644
--- a/activesupport/lib/active_support/duration/iso8601_parser.rb
+++ b/activesupport/lib/active_support/duration/iso8601_parser.rb
@@ -80,7 +80,6 @@ module ActiveSupport
end
private
-
def finished?
scanner.eos?
end
diff --git a/activesupport/lib/active_support/duration/iso8601_serializer.rb b/activesupport/lib/active_support/duration/iso8601_serializer.rb
index 1125454919..8314df12b0 100644
--- a/activesupport/lib/active_support/duration/iso8601_serializer.rb
+++ b/activesupport/lib/active_support/duration/iso8601_serializer.rb
@@ -32,7 +32,6 @@ module ActiveSupport
end
private
-
# Return pair of duration's parts and whole duration sign.
# Parts are summarized (as they can become repetitive due to addition, etc).
# Zero parts are removed as not significant.
diff --git a/activesupport/lib/active_support/evented_file_update_checker.rb b/activesupport/lib/active_support/evented_file_update_checker.rb
index 3893b0de0e..6075e0a3d3 100644
--- a/activesupport/lib/active_support/evented_file_update_checker.rb
+++ b/activesupport/lib/active_support/evented_file_update_checker.rb
@@ -107,13 +107,23 @@ module ActiveSupport
private
def boot!
- Listen.to(*@dtw, &method(:changed)).start
+ normalize_dirs!
+
+ unless @dtw.empty?
+ Listen.to(*@dtw, &method(:changed)).start
+ end
end
def shutdown!
Listen.stop
end
+ def normalize_dirs!
+ @dirs.transform_keys! do |dir|
+ dir.exist? ? dir.realpath : dir
+ end
+ end
+
def changed(modified, added, removed)
unless updated?
@updated.make_true if (modified + added + removed).any? { |f| watching?(f) }
@@ -187,13 +197,6 @@ module ActiveSupport
lcsp
end
- # Returns the deepest existing ascendant, which could be the argument itself.
- def existing_parent(dir)
- dir.ascend do |ascendant|
- break ascendant if ascendant.directory?
- end
- end
-
# Filters out directories which are descendants of others in the collection (stable).
def filter_out_descendants(dirs)
return dirs if dirs.length < 2
@@ -214,7 +217,6 @@ module ActiveSupport
end
private
-
def ascendant_of?(base, other)
base != other && other.ascend do |ascendant|
break true if base == ascendant
diff --git a/activesupport/lib/active_support/file_update_checker.rb b/activesupport/lib/active_support/file_update_checker.rb
index 1a0bb10815..9b665e7f19 100644
--- a/activesupport/lib/active_support/file_update_checker.rb
+++ b/activesupport/lib/active_support/file_update_checker.rb
@@ -98,7 +98,6 @@ module ActiveSupport
end
private
-
def watched
@watched || begin
all = @files.select { |f| File.exist?(f) }
diff --git a/activesupport/lib/active_support/gem_version.rb b/activesupport/lib/active_support/gem_version.rb
index cf17922d7d..b1ac3e7969 100644
--- a/activesupport/lib/active_support/gem_version.rb
+++ b/activesupport/lib/active_support/gem_version.rb
@@ -8,9 +8,9 @@ module ActiveSupport
module VERSION
MAJOR = 6
- MINOR = 0
+ MINOR = 1
TINY = 0
- PRE = "beta3"
+ PRE = "alpha"
STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
end
diff --git a/activesupport/lib/active_support/hash_with_indifferent_access.rb b/activesupport/lib/active_support/hash_with_indifferent_access.rb
index 42ae7e9b7b..6acf64cb39 100644
--- a/activesupport/lib/active_support/hash_with_indifferent_access.rb
+++ b/activesupport/lib/active_support/hash_with_indifferent_access.rb
@@ -69,7 +69,7 @@ module ActiveSupport
super()
update(constructor)
- hash = constructor.to_hash
+ hash = constructor.is_a?(Hash) ? constructor : constructor.to_hash
self.default = hash.default if hash.default
self.default_proc = hash.default_proc if hash.default_proc
else
@@ -367,18 +367,20 @@ module ActiveSupport
key.kind_of?(Symbol) ? key.to_s : key
end
- def convert_value(value, options = {}) # :doc:
+ def convert_value(value, for: nil) # :doc:
+ conversion = binding.local_variable_get(:for)
+
if value.is_a? Hash
- if options[:for] == :to_hash
+ if conversion == :to_hash
value.to_hash
else
value.nested_under_indifferent_access
end
elsif value.is_a?(Array)
- if options[:for] != :assignment || value.frozen?
+ if conversion != :assignment || value.frozen?
value = value.dup
end
- value.map! { |e| convert_value(e, options) }
+ value.map! { |e| convert_value(e, for: conversion) }
else
value
end
diff --git a/activesupport/lib/active_support/i18n_railtie.rb b/activesupport/lib/active_support/i18n_railtie.rb
index 584930e413..8faa93a3e4 100644
--- a/activesupport/lib/active_support/i18n_railtie.rb
+++ b/activesupport/lib/active_support/i18n_railtie.rb
@@ -97,7 +97,8 @@ module I18n
If you desire the default locale to be included in the defaults, please
explicitly configure it with `config.i18n.fallbacks.defaults =
[I18n.default_locale]` or `config.i18n.fallbacks = [I18n.default_locale,
- {...}]`
+ {...}]`. If you want to opt-in to the new behavior, use
+ `config.i18n.fallbacks.defaults = [nil, {...}]`.
MSG
args.unshift I18n.default_locale
end
diff --git a/activesupport/lib/active_support/inflector/inflections.rb b/activesupport/lib/active_support/inflector/inflections.rb
index 88cdd99dbd..efee74a1df 100644
--- a/activesupport/lib/active_support/inflector/inflections.rb
+++ b/activesupport/lib/active_support/inflector/inflections.rb
@@ -2,7 +2,6 @@
require "concurrent/map"
require "active_support/i18n"
-require "active_support/deprecation"
module ActiveSupport
module Inflector
@@ -230,7 +229,6 @@ module ActiveSupport
end
private
-
def define_acronym_regex_patterns
@acronym_regex = @acronyms.empty? ? /(?=a)b/ : /#{@acronyms.values.join("|")}/
@acronyms_camelize_regex = /^(?:#{@acronym_regex}(?=\b|[A-Z_])|\w)/
diff --git a/activesupport/lib/active_support/inflector/methods.rb b/activesupport/lib/active_support/inflector/methods.rb
index ee193add6f..18f3f53879 100644
--- a/activesupport/lib/active_support/inflector/methods.rb
+++ b/activesupport/lib/active_support/inflector/methods.rb
@@ -359,7 +359,6 @@ module ActiveSupport
end
private
-
# Mounts a regular expression, returned as a string to ease interpolation,
# that will match part by part the given constant.
#
diff --git a/activesupport/lib/active_support/json/decoding.rb b/activesupport/lib/active_support/json/decoding.rb
index 402a3fbe60..b4bf882bc3 100644
--- a/activesupport/lib/active_support/json/decoding.rb
+++ b/activesupport/lib/active_support/json/decoding.rb
@@ -44,7 +44,6 @@ module ActiveSupport
end
private
-
def convert_dates_from(data)
case data
when nil
diff --git a/activesupport/lib/active_support/lazy_load_hooks.rb b/activesupport/lib/active_support/lazy_load_hooks.rb
index a6b096a973..c6f7ccf0a2 100644
--- a/activesupport/lib/active_support/lazy_load_hooks.rb
+++ b/activesupport/lib/active_support/lazy_load_hooks.rb
@@ -54,7 +54,6 @@ module ActiveSupport
end
private
-
def with_execution_control(name, block, once)
unless @run_once[name].include?(block)
@run_once[name] << block if once
diff --git a/activesupport/lib/active_support/log_subscriber.rb b/activesupport/lib/active_support/log_subscriber.rb
index 938cfdb914..8b9dd1fffe 100644
--- a/activesupport/lib/active_support/log_subscriber.rb
+++ b/activesupport/lib/active_support/log_subscriber.rb
@@ -29,6 +29,9 @@ module ActiveSupport
# subscriber, the line above should be called after your
# <tt>ActiveRecord::LogSubscriber</tt> definition.
#
+ # A logger also needs to be set with <tt>ActiveRecord::LogSubscriber.logger=</tt>.
+ # This is assigned automatically in a Rails environment.
+ #
# After configured, whenever a <tt>"sql.active_record"</tt> notification is published,
# it will properly dispatch the event
# (<tt>ActiveSupport::Notifications::Event</tt>) to the sql method.
@@ -112,7 +115,6 @@ module ActiveSupport
end
private
-
%w(info debug warn error fatal unknown).each do |level|
class_eval <<-METHOD, __FILE__, __LINE__ + 1
def #{level}(progname = nil, &block)
diff --git a/activesupport/lib/active_support/logger_thread_safe_level.rb b/activesupport/lib/active_support/logger_thread_safe_level.rb
index f16c90cfc6..1775a41492 100644
--- a/activesupport/lib/active_support/logger_thread_safe_level.rb
+++ b/activesupport/lib/active_support/logger_thread_safe_level.rb
@@ -3,6 +3,7 @@
require "active_support/concern"
require "active_support/core_ext/module/attribute_accessors"
require "concurrent"
+require "fiber"
module ActiveSupport
module LoggerThreadSafeLevel # :nodoc:
@@ -28,7 +29,7 @@ module ActiveSupport
end
def local_log_id
- Thread.current.__id__
+ Fiber.current.__id__
end
def local_level
diff --git a/activesupport/lib/active_support/message_verifier.rb b/activesupport/lib/active_support/message_verifier.rb
index c4a4afe95f..a5dc1181d8 100644
--- a/activesupport/lib/active_support/message_verifier.rb
+++ b/activesupport/lib/active_support/message_verifier.rb
@@ -178,8 +178,8 @@ module ActiveSupport
# Generates a signed message for the provided value.
#
- # The message is signed with the +MessageVerifier+'s secret. Without knowing
- # the secret, the original value cannot be extracted from the message.
+ # The message is signed with the +MessageVerifier+'s secret.
+ # Returns Base64-encoded message joined with the generated signature.
#
# verifier = ActiveSupport::MessageVerifier.new 's3Krit'
# verifier.generate 'a private message' # => "BAhJIhRwcml2YXRlLW1lc3NhZ2UGOgZFVA==--e2d724331ebdee96a10fb99b089508d1c72bd772"
diff --git a/activesupport/lib/active_support/messages/rotator.rb b/activesupport/lib/active_support/messages/rotator.rb
index 823a399d67..50ea7dcd8d 100644
--- a/activesupport/lib/active_support/messages/rotator.rb
+++ b/activesupport/lib/active_support/messages/rotator.rb
@@ -3,11 +3,12 @@
module ActiveSupport
module Messages
module Rotator # :nodoc:
- def initialize(*, **options)
+ def initialize(*, on_rotation: nil, **options)
super
@options = options
@rotations = []
+ @on_rotation = on_rotation
end
def rotate(*secrets, **options)
@@ -17,7 +18,7 @@ module ActiveSupport
module Encryptor
include Rotator
- def decrypt_and_verify(*args, on_rotation: nil, **options)
+ def decrypt_and_verify(*args, on_rotation: @on_rotation, **options)
super
rescue MessageEncryptor::InvalidMessage, MessageVerifier::InvalidSignature
run_rotations(on_rotation) { |encryptor| encryptor.decrypt_and_verify(*args, options) } || raise
@@ -32,7 +33,7 @@ module ActiveSupport
module Verifier
include Rotator
- def verified(*args, on_rotation: nil, **options)
+ def verified(*args, on_rotation: @on_rotation, **options)
super || run_rotations(on_rotation) { |verifier| verifier.verified(*args, options) }
end
@@ -46,7 +47,7 @@ module ActiveSupport
def run_rotations(on_rotation)
@rotations.find do |rotation|
if message = yield(rotation) rescue next
- on_rotation.call if on_rotation
+ on_rotation&.call
return message
end
end
diff --git a/activesupport/lib/active_support/multibyte/chars.rb b/activesupport/lib/active_support/multibyte/chars.rb
index a1e23aeaca..2ba3936cae 100644
--- a/activesupport/lib/active_support/multibyte/chars.rb
+++ b/activesupport/lib/active_support/multibyte/chars.rb
@@ -207,7 +207,6 @@ module ActiveSupport #:nodoc:
end
private
-
def chars(string)
self.class.new(string)
end
diff --git a/activesupport/lib/active_support/multibyte/unicode.rb b/activesupport/lib/active_support/multibyte/unicode.rb
index ce8ecece69..3956137049 100644
--- a/activesupport/lib/active_support/multibyte/unicode.rb
+++ b/activesupport/lib/active_support/multibyte/unicode.rb
@@ -148,7 +148,6 @@ module ActiveSupport
end
private
-
def recode_windows1252_chars(string)
string.encode(Encoding::UTF_8, Encoding::Windows_1252, invalid: :replace, undef: :replace)
end
diff --git a/activesupport/lib/active_support/notifications.rb b/activesupport/lib/active_support/notifications.rb
index d9e93b530c..a7a6112b0f 100644
--- a/activesupport/lib/active_support/notifications.rb
+++ b/activesupport/lib/active_support/notifications.rb
@@ -38,6 +38,19 @@ module ActiveSupport
# payload # => Hash, the payload
# end
#
+ # Here, the +start+ and +finish+ values represent wall-clock time. If you are
+ # concerned about accuracy, you can register a monotonic subscriber.
+ #
+ # ActiveSupport::Notifications.monotonic_subscribe('render') do |name, start, finish, id, payload|
+ # name # => String, name of the event (such as 'render' from above)
+ # start # => Monotonic time, when the instrumented block started execution
+ # finish # => Monotonic time, when the instrumented block ended execution
+ # id # => String, unique ID for the instrumenter that fired the event
+ # payload # => Hash, the payload
+ # end
+ #
+ # The +start+ and +finish+ values above represent monotonic time.
+ #
# For instance, let's store all "render" events in an array:
#
# events = []
@@ -135,6 +148,16 @@ module ActiveSupport
# during the execution of the block. The callback is unsubscribed automatically
# after that.
#
+ # To record +started+ and +finished+ values with monotonic time,
+ # specify the optional <tt>:monotonic</tt> option to the
+ # <tt>subscribed</tt> method. The <tt>:monotonic</tt> option is set
+ # to +false+ by default.
+ #
+ # callback = lambda {|name, started, finished, unique_id, payload| ... }
+ # ActiveSupport::Notifications.subscribed(callback, "sql.active_record", monotonic: true) do
+ # ...
+ # end
+ #
# === Manual Unsubscription
#
# The +subscribe+ method returns a subscriber object:
@@ -208,12 +231,16 @@ module ActiveSupport
# ActiveSupport::Notifications.subscribe(/render/) do |event|
# @event = event
# end
- def subscribe(*args, &block)
- notifier.subscribe(*args, &block)
+ def subscribe(pattern = nil, callback = nil, &block)
+ notifier.subscribe(pattern, callback, monotonic: false, &block)
+ end
+
+ def monotonic_subscribe(pattern = nil, callback = nil, &block)
+ notifier.subscribe(pattern, callback, monotonic: true, &block)
end
- def subscribed(callback, *args, &block)
- subscriber = subscribe(*args, &callback)
+ def subscribed(callback, pattern = nil, monotonic: false, &block)
+ subscriber = notifier.subscribe(pattern, callback, monotonic: monotonic)
yield
ensure
unsubscribe(subscriber)
diff --git a/activesupport/lib/active_support/notifications/fanout.rb b/activesupport/lib/active_support/notifications/fanout.rb
index c506b35b1e..dda71b880e 100644
--- a/activesupport/lib/active_support/notifications/fanout.rb
+++ b/activesupport/lib/active_support/notifications/fanout.rb
@@ -20,8 +20,8 @@ module ActiveSupport
super
end
- def subscribe(pattern = nil, callable = nil, &block)
- subscriber = Subscribers.new(pattern, callable || block)
+ def subscribe(pattern = nil, callable = nil, monotonic: false, &block)
+ subscriber = Subscribers.new(pattern, callable || block, monotonic)
synchronize do
if String === pattern
@string_subscribers[pattern] << subscriber
@@ -84,8 +84,8 @@ module ActiveSupport
end
module Subscribers # :nodoc:
- def self.new(pattern, listener)
- subscriber_class = Timed
+ def self.new(pattern, listener, monotonic)
+ subscriber_class = monotonic ? MonotonicTimed : Timed
if listener.respond_to?(:start) && listener.respond_to?(:finish)
subscriber_class = Evented
@@ -190,6 +190,23 @@ module ActiveSupport
end
end
+ class MonotonicTimed < Evented # :nodoc:
+ def publish(name, *args)
+ @delegate.call name, *args
+ end
+
+ def start(name, id, payload)
+ timestack = Thread.current[:_timestack_monotonic] ||= []
+ timestack.push Concurrent.monotonic_time
+ end
+
+ def finish(name, id, payload)
+ timestack = Thread.current[:_timestack_monotonic]
+ started = timestack.pop
+ @delegate.call(name, started, Concurrent.monotonic_time, id, payload)
+ end
+ end
+
class EventObject < Evented
def start(name, id, payload)
stack = Thread.current[:_event_stack] ||= []
@@ -201,6 +218,7 @@ module ActiveSupport
def finish(name, id, payload)
stack = Thread.current[:_event_stack]
event = stack.pop
+ event.payload = payload
event.finish!
@delegate.call event
end
diff --git a/activesupport/lib/active_support/notifications/instrumenter.rb b/activesupport/lib/active_support/notifications/instrumenter.rb
index a03e7e483e..24e1ab313a 100644
--- a/activesupport/lib/active_support/notifications/instrumenter.rb
+++ b/activesupport/lib/active_support/notifications/instrumenter.rb
@@ -46,14 +46,14 @@ module ActiveSupport
end
private
-
def unique_id
SecureRandom.hex(10)
end
end
class Event
- attr_reader :name, :time, :end, :transaction_id, :payload, :children
+ attr_reader :name, :time, :end, :transaction_id, :children
+ attr_accessor :payload
def self.clock_gettime_supported? # :nodoc:
defined?(Process::CLOCK_PROCESS_CPUTIME_ID) &&
@@ -68,9 +68,8 @@ module ActiveSupport
@transaction_id = transaction_id
@end = ending
@children = []
- @duration = nil
- @cpu_time_start = nil
- @cpu_time_finish = nil
+ @cpu_time_start = 0
+ @cpu_time_finish = 0
@allocation_count_start = 0
@allocation_count_finish = 0
end
@@ -125,7 +124,7 @@ module ActiveSupport
#
# @event.duration # => 1000.138
def duration
- @duration ||= 1000.0 * (self.end - time)
+ 1000.0 * (self.end - time)
end
def <<(event)
diff --git a/activesupport/lib/active_support/number_helper.rb b/activesupport/lib/active_support/number_helper.rb
index d19a2f64d4..0c87114c0d 100644
--- a/activesupport/lib/active_support/number_helper.rb
+++ b/activesupport/lib/active_support/number_helper.rb
@@ -1,7 +1,5 @@
# frozen_string_literal: true
-require "active_support/dependencies/autoload"
-
module ActiveSupport
module NumberHelper
extend ActiveSupport::Autoload
@@ -228,7 +226,7 @@ module ActiveSupport
end
# Formats the bytes in +number+ into a more understandable
- # representation (e.g., giving it 1500 yields 1.5 KB). This
+ # representation (e.g., giving it 1500 yields 1.46 KB). This
# method is useful for reporting file sizes to users. You can
# customize the format in the +options+ hash.
#
diff --git a/activesupport/lib/active_support/number_helper/number_converter.rb b/activesupport/lib/active_support/number_helper/number_converter.rb
index 06ba797a13..76dd12f27d 100644
--- a/activesupport/lib/active_support/number_helper/number_converter.rb
+++ b/activesupport/lib/active_support/number_helper/number_converter.rb
@@ -136,7 +136,6 @@ module ActiveSupport
end
private
-
def options
@options ||= format_options.merge(opts)
end
diff --git a/activesupport/lib/active_support/number_helper/number_to_currency_converter.rb b/activesupport/lib/active_support/number_helper/number_to_currency_converter.rb
index 0e8ae82dd5..4ad89dc62e 100644
--- a/activesupport/lib/active_support/number_helper/number_to_currency_converter.rb
+++ b/activesupport/lib/active_support/number_helper/number_to_currency_converter.rb
@@ -21,7 +21,6 @@ module ActiveSupport
end
private
-
def absolute_value(number)
number.respond_to?(:abs) ? number.abs : number.sub(/\A-/, "")
end
diff --git a/activesupport/lib/active_support/number_helper/number_to_delimited_converter.rb b/activesupport/lib/active_support/number_helper/number_to_delimited_converter.rb
index 467a580a2e..351444289c 100644
--- a/activesupport/lib/active_support/number_helper/number_to_delimited_converter.rb
+++ b/activesupport/lib/active_support/number_helper/number_to_delimited_converter.rb
@@ -14,7 +14,6 @@ module ActiveSupport
end
private
-
def parts
left, right = number.to_s.split(".")
left.gsub!(delimiter_pattern) do |digit_to_delimit|
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 494408fc01..f089d7ae65 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
@@ -31,7 +31,6 @@ module ActiveSupport
end
private
-
def format
options[:format] || translate_in_locale("human.decimal_units.format")
end
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 91262fa656..ed8acbda6e 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
@@ -28,7 +28,6 @@ module ActiveSupport
end
private
-
def conversion_format
translate_number_value_with_default("human.storage_units.format", locale: options[:locale], raise: true)
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 d5e72981b4..21eadfdcc7 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
@@ -12,7 +12,6 @@ module ActiveSupport
end
private
-
def convert_to_phone_number(number)
if opts[:area_code]
convert_with_area_code(number)
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 6ceb9a572e..767cfe22ad 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
@@ -38,7 +38,6 @@ module ActiveSupport
end
private
-
def strip_insignificant_zeros
options[:strip_insignificant_zeros]
end
diff --git a/activesupport/lib/active_support/parameter_filter.rb b/activesupport/lib/active_support/parameter_filter.rb
index 1389d82523..e1cd7c46c1 100644
--- a/activesupport/lib/active_support/parameter_filter.rb
+++ b/activesupport/lib/active_support/parameter_filter.rb
@@ -51,7 +51,6 @@ module ActiveSupport
end
private
-
def compiled_filter
@compiled_filter ||= CompiledFilter.compile(@filters, mask: @mask)
end
@@ -110,7 +109,12 @@ module ActiveSupport
elsif value.is_a?(Hash)
value = call(value, parents, original_params)
elsif value.is_a?(Array)
- value = value.map { |v| v.is_a?(Hash) ? call(v, parents, original_params) : v }
+ # If we don't pop the current parent it will be duplicated as we
+ # process each array value.
+ parents.pop if deep_regexps
+ value = value.map { |v| value_for_key(key, v, parents, original_params) }
+ # Restore the parent stack after processing the array.
+ parents.push(key) if deep_regexps
elsif blocks.any?
key = key.dup if key.duplicable?
value = value.dup if value.duplicable?
diff --git a/activesupport/lib/active_support/rails.rb b/activesupport/lib/active_support/rails.rb
index 8b727a69ec..30857f04d8 100644
--- a/activesupport/lib/active_support/rails.rb
+++ b/activesupport/lib/active_support/rails.rb
@@ -13,9 +13,6 @@
# Defines Object#blank? and Object#present?.
require "active_support/core_ext/object/blank"
-# Rails own autoload, eager_load, etc.
-require "active_support/dependencies/autoload"
-
# Support for ClassMethods and the included macro.
require "active_support/concern"
diff --git a/activesupport/lib/active_support/secure_compare_rotator.rb b/activesupport/lib/active_support/secure_compare_rotator.rb
new file mode 100644
index 0000000000..97110d41f7
--- /dev/null
+++ b/activesupport/lib/active_support/secure_compare_rotator.rb
@@ -0,0 +1,52 @@
+# frozen_string_literal: true
+
+require "active_support/security_utils"
+require "active_support/messages/rotator"
+
+module ActiveSupport
+ # The ActiveSupport::SecureCompareRotator is a wrapper around +ActiveSupport::SecurityUtils.secure_compare+
+ # and allows you to rotate a previously defined value to a new one.
+ #
+ # It can be used as follow:
+ #
+ # rotator = ActiveSupport::SecureCompareRotator.new('new_production_value')
+ # rotator.rotate('previous_production_value')
+ # rotator.secure_compare!('previous_production_value')
+ #
+ # One real use case example would be to rotate a basic auth credentials:
+ #
+ # class MyController < ApplicationController
+ # def authenticate_request
+ # rotator = ActiveSupport::SecureComparerotator.new('new_password')
+ # rotator.rotate('old_password')
+ #
+ # authenticate_or_request_with_http_basic do |username, password|
+ # rotator.secure_compare!(password)
+ # rescue ActiveSupport::SecureCompareRotator::InvalidMatch
+ # false
+ # end
+ # end
+ # end
+ class SecureCompareRotator
+ include SecurityUtils
+ prepend Messages::Rotator
+
+ InvalidMatch = Class.new(StandardError)
+
+ def initialize(value, **_options)
+ @value = value
+ end
+
+ def secure_compare!(other_value, on_rotation: @on_rotation)
+ secure_compare(@value, other_value) ||
+ run_rotations(on_rotation) { |wrapper| wrapper.secure_compare!(other_value) } ||
+ raise(InvalidMatch)
+ end
+
+ private
+
+ def build_rotation(previous_value, _options)
+ self.class.new(previous_value)
+ end
+ end
+end
diff --git a/activesupport/lib/active_support/string_inquirer.rb b/activesupport/lib/active_support/string_inquirer.rb
index a3af36720e..e5091e127a 100644
--- a/activesupport/lib/active_support/string_inquirer.rb
+++ b/activesupport/lib/active_support/string_inquirer.rb
@@ -18,7 +18,6 @@ module ActiveSupport
# vehicle.bike? # => false
class StringInquirer < String
private
-
def respond_to_missing?(method_name, include_private = false)
(method_name[-1] == "?") || super
end
diff --git a/activesupport/lib/active_support/testing/parallelization.rb b/activesupport/lib/active_support/testing/parallelization.rb
index 63440069b1..96518a4a58 100644
--- a/activesupport/lib/active_support/testing/parallelization.rb
+++ b/activesupport/lib/active_support/testing/parallelization.rb
@@ -27,6 +27,10 @@ module ActiveSupport
@queue << o
end
+ def length
+ @queue.length
+ end
+
def pop; @queue.pop; end
end
@@ -68,10 +72,16 @@ module ActiveSupport
def start
@pool = @queue_size.times.map do |worker|
+ title = "Rails test worker #{worker}"
+
fork do
+ Process.setproctitle("#{title} - (starting)")
+
DRb.stop_service
- after_fork(worker)
+ begin
+ after_fork(worker)
+ rescue => setup_exception; end
queue = DRbObject.new_with_uri(@url)
@@ -79,7 +89,14 @@ module ActiveSupport
klass = job[0]
method = job[1]
reporter = job[2]
- result = Minitest.run_one_method(klass, method)
+
+ Process.setproctitle("#{title} - #{klass}##{method}")
+
+ result = klass.with_info_handler reporter do
+ Minitest.run_one_method(klass, method)
+ end
+
+ add_setup_exception(result, setup_exception) if setup_exception
begin
queue.record(reporter, result)
@@ -89,8 +106,12 @@ module ActiveSupport
end
queue.record(reporter, result)
end
+
+ Process.setproctitle("#{title} - (idle)")
end
ensure
+ Process.setproctitle("#{title} - (stopping)")
+
run_cleanup(worker)
end
end
@@ -103,7 +124,16 @@ module ActiveSupport
def shutdown
@queue_size.times { @queue << nil }
@pool.each { |pid| Process.waitpid pid }
+
+ if @queue.length > 0
+ raise "Queue not empty, but all workers have finished. This probably means that a worker crashed and #{@queue.length} tests were missed."
+ end
end
+
+ private
+ def add_setup_exception(result, setup_exception)
+ result.failures.prepend Minitest::UnexpectedError.new(setup_exception)
+ end
end
end
end
diff --git a/activesupport/lib/active_support/testing/stream.rb b/activesupport/lib/active_support/testing/stream.rb
index 127cfe1e12..f895a74644 100644
--- a/activesupport/lib/active_support/testing/stream.rb
+++ b/activesupport/lib/active_support/testing/stream.rb
@@ -4,7 +4,6 @@ module ActiveSupport
module Testing
module Stream #:nodoc:
private
-
def silence_stream(stream)
old_stream = stream.dup
stream.reopen(IO::NULL)
diff --git a/activesupport/lib/active_support/testing/time_helpers.rb b/activesupport/lib/active_support/testing/time_helpers.rb
index 5a3fa9346c..84bd920d86 100644
--- a/activesupport/lib/active_support/testing/time_helpers.rb
+++ b/activesupport/lib/active_support/testing/time_helpers.rb
@@ -40,7 +40,6 @@ module ActiveSupport
end
private
-
def unstub_object(stub)
singleton_class = stub.object.singleton_class
singleton_class.silence_redefinition_of_method stub.method_name
@@ -191,7 +190,6 @@ module ActiveSupport
end
private
-
def simple_stubs
@simple_stubs ||= SimpleStubs.new
end
diff --git a/activesupport/lib/active_support/xml_mini.rb b/activesupport/lib/active_support/xml_mini.rb
index 075cd4ed8b..f6ae08bb5d 100644
--- a/activesupport/lib/active_support/xml_mini.rb
+++ b/activesupport/lib/active_support/xml_mini.rb
@@ -155,7 +155,6 @@ module ActiveSupport
end
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]
diff --git a/activesupport/lib/active_support/xml_mini/jdom.rb b/activesupport/lib/active_support/xml_mini/jdom.rb
index 32fe6ade28..12ca19a76f 100644
--- a/activesupport/lib/active_support/xml_mini/jdom.rb
+++ b/activesupport/lib/active_support/xml_mini/jdom.rb
@@ -53,7 +53,6 @@ module ActiveSupport
end
private
-
# Convert an XML element and merge into the hash
#
# hash::
diff --git a/activesupport/test/actionable_error_test.rb b/activesupport/test/actionable_error_test.rb
new file mode 100644
index 0000000000..63046b937c
--- /dev/null
+++ b/activesupport/test/actionable_error_test.rb
@@ -0,0 +1,47 @@
+# frozen_string_literal: true
+
+require "abstract_unit"
+require "active_support/actionable_error"
+
+class ActionableErrorTest < ActiveSupport::TestCase
+ NonActionableError = Class.new(StandardError)
+
+ class DispatchableError < StandardError
+ include ActiveSupport::ActionableError
+
+ class_attribute :flip1, default: false
+ class_attribute :flip2, default: false
+
+ action "Flip 1" do
+ self.flip1 = true
+ end
+
+ action "Flip 2" do
+ self.flip2 = true
+ end
+ end
+
+ test "returns all action of an actionable error" do
+ assert_equal ["Flip 1", "Flip 2"], ActiveSupport::ActionableError.actions(DispatchableError).keys
+ assert_equal ["Flip 1", "Flip 2"], ActiveSupport::ActionableError.actions(DispatchableError.new).keys
+ end
+
+ test "returns no actions for non-actionable errors" do
+ assert ActiveSupport::ActionableError.actions(Exception).empty?
+ assert ActiveSupport::ActionableError.actions(Exception.new).empty?
+ end
+
+ test "dispatches actions from error and name" do
+ assert_changes "DispatchableError.flip1", from: false, to: true do
+ ActiveSupport::ActionableError.dispatch DispatchableError, "Flip 1"
+ end
+ end
+
+ test "cannot dispatch missing actions" do
+ err = assert_raises ActiveSupport::ActionableError::NonActionable do
+ ActiveSupport::ActionableError.dispatch NonActionableError, "action"
+ end
+
+ assert_equal 'Cannot find action "action"', err.to_s
+ end
+end
diff --git a/activesupport/test/cache/behaviors/cache_store_behavior.rb b/activesupport/test/cache/behaviors/cache_store_behavior.rb
index a696760bb2..e6f014e08d 100644
--- a/activesupport/test/cache/behaviors/cache_store_behavior.rb
+++ b/activesupport/test/cache/behaviors/cache_store_behavior.rb
@@ -507,7 +507,6 @@ module CacheStoreBehavior
end
private
-
def assert_compressed(value, **options)
assert_compression(true, value, **options)
end
diff --git a/activesupport/test/cache/behaviors/local_cache_behavior.rb b/activesupport/test/cache/behaviors/local_cache_behavior.rb
index baa38ba6ac..6f5d53c190 100644
--- a/activesupport/test/cache/behaviors/local_cache_behavior.rb
+++ b/activesupport/test/cache/behaviors/local_cache_behavior.rb
@@ -46,6 +46,15 @@ module LocalCacheBehavior
end
end
+ def test_local_cache_of_read_returns_a_copy_of_the_entry
+ @cache.with_local_cache do
+ @cache.write(:foo, type: "bar")
+ value = @cache.read(:foo)
+ assert_equal("bar", value.delete(:type))
+ assert_equal({ type: "bar" }, @cache.read(:foo))
+ end
+ end
+
def test_local_cache_of_read
@cache.write("foo", "bar")
@cache.with_local_cache do
diff --git a/activesupport/test/cache/cache_key_test.rb b/activesupport/test/cache/cache_key_test.rb
index c2240d03c2..f0cd991553 100644
--- a/activesupport/test/cache/cache_key_test.rb
+++ b/activesupport/test/cache/cache_key_test.rb
@@ -79,7 +79,6 @@ class CacheKeyTest < ActiveSupport::TestCase
end
private
-
def with_env(kv)
old_values = {}
kv.each { |key, value| old_values[key], ENV[key] = ENV[key], value }
diff --git a/activesupport/test/cache/stores/mem_cache_store_test.rb b/activesupport/test/cache/stores/mem_cache_store_test.rb
index 0e472f5a1a..3917d14d1c 100644
--- a/activesupport/test/cache/stores/mem_cache_store_test.rb
+++ b/activesupport/test/cache/stores/mem_cache_store_test.rb
@@ -113,7 +113,6 @@ class MemCacheStoreTest < ActiveSupport::TestCase
end
private
-
def store
[:mem_cache_store, ENV["MEMCACHE_SERVERS"] || "localhost:11211"]
end
diff --git a/activesupport/test/cache/stores/redis_cache_store_test.rb b/activesupport/test/cache/stores/redis_cache_store_test.rb
index 790534cd3c..a2177e0476 100644
--- a/activesupport/test/cache/stores/redis_cache_store_test.rb
+++ b/activesupport/test/cache/stores/redis_cache_store_test.rb
@@ -191,7 +191,6 @@ module ActiveSupport::Cache::RedisCacheStoreTests
include ConnectionPoolBehavior
private
-
def store
[:redis_cache_store]
end
@@ -238,7 +237,6 @@ module ActiveSupport::Cache::RedisCacheStoreTests
include FailureSafetyBehavior
private
-
def emulating_unavailability
old_client = Redis.send(:remove_const, :Client)
Redis.const_set(:Client, UnavailableRedisClient)
diff --git a/activesupport/test/callbacks_test.rb b/activesupport/test/callbacks_test.rb
index 79098b2a7d..cc37c4fa99 100644
--- a/activesupport/test/callbacks_test.rb
+++ b/activesupport/test/callbacks_test.rb
@@ -397,7 +397,6 @@ module CallbacksTest
end
private
-
def record1
@recorder << 1
end
@@ -989,6 +988,7 @@ module CallbacksTest
define_callbacks :foo, scope: [:name]
set_callback :foo, :before, :foo, if: callback
def run; run_callbacks :foo; end
+
private
def foo; end
}
diff --git a/activesupport/test/core_ext/date_and_time_behavior.rb b/activesupport/test/core_ext/date_and_time_behavior.rb
index b77ea22701..6d31734f27 100644
--- a/activesupport/test/core_ext/date_and_time_behavior.rb
+++ b/activesupport/test/core_ext/date_and_time_behavior.rb
@@ -8,31 +8,11 @@ module DateAndTimeBehavior
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, 24, 10, 10, 10), date_time_init(2005, 2, 22, 10, 10, 10).prev_day(-2)
- assert_equal date_time_init(2005, 2, 23, 10, 10, 10), date_time_init(2005, 2, 22, 10, 10, 10).prev_day(-1)
- assert_equal date_time_init(2005, 2, 22, 10, 10, 10), date_time_init(2005, 2, 22, 10, 10, 10).prev_day(0)
- assert_equal date_time_init(2005, 2, 21, 10, 10, 10), date_time_init(2005, 2, 22, 10, 10, 10).prev_day(1)
- assert_equal date_time_init(2005, 2, 20, 10, 10, 10), date_time_init(2005, 2, 22, 10, 10, 10).prev_day(2)
- 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
end
- def test_next_day
- assert_equal date_time_init(2005, 2, 20, 10, 10, 10), date_time_init(2005, 2, 22, 10, 10, 10).next_day(-2)
- assert_equal date_time_init(2005, 2, 21, 10, 10, 10), date_time_init(2005, 2, 22, 10, 10, 10).next_day(-1)
- assert_equal date_time_init(2005, 2, 22, 10, 10, 10), date_time_init(2005, 2, 22, 10, 10, 10).next_day(0)
- assert_equal date_time_init(2005, 2, 23, 10, 10, 10), date_time_init(2005, 2, 22, 10, 10, 10).next_day(1)
- assert_equal date_time_init(2005, 2, 24, 10, 10, 10), date_time_init(2005, 2, 22, 10, 10, 10).next_day(2)
- 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)
@@ -161,16 +141,6 @@ module DateAndTimeBehavior
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
- assert_equal date_time_init(2004, 12, 22, 10, 10, 10), date_time_init(2005, 2, 22, 10, 10, 10).next_month(-2)
- assert_equal date_time_init(2005, 1, 22, 10, 10, 10), date_time_init(2005, 2, 22, 10, 10, 10).next_month(-1)
- assert_equal date_time_init(2005, 2, 22, 10, 10, 10), date_time_init(2005, 2, 22, 10, 10, 10).next_month(0)
- assert_equal date_time_init(2005, 3, 22, 10, 10, 10), date_time_init(2005, 2, 22, 10, 10, 10).next_month(1)
- assert_equal date_time_init(2005, 4, 22, 10, 10, 10), date_time_init(2005, 2, 22, 10, 10, 10).next_month(2)
- assert_equal date_time_init(2005, 3, 22, 10, 10, 10), date_time_init(2005, 2, 22, 10, 10, 10).next_month
- assert_equal date_time_init(2005, 4, 22, 10, 10, 10), date_time_init(2005, 2, 22, 10, 10, 10).next_month.next_month
- 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
end
@@ -179,16 +149,6 @@ module DateAndTimeBehavior
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(2003, 6, 5, 10, 10, 10), date_time_init(2005, 6, 5, 10, 10, 10).next_year(-2)
- assert_equal date_time_init(2004, 6, 5, 10, 10, 10), date_time_init(2005, 6, 5, 10, 10, 10).next_year(-1)
- assert_equal date_time_init(2005, 6, 5, 10, 10, 10), date_time_init(2005, 6, 5, 10, 10, 10).next_year(0)
- assert_equal date_time_init(2006, 6, 5, 10, 10, 10), date_time_init(2005, 6, 5, 10, 10, 10).next_year(1)
- assert_equal date_time_init(2007, 6, 5, 10, 10, 10), date_time_init(2005, 6, 5, 10, 10, 10).next_year(2)
- 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(2007, 6, 5, 10, 10, 10), date_time_init(2005, 6, 5, 10, 10, 10).next_year.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)
@@ -229,16 +189,6 @@ module DateAndTimeBehavior
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
- assert_equal date_time_init(2005, 4, 22, 10, 10, 10), date_time_init(2005, 2, 22, 10, 10, 10).prev_month(-2)
- assert_equal date_time_init(2005, 3, 22, 10, 10, 10), date_time_init(2005, 2, 22, 10, 10, 10).prev_month(-1)
- assert_equal date_time_init(2005, 2, 22, 10, 10, 10), date_time_init(2005, 2, 22, 10, 10, 10).prev_month(0)
- assert_equal date_time_init(2005, 1, 22, 10, 10, 10), date_time_init(2005, 2, 22, 10, 10, 10).prev_month(1)
- assert_equal date_time_init(2004, 12, 22, 10, 10, 10), date_time_init(2005, 2, 22, 10, 10, 10).prev_month(2)
- assert_equal date_time_init(2005, 1, 22, 10, 10, 10), date_time_init(2005, 2, 22, 10, 10, 10).prev_month
- assert_equal date_time_init(2004, 12, 22, 10, 10, 10), date_time_init(2005, 2, 22, 10, 10, 10).prev_month.prev_month
- 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
end
@@ -247,16 +197,6 @@ module DateAndTimeBehavior
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(2007, 6, 5, 10, 10, 10), date_time_init(2005, 6, 5, 10, 10, 10).prev_year(-2)
- assert_equal date_time_init(2006, 6, 5, 10, 10, 10), date_time_init(2005, 6, 5, 10, 10, 10).prev_year(-1)
- assert_equal date_time_init(2005, 6, 5, 10, 10, 10), date_time_init(2005, 6, 5, 10, 10, 10).prev_year(0)
- assert_equal date_time_init(2004, 6, 5, 10, 10, 10), date_time_init(2005, 6, 5, 10, 10, 10).prev_year(1)
- assert_equal date_time_init(2003, 6, 5, 10, 10, 10), date_time_init(2005, 6, 5, 10, 10, 10).prev_year(2)
- 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(2003, 6, 5, 10, 10, 10), date_time_init(2005, 6, 5, 10, 10, 10).prev_year.prev_year
- end
-
def test_last_month_on_31st
assert_equal date_time_init(2004, 2, 29, 0, 0, 0), date_time_init(2004, 3, 31, 0, 0, 0).last_month
end
diff --git a/activesupport/test/core_ext/date_ext_test.rb b/activesupport/test/core_ext/date_ext_test.rb
index b8652884ce..7060dd3c56 100644
--- a/activesupport/test/core_ext/date_ext_test.rb
+++ b/activesupport/test/core_ext/date_ext_test.rb
@@ -112,14 +112,6 @@ class DateExtCalculationsTest < ActiveSupport::TestCase
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
- end
-
- def test_prev_year_in_calendar_reform
- assert_equal Date.new(1582, 10, 4), Date.new(1583, 10, 14).prev_year
- end
-
def test_last_year_in_leap_years
assert_equal Date.new(1999, 2, 28), Date.new(2000, 2, 29).last_year
end
@@ -128,14 +120,6 @@ class DateExtCalculationsTest < ActiveSupport::TestCase
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
- end
-
- def test_next_year_in_calendar_reform
- 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)
diff --git a/activesupport/test/core_ext/enumerable_test.rb b/activesupport/test/core_ext/enumerable_test.rb
index 381b5a1f32..a9bf4b82f4 100644
--- a/activesupport/test/core_ext/enumerable_test.rb
+++ b/activesupport/test/core_ext/enumerable_test.rb
@@ -242,4 +242,28 @@ class EnumerableTests < ActiveSupport::TestCase
])
assert_equal [[5, 99], [15, 0], [10, 50]], payments.pluck(:dollars, :cents)
end
+
+ def test_compact_blank
+ values = GenericEnumerable.new([1, "", nil, 2, " ", [], {}, false, true])
+
+ assert_equal [1, 2, true], values.compact_blank
+ end
+
+ def test_array_compact_blank!
+ values = [1, "", nil, 2, " ", [], {}, false, true]
+ values.compact_blank!
+
+ assert_equal [1, 2, true], values
+ end
+
+ def test_hash_compact_blank
+ values = { a: "", b: 1, c: nil, d: [], e: false, f: true }
+ assert_equal({ b: 1, f: true }, values.compact_blank)
+ end
+
+ def test_hash_compact_blank!
+ values = { a: "", b: 1, c: nil, d: [], e: false, f: true }
+ values.compact_blank!
+ assert_equal({ b: 1, f: true }, values)
+ end
end
diff --git a/activesupport/test/core_ext/module_test.rb b/activesupport/test/core_ext/module_test.rb
index 04692f1484..dd36a9373a 100644
--- a/activesupport/test/core_ext/module_test.rb
+++ b/activesupport/test/core_ext/module_test.rb
@@ -25,7 +25,6 @@ Someone = Struct.new(:name, :place) do
delegate :bar, to: :place, allow_nil: true
private
-
def private_name
"Private"
end
@@ -92,6 +91,16 @@ DecoratedTester = Struct.new(:client) do
delegate_missing_to :client
end
+class DecoratedMissingAllowNil
+ delegate_missing_to :case, allow_nil: true
+
+ attr_reader :case
+
+ def initialize(kase)
+ @case = kase
+ end
+end
+
class DecoratedReserved
delegate_missing_to :case
@@ -102,6 +111,24 @@ class DecoratedReserved
end
end
+class Maze
+ attr_accessor :cavern, :passages
+end
+
+class Cavern
+ delegate_missing_to :target
+
+ attr_reader :maze
+
+ def initialize(maze)
+ @maze = maze
+ end
+
+ def target
+ @maze.passages = :twisty
+ end
+end
+
class Block
def hello?
true
@@ -382,6 +409,10 @@ class ModuleTest < ActiveSupport::TestCase
assert_equal "name delegated to client, but client is nil", e.message
end
+ def test_delegate_missing_to_returns_nil_if_allow_nil_and_nil_target
+ assert_nil DecoratedMissingAllowNil.new(nil).name
+ end
+
def test_delegate_missing_to_affects_respond_to
assert_respond_to DecoratedTester.new(@david), :name
assert_not_respond_to DecoratedTester.new(@david), :private_name
@@ -398,6 +429,17 @@ class ModuleTest < ActiveSupport::TestCase
assert_respond_to DecoratedTester.new(@david), :extra_missing
end
+ def test_delegate_missing_to_does_not_interfere_with_marshallization
+ maze = Maze.new
+ maze.cavern = Cavern.new(maze)
+
+ array = [maze, nil]
+ serialized_array = Marshal.dump(array)
+ deserialized_array = Marshal.load(serialized_array)
+
+ assert_nil deserialized_array[1]
+ end
+
def test_delegate_with_case
event = Event.new(Tester.new)
assert_equal 1, event.foo
diff --git a/activesupport/test/core_ext/object/duplicable_test.rb b/activesupport/test/core_ext/object/duplicable_test.rb
index 5203434ae6..a577c30c40 100644
--- a/activesupport/test/core_ext/object/duplicable_test.rb
+++ b/activesupport/test/core_ext/object/duplicable_test.rb
@@ -6,13 +6,8 @@ require "active_support/core_ext/object/duplicable"
require "active_support/core_ext/numeric/time"
class DuplicableTest < ActiveSupport::TestCase
- if RUBY_VERSION >= "2.5.0"
- RAISE_DUP = [method(:puts)]
- ALLOW_DUP = ["1", "symbol_from_string".to_sym, Object.new, /foo/, [], {}, Time.now, Class.new, Module.new, BigDecimal("4.56"), nil, false, true, 1, 2.3, Complex(1), Rational(1)]
- else
- RAISE_DUP = [method(:puts), Complex(1), Rational(1)]
- ALLOW_DUP = ["1", "symbol_from_string".to_sym, Object.new, /foo/, [], {}, Time.now, Class.new, Module.new, BigDecimal("4.56"), nil, false, true, 1, 2.3]
- end
+ RAISE_DUP = [method(:puts), method(:puts).unbind]
+ ALLOW_DUP = ["1", "symbol_from_string".to_sym, Object.new, /foo/, [], {}, Time.now, Class.new, Module.new, BigDecimal("4.56"), nil, false, true, 1, 2.3, Complex(1), Rational(1)]
def test_duplicable
rubinius_skip "* Method#dup is allowed at the moment on Rubinius\n" \
diff --git a/activesupport/test/core_ext/string_ext_test.rb b/activesupport/test/core_ext/string_ext_test.rb
index 4ffa33aa61..c5a000b67a 100644
--- a/activesupport/test/core_ext/string_ext_test.rb
+++ b/activesupport/test/core_ext/string_ext_test.rb
@@ -291,6 +291,11 @@ class StringInflectionsTest < ActiveSupport::TestCase
assert_equal "Hello Big[...]", "Hello Big World!".truncate(15, omission: "[...]", separator: /\s/)
end
+ def test_truncate_returns_frozen_string
+ assert_not "Hello World!".truncate(12).frozen?
+ assert_not "Hello World!!".truncate(12).frozen?
+ end
+
def test_truncate_bytes
assert_equal "👍👍👍👍", "👍👍👍👍".truncate_bytes(16)
assert_equal "👍👍👍👍", "👍👍👍👍".truncate_bytes(16, omission: nil)
diff --git a/activesupport/test/core_ext/time_ext_test.rb b/activesupport/test/core_ext/time_ext_test.rb
index 590b81b770..41ab98e721 100644
--- a/activesupport/test/core_ext/time_ext_test.rb
+++ b/activesupport/test/core_ext/time_ext_test.rb
@@ -949,6 +949,66 @@ class TimeExtCalculationsTest < ActiveSupport::TestCase
assert_equal "invalid date", exception.message
end
+
+ def test_prev_day
+ assert_equal date_time_init(2005, 2, 24, 10, 10, 10), date_time_init(2005, 2, 22, 10, 10, 10).prev_day(-2)
+ assert_equal date_time_init(2005, 2, 23, 10, 10, 10), date_time_init(2005, 2, 22, 10, 10, 10).prev_day(-1)
+ assert_equal date_time_init(2005, 2, 22, 10, 10, 10), date_time_init(2005, 2, 22, 10, 10, 10).prev_day(0)
+ assert_equal date_time_init(2005, 2, 21, 10, 10, 10), date_time_init(2005, 2, 22, 10, 10, 10).prev_day(1)
+ assert_equal date_time_init(2005, 2, 20, 10, 10, 10), date_time_init(2005, 2, 22, 10, 10, 10).prev_day(2)
+ 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_next_day
+ assert_equal date_time_init(2005, 2, 20, 10, 10, 10), date_time_init(2005, 2, 22, 10, 10, 10).next_day(-2)
+ assert_equal date_time_init(2005, 2, 21, 10, 10, 10), date_time_init(2005, 2, 22, 10, 10, 10).next_day(-1)
+ assert_equal date_time_init(2005, 2, 22, 10, 10, 10), date_time_init(2005, 2, 22, 10, 10, 10).next_day(0)
+ assert_equal date_time_init(2005, 2, 23, 10, 10, 10), date_time_init(2005, 2, 22, 10, 10, 10).next_day(1)
+ assert_equal date_time_init(2005, 2, 24, 10, 10, 10), date_time_init(2005, 2, 22, 10, 10, 10).next_day(2)
+ 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_prev_month
+ assert_equal date_time_init(2005, 4, 22, 10, 10, 10), date_time_init(2005, 2, 22, 10, 10, 10).prev_month(-2)
+ assert_equal date_time_init(2005, 3, 22, 10, 10, 10), date_time_init(2005, 2, 22, 10, 10, 10).prev_month(-1)
+ assert_equal date_time_init(2005, 2, 22, 10, 10, 10), date_time_init(2005, 2, 22, 10, 10, 10).prev_month(0)
+ assert_equal date_time_init(2005, 1, 22, 10, 10, 10), date_time_init(2005, 2, 22, 10, 10, 10).prev_month(1)
+ assert_equal date_time_init(2004, 12, 22, 10, 10, 10), date_time_init(2005, 2, 22, 10, 10, 10).prev_month(2)
+ assert_equal date_time_init(2005, 1, 22, 10, 10, 10), date_time_init(2005, 2, 22, 10, 10, 10).prev_month
+ assert_equal date_time_init(2004, 12, 22, 10, 10, 10), date_time_init(2005, 2, 22, 10, 10, 10).prev_month.prev_month
+ end
+
+ def test_next_month
+ assert_equal date_time_init(2004, 12, 22, 10, 10, 10), date_time_init(2005, 2, 22, 10, 10, 10).next_month(-2)
+ assert_equal date_time_init(2005, 1, 22, 10, 10, 10), date_time_init(2005, 2, 22, 10, 10, 10).next_month(-1)
+ assert_equal date_time_init(2005, 2, 22, 10, 10, 10), date_time_init(2005, 2, 22, 10, 10, 10).next_month(0)
+ assert_equal date_time_init(2005, 3, 22, 10, 10, 10), date_time_init(2005, 2, 22, 10, 10, 10).next_month(1)
+ assert_equal date_time_init(2005, 4, 22, 10, 10, 10), date_time_init(2005, 2, 22, 10, 10, 10).next_month(2)
+ assert_equal date_time_init(2005, 3, 22, 10, 10, 10), date_time_init(2005, 2, 22, 10, 10, 10).next_month
+ assert_equal date_time_init(2005, 4, 22, 10, 10, 10), date_time_init(2005, 2, 22, 10, 10, 10).next_month.next_month
+ end
+
+ def test_prev_year
+ assert_equal date_time_init(2007, 6, 5, 10, 10, 10), date_time_init(2005, 6, 5, 10, 10, 10).prev_year(-2)
+ assert_equal date_time_init(2006, 6, 5, 10, 10, 10), date_time_init(2005, 6, 5, 10, 10, 10).prev_year(-1)
+ assert_equal date_time_init(2005, 6, 5, 10, 10, 10), date_time_init(2005, 6, 5, 10, 10, 10).prev_year(0)
+ assert_equal date_time_init(2004, 6, 5, 10, 10, 10), date_time_init(2005, 6, 5, 10, 10, 10).prev_year(1)
+ assert_equal date_time_init(2003, 6, 5, 10, 10, 10), date_time_init(2005, 6, 5, 10, 10, 10).prev_year(2)
+ 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(2003, 6, 5, 10, 10, 10), date_time_init(2005, 6, 5, 10, 10, 10).prev_year.prev_year
+ end
+
+ def test_next_year
+ assert_equal date_time_init(2003, 6, 5, 10, 10, 10), date_time_init(2005, 6, 5, 10, 10, 10).next_year(-2)
+ assert_equal date_time_init(2004, 6, 5, 10, 10, 10), date_time_init(2005, 6, 5, 10, 10, 10).next_year(-1)
+ assert_equal date_time_init(2005, 6, 5, 10, 10, 10), date_time_init(2005, 6, 5, 10, 10, 10).next_year(0)
+ assert_equal date_time_init(2006, 6, 5, 10, 10, 10), date_time_init(2005, 6, 5, 10, 10, 10).next_year(1)
+ assert_equal date_time_init(2007, 6, 5, 10, 10, 10), date_time_init(2005, 6, 5, 10, 10, 10).next_year(2)
+ 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(2007, 6, 5, 10, 10, 10), date_time_init(2005, 6, 5, 10, 10, 10).next_year.next_year
+ end
end
class TimeExtMarshalingTest < ActiveSupport::TestCase
diff --git a/activesupport/test/dependencies_test.rb b/activesupport/test/dependencies_test.rb
index d4e709137e..6bad69f7f2 100644
--- a/activesupport/test/dependencies_test.rb
+++ b/activesupport/test/dependencies_test.rb
@@ -481,17 +481,14 @@ class DependenciesTest < ActiveSupport::TestCase
end
end
- # This raises only on 2.5.. (warns on ..2.4)
- if RUBY_VERSION > "2.5"
- def test_access_thru_and_upwards_fails
- with_autoloading_fixtures do
- assert_not defined?(ModuleFolder)
- assert_raise(NameError) { ModuleFolder::Object }
- assert_raise(NameError) { ModuleFolder::NestedClass::Object }
- end
- ensure
- remove_constants(:ModuleFolder)
+ def test_access_thru_and_upwards_fails
+ with_autoloading_fixtures do
+ assert_not defined?(ModuleFolder)
+ assert_raise(NameError) { ModuleFolder::Object }
+ assert_raise(NameError) { ModuleFolder::NestedClass::Object }
end
+ ensure
+ remove_constants(:ModuleFolder)
end
def test_non_existing_const_raises_name_error_with_fully_qualified_name
@@ -595,6 +592,13 @@ class DependenciesTest < ActiveSupport::TestCase
nil_name = Module.new
def nil_name.name() nil end
assert_not ActiveSupport::Dependencies.autoloaded?(nil_name)
+
+ invalid_constant_name = Module.new do
+ def self.name
+ "primary::SchemaMigration"
+ end
+ end
+ assert_not ActiveSupport::Dependencies.autoloaded?(invalid_constant_name)
end
ensure
remove_constants(:ModuleFolder)
diff --git a/activesupport/test/deprecation/method_wrappers_test.rb b/activesupport/test/deprecation/method_wrappers_test.rb
index 18729941bc..8c40534452 100644
--- a/activesupport/test/deprecation/method_wrappers_test.rb
+++ b/activesupport/test/deprecation/method_wrappers_test.rb
@@ -10,12 +10,10 @@ class MethodWrappersTest < ActiveSupport::TestCase
alias_method :old_method, :new_method
protected
-
def new_protected_method; "abc" end
alias_method :old_protected_method, :new_protected_method
private
-
def new_private_method; "abc" end
alias_method :old_private_method, :new_private_method
end
@@ -89,12 +87,4 @@ class MethodWrappersTest < ActiveSupport::TestCase
warning = /old_method is deprecated and will be removed from Rails \d.\d \(use new_method instead\)/
assert_deprecated(warning) { assert_equal "abc", @klass.old_method }
end
-
- def test_method_with_without_deprecation_is_exposed
- ActiveSupport::Deprecation.deprecate_methods(@klass, old_method: :new_method)
-
- warning = /old_method is deprecated and will be removed from Rails \d.\d \(use new_method instead\)/
- assert_deprecated(warning) { assert_equal "abc", @klass.new.old_method_with_deprecation }
- assert_equal "abc", @klass.new.old_method_without_deprecation
- end
end
diff --git a/activesupport/test/deprecation_test.rb b/activesupport/test/deprecation_test.rb
index f25c704586..ae2f4a6e58 100644
--- a/activesupport/test/deprecation_test.rb
+++ b/activesupport/test/deprecation_test.rb
@@ -38,6 +38,11 @@ class Deprecatee
C = 1
end
A = ActiveSupport::Deprecation::DeprecatedConstantProxy.new("Deprecatee::A", "Deprecatee::B::C")
+
+ module New
+ class Descendant; end
+ end
+ Old = ActiveSupport::Deprecation::DeprecatedConstantProxy.new("Deprecatee::Old", "Deprecatee::New")
end
class DeprecateeWithAccessor
@@ -210,6 +215,18 @@ class DeprecationTest < ActiveSupport::TestCase
assert_not_deprecated { assert_equal Deprecatee::B::C.class, Deprecatee::A.class }
end
+ def test_deprecated_constant_descendant
+ assert_not_deprecated { Deprecatee::New::Descendant }
+
+ assert_deprecated("Deprecatee::Old") do
+ assert_equal Deprecatee::Old::Descendant, Deprecatee::New::Descendant
+ end
+
+ assert_raises(NameError) do
+ assert_deprecated("Deprecatee::Old") { Deprecatee::Old::NON_EXISTENCE }
+ end
+ end
+
def test_deprecated_constant_accessor
assert_not_deprecated { DeprecateeWithAccessor::B::C }
assert_deprecated("DeprecateeWithAccessor::A") { assert_equal DeprecateeWithAccessor::B::C, DeprecateeWithAccessor::A }
diff --git a/activesupport/test/descendants_tracker_test_cases.rb b/activesupport/test/descendants_tracker_test_cases.rb
index f8752688d2..7d39783826 100644
--- a/activesupport/test/descendants_tracker_test_cases.rb
+++ b/activesupport/test/descendants_tracker_test_cases.rb
@@ -52,7 +52,6 @@ module DescendantsTrackerTestCases
end
private
-
def assert_equal_sets(expected, actual)
assert_equal Set.new(expected), Set.new(actual)
end
diff --git a/activesupport/test/evented_file_update_checker_test.rb b/activesupport/test/evented_file_update_checker_test.rb
index b2d5eb94c2..4d5a9bed7a 100644
--- a/activesupport/test/evented_file_update_checker_test.rb
+++ b/activesupport/test/evented_file_update_checker_test.rb
@@ -77,32 +77,48 @@ class EventedFileUpdateCheckerTest < ActiveSupport::TestCase
Process.wait(pid)
end
+ test "should detect changes through symlink" do
+ actual_dir = File.join(tmpdir, "actual")
+ linked_dir = File.join(tmpdir, "linked")
+
+ Dir.mkdir(actual_dir)
+ FileUtils.ln_s(actual_dir, linked_dir)
+
+ checker = new_checker([], linked_dir => ".rb") { }
+
+ assert_not_predicate checker, :updated?
+
+ FileUtils.touch(File.join(actual_dir, "a.rb"))
+ wait
+
+ assert_predicate checker, :updated?
+ assert checker.execute_if_updated
+ end
+
test "updated should become true when nonexistent directory is added later" do
- Dir.mktmpdir do |dir|
- watched_dir = File.join(dir, "app")
- unwatched_dir = File.join(dir, "node_modules")
- not_exist_watched_dir = File.join(dir, "test")
+ watched_dir = File.join(tmpdir, "app")
+ unwatched_dir = File.join(tmpdir, "node_modules")
+ not_exist_watched_dir = File.join(tmpdir, "test")
- Dir.mkdir(watched_dir)
- Dir.mkdir(unwatched_dir)
+ Dir.mkdir(watched_dir)
+ Dir.mkdir(unwatched_dir)
- checker = new_checker([], watched_dir => ".rb", not_exist_watched_dir => ".rb") { }
+ checker = new_checker([], watched_dir => ".rb", not_exist_watched_dir => ".rb") { }
- FileUtils.touch(File.join(watched_dir, "a.rb"))
- wait
- assert_predicate checker, :updated?
- assert checker.execute_if_updated
+ FileUtils.touch(File.join(watched_dir, "a.rb"))
+ wait
+ assert_predicate checker, :updated?
+ assert checker.execute_if_updated
- Dir.mkdir(not_exist_watched_dir)
- wait
- assert_predicate checker, :updated?
- assert checker.execute_if_updated
+ Dir.mkdir(not_exist_watched_dir)
+ wait
+ assert_predicate checker, :updated?
+ assert checker.execute_if_updated
- FileUtils.touch(File.join(unwatched_dir, "a.rb"))
- wait
- assert_not_predicate checker, :updated?
- assert_not checker.execute_if_updated
- end
+ FileUtils.touch(File.join(unwatched_dir, "a.rb"))
+ wait
+ assert_not_predicate checker, :updated?
+ assert_not checker.execute_if_updated
end
end
@@ -156,14 +172,6 @@ class EventedFileUpdateCheckerPathHelperTest < ActiveSupport::TestCase
assert_nil @ph.longest_common_subpath([])
end
- test "#existing_parent returns the most specific existing ascendant" do
- wd = Pathname.getwd
-
- assert_equal wd, @ph.existing_parent(wd)
- assert_equal wd, @ph.existing_parent(wd.join("non-existing/directory"))
- assert_equal pn("/"), @ph.existing_parent(pn("/non-existing/directory"))
- end
-
test "#filter_out_descendants returns the same collection if there are no descendants (empty)" do
assert_equal [], @ph.filter_out_descendants([])
end
diff --git a/activesupport/test/hash_with_indifferent_access_test.rb b/activesupport/test/hash_with_indifferent_access_test.rb
index 1d6a07ec5f..af96231801 100644
--- a/activesupport/test/hash_with_indifferent_access_test.rb
+++ b/activesupport/test/hash_with_indifferent_access_test.rb
@@ -836,4 +836,32 @@ class HashWithIndifferentAccessTest < ActiveSupport::TestCase
assert_equal 3, hash_wia[:foo]
assert_equal 3, hash_wia[:bar]
end
+
+ def test_should_copy_the_default_when_converting_non_hash_to_hash_with_indifferent_access
+ non_hash = Object.new
+
+ def non_hash.to_hash
+ h = { foo: :bar }
+ h.default = :baz
+ h
+ end
+
+ hash_wia = HashWithIndifferentAccess.new(non_hash)
+ assert_equal :bar, hash_wia[:foo]
+ assert_equal :baz, hash_wia[:missing]
+ end
+
+ def test_should_copy_the_default_proc_when_converting_non_hash_to_hash_with_indifferent_access
+ non_hash = Object.new
+
+ def non_hash.to_hash
+ h = { foo: :bar }
+ h.default_proc = ->(hash, key) { hash[key] = :baz }
+ h
+ end
+
+ hash_wia = HashWithIndifferentAccess.new(non_hash)
+ assert_equal :bar, hash_wia[:foo]
+ assert_equal :baz, hash_wia[:missing]
+ end
end
diff --git a/activesupport/test/json/decoding_test.rb b/activesupport/test/json/decoding_test.rb
index 8d9587f248..2e8ea01aca 100644
--- a/activesupport/test/json/decoding_test.rb
+++ b/activesupport/test/json/decoding_test.rb
@@ -113,7 +113,6 @@ class TestJSONDecoding < ActiveSupport::TestCase
end
private
-
def with_parse_json_times(value)
old_value = ActiveSupport.parse_json_times
ActiveSupport.parse_json_times = value
diff --git a/activesupport/test/json/encoding_test.rb b/activesupport/test/json/encoding_test.rb
index 7589ffd0ea..c4e0b71f26 100644
--- a/activesupport/test/json/encoding_test.rb
+++ b/activesupport/test/json/encoding_test.rb
@@ -466,7 +466,6 @@ EXPECTED
end
private
-
def object_keys(json_object)
json_object[1..-2].scan(/([^{}:,\s]+):/).flatten.sort
end
diff --git a/activesupport/test/lazy_load_hooks_test.rb b/activesupport/test/lazy_load_hooks_test.rb
index 50a703e49f..5e9a21f047 100644
--- a/activesupport/test/lazy_load_hooks_test.rb
+++ b/activesupport/test/lazy_load_hooks_test.rb
@@ -175,7 +175,6 @@ class LazyLoadHooksTest < ActiveSupport::TestCase
end
private
-
def incr_amt
5
end
diff --git a/activesupport/test/logger_test.rb b/activesupport/test/logger_test.rb
index 160e1156b6..6f7a186022 100644
--- a/activesupport/test/logger_test.rb
+++ b/activesupport/test/logger_test.rb
@@ -258,6 +258,50 @@ class LoggerTest < ActiveSupport::TestCase
assert_level(Logger::INFO)
end
+ def test_logger_level_main_fiber_safety
+ @logger.level = Logger::INFO
+ assert_level(Logger::INFO)
+
+ fiber = Fiber.new do
+ assert_level(Logger::INFO)
+ end
+
+ @logger.silence(Logger::ERROR) do
+ assert_level(Logger::ERROR)
+ fiber.resume
+ end
+ end
+
+ def test_logger_level_local_fiber_safety
+ @logger.level = Logger::INFO
+ assert_level(Logger::INFO)
+
+ another_fiber = Fiber.new do
+ @logger.silence(Logger::ERROR) do
+ assert_level(Logger::ERROR)
+ @logger.silence(Logger::DEBUG) do
+ assert_level(Logger::DEBUG)
+ end
+ end
+
+ assert_level(Logger::INFO)
+ end
+
+ Fiber.new do
+ @logger.silence(Logger::ERROR) do
+ assert_level(Logger::ERROR)
+ @logger.silence(Logger::DEBUG) do
+ another_fiber.resume
+ assert_level(Logger::DEBUG)
+ end
+ end
+
+ assert_level(Logger::INFO)
+ end.resume
+
+ assert_level(Logger::INFO)
+ end
+
private
def level_name(level)
::Logger::Severity.constants.find do |severity|
diff --git a/activesupport/test/message_encryptor_test.rb b/activesupport/test/message_encryptor_test.rb
index 9edf07f762..097aa8b5f8 100644
--- a/activesupport/test/message_encryptor_test.rb
+++ b/activesupport/test/message_encryptor_test.rb
@@ -171,6 +171,34 @@ class MessageEncryptorTest < ActiveSupport::TestCase
assert rotated
end
+ def test_on_rotation_can_be_passed_at_the_constructor_level
+ older_message = ActiveSupport::MessageEncryptor.new(secrets[:older], "older sign").encrypt_and_sign(encoded: "message")
+
+ rotated = false
+ encryptor = ActiveSupport::MessageEncryptor.new(@secret, on_rotation: proc { rotated = true })
+ encryptor.rotate secrets[:older], "older sign"
+
+ assert_changes(:rotated, from: false, to: true) do
+ message = encryptor.decrypt_and_verify(older_message)
+
+ assert_equal({ encoded: "message" }, message)
+ end
+ end
+
+ def test_on_rotation_option_takes_precedence_over_the_one_given_in_constructor
+ older_message = ActiveSupport::MessageEncryptor.new(secrets[:older], "older sign").encrypt_and_sign(encoded: "message")
+
+ rotated = false
+ encryptor = ActiveSupport::MessageEncryptor.new(@secret, on_rotation: proc { rotated = true })
+ encryptor.rotate secrets[:older], "older sign"
+
+ assert_changes(:rotated, from: false, to: "Yes") do
+ message = encryptor.decrypt_and_verify(older_message, on_rotation: proc { rotated = "Yes" })
+
+ assert_equal({ encoded: "message" }, message)
+ end
+ end
+
def test_with_rotated_metadata
old_message = ActiveSupport::MessageEncryptor.new(secrets[:old], cipher: "aes-256-gcm").
encrypt_and_sign("metadata", purpose: :rotation)
diff --git a/activesupport/test/multibyte_chars_test.rb b/activesupport/test/multibyte_chars_test.rb
index 5f4e3f3fd3..e0e0d9afc0 100644
--- a/activesupport/test/multibyte_chars_test.rb
+++ b/activesupport/test/multibyte_chars_test.rb
@@ -783,7 +783,6 @@ class MultibyteCharsExtrasTest < ActiveSupport::TestCase
end
private
-
def string_from_classes(classes)
# Characters from the character classes as described in UAX #29
character_from_class = {
diff --git a/activesupport/test/notifications_test.rb b/activesupport/test/notifications_test.rb
index bb20d26a25..08277e5436 100644
--- a/activesupport/test/notifications_test.rb
+++ b/activesupport/test/notifications_test.rb
@@ -20,7 +20,6 @@ module Notifications
end
private
-
def event(*args)
ActiveSupport::Notifications::Event.new(*args)
end
@@ -42,6 +41,27 @@ module Notifications
assert_operator event.duration, :>, 0
end
+ def test_subscribe_to_events_where_payload_is_changed_during_instrumentation
+ @notifier.subscribe do |event|
+ assert_equal "success!", event.payload[:my_key]
+ end
+
+ ActiveSupport::Notifications.instrument("foo") do |payload|
+ payload[:my_key] = "success!"
+ end
+ end
+
+ def test_subscribe_to_events_can_handle_nested_hashes_in_the_paylaod
+ @notifier.subscribe do |event|
+ assert_equal "success!", event.payload[:some_key][:key_one]
+ assert_equal "great_success!", event.payload[:some_key][:key_two]
+ end
+
+ ActiveSupport::Notifications.instrument("foo", some_key: { key_one: "success!" }) do |payload|
+ payload[:some_key][:key_two] = "great_success!"
+ end
+ end
+
def test_subscribe_via_top_level_api
old_notifier = ActiveSupport::Notifications.notifier
ActiveSupport::Notifications.notifier = ActiveSupport::Notifications::Fanout.new
@@ -62,6 +82,38 @@ module Notifications
end
end
+ class TimedAndMonotonicTimedSubscriberTest < TestCase
+ def test_subscribe
+ event_name = "foo"
+ class_of_started = nil
+ class_of_finished = nil
+
+ ActiveSupport::Notifications.subscribe(event_name) do |name, started, finished, unique_id, data|
+ class_of_started = started.class
+ class_of_finished = finished.class
+ end
+
+ ActiveSupport::Notifications.instrument(event_name)
+
+ assert_equal [Time, Time], [class_of_started, class_of_finished]
+ end
+
+ def test_monotonic_subscribe
+ event_name = "foo"
+ class_of_started = nil
+ class_of_finished = nil
+
+ ActiveSupport::Notifications.monotonic_subscribe(event_name) do |name, started, finished, unique_id, data|
+ class_of_started = started.class
+ class_of_finished = finished.class
+ end
+
+ ActiveSupport::Notifications.instrument(event_name)
+
+ assert_equal [Float, Float], [class_of_started, class_of_finished]
+ end
+ end
+
class SubscribedTest < TestCase
def test_subscribed
name = "foo"
@@ -81,6 +133,24 @@ module Notifications
assert_equal expected, events
end
+ def test_subscribed_all_messages
+ name = "foo"
+ name2 = name * 2
+ expected = [name, name2, name]
+
+ events = []
+ callback = lambda { |*_| events << _.first }
+ ActiveSupport::Notifications.subscribed(callback) do
+ ActiveSupport::Notifications.instrument(name)
+ ActiveSupport::Notifications.instrument(name2)
+ ActiveSupport::Notifications.instrument(name)
+ end
+ assert_equal expected, events
+
+ ActiveSupport::Notifications.instrument(name)
+ assert_equal expected, events
+ end
+
def test_subscribing_to_instrumentation_while_inside_it
# the repro requires that there are no evented subscribers for the "foo" event,
# so we have to duplicate some of the setup code
@@ -95,6 +165,42 @@ module Notifications
ensure
ActiveSupport::Notifications.notifier = old_notifier
end
+
+ def test_timed_subscribed
+ event_name = "foo"
+ class_of_started = nil
+ class_of_finished = nil
+ callback = lambda do |name, started, finished, unique_id, data|
+ class_of_started = started.class
+ class_of_finished = finished.class
+ end
+
+ ActiveSupport::Notifications.subscribed(callback, event_name) do
+ ActiveSupport::Notifications.instrument(event_name)
+ end
+
+ ActiveSupport::Notifications.instrument(event_name)
+
+ assert_equal [Time, Time], [class_of_started, class_of_finished]
+ end
+
+ def test_monotonic_timed_subscribed
+ event_name = "foo"
+ class_of_started = nil
+ class_of_finished = nil
+ callback = lambda do |name, started, finished, unique_id, data|
+ class_of_started = started.class
+ class_of_finished = finished.class
+ end
+
+ ActiveSupport::Notifications.subscribed(callback, event_name, monotonic: true) do
+ ActiveSupport::Notifications.instrument(event_name)
+ end
+
+ ActiveSupport::Notifications.instrument(event_name)
+
+ assert_equal [Float, Float], [class_of_started, class_of_finished]
+ end
end
class UnsubscribeTest < TestCase
@@ -310,15 +416,22 @@ module Notifications
assert_in_delta 10.0, event.duration, 0.00001
end
+ def test_event_cpu_time_does_not_raise_error_when_start_or_finished_not_called
+ time = Time.now
+ event = event(:foo, time, time + 0.01, random_id, {})
+
+ assert_equal 0, event.cpu_time
+ end
+
def test_events_consumes_information_given_as_payload
- event = event(:foo, Time.now, Time.now + 1, random_id, payload: :bar)
+ event = event(:foo, Concurrent.monotonic_time, Concurrent.monotonic_time + 1, random_id, payload: :bar)
assert_equal Hash[payload: :bar], event.payload
end
def test_event_is_parent_based_on_children
- time = Time.utc(2009, 01, 01, 0, 0, 1)
+ time = Concurrent.monotonic_time
- parent = event(:foo, Time.utc(2009), Time.utc(2009) + 100, random_id, {})
+ parent = event(:foo, Concurrent.monotonic_time, Concurrent.monotonic_time + 100, random_id, {})
child = event(:foo, time, time + 10, random_id, {})
not_child = event(:foo, time, time + 100, random_id, {})
diff --git a/activesupport/test/parameter_filter_test.rb b/activesupport/test/parameter_filter_test.rb
index d2dc71061d..e680a22479 100644
--- a/activesupport/test/parameter_filter_test.rb
+++ b/activesupport/test/parameter_filter_test.rb
@@ -28,10 +28,17 @@ class ParameterFilterTest < ActiveSupport::TestCase
value.replace("world!") if original_params["barg"]["blah"] == "bar" && key == "hello"
}
+ filter_words << lambda { |key, value|
+ value.upcase! if key == "array_elements"
+ }
+
parameter_filter = ActiveSupport::ParameterFilter.new(filter_words)
before_filter["barg"] = { :bargain => "gain", "blah" => "bar", "bar" => { "bargain" => { "blah" => "foo", "hello" => "world" } } }
after_filter["barg"] = { :bargain => "niag", "blah" => "[FILTERED]", "bar" => { "bargain" => { "blah" => "[FILTERED]", "hello" => "world!" } } }
+ before_filter["array_elements"] = %w(element1 element2)
+ after_filter["array_elements"] = %w(ELEMENT1 ELEMENT2)
+
assert_equal after_filter, parameter_filter.filter(before_filter)
end
end
diff --git a/activesupport/test/safe_buffer_test.rb b/activesupport/test/safe_buffer_test.rb
index b1a1c2d390..f475e05c9a 100644
--- a/activesupport/test/safe_buffer_test.rb
+++ b/activesupport/test/safe_buffer_test.rb
@@ -150,6 +150,14 @@ class SafeBufferTest < ActiveSupport::TestCase
assert_equal "hello&lt;&gt;", clean + @buffer
end
+ test "Should preserve html_safe? status on multiplication" do
+ multiplied_safe_buffer = "<br />".html_safe * 2
+ assert_predicate multiplied_safe_buffer, :html_safe?
+
+ multiplied_unsafe_buffer = @buffer.gsub("", "<>") * 2
+ assert_not_predicate multiplied_unsafe_buffer, :html_safe?
+ end
+
test "Should concat as a normal string when safe" do
clean = "hello".html_safe
@buffer.gsub!("", "<>")
diff --git a/activesupport/test/secure_compare_rotator_test.rb b/activesupport/test/secure_compare_rotator_test.rb
new file mode 100644
index 0000000000..d80faea128
--- /dev/null
+++ b/activesupport/test/secure_compare_rotator_test.rb
@@ -0,0 +1,57 @@
+# frozen_string_literal: true
+
+require "abstract_unit"
+require "active_support/secure_compare_rotator"
+
+class SecureCompareRotatorTest < ActiveSupport::TestCase
+ test "#secure_compare! works correctly after rotation" do
+ wrapper = ActiveSupport::SecureCompareRotator.new("old_secret")
+ wrapper.rotate("new_secret")
+
+ assert_equal(true, wrapper.secure_compare!("new_secret"))
+ end
+
+ test "#secure_compare! works correctly after multiple rotation" do
+ wrapper = ActiveSupport::SecureCompareRotator.new("old_secret")
+ wrapper.rotate("new_secret")
+ wrapper.rotate("another_secret")
+ wrapper.rotate("and_another_one")
+
+ assert_equal(true, wrapper.secure_compare!("and_another_one"))
+ end
+
+ test "#secure_compare! fails correctly when credential is not part of the rotation" do
+ wrapper = ActiveSupport::SecureCompareRotator.new("old_secret")
+ wrapper.rotate("new_secret")
+
+ assert_raises(ActiveSupport::SecureCompareRotator::InvalidMatch) do
+ wrapper.secure_compare!("different_secret")
+ end
+ end
+
+ test "#secure_compare! calls the on_rotation proc" do
+ wrapper = ActiveSupport::SecureCompareRotator.new("old_secret")
+ wrapper.rotate("new_secret")
+ wrapper.rotate("another_secret")
+ wrapper.rotate("and_another_one")
+
+ @witness = nil
+
+ assert_changes(:@witness, from: nil, to: true) do
+ assert_equal(true, wrapper.secure_compare!("and_another_one", on_rotation: -> { @witness = true }))
+ end
+ end
+
+ test "#secure_compare! calls the on_rotation proc that given in constructor" do
+ @witness = nil
+
+ wrapper = ActiveSupport::SecureCompareRotator.new("old_secret", on_rotation: -> { @witness = true })
+ wrapper.rotate("new_secret")
+ wrapper.rotate("another_secret")
+ wrapper.rotate("and_another_one")
+
+ assert_changes(:@witness, from: nil, to: true) do
+ assert_equal(true, wrapper.secure_compare!("and_another_one"))
+ end
+ end
+end
diff --git a/activesupport/test/share_lock_test.rb b/activesupport/test/share_lock_test.rb
index a40c813fe3..68e78ccc16 100644
--- a/activesupport/test/share_lock_test.rb
+++ b/activesupport/test/share_lock_test.rb
@@ -488,12 +488,10 @@ class ShareLockTest < ActiveSupport::TestCase
end
private
-
module CustomAssertions
SUFFICIENT_TIMEOUT = 0.2
private
-
def assert_threads_stuck_but_releasable_by_latch(threads, latch)
assert_threads_stuck threads
latch.count_down
diff --git a/activesupport/test/subscriber_test.rb b/activesupport/test/subscriber_test.rb
index bc8d8f1c13..829fba6057 100644
--- a/activesupport/test/subscriber_test.rb
+++ b/activesupport/test/subscriber_test.rb
@@ -17,7 +17,6 @@ class TestSubscriber < ActiveSupport::Subscriber
end
private
-
def private_party(event)
events << event
end
diff --git a/activesupport/test/test_case_test.rb b/activesupport/test/test_case_test.rb
index 56cd2665e0..dd75548e9c 100644
--- a/activesupport/test/test_case_test.rb
+++ b/activesupport/test/test_case_test.rb
@@ -315,7 +315,6 @@ class SetupAndTeardownTest < ActiveSupport::TestCase
end
private
-
def reset_callback_record
@called_back = []
end
diff --git a/guides/CHANGELOG.md b/guides/CHANGELOG.md
index 798354550b..37823047bc 100644
--- a/guides/CHANGELOG.md
+++ b/guides/CHANGELOG.md
@@ -1,35 +1,3 @@
-* Added documentation about the `variants` option to `render`
- *Edward Rudd*
-
-## Rails 6.0.0.beta3 (March 11, 2019) ##
-
-* No changes.
-
-
-## Rails 6.0.0.beta2 (February 25, 2019) ##
-
-* No changes.
-
-
-## Rails 6.0.0.beta1 (January 18, 2019) ##
-
-* Add "Action Text Overview" Guide.
-
- *DHH*
-
-* Add "Action Mailbox Basics" Guide.
-
- *DHH*
-
-* New section _Troubleshooting_ in the _Autoloading and Reloading Constants_ guide.
-
- *Xavier Noria*
-
-* Rails 6 requires Ruby 2.5.0 or newer.
-
- *Jeremy Daer*, *Kasper Timm Hansen*
-
-
-Please check [5-2-stable](https://github.com/rails/rails/blob/5-2-stable/guides/CHANGELOG.md) for previous changes.
+Please check [6-0-stable](https://github.com/rails/rails/blob/6-0-stable/guides/CHANGELOG.md) for previous changes.
diff --git a/guides/assets/images/getting_started/unknown_action_create_for_articles.png b/guides/assets/images/getting_started/unknown_action_create_for_articles.png
index ec4758e085..e75b9780cc 100644
--- a/guides/assets/images/getting_started/unknown_action_create_for_articles.png
+++ b/guides/assets/images/getting_started/unknown_action_create_for_articles.png
Binary files differ
diff --git a/guides/bug_report_templates/action_controller_gem.rb b/guides/bug_report_templates/action_controller_gem.rb
index 6c74200761..3b3730c20e 100644
--- a/guides/bug_report_templates/action_controller_gem.rb
+++ b/guides/bug_report_templates/action_controller_gem.rb
@@ -8,7 +8,7 @@ gemfile(true) do
git_source(:github) { |repo| "https://github.com/#{repo}.git" }
# Activate the gem you are reporting the issue against.
- gem "rails", "5.2.0"
+ gem "rails", "6.0.0.rc1"
end
require "rack/test"
diff --git a/guides/bug_report_templates/active_job_gem.rb b/guides/bug_report_templates/active_job_gem.rb
index eb9d1316e9..b5a7bc4151 100644
--- a/guides/bug_report_templates/active_job_gem.rb
+++ b/guides/bug_report_templates/active_job_gem.rb
@@ -8,7 +8,7 @@ gemfile(true) do
git_source(:github) { |repo| "https://github.com/#{repo}.git" }
# Activate the gem you are reporting the issue against.
- gem "activejob", "5.2.0"
+ gem "activejob", "6.0.0.rc1"
end
require "minitest/autorun"
diff --git a/guides/bug_report_templates/active_record_gem.rb b/guides/bug_report_templates/active_record_gem.rb
index 74b329115d..0cc80d195c 100644
--- a/guides/bug_report_templates/active_record_gem.rb
+++ b/guides/bug_report_templates/active_record_gem.rb
@@ -8,8 +8,8 @@ gemfile(true) do
git_source(:github) { |repo| "https://github.com/#{repo}.git" }
# Activate the gem you are reporting the issue against.
- gem "activerecord", "5.2.0"
- gem "sqlite3", "~> 1.3.6"
+ gem "activerecord", "6.0.0.rc1"
+ gem "sqlite3"
end
require "active_record"
diff --git a/guides/bug_report_templates/active_record_migrations_gem.rb b/guides/bug_report_templates/active_record_migrations_gem.rb
index 8d71863034..3d2f23176b 100644
--- a/guides/bug_report_templates/active_record_migrations_gem.rb
+++ b/guides/bug_report_templates/active_record_migrations_gem.rb
@@ -8,8 +8,8 @@ gemfile(true) do
git_source(:github) { |repo| "https://github.com/#{repo}.git" }
# Activate the gem you are reporting the issue against.
- gem "activerecord", "5.2.0"
- gem "sqlite3", "~> 1.3.6"
+ gem "activerecord", "6.0.0.rc1"
+ gem "sqlite3"
end
require "active_record"
@@ -29,7 +29,7 @@ end
class Payment < ActiveRecord::Base
end
-class ChangeAmountToAddScale < ActiveRecord::Migration[5.2]
+class ChangeAmountToAddScale < ActiveRecord::Migration[6.0]
def change
reversible do |dir|
dir.up do
diff --git a/guides/bug_report_templates/generic_gem.rb b/guides/bug_report_templates/generic_gem.rb
index 3fd54437f7..9c7ed2c2a1 100644
--- a/guides/bug_report_templates/generic_gem.rb
+++ b/guides/bug_report_templates/generic_gem.rb
@@ -8,7 +8,7 @@ gemfile(true) do
git_source(:github) { |repo| "https://github.com/#{repo}.git" }
# Activate the gem you are reporting the issue against.
- gem "activesupport", "5.2.0"
+ gem "activesupport", "6.0.0.rc1"
end
require "active_support"
diff --git a/guides/rails_guides/generator.rb b/guides/rails_guides/generator.rb
index 7d4a15962c..2b2ccfa906 100644
--- a/guides/rails_guides/generator.rb
+++ b/guides/rails_guides/generator.rb
@@ -43,7 +43,6 @@ module RailsGuides
end
private
-
def register_kindle_mime_types
Mime::Type.register_alias("application/xml", :opf, %w(opf))
Mime::Type.register_alias("application/xml", :ncx, %w(ncx))
diff --git a/guides/rails_guides/indexer.rb b/guides/rails_guides/indexer.rb
index c707464cdf..576ec85b27 100644
--- a/guides/rails_guides/indexer.rb
+++ b/guides/rails_guides/indexer.rb
@@ -18,7 +18,6 @@ module RailsGuides
end
private
-
def process(string, current_level = 3, counters = [1])
s = StringScanner.new(string)
diff --git a/guides/rails_guides/markdown.rb b/guides/rails_guides/markdown.rb
index 018f49ffd0..d926d233bb 100644
--- a/guides/rails_guides/markdown.rb
+++ b/guides/rails_guides/markdown.rb
@@ -30,7 +30,6 @@ module RailsGuides
end
private
-
def dom_id(nodes)
dom_id = dom_id_text(nodes.last.text)
diff --git a/guides/rails_guides/markdown/renderer.rb b/guides/rails_guides/markdown/renderer.rb
index 7f14c28bbc..4877e806fd 100644
--- a/guides/rails_guides/markdown/renderer.rb
+++ b/guides/rails_guides/markdown/renderer.rb
@@ -53,7 +53,6 @@ HTML
end
private
-
def convert_footnotes(text)
text.gsub(/\[<sup>(\d+)\]<\/sup>/i) do
%(<sup class="footnote" id="footnote-#{$1}-ref">) +
diff --git a/guides/source/2_3_release_notes.md b/guides/source/2_3_release_notes.md
index ee9a499953..b46d5ee4db 100644
--- a/guides/source/2_3_release_notes.md
+++ b/guides/source/2_3_release_notes.md
@@ -415,7 +415,7 @@ select_datetime(DateTime.now, :prompt =>
### AssetTag Timestamp Caching
-You're likely familiar with Rails' practice of adding timestamps to static asset paths as a "cache buster." This helps ensure that stale copies of things like images and stylesheets don't get served out of the user's browser cache when you change them on the server. You can now modify this behavior with the `cache_asset_timestamps` configuration option for Action View. If you enable the cache, then Rails will calculate the timestamp once when it first serves an asset, and save that value. This means fewer (expensive) file system calls to serve static assets - but it also means that you can't modify any of the assets while the server is running and expect the changes to get picked up by clients.
+You're likely familiar with Rails' practice of adding timestamps to static asset paths as a "cache buster". This helps ensure that stale copies of things like images and stylesheets don't get served out of the user's browser cache when you change them on the server. You can now modify this behavior with the `cache_asset_timestamps` configuration option for Action View. If you enable the cache, then Rails will calculate the timestamp once when it first serves an asset, and save that value. This means fewer (expensive) file system calls to serve static assets - but it also means that you can't modify any of the assets while the server is running and expect the changes to get picked up by clients.
### Asset Hosts as Objects
diff --git a/guides/source/5_1_release_notes.md b/guides/source/5_1_release_notes.md
index e885b1e42e..f870c4c47c 100644
--- a/guides/source/5_1_release_notes.md
+++ b/guides/source/5_1_release_notes.md
@@ -41,8 +41,8 @@ Major Features
[Pull Request](https://github.com/rails/rails/pull/26836)
Rails 5.1 allows managing JavaScript dependencies
-from NPM via Yarn. This will make it easy to use libraries like React, VueJS
-or any other library from NPM world. The Yarn support is integrated with
+from npm via Yarn. This will make it easy to use libraries like React, VueJS
+or any other library from npm world. The Yarn support is integrated with
the asset pipeline so that all dependencies will work seamlessly with the
Rails 5.1 app.
diff --git a/guides/source/5_2_release_notes.md b/guides/source/5_2_release_notes.md
index ac247bc3f9..7aac07dbbe 100644
--- a/guides/source/5_2_release_notes.md
+++ b/guides/source/5_2_release_notes.md
@@ -326,7 +326,7 @@ Please refer to the [Changelog][action-view] for detailed changes.
select divider `option`.
([Pull Request](https://github.com/rails/rails/pull/31088))
-* Change `form_with` to generates ids by default.
+* Change `form_with` to generate ids by default.
([Commit](https://github.com/rails/rails/commit/260d6f112a0ffdbe03e6f5051504cb441c1e94cd))
* Add `preload_link_tag` helper.
diff --git a/guides/source/6_0_release_notes.md b/guides/source/6_0_release_notes.md
index f1f41dc8fc..7c5478a03d 100644
--- a/guides/source/6_0_release_notes.md
+++ b/guides/source/6_0_release_notes.md
@@ -151,25 +151,44 @@ Please refer to the [Changelog][action-cable] for detailed changes.
### Removals
+* Replace `ActionCable.startDebugging()` and `ActionCable.stopDebugging()`
+ with `ActionCable.logger.enabled`.
+ ([Pull Request](https://github.com/rails/rails/pull/34370))
+
### Deprecations
+* There are no deprecations for Action Cable in Rails 6.0.
+
### Notable changes
-* The ActionCable javascript package has been converted from CoffeeScript
- to ES2015, and we now publish the source code in the npm distribution.
+* Add support for the `channel_prefix` option for PostgreSQL subscription adapters
+ in `cable.yml`.
+ ([Pull Request](https://github.com/rails/rails/pull/35276))
+
+* Allow passing a custom configuration to `ActionCable::Server::Base`.
+ ([Pull Request](https://github.com/rails/rails/pull/34714))
+
+* Add `:action_cable_connection` and `:action_cable_channel` load hooks.
+ ([Pull Request](https://github.com/rails/rails/pull/35094))
+
+* Add `Channel::Base#broadcast_to` and `Channel::Base.broadcasting_for`.
+ ([Pull Request](https://github.com/rails/rails/pull/35021))
- This allows ActionCable users to depend on the javascript source code
- rather than the compiled code, which can produce smaller javascript bundles.
+* Close a connection when calling `reject_unauthorized_connection` from an
+ `ActionCable::Connection`.
+ ([Pull Request](https://github.com/rails/rails/pull/34194))
- This change includes some breaking changes to optional parts of the
- ActionCable javascript API:
+* Convert the Action Cable JavaScript package from CoffeeScript to ES2015 and
+ publish the source code in the npm distribution.
+ ([Pull Request](https://github.com/rails/rails/pull/34370))
- - Configuration of the WebSocket adapter and logger adapter have been moved
- from properties of `ActionCable` to properties of `ActionCable.adapters`.
+* Move the configuration of the WebSocket adapter and logger adapter
+ from properties of `ActionCable` to `ActionCable.adapters`.
+ ([Pull Request](https://github.com/rails/rails/pull/34370))
- - The `ActionCable.startDebugging()` and `ActionCable.stopDebugging()`
- methods have been removed and replaced with the property
- `ActionCable.logger.enabled`.
+* Add an `id` option to the Redis adapter to distinguish Action Cable's Redis
+ connections.
+ ([Pull Request](https://github.com/rails/rails/pull/33798))
Action Pack
-----------
@@ -178,10 +197,82 @@ Please refer to the [Changelog][action-pack] for detailed changes.
### Removals
+* Remove deprecated `fragment_cache_key` helper in favor of `combined_fragment_cache_key`.
+ ([Commit](https://github.com/rails/rails/commit/e70d3df7c9b05c129b0fdcca57f66eca316c5cfc))
+
+* Remove deprecated methods in `ActionDispatch::TestResponse`:
+ `#success?` in favor of `#successful?`, `#missing?` in favor of `#not_found?`,
+ `#error?` in favor of `#server_error?`.
+ ([Commit](https://github.com/rails/rails/commit/13ddc92e079e59a0b894e31bf5bb4fdecbd235d1))
+
### Deprecations
+* Deprecate `ActionDispatch::Http::ParameterFilter` in favor of `ActiveSupport::ParameterFilter`.
+ ([Pull Request](https://github.com/rails/rails/pull/34039))
+
+* Deprecate controller level `force_ssl` in favor of `config.force_ssl`.
+ ([Pull Request](https://github.com/rails/rails/pull/32277))
+
### Notable changes
+* Change `ActionDispatch::Response#content_type` returning Content-Type
+ header as it is.
+ ([Pull Request](https://github.com/rails/rails/pull/36034))
+
+* Raise an `ArgumentError` if a resource param contains a colon.
+ ([Pull Request](https://github.com/rails/rails/pull/35236))
+
+* Allow `ActionDispatch::SystemTestCase.driven_by` to be called with a block
+ to define specific browser capabilities.
+ ([Pull Request](https://github.com/rails/rails/pull/35081))
+
+* Add `ActionDispatch::HostAuthorization` middleware that guards against DNS rebinding
+ attacks.
+ ([Pull Request](https://github.com/rails/rails/pull/33145))
+
+* Allow the use of `parsed_body` in `ActionController::TestCase`.
+ ([Pull Request](https://github.com/rails/rails/pull/34717))
+
+* Raise an `ArgumentError` when multiple root routes exist in the same context
+ without `as:` naming specifications.
+ ([Pull Request](https://github.com/rails/rails/pull/34494))
+
+* Allow the use of `#rescue_from` for handling parameter parsing errors.
+ ([Pull Request](https://github.com/rails/rails/pull/34341))
+
+* Add `ActionController::Parameters#each_value` for iterating through parameters.
+ ([Pull Request](https://github.com/rails/rails/pull/33979))
+
+* Encode Content-Disposition filenames on `send_data` and `send_file`.
+ ([Pull Request](https://github.com/rails/rails/pull/33829))
+
+* Expose `ActionController::Parameters#each_key`.
+ ([Pull Request](https://github.com/rails/rails/pull/33758))
+
+* Add purpose and expiry metadata inside signed/encrypted cookies to prevent copying the value of
+ cookies into one another.
+ ([Pull Request](https://github.com/rails/rails/pull/32937))
+
+* Raise `ActionController::RespondToMismatchError` for conflicting `respond_to` invocations.
+ ([Pull Request](https://github.com/rails/rails/pull/33446))
+
+* Add an explicit error page for when a template is missing for a request format.
+ ([Pull Request](https://github.com/rails/rails/pull/29286))
+
+* Introduce `ActionDispatch::DebugExceptions.register_interceptor`, a way to hook into
+ DebugExceptions and process the exception, before being rendered.
+ ([Pull Request](https://github.com/rails/rails/pull/23868))
+
+* Output only one Content-Security-Policy nonce header value per request.
+ ([Pull Request](https://github.com/rails/rails/pull/32602))
+
+* Add a module specifically for the Rails default headers configuration
+ that can be explicitly included in controllers.
+ ([Pull Request](https://github.com/rails/rails/pull/32484))
+
+* Add `#dig` to `ActionDispatch::Request::Session`.
+ ([Pull Request](https://github.com/rails/rails/pull/32446))
+
Action View
-----------
@@ -189,10 +280,63 @@ Please refer to the [Changelog][action-view] for detailed changes.
### Removals
+* Remove deprecated `image_alt` helper.
+ ([Commit](https://github.com/rails/rails/commit/60c8a03c8d1e45e48fcb1055ba4c49ed3d5ff78f))
+
+* Remove an empty `RecordTagHelper` module from which the functionality
+ was already moved to the `record_tag_helper` gem.
+ ([Commit](https://github.com/rails/rails/commit/5c5ddd69b1e06fb6b2bcbb021e9b8dae17e7cb31))
+
### Deprecations
+* Deprecate `ActionView::Template.finalize_compiled_template_methods` with
+ no replacement.
+ ([Pull Request](https://github.com/rails/rails/pull/35036))
+
+* Deprecate `config.action_view.finalize_compiled_template_methods` with
+ no replacement.
+ ([Pull Request](https://github.com/rails/rails/pull/35036))
+
+* Deprecate calling private model methods from the `options_from_collection_for_select` view helper.
+ ([Pull Request](https://github.com/rails/rails/pull/33547))
+
### Notable changes
+* Clear Action View cache in development only on file changes, speeding up
+ development mode.
+ ([Pull Request](https://github.com/rails/rails/pull/35629))
+
+* Move all of the Rails npm packages into a `@rails` scope.
+ ([Pull Request](https://github.com/rails/rails/pull/34905))
+
+* Only accept formats from registered MIME types.
+ ([Pull Request](https://github.com/rails/rails/pull/35604), [Pull Request](https://github.com/rails/rails/pull/35753))
+
+* Add allocations to the template and partial rendering server output.
+ ([Pull Request](https://github.com/rails/rails/pull/34136))
+
+* Add a `year_format` option to `date_select` tag, making it possible to
+ customize year names.
+ ([Pull Request](https://github.com/rails/rails/pull/32190))
+
+* Add a `nonce: true` option for `javascript_include_tag` helper to
+ support automatic nonce generation for a Content Security Policy.
+ ([Pull Request](https://github.com/rails/rails/pull/32607))
+
+* Add a `action_view.finalize_compiled_template_methods` configuration to disable or
+ enable `ActionView::Template` finalizers.
+ ([Pull Request](https://github.com/rails/rails/pull/32418))
+
+* Extract the JavaScript `confirm` call to its own, overridable method in `rails_ujs`.
+ ([Pull Request](https://github.com/rails/rails/pull/32404))
+
+* Add a `action_controller.default_enforce_utf8` configuration option to handle
+ enforcing UTF-8 encoding. This defaults to `false`.
+ ([Pull Request](https://github.com/rails/rails/pull/32125))
+
+* Add I18n key style support for locale keys to submit tags.
+ ([Pull Request](https://github.com/rails/rails/pull/26799))
+
Action Mailer
-------------
@@ -202,8 +346,41 @@ Please refer to the [Changelog][action-mailer] for detailed changes.
### Deprecations
+* Deprecate `ActionMailer::Base.receive` in favor of Action Mailbox.
+ ([Commit](https://github.com/rails/rails/commit/e3f832a7433a291a51c5df397dc3dd654c1858cb))
+
+* Deprecate `DeliveryJob` and `Parameterized::DeliveryJob` in favor of
+ `MailDeliveryJob`.
+ ([Pull Request](https://github.com/rails/rails/pull/34591))
+
### Notable changes
+* Add `MailDeliveryJob` for delivering both regular and parameterized mail.
+ ([Pull Request](https://github.com/rails/rails/pull/34591))
+
+* Allow custom email delivery jobs to work with the Action Mailer test assertions.
+ ([Pull Request](https://github.com/rails/rails/pull/34339))
+
+* Allow specifying a template name for multipart emails with blocks instead of
+ using just the action name.
+ ([Pull Request](https://github.com/rails/rails/pull/22534))
+
+* Add `perform_deliveries` to payload of `deliver.action_mailer` notification.
+ ([Pull Request](https://github.com/rails/rails/pull/33824))
+
+* Improve the logging message when `perform_deliveries` is false to indicate
+ that sending of emails was skipped.
+ ([Pull Request](https://github.com/rails/rails/pull/33824))
+
+* Allow calling `assert_enqueued_email_with` without block.
+ ([Pull Request](https://github.com/rails/rails/pull/33258))
+
+* Perform the enqueued mail delivery jobs in the `assert_emails` block.
+ ([Pull Request](https://github.com/rails/rails/pull/32231))
+
+* Allow `ActionMailer::Base` to unregister observers and interceptors.
+ ([Pull Request](https://github.com/rails/rails/pull/32207))
+
Active Record
-------------
@@ -229,7 +406,7 @@ Please refer to the [Changelog][active-record] for detailed changes.
* Remove support for passing the column name to `count` when a block is passed.
([Commit](https://github.com/rails/rails/commit/67356f2034ab41305af7218f7c8b2fee2d614129))
-* Remove support for delegation of missing methods in a relation to arel.
+* Remove support for delegation of missing methods in a relation to Arel.
([Commit](https://github.com/rails/rails/commit/d97980a16d76ad190042b4d8578109714e9c53d0))
* Remove support for delegating missing methods in a relation to private methods of the class.
@@ -272,7 +449,7 @@ Please refer to the [Changelog][active-record] for detailed changes.
### Notable changes
-* Bump the minimum sqlite3 version to 1.4.
+* Bump the minimum version of the `sqlite3` gem to 1.4.
([Pull Request](https://github.com/rails/rails/pull/35844))
* Add `rails db:prepare` to create a database if it doesn't exist, and run its migrations.
@@ -294,7 +471,7 @@ Please refer to the [Changelog][active-record] for detailed changes.
([Pull Request](https://github.com/rails/rails/pull/35631))
* Add `rails db:seed:replant` that truncates tables of each database
- for ther current environment and loads the seeds.
+ for the current environment and loads the seeds.
([Pull Request](https://github.com/rails/rails/pull/34779))
* Add `reselect` method, which is a short-hand for `unscope(:select).select(fields)`.
@@ -336,7 +513,7 @@ Please refer to the [Changelog][active-record] for detailed changes.
([Pull Request](https://github.com/rails/rails/pull/34742))
* Add the ability to disable scopes generated by `ActiveRecord.enum`.
- ([Pull Request](https://github.com/rails/rails/pull/34605/files))
+ ([Pull Request](https://github.com/rails/rails/pull/34605))
* Make implicit ordering configurable for a column.
([Pull Request](https://github.com/rails/rails/pull/34480))
@@ -404,7 +581,7 @@ Please refer to the [Changelog][active-record] for detailed changes.
* Allow the `:to_table` option of `remove_foreign_key` to be invertible.
([Pull Request](https://github.com/rails/rails/pull/33530))
-* Fix default value for mysql time types with specified precision.
+* Fix default value for MySQL time types with specified precision.
([Pull Request](https://github.com/rails/rails/pull/33280))
* Fix the `touch` option to behave consistently with `Persistence#touch` method.
@@ -451,8 +628,72 @@ Please refer to the [Changelog][active-storage] for detailed changes.
### Deprecations
+* Deprecate `config.active_storage.queue` in favor of `config.active_storage.queues.analysis`
+ and `config.active_storage.queues.purge`.
+ ([Pull Request](https://github.com/rails/rails/pull/34838))
+
+* Deprecate `ActiveStorage::Downloading` in favor of `ActiveStorage::Blob#open`.
+ ([Commit](https://github.com/rails/rails/commit/ee21b7c2eb64def8f00887a9fafbd77b85f464f1))
+
+* Deprecate using `mini_magick` directly for generating image variants in favor of
+ `image_processing`.
+ ([Commit](https://github.com/rails/rails/commit/697f4a93ad386f9fb7795f0ba68f815f16ebad0f))
+
+* Deprecate `:combine_options` in Active Storage's ImageProcessing transformer
+ without replacement.
+ ([Commit](https://github.com/rails/rails/commit/697f4a93ad386f9fb7795f0ba68f815f16ebad0f))
+
### Notable changes
+* Add support for generating BMP image variants.
+ ([Pull Request](https://github.com/rails/rails/pull/36051))
+
+* Add support for generating TIFF image variants.
+ ([Pull Request](https://github.com/rails/rails/pull/34824))
+
+* Add support for generating progressive JPEG image variants.
+ ([Pull Request](https://github.com/rails/rails/pull/34455))
+
+* Add `ActiveStorage.routes_prefix` for configuring the Active Storage generated routes.
+ ([Pull Request](https://github.com/rails/rails/pull/33883))
+
+* Generate a 404 Not Found response on `ActiveStorage::DiskController#show` when
+ the requested file is missing from the disk service.
+ ([Pull Request](https://github.com/rails/rails/pull/33666))
+
+* Raise `ActiveStorage::FileNotFoundError` when the requested file is missing for
+ `ActiveStorage::Blob#download` and `ActiveStorage::Blob#open`.
+ ([Pull Request](https://github.com/rails/rails/pull/33666))
+
+* Add a generic `ActiveStorage::Error` class that Active Storage exceptions inherit from.
+ ([Commit](https://github.com/rails/rails/commit/18425b837149bc0d50f8d5349e1091a623762d6b))
+
+* Persist uploaded files assigned to a record to storage when the record
+ is saved instead of immediately.
+ ([Pull Request](https://github.com/rails/rails/pull/33303))
+
+* Optionally replace existing files instead of adding to them when assigning to
+ a collection of attachments (as in `@user.update!(images: [ … ])`). Use
+ `config.active_storage.replace_on_assign_to_many` to control this behavior.
+ ([Pull Request](https://github.com/rails/rails/pull/33303),
+ [Pull Request](https://github.com/rails/rails/pull/36716))
+
+* Add the ability to reflect on defined attachments using the existing
+ Active Record reflection mechanism.
+ ([Pull Request](https://github.com/rails/rails/pull/33018))
+
+* Add `ActiveStorage::Blob#open`, which downloads a blob to a tempfile on disk
+ and yields the tempfile.
+ ([Commit](https://github.com/rails/rails/commit/ee21b7c2eb64def8f00887a9fafbd77b85f464f1))
+
+* Support streaming downloads from Google Cloud Storage. Require version 1.11+
+ of the `google-cloud-storage` gem.
+ ([Pull Request](https://github.com/rails/rails/pull/32788))
+
+* Use the `image_processing` gem for Active Storage variants. This replaces using
+ `mini_magick` directly.
+ ([Pull Request](https://github.com/rails/rails/pull/32471))
+
Active Model
------------
@@ -476,6 +717,30 @@ Please refer to the [Changelog][active-model] for detailed changes.
* Add `ActiveModel::Errors#of_kind?` to check presence of a specific error.
([Pull Request](https://github.com/rails/rails/pull/34866))
+* Fix `ActiveModel::Serializers::JSON#as_json` method for timestamps.
+ ([Pull Request](https://github.com/rails/rails/pull/31503))
+
+* Fix numericality validator to still use value before type cast except Active Record.
+ ([Pull Request](https://github.com/rails/rails/pull/33654))
+
+* Fix numericality equality validation of `BigDecimal` and `Float`
+ by casting to `BigDecimal` on both ends of the validation.
+ ([Pull Request](https://github.com/rails/rails/pull/32852))
+
+* Fix year value when casting a multiparameter time hash.
+ ([Pull Request](https://github.com/rails/rails/pull/34990))
+
+* Type cast falsy boolean symbols on boolean attribute as false.
+ ([Pull Request](https://github.com/rails/rails/pull/35794))
+
+* Return correct date while converting parameters in `value_from_multiparameter_assignment`
+ for `ActiveModel::Type::Date`.
+ ([Pull Request](https://github.com/rails/rails/pull/29651))
+
+* Fall back to parent locale before falling back to the `:errors` namespace while fetching
+ error translations.
+ ([Pull Request](https://github.com/rails/rails/pull/35424))
+
Active Support
--------------
@@ -483,10 +748,196 @@ Please refer to the [Changelog][active-support] for detailed changes.
### Removals
+* Remove deprecated `#acronym_regex` method from `Inflections`.
+ ([Commit](https://github.com/rails/rails/commit/0ce67d3cd6d1b7b9576b07fecae3dd5b422a5689))
+
+* Remove deprecated `Module#reachable?` method.
+ ([Commit](https://github.com/rails/rails/commit/6eb1d56a333fd2015610d31793ed6281acd66551))
+
+* Remove `` Kernel#` `` without any replacement.
+ ([Pull Request](https://github.com/rails/rails/pull/31253))
+
### Deprecations
+* Deprecate using negative integer arguments for `String#first` and
+ `String#last`.
+ ([Pull Request](https://github.com/rails/rails/pull/33058))
+
+* Deprecate `ActiveSupport::Multibyte::Unicode#downcase/upcase/swapcase`
+ in favor of `String#downcase/upcase/swapcase`.
+ ([Pull Request](https://github.com/rails/rails/pull/34123))
+
+* Deprecate `ActiveSupport::Multibyte::Unicode#normalize`
+ and `ActiveSupport::Multibyte::Chars#normalize` in favor of
+ `String#unicode_normalize`.
+ ([Pull Request](https://github.com/rails/rails/pull/34202))
+
+* Deprecate `ActiveSupport::Multibyte::Chars.consumes?` in favor of
+ `String#is_utf8?`.
+ ([Pull Request](https://github.com/rails/rails/pull/34215))
+
+* Deprecate `ActiveSupport::Multibyte::Unicode#pack_graphemes(array)`
+ and `ActiveSupport::Multibyte::Unicode#unpack_graphemes(string)`
+ in favor of `array.flatten.pack("U*")` and `string.scan(/\X/).map(&:codepoints)`,
+ respectively.
+ ([Pull Request](https://github.com/rails/rails/pull/34254))
+
### Notable changes
+* Add support for parallel testing.
+ ([Pull Request](https://github.com/rails/rails/pull/31900))
+
+* Make sure that `String#strip_heredoc` preserves frozen-ness of strings.
+ ([Pull Request](https://github.com/rails/rails/pull/32037))
+
+* Add `String#truncate_bytes` to truncate a string to a maximum bytesize
+ without breaking multibyte characters or grapheme clusters.
+ ([Pull Request](https://github.com/rails/rails/pull/27319))
+
+* Add `private` option to `delegate` method in order to delegate to
+ private methods. This option accepts `true/false` as the value.
+ ([Pull Request](https://github.com/rails/rails/pull/31944))
+
+* Add support for translations through I18n for `ActiveSupport::Inflector#ordinal`
+ and `ActiveSupport::Inflector#ordinalize`.
+ ([Pull Request](https://github.com/rails/rails/pull/32168))
+
+* Add `before?` and `after?` methods to `Date`, `DateTime`,
+ `Time`, and `TimeWithZone`.
+ ([Pull Request](https://github.com/rails/rails/pull/32185))
+
+* Fix bug where `URI.unescape` would fail with mixed Unicode/escaped character
+ input.
+ ([Pull Request](https://github.com/rails/rails/pull/32183))
+
+* Fix bug where `ActiveSupport::Cache` would massively inflate the storage
+ size when compression was enabled.
+ ([Pull Request](https://github.com/rails/rails/pull/32539))
+
+* Redis cache store: `delete_matched` no longer blocks the Redis server.
+ ([Pull Request](https://github.com/rails/rails/pull/32614))
+
+* Fix bug where `ActiveSupport::TimeZone.all` would fail when tzinfo data for
+ any timezone defined in `ActiveSupport::TimeZone::MAPPING` was missing.
+ ([Pull Request](https://github.com/rails/rails/pull/32613))
+
+* Add `Enumerable#index_with` which allows creating a hash from an enumerable
+ with the value from a passed block or a default argument.
+ ([Pull Request](https://github.com/rails/rails/pull/32523))
+
+* Allow `Range#===` and `Range#cover?` methods to work with `Range` argument.
+ ([Pull Request](https://github.com/rails/rails/pull/32938))
+
+* Support key expiry in `increment/decrement` operations of RedisCacheStore.
+ ([Pull Request](https://github.com/rails/rails/pull/33254))
+
+* Add cpu time, idle time, and allocations features to log subscriber events.
+ ([Pull Request](https://github.com/rails/rails/pull/33449))
+
+* Add support for event object to the Active Support notification system.
+ ([Pull Request](https://github.com/rails/rails/pull/33451))
+
+* Add support for not caching `nil` entries by introducing new option `skip_nil`
+ for `ActiveSupport::Cache#fetch`.
+ ([Pull Request](https://github.com/rails/rails/pull/25437))
+
+* Add `Array#extract!` method which removes and returns the elements for which
+ block returns a true value.
+ ([Pull Request](https://github.com/rails/rails/pull/33137))
+
+* Keep an HTML-safe string HTML-safe after slicing.
+ ([Pull Request](https://github.com/rails/rails/pull/33808))
+
+* Add support for tracing constant autoloads via logging.
+ ([Commit](https://github.com/rails/rails/commit/c03bba4f1f03bad7dc034af555b7f2b329cf76f5))
+
+* Define `unfreeze_time` as an alias of `travel_back`.
+ ([Pull Request](https://github.com/rails/rails/pull/33813))
+
+* Change `ActiveSupport::TaggedLogging.new` to return a new logger instance
+ instead of mutating the one received as argument.
+ ([Pull Request](https://github.com/rails/rails/pull/27792))
+
+* Treat `#delete_prefix`, `#delete_suffix` and `#unicode_normalize` methods
+ as non HTML-safe methods.
+ ([Pull Request](https://github.com/rails/rails/pull/33990))
+
+* Fix bug where `#without` for `ActiveSupport::HashWithIndifferentAccess`
+ would fail with symbol arguments.
+ ([Pull Request](https://github.com/rails/rails/pull/34012))
+
+* Rename `Module#parent`, `Module#parents`, and `Module#parent_name` to
+ `module_parent`, `module_parents`, and `module_parent_name`.
+ ([Pull Request](https://github.com/rails/rails/pull/34051))
+
+* Add `ActiveSupport::ParameterFilter`.
+ ([Pull Request](https://github.com/rails/rails/pull/34039))
+
+* Fix issue where duration was being rounded to a full second when a float
+ was added to the duration.
+ ([Pull Request](https://github.com/rails/rails/pull/34135))
+
+* Make `#to_options` an alias for `#symbolize_keys` in
+ `ActiveSupport::HashWithIndifferentAccess`.
+ ([Pull Request](https://github.com/rails/rails/pull/34360))
+
+* Don't raise an exception anymore if the same block is included multiple times
+ for a Concern.
+ ([Pull Request](https://github.com/rails/rails/pull/34553))
+
+* Preserve key order passed to `ActiveSupport::CacheStore#fetch_multi`.
+ ([Pull Request](https://github.com/rails/rails/pull/34700))
+
+* Fix `String#safe_constantize` to not throw a `LoadError` for incorrectly
+ cased constant references.
+ ([Pull Request](https://github.com/rails/rails/pull/34892))
+
+* Add `Hash#deep_transform_values` and `Hash#deep_transform_values!`.
+ ([Commit](https://github.com/rails/rails/commit/b8dc06b8fdc16874160f61dcf58743fcc10e57db))
+
+* Add `ActiveSupport::HashWithIndifferentAccess#assoc`.
+ ([Pull Request](https://github.com/rails/rails/pull/35080))
+
+* Add `before_reset` callback to `CurrentAttributes` and define
+ `after_reset` as an alias of `resets` for symmetry.
+ ([Pull Request](https://github.com/rails/rails/pull/35063))
+
+* Revise `ActiveSupport::Notifications.unsubscribe` to correctly
+ handle Regex or other multiple-pattern subscribers.
+ ([Pull Request](https://github.com/rails/rails/pull/32861))
+
+* Add new autoloading mechanism using Zeitwerk.
+ ([Commit](https://github.com/rails/rails/commit/e53430fa9af239e21e11548499d814f540d421e5))
+
+* Add `Array#including` and `Enumerable#including` to conveniently enlarge
+ a collection.
+ ([Commit](https://github.com/rails/rails/commit/bfaa3091c3c32b5980a614ef0f7b39cbf83f6db3))
+
+* Rename `Array#without` and `Enumerable#without` to `Array#excluding`
+ and `Enumerable#excluding`. Old method names are retained as aliases.
+ ([Commit](https://github.com/rails/rails/commit/bfaa3091c3c32b5980a614ef0f7b39cbf83f6db3))
+
+* Add support for supplying `locale` to `transliterate` and `parameterize`.
+ ([Pull Request](https://github.com/rails/rails/pull/35571))
+
+* Fix `Time#advance` to work with dates before 1001-03-07.
+ ([Pull Request](https://github.com/rails/rails/pull/35659))
+
+* Update `ActiveSupport::Notifications::Instrumenter#instrument` to allow
+ not passing block.
+ ([Pull Request](https://github.com/rails/rails/pull/35705))
+
+* Use weak references in descendants tracker to allow anonymous subclasses to
+ be garbage collected.
+ ([Pull Request](https://github.com/rails/rails/pull/31442))
+
+* Calling test methods with `with_info_handler` method to allow minitest-hooks
+ plugin to work.
+ ([Commit](https://github.com/rails/rails/commit/758ba117a008b6ea2d3b92c53b6a7a8d7ccbca69))
+
+* Preserve `html_safe?` status on `ActiveSupport::SafeBuffer#*`.
+ ([Pull Request](https://github.com/rails/rails/pull/36012))
+
Active Job
----------
@@ -551,6 +1002,15 @@ Please refer to the [Changelog][guides] for detailed changes.
### Notable changes
+* Add a section about troubleshooting of autoloading constants.
+ ([Commit](https://github.com/rails/rails/commit/c03bba4f1f03bad7dc034af555b7f2b329cf76f5))
+
+* Add Action Mailbox Basics guide.
+ ([Pull Request](https://github.com/rails/rails/pull/34812))
+
+* Add Action Text Overview guide.
+ ([Pull Request](https://github.com/rails/rails/pull/34878))
+
Credits
-------
diff --git a/guides/source/action_text_overview.md b/guides/source/action_text_overview.md
index 08ec35e52a..a735ec2b0e 100644
--- a/guides/source/action_text_overview.md
+++ b/guides/source/action_text_overview.md
@@ -46,6 +46,8 @@ happens after every keystroke, and avoids the need to use execCommand at all.
## Installation
Run `rails action_text:install` to add the Yarn package and copy over the necessary migration.
+Also, you need to set up Active Storage for embedded images and other attachments.
+Please refer to the [Active Storage Overview](active_storage_overview.html) guide.
## Examples
@@ -91,7 +93,7 @@ end
By default, the Action Text editor and content is styled by the Trix defaults.
If you want to change these defaults, you'll want to remove
-the `app/assets/stylesheets/actiontext.css` linker and base your stylings on
+the `app/assets/stylesheets/actiontext.scss` linker and base your stylings on
the [contents of that file](https://raw.githubusercontent.com/basecamp/trix/master/dist/trix.css).
You can also style the HTML used for embedded images and other attachments (known as blobs).
diff --git a/guides/source/action_view_overview.md b/guides/source/action_view_overview.md
index a1b69edd22..dda3ae0863 100644
--- a/guides/source/action_view_overview.md
+++ b/guides/source/action_view_overview.md
@@ -1025,6 +1025,34 @@ If `@article.author_id` is 1, this would return:
<label for="article_author_id_3">M. Clark</label>
```
+Recovering some option passed (e.g. programatically checking an object from collection):
+
+```ruby
+collection_radio_buttons(:article, :author_id, Author.all, :id, :name_with_initial, {checked: Author.last})
+```
+
+In this case, the last object from the collection will be checked:
+
+```html
+<input id="article_author_id_1" name="article[author_id]" type="radio" value="1" />
+<label for="article_author_id_1">D. Heinemeier Hansson</label>
+<input id="article_author_id_2" name="article[author_id]" type="radio" value="2" />
+<label for="article_author_id_2">D. Thomas</label>
+<input id="article_author_id_3" name="article[author_id]" type="radio" value="3" checked="checked" />
+<label for="article_author_id_3">M. Clark</label>
+```
+
+To access the passed options programatically (e.g. adding a custom class if checked):
+
+**Sample html.erb**
+
+```html+erb
+<%= collection_radio_buttons(:article, :author_id, Author.all, :id, :name_with_initial, {checked: Author.last, required: true} do |rb| %>
+ <%= rb.label(class: "#{'my-custom-class' if rb.value == Author.last.id}") { rb.radio_button + rb.text } %>
+<% end %>
+```
+
+
#### collection_check_boxes
Returns `check_box` tags for the collection of existing return values of `method` for `object`'s class.
diff --git a/guides/source/active_record_callbacks.md b/guides/source/active_record_callbacks.md
index 8f54e78224..617f81d37b 100644
--- a/guides/source/active_record_callbacks.md
+++ b/guides/source/active_record_callbacks.md
@@ -495,11 +495,3 @@ end
>> @user.save
=> User was saved to database
```
-
-To register callbacks for both create and destroy actions, use `after_commit` instead.
-
-```ruby
-class User < ApplicationRecord
- after_commit :log_user_saved_to_db, on: [:create, :destroy]
-end
-```
diff --git a/guides/source/active_record_migrations.md b/guides/source/active_record_migrations.md
index 270e4a3bf9..9398244ccf 100644
--- a/guides/source/active_record_migrations.md
+++ b/guides/source/active_record_migrations.md
@@ -225,6 +225,8 @@ class CreateProducts < ActiveRecord::Migration[5.0]
create_table :products do |t|
t.string :name
t.string :part_number
+
+ t.timestamps
end
end
end
diff --git a/guides/source/active_record_multiple_databases.md b/guides/source/active_record_multiple_databases.md
new file mode 100644
index 0000000000..07be21a254
--- /dev/null
+++ b/guides/source/active_record_multiple_databases.md
@@ -0,0 +1,288 @@
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON https://guides.rubyonrails.org.**
+
+Multiple Databases with Active Record
+=====================================
+
+This guide covers using multiple databases with your Rails application.
+
+After reading this guide you will know:
+
+* How to setup your application for multiple databases.
+* How automatic connection switching works.
+* What features are supported and what's still a work in progress.
+
+--------------------------------------------------------------------------------
+
+As an application grows in popularity and usage you'll need to scale the application
+to support your new users and their data. One way in which your application may need
+to scale is on the database level. Rails now has support for multiple databases
+so you don't have to store your data all in one place.
+
+At this time the following features are supported:
+
+* Multiple primary databases and a replica for each
+* Automatic connection switching for the model you're working with
+* Automatic swapping between the primary and replica depending on the HTTP verb
+and recent writes
+* Rails tasks for creating, dropping, migrating, and interacting with the multiple
+databases
+
+The following features are not (yet) supported:
+
+* Sharding
+* Joining across clusters
+* Load balancing replicas
+* Dumping schema caches for multiple databases
+
+## Setting up your application
+
+While Rails tries to do most of the work for you there are still some steps you'll
+need to do to get your application ready for multiple databases.
+
+Let's say we have an application with a single primary database and we need to add a
+new database for some new tables we're adding. The name of the new database will be
+"animals".
+
+The database.yml looks like this:
+
+```yaml
+production:
+ database: my_primary_database
+ user: root
+ adapter: mysql
+```
+
+Let's add a replica for the primary, a new writer called animals and a replica for that
+as well. To do this we need to change our database.yml from a 2-tier to a 3-tier config.
+
+```yaml
+production:
+ primary:
+ database: my_primary_database
+ user: root
+ adapter: mysql
+ primary_replica:
+ database: my_primary_database
+ user: root_readonly
+ adapter: mysql
+ replica: true
+ animals:
+ database: my_animals_database
+ user: animals_root
+ adapter: mysql
+ migrations_paths: db/animals_migrate
+ animals_replica:
+ database: my_animals_database
+ user: animals_readonly
+ adapter: mysql
+ replica: true
+```
+
+When using multiple databases there are a few important settings.
+
+First, the database name for the primary and replica should be the same because they contain
+the same data. Second, the username for the primary and replica should be different, and the
+replica user's permissions should be to read and not write.
+
+When using a replica database you need to add a `replica: true` entry to the replica in the
+`database.yml`. This is because Rails otherwise has no way of knowing which one is a replica
+and which one is the primary.
+
+Lastly, for new primary databases you need to set the `migrations_paths` to the directory
+where you will store migrations for that database. We'll look more at `migrations_paths`
+later on in this guide.
+
+Now that we have a new database, let's set up the model. In order to use the new database we
+need to create a new abstract class and connect to the animals databases.
+
+```ruby
+class AnimalsBase < ApplicationRecord
+ self.abstract_class = true
+
+ connects_to database: { writing: :animals, reading: :animals_replica }
+end
+```
+ Then we need to
+update `ApplicationRecord` to be aware of our new replica.
+
+```ruby
+class ApplicationRecord < ActiveRecord::Base
+ self.abstract_class = true
+
+ connects_to database: { writing: :primary, reading: :primary_replica }
+end
+```
+
+By default Rails expects the database roles to be `writing` and `reading` for the primary
+and replica respectively. If you have a legacy system you may already have roles set up that
+you don't want to change. In that case you can set a new role name in your application config.
+
+```ruby
+config.active_record.writing_role = :default
+config.active_record.reading_role = :readonly
+```
+
+It's important to connect to your database in a single model and then inherit from that model
+for the tables rather than connect multiple individual models to the same database. Database
+clients have a limit to the number of open connections there can be and if you do this it will
+multiply the number of connections you have since Rails uses the model class name for the
+connection specification name.
+
+Now that we have the database.yml and the new model set up it's time to create the databases.
+Rails 6.0 ships with all the rails tasks you need to use multiple databases in Rails.
+
+You can run `rails -T` to see all the commands you're able to run. You should see the following:
+
+```
+$ rails -T
+rails db:create # Creates the database from DATABASE_URL or config/database.yml for the ...
+rails db:create:animals # Create animals database for current environment
+rails db:create:primary # Create primary database for current environment
+rails db:drop # Drops the database from DATABASE_URL or config/database.yml for the cu...
+rails db:drop:animals # Drop animals database for current environment
+rails db:drop:primary # Drop primary database for current environment
+rails db:migrate # Migrate the database (options: VERSION=x, VERBOSE=false, SCOPE=blog)
+rails db:migrate:animals # Migrate animals database for current environment
+rails db:migrate:primary # Migrate primary database for current environment
+rails db:migrate:status # Display status of migrations
+rails db:migrate:status:animals # Display status of migrations for animals database
+rails db:migrate:status:primary # Display status of migrations for primary database
+```
+
+Running a command like `rails db:create` will create both the primary and animals databases.
+Note that there is no command for creating the users and you'll need to do that manually
+to support the readonly users for your replicas. If you want to create just the animals
+database you can run `rails db:create:animals`.
+
+## Migrations
+
+Migrations for multiple databases should live in their own folders prefixed with the
+name of the database key in the configuration.
+
+You also need to set the `migrations_paths` in the database configurations to tell Rails
+where to find the migrations.
+
+For example the `animals` database would look in the `db/animals_migrate` directory and
+`primary` would look in `db/migrate`. Rails generators now take a `--database` option
+so that the file is generated in the correct directory. The command can be run like so:
+
+```
+$ rails g migration CreateDogs name:string --database animals
+```
+
+## Activating automatic connection switching
+
+Finally, in order to use the read-only replica in your application you'll need to activate
+the middleware for automatic switching.
+
+Automatic switching allows the application to switch from the primary to replica or replica
+to primary based on the HTTP verb and whether there was a recent write.
+
+If the application is receiving a POST, PUT, DELETE, or PATCH request the application will
+automatically write to the primary. For the specified time after the write the application
+will read from the primary. For a GET or HEAD request the application will read from the
+replica unless there was a recent write.
+
+To activate the automatic connection switching middleware, add or uncomment the following
+lines in your application config.
+
+```ruby
+config.active_record.database_selector = { delay: 2.seconds }
+config.active_record.database_resolver = ActiveRecord::Middleware::DatabaseSelector::Resolver
+config.active_record.database_resolver_context = ActiveRecord::Middleware::DatabaseSelector::Resolver::Session
+```
+
+Rails guarantees "read your own write" and will send your GET or HEAD request to the
+primary if it's within the `delay` window. By default the delay is set to 2 seconds. You
+should change this based on your database infrastructure. Rails doesn't guarantee "read
+a recent write" for other users within the delay window and will send GET and HEAD requests
+to the replicas unless they wrote recently.
+
+The automatic connection switching in Rails is relatively primitive and deliberately doesn't
+do a whole lot. The goal was a system that demonstrated how to do automatic connection
+switching that was flexible enough to be customizable by app developers.
+
+The setup in Rails allows you to easily change how the switching is done and what
+parameters it's based on. Let's say you want to use a cookie instead of a session to
+decide when to swap connections. You can write your own class:
+
+```ruby
+class MyCookieResolver
+ # code for your cookie class
+end
+```
+
+And then pass it to the middleware:
+
+```ruby
+config.active_record.database_selector = { delay: 2.seconds }
+config.active_record.database_resolver = ActiveRecord::Middleware::DatabaseSelector::Resolver
+config.active_record.database_resolver_context = MyCookieResolver
+```
+
+## Using manual connection switching
+
+There are some cases where you may want your application to connect to a primary or a replica
+and the automatic connection switching isn't adequate. For example, you may know that for a
+particular request you always want to send the request to a replica, even when you are in a
+POST request path.
+
+To do this Rails provides a `connected_to` method that will switch to the connection you
+need.
+
+```ruby
+ActiveRecord::Base.connected_to(role: :reading) do
+ # all code in this block will be connected to the reading role
+end
+```
+
+The "role" in the `connected_to` call looks up the connections that are connected on that
+connection handler (or role). The `reading` connection handler will hold all the connections
+that were connected via `connects_to` with the role name of `reading`.
+
+There also may be a case where you have a database that you don't always want to connect to
+on application boot but may need for a slow query or analytics. After defining that database
+in the database.yml you can connect by passing a database argument to `connected_to`
+
+```ruby
+ActiveRecord::Base.connected_to(database: { reading_slow: :animals_slow_replica }) do
+ # do something while connected to the slow replica
+end
+```
+
+The `database` argument for `connected_to` will take a symbol or a config hash.
+
+Note that `connected_to` with a role will look up an existing connection and switch
+using the connection specification name. This means that if you pass an unknown role
+like `connected_to(role: :nonexistent)` you will get an error that says
+`ActiveRecord::ConnectionNotEstablished (No connection pool with 'AnimalsBase' found
+for the 'nonexistent' role.)`
+
+## Caveats
+
+### Sharding
+
+As noted at the top, Rails doesn't (yet) support sharding. We had to do a lot of work
+to support multiple databases for Rails 6.0. The lack of support for sharding isn't
+an oversight, but does require additional work that didn't make it in for 6.0. For now
+if you need sharding it may be advisable to continue using one of the many gems
+that supports this.
+
+### Load Balancing Replicas
+
+Rails also doesn't support automatic load balancing of replicas. This is very
+dependent on your infrastructure. We may implement basic, primitive load balancing
+in the future, but for an application at scale this should be something your application
+handles outside of Rails.
+
+### Joining Across Databases
+
+Applications cannot join across databases. Rails 6.1 will support using `has_many`
+relationships and creating 2 queries instead of joining, but Rails 6.0 will require
+you to split the joins into 2 selects manually.
+
+### Schema Cache
+
+If you use a schema cache and multiple databases you'll need to write an initializer
+that loads the schema cache from your app. This wasn't an issue we could resolve in
+time for Rails 6.0 but hope to have it in a future version soon.
diff --git a/guides/source/active_record_querying.md b/guides/source/active_record_querying.md
index e40f16e62d..5fb030fad4 100644
--- a/guides/source/active_record_querying.md
+++ b/guides/source/active_record_querying.md
@@ -1821,6 +1821,21 @@ Client.limit(1).pluck(:name)
# => ["David"]
```
+NOTE: You should also know that using `pluck` will trigger eager loading if the relation object contains include values, even if the eager loading is not necessary for the query. For example:
+
+```ruby
+# store association for reusing it
+assoc = Company.includes(:account)
+assoc.pluck(:id)
+# SELECT "companies"."id" FROM "companies" LEFT OUTER JOIN "accounts" ON "accounts"."id" = "companies"."account_id"
+```
+
+One way to avoid this is to `unscope` the includes:
+
+```ruby
+assoc.unscope(:includes).pluck(:id)
+```
+
### `ids`
`ids` can be used to pluck all the IDs for the relation using the table's primary key.
diff --git a/guides/source/active_record_validations.md b/guides/source/active_record_validations.md
index e68f34dd5d..f904d4de65 100644
--- a/guides/source/active_record_validations.md
+++ b/guides/source/active_record_validations.md
@@ -664,7 +664,7 @@ This helper passes the record to a separate class for validation.
class GoodnessValidator < ActiveModel::Validator
def validate(record)
if record.first_name == "Evil"
- record.errors[:base] << "This person is evil"
+ record.errors.add :base, "This person is evil"
end
end
end
@@ -692,7 +692,7 @@ validator class as `options`:
class GoodnessValidator < ActiveModel::Validator
def validate(record)
if options[:fields].any?{|field| record.send(field) == "Evil" }
- record.errors[:base] << "This person is evil"
+ record.errors.add :base, "This person is evil"
end
end
end
@@ -723,7 +723,7 @@ class GoodnessValidator
def validate
if some_complex_condition_involving_ivars_and_private_methods?
- @person.errors[:base] << "This person is evil"
+ @person.errors.add :base, "This person is evil"
end
end
@@ -1004,7 +1004,7 @@ and performs the validation on it. The custom validator is called using the
class MyValidator < ActiveModel::Validator
def validate(record)
unless record.name.starts_with? 'X'
- record.errors[:name] << 'Need a name starting with X please!'
+ record.errors.add :name, "Need a name starting with X please!"
end
end
end
@@ -1026,7 +1026,7 @@ instance.
class EmailValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
unless value =~ /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i
- record.errors[attribute] << (options[:message] || "is not an email")
+ record.errors.add attribute, (options[:message] || "is not an email")
end
end
end
@@ -1203,7 +1203,7 @@ You can add error messages that are related to the object's state as a whole, in
```ruby
class Person < ApplicationRecord
def a_method_used_for_validation_purposes
- errors[:base] << "This person is invalid because ..."
+ errors.add :base, "This person is invalid because ..."
end
end
```
diff --git a/guides/source/active_storage_overview.md b/guides/source/active_storage_overview.md
index 615f576797..54f8f5c2b5 100644
--- a/guides/source/active_storage_overview.md
+++ b/guides/source/active_storage_overview.md
@@ -41,6 +41,10 @@ application (or upgrading your application to Rails 5.2), run
`rails active_storage:install` to generate a migration that creates these
tables. Use `rails db:migrate` to run the migration.
+WARNING: `active_storage_attachments` is a polymorphic join table that stores your model's class name. If your model's class name changes, you will need to run a migration on this table to update the underlying `record_type` to your model's new class name.
+
+WARNING: If you are using UUIDs instead of integers as the primary key on your models you will need to change the column type of `record_id` for the `active_storage_attachments` table in the generated migration accordingly.
+
Declare Active Storage services in `config/storage.yml`. For each service your
application uses, provide a name and the requisite configuration. The example
below declares three services named `local`, `test`, and `amazon`:
@@ -189,14 +193,21 @@ gem "google-cloud-storage", "~> 1.11", require: false
### Mirror Service
-You can keep multiple services in sync by defining a mirror service. When a file
-is uploaded or deleted, it's done across all the mirrored services. Mirrored
-services can be used to facilitate a migration between services in production.
-You can start mirroring to the new service, copy existing files from the old
-service to the new, then go all-in on the new service. Define each of the
-services you'd like to use as described above and reference them from a mirrored
+You can keep multiple services in sync by defining a mirror service. A mirror
+service replicates uploads and deletes across two or more subordinate services.
+
+A mirror service is intended to be used temporarily during a migration between
+services in production. You can start mirroring to a new service, copy
+pre-existing files from the old service to the new, then go all-in on the new
service.
+NOTE: Mirroring is not atomic. It is possible for an upload to succeed on the
+primary service and fail on any of the subordinate services. Before going
+all-in on a new service, verify that all files have been copied.
+
+Define each of the services you'd like to mirror as described above. Reference
+them by name when defining a mirror service:
+
```yaml
s3_west_coast:
service: S3
@@ -219,9 +230,12 @@ production:
- s3_west_coast
```
-NOTE: Files are served from the primary service.
+Although all secondary services receive uploads, downloads are always handled
+by the primary service.
-NOTE: This is not compatible with the [direct uploads](#direct-uploads) feature.
+Mirror services are compatible with direct uploads. New files are directly
+uploaded to the primary service. When a directly-uploaded file is attached to a
+record, a background job is enqueued to copy it to the secondary services.
Attaching Files to Records
--------------------------
@@ -386,6 +400,10 @@ helper allows you to set the disposition.
rails_blob_path(user.avatar, disposition: "attachment")
```
+WARNING: To prevent XSS attacks, ActiveStorage forces the Content-Disposition header
+to "attachment" for some kind of files. To change this behaviour see the
+available configuration opions in [Configuring Rails Applications](configuring.html#configuring-active-storage).
+
If you need to create a link from outside of controller/view context (Background
jobs, Cronjobs, etc.), you can access the rails_blob_path like this:
diff --git a/guides/source/active_support_core_extensions.md b/guides/source/active_support_core_extensions.md
index 1a057832d4..f36cacfe8d 100644
--- a/guides/source/active_support_core_extensions.md
+++ b/guides/source/active_support_core_extensions.md
@@ -155,15 +155,6 @@ Complex(1).duplicable? # => true
1.method(:+).duplicable? # => false
```
-`duplicable?` matches the current Ruby version's `dup` behavior,
-so results will vary according the version of Ruby you're using.
-In Ruby 2.4, for example, Complex and Rational are not duplicable:
-
-```ruby
-Rational(1).duplicable? # => false
-Complex(1).duplicable? # => false
-```
-
WARNING: Any class can disallow duplication by removing `dup` and `clone` or raising exceptions from them. Thus only `rescue` can tell whether a given arbitrary object is duplicable. `duplicable?` depends on the hard-coded list above, but it is much faster than `rescue`. Use it only if you know the hard-coded list is enough in your use case.
NOTE: Defined in `active_support/core_ext/object/duplicable.rb`.
@@ -2358,10 +2349,6 @@ There's also a related idiom that uses the splat operator:
[*object]
```
-which in Ruby 1.8 returns `[nil]` for `nil`, and calls to `Array(object)` otherwise. (Please if you know the exact behavior in 1.9 contact fxn.)
-
-Thus, in this case the behavior is different for `nil`, and the differences with `Kernel#Array` explained above apply to the rest of `object`s.
-
NOTE: Defined in `active_support/core_ext/array/wrap.rb`.
### Duplicating
@@ -2633,14 +2620,12 @@ The method `stringify_keys` returns a hash that has a stringified version of the
# => {"" => 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:
+In case of key collision, the value will be the one most recently inserted into the hash:
```ruby
{"a" => 1, a: 2}.stringify_keys
-# The result could either be
+# The result will be
# => {"a"=>2}
-# or
-# => {"a"=>1}
```
This method may be useful for example to easily accept both symbols and strings as options. For instance `ActionView::Helpers::FormHelper` defines:
@@ -2677,14 +2662,12 @@ The method `symbolize_keys` returns a hash that has a symbolized version of the
WARNING. Note in the previous example only one key was symbolized.
-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:
+In case of key collision, the value will be the one most recently inserted into the hash:
```ruby
{"a" => 1, a: 2}.symbolize_keys
-# The result could either be
+# The result will be
# => {:a=>2}
-# or
-# => {:a=>1}
```
This method may be useful for example to easily accept both symbols and strings as options. For instance `ActionController::UrlRewriter` defines
@@ -3420,56 +3403,56 @@ NOTE: Defined in `active_support/core_ext/date_and_time/calculations.rb`.
#### `prev_day`, `next_day`
-In Ruby 1.9 `prev_day` and `next_day` return the date in the last or next day:
+`prev_day` and `next_day` return the time in the last or next day:
```ruby
-d = Date.new(2010, 5, 8) # => Sat, 08 May 2010
-d.prev_day # => Fri, 07 May 2010
-d.next_day # => Sun, 09 May 2010
+t = Time.new(2010, 5, 8) # => 2010-05-08 00:00:00 +0900
+t.prev_day # => 2010-05-07 00:00:00 +0900
+t.next_day # => 2010-05-09 00:00:00 +0900
```
-NOTE: Defined in `active_support/core_ext/date_and_time/calculations.rb`.
+NOTE: Defined in `active_support/core_ext/time/calculations.rb`.
#### `prev_month`, `next_month`
-In Ruby 1.9 `prev_month` and `next_month` return the date with the same day in the last or next month:
+`prev_month` and `next_month` return the time with the same day in the last or next month:
```ruby
-d = Date.new(2010, 5, 8) # => Sat, 08 May 2010
-d.prev_month # => Thu, 08 Apr 2010
-d.next_month # => Tue, 08 Jun 2010
+t = Time.new(2010, 5, 8) # => 2010-05-08 00:00:00 +0900
+t.prev_month # => 2010-04-08 00:00:00 +0900
+t.next_month # => 2010-06-08 00:00:00 +0900
```
If such a day does not exist, the last day of the corresponding month is returned:
```ruby
-Date.new(2000, 5, 31).prev_month # => Sun, 30 Apr 2000
-Date.new(2000, 3, 31).prev_month # => Tue, 29 Feb 2000
-Date.new(2000, 5, 31).next_month # => Fri, 30 Jun 2000
-Date.new(2000, 1, 31).next_month # => Tue, 29 Feb 2000
+Time.new(2000, 5, 31).prev_month # => 2000-04-30 00:00:00 +0900
+Time.new(2000, 3, 31).prev_month # => 2000-02-29 00:00:00 +0900
+Time.new(2000, 5, 31).next_month # => 2000-06-30 00:00:00 +0900
+Time.new(2000, 1, 31).next_month # => 2000-02-29 00:00:00 +0900
```
-NOTE: Defined in `active_support/core_ext/date_and_time/calculations.rb`.
+NOTE: Defined in `active_support/core_ext/time/calculations.rb`.
#### `prev_year`, `next_year`
-In Ruby 1.9 `prev_year` and `next_year` return a date with the same day/month in the last or next year:
+`prev_year` and `next_year` return a time with the same day/month in the last or next year:
```ruby
-d = Date.new(2010, 5, 8) # => Sat, 08 May 2010
-d.prev_year # => Fri, 08 May 2009
-d.next_year # => Sun, 08 May 2011
+t = Time.new(2010, 5, 8) # => 2010-05-08 00:00:00 +0900
+t.prev_year # => 2009-05-08 00:00:00 +0900
+t.next_year # => 2011-05-08 00:00:00 +0900
```
If date is the 29th of February of a leap year, you obtain the 28th:
```ruby
-d = Date.new(2000, 2, 29) # => Tue, 29 Feb 2000
-d.prev_year # => Sun, 28 Feb 1999
-d.next_year # => Wed, 28 Feb 2001
+t = Time.new(2000, 2, 29) # => 2000-02-29 00:00:00 +0900
+t.prev_year # => 1999-02-28 00:00:00 +0900
+t.next_year # => 2001-02-28 00:00:00 +0900
```
-NOTE: Defined in `active_support/core_ext/date_and_time/calculations.rb`.
+NOTE: Defined in `active_support/core_ext/time/calculations.rb`.
#### `prev_quarter`, `next_quarter`
diff --git a/guides/source/active_support_instrumentation.md b/guides/source/active_support_instrumentation.md
index e5ed283c45..9f15e70da6 100644
--- a/guides/source/active_support_instrumentation.md
+++ b/guides/source/active_support_instrumentation.md
@@ -545,6 +545,14 @@ Active Storage
| `:key` | Secure token |
| `:service` | Name of the service |
+### service_download_chunk.active_storage
+
+| Key | Value |
+| ------------ | ------------------------------- |
+| `:key` | Secure token |
+| `:service` | Name of the service |
+| `:range` | Byte range attempted to be read |
+
### service_download.active_storage
| Key | Value |
@@ -582,6 +590,23 @@ Active Storage
| `:service` | Name of the service |
| `:url` | Generated URL |
+### service_update_metadata.active_storage
+
+| Key | Value |
+| --------------- | ------------------------------ |
+| `:key` | Secure token |
+| `:service` | Name of the service |
+| `:content_type` | HTTP Content-Type field |
+| `:disposition` | HTTP Content-Disposition field |
+
+INFO. The only ActiveStorage service that provides this hook so far is GCS.
+
+### preview.active_storage
+
+| Key | Value |
+| ------------ | ------------------- |
+| `:key` | Secure token |
+
Railties
--------
@@ -618,7 +643,16 @@ The block receives the following arguments:
```ruby
ActiveSupport::Notifications.subscribe "process_action.action_controller" do |name, started, finished, unique_id, data|
# your own custom stuff
- Rails.logger.info "#{name} Received!"
+ Rails.logger.info "#{name} Received! (started: #{started}, finished: #{finished})" # process_action.action_controller Received (started: 2019-05-05 13:43:57 -0800, finished: 2019-05-05 13:43:58 -0800)
+end
+```
+
+If you are concerned about the accuracy of `started` and `finished` to compute a precise elapsed time then use `ActiveSupport::Notifications.monotonic_subscribe`. The given block would receive the same arguments as above but the `started` and `finished` will have values with an accurate monotonic time instead of wall-clock time.
+
+```ruby
+ActiveSupport::Notifications.monotonic_subscribe "process_action.action_controller" do |name, started, finished, unique_id, data|
+ # your own custom stuff
+ Rails.logger.info "#{name} Received! (started: #{started}, finished: #{finished})" # process_action.action_controller Received (started: 1560978.425334, finished: 1560979.429234)
end
```
diff --git a/guides/source/api_app.md b/guides/source/api_app.md
index b8b6cb7874..181d39e7e0 100644
--- a/guides/source/api_app.md
+++ b/guides/source/api_app.md
@@ -420,6 +420,15 @@ Some common modules you might want to add:
- `ActionController::MimeResponds`: Support for `respond_to`.
- `ActionController::Cookies`: Support for `cookies`, which includes
support for signed and encrypted cookies. This requires the cookies middleware.
+- `ActionController::Caching`: Support view caching for the API controller. Please notice that
+ you will need to manually specify cache store inside the controller like:
+ ```ruby
+ class ApplicationController < ActionController::API
+ include ::ActionController::Caching
+ self.cache_store = :mem_cached_store
+ end
+ ```
+ Rails does *not* pass this configuration automatically.
The best place to add a module is in your `ApplicationController`, but you can
also add modules to individual controllers.
diff --git a/guides/source/asset_pipeline.md b/guides/source/asset_pipeline.md
index 454613e733..d853559440 100644
--- a/guides/source/asset_pipeline.md
+++ b/guides/source/asset_pipeline.md
@@ -33,13 +33,11 @@ passing the `--skip-sprockets` option.
rails new appname --skip-sprockets
```
-Rails automatically adds the `sass-rails`, `coffee-rails` and `uglifier`
-gems to your `Gemfile`, which are used by Sprockets for asset compression:
+Rails automatically adds the `sass-rails` gem to your `Gemfile`, which is used
+by Sprockets for asset compression:
```ruby
gem 'sass-rails'
-gem 'uglifier'
-gem 'coffee-rails'
```
Using the `--skip-sprockets` option will prevent Rails from adding
@@ -176,8 +174,7 @@ in `app/assets` are never served directly in production.
### Controller Specific Assets
-When you generate a scaffold or a controller, Rails also generates a JavaScript
-file (or CoffeeScript file if the `coffee-rails` gem is in the `Gemfile`) and a
+When you generate a scaffold or a controller, Rails also generates a
Cascading Style Sheet file (or SCSS file if `sass-rails` is in the `Gemfile`)
for that controller. Additionally, when generating a scaffold, Rails generates
the file `scaffolds.css` (or `scaffolds.scss` if `sass-rails` is in the
@@ -434,9 +431,8 @@ one file rather than many, the load time of pages can be greatly reduced because
the browser makes fewer requests. Compression also reduces file size, enabling
the browser to download them faster.
-
-For example, a new Rails application includes a default
-`app/assets/javascripts/application.js` file containing the following lines:
+For example, with a `app/assets/javascripts/application.js` file containing the
+following lines:
```js
// ...
@@ -476,8 +472,7 @@ which contains these lines:
*/
```
-Rails creates both `app/assets/javascripts/application.js` and
-`app/assets/stylesheets/application.css` regardless of whether the
+Rails create `app/assets/stylesheets/application.css` regardless of whether the
--skip-sprockets option is used when creating a new Rails application. This is
so you can easily add asset pipelining later if you like.
@@ -517,8 +512,7 @@ The file extensions used on an asset determine what preprocessing is applied.
When a controller or a scaffold is generated with the default Rails gemset, a
CoffeeScript file and a SCSS file are generated in place of a regular JavaScript
and CSS file. The example used before was a controller called "projects", which
-generated an `app/assets/javascripts/projects.coffee` and an
-`app/assets/stylesheets/projects.scss` file.
+generated an `app/assets/stylesheets/projects.scss` file.
In development mode, or if the asset pipeline is disabled, when these files are
requested they are processed by the processors provided by the `coffee-script`
@@ -1083,7 +1077,7 @@ Possible options for JavaScript compression are `:closure`, `:uglifier` and
`:yui`. These require the use of the `closure-compiler`, `uglifier` or
`yui-compressor` gems, respectively.
-The default `Gemfile` includes [uglifier](https://github.com/lautis/uglifier).
+Take the `uglifier` gem, for example.
This gem wraps [UglifyJS](https://github.com/mishoo/UglifyJS) (written for
NodeJS) in Ruby. It compresses your code by removing white space and comments,
shortening local variable names, and performing other micro-optimizations such
@@ -1230,4 +1224,3 @@ it as a preprocessor for your mime type.
```ruby
Sprockets.register_preprocessor 'text/css', AddComment
```
-
diff --git a/guides/source/association_basics.md b/guides/source/association_basics.md
index a60ce7fb32..27387ac3ac 100644
--- a/guides/source/association_basics.md
+++ b/guides/source/association_basics.md
@@ -1231,6 +1231,7 @@ The `has_one` association supports these options:
* `:source`
* `:source_type`
* `:through`
+* `:touch`
* `:validate`
##### `:as`
@@ -1324,6 +1325,28 @@ class DustJacket < ApplicationRecord; end
The `:through` option specifies a join model through which to perform the query. `has_one :through` associations were discussed in detail [earlier in this guide](#the-has-one-through-association).
+##### `:touch`
+
+If you set the `:touch` option to `true`, then the `updated_at` or `updated_on` timestamp on the associated object will be set to the current time whenever this object is saved or destroyed:
+
+```ruby
+class Supplier < ApplicationRecord
+ has_one :account, touch: true
+end
+
+class Account < ApplicationRecord
+ belongs_to :supplier
+end
+```
+
+In this case, saving or destroying a supplier will update the timestamp on the associated account. You can also specify a particular timestamp attribute to update:
+
+```ruby
+class Supplier < ApplicationRecord
+ has_one :account, touch: :suppliers_updated_at
+end
+```
+
##### `:validate`
If you set the `:validate` option to `true`, then associated objects will be validated whenever you save this object. By default, this is `false`: associated objects will not be validated when this object is saved.
@@ -2383,11 +2406,11 @@ NOTE: These callbacks are called only when the associated objects are added or r
```ruby
# Triggers `before_add` callback
-author.books << book
+author.books << book
author.books = [book, book2]
# Does not trigger the `before_add` callback
-book.update(author_id: 1)
+book.update(author_id: 1)
```
### Association Extensions
@@ -2428,8 +2451,8 @@ Extensions can refer to the internals of the association proxy using these three
* `proxy_association.reflection` returns the reflection object that describes the association.
* `proxy_association.target` returns the associated object for `belongs_to` or `has_one`, or the collection of associated objects for `has_many` or `has_and_belongs_to_many`.
-Single Table Inheritance
-------------------------
+Single Table Inheritance (STI)
+------------------------------
Sometimes, you may want to share fields and behavior between different models.
Let's say we have Car, Motorcycle, and Bicycle models. We will want to share
diff --git a/guides/source/autoloading_and_reloading_constants.md b/guides/source/autoloading_and_reloading_constants.md
index 7dfc39e192..8cd2d353de 100644
--- a/guides/source/autoloading_and_reloading_constants.md
+++ b/guides/source/autoloading_and_reloading_constants.md
@@ -1,18 +1,18 @@
**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON https://guides.rubyonrails.org.**
-Autoloading and Reloading Constants
-===================================
+Autoloading and Reloading Constants (Zeitwerk Mode)
+======================================================
-This guide documents how constant autoloading and reloading works.
+This guide documents how autoloading and reloading works in `zeitwerk` mode.
After reading this guide, you will know:
-* Key aspects of Ruby constants
-* What are the `autoload_paths` and how does eager loading work in production?
-* How constant autoloading works
-* What is `require_dependency`
-* How constant reloading works
-* Solutions to common autoloading gotchas
+* Autoloading modes
+* Related Rails configuration
+* Project structure
+* Autoloading, reloading, and eager loading
+* Single Table Inheritance
+* And more
--------------------------------------------------------------------------------
@@ -20,13 +20,15 @@ After reading this guide, you will know:
Introduction
------------
-Ruby on Rails allows applications to be written as if their code was preloaded.
+INFO. This guide documents autoloading in `zeitwerk` mode, which is new in Rails 6. If you'd like to read about `classic` mode instead, please check [Autoloading and Reloading Constants (Classic Mode)](autoloading_and_reloading_constants_classic_mode.html).
-In a normal Ruby program classes need to load their dependencies:
+In a normal Ruby program, dependencies need to be loaded by hand. For example, the following controller uses classes `ApplicationController` and `Post`, and normally you'd need to put `require` calls for them:
```ruby
-require 'application_controller'
-require 'post'
+# DO NOT DO THIS.
+require "application_controller"
+require "post"
+# DO NOT DO THIS.
class PostsController < ApplicationController
def index
@@ -35,16 +37,7 @@ class PostsController < ApplicationController
end
```
-Our Rubyist instinct quickly sees some redundancy in there: If classes were
-defined in files matching their name, couldn't their loading be automated
-somehow? We could save scanning the file for dependencies, which is brittle.
-
-Moreover, `Kernel#require` loads files once, but development is much more smooth
-if code gets refreshed when it changes without restarting the server. It would
-be nice to be able to use `Kernel#load` in development, and `Kernel#require` in
-production.
-
-Indeed, those features are provided by Ruby on Rails, where we just write
+This is not the case in Rails applications, where application classes and modules are just available everywhere:
```ruby
class PostsController < ApplicationController
@@ -54,1330 +47,255 @@ class PostsController < ApplicationController
end
```
-This guide documents how that works.
-
-
-Constants Refresher
--------------------
-
-While constants are trivial in most programming languages, they are a rich
-topic in Ruby.
-
-It is beyond the scope of this guide to document Ruby constants, but we are
-nevertheless going to highlight a few key topics. Truly grasping the following
-sections is instrumental to understanding constant autoloading and reloading.
-
-### Nesting
-
-Class and module definitions can be nested to create namespaces:
-
-```ruby
-module XML
- class SAXParser
- # (1)
- end
-end
-```
-
-The *nesting* at any given place is the collection of enclosing nested class and
-module objects outwards. The nesting at any given place can be inspected with
-`Module.nesting`. For example, in the previous example, the nesting at
-(1) is
-
-```ruby
-[XML::SAXParser, XML]
-```
-
-It is important to understand that the nesting is composed of class and module
-*objects*, it has nothing to do with the constants used to access them, and is
-also unrelated to their names.
-
-For instance, while this definition is similar to the previous one:
-
-```ruby
-class XML::SAXParser
- # (2)
-end
-```
-
-the nesting in (2) is different:
-
-```ruby
-[XML::SAXParser]
-```
-
-`XML` does not belong to it.
-
-We can see in this example that the name of a class or module that belongs to a
-certain nesting does not necessarily correlate with the namespaces at the spot.
-
-Even more, they are totally independent, take for instance
-
-```ruby
-module X
- module Y
- end
-end
-
-module A
- module B
- end
-end
-
-module X::Y
- module A::B
- # (3)
- end
-end
-```
-
-The nesting in (3) consists of two module objects:
-
-```ruby
-[A::B, X::Y]
-```
-
-So, it not only doesn't end in `A`, which does not even belong to the nesting,
-but it also contains `X::Y`, which is independent from `A::B`.
-
-The nesting is an internal stack maintained by the interpreter, and it gets
-modified according to these rules:
-
-* The class object following a `class` keyword gets pushed when its body is
-executed, and popped after it.
-
-* The module object following a `module` keyword gets pushed when its body is
-executed, and popped after it.
-
-* A singleton class opened with `class << object` gets pushed, and popped later.
-
-* When `instance_eval` is called using a string argument,
-the singleton class of the receiver is pushed to the nesting of the eval'ed
-code. When `class_eval` or `module_eval` is called using a string argument,
-the receiver is pushed to the nesting of the eval'ed code.
-
-* The nesting at the top-level of code interpreted by `Kernel#load` is empty
-unless the `load` call receives a true value as second argument, in which case
-a newly created anonymous module is pushed by Ruby.
-
-It is interesting to observe that blocks do not modify the stack. In particular
-the blocks that may be passed to `Class.new` and `Module.new` do not get the
-class or module being defined pushed to their nesting. That's one of the
-differences between defining classes and modules in one way or another.
-
-### Class and Module Definitions are Constant Assignments
-
-Let's suppose the following snippet creates a class (rather than reopening it):
-
-```ruby
-class C
-end
-```
-
-Ruby creates a constant `C` in `Object` and stores in that constant a class
-object. The name of the class instance is "C", a string, named after the
-constant.
+Idiomatic Rails applications only issue `require` calls to load stuff from their `lib` directory, the Ruby standard library, Ruby gems, etc. That is, anything that does not belong to their autoload paths, explained below.
-That is,
-```ruby
-class Project < ApplicationRecord
-end
-```
+Enabling Zeitwerk Mode
+----------------------
-performs a constant assignment equivalent to
+The autoloading `zeitwerk` mode is enabled by default in Rails 6 applications running on CRuby:
```ruby
-Project = Class.new(ApplicationRecord)
+# config/application.rb
+config.load_defaults "6.x" # enables zeitwerk mode in CRuby
```
-including setting the name of the class as a side-effect:
+In `zeitwerk` mode, Rails uses [Zeitwerk](https://github.com/fxn/zeitwerk) internally to autoload, reload, and eager load. Rails instantiates and configures a dedicated Zeitwerk instance that manages the project.
-```ruby
-Project.name # => "Project"
-```
+INFO. You do not configure Zeitwerk manually in a Rails application. Rather, you configure the application using the portable configuration points explained in this guide, and Rails translates that to Zeitwerk on your behalf.
-Constant assignment has a special rule to make that happen: if the object
-being assigned is an anonymous class or module, Ruby sets the object's name to
-the name of the constant.
+Project Structure
+-----------------
-INFO. From then on, what happens to the constant and the instance does not
-matter. For example, the constant could be deleted, the class object could be
-assigned to a different constant, be stored in no constant anymore, etc. Once
-the name is set, it doesn't change.
+In a Rails application file names have to match the constants they define, with directories acting as namespaces.
-Similarly, module creation using the `module` keyword as in
+For example, the file `app/helpers/users_helper.rb` should define `UsersHelper` and the file `app/controllers/admin/payments_controller.rb` should define `Admin::PaymentsController`.
-```ruby
-module Admin
-end
-```
-
-performs a constant assignment equivalent to
+Rails configures Zeitwerk to inflect file names with `String#camelize`. For example, it expects that `app/controllers/users_controller.rb` defines the constant `UsersController` because
```ruby
-Admin = Module.new
+"users_controller".camelize # => UsersController
```
-including setting the name as a side-effect:
+If you need to customize any of these inflections, for example to add an acronym, please have a look at `config/initializers/inflections.rb`.
-```ruby
-Admin.name # => "Admin"
-```
+Please, check the [Zeitwerk documentation](https://github.com/fxn/zeitwerk#file-structure) for further details.
-WARNING. The execution context of a block passed to `Class.new` or `Module.new`
-is not entirely equivalent to the one of the body of the definitions using the
-`class` and `module` keywords. But both idioms result in the same constant
-assignment.
-
-Thus, an informal expression like "the `String` class" technically means the
-class object stored in the constant called "String". That constant, in turn,
-belongs to the class object stored in the constant called "Object".
-
-`String` is an ordinary constant, and everything related to them such as
-resolution algorithms applies to it.
-
-Likewise, in the controller
-
-```ruby
-class PostsController < ApplicationController
- def index
- @posts = Post.all
- end
-end
-```
-
-`Post` is not syntax for a class. Rather, `Post` is a regular Ruby constant. If
-all is good, the constant is evaluated to an object that responds to `all`.
+Autoload paths
+--------------
-That is why we talk about *constant* autoloading, Rails has the ability to
-load constants on the fly.
+We call _autoload paths_ to the list of application directories whose contents are to be autoloaded. For example, `app/models`. Such directories represent the root namespace: `Object`.
-### Constants are Stored in Modules
+INFO. Autoload paths are called _root directories_ in Zeitwerk documentation, but we'll stay with "autoload path" in this guide.
-Constants belong to modules in a very literal sense. Classes and modules have
-a constant table; think of it as a hash table.
+Within an autoload path, file names must match the constants they define as documented [here](https://github.com/fxn/zeitwerk#file-structure).
-Let's analyze an example to really understand what that means. While common
-abuses of language like "the `String` class" are convenient, the exposition is
-going to be precise here for didactic purposes.
+By default, the autoload paths of an application consist of all the subdirectories of `app` that exist when the application boots ---except for `aasets`, `javascripts`, `views`,--- plus the autoload paths of engines it might depend on.
-Let's consider the following module definition:
+For example, if `UsersHelper` is implemented in `app/helpers/users_helper.rb`, the module is autoloadable, you do not need (and should not write) a `require` call for it:
-```ruby
-module Colors
- RED = '0xff0000'
-end
```
-
-First, when the `module` keyword is processed, the interpreter creates a new
-entry in the constant table of the class object stored in the `Object` constant.
-Said entry associates the name "Colors" to a newly created module object.
-Furthermore, the interpreter sets the name of the new module object to be the
-string "Colors".
-
-Later, when the body of the module definition is interpreted, a new entry is
-created in the constant table of the module object stored in the `Colors`
-constant. That entry maps the name "RED" to the string "0xff0000".
-
-In particular, `Colors::RED` is totally unrelated to any other `RED` constant
-that may live in any other class or module object. If there were any, they
-would have separate entries in their respective constant tables.
-
-Pay special attention in the previous paragraphs to the distinction between
-class and module objects, constant names, and value objects associated to them
-in constant tables.
-
-### Resolution Algorithms
-
-#### Resolution Algorithm for Relative Constants
-
-At any given place in the code, let's define *cref* to be the first element of
-the nesting if it is not empty, or `Object` otherwise.
-
-Without getting too much into the details, the resolution algorithm for relative
-constant references goes like this:
-
-1. If the nesting is not empty the constant is looked up in its elements and in
-order. The ancestors of those elements are ignored.
-
-2. If not found, then the algorithm walks up the ancestor chain of the cref.
-
-3. If not found and the cref is a module, the constant is looked up in `Object`.
-
-4. If not found, `const_missing` is invoked on the cref. The default
-implementation of `const_missing` raises `NameError`, but it can be overridden.
-
-Rails autoloading **does not emulate this algorithm**, but its starting point is
-the name of the constant to be autoloaded, and the cref. See more in [Relative
-References](#autoloading-algorithms-relative-references).
-
-#### Resolution Algorithm for Qualified Constants
-
-Qualified constants look like this:
-
-```ruby
-Billing::Invoice
+$ bin/rails runner 'p UsersHelper'
+UsersHelper
```
-`Billing::Invoice` is composed of two constants: `Billing` is relative and is
-resolved using the algorithm of the previous section.
-
-INFO. Leading colons would make the first segment absolute rather than
-relative: `::Billing::Invoice`. That would force `Billing` to be looked up
-only as a top-level constant.
-
-`Invoice` on the other hand is qualified by `Billing` and we are going to see
-its resolution next. Let's define *parent* to be that qualifying class or module
-object, that is, `Billing` in the example above. The algorithm for qualified
-constants goes like this:
-
-1. The constant is looked up in the parent and its ancestors. In Ruby >= 2.5,
-`Object` is skipped if present among the ancestors. `Kernel` and `BasicObject`
-are still checked though.
+Autoload paths automatically pick any custom directories under `app`. For example, if your application has `app/presenters`, or `app/services`, etc., they are added to autoload paths.
-2. If the lookup fails, `const_missing` is invoked in the parent. The default
-implementation of `const_missing` raises `NameError`, but it can be overridden.
+The array of autoload paths can be extended by mutating `config.autoload_paths`, in `config/application.rb`, but nowadays this is discouraged.
-INFO. In Ruby < 2.5 `String::Hash` evaluates to `Hash` and the interpreter
-issues a warning: "toplevel constant Hash referenced by String::Hash". Starting
-with 2.5, `String::Hash` raises `NameError` because `Object` is skipped.
+WARNING. Please, do not mutate `ActiveSupport::Dependencies.autoload_paths`, the public interface to change autoload paths is `config.autoload_paths`.
-As you see, this algorithm is simpler than the one for relative constants. In
-particular, the nesting plays no role here, and modules are not special-cased,
-if neither they nor their ancestors have the constants, `Object` is **not**
-checked.
-Rails autoloading **does not emulate this algorithm**, but its starting point is
-the name of the constant to be autoloaded, and the parent. See more in
-[Qualified References](#autoloading-algorithms-qualified-references).
-
-
-Vocabulary
+$LOAD_PATH
----------
-### Parent Namespaces
-
-Given a string with a constant path we define its *parent namespace* to be the
-string that results from removing its rightmost segment.
-
-For example, the parent namespace of the string "A::B::C" is the string "A::B",
-the parent namespace of "A::B" is "A", and the parent namespace of "A" is "".
-
-The interpretation of a parent namespace when thinking about classes and modules
-is tricky though. Let's consider a module M named "A::B":
-
-* The parent namespace, "A", may not reflect nesting at a given spot.
-
-* The constant `A` may no longer exist, some code could have removed it from
-`Object`.
-
-* If `A` exists, the class or module that was originally in `A` may not be there
-anymore. For example, if after a constant removal there was another constant
-assignment there would generally be a different object in there.
-
-* In such case, it could even happen that the reassigned `A` held a new class or
-module called also "A"!
-
-* In the previous scenarios M would no longer be reachable through `A::B` but
-the module object itself could still be alive somewhere and its name would
-still be "A::B".
-
-The idea of a parent namespace is at the core of the autoloading algorithms
-and helps explain and understand their motivation intuitively, but as you see
-that metaphor leaks easily. Given an edge case to reason about, take always into
-account that by "parent namespace" the guide means exactly that specific string
-derivation.
-
-### Loading Mechanism
-
-Rails autoloads files with `Kernel#load` when `config.cache_classes` is false,
-the default in development mode, and with `Kernel#require` otherwise, the
-default in production mode.
-
-`Kernel#load` allows Rails to execute files more than once if [constant
-reloading](#constant-reloading) is enabled.
-
-This guide uses the word "load" freely to mean a given file is interpreted, but
-the actual mechanism can be `Kernel#load` or `Kernel#require` depending on that
-flag.
-
-
-Autoloading Availability
-------------------------
-
-Rails is always able to autoload provided its environment is in place. For
-example the `runner` command autoloads:
-
-```
-$ rails runner 'p User.column_names'
-["id", "email", "created_at", "updated_at"]
-```
-
-The console autoloads, the test suite autoloads, and of course the application
-autoloads.
-
-By default, Rails eager loads the application files when it boots in production
-mode, so most of the autoloading going on in development does not happen. But
-autoloading may still be triggered during eager loading.
-
-For example, given
-
-```ruby
-class BeachHouse < House
-end
-```
-
-if `House` is still unknown when `app/models/beach_house.rb` is being eager
-loaded, Rails autoloads it.
-
-
-autoload_paths and eager_load_paths
------------------------------------
-
-As you probably know, when `require` gets a relative file name:
+Autoload paths are added to `$LOAD_PATH` by default. However, Zeitwerk uses absolute file names internally, and your application should not issue `require` calls for autoloadable files, so those directories are actually not needed there. You can opt-out with this flag:
```ruby
-require 'erb'
+config.add_autoload_paths_to_load_path = false
```
-Ruby looks for the file in the directories listed in `$LOAD_PATH`. That is, Ruby
-iterates over all its directories and for each one of them checks whether they
-have a file called "erb.rb", or "erb.so", or "erb.o", or "erb.dll". If it finds
-any of them, the interpreter loads it and ends the search. Otherwise, it tries
-again in the next directory of the list. If the list gets exhausted, `LoadError`
-is raised.
-
-We are going to cover how constant autoloading works in more detail later, but
-the idea is that when a constant like `Post` is hit and missing, if there's a
-`post.rb` file for example in `app/models` Rails is going to find it, evaluate
-it, and have `Post` defined as a side-effect.
-
-All right, Rails has a collection of directories similar to `$LOAD_PATH` in which
-to look up `post.rb`. That collection is called `autoload_paths` and by
-default it contains:
-
-* All subdirectories of `app` in the application and engines present at boot
- time. For example, `app/controllers`. They do not need to be the default
- ones, any custom directories like `app/workers` belong automatically to
- `autoload_paths`.
+That may speed legit `require` calls a bit, since there are less lookups. Also, if your application uses [Bootsnap](https://github.com/Shopify/bootsnap), that saves the library from building unnecessary indexes, and saves the RAM they would need.
-* Any existing second level directories called `app/*/concerns` in the
- application and engines.
-* The directory `test/mailers/previews`.
+Reloading
+---------
-`eager_load_paths` is initially the `app` paths above
+Rails automatically reloads classes and modules if application files change.
-How files are autoloaded depends on `eager_load` and `cache_classes` config settings which typically vary in development, production, and test modes:
+More precisely, if the web server is running and application files have been modified, Rails unloads all autoloaded constants just before the next request is processed. That way, application classes or modules used during that request are going to be autoloaded, thus picking up their current implementation in the file system.
- * In **development**, you want quicker startup with incremental loading of application code. So `eager_load` should be set to `false`, and Rails will autoload files as needed (see [Autoloading Algorithms](#autoloading-algorithms) below) -- and then reload them when they change (see [Constant Reloading](#constant-reloading) below).
- * In **production**, however, you want consistency and thread-safety and can live with a longer boot time. So `eager_load` is set to `true`, and then during boot (before the app is ready to receive requests) Rails loads all files in the `eager_load_paths` and then turns off auto loading (NB: autoloading may be needed during eager loading). Not autoloading after boot is a `good thing`, as autoloading can cause the app to be have thread-safety problems.
- * In **test**, for speed of execution (of individual tests) `eager_load` is `false`, so Rails follows development behaviour.
+Reloading can be enabled or disabled. The setting that controls this behavior is `config.cache_classes`, which is false by default in `development` mode (reloading enabled), and true by default in `production` mode (reloading disabled).
-What is described above are the defaults with a newly generated Rails app. There are multiple ways this can be configured differently (see [Configuring Rails Applications](configuring.html#rails-general-configuration).
-). But using `autoload_paths` on its own in the past (before Rails 5) developers might configure `autoload_paths` to add in extra locations (e.g. `lib` which used to be an autoload path list years ago, but no longer is). However this is now discouraged for most purposes, as it is likely to lead to production-only errors. It is possible to add new locations to both `config.eager_load_paths` and `config.autoload_paths` but use at your own risk.
+Rails detects files have changed using an evented file monitor (default), or walking the autoload paths, depending on `config.file_watcher`.
-See also [Autoloading in the Test Environment](#autoloading-in-the-test-environment).
+In a Rails console there is no file watcher active regardless of the value of `config.cache_classes`. This is so because, normally, it would be confusing to have code reloaded in the middle of a console session, the same way you generally want an individual request to be served by a consistent, non-changing set of application classes and modules.
-`config.autoload_paths` is not changeable from environment-specific configuration files.
-
-The value of `autoload_paths` can be inspected. In a just-generated application
-it is (edited):
+However, you can force a reload in the console executing `reload!`:
```
-$ rails r 'puts ActiveSupport::Dependencies.autoload_paths'
-.../app/assets
-.../app/channels
-.../app/controllers
-.../app/controllers/concerns
-.../app/helpers
-.../app/jobs
-.../app/mailers
-.../app/models
-.../app/models/concerns
-.../activestorage/app/assets
-.../activestorage/app/controllers
-.../activestorage/app/javascript
-.../activestorage/app/jobs
-.../activestorage/app/models
-.../actioncable/app/assets
-.../actionview/app/assets
-.../test/mailers/previews
+$ bin/rails c
+Loading development environment (Rails 6.0.0)
+irb(main):001:0> User.object_id
+=> 70136277390120
+irb(main):002:0> reload!
+Reloading...
+=> true
+irb(main):003:0> User.object_id
+=> 70136284426020
```
-INFO. `autoload_paths` is computed and cached during the initialization process.
-The application needs to be restarted to reflect any changes in the directory
-structure.
+as you can see, the class object stored in the `User` constant is different after reloading.
+### Reloading and Stale Objects
-Autoloading Algorithms
-----------------------
+It is very important to understand that Ruby does not have a way to truly reload classes and modules in memory, and have that reflected everywhere they are already used. Technically, "unloading" the `User` class means removing the `User` constant via `Object.send(:remove_const, "User")`.
-### Relative References
+Therefore, if you store a reloadable class or module object in a place that is not reloaded, that value is going to become stale.
-A relative constant reference may appear in several places, for example, in
+For example, if an initializer stores and caches a certain class object
```ruby
-class PostsController < ApplicationController
- def index
- @posts = Post.all
- end
-end
+# config/initializers/configure_payment_gateway.rb
+# DO NOT DO THIS.
+$PAYMENT_GATEWAY = Rails.env.production? ? RealGateway : MockedGateway
+# DO NOT DO THIS.
```
-all three constant references are relative.
-
-#### Constants after the `class` and `module` Keywords
-
-Ruby performs a lookup for the constant that follows a `class` or `module`
-keyword because it needs to know if the class or module is going to be created
-or reopened.
-
-If the constant is not defined at that point it is not considered to be a
-missing constant, autoloading is **not** triggered.
-
-So, in the previous example, if `PostsController` is not defined when the file
-is interpreted Rails autoloading is not going to be triggered, Ruby will just
-define the controller.
-
-#### Top-Level Constants
-
-On the contrary, if `ApplicationController` is unknown, the constant is
-considered missing and an autoload is going to be attempted by Rails.
-
-In order to load `ApplicationController`, Rails iterates over `autoload_paths`.
-First it checks if `app/assets/application_controller.rb` exists. If it does not,
-which is normally the case, it continues and finds
-`app/controllers/application_controller.rb`.
+and `MockedGateway` gets reloaded, `$PAYMENT_GATEWAY` still stores the class object `MockedGateway` evaluated to when the initializer ran. Reloading does not change the class object stored in `$PAYMENT_GATEWAY`.
-If the file defines the constant `ApplicationController` all is fine, otherwise
-`LoadError` is raised:
+Similarly, in the Rails console, if you have a user instance and reload:
```
-unable to autoload constant ApplicationController, expected
-<full path to application_controller.rb> to define it (LoadError)
+> user = User.new
+> reload!
```
-INFO. Rails does not require the value of autoloaded constants to be a class or
-module object. For example, if the file `app/models/max_clients.rb` defines
-`MAX_CLIENTS = 100` autoloading `MAX_CLIENTS` works just fine.
-
-#### Namespaces
+the `user` object is instance of a stale class object. Ruby gives you a new class if you evaluate `User` again, but does not update the class `user` is instance of.
-Autoloading `ApplicationController` looks directly under the directories of
-`autoload_paths` because the nesting in that spot is empty. The situation of
-`Post` is different, the nesting in that line is `[PostsController]` and support
-for namespaces comes into play.
-
-The basic idea is that given
+Another use case of this gotcha is subclassing reloadable classes in a place that is not reloaded:
```ruby
-module Admin
- class BaseController < ApplicationController
- @@all_roles = Role.all
- end
+# lib/vip_user.rb
+class VipUser < User
end
```
-to autoload `Role` we are going to check if it is defined in the current or
-parent namespaces, one at a time. So, conceptually we want to try to autoload
-any of
-
-```
-Admin::BaseController::Role
-Admin::Role
-Role
-```
-
-in that order. That's the idea. To do so, Rails looks in `autoload_paths`
-respectively for file names like these:
-
-```
-admin/base_controller/role.rb
-admin/role.rb
-role.rb
-```
-
-modulus some additional directory lookups we are going to cover soon.
-
-INFO. `'Constant::Name'.underscore` gives the relative path without extension of
-the file name where `Constant::Name` is expected to be defined.
-
-Let's see how Rails autoloads the `Post` constant in the `PostsController`
-above assuming the application has a `Post` model defined in
-`app/models/post.rb`.
-
-First it checks for `posts_controller/post.rb` in `autoload_paths`:
-
-```
-app/assets/posts_controller/post.rb
-app/controllers/posts_controller/post.rb
-app/helpers/posts_controller/post.rb
-...
-test/mailers/previews/posts_controller/post.rb
-```
-
-Since the lookup is exhausted without success, a similar search for a directory
-is performed, we are going to see why in the [next section](#automatic-modules):
+if `User` is reloaded, since `VipUser` is not, the superclass of `VipUser` is the original stale class object.
-```
-app/assets/posts_controller/post
-app/controllers/posts_controller/post
-app/helpers/posts_controller/post
-...
-test/mailers/previews/posts_controller/post
-```
+Bottom line: **do not cache reloadable classes or modules**.
-If all those attempts fail, then Rails starts the lookup again in the parent
-namespace. In this case only the top-level remains:
-```
-app/assets/post.rb
-app/controllers/post.rb
-app/helpers/post.rb
-app/mailers/post.rb
-app/models/post.rb
-```
+Eager Loading
+-------------
-A matching file is found in `app/models/post.rb`. The lookup stops there and the
-file is loaded. If the file actually defines `Post` all is fine, otherwise
-`LoadError` is raised.
+In production-like environments it is generally better to load all the application code when the application boots. Eager loading puts everything in memory ready to serve requests right away, and it is also [CoW](https://en.wikipedia.org/wiki/Copy-on-write)-friendly.
-### Qualified References
+Eager loading is controlled by the flag `config.eager_load`, which is enabled by default in `production` mode.
-When a qualified constant is missing Rails does not look for it in the parent
-namespaces. But there is a caveat: when a constant is missing, Rails is
-unable to tell if the trigger was a relative reference or a qualified one.
+The order in which files are eager loaded is undefined.
-For example, consider
+if the `Zeitwerk` constant is defined, Rails invokes `Zeitwerk::Loader.eager_load_all` regardless of the application autoloading mode. That ensures dependencies managed by Zeitwerk are eager loaded.
-```ruby
-module Admin
- User
-end
-```
-and
-
-```ruby
-Admin::User
-```
-
-If `User` is missing, in either case all Rails knows is that a constant called
-"User" was missing in a module called "Admin".
+Single Table Inheritance
+------------------------
-If there is a top-level `User` Ruby would resolve it in the former example, but
-wouldn't in the latter. In general, Rails does not emulate the Ruby constant
-resolution algorithms, but in this case it tries using the following heuristic:
+Single Table Inheritance is a feature that doesn't play well with lazy loading. Reason is, its API generally needs to be able to enumerate the STI hierarchy to work correctly, whereas lazy loading defers loading classes until they are referenced. You can't enumerate what you haven't referenced yet.
-> If none of the parent namespaces of the class or module has the missing
-> constant then Rails assumes the reference is relative. Otherwise qualified.
+In a sense, applications need to eager load STI hierarchies regardless of the loading mode.
-For example, if this code triggers autoloading
+Of course, if the application eager loads on boot, that is already accomplished. When it does not, it is in practice enough to instantiate the existing types in the database, which in development or test modes is usually fine. One way to do that is to throw this module into the `lib` directory:
```ruby
-Admin::User
-```
-
-and the `User` constant is already present in `Object`, it is not possible that
-the situation is
-
-```ruby
-module Admin
- User
-end
-```
-
-because otherwise Ruby would have resolved `User` and no autoloading would have
-been triggered in the first place. Thus, Rails assumes a qualified reference and
-considers the file `admin/user.rb` and directory `admin/user` to be the only
-valid options.
-
-In practice, this works quite well as long as the nesting matches all parent
-namespaces respectively and the constants that make the rule apply are known at
-that time.
-
-However, autoloading happens on demand. If by chance the top-level `User` was
-not yet loaded, then Rails assumes a relative reference by contract.
-
-Naming conflicts of this kind are rare in practice, but if one occurs,
-`require_dependency` provides a solution by ensuring that the constant needed
-to trigger the heuristic is defined in the conflicting place.
-
-### Automatic Modules
-
-When a module acts as a namespace, Rails does not require the application to
-define a file for it, a directory matching the namespace is enough.
+module StiPreload
+ unless Rails.application.config.eager_load
+ extend ActiveSupport::Concern
-Suppose an application has a back office whose controllers are stored in
-`app/controllers/admin`. If the `Admin` module is not yet loaded when
-`Admin::UsersController` is hit, Rails needs first to autoload the constant
-`Admin`.
-
-If `autoload_paths` has a file called `admin.rb` Rails is going to load that
-one, but if there's no such file and a directory called `admin` is found, Rails
-creates an empty module and assigns it to the `Admin` constant on the fly.
-
-### Generic Procedure
-
-Relative references are reported to be missing in the cref where they were hit,
-and qualified references are reported to be missing in their parent (see
-[Resolution Algorithm for Relative
-Constants](#resolution-algorithm-for-relative-constants) at the beginning of
-this guide for the definition of *cref*, and [Resolution Algorithm for Qualified
-Constants](#resolution-algorithm-for-qualified-constants) for the definition of
-*parent*).
-
-The procedure to autoload constant `C` in an arbitrary situation is as follows:
-
-```
-if the class or module in which C is missing is Object
- let ns = ''
-else
- let M = the class or module in which C is missing
-
- if M is anonymous
- let ns = ''
- else
- let ns = M.name
- end
-end
-
-loop do
- # Look for a regular file.
- for dir in autoload_paths
- if the file "#{dir}/#{ns.underscore}/c.rb" exists
- load/require "#{dir}/#{ns.underscore}/c.rb"
-
- if C is now defined
- return
- else
- raise LoadError
- end
+ included do
+ cattr_accessor :preloaded, instance_accessor: false
end
- end
- # Look for an automatic module.
- for dir in autoload_paths
- if the directory "#{dir}/#{ns.underscore}/c" exists
- if ns is an empty string
- let C = Module.new in Object and return
- else
- let C = Module.new in ns.constantize and return
+ class_methods do
+ def descendants
+ preload_sti unless preloaded
+ super
end
- end
- end
-
- if ns is empty
- # We reached the top-level without finding the constant.
- raise NameError
- else
- if C exists in any of the parent namespaces
- # Qualified constants heuristic.
- raise NameError
- else
- # Try again in the parent namespace.
- let ns = the parent namespace of ns and retry
- end
- end
-end
-```
-
-
-require_dependency
-------------------
-
-Constant autoloading is triggered on demand and therefore code that uses a
-certain constant may have it already defined or may trigger an autoload. That
-depends on the execution path and it may vary between runs.
-
-There are times, however, in which you want to make sure a certain constant is
-known when the execution reaches some code. `require_dependency` provides a way
-to load a file using the current [loading mechanism](#loading-mechanism), and
-keeping track of constants defined in that file as if they were autoloaded to
-have them reloaded as needed.
-
-`require_dependency` is rarely needed, but see a couple of use cases in
-[Autoloading and STI](#autoloading-and-sti) and [When Constants aren't
-Triggered](#when-constants-aren-t-missed).
-
-WARNING. Unlike autoloading, `require_dependency` does not expect the file to
-define any particular constant. Exploiting this behavior would be a bad practice
-though, file and constant paths should match.
-
-
-Constant Reloading
-------------------
-
-When `config.cache_classes` is false Rails is able to reload autoloaded
-constants.
-For example, if you're in a console session and edit some file behind the
-scenes, the code can be reloaded with the `reload!` command:
-
-```
-> reload!
-```
-
-When the application runs, code is reloaded when something relevant to this
-logic changes. In order to do that, Rails monitors a number of things:
-
-* `config/routes.rb`.
-
-* Locales.
-
-* Ruby files under `autoload_paths`.
-
-* `db/schema.rb` and `db/structure.sql`.
-
-If anything in there changes, there is a middleware that detects it and reloads
-the code.
-
-Autoloading keeps track of autoloaded constants. Reloading is implemented by
-removing them all from their respective classes and modules using
-`Module#remove_const`. That way, when the code goes on, those constants are
-going to be unknown again, and files reloaded on demand.
-
-INFO. This is an all-or-nothing operation, Rails does not attempt to reload only
-what changed since dependencies between classes makes that really tricky.
-Instead, everything is wiped.
-
-
-Module#autoload isn't Involved
-------------------------------
-
-`Module#autoload` provides a lazy way to load constants that is fully integrated
-with the Ruby constant lookup algorithms, dynamic constant API, etc. It is quite
-transparent.
-
-Rails internals make extensive use of it to defer as much work as possible from
-the boot process. But constant autoloading in Rails is **not** implemented with
-`Module#autoload`.
-
-One possible implementation based on `Module#autoload` would be to walk the
-application tree and issue `autoload` calls that map existing file names to
-their conventional constant name.
-
-There are a number of reasons that prevent Rails from using that implementation.
-
-For example, `Module#autoload` is only capable of loading files using `require`,
-so reloading would not be possible. Not only that, it uses an internal `require`
-which is not `Kernel#require`.
-
-Then, it provides no way to remove declarations in case a file is deleted. If a
-constant gets removed with `Module#remove_const` its `autoload` is not triggered
-again. Also, it doesn't support qualified names, so files with namespaces should
-be interpreted during the walk tree to install their own `autoload` calls, but
-those files could have constant references not yet configured.
-
-An implementation based on `Module#autoload` would be awesome but, as you see,
-at least as of today it is not possible. Constant autoloading in Rails is
-implemented with `Module#const_missing`, and that's why it has its own contract,
-documented in this guide.
-
-
-Common Gotchas
---------------
-
-### Nesting and Qualified Constants
-
-Let's consider
-
-```ruby
-module Admin
- class UsersController < ApplicationController
- def index
- @users = User.all
+ # Constantizes all types present in the database. There might be more on
+ # disk, but that does not matter in practice as far as the STI API is
+ # concerned.
+ #
+ # Assumes store_full_sti_class is true, the default.
+ def preload_sti
+ types_in_db = \
+ base_class.
+ select(inheritance_column).
+ distinct.
+ pluck(inheritance_column).
+ compact.
+ each(&:constantize)
+
+ types_in_db.each do |type|
+ logger.debug("Preloading STI type #{type}")
+ type.constantize
+ end
+
+ self.preloaded = true
+ end
end
end
end
```
-and
+and then include it in the STI root classes of your project:
```ruby
-class Admin::UsersController < ApplicationController
- def index
- @users = User.all
- end
-end
-```
-
-To resolve `User` Ruby checks `Admin` in the former case, but it does not in
-the latter because it does not belong to the nesting (see [Nesting](#nesting)
-and [Resolution Algorithms](#resolution-algorithms)).
-
-Unfortunately Rails autoloading does not know the nesting in the spot where the
-constant was missing and so it is not able to act as Ruby would. In particular,
-`Admin::User` will get autoloaded in either case.
-
-Albeit qualified constants with `class` and `module` keywords may technically
-work with autoloading in some cases, it is preferable to use relative constants
-instead:
+# app/models/shape.rb
+require "sti_preload"
-```ruby
-module Admin
- class UsersController < ApplicationController
- def index
- @users = User.all
- end
- end
+class Shape < ApplicationRecord
+ include StiPreload # Only in the root class.
end
-```
-### Autoloading and STI
-
-Single Table Inheritance (STI) is a feature of Active Record that enables
-storing a hierarchy of models in one single table. The API of such models is
-aware of the hierarchy and encapsulates some common needs. For example, given
-these classes:
-
-```ruby
# app/models/polygon.rb
-class Polygon < ApplicationRecord
+class Polygon < Shape
end
# app/models/triangle.rb
class Triangle < Polygon
end
-
-# app/models/rectangle.rb
-class Rectangle < Polygon
-end
-```
-
-`Triangle.create` creates a row that represents a triangle, and
-`Rectangle.create` creates a row that represents a rectangle. If `id` is the
-ID of an existing record, `Polygon.find(id)` returns an object of the correct
-type.
-
-Methods that operate on collections are also aware of the hierarchy. For
-example, `Polygon.all` returns all the records of the table, because all
-rectangles and triangles are polygons. Active Record takes care of returning
-instances of their corresponding class in the result set.
-
-Types are autoloaded as needed. For example, if `Polygon.first` is a rectangle
-and `Rectangle` has not yet been loaded, Active Record autoloads it and the
-record is correctly instantiated.
-
-All good, but if instead of performing queries based on the root class we need
-to work on some subclass, things get interesting.
-
-While working with `Polygon` you do not need to be aware of all its descendants,
-because anything in the table is by definition a polygon, but when working with
-subclasses Active Record needs to be able to enumerate the types it is looking
-for. Let's see an example.
-
-`Rectangle.all` only loads rectangles by adding a type constraint to the query:
-
-```sql
-SELECT "polygons".* FROM "polygons"
-WHERE "polygons"."type" IN ("Rectangle")
-```
-
-Let's introduce now a subclass of `Rectangle`:
-
-```ruby
-# app/models/square.rb
-class Square < Rectangle
-end
-```
-
-`Rectangle.all` should now return rectangles **and** squares:
-
-```sql
-SELECT "polygons".* FROM "polygons"
-WHERE "polygons"."type" IN ("Rectangle", "Square")
-```
-
-But there's a caveat here: How does Active Record know that the class `Square`
-exists at all?
-
-Even if the file `app/models/square.rb` exists and defines the `Square` class,
-if no code yet used that class, `Rectangle.all` issues the query
-
-```sql
-SELECT "polygons".* FROM "polygons"
-WHERE "polygons"."type" IN ("Rectangle")
-```
-
-That is not a bug, the query includes all *known* descendants of `Rectangle`.
-
-A way to ensure this works correctly regardless of the order of execution is to
-manually load the direct subclasses at the bottom of the file that defines each
-intermediate class:
-
-```ruby
-# app/models/rectangle.rb
-class Rectangle < Polygon
-end
-require_dependency 'square'
-```
-
-This needs to happen for every intermediate (non-root and non-leaf) class. The
-root class does not scope the query by type, and therefore does not necessarily
-have to know all its descendants.
-
-### Autoloading and `require`
-
-Files defining constants to be autoloaded should never be `require`d:
-
-```ruby
-require 'user' # DO NOT DO THIS
-
-class UsersController < ApplicationController
- ...
-end
-```
-
-There are two possible gotchas here in development mode:
-
-1. If `User` is autoloaded before reaching the `require`, `app/models/user.rb`
-runs again because `load` does not update `$LOADED_FEATURES`.
-
-2. If the `require` runs first Rails does not mark `User` as an autoloaded
-constant and changes to `app/models/user.rb` aren't reloaded.
-
-Just follow the flow and use constant autoloading always, never mix
-autoloading and `require`. As a last resort, if some file absolutely needs to
-load a certain file use `require_dependency` to play nice with constant
-autoloading. This option is rarely needed in practice, though.
-
-Of course, using `require` in autoloaded files to load ordinary 3rd party
-libraries is fine, and Rails is able to distinguish their constants, they are
-not marked as autoloaded.
-
-### Autoloading and Initializers
-
-Consider this assignment in `config/initializers/set_auth_service.rb`:
-
-```ruby
-AUTH_SERVICE = if Rails.env.production?
- RealAuthService
-else
- MockedAuthService
-end
-```
-
-The purpose of this setup would be that the application uses the class that
-corresponds to the environment via `AUTH_SERVICE`. In development mode
-`MockedAuthService` gets autoloaded when the initializer runs. Let's suppose
-we do some requests, change its implementation, and hit the application again.
-To our surprise the changes are not reflected. Why?
-
-As [we saw earlier](#constant-reloading), Rails removes autoloaded constants,
-but `AUTH_SERVICE` stores the original class object. Stale, non-reachable
-using the original constant, but perfectly functional.
-
-The following code summarizes the situation:
-
-```ruby
-class C
- def quack
- 'quack!'
- end
-end
-
-X = C
-Object.instance_eval { remove_const(:C) }
-X.new.quack # => quack!
-X.name # => C
-C # => uninitialized constant C (NameError)
-```
-
-Because of that, it is not a good idea to autoload constants on application
-initialization.
-
-In the case above we could implement a dynamic access point:
-
-```ruby
-# app/models/auth_service.rb
-class AuthService
- if Rails.env.production?
- def self.instance
- RealAuthService
- end
- else
- def self.instance
- MockedAuthService
- end
- end
-end
-```
-
-and have the application use `AuthService.instance` instead. `AuthService`
-would be loaded on demand and be autoload-friendly.
-
-### `require_dependency` and Initializers
-
-As we saw before, `require_dependency` loads files in an autoloading-friendly
-way. Normally, though, such a call does not make sense in an initializer.
-
-One could think about doing some [`require_dependency`](#require-dependency)
-calls in an initializer to make sure certain constants are loaded upfront, for
-example as an attempt to address the [gotcha with STIs](#autoloading-and-sti).
-
-Problem is, in development mode [autoloaded constants are wiped](#constant-reloading)
-if there is any relevant change in the file system. If that happens then
-we are in the very same situation the initializer wanted to avoid!
-
-Calls to `require_dependency` have to be strategically written in autoloaded
-spots.
-
-### When Constants aren't Missed
-
-#### Relative References
-
-Let's consider a flight simulator. The application has a default flight model
-
-```ruby
-# app/models/flight_model.rb
-class FlightModel
-end
-```
-
-that can be overridden by each airplane, for instance
-
-```ruby
-# app/models/bell_x1/flight_model.rb
-module BellX1
- class FlightModel < FlightModel
- end
-end
-
-# app/models/bell_x1/aircraft.rb
-module BellX1
- class Aircraft
- def initialize
- @flight_model = FlightModel.new
- end
- end
-end
-```
-
-The initializer wants to create a `BellX1::FlightModel` and nesting has
-`BellX1`, that looks good. But if the default flight model is loaded and the
-one for the Bell-X1 is not, the interpreter is able to resolve the top-level
-`FlightModel` and autoloading is thus not triggered for `BellX1::FlightModel`.
-
-That code depends on the execution path.
-
-These kind of ambiguities can often be resolved using qualified constants:
-
-```ruby
-module BellX1
- class Plane
- def flight_model
- @flight_model ||= BellX1::FlightModel.new
- end
- end
-end
-```
-
-Also, `require_dependency` is a solution:
-
-```ruby
-require_dependency 'bell_x1/flight_model'
-
-module BellX1
- class Plane
- def flight_model
- @flight_model ||= FlightModel.new
- end
- end
-end
-```
-
-#### Qualified References
-
-WARNING. This gotcha is only possible in Ruby < 2.5.
-
-Given
-
-```ruby
-# app/models/hotel.rb
-class Hotel
-end
-
-# app/models/image.rb
-class Image
-end
-
-# app/models/hotel/image.rb
-class Hotel
- class Image < Image
- end
-end
-```
-
-the expression `Hotel::Image` is ambiguous because it depends on the execution
-path.
-
-As [we saw before](#resolution-algorithm-for-qualified-constants), Ruby looks
-up the constant in `Hotel` and its ancestors. If `app/models/image.rb` has
-been loaded but `app/models/hotel/image.rb` hasn't, Ruby does not find `Image`
-in `Hotel`, but it does in `Object`:
-
-```
-$ rails r 'Image; p Hotel::Image' 2>/dev/null
-Image # NOT Hotel::Image!
-```
-
-The code evaluating `Hotel::Image` needs to make sure
-`app/models/hotel/image.rb` has been loaded, possibly with
-`require_dependency`.
-
-In these cases the interpreter issues a warning though:
-
-```
-warning: toplevel constant Image referenced by Hotel::Image
-```
-
-This surprising constant resolution can be observed with any qualifying class:
-
-```
-2.1.5 :001 > String::Array
-(irb):1: warning: toplevel constant Array referenced by String::Array
- => Array
-```
-
-WARNING. To find this gotcha the qualifying namespace has to be a class,
-`Object` is not an ancestor of modules.
-
-### Autoloading within Singleton Classes
-
-Let's suppose we have these class definitions:
-
-```ruby
-# app/models/hotel/services.rb
-module Hotel
- class Services
- end
-end
-
-# app/models/hotel/geo_location.rb
-module Hotel
- class GeoLocation
- class << self
- Services
- end
- end
-end
```
-If `Hotel::Services` is known by the time `app/models/hotel/geo_location.rb`
-is being loaded, `Services` is resolved by Ruby because `Hotel` belongs to the
-nesting when the singleton class of `Hotel::GeoLocation` is opened.
+Rails.autoloaders
+-----------------
-But if `Hotel::Services` is not known, Rails is not able to autoload it, the
-application raises `NameError`.
-
-The reason is that autoloading is triggered for the singleton class, which is
-anonymous, and as [we saw before](#generic-procedure), Rails only checks the
-top-level namespace in that edge case.
-
-An easy solution to this caveat is to qualify the constant:
+The Zeitwerk instances managing your application are availabe at
```ruby
-module Hotel
- class GeoLocation
- class << self
- Hotel::Services
- end
- end
-end
+Rails.autoloaders.main
+Rails.autoloaders.once
```
-### Autoloading in `BasicObject`
-
-Direct descendants of `BasicObject` do not have `Object` among their ancestors
-and cannot resolve top-level constants:
-
-```ruby
-class C < BasicObject
- String # NameError: uninitialized constant C::String
-end
-```
+The former is the main one. The latter is there mostly for backwards compatibily reasons, in case the application has something in `config.autoload_once_paths` (this is discouraged nowadays).
-When autoloading is involved that plot has a twist. Let's consider:
+You can check if `zeitwerk` mode is enabled with
```ruby
-class C < BasicObject
- def user
- User # WRONG
- end
-end
+Rails.autoloaders.zeitwerk_enabled?
```
-Since Rails checks the top-level namespace `User` gets autoloaded just fine the
-first time the `user` method is invoked. You only get the exception if the
-`User` constant is known at that point, in particular in a *second* call to
-`user`:
-
-```ruby
-c = C.new
-c.user # surprisingly fine, User
-c.user # NameError: uninitialized constant C::User
-```
-
-because it detects that a parent namespace already has the constant (see [Qualified
-References](#autoloading-algorithms-qualified-references)).
-
-As with pure Ruby, within the body of a direct descendant of `BasicObject` use
-always absolute constant paths:
-
-```ruby
-class C < BasicObject
- ::String # RIGHT
-
- def user
- ::User # RIGHT
- end
-end
-```
-
-### Autoloading in the Test Environment
-
-When configuring the `test` environment for autoloading you might consider multiple factors.
-
-For example it might be worth running your tests with an identical setup to production (`config.eager_load = true`, `config.cache_classes = true`) in order to catch any problems before they hit production (this is compensation for the lack of dev-prod parity). However this will slow down the boot time for individual tests on a dev machine (and is not immediately compatible with spring see below). So one possibility is to do this on a
-[CI](https://en.wikipedia.org/wiki/Continuous_integration) machine only (which should run without spring).
-
-On a development machine you can then have your tests running with whatever is fastest (ideally `config.eager_load = false`).
-
-With the [Spring](https://github.com/rails/spring) pre-loader (included with new Rails apps), you ideally keep `config.eager_load = false` as per development. Sometimes you may end up with a hybrid configuration (`config.eager_load = true`, `config.cache_classes = true` AND `config.enable_dependency_loading = true`), see [spring issue](https://github.com/rails/spring/issues/519#issuecomment-348324369). However it might be simpler to keep the same configuration as development, and work out whatever it is that is causing autoloading to fail (perhaps by the results of your CI test results).
-
-Occasionally you may need to explicitly eager_load by using `Rails
-.application.eager_load!` in the setup of your tests -- this might occur if your [tests involve multithreading](https://stackoverflow.com/questions/25796409/in-rails-how-can-i-eager-load-all-code-before-a-specific-rspec-test).
-
-## Troubleshooting
-
-### Tracing Autoloads
-
-Active Support is able to report constants as they are autoloaded. To enable these traces in a Rails application, put the following two lines in some initializer:
-
-```ruby
-ActiveSupport::Dependencies.logger = Rails.logger
-ActiveSupport::Dependencies.verbose = true
-```
-
-### Where is a Given Autoload Triggered?
+Opting Out
+----------
-If constant `Foo` is being autoloaded, and you'd like to know where is that autoload coming from, just throw
+Applications can load Rails 6 defaults and still use the classic autoloader this way:
```ruby
-puts caller
+# config/application.rb
+config.load_defaults "6.x"
+config.autoloader = :classic
```
-at the top of `foo.rb` and inspect the printed stack trace.
-
-### Which Constants Have Been Autoloaded?
-
-At any given time,
-
-```ruby
-ActiveSupport::Dependencies.autoloaded_constants
-```
+That may be handy if upgrading to Rails 6 in different phases, but classic mode is discouraged for new applications.
-has the collection of constants that have been autoloaded so far.
+`zeitwerk` mode is not available in versions of Rails previous to 6.0.
diff --git a/guides/source/autoloading_and_reloading_constants_classic_mode.md b/guides/source/autoloading_and_reloading_constants_classic_mode.md
new file mode 100644
index 0000000000..d0d9e076d6
--- /dev/null
+++ b/guides/source/autoloading_and_reloading_constants_classic_mode.md
@@ -0,0 +1,1351 @@
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON https://guides.rubyonrails.org.**
+
+Autoloading and Reloading Constants (Classic Mode)
+==================================================
+
+This guide documents how constant autoloading and reloading works in `classic` mode.
+
+After reading this guide, you will know:
+
+* Key aspects of Ruby constants
+* What are the `autoload_paths` and how does eager loading work in production?
+* How constant autoloading works
+* What is `require_dependency`
+* How constant reloading works
+* Solutions to common autoloading gotchas
+
+--------------------------------------------------------------------------------
+
+
+Introduction
+------------
+
+INFO. This guide documents autoloading in `classic` mode, which is the traditional one. If you'd like to read about `zeiwerk` mode instead, the new one in Rails 6, please check [Autoloading and Reloading Constants (Zeitwerk Mode)](autoloading_and_reloading_constants.html).
+
+Ruby on Rails allows applications to be written as if their code was preloaded.
+
+In a normal Ruby program classes need to load their dependencies:
+
+```ruby
+require 'application_controller'
+require 'post'
+
+class PostsController < ApplicationController
+ def index
+ @posts = Post.all
+ end
+end
+```
+
+Our Rubyist instinct quickly sees some redundancy in there: If classes were
+defined in files matching their name, couldn't their loading be automated
+somehow? We could save scanning the file for dependencies, which is brittle.
+
+Moreover, `Kernel#require` loads files once, but development is much more smooth
+if code gets refreshed when it changes without restarting the server. It would
+be nice to be able to use `Kernel#load` in development, and `Kernel#require` in
+production.
+
+Indeed, those features are provided by Ruby on Rails, where we just write
+
+```ruby
+class PostsController < ApplicationController
+ def index
+ @posts = Post.all
+ end
+end
+```
+
+This guide documents how that works.
+
+
+Constants Refresher
+-------------------
+
+While constants are trivial in most programming languages, they are a rich
+topic in Ruby.
+
+It is beyond the scope of this guide to document Ruby constants, but we are
+nevertheless going to highlight a few key topics. Truly grasping the following
+sections is instrumental to understanding constant autoloading and reloading.
+
+### Nesting
+
+Class and module definitions can be nested to create namespaces:
+
+```ruby
+module XML
+ class SAXParser
+ # (1)
+ end
+end
+```
+
+The *nesting* at any given place is the collection of enclosing nested class and
+module objects outwards. The nesting at any given place can be inspected with
+`Module.nesting`. For example, in the previous example, the nesting at
+(1) is
+
+```ruby
+[XML::SAXParser, XML]
+```
+
+It is important to understand that the nesting is composed of class and module
+*objects*, it has nothing to do with the constants used to access them, and is
+also unrelated to their names.
+
+For instance, while this definition is similar to the previous one:
+
+```ruby
+class XML::SAXParser
+ # (2)
+end
+```
+
+the nesting in (2) is different:
+
+```ruby
+[XML::SAXParser]
+```
+
+`XML` does not belong to it.
+
+We can see in this example that the name of a class or module that belongs to a
+certain nesting does not necessarily correlate with the namespaces at the spot.
+
+Even more, they are totally independent, take for instance
+
+```ruby
+module X
+ module Y
+ end
+end
+
+module A
+ module B
+ end
+end
+
+module X::Y
+ module A::B
+ # (3)
+ end
+end
+```
+
+The nesting in (3) consists of two module objects:
+
+```ruby
+[A::B, X::Y]
+```
+
+So, it not only doesn't end in `A`, which does not even belong to the nesting,
+but it also contains `X::Y`, which is independent from `A::B`.
+
+The nesting is an internal stack maintained by the interpreter, and it gets
+modified according to these rules:
+
+* The class object following a `class` keyword gets pushed when its body is
+executed, and popped after it.
+
+* The module object following a `module` keyword gets pushed when its body is
+executed, and popped after it.
+
+* A singleton class opened with `class << object` gets pushed, and popped later.
+
+* When `instance_eval` is called using a string argument,
+the singleton class of the receiver is pushed to the nesting of the eval'ed
+code. When `class_eval` or `module_eval` is called using a string argument,
+the receiver is pushed to the nesting of the eval'ed code.
+
+* The nesting at the top-level of code interpreted by `Kernel#load` is empty
+unless the `load` call receives a true value as second argument, in which case
+a newly created anonymous module is pushed by Ruby.
+
+It is interesting to observe that blocks do not modify the stack. In particular
+the blocks that may be passed to `Class.new` and `Module.new` do not get the
+class or module being defined pushed to their nesting. That's one of the
+differences between defining classes and modules in one way or another.
+
+### Class and Module Definitions are Constant Assignments
+
+Let's suppose the following snippet creates a class (rather than reopening it):
+
+```ruby
+class C
+end
+```
+
+Ruby creates a constant `C` in `Object` and stores in that constant a class
+object. The name of the class instance is "C", a string, named after the
+constant.
+
+That is,
+
+```ruby
+class Project < ApplicationRecord
+end
+```
+
+performs a constant assignment equivalent to
+
+```ruby
+Project = Class.new(ApplicationRecord)
+```
+
+including setting the name of the class as a side-effect:
+
+```ruby
+Project.name # => "Project"
+```
+
+Constant assignment has a special rule to make that happen: if the object
+being assigned is an anonymous class or module, Ruby sets the object's name to
+the name of the constant.
+
+INFO. From then on, what happens to the constant and the instance does not
+matter. For example, the constant could be deleted, the class object could be
+assigned to a different constant, be stored in no constant anymore, etc. Once
+the name is set, it doesn't change.
+
+Similarly, module creation using the `module` keyword as in
+
+```ruby
+module Admin
+end
+```
+
+performs a constant assignment equivalent to
+
+```ruby
+Admin = Module.new
+```
+
+including setting the name as a side-effect:
+
+```ruby
+Admin.name # => "Admin"
+```
+
+WARNING. The execution context of a block passed to `Class.new` or `Module.new`
+is not entirely equivalent to the one of the body of the definitions using the
+`class` and `module` keywords. But both idioms result in the same constant
+assignment.
+
+Thus, an informal expression like "the `String` class" technically means the
+class object stored in the constant called "String". That constant, in turn,
+belongs to the class object stored in the constant called "Object".
+
+`String` is an ordinary constant, and everything related to them such as
+resolution algorithms applies to it.
+
+Likewise, in the controller
+
+```ruby
+class PostsController < ApplicationController
+ def index
+ @posts = Post.all
+ end
+end
+```
+
+`Post` is not syntax for a class. Rather, `Post` is a regular Ruby constant. If
+all is good, the constant is evaluated to an object that responds to `all`.
+
+That is why we talk about *constant* autoloading, Rails has the ability to
+load constants on the fly.
+
+### Constants are Stored in Modules
+
+Constants belong to modules in a very literal sense. Classes and modules have
+a constant table; think of it as a hash table.
+
+Let's analyze an example to really understand what that means. While common
+abuses of language like "the `String` class" are convenient, the exposition is
+going to be precise here for didactic purposes.
+
+Let's consider the following module definition:
+
+```ruby
+module Colors
+ RED = '0xff0000'
+end
+```
+
+First, when the `module` keyword is processed, the interpreter creates a new
+entry in the constant table of the class object stored in the `Object` constant.
+Said entry associates the name "Colors" to a newly created module object.
+Furthermore, the interpreter sets the name of the new module object to be the
+string "Colors".
+
+Later, when the body of the module definition is interpreted, a new entry is
+created in the constant table of the module object stored in the `Colors`
+constant. That entry maps the name "RED" to the string "0xff0000".
+
+In particular, `Colors::RED` is totally unrelated to any other `RED` constant
+that may live in any other class or module object. If there were any, they
+would have separate entries in their respective constant tables.
+
+Pay special attention in the previous paragraphs to the distinction between
+class and module objects, constant names, and value objects associated to them
+in constant tables.
+
+### Resolution Algorithms
+
+#### Resolution Algorithm for Relative Constants
+
+At any given place in the code, let's define *cref* to be the first element of
+the nesting if it is not empty, or `Object` otherwise.
+
+Without getting too much into the details, the resolution algorithm for relative
+constant references goes like this:
+
+1. If the nesting is not empty the constant is looked up in its elements and in
+order. The ancestors of those elements are ignored.
+
+2. If not found, then the algorithm walks up the ancestor chain of the cref.
+
+3. If not found and the cref is a module, the constant is looked up in `Object`.
+
+4. If not found, `const_missing` is invoked on the cref. The default
+implementation of `const_missing` raises `NameError`, but it can be overridden.
+
+Rails autoloading **does not emulate this algorithm**, but its starting point is
+the name of the constant to be autoloaded, and the cref. See more in [Relative
+References](#autoloading-algorithms-relative-references).
+
+#### Resolution Algorithm for Qualified Constants
+
+Qualified constants look like this:
+
+```ruby
+Billing::Invoice
+```
+
+`Billing::Invoice` is composed of two constants: `Billing` is relative and is
+resolved using the algorithm of the previous section.
+
+INFO. Leading colons would make the first segment absolute rather than
+relative: `::Billing::Invoice`. That would force `Billing` to be looked up
+only as a top-level constant.
+
+`Invoice` on the other hand is qualified by `Billing` and we are going to see
+its resolution next. Let's define *parent* to be that qualifying class or module
+object, that is, `Billing` in the example above. The algorithm for qualified
+constants goes like this:
+
+1. The constant is looked up in the parent and its ancestors. In Ruby >= 2.5,
+`Object` is skipped if present among the ancestors. `Kernel` and `BasicObject`
+are still checked though.
+
+2. If the lookup fails, `const_missing` is invoked in the parent. The default
+implementation of `const_missing` raises `NameError`, but it can be overridden.
+
+INFO. In Ruby < 2.5 `String::Hash` evaluates to `Hash` and the interpreter
+issues a warning: "toplevel constant Hash referenced by String::Hash". Starting
+with 2.5, `String::Hash` raises `NameError` because `Object` is skipped.
+
+As you see, this algorithm is simpler than the one for relative constants. In
+particular, the nesting plays no role here, and modules are not special-cased,
+if neither they nor their ancestors have the constants, `Object` is **not**
+checked.
+
+Rails autoloading **does not emulate this algorithm**, but its starting point is
+the name of the constant to be autoloaded, and the parent. See more in
+[Qualified References](#autoloading-algorithms-qualified-references).
+
+
+Vocabulary
+----------
+
+### Parent Namespaces
+
+Given a string with a constant path we define its *parent namespace* to be the
+string that results from removing its rightmost segment.
+
+For example, the parent namespace of the string "A::B::C" is the string "A::B",
+the parent namespace of "A::B" is "A", and the parent namespace of "A" is "".
+
+The interpretation of a parent namespace when thinking about classes and modules
+is tricky though. Let's consider a module M named "A::B":
+
+* The parent namespace, "A", may not reflect nesting at a given spot.
+
+* The constant `A` may no longer exist, some code could have removed it from
+`Object`.
+
+* If `A` exists, the class or module that was originally in `A` may not be there
+anymore. For example, if after a constant removal there was another constant
+assignment there would generally be a different object in there.
+
+* In such case, it could even happen that the reassigned `A` held a new class or
+module called also "A"!
+
+* In the previous scenarios M would no longer be reachable through `A::B` but
+the module object itself could still be alive somewhere and its name would
+still be "A::B".
+
+The idea of a parent namespace is at the core of the autoloading algorithms
+and helps explain and understand their motivation intuitively, but as you see
+that metaphor leaks easily. Given an edge case to reason about, take always into
+account that by "parent namespace" the guide means exactly that specific string
+derivation.
+
+### Loading Mechanism
+
+Rails autoloads files with `Kernel#load` when `config.cache_classes` is false,
+the default in development mode, and with `Kernel#require` otherwise, the
+default in production mode.
+
+`Kernel#load` allows Rails to execute files more than once if [constant
+reloading](#constant-reloading) is enabled.
+
+This guide uses the word "load" freely to mean a given file is interpreted, but
+the actual mechanism can be `Kernel#load` or `Kernel#require` depending on that
+flag.
+
+
+Autoloading Availability
+------------------------
+
+Rails is always able to autoload provided its environment is in place. For
+example the `runner` command autoloads:
+
+```
+$ rails runner 'p User.column_names'
+["id", "email", "created_at", "updated_at"]
+```
+
+The console autoloads, the test suite autoloads, and of course the application
+autoloads.
+
+By default, Rails eager loads the application files when it boots in production
+mode, so most of the autoloading going on in development does not happen. But
+autoloading may still be triggered during eager loading.
+
+For example, given
+
+```ruby
+class BeachHouse < House
+end
+```
+
+if `House` is still unknown when `app/models/beach_house.rb` is being eager
+loaded, Rails autoloads it.
+
+
+autoload_paths and eager_load_paths
+-----------------------------------
+
+As you probably know, when `require` gets a relative file name:
+
+```ruby
+require 'erb'
+```
+
+Ruby looks for the file in the directories listed in `$LOAD_PATH`. That is, Ruby
+iterates over all its directories and for each one of them checks whether they
+have a file called "erb.rb", or "erb.so", or "erb.o", or "erb.dll". If it finds
+any of them, the interpreter loads it and ends the search. Otherwise, it tries
+again in the next directory of the list. If the list gets exhausted, `LoadError`
+is raised.
+
+We are going to cover how constant autoloading works in more detail later, but
+the idea is that when a constant like `Post` is hit and missing, if there's a
+`post.rb` file for example in `app/models` Rails is going to find it, evaluate
+it, and have `Post` defined as a side-effect.
+
+All right, Rails has a collection of directories similar to `$LOAD_PATH` in which
+to look up `post.rb`. That collection is called `autoload_paths` and by
+default it contains:
+
+* All subdirectories of `app` in the application and engines present at boot
+ time. For example, `app/controllers`. They do not need to be the default
+ ones, any custom directories like `app/workers` belong automatically to
+ `autoload_paths`.
+
+* Any existing second level directories called `app/*/concerns` in the
+ application and engines.
+
+* The directory `test/mailers/previews`.
+
+`eager_load_paths` is initially the `app` paths above
+
+How files are autoloaded depends on `eager_load` and `cache_classes` config settings which typically vary in development, production, and test modes:
+
+ * In **development**, you want quicker startup with incremental loading of application code. So `eager_load` should be set to `false`, and Rails will autoload files as needed (see [Autoloading Algorithms](#autoloading-algorithms) below) -- and then reload them when they change (see [Constant Reloading](#constant-reloading) below).
+ * In **production**, however, you want consistency and thread-safety and can live with a longer boot time. So `eager_load` is set to `true`, and then during boot (before the app is ready to receive requests) Rails loads all files in the `eager_load_paths` and then turns off auto loading (NB: autoloading may be needed during eager loading). Not autoloading after boot is a `good thing`, as autoloading can cause the app to be have thread-safety problems.
+ * In **test**, for speed of execution (of individual tests) `eager_load` is `false`, so Rails follows development behaviour.
+
+What is described above are the defaults with a newly generated Rails app. There are multiple ways this can be configured differently (see [Configuring Rails Applications](configuring.html#rails-general-configuration).
+). But using `autoload_paths` on its own in the past (before Rails 5) developers might configure `autoload_paths` to add in extra locations (e.g. `lib` which used to be an autoload path list years ago, but no longer is). However this is now discouraged for most purposes, as it is likely to lead to production-only errors. It is possible to add new locations to both `config.eager_load_paths` and `config.autoload_paths` but use at your own risk.
+
+See also [Autoloading in the Test Environment](#autoloading-in-the-test-environment).
+
+`config.autoload_paths` is not changeable from environment-specific configuration files.
+
+The value of `autoload_paths` can be inspected. In a just-generated application
+it is (edited):
+
+```
+$ rails r 'puts ActiveSupport::Dependencies.autoload_paths'
+.../app/assets
+.../app/channels
+.../app/controllers
+.../app/controllers/concerns
+.../app/helpers
+.../app/jobs
+.../app/mailers
+.../app/models
+.../app/models/concerns
+.../activestorage/app/assets
+.../activestorage/app/controllers
+.../activestorage/app/javascript
+.../activestorage/app/jobs
+.../activestorage/app/models
+.../actioncable/app/assets
+.../actionview/app/assets
+.../test/mailers/previews
+```
+
+INFO. `autoload_paths` is computed and cached during the initialization process.
+The application needs to be restarted to reflect any changes in the directory
+structure.
+
+
+Autoloading Algorithms
+----------------------
+
+### Relative References
+
+A relative constant reference may appear in several places, for example, in
+
+```ruby
+class PostsController < ApplicationController
+ def index
+ @posts = Post.all
+ end
+end
+```
+
+all three constant references are relative.
+
+#### Constants after the `class` and `module` Keywords
+
+Ruby performs a lookup for the constant that follows a `class` or `module`
+keyword because it needs to know if the class or module is going to be created
+or reopened.
+
+If the constant is not defined at that point it is not considered to be a
+missing constant, autoloading is **not** triggered.
+
+So, in the previous example, if `PostsController` is not defined when the file
+is interpreted Rails autoloading is not going to be triggered, Ruby will just
+define the controller.
+
+#### Top-Level Constants
+
+On the contrary, if `ApplicationController` is unknown, the constant is
+considered missing and an autoload is going to be attempted by Rails.
+
+In order to load `ApplicationController`, Rails iterates over `autoload_paths`.
+First it checks if `app/assets/application_controller.rb` exists. If it does not,
+which is normally the case, it continues and finds
+`app/controllers/application_controller.rb`.
+
+If the file defines the constant `ApplicationController` all is fine, otherwise
+`LoadError` is raised:
+
+```
+unable to autoload constant ApplicationController, expected
+<full path to application_controller.rb> to define it (LoadError)
+```
+
+INFO. Rails does not require the value of autoloaded constants to be a class or
+module object. For example, if the file `app/models/max_clients.rb` defines
+`MAX_CLIENTS = 100` autoloading `MAX_CLIENTS` works just fine.
+
+#### Namespaces
+
+Autoloading `ApplicationController` looks directly under the directories of
+`autoload_paths` because the nesting in that spot is empty. The situation of
+`Post` is different, the nesting in that line is `[PostsController]` and support
+for namespaces comes into play.
+
+The basic idea is that given
+
+```ruby
+module Admin
+ class BaseController < ApplicationController
+ @@all_roles = Role.all
+ end
+end
+```
+
+to autoload `Role` we are going to check if it is defined in the current or
+parent namespaces, one at a time. So, conceptually we want to try to autoload
+any of
+
+```
+Admin::BaseController::Role
+Admin::Role
+Role
+```
+
+in that order. That's the idea. To do so, Rails looks in `autoload_paths`
+respectively for file names like these:
+
+```
+admin/base_controller/role.rb
+admin/role.rb
+role.rb
+```
+
+modulus some additional directory lookups we are going to cover soon.
+
+INFO. `'Constant::Name'.underscore` gives the relative path without extension of
+the file name where `Constant::Name` is expected to be defined.
+
+Let's see how Rails autoloads the `Post` constant in the `PostsController`
+above assuming the application has a `Post` model defined in
+`app/models/post.rb`.
+
+First it checks for `posts_controller/post.rb` in `autoload_paths`:
+
+```
+app/assets/posts_controller/post.rb
+app/controllers/posts_controller/post.rb
+app/helpers/posts_controller/post.rb
+...
+test/mailers/previews/posts_controller/post.rb
+```
+
+Since the lookup is exhausted without success, a similar search for a directory
+is performed, we are going to see why in the [next section](#automatic-modules):
+
+```
+app/assets/posts_controller/post
+app/controllers/posts_controller/post
+app/helpers/posts_controller/post
+...
+test/mailers/previews/posts_controller/post
+```
+
+If all those attempts fail, then Rails starts the lookup again in the parent
+namespace. In this case only the top-level remains:
+
+```
+app/assets/post.rb
+app/controllers/post.rb
+app/helpers/post.rb
+app/mailers/post.rb
+app/models/post.rb
+```
+
+A matching file is found in `app/models/post.rb`. The lookup stops there and the
+file is loaded. If the file actually defines `Post` all is fine, otherwise
+`LoadError` is raised.
+
+### Qualified References
+
+When a qualified constant is missing Rails does not look for it in the parent
+namespaces. But there is a caveat: when a constant is missing, Rails is
+unable to tell if the trigger was a relative reference or a qualified one.
+
+For example, consider
+
+```ruby
+module Admin
+ User
+end
+```
+
+and
+
+```ruby
+Admin::User
+```
+
+If `User` is missing, in either case all Rails knows is that a constant called
+"User" was missing in a module called "Admin".
+
+If there is a top-level `User` Ruby would resolve it in the former example, but
+wouldn't in the latter. In general, Rails does not emulate the Ruby constant
+resolution algorithms, but in this case it tries using the following heuristic:
+
+> If none of the parent namespaces of the class or module has the missing
+> constant then Rails assumes the reference is relative. Otherwise qualified.
+
+For example, if this code triggers autoloading
+
+```ruby
+Admin::User
+```
+
+and the `User` constant is already present in `Object`, it is not possible that
+the situation is
+
+```ruby
+module Admin
+ User
+end
+```
+
+because otherwise Ruby would have resolved `User` and no autoloading would have
+been triggered in the first place. Thus, Rails assumes a qualified reference and
+considers the file `admin/user.rb` and directory `admin/user` to be the only
+valid options.
+
+In practice, this works quite well as long as the nesting matches all parent
+namespaces respectively and the constants that make the rule apply are known at
+that time.
+
+However, autoloading happens on demand. If by chance the top-level `User` was
+not yet loaded, then Rails assumes a relative reference by contract.
+
+Naming conflicts of this kind are rare in practice, but if one occurs,
+`require_dependency` provides a solution by ensuring that the constant needed
+to trigger the heuristic is defined in the conflicting place.
+
+### Automatic Modules
+
+When a module acts as a namespace, Rails does not require the application to
+define a file for it, a directory matching the namespace is enough.
+
+Suppose an application has a back office whose controllers are stored in
+`app/controllers/admin`. If the `Admin` module is not yet loaded when
+`Admin::UsersController` is hit, Rails needs first to autoload the constant
+`Admin`.
+
+If `autoload_paths` has a file called `admin.rb` Rails is going to load that
+one, but if there's no such file and a directory called `admin` is found, Rails
+creates an empty module and assigns it to the `Admin` constant on the fly.
+
+### Generic Procedure
+
+Relative references are reported to be missing in the cref where they were hit,
+and qualified references are reported to be missing in their parent (see
+[Resolution Algorithm for Relative
+Constants](#resolution-algorithm-for-relative-constants) at the beginning of
+this guide for the definition of *cref*, and [Resolution Algorithm for Qualified
+Constants](#resolution-algorithm-for-qualified-constants) for the definition of
+*parent*).
+
+The procedure to autoload constant `C` in an arbitrary situation is as follows:
+
+```
+if the class or module in which C is missing is Object
+ let ns = ''
+else
+ let M = the class or module in which C is missing
+
+ if M is anonymous
+ let ns = ''
+ else
+ let ns = M.name
+ end
+end
+
+loop do
+ # Look for a regular file.
+ for dir in autoload_paths
+ if the file "#{dir}/#{ns.underscore}/c.rb" exists
+ load/require "#{dir}/#{ns.underscore}/c.rb"
+
+ if C is now defined
+ return
+ else
+ raise LoadError
+ end
+ end
+ end
+
+ # Look for an automatic module.
+ for dir in autoload_paths
+ if the directory "#{dir}/#{ns.underscore}/c" exists
+ if ns is an empty string
+ let C = Module.new in Object and return
+ else
+ let C = Module.new in ns.constantize and return
+ end
+ end
+ end
+
+ if ns is empty
+ # We reached the top-level without finding the constant.
+ raise NameError
+ else
+ if C exists in any of the parent namespaces
+ # Qualified constants heuristic.
+ raise NameError
+ else
+ # Try again in the parent namespace.
+ let ns = the parent namespace of ns and retry
+ end
+ end
+end
+```
+
+
+require_dependency
+------------------
+
+Constant autoloading is triggered on demand and therefore code that uses a
+certain constant may have it already defined or may trigger an autoload. That
+depends on the execution path and it may vary between runs.
+
+There are times, however, in which you want to make sure a certain constant is
+known when the execution reaches some code. `require_dependency` provides a way
+to load a file using the current [loading mechanism](#loading-mechanism), and
+keeping track of constants defined in that file as if they were autoloaded to
+have them reloaded as needed.
+
+`require_dependency` is rarely needed, but see a couple of use cases in
+[Autoloading and STI](#autoloading-and-sti) and [When Constants aren't
+Triggered](#when-constants-aren-t-missed).
+
+WARNING. Unlike autoloading, `require_dependency` does not expect the file to
+define any particular constant. Exploiting this behavior would be a bad practice
+though, file and constant paths should match.
+
+
+Constant Reloading
+------------------
+
+When `config.cache_classes` is false Rails is able to reload autoloaded
+constants.
+
+For example, if you're in a console session and edit some file behind the
+scenes, the code can be reloaded with the `reload!` command:
+
+```
+> reload!
+```
+
+When the application runs, code is reloaded when something relevant to this
+logic changes. In order to do that, Rails monitors a number of things:
+
+* `config/routes.rb`.
+
+* Locales.
+
+* Ruby files under `autoload_paths`.
+
+* `db/schema.rb` and `db/structure.sql`.
+
+If anything in there changes, there is a middleware that detects it and reloads
+the code.
+
+Autoloading keeps track of autoloaded constants. Reloading is implemented by
+removing them all from their respective classes and modules using
+`Module#remove_const`. That way, when the code goes on, those constants are
+going to be unknown again, and files reloaded on demand.
+
+INFO. This is an all-or-nothing operation, Rails does not attempt to reload only
+what changed since dependencies between classes makes that really tricky.
+Instead, everything is wiped.
+
+Common Gotchas
+--------------
+
+### Nesting and Qualified Constants
+
+Let's consider
+
+```ruby
+module Admin
+ class UsersController < ApplicationController
+ def index
+ @users = User.all
+ end
+ end
+end
+```
+
+and
+
+```ruby
+class Admin::UsersController < ApplicationController
+ def index
+ @users = User.all
+ end
+end
+```
+
+To resolve `User` Ruby checks `Admin` in the former case, but it does not in
+the latter because it does not belong to the nesting (see [Nesting](#nesting)
+and [Resolution Algorithms](#resolution-algorithms)).
+
+Unfortunately Rails autoloading does not know the nesting in the spot where the
+constant was missing and so it is not able to act as Ruby would. In particular,
+`Admin::User` will get autoloaded in either case.
+
+Albeit qualified constants with `class` and `module` keywords may technically
+work with autoloading in some cases, it is preferable to use relative constants
+instead:
+
+```ruby
+module Admin
+ class UsersController < ApplicationController
+ def index
+ @users = User.all
+ end
+ end
+end
+```
+
+### Autoloading and STI
+
+Single Table Inheritance (STI) is a feature of Active Record that enables
+storing a hierarchy of models in one single table. The API of such models is
+aware of the hierarchy and encapsulates some common needs. For example, given
+these classes:
+
+```ruby
+# app/models/polygon.rb
+class Polygon < ApplicationRecord
+end
+
+# app/models/triangle.rb
+class Triangle < Polygon
+end
+
+# app/models/rectangle.rb
+class Rectangle < Polygon
+end
+```
+
+`Triangle.create` creates a row that represents a triangle, and
+`Rectangle.create` creates a row that represents a rectangle. If `id` is the
+ID of an existing record, `Polygon.find(id)` returns an object of the correct
+type.
+
+Methods that operate on collections are also aware of the hierarchy. For
+example, `Polygon.all` returns all the records of the table, because all
+rectangles and triangles are polygons. Active Record takes care of returning
+instances of their corresponding class in the result set.
+
+Types are autoloaded as needed. For example, if `Polygon.first` is a rectangle
+and `Rectangle` has not yet been loaded, Active Record autoloads it and the
+record is correctly instantiated.
+
+All good, but if instead of performing queries based on the root class we need
+to work on some subclass, things get interesting.
+
+While working with `Polygon` you do not need to be aware of all its descendants,
+because anything in the table is by definition a polygon, but when working with
+subclasses Active Record needs to be able to enumerate the types it is looking
+for. Let's see an example.
+
+`Rectangle.all` only loads rectangles by adding a type constraint to the query:
+
+```sql
+SELECT "polygons".* FROM "polygons"
+WHERE "polygons"."type" IN ("Rectangle")
+```
+
+Let's introduce now a subclass of `Rectangle`:
+
+```ruby
+# app/models/square.rb
+class Square < Rectangle
+end
+```
+
+`Rectangle.all` should now return rectangles **and** squares:
+
+```sql
+SELECT "polygons".* FROM "polygons"
+WHERE "polygons"."type" IN ("Rectangle", "Square")
+```
+
+But there's a caveat here: How does Active Record know that the class `Square`
+exists at all?
+
+Even if the file `app/models/square.rb` exists and defines the `Square` class,
+if no code yet used that class, `Rectangle.all` issues the query
+
+```sql
+SELECT "polygons".* FROM "polygons"
+WHERE "polygons"."type" IN ("Rectangle")
+```
+
+That is not a bug, the query includes all *known* descendants of `Rectangle`.
+
+A way to ensure this works correctly regardless of the order of execution is to
+manually load the direct subclasses at the bottom of the file that defines each
+intermediate class:
+
+```ruby
+# app/models/rectangle.rb
+class Rectangle < Polygon
+end
+require_dependency 'square'
+```
+
+This needs to happen for every intermediate (non-root and non-leaf) class. The
+root class does not scope the query by type, and therefore does not necessarily
+have to know all its descendants.
+
+### Autoloading and `require`
+
+Files defining constants to be autoloaded should never be `require`d:
+
+```ruby
+require 'user' # DO NOT DO THIS
+
+class UsersController < ApplicationController
+ ...
+end
+```
+
+There are two possible gotchas here in development mode:
+
+1. If `User` is autoloaded before reaching the `require`, `app/models/user.rb`
+runs again because `load` does not update `$LOADED_FEATURES`.
+
+2. If the `require` runs first Rails does not mark `User` as an autoloaded
+constant and changes to `app/models/user.rb` aren't reloaded.
+
+Just follow the flow and use constant autoloading always, never mix
+autoloading and `require`. As a last resort, if some file absolutely needs to
+load a certain file use `require_dependency` to play nice with constant
+autoloading. This option is rarely needed in practice, though.
+
+Of course, using `require` in autoloaded files to load ordinary 3rd party
+libraries is fine, and Rails is able to distinguish their constants, they are
+not marked as autoloaded.
+
+### Autoloading and Initializers
+
+Consider this assignment in `config/initializers/set_auth_service.rb`:
+
+```ruby
+AUTH_SERVICE = if Rails.env.production?
+ RealAuthService
+else
+ MockedAuthService
+end
+```
+
+The purpose of this setup would be that the application uses the class that
+corresponds to the environment via `AUTH_SERVICE`. In development mode
+`MockedAuthService` gets autoloaded when the initializer runs. Let's suppose
+we do some requests, change its implementation, and hit the application again.
+To our surprise the changes are not reflected. Why?
+
+As [we saw earlier](#constant-reloading), Rails removes autoloaded constants,
+but `AUTH_SERVICE` stores the original class object. Stale, non-reachable
+using the original constant, but perfectly functional.
+
+The following code summarizes the situation:
+
+```ruby
+class C
+ def quack
+ 'quack!'
+ end
+end
+
+X = C
+Object.instance_eval { remove_const(:C) }
+X.new.quack # => quack!
+X.name # => C
+C # => uninitialized constant C (NameError)
+```
+
+Because of that, it is not a good idea to autoload constants on application
+initialization.
+
+In the case above we could implement a dynamic access point:
+
+```ruby
+# app/models/auth_service.rb
+class AuthService
+ if Rails.env.production?
+ def self.instance
+ RealAuthService
+ end
+ else
+ def self.instance
+ MockedAuthService
+ end
+ end
+end
+```
+
+and have the application use `AuthService.instance` instead. `AuthService`
+would be loaded on demand and be autoload-friendly.
+
+### `require_dependency` and Initializers
+
+As we saw before, `require_dependency` loads files in an autoloading-friendly
+way. Normally, though, such a call does not make sense in an initializer.
+
+One could think about doing some [`require_dependency`](#require-dependency)
+calls in an initializer to make sure certain constants are loaded upfront, for
+example as an attempt to address the [gotcha with STIs](#autoloading-and-sti).
+
+Problem is, in development mode [autoloaded constants are wiped](#constant-reloading)
+if there is any relevant change in the file system. If that happens then
+we are in the very same situation the initializer wanted to avoid!
+
+Calls to `require_dependency` have to be strategically written in autoloaded
+spots.
+
+### When Constants aren't Missed
+
+#### Relative References
+
+Let's consider a flight simulator. The application has a default flight model
+
+```ruby
+# app/models/flight_model.rb
+class FlightModel
+end
+```
+
+that can be overridden by each airplane, for instance
+
+```ruby
+# app/models/bell_x1/flight_model.rb
+module BellX1
+ class FlightModel < FlightModel
+ end
+end
+
+# app/models/bell_x1/aircraft.rb
+module BellX1
+ class Aircraft
+ def initialize
+ @flight_model = FlightModel.new
+ end
+ end
+end
+```
+
+The initializer wants to create a `BellX1::FlightModel` and nesting has
+`BellX1`, that looks good. But if the default flight model is loaded and the
+one for the Bell-X1 is not, the interpreter is able to resolve the top-level
+`FlightModel` and autoloading is thus not triggered for `BellX1::FlightModel`.
+
+That code depends on the execution path.
+
+These kind of ambiguities can often be resolved using qualified constants:
+
+```ruby
+module BellX1
+ class Plane
+ def flight_model
+ @flight_model ||= BellX1::FlightModel.new
+ end
+ end
+end
+```
+
+Also, `require_dependency` is a solution:
+
+```ruby
+require_dependency 'bell_x1/flight_model'
+
+module BellX1
+ class Plane
+ def flight_model
+ @flight_model ||= FlightModel.new
+ end
+ end
+end
+```
+
+#### Qualified References
+
+WARNING. This gotcha is only possible in Ruby < 2.5.
+
+Given
+
+```ruby
+# app/models/hotel.rb
+class Hotel
+end
+
+# app/models/image.rb
+class Image
+end
+
+# app/models/hotel/image.rb
+class Hotel
+ class Image < Image
+ end
+end
+```
+
+the expression `Hotel::Image` is ambiguous because it depends on the execution
+path.
+
+As [we saw before](#resolution-algorithm-for-qualified-constants), Ruby looks
+up the constant in `Hotel` and its ancestors. If `app/models/image.rb` has
+been loaded but `app/models/hotel/image.rb` hasn't, Ruby does not find `Image`
+in `Hotel`, but it does in `Object`:
+
+```
+$ rails r 'Image; p Hotel::Image' 2>/dev/null
+Image # NOT Hotel::Image!
+```
+
+The code evaluating `Hotel::Image` needs to make sure
+`app/models/hotel/image.rb` has been loaded, possibly with
+`require_dependency`.
+
+In these cases the interpreter issues a warning though:
+
+```
+warning: toplevel constant Image referenced by Hotel::Image
+```
+
+This surprising constant resolution can be observed with any qualifying class:
+
+```
+2.1.5 :001 > String::Array
+(irb):1: warning: toplevel constant Array referenced by String::Array
+ => Array
+```
+
+WARNING. To find this gotcha the qualifying namespace has to be a class,
+`Object` is not an ancestor of modules.
+
+### Autoloading within Singleton Classes
+
+Let's suppose we have these class definitions:
+
+```ruby
+# app/models/hotel/services.rb
+module Hotel
+ class Services
+ end
+end
+
+# app/models/hotel/geo_location.rb
+module Hotel
+ class GeoLocation
+ class << self
+ Services
+ end
+ end
+end
+```
+
+If `Hotel::Services` is known by the time `app/models/hotel/geo_location.rb`
+is being loaded, `Services` is resolved by Ruby because `Hotel` belongs to the
+nesting when the singleton class of `Hotel::GeoLocation` is opened.
+
+But if `Hotel::Services` is not known, Rails is not able to autoload it, the
+application raises `NameError`.
+
+The reason is that autoloading is triggered for the singleton class, which is
+anonymous, and as [we saw before](#generic-procedure), Rails only checks the
+top-level namespace in that edge case.
+
+An easy solution to this caveat is to qualify the constant:
+
+```ruby
+module Hotel
+ class GeoLocation
+ class << self
+ Hotel::Services
+ end
+ end
+end
+```
+
+### Autoloading in `BasicObject`
+
+Direct descendants of `BasicObject` do not have `Object` among their ancestors
+and cannot resolve top-level constants:
+
+```ruby
+class C < BasicObject
+ String # NameError: uninitialized constant C::String
+end
+```
+
+When autoloading is involved that plot has a twist. Let's consider:
+
+```ruby
+class C < BasicObject
+ def user
+ User # WRONG
+ end
+end
+```
+
+Since Rails checks the top-level namespace `User` gets autoloaded just fine the
+first time the `user` method is invoked. You only get the exception if the
+`User` constant is known at that point, in particular in a *second* call to
+`user`:
+
+```ruby
+c = C.new
+c.user # surprisingly fine, User
+c.user # NameError: uninitialized constant C::User
+```
+
+because it detects that a parent namespace already has the constant (see [Qualified
+References](#autoloading-algorithms-qualified-references)).
+
+As with pure Ruby, within the body of a direct descendant of `BasicObject` use
+always absolute constant paths:
+
+```ruby
+class C < BasicObject
+ ::String # RIGHT
+
+ def user
+ ::User # RIGHT
+ end
+end
+```
+
+### Autoloading in the Test Environment
+
+When configuring the `test` environment for autoloading you might consider multiple factors.
+
+For example it might be worth running your tests with an identical setup to production (`config.eager_load = true`, `config.cache_classes = true`) in order to catch any problems before they hit production (this is compensation for the lack of dev-prod parity). However this will slow down the boot time for individual tests on a dev machine (and is not immediately compatible with spring see below). So one possibility is to do this on a
+[CI](https://en.wikipedia.org/wiki/Continuous_integration) machine only (which should run without spring).
+
+On a development machine you can then have your tests running with whatever is fastest (ideally `config.eager_load = false`).
+
+With the [Spring](https://github.com/rails/spring) pre-loader (included with new Rails apps), you ideally keep `config.eager_load = false` as per development. Sometimes you may end up with a hybrid configuration (`config.eager_load = true`, `config.cache_classes = true` AND `config.enable_dependency_loading = true`), see [spring issue](https://github.com/rails/spring/issues/519#issuecomment-348324369). However it might be simpler to keep the same configuration as development, and work out whatever it is that is causing autoloading to fail (perhaps by the results of your CI test results).
+
+Occasionally you may need to explicitly eager_load by using `Rails
+.application.eager_load!` in the setup of your tests -- this might occur if your [tests involve multithreading](https://stackoverflow.com/questions/25796409/in-rails-how-can-i-eager-load-all-code-before-a-specific-rspec-test).
+
+## Troubleshooting
+
+### Tracing Autoloads
+
+Active Support is able to report constants as they are autoloaded. To enable these traces in a Rails application, put the following two lines in some initializer:
+
+```ruby
+ActiveSupport::Dependencies.logger = Rails.logger
+ActiveSupport::Dependencies.verbose = true
+```
+
+### Where is a Given Autoload Triggered?
+
+If constant `Foo` is being autoloaded, and you'd like to know where is that autoload coming from, just throw
+
+```ruby
+puts caller
+```
+
+at the top of `foo.rb` and inspect the printed stack trace.
+
+### Which Constants Have Been Autoloaded?
+
+At any given time,
+
+```ruby
+ActiveSupport::Dependencies.autoloaded_constants
+```
+
+has the collection of constants that have been autoloaded so far.
diff --git a/guides/source/command_line.md b/guides/source/command_line.md
index 0091271363..60d0de17bc 100644
--- a/guides/source/command_line.md
+++ b/guides/source/command_line.md
@@ -92,6 +92,28 @@ $ rails new commandsapp
Rails will set you up with what seems like a huge amount of stuff for such a tiny command! You've got the entire Rails directory structure now with all the code you need to run our simple application right out of the box.
+If you wish to skip some files or components from being generated, you can append the following arguments to your `rails new` command:
+
+| Argument | Description |
+| ----------------------- | ----------------------------------------------------------- |
+| `--skip-gemfile` | Don't create a Gemfile |
+| `--skip-git` | Skip .gitignore file |
+| `--skip-keeps` | Skip source control .keep files |
+| `--skip-action-mailer` | Skip Action Mailer files |
+| `--skip-action-text` | Skip Action Text gem |
+| `--skip-active-record` | Skip Active Record files |
+| `--skip-active-storage` | Skip Active Storage files |
+| `--skip-puma` | Skip Puma related files |
+| `--skip-action-cable` | Skip Action Cable files |
+| `--skip-sprockets` | Skip Sprockets files |
+| `--skip-spring` | Don't install Spring application preloader |
+| `--skip-listen` | Don't generate configuration that depends on the listen gem |
+| `--skip-javascript` | Skip JavaScript files |
+| `--skip-turbolinks` | Skip turbolinks gem |
+| `--skip-test` | Skip test files |
+| `--skip-system-test` | Skip system test files |
+| `--skip-bootsnap` | Skip bootsnap gem |
+
### `rails server`
The `rails server` command launches a web server named Puma which comes bundled with Rails. You'll use this any time you want to access your application through a web browser.
@@ -238,14 +260,17 @@ Usage:
...
-Active Record options:
- [--migration] # Indicates when to generate migration
- # Default: true
+ActiveRecord options:
+ [--migration], [--no-migration] # Indicates when to generate migration
+ # Default: true
...
Description:
- Create rails files for model generator.
+ Stubs out a new model. Pass the model name, either CamelCased or
+ under_scored, and an optional list of attribute pairs as arguments.
+
+...
```
NOTE: For a list of available field types for the `type` parameter, refer to the [API documentation](https://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html#method-i-add_column) for the add_column method for the `SchemaStatements` module. The `index` parameter generates a corresponding index for the column.
@@ -257,7 +282,7 @@ We will set up a simple resource called "HighScore" that will keep track of our
```bash
$ rails generate scaffold HighScore game:string score:integer
invoke active_record
- create db/migrate/20130717151933_create_high_scores.rb
+ create db/migrate/20190416145729_create_high_scores.rb
create app/models/high_score.rb
invoke test_unit
create test/models/high_score_test.rb
@@ -275,20 +300,19 @@ $ rails generate scaffold HighScore game:string score:integer
create app/views/high_scores/_form.html.erb
invoke test_unit
create test/controllers/high_scores_controller_test.rb
+ create test/system/high_scores_test.rb
invoke helper
create app/helpers/high_scores_helper.rb
+ invoke test_unit
invoke jbuilder
create app/views/high_scores/index.json.jbuilder
create app/views/high_scores/show.json.jbuilder
- invoke test_unit
- create test/system/high_scores_test.rb
+ create app/views/high_scores/_high_score.json.jbuilder
invoke assets
- invoke coffee
- create app/assets/javascripts/high_scores.coffee
invoke scss
create app/assets/stylesheets/high_scores.scss
invoke scss
- identical app/assets/stylesheets/scaffolds.scss
+ create app/assets/stylesheets/scaffolds.scss
```
The generator checks that there exist the directories for models, controllers, helpers, layouts, functional and unit tests, stylesheets, creates the views, controller, model and database migration for HighScore (creating the `high_scores` table and fields), takes care of the route for the **resource**, and new tests for everything.
@@ -577,6 +601,8 @@ The `tmp:` namespaced commands will help you clear and create the `Rails.root/tm
### Miscellaneous
+* `rails initializers` prints out all defined initializers in the order they are invoked by Rails.
+* `rails middleware` lists Rack middleware stack enabled for your app.
* `rails stats` is great for looking at statistics on your code, displaying things like KLOCs (thousands of lines of code) and your code to test ratio.
* `rails secret` will give you a pseudo-random key to use for your session secret.
* `rails time:zones:all` lists all the timezones Rails knows about.
diff --git a/guides/source/configuring.md b/guides/source/configuring.md
index e1c9fad232..c5d3d09bd0 100644
--- a/guides/source/configuring.md
+++ b/guides/source/configuring.md
@@ -64,10 +64,12 @@ These configuration methods are to be called on a `Rails::Railtie` object, such
* `config.autoload_paths` accepts an array of paths from which Rails will autoload constants. Default is all directories under `app`. It is no longer recommended to adjust this. See [Autoloading and Reloading Constants](autoloading_and_reloading_constants.html#autoload-paths-and-eager-load-paths)
+* `config.add_autoload_paths_to_load_path` says whether autoload paths have to be added to `$LOAD_PATH`. This flag is `true` by default, but it is recommended to be set to `false` in `:zeitwerk` mode early, in `config/application.rb`. Zeitwerk uses absolute paths internally, and applications running in `:zeitwerk` mode do not need `require_dependency`, so models, controllers, jobs, etc. do not need to be in `$LOAD_PATH`. Setting this to `false` saves Ruby from checking these directories when resolving `require` calls with relative paths, and saves Bootsnap work and RAM, since it does not need to build an index for them.
+
* `config.cache_classes` controls whether or not application classes and modules should be reloaded on each request. Defaults to `false` in development mode, and `true` in test and production modes.
* `config.beginning_of_week` sets the default beginning of week for the
-application. Accepts a valid week day symbol (e.g. `:monday`).
+application. Accepts a valid day of week as a symbol (e.g. `:monday`).
* `config.cache_store` configures which cache store to use for Rails caching. Options include one of the symbols `:memory_store`, `:file_store`, `:mem_cache_store`, `:null_store`, `:redis_cache_store`, or an object that implements the cache API. Defaults to `:file_store`.
@@ -143,7 +145,7 @@ defaults to `:debug` for all environments. The available log levels are: `:debug
* `secret_key_base` is used for specifying a key which allows sessions for the application to be verified against a known secure key to prevent tampering. Applications get a random generated key in test and development environments, other environments should set one in `config/credentials.yml.enc`.
-* `config.public_file_server.enabled` configures Rails to serve static files from the public directory. This option defaults to `true`, but in the production environment it is set to `false` because the server software (e.g. NGINX or Apache) used to run the application should serve static files instead. If you are running or testing your app in production mode using WEBrick (it is not recommended to use WEBrick in production) set the option to `true.` Otherwise, you won't be able to use page caching and request for files that exist under the public directory.
+* `config.public_file_server.enabled` configures Rails to serve static files from the public directory. This option defaults to `true`, but in the production environment it is set to `false` because the server software (e.g. NGINX or Apache) used to run the application should serve static files instead. If you are running or testing your app in production mode using WEBrick (it is not recommended to use WEBrick in production) set the option to `true`. Otherwise, you won't be able to use page caching and request for files that exist under the public directory.
* `config.session_store` specifies what class to use to store the session. Possible values are `:cookie_store` which is the default, `:mem_cache_store`, and `:disabled`. The last one tells Rails not to deal with sessions. Defaults to a cookie store with application name as the session key. Custom session stores can also be specified:
@@ -155,6 +157,13 @@ defaults to `:debug` for all environments. The available log levels are: `:debug
* `config.time_zone` sets the default time zone for the application and enables time zone awareness for Active Record.
+* `config.autoloader` sets the autoloading mode. This option defaults to `:zeitwerk` if `6.0` is specified in `config.load_defaults`. Applications can still use the classic autoloader by setting this value to `:classic` after loading the framework defaults:
+
+ ```ruby
+ config.load_defaults "6.0"
+ config.autoloader = :classic
+ ```
+
### Configuring Assets
* `config.assets.enabled` a flag that controls whether the asset
@@ -344,7 +353,7 @@ All these configuration options are delegated to the `I18n` library.
* `config.active_record.lock_optimistically` controls whether Active Record will use optimistic locking and is `true` by default.
-* `config.active_record.cache_timestamp_format` controls the format of the timestamp value in the cache key. Default is `:nsec`.
+* `config.active_record.cache_timestamp_format` controls the format of the timestamp value in the cache key. Default is `:usec`.
* `config.active_record.record_timestamps` is a boolean value which controls whether or not timestamping of `create` and `update` operations on a model occur. The default value is `true`.
@@ -381,6 +390,12 @@ All these configuration options are delegated to the `I18n` library.
having to send a query to the database to get this information.
Defaults to `true`.
+* `config.active_record.collection_cache_versioning` enables the same cache key
+ to be reused when the object being cached of type `ActiveRecord::Relation`
+ changes by moving the volatile information (max updated at and count) of
+ the relation's cache key into the cache version to support recycling cache key.
+ Defaults to `false`.
+
The MySQL adapter adds one additional configuration option:
* `ActiveRecord::ConnectionAdapters::Mysql2Adapter.emulate_booleans` controls whether Active Record will consider all `tinyint(1)` columns as booleans. Defaults to `true`.
@@ -409,7 +424,7 @@ The schema dumper adds two additional configuration options:
* `config.action_controller.asset_host` sets the host for the assets. Useful when CDNs are used for hosting assets rather than the application server itself.
-* `config.action_controller.perform_caching` configures whether the application should perform the caching features provided by the Action Controller component or not. Set to `false` in development mode, `true` in production.
+* `config.action_controller.perform_caching` configures whether the application should perform the caching features provided by the Action Controller component or not. Set to `false` in development mode, `true` in production. If it's not specified, the default will be `true`.
* `config.action_controller.default_static_extension` configures the extension used for cached pages. Defaults to `.html`.
@@ -538,6 +553,10 @@ Defaults to `'signed cookie'`.
Any exceptions that are not configured will be mapped to 500 Internal Server Error.
+* `config.action_dispatch.return_only_media_type_on_content_type` change the
+ return value of `ActionDispatch::Response#content_type` to the Content-Type
+ header without modification. Defaults to `false`.
+
* `ActionDispatch::Callbacks.before` takes a block of code to run before the request.
* `ActionDispatch::Callbacks.after` takes a block of code to run after the request.
@@ -592,7 +611,7 @@ Defaults to `'signed cookie'`.
* `config.action_view.form_with_generates_remote_forms` determines whether `form_with` generates remote forms or not. This defaults to `true`.
-* `config.action_view.form_with_generates_ids` determines whether `form_with` generates ids on inputs. This defaults to `true`.
+* `config.action_view.form_with_generates_ids` determines whether `form_with` generates ids on inputs. This defaults to `false`.
* `config.action_view.default_enforce_utf8` determines whether forms are generated with a hidden tag that forces older versions of Internet Explorer to submit forms encoded in UTF-8. This defaults to `false`.
@@ -696,7 +715,7 @@ There are a number of settings available on `config.action_mailer`:
* `config.action_mailer.deliver_later_queue_name` specifies the queue name for
mailers. By default this is `mailers`.
-* `config.action_mailer.perform_caching` specifies whether the mailer templates should perform fragment caching or not. By default this is `false` in all environments.
+* `config.action_mailer.perform_caching` specifies whether the mailer templates should perform fragment caching or not. If it's not specified, the default will be `true`.
* `config.action_mailer.delivery_job` specifies delivery job for mail. Defaults to `ActionMailer::DeliveryJob`.
@@ -820,10 +839,12 @@ You can find more detailed configuration options in the
config.active_storage.paths[:ffprobe] = '/usr/local/bin/ffprobe'
```
-* `config.active_storage.variable_content_types` accepts an array of strings indicating the content types that Active Storage can transform through ImageMagick. The default is `%w(image/png image/gif image/jpg image/jpeg image/pjpeg image/tiff image/vnd.adobe.photoshop image/vnd.microsoft.icon)`.
+* `config.active_storage.variable_content_types` accepts an array of strings indicating the content types that Active Storage can transform through ImageMagick. The default is `%w(image/png image/gif image/jpg image/jpeg image/pjpeg image/tiff image/bmp image/vnd.adobe.photoshop image/vnd.microsoft.icon)`.
* `config.active_storage.content_types_to_serve_as_binary` accepts an array of strings indicating the content types that Active Storage will always serve as an attachment, rather than inline. The default is `%w(text/html
-text/javascript image/svg+xml application/postscript application/x-shockwave-flash text/xml application/xml application/xhtml+xml)`.
+text/javascript image/svg+xml application/postscript application/x-shockwave-flash text/xml application/xml application/xhtml+xml application/mathml+xml text/cache-manifest)`.
+
+* `config.active_storage.content_types_allowed_inline` accepts an array of strings indicating the content types that Active Storage allows to serve as inline. The default is `%w(image/png image/gif image/jpg image/jpeg image/vnd.adobe.photoshop image/vnd.microsoft.icon application/pdf)`.
* `config.active_storage.queues.analysis` accepts a symbol indicating the Active Job queue to use for analysis jobs. When this option is `nil`, analysis jobs are sent to the default Active Job queue (see `config.active_job.default_queue_name`).
@@ -837,6 +858,12 @@ text/javascript image/svg+xml application/postscript application/x-shockwave-fla
config.active_storage.queues.purge = :low_priority
```
+* `config.active_storage.queues.mirror` accepts a symbol indicating the Active Job queue to use for direct upload mirroring jobs. The default is `:active_storage_mirror`.
+
+ ```ruby
+ config.active_storage.queues.mirror = :low_priority
+ ```
+
* `config.active_storage.logger` can be used to set the logger used by Active Storage. Accepts a logger conforming to the interface of Log4r or the default Ruby Logger class.
```ruby
@@ -856,7 +883,11 @@ text/javascript image/svg+xml application/postscript application/x-shockwave-fla
config.active_storage.routes_prefix = '/files'
```
- The default is `/rails/active_storage`
+ The default is `/rails/active_storage`.
+
+* `config.active_storage.replace_on_assign_to_many` determines whether assigning to a collection of attachments declared with `has_many_attached` replaces any existing attachments or appends to them. The default is `true`.
+
+* `config.active_storage.draw_routes` can be used to toggle Active Storage route generation. The default is `true`.
### Results of `load_defaults`
@@ -876,7 +907,7 @@ text/javascript image/svg+xml application/postscript application/x-shockwave-fla
#### With '5.2':
- `config.active_record.cache_versioning`: `true`
-- `action_dispatch.use_authenticated_cookie_encryption`: `true`
+- `config.action_dispatch.use_authenticated_cookie_encryption`: `true`
- `config.active_support.use_authenticated_message_encryption`: `true`
- `config.active_support.use_sha1_digests`: `true`
- `config.action_controller.default_protect_from_forgery`: `true`
@@ -884,12 +915,16 @@ text/javascript image/svg+xml application/postscript application/x-shockwave-fla
#### With '6.0':
+- `config.autoloader`: `:zeitwerk`
- `config.action_view.default_enforce_utf8`: `false`
- `config.action_dispatch.use_cookies_with_metadata`: `true`
+- `config.action_dispatch.return_only_media_type_on_content_type`: `false`
- `config.action_mailer.delivery_job`: `"ActionMailer::MailDeliveryJob"`
- `config.active_job.return_false_on_aborted_enqueue`: `true`
- `config.active_storage.queues.analysis`: `:active_storage_analysis`
- `config.active_storage.queues.purge`: `:active_storage_purge`
+- `config.active_storage.replace_on_assign_to_many`: `true`
+- `config.active_record.collection_cache_versioning`: `true`
### Configuring a Database
@@ -1513,7 +1548,7 @@ Disallow: /
```
To block just specific pages, it's necessary to use a more complex syntax. Learn
-it on the [official documentation](http://www.robotstxt.org/robotstxt.html).
+it on the [official documentation](https://www.robotstxt.org/robotstxt.html).
Evented File System Monitor
---------------------------
diff --git a/guides/source/contributing_to_ruby_on_rails.md b/guides/source/contributing_to_ruby_on_rails.md
index c33d523c0e..9c28ff6a9c 100644
--- a/guides/source/contributing_to_ruby_on_rails.md
+++ b/guides/source/contributing_to_ruby_on_rails.md
@@ -13,7 +13,7 @@ After reading this guide, you will know:
* How to contribute to the Ruby on Rails documentation.
* How to contribute to the Ruby on Rails code.
-Ruby on Rails is not "someone else's framework." Over the years, thousands of people have contributed to Ruby on Rails ranging from a single character to massive architectural changes or significant documentation - all with the goal of making Ruby on Rails better for everyone. Even if you don't feel up to writing code or documentation yet, there are a variety of other ways that you can contribute, from reporting issues to testing patches.
+Ruby on Rails is not "someone else's framework". Over the years, thousands of people have contributed to Ruby on Rails ranging from a single character to massive architectural changes or significant documentation - all with the goal of making Ruby on Rails better for everyone. Even if you don't feel up to writing code or documentation yet, there are a variety of other ways that you can contribute, from reporting issues to testing patches.
As mentioned in [Rails'
README](https://github.com/rails/rails/blob/master/README.md), everyone interacting in Rails and its sub-projects' codebases, issue trackers, chat rooms, and mailing lists is expected to follow the Rails [code of conduct](https://rubyonrails.org/conduct/).
@@ -76,7 +76,7 @@ a patch, please send an email to the [rails-core mailing
list](https://groups.google.com/forum/?fromgroups#!forum/rubyonrails-core). You
might get no response, which means that everyone is indifferent. You might find
someone who's also interested in building that feature. You might get a "This
-won't be accepted." But it's the proper place to discuss new ideas. GitHub
+won't be accepted". But it's the proper place to discuss new ideas. GitHub
Issues are not a particularly good venue for the sometimes long and involved
discussions new features require.
@@ -139,7 +139,7 @@ changes to the master branch.
When working with documentation, please take into account the [API Documentation Guidelines](api_documentation_guidelines.html) and the [Ruby on Rails Guides Guidelines](ruby_on_rails_guides_guidelines.html).
-NOTE: To help our CI servers you should add [ci skip] to your documentation commit message to skip build on that commit. Please remember to use it for commits containing only documentation changes.
+NOTE: For documentation changes, your commit message should include [ci skip]. This will skip running the test suite, helping us to cut down on our server costs. Keep in mind that you should only skip CI when your change touches documentation exclusively.
Translating Rails Guides
------------------------
@@ -247,7 +247,7 @@ Rails follows a simple set of coding style conventions:
* Two spaces, no tabs (for indentation).
* No trailing whitespace. Blank lines should not have any spaces.
-* Indent after private/protected.
+* Indent and no blank line after private/protected.
* Use Ruby >= 1.9 syntax for hashes. Prefer `{ a: :b }` over `{ :a => :b }`.
* Prefer `&&`/`||` over `and`/`or`.
* Prefer class << self over self.method for class methods.
@@ -340,7 +340,7 @@ $ TEST_DIR=generators bundle exec rake test
You can run the tests for a particular file by using:
```bash
-$ cd actionpack
+$ cd actionview
$ bundle exec ruby -w -Itest test/template/form_helper_test.rb
```
diff --git a/guides/source/documents.yaml b/guides/source/documents.yaml
index 1e67b2bce7..da11236064 100644
--- a/guides/source/documents.yaml
+++ b/guides/source/documents.yaml
@@ -139,9 +139,13 @@
url: initialization.html
description: This guide explains the internals of the Rails initialization process.
-
- name: Autoloading and Reloading Constants
+ name: Autoloading and Reloading Constants (Zeitwerk Mode)
url: autoloading_and_reloading_constants.html
- description: This guide documents how autoloading and reloading constants work.
+ description: This guide documents how autoloading and reloading constants work (Zeitwerk mode).
+ -
+ name: Autoloading and Reloading Constants (Classic Mode)
+ url: autoloading_and_reloading_constants_classic_mode.html
+ description: This guide documents how autoloading and reloading constants work (Classic mode).
-
name: "Caching with Rails: An Overview"
url: caching_with_rails.html
@@ -160,6 +164,11 @@
work_in_progress: true
url: active_record_postgresql.html
description: This guide covers PostgreSQL specific usage of Active Record.
+ -
+ name: Multiple Databases with Active Record
+ work_in_progress: true
+ url: active_record_multiple_databases.html
+ description: This guide covers using multiple databases in your application.
-
name: Extending Rails
@@ -193,7 +202,7 @@
-
name: Contributing to Ruby on Rails
url: contributing_to_ruby_on_rails.html
- description: Rails is not 'somebody else's framework.' This guide covers a variety of ways that you can get involved in the ongoing development of Rails.
+ description: Rails is not "someone else's framework". This guide covers a variety of ways that you can get involved in the ongoing development of Rails.
-
name: API Documentation Guidelines
url: api_documentation_guidelines.html
diff --git a/guides/source/engines.md b/guides/source/engines.md
index 3031c62928..8961a079b5 100644
--- a/guides/source/engines.md
+++ b/guides/source/engines.md
@@ -220,7 +220,7 @@ Inside the `app` directory are the standard `assets`, `controllers`, `helpers`,
`jobs`, `mailers`, `models`, and `views` directories that you should be familiar with
from an application. We'll look more into models in a future section, when we're writing the engine.
-Within the `app/assets` directory, there are the `images`, `javascripts` and
+Within the `app/assets` directory, there are the `images` and
`stylesheets` directories which, again, you should be familiar with due to their
similarity to an application. One difference here, however, is that each
directory contains a sub-directory with the engine name. Because this engine is
@@ -1511,34 +1511,35 @@ These are the hooks you can use in your own code.
To hook into the initialization process of one of the following classes use the available hook.
-| Class | Available Hooks |
-| --------------------------------- | ------------------------------------ |
-| `ActionCable` | `action_cable` |
-| `ActionCable::Channel::Base` | `action_cable_channel` |
-| `ActionCable::Connection::Base` | `action_cable_connection` |
-| `ActionController::API` | `action_controller_api` |
-| `ActionController::API` | `action_controller` |
-| `ActionController::Base` | `action_controller_base` |
-| `ActionController::Base` | `action_controller` |
-| `ActionController::TestCase` | `action_controller_test_case` |
-| `ActionDispatch::IntegrationTest` | `action_dispatch_integration_test` |
-| `ActionDispatch::SystemTestCase` | `action_dispatch_system_test_case` |
-| `ActionMailbox::Base` | `action_mailbox` |
-| `ActionMailbox::InboundEmail` | `action_mailbox_inbound_email` |
-| `ActionMailbox::TestCase` | `action_mailbox_test_case` |
-| `ActionMailer::Base` | `action_mailer` |
-| `ActionMailer::TestCase` | `action_mailer_test_case` |
-| `ActionText::Content` | `action_text_content` |
-| `ActionText::RichText` | `action_text_rich_text` |
-| `ActionView::Base` | `action_view` |
-| `ActionView::TestCase` | `action_view_test_case` |
-| `ActiveJob::Base` | `active_job` |
-| `ActiveJob::TestCase` | `active_job_test_case` |
-| `ActiveRecord::Base` | `active_record` |
-| `ActiveStorage::Attachment` | `active_storage_attachment` |
-| `ActiveStorage::Blob` | `active_storage_blob` |
-| `ActiveSupport::TestCase` | `active_support_test_case` |
-| `i18n` | `i18n` |
+| Class | Available Hooks |
+| -------------------------------------| ------------------------------------ |
+| `ActionCable` | `action_cable` |
+| `ActionCable::Channel::Base` | `action_cable_channel` |
+| `ActionCable::Connection::Base` | `action_cable_connection` |
+| `ActionCable::Connection::TestCase` | `action_cable_connection_test_case` |
+| `ActionController::API` | `action_controller_api` |
+| `ActionController::API` | `action_controller` |
+| `ActionController::Base` | `action_controller_base` |
+| `ActionController::Base` | `action_controller` |
+| `ActionController::TestCase` | `action_controller_test_case` |
+| `ActionDispatch::IntegrationTest` | `action_dispatch_integration_test` |
+| `ActionDispatch::SystemTestCase` | `action_dispatch_system_test_case` |
+| `ActionMailbox::Base` | `action_mailbox` |
+| `ActionMailbox::InboundEmail` | `action_mailbox_inbound_email` |
+| `ActionMailbox::TestCase` | `action_mailbox_test_case` |
+| `ActionMailer::Base` | `action_mailer` |
+| `ActionMailer::TestCase` | `action_mailer_test_case` |
+| `ActionText::Content` | `action_text_content` |
+| `ActionText::RichText` | `action_text_rich_text` |
+| `ActionView::Base` | `action_view` |
+| `ActionView::TestCase` | `action_view_test_case` |
+| `ActiveJob::Base` | `active_job` |
+| `ActiveJob::TestCase` | `active_job_test_case` |
+| `ActiveRecord::Base` | `active_record` |
+| `ActiveStorage::Attachment` | `active_storage_attachment` |
+| `ActiveStorage::Blob` | `active_storage_blob` |
+| `ActiveSupport::TestCase` | `active_support_test_case` |
+| `i18n` | `i18n` |
## Configuration hooks
diff --git a/guides/source/getting_started.md b/guides/source/getting_started.md
index e486c53fe3..ce45dbb2a7 100644
--- a/guides/source/getting_started.md
+++ b/guides/source/getting_started.md
@@ -28,7 +28,7 @@ curve diving straight into Rails. There are several curated lists of online reso
for learning Ruby:
* [Official Ruby Programming Language website](https://www.ruby-lang.org/en/documentation/)
-* [List of Free Programming Books](https://github.com/vhf/free-programming-books/blob/master/free-programming-books.md#ruby)
+* [List of Free Programming Books](https://github.com/EbookFoundation/free-programming-books/blob/master/free-programming-books.md#ruby)
Be aware that some resources, while still excellent, cover versions of Ruby as old as
1.6, and commonly 1.8, and will not include some syntax that you will see in day-to-day
@@ -55,7 +55,7 @@ The Rails philosophy includes two major guiding principles:
* **Don't Repeat Yourself:** DRY is a principle of software development which
states that "Every piece of knowledge must have a single, unambiguous, authoritative
- representation within a system." By not writing the same information over and over
+ representation within a system". By not writing the same information over and over
again, our code is more maintainable, more extensible, and less buggy.
* **Convention Over Configuration:** Rails has opinions about the best way to do many
things in a web application, and defaults to this set of conventions, rather than
@@ -126,7 +126,7 @@ run the following:
$ rails --version
```
-If it says something like "Rails 5.2.1", you are ready to continue.
+If it says something like "Rails 6.0.0", you are ready to continue.
### Creating the Blog Application
@@ -205,12 +205,10 @@ $ rails server
TIP: If you are using Windows, you have to pass the scripts under the `bin`
folder directly to the Ruby interpreter e.g. `ruby bin\rails server`.
-TIP: Compiling CoffeeScript and JavaScript asset compression requires you
+TIP: JavaScript asset compression requires you
have a JavaScript runtime available on your system, in the absence
-of a runtime you will see an `execjs` error during asset compilation.
+of a runtime you will see an `execjs` error during asset compression.
Usually macOS and Windows come with a JavaScript runtime installed.
-Rails adds the `mini_racer` gem to the generated `Gemfile` in a
-commented line for new apps and you can uncomment if you need it.
`therubyrhino` is the recommended runtime for JRuby users and is added by
default to the `Gemfile` in apps generated under JRuby. You can investigate
all the supported runtimes at [ExecJS](https://github.com/rails/execjs#readme).
@@ -1560,7 +1558,7 @@ class CreateComments < ActiveRecord::Migration[6.0]
create_table :comments do |t|
t.string :commenter
t.text :body
- t.references :article, foreign_key: true
+ t.references :article, null: false, foreign_key: true
t.timestamps
end
diff --git a/guides/source/i18n.md b/guides/source/i18n.md
index d6fccadb28..5d91a6df33 100644
--- a/guides/source/i18n.md
+++ b/guides/source/i18n.md
@@ -992,27 +992,6 @@ So, for example, instead of the default error message `"cannot be blank"` you co
| numericality | :odd | :odd | - |
| numericality | :even | :even | - |
-#### Translations for the Active Record `error_messages_for` Helper
-
-If you are using the Active Record `error_messages_for` helper, you will want to add
-translations for it.
-
-Rails ships with the following translations:
-
-```yaml
-en:
- activerecord:
- errors:
- template:
- header:
- one: "1 error prohibited this %{model} from being saved"
- other: "%{count} errors prohibited this %{model} from being saved"
- body: "There were problems with the following fields:"
-```
-
-NOTE: In order to use this helper, you need to install [DynamicForm](https://github.com/joelmoss/dynamic_form)
-gem by adding this line to your `Gemfile`: `gem 'dynamic_form'`.
-
### Translations for Action Mailer E-Mail Subjects
If you don't pass a subject to the `mail` method, Action Mailer will try to find
diff --git a/guides/source/initialization.md b/guides/source/initialization.md
index c41eae18cf..556c85cc0f 100644
--- a/guides/source/initialization.md
+++ b/guides/source/initialization.md
@@ -108,6 +108,8 @@ A standard Rails application depends on several gems, specifically:
* activerecord
* activestorage
* activesupport
+* actionmailbox
+* actiontext
* arel
* builder
* bundler
@@ -160,8 +162,8 @@ namespace and executes the command if found.
If Rails doesn't recognize the command, it hands the reins over to Rake
to run a task of the same name.
-As shown, `Rails::Command` displays the help output automatically if the `args`
-are empty.
+As shown, `Rails::Command` displays the help output automatically if the `namespace`
+is empty.
```ruby
module Rails::Command
@@ -289,7 +291,7 @@ def default_options
environment: (ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development").dup,
daemonize: false,
caching: nil,
- pid: Options::DEFAULT_PID_PATH,
+ pid: ENV.fetch("PIDFILE", Options::DEFAULT_PIDFILE).dup,
restart_cmd: restart_command)
end
```
@@ -538,6 +540,8 @@ require "rails"
action_mailer/railtie
active_job/railtie
action_cable/engine
+ action_mailbox/engine
+ action_text/engine
rails/test_unit/railtie
sprockets/railtie
).each do |railtie|
diff --git a/guides/source/layouts_and_rendering.md b/guides/source/layouts_and_rendering.md
index 2808527141..ce90a60e36 100644
--- a/guides/source/layouts_and_rendering.md
+++ b/guides/source/layouts_and_rendering.md
@@ -149,25 +149,6 @@ Rails knows that this view belongs to a different controller because of the embe
render template: "products/show"
```
-#### Rendering an Arbitrary File
-
-The `render` method can also use a view that's entirely outside of your application:
-
-```ruby
-render file: "/u/apps/warehouse_app/current/app/views/products/show"
-```
-
-The `:file` option takes an absolute file-system path. Of course, you need to have rights
-to the view that you're using to render the content.
-
-NOTE: Using the `:file` option in combination with users input can lead to security problems
-since an attacker could use this action to access security sensitive files in your file system.
-
-NOTE: By default, the file is rendered using the current layout.
-
-TIP: If you're running Rails on Microsoft Windows, you should use the `:file` option to
-render a file, because Windows filenames do not have the same format as Unix filenames.
-
#### Wrapping it up
The above three ways of rendering (rendering another template within the controller, rendering a template within another controller, and rendering an arbitrary file on the file system) are actually variants of the same action.
@@ -178,17 +159,9 @@ In fact, in the BooksController class, inside of the update action where we want
render :edit
render action: :edit
render "edit"
-render "edit.html.erb"
render action: "edit"
-render action: "edit.html.erb"
render "books/edit"
-render "books/edit.html.erb"
render template: "books/edit"
-render template: "books/edit.html.erb"
-render "/path/to/rails/app/views/books/edit"
-render "/path/to/rails/app/views/books/edit.html.erb"
-render file: "/path/to/rails/app/views/books/edit"
-render file: "/path/to/rails/app/views/books/edit.html.erb"
```
Which one you use is really a matter of style and convention, but the rule of thumb is to use the simplest one that makes sense for the code you are writing.
@@ -287,6 +260,23 @@ time.
NOTE: Unless overridden, your response returned from this render option will be
`text/plain`, as that is the default content type of Action Dispatch response.
+#### Rendering raw file
+
+Rails can render a raw file from an absolute path. This is useful for
+conditionally rendering static files like error pages.
+
+```ruby
+render file: "#{Rails.root}/public/404.html", layout: false
+```
+
+This renders the raw file (it doesn't support ERB or other handlers). By
+default it is rendered within the current layout.
+
+WARNING: Using the `:file` option in combination with users input can lead to security problems
+since an attacker could use this action to access security sensitive files in your file system.
+
+TIP: `send_file` is often a faster and better option if a layout isn't required.
+
#### Options for `render`
Calls to the `render` method generally accept five options:
@@ -303,7 +293,7 @@ Calls to the `render` method generally accept five options:
By default, Rails will serve the results of a rendering operation with the MIME content-type of `text/html` (or `application/json` if you use the `:json` option, or `application/xml` for the `:xml` option.). There are times when you might like to change this, and you can do so by setting the `:content_type` option:
```ruby
-render file: filename, content_type: "application/rss"
+render template: "feed", content_type: "application/rss"
```
##### The `:layout` Option
@@ -689,7 +679,7 @@ Just like the `:status` option for `render`, `:status` for `redirect_to` accepts
#### The Difference Between `render` and `redirect_to`
-Sometimes inexperienced developers think of `redirect_to` as a sort of `goto` command, moving execution from one place to another in your Rails code. This is _not_ correct. Your code stops running and waits for a new request for the browser. It just happens that you've told the browser what request it should make next, by sending back an HTTP 302 status code.
+Sometimes inexperienced developers think of `redirect_to` as a sort of `goto` command, moving execution from one place to another in your Rails code. This is _not_ correct. Your code stops running and waits for a new request from the browser. It just happens that you've told the browser what request it should make next, by sending back an HTTP 302 status code.
Consider these actions to see the difference:
diff --git a/guides/source/rails_application_templates.md b/guides/source/rails_application_templates.md
index 982df26987..e0e79fc41b 100644
--- a/guides/source/rails_application_templates.md
+++ b/guides/source/rails_application_templates.md
@@ -82,10 +82,10 @@ end
Adds the given source to the generated application's `Gemfile`.
-For example, if you need to source a gem from `"http://code.whytheluckystiff.net"`:
+For example, if you need to source a gem from `"http://gems.github.com"`:
```ruby
-add_source "http://code.whytheluckystiff.net"
+add_source "http://gems.github.com"
```
If block is given, gem entries in block are wrapped into the source group.
diff --git a/guides/source/routing.md b/guides/source/routing.md
index e3a6bbb138..4aeb9ee585 100644
--- a/guides/source/routing.md
+++ b/guides/source/routing.md
@@ -508,7 +508,7 @@ end
This will recognize `/photos/1/preview` with GET, and route to the `preview` action of `PhotosController`, with the resource id value passed in `params[:id]`. It will also create the `preview_photo_url` and `preview_photo_path` helpers.
-Within the block of member routes, each route name specifies the HTTP verb
+Within the block of member routes, each route name specifies the HTTP verb that
will be recognized. You can use `get`, `patch`, `put`, `post`, or `delete` here
. If you don't have multiple `member` routes, you can also pass `:on` to a
route, eliminating the block:
diff --git a/guides/source/security.md b/guides/source/security.md
index 22c122d4b9..5bb7a51524 100644
--- a/guides/source/security.md
+++ b/guides/source/security.md
@@ -1211,4 +1211,4 @@ The security landscape shifts and it is important to keep up to date, because mi
* Subscribe to the Rails security [mailing list](https://groups.google.com/forum/#!forum/rubyonrails-security).
* [Brakeman - Rails Security Scanner](https://brakemanscanner.org/) - To perform static security analysis for Rails applications.
* [Keep up to date on the other application layers](http://secunia.com/) (they have a weekly newsletter, too).
-* A [good security blog](https://www.owasp.org) including the [Cross-Site scripting Cheat Sheet](https://www.owasp.org/index.php/DOM_based_XSS_Prevention_Cheat_Sheet).
+* A [good security blog](https://www.owasp.org) including the [Cross-Site scripting Cheat Sheet](https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.md).
diff --git a/guides/source/testing.md b/guides/source/testing.md
index 1fad02812b..41bc54b924 100644
--- a/guides/source/testing.md
+++ b/guides/source/testing.md
@@ -781,7 +781,7 @@ This can be helpful for viewing the browser at the point a test failed, or
to view screenshots later for debugging.
Two methods are provided: `take_screenshot` and `take_failed_screenshot`.
-`take_failed_screenshot` is automatically included in `after_teardown` inside
+`take_failed_screenshot` is automatically included in `before_teardown` inside
Rails.
The `take_screenshot` helper method can be included anywhere in your tests to
@@ -1144,7 +1144,7 @@ test "ajax request" do
get article_url(article), xhr: true
assert_equal 'hello world', @response.body
- assert_equal "text/javascript", @response.content_type
+ assert_equal "text/javascript", @response.media_type
end
```
@@ -1212,7 +1212,7 @@ Let's start by adding this assertion to our `test_should_create_article` test:
```ruby
test "should create article" do
assert_difference('Article.count') do
- post article_url, params: { article: { title: 'Some title' } }
+ post articles_url, params: { article: { title: 'Some title' } }
end
assert_redirected_to article_path(Article.last)
@@ -1733,6 +1733,25 @@ class ProductTest < ActiveSupport::TestCase
end
```
+### Asserting Time Arguments in Jobs
+
+When serializing job arguments, `Time`, `DateTime`, and `ActiveSupport::TimeWithZone` lose microsecond precision. This means comparing deserialized time with actual time doesn't always work. To compensate for the loss of precision, `assert_enqueued_with` and `assert_performed_with` will remove microseconds from time objects in argument assertions.
+
+```ruby
+require 'test_helper'
+
+class ProductTest < ActiveSupport::TestCase
+ include ActiveJob::TestHelper
+
+ test 'that product is reserved at a given time' do
+ now = Time.now
+ assert_performed_with(job: ReservationJob, args: [product, now]) do
+ product.reserve(now)
+ end
+ end
+end
+```
+
Testing Action Cable
--------------------
diff --git a/guides/source/upgrading_ruby_on_rails.md b/guides/source/upgrading_ruby_on_rails.md
index 79bad8f4ed..05980d1614 100644
--- a/guides/source/upgrading_ruby_on_rails.md
+++ b/guides/source/upgrading_ruby_on_rails.md
@@ -85,30 +85,31 @@ Rails 6.1. You are encouraged to enable `config.force_ssl` to enforce HTTPS
connections throughout your application. If you need to exempt certain endpoints
from redirection, you can use `config.ssl_options` to configure that behavior.
-### Purpose in signed or encrypted cookie is now embedded in the cookies values
+### Purpose and expiry metadata is now embedded inside signed and encrypted cookies for increased security
-To improve security, Rails now embeds the purpose information in encrypted or signed cookies value.
-Rails can now thwart attacks that attempt to copy signed/encrypted value
+To improve security, Rails embeds the purpose and expiry metadata inside encrypted or signed cookies value.
+
+Rails can then thwart attacks that attempt to copy the signed/encrypted value
of a cookie and use it as the value of another cookie.
-This new embed information make those cookies incompatible with versions of Rails older than 6.0.
+This new embed metadata make those cookies incompatible with versions of Rails older than 6.0.
-If you require your cookies to be read by 5.2 and older, or you are still validating your 6.0 deploy and want
-to allow you to rollback set
+If you require your cookies to be read by Rails 5.2 and older, or you are still validating your 6.0 deploy and want
+to be able to rollback set
`Rails.application.config.action_dispatch.use_cookies_with_metadata` to `false`.
-### ActionCable javascript API Changes
+### Action Cable JavaScript API Changes
-The ActionCable javascript package has been converted from CoffeeScript
+The Action Cable JavaScript package has been converted from CoffeeScript
to ES2015, and we now publish the source code in the npm distribution.
-This change includes some breaking changes to optional parts of the
-ActionCable javascript API:
+This release includes some breaking changes to optional parts of the
+Action Cable JavaScript API:
- Configuration of the WebSocket adapter and logger adapter have been moved
from properties of `ActionCable` to properties of `ActionCable.adapters`.
- If you are currently configuring these adapters you will need to make
- these changes when upgrading:
+ If you are configuring these adapters you will need to make
+ these changes:
```diff
- ActionCable.WebSocket = MyWebSocket
@@ -121,8 +122,8 @@ ActionCable javascript API:
- The `ActionCable.startDebugging()` and `ActionCable.stopDebugging()`
methods have been removed and replaced with the property
- `ActionCable.logger.enabled`. If you are currently using these methods you
- will need to make these changes when upgrading:
+ `ActionCable.logger.enabled`. If you are using these methods you
+ will need to make these changes:
```diff
- ActionCable.startDebugging()
@@ -133,6 +134,318 @@ ActionCable javascript API:
+ ActionCable.logger.enabled = false
```
+### `ActionDispatch::Response#content_type` now returns the Content-Type header without modification
+
+Previously, the return value of `ActionDispatch::Response#content_type` did NOT contain the charset part.
+This behavior has changed to include the previously omitted charset part as well.
+
+If you want just the MIME type, please use `ActionDispatch::Response#media_type` instead.
+
+Before:
+
+```ruby
+resp = ActionDispatch::Response.new(200, "Content-Type" => "text/csv; header=present; charset=utf-16")
+resp.content_type #=> "text/csv; header=present"
+```
+
+After:
+
+```ruby
+resp = ActionDispatch::Response.new(200, "Content-Type" => "text/csv; header=present; charset=utf-16")
+resp.content_type #=> "text/csv; header=present; charset=utf-16"
+resp.media_type #=> "text/csv"
+```
+
+### Autoloading
+
+The default configuration for Rails 6
+
+```ruby
+# config/application.rb
+
+config.load_defaults "6.0"
+```
+
+enables `zeitwerk` autoloading mode on CRuby. In that mode, autoloading, reloading, and eager loading are managed by [Zeitwerk](https://github.com/fxn/zeitwerk).
+
+#### Public API
+
+In general, applications do not need to use the API of Zeitwerk directly. Rails sets things up according to the existing contract: `config.autoload_paths`, `config.cache_classes`, etc.
+
+While applications should stick to that interface, the actual Zeitwerk loader object can be accessed as
+
+```ruby
+Rails.autoloaders.main
+```
+
+That may be handy if you need to preload STIs or configure a custom inflector, for example.
+
+#### Project Structure
+
+If the application being upgraded autoloads correctly, the project structure should be already mostly compatible.
+
+However, `classic` mode infers file names from missing constant names (`underscore`), whereas `zeitwerk` mode infers constant names from file names (`camelize`). These helpers are not always inverse of each other, in particular if acronyms are involved. For instance, `"FOO".underscore` is `"foo"`, but `"foo".camelize` is `"Foo"`, not `"FOO"`.
+
+Compatibility can be checked with the `zeitwerk:check` task:
+
+```
+$ bin/rails zeitwerk:check
+Hold on, I am eager loading the application.
+All is good!
+```
+
+#### require_dependency
+
+All known use cases of `require_dependency` have been eliminated, you should grep the project and delete them.
+
+If your application has STIs, please check their section in the guide [Autoloading and Reloading Constants (Zeitwerk Mode)](autoloading_and_reloading_constants.html#single-table-inheritance).
+
+#### Qualified names in class and module definitions
+
+You can now robustly use constant paths in class and module definitions:
+
+```ruby
+# Autoloading in this class' body matches Ruby semantics now.
+class Admin::UsersController < ApplicationController
+ # ...
+end
+```
+
+A gotcha to be aware of is that, depending on the order of execution, the classic autoloader could sometimes be able to autoload `Foo::Wadus` in
+
+```ruby
+class Foo::Bar
+ Wadus
+end
+```
+
+That does not match Ruby semantics because `Foo` is not in the nesting, and won't work at all in `zeitwerk` mode. If you find such corner case you can use the qualified name `Foo::Wadus`:
+
+```ruby
+class Foo::Bar
+ Foo::Wadus
+end
+```
+
+or add `Foo` to the nesting:
+
+```ruby
+module Foo
+ class Bar
+ Wadus
+ end
+end
+```
+
+#### Concerns
+
+You can autoload and eager load from a standard structure like
+
+```
+app/models
+app/models/concerns
+```
+
+In that case, `app/models/concerns` is assumed to be a root directory (because it belongs to the autoload paths), and it is ignored as namespace. So, `app/models/concerns/foo.rb` should define `Foo`, not `Concerns::Foo`.
+
+The `Concerns::` namespace worked with the classic autoloader as a side-effect of the implementation, but it was not really an intended behavior. An application using `Concerns::` needs to rename those classes and modules to be able to run in `zeitwerk` mode.
+
+#### Having `app` in the autoload paths
+
+Some projects want something like `app/api/base.rb` to define `API::Base`, and add `app` to the autoload paths to accomplish that in `classic` mode. Since Rails adds all subdirectories of `app` to the autoload paths automatically, we have another situation in which there are nested root directories, so that setup no longer works. Similar principle we explained above with `concerns`.
+
+If you want to keep that structure, you'll need to delete the subdirectory from the autoload paths in an initializer:
+
+```ruby
+ActiveSupport::Dependencies.autoload_paths.delete("#{Rails.root}/app/api")
+```
+
+#### Autoloaded Constants and Explicit Namespaces
+
+If a namespace is defined in a file, as `Hotel` is here:
+
+```
+app/models/hotel.rb # Defines Hotel.
+app/models/hotel/pricing.rb # Defines Hotel::Pricing.
+```
+
+the `Hotel` constant has to be set using the `class` or `module` keywords. For example:
+
+```ruby
+class Hotel
+end
+```
+
+is good.
+
+Alternatives like
+
+```ruby
+Hotel = Class.new
+```
+
+or
+
+```ruby
+Hotel = Struct.new
+```
+
+won't work, child objects like `Hotel::Pricing` won't be found.
+
+This restriction only applies to explicit namespaces. Classes and modules not defining a namespace can be defined using those idioms.
+
+#### One file, one constant (at the same top-level)
+
+In `classic` mode you could technically define several constants at the same top-level and have them all reloaded. For example, given
+
+```ruby
+# app/models/foo.rb
+
+class Foo
+end
+
+class Bar
+end
+```
+
+while `Bar` could not be autoloaded, autoloading `Foo` would mark `Bar` as autoloaded too. This is not the case in `zeitwerk` mode, you need to move `Bar` to its own file `bar.rb`. One file, one constant.
+
+This affects only to constants at the same top-level as in the example above. Inner classes and modules are fine. For example, consider
+
+```ruby
+# app/models/foo.rb
+
+class Foo
+ class InnerClass
+ end
+end
+```
+
+If the application reloads `Foo`, it will reload `Foo::InnerClass` too.
+
+#### Spring and the `test` Environment
+
+Spring reloads the application code if something changes. In the `test` environment you need to enable reloading for that to work:
+
+```ruby
+# config/environments/test.rb
+
+config.cache_classes = false
+```
+
+Otherwise you'll get this error:
+
+```
+reloading is disabled because config.cache_classes is true
+```
+
+#### Bootsnap
+
+Bootsnap should be at least version 1.4.2.
+
+In addition to that, Bootsnap needs to disable the iseq cache due to a bug in the interpreter if running Ruby 2.5. Please make sure to depend on at least Bootsnap 1.4.4 in that case.
+
+#### `config.add_autoload_paths_to_load_path`
+
+The new configuration point
+
+```ruby
+config.add_autoload_paths_to_load_path
+```
+
+is `true` by default for backwards compatibility, but allows you to opt-out from adding the autoload paths to `$LOAD_PATH`.
+
+This makes sense in most applications, since you never should require a file in `app/models`, for example, and Zeitwerk only uses absolute file names internally.
+
+By opting-out you optimize `$LOAD_PATH` lookups (less directories to check), and save Bootsnap work and memory consumption, since it does not need to build an index for these directories.
+
+#### Thread-safety
+
+In classic mode, constant autoloading is not thread-safe, though Rails has locks in place for example to make web requests thread-safe when autoloading is enabled, as it is common in `development` mode.
+
+Constant autoloading is thread-safe in `zeitwerk` mode. For example, you can now autoload in multi-threaded scripts executed by the `runner` command.
+
+#### Globs in config.autoload_paths
+
+Beware of configurations like
+
+```ruby
+config.autoload_paths += Dir["#{config.root}/lib/**/"]
+```
+
+Every element of `config.autoload_paths` should represent the top-level namespace (`Object`) and they cannot be nested in consequence (with the exception of `concerns` directories explained above).
+
+To fix this, just remove the wildcards:
+
+```ruby
+config.autoload_paths << "#{config.root}/lib"
+```
+
+#### Eager loading and autoloading are consistent
+
+In `classic` mode, if `app/models/foo.rb` defines `Bar`, you won't be able to autoload that file, but eager loading will work because it loads files recursively blindly. This can be a source of errors if you test things first eager loading, execution may fail later autoloading.
+
+In `zeitwerk` mode both loading modes are consistent, they fail and err in the same files.
+
+#### How to Use the Classic Autoloader in Rails 6
+
+Applications can load Rails 6 defaults and still use the classic autoloader by setting `config.autoloader` this way:
+
+```ruby
+# config/application.rb
+
+config.load_defaults "6.0"
+config.autoloader = :classic
+```
+
+### Active Storage assignment behavior change
+
+In Rails 5.2, assigning to a collection of attachments declared with `has_many_attached` appended new files:
+
+```ruby
+class User < ApplicationRecord
+ has_many_attached :highlights
+end
+
+user.highlights.attach(filename: "funky.jpg", ...)
+user.higlights.count # => 1
+
+blob = ActiveStorage::Blob.create_after_upload!(filename: "town.jpg", ...)
+user.update!(highlights: [ blob ])
+
+user.highlights.count # => 2
+user.highlights.first.filename # => "funky.jpg"
+user.highlights.second.filename # => "town.jpg"
+```
+
+With the default configuration for Rails 6.0, assigning to a collection of attachments replaces existing files
+instead of appending to them. This matches Active Record behavior when assigning to a collection association:
+
+```ruby
+user.highlights.attach(filename: "funky.jpg", ...)
+user.highlights.count # => 1
+
+blob = ActiveStorage::Blob.create_after_upload!(filename: "town.jpg", ...)
+user.update!(highlights: [ blob ])
+
+user.highlights.count # => 1
+user.highlights.first.filename # => "town.jpg"
+```
+
+`#attach` can be used to add new attachments without removing the existing ones:
+
+```ruby
+blob = ActiveStorage::Blob.create_after_upload!(filename: "town.jpg", ...)
+user.highlights.attach(blob)
+
+user.highlights.count # => 2
+user.highlights.first.filename # => "funky.jpg"
+user.highlights.second.filename # => "town.jpg"
+```
+
+Opt in to the new default behavior by setting `config.active_storage.replace_on_assign_to_many` to `true`.
+The old behavior will be deprecated in Rails 6.1 and removed in a subsequent release.
+
Upgrading from Rails 5.1 to Rails 5.2
-------------------------------------
@@ -1557,7 +1870,7 @@ config.assets.enabled = true
config.assets.version = '1.0'
```
-If your application is using an "/assets" route for a resource you may want change the prefix used for assets to avoid conflicts:
+If your application is using an "/assets" route for a resource you may want to change the prefix used for assets to avoid conflicts:
```ruby
# Defaults to '/assets'
diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md
index 109c4836d5..cfb30719da 100644
--- a/railties/CHANGELOG.md
+++ b/railties/CHANGELOG.md
@@ -1,379 +1,5 @@
-* New applications get `config.cache_classes = false` in `config/environments/test.rb`
- unless `--skip-spring`.
+* Support using environment variable to set pidfile
- *Xavier Noria*
+ *Ben Thorner*
-* Autoloading during initialization is deprecated.
-
- *Xavier Noria*
-
-* Only force `:async` ActiveJob adapter to `:inline` during seeding.
-
- *BatedUrGonnaDie*
-
-* The `connection` option of `rails dbconsole` command is deprecated in
- favor of `database` option.
-
- *Yuji Yaginuma*
-
-* Replace `chromedriver-helper` gem with `webdrivers` in default Gemfile.
- `chromedriver-helper` is deprecated as of March 31, 2019 and won't
- receive any further updates.
-
- *Guillermo Iguaran‮*
-
-* Applications running in `:zeitwerk` mode that use `bootsnap` need
- to upgrade `bootsnap` to at least 1.4.2.
-
- *Xavier Noria*
-
-* Add `config.disable_sandbox` option to Rails console.
-
- This setting will disable `rails console --sandbox` mode, preventing
- developer from accidentally starting a sandbox console,
- which when left inactive, can cause the database server to run out of memory.
-
- *Prem Sichanugrist*
-
-* Add `-e/--environment` option to `rails initializers`.
-
- *Yuji Yaginuma*
-
-
-## Rails 6.0.0.beta3 (March 11, 2019) ##
-
-* Generate random development secrets
-
- A random development secret is now generated to tmp/development_secret.txt
-
- This avoids an issue where development mode servers were vulnerable to
- remote code execution.
-
- Fixes CVE-2019-5420
-
- *Eileen M. Uchitelle*, *Aaron Patterson*, *John Hawthorn*
-
-
-## Rails 6.0.0.beta2 (February 25, 2019) ##
-
-* Fix non-symbol access to nested hashes returned from `Rails::Application.config_for`
- being broken by allowing non-symbol access with a deprecation notice.
-
- *Ufuk Kayserilioglu*
-
-* Fix deeply nested namespace command printing.
-
- *Gannon McGibbon*
-
-
-## Rails 6.0.0.beta1 (January 18, 2019) ##
-
-* Remove deprecated `after_bundle` helper inside plugins templates.
-
- *Rafael Mendonça França*
-
-* Remove deprecated support to old `config.ru` that use the application class as argument of `run`.
-
- *Rafael Mendonça França*
-
-* Remove deprecated `environment` argument from the rails commands.
-
- *Rafael Mendonça França*
-
-* Remove deprecated `capify!`.
-
- *Rafael Mendonça França*
-
-* Remove deprecated `config.secret_token`.
-
- *Rafael Mendonça França*
-
-* Seed database with inline ActiveJob job adapter.
-
- *Gannon McGibbon*
-
-* Add `rails db:system:change` command for changing databases.
-
- ```
- bin/rails db:system:change --to=postgresql
- force config/database.yml
- gsub Gemfile
- ```
-
- The change command copies a template `config/database.yml` with
- the target database adapter into your app, and replaces your database gem
- with the target database gem.
-
- *Gannon McGibbon*
-
-* Add `rails test:channels`.
-
- *bogdanvlviv*
-
-* Use original `bundler` environment variables during the process of generating a new rails project.
-
- *Marco Costa*
-
-* Send Active Storage analysis and purge jobs to dedicated queues by default.
-
- Analysis jobs now use the `:active_storage_analysis` queue, and purge jobs
- now use the `:active_storage_purge` queue. This matches Action Mailbox,
- which sends its jobs to dedicated queues by default.
-
- *George Claghorn*
-
-* Add `rails test:mailboxes`.
-
- *George Claghorn*
-
-* Introduce guard against DNS rebinding attacks.
-
- The `ActionDispatch::HostAuthorization` is a new middleware that prevents
- against DNS rebinding and other `Host` header attacks. It is included in
- the development environment by default with the following configuration:
-
- Rails.application.config.hosts = [
- IPAddr.new("0.0.0.0/0"), # All IPv4 addresses.
- IPAddr.new("::/0"), # All IPv6 addresses.
- "localhost" # The localhost reserved domain.
- ]
-
- In other environments `Rails.application.config.hosts` is empty and no
- `Host` header checks will be done. If you want to guard against header
- attacks on production, you have to manually permit the allowed hosts
- with:
-
- Rails.application.config.hosts << "product.com"
-
- The host of a request is checked against the `hosts` entries with the case
- operator (`#===`), which lets `hosts` support entries of type `RegExp`,
- `Proc` and `IPAddr` to name a few. Here is an example with a regexp.
-
- # Allow requests from subdomains like `www.product.com` and
- # `beta1.product.com`.
- Rails.application.config.hosts << /.*\.product\.com/
-
- A special case is supported that allows you to permit all sub-domains:
-
- # Allow requests from subdomains like `www.product.com` and
- # `beta1.product.com`.
- Rails.application.config.hosts << ".product.com"
-
- *Genadi Samokovarov*
-
-* Remove redundant suffixes on generated helpers.
-
- *Gannon McGibbon*
-
-* Remove redundant suffixes on generated integration tests.
-
- *Gannon McGibbon*
-
-* Fix boolean interaction in scaffold system tests.
-
- *Gannon McGibbon*
-
-* Remove redundant suffixes on generated system tests.
-
- *Gannon McGibbon*
-
-* Add an `abort_on_failure` boolean option to the generator method that shell
- out (`generate`, `rake`, `rails_command`) to abort the generator if the
- command fails.
-
- *David Rodríguez*
-
-* Remove `app/assets` and `app/javascript` from `eager_load_paths` and `autoload_paths`.
-
- *Gannon McGibbon*
-
-* Use Ids instead of memory addresses when displaying references in scaffold views.
-
- Fixes #29200.
-
- *Rasesh Patel*
-
-* Adds support for multiple databases to `rails db:migrate:status`.
- Subtasks are also added to get the status of individual databases (eg. `rails db:migrate:status:animals`).
-
- *Gannon McGibbon*
-
-* Use Webpacker by default to manage app-level JavaScript through the new app/javascript directory.
- Sprockets is now solely in charge, by default, of compiling CSS and other static assets.
- Action Cable channel generators will create ES6 stubs rather than use CoffeeScript.
- Active Storage, Action Cable, Turbolinks, and Rails-UJS are loaded by a new application.js pack.
- Generators no longer generate JavaScript stubs.
-
- *DHH*, *Lachlan Sylvester*
-
-* Add `database` (aliased as `db`) option to model generator to allow
- setting the database. This is useful for applications that use
- multiple databases and put migrations per database in their own directories.
-
- ```
- bin/rails g model Room capacity:integer --database=kingston
- invoke active_record
- create db/kingston_migrate/20180830151055_create_rooms.rb
- ```
-
- Because rails scaffolding uses the model generator, you can
- also specify a database with the scaffold generator.
-
- *Gannon McGibbon*
-
-* Raise an error when "recyclable cache keys" are being used by a cache store
- that does not explicitly support it. Custom cache keys that do support this feature
- can bypass this error by implementing the `supports_cache_versioning?` method on their
- class and returning a truthy value.
-
- *Richard Schneeman*
-
-* Support environment specific credentials overrides.
-
- So any environment will look for `config/credentials/#{Rails.env}.yml.enc` and fall back
- to `config/credentials.yml.enc`.
-
- The encryption key can be in `ENV["RAILS_MASTER_KEY"]` or `config/credentials/production.key`.
-
- Environment credentials overrides can be edited with `rails credentials:edit --environment production`.
- If no override is set up for the passed environment, it will be created.
-
- Additionally, the default lookup paths can be overwritten with these configs:
-
- - `config.credentials.content_path`
- - `config.credentials.key_path`
-
- *Wojciech Wnętrzak*
-
-* Make `ActiveSupport::Cache::NullStore` the default cache store in the test environment.
-
- *Michael C. Nelson*
-
-* Emit warning for unknown inflection rule when generating model.
-
- *Yoshiyuki Kinjo*
-
-* Add `database` (aliased as `db`) option to migration generator.
-
- If you're using multiple databases and have a folder for each database
- for migrations (ex db/migrate and db/new_db_migrate) you can now pass the
- `--database` option to the generator to make sure the the migration
- is inserted into the correct folder.
-
- ```
- rails g migration CreateHouses --database=kingston
- invoke active_record
- create db/kingston_migrate/20180830151055_create_houses.rb
- ```
-
- *Eileen M. Uchitelle*
-
-* Deprecate `rake routes` in favor of `rails routes`.
-
- *Yuji Yaginuma*
-
-* Deprecate `rake initializers` in favor of `rails initializers`.
-
- *Annie-Claude Côté*
-
-* Deprecate `rake dev:cache` in favor of `rails dev:cache`.
-
- *Annie-Claude Côté*
-
-* Deprecate `rails notes` subcommands in favor of passing an `annotations` argument to `rails notes`.
-
- The following subcommands are replaced by passing `--annotations` or `-a` to `rails notes`:
- - `rails notes:custom ANNOTATION=custom` is deprecated in favor of using `rails notes -a custom`.
- - `rails notes:optimize` is deprecated in favor of using `rails notes -a OPTIMIZE`.
- - `rails notes:todo` is deprecated in favor of using`rails notes -a TODO`.
- - `rails notes:fixme` is deprecated in favor of using `rails notes -a FIXME`.
-
- *Annie-Claude Côté*
-
-* Deprecate `SOURCE_ANNOTATION_DIRECTORIES` environment variable used by `rails notes`
- through `Rails::SourceAnnotationExtractor::Annotation` in favor of using `config.annotations.register_directories`.
-
- *Annie-Claude Côté*
-
-* Deprecate `rake notes` in favor of `rails notes`.
-
- *Annie-Claude Côté*
-
-* Don't generate unused files in `app:update` task.
-
- Skip the assets' initializer when sprockets isn't loaded.
-
- Skip `config/spring.rb` when spring isn't loaded.
-
- Skip yarn's contents when yarn integration isn't used.
-
- *Tsukuru Tanimichi*
-
-* Make the master.key file read-only for the owner upon generation on
- POSIX-compliant systems.
-
- Previously:
-
- $ ls -l config/master.key
- -rw-r--r-- 1 owner group 32 Jan 1 00:00 master.key
-
- Now:
-
- $ ls -l config/master.key
- -rw------- 1 owner group 32 Jan 1 00:00 master.key
-
- Fixes #32604.
-
- *Jose Luis Duran*
-
-* Deprecate support for using the `HOST` environment variable to specify the server IP.
-
- The `BINDING` environment variable should be used instead.
-
- Fixes #29516.
-
- *Yuji Yaginuma*
-
-* Deprecate passing Rack server name as a regular argument to `rails server`.
-
- Previously:
-
- $ bin/rails server thin
-
- There wasn't an explicit option for the Rack server to use, now we have the
- `--using` option with the `-u` short switch.
-
- Now:
-
- $ bin/rails server -u thin
-
- This change also improves the error message if a missing or mistyped rack
- server is given.
-
- *Genadi Samokovarov*
-
-* Add "rails routes --expanded" option to output routes in expanded mode like
- "psql --expanded". Result looks like:
-
- ```
- $ rails routes --expanded
- --[ Route 1 ]------------------------------------------------------------
- Prefix | high_scores
- Verb | GET
- URI | /high_scores(.:format)
- Controller#Action | high_scores#index
- --[ Route 2 ]------------------------------------------------------------
- Prefix | new_high_score
- Verb | GET
- URI | /high_scores/new(.:format)
- Controller#Action | high_scores#new
- ```
-
- *Benoit Tigeot*
-
-* Rails 6 requires Ruby 2.5.0 or newer.
-
- *Jeremy Daer*, *Kasper Timm Hansen*
-
-
-Please check [5-2-stable](https://github.com/rails/rails/blob/5-2-stable/railties/CHANGELOG.md) for previous changes.
+Please check [6-0-stable](https://github.com/rails/rails/blob/6-0-stable/railties/CHANGELOG.md) for previous changes.
diff --git a/railties/Rakefile b/railties/Rakefile
index 0f305ea332..51f46d1817 100644
--- a/railties/Rakefile
+++ b/railties/Rakefile
@@ -95,14 +95,18 @@ namespace :test do
])
puts fake_command
- # We could run these in parallel, but pretty much all of the
- # railties tests already run in parallel, so ¯\_(⊙︿⊙)_/¯
- Process.waitpid fork {
- ARGV.clear.concat test_options
- Rake.application = nil
-
- load file
- }
+ if Process.respond_to?(:fork)
+ # We could run these in parallel, but pretty much all of the
+ # railties tests already run in parallel, so ¯\_(⊙︿⊙)_/¯
+ Process.waitpid fork {
+ ARGV.clear.concat test_options
+ Rake.application = nil
+
+ load file
+ }
+ else
+ Process.wait spawn(fake_command)
+ end
unless $?.success?
failing_files << file
diff --git a/railties/lib/rails.rb b/railties/lib/rails.rb
index 1f533a8c04..440d2953c3 100644
--- a/railties/lib/rails.rb
+++ b/railties/lib/rails.rb
@@ -5,7 +5,6 @@ require "rails/ruby_version_check"
require "pathname"
require "active_support"
-require "active_support/dependencies/autoload"
require "active_support/core_ext/kernel/reporting"
require "active_support/core_ext/module/delegation"
require "active_support/core_ext/array/extract_options"
diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb
index 038284ebdd..cbaab6cc33 100644
--- a/railties/lib/rails/application.rb
+++ b/railties/lib/rails/application.rb
@@ -270,7 +270,9 @@ module Rails
"action_dispatch.use_cookies_with_metadata" => config.action_dispatch.use_cookies_with_metadata,
"action_dispatch.content_security_policy" => config.content_security_policy,
"action_dispatch.content_security_policy_report_only" => config.content_security_policy_report_only,
- "action_dispatch.content_security_policy_nonce_generator" => config.content_security_policy_nonce_generator
+ "action_dispatch.content_security_policy_nonce_generator" => config.content_security_policy_nonce_generator,
+ "action_dispatch.content_security_policy_nonce_directives" => config.content_security_policy_nonce_directives,
+ "action_dispatch.feature_policy" => config.feature_policy,
)
end
end
@@ -501,7 +503,6 @@ module Rails
end
protected
-
alias :build_middleware_stack :app
def run_tasks_blocks(app) #:nodoc:
@@ -581,7 +582,6 @@ module Rails
end
private
-
def generate_development_secret
if secrets.secret_key_base.nil?
key_file = Rails.root.join("tmp/development_secret.txt")
@@ -623,7 +623,6 @@ module Rails
end
private
-
def convert_key(key)
unless key.kind_of?(Symbol)
ActiveSupport::Deprecation.warn(<<~MESSAGE.squish)
diff --git a/railties/lib/rails/application/bootstrap.rb b/railties/lib/rails/application/bootstrap.rb
index e3c0759f95..1fdc7b2d71 100644
--- a/railties/lib/rails/application/bootstrap.rb
+++ b/railties/lib/rails/application/bootstrap.rb
@@ -19,14 +19,14 @@ module Rails
initializer :set_eager_load, group: :all do
if config.eager_load.nil?
- warn <<-INFO
-config.eager_load is set to nil. Please update your config/environments/*.rb files accordingly:
+ warn <<~INFO
+ config.eager_load is set to nil. Please update your config/environments/*.rb files accordingly:
- * development - set it to false
- * test - set it to false (unless you use a tool that preloads your test environment)
- * production - set it to true
+ * development - set it to false
+ * test - set it to false (unless you use a tool that preloads your test environment)
+ * production - set it to true
-INFO
+ INFO
config.eager_load = config.cache_classes
end
end
@@ -34,20 +34,12 @@ INFO
# Initialize the logger early in the stack in case we need to log some deprecation.
initializer :initialize_logger, group: :all do
Rails.logger ||= config.logger || begin
- path = config.paths["log"].first
- unless File.exist? File.dirname path
- FileUtils.mkdir_p File.dirname path
- end
-
- f = File.open path, "a"
- f.binmode
- f.sync = config.autoflush_log # if true make sure every write flushes
-
- logger = ActiveSupport::Logger.new f
+ logger = ActiveSupport::Logger.new(config.default_log_file)
logger.formatter = config.log_formatter
logger = ActiveSupport::TaggedLogging.new(logger)
logger
rescue StandardError
+ path = config.paths["log"].first
logger = ActiveSupport::TaggedLogging.new(ActiveSupport::Logger.new(STDERR))
logger.level = ActiveSupport::Logger::WARN
logger.warn(
diff --git a/railties/lib/rails/application/configuration.rb b/railties/lib/rails/application/configuration.rb
index b79dbdbc6f..934578e9f1 100644
--- a/railties/lib/rails/application/configuration.rb
+++ b/railties/lib/rails/application/configuration.rb
@@ -18,8 +18,8 @@ module Rails
:session_options, :time_zone, :reload_classes_only_on_change,
:beginning_of_week, :filter_redirect, :x, :enable_dependency_loading,
:read_encrypted_secrets, :log_level, :content_security_policy_report_only,
- :content_security_policy_nonce_generator, :require_master_key, :credentials,
- :disable_sandbox
+ :content_security_policy_nonce_generator, :content_security_policy_nonce_directives,
+ :require_master_key, :credentials, :disable_sandbox, :add_autoload_paths_to_load_path
attr_reader :encoding, :api_only, :loaded_config_version, :autoloader
@@ -60,6 +60,7 @@ module Rails
@content_security_policy = nil
@content_security_policy_report_only = false
@content_security_policy_nonce_generator = nil
+ @content_security_policy_nonce_directives = nil
@require_master_key = false
@loaded_config_version = nil
@credentials = ActiveSupport::OrderedOptions.new
@@ -67,6 +68,8 @@ module Rails
@credentials.key_path = default_credentials_key_path
@autoloader = :classic
@disable_sandbox = false
+ @add_autoload_paths_to_load_path = true
+ @feature_policy = nil
end
def load_defaults(target_version)
@@ -128,6 +131,7 @@ module Rails
if respond_to?(:action_dispatch)
action_dispatch.use_cookies_with_metadata = true
+ action_dispatch.return_only_media_type_on_content_type = false
end
if respond_to?(:action_mailer)
@@ -141,7 +145,15 @@ module Rails
if respond_to?(:active_storage)
active_storage.queues.analysis = :active_storage_analysis
active_storage.queues.purge = :active_storage_purge
+
+ active_storage.replace_on_assign_to_many = true
+ end
+
+ if respond_to?(:active_record)
+ active_record.collection_cache_versioning = true
end
+ when "6.1"
+ load_defaults "6.0"
else
raise "Unknown version #{target_version.to_s.inspect}"
end
@@ -200,7 +212,7 @@ module Rails
yaml = Pathname.new(path)
erb = DummyERB.new(yaml.read)
- YAML.load(erb.result)
+ YAML.load(erb.result) || {}
else
{}
end
@@ -292,6 +304,14 @@ module Rails
end
end
+ def feature_policy(&block)
+ if block_given?
+ @feature_policy = ActionDispatch::FeaturePolicy.new(&block)
+ else
+ @feature_policy
+ end
+ end
+
def autoloader=(autoloader)
case autoloader
when :classic
@@ -304,6 +324,18 @@ module Rails
end
end
+ def default_log_file
+ path = paths["log"].first
+ unless File.exist? File.dirname path
+ FileUtils.mkdir_p File.dirname path
+ end
+
+ f = File.open path, "a"
+ f.binmode
+ f.sync = autoflush_log # if true make sure every write flushes
+ f
+ end
+
class Custom #:nodoc:
def initialize
@configurations = Hash.new
diff --git a/railties/lib/rails/application/default_middleware_stack.rb b/railties/lib/rails/application/default_middleware_stack.rb
index 193cc59f3a..572f51fca2 100644
--- a/railties/lib/rails/application/default_middleware_stack.rb
+++ b/railties/lib/rails/application/default_middleware_stack.rb
@@ -49,6 +49,7 @@ module Rails
middleware.use ::Rails::Rack::Logger, config.log_tags
middleware.use ::ActionDispatch::ShowExceptions, show_exceptions_app
middleware.use ::ActionDispatch::DebugExceptions, app, config.debug_exception_response_format
+ middleware.use ::ActionDispatch::ActionableExceptions
unless config.cache_classes
middleware.use ::ActionDispatch::Reloader, app.reloader
@@ -67,6 +68,7 @@ module Rails
unless config.api_only
middleware.use ::ActionDispatch::ContentSecurityPolicy::Middleware
+ middleware.use ::ActionDispatch::FeaturePolicy::Middleware
end
middleware.use ::Rack::Head
@@ -78,7 +80,6 @@ module Rails
end
private
-
def load_rack_cache
rack_cache = config.action_dispatch.rack_cache
return unless rack_cache
diff --git a/railties/lib/rails/application/dummy_erb_compiler.rb b/railties/lib/rails/application/dummy_erb_compiler.rb
index c4659123bb..028e790292 100644
--- a/railties/lib/rails/application/dummy_erb_compiler.rb
+++ b/railties/lib/rails/application/dummy_erb_compiler.rb
@@ -11,9 +11,8 @@ end
class DummyCompiler < ERB::Compiler # :nodoc:
def compile_content(stag, out)
- case stag
- when "<%="
- out.push "_erbout << 'dummy_compiler'"
+ if stag == "<%="
+ out.push "_erbout << ''"
end
end
end
diff --git a/railties/lib/rails/application/finisher.rb b/railties/lib/rails/application/finisher.rb
index 109c560c80..9f4009ad20 100644
--- a/railties/lib/rails/application/finisher.rb
+++ b/railties/lib/rails/application/finisher.rb
@@ -39,8 +39,14 @@ module Rails
example = autoloaded.first
example_klass = example.constantize.class
- ActiveSupport::DescendantsTracker.clear
- ActiveSupport::Dependencies.clear
+ if config.autoloader == :zeitwerk
+ ActiveSupport::DescendantsTracker.clear
+ ActiveSupport::Dependencies.clear
+
+ unload_message = "#{these} autoloaded #{constants} #{have} been unloaded."
+ else
+ unload_message = "`config.autoloader` is set to `#{config.autoloader}`. #{these} autoloaded #{constants} would have been unloaded if `config.autoloader` had been set to `:zeitwerk`."
+ end
ActiveSupport::Deprecation.warn(<<~WARNING)
Initialization autoloaded the #{constants} #{enum}.
@@ -52,7 +58,7 @@ module Rails
initialization does not run again. So, if you reload #{example}, for example,
the expected changes won't be reflected in that stale #{example_klass} object.
- #{these} autoloaded #{constants} #{have} been unloaded.
+ #{unload_message}
Please, check the "Autoloading and Reloading Constants" guide for solutions.
WARNING
diff --git a/railties/lib/rails/application/routes_reloader.rb b/railties/lib/rails/application/routes_reloader.rb
index 3ecb8e264e..1070362253 100644
--- a/railties/lib/rails/application/routes_reloader.rb
+++ b/railties/lib/rails/application/routes_reloader.rb
@@ -25,7 +25,6 @@ module Rails
end
private
-
def updater
@updater ||= ActiveSupport::FileUpdateChecker.new(paths) { reload! }
end
diff --git a/railties/lib/rails/application_controller.rb b/railties/lib/rails/application_controller.rb
index b3fe822218..8c00633515 100644
--- a/railties/lib/rails/application_controller.rb
+++ b/railties/lib/rails/application_controller.rb
@@ -12,7 +12,6 @@ class Rails::ApplicationController < ActionController::Base # :nodoc:
end
private
-
def require_local!
unless local_request?
render html: "<p>For security purposes, this information is only available to local requests.</p>".html_safe, status: :forbidden
diff --git a/railties/lib/rails/command.rb b/railties/lib/rails/command.rb
index f09aa3ae0d..7e6e968c92 100644
--- a/railties/lib/rails/command.rb
+++ b/railties/lib/rails/command.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require "active_support"
-require "active_support/dependencies/autoload"
require "active_support/core_ext/enumerable"
require "active_support/core_ext/object/blank"
diff --git a/railties/lib/rails/command/spellchecker.rb b/railties/lib/rails/command/spellchecker.rb
index 085d5b16df..c5d0253185 100644
--- a/railties/lib/rails/command/spellchecker.rb
+++ b/railties/lib/rails/command/spellchecker.rb
@@ -13,7 +13,6 @@ module Rails
end
private
-
# This code is based directly on the Text gem implementation.
# Copyright (c) 2006-2013 Paul Battley, Michael Neumann, Tim Fletcher.
#
diff --git a/railties/lib/rails/commands/dev/dev_command.rb b/railties/lib/rails/commands/dev/dev_command.rb
index a3f02f3172..9b2cb2b04a 100644
--- a/railties/lib/rails/commands/dev/dev_command.rb
+++ b/railties/lib/rails/commands/dev/dev_command.rb
@@ -5,8 +5,10 @@ require "rails/dev_caching"
module Rails
module Command
class DevCommand < Base # :nodoc:
- def help
- say "rails dev:cache # Toggle development mode caching on/off."
+ no_commands do
+ def help
+ say "rails dev:cache # Toggle development mode caching on/off."
+ end
end
def cache
diff --git a/railties/lib/rails/commands/server/server_command.rb b/railties/lib/rails/commands/server/server_command.rb
index 982b83ead5..84248e03c1 100644
--- a/railties/lib/rails/commands/server/server_command.rb
+++ b/railties/lib/rails/commands/server/server_command.rb
@@ -99,7 +99,7 @@ module Rails
RACK_SERVERS = %w(cgi fastcgi webrick lsws scgi thin puma unicorn)
DEFAULT_PORT = 3000
- DEFAULT_PID_PATH = "tmp/pids/server.pid"
+ DEFAULT_PIDFILE = "tmp/pids/server.pid"
argument :using, optional: true
@@ -114,8 +114,8 @@ module Rails
desc: "Runs server as a Daemon."
class_option :using, aliases: "-u", type: :string,
desc: "Specifies the Rack server used to run the application (thin/puma/webrick).", banner: :name
- class_option :pid, aliases: "-P", type: :string, default: DEFAULT_PID_PATH,
- desc: "Specifies the PID file."
+ class_option :pid, aliases: "-P", type: :string,
+ desc: "Specifies the PID file - defaults to #{DEFAULT_PIDFILE}."
class_option :dev_caching, aliases: "-C", type: :boolean, default: nil,
desc: "Specifies whether to perform caching in development."
class_option :restart, type: :boolean, default: nil, hide: true
@@ -207,6 +207,7 @@ module Rails
end
user_supplied_options << :Host if ENV["HOST"] || ENV["BINDING"]
user_supplied_options << :Port if ENV["PORT"]
+ user_supplied_options << :pid if ENV["PIDFILE"]
user_supplied_options.uniq
end
end
@@ -253,7 +254,7 @@ module Rails
end
def pid
- File.expand_path(options[:pid])
+ File.expand_path(options[:pid] || ENV.fetch("PIDFILE", DEFAULT_PIDFILE))
end
def self.banner(*)
@@ -261,7 +262,7 @@ module Rails
end
def prepare_restart
- FileUtils.rm_f(options[:pid]) if options[:restart]
+ FileUtils.rm_f(pid) if options[:restart]
end
def deprecate_positional_rack_server_and_rewrite_to_option(original_options)
diff --git a/railties/lib/rails/engine.rb b/railties/lib/rails/engine.rb
index eb2f0e8fca..46f1d38b96 100644
--- a/railties/lib/rails/engine.rb
+++ b/railties/lib/rails/engine.rb
@@ -552,16 +552,15 @@ module Rails
seed_file = paths["db/seeds.rb"].existent.first
return unless seed_file
- if config.active_job.queue_adapter == :async
+ if config.try(:active_job)&.queue_adapter == :async
with_inline_jobs { load(seed_file) }
else
load(seed_file)
end
end
- # Add configured load paths to Ruby's load path, and remove duplicate entries.
- initializer :set_load_path, before: :bootstrap_hook do
- _all_load_paths.reverse_each do |path|
+ initializer :set_load_path, before: :bootstrap_hook do |app|
+ _all_load_paths(app.config.add_autoload_paths_to_load_path).reverse_each do |path|
$LOAD_PATH.unshift(path) if File.directory?(path)
end
$LOAD_PATH.uniq!
@@ -655,14 +654,12 @@ module Rails
end
protected
-
def run_tasks_blocks(*) #:nodoc:
super
paths["lib/tasks"].existent.sort.each { |ext| load(ext) }
end
private
-
def load_config_initializer(initializer) # :doc:
ActiveSupport::Notifications.instrument("load_config_initializer.railties", initializer: initializer) do
load(initializer)
@@ -709,8 +706,12 @@ module Rails
@_all_autoload_paths ||= (config.autoload_paths + config.eager_load_paths + config.autoload_once_paths).uniq
end
- def _all_load_paths
- @_all_load_paths ||= (config.paths.load_paths + _all_autoload_paths).uniq
+ def _all_load_paths(add_autoload_paths_to_load_path)
+ @_all_load_paths ||= begin
+ load_paths = config.paths.load_paths
+ load_paths += _all_autoload_paths if add_autoload_paths_to_load_path
+ load_paths.uniq
+ end
end
def build_request(env)
diff --git a/railties/lib/rails/engine/configuration.rb b/railties/lib/rails/engine/configuration.rb
index 4143b3c881..612bd170c6 100644
--- a/railties/lib/rails/engine/configuration.rb
+++ b/railties/lib/rails/engine/configuration.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true
require "rails/railtie/configuration"
+require "yaml"
module Rails
class Engine
@@ -40,7 +41,7 @@ module Rails
paths.add "app", eager_load: true,
glob: "{*,*/concerns}",
- exclude: %w(assets javascript)
+ exclude: ["assets", webpacker_path]
paths.add "app/assets", glob: "*"
paths.add "app/controllers", eager_load: true
paths.add "app/channels", eager_load: true, glob: "**/*_channel.rb"
@@ -85,6 +86,14 @@ module Rails
def autoload_paths
@autoload_paths ||= paths.autoload_paths
end
+
+ def webpacker_path
+ if File.file?("#{Rails.root}/config/webpacker.yml")
+ YAML.load_file("#{Rails.root}/config/webpacker.yml")[Rails.env]["source_path"]&.gsub("app/", "")
+ else
+ "javascript"
+ end
+ end
end
end
end
diff --git a/railties/lib/rails/gem_version.rb b/railties/lib/rails/gem_version.rb
index fea24810f5..a6b3ccf8f8 100644
--- a/railties/lib/rails/gem_version.rb
+++ b/railties/lib/rails/gem_version.rb
@@ -8,9 +8,9 @@ module Rails
module VERSION
MAJOR = 6
- MINOR = 0
+ MINOR = 1
TINY = 0
- PRE = "beta3"
+ PRE = "alpha"
STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
end
diff --git a/railties/lib/rails/generators.rb b/railties/lib/rails/generators.rb
index b835b3f3fd..436315ce1e 100644
--- a/railties/lib/rails/generators.rb
+++ b/railties/lib/rails/generators.rb
@@ -6,8 +6,6 @@ $:.unshift(activesupport_path) if File.directory?(activesupport_path) && !$:.inc
require "thor/group"
require "rails/command"
-require "active_support"
-require "active_support/core_ext/object/blank"
require "active_support/core_ext/kernel/singleton_class"
require "active_support/core_ext/array/extract_options"
require "active_support/core_ext/hash/deep_merge"
@@ -35,6 +33,8 @@ module Rails
rails: {
actions: "-a",
orm: "-o",
+ javascripts: "-j",
+ javascript_engine: "-je",
resource_controller: "-c",
scaffold_controller: "-c",
stylesheets: "-y",
@@ -285,7 +285,6 @@ module Rails
end
private
-
def print_list(base, namespaces) # :doc:
namespaces = namespaces.reject { |n| hidden_namespaces.include?(n) }
super
diff --git a/railties/lib/rails/generators/actions.rb b/railties/lib/rails/generators/actions.rb
index 1a5f2ff203..b6225cd8c0 100644
--- a/railties/lib/rails/generators/actions.rb
+++ b/railties/lib/rails/generators/actions.rb
@@ -40,8 +40,7 @@ module Rails
in_root do
str = "gem #{parts.join(", ")}"
str = indentation + str
- str = "\n" + str
- append_file "Gemfile", str, verbose: false
+ append_file_with_newline "Gemfile", str, verbose: false
end
end
@@ -58,9 +57,9 @@ module Rails
log :gemfile, "group #{str}"
in_root do
- append_file "Gemfile", "\ngroup #{str} do", force: true
+ append_file_with_newline "Gemfile", "\ngroup #{str} do", force: true
with_indentation(&block)
- append_file "Gemfile", "\nend\n", force: true
+ append_file_with_newline "Gemfile", "end", force: true
end
end
@@ -71,9 +70,13 @@ module Rails
log :github, "github #{str}"
in_root do
- append_file "Gemfile", "\n#{indentation}github #{str} do", force: true
+ if @indentation.zero?
+ append_file_with_newline "Gemfile", "\ngithub #{str} do", force: true
+ else
+ append_file_with_newline "Gemfile", "#{indentation}github #{str} do", force: true
+ end
with_indentation(&block)
- append_file "Gemfile", "\n#{indentation}end", force: true
+ append_file_with_newline "Gemfile", "#{indentation}end", force: true
end
end
@@ -91,9 +94,9 @@ module Rails
in_root do
if block
- append_file "Gemfile", "\nsource #{quote(source)} do", force: true
+ append_file_with_newline "Gemfile", "\nsource #{quote(source)} do", force: true
with_indentation(&block)
- append_file "Gemfile", "\nend\n", force: true
+ append_file_with_newline "Gemfile", "end", force: true
else
prepend_file "Gemfile", "source #{quote(source)}\n", verbose: false
end
@@ -268,7 +271,6 @@ module Rails
end
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.
@@ -345,6 +347,13 @@ module Rails
ensure
@indentation -= 1
end
+
+ # Append string to a file with a newline if necessary
+ def append_file_with_newline(path, str, options = {})
+ gsub_file path, /\n?\z/, options do |match|
+ match.end_with?("\n") ? "" : "\n#{str}\n"
+ end
+ end
end
end
end
diff --git a/railties/lib/rails/generators/actions/create_migration.rb b/railties/lib/rails/generators/actions/create_migration.rb
index 05bc242447..67a8139cd3 100644
--- a/railties/lib/rails/generators/actions/create_migration.rb
+++ b/railties/lib/rails/generators/actions/create_migration.rb
@@ -40,7 +40,6 @@ module Rails
alias :exists? :existing_migration
private
-
def on_conflict_behavior # :doc:
options = base.options.merge(config)
if identical?
diff --git a/railties/lib/rails/generators/app_base.rb b/railties/lib/rails/generators/app_base.rb
index 66f6b57379..dbfb7337f0 100644
--- a/railties/lib/rails/generators/app_base.rb
+++ b/railties/lib/rails/generators/app_base.rb
@@ -108,7 +108,6 @@ module Rails
end
private
-
def gemfile_entry(name, *args) # :doc:
options = args.extract_options!
version = args.first
@@ -307,7 +306,7 @@ module Rails
def assets_gemfile_entry
return [] if options[:skip_sprockets]
- GemfileEntry.version("sass-rails", "~> 5.0", "Use SCSS for stylesheets")
+ GemfileEntry.version("sass-rails", ">= 5", "Use SCSS for stylesheets")
end
def webpacker_gemfile_entry
@@ -322,7 +321,7 @@ module Rails
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]
+ GemfileEntry.new "jbuilder", "~> 2.7", comment, {}, options[:api]
end
def javascript_gemfile_entry
diff --git a/railties/lib/rails/generators/base.rb b/railties/lib/rails/generators/base.rb
index 5523a3f659..a153923ce3 100644
--- a/railties/lib/rails/generators/base.rb
+++ b/railties/lib/rails/generators/base.rb
@@ -20,6 +20,8 @@ module Rails
class_option :skip_namespace, type: :boolean, default: false,
desc: "Skip namespace (affects only isolated applications)"
+ class_option :skip_collision_check, type: :boolean, default: false,
+ desc: "Skip collision check"
add_runtime_options!
strict_args_position!
@@ -245,11 +247,11 @@ module Rails
end
private
-
# Check whether the given class names are already taken by user
# application or Ruby on Rails.
def class_collisions(*class_names)
return unless behavior == :invoke
+ return if options.skip_collision_check?
class_names.flatten.each do |class_name|
class_name = class_name.to_s
@@ -262,8 +264,8 @@ module Rails
if last && last.const_defined?(last_name.camelize, false)
raise Error, "The name '#{class_name}' is either already used in your application " \
- "or reserved by Ruby on Rails. Please choose an alternative and run " \
- "this generator again."
+ "or reserved by Ruby on Rails. Please choose an alternative or use --skip-collision-check " \
+ "to skip this check and run this generator again."
end
end
end
diff --git a/railties/lib/rails/generators/erb.rb b/railties/lib/rails/generators/erb.rb
index ba20bcd32a..aab2d634d3 100644
--- a/railties/lib/rails/generators/erb.rb
+++ b/railties/lib/rails/generators/erb.rb
@@ -6,7 +6,6 @@ module Erb # :nodoc:
module Generators # :nodoc:
class Base < Rails::Generators::NamedBase #:nodoc:
private
-
def formats
[format]
end
diff --git a/railties/lib/rails/generators/erb/mailer/mailer_generator.rb b/railties/lib/rails/generators/erb/mailer/mailer_generator.rb
index 997602cb8c..f66be1ee44 100644
--- a/railties/lib/rails/generators/erb/mailer/mailer_generator.rb
+++ b/railties/lib/rails/generators/erb/mailer/mailer_generator.rb
@@ -29,7 +29,6 @@ module Erb # :nodoc:
end
private
-
def formats
[:text, :html]
end
diff --git a/railties/lib/rails/generators/erb/scaffold/scaffold_generator.rb b/railties/lib/rails/generators/erb/scaffold/scaffold_generator.rb
index 2fc04e4094..6d3bccab0b 100644
--- a/railties/lib/rails/generators/erb/scaffold/scaffold_generator.rb
+++ b/railties/lib/rails/generators/erb/scaffold/scaffold_generator.rb
@@ -24,7 +24,6 @@ module Erb # :nodoc:
end
private
-
def available_views
%w(index edit show new _form)
end
diff --git a/railties/lib/rails/generators/erb/scaffold/templates/show.html.erb.tt b/railties/lib/rails/generators/erb/scaffold/templates/show.html.erb.tt
index 6b216001d2..d3f996188c 100644
--- a/railties/lib/rails/generators/erb/scaffold/templates/show.html.erb.tt
+++ b/railties/lib/rails/generators/erb/scaffold/templates/show.html.erb.tt
@@ -4,7 +4,7 @@
<p>
<strong><%= attribute.human_name %>:</strong>
<% if attribute.attachment? -%>
- <%%= link_to @<%= singular_table_name %>.<%= attribute.column_name %>.filename, @<%= singular_table_name %>.<%= attribute.column_name %> %>
+ <%%= link_to @<%= singular_table_name %>.<%= attribute.column_name %>.filename, @<%= singular_table_name %>.<%= attribute.column_name %> if @<%= singular_table_name %>.<%= attribute.column_name %>.attached? %>
<% elsif attribute.attachments? -%>
<%% @<%= singular_table_name %>.<%= attribute.column_name %>.each do |<%= attribute.singular_name %>| %>
<div><%%= link_to <%= attribute.singular_name %>.filename, <%= attribute.singular_name %> %></div>
diff --git a/railties/lib/rails/generators/generated_attribute.rb b/railties/lib/rails/generators/generated_attribute.rb
index 99c1bc4269..4e348be9be 100644
--- a/railties/lib/rails/generators/generated_attribute.rb
+++ b/railties/lib/rails/generators/generated_attribute.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true
require "active_support/time"
+require "active_support/deprecation"
module Rails
module Generators
@@ -38,7 +39,6 @@ module Rails
end
private
-
# parse possible attribute options like :limit for string/text/binary/integer, :precision/:scale for decimals or :polymorphic for references/belongs_to
# when declaring options curly brackets should be used
def parse_type_and_options(type)
@@ -51,6 +51,12 @@ module Rails
type = $1
provided_options = $2.split(/[,.-]/)
options = Hash[provided_options.map { |opt| [opt.to_sym, true] }]
+
+ if options[:required]
+ ActiveSupport::Deprecation.warn("Passing {required} option has no effect on the model generator. It will be removed in Rails 6.1.\n")
+ options.delete(:required)
+ end
+
return type, options
else
return type, {}
@@ -137,7 +143,7 @@ module Rails
end
def required?
- attr_options[:required]
+ reference? && Rails.application.config.active_record.belongs_to_required_by_default
end
def has_index?
@@ -183,7 +189,6 @@ module Rails
def options_for_migration
@attr_options.dup.tap do |options|
if required?
- options.delete(:required)
options[:null] = false
end
diff --git a/railties/lib/rails/generators/named_base.rb b/railties/lib/rails/generators/named_base.rb
index 42e64cd11f..d6732f8ff1 100644
--- a/railties/lib/rails/generators/named_base.rb
+++ b/railties/lib/rails/generators/named_base.rb
@@ -187,7 +187,6 @@ module Rails
def attributes_names # :doc:
@attributes_names ||= attributes.each_with_object([]) do |a, names|
- next if a.attachments?
names << a.column_name
names << "password_confirmation" if a.password_digest?
names << "#{a.name}_type" if a.polymorphic?
diff --git a/railties/lib/rails/generators/rails/app/app_generator.rb b/railties/lib/rails/generators/rails/app/app_generator.rb
index f2f46d6e25..ea3968bf39 100644
--- a/railties/lib/rails/generators/rails/app/app_generator.rb
+++ b/railties/lib/rails/generators/rails/app/app_generator.rb
@@ -243,7 +243,9 @@ module Rails
# can change in Ruby 1.8.7 when we FileUtils.cd.
RAILS_DEV_PATH = File.expand_path("../../../../../..", __dir__)
- class AppGenerator < AppBase # :nodoc:
+ class AppGenerator < AppBase
+ # :stopdoc:
+
WEBPACKS = %w( react vue angular elm stimulus )
add_shared_options_for "application"
@@ -492,8 +494,9 @@ module Rails
"rails new #{arguments.map(&:usage).join(' ')} [options]"
end
- private
+ # :startdoc:
+ private
# Define file as an alias to create_file for backwards compatibility.
def file(*args, &block)
create_file(*args, &block)
@@ -538,7 +541,6 @@ module Rails
end
private
-
def handle_version_request!(argument)
if ["--version", "-v"].include?(argument)
require "rails/version"
diff --git a/railties/lib/rails/generators/rails/app/templates/Gemfile.tt b/railties/lib/rails/generators/rails/app/templates/Gemfile.tt
index d7221453e7..cf5462f7dc 100644
--- a/railties/lib/rails/generators/rails/app/templates/Gemfile.tt
+++ b/railties/lib/rails/generators/rails/app/templates/Gemfile.tt
@@ -28,7 +28,7 @@ ruby <%= "'#{RUBY_VERSION}'" -%>
<% if depend_on_bootsnap? -%>
# Reduces boot times through caching; required in config/boot.rb
-gem 'bootsnap', '>= 1.4.2', require: false
+gem 'bootsnap', '>= 1.4.4', require: false
<%- end -%>
<%- if options.api? -%>
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 9a7267c783..b8c1f21c0b 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
@@ -6,7 +6,7 @@
<%%= csp_meta_tag %>
<%- if options[:skip_javascript] -%>
- <%%= stylesheet_link_tag 'application', media: 'all' %>
+ <%%= stylesheet_link_tag 'application', media: 'all' %>
<%- else -%>
<%- unless options[:skip_turbolinks] -%>
<%%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
diff --git a/railties/lib/rails/generators/rails/app/templates/bin/setup.tt b/railties/lib/rails/generators/rails/app/templates/bin/setup.tt
index 3f73bae3da..5928deb6aa 100644
--- a/railties/lib/rails/generators/rails/app/templates/bin/setup.tt
+++ b/railties/lib/rails/generators/rails/app/templates/bin/setup.tt
@@ -8,7 +8,8 @@ def system!(*args)
end
FileUtils.chdir APP_ROOT do
- # This script is a starting point to setup your application.
+ # This script is a way to setup or update your development environment automatically.
+ # This script is idempotent, so that you can run it at anytime and get an expectable outcome.
# Add necessary setup steps to this file.
puts '== Installing dependencies =='
@@ -27,7 +28,7 @@ FileUtils.chdir APP_ROOT do
# end
puts "\n== Preparing database =="
- system! 'bin/rails db:setup'
+ system! 'bin/rails db:prepare'
<% end -%>
puts "\n== Removing old logs and tempfiles =="
diff --git a/railties/lib/rails/generators/rails/app/templates/bin/update.tt b/railties/lib/rails/generators/rails/app/templates/bin/update.tt
deleted file mode 100644
index 03b77d0d46..0000000000
--- a/railties/lib/rails/generators/rails/app/templates/bin/update.tt
+++ /dev/null
@@ -1,33 +0,0 @@
-require 'fileutils'
-
-# path to your application root.
-APP_ROOT = File.expand_path('..', __dir__)
-
-def system!(*args)
- system(*args) || abort("\n== Command #{args} failed ==")
-end
-
-FileUtils.chdir APP_ROOT do
- # This script is a way to update your development environment automatically.
- # Add necessary update steps to this file.
-
- puts '== Installing dependencies =='
- system! 'gem install bundler --conservative'
- system('bundle check') || system!('bundle install')
-<% unless options.skip_javascript? -%>
-
- # Install JavaScript dependencies
- # system('bin/yarn')
-<% end -%>
-<% unless options.skip_active_record? -%>
-
- puts "\n== Updating database =="
- system! 'rails db:migrate'
-<% end -%>
-
- puts "\n== Removing old logs and tempfiles =="
- system! 'rails log:clear tmp:clear'
-
- puts "\n== Restarting application server =="
- system! 'rails restart'
-end
diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/jdbcsqlite3.yml.tt b/railties/lib/rails/generators/rails/app/templates/config/databases/jdbcsqlite3.yml.tt
index 371415e6a8..006b0a74c3 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/databases/jdbcsqlite3.yml.tt
+++ b/railties/lib/rails/generators/rails/app/templates/config/databases/jdbcsqlite3.yml.tt
@@ -1,4 +1,4 @@
-# SQLite version 3.x
+# SQLite. Versions 3.8.0 and up are supported.
# gem 'activerecord-jdbcsqlite3-adapter'
#
# Configure Using Gemfile
diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/sqlite3.yml.tt b/railties/lib/rails/generators/rails/app/templates/config/databases/sqlite3.yml.tt
index 9510568124..a7c2bf2eac 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/databases/sqlite3.yml.tt
+++ b/railties/lib/rails/generators/rails/app/templates/config/databases/sqlite3.yml.tt
@@ -1,4 +1,4 @@
-# SQLite version 3.x
+# SQLite. Versions 3.8.0 and up are supported.
# gem install sqlite3
#
# Ensure the SQLite 3 gem is defined in your Gemfile
diff --git a/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt
index 2887bc2d67..404ab8b5de 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt
+++ b/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt
@@ -15,9 +15,11 @@ Rails.application.configure do
# Enable/disable caching. By default caching is disabled.
# Run rails dev:cache to toggle caching.
if Rails.root.join('tmp', 'caching-dev.txt').exist?
+ <%- unless options.api? -%>
config.action_controller.perform_caching = true
config.action_controller.enable_fragment_cache_logging = true
+ <%- end -%>
config.cache_store = :memory_store
config.public_file_server.headers = {
'Cache-Control' => "public, max-age=#{2.days.to_i}"
diff --git a/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt
index ed1cf09eeb..08d437a0be 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt
+++ b/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt
@@ -12,7 +12,9 @@ Rails.application.configure do
# Full error reports are disabled and caching is turned on.
config.consider_all_requests_local = false
+ <%- unless options.api? -%>
config.action_controller.perform_caching = true
+ <%- end -%>
# Ensures that a master key has been made available in either ENV["RAILS_MASTER_KEY"]
# or in config/master.key. This key is used to decrypt credentials (and other encrypted files).
diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/content_security_policy.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/initializers/content_security_policy.rb.tt
index c517b0f96b..3d468f7633 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/initializers/content_security_policy.rb.tt
+++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/content_security_policy.rb.tt
@@ -23,6 +23,9 @@
# If you are using UJS then enable automatic nonce generation
# Rails.application.config.content_security_policy_nonce_generator = -> request { SecureRandom.base64(16) }
+# Set the nonce only to specific directives
+# Rails.application.config.content_security_policy_nonce_directives = %w(script-src)
+
# Report CSP violations to a specified URI
# For further information see the following documentation:
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only
diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/feature_policy.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/initializers/feature_policy.rb.tt
new file mode 100644
index 0000000000..a1c46695d2
--- /dev/null
+++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/feature_policy.rb.tt
@@ -0,0 +1,11 @@
+# Define an application-wide HTTP feature policy. For further
+# information see https://developers.google.com/web/updates/2018/06/feature-policy
+#
+# Rails.application.config.feature_policy do |f|
+# f.camera :none
+# f.gyroscope :none
+# f.microphone :none
+# f.usb :none
+# f.fullscreen :self
+# f.payment :self, "https://secure.example.com"
+# end
diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/filter_parameter_logging.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/initializers/filter_parameter_logging.rb.tt
index 4a994e1e7b..eea99edb65 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/initializers/filter_parameter_logging.rb.tt
+++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/filter_parameter_logging.rb.tt
@@ -1,4 +1,6 @@
# Be sure to restart your server when you modify this file.
# Configure sensitive parameters which will be filtered from the log file.
-Rails.application.config.filter_parameters += [:password]
+Rails.application.config.filter_parameters += [
+ :password, :secret, :token, :_key, :auth, :crypt, :salt, :certificate, :otp, :access, :private, :protected, :ssn
+]
diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults_6_0.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults_6_0.rb.tt
index d25552e923..2510ab906f 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults_6_0.rb.tt
+++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults_6_0.rb.tt
@@ -16,6 +16,9 @@
# It's best enabled when your entire app is migrated and stable on 6.0.
# Rails.application.config.action_dispatch.use_cookies_with_metadata = true
+# Change the return value of `ActionDispatch::Response#content_type` to Content-Type header without modification.
+# Rails.application.config.action_dispatch.return_only_media_type_on_content_type = true
+
# Return false instead of self when enqueuing is aborted from a callback.
# Rails.application.config.active_job.return_false_on_aborted_enqueue = true
@@ -23,6 +26,10 @@
# Rails.application.config.active_storage.queues.analysis = :active_storage_analysis
# Rails.application.config.active_storage.queues.purge = :active_storage_purge
+# When assigning to a collection of attachments declared via `has_many_attached`, replace existing
+# attachments instead of appending. Use #attach to add new attachments without replacing existing ones.
+# Rails.application.config.active_storage.replace_on_assign_to_many = true
+
# Use ActionMailer::MailDeliveryJob for sending parameterized and normal mail.
#
# The default delivery jobs (ActionMailer::Parameterized::DeliveryJob, ActionMailer::DeliveryJob),
@@ -31,3 +38,8 @@
# MailDeliveryJob to ensure all delivery jobs are processed properly.
# Make sure your entire app is migrated and stable on 6.0 before using this setting.
# Rails.application.config.action_mailer.delivery_job = "ActionMailer::MailDeliveryJob"
+
+# Enable the same cache key to be reused when the object being cached of type
+# `ActiveRecord::Relation` changes by moving the volatile information (max updated at and count)
+# of the relation's cache key into the cache version to support recycling cache key.
+# Rails.application.config.active_record.collection_cache_versioning = true
diff --git a/railties/lib/rails/generators/rails/app/templates/config/puma.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/puma.rb.tt
index 649253aeca..5ed4437744 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/puma.rb.tt
+++ b/railties/lib/rails/generators/rails/app/templates/config/puma.rb.tt
@@ -16,6 +16,9 @@ port ENV.fetch("PORT") { 3000 }
#
environment ENV.fetch("RAILS_ENV") { "development" }
+# Specifies the `pidfile` that Puma will use.
+pidfile ENV.fetch("PIDFILE") { "tmp/pids/server.pid" }
+
# Specifies the number of `workers` to boot in clustered mode.
# Workers are forked web server processes. If using threads and workers together
# the concurrency of the application would be max `threads` * `workers`.
diff --git a/railties/lib/rails/generators/rails/app/templates/public/robots.txt b/railties/lib/rails/generators/rails/app/templates/public/robots.txt
index 37b576a4a0..c19f78ab68 100644
--- a/railties/lib/rails/generators/rails/app/templates/public/robots.txt
+++ b/railties/lib/rails/generators/rails/app/templates/public/robots.txt
@@ -1 +1 @@
-# See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file
+# See https://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file
diff --git a/railties/lib/rails/generators/rails/assets/assets_generator.rb b/railties/lib/rails/generators/rails/assets/assets_generator.rb
index 9ce8570172..e60637ff37 100644
--- a/railties/lib/rails/generators/rails/assets/assets_generator.rb
+++ b/railties/lib/rails/generators/rails/assets/assets_generator.rb
@@ -3,7 +3,10 @@
module Rails
module Generators
class AssetsGenerator < NamedBase # :nodoc:
+ class_option :javascripts, type: :boolean, desc: "Generate JavaScripts"
class_option :stylesheets, type: :boolean, desc: "Generate Stylesheets"
+
+ class_option :javascript_engine, desc: "Engine for JavaScripts"
class_option :stylesheet_engine, desc: "Engine for Stylesheets"
private
@@ -11,6 +14,10 @@ module Rails
file_name
end
+ hook_for :javascript_engine do |javascript_engine|
+ invoke javascript_engine, [name] if options[:javascripts]
+ end
+
hook_for :stylesheet_engine do |stylesheet_engine|
invoke stylesheet_engine, [name] if options[:stylesheets]
end
diff --git a/railties/lib/rails/generators/rails/controller/controller_generator.rb b/railties/lib/rails/generators/rails/controller/controller_generator.rb
index eb75e7e661..88729545f3 100644
--- a/railties/lib/rails/generators/rails/controller/controller_generator.rb
+++ b/railties/lib/rails/generators/rails/controller/controller_generator.rb
@@ -25,7 +25,6 @@ module Rails
end
private
-
def file_name
@_file_name ||= remove_possible_suffix(super)
end
diff --git a/railties/lib/rails/generators/rails/generator/generator_generator.rb b/railties/lib/rails/generators/rails/generator/generator_generator.rb
index 747acd68d1..b0146641b4 100644
--- a/railties/lib/rails/generators/rails/generator/generator_generator.rb
+++ b/railties/lib/rails/generators/rails/generator/generator_generator.rb
@@ -15,7 +15,6 @@ module Rails
hook_for :test_framework
private
-
def generator_dir
if options[:namespace]
File.join("lib", "generators", regular_class_path, file_name)
diff --git a/railties/lib/rails/generators/rails/plugin/plugin_generator.rb b/railties/lib/rails/generators/rails/plugin/plugin_generator.rb
index 895b3b2e92..4c18bdb430 100644
--- a/railties/lib/rails/generators/rails/plugin/plugin_generator.rb
+++ b/railties/lib/rails/generators/rails/plugin/plugin_generator.rb
@@ -269,7 +269,6 @@ task default: :test
end
private
-
def create_dummy_app(path = nil)
dummy_path(path) if path
diff --git a/railties/lib/rails/generators/rails/scaffold_controller/scaffold_controller_generator.rb b/railties/lib/rails/generators/rails/scaffold_controller/scaffold_controller_generator.rb
index 8b46eb88ae..b2cdeee6d1 100644
--- a/railties/lib/rails/generators/rails/scaffold_controller/scaffold_controller_generator.rb
+++ b/railties/lib/rails/generators/rails/scaffold_controller/scaffold_controller_generator.rb
@@ -34,11 +34,16 @@ module Rails
end
private
-
def permitted_params
- params = attributes_names.map { |name| ":#{name}" }.join(", ")
- params += attributes.select(&:attachments?).map { |a| ", #{a.name}: []" }.join
- params
+ attachments, others = attributes_names.partition { |name| attachments?(name) }
+ params = others.map { |name| ":#{name}" }
+ params += attachments.map { |name| "#{name}: []" }
+ params.join(", ")
+ end
+
+ def attachments?(name)
+ attribute = attributes.find { |attr| attr.name == name }
+ attribute&.attachments?
end
end
end
diff --git a/railties/lib/rails/generators/test_unit/controller/templates/functional_test.rb.tt b/railties/lib/rails/generators/test_unit/controller/templates/functional_test.rb.tt
index ff41fef9e9..a69a24e281 100644
--- a/railties/lib/rails/generators/test_unit/controller/templates/functional_test.rb.tt
+++ b/railties/lib/rails/generators/test_unit/controller/templates/functional_test.rb.tt
@@ -16,7 +16,7 @@ class <%= class_name %>ControllerTest < ActionDispatch::IntegrationTest
get <%= url_helper_prefix %>_<%= action %>_url
assert_response :success
end
-
+<%= "\n" unless action == actions.last -%>
<% end -%>
<% 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 19be4f2f51..ae7e28580e 100644
--- a/railties/lib/rails/generators/test_unit/generator/generator_generator.rb
+++ b/railties/lib/rails/generators/test_unit/generator/generator_generator.rb
@@ -15,7 +15,6 @@ module TestUnit # :nodoc:
end
private
-
def generator_path
if options[:namespace]
File.join("generators", regular_class_path, file_name, "#{file_name}_generator")
diff --git a/railties/lib/rails/generators/test_unit/integration/integration_generator.rb b/railties/lib/rails/generators/test_unit/integration/integration_generator.rb
index ba27ed329b..86fea3f677 100644
--- a/railties/lib/rails/generators/test_unit/integration/integration_generator.rb
+++ b/railties/lib/rails/generators/test_unit/integration/integration_generator.rb
@@ -12,7 +12,6 @@ module TestUnit # :nodoc:
end
private
-
def file_name
@_file_name ||= super.sub(/_test\z/i, "")
end
diff --git a/railties/lib/rails/generators/test_unit/scaffold/scaffold_generator.rb b/railties/lib/rails/generators/test_unit/scaffold/scaffold_generator.rb
index 6df50c3217..a4bc81cad6 100644
--- a/railties/lib/rails/generators/test_unit/scaffold/scaffold_generator.rb
+++ b/railties/lib/rails/generators/test_unit/scaffold/scaffold_generator.rb
@@ -38,7 +38,6 @@ module TestUnit # :nodoc:
end
private
-
def attributes_string
attributes_hash.map { |k, v| "#{k}: #{v}" }.join(", ")
end
@@ -49,16 +48,21 @@ module TestUnit # :nodoc:
attributes_names.map do |name|
if %w(password password_confirmation).include?(name) && attributes.any?(&:password_digest?)
["#{name}", "'secret'"]
- else
+ elsif !virtual?(name)
["#{name}", "@#{singular_table_name}.#{name}"]
end
- end.sort.to_h
+ end.compact.sort.to_h
end
def boolean?(name)
attribute = attributes.find { |attr| attr.name == name }
attribute&.type == :boolean
end
+
+ def virtual?(name)
+ attribute = attributes.find { |attr| attr.name == name }
+ attribute&.virtual?
+ end
end
end
end
diff --git a/railties/lib/rails/generators/testing/behaviour.rb b/railties/lib/rails/generators/testing/behaviour.rb
index ec29ad12ba..a092faec89 100644
--- a/railties/lib/rails/generators/testing/behaviour.rb
+++ b/railties/lib/rails/generators/testing/behaviour.rb
@@ -88,7 +88,6 @@ module Rails
end
private
-
def destination_root_is_set?
raise "You need to configure your Rails::Generators::TestCase destination root." unless destination_root
end
diff --git a/railties/lib/rails/info_controller.rb b/railties/lib/rails/info_controller.rb
index f74d979721..d51010d422 100644
--- a/railties/lib/rails/info_controller.rb
+++ b/railties/lib/rails/info_controller.rb
@@ -33,7 +33,6 @@ class Rails::InfoController < Rails::ApplicationController # :nodoc:
end
private
-
def match_route
_routes.routes.select { |route|
yield route.path
diff --git a/railties/lib/rails/mailers_controller.rb b/railties/lib/rails/mailers_controller.rb
index 5cffa52860..4a1942790b 100644
--- a/railties/lib/rails/mailers_controller.rb
+++ b/railties/lib/rails/mailers_controller.rb
@@ -5,8 +5,9 @@ require "rails/application_controller"
class Rails::MailersController < Rails::ApplicationController # :nodoc:
prepend_view_path ActionDispatch::DebugView::RESCUES_TEMPLATE_PATH
+ around_action :set_locale, only: :preview
+ before_action :find_preview, only: :preview
before_action :require_local!, unless: :show_previews?
- before_action :find_preview, :set_locale, only: :preview
helper_method :part_query, :locale_query
@@ -92,6 +93,8 @@ class Rails::MailersController < Rails::ApplicationController # :nodoc:
end
def set_locale
- I18n.locale = params[:locale] || I18n.default_locale
+ I18n.with_locale(params[:locale] || I18n.default_locale) do
+ yield
+ end
end
end
diff --git a/railties/lib/rails/paths.rb b/railties/lib/rails/paths.rb
index 8367ac8980..0664338e0b 100644
--- a/railties/lib/rails/paths.rb
+++ b/railties/lib/rails/paths.rb
@@ -98,7 +98,6 @@ module Rails
end
private
-
def filter_by(&block)
all_paths.find_all(&block).flat_map { |path|
paths = path.existent
@@ -223,14 +222,11 @@ module Rails
alias to_a expanded
private
-
def files_in(path)
- Dir.chdir(path) do
- files = Dir.glob(@glob)
- files -= @exclude if @exclude
- files.map! { |file| File.join(path, file) }
- files.sort
- end
+ files = Dir.glob(@glob, base: path)
+ files -= @exclude if @exclude
+ files.map! { |file| File.join(path, file) }
+ files.sort
end
end
end
diff --git a/railties/lib/rails/rack/logger.rb b/railties/lib/rails/rack/logger.rb
index 3a95b55811..8d69663dbd 100644
--- a/railties/lib/rails/rack/logger.rb
+++ b/railties/lib/rails/rack/logger.rb
@@ -30,7 +30,6 @@ module Rails
end
private
-
def call_app(request, env) # :doc:
instrumenter = ActiveSupport::Notifications.instrumenter
instrumenter.start "request.action_dispatch", request: request
diff --git a/railties/lib/rails/railtie.rb b/railties/lib/rails/railtie.rb
index a67b90e285..178c584f95 100644
--- a/railties/lib/rails/railtie.rb
+++ b/railties/lib/rails/railtie.rb
@@ -228,7 +228,6 @@ module Rails
end
protected
-
def run_console_blocks(app) #:nodoc:
each_registered_block(:console) { |block| block.call(app) }
end
@@ -247,7 +246,6 @@ module Rails
end
private
-
# run `&block` in every registered block in `#register_block_for`
def each_registered_block(type, &block)
klass = self.class
diff --git a/railties/lib/rails/railtie/configurable.rb b/railties/lib/rails/railtie/configurable.rb
index 7f42fae10a..ba14089a2a 100644
--- a/railties/lib/rails/railtie/configurable.rb
+++ b/railties/lib/rails/railtie/configurable.rb
@@ -27,7 +27,6 @@ module Rails
end
private
-
def method_missing(*args, &block)
instance.send(*args, &block)
end
diff --git a/railties/lib/rails/railtie/configuration.rb b/railties/lib/rails/railtie/configuration.rb
index 70274b948c..c90fcac51a 100644
--- a/railties/lib/rails/railtie/configuration.rb
+++ b/railties/lib/rails/railtie/configuration.rb
@@ -87,7 +87,6 @@ module Rails
end
private
-
def method_missing(name, *args, &blk)
if name.to_s =~ /=$/
@@options[$`.to_sym] = args.first
diff --git a/railties/lib/rails/source_annotation_extractor.rb b/railties/lib/rails/source_annotation_extractor.rb
index 9ce22b96a6..77a99036ec 100644
--- a/railties/lib/rails/source_annotation_extractor.rb
+++ b/railties/lib/rails/source_annotation_extractor.rb
@@ -2,11 +2,6 @@
require "active_support/deprecation"
-# Remove this deprecated class in the next minor version
-#:nodoc:
-SourceAnnotationExtractor = ActiveSupport::Deprecation::DeprecatedConstantProxy.
- new("SourceAnnotationExtractor", "Rails::SourceAnnotationExtractor")
-
module Rails
# Implements the logic behind <tt>Rails::Command::NotesCommand</tt>. See <tt>rails notes --help</tt> for usage information.
#
@@ -160,3 +155,8 @@ module Rails
end
end
end
+
+# Remove this deprecated class in the next minor version
+#:nodoc:
+SourceAnnotationExtractor = ActiveSupport::Deprecation::DeprecatedConstantProxy.
+ new("SourceAnnotationExtractor", "Rails::SourceAnnotationExtractor")
diff --git a/railties/lib/rails/tasks.rb b/railties/lib/rails/tasks.rb
index 2f644a20c9..0e592e15c3 100644
--- a/railties/lib/rails/tasks.rb
+++ b/railties/lib/rails/tasks.rb
@@ -15,6 +15,7 @@ require "rake"
routes
tmp
yarn
+ zeitwerk
).tap { |arr|
arr << "statistics" if Rake.application.current_scope.empty?
}.each do |task|
diff --git a/railties/lib/rails/tasks/zeitwerk.rake b/railties/lib/rails/tasks/zeitwerk.rake
new file mode 100644
index 0000000000..5421af6e8b
--- /dev/null
+++ b/railties/lib/rails/tasks/zeitwerk.rake
@@ -0,0 +1,66 @@
+# frozen_string_literal: true
+
+ensure_zeitwerk_mode = ->() do
+ unless Rails.autoloaders.zeitwerk_enabled?
+ abort "Please, enable :zeitwerk mode in config/application.rb and try again."
+ end
+end
+
+eager_load = ->() do
+ puts "Hold on, I am eager loading the application."
+ Zeitwerk::Loader.eager_load_all
+end
+
+report_not_checked = ->(not_checked) do
+ puts
+ puts <<~EOS
+ WARNING: The files in these directories cannot be checked because they
+ are not eager loaded:
+ EOS
+ puts
+
+ not_checked.each { |dir| puts " #{dir}" }
+ puts
+
+ puts <<~EOS
+ You may verify them manually, or add them to config.eager_load_paths
+ in config/application.rb and run zeitwerk:check again.
+ EOS
+ puts
+end
+
+report = ->(not_checked) do
+ if not_checked.any?
+ report_not_checked[not_checked]
+ puts "Otherwise, all is good!"
+ else
+ puts "All is good!"
+ end
+end
+
+namespace :zeitwerk do
+ desc "Checks project structure for Zeitwerk compatibility"
+ task check: :environment do
+ ensure_zeitwerk_mode[]
+
+ begin
+ eager_load[]
+ rescue NameError => e
+ if e.message =~ /expected file .*? to define constant \S+/
+ abort $&.sub(/#{Regexp.escape(Rails.root.to_s)}./, "")
+ else
+ raise
+ end
+ end
+
+ eager_load_paths = Rails.configuration.eager_load_namespaces.map do |eln|
+ eln.config.eager_load_paths if eln.respond_to?(:config)
+ end.compact.flatten
+
+ not_checked = ActiveSupport::Dependencies.autoload_paths - eager_load_paths
+ not_checked.select! { |dir| Dir.exist?(dir) }
+ not_checked.reject! { |dir| Dir.empty?(dir) }
+
+ report[not_checked]
+ end
+end
diff --git a/railties/lib/rails/test_unit/runner.rb b/railties/lib/rails/test_unit/runner.rb
index d38952bb30..7b294751fc 100644
--- a/railties/lib/rails/test_unit/runner.rb
+++ b/railties/lib/rails/test_unit/runner.rb
@@ -45,7 +45,7 @@ module Rails
patterns = extract_filters(argv)
tests = Rake::FileList[patterns.any? ? patterns : "test/**/*_test.rb"]
- tests.exclude("test/system/**/*") if patterns.empty?
+ tests.exclude("test/system/**/*", "test/dummy/**/*") if patterns.empty?
tests.to_a.each { |path| require File.expand_path(path) }
end
diff --git a/railties/test/application/assets_test.rb b/railties/test/application/assets_test.rb
index a80581211b..a7dd233f3d 100644
--- a/railties/test/application/assets_test.rb
+++ b/railties/test/application/assets_test.rb
@@ -491,7 +491,6 @@ module ApplicationTests
end
private
-
def app_with_assets_in_view
app_file "app/assets/javascripts/application.js", "//= require_tree ."
app_file "app/assets/javascripts/xmlhr.js", "function f1() { alert(); }"
diff --git a/railties/test/application/bin_setup_test.rb b/railties/test/application/bin_setup_test.rb
index a952d2466b..d84ab61cf9 100644
--- a/railties/test/application/bin_setup_test.rb
+++ b/railties/test/application/bin_setup_test.rb
@@ -6,21 +6,12 @@ module ApplicationTests
class BinSetupTest < ActiveSupport::TestCase
include ActiveSupport::Testing::Isolation
- def setup
- build_app
- end
-
- def teardown
- teardown_app
- end
+ setup :build_app
+ teardown :teardown_app
def test_bin_setup
Dir.chdir(app_path) do
- app_file "db/schema.rb", <<-RUBY
- ActiveRecord::Schema.define(version: 20140423102712) do
- create_table(:articles) {}
- end
- RUBY
+ rails "generate", "model", "article"
list_tables = lambda { rails("runner", "p ActiveRecord::Base.connection.tables").strip }
File.write("log/test.log", "zomg!")
@@ -28,15 +19,20 @@ module ApplicationTests
assert_equal "[]", list_tables.call
assert_equal 5, File.size("log/test.log")
assert_not File.exist?("tmp/restart.txt")
+
`bin/setup 2>&1`
assert_equal 0, File.size("log/test.log")
- assert_equal '["articles", "schema_migrations", "ar_internal_metadata"]', list_tables.call
+ assert_equal '["schema_migrations", "ar_internal_metadata", "articles"]', list_tables.call
assert File.exist?("tmp/restart.txt")
end
end
def test_bin_setup_output
Dir.chdir(app_path) do
+ # SQLite3 seems to auto-create the database on first checkout.
+ rails "db:system:change", "--to=postgresql"
+ rails "db:drop", allow_failure: true
+
app_file "db/schema.rb", ""
output = `bin/setup 2>&1`
@@ -53,8 +49,8 @@ module ApplicationTests
The Gemfile's dependencies are satisfied
== Preparing database ==
- Created database 'db/development.sqlite3'
- Created database 'db/test.sqlite3'
+ Created database 'app_development'
+ Created database 'app_test'
== Removing old logs and tempfiles ==
diff --git a/railties/test/application/configuration_test.rb b/railties/test/application/configuration_test.rb
index a2e3e781c0..38dda20bec 100644
--- a/railties/test/application/configuration_test.rb
+++ b/railties/test/application/configuration_test.rb
@@ -4,6 +4,7 @@ require "isolation/abstract_unit"
require "rack/test"
require "env_helpers"
require "set"
+require "active_support/core_ext/string/starts_ends_with"
class ::MyMailInterceptor
def self.delivering_email(email); email; end
@@ -1606,6 +1607,13 @@ module ApplicationTests
assert_not_nil Rails::SourceAnnotationExtractor::Annotation.extensions[/\.(coffee)$/]
end
+ test "config.default_log_file returns a File instance" do
+ app "development"
+
+ assert_instance_of File, app.config.default_log_file
+ assert_equal Rails.application.config.paths["log"].first, app.config.default_log_file.path
+ end
+
test "rake_tasks block works at instance level" do
app_file "config/environments/development.rb", <<-RUBY
Rails.application.configure do
@@ -1700,10 +1708,37 @@ module ApplicationTests
app "development"
ActiveSupport::Dependencies.autoload_paths.each do |path|
assert_not_operator path, :ends_with?, "app/assets"
- assert_not_operator path, :ends_with?, "app/javascript"
+ assert_not_operator path, :ends_with?, "app/#{Rails.configuration.webpacker_path}"
end
end
+ test "autoload paths are added to $LOAD_PATH by default" do
+ app "development"
+
+ # Action Mailer modifies AS::Dependencies.autoload_paths in-place.
+ autoload_paths = ActiveSupport::Dependencies.autoload_paths
+ autoload_paths_from_app_and_engines = autoload_paths.reject do |path|
+ path.ends_with?("mailers/previews")
+ end
+ assert_equal true, Rails.configuration.add_autoload_paths_to_load_path
+ assert_empty autoload_paths_from_app_and_engines - $LOAD_PATH
+
+ # Precondition, ensure we are testing something next.
+ assert_not_empty Rails.configuration.paths.load_paths
+ assert_empty Rails.configuration.paths.load_paths - $LOAD_PATH
+ end
+
+ test "autoload paths are not added to $LOAD_PATH if opted-out" do
+ add_to_config "config.add_autoload_paths_to_load_path = false"
+ app "development"
+
+ assert_empty ActiveSupport::Dependencies.autoload_paths & $LOAD_PATH
+
+ # Precondition, ensure we are testing something next.
+ assert_not_empty Rails.configuration.paths.load_paths
+ assert_empty Rails.configuration.paths.load_paths - $LOAD_PATH
+ end
+
test "autoloading during initialization gets deprecation message and clearing if config.cache_classes is false" do
app_file "lib/c.rb", <<~EOS
class C
@@ -1721,7 +1756,7 @@ module ApplicationTests
end
EOS
- app_file "config/initializers/autoload.rb", "D"
+ app_file "config/initializers/autoload.rb", "D.class"
app "development"
@@ -1749,7 +1784,7 @@ module ApplicationTests
end
EOS
- app_file "config/initializers/autoload.rb", "D"
+ app_file "config/initializers/autoload.rb", "D.class"
app "production"
@@ -1758,6 +1793,11 @@ module ApplicationTests
assert_equal [X, D], C.descendants
end
+ test "load_database_yaml returns blank hash if configuration file is blank" do
+ app_file "config/database.yml", ""
+ app "development"
+ assert_equal({}, Rails.application.config.load_database_yaml)
+ end
test "raises with proper error message if no database configuration found" do
FileUtils.rm("#{app_path}/config/database.yml")
@@ -2401,6 +2441,33 @@ module ApplicationTests
assert_nil ActiveStorage.queues[:purge]
end
+ test "ActionDispatch::Response.return_only_media_type_on_content_type is false by default" do
+ app "development"
+
+ assert_equal false, ActionDispatch::Response.return_only_media_type_on_content_type
+ end
+
+ test "ActionDispatch::Response.return_only_media_type_on_content_type is true in the 5.x defaults" do
+ remove_from_config '.*config\.load_defaults.*\n'
+ add_to_config 'config.load_defaults "5.2"'
+
+ app "development"
+
+ assert_equal true, ActionDispatch::Response.return_only_media_type_on_content_type
+ end
+
+ test "ActionDispatch::Response.return_only_media_type_on_content_type can be configured in the new framework defaults" do
+ remove_from_config '.*config\.load_defaults.*\n'
+
+ app_file "config/initializers/new_framework_defaults_6_0.rb", <<-RUBY
+ Rails.application.config.action_dispatch.return_only_media_type_on_content_type = true
+ RUBY
+
+ app "development"
+
+ assert_equal true, ActionDispatch::Response.return_only_media_type_on_content_type
+ end
+
test "ActionMailbox.logger is Rails.logger by default" do
app "development"
@@ -2526,6 +2593,21 @@ module ApplicationTests
MESSAGE
end
+ test "ActiveStorage.draw_routes can be configured via config.active_storage.draw_routes" do
+ app_file "config/environments/development.rb", <<-RUBY
+ Rails.application.configure do
+ config.active_storage.draw_routes = false
+ end
+ RUBY
+
+ output = rails("routes")
+ assert_not_includes(output, "rails_service_blob")
+ assert_not_includes(output, "rails_blob_representation")
+ assert_not_includes(output, "rails_disk_service")
+ assert_not_includes(output, "update_rails_disk_service")
+ assert_not_includes(output, "rails_direct_uploads")
+ end
+
test "hosts include .localhost in development" do
app "development"
assert_includes Rails.application.config.hosts, ".localhost"
diff --git a/railties/test/application/content_security_policy_test.rb b/railties/test/application/content_security_policy_test.rb
index 0d28df16f8..0bb6ee917a 100644
--- a/railties/test/application/content_security_policy_test.rb
+++ b/railties/test/application/content_security_policy_test.rb
@@ -119,6 +119,38 @@ module ApplicationTests
assert_policy "default-src 'self' https:", report_only: true
end
+ test "global content security policy nonce directives in an initializer" do
+ controller :pages, <<-RUBY
+ class PagesController < ApplicationController
+ def index
+ render html: "<h1>Welcome to Rails!</h1>"
+ end
+ end
+ RUBY
+
+ app_file "config/initializers/content_security_policy.rb", <<-RUBY
+ Rails.application.config.content_security_policy do |p|
+ p.default_src :self, :https
+ p.script_src :self, :https
+ p.style_src :self, :https
+ end
+
+ Rails.application.config.content_security_policy_nonce_generator = proc { "iyhD0Yc0W+c=" }
+ Rails.application.config.content_security_policy_nonce_directives = %w(script-src)
+ RUBY
+
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ root to: "pages#index"
+ end
+ RUBY
+
+ app("development")
+
+ get "/"
+ assert_policy "default-src 'self' https:; script-src 'self' https: 'nonce-iyhD0Yc0W+c='; style-src 'self' https:"
+ end
+
test "override content security policy in a controller" do
controller :pages, <<-RUBY
class PagesController < ApplicationController
@@ -204,7 +236,6 @@ module ApplicationTests
end
private
-
def assert_policy(expected, report_only: false)
assert_equal 200, last_response.status
diff --git a/railties/test/application/feature_policy_test.rb b/railties/test/application/feature_policy_test.rb
new file mode 100644
index 0000000000..e751d782ee
--- /dev/null
+++ b/railties/test/application/feature_policy_test.rb
@@ -0,0 +1,191 @@
+# frozen_string_literal: true
+
+require "isolation/abstract_unit"
+require "rack/test"
+
+module ApplicationTests
+ class FeaturePolicyTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+ include Rack::Test::Methods
+
+ def setup
+ build_app
+ end
+
+ def teardown
+ teardown_app
+ end
+
+ test "feature policy is not enabled by default" do
+ controller :pages, <<-RUBY
+ class PagesController < ApplicationController
+ def index
+ render html: "<h1>Welcome to Rails!</h1>"
+ end
+ end
+ RUBY
+
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ root to: "pages#index"
+ end
+ RUBY
+
+ app("development")
+
+ get "/"
+ assert_nil last_response.headers["Feature-Policy"]
+ end
+
+ test "global feature policy in an initializer" do
+ controller :pages, <<-RUBY
+ class PagesController < ApplicationController
+ def index
+ render html: "<h1>Welcome to Rails!</h1>"
+ end
+ end
+ RUBY
+
+ app_file "config/initializers/feature_policy.rb", <<-RUBY
+ Rails.application.config.feature_policy do |p|
+ p.geolocation :none
+ end
+ RUBY
+
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ root to: "pages#index"
+ end
+ RUBY
+
+ app("development")
+
+ get "/"
+ assert_policy "geolocation 'none'"
+ end
+
+ test "override feature policy using same directive in a controller" do
+ controller :pages, <<-RUBY
+ class PagesController < ApplicationController
+ feature_policy do |p|
+ p.geolocation "https://example.com"
+ end
+
+ def index
+ render html: "<h1>Welcome to Rails!</h1>"
+ end
+ end
+ RUBY
+
+ app_file "config/initializers/feature_policy.rb", <<-RUBY
+ Rails.application.config.feature_policy do |p|
+ p.geolocation :none
+ end
+ RUBY
+
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ root to: "pages#index"
+ end
+ RUBY
+
+ app("development")
+
+ get "/"
+ assert_policy "geolocation https://example.com"
+ end
+
+ test "override feature policy by unsetting a directive in a controller" do
+ controller :pages, <<-RUBY
+ class PagesController < ApplicationController
+ feature_policy do |p|
+ p.geolocation nil
+ end
+
+ def index
+ render html: "<h1>Welcome to Rails!</h1>"
+ end
+ end
+ RUBY
+
+ app_file "config/initializers/feature_policy.rb", <<-RUBY
+ Rails.application.config.feature_policy do |p|
+ p.geolocation :none
+ end
+ RUBY
+
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ root to: "pages#index"
+ end
+ RUBY
+
+ app("development")
+
+ get "/"
+ assert_equal 200, last_response.status
+ assert_nil last_response.headers["Feature-Policy"]
+ end
+
+ test "override feature policy using different directives in a controller" do
+ controller :pages, <<-RUBY
+ class PagesController < ApplicationController
+ feature_policy do |p|
+ p.geolocation nil
+ p.payment "https://secure.example.com"
+ p.autoplay :none
+ end
+
+ def index
+ render html: "<h1>Welcome to Rails!</h1>"
+ end
+ end
+ RUBY
+
+ app_file "config/initializers/feature_policy.rb", <<-RUBY
+ Rails.application.config.feature_policy do |p|
+ p.geolocation :none
+ end
+ RUBY
+
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ root to: "pages#index"
+ end
+ RUBY
+
+ app("development")
+
+ get "/"
+ assert_policy "payment https://secure.example.com; autoplay 'none'"
+ end
+
+ test "global feature policy added to rack app" do
+ app_file "config/initializers/feature_policy.rb", <<-RUBY
+ Rails.application.config.feature_policy do |p|
+ p.payment :none
+ end
+ RUBY
+
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ app = ->(env) {
+ [200, { "Content-Type" => "text/html" }, ["<p>Hello, World!</p>"]]
+ }
+ root to: app
+ end
+ RUBY
+
+ app("development")
+
+ get "/"
+ assert_policy "payment 'none'"
+ end
+
+ private
+ def assert_policy(expected)
+ assert_equal 200, last_response.status
+ assert_equal expected, last_response.headers["Feature-Policy"]
+ end
+ end
+end
diff --git a/railties/test/application/generators_test.rb b/railties/test/application/generators_test.rb
index e5e557d204..8ec26db772 100644
--- a/railties/test/application/generators_test.rb
+++ b/railties/test/application/generators_test.rb
@@ -198,5 +198,15 @@ module ApplicationTests
assert_no_match "active_record:migration", output
end
end
+
+ test "skip collision check" do
+ rails("generate", "model", "post", "title:string")
+
+ output = rails("generate", "model", "post", "title:string", "body:string")
+ assert_match(/The name 'Post' is either already used in your application or reserved/, output)
+
+ output = rails("generate", "model", "post", "title:string", "body:string", "--skip-collision-check")
+ assert_no_match(/The name 'Post' is either already used in your application or reserved/, output)
+ end
end
end
diff --git a/railties/test/application/initializers/frameworks_test.rb b/railties/test/application/initializers/frameworks_test.rb
index a35247fc43..05978e1d99 100644
--- a/railties/test/application/initializers/frameworks_test.rb
+++ b/railties/test/application/initializers/frameworks_test.rb
@@ -218,8 +218,9 @@ module ApplicationTests
rails %w(generate model post title:string)
rails %w(db:migrate db:schema:cache:dump)
require "#{app_path}/config/environment"
- ActiveRecord::Base.connection.drop_table("posts") # force drop posts table for test.
assert ActiveRecord::Base.connection.schema_cache.data_sources("posts")
+ ensure
+ ActiveRecord::Base.connection.drop_table("posts", if_exists: true) # force drop posts table for test.
end
test "expire schema cache dump" do
diff --git a/railties/test/application/loading_test.rb b/railties/test/application/loading_test.rb
index 9c98489590..9ab400acfc 100644
--- a/railties/test/application/loading_test.rb
+++ b/railties/test/application/loading_test.rb
@@ -116,7 +116,7 @@ class LoadingTest < ActiveSupport::TestCase
RUBY
app_file "app/models/post.rb", <<-MODEL
- class Post < ActiveRecord::Base
+ class Post < ApplicationRecord
end
MODEL
@@ -133,11 +133,12 @@ class LoadingTest < ActiveSupport::TestCase
require "#{rails_root}/config/environment"
setup_ar!
- assert_equal [ActiveStorage::Blob, ActiveStorage::Attachment, ActiveRecord::SchemaMigration, ActiveRecord::InternalMetadata].collect(&:to_s).sort, ActiveRecord::Base.descendants.collect(&:to_s).sort
+ initial = [ActiveStorage::Blob, ActiveStorage::Attachment, ActiveRecord::SchemaMigration, ActiveRecord::InternalMetadata, ApplicationRecord, "primary::SchemaMigration"].collect(&:to_s).sort
+ assert_equal initial, ActiveRecord::Base.descendants.collect(&:to_s).sort
get "/load"
- assert_equal [ActiveStorage::Blob, ActiveStorage::Attachment, ActiveRecord::SchemaMigration, ActiveRecord::InternalMetadata, Post].collect(&:to_s).sort, ActiveRecord::Base.descendants.collect(&:to_s).sort
+ assert_equal [Post].collect(&:to_s).sort, ActiveRecord::Base.descendants.collect(&:to_s).sort - initial
get "/unload"
- assert_equal [ActiveStorage::Blob, ActiveStorage::Attachment, ActiveRecord::SchemaMigration, ActiveRecord::InternalMetadata].collect(&:to_s).sort, ActiveRecord::Base.descendants.collect(&:to_s).sort
+ assert_equal ["ActiveRecord::InternalMetadata", "ActiveRecord::SchemaMigration", "primary::SchemaMigration"], ActiveRecord::Base.descendants.collect(&:to_s).sort.uniq
end
test "initialize cant be called twice" do
@@ -454,7 +455,6 @@ class LoadingTest < ActiveSupport::TestCase
end
private
-
def setup_ar!
ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
ActiveRecord::Migration.verbose = false
diff --git a/railties/test/application/mailer_previews_test.rb b/railties/test/application/mailer_previews_test.rb
index fb84276b8a..fa9ed868c4 100644
--- a/railties/test/application/mailer_previews_test.rb
+++ b/railties/test/application/mailer_previews_test.rb
@@ -515,6 +515,13 @@ module ApplicationTests
assert_match '<option selected value="locale=ja">ja', last_response.body
end
+ test "preview does not leak I18n global setting changes" do
+ I18n.with_locale(:en) do
+ get "/rails/mailers/notifier/foo.txt?locale=ja"
+ assert_equal :en, I18n.locale
+ end
+ end
+
test "mailer previews create correct links when loaded on a subdirectory" do
mailer "notifier", <<-RUBY
class Notifier < ActionMailer::Base
diff --git a/railties/test/application/middleware/exceptions_test.rb b/railties/test/application/middleware/exceptions_test.rb
index 17df78ed4e..5fae521937 100644
--- a/railties/test/application/middleware/exceptions_test.rb
+++ b/railties/test/application/middleware/exceptions_test.rb
@@ -136,5 +136,21 @@ module ApplicationTests
assert_match(/boooom/, last_response.body)
assert_match(/測試テスト시험/, last_response.body)
end
+
+ test "displays diagnostics message when malformed query parameters are provided" do
+ controller :foo, <<-RUBY
+ class FooController < ActionController::Base
+ def index
+ end
+ end
+ RUBY
+
+ app.config.action_dispatch.show_exceptions = true
+ app.config.consider_all_requests_local = true
+
+ get "/foo?x[y]=1&x[y][][w]=2"
+ assert_equal 400, last_response.status
+ assert_match "Invalid query parameters", last_response.body
+ end
end
end
diff --git a/railties/test/application/middleware_test.rb b/railties/test/application/middleware_test.rb
index 4242daf39a..e93f2f5aa4 100644
--- a/railties/test/application/middleware_test.rb
+++ b/railties/test/application/middleware_test.rb
@@ -38,6 +38,7 @@ module ApplicationTests
"Rails::Rack::Logger",
"ActionDispatch::ShowExceptions",
"ActionDispatch::DebugExceptions",
+ "ActionDispatch::ActionableExceptions",
"ActionDispatch::Reloader",
"ActionDispatch::Callbacks",
"ActiveRecord::Migration::CheckPending",
@@ -45,6 +46,7 @@ module ApplicationTests
"ActionDispatch::Session::CookieStore",
"ActionDispatch::Flash",
"ActionDispatch::ContentSecurityPolicy::Middleware",
+ "ActionDispatch::FeaturePolicy::Middleware",
"Rack::Head",
"Rack::ConditionalGet",
"Rack::ETag",
@@ -70,6 +72,7 @@ module ApplicationTests
"Rails::Rack::Logger",
"ActionDispatch::ShowExceptions",
"ActionDispatch::DebugExceptions",
+ "ActionDispatch::ActionableExceptions",
"ActionDispatch::Reloader",
"ActionDispatch::Callbacks",
"Rack::Head",
@@ -307,7 +310,6 @@ module ApplicationTests
end
private
-
def boot!
require "#{app_path}/config/environment"
end
diff --git a/railties/test/application/rake/dbs_test.rb b/railties/test/application/rake/dbs_test.rb
index 258066a7e6..c9931c45a6 100644
--- a/railties/test/application/rake/dbs_test.rb
+++ b/railties/test/application/rake/dbs_test.rb
@@ -40,6 +40,15 @@ module ApplicationTests
end
end
+ def db_create_with_warning(expected_database)
+ Dir.chdir(app_path) do
+ output = rails("db:create")
+ assert_match(/Rails couldn't infer whether you are using multiple databases/, output)
+ assert_match(/Created database/, output)
+ assert File.exist?(expected_database)
+ end
+ end
+
test "db:create and db:drop without database URL" do
require "#{app_path}/config/environment"
db_create_and_drop ActiveRecord::Base.configurations[Rails.env]["database"]
@@ -86,6 +95,25 @@ module ApplicationTests
db_create_and_drop("db/development.sqlite3", environment_loaded: false)
end
+ test "db:create and db:drop show warning but doesn't raise errors when loading YAML with alias ERB" do
+ app_file "config/database.yml", <<-YAML
+ sqlite: &sqlite
+ adapter: sqlite3
+ database: db/development.sqlite3
+
+ development:
+ <<: *<%= ENV["DB"] || "sqlite" %>
+ YAML
+
+ app_file "config/environments/development.rb", <<-RUBY
+ Rails.application.configure do
+ config.database = "db/development.sqlite3"
+ end
+ RUBY
+
+ db_create_with_warning("db/development.sqlite3")
+ end
+
test "db:create and db:drop don't raise errors when loading YAML containing conditional statements in ERB" do
app_file "config/database.yml", <<-YAML
development:
@@ -122,6 +150,39 @@ module ApplicationTests
db_create_and_drop("db/development.sqlite3", environment_loaded: false)
end
+ test "db:create and db:drop dont raise errors when loading YAML with FIXME ERB" do
+ app_file "config/database.yml", <<-YAML
+ development:
+ <%= Rails.application.config.database ? 'database: db/development.sqlite3' : 'database: db/development.sqlite3' %>
+ adapter: sqlite3
+ YAML
+
+ app_file "config/environments/development.rb", <<-RUBY
+ Rails.application.configure do
+ config.database = "db/development.sqlite3"
+ end
+ RUBY
+
+ db_create_and_drop("db/development.sqlite3", environment_loaded: false)
+ end
+
+ test "db:create and db:drop don't raise errors when loading YAML which contains a key's value as an ERB statement" do
+ app_file "config/database.yml", <<-YAML
+ development:
+ database: <%= Rails.application.config.database ? 'db/development.sqlite3' : 'db/development.sqlite3' %>
+ custom_option: <%= ENV['CUSTOM_OPTION'] %>
+ adapter: sqlite3
+ YAML
+
+ app_file "config/environments/development.rb", <<-RUBY
+ Rails.application.configure do
+ config.database = "db/development.sqlite3"
+ end
+ RUBY
+
+ db_create_and_drop("db/development.sqlite3", environment_loaded: false)
+ end
+
def with_database_existing
Dir.chdir(app_path) do
set_database_url
@@ -569,6 +630,22 @@ module ApplicationTests
assert_match(/CreateRecipes: migrated/, output)
end
end
+
+ test "db:prepare does not touch schema when dumping is disabled" do
+ Dir.chdir(app_path) do
+ rails "generate", "model", "book", "title:string"
+ rails "db:create", "db:migrate"
+
+ app_file "db/schema.rb", "Not touched"
+ app_file "config/initializers/disable_dumping_schema.rb", <<-RUBY
+ Rails.application.config.active_record.dump_schema_after_migration = false
+ RUBY
+
+ rails "db:prepare"
+
+ assert_equal("Not touched", File.read("db/schema.rb").strip)
+ end
+ end
end
end
end
diff --git a/railties/test/application/rake/multi_dbs_test.rb b/railties/test/application/rake/multi_dbs_test.rb
index 147b8f94e1..2606e64424 100644
--- a/railties/test/application/rake/multi_dbs_test.rb
+++ b/railties/test/application/rake/multi_dbs_test.rb
@@ -24,7 +24,6 @@ module ApplicationTests
assert_no_match(/already exists/, output)
assert File.exist?(expected_database)
-
output = rails("db:drop")
assert_match(/Dropped database/, output)
assert_match_namespace(namespace, output)
@@ -137,6 +136,36 @@ module ApplicationTests
end
end
+ def db_up_and_down(version, namespace = nil)
+ Dir.chdir(app_path) do
+ generate_models_for_animals
+ rails("db:migrate")
+
+ if namespace
+ down_output = rails("db:migrate:down:#{namespace}", "VERSION=#{version}")
+ up_output = rails("db:migrate:up:#{namespace}", "VERSION=#{version}")
+ else
+ assert_raises RuntimeError, /You're using a multiple database application/ do
+ down_output = rails("db:migrate:down", "VERSION=#{version}")
+ end
+
+ assert_raises RuntimeError, /You're using a multiple database application/ do
+ up_output = rails("db:migrate:up", "VERSION=#{version}")
+ end
+ end
+
+ case namespace
+ when "primary"
+ assert_match(/OneMigration: reverting/, down_output)
+ assert_match(/OneMigration: migrated/, up_output)
+ when nil
+ else
+ assert_match(/TwoMigration: reverting/, down_output)
+ assert_match(/TwoMigration: migrated/, up_output)
+ end
+ end
+ end
+
def db_prepare
Dir.chdir(app_path) do
generate_models_for_animals
@@ -219,6 +248,34 @@ module ApplicationTests
end
end
+ test "db:migrate:down and db:migrate:up without a namespace raises in a multi-db application" do
+ require "#{app_path}/config/environment"
+
+ app_file "db/migrate/01_one_migration.rb", <<-MIGRATION
+ class OneMigration < ActiveRecord::Migration::Current
+ end
+ MIGRATION
+
+ db_up_and_down "01"
+ end
+
+ test "db:migrate:down:namespace and db:migrate:up:namespace works" do
+ require "#{app_path}/config/environment"
+
+ app_file "db/migrate/01_one_migration.rb", <<-MIGRATION
+ class OneMigration < ActiveRecord::Migration::Current
+ end
+ MIGRATION
+
+ app_file "db/animals_migrate/02_two_migration.rb", <<-MIGRATION
+ class TwoMigration < ActiveRecord::Migration::Current
+ end
+ MIGRATION
+
+ db_up_and_down "01", "primary"
+ db_up_and_down "02", "animals"
+ end
+
test "db:migrate:status works on all databases" do
require "#{app_path}/config/environment"
db_migrate_and_migrate_status
@@ -242,10 +299,56 @@ module ApplicationTests
db_migrate_and_schema_cache_dump_and_schema_cache_clear
end
+ test "db:abort_if_pending_migrations works on all databases" do
+ require "#{app_path}/config/environment"
+
+ app_file "db/animals_migrate/02_two_migration.rb", <<-MIGRATION
+ class TwoMigration < ActiveRecord::Migration::Current
+ end
+ MIGRATION
+
+ output = rails("db:abort_if_pending_migrations", allow_failure: true)
+ assert_match(/You have 1 pending migration/, output)
+ end
+
+ test "db:abort_if_pending_migrations:namespace works" do
+ require "#{app_path}/config/environment"
+
+ app_file "db/animals_migrate/02_two_migration.rb", <<-MIGRATION
+ class TwoMigration < ActiveRecord::Migration::Current
+ end
+ MIGRATION
+
+ output = rails("db:abort_if_pending_migrations:primary")
+ assert_no_match(/You have \d+ pending migration/, output)
+ output = rails("db:abort_if_pending_migrations:animals", allow_failure: true)
+ assert_match(/You have 1 pending migration/, output)
+ end
+
test "db:prepare works on all databases" do
require "#{app_path}/config/environment"
db_prepare
end
+
+ test "db:prepare setups missing database without clearing existing one" do
+ require "#{app_path}/config/environment"
+ Dir.chdir(app_path) do
+ # Bug not visible on SQLite3. Can be simplified when https://github.com/rails/rails/issues/36383 resolved
+ use_postgresql(multi_db: true)
+ generate_models_for_animals
+
+ rails "db:create:animals", "db:migrate:animals", "db:create:primary", "db:migrate:primary", "db:schema:dump"
+ rails "db:drop:primary"
+ Dog.create!
+ output = rails("db:prepare")
+
+ assert_match(/Created database/, output)
+ assert_equal 1, Dog.count
+ ensure
+ Dog.connection.disconnect!
+ rails "db:drop" rescue nil
+ end
+ end
end
end
end
diff --git a/railties/test/application/rake/notes_test.rb b/railties/test/application/rake/notes_test.rb
index 60802ef7c4..47822e55b9 100644
--- a/railties/test/application/rake/notes_test.rb
+++ b/railties/test/application/rake/notes_test.rb
@@ -159,7 +159,6 @@ module ApplicationTests
end
private
-
def run_rake_notes(command = "bin/rake notes")
Dir.chdir(app_path) do
output = `#{command}`
diff --git a/railties/test/application/system_test_case_test.rb b/railties/test/application/system_test_case_test.rb
new file mode 100644
index 0000000000..d15a0d9210
--- /dev/null
+++ b/railties/test/application/system_test_case_test.rb
@@ -0,0 +1,45 @@
+# frozen_string_literal: true
+
+require "isolation/abstract_unit"
+require "rack/test"
+
+class SystemTestCaseTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+
+ def setup
+ build_app
+ end
+
+ def teardown
+ teardown_app
+ end
+
+ test "url helpers are delegated to a proxy class" do
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ get 'foo', to: 'foo#index', as: 'test_foo'
+ end
+ RUBY
+
+ app("test")
+
+ assert_not_includes(ActionDispatch::SystemTestCase.runnable_methods, :test_foo_url)
+ end
+
+ test "system tests set the Capybara host in the url_options by default" do
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ get 'foo', to: 'foo#index', as: 'test_foo'
+ end
+ RUBY
+
+ app("test")
+ system_test = ActionDispatch::SystemTestCase.new("my_test")
+ previous_app_host = ::Capybara.app_host
+ ::Capybara.app_host = "https://my_test_example.com"
+
+ assert_equal("https://my_test_example.com/foo", system_test.test_foo_url)
+ ensure
+ ::Capybara.app_host = previous_app_host
+ end
+end
diff --git a/railties/test/application/test_runner_test.rb b/railties/test/application/test_runner_test.rb
index 0bdd2b314d..7fc918898b 100644
--- a/railties/test/application/test_runner_test.rb
+++ b/railties/test/application/test_runner_test.rb
@@ -564,6 +564,24 @@ module ApplicationTests
assert_no_match "create_table(:users)", output
end
+ def test_run_in_parallel_with_process_worker_crash
+ exercise_parallelization_regardless_of_machine_core_count(with: :processes)
+
+ file_name = app_file("test/models/parallel_test.rb", <<-RUBY)
+ require 'test_helper'
+
+ class ParallelTest < ActiveSupport::TestCase
+ def test_crash
+ Kernel.exit 1
+ end
+ end
+ RUBY
+
+ output = run_test_command(file_name)
+
+ assert_match %r{Queue not empty, but all workers have finished. This probably means that a worker crashed and 1 tests were missed.}, output
+ end
+
def test_run_in_parallel_with_threads
exercise_parallelization_regardless_of_machine_core_count(with: :threads)
@@ -794,6 +812,32 @@ module ApplicationTests
assert_match "Capybara.reset_sessions! called", output
end
+ def test_failed_system_test_screenshot_should_be_taken_before_other_teardown
+ app_file "test/system/failed_system_test_screenshot_should_be_taken_before_other_teardown_test.rb", <<~RUBY
+ require "application_system_test_case"
+ require "selenium/webdriver"
+
+ class FailedSystemTestScreenshotShouldBeTakenBeforeOtherTeardownTest < ApplicationSystemTestCase
+ ActionDispatch::SystemTestCase.class_eval do
+ def take_failed_screenshot
+ puts "take_failed_screenshot called"
+ super
+ end
+ end
+
+ def teardown
+ puts "test teardown called"
+ super
+ end
+
+ test "dummy" do
+ end
+ end
+ RUBY
+ output = run_test_command("test/system/failed_system_test_screenshot_should_be_taken_before_other_teardown_test.rb")
+ assert_match(/take_failed_screenshot called\n.*test teardown called/, output)
+ end
+
def test_system_tests_are_not_run_with_the_default_test_command
app_file "test/system/dummy_test.rb", <<-RUBY
require "application_system_test_case"
diff --git a/railties/test/application/zeitwerk_integration_test.rb b/railties/test/application/zeitwerk_integration_test.rb
index 40d06ee999..ff8c06b479 100644
--- a/railties/test/application/zeitwerk_integration_test.rb
+++ b/railties/test/application/zeitwerk_integration_test.rb
@@ -98,35 +98,56 @@ class ZeitwerkIntegrationTest < ActiveSupport::TestCase
assert_nil deps.safe_constantize("Admin")
end
- test "to_unload? says if a constant would be unloaded (main)" do
+ test "autoloaded? and overridden class names" do
+ invalid_constant_name = Module.new do
+ def self.name
+ "primary::SchemaMigration"
+ end
+ end
+ assert_not deps.autoloaded?(invalid_constant_name)
+ end
+
+ test "unloadable constants (main)" do
app_file "app/models/user.rb", "class User; end"
app_file "app/models/post.rb", "class Post; end"
boot
assert Post
- assert deps.to_unload?("Post")
- assert_not deps.to_unload?("User")
+
+ assert deps.autoloaded?("Post")
+ assert deps.autoloaded?(Post)
+ assert_not deps.autoloaded?("User")
+
+ assert_equal ["Post"], deps.autoloaded_constants
end
- test "to_unload? says if a constant would be unloaded (once)" do
+ test "unloadable constants (once)" do
add_to_config 'config.autoload_once_paths << "#{Rails.root}/extras"'
app_file "extras/foo.rb", "class Foo; end"
app_file "extras/bar.rb", "class Bar; end"
boot
assert Foo
- assert_not deps.to_unload?("Foo")
- assert_not deps.to_unload?("Bar")
+
+ assert_not deps.autoloaded?("Foo")
+ assert_not deps.autoloaded?(Foo)
+ assert_not deps.autoloaded?("Bar")
+
+ assert_empty deps.autoloaded_constants
end
- test "to_unload? says if a constant would be unloaded (reloading disabled)" do
+ test "unloadable constants (reloading disabled)" do
app_file "app/models/user.rb", "class User; end"
app_file "app/models/post.rb", "class Post; end"
boot("production")
assert Post
- assert_not deps.to_unload?("Post")
- assert_not deps.to_unload?("User")
+
+ assert_not deps.autoloaded?("Post")
+ assert_not deps.autoloaded?(Post)
+ assert_not deps.autoloaded?("User")
+
+ assert_empty deps.autoloaded_constants
end
test "eager loading loads the application code" do
@@ -335,46 +356,4 @@ class ZeitwerkIntegrationTest < ActiveSupport::TestCase
assert_nil autoloader.logger
end
end
-
- # This is here because to guarantee classic mode works as always, Zeitwerk
- # integration does not touch anything in classic. The descendants tracker is a
- # very small one-liner exception. We leave its main test suite untouched, and
- # add some minimal safety net here.
- #
- # When time passes, things are going to be reorganized (famous last words).
- test "descendants tracker" do
- class ::ZeitwerkDTIntegrationTestRoot
- extend ActiveSupport::DescendantsTracker
- end
- class ::ZeitwerkDTIntegrationTestChild < ::ZeitwerkDTIntegrationTestRoot; end
- class ::ZeitwerkDTIntegrationTestGrandchild < ::ZeitwerkDTIntegrationTestChild; end
-
- begin
- app_file "app/models/user.rb", "class User < ZeitwerkDTIntegrationTestRoot; end"
- app_file "app/models/post.rb", "class Post < ZeitwerkDTIntegrationTestRoot; end"
- app_file "app/models/tutorial.rb", "class Tutorial < Post; end"
- boot
-
- assert User
- assert Tutorial
-
- direct_descendants = [ZeitwerkDTIntegrationTestChild, User, Post].to_set
- assert_equal direct_descendants, ZeitwerkDTIntegrationTestRoot.direct_descendants.to_set
-
- descendants = direct_descendants.merge([ZeitwerkDTIntegrationTestGrandchild, Tutorial])
- assert_equal descendants, ZeitwerkDTIntegrationTestRoot.descendants.to_set
-
- ActiveSupport::DescendantsTracker.clear
-
- direct_descendants = [ZeitwerkDTIntegrationTestChild].to_set
- assert_equal direct_descendants, ZeitwerkDTIntegrationTestRoot.direct_descendants.to_set
-
- descendants = direct_descendants.merge([ZeitwerkDTIntegrationTestGrandchild])
- assert_equal descendants, ZeitwerkDTIntegrationTestRoot.descendants.to_set
- ensure
- Object.send(:remove_const, :ZeitwerkDTIntegrationTestRoot)
- Object.send(:remove_const, :ZeitwerkDTIntegrationTestChild)
- Object.send(:remove_const, :ZeitwerkDTIntegrationTestGrandchild)
- end
- end
end
diff --git a/railties/test/backtrace_cleaner_test.rb b/railties/test/backtrace_cleaner_test.rb
index ec512b6b64..6de23acebe 100644
--- a/railties/test/backtrace_cleaner_test.rb
+++ b/railties/test/backtrace_cleaner_test.rb
@@ -17,6 +17,16 @@ class BacktraceCleanerTest < ActiveSupport::TestCase
assert_equal 1, result.length
end
+ test "can filter for noise" do
+ backtrace = [ "(irb):1",
+ "/Path/to/rails/railties/lib/rails/commands/console.rb:77:in `start'",
+ "bin/rails:4:in `<main>'" ]
+ result = @cleaner.clean(backtrace, :noise)
+ assert_equal "/Path/to/rails/railties/lib/rails/commands/console.rb:77:in `start'", result[0]
+ assert_equal "bin/rails:4:in `<main>'", result[1]
+ assert_equal 2, result.length
+ end
+
test "should omit ActionView template methods names" do
method_name = ActionView::Template.new(nil, "app/views/application/index.html.erb", nil, locals: []).send :method_name
backtrace = [ "app/views/application/index.html.erb:4:in `block in #{method_name}'"]
diff --git a/railties/test/commands/console_test.rb b/railties/test/commands/console_test.rb
index f6df2b694a..ee4335e31d 100644
--- a/railties/test/commands/console_test.rb
+++ b/railties/test/commands/console_test.rb
@@ -116,7 +116,6 @@ class Rails::ConsoleTest < ActiveSupport::TestCase
private :output
private
-
def start(argv = [])
rails_console = Rails::Console.new(app, parse_arguments(argv))
@output = capture(:stdout) { rails_console.start }
diff --git a/railties/test/commands/dbconsole_test.rb b/railties/test/commands/dbconsole_test.rb
index 76a7cd055f..45b72fd909 100644
--- a/railties/test/commands/dbconsole_test.rb
+++ b/railties/test/commands/dbconsole_test.rb
@@ -275,7 +275,6 @@ class Rails::DBConsoleTest < ActiveSupport::TestCase
private :aborted, :output
private
-
def app_db_config(results)
Rails.application.config.stub(:database_configuration, results || {}) do
yield
diff --git a/railties/test/commands/server_test.rb b/railties/test/commands/server_test.rb
index b78370a233..c9026e2d95 100644
--- a/railties/test/commands/server_test.rb
+++ b/railties/test/commands/server_test.rb
@@ -116,6 +116,13 @@ class Rails::Command::ServerCommandTest < ActiveSupport::TestCase
end
end
+ def test_environment_with_pidfile
+ switch_env "PIDFILE", "/tmp/rails.pid" do
+ options = parse_arguments
+ assert_equal "/tmp/rails.pid", options[:pid]
+ end
+ end
+
def test_caching_without_option
args = []
options = parse_arguments(args)
@@ -234,6 +241,12 @@ class Rails::Command::ServerCommandTest < ActiveSupport::TestCase
options = parse_arguments(args)
assert_equal "127.0.0.1", options[:Host]
end
+
+ switch_env "PIDFILE", "/tmp/rails.pid" do
+ args = ["-P", "/somewhere/else.pid"]
+ options = parse_arguments(args)
+ assert_equal "/somewhere/else.pid", options[:pid]
+ end
end
def test_records_user_supplied_options
@@ -253,6 +266,16 @@ class Rails::Command::ServerCommandTest < ActiveSupport::TestCase
server_options = parse_arguments
assert_equal [:Host], server_options[:user_supplied_options]
end
+
+ switch_env "PORT", "3001" do
+ server_options = parse_arguments
+ assert_equal [:Port], server_options[:user_supplied_options]
+ end
+
+ switch_env "PIDFILE", "/tmp/server.pid" do
+ server_options = parse_arguments
+ assert_equal [:pid], server_options[:user_supplied_options]
+ end
end
def test_default_options
diff --git a/railties/test/configuration/middleware_stack_proxy_test.rb b/railties/test/configuration/middleware_stack_proxy_test.rb
index bc72b7f0c9..b67142f4c2 100644
--- a/railties/test/configuration/middleware_stack_proxy_test.rb
+++ b/railties/test/configuration/middleware_stack_proxy_test.rb
@@ -51,7 +51,6 @@ module Rails
end
private
-
def assert_playback(msg_name, args)
mock = Minitest::Mock.new
mock.expect :send, nil, [msg_name, args]
diff --git a/railties/test/env_helpers.rb b/railties/test/env_helpers.rb
index 336832b867..e3157e0c77 100644
--- a/railties/test/env_helpers.rb
+++ b/railties/test/env_helpers.rb
@@ -4,7 +4,6 @@ require "rails"
module EnvHelpers
private
-
def with_rails_env(env)
Rails.instance_variable_set :@_env, nil
switch_env "RAILS_ENV", env do
diff --git a/railties/test/generators/actions_test.rb b/railties/test/generators/actions_test.rb
index d913bb5438..5d6d7f1595 100644
--- a/railties/test/generators/actions_test.rb
+++ b/railties/test/generators/actions_test.rb
@@ -43,7 +43,7 @@ class ActionsTest < Rails::Generators::TestCase
def test_add_source_adds_source_to_gemfile
run_generator
action :add_source, "http://gems.github.com"
- assert_file "Gemfile", /source 'http:\/\/gems\.github\.com'/
+ assert_file "Gemfile", /source 'http:\/\/gems\.github\.com'\n/
end
def test_add_source_with_block_adds_source_to_gemfile_with_gem
@@ -51,7 +51,7 @@ class ActionsTest < Rails::Generators::TestCase
action :add_source, "http://gems.github.com" do
gem "rspec-rails"
end
- assert_file "Gemfile", /source 'http:\/\/gems\.github\.com' do\n gem 'rspec-rails'\nend/
+ assert_file "Gemfile", /\n\nsource 'http:\/\/gems\.github\.com' do\n gem 'rspec-rails'\nend\n\z/
end
def test_add_source_with_block_adds_source_to_gemfile_after_gem
@@ -60,13 +60,25 @@ class ActionsTest < Rails::Generators::TestCase
action :add_source, "http://gems.github.com" do
gem "rspec-rails"
end
- assert_file "Gemfile", /gem 'will-paginate'\nsource 'http:\/\/gems\.github\.com' do\n gem 'rspec-rails'\nend/
+ assert_file "Gemfile", /\ngem 'will-paginate'\n\nsource 'http:\/\/gems\.github\.com' do\n gem 'rspec-rails'\nend\n\z/
+ end
+
+ def test_add_source_should_create_newline_between_blocks
+ run_generator
+ action :add_source, "http://gems.github.com" do
+ gem "rspec-rails"
+ end
+
+ action :add_source, "http://gems2.github.com" do
+ gem "fakeweb"
+ end
+ assert_file "Gemfile", /\n\nsource 'http:\/\/gems\.github\.com' do\n gem 'rspec-rails'\nend\n\nsource 'http:\/\/gems2\.github\.com' do\n gem 'fakeweb'\nend\n\z/
end
def test_gem_should_put_gem_dependency_in_gemfile
run_generator
action :gem, "will-paginate"
- assert_file "Gemfile", /gem 'will\-paginate'/
+ assert_file "Gemfile", /gem 'will\-paginate'\n\z/
end
def test_gem_with_version_should_include_version_in_gemfile
@@ -141,7 +153,7 @@ class ActionsTest < Rails::Generators::TestCase
gem "fakeweb"
end
- assert_file "Gemfile", /\ngroup :development, :test do\n gem 'rspec-rails'\nend\n\ngroup :test do\n gem 'fakeweb'\nend/
+ assert_file "Gemfile", /\n\ngroup :development, :test do\n gem 'rspec-rails'\nend\n\ngroup :test do\n gem 'fakeweb'\nend\n\z/
end
def test_github_should_create_an_indented_block
@@ -153,7 +165,7 @@ class ActionsTest < Rails::Generators::TestCase
gem "baz"
end
- assert_file "Gemfile", /\ngithub 'user\/repo' do\n gem 'foo'\n gem 'bar'\n gem 'baz'\nend/
+ assert_file "Gemfile", /\n\ngithub 'user\/repo' do\n gem 'foo'\n gem 'bar'\n gem 'baz'\nend\n\z/
end
def test_github_should_create_an_indented_block_with_options
@@ -165,7 +177,7 @@ class ActionsTest < Rails::Generators::TestCase
gem "baz"
end
- assert_file "Gemfile", /\ngithub 'user\/repo', a: 'correct', other: true do\n gem 'foo'\n gem 'bar'\n gem 'baz'\nend/
+ assert_file "Gemfile", /\n\ngithub 'user\/repo', a: 'correct', other: true do\n gem 'foo'\n gem 'bar'\n gem 'baz'\nend\n\z/
end
def test_github_should_create_an_indented_block_within_a_group
@@ -177,9 +189,73 @@ class ActionsTest < Rails::Generators::TestCase
gem "bar"
gem "baz"
end
+ github "user/repo2", a: "correct", other: true do
+ gem "foo"
+ gem "bar"
+ gem "baz"
+ end
+ end
+
+ assert_file "Gemfile", /\n\ngroup :magic do\n github 'user\/repo', a: 'correct', other: true do\n gem 'foo'\n gem 'bar'\n gem 'baz'\n end\n github 'user\/repo2', a: 'correct', other: true do\n gem 'foo'\n gem 'bar'\n gem 'baz'\n end\nend\n\z/
+ end
+
+ def test_github_should_create_newline_between_blocks
+ run_generator
+
+ action :github, "user/repo", a: "correct", other: true do
+ gem "foo"
+ gem "bar"
+ gem "baz"
+ end
+
+ action :github, "user/repo2", a: "correct", other: true do
+ gem "foo"
+ gem "bar"
+ gem "baz"
+ end
+
+ assert_file "Gemfile", /\n\ngithub 'user\/repo', a: 'correct', other: true do\n gem 'foo'\n gem 'bar'\n gem 'baz'\nend\n\ngithub 'user\/repo2', a: 'correct', other: true do\n gem 'foo'\n gem 'bar'\n gem 'baz'\nend\n\z/
+ end
+
+ def test_gem_with_gemfile_without_newline_at_the_end
+ run_generator
+ File.open("Gemfile", "a") { |f| f.write("gem 'rspec-rails'") }
+
+ action :gem, "will-paginate"
+ assert_file "Gemfile", /gem 'rspec-rails'\ngem 'will-paginate'\n\z/
+ end
+
+ def test_gem_group_with_gemfile_without_newline_at_the_end
+ run_generator
+ File.open("Gemfile", "a") { |f| f.write("gem 'rspec-rails'") }
+
+ action :gem_group, :test do
+ gem "fakeweb"
end
- assert_file "Gemfile", /\ngroup :magic do\n github 'user\/repo', a: 'correct', other: true do\n gem 'foo'\n gem 'bar'\n gem 'baz'\n end\nend\n/
+ assert_file "Gemfile", /gem 'rspec-rails'\n\ngroup :test do\n gem 'fakeweb'\nend\n\z/
+ end
+
+ def test_add_source_with_gemfile_without_newline_at_the_end
+ run_generator
+ File.open("Gemfile", "a") { |f| f.write("gem 'rspec-rails'") }
+
+ action :add_source, "http://gems.github.com" do
+ gem "fakeweb"
+ end
+
+ assert_file "Gemfile", /gem 'rspec-rails'\n\nsource 'http:\/\/gems\.github\.com' do\n gem 'fakeweb'\nend\n\z/
+ end
+
+ def test_github_with_gemfile_without_newline_at_the_end
+ run_generator
+ File.open("Gemfile", "a") { |f| f.write("gem 'rspec-rails'") }
+
+ action :github, "user/repo" do
+ gem "fakeweb"
+ end
+
+ assert_file "Gemfile", /gem 'rspec-rails'\n\ngithub 'user\/repo' do\n gem 'fakeweb'\nend\n\z/
end
def test_environment_should_include_data_in_environment_initializer_block
@@ -505,7 +581,6 @@ F
end
private
-
def action(*args, &block)
capture(:stdout) { generator.send(*args, &block) }
end
diff --git a/railties/test/generators/api_app_generator_test.rb b/railties/test/generators/api_app_generator_test.rb
index 4b9878187b..099bad8fb9 100644
--- a/railties/test/generators/api_app_generator_test.rb
+++ b/railties/test/generators/api_app_generator_test.rb
@@ -50,6 +50,13 @@ class ApiAppGeneratorTest < Rails::Generators::TestCase
assert_file "config/application.rb", /config\.api_only = true/
assert_file "app/controllers/application_controller.rb", /ActionController::API/
+
+ assert_file "config/environments/development.rb" do |content|
+ assert_no_match(/action_controller\.perform_caching = true/, content)
+ end
+ assert_file "config/environments/production.rb" do |content|
+ assert_no_match(/action_controller\.perform_caching = true/, content)
+ end
end
def test_generator_if_skip_action_cable_is_given
@@ -102,7 +109,6 @@ class ApiAppGeneratorTest < Rails::Generators::TestCase
end
private
-
def default_files
%w(.gitignore
.ruby-version
@@ -120,7 +126,6 @@ class ApiAppGeneratorTest < Rails::Generators::TestCase
bin/rails
bin/rake
bin/setup
- bin/update
config/application.rb
config/boot.rb
config/cable.yml
diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb
index 8771b53071..5b439fdcba 100644
--- a/railties/test/generators/app_generator_test.rb
+++ b/railties/test/generators/app_generator_test.rb
@@ -41,7 +41,6 @@ DEFAULT_APP_FILES = %w(
bin/rails
bin/rake
bin/setup
- bin/update
bin/yarn
config/application.rb
config/boot.rb
@@ -321,10 +320,6 @@ class AppGeneratorTest < Rails::Generators::TestCase
assert_file "#{app_root}/bin/setup" do |content|
assert_no_match(/system\('bin\/yarn'\)/, content)
end
-
- assert_file "#{app_root}/bin/update" do |content|
- assert_no_match(/system\('bin\/yarn'\)/, content)
- end
end
end
diff --git a/railties/test/generators/generated_attribute_test.rb b/railties/test/generators/generated_attribute_test.rb
index bf60d6bc22..82d550a124 100644
--- a/railties/test/generators/generated_attribute_test.rb
+++ b/railties/test/generators/generated_attribute_test.rb
@@ -6,6 +6,15 @@ require "rails/generators/generated_attribute"
class GeneratedAttributeTest < Rails::Generators::TestCase
include GeneratorsTestHelper
+ def setup
+ @old_belongs_to_required_by_default = Rails.application.config.active_record.belongs_to_required_by_default
+ Rails.application.config.active_record.belongs_to_required_by_default = true
+ end
+
+ def teardown
+ Rails.application.config.active_record.belongs_to_required_by_default = @old_belongs_to_required_by_default
+ end
+
def test_field_type_returns_number_field
assert_field_type :integer, :number_field
end
@@ -155,10 +164,16 @@ class GeneratedAttributeTest < Rails::Generators::TestCase
end
def test_parse_required_attribute_with_index
- att = Rails::Generators::GeneratedAttribute.parse("supplier:references{required}:index")
+ att = Rails::Generators::GeneratedAttribute.parse("supplier:references:index")
assert_equal "supplier", att.name
assert_equal :references, att.type
assert_predicate att, :has_index?
assert_predicate att, :required?
end
+
+ def test_parse_required_attribute_with_index_false_when_belongs_to_required_by_default_global_config_is_false
+ Rails.application.config.active_record.belongs_to_required_by_default = false
+ att = Rails::Generators::GeneratedAttribute.parse("supplier:references:index")
+ assert_not_predicate att, :required?
+ end
end
diff --git a/railties/test/generators/migration_generator_test.rb b/railties/test/generators/migration_generator_test.rb
index 540bed551b..080e54a1ca 100644
--- a/railties/test/generators/migration_generator_test.rb
+++ b/railties/test/generators/migration_generator_test.rb
@@ -6,6 +6,16 @@ require "rails/generators/rails/migration/migration_generator"
class MigrationGeneratorTest < Rails::Generators::TestCase
include GeneratorsTestHelper
+ def setup
+ @old_belongs_to_required_by_default = Rails.application.config.active_record.belongs_to_required_by_default
+
+ Rails.application.config.active_record.belongs_to_required_by_default = true
+ end
+
+ def teardown
+ Rails.application.config.active_record.belongs_to_required_by_default = @old_belongs_to_required_by_default
+ end
+
def test_migration
migration = "change_title_body_from_posts"
run_generator [migration]
@@ -196,9 +206,9 @@ class MigrationGeneratorTest < Rails::Generators::TestCase
end
end
- def test_add_migration_with_required_references
+ def test_add_migration_with_references_adds_null_false_by_default
migration = "add_references_to_books"
- run_generator [migration, "author:belongs_to{required}", "distributor:references{polymorphic,required}"]
+ run_generator [migration, "author:belongs_to", "distributor:references{polymorphic}"]
assert_migration "db/migrate/#{migration}.rb" do |content|
assert_method :change, content do |change|
@@ -208,6 +218,21 @@ class MigrationGeneratorTest < Rails::Generators::TestCase
end
end
+ def test_add_migration_with_references_does_not_add_belongs_to_when_required_by_default_global_config_is_false
+ Rails.application.config.active_record.belongs_to_required_by_default = false
+
+ migration = "add_references_to_books"
+
+ run_generator [migration, "author:belongs_to", "distributor:references{polymorphic}"]
+
+ assert_migration "db/migrate/#{migration}.rb" do |content|
+ assert_method :change, content do |change|
+ assert_match(/add_reference :books, :author/, change)
+ assert_match(/add_reference :books, :distributor, polymorphic: true/, change)
+ end
+ end
+ end
+
def test_add_migration_with_references_adds_foreign_keys
migration = "add_references_to_books"
run_generator [migration, "author:belongs_to", "distributor:references{polymorphic}"]
@@ -245,6 +270,21 @@ class MigrationGeneratorTest < Rails::Generators::TestCase
end
end
+ def test_create_table_migration_with_timestamps
+ run_generator ["create_books", "title:string", "content:text"]
+ assert_migration "db/migrate/create_books.rb", /t.timestamps/
+ end
+
+ def test_create_table_timestamps_are_skipped
+ run_generator ["create_books", "title:string", "content:text", "--no-timestamps"]
+
+ assert_migration "db/migrate/create_books.rb" do |m|
+ assert_method :change, m do |change|
+ assert_no_match(/t.timestamps/, change)
+ end
+ end
+ end
+
def test_add_uuid_to_create_table_migration
run_generator ["create_books", "--primary_key_type=uuid"]
assert_migration "db/migrate/create_books.rb" do |content|
@@ -405,7 +445,6 @@ class MigrationGeneratorTest < Rails::Generators::TestCase
end
private
-
def with_singular_table_name
old_state = ActiveRecord::Base.pluralize_table_names
ActiveRecord::Base.pluralize_table_names = false
diff --git a/railties/test/generators/model_generator_test.rb b/railties/test/generators/model_generator_test.rb
index 1b86e509ee..d6abaf7a6f 100644
--- a/railties/test/generators/model_generator_test.rb
+++ b/railties/test/generators/model_generator_test.rb
@@ -10,6 +10,14 @@ class ModelGeneratorTest < Rails::Generators::TestCase
def setup
super
Rails::Generators::ModelHelpers.skip_warn = false
+ @old_belongs_to_required_by_default = Rails.application.config.active_record.belongs_to_required_by_default
+
+ Rails.application.config.active_record.belongs_to_required_by_default = true
+ end
+
+
+ def teardown
+ Rails.application.config.active_record.belongs_to_required_by_default = @old_belongs_to_required_by_default
end
def test_help_shows_invoked_generators_options
@@ -414,8 +422,8 @@ class ModelGeneratorTest < Rails::Generators::TestCase
end
end
- def test_required_polymorphic_belongs_to_generages_correct_model
- run_generator ["account", "supplier:references{required,polymorphic}"]
+ def test_polymorphic_belongs_to_generates_correct_model
+ run_generator ["account", "supplier:references{polymorphic}"]
expected_file = <<~FILE
class Account < ApplicationRecord
@@ -425,23 +433,46 @@ class ModelGeneratorTest < Rails::Generators::TestCase
assert_file "app/models/account.rb", expected_file
end
- def test_required_and_polymorphic_are_order_independent
- run_generator ["account", "supplier:references{polymorphic.required}"]
+ def test_passing_required_to_model_generator_is_deprecated
+ assert_deprecated do
+ run_generator ["account", "supplier:references{required}"]
+ end
- expected_file = <<~FILE
- class Account < ApplicationRecord
- belongs_to :supplier, polymorphic: true
+ assert_migration "db/migrate/create_accounts.rb" do |m|
+ assert_method :change, m do |up|
+ assert_match(/t\.references :supplier,.*\snull: false/, up)
end
- FILE
- assert_file "app/models/account.rb", expected_file
+ end
end
- def test_required_adds_null_false_to_column
- run_generator ["account", "supplier:references{required}"]
+ def test_null_false_is_added_for_references_by_default
+ run_generator ["account", "user:references"]
assert_migration "db/migrate/create_accounts.rb" do |m|
assert_method :change, m do |up|
- assert_match(/t\.references :supplier,.*\snull: false/, up)
+ assert_match(/t\.references :user,.*\snull: false/, up)
+ end
+ end
+ end
+
+ def test_null_false_is_added_for_belongs_to_by_default
+ run_generator ["account", "user:belongs_to"]
+
+ assert_migration "db/migrate/create_accounts.rb" do |m|
+ assert_method :change, m do |up|
+ assert_match(/t\.belongs_to :user,.*\snull: false/, up)
+ end
+ end
+ end
+
+ def test_null_false_is_not_added_when_belongs_to_required_by_default_global_config_is_false
+ Rails.application.config.active_record.belongs_to_required_by_default = false
+
+ run_generator ["account", "user:belongs_to"]
+
+ assert_migration "db/migrate/create_accounts.rb" do |m|
+ assert_method :change, m do |up|
+ assert_match(/t\.belongs_to :user/, up)
end
end
end
diff --git a/railties/test/generators/named_base_test.rb b/railties/test/generators/named_base_test.rb
index 4e61b660d7..1b45c58b0e 100644
--- a/railties/test/generators/named_base_test.rb
+++ b/railties/test/generators/named_base_test.rb
@@ -169,7 +169,6 @@ class NamedBaseTest < Rails::Generators::TestCase
end
private
-
def assert_name(generator, value, method)
assert_equal value, generator.send(method)
end
diff --git a/railties/test/generators/plugin_generator_test.rb b/railties/test/generators/plugin_generator_test.rb
index f45464f8d0..db774f6ac1 100644
--- a/railties/test/generators/plugin_generator_test.rb
+++ b/railties/test/generators/plugin_generator_test.rb
@@ -713,7 +713,6 @@ class PluginGeneratorTest < Rails::Generators::TestCase
end
private
-
def action(*args, &block)
silence(:stdout) { generator.send(*args, &block) }
end
diff --git a/railties/test/generators/scaffold_controller_generator_test.rb b/railties/test/generators/scaffold_controller_generator_test.rb
index 1348744b0b..8278b72a5a 100644
--- a/railties/test/generators/scaffold_controller_generator_test.rb
+++ b/railties/test/generators/scaffold_controller_generator_test.rb
@@ -89,6 +89,15 @@ class ScaffoldControllerGeneratorTest < Rails::Generators::TestCase
end
end
+ def test_controller_permit_attachments_attributes_only
+ run_generator ["Message", "photos:attachments"]
+
+ assert_file "app/controllers/messages_controller.rb" do |content|
+ assert_match(/def message_params/, content)
+ assert_match(/params\.require\(:message\)\.permit\(photos: \[\]\)/, content)
+ end
+ end
+
def test_helper_are_invoked_with_a_pluralized_name
run_generator
assert_file "app/helpers/users_helper.rb", /module UsersHelper/
diff --git a/railties/test/generators/scaffold_generator_test.rb b/railties/test/generators/scaffold_generator_test.rb
index bfa52a1beb..fa9a42215b 100644
--- a/railties/test/generators/scaffold_generator_test.rb
+++ b/railties/test/generators/scaffold_generator_test.rb
@@ -487,6 +487,36 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase
assert_match(/^\W{4}<%= form\.file_field :video %>/, content)
assert_match(/^\W{4}<%= form\.file_field :photos, multiple: true %>/, content)
end
+
+ assert_file "app/views/messages/show.html.erb" do |content|
+ assert_match(/^\W{2}<%= link_to @message\.video\.filename, @message\.video if @message\.video\.attached\? %>/, content)
+ assert_match(/^\W{4}<div><%= link_to photo\.filename, photo %>/, content)
+ end
+
+ assert_file "test/system/messages_test.rb" do |content|
+ assert_no_match(/fill_in "Video"/, content)
+ assert_no_match(/fill_in "Photos"/, content)
+ end
+ end
+
+ def test_scaffold_generator_rich_text
+ run_generator ["message", "content:rich_text"]
+
+ assert_file "app/models/message.rb", /rich_text :content/
+
+ assert_file "app/controllers/messages_controller.rb" do |content|
+ assert_instance_method :message_params, content do |m|
+ assert_match(/permit\(:content\)/, m)
+ end
+ end
+
+ assert_file "app/views/messages/_form.html.erb" do |content|
+ assert_match(/^\W{4}<%= form\.rich_text_area :content %>/, content)
+ end
+
+ assert_file "test/system/messages_test.rb" do |content|
+ assert_no_match(/fill_in "Content"/, content)
+ end
end
def test_scaffold_generator_database
diff --git a/railties/test/generators/shared_generator_tests.rb b/railties/test/generators/shared_generator_tests.rb
index 26ce487c5f..3e8ce1c018 100644
--- a/railties/test/generators/shared_generator_tests.rb
+++ b/railties/test/generators/shared_generator_tests.rb
@@ -191,10 +191,7 @@ module SharedGeneratorTests
assert_no_match(/fixtures :all/, helper_content)
end
assert_file "#{application_path}/bin/setup" do |setup_content|
- assert_no_match(/db:setup/, setup_content)
- end
- assert_file "#{application_path}/bin/update" do |update_content|
- assert_no_match(/db:migrate/, update_content)
+ assert_no_match(/db:prepare/, setup_content)
end
assert_file ".gitignore" do |content|
assert_no_match(/sqlite/i, content)
diff --git a/railties/test/isolation/abstract_unit.rb b/railties/test/isolation/abstract_unit.rb
index 3fcfaa9623..0fe62df8ba 100644
--- a/railties/test/isolation/abstract_unit.rb
+++ b/railties/test/isolation/abstract_unit.rb
@@ -18,7 +18,9 @@ require "active_support/testing/method_call_assertions"
require "active_support/test_case"
require "minitest/retry"
-Minitest::Retry.use!(verbose: false, retry_count: 1)
+if ENV["BUILDKITE"]
+ Minitest::Retry.use!(verbose: false, retry_count: 1)
+end
RAILS_FRAMEWORK_ROOT = File.expand_path("../../..", __dir__)
@@ -446,18 +448,36 @@ module TestHelpers
$:.reject! { |path| path =~ %r'/(#{to_remove.join('|')})/' }
end
- def use_postgresql
- File.open("#{app_path}/config/database.yml", "w") do |f|
- f.puts <<-YAML
- default: &default
- adapter: postgresql
- pool: 5
- database: railties_test
- development:
- <<: *default
- test:
- <<: *default
- YAML
+ def use_postgresql(multi_db: false)
+ if multi_db
+ File.open("#{app_path}/config/database.yml", "w") do |f|
+ f.puts <<-YAML
+ default: &default
+ adapter: postgresql
+ pool: 5
+ development:
+ primary:
+ <<: *default
+ database: railties_test
+ animals:
+ <<: *default
+ database: railties_animals_test
+ migrations_paths: db/animals_migrate
+ YAML
+ end
+ else
+ File.open("#{app_path}/config/database.yml", "w") do |f|
+ f.puts <<-YAML
+ default: &default
+ adapter: postgresql
+ pool: 5
+ database: railties_test
+ development:
+ <<: *default
+ test:
+ <<: *default
+ YAML
+ end
end
end
end
diff --git a/railties/test/railties/engine_test.rb b/railties/test/railties/engine_test.rb
index fe5c62c07d..7f05b9d9cf 100644
--- a/railties/test/railties/engine_test.rb
+++ b/railties/test/railties/engine_test.rb
@@ -34,7 +34,7 @@ module RailtiesTest
def migrations
migration_root = File.expand_path(ActiveRecord::Migrator.migrations_paths.first, app_path)
- ActiveRecord::MigrationContext.new(migration_root).migrations
+ ActiveRecord::MigrationContext.new(migration_root, ActiveRecord::SchemaMigration).migrations
end
test "serving sprocket's assets" do
@@ -904,6 +904,32 @@ YAML
assert_instance_of ActiveJob::QueueAdapters::DelayedJobAdapter, ActiveJob::Base.queue_adapter
end
+ test "seed data can be loaded when ActiveJob is not present" do
+ @plugin.write "db/seeds.rb", <<-RUBY
+ Bukkits::Engine.config.bukkits_seeds_loaded = true
+ RUBY
+
+ app_file "db/seeds.rb", <<-RUBY
+ Rails.application.config.app_seeds_loaded = true
+ RUBY
+
+ boot_rails
+
+ # In a real app, config.active_job would be undefined when
+ # NOT requiring rails/all AND NOT requiring active_job/railtie
+ # that doesn't work as expected in this test environment, so:
+ undefine_config_option(:active_job)
+ assert_raise(NoMethodError) { Rails.application.config.active_job }
+
+ assert_raise(NoMethodError) { Rails.application.config.app_seeds_loaded }
+ assert_raise(NoMethodError) { Bukkits::Engine.config.bukkits_seeds_loaded }
+
+ Rails.application.load_seed
+ assert Rails.application.config.app_seeds_loaded
+ Bukkits::Engine.load_seed
+ assert Bukkits::Engine.config.bukkits_seeds_loaded
+ end
+
test "skips nonexistent seed data" do
FileUtils.rm "#{app_path}/db/seeds.rb"
boot_rails
@@ -1523,6 +1549,10 @@ YAML
Rails.application
end
+ def undefine_config_option(name)
+ Rails.application.config.class.class_variable_get(:@@options).delete(name)
+ end
+
# Restrict frameworks to load in order to avoid engine frameworks affect tests.
def restrict_frameworks
remove_from_config("require 'rails/all'")
diff --git a/tools/profile b/tools/profile
index 6fb571f43b..d38ba1dd84 100755
--- a/tools/profile
+++ b/tools/profile
@@ -68,7 +68,6 @@ module CodeTools
end
private
-
def assert_ruby_file_exists(path)
fail Error.new("No such file") unless File.exist?(path)
fail Error.new("#{path} is a directory") if File.directory?(path)
diff --git a/version.rb b/version.rb
index fea24810f5..a6b3ccf8f8 100644
--- a/version.rb
+++ b/version.rb
@@ -8,9 +8,9 @@ module Rails
module VERSION
MAJOR = 6
- MINOR = 0
+ MINOR = 1
TINY = 0
- PRE = "beta3"
+ PRE = "alpha"
STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
end
diff --git a/yarn.lock b/yarn.lock
index fdf508b9a3..42f8b5bd87 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2,6 +2,13 @@
# yarn lockfile v1
+"@rails/activestorage@^6.0.0-alpha":
+ version "6.0.0-alpha"
+ resolved "https://registry.yarnpkg.com/@rails/activestorage/-/activestorage-6.0.0-alpha.tgz#6eb6c0f49bcaa4ad68fcd13a750b9a17f76f086f"
+ integrity sha512-ZQneYyDWrazLFZgyfZ3G4h9Q+TxvSQE5BEenBhvusyAeYMVaYxrZU/PWFcbeyiQe5xz1SeN3g/UejU3ytCYJwQ==
+ dependencies:
+ spark-md5 "^3.0.0"
+
"@types/estree@0.0.38":
version "0.0.38"
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.38.tgz#c1be40aa933723c608820a99a373a16d215a1ca2"
@@ -5692,11 +5699,6 @@ trim-right@^1.0.1:
resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003"
integrity sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=
-trix@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/trix/-/trix-1.0.0.tgz#e9cc98cf6030c908f8d54e317b5b072f927b0c6b"
- integrity sha512-feli9QVXe6gzZOCUfpPGpNDURW9jMciIRVQ5gkDudOctcA1oMtI5K/qEbsL2rFCoGl1rSoeRt+HPhIFGyQscKg==
-
tslib@^1.9.0:
version "1.9.3"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286"