aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.rubocop.yml14
-rw-r--r--.travis.yml4
-rw-r--r--Gemfile16
-rw-r--r--Gemfile.lock116
-rw-r--r--README.md2
-rw-r--r--Rakefile2
-rw-r--r--actioncable/CHANGELOG.md6
-rw-r--r--actioncable/README.md2
-rw-r--r--actioncable/lib/action_cable/connection/base.rb2
-rw-r--r--actioncable/test/channel/stream_test.rb2
-rw-r--r--actioncable/test/connection/multiple_identifiers_test.rb4
-rw-r--r--actioncable/test/connection/string_identifier_test.rb4
-rw-r--r--actionmailer/lib/action_mailer/base.rb4
-rw-r--r--actionmailer/lib/action_mailer/railtie.rb6
-rw-r--r--actionmailer/test/message_delivery_test.rb5
-rw-r--r--actionpack/CHANGELOG.md14
-rw-r--r--actionpack/lib/action_controller/railtie.rb8
-rw-r--r--actionpack/lib/action_dispatch/testing/test_response.rb24
-rw-r--r--actionpack/test/controller/live_stream_test.rb4
-rw-r--r--actionpack/test/controller/redirect_test.rb2
-rw-r--r--actionpack/test/controller/resources_test.rb20
-rw-r--r--actionpack/test/controller/test_case_test.rb8
-rw-r--r--actionpack/test/dispatch/response_test.rb9
-rw-r--r--actionpack/test/dispatch/routing_assertions_test.rb12
-rw-r--r--actionpack/test/dispatch/test_response_test.rb7
-rw-r--r--actionpack/test/journey/router_test.rb10
-rw-r--r--actionview/lib/action_view/helpers/asset_tag_helper.rb25
-rw-r--r--actionview/lib/action_view/helpers/form_helper.rb24
-rw-r--r--actionview/lib/action_view/helpers/form_tag_helper.rb9
-rw-r--r--actionview/lib/action_view/renderer/partial_renderer.rb2
-rw-r--r--actionview/lib/action_view/test_case.rb12
-rw-r--r--actionview/test/actionpack/abstract/abstract_controller_test.rb4
-rw-r--r--actionview/test/template/asset_tag_helper_test.rb3
-rw-r--r--actionview/test/template/date_helper_test.rb52
-rw-r--r--actionview/test/template/form_collections_helper_test.rb8
-rw-r--r--actionview/test/template/form_helper_test.rb33
-rw-r--r--actionview/test/template/form_options_helper_test.rb48
-rw-r--r--actionview/test/template/form_tag_helper_test.rb53
-rw-r--r--actionview/test/template/text_helper_test.rb8
-rw-r--r--activejob/test/support/integration/adapters/delayed_job.rb1
-rw-r--r--activemodel/lib/active_model/type/integer.rb2
-rw-r--r--activerecord/CHANGELOG.md18
-rw-r--r--activerecord/RUNNING_UNIT_TESTS.rdoc4
-rw-r--r--activerecord/lib/active_record/associations.rb24
-rw-r--r--activerecord/lib/active_record/associations/association_scope.rb16
-rw-r--r--activerecord/lib/active_record/associations/collection_association.rb37
-rw-r--r--activerecord/lib/active_record/associations/collection_proxy.rb5
-rw-r--r--activerecord/lib/active_record/associations/preloader/association.rb12
-rw-r--r--activerecord/lib/active_record/associations/through_association.rb2
-rw-r--r--activerecord/lib/active_record/attribute_methods/dirty.rb27
-rw-r--r--activerecord/lib/active_record/attribute_methods/serialization.rb21
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb15
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb2
-rw-r--r--activerecord/lib/active_record/core.rb4
-rw-r--r--activerecord/lib/active_record/errors.rb2
-rw-r--r--activerecord/lib/active_record/fixtures.rb4
-rw-r--r--activerecord/lib/active_record/migration/compatibility.rb2
-rw-r--r--activerecord/lib/active_record/reflection.rb72
-rw-r--r--activerecord/lib/active_record/relation/batches.rb7
-rw-r--r--activerecord/lib/active_record/relation/calculations.rb3
-rw-r--r--activerecord/lib/active_record/relation/finder_methods.rb6
-rw-r--r--activerecord/lib/active_record/relation/predicate_builder/association_query_value.rb4
-rw-r--r--activerecord/lib/active_record/scoping/named.rb10
-rw-r--r--activerecord/lib/active_record/table_metadata.rb2
-rw-r--r--activerecord/lib/active_record/tasks/postgresql_database_tasks.rb4
-rw-r--r--activerecord/lib/active_record/transactions.rb4
-rw-r--r--activerecord/lib/active_record/validations/uniqueness.rb4
-rw-r--r--activerecord/lib/rails/generators/active_record/application_record/application_record_generator.rb11
-rw-r--r--activerecord/test/cases/adapters/postgresql/array_test.rb33
-rw-r--r--activerecord/test/cases/adapters/postgresql/json_test.rb4
-rw-r--r--activerecord/test/cases/associations/has_many_associations_test.rb17
-rw-r--r--activerecord/test/cases/associations/has_many_through_associations_test.rb67
-rw-r--r--activerecord/test/cases/associations/has_one_associations_test.rb2
-rw-r--r--activerecord/test/cases/associations/join_model_test.rb8
-rw-r--r--activerecord/test/cases/attribute_set_test.rb2
-rw-r--r--activerecord/test/cases/base_test.rb11
-rw-r--r--activerecord/test/cases/dirty_test.rb7
-rw-r--r--activerecord/test/cases/enum_test.rb14
-rw-r--r--activerecord/test/cases/finder_test.rb2
-rw-r--r--activerecord/test/cases/fixtures_test.rb3
-rw-r--r--activerecord/test/cases/inheritance_test.rb13
-rw-r--r--activerecord/test/cases/json_shared_test_cases.rb33
-rw-r--r--activerecord/test/cases/relation/where_test.rb14
-rw-r--r--activerecord/test/cases/scoping/named_scoping_test.rb6
-rw-r--r--activerecord/test/cases/validations/uniqueness_validation_test.rb7
-rw-r--r--activerecord/test/fixtures/binaries.yml4
-rw-r--r--activerecord/test/models/post.rb7
-rw-r--r--activerecord/test/schema/schema.rb20
-rw-r--r--activestorage/.babelrc5
-rw-r--r--activestorage/.codeclimate.yml7
-rw-r--r--activestorage/.eslintrc19
-rw-r--r--activestorage/.gitignore6
-rw-r--r--activestorage/CHANGELOG.md3
-rw-r--r--activestorage/MIT-LICENSE20
-rw-r--r--activestorage/README.md141
-rw-r--r--activestorage/Rakefile14
-rw-r--r--activestorage/activestorage.gemspec30
-rw-r--r--activestorage/app/assets/javascripts/activestorage.js1
-rw-r--r--activestorage/app/controllers/active_storage/blobs_controller.rb25
-rw-r--r--activestorage/app/controllers/active_storage/direct_uploads_controller.rb23
-rw-r--r--activestorage/app/controllers/active_storage/disk_controller.rb53
-rw-r--r--activestorage/app/controllers/active_storage/variants_controller.rb29
-rw-r--r--activestorage/app/javascript/activestorage/blob_record.js54
-rw-r--r--activestorage/app/javascript/activestorage/blob_upload.js34
-rw-r--r--activestorage/app/javascript/activestorage/direct_upload.js42
-rw-r--r--activestorage/app/javascript/activestorage/direct_upload_controller.js67
-rw-r--r--activestorage/app/javascript/activestorage/direct_uploads_controller.js50
-rw-r--r--activestorage/app/javascript/activestorage/file_checksum.js53
-rw-r--r--activestorage/app/javascript/activestorage/helpers.js42
-rw-r--r--activestorage/app/javascript/activestorage/index.js11
-rw-r--r--activestorage/app/javascript/activestorage/ujs.js74
-rw-r--r--activestorage/app/jobs/active_storage/purge_job.rb11
-rw-r--r--activestorage/app/models/active_storage/attachment.rb30
-rw-r--r--activestorage/app/models/active_storage/blob.rb199
-rw-r--r--activestorage/app/models/active_storage/filename.rb51
-rw-r--r--activestorage/app/models/active_storage/variant.rb82
-rw-r--r--activestorage/app/models/active_storage/variation.rb55
-rw-r--r--activestorage/config/routes.rb30
-rw-r--r--activestorage/db/migrate/20170806125915_create_active_storage_tables.rb27
-rw-r--r--activestorage/lib/active_storage.rb38
-rw-r--r--activestorage/lib/active_storage/attached.rb40
-rw-r--r--activestorage/lib/active_storage/attached/macros.rb82
-rw-r--r--activestorage/lib/active_storage/attached/many.rb55
-rw-r--r--activestorage/lib/active_storage/attached/one.rb60
-rw-r--r--activestorage/lib/active_storage/engine.rb65
-rw-r--r--activestorage/lib/active_storage/gem_version.rb17
-rw-r--r--activestorage/lib/active_storage/log_subscriber.rb52
-rw-r--r--activestorage/lib/active_storage/service.rb117
-rw-r--r--activestorage/lib/active_storage/service/azure_storage_service.rb120
-rw-r--r--activestorage/lib/active_storage/service/configurator.rb32
-rw-r--r--activestorage/lib/active_storage/service/disk_service.rb128
-rw-r--r--activestorage/lib/active_storage/service/gcs_service.rb83
-rw-r--r--activestorage/lib/active_storage/service/mirror_service.rb50
-rw-r--r--activestorage/lib/active_storage/service/s3_service.rb100
-rw-r--r--activestorage/lib/active_storage/version.rb10
-rw-r--r--activestorage/lib/tasks/activestorage.rake8
-rw-r--r--activestorage/package.json33
-rw-r--r--activestorage/test/controllers/blobs_controller_test.rb17
-rw-r--r--activestorage/test/controllers/direct_uploads_controller_test.rb124
-rw-r--r--activestorage/test/controllers/disk_controller_test.rb59
-rw-r--r--activestorage/test/controllers/variants_controller_test.rb23
-rw-r--r--activestorage/test/database/create_users_migration.rb9
-rw-r--r--activestorage/test/database/setup.rb7
-rw-r--r--activestorage/test/dummy/Rakefile5
-rw-r--r--activestorage/test/dummy/app/assets/config/manifest.js5
-rw-r--r--activestorage/test/dummy/app/assets/images/.keep0
-rw-r--r--activestorage/test/dummy/app/assets/javascripts/application.js13
-rw-r--r--activestorage/test/dummy/app/assets/stylesheets/application.css15
-rw-r--r--activestorage/test/dummy/app/controllers/application_controller.rb5
-rw-r--r--activestorage/test/dummy/app/controllers/concerns/.keep0
-rw-r--r--activestorage/test/dummy/app/helpers/application_helper.rb4
-rw-r--r--activestorage/test/dummy/app/jobs/application_job.rb4
-rw-r--r--activestorage/test/dummy/app/models/application_record.rb5
-rw-r--r--activestorage/test/dummy/app/models/concerns/.keep0
-rw-r--r--activestorage/test/dummy/app/views/layouts/application.html.erb14
-rwxr-xr-xactivestorage/test/dummy/bin/bundle5
-rwxr-xr-xactivestorage/test/dummy/bin/rails6
-rwxr-xr-xactivestorage/test/dummy/bin/rake6
-rwxr-xr-xactivestorage/test/dummy/bin/yarn13
-rw-r--r--activestorage/test/dummy/config.ru7
-rw-r--r--activestorage/test/dummy/config/application.rb26
-rw-r--r--activestorage/test/dummy/config/boot.rb7
-rw-r--r--activestorage/test/dummy/config/database.yml25
-rw-r--r--activestorage/test/dummy/config/environment.rb7
-rw-r--r--activestorage/test/dummy/config/environments/development.rb51
-rw-r--r--activestorage/test/dummy/config/environments/production.rb84
-rw-r--r--activestorage/test/dummy/config/environments/test.rb35
-rw-r--r--activestorage/test/dummy/config/initializers/application_controller_renderer.rb7
-rw-r--r--activestorage/test/dummy/config/initializers/assets.rb16
-rw-r--r--activestorage/test/dummy/config/initializers/backtrace_silencers.rb8
-rw-r--r--activestorage/test/dummy/config/initializers/cookies_serializer.rb7
-rw-r--r--activestorage/test/dummy/config/initializers/filter_parameter_logging.rb6
-rw-r--r--activestorage/test/dummy/config/initializers/inflections.rb17
-rw-r--r--activestorage/test/dummy/config/initializers/mime_types.rb5
-rw-r--r--activestorage/test/dummy/config/initializers/wrap_parameters.rb16
-rw-r--r--activestorage/test/dummy/config/routes.rb4
-rw-r--r--activestorage/test/dummy/config/secrets.yml32
-rw-r--r--activestorage/test/dummy/config/spring.rb8
-rw-r--r--activestorage/test/dummy/config/storage.yml3
-rw-r--r--activestorage/test/dummy/lib/assets/.keep0
-rw-r--r--activestorage/test/dummy/log/.keep0
-rw-r--r--activestorage/test/dummy/package.json5
-rw-r--r--activestorage/test/dummy/public/404.html67
-rw-r--r--activestorage/test/dummy/public/422.html67
-rw-r--r--activestorage/test/dummy/public/500.html66
-rw-r--r--activestorage/test/dummy/public/apple-touch-icon-precomposed.png0
-rw-r--r--activestorage/test/dummy/public/apple-touch-icon.png0
-rw-r--r--activestorage/test/dummy/public/favicon.ico0
-rw-r--r--activestorage/test/fixtures/files/racecar.jpgbin0 -> 1124062 bytes
-rw-r--r--activestorage/test/models/attachments_test.rb125
-rw-r--r--activestorage/test/models/blob_test.rb49
-rw-r--r--activestorage/test/models/filename_test.rb38
-rw-r--r--activestorage/test/models/variant_test.rb29
-rw-r--r--activestorage/test/service/azure_storage_service_test.rb22
-rw-r--r--activestorage/test/service/configurations.yml30
-rw-r--r--activestorage/test/service/configurations.yml.encbin0 -> 2864 bytes
-rw-r--r--activestorage/test/service/configurator_test.rb16
-rw-r--r--activestorage/test/service/disk_service_test.rb14
-rw-r--r--activestorage/test/service/gcs_service_test.rb46
-rw-r--r--activestorage/test/service/mirror_service_test.rb64
-rw-r--r--activestorage/test/service/s3_service_test.rb59
-rw-r--r--activestorage/test/service/shared_service_tests.rb69
-rw-r--r--activestorage/test/template/image_tag_test.rb38
-rw-r--r--activestorage/test/test_helper.rb69
-rw-r--r--activestorage/webpack.config.js27
-rw-r--r--activestorage/yarn.lock3164
-rw-r--r--activesupport/CHANGELOG.md19
-rw-r--r--activesupport/lib/active_support/core_ext/module/delegation.rb10
-rw-r--r--activesupport/lib/active_support/current_attributes.rb2
-rw-r--r--activesupport/lib/active_support/hash_with_indifferent_access.rb2
-rw-r--r--activesupport/lib/active_support/lazy_load_hooks.rb38
-rw-r--r--activesupport/lib/active_support/message_encryptor.rb14
-rw-r--r--activesupport/lib/active_support/message_verifier.rb5
-rw-r--r--activesupport/lib/active_support/messages/metadata.rb49
-rw-r--r--activesupport/test/core_ext/module_test.rb8
-rw-r--r--activesupport/test/json/encoding_test.rb4
-rw-r--r--activesupport/test/lazy_load_hooks_test.rb42
-rw-r--r--activesupport/test/message_verifier_test.rb19
-rw-r--r--activesupport/test/metadata/shared_metadata_tests.rb61
-rwxr-xr-xci/travis.rb4
-rw-r--r--guides/Rakefile2
-rw-r--r--guides/bug_report_templates/action_controller_gem.rb2
-rw-r--r--guides/bug_report_templates/action_controller_master.rb2
-rw-r--r--guides/bug_report_templates/active_job_gem.rb2
-rw-r--r--guides/bug_report_templates/active_job_master.rb2
-rw-r--r--guides/bug_report_templates/active_record_gem.rb2
-rw-r--r--guides/bug_report_templates/active_record_master.rb2
-rw-r--r--guides/bug_report_templates/active_record_migrations_gem.rb2
-rw-r--r--guides/bug_report_templates/active_record_migrations_master.rb2
-rw-r--r--guides/bug_report_templates/benchmark.rb2
-rw-r--r--guides/bug_report_templates/generic_gem.rb2
-rw-r--r--guides/bug_report_templates/generic_master.rb2
-rw-r--r--guides/rails_guides.rb2
-rw-r--r--guides/rails_guides/generator.rb2
-rw-r--r--guides/rails_guides/helpers.rb2
-rw-r--r--guides/rails_guides/indexer.rb2
-rw-r--r--guides/rails_guides/kindle.rb1
-rw-r--r--guides/rails_guides/levenshtein.rb2
-rw-r--r--guides/rails_guides/markdown.rb2
-rw-r--r--guides/rails_guides/markdown/renderer.rb2
-rw-r--r--guides/source/action_cable_overview.md2
-rw-r--r--guides/source/action_mailer_basics.md2
-rw-r--r--guides/source/active_job_basics.md52
-rw-r--r--guides/source/active_record_callbacks.md2
-rw-r--r--guides/source/asset_pipeline.md4
-rw-r--r--guides/source/association_basics.md36
-rw-r--r--guides/source/command_line.md3
-rw-r--r--guides/source/configuring.md4
-rw-r--r--guides/source/debugging_rails_applications.md14
-rw-r--r--guides/source/engines.md3
-rw-r--r--guides/source/generators.md10
-rw-r--r--guides/source/plugins.md57
-rw-r--r--guides/source/routing.md2
-rw-r--r--guides/source/security.md6
-rw-r--r--guides/source/testing.md10
-rw-r--r--guides/source/upgrading_ruby_on_rails.md2
-rw-r--r--guides/w3c_validator.rb2
-rw-r--r--rails.gemspec3
-rw-r--r--railties/CHANGELOG.md34
-rw-r--r--railties/Rakefile2
-rwxr-xr-xrailties/bin/test1
-rwxr-xr-xrailties/exe/rails1
-rw-r--r--railties/lib/minitest/rails_plugin.rb5
-rw-r--r--railties/lib/rails.rb2
-rw-r--r--railties/lib/rails/all.rb3
-rw-r--r--railties/lib/rails/api/generator.rb2
-rw-r--r--railties/lib/rails/api/task.rb9
-rw-r--r--railties/lib/rails/app_loader.rb2
-rw-r--r--railties/lib/rails/app_updater.rb2
-rw-r--r--railties/lib/rails/application.rb2
-rw-r--r--railties/lib/rails/application/bootstrap.rb2
-rw-r--r--railties/lib/rails/application/configuration.rb2
-rw-r--r--railties/lib/rails/application/default_middleware_stack.rb2
-rw-r--r--railties/lib/rails/application/finisher.rb2
-rw-r--r--railties/lib/rails/application/routes_reloader.rb2
-rw-r--r--railties/lib/rails/application_controller.rb2
-rw-r--r--railties/lib/rails/backtrace_cleaner.rb2
-rw-r--r--railties/lib/rails/cli.rb2
-rw-r--r--railties/lib/rails/code_statistics.rb2
-rw-r--r--railties/lib/rails/code_statistics_calculator.rb2
-rw-r--r--railties/lib/rails/command.rb2
-rw-r--r--railties/lib/rails/command/actions.rb2
-rw-r--r--railties/lib/rails/command/base.rb4
-rw-r--r--railties/lib/rails/command/behavior.rb2
-rw-r--r--railties/lib/rails/command/environment_argument.rb2
-rw-r--r--railties/lib/rails/commands.rb2
-rw-r--r--railties/lib/rails/commands/application/application_command.rb2
-rw-r--r--railties/lib/rails/commands/console/console_command.rb2
-rw-r--r--railties/lib/rails/commands/dbconsole/dbconsole_command.rb4
-rw-r--r--railties/lib/rails/commands/destroy/destroy_command.rb2
-rw-r--r--railties/lib/rails/commands/generate/generate_command.rb2
-rw-r--r--railties/lib/rails/commands/help/help_command.rb2
-rw-r--r--railties/lib/rails/commands/new/new_command.rb2
-rw-r--r--railties/lib/rails/commands/plugin/plugin_command.rb2
-rw-r--r--railties/lib/rails/commands/rake/rake_command.rb2
-rw-r--r--railties/lib/rails/commands/runner/runner_command.rb2
-rw-r--r--railties/lib/rails/commands/secrets/secrets_command.rb2
-rw-r--r--railties/lib/rails/commands/server/server_command.rb11
-rw-r--r--railties/lib/rails/commands/test/test_command.rb37
-rw-r--r--railties/lib/rails/commands/version/version_command.rb2
-rw-r--r--railties/lib/rails/configuration.rb2
-rw-r--r--railties/lib/rails/console/app.rb2
-rw-r--r--railties/lib/rails/console/helpers.rb2
-rw-r--r--railties/lib/rails/dev_caching.rb2
-rw-r--r--railties/lib/rails/engine.rb2
-rw-r--r--railties/lib/rails/engine/commands.rb2
-rw-r--r--railties/lib/rails/engine/configuration.rb2
-rw-r--r--railties/lib/rails/engine/railties.rb2
-rw-r--r--railties/lib/rails/engine/updater.rb4
-rw-r--r--railties/lib/rails/gem_version.rb2
-rw-r--r--railties/lib/rails/generators.rb5
-rw-r--r--railties/lib/rails/generators/actions.rb47
-rw-r--r--railties/lib/rails/generators/actions/create_migration.rb2
-rw-r--r--railties/lib/rails/generators/active_model.rb2
-rw-r--r--railties/lib/rails/generators/app_base.rb2
-rw-r--r--railties/lib/rails/generators/base.rb2
-rw-r--r--railties/lib/rails/generators/css/assets/assets_generator.rb2
-rw-r--r--railties/lib/rails/generators/css/scaffold/scaffold_generator.rb2
-rw-r--r--railties/lib/rails/generators/erb.rb2
-rw-r--r--railties/lib/rails/generators/erb/controller/controller_generator.rb2
-rw-r--r--railties/lib/rails/generators/erb/mailer/mailer_generator.rb2
-rw-r--r--railties/lib/rails/generators/erb/scaffold/scaffold_generator.rb2
-rw-r--r--railties/lib/rails/generators/generated_attribute.rb4
-rw-r--r--railties/lib/rails/generators/js/assets/assets_generator.rb2
-rw-r--r--railties/lib/rails/generators/migration.rb2
-rw-r--r--railties/lib/rails/generators/model_helpers.rb2
-rw-r--r--railties/lib/rails/generators/named_base.rb2
-rw-r--r--railties/lib/rails/generators/rails/app/app_generator.rb20
-rw-r--r--railties/lib/rails/generators/rails/app/templates/Rakefile2
-rw-r--r--railties/lib/rails/generators/rails/app/templates/app/assets/javascripts/application.js.tt1
-rw-r--r--railties/lib/rails/generators/rails/app/templates/app/channels/application_cable/channel.rb2
-rw-r--r--railties/lib/rails/generators/rails/app/templates/app/channels/application_cable/connection.rb2
-rw-r--r--railties/lib/rails/generators/rails/app/templates/app/controllers/application_controller.rb.tt2
-rw-r--r--railties/lib/rails/generators/rails/app/templates/app/helpers/application_helper.rb2
-rw-r--r--railties/lib/rails/generators/rails/app/templates/app/jobs/application_job.rb2
-rw-r--r--railties/lib/rails/generators/rails/app/templates/app/mailers/application_mailer.rb2
-rw-r--r--railties/lib/rails/generators/rails/app/templates/app/models/application_record.rb2
-rw-r--r--railties/lib/rails/generators/rails/app/templates/bin/setup.tt4
-rw-r--r--railties/lib/rails/generators/rails/app/templates/bin/update.tt2
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/application.rb5
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/boot.rb2
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/environment.rb2
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt5
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt3
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt5
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/initializers/application_controller_renderer.rb12
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/initializers/backtrace_silencers.rb2
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/initializers/cookies_serializer.rb2
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/initializers/cors.rb2
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/initializers/filter_parameter_logging.rb2
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/initializers/inflections.rb2
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/initializers/mime_types.rb2
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/puma.rb2
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/routes.rb2
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/spring.rb2
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/storage.yml35
-rw-r--r--railties/lib/rails/generators/rails/app/templates/gitignore8
-rw-r--r--railties/lib/rails/generators/rails/app/templates/test/application_system_test_case.rb2
-rw-r--r--railties/lib/rails/generators/rails/app/templates/test/test_helper.rb2
-rw-r--r--railties/lib/rails/generators/rails/assets/assets_generator.rb2
-rw-r--r--railties/lib/rails/generators/rails/controller/controller_generator.rb20
-rw-r--r--railties/lib/rails/generators/rails/controller/templates/controller.rb2
-rw-r--r--railties/lib/rails/generators/rails/encrypted_secrets/encrypted_secrets_generator.rb2
-rw-r--r--railties/lib/rails/generators/rails/generator/generator_generator.rb2
-rw-r--r--railties/lib/rails/generators/rails/helper/helper_generator.rb2
-rw-r--r--railties/lib/rails/generators/rails/helper/templates/helper.rb2
-rw-r--r--railties/lib/rails/generators/rails/integration_test/integration_test_generator.rb2
-rw-r--r--railties/lib/rails/generators/rails/migration/migration_generator.rb2
-rw-r--r--railties/lib/rails/generators/rails/model/model_generator.rb2
-rw-r--r--railties/lib/rails/generators/rails/plugin/plugin_generator.rb9
-rw-r--r--railties/lib/rails/generators/rails/plugin/templates/Gemfile1
-rw-r--r--railties/lib/rails/generators/rails/plugin/templates/Rakefile13
-rw-r--r--railties/lib/rails/generators/rails/plugin/templates/bin/rails.tt17
-rw-r--r--railties/lib/rails/generators/rails/plugin/templates/config/routes.rb2
-rw-r--r--railties/lib/rails/generators/rails/plugin/templates/gitignore6
-rw-r--r--railties/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%.rb2
-rw-r--r--railties/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%/engine.rb2
-rw-r--r--railties/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%/railtie.rb2
-rw-r--r--railties/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%/version.rb2
-rw-r--r--railties/lib/rails/generators/rails/plugin/templates/lib/tasks/%namespaced_name%_tasks.rake2
-rw-r--r--railties/lib/rails/generators/rails/plugin/templates/rails/application.rb11
-rw-r--r--railties/lib/rails/generators/rails/plugin/templates/rails/boot.rb2
-rw-r--r--railties/lib/rails/generators/rails/plugin/templates/rails/dummy_manifest.js1
-rw-r--r--railties/lib/rails/generators/rails/plugin/templates/rails/routes.rb2
-rw-r--r--railties/lib/rails/generators/rails/plugin/templates/test/%namespaced_name%_test.rb2
-rw-r--r--railties/lib/rails/generators/rails/plugin/templates/test/application_system_test_case.rb2
-rw-r--r--railties/lib/rails/generators/rails/plugin/templates/test/integration/navigation_test.rb2
-rw-r--r--railties/lib/rails/generators/rails/plugin/templates/test/test_helper.rb2
-rw-r--r--railties/lib/rails/generators/rails/resource/resource_generator.rb2
-rw-r--r--railties/lib/rails/generators/rails/resource_route/resource_route_generator.rb45
-rw-r--r--railties/lib/rails/generators/rails/scaffold/scaffold_generator.rb2
-rw-r--r--railties/lib/rails/generators/rails/scaffold_controller/scaffold_controller_generator.rb2
-rw-r--r--railties/lib/rails/generators/rails/scaffold_controller/templates/api_controller.rb2
-rw-r--r--railties/lib/rails/generators/rails/scaffold_controller/templates/controller.rb2
-rw-r--r--railties/lib/rails/generators/rails/system_test/system_test_generator.rb2
-rw-r--r--railties/lib/rails/generators/rails/task/task_generator.rb2
-rw-r--r--railties/lib/rails/generators/rails/task/templates/task.rb2
-rw-r--r--railties/lib/rails/generators/resource_helpers.rb2
-rw-r--r--railties/lib/rails/generators/test_case.rb2
-rw-r--r--railties/lib/rails/generators/test_unit.rb2
-rw-r--r--railties/lib/rails/generators/test_unit/controller/controller_generator.rb2
-rw-r--r--railties/lib/rails/generators/test_unit/controller/templates/functional_test.rb2
-rw-r--r--railties/lib/rails/generators/test_unit/generator/generator_generator.rb2
-rw-r--r--railties/lib/rails/generators/test_unit/generator/templates/generator_test.rb2
-rw-r--r--railties/lib/rails/generators/test_unit/helper/helper_generator.rb2
-rw-r--r--railties/lib/rails/generators/test_unit/integration/integration_generator.rb2
-rw-r--r--railties/lib/rails/generators/test_unit/integration/templates/integration_test.rb2
-rw-r--r--railties/lib/rails/generators/test_unit/job/job_generator.rb2
-rw-r--r--railties/lib/rails/generators/test_unit/mailer/mailer_generator.rb2
-rw-r--r--railties/lib/rails/generators/test_unit/mailer/templates/functional_test.rb2
-rw-r--r--railties/lib/rails/generators/test_unit/mailer/templates/preview.rb2
-rw-r--r--railties/lib/rails/generators/test_unit/model/model_generator.rb2
-rw-r--r--railties/lib/rails/generators/test_unit/model/templates/unit_test.rb2
-rw-r--r--railties/lib/rails/generators/test_unit/plugin/plugin_generator.rb2
-rw-r--r--railties/lib/rails/generators/test_unit/plugin/templates/test_helper.rb2
-rw-r--r--railties/lib/rails/generators/test_unit/scaffold/scaffold_generator.rb2
-rw-r--r--railties/lib/rails/generators/test_unit/scaffold/templates/api_functional_test.rb2
-rw-r--r--railties/lib/rails/generators/test_unit/scaffold/templates/functional_test.rb2
-rw-r--r--railties/lib/rails/generators/test_unit/scaffold/templates/system_test.rb2
-rw-r--r--railties/lib/rails/generators/test_unit/system/system_generator.rb2
-rw-r--r--railties/lib/rails/generators/test_unit/system/templates/application_system_test_case.rb2
-rw-r--r--railties/lib/rails/generators/test_unit/system/templates/system_test.rb2
-rw-r--r--railties/lib/rails/generators/testing/assertions.rb2
-rw-r--r--railties/lib/rails/generators/testing/behaviour.rb2
-rw-r--r--railties/lib/rails/generators/testing/setup_and_teardown.rb2
-rw-r--r--railties/lib/rails/info.rb4
-rw-r--r--railties/lib/rails/info_controller.rb2
-rw-r--r--railties/lib/rails/initializable.rb2
-rw-r--r--railties/lib/rails/mailers_controller.rb2
-rw-r--r--railties/lib/rails/paths.rb2
-rw-r--r--railties/lib/rails/plugin/test.rb2
-rw-r--r--railties/lib/rails/rack.rb2
-rw-r--r--railties/lib/rails/rack/logger.rb2
-rw-r--r--railties/lib/rails/railtie.rb2
-rw-r--r--railties/lib/rails/railtie/configurable.rb2
-rw-r--r--railties/lib/rails/railtie/configuration.rb2
-rw-r--r--railties/lib/rails/ruby_version_check.rb2
-rw-r--r--railties/lib/rails/secrets.rb2
-rw-r--r--railties/lib/rails/source_annotation_extractor.rb4
-rw-r--r--railties/lib/rails/tasks.rb2
-rw-r--r--railties/lib/rails/tasks/annotations.rake2
-rw-r--r--railties/lib/rails/tasks/dev.rake2
-rw-r--r--railties/lib/rails/tasks/engine.rake2
-rw-r--r--railties/lib/rails/tasks/framework.rake4
-rw-r--r--railties/lib/rails/tasks/initializers.rake2
-rw-r--r--railties/lib/rails/tasks/log.rake2
-rw-r--r--railties/lib/rails/tasks/middleware.rake2
-rw-r--r--railties/lib/rails/tasks/misc.rake2
-rw-r--r--railties/lib/rails/tasks/restart.rake2
-rw-r--r--railties/lib/rails/tasks/routes.rake2
-rw-r--r--railties/lib/rails/tasks/statistics.rake2
-rw-r--r--railties/lib/rails/tasks/tmp.rake2
-rw-r--r--railties/lib/rails/tasks/yarn.rake2
-rw-r--r--railties/lib/rails/test_help.rb2
-rw-r--r--railties/lib/rails/test_unit/line_filtering.rb2
-rw-r--r--railties/lib/rails/test_unit/railtie.rb2
-rw-r--r--railties/lib/rails/test_unit/reporter.rb2
-rw-r--r--railties/lib/rails/test_unit/runner.rb2
-rw-r--r--railties/lib/rails/test_unit/testing.rake2
-rw-r--r--railties/lib/rails/version.rb2
-rw-r--r--railties/lib/rails/welcome_controller.rb2
-rw-r--r--railties/railties.gemspec2
-rw-r--r--railties/test/abstract_unit.rb2
-rw-r--r--railties/test/app_loader_test.rb2
-rw-r--r--railties/test/application/asset_debugging_test.rb2
-rw-r--r--railties/test/application/assets_test.rb6
-rw-r--r--railties/test/application/bin_setup_test.rb2
-rw-r--r--railties/test/application/configuration/custom_test.rb2
-rw-r--r--railties/test/application/configuration_test.rb82
-rw-r--r--railties/test/application/console_test.rb2
-rw-r--r--railties/test/application/current_attributes_integration_test.rb2
-rw-r--r--railties/test/application/dbconsole_test.rb2
-rw-r--r--railties/test/application/generators_test.rb2
-rw-r--r--railties/test/application/help_test.rb2
-rw-r--r--railties/test/application/initializers/frameworks_test.rb2
-rw-r--r--railties/test/application/initializers/hooks_test.rb2
-rw-r--r--railties/test/application/initializers/i18n_test.rb2
-rw-r--r--railties/test/application/initializers/load_path_test.rb2
-rw-r--r--railties/test/application/initializers/notifications_test.rb2
-rw-r--r--railties/test/application/integration_test_case_test.rb2
-rw-r--r--railties/test/application/loading_test.rb8
-rw-r--r--railties/test/application/mailer_previews_test.rb6
-rw-r--r--railties/test/application/middleware/cache_test.rb12
-rw-r--r--railties/test/application/middleware/cookies_test.rb2
-rw-r--r--railties/test/application/middleware/exceptions_test.rb2
-rw-r--r--railties/test/application/middleware/remote_ip_test.rb2
-rw-r--r--railties/test/application/middleware/sendfile_test.rb2
-rw-r--r--railties/test/application/middleware/session_test.rb2
-rw-r--r--railties/test/application/middleware/static_test.rb2
-rw-r--r--railties/test/application/middleware_test.rb4
-rw-r--r--railties/test/application/multiple_applications_test.rb2
-rw-r--r--railties/test/application/paths_test.rb2
-rw-r--r--railties/test/application/per_request_digest_cache_test.rb2
-rw-r--r--railties/test/application/rack/logger_test.rb2
-rw-r--r--railties/test/application/rackup_test.rb2
-rw-r--r--railties/test/application/rake/dbs_test.rb2
-rw-r--r--railties/test/application/rake/dev_test.rb2
-rw-r--r--railties/test/application/rake/framework_test.rb2
-rw-r--r--railties/test/application/rake/log_test.rb2
-rw-r--r--railties/test/application/rake/migrations_test.rb2
-rw-r--r--railties/test/application/rake/notes_test.rb5
-rw-r--r--railties/test/application/rake/restart_test.rb2
-rw-r--r--railties/test/application/rake/tmp_test.rb2
-rw-r--r--railties/test/application/rake_test.rb48
-rw-r--r--railties/test/application/rendering_test.rb2
-rw-r--r--railties/test/application/routing_test.rb2
-rw-r--r--railties/test/application/runner_test.rb2
-rw-r--r--railties/test/application/server_test.rb33
-rw-r--r--railties/test/application/test_runner_test.rb2
-rw-r--r--railties/test/application/test_test.rb2
-rw-r--r--railties/test/application/url_generation_test.rb2
-rw-r--r--railties/test/application/version_test.rb2
-rw-r--r--railties/test/backtrace_cleaner_test.rb2
-rw-r--r--railties/test/code_statistics_calculator_test.rb2
-rw-r--r--railties/test/code_statistics_test.rb2
-rw-r--r--railties/test/command/base_test.rb2
-rw-r--r--railties/test/commands/console_test.rb2
-rw-r--r--railties/test/commands/dbconsole_test.rb2
-rw-r--r--railties/test/commands/secrets_test.rb2
-rw-r--r--railties/test/commands/server_test.rb2
-rw-r--r--railties/test/configuration/middleware_stack_proxy_test.rb2
-rw-r--r--railties/test/console_helpers.rb4
-rw-r--r--railties/test/engine/commands_test.rb2
-rw-r--r--railties/test/engine_test.rb2
-rw-r--r--railties/test/env_helpers.rb2
-rw-r--r--railties/test/fixtures/about_yml_plugins/bad_about_yml/init.rb2
-rw-r--r--railties/test/fixtures/about_yml_plugins/plugin_without_about_yml/init.rb2
-rw-r--r--railties/test/fixtures/lib/create_test_dummy_template.rb2
-rw-r--r--railties/test/fixtures/lib/generators/active_record/fixjour_generator.rb2
-rw-r--r--railties/test/fixtures/lib/generators/fixjour_generator.rb2
-rw-r--r--railties/test/fixtures/lib/generators/model_generator.rb2
-rw-r--r--railties/test/fixtures/lib/generators/usage_template/usage_template_generator.rb2
-rw-r--r--railties/test/fixtures/lib/rails/generators/foobar/foobar_generator.rb2
-rw-r--r--railties/test/fixtures/lib/template.rb2
-rw-r--r--railties/test/generators/actions_test.rb76
-rw-r--r--railties/test/generators/api_app_generator_test.rb61
-rw-r--r--railties/test/generators/app_generator_test.rb100
-rw-r--r--railties/test/generators/application_record_generator_test.rb2
-rw-r--r--railties/test/generators/argv_scrubber_test.rb2
-rw-r--r--railties/test/generators/assets_generator_test.rb2
-rw-r--r--railties/test/generators/channel_generator_test.rb2
-rw-r--r--railties/test/generators/controller_generator_test.rb9
-rw-r--r--railties/test/generators/create_migration_test.rb10
-rw-r--r--railties/test/generators/encrypted_secrets_generator_test.rb2
-rw-r--r--railties/test/generators/generated_attribute_test.rb2
-rw-r--r--railties/test/generators/generator_generator_test.rb2
-rw-r--r--railties/test/generators/generator_test.rb2
-rw-r--r--railties/test/generators/generators_test_helper.rb2
-rw-r--r--railties/test/generators/helper_generator_test.rb2
-rw-r--r--railties/test/generators/integration_test_generator_test.rb2
-rw-r--r--railties/test/generators/job_generator_test.rb2
-rw-r--r--railties/test/generators/mailer_generator_test.rb2
-rw-r--r--railties/test/generators/migration_generator_test.rb2
-rw-r--r--railties/test/generators/model_generator_test.rb2
-rw-r--r--railties/test/generators/named_base_test.rb2
-rw-r--r--railties/test/generators/namespaced_generators_test.rb2
-rw-r--r--railties/test/generators/orm_test.rb2
-rw-r--r--railties/test/generators/plugin_generator_test.rb96
-rw-r--r--railties/test/generators/plugin_test_helper.rb2
-rw-r--r--railties/test/generators/plugin_test_runner_test.rb2
-rw-r--r--railties/test/generators/resource_generator_test.rb2
-rw-r--r--railties/test/generators/scaffold_controller_generator_test.rb2
-rw-r--r--railties/test/generators/scaffold_generator_test.rb2
-rw-r--r--railties/test/generators/shared_generator_tests.rb34
-rw-r--r--railties/test/generators/system_test_generator_test.rb2
-rw-r--r--railties/test/generators/task_generator_test.rb2
-rw-r--r--railties/test/generators/test_runner_in_engine_test.rb2
-rw-r--r--railties/test/generators_test.rb2
-rw-r--r--railties/test/initializable_test.rb2
-rw-r--r--railties/test/isolation/abstract_unit.rb10
-rw-r--r--railties/test/json_params_parsing_test.rb2
-rw-r--r--railties/test/path_generation_test.rb2
-rw-r--r--railties/test/paths_test.rb2
-rw-r--r--railties/test/rack_logger_test.rb2
-rw-r--r--railties/test/rails_info_controller_test.rb2
-rw-r--r--railties/test/rails_info_test.rb2
-rw-r--r--railties/test/railties/engine_test.rb30
-rw-r--r--railties/test/railties/generators_test.rb2
-rw-r--r--railties/test/railties/mounted_engine_test.rb14
-rw-r--r--railties/test/railties/railtie_test.rb2
-rw-r--r--railties/test/secrets_test.rb6
-rw-r--r--railties/test/test_unit/reporter_test.rb2
-rw-r--r--railties/test/version_test.rb2
-rw-r--r--tasks/release.rb12
-rwxr-xr-xtools/console2
-rwxr-xr-xtools/profile2
-rw-r--r--tools/test.rb2
-rw-r--r--version.rb2
589 files changed, 9655 insertions, 828 deletions
diff --git a/.rubocop.yml b/.rubocop.yml
index 0408ca4aa1..c1101d849b 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -16,6 +16,7 @@ Style/AndOr:
# method call.
Style/BracesAroundHashParameters:
Enabled: true
+ EnforcedStyle: context_dependent
# Align `when` with `case`.
Layout/CaseIndentation:
@@ -28,10 +29,6 @@ Layout/CommentIndentation:
Layout/EmptyLineAfterMagicComment:
Enabled: true
-# No extra empty lines.
-Layout/EmptyLines:
- Enabled: true
-
# In a regular class definition, no empty lines around the body.
Layout/EmptyLinesAroundClassBody:
Enabled: true
@@ -86,15 +83,6 @@ Style/MethodDefParentheses:
Style/FrozenStringLiteralComment:
Enabled: true
EnforcedStyle: always
- Include:
- - 'activesupport/**/*'
- - 'activemodel/**/*'
- - 'actioncable/**/*'
- - 'activejob/**/*'
- - 'activerecord/**/*'
- - 'actionmailer/**/*'
- - 'actionview/**/*'
- - 'actionpack/**/*'
Exclude:
- 'actionview/test/**/*.builder'
- 'actionview/test/**/*.ruby'
diff --git a/.travis.yml b/.travis.yml
index 91ac7e8e5e..95a9099901 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -22,12 +22,12 @@ before_install:
- "gem update bundler"
- "[ -f /tmp/beanstalkd-1.10/Makefile ] || (curl -L https://github.com/kr/beanstalkd/archive/v1.10.tar.gz | tar xz -C /tmp)"
- "pushd /tmp/beanstalkd-1.10 && make && (./beanstalkd &); popd"
+ - "[[ -z $encrypted_8a915ebdd931_key && -z $encrypted_8a915ebdd931_iv ]] || openssl aes-256-cbc -K $encrypted_8a915ebdd931_key -iv $encrypted_8a915ebdd931_iv -in activestorage/test/service/configurations.yml.enc -out activestorage/test/service/configurations.yml -d"
- "[[ $GEM != 'av:ujs' ]] || nvm install node"
- "[[ $GEM != 'av:ujs' ]] || node --version"
- "[[ $GEM != 'av:ujs' ]] || (cd actionview && npm install)"
- "[[ $GEM != 'av:ujs' ]] || [[ $(phantomjs --version) > '2' ]] || npm install -g phantomjs-prebuilt"
-
before_script:
# Set Sauce Labs username and access key. Obfuscated, purposefully not encrypted.
# Decodes to e.g. `export VARIABLE=VALUE`
@@ -43,7 +43,7 @@ env:
matrix:
- "GEM=railties"
- "GEM=ap,ac"
- - "GEM=am,amo,as,av,aj"
+ - "GEM=am,amo,as,av,aj,ast"
- "GEM=as PRESERVE_TIMEZONES=1"
- "GEM=ar:mysql2"
- "GEM=ar:sqlite3"
diff --git a/Gemfile b/Gemfile
index 967aef2e92..0b98dc1f75 100644
--- a/Gemfile
+++ b/Gemfile
@@ -1,9 +1,8 @@
+# frozen_string_literal: true
+
source "https://rubygems.org"
-git_source(:github) do |repo_name|
- repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include?("/")
- "https://github.com/#{repo_name}.git"
-end
+git_source(:github) { |repo| "https://github.com/#{repo}.git" }
gemspec
@@ -11,7 +10,6 @@ gem "arel", github: "rails/arel"
# We need a newish Rake since Active Job sets its test tasks' descriptions.
gem "rake", ">= 11.1"
-gem "thor", github: "erikhuda/thor"
# This needs to be with require false to ensure correct loading order, as it has to
# be loaded after loading the test library.
@@ -92,6 +90,14 @@ group :cable do
gem "sprockets-export", require: false
end
+group :storage do
+ gem "aws-sdk", "~> 2", require: false
+ gem "google-cloud-storage", "~> 1.3", require: false
+ gem "azure-storage", require: false
+
+ gem "mini_magick"
+end
+
# Add your own local bundler stuff.
local_gemfile = File.expand_path(".Gemfile", __dir__)
instance_eval File.read local_gemfile if File.exist? local_gemfile
diff --git a/Gemfile.lock b/Gemfile.lock
index 35940676f7..2096465a8b 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -7,12 +7,6 @@ GIT
pg (>= 0.17, < 0.20)
GIT
- remote: https://github.com/erikhuda/thor.git
- revision: df5ba2b653a28087b3617d6c082b00866b0c0d6c
- specs:
- thor (0.19.4)
-
-GIT
remote: https://github.com/matthewd/rb-inotify.git
revision: 856730aad4b285969e8dd621e44808a7c5af4242
branch: close-handling
@@ -31,7 +25,7 @@ GIT
GIT
remote: https://github.com/rails/arel.git
- revision: f4227bf20a16a1d688afc24cf0038e2f98905dd4
+ revision: 42510bf71472e2e35d9becb546edd05562672344
specs:
arel (9.0.0.alpha)
@@ -82,6 +76,9 @@ PATH
activemodel (= 5.2.0.alpha)
activesupport (= 5.2.0.alpha)
arel (= 9.0.0.alpha)
+ activestorage (5.2.0.alpha)
+ actionpack (= 5.2.0.alpha)
+ activerecord (= 5.2.0.alpha)
activesupport (5.2.0.alpha)
concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (~> 0.7)
@@ -95,6 +92,7 @@ PATH
activejob (= 5.2.0.alpha)
activemodel (= 5.2.0.alpha)
activerecord (= 5.2.0.alpha)
+ activestorage (= 5.2.0.alpha)
activesupport (= 5.2.0.alpha)
bundler (>= 1.3.0)
railties (= 5.2.0.alpha)
@@ -113,6 +111,21 @@ GEM
public_suffix (~> 2.0, >= 2.0.2)
amq-protocol (2.2.0)
ast (2.3.0)
+ aws-sdk (2.10.27)
+ aws-sdk-resources (= 2.10.27)
+ aws-sdk-core (2.10.27)
+ aws-sigv4 (~> 1.0)
+ jmespath (~> 1.0)
+ aws-sdk-resources (2.10.27)
+ aws-sdk-core (= 2.10.27)
+ aws-sigv4 (1.0.1)
+ azure-core (0.1.10)
+ faraday (~> 0.9)
+ faraday_middleware (~> 0.10)
+ azure-storage (0.12.3.preview)
+ azure-core (~> 0.1)
+ faraday (~> 0.9)
+ faraday_middleware (~> 0.10)
backburner (1.4.1)
beaneater (~> 1.0)
concurrent-ruby (~> 1.0.1)
@@ -135,7 +148,7 @@ GEM
thor (~> 0.19.1)
useragent (~> 0.16.7)
blade-qunit_adapter (2.0.1)
- blade-sauce_labs_plugin (0.7.1)
+ blade-sauce_labs_plugin (0.7.2)
childprocess
faraday
selenium-webdriver
@@ -145,9 +158,9 @@ GEM
bunny (2.6.6)
amq-protocol (>= 2.1.0)
byebug (9.0.6)
- capybara (2.14.4)
+ capybara (2.15.1)
addressable
- mime-types (>= 1.16)
+ mini_mime (>= 0.1.3)
nokogiri (>= 1.3.3)
rack (>= 1.0.0)
rack-test (>= 0.5.4)
@@ -168,11 +181,14 @@ GEM
daemons (1.2.4)
dalli (2.7.6)
dante (0.2.0)
+ declarative (0.0.9)
+ declarative-option (0.1.0)
delayed_job (4.1.3)
activesupport (>= 3.0, < 5.2)
delayed_job_active_record (4.1.2)
activerecord (>= 3.0, < 5.2)
delayed_job (>= 3.0, < 5)
+ digest-crc (0.4.1)
em-hiredis (0.3.1)
eventmachine (~> 1.0)
hiredis (~> 0.6.0)
@@ -189,12 +205,14 @@ GEM
et-orbi (1.0.5)
tzinfo
event_emitter (0.2.6)
- eventmachine (1.2.3)
- eventmachine (1.2.3-x64-mingw32)
- eventmachine (1.2.3-x86-mingw32)
+ eventmachine (1.2.5)
+ eventmachine (1.2.5-x64-mingw32)
+ eventmachine (1.2.5-x86-mingw32)
execjs (2.7.0)
- faraday (0.12.2)
+ faraday (0.13.0)
multipart-post (>= 1.2, < 3)
+ faraday_middleware (0.12.2)
+ faraday (>= 0.7.4, < 1.0)
faye (1.2.4)
cookiejar (>= 0.3.0)
em-http-request (>= 0.3.0)
@@ -211,14 +229,41 @@ GEM
ffi (1.9.18-x86-mingw32)
globalid (0.4.0)
activesupport (>= 4.2.0)
+ google-api-client (0.13.1)
+ addressable (~> 2.5, >= 2.5.1)
+ googleauth (~> 0.5)
+ httpclient (>= 2.8.1, < 3.0)
+ mime-types (~> 3.0)
+ representable (~> 3.0)
+ retriable (>= 2.0, < 4.0)
+ google-cloud-core (1.0.0)
+ google-cloud-env (~> 1.0)
+ googleauth (~> 0.5.1)
+ google-cloud-env (1.0.1)
+ faraday (~> 0.11)
+ google-cloud-storage (1.4.0)
+ digest-crc (~> 0.4)
+ google-api-client (~> 0.13.0)
+ google-cloud-core (~> 1.0)
+ googleauth (0.5.3)
+ faraday (~> 0.12)
+ jwt (~> 1.4)
+ logging (~> 2.0)
+ memoist (~> 0.12)
+ multi_json (~> 1.11)
+ os (~> 0.9)
+ signet (~> 0.7)
hiredis (0.6.1)
http_parser.rb (0.6.0)
+ httpclient (2.8.3)
i18n (0.8.6)
+ jmespath (1.3.1)
jquery-rails (4.3.1)
rails-dom-testing (>= 1, < 3)
railties (>= 4.2.0)
thor (>= 0.14, < 2.0)
json (2.1.0)
+ jwt (1.5.6)
kindlerb (1.2.0)
mustache
nokogiri
@@ -227,17 +272,24 @@ GEM
rb-fsevent (~> 0.9, >= 0.9.4)
rb-inotify (~> 0.9, >= 0.9.7)
ruby_dep (~> 1.2)
+ little-plugger (1.1.4)
+ logging (2.2.2)
+ little-plugger (~> 1.1)
+ multi_json (~> 1.10)
loofah (2.0.3)
nokogiri (>= 1.5.9)
mail (2.6.6)
mime-types (>= 1.16, < 4)
+ memoist (0.16.0)
metaclass (0.0.4)
method_source (0.8.2)
mime-types (3.1)
mime-types-data (~> 3.2015)
mime-types-data (3.2016.0521)
+ mini_magick (4.8.0)
+ mini_mime (0.1.4)
mini_portile2 (2.2.0)
- minitest (5.10.2)
+ minitest (5.10.3)
minitest-bisect (1.4.0)
minitest-server (~> 1.0)
path_expander (~> 1.0)
@@ -253,9 +305,9 @@ GEM
multipart-post (2.0.0)
mustache (1.0.5)
mustermann (1.0.0)
- mysql2 (0.4.8)
- mysql2 (0.4.8-x64-mingw32)
- mysql2 (0.4.8-x86-mingw32)
+ mysql2 (0.4.9)
+ mysql2 (0.4.9-x64-mingw32)
+ mysql2 (0.4.9-x86-mingw32)
nio4r (2.1.0)
nokogiri (1.8.0)
mini_portile2 (~> 2.2.0)
@@ -263,10 +315,11 @@ GEM
mini_portile2 (~> 2.2.0)
nokogiri (1.8.0-x86-mingw32)
mini_portile2 (~> 2.2.0)
- parallel (1.11.2)
+ os (0.9.6)
+ parallel (1.12.0)
parser (2.4.0.0)
ast (~> 2.2)
- path_expander (1.0.1)
+ path_expander (1.0.2)
pg (0.19.0)
pg (0.19.0-x64-mingw32)
pg (0.19.0-x86-mingw32)
@@ -280,7 +333,7 @@ GEM
qu (= 0.2.0)
redis-namespace
simple_uuid
- que (0.13.1)
+ que (0.14.0)
racc (1.4.14)
rack (2.0.3)
rack-cache (1.7.0)
@@ -303,6 +356,10 @@ GEM
redis (3.3.3)
redis-namespace (1.5.3)
redis (~> 3.0, >= 3.0.4)
+ representable (3.0.4)
+ declarative (< 0.1.0)
+ declarative-option (< 0.2.0)
+ uber (< 0.2.0)
resque (1.27.4)
mono_logger (~> 1.0)
multi_json (~> 1.0)
@@ -314,6 +371,7 @@ GEM
redis (~> 3.3)
resque (~> 1.26)
rufus-scheduler (~> 3.2)
+ retriable (3.1.1)
rubocop (0.49.1)
parallel (~> 1.10)
parser (>= 2.3.3.1, < 3.0)
@@ -333,10 +391,10 @@ GEM
rb-inotify (~> 0.9, >= 0.9.7)
sdoc (1.0.0.rc2)
rdoc (~> 5.0)
- selenium-webdriver (3.4.4)
+ selenium-webdriver (3.5.1)
childprocess (~> 0.5)
rubyzip (~> 1.0)
- sequel (4.48.0)
+ sequel (4.49.0)
serverengine (1.5.11)
sigdump (~> 0.2.2)
sidekiq (5.0.4)
@@ -345,6 +403,11 @@ GEM
rack-protection (>= 1.5.0)
redis (~> 3.3, >= 3.3.3)
sigdump (0.2.4)
+ signet (0.7.3)
+ addressable (~> 2.3)
+ faraday (~> 0.9)
+ jwt (~> 1.5)
+ multi_json (~> 1.10)
simple_uuid (0.4.0)
sinatra (2.0.0)
mustermann (~> 1.0)
@@ -374,6 +437,7 @@ GEM
daemons (~> 1.0, >= 1.0.9)
eventmachine (~> 1.0, >= 1.0.4)
rack (>= 1, < 3)
+ thor (0.19.4)
thread (0.1.7)
thread_safe (0.3.6)
tilt (2.0.8)
@@ -384,6 +448,7 @@ GEM
thread_safe (~> 0.1)
tzinfo-data (1.2017.2)
tzinfo (>= 1.0.0)
+ uber (0.1.0)
uglifier (3.2.0)
execjs (>= 0.3.0, < 3)
unicode-display_width (1.3.0)
@@ -411,6 +476,8 @@ DEPENDENCIES
activerecord-jdbcpostgresql-adapter (>= 1.3.0)
activerecord-jdbcsqlite3-adapter (>= 1.3.0)
arel!
+ aws-sdk (~> 2)
+ azure-storage
backburner
bcrypt (~> 3.1.11)
benchmark-ips
@@ -425,12 +492,14 @@ DEPENDENCIES
delayed_job_active_record
em-hiredis
erubis (~> 2.7.0)
+ google-cloud-storage (~> 1.3)
hiredis
jquery-rails
json (>= 2.0.0)
kindlerb (~> 1.2.0)
libxml-ruby
listen (>= 3.0.5, < 3.2)
+ mini_magick
minitest-bisect
mocha (~> 0.14)
mysql2 (>= 0.4.4)
@@ -460,7 +529,6 @@ DEPENDENCIES
sqlite3 (~> 1.3.6)
stackprof
sucker_punch
- thor!
turbolinks (~> 5)
tzinfo-data
uglifier (>= 1.3.0)
diff --git a/README.md b/README.md
index 8bd066cb27..ee0ee2019d 100644
--- a/README.md
+++ b/README.md
@@ -40,6 +40,8 @@ to generate and send emails; Active Job ([README](activejob/README.md)), a
framework for declaring jobs and making them run on a variety of queueing
backends; Action Cable ([README](actioncable/README.md)), a framework to
integrate WebSockets with a Rails application;
+Active Storage ([README](activestorage/README.md)), a library to attach cloud
+and local files to Rails applications;
and Active Support ([README](activesupport/README.rdoc)), a collection
of utility classes and standard library extensions that are useful for Rails,
and may also be used independently outside Rails.
diff --git a/Rakefile b/Rakefile
index 1421851900..a67f8fd028 100644
--- a/Rakefile
+++ b/Rakefile
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "net/http"
$:.unshift __dir__
diff --git a/actioncable/CHANGELOG.md b/actioncable/CHANGELOG.md
index fbe9863af7..560ee89846 100644
--- a/actioncable/CHANGELOG.md
+++ b/actioncable/CHANGELOG.md
@@ -1,4 +1,4 @@
-* Hash long stream identifiers when using Postgres adapter.
+* Hash long stream identifiers when using PostgreSQL adapter.
PostgreSQL has a limit on identifiers length (63 chars, [docs](https://www.postgresql.org/docs/current/static/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS)).
Provided fix minifies identifiers longer than 63 chars by hashing them with SHA1.
@@ -7,7 +7,7 @@
*Vladimir Dementyev*
-* ActionCable's `redis` adapter allows for other common redis-rb options (`host`, `port`, `db`, `password`) in cable.yml.
+* Action Cable's `redis` adapter allows for other common redis-rb options (`host`, `port`, `db`, `password`) in cable.yml.
Previously, it accepts only a [redis:// url](https://www.iana.org/assignments/uri-schemes/prov/redis) as an option.
While we can add all of these options to the `url` itself, it is not explicitly documented. This alternative setup
@@ -16,7 +16,7 @@
*Marc Rendl Ignacio*
-* ActionCable socket errors are now logged to the console
+* Action Cable socket errors are now logged to the console
Previously any socket errors were ignored and this made it hard to diagnose socket issues (e.g. as discussed in #28362).
diff --git a/actioncable/README.md b/actioncable/README.md
index 6946dbefb0..9667403673 100644
--- a/actioncable/README.md
+++ b/actioncable/README.md
@@ -53,7 +53,7 @@ module ApplicationCable
private
def find_verified_user
- if verified_user = User.find_by(id: cookies.signed[:user_id])
+ if verified_user = User.find_by(id: cookies.encrypted[:user_id])
verified_user
else
reject_unauthorized_connection
diff --git a/actioncable/lib/action_cable/connection/base.rb b/actioncable/lib/action_cable/connection/base.rb
index 8dbafe5105..84053db9fd 100644
--- a/actioncable/lib/action_cable/connection/base.rb
+++ b/actioncable/lib/action_cable/connection/base.rb
@@ -26,7 +26,7 @@ module ActionCable
#
# private
# def find_verified_user
- # User.find_by_identity(cookies.signed[:identity_id]) ||
+ # User.find_by_identity(cookies.encrypted[:identity_id]) ||
# reject_unauthorized_connection
# end
# end
diff --git a/actioncable/test/channel/stream_test.rb b/actioncable/test/channel/stream_test.rb
index 79c25d936f..eca06fe365 100644
--- a/actioncable/test/channel/stream_test.rb
+++ b/actioncable/test/channel/stream_test.rb
@@ -154,7 +154,7 @@ module ActionCable::StreamTests
subscribe_to connection, identifiers: { id: 1 }
connection.websocket.expects(:transmit)
- @server.broadcast "test_room_1", { foo: "bar" }, coder: DummyEncoder
+ @server.broadcast "test_room_1", { foo: "bar" }, { coder: DummyEncoder }
wait_for_async
wait_for_executor connection.server.worker_pool.executor
end
diff --git a/actioncable/test/connection/multiple_identifiers_test.rb b/actioncable/test/connection/multiple_identifiers_test.rb
index 230433a110..7f90cb3876 100644
--- a/actioncable/test/connection/multiple_identifiers_test.rb
+++ b/actioncable/test/connection/multiple_identifiers_test.rb
@@ -36,8 +36,4 @@ class ActionCable::Connection::MultipleIdentifiersTest < ActionCable::TestCase
@connection.process
@connection.send :handle_open
end
-
- def close_connection
- @connection.send :handle_close
- end
end
diff --git a/actioncable/test/connection/string_identifier_test.rb b/actioncable/test/connection/string_identifier_test.rb
index 6387fb792c..4cb58e7fd0 100644
--- a/actioncable/test/connection/string_identifier_test.rb
+++ b/actioncable/test/connection/string_identifier_test.rb
@@ -38,8 +38,4 @@ class ActionCable::Connection::StringIdentifierTest < ActionCable::TestCase
@connection.process
@connection.send :on_open
end
-
- def close_connection
- @connection.send :on_close
- end
end
diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb
index 6add4ec89c..a54eb52dcb 100644
--- a/actionmailer/lib/action_mailer/base.rb
+++ b/actionmailer/lib/action_mailer/base.rb
@@ -590,10 +590,6 @@ module ActionMailer
attr_internal :message
- # Instantiate a new mailer object. If +method_name+ is not +nil+, the mailer
- # will be initialized according to the named method. If not, the mailer will
- # remain uninitialized (useful when you only need to invoke the "receive"
- # method, for instance).
def initialize
super()
@_mail_was_called = false
diff --git a/actionmailer/lib/action_mailer/railtie.rb b/actionmailer/lib/action_mailer/railtie.rb
index 36c2e5866d..69578471b0 100644
--- a/actionmailer/lib/action_mailer/railtie.rb
+++ b/actionmailer/lib/action_mailer/railtie.rb
@@ -58,6 +58,12 @@ module ActionMailer
end
end
+ initializer "action_mailer.eager_load_actions" do
+ ActiveSupport.on_load(:after_initialize) do
+ ActionMailer::Base.descendants.each(&:action_methods) if config.eager_load
+ end
+ end
+
config.after_initialize do |app|
options = app.config.action_mailer
diff --git a/actionmailer/test/message_delivery_test.rb b/actionmailer/test/message_delivery_test.rb
index 89a3c7475e..03e8d4fb66 100644
--- a/actionmailer/test/message_delivery_test.rb
+++ b/actionmailer/test/message_delivery_test.rb
@@ -14,7 +14,6 @@ class MessageDeliveryTest < ActiveSupport::TestCase
ActionMailer::Base.deliver_later_queue_name = :test_queue
ActionMailer::Base.delivery_method = :test
ActiveJob::Base.logger = Logger.new(nil)
- ActionMailer::Base.deliveries.clear
ActiveJob::Base.queue_adapter.perform_enqueued_at_jobs = true
ActiveJob::Base.queue_adapter.perform_enqueued_jobs = true
@@ -25,6 +24,8 @@ class MessageDeliveryTest < ActiveSupport::TestCase
end
teardown do
+ ActionMailer::Base.deliveries.clear
+
ActiveJob::Base.logger = @previous_logger
ActionMailer::Base.delivery_method = @previous_delivery_method
ActionMailer::Base.deliver_later_queue_name = @previous_deliver_later_queue_name
@@ -60,8 +61,6 @@ class MessageDeliveryTest < ActiveSupport::TestCase
def test_should_enqueue_and_run_correctly_in_activejob
@mail.deliver_later!
assert_equal 1, ActionMailer::Base.deliveries.size
- ensure
- ActionMailer::Base.deliveries.clear
end
test "should enqueue the email with :deliver_now delivery method" do
diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md
index 291e019530..938b24a6b9 100644
--- a/actionpack/CHANGELOG.md
+++ b/actionpack/CHANGELOG.md
@@ -1,3 +1,11 @@
+* Deprecate `ActionDispatch::TestResponse` response aliases
+
+ `#success?`, `#missing?` & `#error?` are not supported by the actual
+ `ActionDispatch::Response` object and can produce false-positives. Instead,
+ use the response helpers provided by `Rack::Response`.
+
+ *Trevor Wistaff*
+
* Protect from forgery by default
Rather than protecting from forgery in the generated `ApplicationController`,
@@ -14,11 +22,11 @@
* `driven_by` now registers poltergeist and capybara-webkit
- If driver poltergeist or capybara-webkit is set for System Tests,
+ If poltergeist or capybara-webkit are set as drivers is set for System Tests,
`driven_by` will register the driver and set additional options passed via
- `:options` param.
+ the `:options` parameter.
- Refer to drivers documentation to learn what options can be passed.
+ Refer to the respective driver's documentation to see what options can be passed.
*Mario Chavez*
diff --git a/actionpack/lib/action_controller/railtie.rb b/actionpack/lib/action_controller/railtie.rb
index 7dea22931c..769be39004 100644
--- a/actionpack/lib/action_controller/railtie.rb
+++ b/actionpack/lib/action_controller/railtie.rb
@@ -24,7 +24,7 @@ module ActionController
initializer "action_controller.parameters_config" do |app|
options = app.config.action_controller
- ActiveSupport.on_load(:action_controller) do
+ ActiveSupport.on_load(:action_controller, run_once: true) do
ActionController::Parameters.permit_all_parameters = options.delete(:permit_all_parameters) { false }
if app.config.action_controller[:always_permitted_parameters]
ActionController::Parameters.always_permitted_parameters =
@@ -79,5 +79,11 @@ module ActionController
end
end
end
+
+ initializer "action_controller.eager_load_actions" do
+ ActiveSupport.on_load(:after_initialize) do
+ ActionController::Metal.descendants.each(&:action_methods) if config.eager_load
+ end
+ end
end
end
diff --git a/actionpack/lib/action_dispatch/testing/test_response.rb b/actionpack/lib/action_dispatch/testing/test_response.rb
index 0b7e9ca945..b23ea7479c 100644
--- a/actionpack/lib/action_dispatch/testing/test_response.rb
+++ b/actionpack/lib/action_dispatch/testing/test_response.rb
@@ -20,13 +20,31 @@ module ActionDispatch
end
# Was the response successful?
- alias_method :success?, :successful?
+ def success?
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
+ The success? predicate is deprecated and will be removed in Rails 6.0.
+ Please use successful? as provided by Rack::Response::Helpers.
+ MSG
+ successful?
+ end
# Was the URL not found?
- alias_method :missing?, :not_found?
+ def missing?
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
+ The missing? predicate is deprecated and will be removed in Rails 6.0.
+ Please use not_found? as provided by Rack::Response::Helpers.
+ MSG
+ not_found?
+ end
# Was there a server-side error?
- alias_method :error?, :server_error?
+ def error?
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
+ The error? predicate is deprecated and will be removed in Rails 6.0.
+ Please use server_error? as provided by Rack::Response::Helpers.
+ MSG
+ server_error?
+ end
def parsed_body
@parsed_body ||= @response_parser.call(body)
diff --git a/actionpack/test/controller/live_stream_test.rb b/actionpack/test/controller/live_stream_test.rb
index 1ccbee8a0f..8cfb43a6bc 100644
--- a/actionpack/test/controller/live_stream_test.rb
+++ b/actionpack/test/controller/live_stream_test.rb
@@ -30,7 +30,7 @@ module ActionController
def sse_with_retry
sse = SSE.new(response.stream, retry: 1000)
sse.write("{\"name\":\"John\"}")
- sse.write({ name: "Ryan" }, retry: 1500)
+ sse.write({ name: "Ryan" }, { retry: 1500 })
ensure
sse.close
end
@@ -38,7 +38,7 @@ module ActionController
def sse_with_id
sse = SSE.new(response.stream)
sse.write("{\"name\":\"John\"}", id: 1)
- sse.write({ name: "Ryan" }, id: 2)
+ sse.write({ name: "Ryan" }, { id: 2 })
ensure
sse.close
end
diff --git a/actionpack/test/controller/redirect_test.rb b/actionpack/test/controller/redirect_test.rb
index 7546b36bc3..e447b66486 100644
--- a/actionpack/test/controller/redirect_test.rb
+++ b/actionpack/test/controller/redirect_test.rb
@@ -35,7 +35,7 @@ class RedirectController < ActionController::Base
end
def redirect_with_status_hash
- redirect_to({ action: "hello_world" }, status: 301)
+ redirect_to({ action: "hello_world" }, { status: 301 })
end
def redirect_with_protocol
diff --git a/actionpack/test/controller/resources_test.rb b/actionpack/test/controller/resources_test.rb
index 871fba7e73..3d98237003 100644
--- a/actionpack/test/controller/resources_test.rb
+++ b/actionpack/test/controller/resources_test.rb
@@ -943,8 +943,8 @@ class ResourcesTest < ActionController::TestCase
assert_resource_allowed_routes("products", {}, { id: "1" }, [], [:index, :new, :create, :show, :edit, :update, :destroy])
assert_resource_allowed_routes("products", { format: "xml" }, { id: "1" }, [], [:index, :new, :create, :show, :edit, :update, :destroy])
- assert_recognizes({ controller: "products", action: "sale" }, path: "products/sale", method: :get)
- assert_recognizes({ controller: "products", action: "sale", format: "xml" }, path: "products/sale.xml", method: :get)
+ assert_recognizes({ controller: "products", action: "sale" }, { path: "products/sale", method: :get })
+ assert_recognizes({ controller: "products", action: "sale", format: "xml" }, { path: "products/sale.xml", method: :get })
end
end
@@ -959,8 +959,8 @@ class ResourcesTest < ActionController::TestCase
assert_resource_allowed_routes("products", {}, { id: "1" }, [], [:index, :new, :create, :show, :edit, :update, :destroy])
assert_resource_allowed_routes("products", { format: "xml" }, { id: "1" }, [], [:index, :new, :create, :show, :edit, :update, :destroy])
- assert_recognizes({ controller: "products", action: "preview", id: "1" }, path: "products/1/preview", method: :get)
- assert_recognizes({ controller: "products", action: "preview", id: "1", format: "xml" }, path: "products/1/preview.xml", method: :get)
+ assert_recognizes({ controller: "products", action: "preview", id: "1" }, { path: "products/1/preview", method: :get })
+ assert_recognizes({ controller: "products", action: "preview", id: "1", format: "xml" }, { path: "products/1/preview.xml", method: :get })
end
end
@@ -977,8 +977,8 @@ class ResourcesTest < ActionController::TestCase
assert_singleton_resource_allowed_routes("accounts", {}, [], [:new, :create, :show, :edit, :update, :destroy])
assert_singleton_resource_allowed_routes("accounts", { format: "xml" }, [], [:new, :create, :show, :edit, :update, :destroy])
- assert_recognizes({ controller: "accounts", action: "signup" }, path: "account/signup", method: :get)
- assert_recognizes({ controller: "accounts", action: "signup", format: "xml" }, path: "account/signup.xml", method: :get)
+ assert_recognizes({ controller: "accounts", action: "signup" }, { path: "account/signup", method: :get })
+ assert_recognizes({ controller: "accounts", action: "signup", format: "xml" }, { path: "account/signup.xml", method: :get })
end
end
@@ -995,8 +995,8 @@ class ResourcesTest < ActionController::TestCase
assert_resource_allowed_routes("images", { product_id: "1" }, { id: "2" }, :show, [:index, :new, :create, :edit, :update, :destroy], "products/1/images")
assert_resource_allowed_routes("images", { product_id: "1", format: "xml" }, { id: "2" }, :show, [:index, :new, :create, :edit, :update, :destroy], "products/1/images")
- assert_recognizes({ controller: "images", action: "thumbnail", product_id: "1", id: "2" }, path: "products/1/images/2/thumbnail", method: :get)
- assert_recognizes({ controller: "images", action: "thumbnail", product_id: "1", id: "2", format: "jpg" }, path: "products/1/images/2/thumbnail.jpg", method: :get)
+ assert_recognizes({ controller: "images", action: "thumbnail", product_id: "1", id: "2" }, { path: "products/1/images/2/thumbnail", method: :get })
+ assert_recognizes({ controller: "images", action: "thumbnail", product_id: "1", id: "2", format: "jpg" }, { path: "products/1/images/2/thumbnail.jpg", method: :get })
end
end
@@ -1069,7 +1069,7 @@ class ResourcesTest < ActionController::TestCase
match "/products", to: "products#show", via: :all
end
- assert_routing({ method: "all", path: "/products" }, controller: "products", action: "show")
+ assert_routing({ method: "all", path: "/products" }, { controller: "products", action: "show" })
end
end
@@ -1080,7 +1080,7 @@ class ResourcesTest < ActionController::TestCase
end
assert_raises(Minitest::Assertion) do
- assert_routing({ method: "all", path: "/products" }, controller: "products", action: "show")
+ assert_routing({ method: "all", path: "/products" }, { controller: "products", action: "show" })
end
end
end
diff --git a/actionpack/test/controller/test_case_test.rb b/actionpack/test/controller/test_case_test.rb
index 92b1c75443..46369ebbb0 100644
--- a/actionpack/test/controller/test_case_test.rb
+++ b/actionpack/test/controller/test_case_test.rb
@@ -392,9 +392,9 @@ XML
def test_assert_generates
assert_generates "controller/action/5", controller: "controller", action: "action", id: "5"
- assert_generates "controller/action/7", { id: "7" }, controller: "controller", action: "action"
- assert_generates "controller/action/5", { controller: "controller", action: "action", id: "5", name: "bob" }, {}, name: "bob"
- assert_generates "controller/action/7", { id: "7", name: "bob" }, { controller: "controller", action: "action" }, name: "bob"
+ assert_generates "controller/action/7", { id: "7" }, { controller: "controller", action: "action" }
+ assert_generates "controller/action/5", { controller: "controller", action: "action", id: "5", name: "bob" }, {}, { name: "bob" }
+ assert_generates "controller/action/7", { id: "7", name: "bob" }, { controller: "controller", action: "action" }, { name: "bob" }
assert_generates "controller/action/7", { id: "7" }, { controller: "controller", action: "action", name: "bob" }, {}
end
@@ -405,7 +405,7 @@ XML
def test_assert_routing_with_method
with_routing do |set|
set.draw { resources(:content) }
- assert_routing({ method: "post", path: "content" }, controller: "content", action: "create")
+ assert_routing({ method: "post", path: "content" }, { controller: "content", action: "create" })
end
end
diff --git a/actionpack/test/dispatch/response_test.rb b/actionpack/test/dispatch/response_test.rb
index 67d6dea86f..a14083c6a2 100644
--- a/actionpack/test/dispatch/response_test.rb
+++ b/actionpack/test/dispatch/response_test.rb
@@ -296,13 +296,8 @@ class ResponseTest < ActiveSupport::TestCase
end
test "read content type with default charset utf-8" do
- original = ActionDispatch::Response.default_charset
- begin
- resp = ActionDispatch::Response.new(200, "Content-Type" => "text/xml")
- assert_equal("utf-8", resp.charset)
- ensure
- ActionDispatch::Response.default_charset = original
- end
+ resp = ActionDispatch::Response.new(200, "Content-Type" => "text/xml")
+ assert_equal("utf-8", resp.charset)
end
test "read content type with charset utf-16" do
diff --git a/actionpack/test/dispatch/routing_assertions_test.rb b/actionpack/test/dispatch/routing_assertions_test.rb
index a196fec7e9..e492a56653 100644
--- a/actionpack/test/dispatch/routing_assertions_test.rb
+++ b/actionpack/test/dispatch/routing_assertions_test.rb
@@ -33,11 +33,11 @@ class RoutingAssertionsTest < ActionController::TestCase
end
def test_assert_generates_with_defaults
- assert_generates("/articles/1/edit", { controller: "articles", action: "edit" }, id: "1")
+ assert_generates("/articles/1/edit", { controller: "articles", action: "edit" }, { id: "1" })
end
def test_assert_generates_with_extras
- assert_generates("/articles", { controller: "articles", action: "index", page: "1" }, {}, page: "1")
+ assert_generates("/articles", { controller: "articles", action: "index", page: "1" }, {}, { page: "1" })
end
def test_assert_recognizes
@@ -50,8 +50,8 @@ class RoutingAssertionsTest < ActionController::TestCase
end
def test_assert_recognizes_with_method
- assert_recognizes({ controller: "articles", action: "create" }, path: "/articles", method: :post)
- assert_recognizes({ controller: "articles", action: "update", id: "1" }, path: "/articles/1", method: :put)
+ assert_recognizes({ controller: "articles", action: "create" }, { path: "/articles", method: :post })
+ assert_recognizes({ controller: "articles", action: "update", id: "1" }, { path: "/articles/1", method: :put })
end
def test_assert_recognizes_with_hash_constraint
@@ -96,11 +96,11 @@ class RoutingAssertionsTest < ActionController::TestCase
end
def test_assert_routing_with_defaults
- assert_routing("/articles/1/edit", { controller: "articles", action: "edit", id: "1" }, id: "1")
+ assert_routing("/articles/1/edit", { controller: "articles", action: "edit", id: "1" }, { id: "1" })
end
def test_assert_routing_with_extras
- assert_routing("/articles", { controller: "articles", action: "index", page: "1" }, {}, page: "1")
+ assert_routing("/articles", { controller: "articles", action: "index", page: "1" }, {}, { page: "1" })
end
def test_assert_routing_with_hash_constraint
diff --git a/actionpack/test/dispatch/test_response_test.rb b/actionpack/test/dispatch/test_response_test.rb
index 2629a61057..f0b8f7785d 100644
--- a/actionpack/test/dispatch/test_response_test.rb
+++ b/actionpack/test/dispatch/test_response_test.rb
@@ -27,4 +27,11 @@ class TestResponseTest < ActiveSupport::TestCase
response = ActionDispatch::TestResponse.create(200, { "Content-Type" => "application/json" }, '{ "foo": "fighters" }')
assert_equal({ "foo" => "fighters" }, response.parsed_body)
end
+
+ test "response status aliases deprecated" do
+ response = ActionDispatch::TestResponse.create
+ assert_deprecated { response.success? }
+ assert_deprecated { response.missing? }
+ assert_deprecated { response.error? }
+ end
end
diff --git a/actionpack/test/journey/router_test.rb b/actionpack/test/journey/router_test.rb
index 27f4f42aab..29cc74471d 100644
--- a/actionpack/test/journey/router_test.rb
+++ b/actionpack/test/journey/router_test.rb
@@ -186,14 +186,14 @@ module ActionDispatch
def test_required_part_in_recall
get "/messages/:a/:b", to: "foo#bar"
- path, _ = @formatter.generate(nil, { controller: "foo", action: "bar", a: "a" }, b: "b")
+ path, _ = @formatter.generate(nil, { controller: "foo", action: "bar", a: "a" }, { b: "b" })
assert_equal "/messages/a/b", path
end
def test_splat_in_recall
get "/*path", to: "foo#bar"
- path, _ = @formatter.generate(nil, { controller: "foo", action: "bar" }, path: "b")
+ path, _ = @formatter.generate(nil, { controller: "foo", action: "bar" }, { path: "b" })
assert_equal "/b", path
end
@@ -201,7 +201,7 @@ module ActionDispatch
get "/messages/:action(/:id(.:format))", to: "foo#bar"
get "/messages/:id(.:format)", to: "bar#baz"
- path, _ = @formatter.generate(nil, { controller: "foo", id: 10 }, action: "index")
+ path, _ = @formatter.generate(nil, { controller: "foo", id: 10 }, { action: "index" })
assert_equal "/messages/index/10", path
end
@@ -314,7 +314,7 @@ module ActionDispatch
path, params = @formatter.generate(
nil,
{ controller: "tasks", id: 10 },
- action: "index")
+ { action: "index" })
assert_equal "/tasks/index/10", path
assert_equal({}, params)
end
@@ -325,7 +325,7 @@ module ActionDispatch
path, params = @formatter.generate(
"tasks",
{ controller: "tasks" },
- controller: "tasks", action: "index")
+ { controller: "tasks", action: "index" })
assert_equal "/tasks", path
assert_equal({}, params)
end
diff --git a/actionview/lib/action_view/helpers/asset_tag_helper.rb b/actionview/lib/action_view/helpers/asset_tag_helper.rb
index 66e31978c6..82b0fcbf2e 100644
--- a/actionview/lib/action_view/helpers/asset_tag_helper.rb
+++ b/actionview/lib/action_view/helpers/asset_tag_helper.rb
@@ -200,7 +200,7 @@ module ActionView
end
# Returns an HTML image tag for the +source+. The +source+ can be a full
- # path or a file.
+ # path, a file or an Active Storage attachment.
#
# ==== Options
#
@@ -217,6 +217,8 @@ module ActionView
#
# ==== Examples
#
+ # Assets (images that are part of your app):
+ #
# image_tag("icon")
# # => <img alt="Icon" src="/assets/icon" />
# image_tag("icon.png")
@@ -235,12 +237,21 @@ module ActionView
# # => <img src="/assets/icon.png" srcset="/assets/icon_2x.png 2x, /assets/icon_4x.png 4x">
# image_tag("pic.jpg", srcset: [["pic_1024.jpg", "1024w"], ["pic_1980.jpg", "1980w"]], sizes: "100vw")
# # => <img src="/assets/pic.jpg" srcset="/assets/pic_1024.jpg 1024w, /assets/pic_1980.jpg 1980w" sizes="100vw">
+ #
+ # Active Storage (images that are uploaded by the users of your app):
+ #
+ # image_tag(user.avatar)
+ # # => <img src="/rails/active_storage/blobs/.../tiger.jpg" alt="Tiger" />
+ # image_tag(user.avatar.variant(resize: "100x100"))
+ # # => <img src="/rails/active_storage/variants/.../tiger.jpg" alt="Tiger" />
+ # image_tag(user.avatar.variant(resize: "100x100"), size: '100')
+ # # => <img width="100" height="100" src="/rails/active_storage/variants/.../tiger.jpg" alt="Tiger" />
def image_tag(source, options = {})
options = options.symbolize_keys
check_for_image_tag_errors(options)
skip_pipeline = options.delete(:skip_pipeline)
- src = options[:src] = path_to_image(source, skip_pipeline: skip_pipeline)
+ src = options[:src] = resolve_image_source(source, skip_pipeline)
unless src.start_with?("cid:") || src.start_with?("data:") || src.blank?
options[:alt] = options.fetch(:alt) { image_alt(src) }
@@ -364,6 +375,16 @@ module ActionView
end
end
+ def resolve_image_source(source, skip_pipeline)
+ if source.is_a?(Symbol) || source.is_a?(String)
+ path_to_image(source, skip_pipeline: skip_pipeline)
+ else
+ polymorphic_url(source)
+ end
+ rescue NoMethodError => e
+ raise ArgumentError, "Can't resolve image into URL: #{e}"
+ end
+
def extract_dimensions(size)
size = size.to_s
if /\A\d+x\d+\z/.match?(size)
diff --git a/actionview/lib/action_view/helpers/form_helper.rb b/actionview/lib/action_view/helpers/form_helper.rb
index 4eac086a87..e5a2f7e520 100644
--- a/actionview/lib/action_view/helpers/form_helper.rb
+++ b/actionview/lib/action_view/helpers/form_helper.rb
@@ -1193,7 +1193,7 @@ module ActionView
# file_field(:attachment, :file, class: 'file_input')
# # => <input type="file" id="attachment_file" name="attachment[file]" class="file_input" />
def file_field(object_name, method, options = {})
- Tags::FileField.new(object_name, method, self, options).render
+ Tags::FileField.new(object_name, method, self, convert_direct_upload_option_to_url(options.dup)).render
end
# Returns a textarea opening and closing tag set tailored for accessing a specified attribute (identified by +method+)
@@ -1569,7 +1569,7 @@ module ActionView
# In the above block, a +FormBuilder+ object is yielded as the
# +person_form+ variable. This allows you to generate the +text_field+
# and +check_box+ fields by specifying their eponymous methods, which
- # modify the underlying template and associates the +@person+ model object
+ # modify the underlying template and associates the <tt>@person</tt> model object
# with the form.
#
# The +FormBuilder+ object can be thought of as serving as a proxy for the
@@ -2165,11 +2165,11 @@ module ActionView
# <%= f.submit %>
# <% end %>
#
- # In the example above, if @post is a new record, it will use "Create Post" as
- # submit button label, otherwise, it uses "Update Post".
+ # In the example above, if <tt>@post</tt> is a new record, it will use "Create Post" as
+ # submit button label; otherwise, it uses "Update Post".
#
- # Those labels can be customized using I18n, under the helpers.submit key and accept
- # the %{model} as translation interpolation:
+ # Those labels can be customized using I18n under the +helpers.submit+ key and using
+ # <tt>%{model}</tt> for translation interpolation:
#
# en:
# helpers:
@@ -2177,7 +2177,7 @@ module ActionView
# create: "Create a %{model}"
# update: "Confirm changes to %{model}"
#
- # It also searches for a key specific for the given object:
+ # It also searches for a key specific to the given object:
#
# en:
# helpers:
@@ -2198,11 +2198,11 @@ module ActionView
# <%= f.button %>
# <% end %>
#
- # In the example above, if @post is a new record, it will use "Create Post" as
- # button label, otherwise, it uses "Update Post".
+ # In the example above, if <tt>@post</tt> is a new record, it will use "Create Post" as
+ # button label; otherwise, it uses "Update Post".
#
- # Those labels can be customized using I18n, under the helpers.submit key
- # (the same as submit helper) and accept the %{model} as translation interpolation:
+ # Those labels can be customized using I18n under the +helpers.submit+ key
+ # (the same as submit helper) and using <tt>%{model}</tt> for translation interpolation:
#
# en:
# helpers:
@@ -2210,7 +2210,7 @@ module ActionView
# create: "Create a %{model}"
# update: "Confirm changes to %{model}"
#
- # It also searches for a key specific for the given object:
+ # It also searches for a key specific to the given object:
#
# en:
# helpers:
diff --git a/actionview/lib/action_view/helpers/form_tag_helper.rb b/actionview/lib/action_view/helpers/form_tag_helper.rb
index 91046acbf8..2519ff2837 100644
--- a/actionview/lib/action_view/helpers/form_tag_helper.rb
+++ b/actionview/lib/action_view/helpers/form_tag_helper.rb
@@ -274,7 +274,7 @@ module ActionView
# file_field_tag 'file', accept: 'text/html', class: 'upload', value: 'index.html'
# # => <input accept="text/html" class="upload" id="file" name="file" type="file" value="index.html" />
def file_field_tag(name, options = {})
- text_field_tag(name, nil, options.merge(type: :file))
+ text_field_tag(name, nil, convert_direct_upload_option_to_url(options.merge(type: :file)))
end
# Creates a password field, a masked text field that will hide the users input behind a mask character.
@@ -904,6 +904,13 @@ module ActionView
tag_options.delete("data-disable-with")
end
+
+ def convert_direct_upload_option_to_url(options)
+ if options.delete(:direct_upload) && respond_to?(:rails_direct_uploads_url)
+ options["data-direct-upload-url"] = rails_direct_uploads_url
+ end
+ options
+ end
end
end
end
diff --git a/actionview/lib/action_view/renderer/partial_renderer.rb b/actionview/lib/action_view/renderer/partial_renderer.rb
index f548fc24ed..f2edcb750c 100644
--- a/actionview/lib/action_view/renderer/partial_renderer.rb
+++ b/actionview/lib/action_view/renderer/partial_renderer.rb
@@ -355,7 +355,7 @@ module ActionView
# finds the options and details and extracts them. The method also contains
# logic that handles the type of object passed in as the partial.
#
- # If +options[:partial]+ is a string, then the +@path+ instance variable is
+ # If +options[:partial]+ is a string, then the <tt>@path</tt> instance variable is
# set to that string. Otherwise, the +options[:partial]+ object must
# respond to +to_partial_path+ in order to setup the path.
def setup(context, options, block)
diff --git a/actionview/lib/action_view/test_case.rb b/actionview/lib/action_view/test_case.rb
index efe8c87b9b..6913c31a20 100644
--- a/actionview/lib/action_view/test_case.rb
+++ b/actionview/lib/action_view/test_case.rb
@@ -281,6 +281,18 @@ module ActionView
super
end
end
+
+ def respond_to_missing?(name, include_private = false)
+ begin
+ routes = @controller.respond_to?(:_routes) && @controller._routes
+ rescue
+ # Dont call routes, if there is an error on _routes call
+ end
+
+ routes &&
+ (routes.named_routes.route_defined?(name) ||
+ routes.mounted_helpers.method_defined?(name))
+ end
end
include Behavior
diff --git a/actionview/test/actionpack/abstract/abstract_controller_test.rb b/actionview/test/actionpack/abstract/abstract_controller_test.rb
index 2bf2ddcc70..468a6376c7 100644
--- a/actionview/test/actionpack/abstract/abstract_controller_test.rb
+++ b/actionview/test/actionpack/abstract/abstract_controller_test.rb
@@ -191,10 +191,10 @@ module AbstractController
private
def self.layout(formats)
- find_template(name.underscore, { formats: formats }, _prefixes: ["layouts"])
+ find_template(name.underscore, { formats: formats }, { _prefixes: ["layouts"] })
rescue ActionView::MissingTemplate
begin
- find_template("application", { formats: formats }, _prefixes: ["layouts"])
+ find_template("application", { formats: formats }, { _prefixes: ["layouts"] })
rescue ActionView::MissingTemplate
end
end
diff --git a/actionview/test/template/asset_tag_helper_test.rb b/actionview/test/template/asset_tag_helper_test.rb
index 4d312d3b6d..5b8e50cc5e 100644
--- a/actionview/test/template/asset_tag_helper_test.rb
+++ b/actionview/test/template/asset_tag_helper_test.rb
@@ -565,9 +565,6 @@ class AssetTagHelperTest < ActionView::TestCase
def blank?; true; end
def to_s; "no-image-yet.png"; end
end
- def test_image_tag_with_blank_placeholder
- assert_equal '<img alt="" src="/images/no-image-yet.png" />', image_tag(PlaceholderImage.new, alt: "")
- end
def test_image_path_with_blank_placeholder
assert_equal "/images/no-image-yet.png", image_path(PlaceholderImage.new)
end
diff --git a/actionview/test/template/date_helper_test.rb b/actionview/test/template/date_helper_test.rb
index 35a08ea672..5a5550438b 100644
--- a/actionview/test/template/date_helper_test.rb
+++ b/actionview/test/template/date_helper_test.rb
@@ -255,8 +255,8 @@ class DateHelperTest < ActionView::TestCase
expected << %(<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16" selected="selected">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n)
expected << "</select>\n"
- assert_dom_equal expected, select_day(Time.mktime(2003, 8, 16), {}, class: "selector")
- assert_dom_equal expected, select_day(16, {}, class: "selector")
+ assert_dom_equal expected, select_day(Time.mktime(2003, 8, 16), {}, { class: "selector" })
+ assert_dom_equal expected, select_day(16, {}, { class: "selector" })
end
def test_select_day_with_default_prompt
@@ -425,7 +425,7 @@ class DateHelperTest < ActionView::TestCase
expected << %(<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6">June</option>\n<option value="7">July</option>\n<option value="8" selected="selected">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n)
expected << "</select>\n"
- assert_dom_equal expected, select_month(Time.mktime(2003, 8, 16), {}, class: "selector", accesskey: "M")
+ assert_dom_equal expected, select_month(Time.mktime(2003, 8, 16), {}, { class: "selector", accesskey: "M" })
end
def test_select_month_with_default_prompt
@@ -520,7 +520,7 @@ class DateHelperTest < ActionView::TestCase
expected << %(<option value="2003" selected="selected">2003</option>\n<option value="2004">2004</option>\n<option value="2005">2005</option>\n)
expected << "</select>\n"
- assert_dom_equal expected, select_year(Time.mktime(2003, 8, 16), { start_year: 2003, end_year: 2005 }, class: "selector", accesskey: "M")
+ assert_dom_equal expected, select_year(Time.mktime(2003, 8, 16), { start_year: 2003, end_year: 2005 }, { class: "selector", accesskey: "M" })
end
def test_select_year_with_default_prompt
@@ -615,7 +615,7 @@ class DateHelperTest < ActionView::TestCase
expected << %(<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08" selected="selected">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n)
expected << "</select>\n"
- assert_dom_equal expected, select_hour(Time.mktime(2003, 8, 16, 8, 4, 18), {}, class: "selector", accesskey: "M")
+ assert_dom_equal expected, select_hour(Time.mktime(2003, 8, 16, 8, 4, 18), {}, { class: "selector", accesskey: "M" })
end
def test_select_hour_with_default_prompt
@@ -719,7 +719,7 @@ class DateHelperTest < ActionView::TestCase
expected << %(<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04" selected="selected">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n<option value="32">32</option>\n<option value="33">33</option>\n<option value="34">34</option>\n<option value="35">35</option>\n<option value="36">36</option>\n<option value="37">37</option>\n<option value="38">38</option>\n<option value="39">39</option>\n<option value="40">40</option>\n<option value="41">41</option>\n<option value="42">42</option>\n<option value="43">43</option>\n<option value="44">44</option>\n<option value="45">45</option>\n<option value="46">46</option>\n<option value="47">47</option>\n<option value="48">48</option>\n<option value="49">49</option>\n<option value="50">50</option>\n<option value="51">51</option>\n<option value="52">52</option>\n<option value="53">53</option>\n<option value="54">54</option>\n<option value="55">55</option>\n<option value="56">56</option>\n<option value="57">57</option>\n<option value="58">58</option>\n<option value="59">59</option>\n)
expected << "</select>\n"
- assert_dom_equal expected, select_minute(Time.mktime(2003, 8, 16, 8, 4, 18), {}, class: "selector", accesskey: "M")
+ assert_dom_equal expected, select_minute(Time.mktime(2003, 8, 16, 8, 4, 18), {}, { class: "selector", accesskey: "M" })
end
def test_select_minute_with_default_prompt
@@ -799,7 +799,7 @@ class DateHelperTest < ActionView::TestCase
expected << %(<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18" selected="selected">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n<option value="32">32</option>\n<option value="33">33</option>\n<option value="34">34</option>\n<option value="35">35</option>\n<option value="36">36</option>\n<option value="37">37</option>\n<option value="38">38</option>\n<option value="39">39</option>\n<option value="40">40</option>\n<option value="41">41</option>\n<option value="42">42</option>\n<option value="43">43</option>\n<option value="44">44</option>\n<option value="45">45</option>\n<option value="46">46</option>\n<option value="47">47</option>\n<option value="48">48</option>\n<option value="49">49</option>\n<option value="50">50</option>\n<option value="51">51</option>\n<option value="52">52</option>\n<option value="53">53</option>\n<option value="54">54</option>\n<option value="55">55</option>\n<option value="56">56</option>\n<option value="57">57</option>\n<option value="58">58</option>\n<option value="59">59</option>\n)
expected << "</select>\n"
- assert_dom_equal expected, select_second(Time.mktime(2003, 8, 16, 8, 4, 18), {}, class: "selector", accesskey: "M")
+ assert_dom_equal expected, select_second(Time.mktime(2003, 8, 16, 8, 4, 18), {}, { class: "selector", accesskey: "M" })
end
def test_select_second_with_default_prompt
@@ -1066,7 +1066,7 @@ class DateHelperTest < ActionView::TestCase
expected << %(<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16" selected="selected">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n)
expected << "</select>\n"
- assert_dom_equal expected, select_date(Time.mktime(2003, 8, 16), { start_year: 2003, end_year: 2005, prefix: "date[first]" }, class: "selector")
+ assert_dom_equal expected, select_date(Time.mktime(2003, 8, 16), { start_year: 2003, end_year: 2005, prefix: "date[first]" }, { class: "selector" })
end
def test_select_date_with_separator
@@ -1170,7 +1170,7 @@ class DateHelperTest < ActionView::TestCase
expected << %(<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16" selected="selected">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n)
expected << "</select>\n"
- assert_dom_equal expected, select_date(Time.mktime(2003, 8, 16), { start_year: 2003, end_year: 2005, prefix: "date[first]", with_css_classes: true }, class: "datetime optional")
+ assert_dom_equal expected, select_date(Time.mktime(2003, 8, 16), { start_year: 2003, end_year: 2005, prefix: "date[first]", with_css_classes: true }, { class: "datetime optional" })
end
def test_select_date_with_custom_with_css_classes_and_html_class_option
@@ -1186,7 +1186,7 @@ class DateHelperTest < ActionView::TestCase
expected << %(<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16" selected="selected">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n)
expected << "</select>\n"
- assert_dom_equal expected, select_date(Time.mktime(2003, 8, 16), { start_year: 2003, end_year: 2005, with_css_classes: { year: "my-year", month: "my-month", day: "my-day" } }, class: "date optional")
+ assert_dom_equal expected, select_date(Time.mktime(2003, 8, 16), { start_year: 2003, end_year: 2005, with_css_classes: { year: "my-year", month: "my-month", day: "my-day" } }, { class: "date optional" })
end
def test_select_date_with_partial_with_css_classes_and_html_class_option
@@ -1202,7 +1202,7 @@ class DateHelperTest < ActionView::TestCase
expected << %(<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16" selected="selected">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n)
expected << "</select>\n"
- assert_dom_equal expected, select_date(Time.mktime(2003, 8, 16), { start_year: 2003, end_year: 2005, with_css_classes: { month: "my-month custom-grid" } }, class: "date optional")
+ assert_dom_equal expected, select_date(Time.mktime(2003, 8, 16), { start_year: 2003, end_year: 2005, with_css_classes: { month: "my-month custom-grid" } }, { class: "date optional" })
end
def test_select_date_with_html_class_option
@@ -1218,7 +1218,7 @@ class DateHelperTest < ActionView::TestCase
expected << %(<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16" selected="selected">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n)
expected << "</select>\n"
- assert_dom_equal expected, select_date(Time.mktime(2003, 8, 16), { start_year: 2003, end_year: 2005 }, class: "date optional custom-grid")
+ assert_dom_equal expected, select_date(Time.mktime(2003, 8, 16), { start_year: 2003, end_year: 2005 }, { class: "date optional custom-grid" })
end
def test_select_datetime
@@ -1358,7 +1358,7 @@ class DateHelperTest < ActionView::TestCase
expected << %(<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04" selected="selected">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n<option value="32">32</option>\n<option value="33">33</option>\n<option value="34">34</option>\n<option value="35">35</option>\n<option value="36">36</option>\n<option value="37">37</option>\n<option value="38">38</option>\n<option value="39">39</option>\n<option value="40">40</option>\n<option value="41">41</option>\n<option value="42">42</option>\n<option value="43">43</option>\n<option value="44">44</option>\n<option value="45">45</option>\n<option value="46">46</option>\n<option value="47">47</option>\n<option value="48">48</option>\n<option value="49">49</option>\n<option value="50">50</option>\n<option value="51">51</option>\n<option value="52">52</option>\n<option value="53">53</option>\n<option value="54">54</option>\n<option value="55">55</option>\n<option value="56">56</option>\n<option value="57">57</option>\n<option value="58">58</option>\n<option value="59">59</option>\n)
expected << "</select>\n"
- assert_dom_equal expected, select_datetime(Time.mktime(2003, 8, 16, 8, 4, 18), { start_year: 2003, end_year: 2005, prefix: "date[first]" }, class: "selector")
+ assert_dom_equal expected, select_datetime(Time.mktime(2003, 8, 16, 8, 4, 18), { start_year: 2003, end_year: 2005, prefix: "date[first]" }, { class: "selector" })
end
def test_select_datetime_with_all_separators
@@ -1390,7 +1390,7 @@ class DateHelperTest < ActionView::TestCase
expected << %(<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04" selected="selected">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n<option value="32">32</option>\n<option value="33">33</option>\n<option value="34">34</option>\n<option value="35">35</option>\n<option value="36">36</option>\n<option value="37">37</option>\n<option value="38">38</option>\n<option value="39">39</option>\n<option value="40">40</option>\n<option value="41">41</option>\n<option value="42">42</option>\n<option value="43">43</option>\n<option value="44">44</option>\n<option value="45">45</option>\n<option value="46">46</option>\n<option value="47">47</option>\n<option value="48">48</option>\n<option value="49">49</option>\n<option value="50">50</option>\n<option value="51">51</option>\n<option value="52">52</option>\n<option value="53">53</option>\n<option value="54">54</option>\n<option value="55">55</option>\n<option value="56">56</option>\n<option value="57">57</option>\n<option value="58">58</option>\n<option value="59">59</option>\n)
expected << "</select>\n"
- assert_dom_equal expected, select_datetime(Time.mktime(2003, 8, 16, 8, 4, 18), { datetime_separator: "&mdash;", date_separator: "/", time_separator: ":", start_year: 2003, end_year: 2005, prefix: "date[first]" }, class: "selector")
+ assert_dom_equal expected, select_datetime(Time.mktime(2003, 8, 16, 8, 4, 18), { datetime_separator: "&mdash;", date_separator: "/", time_separator: ":", start_year: 2003, end_year: 2005, prefix: "date[first]" }, { class: "selector" })
end
def test_select_datetime_should_work_with_date
@@ -1670,8 +1670,8 @@ class DateHelperTest < ActionView::TestCase
expected << %(<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04" selected="selected">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n<option value="32">32</option>\n<option value="33">33</option>\n<option value="34">34</option>\n<option value="35">35</option>\n<option value="36">36</option>\n<option value="37">37</option>\n<option value="38">38</option>\n<option value="39">39</option>\n<option value="40">40</option>\n<option value="41">41</option>\n<option value="42">42</option>\n<option value="43">43</option>\n<option value="44">44</option>\n<option value="45">45</option>\n<option value="46">46</option>\n<option value="47">47</option>\n<option value="48">48</option>\n<option value="49">49</option>\n<option value="50">50</option>\n<option value="51">51</option>\n<option value="52">52</option>\n<option value="53">53</option>\n<option value="54">54</option>\n<option value="55">55</option>\n<option value="56">56</option>\n<option value="57">57</option>\n<option value="58">58</option>\n<option value="59">59</option>\n)
expected << "</select>\n"
- assert_dom_equal expected, select_time(Time.mktime(2003, 8, 16, 8, 4, 18), {}, class: "selector")
- assert_dom_equal expected, select_time(Time.mktime(2003, 8, 16, 8, 4, 18), { include_seconds: false }, class: "selector")
+ assert_dom_equal expected, select_time(Time.mktime(2003, 8, 16, 8, 4, 18), {}, { class: "selector" })
+ assert_dom_equal expected, select_time(Time.mktime(2003, 8, 16, 8, 4, 18), { include_seconds: false }, { class: "selector" })
end
def test_select_time_should_work_with_date
@@ -1928,7 +1928,7 @@ class DateHelperTest < ActionView::TestCase
expected << %{<option value="1999">1999</option>\n<option value="2000">2000</option>\n<option value="2001">2001</option>\n<option value="2002">2002</option>\n<option value="2003">2003</option>\n<option value="2004" selected="selected">2004</option>\n<option value="2005">2005</option>\n<option value="2006">2006</option>\n<option value="2007">2007</option>\n<option value="2008">2008</option>\n<option value="2009">2009</option>\n}
expected << "</select>\n"
- assert_dom_equal expected, date_select("post", "written_on", { order: [ :month, :year ] }, disabled: true)
+ assert_dom_equal expected, date_select("post", "written_on", { order: [ :month, :year ] }, { disabled: true })
end
def test_date_select_within_fields_for
@@ -2170,7 +2170,7 @@ class DateHelperTest < ActionView::TestCase
expected << "</select>\n"
- assert_dom_equal expected, date_select("post", "written_on", {}, class: "selector")
+ assert_dom_equal expected, date_select("post", "written_on", {}, { class: "selector" })
end
def test_date_select_with_html_options_within_fields_for
@@ -2178,7 +2178,7 @@ class DateHelperTest < ActionView::TestCase
@post.written_on = Date.new(2004, 6, 15)
output_buffer = fields_for :post, @post do |f|
- concat f.date_select(:written_on, {}, class: "selector")
+ concat f.date_select(:written_on, {}, { class: "selector" })
end
expected = %{<select id="post_written_on_1i" name="post[written_on(1i)]" class="selector">\n}.dup
@@ -2453,7 +2453,7 @@ class DateHelperTest < ActionView::TestCase
0.upto(59) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 16}>#{sprintf("%02d", i)}</option>\n) }
expected << "</select>\n"
- assert_dom_equal expected, time_select("post", "written_on", {}, class: "selector")
+ assert_dom_equal expected, time_select("post", "written_on", {}, { class: "selector" })
end
def test_time_select_with_html_options_within_fields_for
@@ -2461,7 +2461,7 @@ class DateHelperTest < ActionView::TestCase
@post.written_on = Time.local(2004, 6, 15, 15, 16, 35)
output_buffer = fields_for :post, @post do |f|
- concat f.time_select(:written_on, {}, class: "selector")
+ concat f.time_select(:written_on, {}, { class: "selector" })
end
expected = %{<input type="hidden" id="post_written_on_1i" name="post[written_on(1i)]" value="2004" />\n}.dup
@@ -2606,7 +2606,7 @@ class DateHelperTest < ActionView::TestCase
0.upto(59) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 16}>#{sprintf("%02d", i)}</option>\n) }
expected << "</select>\n"
- assert_dom_equal expected, time_select("post", "written_on", {}, disabled: true)
+ assert_dom_equal expected, time_select("post", "written_on", {}, { disabled: true })
end
def test_datetime_select
@@ -2740,7 +2740,7 @@ class DateHelperTest < ActionView::TestCase
@post.updated_at = Time.local(2004, 6, 15, 16, 35)
output_buffer = fields_for :post, @post do |f|
- concat f.datetime_select(:updated_at, {}, class: "selector")
+ concat f.datetime_select(:updated_at, {}, { class: "selector" })
end
expected = %{<select id="post_updated_at_1i" name="post[updated_at(1i)]" class="selector">\n<option value="1999">1999</option>\n<option value="2000">2000</option>\n<option value="2001">2001</option>\n<option value="2002">2002</option>\n<option value="2003">2003</option>\n<option selected="selected" value="2004">2004</option>\n<option value="2005">2005</option>\n<option value="2006">2006</option>\n<option value="2007">2007</option>\n<option value="2008">2008</option>\n<option value="2009">2009</option>\n</select>\n}.dup
@@ -3222,7 +3222,7 @@ class DateHelperTest < ActionView::TestCase
0.upto(59) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 16}>#{sprintf("%02d", i)}</option>\n) }
expected << "</select>\n"
- assert_dom_equal expected, datetime_select("post", "updated_at", { discard_year: true, discard_month: true }, disabled: true)
+ assert_dom_equal expected, datetime_select("post", "updated_at", { discard_year: true, discard_month: true }, { disabled: true })
end
def test_datetime_select_discard_hour
@@ -3442,7 +3442,7 @@ class DateHelperTest < ActionView::TestCase
expected << %{<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n<option value="32">32</option>\n<option value="33">33</option>\n<option value="34">34</option>\n<option value="35" selected="selected">35</option>\n<option value="36">36</option>\n<option value="37">37</option>\n<option value="38">38</option>\n<option value="39">39</option>\n<option value="40">40</option>\n<option value="41">41</option>\n<option value="42">42</option>\n<option value="43">43</option>\n<option value="44">44</option>\n<option value="45">45</option>\n<option value="46">46</option>\n<option value="47">47</option>\n<option value="48">48</option>\n<option value="49">49</option>\n<option value="50">50</option>\n<option value="51">51</option>\n<option value="52">52</option>\n<option value="53">53</option>\n<option value="54">54</option>\n<option value="55">55</option>\n<option value="56">56</option>\n<option value="57">57</option>\n<option value="58">58</option>\n<option value="59">59</option>\n}
expected << "</select>\n"
- assert_dom_equal expected, datetime_select("post", "updated_at", {}, class: "selector")
+ assert_dom_equal expected, datetime_select("post", "updated_at", {}, { class: "selector" })
end
def test_date_select_should_not_change_passed_options_hash
@@ -3602,7 +3602,7 @@ class DateHelperTest < ActionView::TestCase
assert select_minute(8, use_hidden: true).html_safe?
assert select_month(8, prompt: "Choose month").html_safe?
- assert select_time(Time.mktime(2003, 8, 16, 8, 4, 18), {}, class: "selector").html_safe?
+ assert select_time(Time.mktime(2003, 8, 16, 8, 4, 18), {}, { class: "selector" }).html_safe?
assert select_date(Time.mktime(2003, 8, 16), date_separator: " / ", start_year: 2003, end_year: 2005, prefix: "date[first]").html_safe?
end
diff --git a/actionview/test/template/form_collections_helper_test.rb b/actionview/test/template/form_collections_helper_test.rb
index bba529a98a..6db55a1447 100644
--- a/actionview/test/template/form_collections_helper_test.rb
+++ b/actionview/test/template/form_collections_helper_test.rb
@@ -95,7 +95,7 @@ class FormCollectionsHelperTest < ActionView::TestCase
test "collection radio accepts html options as input" do
collection = [[1, true], [0, false]]
- with_collection_radio_buttons :user, :active, collection, :last, :first, {}, class: "special-radio"
+ with_collection_radio_buttons :user, :active, collection, :last, :first, {}, { class: "special-radio" }
assert_select "input[type=radio][value=true].special-radio#user_active_true"
assert_select "input[type=radio][value=false].special-radio#user_active_false"
@@ -215,7 +215,7 @@ class FormCollectionsHelperTest < ActionView::TestCase
test "collection radio buttons generates a hidden field using the given :name in :html_options" do
collection = [Category.new(1, "Category 1"), Category.new(2, "Category 2")]
- with_collection_radio_buttons :user, :category_ids, collection, :id, :name, {}, name: "user[other_category_ids]"
+ with_collection_radio_buttons :user, :category_ids, collection, :id, :name, {}, { name: "user[other_category_ids]" }
assert_select "input[type=hidden][name='user[other_category_ids]'][value='']", count: 1
end
@@ -259,7 +259,7 @@ class FormCollectionsHelperTest < ActionView::TestCase
test "collection check boxes generates a hidden field using the given :name in :html_options" do
collection = [Category.new(1, "Category 1"), Category.new(2, "Category 2")]
- with_collection_check_boxes :user, :category_ids, collection, :id, :name, {}, name: "user[other_category_ids][]"
+ with_collection_check_boxes :user, :category_ids, collection, :id, :name, {}, { name: "user[other_category_ids][]" }
assert_select "input[type=hidden][name='user[other_category_ids][]'][value='']", count: 1
end
@@ -446,7 +446,7 @@ class FormCollectionsHelperTest < ActionView::TestCase
test "collection check boxes accepts html options" do
collection = [[1, "Category 1"], [2, "Category 2"]]
- with_collection_check_boxes :user, :category_ids, collection, :first, :last, {}, class: "check"
+ with_collection_check_boxes :user, :category_ids, collection, :first, :last, {}, { class: "check" }
assert_select 'input.check[type=checkbox][value="1"]'
assert_select 'input.check[type=checkbox][value="2"]'
diff --git a/actionview/test/template/form_helper_test.rb b/actionview/test/template/form_helper_test.rb
index 8d689aebeb..ac64096908 100644
--- a/actionview/test/template/form_helper_test.rb
+++ b/actionview/test/template/form_helper_test.rb
@@ -8,6 +8,16 @@ class FormHelperTest < ActionView::TestCase
tests ActionView::Helpers::FormHelper
+ class WithActiveStorageRoutesControllers < ActionController::Base
+ test_routes do
+ post "/rails/active_storage/direct_uploads" => "active_storage/direct_uploads#create", as: :rails_direct_uploads
+ end
+
+ def url_options
+ { host: "testtwo.host" }
+ end
+ end
+
def form_for(*)
@output_buffer = super
end
@@ -542,6 +552,27 @@ class FormHelperTest < ActionView::TestCase
assert_dom_equal expected, file_field("import", "file", multiple: true, name: "custom")
end
+ def test_file_field_with_direct_upload_when_rails_direct_uploads_url_is_not_defined
+ expected = '<input type="file" name="import[file]" id="import_file" />'
+ assert_dom_equal expected, file_field("import", "file", direct_upload: true)
+ end
+
+ def test_file_field_with_direct_upload_when_rails_direct_uploads_url_is_defined
+ @controller = WithActiveStorageRoutesControllers.new
+
+ expected = '<input data-direct-upload-url="http://testtwo.host/rails/active_storage/direct_uploads" type="file" name="import[file]" id="import_file" />'
+ assert_dom_equal expected, file_field("import", "file", direct_upload: true)
+ end
+
+ def test_file_field_with_direct_upload_dont_mutate_arguments
+ original_options = { class: "pix", direct_upload: true }
+
+ expected = '<input class="pix" type="file" name="import[file]" id="import_file" />'
+ assert_dom_equal expected, file_field("import", "file", original_options)
+
+ assert_equal({ class: "pix", direct_upload: true }, original_options)
+ end
+
def test_hidden_field
assert_dom_equal(
'<input id="post_title" name="post[title]" type="hidden" value="Hello World" />',
@@ -1355,7 +1386,7 @@ class FormHelperTest < ActionView::TestCase
)
assert_dom_equal(
'<select name="post[secret]"></select>',
- select("post", "secret", [], {}, "id" => nil)
+ select("post", "secret", [], {}, { "id" => nil })
)
assert_dom_equal(
text_field("post", "title", "id" => nil),
diff --git a/actionview/test/template/form_options_helper_test.rb b/actionview/test/template/form_options_helper_test.rb
index 56a8a125e5..a66db2f3dc 100644
--- a/actionview/test/template/form_options_helper_test.rb
+++ b/actionview/test/template/form_options_helper_test.rb
@@ -491,7 +491,7 @@ class FormOptionsHelperTest < ActionView::TestCase
def test_select_without_multiple
assert_dom_equal(
"<select id=\"post_category\" name=\"post[category]\"></select>",
- select(:post, :category, "", {}, multiple: false)
+ select(:post, :category, "", {}, { multiple: false })
)
end
@@ -639,7 +639,7 @@ class FormOptionsHelperTest < ActionView::TestCase
end
def test_select_with_multiple_to_add_hidden_input
- output_buffer = select(:post, :category, "", {}, multiple: true)
+ output_buffer = select(:post, :category, "", {}, { multiple: true })
assert_dom_equal(
"<input type=\"hidden\" name=\"post[category][]\" value=\"\"/><select multiple=\"multiple\" id=\"post_category\" name=\"post[category][]\"></select>",
output_buffer
@@ -647,7 +647,7 @@ class FormOptionsHelperTest < ActionView::TestCase
end
def test_select_with_multiple_and_without_hidden_input
- output_buffer = select(:post, :category, "", { include_hidden: false }, multiple: true)
+ output_buffer = select(:post, :category, "", { include_hidden: false }, { multiple: true })
assert_dom_equal(
"<select multiple=\"multiple\" id=\"post_category\" name=\"post[category][]\"></select>",
output_buffer
@@ -655,7 +655,7 @@ class FormOptionsHelperTest < ActionView::TestCase
end
def test_select_with_multiple_and_with_explicit_name_ending_with_brackets
- output_buffer = select(:post, :category, [], { include_hidden: false }, multiple: true, name: "post[category][]")
+ output_buffer = select(:post, :category, [], { include_hidden: false }, { multiple: true, name: "post[category][]" })
assert_dom_equal(
"<select multiple=\"multiple\" id=\"post_category\" name=\"post[category][]\"></select>",
output_buffer
@@ -663,7 +663,7 @@ class FormOptionsHelperTest < ActionView::TestCase
end
def test_select_with_multiple_and_disabled_to_add_disabled_hidden_input
- output_buffer = select(:post, :category, "", {}, multiple: true, disabled: true)
+ output_buffer = select(:post, :category, "", {}, { multiple: true, disabled: true })
assert_dom_equal(
"<input disabled=\"disabled\"type=\"hidden\" name=\"post[category][]\" value=\"\"/><select multiple=\"multiple\" disabled=\"disabled\" id=\"post_category\" name=\"post[category][]\"></select>",
output_buffer
@@ -682,7 +682,7 @@ class FormOptionsHelperTest < ActionView::TestCase
def test_select_with_include_blank_false_and_required
@post = Post.new
@post.category = "<mus>"
- e = assert_raises(ArgumentError) { select("post", "category", %w( abe <mus> hest), { include_blank: false }, required: "required") }
+ e = assert_raises(ArgumentError) { select("post", "category", %w( abe <mus> hest), { include_blank: false }, { required: "required" }) }
assert_match(/include_blank cannot be false for a required field./, e.message)
end
@@ -762,7 +762,7 @@ class FormOptionsHelperTest < ActionView::TestCase
@post.category = ""
assert_dom_equal(
"<select class=\"disabled\" disabled=\"disabled\" name=\"post[category]\" id=\"post_category\"><option value=\"\">Please select</option>\n<option value=\"\"></option>\n</select>",
- select("post", "category", [], { prompt: true, include_blank: true }, class: "disabled", disabled: true)
+ select("post", "category", [], { prompt: true, include_blank: true }, { class: "disabled", disabled: true })
)
end
@@ -778,42 +778,42 @@ class FormOptionsHelperTest < ActionView::TestCase
def test_required_select
assert_dom_equal(
%(<select id="post_category" name="post[category]" required="required"><option value=""></option>\n<option value="abe">abe</option>\n<option value="mus">mus</option>\n<option value="hest">hest</option></select>),
- select("post", "category", %w(abe mus hest), {}, required: true)
+ select("post", "category", %w(abe mus hest), {}, { required: true })
)
end
def test_required_select_with_include_blank_prompt
assert_dom_equal(
%(<select id="post_category" name="post[category]" required="required"><option value="">Select one</option>\n<option value="abe">abe</option>\n<option value="mus">mus</option>\n<option value="hest">hest</option></select>),
- select("post", "category", %w(abe mus hest), { include_blank: "Select one" }, required: true)
+ select("post", "category", %w(abe mus hest), { include_blank: "Select one" }, { required: true })
)
end
def test_required_select_with_prompt
assert_dom_equal(
%(<select id="post_category" name="post[category]" required="required"><option value="">Select one</option>\n<option value="abe">abe</option>\n<option value="mus">mus</option>\n<option value="hest">hest</option></select>),
- select("post", "category", %w(abe mus hest), { prompt: "Select one" }, required: true)
+ select("post", "category", %w(abe mus hest), { prompt: "Select one" }, { required: true })
)
end
def test_required_select_display_size_equals_to_one
assert_dom_equal(
%(<select id="post_category" name="post[category]" required="required" size="1"><option value=""></option>\n<option value="abe">abe</option>\n<option value="mus">mus</option>\n<option value="hest">hest</option></select>),
- select("post", "category", %w(abe mus hest), {}, required: true, size: 1)
+ select("post", "category", %w(abe mus hest), {}, { required: true, size: 1 })
)
end
def test_required_select_with_display_size_bigger_than_one
assert_dom_equal(
%(<select id="post_category" name="post[category]" required="required" size="2"><option value="abe">abe</option>\n<option value="mus">mus</option>\n<option value="hest">hest</option></select>),
- select("post", "category", %w(abe mus hest), {}, required: true, size: 2)
+ select("post", "category", %w(abe mus hest), {}, { required: true, size: 2 })
)
end
def test_required_select_with_multiple_option
assert_dom_equal(
%(<input name="post[category][]" type="hidden" value=""/><select id="post_category" multiple="multiple" name="post[category][]" required="required"><option value="abe">abe</option>\n<option value="mus">mus</option>\n<option value="hest">hest</option></select>),
- select("post", "category", %w(abe mus hest), {}, required: true, multiple: true)
+ select("post", "category", %w(abe mus hest), {}, { required: true, multiple: true })
)
end
@@ -852,7 +852,7 @@ class FormOptionsHelperTest < ActionView::TestCase
assert_dom_equal(
expected,
- select("album[]", "genre", %w[rap rock country], {}, index: nil)
+ select("album[]", "genre", %w[rap rock country], {}, { index: nil })
)
end
@@ -982,7 +982,7 @@ class FormOptionsHelperTest < ActionView::TestCase
assert_dom_equal(
"<select id=\"post_author_name\" name=\"post[author_name]\" style=\"width: 200px\"><option value=\"\"></option>\n<option value=\"&lt;Abe&gt;\">&lt;Abe&gt;</option>\n<option value=\"Babe\" selected=\"selected\">Babe</option>\n<option value=\"Cabe\">Cabe</option></select>",
- collection_select("post", "author_name", dummy_posts, "author_name", "author_name", { include_blank: true }, "style" => "width: 200px")
+ collection_select("post", "author_name", dummy_posts, "author_name", "author_name", { include_blank: true }, { "style" => "width: 200px" })
)
end
@@ -992,7 +992,7 @@ class FormOptionsHelperTest < ActionView::TestCase
assert_dom_equal(
"<select id=\"post_author_name\" name=\"post[author_name]\" style=\"width: 200px\"><option value=\"\">No Selection</option>\n<option value=\"&lt;Abe&gt;\">&lt;Abe&gt;</option>\n<option value=\"Babe\" selected=\"selected\">Babe</option>\n<option value=\"Cabe\">Cabe</option></select>",
- collection_select("post", "author_name", dummy_posts, "author_name", "author_name", { include_blank: "No Selection" }, "style" => "width: 200px")
+ collection_select("post", "author_name", dummy_posts, "author_name", "author_name", { include_blank: "No Selection" }, { "style" => "width: 200px" })
)
end
@@ -1003,10 +1003,10 @@ class FormOptionsHelperTest < ActionView::TestCase
expected = "<input type=\"hidden\" name=\"post[author_name][]\" value=\"\"/><select id=\"post_author_name\" name=\"post[author_name][]\" multiple=\"multiple\"><option value=\"\"></option>\n<option value=\"&lt;Abe&gt;\">&lt;Abe&gt;</option>\n<option value=\"Babe\" selected=\"selected\">Babe</option>\n<option value=\"Cabe\">Cabe</option></select>"
# Should suffix default name with [].
- assert_dom_equal expected, collection_select("post", "author_name", dummy_posts, "author_name", "author_name", { include_blank: true }, multiple: true)
+ assert_dom_equal expected, collection_select("post", "author_name", dummy_posts, "author_name", "author_name", { include_blank: true }, { multiple: true })
# Shouldn't suffix custom name with [].
- assert_dom_equal expected, collection_select("post", "author_name", dummy_posts, "author_name", "author_name", { include_blank: true, name: "post[author_name][]" }, multiple: true)
+ assert_dom_equal expected, collection_select("post", "author_name", dummy_posts, "author_name", "author_name", { include_blank: true, name: "post[author_name][]" }, { multiple: true })
end
def test_collection_select_with_blank_and_selected
@@ -1149,7 +1149,7 @@ class FormOptionsHelperTest < ActionView::TestCase
def test_time_zone_select_with_style
@firm = Firm.new("D")
html = time_zone_select("firm", "time_zone", nil, {},
- "style" => "color: red")
+ { "style" => "color: red" })
assert_dom_equal "<select id=\"firm_time_zone\" name=\"firm[time_zone]\" style=\"color: red\">" \
"<option value=\"A\">A</option>\n" \
"<option value=\"B\">B</option>\n" \
@@ -1159,13 +1159,13 @@ class FormOptionsHelperTest < ActionView::TestCase
"</select>",
html
assert_dom_equal html, time_zone_select("firm", "time_zone", nil, {},
- style: "color: red")
+ { style: "color: red" })
end
def test_time_zone_select_with_blank_and_style
@firm = Firm.new("D")
html = time_zone_select("firm", "time_zone", nil,
- { include_blank: true }, "style" => "color: red")
+ { include_blank: true }, { "style" => "color: red" })
assert_dom_equal "<select id=\"firm_time_zone\" name=\"firm[time_zone]\" style=\"color: red\">" \
"<option value=\"\"></option>\n" \
"<option value=\"A\">A</option>\n" \
@@ -1176,13 +1176,13 @@ class FormOptionsHelperTest < ActionView::TestCase
"</select>",
html
assert_dom_equal html, time_zone_select("firm", "time_zone", nil,
- { include_blank: true }, style: "color: red")
+ { include_blank: true }, { style: "color: red" })
end
def test_time_zone_select_with_blank_as_string_and_style
@firm = Firm.new("D")
html = time_zone_select("firm", "time_zone", nil,
- { include_blank: "No Zone" }, "style" => "color: red")
+ { include_blank: "No Zone" }, { "style" => "color: red" })
assert_dom_equal "<select id=\"firm_time_zone\" name=\"firm[time_zone]\" style=\"color: red\">" \
"<option value=\"\">No Zone</option>\n" \
"<option value=\"A\">A</option>\n" \
@@ -1193,7 +1193,7 @@ class FormOptionsHelperTest < ActionView::TestCase
"</select>",
html
assert_dom_equal html, time_zone_select("firm", "time_zone", nil,
- { include_blank: "No Zone" }, style: "color: red")
+ { include_blank: "No Zone" }, { style: "color: red" })
end
def test_time_zone_select_with_priority_zones
diff --git a/actionview/test/template/form_tag_helper_test.rb b/actionview/test/template/form_tag_helper_test.rb
index 62b67b0435..48c567008d 100644
--- a/actionview/test/template/form_tag_helper_test.rb
+++ b/actionview/test/template/form_tag_helper_test.rb
@@ -7,6 +7,16 @@ class FormTagHelperTest < ActionView::TestCase
tests ActionView::Helpers::FormTagHelper
+ class WithActiveStorageRoutesControllers < ActionController::Base
+ test_routes do
+ post "/rails/active_storage/direct_uploads" => "active_storage/direct_uploads#create", as: :rails_direct_uploads
+ end
+
+ def url_options
+ { host: "testtwo.host" }
+ end
+ end
+
def setup
super
@controller = BasicController.new
@@ -90,53 +100,53 @@ class FormTagHelperTest < ActionView::TestCase
end
def test_form_tag_multipart
- actual = form_tag({}, "multipart" => true)
+ actual = form_tag({}, { "multipart" => true })
expected = whole_form("http://www.example.com", enctype: true)
assert_dom_equal expected, actual
end
def test_form_tag_with_method_patch
- actual = form_tag({}, method: :patch)
+ actual = form_tag({}, { method: :patch })
expected = whole_form("http://www.example.com", method: :patch)
assert_dom_equal expected, actual
end
def test_form_tag_with_method_put
- actual = form_tag({}, method: :put)
+ actual = form_tag({}, { method: :put })
expected = whole_form("http://www.example.com", method: :put)
assert_dom_equal expected, actual
end
def test_form_tag_with_method_delete
- actual = form_tag({}, method: :delete)
+ actual = form_tag({}, { method: :delete })
expected = whole_form("http://www.example.com", method: :delete)
assert_dom_equal expected, actual
end
def test_form_tag_with_remote
- actual = form_tag({}, remote: true)
+ actual = form_tag({}, { remote: true })
expected = whole_form("http://www.example.com", remote: true)
assert_dom_equal expected, actual
end
def test_form_tag_with_remote_false
- actual = form_tag({}, remote: false)
+ actual = form_tag({}, { remote: false })
expected = whole_form
assert_dom_equal expected, actual
end
def test_form_tag_enforce_utf8_true
- actual = form_tag({}, enforce_utf8: true)
+ actual = form_tag({}, { enforce_utf8: true })
expected = whole_form("http://www.example.com", enforce_utf8: true)
assert_dom_equal expected, actual
assert actual.html_safe?
end
def test_form_tag_enforce_utf8_false
- actual = form_tag({}, enforce_utf8: false)
+ actual = form_tag({}, { enforce_utf8: false })
expected = whole_form("http://www.example.com", enforce_utf8: false)
assert_dom_equal expected, actual
assert actual.html_safe?
@@ -178,6 +188,33 @@ class FormTagHelperTest < ActionView::TestCase
assert_dom_equal "<input name=\"picsplz\" type=\"file\" id=\"picsplz\" class=\"pix\"/>", file_field_tag("picsplz", class: "pix")
end
+ def test_file_field_tag_with_direct_upload_when_rails_direct_uploads_url_is_not_defined
+ assert_dom_equal(
+ "<input name=\"picsplz\" type=\"file\" id=\"picsplz\" class=\"pix\"/>",
+ file_field_tag("picsplz", class: "pix", direct_upload: true)
+ )
+ end
+
+ def test_file_field_tag_with_direct_upload_when_rails_direct_uploads_url_is_defined
+ @controller = WithActiveStorageRoutesControllers.new
+
+ assert_dom_equal(
+ "<input name=\"picsplz\" type=\"file\" id=\"picsplz\" class=\"pix\" data-direct-upload-url=\"http://testtwo.host/rails/active_storage/direct_uploads\"/>",
+ file_field_tag("picsplz", class: "pix", direct_upload: true)
+ )
+ end
+
+ def test_file_field_tag_with_direct_upload_dont_mutate_arguments
+ original_options = { class: "pix", direct_upload: true }
+
+ assert_dom_equal(
+ "<input name=\"picsplz\" type=\"file\" id=\"picsplz\" class=\"pix\"/>",
+ file_field_tag("picsplz", original_options)
+ )
+
+ assert_equal({ class: "pix", direct_upload: true }, original_options)
+ end
+
def test_password_field_tag
actual = password_field_tag
expected = %(<input id="password" name="password" type="password" />)
diff --git a/actionview/test/template/text_helper_test.rb b/actionview/test/template/text_helper_test.rb
index adb923d93b..f247de066f 100644
--- a/actionview/test/template/text_helper_test.rb
+++ b/actionview/test/template/text_helper_test.rb
@@ -50,19 +50,19 @@ class TextHelperTest < ActionView::TestCase
def test_simple_format_should_sanitize_input_when_sanitize_option_is_true
assert_equal "<p><b> test with unsafe string </b>code!</p>",
- simple_format("<b> test with unsafe string </b><script>code!</script>", {}, sanitize: true)
+ simple_format("<b> test with unsafe string </b><script>code!</script>", {}, { sanitize: true })
end
def test_simple_format_should_not_sanitize_input_when_sanitize_option_is_false
- assert_equal "<p><b> test with unsafe string </b><script>code!</script></p>", simple_format("<b> test with unsafe string </b><script>code!</script>", {}, sanitize: false)
+ assert_equal "<p><b> test with unsafe string </b><script>code!</script></p>", simple_format("<b> test with unsafe string </b><script>code!</script>", {}, { sanitize: false })
end
def test_simple_format_with_custom_wrapper
- assert_equal "<div></div>", simple_format(nil, {}, wrapper_tag: "div")
+ assert_equal "<div></div>", simple_format(nil, {}, { wrapper_tag: "div" })
end
def test_simple_format_with_custom_wrapper_and_multi_line_breaks
- assert_equal "<div>We want to put a wrapper...</div>\n\n<div>...right there.</div>", simple_format("We want to put a wrapper...\n\n...right there.", {}, wrapper_tag: "div")
+ assert_equal "<div>We want to put a wrapper...</div>\n\n<div>...right there.</div>", simple_format("We want to put a wrapper...\n\n...right there.", {}, { wrapper_tag: "div" })
end
def test_simple_format_should_not_change_the_text_passed
diff --git a/activejob/test/support/integration/adapters/delayed_job.rb b/activejob/test/support/integration/adapters/delayed_job.rb
index ae5de78b60..fc9bae47fa 100644
--- a/activejob/test/support/integration/adapters/delayed_job.rb
+++ b/activejob/test/support/integration/adapters/delayed_job.rb
@@ -18,5 +18,6 @@ module DelayedJobJobsManager
def stop_workers
@worker.stop
+ @thread.join
end
end
diff --git a/activemodel/lib/active_model/type/integer.rb b/activemodel/lib/active_model/type/integer.rb
index d1473bd792..fe396998a3 100644
--- a/activemodel/lib/active_model/type/integer.rb
+++ b/activemodel/lib/active_model/type/integer.rb
@@ -6,7 +6,7 @@ module ActiveModel
include Helpers::Numeric
# Column storage size in bytes.
- # 4 bytes means a MySQL int or Postgres integer as opposed to smallint etc.
+ # 4 bytes means an integer as opposed to smallint etc.
DEFAULT_LIMIT = 4
def initialize(*)
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index 8181d67816..b39d28ca31 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,3 +1,13 @@
+* Ensure `sum` honors `distinct` on `has_many :through` associations
+
+ Fixes #16791.
+
+ *Aaron Wortham*
+
+* Add `binary` fixture helper method.
+
+ *Atsushi Yoshida*
+
* When using `Relation#or`, extract the common conditions and put them before the OR condition.
*Maxime Handfield Lapointe*
@@ -9,7 +19,7 @@
*Sean Griffin*
-* ApplicationRecord is no longer generated when generating models. If you
+* `ApplicationRecord` is no longer generated when generating models. If you
need to generate it, it can be created with `rails g application_record`.
*Lisa Ugray*
@@ -83,7 +93,7 @@
* Fix transactions to apply state to child transactions
- Previously if you had a nested transaction and the outer transaction was rolledback the record from the
+ Previously, if you had a nested transaction and the outer transaction was rolledback, the record from the
inner transaction would still be marked as persisted.
This change fixes that by applying the state of the parent transaction to the child transaction when the
@@ -149,7 +159,7 @@
*Kir Shatrov*
-* Prevent making bind param if casted value is nil.
+* Prevent creation of bind param if casted value is nil.
*Ryuta Kamizono*
@@ -210,7 +220,7 @@
*bogdanvlviv*
-* When calling the dynamic fixture accessor method with no arguments it now returns all fixtures of this type.
+* When calling the dynamic fixture accessor method with no arguments, it now returns all fixtures of this type.
Previously this method always returned an empty array.
*Kevin McPhillips*
diff --git a/activerecord/RUNNING_UNIT_TESTS.rdoc b/activerecord/RUNNING_UNIT_TESTS.rdoc
index cd22f76d01..60561e2c0f 100644
--- a/activerecord/RUNNING_UNIT_TESTS.rdoc
+++ b/activerecord/RUNNING_UNIT_TESTS.rdoc
@@ -43,5 +43,9 @@ You can override the +connections:+ parameter in either file using the +ARCONN+
$ ARCONN=postgresql bundle exec ruby -Itest test/cases/base_test.rb
+Or
+
+ $ bundle exec rake test:postgresql TEST=test/cases/base_test.rb
+
You can specify a custom location for the config file using the +ARCONFIG+
environment variable.
diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb
index 840f71bef2..a61c0336db 100644
--- a/activerecord/lib/active_record/associations.rb
+++ b/activerecord/lib/active_record/associations.rb
@@ -349,6 +349,7 @@ module ActiveRecord
# build_other(attributes={}) | X | | X
# create_other(attributes={}) | X | | X
# create_other!(attributes={}) | X | | X
+ # reload_other | X | X | X
#
# === Collection associations (one-to-many / many-to-many)
# | | | has_many
@@ -378,6 +379,7 @@ module ActiveRecord
# others.exists? | X | X | X
# others.distinct | X | X | X
# others.reset | X | X | X
+ # others.reload | X | X | X
#
# === Overriding generated methods
#
@@ -1190,8 +1192,8 @@ module ActiveRecord
# <tt>has_many :clients</tt> would add among others <tt>clients.empty?</tt>.
#
# [collection]
- # Returns an array of all the associated objects.
- # An empty array is returned if none are found.
+ # Returns a Relation of all the associated objects.
+ # An empty Relation is returned if none are found.
# [collection<<(object, ...)]
# Adds one or more objects to the collection by setting their foreign keys to the collection's primary key.
# Note that this operation instantly fires update SQL without waiting for the save or update call on the
@@ -1248,6 +1250,9 @@ module ActiveRecord
# [collection.create!(attributes = {})]
# Does the same as <tt>collection.create</tt>, but raises ActiveRecord::RecordInvalid
# if the record is invalid.
+ # [collection.reload]
+ # Returns a Relation of all of the associated objects, forcing a database read.
+ # An empty Relation is returned if none are found.
#
# === Example
#
@@ -1267,6 +1272,7 @@ module ActiveRecord
# * <tt>Firm#clients.build</tt> (similar to <tt>Client.new("firm_id" => id)</tt>)
# * <tt>Firm#clients.create</tt> (similar to <tt>c = Client.new("firm_id" => id); c.save; c</tt>)
# * <tt>Firm#clients.create!</tt> (similar to <tt>c = Client.new("firm_id" => id); c.save!</tt>)
+ # * <tt>Firm#clients.reload</tt>
# The declaration can also include an +options+ hash to specialize the behavior of the association.
#
# === Scopes
@@ -1426,6 +1432,8 @@ module ActiveRecord
# [create_association!(attributes = {})]
# Does the same as <tt>create_association</tt>, but raises ActiveRecord::RecordInvalid
# if the record is invalid.
+ # [reload_association]
+ # Returns the associated object, forcing a database read.
#
# === Example
#
@@ -1435,6 +1443,7 @@ module ActiveRecord
# * <tt>Account#build_beneficiary</tt> (similar to <tt>Beneficiary.new("account_id" => id)</tt>)
# * <tt>Account#create_beneficiary</tt> (similar to <tt>b = Beneficiary.new("account_id" => id); b.save; b</tt>)
# * <tt>Account#create_beneficiary!</tt> (similar to <tt>b = Beneficiary.new("account_id" => id); b.save!; b</tt>)
+ # * <tt>Account#reload_beneficiary</tt>
#
# === Scopes
#
@@ -1555,6 +1564,8 @@ module ActiveRecord
# [create_association!(attributes = {})]
# Does the same as <tt>create_association</tt>, but raises ActiveRecord::RecordInvalid
# if the record is invalid.
+ # [reload_association]
+ # Returns the associated object, forcing a database read.
#
# === Example
#
@@ -1564,6 +1575,7 @@ module ActiveRecord
# * <tt>Post#build_author</tt> (similar to <tt>post.author = Author.new</tt>)
# * <tt>Post#create_author</tt> (similar to <tt>post.author = Author.new; post.author.save; post.author</tt>)
# * <tt>Post#create_author!</tt> (similar to <tt>post.author = Author.new; post.author.save!; post.author</tt>)
+ # * <tt>Post#reload_author</tt>
# The declaration can also include an +options+ hash to specialize the behavior of the association.
#
# === Scopes
@@ -1704,8 +1716,8 @@ module ActiveRecord
# <tt>has_and_belongs_to_many :categories</tt> would add among others <tt>categories.empty?</tt>.
#
# [collection]
- # Returns an array of all the associated objects.
- # An empty array is returned if none are found.
+ # Returns a Relation of all the associated objects.
+ # An empty Relation is returned if none are found.
# [collection<<(object, ...)]
# Adds one or more objects to the collection by creating associations in the join table
# (<tt>collection.push</tt> and <tt>collection.concat</tt> are aliases to this method).
@@ -1743,6 +1755,9 @@ module ActiveRecord
# Returns a new object of the collection type that has been instantiated
# with +attributes+, linked to this object through the join table, and that has already been
# saved (if it passed the validation).
+ # [collection.reload]
+ # Returns a Relation of all of the associated objects, forcing a database read.
+ # An empty Relation is returned if none are found.
#
# === Example
#
@@ -1761,6 +1776,7 @@ module ActiveRecord
# * <tt>Developer#projects.exists?(...)</tt>
# * <tt>Developer#projects.build</tt> (similar to <tt>Project.new("developer_id" => id)</tt>)
# * <tt>Developer#projects.create</tt> (similar to <tt>c = Project.new("developer_id" => id); c.save; c</tt>)
+ # * <tt>Developer#projects.reload</tt>
# The declaration may include an +options+ hash to specialize the behavior of the association.
#
# === Scopes
diff --git a/activerecord/lib/active_record/associations/association_scope.rb b/activerecord/lib/active_record/associations/association_scope.rb
index 9b0b50977d..3d79e540b8 100644
--- a/activerecord/lib/active_record/associations/association_scope.rb
+++ b/activerecord/lib/active_record/associations/association_scope.rb
@@ -68,11 +68,11 @@ module ActiveRecord
foreign_key = join_keys.foreign_key
value = transform_value(owner[foreign_key])
- scope = scope.where(table.name => { key => value })
+ scope = apply_scope(scope, table, key, value)
if reflection.type
polymorphic_type = transform_value(owner.class.base_class.name)
- scope = scope.where(table.name => { reflection.type => polymorphic_type })
+ scope = apply_scope(scope, table, reflection.type, polymorphic_type)
end
scope
@@ -91,10 +91,10 @@ module ActiveRecord
if reflection.type
value = transform_value(next_reflection.klass.base_class.name)
- scope = scope.where(table.name => { reflection.type => value })
+ scope = apply_scope(scope, table, reflection.type, value)
end
- scope = scope.joins(join(foreign_table, constraint))
+ scope.joins!(join(foreign_table, constraint))
end
class ReflectionProxy < SimpleDelegator # :nodoc:
@@ -165,6 +165,14 @@ module ActiveRecord
scope
end
+ def apply_scope(scope, table, key, value)
+ if scope.table == table
+ scope.where!(key => value)
+ else
+ scope.where!(table.name => { key => value })
+ end
+ end
+
def eval_scope(reflection, table, scope, owner)
reflection.build_scope(table).instance_exec(owner, &scope)
end
diff --git a/activerecord/lib/active_record/associations/collection_association.rb b/activerecord/lib/active_record/associations/collection_association.rb
index 0001b804a8..ceedf150e3 100644
--- a/activerecord/lib/active_record/associations/collection_association.rb
+++ b/activerecord/lib/active_record/associations/collection_association.rb
@@ -55,11 +55,16 @@ module ActiveRecord
pk_type = reflection.association_primary_key_type
ids = Array(ids).reject(&:blank?)
ids.map! { |i| pk_type.cast(i) }
- records = klass.where(reflection.association_primary_key => ids).index_by do |r|
- r.send(reflection.association_primary_key)
+
+ primary_key = reflection.association_primary_key
+ records = klass.where(primary_key => ids).index_by do |r|
+ r.public_send(primary_key)
end.values_at(*ids).compact
+
if records.size != ids.size
- klass.all.raise_record_not_found_exception!(ids, records.size, ids.size, reflection.association_primary_key)
+ found_ids = records.map { |record| record.public_send(primary_key) }
+ not_found_ids = ids - found_ids
+ klass.all.raise_record_not_found_exception!(ids, records.size, ids.size, primary_key, not_found_ids)
else
replace(records)
end
@@ -72,23 +77,19 @@ module ActiveRecord
end
def find(*args)
- if block_given?
- load_target.find(*args) { |*block_args| yield(*block_args) }
- else
- if options[:inverse_of] && loaded?
- args_flatten = args.flatten
- raise RecordNotFound, "Couldn't find #{scope.klass.name} without an ID" if args_flatten.blank?
- result = find_by_scan(*args)
-
- result_size = Array(result).size
- if !result || result_size != args_flatten.size
- scope.raise_record_not_found_exception!(args_flatten, result_size, args_flatten.size)
- else
- result
- end
+ if options[:inverse_of] && loaded?
+ args_flatten = args.flatten
+ raise RecordNotFound, "Couldn't find #{scope.klass.name} without an ID" if args_flatten.blank?
+ result = find_by_scan(*args)
+
+ result_size = Array(result).size
+ if !result || result_size != args_flatten.size
+ scope.raise_record_not_found_exception!(args_flatten, result_size, args_flatten.size)
else
- scope.find(*args)
+ result
end
+ else
+ scope.find(*args)
end
end
diff --git a/activerecord/lib/active_record/associations/collection_proxy.rb b/activerecord/lib/active_record/associations/collection_proxy.rb
index 0678b07699..412e89255d 100644
--- a/activerecord/lib/active_record/associations/collection_proxy.rb
+++ b/activerecord/lib/active_record/associations/collection_proxy.rb
@@ -135,8 +135,9 @@ module ActiveRecord
# # #<Pet id: 2, name: "Spook", person_id: 1>,
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
# # ]
- def find(*args, &block)
- @association.find(*args, &block)
+ def find(*args)
+ return super if block_given?
+ @association.find(*args)
end
##
diff --git a/activerecord/lib/active_record/associations/preloader/association.rb b/activerecord/lib/active_record/associations/preloader/association.rb
index 5ba03c555a..4915a37f06 100644
--- a/activerecord/lib/active_record/associations/preloader/association.rb
+++ b/activerecord/lib/active_record/associations/preloader/association.rb
@@ -116,18 +116,8 @@ module ActiveRecord
@reflection_scope ||= reflection.scope_for(klass)
end
- def klass_scope
- current_scope = klass.current_scope
-
- if current_scope && current_scope.empty_scope?
- klass.unscoped
- else
- klass.default_scoped
- end
- end
-
def build_scope
- scope = klass_scope
+ scope = klass.scope_for_association
if reflection.type
scope.where!(reflection.type => model.base_class.sti_name)
diff --git a/activerecord/lib/active_record/associations/through_association.rb b/activerecord/lib/active_record/associations/through_association.rb
index 76237c4a0c..cba565448f 100644
--- a/activerecord/lib/active_record/associations/through_association.rb
+++ b/activerecord/lib/active_record/associations/through_association.rb
@@ -15,7 +15,7 @@ module ActiveRecord
def target_scope
scope = super
reflection.chain.drop(1).each do |reflection|
- relation = reflection.klass.all
+ relation = reflection.klass.scope_for_association
scope.merge!(
relation.except(:select, :create_with, :includes, :preload, :joins, :eager_load)
)
diff --git a/activerecord/lib/active_record/attribute_methods/dirty.rb b/activerecord/lib/active_record/attribute_methods/dirty.rb
index 5efe051125..c622df132e 100644
--- a/activerecord/lib/active_record/attribute_methods/dirty.rb
+++ b/activerecord/lib/active_record/attribute_methods/dirty.rb
@@ -5,7 +5,7 @@ require_relative "../attribute_mutation_tracker"
module ActiveRecord
module AttributeMethods
- module Dirty # :nodoc:
+ module Dirty
extend ActiveSupport::Concern
include ActiveModel::Dirty
@@ -47,7 +47,7 @@ module ActiveRecord
clear_mutation_trackers
end
- def changes_applied
+ def changes_applied # :nodoc:
@mutations_before_last_save = mutation_tracker
@mutations_from_database = AttributeMutationTracker.new(@attributes)
@changed_attributes = ActiveSupport::HashWithIndifferentAccess.new
@@ -55,27 +55,21 @@ module ActiveRecord
clear_mutation_trackers
end
- def clear_changes_information
+ def clear_changes_information # :nodoc:
@mutations_before_last_save = nil
@changed_attributes = ActiveSupport::HashWithIndifferentAccess.new
forget_attribute_assignments
clear_mutation_trackers
end
- def write_attribute_without_type_cast(attr_name, *)
- result = super
- clear_attribute_change(attr_name)
- result
- end
-
- def clear_attribute_changes(attr_names)
+ def clear_attribute_changes(attr_names) # :nodoc:
super
attr_names.each do |attr_name|
clear_attribute_change(attr_name)
end
end
- def changed_attributes
+ def changed_attributes # :nodoc:
# This should only be set by methods which will call changed_attributes
# multiple times when it is known that the computed value cannot change.
if defined?(@cached_changed_attributes)
@@ -85,17 +79,17 @@ module ActiveRecord
end
end
- def changes
+ def changes # :nodoc:
cache_changed_attributes do
super
end
end
- def previous_changes
+ def previous_changes # :nodoc:
mutations_before_last_save.changes
end
- def attribute_changed_in_place?(attr_name)
+ def attribute_changed_in_place?(attr_name) # :nodoc:
mutation_tracker.changed_in_place?(attr_name)
end
@@ -183,6 +177,11 @@ module ActiveRecord
end
private
+ def write_attribute_without_type_cast(attr_name, _)
+ result = super
+ clear_attribute_change(attr_name)
+ result
+ end
def mutation_tracker
unless defined?(@mutation_tracker)
diff --git a/activerecord/lib/active_record/attribute_methods/serialization.rb b/activerecord/lib/active_record/attribute_methods/serialization.rb
index 6ed45d8737..ebc2baed34 100644
--- a/activerecord/lib/active_record/attribute_methods/serialization.rb
+++ b/activerecord/lib/active_record/attribute_methods/serialization.rb
@@ -5,6 +5,16 @@ module ActiveRecord
module Serialization
extend ActiveSupport::Concern
+ class ColumnNotSerializableError < StandardError
+ def initialize(name, type)
+ super <<-EOS.strip_heredoc
+ Column `#{name}` of type #{type.class} does not support `serialize` feature.
+ Usually it means that you are trying to use `serialize`
+ on a column that already implements serialization natively.
+ EOS
+ end
+ end
+
module ClassMethods
# If you have an attribute that needs to be saved to the database as an
# object, and retrieved as the same object, then specify the name of that
@@ -60,9 +70,20 @@ module ActiveRecord
end
decorate_attribute_type(attr_name, :serialize) do |type|
+ if type_incompatible_with_serialize?(type, class_name_or_coder)
+ raise ColumnNotSerializableError.new(attr_name, type)
+ end
+
Type::Serialized.new(type, coder)
end
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
+ end
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
index c11b7b012f..0759f4d2b3 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
@@ -270,7 +270,7 @@ module ActiveRecord
# Connections must be leased while holding the main pool mutex. This is
# an internal subclass that also +.leases+ returned connections while
# still in queue's critical section (queue synchronizes with the same
- # +@lock+ as the main pool) so that a returned connection is already
+ # <tt>@lock</tt> as the main pool) so that a returned connection is already
# leased and there is no need to re-enter synchronized block.
class ConnectionLeasingQueue < Queue # :nodoc:
include BiasableQueue
@@ -326,8 +326,6 @@ module ActiveRecord
@spec = spec
@checkout_timeout = (spec.config[:checkout_timeout] && spec.config[:checkout_timeout].to_f) || 5
- @reaper = Reaper.new(self, (spec.config[:reaping_frequency] && spec.config[:reaping_frequency].to_f))
- @reaper.run
# default max pool size to 5
@size = (spec.config[:pool] && spec.config[:pool].to_i) || 5
@@ -340,7 +338,7 @@ module ActiveRecord
# then that +thread+ does indeed own that +conn+. However, an absence of a such
# mapping does not mean that the +thread+ doesn't own the said connection. In
# that case +conn.owner+ attr should be consulted.
- # Access and modification of +@thread_cached_conns+ does not require
+ # Access and modification of <tt>@thread_cached_conns</tt> does not require
# synchronization.
@thread_cached_conns = Concurrent::Map.new(initial_capacity: @size)
@@ -357,6 +355,9 @@ module ActiveRecord
@available = ConnectionLeasingQueue.new self
@lock_thread = false
+
+ @reaper = Reaper.new(self, spec.config[:reaping_frequency] && spec.config[:reaping_frequency].to_f)
+ @reaper.run
end
def lock_thread=(lock_thread)
@@ -736,10 +737,10 @@ module ActiveRecord
# Implementation detail: the connection returned by +acquire_connection+
# will already be "+connection.lease+ -ed" to the current thread.
def acquire_connection(checkout_timeout)
- # NOTE: we rely on +@available.poll+ and +try_to_checkout_new_connection+ to
+ # NOTE: we rely on <tt>@available.poll</tt> and +try_to_checkout_new_connection+ to
# +conn.lease+ the returned connection (and to do this in a +synchronized+
# section). This is not the cleanest implementation, as ideally we would
- # <tt>synchronize { conn.lease }</tt> in this method, but by leaving it to +@available.poll+
+ # <tt>synchronize { conn.lease }</tt> in this method, but by leaving it to <tt>@available.poll</tt>
# and +try_to_checkout_new_connection+ we can piggyback on +synchronize+ sections
# of the said methods and avoid an additional +synchronize+ overhead.
if conn = @available.poll || try_to_checkout_new_connection
@@ -763,7 +764,7 @@ module ActiveRecord
end
end
- # If the pool is not at a +@size+ limit, establish new connection. Connecting
+ # If the pool is not at a <tt>@size</tt> limit, establish new connection. Connecting
# to the DB is done outside main synchronized section.
#--
# Implementation constraint: a newly established connection returned by this
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 ecf5201d12..41d93c4322 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb
@@ -1,5 +1,7 @@
# frozen_string_literal: true
+require "concurrent/map"
+
module ActiveRecord
module ConnectionAdapters # :nodoc:
module QueryCache
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 e21f93856e..3c9b25e411 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
@@ -780,7 +780,7 @@ module ActiveRecord
def rename_index(table_name, old_name, new_name)
validate_index_length!(table_name, new_name)
- # this is a naive implementation; some DBs may support this more efficiently (Postgres, for instance)
+ # this is a naive implementation; some DBs may support this more efficiently (PostgreSQL, for instance)
old_index_def = indexes(table_name).detect { |i| i.name == old_name }
return unless old_index_def
add_index(table_name, old_index_def.columns, name: new_name, unique: old_index_def.unique)
diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb
index fbb4c671a5..a84e62ab11 100644
--- a/activerecord/lib/active_record/core.rb
+++ b/activerecord/lib/active_record/core.rb
@@ -143,7 +143,7 @@ module ActiveRecord
self.default_connection_handler = ConnectionAdapters::ConnectionHandler.new
end
- module ClassMethods
+ module ClassMethods # :nodoc:
def allocate
define_attribute_methods
super
@@ -251,7 +251,7 @@ module ActiveRecord
end
end
- # Overwrite the default class equality method to provide support for association proxies.
+ # Overwrite the default class equality method to provide support for decorated models.
def ===(object)
object.is_a?(self)
end
diff --git a/activerecord/lib/active_record/errors.rb b/activerecord/lib/active_record/errors.rb
index 933589d4b1..e790760292 100644
--- a/activerecord/lib/active_record/errors.rb
+++ b/activerecord/lib/active_record/errors.rb
@@ -169,7 +169,7 @@ module ActiveRecord
class NoDatabaseError < StatementInvalid
end
- # Raised when Postgres returns 'cached plan must not change result type' and
+ # Raised when PostgreSQL returns 'cached plan must not change result type' and
# we cannot retry gracefully (e.g. inside a transaction)
class PreparedStatementCacheExpired < StatementInvalid
end
diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb
index ef302fc0a0..4940e122f4 100644
--- a/activerecord/lib/active_record/fixtures.rb
+++ b/activerecord/lib/active_record/fixtures.rb
@@ -1065,6 +1065,10 @@ class ActiveRecord::FixtureSet::RenderContext # :nodoc:
def get_binding
binding()
end
+
+ def binary(path)
+ %(!!binary "#{Base64.strict_encode64(File.read(path))}")
+ end
end
end
end
diff --git a/activerecord/lib/active_record/migration/compatibility.rb b/activerecord/lib/active_record/migration/compatibility.rb
index d6595c9355..784292f3f9 100644
--- a/activerecord/lib/active_record/migration/compatibility.rb
+++ b/activerecord/lib/active_record/migration/compatibility.rb
@@ -39,7 +39,7 @@ module ActiveRecord
end
end
- # Since 5.1 Postgres adapter uses bigserial type for primary
+ # Since 5.1 PostgreSQL adapter uses bigserial type for primary
# keys by default and MySQL uses bigint. This compat layer makes old migrations utilize
# serial/int type instead -- the way it used to work before 5.1.
unless options.key?(:id)
diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb
index 97f1a83033..b847933b2e 100644
--- a/activerecord/lib/active_record/reflection.rb
+++ b/activerecord/lib/active_record/reflection.rb
@@ -221,13 +221,8 @@ module ActiveRecord
end
def klass_join_scope(table, predicate_builder) # :nodoc:
- current_scope = klass.current_scope
-
- if current_scope && current_scope.empty_scope?
- build_scope(table, predicate_builder)
- else
- klass.default_scoped(build_scope(table, predicate_builder))
- end
+ relation = build_scope(table, predicate_builder)
+ klass.scope_for_association(relation)
end
def constraints
@@ -305,13 +300,17 @@ module ActiveRecord
end
def get_join_keys(association_klass)
- JoinKeys.new(join_pk(association_klass), join_fk)
+ JoinKeys.new(join_pk(association_klass), join_foreign_key)
end
def build_scope(table, predicate_builder = predicate_builder(table))
Relation.create(klass, table, predicate_builder)
end
+ def join_foreign_key
+ active_record_primary_key
+ end
+
protected
def actual_source_reflection # FIXME: this is a horrible name
self
@@ -326,8 +325,8 @@ module ActiveRecord
foreign_key
end
- def join_fk
- active_record_primary_key
+ def primary_key(klass)
+ klass.primary_key || raise(UnknownPrimaryKey.new(klass))
end
end
@@ -512,7 +511,7 @@ module ActiveRecord
alias :check_eager_loadable! :check_preloadable!
def join_id_for(owner) # :nodoc:
- owner[active_record_primary_key]
+ owner[join_foreign_key]
end
def through_reflection
@@ -697,10 +696,6 @@ module ActiveRecord
def derive_join_table
ModelSchema.derive_join_table_name active_record.table_name, klass.table_name
end
-
- def primary_key(klass)
- klass.primary_key || raise(UnknownPrimaryKey.new(klass))
- end
end
class HasManyReflection < AssociationReflection # :nodoc:
@@ -715,6 +710,10 @@ module ActiveRecord
Associations::HasManyAssociation
end
end
+
+ def association_primary_key(klass = nil)
+ primary_key(klass || self.klass)
+ end
end
class HasOneReflection < AssociationReflection # :nodoc:
@@ -750,8 +749,8 @@ module ActiveRecord
end
end
- def join_id_for(owner) # :nodoc:
- owner[foreign_key]
+ def join_foreign_key
+ foreign_key
end
private
@@ -760,10 +759,6 @@ module ActiveRecord
!polymorphic?
end
- def join_fk
- foreign_key
- end
-
def join_pk(klass)
polymorphic? ? association_primary_key(klass) : association_primary_key
end
@@ -780,7 +775,7 @@ module ActiveRecord
# Holds all the metadata about a :through association as it was specified
# in the Active Record class.
class ThroughReflection < AbstractReflection #:nodoc:
- delegate :foreign_key, :foreign_type, :association_foreign_key,
+ delegate :foreign_key, :foreign_type, :association_foreign_key, :join_id_for,
:active_record_primary_key, :type, :get_join_keys, to: :source_reflection
def initialize(delegate_reflection)
@@ -943,10 +938,6 @@ module ActiveRecord
through_reflection.options
end
- def join_id_for(owner) # :nodoc:
- source_reflection.join_id_for(owner)
- end
-
def check_validity!
if through_reflection.nil?
raise HasManyThroughAssociationNotFoundError.new(active_record.name, self)
@@ -1024,10 +1015,6 @@ module ActiveRecord
end
end
- def primary_key(klass)
- klass.primary_key || raise(UnknownPrimaryKey.new(klass))
- end
-
def inverse_name; delegate_reflection.send(:inverse_name); end
def derive_class_name
@@ -1073,10 +1060,6 @@ module ActiveRecord
@reflection.scope
end
- def table_name
- @reflection.table_name
- end
-
def plural_name
@reflection.plural_name
end
@@ -1089,15 +1072,16 @@ module ActiveRecord
@reflection.constraints + [source_type_info]
end
- def source_type_info
- type = @previous_reflection.foreign_type
- source_type = @previous_reflection.options[:source_type]
- lambda { |object| where(type => source_type) }
- end
-
def get_join_keys(association_klass)
@reflection.get_join_keys(association_klass)
end
+
+ private
+ def source_type_info
+ type = @previous_reflection.foreign_type
+ source_type = @previous_reflection.options[:source_type]
+ lambda { |object| where(type => source_type) }
+ end
end
class RuntimeReflection < PolymorphicReflection # :nodoc:
@@ -1112,18 +1096,10 @@ module ActiveRecord
@association.klass
end
- def table_name
- klass.table_name
- end
-
def constraints
@reflection.constraints
end
- def source_type_info
- @reflection.source_type_info
- end
-
def alias_candidate(name)
"#{plural_name}_#{name}_join"
end
diff --git a/activerecord/lib/active_record/relation/batches.rb b/activerecord/lib/active_record/relation/batches.rb
index 141ad176ea..fa19c679cf 100644
--- a/activerecord/lib/active_record/relation/batches.rb
+++ b/activerecord/lib/active_record/relation/batches.rb
@@ -47,7 +47,12 @@ module ActiveRecord
# handle from 10000 and beyond by setting the +:start+ and +:finish+
# option on each worker.
#
- # # Let's process from record 10_000 on.
+ # # In worker 1, let's process until 9999 records.
+ # Person.find_each(finish: 9_999) do |person|
+ # person.party_all_night!
+ # end
+ #
+ # # In worker 2, let's process from record 10_000 and onwards.
# Person.find_each(start: 10_000) do |person|
# person.party_all_night!
# end
diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb
index d3b5be6bce..42d43224fa 100644
--- a/activerecord/lib/active_record/relation/calculations.rb
+++ b/activerecord/lib/active_record/relation/calculations.rb
@@ -259,6 +259,9 @@ module ActiveRecord
column = aggregate_column(column_name)
select_value = operation_over_aggregate_column(column, operation, distinct)
+ if operation == "sum" && distinct
+ select_value.distinct = true
+ end
column_alias = select_value.alias
column_alias ||= @klass.connection.column_name_for_operation(operation, select_value)
diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb
index 626c50470e..5da9573052 100644
--- a/activerecord/lib/active_record/relation/finder_methods.rb
+++ b/activerecord/lib/active_record/relation/finder_methods.rb
@@ -330,7 +330,7 @@ module ActiveRecord
# of results obtained should be provided in the +result_size+ argument and
# the expected number of results should be provided in the +expected_size+
# argument.
- def raise_record_not_found_exception!(ids = nil, result_size = nil, expected_size = nil, key = primary_key) # :nodoc:
+ def raise_record_not_found_exception!(ids = nil, result_size = nil, expected_size = nil, key = primary_key, not_found_ids = nil) # :nodoc:
conditions = arel.where_sql(@klass)
conditions = " [#{conditions}]" if conditions
name = @klass.name
@@ -344,8 +344,8 @@ module ActiveRecord
raise RecordNotFound.new(error, name, key, ids)
else
error = "Couldn't find all #{name.pluralize} with '#{key}': ".dup
- error << "(#{ids.join(", ")})#{conditions} (found #{result_size} results, but was looking for #{expected_size})"
-
+ error << "(#{ids.join(", ")})#{conditions} (found #{result_size} results, but was looking for #{expected_size})."
+ error << " Couldn't find #{name.pluralize(not_found_ids.size)} with #{key.to_s.pluralize(not_found_ids.size)} #{not_found_ids.join(', ')}." if not_found_ids
raise RecordNotFound.new(error, name, primary_key, ids)
end
end
diff --git a/activerecord/lib/active_record/relation/predicate_builder/association_query_value.rb b/activerecord/lib/active_record/relation/predicate_builder/association_query_value.rb
index e64d9fdf2a..0255a65bfe 100644
--- a/activerecord/lib/active_record/relation/predicate_builder/association_query_value.rb
+++ b/activerecord/lib/active_record/relation/predicate_builder/association_query_value.rb
@@ -9,7 +9,7 @@ module ActiveRecord
end
def queries
- [associated_table.association_foreign_key.to_s => ids]
+ [associated_table.association_join_foreign_key.to_s => ids]
end
# TODO Change this to private once we've dropped Ruby 2.2 support.
@@ -30,7 +30,7 @@ module ActiveRecord
end
def primary_key
- associated_table.association_primary_key
+ associated_table.association_join_keys.key
end
def convert_to_id(value)
diff --git a/activerecord/lib/active_record/scoping/named.rb b/activerecord/lib/active_record/scoping/named.rb
index 43cce19c1f..6fa096c1fe 100644
--- a/activerecord/lib/active_record/scoping/named.rb
+++ b/activerecord/lib/active_record/scoping/named.rb
@@ -31,6 +31,16 @@ module ActiveRecord
end
end
+ def scope_for_association(scope = relation) # :nodoc:
+ current_scope = self.current_scope
+
+ if current_scope && current_scope.empty_scope?
+ scope
+ else
+ default_scoped(scope)
+ end
+ end
+
def default_scoped(scope = relation) # :nodoc:
build_default_scope(scope) || scope
end
diff --git a/activerecord/lib/active_record/table_metadata.rb b/activerecord/lib/active_record/table_metadata.rb
index 71351449e1..a5187efc84 100644
--- a/activerecord/lib/active_record/table_metadata.rb
+++ b/activerecord/lib/active_record/table_metadata.rb
@@ -2,7 +2,7 @@
module ActiveRecord
class TableMetadata # :nodoc:
- delegate :foreign_type, :foreign_key, to: :association, prefix: true
+ delegate :foreign_type, :foreign_key, :join_keys, :join_foreign_key, to: :association, prefix: true
delegate :association_primary_key, to: :association
def initialize(klass, arel_table, association = nil)
diff --git a/activerecord/lib/active_record/tasks/postgresql_database_tasks.rb b/activerecord/lib/active_record/tasks/postgresql_database_tasks.rb
index a2e74efc2b..5681ccdd23 100644
--- a/activerecord/lib/active_record/tasks/postgresql_database_tasks.rb
+++ b/activerecord/lib/active_record/tasks/postgresql_database_tasks.rb
@@ -22,7 +22,7 @@ module ActiveRecord
configuration.merge("encoding" => encoding)
establish_connection configuration
rescue ActiveRecord::StatementInvalid => error
- if /database .* already exists/.match?(error.message)
+ if error.cause.is_a?(PG::DuplicateDatabase)
raise DatabaseAlreadyExists
else
raise
@@ -136,7 +136,7 @@ module ActiveRecord
ensure
tempfile.close
end
- FileUtils.mv(tempfile.path, filename)
+ FileUtils.cp(tempfile.path, filename)
end
end
end
diff --git a/activerecord/lib/active_record/transactions.rb b/activerecord/lib/active_record/transactions.rb
index f91f0cdf12..603dee8c0d 100644
--- a/activerecord/lib/active_record/transactions.rb
+++ b/activerecord/lib/active_record/transactions.rb
@@ -190,7 +190,7 @@ module ActiveRecord
#
# === Caveats
#
- # If you're on MySQL, then do not use Data Definition Language(DDL) operations in nested
+ # If you're on MySQL, then do not use Data Definition Language (DDL) operations in nested
# transactions blocks that are emulated with savepoints. That is, do not execute statements
# like 'CREATE TABLE' inside such blocks. This is because MySQL automatically
# releases all savepoints upon executing a DDL operation. When +transaction+
@@ -472,7 +472,7 @@ module ActiveRecord
# if it's associated with a transaction, then the state of the Active Record
# object will be updated to reflect the current state of the transaction.
#
- # The +@transaction_state+ variable stores the states of the associated
+ # The <tt>@transaction_state</tt> variable stores the states of the associated
# transaction. This relies on the fact that a transaction can only be in
# one rollback or commit (otherwise a list of states would be required).
# Each Active Record object inside of a transaction carries that transaction's
diff --git a/activerecord/lib/active_record/validations/uniqueness.rb b/activerecord/lib/active_record/validations/uniqueness.rb
index 2677fade18..baeb653c61 100644
--- a/activerecord/lib/active_record/validations/uniqueness.rb
+++ b/activerecord/lib/active_record/validations/uniqueness.rb
@@ -8,6 +8,10 @@ module ActiveRecord
raise ArgumentError, "#{options[:conditions]} was passed as :conditions but is not callable. " \
"Pass a callable instead: `conditions: -> { where(approved: true) }`"
end
+ unless Array(options[:scope]).all? { |scope| scope.respond_to?(:to_sym) }
+ raise ArgumentError, "#{options[:scope]} is not supported format for :scope option. " \
+ "Pass a symbol or an array of symbols instead: `scope: :user_id`"
+ end
super({ case_sensitive: true }.merge!(options))
@klass = options[:class]
end
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 d18330f5b2..35d5664400 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
@@ -15,11 +15,12 @@ module ActiveRecord
private
def application_record_file_name
- @application_record_file_name ||= if namespaced?
- "app/models/#{namespaced_path}/application_record.rb"
- else
- "app/models/application_record.rb"
- end
+ @application_record_file_name ||=
+ if namespaced?
+ "app/models/#{namespaced_path}/application_record.rb"
+ else
+ "app/models/application_record.rb"
+ end
end
end
end
diff --git a/activerecord/test/cases/adapters/postgresql/array_test.rb b/activerecord/test/cases/adapters/postgresql/array_test.rb
index 8507dbb463..0e9e86f425 100644
--- a/activerecord/test/cases/adapters/postgresql/array_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/array_test.rb
@@ -47,6 +47,39 @@ class PostgresqlArrayTest < ActiveRecord::PostgreSQLTestCase
assert ratings_column.array?
end
+ def test_not_compatible_with_serialize_array
+ new_klass = Class.new(PgArray) do
+ serialize :tags, Array
+ end
+ assert_raises(ActiveRecord::AttributeMethods::Serialization::ColumnNotSerializableError) do
+ new_klass.new
+ end
+ end
+
+ class MyTags
+ def initialize(tags); @tags = tags end
+ def to_a; @tags end
+ def self.load(tags); new(tags) end
+ def self.dump(object); object.to_a end
+ end
+
+ def test_array_with_serialized_attributes
+ new_klass = Class.new(PgArray) do
+ serialize :tags, MyTags
+ end
+
+ new_klass.create!(tags: MyTags.new(["one", "two"]))
+ record = new_klass.first
+
+ assert_instance_of MyTags, record.tags
+ assert_equal ["one", "two"], record.tags.to_a
+
+ record.tags = MyTags.new(["three", "four"])
+ record.save!
+
+ assert_equal ["three", "four"], record.reload.tags.to_a
+ end
+
def test_default
@connection.add_column "pg_arrays", "score", :integer, array: true, default: [4, 4, 2]
PgArray.reset_column_information
diff --git a/activerecord/test/cases/adapters/postgresql/json_test.rb b/activerecord/test/cases/adapters/postgresql/json_test.rb
index aa5e03df41..ee08841eb3 100644
--- a/activerecord/test/cases/adapters/postgresql/json_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/json_test.rb
@@ -21,8 +21,8 @@ module PostgresqlJSONSharedTestCases
@connection.add_column "json_data_type", "permissions", column_type, default: { "users": "read", "posts": ["read", "write"] }
klass.reset_column_information
- assert_equal({ "users" => "read", "posts" => ["read", "write"] }, JsonDataType.column_defaults["permissions"])
- assert_equal({ "users" => "read", "posts" => ["read", "write"] }, JsonDataType.new.permissions)
+ assert_equal({ "users" => "read", "posts" => ["read", "write"] }, klass.column_defaults["permissions"])
+ assert_equal({ "users" => "read", "posts" => ["read", "write"] }, klass.new.permissions)
end
def test_deserialize_with_array
diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb
index cedb621b4f..383c7314e1 100644
--- a/activerecord/test/cases/associations/has_many_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_associations_test.rb
@@ -63,7 +63,7 @@ class HasManyAssociationsTestPrimaryKeys < ActiveRecord::TestCase
assert_equal 2, subscriber.subscriptions.size
end
- assert_equal subscriber.subscriptions, Subscription.where(subscriber_id: "webster132")
+ assert_equal Subscription.where(subscriber_id: "webster132"), subscriber.subscriptions
end
def test_association_primary_key_on_new_record_should_fetch_with_query
@@ -74,12 +74,23 @@ class HasManyAssociationsTestPrimaryKeys < ActiveRecord::TestCase
assert_equal 1, author.essays.size
end
- assert_equal author.essays, Essay.where(writer_id: "David")
+ assert_equal Essay.where(writer_id: "David"), author.essays
end
def test_has_many_custom_primary_key
david = authors(:david)
- assert_equal david.essays, Essay.where(writer_id: "David")
+ assert_equal Essay.where(writer_id: "David"), david.essays
+ end
+
+ def test_ids_on_unloaded_association_with_custom_primary_key
+ david = people(:david)
+ assert_equal Essay.where(writer_id: "David").pluck(:id), david.essay_ids
+ end
+
+ def test_ids_on_loaded_association_with_custom_primary_key
+ david = people(:david)
+ david.essays.load
+ assert_equal Essay.where(writer_id: "David").pluck(:id), david.essay_ids
end
def test_has_many_assignment_with_custom_primary_key
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 5797ffef38..f8ea51225a 100644
--- a/activerecord/test/cases/associations/has_many_through_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb
@@ -817,7 +817,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
sarah = Person.create!(first_name: "Sarah", primary_contact_id: people(:susan).id, gender: "F", number1_fan_id: 1)
john = Person.create!(first_name: "John", primary_contact_id: sarah.id, gender: "M", number1_fan_id: 1)
assert_equal sarah.agents, [john]
- assert_equal people(:susan).agents.flat_map(&:agents), people(:susan).agents_of_agents
+ assert_equal people(:susan).agents.flat_map(&:agents).sort, people(:susan).agents_of_agents.sort
end
def test_associate_existing_with_nonstandard_primary_key_on_belongs_to
@@ -880,32 +880,20 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
end
end
- def test_collection_singular_ids_setter_with_changed_primary_key
- company = companies(:first_firm)
- client = companies(:first_client)
- company.clients_using_primary_key_ids = [client.name]
- assert_equal [client], company.clients_using_primary_key
- end
-
def test_collection_singular_ids_setter_raises_exception_when_invalid_ids_set
company = companies(:rails_core)
ids = [Developer.first.id, -9999]
e = assert_raises(ActiveRecord::RecordNotFound) { company.developer_ids = ids }
- assert_match(/Couldn't find all Developers with 'id'/, e.message)
- end
-
- def test_collection_singular_ids_setter_raises_exception_when_invalid_ids_set_with_changed_primary_key
- company = companies(:first_firm)
- ids = [Client.first.name, "unknown client"]
- e = assert_raises(ActiveRecord::RecordNotFound) { company.clients_using_primary_key_ids = ids }
- assert_match(/Couldn't find all Clients with 'name'/, e.message)
+ msg = "Couldn't find all Developers with 'id': (1, -9999) (found 1 results, but was looking for 2). Couldn't find Developer with id -9999."
+ assert_equal(msg, e.message)
end
def test_collection_singular_ids_through_setter_raises_exception_when_invalid_ids_set
author = authors(:david)
ids = [categories(:general).name, "Unknown"]
e = assert_raises(ActiveRecord::RecordNotFound) { author.essay_category_ids = ids }
- assert_equal "Couldn't find all Categories with 'name': (General, Unknown) (found 1 results, but was looking for 2)", e.message
+ msg = "Couldn't find all Categories with 'name': (General, Unknown) (found 1 results, but was looking for 2). Couldn't find Category with name Unknown."
+ assert_equal msg, e.message
end
def test_build_a_model_from_hm_through_association_with_where_clause
@@ -1137,6 +1125,32 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
assert_equal ["parrot", "bulbul"], owner.toys.map { |r| r.pet.name }
end
+ def test_has_many_through_associations_sum_on_columns
+ post1 = Post.create(title: "active", body: "sample")
+ post2 = Post.create(title: "inactive", body: "sample")
+
+ person1 = Person.create(first_name: "aaron", followers_count: 1)
+ person2 = Person.create(first_name: "schmit", followers_count: 2)
+ person3 = Person.create(first_name: "bill", followers_count: 3)
+ person4 = Person.create(first_name: "cal", followers_count: 4)
+
+ Reader.create(post_id: post1.id, person_id: person1.id)
+ Reader.create(post_id: post1.id, person_id: person2.id)
+ Reader.create(post_id: post1.id, person_id: person3.id)
+ Reader.create(post_id: post1.id, person_id: person4.id)
+
+ Reader.create(post_id: post2.id, person_id: person1.id)
+ Reader.create(post_id: post2.id, person_id: person2.id)
+ Reader.create(post_id: post2.id, person_id: person3.id)
+ Reader.create(post_id: post2.id, person_id: person4.id)
+
+ active_persons = Person.joins(:readers).joins(:posts).distinct(true).where("posts.title" => "active")
+
+ assert_equal active_persons.map(&:followers_count).reduce(:+), 10
+ assert_equal active_persons.sum(:followers_count), 10
+ assert_equal active_persons.sum(:followers_count), active_persons.map(&:followers_count).reduce(:+)
+ end
+
def test_has_many_through_associations_on_new_records_use_null_relations
person = Person.new
@@ -1247,6 +1261,25 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
assert_equal 0, users[2].family_members.to_a.size
end
+ def test_through_scope_is_affected_by_unscoping
+ author = authors(:david)
+
+ expected = author.comments.to_a
+ FirstPost.unscoped do
+ assert_equal expected.sort_by(&:id), author.comments_on_first_posts.sort_by(&:id)
+ end
+ end
+
+ def test_through_scope_isnt_affected_by_scoping
+ author = authors(:david)
+
+ expected = author.comments_on_first_posts.to_a
+ FirstPost.where(id: 2).scoping do
+ author.comments_on_first_posts.reset
+ assert_equal expected.sort_by(&:id), author.comments_on_first_posts.sort_by(&:id)
+ end
+ end
+
def test_incorrectly_ordered_through_associations
assert_raises(ActiveRecord::HasManyThroughOrderError) do
DeveloperWithIncorrectlyOrderedHasManyThrough.create(
diff --git a/activerecord/test/cases/associations/has_one_associations_test.rb b/activerecord/test/cases/associations/has_one_associations_test.rb
index 2eb6cef1d9..fdb98d84e0 100644
--- a/activerecord/test/cases/associations/has_one_associations_test.rb
+++ b/activerecord/test/cases/associations/has_one_associations_test.rb
@@ -15,7 +15,7 @@ require "models/post"
class HasOneAssociationsTest < ActiveRecord::TestCase
self.use_transactional_tests = false unless supports_savepoints?
- fixtures :accounts, :companies, :developers, :projects, :developers_projects, :ships, :pirates
+ fixtures :accounts, :companies, :developers, :projects, :developers_projects, :ships, :pirates, :authors
def setup
Account.destroyed_account_ids.clear
diff --git a/activerecord/test/cases/associations/join_model_test.rb b/activerecord/test/cases/associations/join_model_test.rb
index 2f68bc5141..3abdcf3564 100644
--- a/activerecord/test/cases/associations/join_model_test.rb
+++ b/activerecord/test/cases/associations/join_model_test.rb
@@ -99,11 +99,11 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
end
def test_polymorphic_has_many_create_model_with_inheritance_and_custom_base_class
- post = SubStiPost.create title: "SubStiPost", body: "SubStiPost body"
- assert_instance_of SubStiPost, post
+ post = SubAbstractStiPost.create title: "SubAbstractStiPost", body: "SubAbstractStiPost body"
+ assert_instance_of SubAbstractStiPost, post
tagging = tags(:misc).taggings.create(taggable: post)
- assert_equal "SubStiPost", tagging.taggable_type
+ assert_equal "SubAbstractStiPost", tagging.taggable_type
end
def test_polymorphic_has_many_going_through_join_model_with_inheritance
@@ -404,7 +404,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
end
def test_has_many_through_polymorphic_has_one
- assert_equal Tagging.find(1, 2).sort_by(&:id), authors(:david).taggings_2
+ assert_equal Tagging.find(1, 2).sort_by(&:id), authors(:david).taggings_2.sort_by(&:id)
end
def test_has_many_through_polymorphic_has_many
diff --git a/activerecord/test/cases/attribute_set_test.rb b/activerecord/test/cases/attribute_set_test.rb
index 006372a757..8be77ed88f 100644
--- a/activerecord/test/cases/attribute_set_test.rb
+++ b/activerecord/test/cases/attribute_set_test.rb
@@ -16,7 +16,7 @@ module ActiveRecord
test "building with custom types" do
builder = AttributeSet::Builder.new(foo: Type::Float.new)
- attributes = builder.build_from_database({ foo: "3.3", bar: "4.4" }, bar: Type::Integer.new)
+ attributes = builder.build_from_database({ foo: "3.3", bar: "4.4" }, { bar: Type::Integer.new })
assert_equal 3.3, attributes[:foo].value
assert_equal 4, attributes[:bar].value
diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb
index 1d33564989..1a1d4ce039 100644
--- a/activerecord/test/cases/base_test.rb
+++ b/activerecord/test/cases/base_test.rb
@@ -885,10 +885,17 @@ class BasicsTest < ActiveRecord::TestCase
def test_bignum
company = Company.find(1)
- company.rating = 2147483647
+ company.rating = 2147483648
company.save
company = Company.find(1)
- assert_equal 2147483647, company.rating
+ assert_equal 2147483648, company.rating
+ end
+
+ unless current_adapter?(:SQLite3Adapter)
+ def test_bignum_pk
+ company = Company.create!(id: 2147483648, name: "foo")
+ assert_equal company, Company.find(company.id)
+ end
end
# TODO: extend defaults tests to other databases!
diff --git a/activerecord/test/cases/dirty_test.rb b/activerecord/test/cases/dirty_test.rb
index fb3d691d51..a602f83d8c 100644
--- a/activerecord/test/cases/dirty_test.rb
+++ b/activerecord/test/cases/dirty_test.rb
@@ -466,11 +466,10 @@ class DirtyTest < ActiveRecord::TestCase
def test_save_should_not_save_serialized_attribute_with_partial_writes_if_not_present
with_partial_writes(Topic) do
- Topic.create!(author_name: "Bill", content: { a: "a" })
- topic = Topic.select("id, author_name").first
+ topic = Topic.create!(author_name: "Bill", content: { a: "a" })
+ topic = Topic.select("id, author_name").find(topic.id)
topic.update_columns author_name: "John"
- topic = Topic.first
- assert_not_nil topic.content
+ assert_not_nil topic.reload.content
end
end
diff --git a/activerecord/test/cases/enum_test.rb b/activerecord/test/cases/enum_test.rb
index abbc011b34..68a1190d68 100644
--- a/activerecord/test/cases/enum_test.rb
+++ b/activerecord/test/cases/enum_test.rb
@@ -255,12 +255,14 @@ class EnumTest < ActiveRecord::TestCase
assert Book.illustrator_visibility_invisible.create.illustrator_visibility_invisible?
end
- test "_before_type_cast returns the enum label (required for form fields)" do
- if @book.status_came_from_user?
- assert_equal "published", @book.status_before_type_cast
- else
- assert_equal "published", @book.status
- end
+ test "_before_type_cast" do
+ assert_equal 2, @book.status_before_type_cast
+ assert_equal "published", @book.status
+
+ @book.status = "published"
+
+ assert_equal "published", @book.status_before_type_cast
+ assert_equal "published", @book.status
end
test "reserved enum names" do
diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb
index a0d87dfb90..55496147c1 100644
--- a/activerecord/test/cases/finder_test.rb
+++ b/activerecord/test/cases/finder_test.rb
@@ -1153,7 +1153,7 @@ class FinderTest < ActiveRecord::TestCase
e = assert_raises(ActiveRecord::RecordNotFound) do
model.find "Hello", "World!"
end
- assert_equal "Couldn't find all MercedesCars with 'name': (Hello, World!) (found 0 results, but was looking for 2)", e.message
+ assert_equal "Couldn't find all MercedesCars with 'name': (Hello, World!) (found 0 results, but was looking for 2).", e.message
end
end
diff --git a/activerecord/test/cases/fixtures_test.rb b/activerecord/test/cases/fixtures_test.rb
index 6b014bcb3d..b0b63f5203 100644
--- a/activerecord/test/cases/fixtures_test.rb
+++ b/activerecord/test/cases/fixtures_test.rb
@@ -132,7 +132,7 @@ class FixturesTest < ActiveRecord::TestCase
def test_no_args_record_returns_all_without_array
all_binaries = binaries
assert_kind_of(Array, all_binaries)
- assert_equal 1, binaries.length
+ assert_equal 2, binaries.length
end
def test_nil_raises
@@ -313,6 +313,7 @@ class FixturesTest < ActiveRecord::TestCase
data.force_encoding("ASCII-8BIT")
data.freeze
assert_equal data, @flowers.data
+ assert_equal data, @binary_helper.data
end
def test_serialized_fixtures
diff --git a/activerecord/test/cases/inheritance_test.rb b/activerecord/test/cases/inheritance_test.rb
index a263106f6d..c931f7d21c 100644
--- a/activerecord/test/cases/inheritance_test.rb
+++ b/activerecord/test/cases/inheritance_test.rb
@@ -147,12 +147,16 @@ class InheritanceTest < ActiveRecord::TestCase
# Concrete subclass of AR::Base.
assert Post.descends_from_active_record?
+ # Concrete subclasses of a concrete class which has a type column.
+ assert !StiPost.descends_from_active_record?
+ assert !SubStiPost.descends_from_active_record?
+
# Abstract subclass of a concrete class which has a type column.
# This is pathological, as you'll never have Sub < Abstract < Concrete.
- assert !StiPost.descends_from_active_record?
+ assert !AbstractStiPost.descends_from_active_record?
- # Concrete subclasses an abstract class which has a type column.
- assert !SubStiPost.descends_from_active_record?
+ # Concrete subclass of an abstract class which has a type column.
+ assert !SubAbstractStiPost.descends_from_active_record?
end
def test_company_descends_from_active_record
@@ -172,7 +176,8 @@ class InheritanceTest < ActiveRecord::TestCase
assert_equal Post, Post.base_class
assert_equal Post, SpecialPost.base_class
assert_equal Post, StiPost.base_class
- assert_equal SubStiPost, SubStiPost.base_class
+ assert_equal Post, SubStiPost.base_class
+ assert_equal SubAbstractStiPost, SubAbstractStiPost.base_class
end
def test_abstract_inheritance_base_class
diff --git a/activerecord/test/cases/json_shared_test_cases.rb b/activerecord/test/cases/json_shared_test_cases.rb
index f708acf0aa..56ec8c8a82 100644
--- a/activerecord/test/cases/json_shared_test_cases.rb
+++ b/activerecord/test/cases/json_shared_test_cases.rb
@@ -216,6 +216,39 @@ module JSONSharedTestCases
assert_equal true, json.payload
end
+ def test_not_compatible_with_serialize_json
+ new_klass = Class.new(klass) do
+ serialize :payload, JSON
+ end
+ assert_raises(ActiveRecord::AttributeMethods::Serialization::ColumnNotSerializableError) do
+ new_klass.new
+ end
+ end
+
+ class MySettings
+ def initialize(hash); @hash = hash end
+ def to_hash; @hash end
+ def self.load(hash); new(hash) end
+ def self.dump(object); object.to_hash end
+ end
+
+ def test_json_with_serialized_attributes
+ new_klass = Class.new(klass) do
+ serialize :settings, MySettings
+ end
+
+ new_klass.create!(settings: MySettings.new("one" => "two"))
+ record = new_klass.first
+
+ assert_instance_of MySettings, record.settings
+ assert_equal({ "one" => "two" }, record.settings.to_hash)
+
+ record.settings = MySettings.new("three" => "four")
+ record.save!
+
+ assert_equal({ "three" => "four" }, record.reload.settings.to_hash)
+ end
+
private
def klass
JsonDataType
diff --git a/activerecord/test/cases/relation/where_test.rb b/activerecord/test/cases/relation/where_test.rb
index c45fd38bc9..2fe2a67b1c 100644
--- a/activerecord/test/cases/relation/where_test.rb
+++ b/activerecord/test/cases/relation/where_test.rb
@@ -295,6 +295,20 @@ module ActiveRecord
assert_equal essays(:david_modest_proposal), essay
end
+ def test_where_with_relation_on_has_many_association
+ essay = essays(:david_modest_proposal)
+ author = Author.where(essays: Essay.where(id: essay.id)).first
+
+ assert_equal authors(:david), author
+ end
+
+ def test_where_with_relation_on_has_one_association
+ author = authors(:david)
+ author_address = AuthorAddress.where(author: Author.where(id: author.id)).first
+ assert_equal author_addresses(:david_address), author_address
+ end
+
+
def test_where_on_association_with_select_relation
essay = Essay.where(author: Author.where(name: "David").select(:name)).take
assert_equal essays(:david_modest_proposal), essay
diff --git a/activerecord/test/cases/scoping/named_scoping_test.rb b/activerecord/test/cases/scoping/named_scoping_test.rb
index 1db7432e34..b0431a4e34 100644
--- a/activerecord/test/cases/scoping/named_scoping_test.rb
+++ b/activerecord/test/cases/scoping/named_scoping_test.rb
@@ -117,7 +117,8 @@ class NamedScopingTest < ActiveRecord::TestCase
assert_not_equal Post.containing_the_letter_a, authors(:david).posts
assert !Post.containing_the_letter_a.empty?
- assert_equal authors(:david).posts & Post.containing_the_letter_a, authors(:david).posts.containing_the_letter_a
+ expected = authors(:david).posts & Post.containing_the_letter_a
+ assert_equal expected.sort_by(&:id), authors(:david).posts.containing_the_letter_a.sort_by(&:id)
end
def test_scope_with_STI
@@ -129,7 +130,8 @@ class NamedScopingTest < ActiveRecord::TestCase
assert_not_equal Comment.containing_the_letter_e, authors(:david).comments
assert !Comment.containing_the_letter_e.empty?
- assert_equal authors(:david).comments & Comment.containing_the_letter_e, authors(:david).comments.containing_the_letter_e
+ expected = authors(:david).comments & Comment.containing_the_letter_e
+ assert_equal expected.sort_by(&:id), authors(:david).comments.containing_the_letter_e.sort_by(&:id)
end
def test_scopes_honor_current_scopes_from_when_defined
diff --git a/activerecord/test/cases/validations/uniqueness_validation_test.rb b/activerecord/test/cases/validations/uniqueness_validation_test.rb
index fad55916c7..a10567f066 100644
--- a/activerecord/test/cases/validations/uniqueness_validation_test.rb
+++ b/activerecord/test/cases/validations/uniqueness_validation_test.rb
@@ -156,6 +156,13 @@ class UniquenessValidationTest < ActiveRecord::TestCase
assert r3.valid?, "Saving r3"
end
+ def test_validate_uniqueness_with_scope_invalid_syntax
+ error = assert_raises(ArgumentError) do
+ Reply.validates_uniqueness_of(:content, scope: { parent_id: false })
+ end
+ assert_match(/Pass a symbol or an array of symbols instead/, error.to_s)
+ end
+
def test_validate_uniqueness_with_object_scope
Reply.validates_uniqueness_of(:content, scope: :topic)
diff --git a/activerecord/test/fixtures/binaries.yml b/activerecord/test/fixtures/binaries.yml
index ec8f2facdc..53b7883369 100644
--- a/activerecord/test/fixtures/binaries.yml
+++ b/activerecord/test/fixtures/binaries.yml
@@ -131,3 +131,7 @@ flowers:
SgCUASgCUASgCUASgAC74PbXOTvE5/En7jpSoLE8/wBn7uPJjKyj46T9D/NT
pKsXyQzxNpdNP0/akB5484WkMKh4RfXG4UafNmH7b0UxWMrb7Nxg6rl9Z/Im
w+vWq0iscQwxQroiUIvkKsRZQBKAJQBKAJQB/9k=
+
+binary_helper:
+ id: 2
+ data: <%= binary(ASSETS_ROOT + "/flowers.jpg") %>
diff --git a/activerecord/test/models/post.rb b/activerecord/test/models/post.rb
index 4b3576fce8..4c8e847354 100644
--- a/activerecord/test/models/post.rb
+++ b/activerecord/test/models/post.rb
@@ -184,14 +184,19 @@ end
class SpecialPost < Post; end
class StiPost < Post
- self.abstract_class = true
has_one :special_comment, class_name: "SpecialComment"
end
+class AbstractStiPost < Post
+ self.abstract_class = true
+end
+
class SubStiPost < StiPost
self.table_name = Post.table_name
end
+class SubAbstractStiPost < AbstractStiPost; end
+
class FirstPost < ActiveRecord::Base
self.inheritance_column = :disabled
self.table_name = "posts"
diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb
index 05420a4240..8f872c38ba 100644
--- a/activerecord/test/schema/schema.rb
+++ b/activerecord/test/schema/schema.rb
@@ -9,7 +9,7 @@ ActiveRecord::Schema.define do
# ------------------------------------------------------------------- #
create_table :accounts, force: true do |t|
- t.integer :firm_id
+ t.references :firm, index: false
t.string :firm_name
t.integer :credit_limit
end
@@ -192,15 +192,16 @@ ActiveRecord::Schema.define do
t.string :resource_type
t.integer :developer_id
t.datetime :deleted_at
+ t.integer :comments
end
create_table :companies, force: true do |t|
t.string :type
- t.integer :firm_id
+ t.references :firm, index: false
t.string :firm_name
t.string :name
- t.integer :client_of
- t.integer :rating, default: 1
+ t.bigint :client_of
+ t.bigint :rating, default: 1
t.integer :account_id
t.string :description, default: ""
t.index [:firm_id, :type, :rating], name: "company_index", length: { type: 10 }, order: { rating: :desc }
@@ -235,8 +236,8 @@ ActiveRecord::Schema.define do
end
create_table :contracts, force: true do |t|
- t.integer :developer_id
- t.integer :company_id
+ t.references :developer, index: false
+ t.references :company, index: false
end
create_table :customers, force: true do |t|
@@ -262,7 +263,7 @@ ActiveRecord::Schema.define do
t.string :name
t.string :first_name
t.integer :salary, default: 70000
- t.integer :firm_id
+ t.references :firm, index: false
t.integer :mentor_id
if subsecond_precision_supported?
t.datetime :created_at, precision: 6
@@ -719,7 +720,7 @@ ActiveRecord::Schema.define do
create_table :projects, force: true do |t|
t.string :name
t.string :type
- t.integer :firm_id
+ t.references :firm, index: false
t.integer :mentor_id
end
@@ -808,8 +809,7 @@ ActiveRecord::Schema.define do
create_table :sponsors, force: true do |t|
t.integer :club_id
- t.integer :sponsorable_id
- t.string :sponsorable_type
+ t.references :sponsorable, polymorphic: true, index: false
end
create_table :string_key_objects, id: false, force: true do |t|
diff --git a/activestorage/.babelrc b/activestorage/.babelrc
new file mode 100644
index 0000000000..a8211d329f
--- /dev/null
+++ b/activestorage/.babelrc
@@ -0,0 +1,5 @@
+{
+ "presets": [
+ ["env", { "modules": false } ]
+ ]
+}
diff --git a/activestorage/.codeclimate.yml b/activestorage/.codeclimate.yml
new file mode 100644
index 0000000000..18f3dbb7b5
--- /dev/null
+++ b/activestorage/.codeclimate.yml
@@ -0,0 +1,7 @@
+engines:
+ rubocop:
+ enabled: true
+
+ratings:
+ paths:
+ - "**.rb"
diff --git a/activestorage/.eslintrc b/activestorage/.eslintrc
new file mode 100644
index 0000000000..3d9ecd4bce
--- /dev/null
+++ b/activestorage/.eslintrc
@@ -0,0 +1,19 @@
+{
+ "extends": "eslint:recommended",
+ "rules": {
+ "semi": ["error", "never"],
+ "quotes": ["error", "double"],
+ "no-unused-vars": ["error", { "vars": "all", "args": "none" }]
+ },
+ "plugins": [
+ "import"
+ ],
+ "env": {
+ "browser": true,
+ "es6": true
+ },
+ "parserOptions": {
+ "ecmaVersion": 6,
+ "sourceType": "module"
+ }
+}
diff --git a/activestorage/.gitignore b/activestorage/.gitignore
new file mode 100644
index 0000000000..a532335bdd
--- /dev/null
+++ b/activestorage/.gitignore
@@ -0,0 +1,6 @@
+.byebug_history
+node_modules
+test/dummy/db/*.sqlite3
+test/dummy/db/*.sqlite3-journal
+test/dummy/log/*.log
+test/dummy/tmp/
diff --git a/activestorage/CHANGELOG.md b/activestorage/CHANGELOG.md
new file mode 100644
index 0000000000..358552313f
--- /dev/null
+++ b/activestorage/CHANGELOG.md
@@ -0,0 +1,3 @@
+* Added to Rails.
+
+ *DHH*
diff --git a/activestorage/MIT-LICENSE b/activestorage/MIT-LICENSE
new file mode 100644
index 0000000000..4e1c6cad79
--- /dev/null
+++ b/activestorage/MIT-LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2017 David Heinemeier Hansson, Basecamp
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/activestorage/README.md b/activestorage/README.md
new file mode 100644
index 0000000000..17d98978bb
--- /dev/null
+++ b/activestorage/README.md
@@ -0,0 +1,141 @@
+# Active Storage
+
+Active Storage makes it simple to upload and reference files in cloud services like Amazon S3, Google Cloud Storage, or Microsoft Azure Storage, and attach those files to Active Records. Supports having one main service and mirrors in other services for redundancy. It also provides a disk service for testing or local deployments, but the focus is on cloud storage.
+
+Files can be uploaded from the server to the cloud or directly from the client to the cloud.
+
+Image files can furthermore be transformed using on-demand variants for quality, aspect ratio, size, or any other [MiniMagick](https://github.com/minimagick/minimagick) supported transformation.
+
+## Compared to other storage solutions
+
+A key difference to how Active Storage works compared to other attachment solutions in Rails is through the use of built-in [Blob](https://github.com/rails/rails/blob/master/activestorage/app/models/active_storage/blob.rb) and [Attachment](https://github.com/rails/rails/blob/master/activestorage/app/models/active_storage/attachment.rb) models (backed by Active Record). This means existing application models do not need to be modified with additional columns to associate with files. Active Storage uses polymorphic associations via the `Attachment` join model, which then connects to the actual `Blob`.
+
+`Blob` models store attachment metadata (filename, content-type, etc.), and their identifier key in the storage service. Blob models do not store the actual binary data. They are intended to be immutable in spirit. One file, one blob. You can associate the same blob with multiple application models as well. And if you want to do transformations of a given `Blob`, the idea is that you'll simply create a new one, rather than attempt to mutate the existing one (though of course you can delete the previous version later if you don't need it).
+
+## Examples
+
+One attachment:
+
+```ruby
+class User < ApplicationRecord
+ # Associates an attachment and a blob. When the user is destroyed they are
+ # purged by default (models destroyed, and resource files deleted).
+ has_one_attached :avatar
+end
+
+# Attach an avatar to the user.
+user.avatar.attach(io: File.open("~/face.jpg"), filename: "avatar.jpg", content_type: "image/jpg")
+
+# Does the user have an avatar?
+user.avatar.attached? # => true
+
+# Synchronously destroy the avatar and actual resource files.
+user.avatar.purge
+
+# Destroy the associated models and actual resource files async, via Active Job.
+user.avatar.purge_later
+
+# Does the user have an avatar?
+user.avatar.attached? # => false
+
+# Generate a permanent URL for the blob that points to the application.
+# Upon access, a redirect to the actual service endpoint is returned.
+# This indirection decouples the public URL from the actual one, and
+# allows for example mirroring attachments in different services for
+# high-availability. The redirection has an HTTP expiration of 5 min.
+url_for(user.avatar)
+
+class AvatarsController < ApplicationController
+ def update
+ # params[:avatar] contains a ActionDispatch::Http::UploadedFile object
+ Current.user.avatar.attach(params.require(:avatar))
+ redirect_to Current.user
+ end
+end
+```
+
+Many attachments:
+
+```ruby
+class Message < ApplicationRecord
+ has_many_attached :images
+end
+```
+
+```erb
+<%= form_with model: @message do |form| %>
+ <%= form.text_field :title, placeholder: "Title" %><br>
+ <%= form.text_area :content %><br><br>
+
+ <%= form.file_field :images, multiple: true %><br>
+ <%= form.submit %>
+<% end %>
+```
+
+```ruby
+class MessagesController < ApplicationController
+ def index
+ # Use the built-in with_attached_images scope to avoid N+1
+ @messages = Message.all.with_attached_images
+ end
+
+ def create
+ message = Message.create! params.require(:message).permit(:title, :content)
+ message.images.attach(params[:message][:images])
+ redirect_to message
+ end
+
+ def show
+ @message = Message.find(params[:id])
+ end
+end
+```
+
+Variation of image attachment:
+
+```erb
+<%# Hitting the variant URL will lazy transform the original blob and then redirect to its new service location %>
+<%= image_tag user.avatar.variant(resize: "100x100") %>
+```
+
+## Direct uploads
+
+Active Storage, with its included JavaScript library, supports uploading directly from the client to the cloud.
+
+### Direct upload installation
+
+1. Include `activestorage.js` in your application's JavaScript bundle.
+
+ Using the asset pipeline:
+ ```js
+ //= require activestorage
+ ```
+ Using the npm package:
+ ```js
+ import * as ActiveStorage from "activestorage"
+ ActiveStorage.start()
+ ```
+2. Annotate file inputs with the direct upload URL.
+
+ ```ruby
+ <%= form.file_field :attachments, multiple: true, direct_upload: true %>
+ ```
+3. That's it! Uploads begin upon form submission.
+
+### Direct upload JavaScript events
+
+| Event name | Event target | Event data (`event.detail`) | Description |
+| --- | --- | --- | --- |
+| `direct-uploads:start` | `<form>` | None | A form containing files for direct upload fields was submitted. |
+| `direct-upload:initialize` | `<input>` | `{id, file}` | Dispatched for every file after form submission. |
+| `direct-upload:start` | `<input>` | `{id, file}` | A direct upload is starting. |
+| `direct-upload:before-blob-request` | `<input>` | `{id, file, xhr}` | Before making a request to your application for direct upload metadata. |
+| `direct-upload:before-storage-request` | `<input>` | `{id, file, xhr}` | Before making a request to store a file. |
+| `direct-upload:progress` | `<input>` | `{id, file, progress}` | As requests to store files progress. |
+| `direct-upload:error` | `<input>` | `{id, file, error}` | An error occurred. An `alert` will display unless this event is canceled. |
+| `direct-upload:end` | `<input>` | `{id, file}` | A direct upload has ended. |
+| `direct-uploads:end` | `<form>` | None | All direct uploads have ended. |
+
+## License
+
+Active Storage is released under the [MIT License](https://opensource.org/licenses/MIT).
diff --git a/activestorage/Rakefile b/activestorage/Rakefile
new file mode 100644
index 0000000000..aa71a65f6e
--- /dev/null
+++ b/activestorage/Rakefile
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+require "bundler/setup"
+require "bundler/gem_tasks"
+require "rake/testtask"
+
+Rake::TestTask.new do |test|
+ test.libs << "app/controllers"
+ test.libs << "test"
+ test.test_files = FileList["test/**/*_test.rb"]
+ test.warning = false
+end
+
+task default: :test
diff --git a/activestorage/activestorage.gemspec b/activestorage/activestorage.gemspec
new file mode 100644
index 0000000000..911e1a0469
--- /dev/null
+++ b/activestorage/activestorage.gemspec
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+
+version = File.read(File.expand_path("../RAILS_VERSION", __dir__)).strip
+
+Gem::Specification.new do |s|
+ s.platform = Gem::Platform::RUBY
+ s.name = "activestorage"
+ s.version = version
+ s.summary = "Local and cloud file storage framework."
+ s.description = "Attach cloud and local files in Rails applications."
+
+ s.required_ruby_version = ">= 2.2.2"
+
+ s.license = "MIT"
+
+ s.author = "David Heinemeier Hansson"
+ s.email = "david@loudthinking.com"
+ s.homepage = "http://rubyonrails.org"
+
+ s.files = Dir["CHANGELOG.md", "MIT-LICENSE", "README.md", "lib/**/*", "app/**/*", "config/**/*"]
+ s.require_path = "lib"
+
+ s.metadata = {
+ "source_code_uri" => "https://github.com/rails/rails/tree/v#{version}/activestorage",
+ "changelog_uri" => "https://github.com/rails/rails/blob/v#{version}/activestorage/CHANGELOG.md"
+ }
+
+ s.add_dependency "actionpack", version
+ s.add_dependency "activerecord", version
+end
diff --git a/activestorage/app/assets/javascripts/activestorage.js b/activestorage/app/assets/javascripts/activestorage.js
new file mode 100644
index 0000000000..33dc5cdc58
--- /dev/null
+++ b/activestorage/app/assets/javascripts/activestorage.js
@@ -0,0 +1 @@
+!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.ActiveStorage=e():t.ActiveStorage=e()}(this,function(){return function(t){function e(n){if(r[n])return r[n].exports;var i=r[n]={i:n,l:!1,exports:{}};return t[n].call(i.exports,i,i.exports,e),i.l=!0,i.exports}var r={};return e.m=t,e.c=r,e.d=function(t,r,n){e.o(t,r)||Object.defineProperty(t,r,{configurable:!1,enumerable:!0,get:n})},e.n=function(t){var r=t&&t.__esModule?function(){return t.default}:function(){return t};return e.d(r,"a",r),r},e.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},e.p="",e(e.s=2)}([function(t,e,r){"use strict";function n(t){var e=a(document.head,'meta[name="'+t+'"]');if(e)return e.getAttribute("content")}function i(t,e){return"string"==typeof t&&(e=t,t=document),o(t.querySelectorAll(e))}function a(t,e){return"string"==typeof t&&(e=t,t=document),t.querySelector(e)}function u(t,e){var r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},n=r.bubbles,i=r.cancelable,a=r.detail,u=document.createEvent("Event");return u.initEvent(e,n||!0,i||!0),u.detail=a||{},t.dispatchEvent(u),u}function o(t){return Array.isArray(t)?t:Array.from?Array.from(t):[].slice.call(t)}e.d=n,e.c=i,e.b=a,e.a=u,e.e=o},function(t,e,r){"use strict";function n(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function i(t,e){if(t&&"function"==typeof t[e]){for(var r=arguments.length,n=Array(r>2?r-2:0),i=2;i<r;i++)n[i-2]=arguments[i];return t[e].apply(t,n)}}r.d(e,"a",function(){return c});var a=r(6),u=r(8),o=r(9),s=function(){function t(t,e){for(var r=0;r<e.length;r++){var n=e[r];n.enumerable=n.enumerable||!1,n.configurable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(t,n.key,n)}}return function(e,r,n){return r&&t(e.prototype,r),n&&t(e,n),e}}(),f=0,c=function(){function t(e,r,i){n(this,t),this.id=++f,this.file=e,this.url=r,this.delegate=i}return s(t,[{key:"create",value:function(t){var e=this;a.a.create(this.file,function(r,n){var a=new u.a(e.file,n,e.url);i(e.delegate,"directUploadWillCreateBlobWithXHR",a.xhr),a.create(function(r){if(r)t(r);else{var n=new o.a(a);i(e.delegate,"directUploadWillStoreFileWithXHR",n.xhr),n.create(function(e){e?t(e):t(null,a.toJSON())})}})})}}]),t}()},function(t,e,r){"use strict";function n(){window.ActiveStorage&&Object(i.a)()}Object.defineProperty(e,"__esModule",{value:!0});var i=r(3),a=r(1);r.d(e,"start",function(){return i.a}),r.d(e,"DirectUpload",function(){return a.a}),setTimeout(n,1)},function(t,e,r){"use strict";function n(){d||(d=!0,document.addEventListener("submit",i),document.addEventListener("ajax:before",a))}function i(t){u(t)}function a(t){"FORM"==t.target.tagName&&u(t)}function u(t){var e=t.target;if(e.hasAttribute(l))return void t.preventDefault();var r=new c.a(e),n=r.inputs;n.length&&(t.preventDefault(),e.setAttribute(l,""),n.forEach(s),r.start(function(t){e.removeAttribute(l),t?n.forEach(f):o(e)}))}function o(t){var e=Object(h.b)(t,"input[type=submit]");if(e){var r=e,n=r.disabled;e.disabled=!1,e.click(),e.disabled=n}else e=document.createElement("input"),e.type="submit",e.style="display:none",t.appendChild(e),e.click(),t.removeChild(e)}function s(t){t.disabled=!0}function f(t){t.disabled=!1}e.a=n;var c=r(4),h=r(0),l="data-direct-uploads-processing",d=!1},function(t,e,r){"use strict";function n(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}r.d(e,"a",function(){return s});var i=r(5),a=r(0),u=function(){function t(t,e){for(var r=0;r<e.length;r++){var n=e[r];n.enumerable=n.enumerable||!1,n.configurable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(t,n.key,n)}}return function(e,r,n){return r&&t(e.prototype,r),n&&t(e,n),e}}(),o="input[type=file][data-direct-upload-url]:not([disabled])",s=function(){function t(e){n(this,t),this.form=e,this.inputs=Object(a.c)(e,o).filter(function(t){return t.files.length})}return u(t,[{key:"start",value:function(t){var e=this,r=this.createDirectUploadControllers();this.dispatch("start"),function n(){var i=r.shift();i?i.start(function(r){r?(t(r),e.dispatch("end")):n()}):(t(),e.dispatch("end"))}()}},{key:"createDirectUploadControllers",value:function(){var t=[];return this.inputs.forEach(function(e){Object(a.e)(e.files).forEach(function(r){var n=new i.a(e,r);t.push(n)})}),t}},{key:"dispatch",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return Object(a.a)(this.form,"direct-uploads:"+t,{detail:e})}}]),t}()},function(t,e,r){"use strict";function n(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}r.d(e,"a",function(){return o});var i=r(1),a=r(0),u=function(){function t(t,e){for(var r=0;r<e.length;r++){var n=e[r];n.enumerable=n.enumerable||!1,n.configurable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(t,n.key,n)}}return function(e,r,n){return r&&t(e.prototype,r),n&&t(e,n),e}}(),o=function(){function t(e,r){n(this,t),this.input=e,this.file=r,this.directUpload=new i.a(this.file,this.url,this),this.dispatch("initialize")}return u(t,[{key:"start",value:function(t){var e=this,r=document.createElement("input");r.type="hidden",r.name=this.input.name,this.input.insertAdjacentElement("beforebegin",r),this.dispatch("start"),this.directUpload.create(function(n,i){n?(r.parentNode.removeChild(r),e.dispatchError(n)):r.value=i.signed_id,e.dispatch("end"),t(n)})}},{key:"uploadRequestDidProgress",value:function(t){var e=t.loaded/t.total*100;e&&this.dispatch("progress",{progress:e})}},{key:"dispatch",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return e.file=this.file,e.id=this.directUpload.id,Object(a.a)(this.input,"direct-upload:"+t,{detail:e})}},{key:"dispatchError",value:function(t){this.dispatch("error",{error:t}).defaultPrevented||alert(t)}},{key:"directUploadWillCreateBlobWithXHR",value:function(t){this.dispatch("before-blob-request",{xhr:t})}},{key:"directUploadWillStoreFileWithXHR",value:function(t){var e=this;this.dispatch("before-storage-request",{xhr:t}),t.upload.addEventListener("progress",function(t){return e.uploadRequestDidProgress(t)})}},{key:"url",get:function(){return this.input.getAttribute("data-direct-upload-url")}}]),t}()},function(t,e,r){"use strict";function n(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}r.d(e,"a",function(){return s});var i=r(7),a=r.n(i),u=function(){function t(t,e){for(var r=0;r<e.length;r++){var n=e[r];n.enumerable=n.enumerable||!1,n.configurable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(t,n.key,n)}}return function(e,r,n){return r&&t(e.prototype,r),n&&t(e,n),e}}(),o=File.prototype.slice||File.prototype.mozSlice||File.prototype.webkitSlice,s=function(){function t(e){n(this,t),this.file=e,this.chunkSize=2097152,this.chunkCount=Math.ceil(this.file.size/this.chunkSize),this.chunkIndex=0}return u(t,null,[{key:"create",value:function(e,r){new t(e).create(r)}}]),u(t,[{key:"create",value:function(t){var e=this;this.callback=t,this.md5Buffer=new a.a.ArrayBuffer,this.fileReader=new FileReader,this.fileReader.addEventListener("load",function(t){return e.fileReaderDidLoad(t)}),this.fileReader.addEventListener("error",function(t){return e.fileReaderDidError(t)}),this.readNextChunk()}},{key:"fileReaderDidLoad",value:function(t){if(this.md5Buffer.append(t.target.result),!this.readNextChunk()){var e=this.md5Buffer.end(!0),r=btoa(e);this.callback(null,r)}}},{key:"fileReaderDidError",value:function(t){this.callback("Error reading "+this.file.name)}},{key:"readNextChunk",value:function(){if(this.chunkIndex<this.chunkCount){var t=this.chunkIndex*this.chunkSize,e=Math.min(t+this.chunkSize,this.file.size),r=o.call(this.file,t,e);return this.fileReader.readAsArrayBuffer(r),this.chunkIndex++,!0}return!1}}]),t}()},function(t,e,r){!function(e){t.exports=e()}(function(t){"use strict";function e(t,e){var r=t[0],n=t[1],i=t[2],a=t[3];r+=(n&i|~n&a)+e[0]-680876936|0,r=(r<<7|r>>>25)+n|0,a+=(r&n|~r&i)+e[1]-389564586|0,a=(a<<12|a>>>20)+r|0,i+=(a&r|~a&n)+e[2]+606105819|0,i=(i<<17|i>>>15)+a|0,n+=(i&a|~i&r)+e[3]-1044525330|0,n=(n<<22|n>>>10)+i|0,r+=(n&i|~n&a)+e[4]-176418897|0,r=(r<<7|r>>>25)+n|0,a+=(r&n|~r&i)+e[5]+1200080426|0,a=(a<<12|a>>>20)+r|0,i+=(a&r|~a&n)+e[6]-1473231341|0,i=(i<<17|i>>>15)+a|0,n+=(i&a|~i&r)+e[7]-45705983|0,n=(n<<22|n>>>10)+i|0,r+=(n&i|~n&a)+e[8]+1770035416|0,r=(r<<7|r>>>25)+n|0,a+=(r&n|~r&i)+e[9]-1958414417|0,a=(a<<12|a>>>20)+r|0,i+=(a&r|~a&n)+e[10]-42063|0,i=(i<<17|i>>>15)+a|0,n+=(i&a|~i&r)+e[11]-1990404162|0,n=(n<<22|n>>>10)+i|0,r+=(n&i|~n&a)+e[12]+1804603682|0,r=(r<<7|r>>>25)+n|0,a+=(r&n|~r&i)+e[13]-40341101|0,a=(a<<12|a>>>20)+r|0,i+=(a&r|~a&n)+e[14]-1502002290|0,i=(i<<17|i>>>15)+a|0,n+=(i&a|~i&r)+e[15]+1236535329|0,n=(n<<22|n>>>10)+i|0,r+=(n&a|i&~a)+e[1]-165796510|0,r=(r<<5|r>>>27)+n|0,a+=(r&i|n&~i)+e[6]-1069501632|0,a=(a<<9|a>>>23)+r|0,i+=(a&n|r&~n)+e[11]+643717713|0,i=(i<<14|i>>>18)+a|0,n+=(i&r|a&~r)+e[0]-373897302|0,n=(n<<20|n>>>12)+i|0,r+=(n&a|i&~a)+e[5]-701558691|0,r=(r<<5|r>>>27)+n|0,a+=(r&i|n&~i)+e[10]+38016083|0,a=(a<<9|a>>>23)+r|0,i+=(a&n|r&~n)+e[15]-660478335|0,i=(i<<14|i>>>18)+a|0,n+=(i&r|a&~r)+e[4]-405537848|0,n=(n<<20|n>>>12)+i|0,r+=(n&a|i&~a)+e[9]+568446438|0,r=(r<<5|r>>>27)+n|0,a+=(r&i|n&~i)+e[14]-1019803690|0,a=(a<<9|a>>>23)+r|0,i+=(a&n|r&~n)+e[3]-187363961|0,i=(i<<14|i>>>18)+a|0,n+=(i&r|a&~r)+e[8]+1163531501|0,n=(n<<20|n>>>12)+i|0,r+=(n&a|i&~a)+e[13]-1444681467|0,r=(r<<5|r>>>27)+n|0,a+=(r&i|n&~i)+e[2]-51403784|0,a=(a<<9|a>>>23)+r|0,i+=(a&n|r&~n)+e[7]+1735328473|0,i=(i<<14|i>>>18)+a|0,n+=(i&r|a&~r)+e[12]-1926607734|0,n=(n<<20|n>>>12)+i|0,r+=(n^i^a)+e[5]-378558|0,r=(r<<4|r>>>28)+n|0,a+=(r^n^i)+e[8]-2022574463|0,a=(a<<11|a>>>21)+r|0,i+=(a^r^n)+e[11]+1839030562|0,i=(i<<16|i>>>16)+a|0,n+=(i^a^r)+e[14]-35309556|0,n=(n<<23|n>>>9)+i|0,r+=(n^i^a)+e[1]-1530992060|0,r=(r<<4|r>>>28)+n|0,a+=(r^n^i)+e[4]+1272893353|0,a=(a<<11|a>>>21)+r|0,i+=(a^r^n)+e[7]-155497632|0,i=(i<<16|i>>>16)+a|0,n+=(i^a^r)+e[10]-1094730640|0,n=(n<<23|n>>>9)+i|0,r+=(n^i^a)+e[13]+681279174|0,r=(r<<4|r>>>28)+n|0,a+=(r^n^i)+e[0]-358537222|0,a=(a<<11|a>>>21)+r|0,i+=(a^r^n)+e[3]-722521979|0,i=(i<<16|i>>>16)+a|0,n+=(i^a^r)+e[6]+76029189|0,n=(n<<23|n>>>9)+i|0,r+=(n^i^a)+e[9]-640364487|0,r=(r<<4|r>>>28)+n|0,a+=(r^n^i)+e[12]-421815835|0,a=(a<<11|a>>>21)+r|0,i+=(a^r^n)+e[15]+530742520|0,i=(i<<16|i>>>16)+a|0,n+=(i^a^r)+e[2]-995338651|0,n=(n<<23|n>>>9)+i|0,r+=(i^(n|~a))+e[0]-198630844|0,r=(r<<6|r>>>26)+n|0,a+=(n^(r|~i))+e[7]+1126891415|0,a=(a<<10|a>>>22)+r|0,i+=(r^(a|~n))+e[14]-1416354905|0,i=(i<<15|i>>>17)+a|0,n+=(a^(i|~r))+e[5]-57434055|0,n=(n<<21|n>>>11)+i|0,r+=(i^(n|~a))+e[12]+1700485571|0,r=(r<<6|r>>>26)+n|0,a+=(n^(r|~i))+e[3]-1894986606|0,a=(a<<10|a>>>22)+r|0,i+=(r^(a|~n))+e[10]-1051523|0,i=(i<<15|i>>>17)+a|0,n+=(a^(i|~r))+e[1]-2054922799|0,n=(n<<21|n>>>11)+i|0,r+=(i^(n|~a))+e[8]+1873313359|0,r=(r<<6|r>>>26)+n|0,a+=(n^(r|~i))+e[15]-30611744|0,a=(a<<10|a>>>22)+r|0,i+=(r^(a|~n))+e[6]-1560198380|0,i=(i<<15|i>>>17)+a|0,n+=(a^(i|~r))+e[13]+1309151649|0,n=(n<<21|n>>>11)+i|0,r+=(i^(n|~a))+e[4]-145523070|0,r=(r<<6|r>>>26)+n|0,a+=(n^(r|~i))+e[11]-1120210379|0,a=(a<<10|a>>>22)+r|0,i+=(r^(a|~n))+e[2]+718787259|0,i=(i<<15|i>>>17)+a|0,n+=(a^(i|~r))+e[9]-343485551|0,n=(n<<21|n>>>11)+i|0,t[0]=r+t[0]|0,t[1]=n+t[1]|0,t[2]=i+t[2]|0,t[3]=a+t[3]|0}function r(t){var e,r=[];for(e=0;e<64;e+=4)r[e>>2]=t.charCodeAt(e)+(t.charCodeAt(e+1)<<8)+(t.charCodeAt(e+2)<<16)+(t.charCodeAt(e+3)<<24);return r}function n(t){var e,r=[];for(e=0;e<64;e+=4)r[e>>2]=t[e]+(t[e+1]<<8)+(t[e+2]<<16)+(t[e+3]<<24);return r}function i(t){var n,i,a,u,o,s,f=t.length,c=[1732584193,-271733879,-1732584194,271733878];for(n=64;n<=f;n+=64)e(c,r(t.substring(n-64,n)));for(t=t.substring(n-64),i=t.length,a=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],n=0;n<i;n+=1)a[n>>2]|=t.charCodeAt(n)<<(n%4<<3);if(a[n>>2]|=128<<(n%4<<3),n>55)for(e(c,a),n=0;n<16;n+=1)a[n]=0;return u=8*f,u=u.toString(16).match(/(.*?)(.{0,8})$/),o=parseInt(u[2],16),s=parseInt(u[1],16)||0,a[14]=o,a[15]=s,e(c,a),c}function a(t){var r,i,a,u,o,s,f=t.length,c=[1732584193,-271733879,-1732584194,271733878];for(r=64;r<=f;r+=64)e(c,n(t.subarray(r-64,r)));for(t=r-64<f?t.subarray(r-64):new Uint8Array(0),i=t.length,a=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],r=0;r<i;r+=1)a[r>>2]|=t[r]<<(r%4<<3);if(a[r>>2]|=128<<(r%4<<3),r>55)for(e(c,a),r=0;r<16;r+=1)a[r]=0;return u=8*f,u=u.toString(16).match(/(.*?)(.{0,8})$/),o=parseInt(u[2],16),s=parseInt(u[1],16)||0,a[14]=o,a[15]=s,e(c,a),c}function u(t){var e,r="";for(e=0;e<4;e+=1)r+=p[t>>8*e+4&15]+p[t>>8*e&15];return r}function o(t){var e;for(e=0;e<t.length;e+=1)t[e]=u(t[e]);return t.join("")}function s(t){return/[\u0080-\uFFFF]/.test(t)&&(t=unescape(encodeURIComponent(t))),t}function f(t,e){var r,n=t.length,i=new ArrayBuffer(n),a=new Uint8Array(i);for(r=0;r<n;r+=1)a[r]=t.charCodeAt(r);return e?a:i}function c(t){return String.fromCharCode.apply(null,new Uint8Array(t))}function h(t,e,r){var n=new Uint8Array(t.byteLength+e.byteLength);return n.set(new Uint8Array(t)),n.set(new Uint8Array(e),t.byteLength),r?n:n.buffer}function l(t){var e,r=[],n=t.length;for(e=0;e<n-1;e+=2)r.push(parseInt(t.substr(e,2),16));return String.fromCharCode.apply(String,r)}function d(){this.reset()}var p=["0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f"];return"5d41402abc4b2a76b9719d911017c592"!==o(i("hello"))&&function(t,e){var r=(65535&t)+(65535&e);return(t>>16)+(e>>16)+(r>>16)<<16|65535&r},"undefined"==typeof ArrayBuffer||ArrayBuffer.prototype.slice||function(){function e(t,e){return t=0|t||0,t<0?Math.max(t+e,0):Math.min(t,e)}ArrayBuffer.prototype.slice=function(r,n){var i,a,u,o,s=this.byteLength,f=e(r,s),c=s;return n!==t&&(c=e(n,s)),f>c?new ArrayBuffer(0):(i=c-f,a=new ArrayBuffer(i),u=new Uint8Array(a),o=new Uint8Array(this,f,i),u.set(o),a)}}(),d.prototype.append=function(t){return this.appendBinary(s(t)),this},d.prototype.appendBinary=function(t){this._buff+=t,this._length+=t.length;var n,i=this._buff.length;for(n=64;n<=i;n+=64)e(this._hash,r(this._buff.substring(n-64,n)));return this._buff=this._buff.substring(n-64),this},d.prototype.end=function(t){var e,r,n=this._buff,i=n.length,a=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0];for(e=0;e<i;e+=1)a[e>>2]|=n.charCodeAt(e)<<(e%4<<3);return this._finish(a,i),r=o(this._hash),t&&(r=l(r)),this.reset(),r},d.prototype.reset=function(){return this._buff="",this._length=0,this._hash=[1732584193,-271733879,-1732584194,271733878],this},d.prototype.getState=function(){return{buff:this._buff,length:this._length,hash:this._hash}},d.prototype.setState=function(t){return this._buff=t.buff,this._length=t.length,this._hash=t.hash,this},d.prototype.destroy=function(){delete this._hash,delete this._buff,delete this._length},d.prototype._finish=function(t,r){var n,i,a,u=r;if(t[u>>2]|=128<<(u%4<<3),u>55)for(e(this._hash,t),u=0;u<16;u+=1)t[u]=0;n=8*this._length,n=n.toString(16).match(/(.*?)(.{0,8})$/),i=parseInt(n[2],16),a=parseInt(n[1],16)||0,t[14]=i,t[15]=a,e(this._hash,t)},d.hash=function(t,e){return d.hashBinary(s(t),e)},d.hashBinary=function(t,e){var r=i(t),n=o(r);return e?l(n):n},d.ArrayBuffer=function(){this.reset()},d.ArrayBuffer.prototype.append=function(t){var r,i=h(this._buff.buffer,t,!0),a=i.length;for(this._length+=t.byteLength,r=64;r<=a;r+=64)e(this._hash,n(i.subarray(r-64,r)));return this._buff=r-64<a?new Uint8Array(i.buffer.slice(r-64)):new Uint8Array(0),this},d.ArrayBuffer.prototype.end=function(t){var e,r,n=this._buff,i=n.length,a=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0];for(e=0;e<i;e+=1)a[e>>2]|=n[e]<<(e%4<<3);return this._finish(a,i),r=o(this._hash),t&&(r=l(r)),this.reset(),r},d.ArrayBuffer.prototype.reset=function(){return this._buff=new Uint8Array(0),this._length=0,this._hash=[1732584193,-271733879,-1732584194,271733878],this},d.ArrayBuffer.prototype.getState=function(){var t=d.prototype.getState.call(this);return t.buff=c(t.buff),t},d.ArrayBuffer.prototype.setState=function(t){return t.buff=f(t.buff,!0),d.prototype.setState.call(this,t)},d.ArrayBuffer.prototype.destroy=d.prototype.destroy,d.ArrayBuffer.prototype._finish=d.prototype._finish,d.ArrayBuffer.hash=function(t,e){var r=a(new Uint8Array(t)),n=o(r);return e?l(n):n},d})},function(t,e,r){"use strict";function n(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}r.d(e,"a",function(){return u});var i=r(0),a=function(){function t(t,e){for(var r=0;r<e.length;r++){var n=e[r];n.enumerable=n.enumerable||!1,n.configurable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(t,n.key,n)}}return function(e,r,n){return r&&t(e.prototype,r),n&&t(e,n),e}}(),u=function(){function t(e,r,a){var u=this;n(this,t),this.file=e,this.attributes={filename:e.name,content_type:e.type,byte_size:e.size,checksum:r},this.xhr=new XMLHttpRequest,this.xhr.open("POST",a,!0),this.xhr.responseType="json",this.xhr.setRequestHeader("Content-Type","application/json"),this.xhr.setRequestHeader("Accept","application/json"),this.xhr.setRequestHeader("X-Requested-With","XMLHttpRequest"),this.xhr.setRequestHeader("X-CSRF-Token",Object(i.d)("csrf-token")),this.xhr.addEventListener("load",function(t){return u.requestDidLoad(t)}),this.xhr.addEventListener("error",function(t){return u.requestDidError(t)})}return a(t,[{key:"create",value:function(t){this.callback=t,this.xhr.send(JSON.stringify({blob:this.attributes}))}},{key:"requestDidLoad",value:function(t){var e=this.xhr,r=e.status,n=e.response;if(r>=200&&r<300){var i=n.direct_upload;delete n.direct_upload,this.attributes=n,this.directUploadData=i,this.callback(null,this.toJSON())}else this.requestDidError(t)}},{key:"requestDidError",value:function(t){this.callback('Error creating Blob for "'+this.file.name+'". Status: '+this.xhr.status)}},{key:"toJSON",value:function(){var t={};for(var e in this.attributes)t[e]=this.attributes[e];return t}}]),t}()},function(t,e,r){"use strict";function n(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}r.d(e,"a",function(){return a});var i=function(){function t(t,e){for(var r=0;r<e.length;r++){var n=e[r];n.enumerable=n.enumerable||!1,n.configurable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(t,n.key,n)}}return function(e,r,n){return r&&t(e.prototype,r),n&&t(e,n),e}}(),a=function(){function t(e){var r=this;n(this,t),this.blob=e,this.file=e.file;var i=e.directUploadData,a=i.url,u=i.headers;this.xhr=new XMLHttpRequest,this.xhr.open("PUT",a,!0);for(var o in u)this.xhr.setRequestHeader(o,u[o]);this.xhr.addEventListener("load",function(t){return r.requestDidLoad(t)}),this.xhr.addEventListener("error",function(t){return r.requestDidError(t)})}return i(t,[{key:"create",value:function(t){this.callback=t,this.xhr.send(this.file)}},{key:"requestDidLoad",value:function(t){var e=this.xhr,r=e.status,n=e.response;r>=200&&r<300?this.callback(null,n):this.requestDidError(t)}},{key:"requestDidError",value:function(t){this.callback('Error storing "'+this.file.name+'". Status: '+this.xhr.status)}}]),t}()}])}); \ No newline at end of file
diff --git a/activestorage/app/controllers/active_storage/blobs_controller.rb b/activestorage/app/controllers/active_storage/blobs_controller.rb
new file mode 100644
index 0000000000..00aa8567c8
--- /dev/null
+++ b/activestorage/app/controllers/active_storage/blobs_controller.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+# Take a signed permanent reference for a blob and turn it into an expiring service URL for download.
+# Note: These URLs are publicly accessible. If you need to enforce access protection beyond the
+# security-through-obscurity factor of the signed blob references, you'll need to implement your own
+# authenticated redirection controller.
+class ActiveStorage::BlobsController < ActionController::Base
+ def show
+ if blob = find_signed_blob
+ expires_in 5.minutes # service_url defaults to 5 minutes
+ redirect_to blob.service_url(disposition: disposition_param)
+ else
+ head :not_found
+ end
+ end
+
+ private
+ def find_signed_blob
+ ActiveStorage::Blob.find_signed(params[:signed_id])
+ end
+
+ def disposition_param
+ params[:disposition].presence_in(%w( inline attachment )) || "inline"
+ end
+end
diff --git a/activestorage/app/controllers/active_storage/direct_uploads_controller.rb b/activestorage/app/controllers/active_storage/direct_uploads_controller.rb
new file mode 100644
index 0000000000..205d173648
--- /dev/null
+++ b/activestorage/app/controllers/active_storage/direct_uploads_controller.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+# Creates a new blob on the server side in anticipation of a direct-to-service upload from the client side.
+# When the client-side upload is completed, the signed_blob_id can be submitted as part of the form to reference
+# the blob that was created up front.
+class ActiveStorage::DirectUploadsController < ActionController::Base
+ def create
+ blob = ActiveStorage::Blob.create_before_direct_upload!(blob_args)
+ render json: direct_upload_json(blob)
+ end
+
+ private
+ def blob_args
+ params.require(:blob).permit(:filename, :byte_size, :checksum, :content_type, :metadata).to_h.symbolize_keys
+ end
+
+ def direct_upload_json(blob)
+ blob.as_json(methods: :signed_id).merge(direct_upload: {
+ url: blob.service_url_for_direct_upload,
+ headers: blob.service_headers_for_direct_upload
+ })
+ end
+end
diff --git a/activestorage/app/controllers/active_storage/disk_controller.rb b/activestorage/app/controllers/active_storage/disk_controller.rb
new file mode 100644
index 0000000000..b10d4e2cac
--- /dev/null
+++ b/activestorage/app/controllers/active_storage/disk_controller.rb
@@ -0,0 +1,53 @@
+# frozen_string_literal: true
+
+# Serves files stored with the disk service in the same way that the cloud services do.
+# This means using expiring, signed URLs that are meant for immediate access, not permanent linking.
+# Always go through the BlobsController, or your own authenticated controller, rather than directly
+# to the service url.
+class ActiveStorage::DiskController < ActionController::Base
+ def show
+ if key = decode_verified_key
+ send_data disk_service.download(key),
+ filename: params[:filename], disposition: disposition_param, content_type: params[:content_type]
+ else
+ head :not_found
+ end
+ end
+
+ def update
+ if token = decode_verified_token
+ if acceptable_content?(token)
+ disk_service.upload token[:key], request.body, checksum: token[:checksum]
+ else
+ head :unprocessable_entity
+ end
+ else
+ head :not_found
+ end
+ rescue ActiveStorage::IntegrityError
+ head :unprocessable_entity
+ end
+
+ private
+ def disk_service
+ ActiveStorage::Blob.service
+ end
+
+
+ def decode_verified_key
+ ActiveStorage.verifier.verified(params[:encoded_key], purpose: :blob_key)
+ end
+
+ def disposition_param
+ params[:disposition].presence_in(%w( inline attachment )) || "inline"
+ end
+
+
+ def decode_verified_token
+ ActiveStorage.verifier.verified(params[:encoded_token], purpose: :blob_token)
+ end
+
+ def acceptable_content?(token)
+ token[:content_type] == request.content_type && token[:content_length] == request.content_length
+ end
+end
diff --git a/activestorage/app/controllers/active_storage/variants_controller.rb b/activestorage/app/controllers/active_storage/variants_controller.rb
new file mode 100644
index 0000000000..02e3010626
--- /dev/null
+++ b/activestorage/app/controllers/active_storage/variants_controller.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+# Take a signed permanent reference for a variant and turn it into an expiring service URL for download.
+# Note: These URLs are publicly accessible. If you need to enforce access protection beyond the
+# security-through-obscurity factor of the signed blob and variation reference, you'll need to implement your own
+# authenticated redirection controller.
+class ActiveStorage::VariantsController < ActionController::Base
+ def show
+ if blob = find_signed_blob
+ expires_in 5.minutes # service_url defaults to 5 minutes
+ redirect_to ActiveStorage::Variant.new(blob, decoded_variation).processed.service_url(disposition: disposition_param)
+ else
+ head :not_found
+ end
+ end
+
+ private
+ def find_signed_blob
+ ActiveStorage::Blob.find_signed(params[:signed_blob_id])
+ end
+
+ def decoded_variation
+ ActiveStorage::Variation.decode(params[:variation_key])
+ end
+
+ def disposition_param
+ params[:disposition].presence_in(%w( inline attachment )) || "inline"
+ end
+end
diff --git a/activestorage/app/javascript/activestorage/blob_record.js b/activestorage/app/javascript/activestorage/blob_record.js
new file mode 100644
index 0000000000..3c6e6b6ba1
--- /dev/null
+++ b/activestorage/app/javascript/activestorage/blob_record.js
@@ -0,0 +1,54 @@
+import { getMetaValue } from "./helpers"
+
+export class BlobRecord {
+ constructor(file, checksum, url) {
+ this.file = file
+
+ this.attributes = {
+ filename: file.name,
+ content_type: file.type,
+ byte_size: file.size,
+ checksum: checksum
+ }
+
+ this.xhr = new XMLHttpRequest
+ this.xhr.open("POST", url, true)
+ this.xhr.responseType = "json"
+ this.xhr.setRequestHeader("Content-Type", "application/json")
+ this.xhr.setRequestHeader("Accept", "application/json")
+ this.xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest")
+ this.xhr.setRequestHeader("X-CSRF-Token", getMetaValue("csrf-token"))
+ this.xhr.addEventListener("load", event => this.requestDidLoad(event))
+ this.xhr.addEventListener("error", event => this.requestDidError(event))
+ }
+
+ create(callback) {
+ this.callback = callback
+ this.xhr.send(JSON.stringify({ blob: this.attributes }))
+ }
+
+ requestDidLoad(event) {
+ const { status, response } = this.xhr
+ if (status >= 200 && status < 300) {
+ const { direct_upload } = response
+ delete response.direct_upload
+ this.attributes = response
+ this.directUploadData = direct_upload
+ this.callback(null, this.toJSON())
+ } else {
+ this.requestDidError(event)
+ }
+ }
+
+ requestDidError(event) {
+ this.callback(`Error creating Blob for "${this.file.name}". Status: ${this.xhr.status}`)
+ }
+
+ toJSON() {
+ const result = {}
+ for (const key in this.attributes) {
+ result[key] = this.attributes[key]
+ }
+ return result
+ }
+}
diff --git a/activestorage/app/javascript/activestorage/blob_upload.js b/activestorage/app/javascript/activestorage/blob_upload.js
new file mode 100644
index 0000000000..99bf0c9e30
--- /dev/null
+++ b/activestorage/app/javascript/activestorage/blob_upload.js
@@ -0,0 +1,34 @@
+export class BlobUpload {
+ constructor(blob) {
+ this.blob = blob
+ this.file = blob.file
+
+ const { url, headers } = blob.directUploadData
+
+ this.xhr = new XMLHttpRequest
+ this.xhr.open("PUT", url, true)
+ for (const key in headers) {
+ this.xhr.setRequestHeader(key, headers[key])
+ }
+ this.xhr.addEventListener("load", event => this.requestDidLoad(event))
+ this.xhr.addEventListener("error", event => this.requestDidError(event))
+ }
+
+ create(callback) {
+ this.callback = callback
+ this.xhr.send(this.file)
+ }
+
+ requestDidLoad(event) {
+ const { status, response } = this.xhr
+ if (status >= 200 && status < 300) {
+ this.callback(null, response)
+ } else {
+ this.requestDidError(event)
+ }
+ }
+
+ requestDidError(event) {
+ this.callback(`Error storing "${this.file.name}". Status: ${this.xhr.status}`)
+ }
+}
diff --git a/activestorage/app/javascript/activestorage/direct_upload.js b/activestorage/app/javascript/activestorage/direct_upload.js
new file mode 100644
index 0000000000..7085e0a4ab
--- /dev/null
+++ b/activestorage/app/javascript/activestorage/direct_upload.js
@@ -0,0 +1,42 @@
+import { FileChecksum } from "./file_checksum"
+import { BlobRecord } from "./blob_record"
+import { BlobUpload } from "./blob_upload"
+
+let id = 0
+
+export class DirectUpload {
+ constructor(file, url, delegate) {
+ this.id = ++id
+ this.file = file
+ this.url = url
+ this.delegate = delegate
+ }
+
+ create(callback) {
+ FileChecksum.create(this.file, (error, checksum) => {
+ const blob = new BlobRecord(this.file, checksum, this.url)
+ notify(this.delegate, "directUploadWillCreateBlobWithXHR", blob.xhr)
+ blob.create(error => {
+ if (error) {
+ callback(error)
+ } else {
+ const upload = new BlobUpload(blob)
+ notify(this.delegate, "directUploadWillStoreFileWithXHR", upload.xhr)
+ upload.create(error => {
+ if (error) {
+ callback(error)
+ } else {
+ callback(null, blob.toJSON())
+ }
+ })
+ }
+ })
+ })
+ }
+}
+
+function notify(object, methodName, ...messages) {
+ if (object && typeof object[methodName] == "function") {
+ return object[methodName](...messages)
+ }
+}
diff --git a/activestorage/app/javascript/activestorage/direct_upload_controller.js b/activestorage/app/javascript/activestorage/direct_upload_controller.js
new file mode 100644
index 0000000000..987050889a
--- /dev/null
+++ b/activestorage/app/javascript/activestorage/direct_upload_controller.js
@@ -0,0 +1,67 @@
+import { DirectUpload } from "./direct_upload"
+import { dispatchEvent } from "./helpers"
+
+export class DirectUploadController {
+ constructor(input, file) {
+ this.input = input
+ this.file = file
+ this.directUpload = new DirectUpload(this.file, this.url, this)
+ this.dispatch("initialize")
+ }
+
+ start(callback) {
+ const hiddenInput = document.createElement("input")
+ hiddenInput.type = "hidden"
+ hiddenInput.name = this.input.name
+ this.input.insertAdjacentElement("beforebegin", hiddenInput)
+
+ this.dispatch("start")
+
+ this.directUpload.create((error, attributes) => {
+ if (error) {
+ hiddenInput.parentNode.removeChild(hiddenInput)
+ this.dispatchError(error)
+ } else {
+ hiddenInput.value = attributes.signed_id
+ }
+
+ this.dispatch("end")
+ callback(error)
+ })
+ }
+
+ uploadRequestDidProgress(event) {
+ const progress = event.loaded / event.total * 100
+ if (progress) {
+ this.dispatch("progress", { progress })
+ }
+ }
+
+ get url() {
+ return this.input.getAttribute("data-direct-upload-url")
+ }
+
+ dispatch(name, detail = {}) {
+ detail.file = this.file
+ detail.id = this.directUpload.id
+ return dispatchEvent(this.input, `direct-upload:${name}`, { detail })
+ }
+
+ dispatchError(error) {
+ const event = this.dispatch("error", { error })
+ if (!event.defaultPrevented) {
+ alert(error)
+ }
+ }
+
+ // DirectUpload delegate
+
+ directUploadWillCreateBlobWithXHR(xhr) {
+ this.dispatch("before-blob-request", { xhr })
+ }
+
+ directUploadWillStoreFileWithXHR(xhr) {
+ this.dispatch("before-storage-request", { xhr })
+ xhr.upload.addEventListener("progress", event => this.uploadRequestDidProgress(event))
+ }
+}
diff --git a/activestorage/app/javascript/activestorage/direct_uploads_controller.js b/activestorage/app/javascript/activestorage/direct_uploads_controller.js
new file mode 100644
index 0000000000..94b89c9119
--- /dev/null
+++ b/activestorage/app/javascript/activestorage/direct_uploads_controller.js
@@ -0,0 +1,50 @@
+import { DirectUploadController } from "./direct_upload_controller"
+import { findElements, dispatchEvent, toArray } from "./helpers"
+
+const inputSelector = "input[type=file][data-direct-upload-url]:not([disabled])"
+
+export class DirectUploadsController {
+ constructor(form) {
+ this.form = form
+ this.inputs = findElements(form, inputSelector).filter(input => input.files.length)
+ }
+
+ start(callback) {
+ const controllers = this.createDirectUploadControllers()
+
+ const startNextController = () => {
+ const controller = controllers.shift()
+ if (controller) {
+ controller.start(error => {
+ if (error) {
+ callback(error)
+ this.dispatch("end")
+ } else {
+ startNextController()
+ }
+ })
+ } else {
+ callback()
+ this.dispatch("end")
+ }
+ }
+
+ this.dispatch("start")
+ startNextController()
+ }
+
+ createDirectUploadControllers() {
+ const controllers = []
+ this.inputs.forEach(input => {
+ toArray(input.files).forEach(file => {
+ const controller = new DirectUploadController(input, file)
+ controllers.push(controller)
+ })
+ })
+ return controllers
+ }
+
+ dispatch(name, detail = {}) {
+ return dispatchEvent(this.form, `direct-uploads:${name}`, { detail })
+ }
+}
diff --git a/activestorage/app/javascript/activestorage/file_checksum.js b/activestorage/app/javascript/activestorage/file_checksum.js
new file mode 100644
index 0000000000..ffaec1a128
--- /dev/null
+++ b/activestorage/app/javascript/activestorage/file_checksum.js
@@ -0,0 +1,53 @@
+import SparkMD5 from "spark-md5"
+
+const fileSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice
+
+export class FileChecksum {
+ static create(file, callback) {
+ const instance = new FileChecksum(file)
+ instance.create(callback)
+ }
+
+ constructor(file) {
+ this.file = file
+ this.chunkSize = 2097152 // 2MB
+ this.chunkCount = Math.ceil(this.file.size / this.chunkSize)
+ this.chunkIndex = 0
+ }
+
+ create(callback) {
+ this.callback = callback
+ this.md5Buffer = new SparkMD5.ArrayBuffer
+ this.fileReader = new FileReader
+ this.fileReader.addEventListener("load", event => this.fileReaderDidLoad(event))
+ this.fileReader.addEventListener("error", event => this.fileReaderDidError(event))
+ this.readNextChunk()
+ }
+
+ fileReaderDidLoad(event) {
+ this.md5Buffer.append(event.target.result)
+
+ if (!this.readNextChunk()) {
+ const binaryDigest = this.md5Buffer.end(true)
+ const base64digest = btoa(binaryDigest)
+ this.callback(null, base64digest)
+ }
+ }
+
+ fileReaderDidError(event) {
+ this.callback(`Error reading ${this.file.name}`)
+ }
+
+ readNextChunk() {
+ if (this.chunkIndex < this.chunkCount) {
+ const start = this.chunkIndex * this.chunkSize
+ const end = Math.min(start + this.chunkSize, this.file.size)
+ const bytes = fileSlice.call(this.file, start, end)
+ this.fileReader.readAsArrayBuffer(bytes)
+ this.chunkIndex++
+ return true
+ } else {
+ return false
+ }
+ }
+}
diff --git a/activestorage/app/javascript/activestorage/helpers.js b/activestorage/app/javascript/activestorage/helpers.js
new file mode 100644
index 0000000000..52fec8f6f1
--- /dev/null
+++ b/activestorage/app/javascript/activestorage/helpers.js
@@ -0,0 +1,42 @@
+export function getMetaValue(name) {
+ const element = findElement(document.head, `meta[name="${name}"]`)
+ if (element) {
+ return element.getAttribute("content")
+ }
+}
+
+export function findElements(root, selector) {
+ if (typeof root == "string") {
+ selector = root
+ root = document
+ }
+ const elements = root.querySelectorAll(selector)
+ return toArray(elements)
+}
+
+export function findElement(root, selector) {
+ if (typeof root == "string") {
+ selector = root
+ root = document
+ }
+ return root.querySelector(selector)
+}
+
+export function dispatchEvent(element, type, eventInit = {}) {
+ const { bubbles, cancelable, detail } = eventInit
+ const event = document.createEvent("Event")
+ event.initEvent(type, bubbles || true, cancelable || true)
+ event.detail = detail || {}
+ element.dispatchEvent(event)
+ return event
+}
+
+export function toArray(value) {
+ if (Array.isArray(value)) {
+ return value
+ } else if (Array.from) {
+ return Array.from(value)
+ } else {
+ return [].slice.call(value)
+ }
+}
diff --git a/activestorage/app/javascript/activestorage/index.js b/activestorage/app/javascript/activestorage/index.js
new file mode 100644
index 0000000000..a340008fb9
--- /dev/null
+++ b/activestorage/app/javascript/activestorage/index.js
@@ -0,0 +1,11 @@
+import { start } from "./ujs"
+import { DirectUpload } from "./direct_upload"
+export { start, DirectUpload }
+
+function autostart() {
+ if (window.ActiveStorage) {
+ start()
+ }
+}
+
+setTimeout(autostart, 1)
diff --git a/activestorage/app/javascript/activestorage/ujs.js b/activestorage/app/javascript/activestorage/ujs.js
new file mode 100644
index 0000000000..a2ce2cfc58
--- /dev/null
+++ b/activestorage/app/javascript/activestorage/ujs.js
@@ -0,0 +1,74 @@
+import { DirectUploadsController } from "./direct_uploads_controller"
+import { findElement } from "./helpers"
+
+const processingAttribute = "data-direct-uploads-processing"
+let started = false
+
+export function start() {
+ if (!started) {
+ started = true
+ document.addEventListener("submit", didSubmitForm)
+ document.addEventListener("ajax:before", didSubmitRemoteElement)
+ }
+}
+
+function didSubmitForm(event) {
+ handleFormSubmissionEvent(event)
+}
+
+function didSubmitRemoteElement(event) {
+ if (event.target.tagName == "FORM") {
+ handleFormSubmissionEvent(event)
+ }
+}
+
+function handleFormSubmissionEvent(event) {
+ const form = event.target
+
+ if (form.hasAttribute(processingAttribute)) {
+ event.preventDefault()
+ return
+ }
+
+ const controller = new DirectUploadsController(form)
+ const { inputs } = controller
+
+ if (inputs.length) {
+ event.preventDefault()
+ form.setAttribute(processingAttribute, "")
+ inputs.forEach(disable)
+ controller.start(error => {
+ form.removeAttribute(processingAttribute)
+ if (error) {
+ inputs.forEach(enable)
+ } else {
+ submitForm(form)
+ }
+ })
+ }
+}
+
+function submitForm(form) {
+ let button = findElement(form, "input[type=submit]")
+ if (button) {
+ const { disabled } = button
+ button.disabled = false
+ button.click()
+ button.disabled = disabled
+ } else {
+ button = document.createElement("input")
+ button.type = "submit"
+ button.style = "display:none"
+ form.appendChild(button)
+ button.click()
+ form.removeChild(button)
+ }
+}
+
+function disable(input) {
+ input.disabled = true
+}
+
+function enable(input) {
+ input.disabled = false
+}
diff --git a/activestorage/app/jobs/active_storage/purge_job.rb b/activestorage/app/jobs/active_storage/purge_job.rb
new file mode 100644
index 0000000000..369c07929d
--- /dev/null
+++ b/activestorage/app/jobs/active_storage/purge_job.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+# Provides delayed purging of attachments or blobs using their +purge_later+ method.
+class ActiveStorage::PurgeJob < ActiveJob::Base
+ # FIXME: Limit this to a custom ActiveStorage error
+ retry_on StandardError
+
+ def perform(attachment_or_blob)
+ attachment_or_blob.purge
+ end
+end
diff --git a/activestorage/app/models/active_storage/attachment.rb b/activestorage/app/models/active_storage/attachment.rb
new file mode 100644
index 0000000000..ad43845e4e
--- /dev/null
+++ b/activestorage/app/models/active_storage/attachment.rb
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+
+require "active_support/core_ext/module/delegation"
+
+# Attachments associate records with blobs. Usually that's a one record-many blobs relationship,
+# but it is possible to associate many different records with the same blob. If you're doing that,
+# you'll want to declare with <tt>has_one/many_attached :thingy, dependent: false</tt>, so that destroying
+# any one record won't destroy the blob as well. (Then you'll need to do your own garbage collecting, though).
+class ActiveStorage::Attachment < ActiveRecord::Base
+ self.table_name = "active_storage_attachments"
+
+ belongs_to :record, polymorphic: true
+ belongs_to :blob, class_name: "ActiveStorage::Blob"
+
+ delegate_missing_to :blob
+
+ # Purging an attachment will purge the blob (delete the file on the service, then destroy the record)
+ # and then destroy the attachment itself.
+ def purge
+ blob.purge
+ destroy
+ end
+
+ # Purging an attachment means purging the blob, which means talking to the service, which means
+ # talking over the Internet. Whenever you're doing that, it's a good idea to put that work in a job,
+ # so it doesn't hold up other operations. That's what +purge_later+ provides.
+ def purge_later
+ ActiveStorage::PurgeJob.perform_later(self)
+ end
+end
diff --git a/activestorage/app/models/active_storage/blob.rb b/activestorage/app/models/active_storage/blob.rb
new file mode 100644
index 0000000000..9f2ed1e5ac
--- /dev/null
+++ b/activestorage/app/models/active_storage/blob.rb
@@ -0,0 +1,199 @@
+# frozen_string_literal: true
+
+# A blob is a record that contains the metadata about a file and a key for where that file resides on the service.
+# Blobs can be created in two ways:
+#
+# 1) Subsequent to the file being uploaded server-side to the service via <tt>create_after_upload!</tt>.
+# 2) Ahead of the file being directly uploaded client-side to the service via <tt>create_before_direct_upload!</tt>.
+#
+# The first option doesn't require any client-side JavaScript integration, and can be used by any other back-end
+# service that deals with files. The second option is faster, since you're not using your own server as a staging
+# point for uploads, and can work with deployments like Heroku that do not provide large amounts of disk space.
+#
+# Blobs are intended to be immutable in as-so-far as their reference to a specific file goes. You're allowed to
+# update a blob's metadata on a subsequent pass, but you should not update the key or change the uploaded file.
+# If you need to create a derivative or otherwise change the blob, simply create a new blob and purge the old one.
+class ActiveStorage::Blob < ActiveRecord::Base
+ self.table_name = "active_storage_blobs"
+
+ has_secure_token :key
+ store :metadata, coder: JSON
+
+ class_attribute :service
+
+ class << self
+ # You can used the signed ID of a blob to refer to it on the client side without fear of tampering.
+ # This is particularly helpful for direct uploads where the client-side needs to refer to the blob
+ # that was created ahead of the upload itself on form submission.
+ #
+ # The signed ID is also used to create stable URLs for the blob through the BlobsController.
+ def find_signed(id)
+ find ActiveStorage.verifier.verify(id, purpose: :blob_id)
+ end
+
+ # Returns a new, unsaved blob instance after the +io+ has been uploaded to the service.
+ def build_after_upload(io:, filename:, content_type: nil, metadata: nil)
+ new.tap do |blob|
+ blob.filename = filename
+ blob.content_type = content_type
+ blob.metadata = metadata
+
+ blob.upload io
+ end
+ end
+
+ # Returns a saved blob instance after the +io+ has been uploaded to the service. Note, the blob is first built,
+ # then the +io+ is uploaded, then the blob is saved. This is done this way to avoid uploading (which may take
+ # time), while having an open database transaction.
+ def create_after_upload!(io:, filename:, content_type: nil, metadata: nil)
+ build_after_upload(io: io, filename: filename, content_type: content_type, metadata: metadata).tap(&:save!)
+ end
+
+ # Returns a saved blob _without_ uploading a file to the service. This blob will point to a key where there is
+ # no file yet. It's intended to be used together with a client-side upload, which will first create the blob
+ # in order to produce the signed URL for uploading. This signed URL points to the key generated by the blob.
+ # Once the form using the direct upload is submitted, the blob can be associated with the right record using
+ # the signed ID.
+ def create_before_direct_upload!(filename:, byte_size:, checksum:, content_type: nil, metadata: nil)
+ create! filename: filename, byte_size: byte_size, checksum: checksum, content_type: content_type, metadata: metadata
+ end
+ end
+
+
+ # Returns a signed ID for this blob that's suitable for reference on the client-side without fear of tampering.
+ # It uses the framework-wide verifier on <tt>ActiveStorage.verifier</tt>, but with a dedicated purpose.
+ def signed_id
+ ActiveStorage.verifier.generate(id, purpose: :blob_id)
+ end
+
+ # Returns the key pointing to the file on the service that's associated with this blob. The key is in the
+ # standard secure-token format from Rails. So it'll look like: XTAPjJCJiuDrLk3TmwyJGpUo. This key is not intended
+ # to be revealed directly to the user. Always refer to blobs using the signed_id or a verified form of the key.
+ def key
+ # We can't wait until the record is first saved to have a key for it
+ self[:key] ||= self.class.generate_unique_secure_token
+ end
+
+ # Returns an ActiveStorage::Filename instance of the filename that can be
+ # queried for basename, extension, and a sanitized version of the filename
+ # that's safe to use in URLs.
+ def filename
+ ActiveStorage::Filename.new(self[:filename])
+ end
+
+ # Returns true if the content_type of this blob is in the image range, like image/png.
+ def image?
+ content_type.start_with?("image")
+ end
+
+ # Returns true if the content_type of this blob is in the audio range, like audio/mpeg.
+ def audio?
+ content_type.start_with?("audio")
+ end
+
+ # Returns true if the content_type of this blob is in the video range, like video/mp4.
+ def video?
+ content_type.start_with?("video")
+ end
+
+ # Returns true if the content_type of this blob is in the text range, like text/plain.
+ def text?
+ content_type.start_with?("text")
+ end
+
+ # Returns an ActiveStorage::Variant instance with the set of +transformations+
+ # passed in. This is only relevant for image files, and it allows any image to
+ # be transformed for size, colors, and the like. Example:
+ #
+ # avatar.variant(resize: "100x100").processed.service_url
+ #
+ # This will create and process a variant of the avatar blob that's constrained to a height and width of 100.
+ # Then it'll upload said variant to the service according to a derivative key of the blob and the transformations.
+ #
+ # Frequently, though, you don't actually want to transform the variant right away. But rather simply refer to a
+ # specific variant that can be created by a controller on-demand. Like so:
+ #
+ # <%= image_tag url_for(Current.user.avatar.variant(resize: "100x100")) %>
+ #
+ # This will create a URL for that specific blob with that specific variant, which the ActiveStorage::VariantsController
+ # can then produce on-demand.
+ def variant(transformations)
+ ActiveStorage::Variant.new(self, ActiveStorage::Variation.new(transformations))
+ end
+
+
+ # Returns the URL of the blob on the service. This URL is intended to be short-lived for security and not used directly
+ # with users. Instead, the +service_url+ should only be exposed as a redirect from a stable, possibly authenticated URL.
+ # Hiding the +service_url+ behind a redirect also gives you the power to change services without updating all URLs. And
+ # it allows permanent URLs that redirect to the +service_url+ to be cached in the view.
+ def service_url(expires_in: 5.minutes, disposition: :inline)
+ service.url key, expires_in: expires_in, disposition: disposition, filename: filename, content_type: content_type
+ end
+
+ # Returns a URL that can be used to directly upload a file for this blob on the service. This URL is intended to be
+ # short-lived for security and only generated on-demand by the client-side JavaScript responsible for doing the uploading.
+ def service_url_for_direct_upload(expires_in: 5.minutes)
+ service.url_for_direct_upload key, expires_in: expires_in, content_type: content_type, content_length: byte_size, checksum: checksum
+ end
+
+ # Returns a Hash of headers for +service_url_for_direct_upload+ requests.
+ def service_headers_for_direct_upload
+ service.headers_for_direct_upload key, filename: filename, content_type: content_type, content_length: byte_size, checksum: checksum
+ end
+
+ # Uploads the +io+ to the service on the +key+ for this blob. Blobs are intended to be immutable, so you shouldn't be
+ # using this method after a file has already been uploaded to fit with a blob. If you want to create a derivative blob,
+ # you should instead simply create a new blob based on the old one.
+ #
+ # Prior to uploading, we compute the checksum, which is sent to the service for transit integrity validation. If the
+ # checksum does not match what the service receives, an exception will be raised. We also measure the size of the +io+
+ # and store that in +byte_size+ on the blob record.
+ #
+ # Normally, you do not have to call this method directly at all. Use the factory class methods of +build_after_upload+
+ # and +create_after_upload!+.
+ def upload(io)
+ self.checksum = compute_checksum_in_chunks(io)
+ self.byte_size = io.size
+
+ service.upload(key, io, checksum: checksum)
+ end
+
+ # Downloads the file associated with this blob. If no block is given, the entire file is read into memory and returned.
+ # That'll use a lot of RAM for very large files. If a block is given, then the download is streamed and yielded in chunks.
+ def download(&block)
+ service.download key, &block
+ end
+
+
+ # Deletes the file on the service that's associated with this 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 the +#purge+ and +#purge_later+
+ # methods in most circumstances.
+ def delete
+ service.delete key
+ end
+
+ # Deletes the file on the service and then destroys the blob record. This is the recommended way to dispose of unwanted
+ # blobs. Note, though, that deleting the file off the service will initiate a HTTP connection to the service, which may
+ # be slow or prevented, so you should not use this method inside a transaction or in callbacks. Use +#purge_later+ instead.
+ def purge
+ delete
+ destroy
+ end
+
+ # Enqueues an ActiveStorage::PurgeJob job that'll call +purge+. This is the recommended way to purge blobs when the call
+ # needs to be made from a transaction, a callback, or any other real-time scenario.
+ def purge_later
+ ActiveStorage::PurgeJob.perform_later(self)
+ end
+
+ private
+ def compute_checksum_in_chunks(io)
+ Digest::MD5.new.tap do |checksum|
+ while chunk = io.read(5.megabytes)
+ checksum << chunk
+ end
+
+ io.rewind
+ end.base64digest
+ end
+end
diff --git a/activestorage/app/models/active_storage/filename.rb b/activestorage/app/models/active_storage/filename.rb
new file mode 100644
index 0000000000..6a9889addf
--- /dev/null
+++ b/activestorage/app/models/active_storage/filename.rb
@@ -0,0 +1,51 @@
+# frozen_string_literal: true
+
+# Encapsulates a string representing a filename to provide convenience access to parts of it and a sanitized version.
+# This is what's returned by `ActiveStorage::Blob#filename`. A Filename instance is comparable so it can be used for sorting.
+class ActiveStorage::Filename
+ include Comparable
+
+ def initialize(filename)
+ @filename = filename
+ end
+
+ # Filename.new("racecar.jpg").extname # => ".jpg"
+ def extname
+ File.extname(@filename)
+ end
+
+ # Filename.new("racecar.jpg").extension # => "jpg"
+ def extension
+ extname.from(1)
+ end
+
+ # Filename.new("racecar.jpg").base # => "racecar"
+ def base
+ File.basename(@filename, extname)
+ end
+
+ # Filename.new("foo:bar.jpg").sanitized # => "foo-bar.jpg"
+ # Filename.new("foo/bar.jpg").sanitized # => "foo-bar.jpg"
+ #
+ # ...and any other character unsafe for URLs or storage is converted or stripped.
+ def sanitized
+ @filename.encode(Encoding::UTF_8, invalid: :replace, undef: :replace, replace: "�").strip.tr("\u{202E}%$|:;/\t\r\n\\", "-")
+ end
+
+ # Returns the sanitized version of the filename.
+ def to_s
+ sanitized.to_s
+ end
+
+ def as_json(*)
+ to_s
+ end
+
+ def to_json
+ to_s
+ end
+
+ def <=>(other)
+ to_s.downcase <=> other.to_s.downcase
+ end
+end
diff --git a/activestorage/app/models/active_storage/variant.rb b/activestorage/app/models/active_storage/variant.rb
new file mode 100644
index 0000000000..40648a27f7
--- /dev/null
+++ b/activestorage/app/models/active_storage/variant.rb
@@ -0,0 +1,82 @@
+# frozen_string_literal: true
+
+# Image blobs can have variants that are the result of a set of transformations applied to the original.
+# These variants are used to create thumbnails, fixed-size avatars, or any other derivative image from the
+# original.
+#
+# Variants rely on {MiniMagick}(https://github.com/minimagick/minimagick) for the actual transformations
+# of the file, so you must add <tt>gem "mini_magick"</tt> to your Gemfile if you wish to use variants.
+#
+# Note that to create a variant it's necessary to download the entire blob file from the service and load it
+# into memory. The larger the image, the more memory is used. Because of this process, you also want to be
+# considerate about when the variant is actually processed. You shouldn't be processing variants inline in a
+# template, for example. Delay the processing to an on-demand controller, like the one provided in
+# ActiveStorage::VariantsController.
+#
+# To refer to such a delayed on-demand variant, simply link to the variant through the resolved route provided
+# by Active Storage like so:
+#
+# <%= image_tag url_for(Current.user.avatar.variant(resize: "100x100")) %>
+#
+# This will create a URL for that specific blob with that specific variant, which the ActiveStorage::VariantsController
+# can then produce on-demand.
+#
+# When you do want to actually produce the variant needed, call +processed+. This will check that the variant
+# has already been processed and uploaded to the service, and, if so, just return that. Otherwise it will perform
+# the transformations, upload the variant to the service, and return itself again. Example:
+#
+# avatar.variant(resize: "100x100").processed.service_url
+#
+# This will create and process a variant of the avatar blob that's constrained to a height and width of 100.
+# Then it'll upload said variant to the service according to a derivative key of the blob and the transformations.
+#
+# A list of all possible transformations is available at https://www.imagemagick.org/script/mogrify.php. You can
+# combine as many as you like freely:
+#
+# avatar.variant(resize: "100x100", monochrome: true, flip: "-90")
+class ActiveStorage::Variant
+ attr_reader :blob, :variation
+ delegate :service, to: :blob
+
+ def initialize(blob, variation)
+ @blob, @variation = blob, variation
+ end
+
+ # Returns the variant instance itself after it's been processed or an existing processing has been found on the service.
+ def processed
+ process unless processed?
+ self
+ end
+
+ # Returns a combination key of the blob and the variation that together identifies a specific variant.
+ def key
+ "variants/#{blob.key}/#{variation.key}"
+ end
+
+ # Returns the URL of the variant on the service. This URL is intended to be short-lived for security and not used directly
+ # with users. Instead, the +service_url+ should only be exposed as a redirect from a stable, possibly authenticated URL.
+ # Hiding the +service_url+ behind a redirect also gives you the power to change services without updating all URLs. And
+ # it allows permanent URLs that redirect to the +service_url+ to be cached in the view.
+ #
+ # Use <tt>url_for(variant)</tt> (or the implied form, like +link_to variant+ or +redirect_to variant+) to get the stable URL
+ # for a variant that points to the ActiveStorage::VariantsController, which in turn will use this +service_call+ method
+ # for its redirection.
+ def service_url(expires_in: 5.minutes, disposition: :inline)
+ service.url key, expires_in: expires_in, disposition: disposition, filename: blob.filename, content_type: blob.content_type
+ end
+
+
+ private
+ def processed?
+ service.exist?(key)
+ end
+
+ def process
+ service.upload key, transform(service.download(blob.key))
+ end
+
+ def transform(io)
+ require "mini_magick"
+ File.open MiniMagick::Image.read(io).tap { |image| variation.transform(image) }.path
+ end
+end
diff --git a/activestorage/app/models/active_storage/variation.rb b/activestorage/app/models/active_storage/variation.rb
new file mode 100644
index 0000000000..f657d90db4
--- /dev/null
+++ b/activestorage/app/models/active_storage/variation.rb
@@ -0,0 +1,55 @@
+# frozen_string_literal: true
+
+require "active_support/core_ext/object/inclusion"
+
+# A set of transformations that can be applied to a blob to create a variant. This class is exposed via
+# the `ActiveStorage::Blob#variant` method and should rarely be used directly.
+#
+# In case you do need to use this directly, it's instantiated using a hash of transformations where
+# the key is the command and the value is the arguments. Example:
+#
+# ActiveStorage::Variation.new(resize: "100x100", monochrome: true, trim: true, rotate: "-90")
+#
+# A list of all possible transformations is available at https://www.imagemagick.org/script/mogrify.php.
+class ActiveStorage::Variation
+ attr_reader :transformations
+
+ class << self
+ # Returns a variation instance with the transformations that were encoded by +encode+.
+ def decode(key)
+ new ActiveStorage.verifier.verify(key, purpose: :variation)
+ end
+
+ # Returns a signed key for the +transformations+, which can be used to refer to a specific
+ # variation in a URL or combined key (like <tt>ActiveStorage::Variant#key</tt>).
+ def encode(transformations)
+ ActiveStorage.verifier.generate(transformations, purpose: :variation)
+ end
+ end
+
+ def initialize(transformations)
+ @transformations = transformations
+ end
+
+ # Accepts an open MiniMagick image instance, like what's returned by <tt>MiniMagick::Image.read(io)</tt>,
+ # and performs the +transformations+ against it. The transformed image instance is then returned.
+ def transform(image)
+ transformations.each do |(method, argument)|
+ if eligible_argument?(argument)
+ image.public_send(method, argument)
+ else
+ image.public_send(method)
+ end
+ end
+ end
+
+ # Returns a signed key for all the +transformations+ that this variation was instantiated with.
+ def key
+ self.class.encode(transformations)
+ end
+
+ private
+ def eligible_argument?(argument)
+ argument.present? && argument != true
+ end
+end
diff --git a/activestorage/config/routes.rb b/activestorage/config/routes.rb
new file mode 100644
index 0000000000..168788475c
--- /dev/null
+++ b/activestorage/config/routes.rb
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+
+Rails.application.routes.draw do
+ get "/rails/active_storage/blobs/:signed_id/*filename" => "active_storage/blobs#show", as: :rails_service_blob
+
+ direct :rails_blob do |blob|
+ route_for(:rails_service_blob, blob.signed_id, blob.filename)
+ end
+
+ resolve("ActiveStorage::Blob") { |blob| route_for(:rails_blob, blob) }
+ resolve("ActiveStorage::Attachment") { |attachment| route_for(:rails_blob, attachment.blob) }
+
+
+ get "/rails/active_storage/variants/:signed_blob_id/:variation_key/*filename" => "active_storage/variants#show", as: :rails_blob_variation
+
+ direct :rails_variant do |variant|
+ signed_blob_id = variant.blob.signed_id
+ variation_key = variant.variation.key
+ filename = variant.blob.filename
+
+ route_for(:rails_blob_variation, signed_blob_id, variation_key, filename)
+ end
+
+ resolve("ActiveStorage::Variant") { |variant| route_for(:rails_variant, variant) }
+
+
+ get "/rails/active_storage/disk/:encoded_key/*filename" => "active_storage/disk#show", as: :rails_disk_service
+ put "/rails/active_storage/disk/:encoded_token" => "active_storage/disk#update", as: :update_rails_disk_service
+ post "/rails/active_storage/direct_uploads" => "active_storage/direct_uploads#create", as: :rails_direct_uploads
+end
diff --git a/activestorage/db/migrate/20170806125915_create_active_storage_tables.rb b/activestorage/db/migrate/20170806125915_create_active_storage_tables.rb
new file mode 100644
index 0000000000..d333b6bf9c
--- /dev/null
+++ b/activestorage/db/migrate/20170806125915_create_active_storage_tables.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+class CreateActiveStorageTables < ActiveRecord::Migration[5.1]
+ def change
+ create_table :active_storage_blobs do |t|
+ t.string :key, null: false
+ t.string :filename, null: false
+ t.string :content_type
+ t.text :metadata
+ t.integer :byte_size, null: false
+ t.string :checksum, null: false
+ t.datetime :created_at, null: false
+
+ t.index [ :key ], unique: true
+ end
+
+ create_table :active_storage_attachments do |t|
+ t.string :name, null: false
+ t.references :record, null: false, polymorphic: true, index: false
+ t.references :blob, null: false
+
+ t.datetime :created_at, null: false
+
+ t.index [ :record_type, :record_id, :name, :blob_id ], name: "index_active_storage_attachments_uniqueness", unique: true
+ end
+ end
+end
diff --git a/activestorage/lib/active_storage.rb b/activestorage/lib/active_storage.rb
new file mode 100644
index 0000000000..ccc1d4a163
--- /dev/null
+++ b/activestorage/lib/active_storage.rb
@@ -0,0 +1,38 @@
+# frozen_string_literal: true
+
+#--
+# Copyright (c) 2017 David Heinemeier Hansson
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#++
+
+require "active_record"
+require "active_support"
+require "active_support/rails"
+require_relative "active_storage/version"
+
+module ActiveStorage
+ extend ActiveSupport::Autoload
+
+ autoload :Attached
+ autoload :Service
+
+ mattr_accessor :verifier
+end
diff --git a/activestorage/lib/active_storage/attached.rb b/activestorage/lib/active_storage/attached.rb
new file mode 100644
index 0000000000..9c4b6d5d1d
--- /dev/null
+++ b/activestorage/lib/active_storage/attached.rb
@@ -0,0 +1,40 @@
+# frozen_string_literal: true
+
+require "action_dispatch"
+require "action_dispatch/http/upload"
+require "active_support/core_ext/module/delegation"
+
+module ActiveStorage
+ # Abstract base class for the concrete ActiveStorage::Attached::One and ActiveStorage::Attached::Many
+ # classes that both provide proxy access to the blob association for a record.
+ class Attached
+ attr_reader :name, :record
+
+ def initialize(name, record)
+ @name, @record = name, record
+ end
+
+ private
+ def create_blob_from(attachable)
+ case attachable
+ when ActiveStorage::Blob
+ attachable
+ when ActionDispatch::Http::UploadedFile, Rack::Test::UploadedFile
+ ActiveStorage::Blob.create_after_upload! \
+ io: attachable.open,
+ filename: attachable.original_filename,
+ content_type: attachable.content_type
+ when Hash
+ ActiveStorage::Blob.create_after_upload!(attachable)
+ when String
+ ActiveStorage::Blob.find_signed(attachable)
+ else
+ nil
+ end
+ end
+ end
+end
+
+require "active_storage/attached/one"
+require "active_storage/attached/many"
+require "active_storage/attached/macros"
diff --git a/activestorage/lib/active_storage/attached/macros.rb b/activestorage/lib/active_storage/attached/macros.rb
new file mode 100644
index 0000000000..f3879ee2e3
--- /dev/null
+++ b/activestorage/lib/active_storage/attached/macros.rb
@@ -0,0 +1,82 @@
+# frozen_string_literal: true
+
+module ActiveStorage
+ # Provides the class-level DSL for declaring that an Active Record model has attached blobs.
+ module Attached::Macros
+ # Specifies the relation between a single attachment and the model.
+ #
+ # class User < ActiveRecord::Base
+ # has_one_attached :avatar
+ # end
+ #
+ # There is no column defined on the model side, Active Storage takes
+ # care of the mapping between your records and the attachment.
+ #
+ # Under the covers, this relationship is implemented as a +has_one+ association to a
+ # ActiveStorage::Attachment record and a +has_one-through+ association to a
+ # ActiveStorage::Blob record. These associations are available as +avatar_attachment+
+ # and +avatar_blob+. But you shouldn't need to work with these associations directly in
+ # most circumstances.
+ #
+ # The system has been designed to having you go through the ActiveStorage::Attached::One
+ # proxy that provides the dynamic proxy to the associations and factory methods, like +attach+.
+ #
+ # If the +:dependent+ option isn't set, the attachment will be purged
+ # (i.e. destroyed) whenever the record is destroyed.
+ def has_one_attached(name, dependent: :purge_later)
+ class_eval <<-CODE, __FILE__, __LINE__ + 1
+ def #{name}
+ @active_storage_attached_#{name} ||= ActiveStorage::Attached::One.new("#{name}", self)
+ end
+ CODE
+
+ has_one :"#{name}_attachment", -> { where(name: name) }, class_name: "ActiveStorage::Attachment", as: :record
+ has_one :"#{name}_blob", through: :"#{name}_attachment", class_name: "ActiveStorage::Blob", source: :blob
+
+ if dependent == :purge_later
+ before_destroy { public_send(name).purge_later }
+ end
+ end
+
+ # Specifies the relation between multiple attachments and the model.
+ #
+ # class Gallery < ActiveRecord::Base
+ # has_many_attached :photos
+ # end
+ #
+ # There are no columns defined on the model side, Active Storage takes
+ # care of the mapping between your records and the attachments.
+ #
+ # To avoid N+1 queries, you can include the attached blobs in your query like so:
+ #
+ # Gallery.where(user: Current.user).with_attached_photos
+ #
+ # Under the covers, this relationship is implemented as a +has_many+ association to a
+ # ActiveStorage::Attachment record and a +has_many-through+ association to a
+ # ActiveStorage::Blob record. These associations are available as +photos_attachments+
+ # and +photos_blobs+. But you shouldn't need to work with these associations directly in
+ # most circumstances.
+ #
+ # The system has been designed to having you go through the ActiveStorage::Attached::Many
+ # proxy that provides the dynamic proxy to the associations and factory methods, like +#attach+.
+ #
+ # If the +:dependent+ option isn't set, all the attachments will be purged
+ # (i.e. destroyed) whenever the record is destroyed.
+ def has_many_attached(name, dependent: :purge_later)
+ class_eval <<-CODE, __FILE__, __LINE__ + 1
+ def #{name}
+ @active_storage_attached_#{name} ||= ActiveStorage::Attached::Many.new("#{name}", self)
+ end
+ CODE
+
+ has_many :"#{name}_attachments", -> { where(name: name) }, as: :record, class_name: "ActiveStorage::Attachment"
+ has_many :"#{name}_blobs", through: :"#{name}_attachments", class_name: "ActiveStorage::Blob", source: :blob
+
+ scope :"with_attached_#{name}", -> { includes("#{name}_attachments": :blob) }
+
+ if dependent == :purge_later
+ before_destroy { public_send(name).purge_later }
+ end
+ end
+ end
+end
diff --git a/activestorage/lib/active_storage/attached/many.rb b/activestorage/lib/active_storage/attached/many.rb
new file mode 100644
index 0000000000..59b7d7d559
--- /dev/null
+++ b/activestorage/lib/active_storage/attached/many.rb
@@ -0,0 +1,55 @@
+# frozen_string_literal: true
+
+module ActiveStorage
+ # Decorated proxy object representing of multiple attachments to a model.
+ class Attached::Many < Attached
+ delegate_missing_to :attachments
+
+ # Returns all the associated attachment records.
+ #
+ # All methods called on this proxy object that aren't listed here will automatically be delegated to +attachments+.
+ def attachments
+ record.public_send("#{name}_attachments")
+ end
+
+ # Associates one or several attachments with the current record, saving them to the database.
+ # Examples:
+ #
+ # document.images.attach(params[:images]) # Array of ActionDispatch::Http::UploadedFile objects
+ # document.images.attach(params[:signed_blob_id]) # Signed reference to blob from direct upload
+ # document.images.attach(io: File.open("~/racecar.jpg"), filename: "racecar.jpg", content_type: "image/jpg")
+ # document.images.attach([ first_blob, second_blob ])
+ def attach(*attachables)
+ attachables.flatten.collect do |attachable|
+ attachments.create!(name: name, blob: create_blob_from(attachable))
+ end
+ end
+
+ # Returns true if any attachments has been made.
+ #
+ # class Gallery < ActiveRecord::Base
+ # has_many_attached :photos
+ # end
+ #
+ # Gallery.new.photos.attached? # => false
+ def attached?
+ attachments.any?
+ end
+
+ # Directly purges each associated attachment (i.e. destroys the blobs and
+ # attachments and deletes the files on the service).
+ def purge
+ if attached?
+ attachments.each(&:purge)
+ attachments.reload
+ end
+ end
+
+ # Purges each associated attachment through the queuing system.
+ def purge_later
+ if attached?
+ attachments.each(&:purge_later)
+ end
+ end
+ end
+end
diff --git a/activestorage/lib/active_storage/attached/one.rb b/activestorage/lib/active_storage/attached/one.rb
new file mode 100644
index 0000000000..2e5831348e
--- /dev/null
+++ b/activestorage/lib/active_storage/attached/one.rb
@@ -0,0 +1,60 @@
+# frozen_string_literal: true
+
+module ActiveStorage
+ # Representation of a single attachment to a model.
+ class Attached::One < Attached
+ delegate_missing_to :attachment
+
+ # Returns the associated attachment record.
+ #
+ # You don't have to call this method to access the attachment's methods as
+ # they are all available at the model level.
+ def attachment
+ record.public_send("#{name}_attachment")
+ end
+
+ # Associates a given attachment with the current record, saving it to the database.
+ # Examples:
+ #
+ # person.avatar.attach(params[:avatar]) # ActionDispatch::Http::UploadedFile object
+ # person.avatar.attach(params[:signed_blob_id]) # Signed reference to blob from direct upload
+ # person.avatar.attach(io: File.open("~/face.jpg"), filename: "face.jpg", content_type: "image/jpg")
+ # person.avatar.attach(avatar_blob) # ActiveStorage::Blob object
+ def attach(attachable)
+ write_attachment \
+ ActiveStorage::Attachment.create!(record: record, name: name, blob: create_blob_from(attachable))
+ end
+
+ # Returns true if an attachment has been made.
+ #
+ # class User < ActiveRecord::Base
+ # has_one_attached :avatar
+ # end
+ #
+ # User.new.avatar.attached? # => false
+ def attached?
+ attachment.present?
+ end
+
+ # Directly purges the attachment (i.e. destroys the blob and
+ # attachment and deletes the file on the service).
+ def purge
+ if attached?
+ attachment.purge
+ write_attachment nil
+ end
+ end
+
+ # Purges the attachment through the queuing system.
+ def purge_later
+ if attached?
+ attachment.purge_later
+ end
+ end
+
+ private
+ def write_attachment(attachment)
+ record.public_send("#{name}_attachment=", attachment)
+ end
+ end
+end
diff --git a/activestorage/lib/active_storage/engine.rb b/activestorage/lib/active_storage/engine.rb
new file mode 100644
index 0000000000..a5562b32d3
--- /dev/null
+++ b/activestorage/lib/active_storage/engine.rb
@@ -0,0 +1,65 @@
+# frozen_string_literal: true
+
+require "rails"
+require "active_storage"
+
+module ActiveStorage
+ class Engine < Rails::Engine # :nodoc:
+ isolate_namespace ActiveStorage
+
+ config.active_storage = ActiveSupport::OrderedOptions.new
+
+ config.eager_load_namespaces << ActiveStorage
+
+ initializer "active_storage.logger" do
+ require "active_storage/service"
+
+ config.after_initialize do |app|
+ ActiveStorage::Service.logger = app.config.active_storage.logger || Rails.logger
+ end
+ end
+
+ initializer "active_storage.attached" do
+ require "active_storage/attached"
+
+ ActiveSupport.on_load(:active_record) do
+ extend ActiveStorage::Attached::Macros
+ end
+ end
+
+ initializer "active_storage.verifier" do
+ config.after_initialize do |app|
+ if app.secrets.secret_key_base.present?
+ ActiveStorage.verifier = app.message_verifier("ActiveStorage")
+ end
+ end
+ end
+
+ initializer "active_storage.services" do
+ config.to_prepare do
+ if config_choice = Rails.configuration.active_storage.service
+ configs = Rails.configuration.active_storage.service_configurations ||= begin
+ config_file = Pathname.new(Rails.root.join("config/storage.yml"))
+ raise("Couldn't find Active Storage configuration in #{config_file}") unless config_file.exist?
+
+ require "yaml"
+ require "erb"
+
+ YAML.load(ERB.new(config_file.read).result) || {}
+ rescue Psych::SyntaxError => e
+ raise "YAML syntax error occurred while parsing #{config_file}. " \
+ "Please note that YAML must be consistently indented using spaces. Tabs are not allowed. " \
+ "Error: #{e.message}"
+ end
+
+ ActiveStorage::Blob.service =
+ begin
+ ActiveStorage::Service.configure config_choice, configs
+ rescue => e
+ raise e, "Cannot load `Rails.config.active_storage.service`:\n#{e.message}", e.backtrace
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/activestorage/lib/active_storage/gem_version.rb b/activestorage/lib/active_storage/gem_version.rb
new file mode 100644
index 0000000000..e1d7b3493a
--- /dev/null
+++ b/activestorage/lib/active_storage/gem_version.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+module ActiveStorage
+ # Returns the version of the currently loaded Active Storage as a <tt>Gem::Version</tt>.
+ def self.gem_version
+ Gem::Version.new VERSION::STRING
+ end
+
+ module VERSION
+ MAJOR = 5
+ MINOR = 2
+ TINY = 0
+ PRE = "alpha"
+
+ STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
+ end
+end
diff --git a/activestorage/lib/active_storage/log_subscriber.rb b/activestorage/lib/active_storage/log_subscriber.rb
new file mode 100644
index 0000000000..0d00a75c0e
--- /dev/null
+++ b/activestorage/lib/active_storage/log_subscriber.rb
@@ -0,0 +1,52 @@
+# frozen_string_literal: true
+
+require "active_support/log_subscriber"
+
+module ActiveStorage
+ class LogSubscriber < ActiveSupport::LogSubscriber
+ def service_upload(event)
+ message = "Uploaded file to key: #{key_in(event)}"
+ message += " (checksum: #{event.payload[:checksum]})" if event.payload[:checksum]
+ info event, color(message, GREEN)
+ end
+
+ def service_download(event)
+ info event, color("Downloaded file from key: #{key_in(event)}", BLUE)
+ end
+
+ def service_delete(event)
+ info event, color("Deleted file from key: #{key_in(event)}", RED)
+ end
+
+ def service_exist(event)
+ debug event, color("Checked if file exists at key: #{key_in(event)} (#{event.payload[:exist] ? "yes" : "no"})", BLUE)
+ end
+
+ def service_url(event)
+ debug event, color("Generated URL for file at key: #{key_in(event)} (#{event.payload[:url]})", BLUE)
+ end
+
+ def logger
+ ActiveStorage::Service.logger
+ end
+
+ private
+ def info(event, colored_message)
+ super log_prefix_for_service(event) + colored_message
+ end
+
+ def debug(event, colored_message)
+ super log_prefix_for_service(event) + colored_message
+ end
+
+ def log_prefix_for_service(event)
+ color " #{event.payload[:service]} Storage (#{event.duration.round(1)}ms) ", CYAN
+ end
+
+ def key_in(event)
+ event.payload[:key]
+ end
+ end
+end
+
+ActiveStorage::LogSubscriber.attach_to :active_storage
diff --git a/activestorage/lib/active_storage/service.rb b/activestorage/lib/active_storage/service.rb
new file mode 100644
index 0000000000..ce736b8728
--- /dev/null
+++ b/activestorage/lib/active_storage/service.rb
@@ -0,0 +1,117 @@
+# frozen_string_literal: true
+
+require "active_storage/log_subscriber"
+
+module ActiveStorage
+ class IntegrityError < StandardError; end
+ # Abstract class serving as an interface for concrete services.
+ #
+ # The available services are:
+ #
+ # * +Disk+, to manage attachments saved directly on the hard drive.
+ # * +GCS+, to manage attachments through Google Cloud Storage.
+ # * +S3+, to manage attachments through Amazon S3.
+ # * +AzureStorage+, to manage attachments through Microsoft Azure Storage.
+ # * +Mirror+, to be able to use several services to manage attachments.
+ #
+ # Inside a Rails application, you can set-up your services through the
+ # generated <tt>config/storage.yml</tt> file and reference one
+ # of the aforementioned constant under the +service+ key. For example:
+ #
+ # local:
+ # service: Disk
+ # root: <%= Rails.root.join("storage") %>
+ #
+ # You can checkout the service's constructor to know which keys are required.
+ #
+ # Then, in your application's configuration, you can specify the service to
+ # use like this:
+ #
+ # config.active_storage.service = :local
+ #
+ # If you are using Active Storage outside of a Ruby on Rails application, you
+ # can configure the service to use like this:
+ #
+ # ActiveStorage::Blob.service = ActiveStorage::Service.configure(
+ # :Disk,
+ # root: Pathname("/foo/bar/storage")
+ # )
+ class Service
+ extend ActiveSupport::Autoload
+ autoload :Configurator
+
+ class_attribute :logger
+
+ class << self
+ # Configure an Active Storage service by name from a set of configurations,
+ # typically loaded from a YAML file. The Active Storage engine uses this
+ # to set the global Active Storage service when the app boots.
+ def configure(service_name, configurations)
+ Configurator.build(service_name, configurations)
+ end
+
+ # Override in subclasses that stitch together multiple services and hence
+ # need to build additional services using the configurator.
+ #
+ # Passes the configurator and all of the service's config as keyword args.
+ #
+ # See MirrorService for an example.
+ def build(configurator:, service: nil, **service_config) #:nodoc:
+ new(**service_config)
+ end
+ end
+
+ # Upload the +io+ to the +key+ specified. If a +checksum+ is provided, the service will
+ # ensure a match when the upload has completed or raise an ActiveStorage::IntegrityError.
+ def upload(key, io, checksum: nil)
+ raise NotImplementedError
+ end
+
+ # Return the content of the file at the +key+.
+ def download(key)
+ raise NotImplementedError
+ end
+
+ # Delete the file at the +key+.
+ def delete(key)
+ raise NotImplementedError
+ end
+
+ # Return true if a file exists at the +key+.
+ def exist?(key)
+ raise NotImplementedError
+ end
+
+ # Returns a signed, temporary URL for the file at the +key+. The URL will be valid for the amount
+ # of seconds specified in +expires_in+. You most also provide the +disposition+ (+:inline+ or +:attachment+),
+ # +filename+, and +content_type+ that you wish the file to be served with on request.
+ def url(key, expires_in:, disposition:, filename:, content_type:)
+ raise NotImplementedError
+ end
+
+ # Returns a signed, temporary URL that a direct upload file can be PUT to on the +key+.
+ # The URL will be valid for the amount of seconds specified in +expires_in+.
+ # You most also provide the +content_type+, +content_length+, and +checksum+ of the file
+ # that will be uploaded. All these attributes will be validated by the service upon upload.
+ def url_for_direct_upload(key, expires_in:, content_type:, content_length:, checksum:)
+ raise NotImplementedError
+ end
+
+ # Returns a Hash of headers for +url_for_direct_upload+ requests.
+ def headers_for_direct_upload(key, filename:, content_type:, content_length:, checksum:)
+ {}
+ end
+
+ private
+ def instrument(operation, key, payload = {}, &block)
+ ActiveSupport::Notifications.instrument(
+ "service_#{operation}.active_storage",
+ payload.merge(key: key, service: service_name), &block)
+ end
+
+ def service_name
+ # ActiveStorage::Service::DiskService => Disk
+ self.class.name.split("::").third.remove("Service")
+ end
+ end
+end
diff --git a/activestorage/lib/active_storage/service/azure_storage_service.rb b/activestorage/lib/active_storage/service/azure_storage_service.rb
new file mode 100644
index 0000000000..d0ee2273c5
--- /dev/null
+++ b/activestorage/lib/active_storage/service/azure_storage_service.rb
@@ -0,0 +1,120 @@
+# frozen_string_literal: true
+
+require "active_support/core_ext/numeric/bytes"
+require "azure/storage"
+require "azure/storage/core/auth/shared_access_signature"
+
+module ActiveStorage
+ # Wraps the Microsoft Azure Storage Blob Service as an Active Storage service.
+ # See ActiveStorage::Service for the generic API documentation that applies to all services.
+ class Service::AzureStorageService < Service
+ attr_reader :client, :path, :blobs, :container, :signer
+
+ def initialize(path:, storage_account_name:, storage_access_key:, container:)
+ @client = Azure::Storage::Client.create(storage_account_name: storage_account_name, storage_access_key: storage_access_key)
+ @signer = Azure::Storage::Core::Auth::SharedAccessSignature.new(storage_account_name, storage_access_key)
+ @blobs = client.blob_client
+ @container = container
+ @path = path
+ end
+
+ def upload(key, io, checksum: nil)
+ instrument :upload, key, checksum: checksum do
+ begin
+ blobs.create_block_blob(container, key, io, content_md5: checksum)
+ rescue Azure::Core::Http::HTTPError
+ raise ActiveStorage::IntegrityError
+ end
+ end
+ end
+
+ def download(key)
+ if block_given?
+ instrument :streaming_download, key do
+ stream(key, &block)
+ end
+ else
+ instrument :download, key do
+ _, io = blobs.get_blob(container, key)
+ io.force_encoding(Encoding::BINARY)
+ end
+ end
+ end
+
+ def delete(key)
+ instrument :delete, key do
+ begin
+ blobs.delete_blob(container, key)
+ rescue Azure::Core::Http::HTTPError
+ false
+ end
+ end
+ end
+
+ def exist?(key)
+ instrument :exist, key do |payload|
+ answer = blob_for(key).present?
+ payload[:exist] = answer
+ answer
+ end
+ end
+
+ def url(key, expires_in:, disposition:, filename:, content_type:)
+ instrument :url, key do |payload|
+ base_url = url_for(key)
+ generated_url = signer.signed_uri(URI(base_url), false, permissions: "r",
+ expiry: format_expiry(expires_in), content_type: content_type,
+ content_disposition: "#{disposition}; filename=\"#{filename}\"").to_s
+
+ payload[:url] = generated_url
+
+ generated_url
+ end
+ end
+
+ def url_for_direct_upload(key, expires_in:, content_type:, content_length:, checksum:)
+ instrument :url, key do |payload|
+ base_url = url_for(key)
+ generated_url = signer.signed_uri(URI(base_url), false, permissions: "rw",
+ expiry: format_expiry(expires_in)).to_s
+
+ payload[:url] = generated_url
+
+ generated_url
+ end
+ end
+
+ def headers_for_direct_upload(key, content_type:, checksum:, **)
+ { "Content-Type" => content_type, "Content-MD5" => checksum, "x-ms-blob-type" => "BlockBlob" }
+ end
+
+ private
+ def url_for(key)
+ "#{path}/#{container}/#{key}"
+ end
+
+ def blob_for(key)
+ blobs.get_blob_properties(container, key)
+ rescue Azure::Core::Http::HTTPError
+ false
+ end
+
+ def format_expiry(expires_in)
+ expires_in ? Time.now.utc.advance(seconds: expires_in).iso8601 : nil
+ end
+
+ # Reads the object for the given key in chunks, yielding each to the block.
+ def stream(key, options = {}, &block)
+ blob = blob_for(key)
+
+ chunk_size = 5.megabytes
+ offset = 0
+
+ while offset < blob.properties[:content_length]
+ _, io = blobs.get_blob(container, key, start_range: offset, end_range: offset + chunk_size - 1)
+ yield io
+ offset += chunk_size
+ end
+ end
+ end
+end
diff --git a/activestorage/lib/active_storage/service/configurator.rb b/activestorage/lib/active_storage/service/configurator.rb
new file mode 100644
index 0000000000..39951fd026
--- /dev/null
+++ b/activestorage/lib/active_storage/service/configurator.rb
@@ -0,0 +1,32 @@
+# frozen_string_literal: true
+
+module ActiveStorage
+ class Service::Configurator #:nodoc:
+ attr_reader :configurations
+
+ def self.build(service_name, configurations)
+ new(configurations).build(service_name)
+ end
+
+ def initialize(configurations)
+ @configurations = configurations.deep_symbolize_keys
+ end
+
+ def build(service_name)
+ config = config_for(service_name.to_sym)
+ resolve(config.fetch(:service)).build(**config, configurator: self)
+ end
+
+ private
+ def config_for(name)
+ configurations.fetch name do
+ raise "Missing configuration for the #{name.inspect} Active Storage service. Configurations available for #{configurations.keys.inspect}"
+ end
+ end
+
+ def resolve(class_name)
+ require "active_storage/service/#{class_name.to_s.underscore}_service"
+ ActiveStorage::Service.const_get(:"#{class_name}Service")
+ end
+ end
+end
diff --git a/activestorage/lib/active_storage/service/disk_service.rb b/activestorage/lib/active_storage/service/disk_service.rb
new file mode 100644
index 0000000000..1d121dcb6c
--- /dev/null
+++ b/activestorage/lib/active_storage/service/disk_service.rb
@@ -0,0 +1,128 @@
+# frozen_string_literal: true
+
+require "fileutils"
+require "pathname"
+require "digest/md5"
+require "active_support/core_ext/numeric/bytes"
+
+module ActiveStorage
+ # Wraps a local disk path as an Active Storage service. See ActiveStorage::Service for the generic API
+ # documentation that applies to all services.
+ class Service::DiskService < Service
+ attr_reader :root
+
+ def initialize(root:)
+ @root = root
+ end
+
+ def upload(key, io, checksum: nil)
+ instrument :upload, key, checksum: checksum do
+ IO.copy_stream(io, make_path_for(key))
+ ensure_integrity_of(key, checksum) if checksum
+ end
+ end
+
+ def download(key)
+ if block_given?
+ instrument :streaming_download, key do
+ File.open(path_for(key), "rb") do |file|
+ while data = file.read(64.kilobytes)
+ yield data
+ end
+ end
+ end
+ else
+ instrument :download, key do
+ File.binread path_for(key)
+ end
+ end
+ end
+
+ def delete(key)
+ instrument :delete, key do
+ begin
+ File.delete path_for(key)
+ rescue Errno::ENOENT
+ # Ignore files already deleted
+ end
+ end
+ end
+
+ def exist?(key)
+ instrument :exist, key do |payload|
+ answer = File.exist? path_for(key)
+ payload[:exist] = answer
+ answer
+ end
+ end
+
+ def url(key, expires_in:, disposition:, filename:, content_type:)
+ instrument :url, key do |payload|
+ verified_key_with_expiration = ActiveStorage.verifier.generate(key, expires_in: expires_in, purpose: :blob_key)
+
+ generated_url =
+ if defined?(Rails.application)
+ Rails.application.routes.url_helpers.rails_disk_service_path \
+ verified_key_with_expiration,
+ disposition: disposition, filename: filename, content_type: content_type
+ else
+ "/rails/active_storage/disk/#{verified_key_with_expiration}/#{filename}?disposition=#{disposition}&content_type=#{content_type}"
+ end
+
+ payload[:url] = generated_url
+
+ generated_url
+ end
+ end
+
+ def url_for_direct_upload(key, expires_in:, content_type:, content_length:, checksum:)
+ instrument :url, key do |payload|
+ verified_token_with_expiration = ActiveStorage.verifier.generate(
+ {
+ key: key,
+ content_type: content_type,
+ content_length: content_length,
+ checksum: checksum
+ },
+ { expires_in: expires_in,
+ purpose: :blob_token }
+ )
+
+ generated_url =
+ if defined?(Rails.application)
+ Rails.application.routes.url_helpers.update_rails_disk_service_path verified_token_with_expiration
+ else
+ "/rails/active_storage/disk/#{verified_token_with_expiration}"
+ end
+
+ payload[:url] = generated_url
+
+ generated_url
+ end
+ end
+
+ def headers_for_direct_upload(key, content_type:, **)
+ { "Content-Type" => content_type }
+ end
+
+ private
+ def path_for(key)
+ File.join root, folder_for(key), key
+ end
+
+ def folder_for(key)
+ [ key[0..1], key[2..3] ].join("/")
+ end
+
+ def make_path_for(key)
+ path_for(key).tap { |path| FileUtils.mkdir_p File.dirname(path) }
+ end
+
+ def ensure_integrity_of(key, checksum)
+ unless Digest::MD5.file(path_for(key)).base64digest == checksum
+ delete key
+ raise ActiveStorage::IntegrityError
+ end
+ end
+ end
+end
diff --git a/activestorage/lib/active_storage/service/gcs_service.rb b/activestorage/lib/active_storage/service/gcs_service.rb
new file mode 100644
index 0000000000..e656f2c69f
--- /dev/null
+++ b/activestorage/lib/active_storage/service/gcs_service.rb
@@ -0,0 +1,83 @@
+# frozen_string_literal: true
+
+require "google/cloud/storage"
+require "active_support/core_ext/object/to_query"
+
+module ActiveStorage
+ # Wraps the Google Cloud Storage as an Active Storage service. See ActiveStorage::Service for the generic API
+ # documentation that applies to all services.
+ class Service::GCSService < Service
+ attr_reader :client, :bucket
+
+ def initialize(project:, keyfile:, bucket:)
+ @client = Google::Cloud::Storage.new(project: project, keyfile: keyfile)
+ @bucket = @client.bucket(bucket)
+ end
+
+ def upload(key, io, checksum: nil)
+ instrument :upload, key, checksum: checksum do
+ begin
+ bucket.create_file(io, key, md5: checksum)
+ rescue Google::Cloud::InvalidArgumentError
+ raise ActiveStorage::IntegrityError
+ end
+ end
+ end
+
+ # FIXME: Add streaming when given a block
+ def download(key)
+ instrument :download, key do
+ io = file_for(key).download
+ io.rewind
+ io.read
+ end
+ end
+
+ def delete(key)
+ instrument :delete, key do
+ file_for(key).try(:delete)
+ end
+ end
+
+ def exist?(key)
+ instrument :exist, key do |payload|
+ answer = file_for(key).present?
+ payload[:exist] = answer
+ answer
+ end
+ end
+
+ def url(key, expires_in:, disposition:, filename:, content_type:)
+ instrument :url, key do |payload|
+ generated_url = file_for(key).signed_url expires: expires_in, query: {
+ "response-content-disposition" => "#{disposition}; filename=\"#{filename}\"",
+ "response-content-type" => content_type
+ }
+
+ payload[:url] = generated_url
+
+ generated_url
+ end
+ end
+
+ def url_for_direct_upload(key, expires_in:, content_type:, content_length:, checksum:)
+ instrument :url, key do |payload|
+ generated_url = bucket.signed_url key, method: "PUT", expires: expires_in,
+ content_type: content_type, content_md5: checksum
+
+ payload[:url] = generated_url
+
+ generated_url
+ end
+ end
+
+ def headers_for_direct_upload(key, content_type:, checksum:, **)
+ { "Content-Type" => content_type, "Content-MD5" => checksum }
+ end
+
+ private
+ def file_for(key)
+ bucket.file(key)
+ end
+ end
+end
diff --git a/activestorage/lib/active_storage/service/mirror_service.rb b/activestorage/lib/active_storage/service/mirror_service.rb
new file mode 100644
index 0000000000..39e922f7ab
--- /dev/null
+++ b/activestorage/lib/active_storage/service/mirror_service.rb
@@ -0,0 +1,50 @@
+# frozen_string_literal: true
+
+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+.
+ class Service::MirrorService < Service
+ attr_reader :primary, :mirrors
+
+ delegate :download, :exist?, :url, to: :primary
+
+ # Stitch together from named services.
+ def self.build(primary:, mirrors:, configurator:, **options) #:nodoc:
+ new \
+ primary: configurator.build(primary),
+ mirrors: mirrors.collect { |name| configurator.build name }
+ end
+
+ def initialize(primary:, mirrors:)
+ @primary, @mirrors = primary, mirrors
+ end
+
+ # Upload the +io+ to the +key+ specified to all services. If a +checksum+ is provided, all services will
+ # ensure a match when the upload has completed or raise an ActiveStorage::IntegrityError.
+ def upload(key, io, checksum: nil)
+ each_service.collect do |service|
+ service.upload key, io.tap(&:rewind), checksum: checksum
+ end
+ end
+
+ # Delete the file at the +key+ on all services.
+ def delete(key)
+ perform_across_services :delete, key
+ end
+
+ private
+ def each_service(&block)
+ [ primary, *mirrors ].each(&block)
+ end
+
+ def perform_across_services(method, *args)
+ # FIXME: Convert to be threaded
+ each_service.collect do |service|
+ service.public_send method, *args
+ end
+ end
+ end
+end
diff --git a/activestorage/lib/active_storage/service/s3_service.rb b/activestorage/lib/active_storage/service/s3_service.rb
new file mode 100644
index 0000000000..bef2ecbea9
--- /dev/null
+++ b/activestorage/lib/active_storage/service/s3_service.rb
@@ -0,0 +1,100 @@
+# frozen_string_literal: true
+
+require "aws-sdk"
+require "active_support/core_ext/numeric/bytes"
+
+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
+
+ def initialize(access_key_id:, secret_access_key:, region:, bucket:, upload: {}, **options)
+ @client = Aws::S3::Resource.new(access_key_id: access_key_id, secret_access_key: secret_access_key, region: region, **options)
+ @bucket = @client.bucket(bucket)
+
+ @upload_options = upload
+ end
+
+ def upload(key, io, checksum: nil)
+ instrument :upload, key, checksum: checksum do
+ begin
+ object_for(key).put(upload_options.merge(body: io, content_md5: checksum))
+ rescue Aws::S3::Errors::BadDigest
+ raise ActiveStorage::IntegrityError
+ end
+ end
+ end
+
+ def download(key)
+ if block_given?
+ instrument :streaming_download, key do
+ stream(key, &block)
+ end
+ else
+ instrument :download, key do
+ object_for(key).get.body.read.force_encoding(Encoding::BINARY)
+ end
+ end
+ end
+
+ def delete(key)
+ instrument :delete, key do
+ object_for(key).delete
+ end
+ end
+
+ def exist?(key)
+ instrument :exist, key do |payload|
+ answer = object_for(key).exists?
+ payload[:exist] = answer
+ answer
+ end
+ end
+
+ def url(key, expires_in:, disposition:, filename:, content_type:)
+ instrument :url, key do |payload|
+ generated_url = object_for(key).presigned_url :get, expires_in: expires_in,
+ response_content_disposition: "#{disposition}; filename=\"#{filename}\"",
+ response_content_type: content_type
+
+ payload[:url] = generated_url
+
+ generated_url
+ end
+ end
+
+ def url_for_direct_upload(key, expires_in:, content_type:, content_length:, checksum:)
+ instrument :url, key do |payload|
+ generated_url = object_for(key).presigned_url :put, expires_in: expires_in,
+ content_type: content_type, content_length: content_length, content_md5: checksum
+
+ payload[:url] = generated_url
+
+ generated_url
+ end
+ end
+
+ def headers_for_direct_upload(key, content_type:, checksum:, **)
+ { "Content-Type" => content_type, "Content-MD5" => checksum }
+ end
+
+ private
+ def object_for(key)
+ bucket.object(key)
+ end
+
+ # Reads the object for the given key in chunks, yielding each to the block.
+ def stream(key, options = {}, &block)
+ object = object_for(key)
+
+ chunk_size = 5.megabytes
+ offset = 0
+
+ while offset < object.content_length
+ yield object.read(options.merge(range: "bytes=#{offset}-#{offset + chunk_size - 1}"))
+ offset += chunk_size
+ end
+ end
+ end
+end
diff --git a/activestorage/lib/active_storage/version.rb b/activestorage/lib/active_storage/version.rb
new file mode 100644
index 0000000000..4b6631832b
--- /dev/null
+++ b/activestorage/lib/active_storage/version.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+require_relative "gem_version"
+
+module ActiveStorage
+ # Returns the version of the currently loaded ActiveStorage as a <tt>Gem::Version</tt>
+ def self.version
+ gem_version
+ end
+end
diff --git a/activestorage/lib/tasks/activestorage.rake b/activestorage/lib/tasks/activestorage.rake
new file mode 100644
index 0000000000..ef923e5926
--- /dev/null
+++ b/activestorage/lib/tasks/activestorage.rake
@@ -0,0 +1,8 @@
+# frozen_string_literal: true
+
+namespace :active_storage do
+ desc "Copy over the migration needed to the application"
+ task install: :environment do
+ Rake::Task["active_storage:install:migrations"].invoke
+ end
+end
diff --git a/activestorage/package.json b/activestorage/package.json
new file mode 100644
index 0000000000..b481295a8d
--- /dev/null
+++ b/activestorage/package.json
@@ -0,0 +1,33 @@
+{
+ "name": "activestorage",
+ "version": "5.2.0.alpha",
+ "description": "Attach cloud and local files in Rails applications",
+ "main": "app/assets/javascripts/activestorage.js",
+ "files": [
+ "app/assets/javascripts/*.js"
+ ],
+ "homepage": "http://rubyonrails.org/",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/rails/rails.git"
+ },
+ "bugs": {
+ "url": "https://github.com/rails/rails/issues"
+ },
+ "author": "Javan Makhmali <javan@javan.us>",
+ "license": "MIT",
+ "devDependencies": {
+ "babel-core": "^6.25.0",
+ "babel-loader": "^7.1.1",
+ "babel-preset-env": "^1.6.0",
+ "eslint": "^4.3.0",
+ "eslint-plugin-import": "^2.7.0",
+ "spark-md5": "^3.0.0",
+ "webpack": "^3.4.0"
+ },
+ "scripts": {
+ "prebuild": "yarn lint",
+ "build": "webpack -p",
+ "lint": "eslint app/javascript"
+ }
+}
diff --git a/activestorage/test/controllers/blobs_controller_test.rb b/activestorage/test/controllers/blobs_controller_test.rb
new file mode 100644
index 0000000000..c37b9c8a10
--- /dev/null
+++ b/activestorage/test/controllers/blobs_controller_test.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+require "test_helper"
+require "database/setup"
+
+class ActiveStorage::BlobsControllerTest < ActionDispatch::IntegrationTest
+ setup do
+ @blob = create_image_blob filename: "racecar.jpg"
+ end
+
+ test "showing blob utilizes browser caching" do
+ get rails_blob_url(@blob)
+
+ assert_redirected_to(/racecar\.jpg/)
+ assert_equal "max-age=300, private", @response.headers["Cache-Control"]
+ end
+end
diff --git a/activestorage/test/controllers/direct_uploads_controller_test.rb b/activestorage/test/controllers/direct_uploads_controller_test.rb
new file mode 100644
index 0000000000..888767086c
--- /dev/null
+++ b/activestorage/test/controllers/direct_uploads_controller_test.rb
@@ -0,0 +1,124 @@
+# frozen_string_literal: true
+
+require "test_helper"
+require "database/setup"
+
+if SERVICE_CONFIGURATIONS[:s3] && SERVICE_CONFIGURATIONS[:s3][:access_key_id].present?
+ class ActiveStorage::S3DirectUploadsControllerTest < 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 "creating new direct upload" do
+ checksum = Digest::MD5.base64digest("Hello")
+
+ post rails_direct_uploads_url, params: { blob: {
+ filename: "hello.txt", byte_size: 6, checksum: checksum, content_type: "text/plain" } }
+
+ response.parsed_body.tap do |details|
+ assert_equal ActiveStorage::Blob.find(details["id"]), ActiveStorage::Blob.find_signed(details["signed_id"])
+ assert_equal "hello.txt", details["filename"]
+ assert_equal 6, details["byte_size"]
+ assert_equal checksum, details["checksum"]
+ assert_equal "text/plain", details["content_type"]
+ assert_match SERVICE_CONFIGURATIONS[:s3][:bucket], details["direct_upload"]["url"]
+ assert_match(/s3\.(\S+)?amazonaws\.com/, details["direct_upload"]["url"])
+ assert_equal({ "Content-Type" => "text/plain", "Content-MD5" => checksum }, details["direct_upload"]["headers"])
+ end
+ end
+ end
+else
+ puts "Skipping S3 Direct Upload tests because no S3 configuration was supplied"
+end
+
+if SERVICE_CONFIGURATIONS[:gcs]
+ class ActiveStorage::GCSDirectUploadsControllerTest < ActionDispatch::IntegrationTest
+ setup do
+ @config = SERVICE_CONFIGURATIONS[:gcs]
+
+ @old_service = ActiveStorage::Blob.service
+ ActiveStorage::Blob.service = ActiveStorage::Service.configure(:gcs, SERVICE_CONFIGURATIONS)
+ end
+
+ teardown do
+ ActiveStorage::Blob.service = @old_service
+ end
+
+ test "creating new direct upload" do
+ checksum = Digest::MD5.base64digest("Hello")
+
+ post rails_direct_uploads_url, params: { blob: {
+ filename: "hello.txt", byte_size: 6, checksum: checksum, content_type: "text/plain" } }
+
+ @response.parsed_body.tap do |details|
+ assert_equal ActiveStorage::Blob.find(details["id"]), ActiveStorage::Blob.find_signed(details["signed_id"])
+ assert_equal "hello.txt", details["filename"]
+ assert_equal 6, details["byte_size"]
+ assert_equal checksum, details["checksum"]
+ assert_equal "text/plain", details["content_type"]
+ assert_match %r{storage\.googleapis\.com/#{@config[:bucket]}}, details["direct_upload"]["url"]
+ assert_equal({ "Content-Type" => "text/plain", "Content-MD5" => checksum }, details["direct_upload"]["headers"])
+ end
+ end
+ end
+else
+ puts "Skipping GCS Direct Upload tests because no GCS configuration was supplied"
+end
+
+if SERVICE_CONFIGURATIONS[:azure]
+ class ActiveStorage::AzureStorageDirectUploadsControllerTest < ActionDispatch::IntegrationTest
+ setup do
+ @config = SERVICE_CONFIGURATIONS[:azure]
+
+ @old_service = ActiveStorage::Blob.service
+ ActiveStorage::Blob.service = ActiveStorage::Service.configure(:azure, SERVICE_CONFIGURATIONS)
+ end
+
+ teardown do
+ ActiveStorage::Blob.service = @old_service
+ end
+
+ test "creating new direct upload" do
+ checksum = Digest::MD5.base64digest("Hello")
+
+ post rails_direct_uploads_url, params: { blob: {
+ filename: "hello.txt", byte_size: 6, checksum: checksum, content_type: "text/plain" } }
+
+ @response.parsed_body.tap do |details|
+ assert_equal ActiveStorage::Blob.find(details["id"]), ActiveStorage::Blob.find_signed(details["signed_id"])
+ assert_equal "hello.txt", details["filename"]
+ assert_equal 6, details["byte_size"]
+ assert_equal checksum, details["checksum"]
+ assert_equal "text/plain", details["content_type"]
+ assert_match %r{#{@config[:storage_account_name]}\.blob\.core\.windows\.net/#{@config[:container]}}, details["direct_upload"]["url"]
+ assert_equal({ "Content-Type" => "text/plain", "Content-MD5" => checksum, "x-ms-blob-type" => "BlockBlob" }, details["direct_upload"]["headers"])
+ end
+ end
+ end
+else
+ puts "Skipping Azure Storage Direct Upload tests because no Azure Storage configuration was supplied"
+end
+
+class ActiveStorage::DiskDirectUploadsControllerTest < ActionDispatch::IntegrationTest
+ test "creating new direct upload" do
+ checksum = Digest::MD5.base64digest("Hello")
+
+ post rails_direct_uploads_url, params: { blob: {
+ filename: "hello.txt", byte_size: 6, checksum: checksum, content_type: "text/plain" } }
+
+ @response.parsed_body.tap do |details|
+ assert_equal ActiveStorage::Blob.find(details["id"]), ActiveStorage::Blob.find_signed(details["signed_id"])
+ assert_equal "hello.txt", details["filename"]
+ assert_equal 6, details["byte_size"]
+ assert_equal checksum, details["checksum"]
+ assert_equal "text/plain", details["content_type"]
+ assert_match(/rails\/active_storage\/disk/, details["direct_upload"]["url"])
+ assert_equal({ "Content-Type" => "text/plain" }, details["direct_upload"]["headers"])
+ end
+ end
+end
diff --git a/activestorage/test/controllers/disk_controller_test.rb b/activestorage/test/controllers/disk_controller_test.rb
new file mode 100644
index 0000000000..53a086f214
--- /dev/null
+++ b/activestorage/test/controllers/disk_controller_test.rb
@@ -0,0 +1,59 @@
+# frozen_string_literal: true
+
+require "test_helper"
+require "database/setup"
+
+class ActiveStorage::DiskControllerTest < ActionDispatch::IntegrationTest
+ test "showing blob inline" do
+ blob = create_blob
+
+ get blob.service_url
+ assert_equal "inline; filename=\"#{blob.filename.base}\"", @response.headers["Content-Disposition"]
+ assert_equal "text/plain", @response.headers["Content-Type"]
+ end
+
+ test "showing blob as attachment" do
+ blob = create_blob
+
+ get blob.service_url(disposition: :attachment)
+ assert_equal "attachment; filename=\"#{blob.filename.base}\"", @response.headers["Content-Disposition"]
+ assert_equal "text/plain", @response.headers["Content-Type"]
+ end
+
+
+ test "directly uploading blob with integrity" do
+ data = "Something else entirely!"
+ blob = create_blob_before_direct_upload byte_size: data.size, checksum: Digest::MD5.base64digest(data)
+
+ put blob.service_url_for_direct_upload, params: data, headers: { "Content-Type" => "text/plain" }
+ assert_response :no_content
+ assert_equal data, blob.download
+ end
+
+ test "directly uploading blob without integrity" do
+ data = "Something else entirely!"
+ blob = create_blob_before_direct_upload byte_size: data.size, checksum: Digest::MD5.base64digest("bad data")
+
+ put blob.service_url_for_direct_upload, params: data
+ assert_response :unprocessable_entity
+ assert_not blob.service.exist?(blob.key)
+ end
+
+ test "directly uploading blob with mismatched content type" do
+ data = "Something else entirely!"
+ blob = create_blob_before_direct_upload byte_size: data.size, checksum: Digest::MD5.base64digest(data)
+
+ put blob.service_url_for_direct_upload, params: data, headers: { "Content-Type" => "application/octet-stream" }
+ assert_response :unprocessable_entity
+ assert_not blob.service.exist?(blob.key)
+ end
+
+ test "directly uploading blob with mismatched content length" do
+ data = "Something else entirely!"
+ blob = create_blob_before_direct_upload byte_size: data.size - 1, checksum: Digest::MD5.base64digest(data)
+
+ put blob.service_url_for_direct_upload, params: data, headers: { "Content-Type" => "text/plain" }
+ assert_response :unprocessable_entity
+ assert_not blob.service.exist?(blob.key)
+ end
+end
diff --git a/activestorage/test/controllers/variants_controller_test.rb b/activestorage/test/controllers/variants_controller_test.rb
new file mode 100644
index 0000000000..0a049f3bc4
--- /dev/null
+++ b/activestorage/test/controllers/variants_controller_test.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+require "test_helper"
+require "database/setup"
+
+class ActiveStorage::VariantsControllerTest < ActionDispatch::IntegrationTest
+ setup do
+ @blob = create_image_blob filename: "racecar.jpg"
+ end
+
+ test "showing variant inline" do
+ get rails_blob_variation_url(
+ filename: @blob.filename,
+ signed_blob_id: @blob.signed_id,
+ variation_key: ActiveStorage::Variation.encode(resize: "100x100"))
+
+ assert_redirected_to(/racecar\.jpg\?.*disposition=inline/)
+
+ image = read_image_variant(@blob.variant(resize: "100x100"))
+ assert_equal 100, image.width
+ assert_equal 67, image.height
+ end
+end
diff --git a/activestorage/test/database/create_users_migration.rb b/activestorage/test/database/create_users_migration.rb
new file mode 100644
index 0000000000..317daa87b5
--- /dev/null
+++ b/activestorage/test/database/create_users_migration.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class ActiveStorageCreateUsers < ActiveRecord::Migration[5.1]
+ def change
+ create_table :users do |t|
+ t.string :name
+ end
+ end
+end
diff --git a/activestorage/test/database/setup.rb b/activestorage/test/database/setup.rb
new file mode 100644
index 0000000000..87564499e6
--- /dev/null
+++ b/activestorage/test/database/setup.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+require_relative "create_users_migration"
+
+ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
+ActiveRecord::Migrator.migrate File.expand_path("../../../db/migrate", __FILE__)
+ActiveStorageCreateUsers.migrate(:up)
diff --git a/activestorage/test/dummy/Rakefile b/activestorage/test/dummy/Rakefile
new file mode 100644
index 0000000000..c4f9523878
--- /dev/null
+++ b/activestorage/test/dummy/Rakefile
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+require_relative "config/application"
+
+Rails.application.load_tasks
diff --git a/activestorage/test/dummy/app/assets/config/manifest.js b/activestorage/test/dummy/app/assets/config/manifest.js
new file mode 100644
index 0000000000..a8adebe722
--- /dev/null
+++ b/activestorage/test/dummy/app/assets/config/manifest.js
@@ -0,0 +1,5 @@
+
+//= link_tree ../images
+//= link_directory ../javascripts .js
+//= link_directory ../stylesheets .css
+//= link active_storage_manifest.js
diff --git a/activestorage/test/dummy/app/assets/images/.keep b/activestorage/test/dummy/app/assets/images/.keep
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/activestorage/test/dummy/app/assets/images/.keep
diff --git a/activestorage/test/dummy/app/assets/javascripts/application.js b/activestorage/test/dummy/app/assets/javascripts/application.js
new file mode 100644
index 0000000000..e54c6461cc
--- /dev/null
+++ b/activestorage/test/dummy/app/assets/javascripts/application.js
@@ -0,0 +1,13 @@
+// This is a manifest file that'll be compiled into application.js, which will include all the files
+// listed below.
+//
+// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
+// or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
+//
+// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
+// compiled file. JavaScript code in this file should be added after the last require_* statement.
+//
+// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
+// about supported directives.
+//
+//= require_tree .
diff --git a/activestorage/test/dummy/app/assets/stylesheets/application.css b/activestorage/test/dummy/app/assets/stylesheets/application.css
new file mode 100644
index 0000000000..0ebd7fe829
--- /dev/null
+++ b/activestorage/test/dummy/app/assets/stylesheets/application.css
@@ -0,0 +1,15 @@
+/*
+ * This is a manifest file that'll be compiled into application.css, which will include all the files
+ * listed below.
+ *
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
+ * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
+ *
+ * You're free to add application-wide styles to this file and they'll appear at the bottom of the
+ * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
+ * files in this directory. Styles in this file should be added after the last require_* statement.
+ * It is generally better to create a new file per style scope.
+ *
+ *= require_tree .
+ *= require_self
+ */
diff --git a/activestorage/test/dummy/app/controllers/application_controller.rb b/activestorage/test/dummy/app/controllers/application_controller.rb
new file mode 100644
index 0000000000..280cc28ce2
--- /dev/null
+++ b/activestorage/test/dummy/app/controllers/application_controller.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+class ApplicationController < ActionController::Base
+ protect_from_forgery with: :exception
+end
diff --git a/activestorage/test/dummy/app/controllers/concerns/.keep b/activestorage/test/dummy/app/controllers/concerns/.keep
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/activestorage/test/dummy/app/controllers/concerns/.keep
diff --git a/activestorage/test/dummy/app/helpers/application_helper.rb b/activestorage/test/dummy/app/helpers/application_helper.rb
new file mode 100644
index 0000000000..15b06f0f67
--- /dev/null
+++ b/activestorage/test/dummy/app/helpers/application_helper.rb
@@ -0,0 +1,4 @@
+# frozen_string_literal: true
+
+module ApplicationHelper
+end
diff --git a/activestorage/test/dummy/app/jobs/application_job.rb b/activestorage/test/dummy/app/jobs/application_job.rb
new file mode 100644
index 0000000000..d92ffddcb5
--- /dev/null
+++ b/activestorage/test/dummy/app/jobs/application_job.rb
@@ -0,0 +1,4 @@
+# frozen_string_literal: true
+
+class ApplicationJob < ActiveJob::Base
+end
diff --git a/activestorage/test/dummy/app/models/application_record.rb b/activestorage/test/dummy/app/models/application_record.rb
new file mode 100644
index 0000000000..71fbba5b32
--- /dev/null
+++ b/activestorage/test/dummy/app/models/application_record.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+class ApplicationRecord < ActiveRecord::Base
+ self.abstract_class = true
+end
diff --git a/activestorage/test/dummy/app/models/concerns/.keep b/activestorage/test/dummy/app/models/concerns/.keep
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/activestorage/test/dummy/app/models/concerns/.keep
diff --git a/activestorage/test/dummy/app/views/layouts/application.html.erb b/activestorage/test/dummy/app/views/layouts/application.html.erb
new file mode 100644
index 0000000000..a6eb0174b7
--- /dev/null
+++ b/activestorage/test/dummy/app/views/layouts/application.html.erb
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>Dummy</title>
+ <%= csrf_meta_tags %>
+
+ <%= stylesheet_link_tag 'application', media: 'all' %>
+ <%= javascript_include_tag 'application' %>
+ </head>
+
+ <body>
+ <%= yield %>
+ </body>
+</html>
diff --git a/activestorage/test/dummy/bin/bundle b/activestorage/test/dummy/bin/bundle
new file mode 100755
index 0000000000..277e128251
--- /dev/null
+++ b/activestorage/test/dummy/bin/bundle
@@ -0,0 +1,5 @@
+#!/usr/bin/env ruby
+# frozen_string_literal: true
+
+ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", __FILE__)
+load Gem.bin_path("bundler", "bundle")
diff --git a/activestorage/test/dummy/bin/rails b/activestorage/test/dummy/bin/rails
new file mode 100755
index 0000000000..22f2d8deee
--- /dev/null
+++ b/activestorage/test/dummy/bin/rails
@@ -0,0 +1,6 @@
+#!/usr/bin/env ruby
+# frozen_string_literal: true
+
+APP_PATH = File.expand_path("../config/application", __dir__)
+require_relative "../config/boot"
+require "rails/commands"
diff --git a/activestorage/test/dummy/bin/rake b/activestorage/test/dummy/bin/rake
new file mode 100755
index 0000000000..e436ea54a1
--- /dev/null
+++ b/activestorage/test/dummy/bin/rake
@@ -0,0 +1,6 @@
+#!/usr/bin/env ruby
+# frozen_string_literal: true
+
+require_relative "../config/boot"
+require "rake"
+Rake.application.run
diff --git a/activestorage/test/dummy/bin/yarn b/activestorage/test/dummy/bin/yarn
new file mode 100755
index 0000000000..c9b7498378
--- /dev/null
+++ b/activestorage/test/dummy/bin/yarn
@@ -0,0 +1,13 @@
+#!/usr/bin/env ruby
+# frozen_string_literal: true
+
+VENDOR_PATH = File.expand_path("..", __dir__)
+Dir.chdir(VENDOR_PATH) do
+ begin
+ exec "yarnpkg #{ARGV.join(" ")}"
+ rescue Errno::ENOENT
+ $stderr.puts "Yarn executable was not detected in the system."
+ $stderr.puts "Download Yarn at https://yarnpkg.com/en/docs/install"
+ exit 1
+ end
+end
diff --git a/activestorage/test/dummy/config.ru b/activestorage/test/dummy/config.ru
new file mode 100644
index 0000000000..bff88d608a
--- /dev/null
+++ b/activestorage/test/dummy/config.ru
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+# This file is used by Rack-based servers to start the application.
+
+require_relative "config/environment"
+
+run Rails.application
diff --git a/activestorage/test/dummy/config/application.rb b/activestorage/test/dummy/config/application.rb
new file mode 100644
index 0000000000..7ee6625bb5
--- /dev/null
+++ b/activestorage/test/dummy/config/application.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+require_relative "boot"
+
+require "rails"
+require "active_model/railtie"
+require "active_job/railtie"
+require "active_record/railtie"
+require "action_controller/railtie"
+require "action_view/railtie"
+require "sprockets/railtie"
+require "active_storage/engine"
+#require "action_mailer/railtie"
+#require "rails/test_unit/railtie"
+#require "action_cable/engine"
+
+
+Bundler.require(*Rails.groups)
+
+module Dummy
+ class Application < Rails::Application
+ config.load_defaults 5.1
+
+ config.active_storage.service = :local
+ end
+end
diff --git a/activestorage/test/dummy/config/boot.rb b/activestorage/test/dummy/config/boot.rb
new file mode 100644
index 0000000000..59459d4ae3
--- /dev/null
+++ b/activestorage/test/dummy/config/boot.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+# Set up gems listed in the Gemfile.
+ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../../Gemfile", __dir__)
+
+require "bundler/setup" if File.exist?(ENV["BUNDLE_GEMFILE"])
+$LOAD_PATH.unshift File.expand_path("../../../lib", __dir__)
diff --git a/activestorage/test/dummy/config/database.yml b/activestorage/test/dummy/config/database.yml
new file mode 100644
index 0000000000..0d02f24980
--- /dev/null
+++ b/activestorage/test/dummy/config/database.yml
@@ -0,0 +1,25 @@
+# SQLite version 3.x
+# gem install sqlite3
+#
+# Ensure the SQLite 3 gem is defined in your Gemfile
+# gem 'sqlite3'
+#
+default: &default
+ adapter: sqlite3
+ pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
+ timeout: 5000
+
+development:
+ <<: *default
+ database: db/development.sqlite3
+
+# Warning: The database defined as "test" will be erased and
+# re-generated from your development database when you run "rake".
+# Do not set this db to the same as development or production.
+test:
+ <<: *default
+ database: db/test.sqlite3
+
+production:
+ <<: *default
+ database: db/production.sqlite3
diff --git a/activestorage/test/dummy/config/environment.rb b/activestorage/test/dummy/config/environment.rb
new file mode 100644
index 0000000000..7df99e89c6
--- /dev/null
+++ b/activestorage/test/dummy/config/environment.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+# Load the Rails application.
+require_relative "application"
+
+# Initialize the Rails application.
+Rails.application.initialize!
diff --git a/activestorage/test/dummy/config/environments/development.rb b/activestorage/test/dummy/config/environments/development.rb
new file mode 100644
index 0000000000..47fc5bf25c
--- /dev/null
+++ b/activestorage/test/dummy/config/environments/development.rb
@@ -0,0 +1,51 @@
+# frozen_string_literal: true
+
+Rails.application.configure do
+ # Settings specified here will take precedence over those in config/application.rb.
+
+ # In the development environment your application's code is reloaded on
+ # every request. This slows down response time but is perfect for development
+ # since you don't have to restart the web server when you make code changes.
+ config.cache_classes = false
+
+ # Do not eager load code on boot.
+ config.eager_load = false
+
+ # Show full error reports.
+ config.consider_all_requests_local = true
+
+ # Enable/disable caching. By default caching is disabled.
+ if Rails.root.join("tmp/caching-dev.txt").exist?
+ config.action_controller.perform_caching = true
+
+ config.cache_store = :memory_store
+ config.public_file_server.headers = {
+ "Cache-Control" => "public, max-age=#{2.days.seconds.to_i}"
+ }
+ else
+ config.action_controller.perform_caching = false
+
+ config.cache_store = :null_store
+ end
+
+ # Print deprecation notices to the Rails logger.
+ config.active_support.deprecation = :log
+
+ # Raise an error on page load if there are pending migrations.
+ config.active_record.migration_error = :page_load
+
+ # Debug mode disables concatenation and preprocessing of assets.
+ # This option may cause significant delays in view rendering with a large
+ # number of complex assets.
+ config.assets.debug = true
+
+ # Suppress logger output for asset requests.
+ config.assets.quiet = true
+
+ # Raises error for missing translations
+ # config.action_view.raise_on_missing_translations = true
+
+ # Use an evented file watcher to asynchronously detect changes in source code,
+ # routes, locales, etc. This feature depends on the listen gem.
+ # config.file_watcher = ActiveSupport::EventedFileUpdateChecker
+end
diff --git a/activestorage/test/dummy/config/environments/production.rb b/activestorage/test/dummy/config/environments/production.rb
new file mode 100644
index 0000000000..34ac3bc561
--- /dev/null
+++ b/activestorage/test/dummy/config/environments/production.rb
@@ -0,0 +1,84 @@
+# frozen_string_literal: true
+
+Rails.application.configure do
+ # Settings specified here will take precedence over those in config/application.rb.
+
+ # Code is not reloaded between requests.
+ config.cache_classes = true
+
+ # Eager load code on boot. This eager loads most of Rails and
+ # your application in memory, allowing both threaded web servers
+ # and those relying on copy on write to perform better.
+ # Rake tasks automatically ignore this option for performance.
+ config.eager_load = true
+
+ # Full error reports are disabled and caching is turned on.
+ config.consider_all_requests_local = false
+ config.action_controller.perform_caching = true
+
+ # Attempt to read encrypted secrets from `config/secrets.yml.enc`.
+ # Requires an encryption key in `ENV["RAILS_MASTER_KEY"]` or
+ # `config/secrets.yml.key`.
+ config.read_encrypted_secrets = true
+
+ # Disable serving static files from the `/public` folder by default since
+ # Apache or NGINX already handles this.
+ config.public_file_server.enabled = ENV["RAILS_SERVE_STATIC_FILES"].present?
+
+ # Compress JavaScripts and CSS.
+ config.assets.js_compressor = :uglifier
+ # config.assets.css_compressor = :sass
+
+ # Do not fallback to assets pipeline if a precompiled asset is missed.
+ config.assets.compile = false
+
+ # `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb
+
+ # Enable serving of images, stylesheets, and JavaScripts from an asset server.
+ # config.action_controller.asset_host = 'http://assets.example.com'
+
+ # Specifies the header that your server uses for sending files.
+ # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache
+ # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX
+
+ # Mount Action Cable outside main process or domain
+ # config.action_cable.mount_path = nil
+ # config.action_cable.url = 'wss://example.com/cable'
+ # config.action_cable.allowed_request_origins = [ 'http://example.com', /http:\/\/example.*/ ]
+
+ # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
+ # config.force_ssl = true
+
+ # Use the lowest log level to ensure availability of diagnostic information
+ # when problems arise.
+ config.log_level = :debug
+
+ # Prepend all log lines with the following tags.
+ config.log_tags = [ :request_id ]
+
+ # Use a different cache store in production.
+ # config.cache_store = :mem_cache_store
+
+ # Enable locale fallbacks for I18n (makes lookups for any locale fall back to
+ # the I18n.default_locale when a translation cannot be found).
+ config.i18n.fallbacks = true
+
+ # Send deprecation notices to registered listeners.
+ config.active_support.deprecation = :notify
+
+ # Use default logging formatter so that PID and timestamp are not suppressed.
+ config.log_formatter = ::Logger::Formatter.new
+
+ # Use a different logger for distributed setups.
+ # require 'syslog/logger'
+ # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name')
+
+ if ENV["RAILS_LOG_TO_STDOUT"].present?
+ logger = ActiveSupport::Logger.new(STDOUT)
+ logger.formatter = config.log_formatter
+ config.logger = ActiveSupport::TaggedLogging.new(logger)
+ end
+
+ # Do not dump schema after migrations.
+ config.active_record.dump_schema_after_migration = false
+end
diff --git a/activestorage/test/dummy/config/environments/test.rb b/activestorage/test/dummy/config/environments/test.rb
new file mode 100644
index 0000000000..ce0889e8ae
--- /dev/null
+++ b/activestorage/test/dummy/config/environments/test.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+Rails.application.configure do
+ # Settings specified here will take precedence over those in config/application.rb.
+
+ # The test environment is used exclusively to run your application's
+ # test suite. You never need to work with it otherwise. Remember that
+ # your test database is "scratch space" for the test suite and is wiped
+ # and recreated between test runs. Don't rely on the data there!
+ config.cache_classes = true
+
+ # Do not eager load code on boot. This avoids loading your whole application
+ # just for the purpose of running a single test. If you are using a tool that
+ # preloads Rails for running tests, you may have to set it to true.
+ config.eager_load = false
+
+ # Configure public file server for tests with Cache-Control for performance.
+ config.public_file_server.enabled = true
+ config.public_file_server.headers = {
+ "Cache-Control" => "public, max-age=#{1.hour.seconds.to_i}"
+ }
+
+ # Show full error reports and disable caching.
+ config.consider_all_requests_local = true
+ config.action_controller.perform_caching = false
+
+ # Raise exceptions instead of rendering exception templates.
+ config.action_dispatch.show_exceptions = false
+
+ # Print deprecation notices to the stderr.
+ config.active_support.deprecation = :stderr
+
+ # Raises error for missing translations
+ # config.action_view.raise_on_missing_translations = true
+end
diff --git a/activestorage/test/dummy/config/initializers/application_controller_renderer.rb b/activestorage/test/dummy/config/initializers/application_controller_renderer.rb
new file mode 100644
index 0000000000..315ac48a9a
--- /dev/null
+++ b/activestorage/test/dummy/config/initializers/application_controller_renderer.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+# Be sure to restart your server when you modify this file.
+
+# ApplicationController.renderer.defaults.merge!(
+# http_host: 'example.org',
+# https: false
+# )
diff --git a/activestorage/test/dummy/config/initializers/assets.rb b/activestorage/test/dummy/config/initializers/assets.rb
new file mode 100644
index 0000000000..ba194685a2
--- /dev/null
+++ b/activestorage/test/dummy/config/initializers/assets.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+# Be sure to restart your server when you modify this file.
+
+# Version of your assets, change this if you want to expire all your assets.
+Rails.application.config.assets.version = "1.0"
+
+# Add additional assets to the asset load path.
+# Rails.application.config.assets.paths << Emoji.images_path
+# Add Yarn node_modules folder to the asset load path.
+Rails.application.config.assets.paths << Rails.root.join("node_modules")
+
+# Precompile additional assets.
+# application.js, application.css, and all non-JS/CSS in the app/assets
+# folder are already added.
+# Rails.application.config.assets.precompile += %w( admin.js admin.css )
diff --git a/activestorage/test/dummy/config/initializers/backtrace_silencers.rb b/activestorage/test/dummy/config/initializers/backtrace_silencers.rb
new file mode 100644
index 0000000000..d0f0d3b5df
--- /dev/null
+++ b/activestorage/test/dummy/config/initializers/backtrace_silencers.rb
@@ -0,0 +1,8 @@
+# frozen_string_literal: true
+# Be sure to restart your server when you modify this file.
+
+# You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.
+# Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ }
+
+# You can also remove all the silencers if you're trying to debug a problem that might stem from framework code.
+# Rails.backtrace_cleaner.remove_silencers!
diff --git a/activestorage/test/dummy/config/initializers/cookies_serializer.rb b/activestorage/test/dummy/config/initializers/cookies_serializer.rb
new file mode 100644
index 0000000000..ee8dff9c99
--- /dev/null
+++ b/activestorage/test/dummy/config/initializers/cookies_serializer.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+# Be sure to restart your server when you modify this file.
+
+# Specify a serializer for the signed and encrypted cookie jars.
+# Valid options are :json, :marshal, and :hybrid.
+Rails.application.config.action_dispatch.cookies_serializer = :json
diff --git a/activestorage/test/dummy/config/initializers/filter_parameter_logging.rb b/activestorage/test/dummy/config/initializers/filter_parameter_logging.rb
new file mode 100644
index 0000000000..7a4f47b4c2
--- /dev/null
+++ b/activestorage/test/dummy/config/initializers/filter_parameter_logging.rb
@@ -0,0 +1,6 @@
+# frozen_string_literal: true
+
+# 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]
diff --git a/activestorage/test/dummy/config/initializers/inflections.rb b/activestorage/test/dummy/config/initializers/inflections.rb
new file mode 100644
index 0000000000..aa7435fbc9
--- /dev/null
+++ b/activestorage/test/dummy/config/initializers/inflections.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+# Be sure to restart your server when you modify this file.
+
+# Add new inflection rules using the following format. Inflections
+# are locale specific, and you may define rules for as many different
+# locales as you wish. All of these examples are active by default:
+# ActiveSupport::Inflector.inflections(:en) do |inflect|
+# inflect.plural /^(ox)$/i, '\1en'
+# inflect.singular /^(ox)en/i, '\1'
+# inflect.irregular 'person', 'people'
+# inflect.uncountable %w( fish sheep )
+# end
+
+# These inflection rules are supported but not enabled by default:
+# ActiveSupport::Inflector.inflections(:en) do |inflect|
+# inflect.acronym 'RESTful'
+# end
diff --git a/activestorage/test/dummy/config/initializers/mime_types.rb b/activestorage/test/dummy/config/initializers/mime_types.rb
new file mode 100644
index 0000000000..6e1d16f027
--- /dev/null
+++ b/activestorage/test/dummy/config/initializers/mime_types.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+# Be sure to restart your server when you modify this file.
+
+# Add new mime types for use in respond_to blocks:
+# Mime::Type.register "text/richtext", :rtf
diff --git a/activestorage/test/dummy/config/initializers/wrap_parameters.rb b/activestorage/test/dummy/config/initializers/wrap_parameters.rb
new file mode 100644
index 0000000000..2f3c0db471
--- /dev/null
+++ b/activestorage/test/dummy/config/initializers/wrap_parameters.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+# Be sure to restart your server when you modify this file.
+
+# This file contains settings for ActionController::ParamsWrapper which
+# is enabled by default.
+
+# Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array.
+ActiveSupport.on_load(:action_controller) do
+ wrap_parameters format: [:json]
+end
+
+# To enable root element in JSON for ActiveRecord objects.
+# ActiveSupport.on_load(:active_record) do
+# self.include_root_in_json = true
+# end
diff --git a/activestorage/test/dummy/config/routes.rb b/activestorage/test/dummy/config/routes.rb
new file mode 100644
index 0000000000..edf04d2d63
--- /dev/null
+++ b/activestorage/test/dummy/config/routes.rb
@@ -0,0 +1,4 @@
+# frozen_string_literal: true
+
+Rails.application.routes.draw do
+end
diff --git a/activestorage/test/dummy/config/secrets.yml b/activestorage/test/dummy/config/secrets.yml
new file mode 100644
index 0000000000..77d1fc383a
--- /dev/null
+++ b/activestorage/test/dummy/config/secrets.yml
@@ -0,0 +1,32 @@
+# Be sure to restart your server when you modify this file.
+
+# Your secret key is used for verifying the integrity of signed cookies.
+# If you change this key, all old signed cookies will become invalid!
+
+# Make sure the secret is at least 30 characters and all random,
+# no regular words or you'll be exposed to dictionary attacks.
+# You can use `rails secret` to generate a secure secret key.
+
+# Make sure the secrets in this file are kept private
+# if you're sharing your code publicly.
+
+# Shared secrets are available across all environments.
+
+# shared:
+# api_key: a1B2c3D4e5F6
+
+# Environmental secrets are only available for that specific environment.
+
+development:
+ secret_key_base: e0ef5744b10d988669be6b2660c259749779964f3dcb487fd6199743b3558e2d89f7681d6a15d16d144e28979cbdae41885f4fb4c2cf56ff92ac22df282ffb66
+
+test:
+ secret_key_base: 6fb1f3a828a8dcd6ac8dc07b43be4a5265ad64379120d417252a1578fe1f790e7b85ade4f95994de1ac8fb78581690de6e3a6ac4af36a0f0139667418c750d05
+
+# Do not keep production secrets in the unencrypted secrets file.
+# Instead, either read values from the environment.
+# Or, use `bin/rails secrets:setup` to configure encrypted secrets
+# and move the `production:` environment over there.
+
+production:
+ secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>
diff --git a/activestorage/test/dummy/config/spring.rb b/activestorage/test/dummy/config/spring.rb
new file mode 100644
index 0000000000..ff5ba06b6d
--- /dev/null
+++ b/activestorage/test/dummy/config/spring.rb
@@ -0,0 +1,8 @@
+# frozen_string_literal: true
+
+%w(
+ .ruby-version
+ .rbenv-vars
+ tmp/restart.txt
+ tmp/caching-dev.txt
+).each { |path| Spring.watch(path) }
diff --git a/activestorage/test/dummy/config/storage.yml b/activestorage/test/dummy/config/storage.yml
new file mode 100644
index 0000000000..2c6762e0d6
--- /dev/null
+++ b/activestorage/test/dummy/config/storage.yml
@@ -0,0 +1,3 @@
+local:
+ service: Disk
+ root: <%= Rails.root.join("storage") %>
diff --git a/activestorage/test/dummy/lib/assets/.keep b/activestorage/test/dummy/lib/assets/.keep
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/activestorage/test/dummy/lib/assets/.keep
diff --git a/activestorage/test/dummy/log/.keep b/activestorage/test/dummy/log/.keep
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/activestorage/test/dummy/log/.keep
diff --git a/activestorage/test/dummy/package.json b/activestorage/test/dummy/package.json
new file mode 100644
index 0000000000..caa2d7bb3f
--- /dev/null
+++ b/activestorage/test/dummy/package.json
@@ -0,0 +1,5 @@
+{
+ "name": "dummy",
+ "private": true,
+ "dependencies": {}
+}
diff --git a/activestorage/test/dummy/public/404.html b/activestorage/test/dummy/public/404.html
new file mode 100644
index 0000000000..2be3af26fc
--- /dev/null
+++ b/activestorage/test/dummy/public/404.html
@@ -0,0 +1,67 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>The page you were looking for doesn't exist (404)</title>
+ <meta name="viewport" content="width=device-width,initial-scale=1">
+ <style>
+ .rails-default-error-page {
+ background-color: #EFEFEF;
+ color: #2E2F30;
+ text-align: center;
+ font-family: arial, sans-serif;
+ margin: 0;
+ }
+
+ .rails-default-error-page div.dialog {
+ width: 95%;
+ max-width: 33em;
+ margin: 4em auto 0;
+ }
+
+ .rails-default-error-page div.dialog > div {
+ border: 1px solid #CCC;
+ border-right-color: #999;
+ border-left-color: #999;
+ border-bottom-color: #BBB;
+ border-top: #B00100 solid 4px;
+ border-top-left-radius: 9px;
+ border-top-right-radius: 9px;
+ background-color: white;
+ padding: 7px 12% 0;
+ box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
+ }
+
+ .rails-default-error-page h1 {
+ font-size: 100%;
+ color: #730E15;
+ line-height: 1.5em;
+ }
+
+ .rails-default-error-page div.dialog > p {
+ margin: 0 0 1em;
+ padding: 1em;
+ background-color: #F7F7F7;
+ border: 1px solid #CCC;
+ border-right-color: #999;
+ border-left-color: #999;
+ border-bottom-color: #999;
+ border-bottom-left-radius: 4px;
+ border-bottom-right-radius: 4px;
+ border-top-color: #DADADA;
+ color: #666;
+ box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
+ }
+ </style>
+</head>
+
+<body class="rails-default-error-page">
+ <!-- This file lives in public/404.html -->
+ <div class="dialog">
+ <div>
+ <h1>The page you were looking for doesn't exist.</h1>
+ <p>You may have mistyped the address or the page may have moved.</p>
+ </div>
+ <p>If you are the application owner check the logs for more information.</p>
+ </div>
+</body>
+</html>
diff --git a/activestorage/test/dummy/public/422.html b/activestorage/test/dummy/public/422.html
new file mode 100644
index 0000000000..c08eac0d1d
--- /dev/null
+++ b/activestorage/test/dummy/public/422.html
@@ -0,0 +1,67 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>The change you wanted was rejected (422)</title>
+ <meta name="viewport" content="width=device-width,initial-scale=1">
+ <style>
+ .rails-default-error-page {
+ background-color: #EFEFEF;
+ color: #2E2F30;
+ text-align: center;
+ font-family: arial, sans-serif;
+ margin: 0;
+ }
+
+ .rails-default-error-page div.dialog {
+ width: 95%;
+ max-width: 33em;
+ margin: 4em auto 0;
+ }
+
+ .rails-default-error-page div.dialog > div {
+ border: 1px solid #CCC;
+ border-right-color: #999;
+ border-left-color: #999;
+ border-bottom-color: #BBB;
+ border-top: #B00100 solid 4px;
+ border-top-left-radius: 9px;
+ border-top-right-radius: 9px;
+ background-color: white;
+ padding: 7px 12% 0;
+ box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
+ }
+
+ .rails-default-error-page h1 {
+ font-size: 100%;
+ color: #730E15;
+ line-height: 1.5em;
+ }
+
+ .rails-default-error-page div.dialog > p {
+ margin: 0 0 1em;
+ padding: 1em;
+ background-color: #F7F7F7;
+ border: 1px solid #CCC;
+ border-right-color: #999;
+ border-left-color: #999;
+ border-bottom-color: #999;
+ border-bottom-left-radius: 4px;
+ border-bottom-right-radius: 4px;
+ border-top-color: #DADADA;
+ color: #666;
+ box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
+ }
+ </style>
+</head>
+
+<body class="rails-default-error-page">
+ <!-- This file lives in public/422.html -->
+ <div class="dialog">
+ <div>
+ <h1>The change you wanted was rejected.</h1>
+ <p>Maybe you tried to change something you didn't have access to.</p>
+ </div>
+ <p>If you are the application owner check the logs for more information.</p>
+ </div>
+</body>
+</html>
diff --git a/activestorage/test/dummy/public/500.html b/activestorage/test/dummy/public/500.html
new file mode 100644
index 0000000000..78a030af22
--- /dev/null
+++ b/activestorage/test/dummy/public/500.html
@@ -0,0 +1,66 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>We're sorry, but something went wrong (500)</title>
+ <meta name="viewport" content="width=device-width,initial-scale=1">
+ <style>
+ .rails-default-error-page {
+ background-color: #EFEFEF;
+ color: #2E2F30;
+ text-align: center;
+ font-family: arial, sans-serif;
+ margin: 0;
+ }
+
+ .rails-default-error-page div.dialog {
+ width: 95%;
+ max-width: 33em;
+ margin: 4em auto 0;
+ }
+
+ .rails-default-error-page div.dialog > div {
+ border: 1px solid #CCC;
+ border-right-color: #999;
+ border-left-color: #999;
+ border-bottom-color: #BBB;
+ border-top: #B00100 solid 4px;
+ border-top-left-radius: 9px;
+ border-top-right-radius: 9px;
+ background-color: white;
+ padding: 7px 12% 0;
+ box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
+ }
+
+ .rails-default-error-page h1 {
+ font-size: 100%;
+ color: #730E15;
+ line-height: 1.5em;
+ }
+
+ .rails-default-error-page div.dialog > p {
+ margin: 0 0 1em;
+ padding: 1em;
+ background-color: #F7F7F7;
+ border: 1px solid #CCC;
+ border-right-color: #999;
+ border-left-color: #999;
+ border-bottom-color: #999;
+ border-bottom-left-radius: 4px;
+ border-bottom-right-radius: 4px;
+ border-top-color: #DADADA;
+ color: #666;
+ box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
+ }
+ </style>
+</head>
+
+<body class="rails-default-error-page">
+ <!-- This file lives in public/500.html -->
+ <div class="dialog">
+ <div>
+ <h1>We're sorry, but something went wrong.</h1>
+ </div>
+ <p>If you are the application owner check the logs for more information.</p>
+ </div>
+</body>
+</html>
diff --git a/activestorage/test/dummy/public/apple-touch-icon-precomposed.png b/activestorage/test/dummy/public/apple-touch-icon-precomposed.png
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/activestorage/test/dummy/public/apple-touch-icon-precomposed.png
diff --git a/activestorage/test/dummy/public/apple-touch-icon.png b/activestorage/test/dummy/public/apple-touch-icon.png
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/activestorage/test/dummy/public/apple-touch-icon.png
diff --git a/activestorage/test/dummy/public/favicon.ico b/activestorage/test/dummy/public/favicon.ico
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/activestorage/test/dummy/public/favicon.ico
diff --git a/activestorage/test/fixtures/files/racecar.jpg b/activestorage/test/fixtures/files/racecar.jpg
new file mode 100644
index 0000000000..934b4caa22
--- /dev/null
+++ b/activestorage/test/fixtures/files/racecar.jpg
Binary files differ
diff --git a/activestorage/test/models/attachments_test.rb b/activestorage/test/models/attachments_test.rb
new file mode 100644
index 0000000000..7cfd8683db
--- /dev/null
+++ b/activestorage/test/models/attachments_test.rb
@@ -0,0 +1,125 @@
+# frozen_string_literal: true
+
+require "test_helper"
+require "database/setup"
+
+class ActiveStorage::AttachmentsTest < ActiveSupport::TestCase
+ include ActiveJob::TestHelper
+
+ setup { @user = User.create!(name: "DHH") }
+
+ teardown { ActiveStorage::Blob.all.each(&:purge) }
+
+ test "attach existing blob" do
+ @user.avatar.attach create_blob(filename: "funky.jpg")
+ assert_equal "funky.jpg", @user.avatar.filename.to_s
+ end
+
+ test "attach existing sgid blob" do
+ @user.avatar.attach create_blob(filename: "funky.jpg").signed_id
+ assert_equal "funky.jpg", @user.avatar.filename.to_s
+ end
+
+ test "attach new blob from a Hash" do
+ @user.avatar.attach io: StringIO.new("STUFF"), filename: "town.jpg", content_type: "image/jpg"
+ assert_equal "town.jpg", @user.avatar.filename.to_s
+ end
+
+ test "attach new blob from an UploadedFile" do
+ file = file_fixture "racecar.jpg"
+ @user.avatar.attach Rack::Test::UploadedFile.new file
+ assert_equal "racecar.jpg", @user.avatar.filename.to_s
+ end
+
+ test "access underlying associations of new blob" do
+ @user.avatar.attach create_blob(filename: "funky.jpg")
+ assert_equal @user, @user.avatar_attachment.record
+ assert_equal @user.avatar_attachment.blob, @user.avatar_blob
+ assert_equal "funky.jpg", @user.avatar_attachment.blob.filename.to_s
+ end
+
+ test "purge attached blob" do
+ @user.avatar.attach create_blob(filename: "funky.jpg")
+ avatar_key = @user.avatar.key
+
+ @user.avatar.purge
+ assert_not @user.avatar.attached?
+ assert_not ActiveStorage::Blob.service.exist?(avatar_key)
+ end
+
+ test "purge attached blob later when the record is destroyed" do
+ @user.avatar.attach create_blob(filename: "funky.jpg")
+ avatar_key = @user.avatar.key
+
+ perform_enqueued_jobs do
+ @user.destroy
+
+ assert_nil ActiveStorage::Blob.find_by(key: avatar_key)
+ assert_not ActiveStorage::Blob.service.exist?(avatar_key)
+ end
+ end
+
+
+ test "attach existing blobs" do
+ @user.highlights.attach create_blob(filename: "funky.jpg"), create_blob(filename: "wonky.jpg")
+
+ assert_equal "funky.jpg", @user.highlights.first.filename.to_s
+ assert_equal "wonky.jpg", @user.highlights.second.filename.to_s
+ end
+
+ test "attach new blobs" do
+ @user.highlights.attach(
+ { io: StringIO.new("STUFF"), filename: "town.jpg", content_type: "image/jpg" },
+ { io: StringIO.new("IT"), filename: "country.jpg", content_type: "image/jpg" })
+
+ assert_equal "town.jpg", @user.highlights.first.filename.to_s
+ assert_equal "country.jpg", @user.highlights.second.filename.to_s
+ end
+
+ test "find attached blobs" do
+ @user.highlights.attach(
+ { io: StringIO.new("STUFF"), filename: "town.jpg", content_type: "image/jpg" },
+ { io: StringIO.new("IT"), filename: "country.jpg", content_type: "image/jpg" })
+
+ highlights = User.where(id: @user.id).with_attached_highlights.first.highlights
+
+ assert_equal "town.jpg", highlights.first.filename.to_s
+ assert_equal "country.jpg", highlights.second.filename.to_s
+ end
+
+ test "access underlying associations of new blobs" do
+ @user.highlights.attach(
+ { io: StringIO.new("STUFF"), filename: "town.jpg", content_type: "image/jpg" },
+ { io: StringIO.new("IT"), filename: "country.jpg", content_type: "image/jpg" })
+
+ assert_equal @user, @user.highlights_attachments.first.record
+ assert_equal @user.highlights_attachments.collect(&:blob).sort, @user.highlights_blobs.sort
+ assert_equal "town.jpg", @user.highlights_attachments.first.blob.filename.to_s
+ end
+
+
+ test "purge attached blobs" do
+ @user.highlights.attach create_blob(filename: "funky.jpg"), create_blob(filename: "wonky.jpg")
+ highlight_keys = @user.highlights.collect(&:key)
+
+ @user.highlights.purge
+ assert_not @user.highlights.attached?
+ assert_not ActiveStorage::Blob.service.exist?(highlight_keys.first)
+ assert_not ActiveStorage::Blob.service.exist?(highlight_keys.second)
+ end
+
+ test "purge attached blobs later when the record is destroyed" do
+ @user.highlights.attach create_blob(filename: "funky.jpg"), create_blob(filename: "wonky.jpg")
+ highlight_keys = @user.highlights.collect(&:key)
+
+ perform_enqueued_jobs do
+ @user.destroy
+
+ assert_nil ActiveStorage::Blob.find_by(key: highlight_keys.first)
+ assert_not ActiveStorage::Blob.service.exist?(highlight_keys.first)
+
+ assert_nil ActiveStorage::Blob.find_by(key: highlight_keys.second)
+ assert_not ActiveStorage::Blob.service.exist?(highlight_keys.second)
+ end
+ end
+end
diff --git a/activestorage/test/models/blob_test.rb b/activestorage/test/models/blob_test.rb
new file mode 100644
index 0000000000..1c50d18994
--- /dev/null
+++ b/activestorage/test/models/blob_test.rb
@@ -0,0 +1,49 @@
+# frozen_string_literal: true
+
+require "test_helper"
+require "database/setup"
+
+class ActiveStorage::BlobTest < ActiveSupport::TestCase
+ test "create after upload sets byte size and checksum" do
+ data = "Hello world!"
+ blob = create_blob data: data
+
+ assert_equal data, blob.download
+ assert_equal data.length, blob.byte_size
+ assert_equal Digest::MD5.base64digest(data), blob.checksum
+ end
+
+ test "text?" do
+ blob = create_blob data: "Hello world!"
+ assert blob.text?
+ assert_not blob.audio?
+ end
+
+ test "download yields chunks" do
+ blob = create_blob data: "a" * 75.kilobytes
+ chunks = []
+
+ blob.download do |chunk|
+ chunks << chunk
+ end
+
+ assert_equal 2, chunks.size
+ assert_equal "a" * 64.kilobytes, chunks.first
+ assert_equal "a" * 11.kilobytes, chunks.second
+ end
+
+ test "urls expiring in 5 minutes" do
+ blob = create_blob
+
+ freeze_time do
+ assert_equal expected_url_for(blob), blob.service_url
+ assert_equal expected_url_for(blob, disposition: :attachment), blob.service_url(disposition: :attachment)
+ end
+ end
+
+ private
+ def expected_url_for(blob, disposition: :inline)
+ query_string = { content_type: blob.content_type, disposition: disposition }.to_param
+ "/rails/active_storage/disk/#{ActiveStorage.verifier.generate(blob.key, expires_in: 5.minutes, purpose: :blob_key)}/#{blob.filename}?#{query_string}"
+ end
+end
diff --git a/activestorage/test/models/filename_test.rb b/activestorage/test/models/filename_test.rb
new file mode 100644
index 0000000000..f1e4a467ba
--- /dev/null
+++ b/activestorage/test/models/filename_test.rb
@@ -0,0 +1,38 @@
+# frozen_string_literal: true
+
+require "test_helper"
+
+class ActiveStorage::FilenameTest < ActiveSupport::TestCase
+ test "sanitize" do
+ "%$|:;/\t\r\n\\".each_char do |character|
+ filename = ActiveStorage::Filename.new("foo#{character}bar.pdf")
+ assert_equal "foo-bar.pdf", filename.sanitized
+ assert_equal "foo-bar.pdf", filename.to_s
+ end
+ end
+
+ test "sanitize transcodes to valid UTF-8" do
+ { "\xF6".dup.force_encoding(Encoding::ISO8859_1) => "ö",
+ "\xC3".dup.force_encoding(Encoding::ISO8859_1) => "Ã",
+ "\xAD" => "�",
+ "\xCF" => "�",
+ "\x00" => "",
+ }.each do |actual, expected|
+ assert_equal expected, ActiveStorage::Filename.new(actual).sanitized
+ end
+ end
+
+ test "strips RTL override chars used to spoof unsafe executables as docs" do
+ # Would be displayed in Windows as "evilexe.pdf" due to the right-to-left
+ # (RTL) override char!
+ assert_equal "evil-fdp.exe", ActiveStorage::Filename.new("evil\u{202E}fdp.exe").sanitized
+ end
+
+ test "compare case-insensitively" do
+ assert_operator ActiveStorage::Filename.new("foobar.pdf"), :==, ActiveStorage::Filename.new("FooBar.PDF")
+ end
+
+ test "compare sanitized" do
+ assert_operator ActiveStorage::Filename.new("foo-bar.pdf"), :==, ActiveStorage::Filename.new("foo\tbar.pdf")
+ end
+end
diff --git a/activestorage/test/models/variant_test.rb b/activestorage/test/models/variant_test.rb
new file mode 100644
index 0000000000..ca112ab907
--- /dev/null
+++ b/activestorage/test/models/variant_test.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+require "test_helper"
+require "database/setup"
+
+class ActiveStorage::VariantTest < ActiveSupport::TestCase
+ setup do
+ @blob = create_image_blob filename: "racecar.jpg"
+ end
+
+ test "resized variation" do
+ variant = @blob.variant(resize: "100x100").processed
+ assert_match(/racecar\.jpg/, variant.service_url)
+
+ image = read_image_variant(variant)
+ assert_equal 100, image.width
+ assert_equal 67, image.height
+ end
+
+ test "resized and monochrome variation" do
+ variant = @blob.variant(resize: "100x100", monochrome: true).processed
+ assert_match(/racecar\.jpg/, variant.service_url)
+
+ image = read_image_variant(variant)
+ assert_equal 100, image.width
+ assert_equal 67, image.height
+ assert_match(/Gray/, image.colorspace)
+ end
+end
diff --git a/activestorage/test/service/azure_storage_service_test.rb b/activestorage/test/service/azure_storage_service_test.rb
new file mode 100644
index 0000000000..dac154cf11
--- /dev/null
+++ b/activestorage/test/service/azure_storage_service_test.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+require "service/shared_service_tests"
+require "uri"
+
+if SERVICE_CONFIGURATIONS[:azure]
+ class ActiveStorage::Service::AzureStorageServiceTest < ActiveSupport::TestCase
+ SERVICE = ActiveStorage::Service.configure(:azure, SERVICE_CONFIGURATIONS)
+
+ include ActiveStorage::Service::SharedServiceTests
+
+ test "signed URL generation" do
+ url = @service.url(FIXTURE_KEY, expires_in: 5.minutes,
+ disposition: :inline, filename: "avatar.png", content_type: "image/png")
+
+ assert_match(/(\S+)&rsct=image%2Fpng&rscd=inline%3B\+filename%3D%22avatar.png/, url)
+ assert_match SERVICE_CONFIGURATIONS[:azure][:container], url
+ end
+ end
+else
+ puts "Skipping Azure Storage Service tests because no Azure configuration was supplied"
+end
diff --git a/activestorage/test/service/configurations.yml b/activestorage/test/service/configurations.yml
new file mode 100644
index 0000000000..56ed37be5d
--- /dev/null
+++ b/activestorage/test/service/configurations.yml
@@ -0,0 +1,30 @@
+# s3:
+# service: S3
+# access_key_id: ""
+# secret_access_key: ""
+# region: ""
+# bucket: ""
+#
+# gcs:
+# service: GCS
+# keyfile: {
+# type: "service_account",
+# project_id: "",
+# private_key_id: "",
+# private_key: "",
+# client_email: "",
+# client_id: "",
+# auth_uri: "https://accounts.google.com/o/oauth2/auth",
+# token_uri: "https://accounts.google.com/o/oauth2/token",
+# auth_provider_x509_cert_url: "https://www.googleapis.com/oauth2/v1/certs",
+# client_x509_cert_url: ""
+# }
+# project:
+# bucket:
+#
+# azure:
+# service: AzureStorage
+# path: ""
+# storage_account_name: ""
+# storage_access_key: ""
+# container: ""
diff --git a/activestorage/test/service/configurations.yml.enc b/activestorage/test/service/configurations.yml.enc
new file mode 100644
index 0000000000..df11aac161
--- /dev/null
+++ b/activestorage/test/service/configurations.yml.enc
Binary files differ
diff --git a/activestorage/test/service/configurator_test.rb b/activestorage/test/service/configurator_test.rb
new file mode 100644
index 0000000000..a2fd035e02
--- /dev/null
+++ b/activestorage/test/service/configurator_test.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+require "service/shared_service_tests"
+
+class ActiveStorage::Service::ConfiguratorTest < ActiveSupport::TestCase
+ test "builds correct service instance based on service name" do
+ service = ActiveStorage::Service::Configurator.build(:foo, foo: { service: "Disk", root: "path" })
+ assert_instance_of ActiveStorage::Service::DiskService, service
+ end
+
+ test "raises error when passing non-existent service name" do
+ assert_raise RuntimeError do
+ ActiveStorage::Service::Configurator.build(:bigfoot, {})
+ end
+ end
+end
diff --git a/activestorage/test/service/disk_service_test.rb b/activestorage/test/service/disk_service_test.rb
new file mode 100644
index 0000000000..835a3a2971
--- /dev/null
+++ b/activestorage/test/service/disk_service_test.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+require "service/shared_service_tests"
+
+class ActiveStorage::Service::DiskServiceTest < ActiveSupport::TestCase
+ SERVICE = ActiveStorage::Service::DiskService.new(root: File.join(Dir.tmpdir, "active_storage"))
+
+ include ActiveStorage::Service::SharedServiceTests
+
+ test "url generation" do
+ assert_match(/rails\/active_storage\/disk\/.*\/avatar\.png\?.+disposition=inline/,
+ @service.url(FIXTURE_KEY, expires_in: 5.minutes, disposition: :inline, filename: "avatar.png", content_type: "image/png"))
+ end
+end
diff --git a/activestorage/test/service/gcs_service_test.rb b/activestorage/test/service/gcs_service_test.rb
new file mode 100644
index 0000000000..5f53f61baf
--- /dev/null
+++ b/activestorage/test/service/gcs_service_test.rb
@@ -0,0 +1,46 @@
+# frozen_string_literal: true
+
+require "service/shared_service_tests"
+require "net/http"
+
+if SERVICE_CONFIGURATIONS[:gcs]
+ class ActiveStorage::Service::GCSServiceTest < ActiveSupport::TestCase
+ SERVICE = ActiveStorage::Service.configure(:gcs, SERVICE_CONFIGURATIONS)
+
+ include ActiveStorage::Service::SharedServiceTests
+
+ test "direct upload" do
+ begin
+ key = SecureRandom.base58(24)
+ data = "Something else entirely!"
+ checksum = Digest::MD5.base64digest(data)
+ url = @service.url_for_direct_upload(key, expires_in: 5.minutes, content_type: "text/plain", content_length: data.size, checksum: checksum)
+
+ uri = URI.parse url
+ request = Net::HTTP::Put.new uri.request_uri
+ request.body = data
+ request.add_field "Content-Type", "text/plain"
+ request.add_field "Content-MD5", checksum
+ Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http|
+ http.request request
+ end
+
+ assert_equal data, @service.download(key)
+ ensure
+ @service.delete key
+ end
+ end
+
+ test "signed URL generation" do
+ freeze_time do
+ url = SERVICE.bucket.signed_url(FIXTURE_KEY, expires: 120) +
+ "&response-content-disposition=inline%3B+filename%3D%22test.txt%22" +
+ "&response-content-type=text%2Fplain"
+
+ assert_equal url, @service.url(FIXTURE_KEY, expires_in: 2.minutes, disposition: :inline, filename: "test.txt", content_type: "text/plain")
+ end
+ end
+ end
+else
+ puts "Skipping GCS Service tests because no GCS configuration was supplied"
+end
diff --git a/activestorage/test/service/mirror_service_test.rb b/activestorage/test/service/mirror_service_test.rb
new file mode 100644
index 0000000000..93e86eff70
--- /dev/null
+++ b/activestorage/test/service/mirror_service_test.rb
@@ -0,0 +1,64 @@
+# frozen_string_literal: true
+
+require "service/shared_service_tests"
+
+class ActiveStorage::Service::MirrorServiceTest < ActiveSupport::TestCase
+ mirror_config = (1..3).map do |i|
+ [ "mirror_#{i}",
+ service: "Disk",
+ root: Dir.mktmpdir("active_storage_tests_mirror_#{i}") ]
+ end.to_h
+
+ config = mirror_config.merge \
+ mirror: { service: "Mirror", primary: "primary", mirrors: mirror_config.keys },
+ primary: { service: "Disk", root: Dir.mktmpdir("active_storage_tests_primary") }
+
+ SERVICE = ActiveStorage::Service.configure :mirror, config
+
+ include ActiveStorage::Service::SharedServiceTests
+
+ test "uploading to all services" do
+ begin
+ data = "Something else entirely!"
+ key = upload(data, to: @service)
+
+ assert_equal data, SERVICE.primary.download(key)
+ SERVICE.mirrors.each do |mirror|
+ assert_equal data, mirror.download(key)
+ end
+ ensure
+ @service.delete key
+ end
+ end
+
+ test "downloading from primary service" do
+ data = "Something else entirely!"
+ key = upload(data, to: SERVICE.primary)
+
+ assert_equal data, @service.download(key)
+ end
+
+ test "deleting from all services" do
+ @service.delete FIXTURE_KEY
+ assert_not SERVICE.primary.exist?(FIXTURE_KEY)
+ SERVICE.mirrors.each do |mirror|
+ assert_not mirror.exist?(FIXTURE_KEY)
+ end
+ end
+
+ test "URL generation in primary service" do
+ freeze_time do
+ assert_equal SERVICE.primary.url(FIXTURE_KEY, expires_in: 2.minutes, disposition: :inline, filename: "test.txt", content_type: "text/plain"),
+ @service.url(FIXTURE_KEY, expires_in: 2.minutes, disposition: :inline, filename: "test.txt", content_type: "text/plain")
+ end
+ end
+
+ private
+ def upload(data, to:)
+ SecureRandom.base58(24).tap do |key|
+ io = StringIO.new(data).tap(&:read)
+ @service.upload key, io, checksum: Digest::MD5.base64digest(data)
+ assert io.eof?
+ end
+ end
+end
diff --git a/activestorage/test/service/s3_service_test.rb b/activestorage/test/service/s3_service_test.rb
new file mode 100644
index 0000000000..98f520741d
--- /dev/null
+++ b/activestorage/test/service/s3_service_test.rb
@@ -0,0 +1,59 @@
+# frozen_string_literal: true
+
+require "service/shared_service_tests"
+require "net/http"
+
+if SERVICE_CONFIGURATIONS[:s3] && SERVICE_CONFIGURATIONS[:s3][:access_key_id].present?
+ class ActiveStorage::Service::S3ServiceTest < ActiveSupport::TestCase
+ SERVICE = ActiveStorage::Service.configure(:s3, SERVICE_CONFIGURATIONS)
+
+ include ActiveStorage::Service::SharedServiceTests
+
+ test "direct upload" do
+ begin
+ key = SecureRandom.base58(24)
+ data = "Something else entirely!"
+ checksum = Digest::MD5.base64digest(data)
+ url = @service.url_for_direct_upload(key, expires_in: 5.minutes, content_type: "text/plain", content_length: data.size, checksum: checksum)
+
+ uri = URI.parse url
+ request = Net::HTTP::Put.new uri.request_uri
+ request.body = data
+ request.add_field "Content-Type", "text/plain"
+ request.add_field "Content-MD5", checksum
+ Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http|
+ http.request request
+ end
+
+ assert_equal data, @service.download(key)
+ ensure
+ @service.delete key
+ end
+ end
+
+ test "signed URL generation" do
+ url = @service.url(FIXTURE_KEY, expires_in: 5.minutes,
+ disposition: :inline, filename: "avatar.png", content_type: "image/png")
+
+ assert_match(/s3\.(\S+)?amazonaws.com.*response-content-disposition=inline.*avatar\.png.*response-content-type=image%2Fpng/, url)
+ assert_match SERVICE_CONFIGURATIONS[:s3][:bucket], url
+ 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)
+
+ begin
+ key = SecureRandom.base58(24)
+ data = "Something else entirely!"
+ service.upload key, StringIO.new(data), checksum: Digest::MD5.base64digest(data)
+
+ assert_equal "AES256", service.bucket.object(key).server_side_encryption
+ ensure
+ service.delete key
+ end
+ end
+ end
+else
+ puts "Skipping S3 Service tests because no S3 configuration was supplied"
+end
diff --git a/activestorage/test/service/shared_service_tests.rb b/activestorage/test/service/shared_service_tests.rb
new file mode 100644
index 0000000000..a9e1cb6ce9
--- /dev/null
+++ b/activestorage/test/service/shared_service_tests.rb
@@ -0,0 +1,69 @@
+# frozen_string_literal: true
+
+require "test_helper"
+require "active_support/core_ext/securerandom"
+
+module ActiveStorage::Service::SharedServiceTests
+ extend ActiveSupport::Concern
+
+ FIXTURE_KEY = SecureRandom.base58(24)
+ FIXTURE_DATA = "\211PNG\r\n\032\n\000\000\000\rIHDR\000\000\000\020\000\000\000\020\001\003\000\000\000%=m\"\000\000\000\006PLTE\000\000\000\377\377\377\245\331\237\335\000\000\0003IDATx\234c\370\377\237\341\377_\206\377\237\031\016\2603\334?\314p\1772\303\315\315\f7\215\031\356\024\203\320\275\317\f\367\201R\314\f\017\300\350\377\177\000Q\206\027(\316]\233P\000\000\000\000IEND\256B`\202".dup.force_encoding(Encoding::BINARY)
+
+ included do
+ setup do
+ @service = self.class.const_get(:SERVICE)
+ @service.upload FIXTURE_KEY, StringIO.new(FIXTURE_DATA)
+ end
+
+ teardown do
+ @service.delete FIXTURE_KEY
+ end
+
+ test "uploading with integrity" do
+ begin
+ key = SecureRandom.base58(24)
+ data = "Something else entirely!"
+ @service.upload(key, StringIO.new(data), checksum: Digest::MD5.base64digest(data))
+
+ assert_equal data, @service.download(key)
+ ensure
+ @service.delete key
+ end
+ end
+
+ test "uploading without integrity" do
+ begin
+ key = SecureRandom.base58(24)
+ data = "Something else entirely!"
+
+ assert_raises(ActiveStorage::IntegrityError) do
+ @service.upload(key, StringIO.new(data), checksum: Digest::MD5.base64digest("bad data"))
+ end
+
+ assert_not @service.exist?(key)
+ ensure
+ @service.delete key
+ end
+ end
+
+ test "downloading" do
+ assert_equal FIXTURE_DATA, @service.download(FIXTURE_KEY)
+ end
+
+ test "existing" do
+ assert @service.exist?(FIXTURE_KEY)
+ assert_not @service.exist?(FIXTURE_KEY + "nonsense")
+ end
+
+ test "deleting" do
+ @service.delete FIXTURE_KEY
+ assert_not @service.exist?(FIXTURE_KEY)
+ end
+
+ test "deleting nonexistent key" do
+ assert_nothing_raised do
+ @service.delete SecureRandom.base58(24)
+ end
+ end
+ end
+end
diff --git a/activestorage/test/template/image_tag_test.rb b/activestorage/test/template/image_tag_test.rb
new file mode 100644
index 0000000000..46dd97b3e9
--- /dev/null
+++ b/activestorage/test/template/image_tag_test.rb
@@ -0,0 +1,38 @@
+# frozen_string_literal: true
+
+require "test_helper"
+require "database/setup"
+
+class ActiveStorage::ImageTagTest < ActionView::TestCase
+ tests ActionView::Helpers::AssetTagHelper
+
+ setup do
+ @blob = create_image_blob filename: "racecar.jpg"
+ end
+
+ test "blob" do
+ assert_dom_equal %(<img alt="Racecar" src="#{polymorphic_url @blob}" />), image_tag(@blob)
+ end
+
+ test "variant" do
+ variant = @blob.variant(resize: "100x100")
+ assert_dom_equal %(<img alt="Racecar" src="#{polymorphic_url variant}" />), image_tag(variant)
+ end
+
+ test "attachment" do
+ attachment = ActiveStorage::Attachment.new(blob: @blob)
+ assert_dom_equal %(<img alt="Racecar" src="#{polymorphic_url attachment}" />), image_tag(attachment)
+ end
+
+ test "error when attachment's empty" do
+ @user = User.create!(name: "DHH")
+
+ assert_not @user.avatar.attached?
+ assert_raises(ArgumentError) { image_tag(@user.avatar) }
+ end
+
+ test "error when object can't be resolved into url" do
+ unresolvable_object = ActionView::Helpers::AssetTagHelper
+ assert_raises(ArgumentError) { image_tag(unresolvable_object) }
+ end
+end
diff --git a/activestorage/test/test_helper.rb b/activestorage/test/test_helper.rb
new file mode 100644
index 0000000000..0bb3a925b8
--- /dev/null
+++ b/activestorage/test/test_helper.rb
@@ -0,0 +1,69 @@
+# frozen_string_literal: true
+
+require File.expand_path("../../test/dummy/config/environment.rb", __FILE__)
+
+require "bundler/setup"
+require "active_support"
+require "active_support/test_case"
+require "active_support/testing/autorun"
+
+begin
+ require "byebug"
+rescue LoadError
+end
+
+require "active_job"
+ActiveJob::Base.queue_adapter = :test
+ActiveJob::Base.logger = ActiveSupport::Logger.new(nil)
+
+# Filter out Minitest backtrace while allowing backtrace from other libraries
+# to be shown.
+Minitest.backtrace_filter = Minitest::BacktraceFilter.new
+
+require "yaml"
+SERVICE_CONFIGURATIONS = begin
+ erb = ERB.new(Pathname.new(File.expand_path("../service/configurations.yml", __FILE__)).read)
+ configuration = YAML.load(erb.result) || {}
+ configuration.deep_symbolize_keys
+rescue Errno::ENOENT
+ puts "Missing service configuration file in test/service/configurations.yml"
+ {}
+end
+
+require "tmpdir"
+ActiveStorage::Blob.service = ActiveStorage::Service::DiskService.new(root: Dir.mktmpdir("active_storage_tests"))
+ActiveStorage::Service.logger = ActiveSupport::Logger.new(STDOUT)
+
+ActiveStorage.verifier = ActiveSupport::MessageVerifier.new("Testing")
+
+class ActiveSupport::TestCase
+ self.file_fixture_path = File.expand_path("../fixtures/files", __FILE__)
+
+ private
+ def create_blob(data: "Hello world!", filename: "hello.txt", content_type: "text/plain")
+ ActiveStorage::Blob.create_after_upload! io: StringIO.new(data), filename: filename, content_type: content_type
+ end
+
+ def create_image_blob(filename: "racecar.jpg", content_type: "image/jpeg")
+ ActiveStorage::Blob.create_after_upload! \
+ io: file_fixture(filename).open,
+ filename: filename, content_type: content_type
+ end
+
+ def create_blob_before_direct_upload(filename: "hello.txt", byte_size:, checksum:, content_type: "text/plain")
+ ActiveStorage::Blob.create_before_direct_upload! filename: filename, byte_size: byte_size, checksum: checksum, content_type: content_type
+ end
+
+ def read_image_variant(variant)
+ MiniMagick::Image.open variant.service.send(:path_for, variant.key)
+ end
+end
+
+require "global_id"
+GlobalID.app = "ActiveStorageExampleApp"
+ActiveRecord::Base.send :include, GlobalID::Identification
+
+class User < ActiveRecord::Base
+ has_one_attached :avatar
+ has_many_attached :highlights
+end
diff --git a/activestorage/webpack.config.js b/activestorage/webpack.config.js
new file mode 100644
index 0000000000..92c4530e7f
--- /dev/null
+++ b/activestorage/webpack.config.js
@@ -0,0 +1,27 @@
+const webpack = require("webpack")
+const path = require("path")
+
+module.exports = {
+ entry: {
+ "activestorage": path.resolve(__dirname, "app/javascript/activestorage/index.js"),
+ },
+
+ output: {
+ filename: "[name].js",
+ path: path.resolve(__dirname, "app/assets/javascripts"),
+ library: "ActiveStorage",
+ libraryTarget: "umd"
+ },
+
+ module: {
+ rules: [
+ {
+ test: /\.js$/,
+ exclude: /node_modules/,
+ use: {
+ loader: "babel-loader"
+ }
+ }
+ ]
+ }
+}
diff --git a/activestorage/yarn.lock b/activestorage/yarn.lock
new file mode 100644
index 0000000000..dd09577445
--- /dev/null
+++ b/activestorage/yarn.lock
@@ -0,0 +1,3164 @@
+# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
+# yarn lockfile v1
+
+
+abbrev@1:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.0.tgz#d0554c2256636e2f56e7c2e5ad183f859428d81f"
+
+acorn-dynamic-import@^2.0.0:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/acorn-dynamic-import/-/acorn-dynamic-import-2.0.2.tgz#c752bd210bef679501b6c6cb7fc84f8f47158cc4"
+ dependencies:
+ acorn "^4.0.3"
+
+acorn-jsx@^3.0.0:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-3.0.1.tgz#afdf9488fb1ecefc8348f6fb22f464e32a58b36b"
+ dependencies:
+ acorn "^3.0.4"
+
+acorn@^3.0.4:
+ version "3.3.0"
+ resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a"
+
+acorn@^4.0.3:
+ version "4.0.13"
+ resolved "https://registry.yarnpkg.com/acorn/-/acorn-4.0.13.tgz#105495ae5361d697bd195c825192e1ad7f253787"
+
+acorn@^5.0.0, acorn@^5.0.1:
+ version "5.1.1"
+ resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.1.1.tgz#53fe161111f912ab999ee887a90a0bc52822fd75"
+
+ajv-keywords@^1.0.0:
+ version "1.5.1"
+ resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-1.5.1.tgz#314dd0a4b3368fad3dfcdc54ede6171b886daf3c"
+
+ajv-keywords@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-2.1.0.tgz#a296e17f7bfae7c1ce4f7e0de53d29cb32162df0"
+
+ajv@^4.7.0, ajv@^4.9.1:
+ version "4.11.8"
+ resolved "https://registry.yarnpkg.com/ajv/-/ajv-4.11.8.tgz#82ffb02b29e662ae53bdc20af15947706739c536"
+ dependencies:
+ co "^4.6.0"
+ json-stable-stringify "^1.0.1"
+
+ajv@^5.1.5, ajv@^5.2.0:
+ version "5.2.2"
+ resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.2.2.tgz#47c68d69e86f5d953103b0074a9430dc63da5e39"
+ dependencies:
+ co "^4.6.0"
+ fast-deep-equal "^1.0.0"
+ json-schema-traverse "^0.3.0"
+ json-stable-stringify "^1.0.1"
+
+align-text@^0.1.1, align-text@^0.1.3:
+ version "0.1.4"
+ resolved "https://registry.yarnpkg.com/align-text/-/align-text-0.1.4.tgz#0cd90a561093f35d0a99256c22b7069433fad117"
+ dependencies:
+ kind-of "^3.0.2"
+ longest "^1.0.1"
+ repeat-string "^1.5.2"
+
+ansi-escapes@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-2.0.0.tgz#5bae52be424878dd9783e8910e3fc2922e83c81b"
+
+ansi-regex@^2.0.0:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df"
+
+ansi-regex@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998"
+
+ansi-styles@^2.2.1:
+ version "2.2.1"
+ resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe"
+
+ansi-styles@^3.1.0:
+ version "3.2.0"
+ resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.0.tgz#c159b8d5be0f9e5a6f346dab94f16ce022161b88"
+ dependencies:
+ color-convert "^1.9.0"
+
+anymatch@^1.3.0:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-1.3.0.tgz#a3e52fa39168c825ff57b0248126ce5a8ff95507"
+ dependencies:
+ arrify "^1.0.0"
+ micromatch "^2.1.5"
+
+aproba@^1.0.3:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.1.2.tgz#45c6629094de4e96f693ef7eab74ae079c240fc1"
+
+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"
+ 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"
+ dependencies:
+ sprintf-js "~1.0.2"
+
+arr-diff@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-2.0.0.tgz#8f3b827f955a8bd669697e4a4256ac3ceae356cf"
+ dependencies:
+ arr-flatten "^1.0.1"
+
+arr-flatten@^1.0.1:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1"
+
+array-union@^1.0.1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39"
+ dependencies:
+ array-uniq "^1.0.1"
+
+array-uniq@^1.0.1:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6"
+
+array-unique@^0.2.1:
+ version "0.2.1"
+ resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.2.1.tgz#a1d97ccafcbc2625cc70fadceb36a50c58b01a53"
+
+arrify@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d"
+
+asn1.js@^4.0.0:
+ version "4.9.1"
+ resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.9.1.tgz#48ba240b45a9280e94748990ba597d216617fd40"
+ 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"
+
+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"
+
+assert-plus@^0.2.0:
+ version "0.2.0"
+ resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-0.2.0.tgz#d74e1b87e7affc0db8aadb7021f3fe48101ab234"
+
+assert@^1.1.1:
+ version "1.4.1"
+ resolved "https://registry.yarnpkg.com/assert/-/assert-1.4.1.tgz#99912d591836b5a6f5b345c0f07eefc08fc65d91"
+ dependencies:
+ util "0.10.3"
+
+async-each@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d"
+
+async@^2.1.2:
+ version "2.5.0"
+ resolved "https://registry.yarnpkg.com/async/-/async-2.5.0.tgz#843190fd6b7357a0b9e1c956edddd5ec8462b54d"
+ dependencies:
+ lodash "^4.14.0"
+
+asynckit@^0.4.0:
+ version "0.4.0"
+ resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
+
+aws-sign2@~0.6.0:
+ version "0.6.0"
+ resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.6.0.tgz#14342dd38dbcc94d0e5b87d763cd63612c0e794f"
+
+aws4@^1.2.1:
+ version "1.6.0"
+ resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.6.0.tgz#83ef5ca860b2b32e4a0deedee8c771b9db57471e"
+
+babel-code-frame@^6.22.0:
+ version "6.22.0"
+ resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.22.0.tgz#027620bee567a88c32561574e7fd0801d33118e4"
+ dependencies:
+ chalk "^1.1.0"
+ esutils "^2.0.2"
+ js-tokens "^3.0.0"
+
+babel-core@^6.24.1, babel-core@^6.25.0:
+ version "6.25.0"
+ resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-6.25.0.tgz#7dd42b0463c742e9d5296deb3ec67a9322dad729"
+ dependencies:
+ babel-code-frame "^6.22.0"
+ babel-generator "^6.25.0"
+ babel-helpers "^6.24.1"
+ babel-messages "^6.23.0"
+ babel-register "^6.24.1"
+ babel-runtime "^6.22.0"
+ babel-template "^6.25.0"
+ babel-traverse "^6.25.0"
+ babel-types "^6.25.0"
+ babylon "^6.17.2"
+ convert-source-map "^1.1.0"
+ debug "^2.1.1"
+ json5 "^0.5.0"
+ lodash "^4.2.0"
+ minimatch "^3.0.2"
+ path-is-absolute "^1.0.0"
+ private "^0.1.6"
+ slash "^1.0.0"
+ source-map "^0.5.0"
+
+babel-generator@^6.25.0:
+ version "6.25.0"
+ resolved "https://registry.yarnpkg.com/babel-generator/-/babel-generator-6.25.0.tgz#33a1af70d5f2890aeb465a4a7793c1df6a9ea9fc"
+ dependencies:
+ babel-messages "^6.23.0"
+ babel-runtime "^6.22.0"
+ babel-types "^6.25.0"
+ detect-indent "^4.0.0"
+ jsesc "^1.3.0"
+ lodash "^4.2.0"
+ source-map "^0.5.0"
+ trim-right "^1.0.1"
+
+babel-helper-builder-binary-assignment-operator-visitor@^6.24.1:
+ version "6.24.1"
+ resolved "https://registry.yarnpkg.com/babel-helper-builder-binary-assignment-operator-visitor/-/babel-helper-builder-binary-assignment-operator-visitor-6.24.1.tgz#cce4517ada356f4220bcae8a02c2b346f9a56664"
+ dependencies:
+ babel-helper-explode-assignable-expression "^6.24.1"
+ babel-runtime "^6.22.0"
+ babel-types "^6.24.1"
+
+babel-helper-call-delegate@^6.24.1:
+ version "6.24.1"
+ resolved "https://registry.yarnpkg.com/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz#ece6aacddc76e41c3461f88bfc575bd0daa2df8d"
+ dependencies:
+ babel-helper-hoist-variables "^6.24.1"
+ babel-runtime "^6.22.0"
+ babel-traverse "^6.24.1"
+ babel-types "^6.24.1"
+
+babel-helper-define-map@^6.24.1:
+ version "6.24.1"
+ resolved "https://registry.yarnpkg.com/babel-helper-define-map/-/babel-helper-define-map-6.24.1.tgz#7a9747f258d8947d32d515f6aa1c7bd02204a080"
+ dependencies:
+ babel-helper-function-name "^6.24.1"
+ babel-runtime "^6.22.0"
+ babel-types "^6.24.1"
+ lodash "^4.2.0"
+
+babel-helper-explode-assignable-expression@^6.24.1:
+ version "6.24.1"
+ resolved "https://registry.yarnpkg.com/babel-helper-explode-assignable-expression/-/babel-helper-explode-assignable-expression-6.24.1.tgz#f25b82cf7dc10433c55f70592d5746400ac22caa"
+ dependencies:
+ babel-runtime "^6.22.0"
+ babel-traverse "^6.24.1"
+ babel-types "^6.24.1"
+
+babel-helper-function-name@^6.24.1:
+ version "6.24.1"
+ resolved "https://registry.yarnpkg.com/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz#d3475b8c03ed98242a25b48351ab18399d3580a9"
+ dependencies:
+ babel-helper-get-function-arity "^6.24.1"
+ babel-runtime "^6.22.0"
+ babel-template "^6.24.1"
+ babel-traverse "^6.24.1"
+ babel-types "^6.24.1"
+
+babel-helper-get-function-arity@^6.24.1:
+ version "6.24.1"
+ resolved "https://registry.yarnpkg.com/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz#8f7782aa93407c41d3aa50908f89b031b1b6853d"
+ dependencies:
+ babel-runtime "^6.22.0"
+ babel-types "^6.24.1"
+
+babel-helper-hoist-variables@^6.24.1:
+ version "6.24.1"
+ resolved "https://registry.yarnpkg.com/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.24.1.tgz#1ecb27689c9d25513eadbc9914a73f5408be7a76"
+ dependencies:
+ babel-runtime "^6.22.0"
+ babel-types "^6.24.1"
+
+babel-helper-optimise-call-expression@^6.24.1:
+ version "6.24.1"
+ resolved "https://registry.yarnpkg.com/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz#f7a13427ba9f73f8f4fa993c54a97882d1244257"
+ dependencies:
+ babel-runtime "^6.22.0"
+ babel-types "^6.24.1"
+
+babel-helper-regex@^6.24.1:
+ version "6.24.1"
+ resolved "https://registry.yarnpkg.com/babel-helper-regex/-/babel-helper-regex-6.24.1.tgz#d36e22fab1008d79d88648e32116868128456ce8"
+ dependencies:
+ babel-runtime "^6.22.0"
+ babel-types "^6.24.1"
+ lodash "^4.2.0"
+
+babel-helper-remap-async-to-generator@^6.24.1:
+ version "6.24.1"
+ resolved "https://registry.yarnpkg.com/babel-helper-remap-async-to-generator/-/babel-helper-remap-async-to-generator-6.24.1.tgz#5ec581827ad723fecdd381f1c928390676e4551b"
+ dependencies:
+ babel-helper-function-name "^6.24.1"
+ babel-runtime "^6.22.0"
+ babel-template "^6.24.1"
+ babel-traverse "^6.24.1"
+ babel-types "^6.24.1"
+
+babel-helper-replace-supers@^6.24.1:
+ version "6.24.1"
+ resolved "https://registry.yarnpkg.com/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz#bf6dbfe43938d17369a213ca8a8bf74b6a90ab1a"
+ dependencies:
+ babel-helper-optimise-call-expression "^6.24.1"
+ babel-messages "^6.23.0"
+ babel-runtime "^6.22.0"
+ babel-template "^6.24.1"
+ babel-traverse "^6.24.1"
+ babel-types "^6.24.1"
+
+babel-helpers@^6.24.1:
+ version "6.24.1"
+ resolved "https://registry.yarnpkg.com/babel-helpers/-/babel-helpers-6.24.1.tgz#3471de9caec388e5c850e597e58a26ddf37602b2"
+ dependencies:
+ babel-runtime "^6.22.0"
+ babel-template "^6.24.1"
+
+babel-loader@^7.1.1:
+ version "7.1.1"
+ resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-7.1.1.tgz#b87134c8b12e3e4c2a94e0546085bc680a2b8488"
+ dependencies:
+ find-cache-dir "^1.0.0"
+ loader-utils "^1.0.2"
+ mkdirp "^0.5.1"
+
+babel-messages@^6.23.0:
+ version "6.23.0"
+ resolved "https://registry.yarnpkg.com/babel-messages/-/babel-messages-6.23.0.tgz#f3cdf4703858035b2a2951c6ec5edf6c62f2630e"
+ dependencies:
+ babel-runtime "^6.22.0"
+
+babel-plugin-check-es2015-constants@^6.22.0:
+ version "6.22.0"
+ resolved "https://registry.yarnpkg.com/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz#35157b101426fd2ffd3da3f75c7d1e91835bbf8a"
+ dependencies:
+ babel-runtime "^6.22.0"
+
+babel-plugin-syntax-async-functions@^6.8.0:
+ version "6.13.0"
+ resolved "https://registry.yarnpkg.com/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz#cad9cad1191b5ad634bf30ae0872391e0647be95"
+
+babel-plugin-syntax-exponentiation-operator@^6.8.0:
+ version "6.13.0"
+ resolved "https://registry.yarnpkg.com/babel-plugin-syntax-exponentiation-operator/-/babel-plugin-syntax-exponentiation-operator-6.13.0.tgz#9ee7e8337290da95288201a6a57f4170317830de"
+
+babel-plugin-syntax-trailing-function-commas@^6.22.0:
+ version "6.22.0"
+ resolved "https://registry.yarnpkg.com/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.22.0.tgz#ba0360937f8d06e40180a43fe0d5616fff532cf3"
+
+babel-plugin-transform-async-to-generator@^6.22.0:
+ version "6.24.1"
+ resolved "https://registry.yarnpkg.com/babel-plugin-transform-async-to-generator/-/babel-plugin-transform-async-to-generator-6.24.1.tgz#6536e378aff6cb1d5517ac0e40eb3e9fc8d08761"
+ dependencies:
+ babel-helper-remap-async-to-generator "^6.24.1"
+ babel-plugin-syntax-async-functions "^6.8.0"
+ babel-runtime "^6.22.0"
+
+babel-plugin-transform-es2015-arrow-functions@^6.22.0:
+ version "6.22.0"
+ resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz#452692cb711d5f79dc7f85e440ce41b9f244d221"
+ dependencies:
+ babel-runtime "^6.22.0"
+
+babel-plugin-transform-es2015-block-scoped-functions@^6.22.0:
+ version "6.22.0"
+ resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-block-scoped-functions/-/babel-plugin-transform-es2015-block-scoped-functions-6.22.0.tgz#bbc51b49f964d70cb8d8e0b94e820246ce3a6141"
+ dependencies:
+ babel-runtime "^6.22.0"
+
+babel-plugin-transform-es2015-block-scoping@^6.23.0:
+ version "6.24.1"
+ resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.24.1.tgz#76c295dc3a4741b1665adfd3167215dcff32a576"
+ dependencies:
+ babel-runtime "^6.22.0"
+ babel-template "^6.24.1"
+ babel-traverse "^6.24.1"
+ babel-types "^6.24.1"
+ lodash "^4.2.0"
+
+babel-plugin-transform-es2015-classes@^6.23.0:
+ version "6.24.1"
+ resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz#5a4c58a50c9c9461e564b4b2a3bfabc97a2584db"
+ dependencies:
+ babel-helper-define-map "^6.24.1"
+ babel-helper-function-name "^6.24.1"
+ babel-helper-optimise-call-expression "^6.24.1"
+ babel-helper-replace-supers "^6.24.1"
+ babel-messages "^6.23.0"
+ babel-runtime "^6.22.0"
+ babel-template "^6.24.1"
+ babel-traverse "^6.24.1"
+ babel-types "^6.24.1"
+
+babel-plugin-transform-es2015-computed-properties@^6.22.0:
+ version "6.24.1"
+ resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.24.1.tgz#6fe2a8d16895d5634f4cd999b6d3480a308159b3"
+ dependencies:
+ babel-runtime "^6.22.0"
+ babel-template "^6.24.1"
+
+babel-plugin-transform-es2015-destructuring@^6.23.0:
+ version "6.23.0"
+ resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.23.0.tgz#997bb1f1ab967f682d2b0876fe358d60e765c56d"
+ dependencies:
+ babel-runtime "^6.22.0"
+
+babel-plugin-transform-es2015-duplicate-keys@^6.22.0:
+ version "6.24.1"
+ resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.24.1.tgz#73eb3d310ca969e3ef9ec91c53741a6f1576423e"
+ dependencies:
+ babel-runtime "^6.22.0"
+ babel-types "^6.24.1"
+
+babel-plugin-transform-es2015-for-of@^6.23.0:
+ version "6.23.0"
+ resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.23.0.tgz#f47c95b2b613df1d3ecc2fdb7573623c75248691"
+ dependencies:
+ babel-runtime "^6.22.0"
+
+babel-plugin-transform-es2015-function-name@^6.22.0:
+ version "6.24.1"
+ resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.24.1.tgz#834c89853bc36b1af0f3a4c5dbaa94fd8eacaa8b"
+ dependencies:
+ babel-helper-function-name "^6.24.1"
+ babel-runtime "^6.22.0"
+ babel-types "^6.24.1"
+
+babel-plugin-transform-es2015-literals@^6.22.0:
+ version "6.22.0"
+ resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-literals-6.22.0.tgz#4f54a02d6cd66cf915280019a31d31925377ca2e"
+ dependencies:
+ babel-runtime "^6.22.0"
+
+babel-plugin-transform-es2015-modules-amd@^6.22.0, babel-plugin-transform-es2015-modules-amd@^6.24.1:
+ version "6.24.1"
+ resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.24.1.tgz#3b3e54017239842d6d19c3011c4bd2f00a00d154"
+ dependencies:
+ babel-plugin-transform-es2015-modules-commonjs "^6.24.1"
+ babel-runtime "^6.22.0"
+ babel-template "^6.24.1"
+
+babel-plugin-transform-es2015-modules-commonjs@^6.23.0, babel-plugin-transform-es2015-modules-commonjs@^6.24.1:
+ version "6.24.1"
+ resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.24.1.tgz#d3e310b40ef664a36622200097c6d440298f2bfe"
+ dependencies:
+ babel-plugin-transform-strict-mode "^6.24.1"
+ babel-runtime "^6.22.0"
+ babel-template "^6.24.1"
+ babel-types "^6.24.1"
+
+babel-plugin-transform-es2015-modules-systemjs@^6.23.0:
+ version "6.24.1"
+ resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.24.1.tgz#ff89a142b9119a906195f5f106ecf305d9407d23"
+ dependencies:
+ babel-helper-hoist-variables "^6.24.1"
+ babel-runtime "^6.22.0"
+ babel-template "^6.24.1"
+
+babel-plugin-transform-es2015-modules-umd@^6.23.0:
+ version "6.24.1"
+ resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.24.1.tgz#ac997e6285cd18ed6176adb607d602344ad38468"
+ dependencies:
+ babel-plugin-transform-es2015-modules-amd "^6.24.1"
+ babel-runtime "^6.22.0"
+ babel-template "^6.24.1"
+
+babel-plugin-transform-es2015-object-super@^6.22.0:
+ version "6.24.1"
+ resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.24.1.tgz#24cef69ae21cb83a7f8603dad021f572eb278f8d"
+ dependencies:
+ babel-helper-replace-supers "^6.24.1"
+ babel-runtime "^6.22.0"
+
+babel-plugin-transform-es2015-parameters@^6.23.0:
+ version "6.24.1"
+ resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz#57ac351ab49caf14a97cd13b09f66fdf0a625f2b"
+ dependencies:
+ babel-helper-call-delegate "^6.24.1"
+ babel-helper-get-function-arity "^6.24.1"
+ babel-runtime "^6.22.0"
+ babel-template "^6.24.1"
+ babel-traverse "^6.24.1"
+ babel-types "^6.24.1"
+
+babel-plugin-transform-es2015-shorthand-properties@^6.22.0:
+ version "6.24.1"
+ resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz#24f875d6721c87661bbd99a4622e51f14de38aa0"
+ dependencies:
+ babel-runtime "^6.22.0"
+ babel-types "^6.24.1"
+
+babel-plugin-transform-es2015-spread@^6.22.0:
+ version "6.22.0"
+ resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-spread-6.22.0.tgz#d6d68a99f89aedc4536c81a542e8dd9f1746f8d1"
+ dependencies:
+ babel-runtime "^6.22.0"
+
+babel-plugin-transform-es2015-sticky-regex@^6.22.0:
+ version "6.24.1"
+ resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.24.1.tgz#00c1cdb1aca71112cdf0cf6126c2ed6b457ccdbc"
+ dependencies:
+ babel-helper-regex "^6.24.1"
+ babel-runtime "^6.22.0"
+ babel-types "^6.24.1"
+
+babel-plugin-transform-es2015-template-literals@^6.22.0:
+ version "6.22.0"
+ resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transform-es2015-template-literals-6.22.0.tgz#a84b3450f7e9f8f1f6839d6d687da84bb1236d8d"
+ dependencies:
+ babel-runtime "^6.22.0"
+
+babel-plugin-transform-es2015-typeof-symbol@^6.23.0:
+ version "6.23.0"
+ resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.23.0.tgz#dec09f1cddff94b52ac73d505c84df59dcceb372"
+ dependencies:
+ babel-runtime "^6.22.0"
+
+babel-plugin-transform-es2015-unicode-regex@^6.22.0:
+ version "6.24.1"
+ resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.24.1.tgz#d38b12f42ea7323f729387f18a7c5ae1faeb35e9"
+ dependencies:
+ babel-helper-regex "^6.24.1"
+ babel-runtime "^6.22.0"
+ regexpu-core "^2.0.0"
+
+babel-plugin-transform-exponentiation-operator@^6.22.0:
+ version "6.24.1"
+ resolved "https://registry.yarnpkg.com/babel-plugin-transform-exponentiation-operator/-/babel-plugin-transform-exponentiation-operator-6.24.1.tgz#2ab0c9c7f3098fa48907772bb813fe41e8de3a0e"
+ dependencies:
+ babel-helper-builder-binary-assignment-operator-visitor "^6.24.1"
+ babel-plugin-syntax-exponentiation-operator "^6.8.0"
+ babel-runtime "^6.22.0"
+
+babel-plugin-transform-regenerator@^6.22.0:
+ version "6.24.1"
+ resolved "https://registry.yarnpkg.com/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.24.1.tgz#b8da305ad43c3c99b4848e4fe4037b770d23c418"
+ dependencies:
+ regenerator-transform "0.9.11"
+
+babel-plugin-transform-strict-mode@^6.24.1:
+ version "6.24.1"
+ resolved "https://registry.yarnpkg.com/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz#d5faf7aa578a65bbe591cf5edae04a0c67020758"
+ dependencies:
+ babel-runtime "^6.22.0"
+ babel-types "^6.24.1"
+
+babel-preset-env@^1.6.0:
+ version "1.6.0"
+ resolved "https://registry.yarnpkg.com/babel-preset-env/-/babel-preset-env-1.6.0.tgz#2de1c782a780a0a5d605d199c957596da43c44e4"
+ dependencies:
+ babel-plugin-check-es2015-constants "^6.22.0"
+ babel-plugin-syntax-trailing-function-commas "^6.22.0"
+ babel-plugin-transform-async-to-generator "^6.22.0"
+ babel-plugin-transform-es2015-arrow-functions "^6.22.0"
+ babel-plugin-transform-es2015-block-scoped-functions "^6.22.0"
+ babel-plugin-transform-es2015-block-scoping "^6.23.0"
+ babel-plugin-transform-es2015-classes "^6.23.0"
+ babel-plugin-transform-es2015-computed-properties "^6.22.0"
+ babel-plugin-transform-es2015-destructuring "^6.23.0"
+ babel-plugin-transform-es2015-duplicate-keys "^6.22.0"
+ babel-plugin-transform-es2015-for-of "^6.23.0"
+ babel-plugin-transform-es2015-function-name "^6.22.0"
+ babel-plugin-transform-es2015-literals "^6.22.0"
+ babel-plugin-transform-es2015-modules-amd "^6.22.0"
+ babel-plugin-transform-es2015-modules-commonjs "^6.23.0"
+ babel-plugin-transform-es2015-modules-systemjs "^6.23.0"
+ babel-plugin-transform-es2015-modules-umd "^6.23.0"
+ babel-plugin-transform-es2015-object-super "^6.22.0"
+ babel-plugin-transform-es2015-parameters "^6.23.0"
+ babel-plugin-transform-es2015-shorthand-properties "^6.22.0"
+ babel-plugin-transform-es2015-spread "^6.22.0"
+ babel-plugin-transform-es2015-sticky-regex "^6.22.0"
+ babel-plugin-transform-es2015-template-literals "^6.22.0"
+ babel-plugin-transform-es2015-typeof-symbol "^6.23.0"
+ babel-plugin-transform-es2015-unicode-regex "^6.22.0"
+ babel-plugin-transform-exponentiation-operator "^6.22.0"
+ babel-plugin-transform-regenerator "^6.22.0"
+ browserslist "^2.1.2"
+ invariant "^2.2.2"
+ semver "^5.3.0"
+
+babel-register@^6.24.1:
+ version "6.24.1"
+ resolved "https://registry.yarnpkg.com/babel-register/-/babel-register-6.24.1.tgz#7e10e13a2f71065bdfad5a1787ba45bca6ded75f"
+ dependencies:
+ babel-core "^6.24.1"
+ babel-runtime "^6.22.0"
+ core-js "^2.4.0"
+ home-or-tmp "^2.0.0"
+ lodash "^4.2.0"
+ mkdirp "^0.5.1"
+ source-map-support "^0.4.2"
+
+babel-runtime@^6.18.0, babel-runtime@^6.22.0:
+ version "6.23.0"
+ resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.23.0.tgz#0a9489f144de70efb3ce4300accdb329e2fc543b"
+ dependencies:
+ core-js "^2.4.0"
+ regenerator-runtime "^0.10.0"
+
+babel-template@^6.24.1, babel-template@^6.25.0:
+ version "6.25.0"
+ resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.25.0.tgz#665241166b7c2aa4c619d71e192969552b10c071"
+ dependencies:
+ babel-runtime "^6.22.0"
+ babel-traverse "^6.25.0"
+ babel-types "^6.25.0"
+ babylon "^6.17.2"
+ lodash "^4.2.0"
+
+babel-traverse@^6.24.1, babel-traverse@^6.25.0:
+ version "6.25.0"
+ resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.25.0.tgz#2257497e2fcd19b89edc13c4c91381f9512496f1"
+ dependencies:
+ babel-code-frame "^6.22.0"
+ babel-messages "^6.23.0"
+ babel-runtime "^6.22.0"
+ babel-types "^6.25.0"
+ babylon "^6.17.2"
+ debug "^2.2.0"
+ globals "^9.0.0"
+ invariant "^2.2.0"
+ lodash "^4.2.0"
+
+babel-types@^6.19.0, babel-types@^6.24.1, babel-types@^6.25.0:
+ version "6.25.0"
+ resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.25.0.tgz#70afb248d5660e5d18f811d91c8303b54134a18e"
+ dependencies:
+ babel-runtime "^6.22.0"
+ esutils "^2.0.2"
+ lodash "^4.2.0"
+ to-fast-properties "^1.0.1"
+
+babylon@^6.17.2:
+ version "6.17.4"
+ resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.17.4.tgz#3e8b7402b88d22c3423e137a1577883b15ff869a"
+
+balanced-match@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
+
+base64-js@^1.0.2:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.2.1.tgz#a91947da1f4a516ea38e5b4ec0ec3773675e0886"
+
+bcrypt-pbkdf@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz#63bc5dcb61331b92bc05fd528953c33462a06f8d"
+ dependencies:
+ tweetnacl "^0.14.3"
+
+big.js@^3.1.3:
+ version "3.1.3"
+ resolved "https://registry.yarnpkg.com/big.js/-/big.js-3.1.3.tgz#4cada2193652eb3ca9ec8e55c9015669c9806978"
+
+binary-extensions@^1.0.0:
+ version "1.9.0"
+ resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.9.0.tgz#66506c16ce6f4d6928a5b3cd6a33ca41e941e37b"
+
+block-stream@*:
+ version "0.0.9"
+ resolved "https://registry.yarnpkg.com/block-stream/-/block-stream-0.0.9.tgz#13ebfe778a03205cfe03751481ebb4b3300c126a"
+ dependencies:
+ inherits "~2.0.0"
+
+bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0:
+ version "4.11.7"
+ resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.7.tgz#ddb048e50d9482790094c13eb3fcfc833ce7ab46"
+
+boom@2.x.x:
+ version "2.10.1"
+ resolved "https://registry.yarnpkg.com/boom/-/boom-2.10.1.tgz#39c8918ceff5799f83f9492a848f625add0c766f"
+ dependencies:
+ hoek "2.x.x"
+
+brace-expansion@^1.1.7:
+ version "1.1.8"
+ resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.8.tgz#c07b211c7c952ec1f8efd51a77ef0d1d3990a292"
+ dependencies:
+ balanced-match "^1.0.0"
+ concat-map "0.0.1"
+
+braces@^1.8.2:
+ version "1.8.5"
+ resolved "https://registry.yarnpkg.com/braces/-/braces-1.8.5.tgz#ba77962e12dff969d6b76711e914b737857bf6a7"
+ dependencies:
+ expand-range "^1.8.1"
+ preserve "^0.2.0"
+ repeat-element "^1.1.2"
+
+brorand@^1.0.1:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f"
+
+browserify-aes@^1.0.0, browserify-aes@^1.0.4:
+ version "1.0.6"
+ resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.0.6.tgz#5e7725dbdef1fd5930d4ebab48567ce451c48a0a"
+ dependencies:
+ buffer-xor "^1.0.2"
+ cipher-base "^1.0.0"
+ create-hash "^1.1.0"
+ evp_bytestokey "^1.0.0"
+ inherits "^2.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"
+ 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"
+ dependencies:
+ cipher-base "^1.0.1"
+ des.js "^1.0.0"
+ inherits "^2.0.1"
+
+browserify-rsa@^4.0.0:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.0.1.tgz#21e0abfaf6f2029cf2fafb133567a701d4135524"
+ dependencies:
+ bn.js "^4.1.0"
+ randombytes "^2.0.1"
+
+browserify-sign@^4.0.0:
+ version "4.0.4"
+ resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.0.4.tgz#aa4eb68e5d7b658baa6bf6a57e630cbd7a93d298"
+ dependencies:
+ bn.js "^4.1.1"
+ browserify-rsa "^4.0.0"
+ create-hash "^1.1.0"
+ create-hmac "^1.1.2"
+ elliptic "^6.0.0"
+ inherits "^2.0.1"
+ parse-asn1 "^5.0.0"
+
+browserify-zlib@^0.1.4:
+ version "0.1.4"
+ resolved "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.1.4.tgz#bb35f8a519f600e0fa6b8485241c979d0141fb2d"
+ dependencies:
+ pako "~0.2.0"
+
+browserslist@^2.1.2:
+ version "2.2.2"
+ resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-2.2.2.tgz#e9b4618b8a01c193f9786beea09f6fd10dbe31c3"
+ dependencies:
+ caniuse-lite "^1.0.30000704"
+ electron-to-chromium "^1.3.16"
+
+buffer-xor@^1.0.2:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9"
+
+buffer@^4.3.0:
+ version "4.9.1"
+ resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.1.tgz#6d1bb601b07a4efced97094132093027c95bc298"
+ dependencies:
+ base64-js "^1.0.2"
+ ieee754 "^1.1.4"
+ isarray "^1.0.0"
+
+builtin-modules@^1.0.0, builtin-modules@^1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f"
+
+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"
+
+caller-path@^0.1.0:
+ version "0.1.0"
+ resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-0.1.0.tgz#94085ef63581ecd3daa92444a8fe94e82577751f"
+ dependencies:
+ callsites "^0.2.0"
+
+callsites@^0.2.0:
+ version "0.2.0"
+ resolved "https://registry.yarnpkg.com/callsites/-/callsites-0.2.0.tgz#afab96262910a7f33c19a5775825c69f34e350ca"
+
+camelcase@^1.0.2:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-1.2.1.tgz#9bb5304d2e0b56698b2c758b08a3eaa9daa58a39"
+
+camelcase@^4.1.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd"
+
+caniuse-lite@^1.0.30000704:
+ version "1.0.30000706"
+ resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000706.tgz#bc59abc41ba7d4a3634dda95befded6114e1f24e"
+
+caseless@~0.12.0:
+ version "0.12.0"
+ resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
+
+center-align@^0.1.1:
+ version "0.1.3"
+ resolved "https://registry.yarnpkg.com/center-align/-/center-align-0.1.3.tgz#aa0d32629b6ee972200411cbd4461c907bc2b7ad"
+ dependencies:
+ align-text "^0.1.3"
+ lazy-cache "^1.0.3"
+
+chalk@^1.1.0, chalk@^1.1.1, chalk@^1.1.3:
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98"
+ dependencies:
+ ansi-styles "^2.2.1"
+ escape-string-regexp "^1.0.2"
+ has-ansi "^2.0.0"
+ strip-ansi "^3.0.0"
+ supports-color "^2.0.0"
+
+chalk@^2.0.0:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.0.1.tgz#dbec49436d2ae15f536114e76d14656cdbc0f44d"
+ dependencies:
+ ansi-styles "^3.1.0"
+ escape-string-regexp "^1.0.5"
+ supports-color "^4.0.0"
+
+chokidar@^1.7.0:
+ version "1.7.0"
+ resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-1.7.0.tgz#798e689778151c8076b4b360e5edd28cda2bb468"
+ dependencies:
+ anymatch "^1.3.0"
+ async-each "^1.0.0"
+ glob-parent "^2.0.0"
+ inherits "^2.0.1"
+ is-binary-path "^1.0.0"
+ is-glob "^2.0.0"
+ path-is-absolute "^1.0.0"
+ readdirp "^2.0.0"
+ optionalDependencies:
+ fsevents "^1.0.0"
+
+cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de"
+ dependencies:
+ inherits "^2.0.1"
+ safe-buffer "^5.0.1"
+
+circular-json@^0.3.1:
+ version "0.3.3"
+ resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.3.3.tgz#815c99ea84f6809529d2f45791bdf82711352d66"
+
+cli-cursor@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5"
+ dependencies:
+ restore-cursor "^2.0.0"
+
+cli-width@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.1.0.tgz#b234ca209b29ef66fc518d9b98d5847b00edf00a"
+
+cliui@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/cliui/-/cliui-2.1.0.tgz#4b475760ff80264c762c3a1719032e91c7fea0d1"
+ dependencies:
+ center-align "^0.1.1"
+ right-align "^0.1.1"
+ wordwrap "0.0.2"
+
+cliui@^3.2.0:
+ version "3.2.0"
+ resolved "https://registry.yarnpkg.com/cliui/-/cliui-3.2.0.tgz#120601537a916d29940f934da3b48d585a39213d"
+ dependencies:
+ string-width "^1.0.1"
+ strip-ansi "^3.0.1"
+ wrap-ansi "^2.0.0"
+
+co@^4.6.0:
+ version "4.6.0"
+ resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184"
+
+code-point-at@^1.0.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77"
+
+color-convert@^1.9.0:
+ version "1.9.0"
+ resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.0.tgz#1accf97dd739b983bf994d56fec8f95853641b7a"
+ dependencies:
+ color-name "^1.1.1"
+
+color-name@^1.1.1:
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
+
+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"
+ dependencies:
+ delayed-stream "~1.0.0"
+
+commondir@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b"
+
+concat-map@0.0.1:
+ version "0.0.1"
+ resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
+
+concat-stream@^1.6.0:
+ version "1.6.0"
+ resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.0.tgz#0aac662fd52be78964d5532f694784e70110acf7"
+ dependencies:
+ inherits "^2.0.3"
+ readable-stream "^2.2.2"
+ typedarray "^0.0.6"
+
+console-browserify@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.1.0.tgz#f0241c45730a9fc6323b206dbf38edc741d0bb10"
+ dependencies:
+ date-now "^0.1.4"
+
+console-control-strings@^1.0.0, console-control-strings@~1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e"
+
+constants-browserify@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75"
+
+contains-path@^0.1.0:
+ version "0.1.0"
+ resolved "https://registry.yarnpkg.com/contains-path/-/contains-path-0.1.0.tgz#fe8cf184ff6670b6baef01a9d4861a5cbec4120a"
+
+convert-source-map@^1.1.0:
+ version "1.5.0"
+ resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.5.0.tgz#9acd70851c6d5dfdd93d9282e5edf94a03ff46b5"
+
+core-js@^2.4.0:
+ version "2.4.1"
+ resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.4.1.tgz#4de911e667b0eae9124e34254b53aea6fc618d3e"
+
+core-util-is@~1.0.0:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
+
+create-ecdh@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.0.tgz#888c723596cdf7612f6498233eebd7a35301737d"
+ dependencies:
+ bn.js "^4.1.0"
+ elliptic "^6.0.0"
+
+create-hash@^1.1.0, create-hash@^1.1.1, create-hash@^1.1.2:
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.1.3.tgz#606042ac8b9262750f483caddab0f5819172d8fd"
+ dependencies:
+ cipher-base "^1.0.1"
+ inherits "^2.0.1"
+ ripemd160 "^2.0.0"
+ 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"
+ dependencies:
+ cipher-base "^1.0.3"
+ create-hash "^1.1.0"
+ inherits "^2.0.1"
+ ripemd160 "^2.0.0"
+ safe-buffer "^5.0.1"
+ sha.js "^2.4.8"
+
+cross-spawn@^5.0.1, cross-spawn@^5.1.0:
+ version "5.1.0"
+ resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449"
+ dependencies:
+ lru-cache "^4.0.1"
+ 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"
+ dependencies:
+ boom "2.x.x"
+
+crypto-browserify@^3.11.0:
+ version "3.11.1"
+ resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.11.1.tgz#948945efc6757a400d6e5e5af47194d10064279f"
+ dependencies:
+ browserify-cipher "^1.0.0"
+ browserify-sign "^4.0.0"
+ create-ecdh "^4.0.0"
+ create-hash "^1.1.0"
+ create-hmac "^1.1.0"
+ diffie-hellman "^5.0.0"
+ inherits "^2.0.1"
+ pbkdf2 "^3.0.3"
+ public-encrypt "^4.0.0"
+ randombytes "^2.0.0"
+
+d@1:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/d/-/d-1.0.0.tgz#754bb5bfe55451da69a58b94d45f4c5b0462d58f"
+ dependencies:
+ es5-ext "^0.10.9"
+
+dashdash@^1.12.0:
+ version "1.14.1"
+ resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0"
+ dependencies:
+ assert-plus "^1.0.0"
+
+date-now@^0.1.4:
+ version "0.1.4"
+ resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b"
+
+debug@^2.1.1, debug@^2.2.0, debug@^2.6.8:
+ version "2.6.8"
+ resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.8.tgz#e731531ca2ede27d188222427da17821d68ff4fc"
+ dependencies:
+ ms "2.0.0"
+
+decamelize@^1.0.0, decamelize@^1.1.1:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
+
+deep-extend@~0.4.0:
+ version "0.4.2"
+ resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.4.2.tgz#48b699c27e334bf89f10892be432f6e4c7d34a7f"
+
+deep-is@~0.1.3:
+ version "0.1.3"
+ resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34"
+
+del@^2.0.2:
+ version "2.2.2"
+ resolved "https://registry.yarnpkg.com/del/-/del-2.2.2.tgz#c12c981d067846c84bcaf862cff930d907ffd1a8"
+ dependencies:
+ globby "^5.0.0"
+ is-path-cwd "^1.0.0"
+ is-path-in-cwd "^1.0.0"
+ object-assign "^4.0.1"
+ pify "^2.0.0"
+ pinkie-promise "^2.0.0"
+ rimraf "^2.2.8"
+
+delayed-stream@~1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
+
+delegates@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a"
+
+des.js@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.0.tgz#c074d2e2aa6a8a9a07dbd61f9a15c2cd83ec8ecc"
+ dependencies:
+ inherits "^2.0.1"
+ minimalistic-assert "^1.0.0"
+
+detect-indent@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-4.0.0.tgz#f76d064352cdf43a1cb6ce619c4ee3a9475de208"
+ dependencies:
+ repeating "^2.0.0"
+
+diffie-hellman@^5.0.0:
+ version "5.0.2"
+ resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.2.tgz#b5835739270cfe26acf632099fded2a07f209e5e"
+ dependencies:
+ bn.js "^4.1.0"
+ miller-rabin "^4.0.0"
+ randombytes "^2.0.0"
+
+doctrine@1.5.0:
+ version "1.5.0"
+ resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa"
+ dependencies:
+ esutils "^2.0.2"
+ isarray "^1.0.0"
+
+doctrine@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.0.0.tgz#c73d8d2909d22291e1a007a395804da8b665fe63"
+ dependencies:
+ esutils "^2.0.2"
+ isarray "^1.0.0"
+
+domain-browser@^1.1.1:
+ version "1.1.7"
+ resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.1.7.tgz#867aa4b093faa05f1de08c06f4d7b21fdf8698bc"
+
+ecc-jsbn@~0.1.1:
+ version "0.1.1"
+ resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz#0fc73a9ed5f0d53c38193398523ef7e543777505"
+ dependencies:
+ jsbn "~0.1.0"
+
+electron-to-chromium@^1.3.16:
+ version "1.3.16"
+ resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.16.tgz#d0e026735754770901ae301a21664cba45d92f7d"
+
+elliptic@^6.0.0:
+ version "6.4.0"
+ resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.4.0.tgz#cac9af8762c85836187003c8dfe193e5e2eae5df"
+ dependencies:
+ bn.js "^4.4.0"
+ brorand "^1.0.1"
+ hash.js "^1.0.0"
+ hmac-drbg "^1.0.0"
+ inherits "^2.0.1"
+ minimalistic-assert "^1.0.0"
+ minimalistic-crypto-utils "^1.0.0"
+
+emojis-list@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389"
+
+enhanced-resolve@^3.4.0:
+ version "3.4.1"
+ resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-3.4.1.tgz#0421e339fd71419b3da13d129b3979040230476e"
+ dependencies:
+ graceful-fs "^4.1.2"
+ memory-fs "^0.4.0"
+ object-assign "^4.0.1"
+ tapable "^0.2.7"
+
+errno@^0.1.3:
+ version "0.1.4"
+ resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.4.tgz#b896e23a9e5e8ba33871fc996abd3635fc9a1c7d"
+ dependencies:
+ prr "~0.0.0"
+
+error-ex@^1.2.0:
+ version "1.3.1"
+ resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.1.tgz#f855a86ce61adc4e8621c3cda21e7a7612c3a8dc"
+ dependencies:
+ is-arrayish "^0.2.1"
+
+es5-ext@^0.10.14, es5-ext@^0.10.9, es5-ext@~0.10.14:
+ version "0.10.24"
+ resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.24.tgz#a55877c9924bc0c8d9bd3c2cbe17495ac1709b14"
+ dependencies:
+ es6-iterator "2"
+ es6-symbol "~3.1"
+
+es6-iterator@2, es6-iterator@^2.0.1, es6-iterator@~2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.1.tgz#8e319c9f0453bf575d374940a655920e59ca5512"
+ dependencies:
+ d "1"
+ es5-ext "^0.10.14"
+ es6-symbol "^3.1"
+
+es6-map@^0.1.3:
+ version "0.1.5"
+ resolved "https://registry.yarnpkg.com/es6-map/-/es6-map-0.1.5.tgz#9136e0503dcc06a301690f0bb14ff4e364e949f0"
+ dependencies:
+ d "1"
+ es5-ext "~0.10.14"
+ es6-iterator "~2.0.1"
+ es6-set "~0.1.5"
+ es6-symbol "~3.1.1"
+ event-emitter "~0.3.5"
+
+es6-set@~0.1.5:
+ version "0.1.5"
+ resolved "https://registry.yarnpkg.com/es6-set/-/es6-set-0.1.5.tgz#d2b3ec5d4d800ced818db538d28974db0a73ccb1"
+ dependencies:
+ d "1"
+ es5-ext "~0.10.14"
+ es6-iterator "~2.0.1"
+ es6-symbol "3.1.1"
+ event-emitter "~0.3.5"
+
+es6-symbol@3.1.1, es6-symbol@^3.1, es6-symbol@^3.1.1, es6-symbol@~3.1, es6-symbol@~3.1.1:
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.1.tgz#bf00ef4fdab6ba1b46ecb7b629b4c7ed5715cc77"
+ dependencies:
+ d "1"
+ es5-ext "~0.10.14"
+
+es6-weak-map@^2.0.1:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/es6-weak-map/-/es6-weak-map-2.0.2.tgz#5e3ab32251ffd1538a1f8e5ffa1357772f92d96f"
+ dependencies:
+ d "1"
+ es5-ext "^0.10.14"
+ es6-iterator "^2.0.1"
+ es6-symbol "^3.1.1"
+
+escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5:
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
+
+escope@^3.6.0:
+ version "3.6.0"
+ resolved "https://registry.yarnpkg.com/escope/-/escope-3.6.0.tgz#e01975e812781a163a6dadfdd80398dc64c889c3"
+ dependencies:
+ es6-map "^0.1.3"
+ es6-weak-map "^2.0.1"
+ esrecurse "^4.1.0"
+ estraverse "^4.1.1"
+
+eslint-config-airbnb-base@^11.3.1:
+ version "11.3.1"
+ resolved "https://registry.yarnpkg.com/eslint-config-airbnb-base/-/eslint-config-airbnb-base-11.3.1.tgz#c0ab108c9beed503cb999e4c60f4ef98eda0ed30"
+ dependencies:
+ eslint-restricted-globals "^0.1.1"
+
+eslint-import-resolver-node@^0.3.1:
+ version "0.3.1"
+ resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.1.tgz#4422574cde66a9a7b099938ee4d508a199e0e3cc"
+ dependencies:
+ debug "^2.6.8"
+ resolve "^1.2.0"
+
+eslint-module-utils@^2.1.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.1.1.tgz#abaec824177613b8a95b299639e1b6facf473449"
+ dependencies:
+ debug "^2.6.8"
+ pkg-dir "^1.0.0"
+
+eslint-plugin-import@^2.7.0:
+ version "2.7.0"
+ resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.7.0.tgz#21de33380b9efb55f5ef6d2e210ec0e07e7fa69f"
+ dependencies:
+ builtin-modules "^1.1.1"
+ contains-path "^0.1.0"
+ debug "^2.6.8"
+ doctrine "1.5.0"
+ eslint-import-resolver-node "^0.3.1"
+ eslint-module-utils "^2.1.1"
+ has "^1.0.1"
+ lodash.cond "^4.3.0"
+ minimatch "^3.0.3"
+ read-pkg-up "^2.0.0"
+
+eslint-restricted-globals@^0.1.1:
+ version "0.1.1"
+ resolved "https://registry.yarnpkg.com/eslint-restricted-globals/-/eslint-restricted-globals-0.1.1.tgz#35f0d5cbc64c2e3ed62e93b4b1a7af05ba7ed4d7"
+
+eslint-scope@^3.7.1:
+ version "3.7.1"
+ resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-3.7.1.tgz#3d63c3edfda02e06e01a452ad88caacc7cdcb6e8"
+ dependencies:
+ esrecurse "^4.1.0"
+ estraverse "^4.1.1"
+
+eslint@^4.3.0:
+ version "4.3.0"
+ resolved "https://registry.yarnpkg.com/eslint/-/eslint-4.3.0.tgz#fcd7c96376bbf34c85ee67ed0012a299642b108f"
+ dependencies:
+ ajv "^5.2.0"
+ babel-code-frame "^6.22.0"
+ chalk "^1.1.3"
+ concat-stream "^1.6.0"
+ cross-spawn "^5.1.0"
+ debug "^2.6.8"
+ doctrine "^2.0.0"
+ eslint-scope "^3.7.1"
+ espree "^3.4.3"
+ esquery "^1.0.0"
+ estraverse "^4.2.0"
+ esutils "^2.0.2"
+ file-entry-cache "^2.0.0"
+ functional-red-black-tree "^1.0.1"
+ glob "^7.1.2"
+ globals "^9.17.0"
+ ignore "^3.3.3"
+ imurmurhash "^0.1.4"
+ inquirer "^3.0.6"
+ is-resolvable "^1.0.0"
+ js-yaml "^3.8.4"
+ json-stable-stringify "^1.0.1"
+ levn "^0.3.0"
+ lodash "^4.17.4"
+ minimatch "^3.0.2"
+ mkdirp "^0.5.1"
+ natural-compare "^1.4.0"
+ optionator "^0.8.2"
+ path-is-inside "^1.0.2"
+ pluralize "^4.0.0"
+ progress "^2.0.0"
+ require-uncached "^1.0.3"
+ semver "^5.3.0"
+ strip-json-comments "~2.0.1"
+ table "^4.0.1"
+ text-table "~0.2.0"
+
+espree@^3.4.3:
+ version "3.4.3"
+ resolved "https://registry.yarnpkg.com/espree/-/espree-3.4.3.tgz#2910b5ccd49ce893c2ffffaab4fd8b3a31b82374"
+ dependencies:
+ acorn "^5.0.1"
+ acorn-jsx "^3.0.0"
+
+esprima@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.0.tgz#4499eddcd1110e0b218bacf2fa7f7f59f55ca804"
+
+esquery@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.0.0.tgz#cfba8b57d7fba93f17298a8a006a04cda13d80fa"
+ dependencies:
+ estraverse "^4.0.0"
+
+esrecurse@^4.1.0:
+ version "4.2.0"
+ resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.0.tgz#fa9568d98d3823f9a41d91e902dcab9ea6e5b163"
+ dependencies:
+ estraverse "^4.1.0"
+ object-assign "^4.0.1"
+
+estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1, estraverse@^4.2.0:
+ version "4.2.0"
+ resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13"
+
+esutils@^2.0.2:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b"
+
+event-emitter@~0.3.5:
+ version "0.3.5"
+ resolved "https://registry.yarnpkg.com/event-emitter/-/event-emitter-0.3.5.tgz#df8c69eef1647923c7157b9ce83840610b02cc39"
+ dependencies:
+ d "1"
+ es5-ext "~0.10.14"
+
+events@^1.0.0:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924"
+
+evp_bytestokey@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.0.tgz#497b66ad9fef65cd7c08a6180824ba1476b66e53"
+ dependencies:
+ create-hash "^1.1.1"
+
+execa@^0.7.0:
+ version "0.7.0"
+ resolved "https://registry.yarnpkg.com/execa/-/execa-0.7.0.tgz#944becd34cc41ee32a63a9faf27ad5a65fc59777"
+ dependencies:
+ cross-spawn "^5.0.1"
+ get-stream "^3.0.0"
+ is-stream "^1.1.0"
+ npm-run-path "^2.0.0"
+ p-finally "^1.0.0"
+ signal-exit "^3.0.0"
+ strip-eof "^1.0.0"
+
+expand-brackets@^0.1.4:
+ version "0.1.5"
+ resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-0.1.5.tgz#df07284e342a807cd733ac5af72411e581d1177b"
+ dependencies:
+ is-posix-bracket "^0.1.0"
+
+expand-range@^1.8.1:
+ version "1.8.2"
+ resolved "https://registry.yarnpkg.com/expand-range/-/expand-range-1.8.2.tgz#a299effd335fe2721ebae8e257ec79644fc85337"
+ dependencies:
+ fill-range "^2.1.0"
+
+extend@~3.0.0:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444"
+
+external-editor@^2.0.4:
+ version "2.0.4"
+ resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-2.0.4.tgz#1ed9199da9cbfe2ef2f7a31b2fde8b0d12368972"
+ dependencies:
+ iconv-lite "^0.4.17"
+ jschardet "^1.4.2"
+ tmp "^0.0.31"
+
+extglob@^0.3.1:
+ version "0.3.2"
+ resolved "https://registry.yarnpkg.com/extglob/-/extglob-0.3.2.tgz#2e18ff3d2f49ab2765cec9023f011daa8d8349a1"
+ dependencies:
+ is-extglob "^1.0.0"
+
+extsprintf@1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.0.2.tgz#e1080e0658e300b06294990cc70e1502235fd550"
+
+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"
+
+fast-levenshtein@~2.0.4:
+ version "2.0.6"
+ resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
+
+figures@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962"
+ dependencies:
+ escape-string-regexp "^1.0.5"
+
+file-entry-cache@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-2.0.0.tgz#c392990c3e684783d838b8c84a45d8a048458361"
+ dependencies:
+ flat-cache "^1.2.1"
+ object-assign "^4.0.1"
+
+filename-regex@^2.0.0:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.1.tgz#c1c4b9bee3e09725ddb106b75c1e301fe2f18b26"
+
+fill-range@^2.1.0:
+ version "2.2.3"
+ resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-2.2.3.tgz#50b77dfd7e469bc7492470963699fe7a8485a723"
+ dependencies:
+ is-number "^2.1.0"
+ isobject "^2.0.0"
+ randomatic "^1.1.3"
+ repeat-element "^1.1.2"
+ repeat-string "^1.5.2"
+
+find-cache-dir@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-1.0.0.tgz#9288e3e9e3cc3748717d39eade17cf71fc30ee6f"
+ dependencies:
+ commondir "^1.0.1"
+ make-dir "^1.0.0"
+ pkg-dir "^2.0.0"
+
+find-up@^1.0.0:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f"
+ dependencies:
+ path-exists "^2.0.0"
+ pinkie-promise "^2.0.0"
+
+find-up@^2.0.0, find-up@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7"
+ dependencies:
+ locate-path "^2.0.0"
+
+flat-cache@^1.2.1:
+ version "1.2.2"
+ resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-1.2.2.tgz#fa86714e72c21db88601761ecf2f555d1abc6b96"
+ dependencies:
+ circular-json "^0.3.1"
+ del "^2.0.2"
+ graceful-fs "^4.1.2"
+ write "^0.2.1"
+
+for-in@^1.0.1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80"
+
+for-own@^0.1.4:
+ version "0.1.5"
+ resolved "https://registry.yarnpkg.com/for-own/-/for-own-0.1.5.tgz#5265c681a4f294dabbf17c9509b6763aa84510ce"
+ dependencies:
+ for-in "^1.0.1"
+
+forever-agent@~0.6.1:
+ version "0.6.1"
+ resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91"
+
+form-data@~2.1.1:
+ version "2.1.4"
+ resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.1.4.tgz#33c183acf193276ecaa98143a69e94bfee1750d1"
+ dependencies:
+ asynckit "^0.4.0"
+ combined-stream "^1.0.5"
+ mime-types "^2.1.12"
+
+fs.realpath@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
+
+fsevents@^1.0.0:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.1.2.tgz#3282b713fb3ad80ede0e9fcf4611b5aa6fc033f4"
+ dependencies:
+ nan "^2.3.0"
+ node-pre-gyp "^0.6.36"
+
+fstream-ignore@^1.0.5:
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/fstream-ignore/-/fstream-ignore-1.0.5.tgz#9c31dae34767018fe1d249b24dada67d092da105"
+ dependencies:
+ fstream "^1.0.0"
+ inherits "2"
+ minimatch "^3.0.0"
+
+fstream@^1.0.0, fstream@^1.0.10, fstream@^1.0.2:
+ version "1.0.11"
+ resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.11.tgz#5c1fb1f117477114f0632a0eb4b71b3cb0fd3171"
+ dependencies:
+ graceful-fs "^4.1.2"
+ inherits "~2.0.0"
+ mkdirp ">=0.5 0"
+ rimraf "2"
+
+function-bind@^1.0.2:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.0.tgz#16176714c801798e4e8f2cf7f7529467bb4a5771"
+
+functional-red-black-tree@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327"
+
+gauge@~2.7.3:
+ version "2.7.4"
+ resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7"
+ dependencies:
+ aproba "^1.0.3"
+ console-control-strings "^1.0.0"
+ has-unicode "^2.0.0"
+ object-assign "^4.1.0"
+ signal-exit "^3.0.0"
+ string-width "^1.0.1"
+ strip-ansi "^3.0.1"
+ wide-align "^1.1.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"
+
+get-stream@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14"
+
+getpass@^0.1.1:
+ version "0.1.7"
+ resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa"
+ dependencies:
+ assert-plus "^1.0.0"
+
+glob-base@^0.3.0:
+ version "0.3.0"
+ resolved "https://registry.yarnpkg.com/glob-base/-/glob-base-0.3.0.tgz#dbb164f6221b1c0b1ccf82aea328b497df0ea3c4"
+ dependencies:
+ glob-parent "^2.0.0"
+ is-glob "^2.0.0"
+
+glob-parent@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-2.0.0.tgz#81383d72db054fcccf5336daa902f182f6edbb28"
+ dependencies:
+ is-glob "^2.0.0"
+
+glob@^7.0.3, glob@^7.0.5, glob@^7.1.2:
+ version "7.1.2"
+ resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15"
+ 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"
+
+globals@^9.0.0, globals@^9.17.0:
+ version "9.18.0"
+ resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a"
+
+globby@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/globby/-/globby-5.0.0.tgz#ebd84667ca0dbb330b99bcfc68eac2bc54370e0d"
+ dependencies:
+ array-union "^1.0.1"
+ arrify "^1.0.0"
+ glob "^7.0.3"
+ object-assign "^4.0.1"
+ pify "^2.0.0"
+ pinkie-promise "^2.0.0"
+
+graceful-fs@^4.1.2:
+ version "4.1.11"
+ resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658"
+
+har-schema@^1.0.5:
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-1.0.5.tgz#d263135f43307c02c602afc8fe95970c0151369e"
+
+har-validator@~4.2.1:
+ version "4.2.1"
+ resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-4.2.1.tgz#33481d0f1bbff600dd203d75812a6a5fba002e2a"
+ dependencies:
+ ajv "^4.9.1"
+ har-schema "^1.0.5"
+
+has-ansi@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91"
+ dependencies:
+ ansi-regex "^2.0.0"
+
+has-flag@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-2.0.0.tgz#e8207af1cc7b30d446cc70b734b5e8be18f88d51"
+
+has-unicode@^2.0.0:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9"
+
+has@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/has/-/has-1.0.1.tgz#8461733f538b0837c9361e39a9ab9e9704dc2f28"
+ 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"
+ dependencies:
+ inherits "^2.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"
+ dependencies:
+ inherits "^2.0.3"
+ minimalistic-assert "^1.0.0"
+
+hawk@~3.1.3:
+ version "3.1.3"
+ resolved "https://registry.yarnpkg.com/hawk/-/hawk-3.1.3.tgz#078444bd7c1640b0fe540d2c9b73d59678e8e1c4"
+ dependencies:
+ boom "2.x.x"
+ cryptiles "2.x.x"
+ hoek "2.x.x"
+ sntp "1.x.x"
+
+hmac-drbg@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1"
+ dependencies:
+ hash.js "^1.0.3"
+ 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"
+
+home-or-tmp@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/home-or-tmp/-/home-or-tmp-2.0.0.tgz#e36c3f2d2cae7d746a857e38d18d5f32a7882db8"
+ dependencies:
+ os-homedir "^1.0.0"
+ os-tmpdir "^1.0.1"
+
+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"
+
+http-signature@~1.1.0:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.1.1.tgz#df72e267066cd0ac67fb76adf8e134a8fbcf91bf"
+ dependencies:
+ assert-plus "^0.2.0"
+ jsprim "^1.2.2"
+ sshpk "^1.7.0"
+
+https-browserify@0.0.1:
+ version "0.0.1"
+ resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-0.0.1.tgz#3f91365cabe60b77ed0ebba24b454e3e09d95a82"
+
+iconv-lite@^0.4.17:
+ version "0.4.18"
+ resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.18.tgz#23d8656b16aae6742ac29732ea8f0336a4789cf2"
+
+ieee754@^1.1.4:
+ version "1.1.8"
+ resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.8.tgz#be33d40ac10ef1926701f6f08a2d86fbfd1ad3e4"
+
+ignore@^3.3.3:
+ version "3.3.3"
+ resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.3.tgz#432352e57accd87ab3110e82d3fea0e47812156d"
+
+imurmurhash@^0.1.4:
+ version "0.1.4"
+ resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
+
+indexof@0.0.1:
+ version "0.0.1"
+ resolved "https://registry.yarnpkg.com/indexof/-/indexof-0.0.1.tgz#82dc336d232b9062179d05ab3293a66059fd435d"
+
+inflight@^1.0.4:
+ version "1.0.6"
+ resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
+ dependencies:
+ once "^1.3.0"
+ wrappy "1"
+
+inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3:
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
+
+inherits@2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1"
+
+ini@~1.3.0:
+ version "1.3.4"
+ resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.4.tgz#0537cb79daf59b59a1a517dff706c86ec039162e"
+
+inquirer@^3.0.6:
+ version "3.2.1"
+ resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-3.2.1.tgz#06ceb0f540f45ca548c17d6840959878265fa175"
+ dependencies:
+ ansi-escapes "^2.0.0"
+ chalk "^2.0.0"
+ cli-cursor "^2.1.0"
+ cli-width "^2.0.0"
+ external-editor "^2.0.4"
+ figures "^2.0.0"
+ lodash "^4.3.0"
+ mute-stream "0.0.7"
+ run-async "^2.2.0"
+ rx-lite "^4.0.8"
+ rx-lite-aggregates "^4.0.8"
+ string-width "^2.1.0"
+ strip-ansi "^4.0.0"
+ through "^2.3.6"
+
+interpret@^1.0.0:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.0.3.tgz#cbc35c62eeee73f19ab7b10a801511401afc0f90"
+
+invariant@^2.2.0, invariant@^2.2.2:
+ version "2.2.2"
+ resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.2.tgz#9e1f56ac0acdb6bf303306f338be3b204ae60360"
+ dependencies:
+ loose-envify "^1.0.0"
+
+invert-kv@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6"
+
+is-arrayish@^0.2.1:
+ version "0.2.1"
+ resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d"
+
+is-binary-path@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898"
+ dependencies:
+ binary-extensions "^1.0.0"
+
+is-buffer@^1.1.5:
+ version "1.1.5"
+ resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.5.tgz#1f3b26ef613b214b88cbca23cc6c01d87961eecc"
+
+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"
+ dependencies:
+ builtin-modules "^1.0.0"
+
+is-dotfile@^1.0.0:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/is-dotfile/-/is-dotfile-1.0.3.tgz#a6a2f32ffd2dfb04f5ca25ecd0f6b83cf798a1e1"
+
+is-equal-shallow@^0.1.3:
+ version "0.1.3"
+ resolved "https://registry.yarnpkg.com/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz#2238098fc221de0bcfa5d9eac4c45d638aa1c534"
+ dependencies:
+ is-primitive "^2.0.0"
+
+is-extendable@^0.1.1:
+ version "0.1.1"
+ resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89"
+
+is-extglob@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-1.0.0.tgz#ac468177c4943405a092fc8f29760c6ffc6206c0"
+
+is-finite@^1.0.0:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.0.2.tgz#cc6677695602be550ef11e8b4aa6305342b6d0aa"
+ dependencies:
+ number-is-nan "^1.0.0"
+
+is-fullwidth-code-point@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb"
+ dependencies:
+ number-is-nan "^1.0.0"
+
+is-fullwidth-code-point@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f"
+
+is-glob@^2.0.0, is-glob@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-2.0.1.tgz#d096f926a3ded5600f3fdfd91198cb0888c2d863"
+ dependencies:
+ is-extglob "^1.0.0"
+
+is-number@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/is-number/-/is-number-2.1.0.tgz#01fcbbb393463a548f2f466cce16dece49db908f"
+ dependencies:
+ kind-of "^3.0.2"
+
+is-number@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195"
+ dependencies:
+ kind-of "^3.0.2"
+
+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"
+
+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"
+ dependencies:
+ is-path-inside "^1.0.0"
+
+is-path-inside@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-1.0.0.tgz#fc06e5a1683fbda13de667aff717bbc10a48f37f"
+ dependencies:
+ path-is-inside "^1.0.1"
+
+is-posix-bracket@^0.1.0:
+ version "0.1.1"
+ resolved "https://registry.yarnpkg.com/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz#3334dc79774368e92f016e6fbc0a88f5cd6e6bc4"
+
+is-primitive@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/is-primitive/-/is-primitive-2.0.0.tgz#207bab91638499c07b2adf240a41a87210034575"
+
+is-promise@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa"
+
+is-resolvable@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.0.0.tgz#8df57c61ea2e3c501408d100fb013cf8d6e0cc62"
+ dependencies:
+ tryit "^1.0.1"
+
+is-stream@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
+
+is-typedarray@~1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
+
+isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
+
+isexe@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
+
+isobject@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89"
+ dependencies:
+ isarray "1.0.0"
+
+isstream@~0.1.2:
+ version "0.1.2"
+ resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
+
+js-tokens@^3.0.0:
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b"
+
+js-yaml@^3.8.4:
+ version "3.9.0"
+ resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.9.0.tgz#4ffbbf25c2ac963b8299dc74da7e3740de1c18ce"
+ dependencies:
+ argparse "^1.0.7"
+ esprima "^4.0.0"
+
+jsbn@~0.1.0:
+ version "0.1.1"
+ resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513"
+
+jschardet@^1.4.2:
+ version "1.5.0"
+ resolved "https://registry.yarnpkg.com/jschardet/-/jschardet-1.5.0.tgz#a61f310306a5a71188e1b1acd08add3cfbb08b1e"
+
+jsesc@^1.3.0:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-1.3.0.tgz#46c3fec8c1892b12b0833db9bc7622176dbab34b"
+
+jsesc@~0.5.0:
+ version "0.5.0"
+ resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d"
+
+json-loader@^0.5.4:
+ version "0.5.7"
+ resolved "https://registry.yarnpkg.com/json-loader/-/json-loader-0.5.7.tgz#dca14a70235ff82f0ac9a3abeb60d337a365185d"
+
+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"
+
+json-schema@0.2.3:
+ version "0.2.3"
+ resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13"
+
+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"
+ 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"
+
+json5@^0.5.0, json5@^0.5.1:
+ version "0.5.1"
+ resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821"
+
+jsonify@~0.0.0:
+ version "0.0.0"
+ resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73"
+
+jsprim@^1.2.2:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.0.tgz#a3b87e40298d8c380552d8cc7628a0bb95a22918"
+ dependencies:
+ assert-plus "1.0.0"
+ extsprintf "1.0.2"
+ json-schema "0.2.3"
+ verror "1.3.6"
+
+kind-of@^3.0.2:
+ version "3.2.2"
+ resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64"
+ dependencies:
+ is-buffer "^1.1.5"
+
+kind-of@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57"
+ dependencies:
+ is-buffer "^1.1.5"
+
+lazy-cache@^1.0.3:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-1.0.4.tgz#a1d78fc3a50474cb80845d3b3b6e1da49a446e8e"
+
+lcid@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835"
+ dependencies:
+ invert-kv "^1.0.0"
+
+levn@^0.3.0, levn@~0.3.0:
+ version "0.3.0"
+ resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee"
+ dependencies:
+ prelude-ls "~1.1.2"
+ type-check "~0.3.2"
+
+load-json-file@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-2.0.0.tgz#7947e42149af80d696cbf797bcaabcfe1fe29ca8"
+ dependencies:
+ graceful-fs "^4.1.2"
+ parse-json "^2.2.0"
+ pify "^2.0.0"
+ strip-bom "^3.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"
+
+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"
+ dependencies:
+ big.js "^3.1.3"
+ emojis-list "^2.0.0"
+ json5 "^0.5.0"
+
+locate-path@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e"
+ dependencies:
+ p-locate "^2.0.0"
+ path-exists "^3.0.0"
+
+lodash.cond@^4.3.0:
+ version "4.5.2"
+ resolved "https://registry.yarnpkg.com/lodash.cond/-/lodash.cond-4.5.2.tgz#f471a1da486be60f6ab955d17115523dd1d255d5"
+
+lodash@^4.0.0, lodash@^4.14.0, lodash@^4.17.4, lodash@^4.2.0, lodash@^4.3.0:
+ version "4.17.4"
+ resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae"
+
+longest@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097"
+
+loose-envify@^1.0.0:
+ version "1.3.1"
+ resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.3.1.tgz#d1a8ad33fa9ce0e713d65fdd0ac8b748d478c848"
+ dependencies:
+ js-tokens "^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"
+ dependencies:
+ pseudomap "^1.0.2"
+ yallist "^2.1.2"
+
+make-dir@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.0.0.tgz#97a011751e91dd87cfadef58832ebb04936de978"
+ dependencies:
+ pify "^2.3.0"
+
+mem@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/mem/-/mem-1.1.0.tgz#5edd52b485ca1d900fe64895505399a0dfa45f76"
+ dependencies:
+ mimic-fn "^1.0.0"
+
+memory-fs@^0.4.0, memory-fs@~0.4.1:
+ version "0.4.1"
+ resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552"
+ dependencies:
+ errno "^0.1.3"
+ readable-stream "^2.0.1"
+
+micromatch@^2.1.5:
+ version "2.3.11"
+ resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565"
+ dependencies:
+ arr-diff "^2.0.0"
+ array-unique "^0.2.1"
+ braces "^1.8.2"
+ expand-brackets "^0.1.4"
+ extglob "^0.3.1"
+ filename-regex "^2.0.0"
+ is-extglob "^1.0.0"
+ is-glob "^2.0.1"
+ kind-of "^3.0.2"
+ normalize-path "^2.0.1"
+ object.omit "^2.0.0"
+ parse-glob "^3.0.4"
+ regex-cache "^0.4.2"
+
+miller-rabin@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.0.tgz#4a62fb1d42933c05583982f4c716f6fb9e6c6d3d"
+ dependencies:
+ bn.js "^4.0.0"
+ brorand "^1.0.1"
+
+mime-db@~1.29.0:
+ version "1.29.0"
+ resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.29.0.tgz#48d26d235589651704ac5916ca06001914266878"
+
+mime-types@^2.1.12, mime-types@~2.1.7:
+ version "2.1.16"
+ resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.16.tgz#2b858a52e5ecd516db897ac2be87487830698e23"
+ dependencies:
+ mime-db "~1.29.0"
+
+mimic-fn@^1.0.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.1.0.tgz#e667783d92e89dbd342818b5230b9d62a672ad18"
+
+minimalistic-assert@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz#702be2dda6b37f4836bcb3f5db56641b64a1d3d3"
+
+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"
+
+minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.3, minimatch@^3.0.4:
+ version "3.0.4"
+ resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
+ dependencies:
+ brace-expansion "^1.1.7"
+
+minimist@0.0.8:
+ version "0.0.8"
+ resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
+
+minimist@^1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284"
+
+"mkdirp@>=0.5 0", mkdirp@^0.5.1, mkdirp@~0.5.0:
+ version "0.5.1"
+ resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
+ dependencies:
+ minimist "0.0.8"
+
+ms@2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
+
+mute-stream@0.0.7:
+ version "0.0.7"
+ resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab"
+
+nan@^2.3.0:
+ version "2.6.2"
+ resolved "https://registry.yarnpkg.com/nan/-/nan-2.6.2.tgz#e4ff34e6c95fdfb5aecc08de6596f43605a7db45"
+
+natural-compare@^1.4.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
+
+node-libs-browser@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-2.0.0.tgz#a3a59ec97024985b46e958379646f96c4b616646"
+ dependencies:
+ assert "^1.1.1"
+ browserify-zlib "^0.1.4"
+ buffer "^4.3.0"
+ console-browserify "^1.1.0"
+ constants-browserify "^1.0.0"
+ crypto-browserify "^3.11.0"
+ domain-browser "^1.1.1"
+ events "^1.0.0"
+ https-browserify "0.0.1"
+ os-browserify "^0.2.0"
+ path-browserify "0.0.0"
+ process "^0.11.0"
+ punycode "^1.2.4"
+ querystring-es3 "^0.2.0"
+ readable-stream "^2.0.5"
+ stream-browserify "^2.0.1"
+ stream-http "^2.3.1"
+ string_decoder "^0.10.25"
+ timers-browserify "^2.0.2"
+ tty-browserify "0.0.0"
+ url "^0.11.0"
+ util "^0.10.3"
+ vm-browserify "0.0.4"
+
+node-pre-gyp@^0.6.36:
+ version "0.6.36"
+ resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.6.36.tgz#db604112cb74e0d477554e9b505b17abddfab786"
+ dependencies:
+ 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"
+
+nopt@^4.0.1:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d"
+ dependencies:
+ abbrev "1"
+ osenv "^0.1.4"
+
+normalize-package-data@^2.3.2:
+ version "2.4.0"
+ resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.4.0.tgz#12f95a307d58352075a04907b84ac8be98ac012f"
+ dependencies:
+ hosted-git-info "^2.1.4"
+ is-builtin-module "^1.0.0"
+ semver "2 || 3 || 4 || 5"
+ validate-npm-package-license "^3.0.1"
+
+normalize-path@^2.0.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9"
+ dependencies:
+ remove-trailing-separator "^1.0.1"
+
+npm-run-path@^2.0.0:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f"
+ dependencies:
+ path-key "^2.0.0"
+
+npmlog@^4.0.2:
+ version "4.1.2"
+ resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b"
+ dependencies:
+ are-we-there-yet "~1.1.2"
+ console-control-strings "~1.1.0"
+ gauge "~2.7.3"
+ set-blocking "~2.0.0"
+
+number-is-nan@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d"
+
+oauth-sign@~0.8.1:
+ version "0.8.2"
+ resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43"
+
+object-assign@^4.0.1, object-assign@^4.1.0:
+ version "4.1.1"
+ resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
+
+object.omit@^2.0.0:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/object.omit/-/object.omit-2.0.1.tgz#1a9c744829f39dbb858c76ca3579ae2a54ebd1fa"
+ dependencies:
+ for-own "^0.1.4"
+ is-extendable "^0.1.1"
+
+once@^1.3.0, once@^1.3.3:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
+ dependencies:
+ wrappy "1"
+
+onetime@^2.0.0:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4"
+ dependencies:
+ mimic-fn "^1.0.0"
+
+optionator@^0.8.2:
+ version "0.8.2"
+ resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64"
+ dependencies:
+ deep-is "~0.1.3"
+ fast-levenshtein "~2.0.4"
+ levn "~0.3.0"
+ prelude-ls "~1.1.2"
+ type-check "~0.3.2"
+ wordwrap "~1.0.0"
+
+os-browserify@^0.2.0:
+ version "0.2.1"
+ resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.2.1.tgz#63fc4ccee5d2d7763d26bbf8601078e6c2e0044f"
+
+os-homedir@^1.0.0:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3"
+
+os-locale@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-2.1.0.tgz#42bc2900a6b5b8bd17376c8e882b65afccf24bf2"
+ dependencies:
+ execa "^0.7.0"
+ lcid "^1.0.0"
+ mem "^1.1.0"
+
+os-tmpdir@^1.0.0, os-tmpdir@^1.0.1, os-tmpdir@~1.0.1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
+
+osenv@^0.1.4:
+ version "0.1.4"
+ resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.4.tgz#42fe6d5953df06c8064be6f176c3d05aaaa34644"
+ dependencies:
+ os-homedir "^1.0.0"
+ os-tmpdir "^1.0.0"
+
+p-finally@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae"
+
+p-limit@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.1.0.tgz#b07ff2d9a5d88bec806035895a2bab66a27988bc"
+
+p-locate@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43"
+ dependencies:
+ p-limit "^1.1.0"
+
+pako@~0.2.0:
+ version "0.2.9"
+ resolved "https://registry.yarnpkg.com/pako/-/pako-0.2.9.tgz#f3f7522f4ef782348da8161bad9ecfd51bf83a75"
+
+parse-asn1@^5.0.0:
+ version "5.1.0"
+ resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.0.tgz#37c4f9b7ed3ab65c74817b5f2480937fbf97c712"
+ 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"
+
+parse-glob@^3.0.4:
+ version "3.0.4"
+ resolved "https://registry.yarnpkg.com/parse-glob/-/parse-glob-3.0.4.tgz#b2c376cfb11f35513badd173ef0bb6e3a388391c"
+ dependencies:
+ glob-base "^0.3.0"
+ is-dotfile "^1.0.0"
+ is-extglob "^1.0.0"
+ is-glob "^2.0.0"
+
+parse-json@^2.2.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9"
+ dependencies:
+ error-ex "^1.2.0"
+
+path-browserify@0.0.0:
+ version "0.0.0"
+ resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.0.tgz#a0b870729aae214005b7d5032ec2cbbb0fb4451a"
+
+path-exists@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b"
+ dependencies:
+ pinkie-promise "^2.0.0"
+
+path-exists@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515"
+
+path-is-absolute@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
+
+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"
+
+path-key@^2.0.0:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40"
+
+path-parse@^1.0.5:
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.5.tgz#3c1adf871ea9cd6c9431b6ea2bd74a0ff055c4c1"
+
+path-type@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/path-type/-/path-type-2.0.0.tgz#f012ccb8415b7096fc2daa1054c3d72389594c73"
+ dependencies:
+ pify "^2.0.0"
+
+pbkdf2@^3.0.3:
+ version "3.0.12"
+ resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.12.tgz#be36785c5067ea48d806ff923288c5f750b6b8a2"
+ dependencies:
+ create-hash "^1.1.2"
+ create-hmac "^1.1.4"
+ ripemd160 "^2.0.1"
+ 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"
+
+pify@^2.0.0, pify@^2.3.0:
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c"
+
+pinkie-promise@^2.0.0:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa"
+ dependencies:
+ pinkie "^2.0.0"
+
+pinkie@^2.0.0:
+ version "2.0.4"
+ resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870"
+
+pkg-dir@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-1.0.0.tgz#7a4b508a8d5bb2d629d447056ff4e9c9314cf3d4"
+ dependencies:
+ find-up "^1.0.0"
+
+pkg-dir@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-2.0.0.tgz#f6d5d1109e19d63edf428e0bd57e12777615334b"
+ dependencies:
+ find-up "^2.1.0"
+
+pluralize@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-4.0.0.tgz#59b708c1c0190a2f692f1c7618c446b052fd1762"
+
+prelude-ls@~1.1.2:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54"
+
+preserve@^0.2.0:
+ version "0.2.0"
+ resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b"
+
+private@^0.1.6:
+ version "0.1.7"
+ resolved "https://registry.yarnpkg.com/private/-/private-0.1.7.tgz#68ce5e8a1ef0a23bb570cc28537b5332aba63ef1"
+
+process-nextick-args@~1.0.6:
+ version "1.0.7"
+ resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3"
+
+process@^0.11.0:
+ version "0.11.10"
+ resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182"
+
+progress@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.0.tgz#8a1be366bf8fc23db2bd23f10c6fe920b4389d1f"
+
+prr@~0.0.0:
+ version "0.0.0"
+ resolved "https://registry.yarnpkg.com/prr/-/prr-0.0.0.tgz#1a84b85908325501411853d0081ee3fa86e2926a"
+
+pseudomap@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3"
+
+public-encrypt@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.0.tgz#39f699f3a46560dd5ebacbca693caf7c65c18cc6"
+ 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"
+
+punycode@1.3.2:
+ version "1.3.2"
+ resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d"
+
+punycode@^1.2.4, punycode@^1.4.1:
+ version "1.4.1"
+ resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e"
+
+qs@~6.4.0:
+ version "6.4.0"
+ resolved "https://registry.yarnpkg.com/qs/-/qs-6.4.0.tgz#13e26d28ad6b0ffaa91312cd3bf708ed351e7233"
+
+querystring-es3@^0.2.0:
+ version "0.2.1"
+ resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73"
+
+querystring@0.2.0:
+ version "0.2.0"
+ resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620"
+
+randomatic@^1.1.3:
+ version "1.1.7"
+ resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-1.1.7.tgz#c7abe9cc8b87c0baa876b19fde83fd464797e38c"
+ dependencies:
+ is-number "^3.0.0"
+ kind-of "^4.0.0"
+
+randombytes@^2.0.0, randombytes@^2.0.1:
+ version "2.0.5"
+ resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.0.5.tgz#dc009a246b8d09a177b4b7a0ae77bc570f4b1b79"
+ dependencies:
+ safe-buffer "^5.1.0"
+
+rc@^1.1.7:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.1.tgz#2e03e8e42ee450b8cb3dce65be1bf8974e1dfd95"
+ dependencies:
+ deep-extend "~0.4.0"
+ ini "~1.3.0"
+ minimist "^1.2.0"
+ strip-json-comments "~2.0.1"
+
+read-pkg-up@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-2.0.0.tgz#6b72a8048984e0c41e79510fd5e9fa99b3b549be"
+ dependencies:
+ find-up "^2.0.0"
+ read-pkg "^2.0.0"
+
+read-pkg@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-2.0.0.tgz#8ef1c0623c6a6db0dc6713c4bfac46332b2368f8"
+ dependencies:
+ load-json-file "^2.0.0"
+ normalize-package-data "^2.3.2"
+ path-type "^2.0.0"
+
+readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.1.4, readable-stream@^2.2.2, readable-stream@^2.2.6:
+ version "2.3.3"
+ resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.3.tgz#368f2512d79f9d46fdfc71349ae7878bbc1eb95c"
+ dependencies:
+ core-util-is "~1.0.0"
+ inherits "~2.0.3"
+ isarray "~1.0.0"
+ process-nextick-args "~1.0.6"
+ safe-buffer "~5.1.1"
+ string_decoder "~1.0.3"
+ 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"
+ dependencies:
+ graceful-fs "^4.1.2"
+ minimatch "^3.0.2"
+ readable-stream "^2.0.2"
+ set-immediate-shim "^1.0.1"
+
+regenerate@^1.2.1:
+ version "1.3.2"
+ resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.3.2.tgz#d1941c67bad437e1be76433add5b385f95b19260"
+
+regenerator-runtime@^0.10.0:
+ version "0.10.5"
+ resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz#336c3efc1220adcedda2c9fab67b5a7955a33658"
+
+regenerator-transform@0.9.11:
+ version "0.9.11"
+ resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.9.11.tgz#3a7d067520cb7b7176769eb5ff868691befe1283"
+ dependencies:
+ babel-runtime "^6.18.0"
+ babel-types "^6.19.0"
+ private "^0.1.6"
+
+regex-cache@^0.4.2:
+ version "0.4.3"
+ resolved "https://registry.yarnpkg.com/regex-cache/-/regex-cache-0.4.3.tgz#9b1a6c35d4d0dfcef5711ae651e8e9d3d7114145"
+ dependencies:
+ is-equal-shallow "^0.1.3"
+ is-primitive "^2.0.0"
+
+regexpu-core@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-2.0.0.tgz#49d038837b8dcf8bfa5b9a42139938e6ea2ae240"
+ dependencies:
+ regenerate "^1.2.1"
+ regjsgen "^0.2.0"
+ regjsparser "^0.1.4"
+
+regjsgen@^0.2.0:
+ version "0.2.0"
+ resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.2.0.tgz#6c016adeac554f75823fe37ac05b92d5a4edb1f7"
+
+regjsparser@^0.1.4:
+ version "0.1.5"
+ resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.1.5.tgz#7ee8f84dc6fa792d3fd0ae228d24bd949ead205c"
+ dependencies:
+ jsesc "~0.5.0"
+
+remove-trailing-separator@^1.0.1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.0.2.tgz#69b062d978727ad14dc6b56ba4ab772fd8d70511"
+
+repeat-element@^1.1.2:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.2.tgz#ef089a178d1483baae4d93eb98b4f9e4e11d990a"
+
+repeat-string@^1.5.2:
+ version "1.6.1"
+ resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637"
+
+repeating@^2.0.0:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda"
+ 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"
+ 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"
+
+require-directory@^2.1.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
+
+require-main-filename@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1"
+
+require-uncached@^1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/require-uncached/-/require-uncached-1.0.3.tgz#4e0d56d6c9662fd31e43011c4b95aa49955421d3"
+ dependencies:
+ caller-path "^0.1.0"
+ resolve-from "^1.0.0"
+
+resolve-from@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-1.0.1.tgz#26cbfe935d1aeeeabb29bc3fe5aeb01e93d44226"
+
+resolve@^1.2.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.4.0.tgz#a75be01c53da25d934a98ebd0e4c4a7312f92a86"
+ dependencies:
+ path-parse "^1.0.5"
+
+restore-cursor@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf"
+ dependencies:
+ onetime "^2.0.0"
+ signal-exit "^3.0.2"
+
+right-align@^0.1.1:
+ version "0.1.3"
+ resolved "https://registry.yarnpkg.com/right-align/-/right-align-0.1.3.tgz#61339b722fe6a3515689210d24e14c96148613ef"
+ dependencies:
+ align-text "^0.1.1"
+
+rimraf@2, rimraf@^2.2.8, rimraf@^2.5.1, rimraf@^2.6.1:
+ version "2.6.1"
+ resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.1.tgz#c2338ec643df7a1b7fe5c54fa86f57428a55f33d"
+ dependencies:
+ glob "^7.0.5"
+
+ripemd160@^2.0.0, ripemd160@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.1.tgz#0f4584295c53a3628af7e6d79aca21ce57d1c6e7"
+ dependencies:
+ hash-base "^2.0.0"
+ inherits "^2.0.1"
+
+run-async@^2.2.0:
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0"
+ dependencies:
+ is-promise "^2.1.0"
+
+rx-lite-aggregates@^4.0.8:
+ version "4.0.8"
+ resolved "https://registry.yarnpkg.com/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz#753b87a89a11c95467c4ac1626c4efc4e05c67be"
+ dependencies:
+ rx-lite "*"
+
+rx-lite@*, rx-lite@^4.0.8:
+ version "4.0.8"
+ resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-4.0.8.tgz#0b1e11af8bc44836f04a6407e92da42467b79444"
+
+safe-buffer@^5.0.1, safe-buffer@^5.1.0, 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"
+
+"semver@2 || 3 || 4 || 5", semver@^5.3.0:
+ version "5.4.1"
+ resolved "https://registry.yarnpkg.com/semver/-/semver-5.4.1.tgz#e059c09d8571f0540823733433505d3a2f00b18e"
+
+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"
+
+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"
+
+setimmediate@^1.0.4:
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285"
+
+sha.js@^2.4.0, sha.js@^2.4.8:
+ version "2.4.8"
+ resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.8.tgz#37068c2c476b6baf402d14a49c67f597921f634f"
+ dependencies:
+ inherits "^2.0.1"
+
+shebang-command@^1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea"
+ dependencies:
+ shebang-regex "^1.0.0"
+
+shebang-regex@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3"
+
+signal-exit@^3.0.0, signal-exit@^3.0.2:
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d"
+
+slash@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55"
+
+slice-ansi@0.0.4:
+ version "0.0.4"
+ resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35"
+
+sntp@1.x.x:
+ version "1.0.9"
+ resolved "https://registry.yarnpkg.com/sntp/-/sntp-1.0.9.tgz#6541184cc90aeea6c6e7b35e2659082443c66198"
+ dependencies:
+ hoek "2.x.x"
+
+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"
+
+source-map-support@^0.4.2:
+ version "0.4.15"
+ resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.15.tgz#03202df65c06d2bd8c7ec2362a193056fef8d3b1"
+ dependencies:
+ source-map "^0.5.6"
+
+source-map@^0.5.0, source-map@^0.5.3, source-map@^0.5.6, source-map@~0.5.1, source-map@~0.5.3:
+ version "0.5.6"
+ resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412"
+
+spark-md5@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/spark-md5/-/spark-md5-3.0.0.tgz#3722227c54e2faf24b1dc6d933cc144e6f71bfef"
+
+spdx-correct@~1.0.0:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-1.0.2.tgz#4b3073d933ff51f3912f03ac5519498a4150db40"
+ dependencies:
+ spdx-license-ids "^1.0.2"
+
+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"
+
+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"
+
+sprintf-js@~1.0.2:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
+
+sshpk@^1.7.0:
+ version "1.13.1"
+ resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.13.1.tgz#512df6da6287144316dc4c18fe1cf1d940739be3"
+ dependencies:
+ asn1 "~0.2.3"
+ assert-plus "^1.0.0"
+ dashdash "^1.12.0"
+ getpass "^0.1.1"
+ optionalDependencies:
+ bcrypt-pbkdf "^1.0.0"
+ ecc-jsbn "~0.1.1"
+ jsbn "~0.1.0"
+ tweetnacl "~0.14.0"
+
+stream-browserify@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.1.tgz#66266ee5f9bdb9940a4e4514cafb43bb71e5c9db"
+ dependencies:
+ inherits "~2.0.1"
+ readable-stream "^2.0.2"
+
+stream-http@^2.3.1:
+ version "2.7.2"
+ resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.7.2.tgz#40a050ec8dc3b53b33d9909415c02c0bf1abfbad"
+ dependencies:
+ builtin-status-codes "^3.0.0"
+ inherits "^2.0.1"
+ readable-stream "^2.2.6"
+ to-arraybuffer "^1.0.0"
+ xtend "^4.0.0"
+
+string-width@^1.0.1, string-width@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3"
+ dependencies:
+ code-point-at "^1.0.0"
+ is-fullwidth-code-point "^1.0.0"
+ strip-ansi "^3.0.0"
+
+string-width@^2.0.0, string-width@^2.1.0:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e"
+ dependencies:
+ is-fullwidth-code-point "^2.0.0"
+ strip-ansi "^4.0.0"
+
+string_decoder@^0.10.25:
+ version "0.10.31"
+ resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94"
+
+string_decoder@~1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.0.3.tgz#0fc67d7c141825de94282dd536bec6b9bce860ab"
+ 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"
+
+strip-ansi@^3.0.0, strip-ansi@^3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf"
+ dependencies:
+ ansi-regex "^2.0.0"
+
+strip-ansi@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f"
+ dependencies:
+ ansi-regex "^3.0.0"
+
+strip-bom@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3"
+
+strip-eof@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf"
+
+strip-json-comments@~2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
+
+supports-color@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7"
+
+supports-color@^4.0.0, supports-color@^4.2.1:
+ version "4.2.1"
+ resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-4.2.1.tgz#65a4bb2631e90e02420dba5554c375a4754bb836"
+ dependencies:
+ has-flag "^2.0.0"
+
+table@^4.0.1:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/table/-/table-4.0.1.tgz#a8116c133fac2c61f4a420ab6cdf5c4d61f0e435"
+ dependencies:
+ ajv "^4.7.0"
+ ajv-keywords "^1.0.0"
+ chalk "^1.1.1"
+ lodash "^4.0.0"
+ slice-ansi "0.0.4"
+ string-width "^2.0.0"
+
+tapable@^0.2.7:
+ version "0.2.7"
+ resolved "https://registry.yarnpkg.com/tapable/-/tapable-0.2.7.tgz#e46c0daacbb2b8a98b9b0cea0f4052105817ed5c"
+
+tar-pack@^3.4.0:
+ version "3.4.0"
+ resolved "https://registry.yarnpkg.com/tar-pack/-/tar-pack-3.4.0.tgz#23be2d7f671a8339376cbdb0b8fe3fdebf317984"
+ 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.2.1:
+ version "2.2.1"
+ resolved "https://registry.yarnpkg.com/tar/-/tar-2.2.1.tgz#8e4d2a256c0e2185c6b18ad694aec968b83cb1d1"
+ dependencies:
+ block-stream "*"
+ fstream "^1.0.2"
+ inherits "2"
+
+text-table@~0.2.0:
+ version "0.2.0"
+ resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
+
+through@^2.3.6:
+ version "2.3.8"
+ resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
+
+timers-browserify@^2.0.2:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.2.tgz#ab4883cf597dcd50af211349a00fbca56ac86b86"
+ dependencies:
+ setimmediate "^1.0.4"
+
+tmp@^0.0.31:
+ version "0.0.31"
+ resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.31.tgz#8f38ab9438e17315e5dbd8b3657e8bfb277ae4a7"
+ dependencies:
+ os-tmpdir "~1.0.1"
+
+to-arraybuffer@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43"
+
+to-fast-properties@^1.0.1:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47"
+
+tough-cookie@~2.3.0:
+ version "2.3.2"
+ resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.2.tgz#f081f76e4c85720e6c37a5faced737150d84072a"
+ dependencies:
+ punycode "^1.4.1"
+
+trim-right@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003"
+
+tryit@^1.0.1:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/tryit/-/tryit-1.0.3.tgz#393be730a9446fd1ead6da59a014308f36c289cb"
+
+tty-browserify@0.0.0:
+ version "0.0.0"
+ resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6"
+
+tunnel-agent@^0.6.0:
+ version "0.6.0"
+ resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd"
+ dependencies:
+ safe-buffer "^5.0.1"
+
+tweetnacl@^0.14.3, tweetnacl@~0.14.0:
+ version "0.14.5"
+ resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64"
+
+type-check@~0.3.2:
+ version "0.3.2"
+ resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72"
+ dependencies:
+ prelude-ls "~1.1.2"
+
+typedarray@^0.0.6:
+ version "0.0.6"
+ resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
+
+uglify-js@^2.8.29:
+ version "2.8.29"
+ resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.8.29.tgz#29c5733148057bb4e1f75df35b7a9cb72e6a59dd"
+ dependencies:
+ source-map "~0.5.1"
+ yargs "~3.10.0"
+ optionalDependencies:
+ uglify-to-browserify "~1.0.0"
+
+uglify-to-browserify@~1.0.0:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz#6e0924d6bda6b5afe349e39a6d632850a0f882b7"
+
+uglifyjs-webpack-plugin@^0.4.6:
+ version "0.4.6"
+ resolved "https://registry.yarnpkg.com/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-0.4.6.tgz#b951f4abb6bd617e66f63eb891498e391763e309"
+ dependencies:
+ source-map "^0.5.6"
+ uglify-js "^2.8.29"
+ webpack-sources "^1.0.1"
+
+uid-number@^0.0.6:
+ version "0.0.6"
+ resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81"
+
+url@^0.11.0:
+ version "0.11.0"
+ resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1"
+ dependencies:
+ punycode "1.3.2"
+ querystring "0.2.0"
+
+util-deprecate@~1.0.1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
+
+util@0.10.3, util@^0.10.3:
+ version "0.10.3"
+ resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9"
+ dependencies:
+ inherits "2.0.1"
+
+uuid@^3.0.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.1.0.tgz#3dd3d3e790abc24d7b0d3a034ffababe28ebbc04"
+
+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"
+ dependencies:
+ spdx-correct "~1.0.0"
+ spdx-expression-parse "~1.0.0"
+
+verror@1.3.6:
+ version "1.3.6"
+ resolved "https://registry.yarnpkg.com/verror/-/verror-1.3.6.tgz#cff5df12946d297d2baaefaa2689e25be01c005c"
+ dependencies:
+ extsprintf "1.0.2"
+
+vm-browserify@0.0.4:
+ version "0.0.4"
+ resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-0.0.4.tgz#5d7ea45bbef9e4a6ff65f95438e0a87c357d5a73"
+ dependencies:
+ indexof "0.0.1"
+
+watchpack@^1.4.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.4.0.tgz#4a1472bcbb952bd0a9bb4036801f954dfb39faac"
+ dependencies:
+ async "^2.1.2"
+ chokidar "^1.7.0"
+ graceful-fs "^4.1.2"
+
+webpack-sources@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.0.1.tgz#c7356436a4d13123be2e2426a05d1dad9cbe65cf"
+ dependencies:
+ source-list-map "^2.0.0"
+ source-map "~0.5.3"
+
+webpack@^3.4.0:
+ version "3.4.0"
+ resolved "https://registry.yarnpkg.com/webpack/-/webpack-3.4.0.tgz#e9465b660ad79dd2d33874d968b31746ea9a8e63"
+ dependencies:
+ acorn "^5.0.0"
+ acorn-dynamic-import "^2.0.0"
+ ajv "^5.1.5"
+ ajv-keywords "^2.0.0"
+ async "^2.1.2"
+ enhanced-resolve "^3.4.0"
+ escope "^3.6.0"
+ interpret "^1.0.0"
+ json-loader "^0.5.4"
+ json5 "^0.5.1"
+ loader-runner "^2.3.0"
+ loader-utils "^1.1.0"
+ memory-fs "~0.4.1"
+ mkdirp "~0.5.0"
+ node-libs-browser "^2.0.0"
+ source-map "^0.5.3"
+ supports-color "^4.2.1"
+ tapable "^0.2.7"
+ uglifyjs-webpack-plugin "^0.4.6"
+ watchpack "^1.4.0"
+ webpack-sources "^1.0.1"
+ yargs "^8.0.2"
+
+which-module@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a"
+
+which@^1.2.9:
+ version "1.2.14"
+ resolved "https://registry.yarnpkg.com/which/-/which-1.2.14.tgz#9a87c4378f03e827cecaf1acdf56c736c01c14e5"
+ dependencies:
+ 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"
+ dependencies:
+ string-width "^1.0.2"
+
+window-size@0.1.0:
+ version "0.1.0"
+ resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d"
+
+wordwrap@0.0.2:
+ version "0.0.2"
+ resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f"
+
+wordwrap@~1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb"
+
+wrap-ansi@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85"
+ dependencies:
+ string-width "^1.0.1"
+ strip-ansi "^3.0.1"
+
+wrappy@1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
+
+write@^0.2.1:
+ version "0.2.1"
+ resolved "https://registry.yarnpkg.com/write/-/write-0.2.1.tgz#5fc03828e264cea3fe91455476f7a3c566cb0757"
+ dependencies:
+ mkdirp "^0.5.1"
+
+xtend@^4.0.0:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af"
+
+y18n@^3.2.1:
+ version "3.2.1"
+ resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41"
+
+yallist@^2.1.2:
+ version "2.1.2"
+ resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52"
+
+yargs-parser@^7.0.0:
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-7.0.0.tgz#8d0ac42f16ea55debd332caf4c4038b3e3f5dfd9"
+ dependencies:
+ camelcase "^4.1.0"
+
+yargs@^8.0.2:
+ version "8.0.2"
+ resolved "https://registry.yarnpkg.com/yargs/-/yargs-8.0.2.tgz#6299a9055b1cefc969ff7e79c1d918dceb22c360"
+ dependencies:
+ camelcase "^4.1.0"
+ cliui "^3.2.0"
+ decamelize "^1.1.1"
+ get-caller-file "^1.0.1"
+ os-locale "^2.0.0"
+ read-pkg-up "^2.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"
+ yargs-parser "^7.0.0"
+
+yargs@~3.10.0:
+ version "3.10.0"
+ resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.10.0.tgz#f7ee7bd857dd7c1d2d38c0e74efbd681d1431fd1"
+ dependencies:
+ camelcase "^1.0.2"
+ cliui "^2.1.0"
+ decamelize "^1.0.0"
+ window-size "0.1.0"
diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md
index 6908882123..ac1043df78 100644
--- a/activesupport/CHANGELOG.md
+++ b/activesupport/CHANGELOG.md
@@ -1,7 +1,12 @@
+* `Module#delegate_missing_to` now raises `DelegationError` if target is nil,
+ similar to `Module#delegate`.
+
+ *Anton Khamets*
+
* Update `String#camelize` to provide feedback when wrong option is passed
`String#camelize` was returning nil without any feedback when an
- invalid option was passed as parameter.
+ invalid option was passed as a parameter.
Previously:
@@ -17,9 +22,9 @@
* Fix modulo operations involving durations
- Rails 5.1 introduce an `ActiveSupport::Duration::Scalar` class as a wrapper
- around a numeric value as a way of ensuring a duration was the outcome of
- an expression. However the implementation was missing support for modulo
+ Rails 5.1 introduced `ActiveSupport::Duration::Scalar` as a wrapper
+ around numeric values as a way of ensuring a duration was the outcome of
+ an expression. However, the implementation was missing support for modulo
operations. This support has now been added and should result in a duration
being returned from expressions involving modulo operations.
@@ -135,15 +140,15 @@
* Fix implicit coercion calculations with scalars and durations
- Previously calculations where the scalar is first would be converted to a duration
- of seconds but this causes issues with dates being converted to times, e.g:
+ Previously, calculations where the scalar is first would be converted to a duration
+ of seconds, but this causes issues with dates being converted to times, e.g:
Time.zone = "Beijing" # => Asia/Shanghai
date = Date.civil(2017, 5, 20) # => Mon, 20 May 2017
2 * 1.day # => 172800 seconds
date + 2 * 1.day # => Mon, 22 May 2017 00:00:00 CST +08:00
- Now the `ActiveSupport::Duration::Scalar` calculation methods will try to maintain
+ Now, the `ActiveSupport::Duration::Scalar` calculation methods will try to maintain
the part structure of the duration where possible, e.g:
Time.zone = "Beijing" # => Asia/Shanghai
diff --git a/activesupport/lib/active_support/core_ext/module/delegation.rb b/activesupport/lib/active_support/core_ext/module/delegation.rb
index 6565d53a06..1840dc942f 100644
--- a/activesupport/lib/active_support/core_ext/module/delegation.rb
+++ b/activesupport/lib/active_support/core_ext/module/delegation.rb
@@ -274,7 +274,15 @@ class Module
if #{target}.respond_to?(method)
#{target}.public_send(method, *args, &block)
else
- super
+ begin
+ super
+ rescue NoMethodError
+ if #{target}.nil?
+ raise DelegationError, "\#{method} delegated to #{target}, but #{target} is nil"
+ else
+ raise
+ end
+ end
end
end
RUBY
diff --git a/activesupport/lib/active_support/current_attributes.rb b/activesupport/lib/active_support/current_attributes.rb
index 9ab1546064..4e6d8e4585 100644
--- a/activesupport/lib/active_support/current_attributes.rb
+++ b/activesupport/lib/active_support/current_attributes.rb
@@ -33,7 +33,7 @@ module ActiveSupport
#
# private
# def authenticate
- # if authenticated_user = User.find_by(id: cookies.signed[:user_id])
+ # if authenticated_user = User.find_by(id: cookies.encrypted[:user_id])
# Current.user = authenticated_user
# else
# redirect_to new_session_url
diff --git a/activesupport/lib/active_support/hash_with_indifferent_access.rb b/activesupport/lib/active_support/hash_with_indifferent_access.rb
index 44e95f58a1..12291af443 100644
--- a/activesupport/lib/active_support/hash_with_indifferent_access.rb
+++ b/activesupport/lib/active_support/hash_with_indifferent_access.rb
@@ -236,7 +236,7 @@ module ActiveSupport
# dup = hash.dup
# dup[:a][:c] = 'c'
#
- # hash[:a][:c] # => nil
+ # hash[:a][:c] # => "c"
# dup[:a][:c] # => "c"
def dup
self.class.new(self).tap do |new_hash|
diff --git a/activesupport/lib/active_support/lazy_load_hooks.rb b/activesupport/lib/active_support/lazy_load_hooks.rb
index c23b319046..dc8080c469 100644
--- a/activesupport/lib/active_support/lazy_load_hooks.rb
+++ b/activesupport/lib/active_support/lazy_load_hooks.rb
@@ -27,33 +27,51 @@ module ActiveSupport
base.class_eval do
@load_hooks = Hash.new { |h, k| h[k] = [] }
@loaded = Hash.new { |h, k| h[k] = [] }
+ @run_once = Hash.new { |h, k| h[k] = [] }
end
end
# Declares a block that will be executed when a Rails component is fully
# loaded.
+ #
+ # Options:
+ #
+ # * <tt>:yield</tt> - Yields the object that run_load_hooks to +block+.
+ # * <tt>:run_once</tt> - Given +block+ will run only once.
def on_load(name, options = {}, &block)
@loaded[name].each do |base|
- execute_hook(base, options, block)
+ execute_hook(name, base, options, block)
end
@load_hooks[name] << [block, options]
end
- def execute_hook(base, options, block)
- if options[:yield]
- block.call(base)
- else
- base.instance_eval(&block)
- end
- end
-
def run_load_hooks(name, base = Object)
@loaded[name] << base
@load_hooks[name].each do |hook, options|
- execute_hook(base, options, hook)
+ execute_hook(name, base, options, hook)
end
end
+
+ private
+
+ def with_execution_control(name, block, once)
+ unless @run_once[name].include?(block)
+ @run_once[name] << block if once
+
+ yield
+ end
+ end
+
+ def execute_hook(name, base, options, block)
+ with_execution_control(name, block, options[:run_once]) do
+ if options[:yield]
+ block.call(base)
+ else
+ base.instance_eval(&block)
+ end
+ end
+ end
end
extend LazyLoadHooks
diff --git a/activesupport/lib/active_support/message_encryptor.rb b/activesupport/lib/active_support/message_encryptor.rb
index 090d51933a..952306b482 100644
--- a/activesupport/lib/active_support/message_encryptor.rb
+++ b/activesupport/lib/active_support/message_encryptor.rb
@@ -121,14 +121,13 @@ module ActiveSupport
# Encrypt and sign a message. We need to sign the message in order to avoid
# padding attacks. Reference: http://www.limited-entropy.com/padding-oracle-attacks.
def encrypt_and_sign(value, expires_at: nil, expires_in: nil, purpose: nil)
- data = Messages::Metadata.wrap(value, expires_at: expires_at, expires_in: expires_in, purpose: purpose)
- verifier.generate(_encrypt(data))
+ verifier.generate(_encrypt(value, expires_at: expires_at, expires_in: expires_in, purpose: purpose))
end
# Decrypt and verify a message. We need to verify the message in order to
# avoid padding attacks. Reference: http://www.limited-entropy.com/padding-oracle-attacks.
def decrypt_and_verify(data, purpose: nil)
- Messages::Metadata.verify(_decrypt(verifier.verify(data)), purpose)
+ _decrypt(verifier.verify(data), purpose)
end
# Given a cipher, returns the key length of the cipher to help generate the key of desired size
@@ -137,7 +136,7 @@ module ActiveSupport
end
private
- def _encrypt(value)
+ def _encrypt(value, **metadata_options)
cipher = new_cipher
cipher.encrypt
cipher.key = @secret
@@ -146,7 +145,7 @@ module ActiveSupport
iv = cipher.random_iv
cipher.auth_data = "" if aead_mode?
- encrypted_data = cipher.update(@serializer.dump(value))
+ encrypted_data = cipher.update(Messages::Metadata.wrap(@serializer.dump(value), metadata_options))
encrypted_data << cipher.final
blob = "#{::Base64.strict_encode64 encrypted_data}--#{::Base64.strict_encode64 iv}"
@@ -154,7 +153,7 @@ module ActiveSupport
blob
end
- def _decrypt(encrypted_message)
+ def _decrypt(encrypted_message, purpose)
cipher = new_cipher
encrypted_data, iv, auth_tag = encrypted_message.split("--".freeze).map { |v| ::Base64.strict_decode64(v) }
@@ -174,7 +173,8 @@ module ActiveSupport
decrypted_data = cipher.update(encrypted_data)
decrypted_data << cipher.final
- @serializer.load(decrypted_data)
+ message = Messages::Metadata.verify(decrypted_data, purpose)
+ @serializer.load(message) if message
rescue OpenSSLCipherError, TypeError, ArgumentError
raise InvalidMessage
end
diff --git a/activesupport/lib/active_support/message_verifier.rb b/activesupport/lib/active_support/message_verifier.rb
index fdd2185f7f..7110d6d2c9 100644
--- a/activesupport/lib/active_support/message_verifier.rb
+++ b/activesupport/lib/active_support/message_verifier.rb
@@ -124,7 +124,8 @@ module ActiveSupport
if valid_message?(signed_message)
begin
data = signed_message.split("--".freeze)[0]
- Messages::Metadata.verify(@serializer.load(decode(data)), purpose)
+ message = Messages::Metadata.verify(decode(data), purpose)
+ @serializer.load(message) if message
rescue ArgumentError => argument_error
return if argument_error.message.include?("invalid base64")
raise
@@ -156,7 +157,7 @@ module ActiveSupport
# verifier = ActiveSupport::MessageVerifier.new 's3Krit'
# verifier.generate 'a private message' # => "BAhJIhRwcml2YXRlLW1lc3NhZ2UGOgZFVA==--e2d724331ebdee96a10fb99b089508d1c72bd772"
def generate(value, expires_at: nil, expires_in: nil, purpose: nil)
- data = encode(@serializer.dump(Messages::Metadata.wrap(value, expires_at: expires_at, expires_in: expires_in, purpose: purpose)))
+ data = encode(Messages::Metadata.wrap(@serializer.dump(value), expires_at: expires_at, expires_in: expires_in, purpose: purpose))
"#{data}--#{generate_digest(data)}"
end
diff --git a/activesupport/lib/active_support/messages/metadata.rb b/activesupport/lib/active_support/messages/metadata.rb
index a45aecfcd0..e97caac766 100644
--- a/activesupport/lib/active_support/messages/metadata.rb
+++ b/activesupport/lib/active_support/messages/metadata.rb
@@ -5,27 +5,25 @@ require "time"
module ActiveSupport
module Messages #:nodoc:
class Metadata #:nodoc:
- def initialize(expires_at, purpose)
- @expires_at, @purpose = expires_at, purpose.to_s
+ def initialize(message, expires_at = nil, purpose = nil)
+ @message, @expires_at, @purpose = message, expires_at, purpose
+ end
+
+ def as_json(options = {})
+ { _rails: { message: @message, exp: @expires_at, pur: @purpose } }
end
class << self
def wrap(message, expires_at: nil, expires_in: nil, purpose: nil)
if expires_at || expires_in || purpose
- { "value" => message, "_rails" => { "exp" => pick_expiry(expires_at, expires_in), "pur" => purpose } }
+ JSON.encode new(encode(message), pick_expiry(expires_at, expires_in), purpose)
else
message
end
end
def verify(message, purpose)
- metadata = extract_metadata(message)
-
- if metadata.nil?
- message if purpose.nil?
- elsif metadata.match?(purpose) && metadata.fresh?
- message["value"]
- end
+ extract_metadata(message).verify(purpose)
end
private
@@ -38,19 +36,36 @@ module ActiveSupport
end
def extract_metadata(message)
- if message.is_a?(Hash) && message.key?("_rails")
- new(message["_rails"]["exp"], message["_rails"]["pur"])
+ data = JSON.decode(message) rescue nil
+
+ if data.is_a?(Hash) && data.key?("_rails")
+ new(decode(data["_rails"]["message"]), data["_rails"]["exp"], data["_rails"]["pur"])
+ else
+ new(message)
end
end
- end
- def match?(purpose)
- @purpose == purpose.to_s
+ def encode(message)
+ ::Base64.strict_encode64(message)
+ end
+
+ def decode(message)
+ ::Base64.strict_decode64(message)
+ end
end
- def fresh?
- @expires_at.nil? || Time.now.utc < Time.iso8601(@expires_at)
+ def verify(purpose)
+ @message if match?(purpose) && fresh?
end
+
+ private
+ def match?(purpose)
+ @purpose.to_s == purpose.to_s
+ end
+
+ def fresh?
+ @expires_at.nil? || Time.now.utc < Time.iso8601(@expires_at)
+ end
end
end
end
diff --git a/activesupport/test/core_ext/module_test.rb b/activesupport/test/core_ext/module_test.rb
index 69c8a312d8..e918823074 100644
--- a/activesupport/test/core_ext/module_test.rb
+++ b/activesupport/test/core_ext/module_test.rb
@@ -374,6 +374,14 @@ class ModuleTest < ActiveSupport::TestCase
assert_match(/undefined method `my_fake_method' for/, e.message)
end
+ def test_delegate_missing_to_raises_delegation_error_if_target_nil
+ e = assert_raises(Module::DelegationError) do
+ DecoratedTester.new(nil).name
+ end
+
+ assert_equal "name delegated to client, but client is nil", e.message
+ end
+
def test_delegate_missing_to_affects_respond_to
assert DecoratedTester.new(@david).respond_to?(:name)
assert_not DecoratedTester.new(@david).respond_to?(:private_name)
diff --git a/activesupport/test/json/encoding_test.rb b/activesupport/test/json/encoding_test.rb
index 80d03ec9d6..eafa2e1712 100644
--- a/activesupport/test/json/encoding_test.rb
+++ b/activesupport/test/json/encoding_test.rb
@@ -100,11 +100,11 @@ class TestJSONEncoding < ActiveSupport::TestCase
end
def test_hash_should_allow_key_filtering_with_only
- assert_equal %({"a":1}), ActiveSupport::JSON.encode({ "a" => 1, :b => 2, :c => 3 }, only: "a")
+ assert_equal %({"a":1}), ActiveSupport::JSON.encode({ "a" => 1, :b => 2, :c => 3 }, { only: "a" })
end
def test_hash_should_allow_key_filtering_with_except
- assert_equal %({"b":2}), ActiveSupport::JSON.encode({ "foo" => "bar", :b => 2, :c => 3 }, except: ["foo", :c])
+ assert_equal %({"b":2}), ActiveSupport::JSON.encode({ "foo" => "bar", :b => 2, :c => 3 }, { except: ["foo", :c] })
end
def test_time_to_json_includes_local_offset
diff --git a/activesupport/test/lazy_load_hooks_test.rb b/activesupport/test/lazy_load_hooks_test.rb
index 9c9264e8fc..721d44d0c1 100644
--- a/activesupport/test/lazy_load_hooks_test.rb
+++ b/activesupport/test/lazy_load_hooks_test.rb
@@ -20,6 +20,23 @@ class LazyLoadHooksTest < ActiveSupport::TestCase
assert_equal 7, i
end
+ def test_basic_hook_with_two_registrations_only_once
+ i = 0
+ block = proc { i += incr }
+ ActiveSupport.on_load(:basic_hook_with_two_once, run_once: true, &block)
+ ActiveSupport.on_load(:basic_hook_with_two_once) do
+ i += incr
+ end
+
+ ActiveSupport.on_load(:different_hook, run_once: true, &block)
+ ActiveSupport.run_load_hooks(:different_hook, FakeContext.new(2))
+ assert_equal 2, i
+ ActiveSupport.run_load_hooks(:basic_hook_with_two_once, FakeContext.new(2))
+ assert_equal 6, i
+ ActiveSupport.run_load_hooks(:basic_hook_with_two_once, FakeContext.new(5))
+ assert_equal 11, i
+ end
+
def test_hook_registered_after_run
i = 0
ActiveSupport.run_load_hooks(:registered_after)
@@ -37,6 +54,15 @@ class LazyLoadHooksTest < ActiveSupport::TestCase
assert_equal 7, i
end
+ def test_hook_registered_after_run_with_two_registrations_only_once
+ i = 0
+ ActiveSupport.run_load_hooks(:registered_after_with_two_once, FakeContext.new(2))
+ ActiveSupport.run_load_hooks(:registered_after_with_two_once, FakeContext.new(5))
+ assert_equal 0, i
+ ActiveSupport.on_load(:registered_after_with_two_once, run_once: true) { i += incr }
+ assert_equal 2, i
+ end
+
def test_hook_registered_interleaved_run_with_two_registrations
i = 0
ActiveSupport.run_load_hooks(:registered_interleaved_with_two, FakeContext.new(2))
@@ -47,6 +73,22 @@ class LazyLoadHooksTest < ActiveSupport::TestCase
assert_equal 7, i
end
+ def test_hook_registered_interleaved_run_with_two_registrations_once
+ i = 0
+ ActiveSupport
+ .run_load_hooks(:registered_interleaved_with_two_once, FakeContext.new(2))
+ assert_equal 0, i
+
+ ActiveSupport.on_load(:registered_interleaved_with_two_once, run_once: true) do
+ i += incr
+ end
+ assert_equal 2, i
+
+ ActiveSupport
+ .run_load_hooks(:registered_interleaved_with_two_once, FakeContext.new(5))
+ assert_equal 2, i
+ end
+
def test_hook_receives_a_context
i = 0
ActiveSupport.on_load(:contextual) { i += incr }
diff --git a/activesupport/test/message_verifier_test.rb b/activesupport/test/message_verifier_test.rb
index f626ab745f..fbeafca203 100644
--- a/activesupport/test/message_verifier_test.rb
+++ b/activesupport/test/message_verifier_test.rb
@@ -101,12 +101,12 @@ class MessageVerifierMetadataTest < ActiveSupport::TestCase
def test_verify_raises_when_purpose_differs
assert_raise(ActiveSupport::MessageVerifier::InvalidSignature) do
- @verifier.verify(@verifier.generate(@message, purpose: "payment"), purpose: "shipping")
+ @verifier.verify(generate(data, purpose: "payment"), purpose: "shipping")
end
end
def test_verify_raises_when_expired
- signed_message = @verifier.generate(@message, expires_in: 1.month)
+ signed_message = generate(data, expires_in: 1.month)
travel 2.months
assert_raise(ActiveSupport::MessageVerifier::InvalidSignature) do
@@ -141,3 +141,18 @@ class MessageVerifierMetadataJSONTest < MessageVerifierMetadataTest
{ serializer: MessageVerifierTest::JSONSerializer.new }
end
end
+
+class MessageEncryptorMetadataNullSerializerTest < MessageVerifierMetadataTest
+ private
+ def data
+ "string message"
+ end
+
+ def null_serializing?
+ true
+ end
+
+ def verifier_options
+ { serializer: ActiveSupport::MessageEncryptor::NullSerializer }
+ end
+end
diff --git a/activesupport/test/metadata/shared_metadata_tests.rb b/activesupport/test/metadata/shared_metadata_tests.rb
index 7d88e255c7..08bb0c648e 100644
--- a/activesupport/test/metadata/shared_metadata_tests.rb
+++ b/activesupport/test/metadata/shared_metadata_tests.rb
@@ -1,57 +1,57 @@
# frozen_string_literal: true
module SharedMessageMetadataTests
- def setup
- @message = { "credit_card_no" => "5012-6784-9087-5678", "card_holder" => { "name" => "Donald" } }
-
- super
- end
-
def teardown
travel_back
-
super
end
+ def null_serializing?
+ false
+ end
+
def test_encryption_and_decryption_with_same_purpose
- assert_equal @message, parse(generate(@message, purpose: "checkout"), purpose: "checkout")
- assert_equal @message, parse(generate(@message))
+ assert_equal data, parse(generate(data, purpose: "checkout"), purpose: "checkout")
+ assert_equal data, parse(generate(data))
string_message = "address: #23, main street"
assert_equal string_message, parse(generate(string_message, purpose: "shipping"), purpose: "shipping")
+ end
- array_message = ["credit_card_no: 5012-6748-9087-5678", { "card_holder" => "Donald", "issued_on" => Time.local(2017) }, 12345]
- assert_equal array_message, parse(generate(array_message, purpose: "registration"), purpose: "registration")
+ def test_verifies_array_when_purpose_matches
+ unless null_serializing?
+ data = [ "credit_card_no: 5012-6748-9087-5678", { "card_holder" => "Donald", "issued_on" => Time.local(2017) }, 12345 ]
+ assert_equal data, parse(generate(data, purpose: :registration), purpose: :registration)
+ end
end
def test_encryption_and_decryption_with_different_purposes_returns_nil
- assert_nil parse(generate(@message, purpose: "payment"), purpose: "sign up")
- assert_nil parse(generate(@message, purpose: "payment"))
- assert_nil parse(generate(@message), purpose: "sign up")
- assert_nil parse(generate(@message), purpose: "")
+ assert_nil parse(generate(data, purpose: "payment"), purpose: "sign up")
+ assert_nil parse(generate(data, purpose: "payment"))
+ assert_nil parse(generate(data), purpose: "sign up")
end
def test_purpose_using_symbols
- assert_equal @message, parse(generate(@message, purpose: :checkout), purpose: :checkout)
- assert_equal @message, parse(generate(@message, purpose: :checkout), purpose: "checkout")
- assert_equal @message, parse(generate(@message, purpose: "checkout"), purpose: :checkout)
+ assert_equal data, parse(generate(data, purpose: :checkout), purpose: :checkout)
+ assert_equal data, parse(generate(data, purpose: :checkout), purpose: "checkout")
+ assert_equal data, parse(generate(data, purpose: "checkout"), purpose: :checkout)
end
def test_passing_expires_at_sets_expiration_date
- encrypted_message = generate(@message, expires_at: 1.hour.from_now)
+ encrypted_message = generate(data, expires_at: 1.hour.from_now)
travel 59.minutes
- assert_equal @message, parse(encrypted_message)
+ assert_equal data, parse(encrypted_message)
travel 2.minutes
assert_nil parse(encrypted_message)
end
def test_set_relative_expiration_date_by_passing_expires_in
- encrypted_message = generate(@message, expires_in: 2.hours)
+ encrypted_message = generate(data, expires_in: 2.hours)
travel 1.hour
- assert_equal @message, parse(encrypted_message)
+ assert_equal data, parse(encrypted_message)
travel 1.hour + 1.second
assert_nil parse(encrypted_message)
@@ -59,10 +59,10 @@ module SharedMessageMetadataTests
def test_passing_expires_in_less_than_a_second_is_not_expired
freeze_time do
- encrypted_message = generate(@message, expires_in: 1.second)
+ encrypted_message = generate(data, expires_in: 1.second)
travel 0.5.seconds
- assert_equal @message, parse(encrypted_message)
+ assert_equal data, parse(encrypted_message)
travel 1.second
assert_nil parse(encrypted_message)
@@ -70,19 +70,24 @@ module SharedMessageMetadataTests
end
def test_favor_expires_at_over_expires_in
- payment_related_message = generate(@message, purpose: "payment", expires_at: 2.year.from_now, expires_in: 1.second)
+ payment_related_message = generate(data, purpose: "payment", expires_at: 2.year.from_now, expires_in: 1.second)
travel 1.year
- assert_equal @message, parse(payment_related_message, purpose: :payment)
+ assert_equal data, parse(payment_related_message, purpose: :payment)
travel 1.year + 1.day
assert_nil parse(payment_related_message, purpose: "payment")
end
def test_skip_expires_at_and_expires_in_to_disable_expiration_check
- payment_related_message = generate(@message, purpose: "payment")
+ payment_related_message = generate(data, purpose: "payment")
travel 100.years
- assert_equal @message, parse(payment_related_message, purpose: "payment")
+ assert_equal data, parse(payment_related_message, purpose: "payment")
end
+
+ private
+ def data
+ { "credit_card_no" => "5012-6784-9087-5678", "card_holder" => { "name" => "Donald" } }
+ end
end
diff --git a/ci/travis.rb b/ci/travis.rb
index 8339e0d08b..b124358789 100755
--- a/ci/travis.rb
+++ b/ci/travis.rb
@@ -1,4 +1,6 @@
#!/usr/bin/env ruby
+# frozen_string_literal: true
+
require "fileutils"
include FileUtils
@@ -28,6 +30,7 @@ class Build
"av" => "actionview",
"aj" => "activejob",
"ac" => "actioncable",
+ "ast" => "activestorage",
"guides" => "guides"
}
@@ -163,6 +166,7 @@ ENV["GEM"].split(",").each do |gem|
next if gem == "aj:integration" && isolated
next if gem == "guides" && isolated
next if gem == "av:ujs" && isolated
+ next if gem == "ast" && isolated
build = Build.new(gem, isolated: isolated)
results[build.key] = build.run!
diff --git a/guides/Rakefile b/guides/Rakefile
index 3a6f10040f..84e18e0972 100644
--- a/guides/Rakefile
+++ b/guides/Rakefile
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
namespace :guides do
desc 'Generate guides (for authors), use ONLY=foo to process just "foo.md"'
task generate: "generate:html"
diff --git a/guides/bug_report_templates/action_controller_gem.rb b/guides/bug_report_templates/action_controller_gem.rb
index 8b7aa893fd..4d8d8db3e5 100644
--- a/guides/bug_report_templates/action_controller_gem.rb
+++ b/guides/bug_report_templates/action_controller_gem.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
begin
require "bundler/inline"
rescue LoadError => e
diff --git a/guides/bug_report_templates/action_controller_master.rb b/guides/bug_report_templates/action_controller_master.rb
index 3dd66c95ec..1f862e07da 100644
--- a/guides/bug_report_templates/action_controller_master.rb
+++ b/guides/bug_report_templates/action_controller_master.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
begin
require "bundler/inline"
rescue LoadError => e
diff --git a/guides/bug_report_templates/active_job_gem.rb b/guides/bug_report_templates/active_job_gem.rb
index 252b270a0c..af777a86ef 100644
--- a/guides/bug_report_templates/active_job_gem.rb
+++ b/guides/bug_report_templates/active_job_gem.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
begin
require "bundler/inline"
rescue LoadError => e
diff --git a/guides/bug_report_templates/active_job_master.rb b/guides/bug_report_templates/active_job_master.rb
index 7591470440..39fb3f60a6 100644
--- a/guides/bug_report_templates/active_job_master.rb
+++ b/guides/bug_report_templates/active_job_master.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
begin
require "bundler/inline"
rescue LoadError => e
diff --git a/guides/bug_report_templates/active_record_gem.rb b/guides/bug_report_templates/active_record_gem.rb
index 61d4e8d395..168e2dcc66 100644
--- a/guides/bug_report_templates/active_record_gem.rb
+++ b/guides/bug_report_templates/active_record_gem.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
begin
require "bundler/inline"
rescue LoadError => e
diff --git a/guides/bug_report_templates/active_record_master.rb b/guides/bug_report_templates/active_record_master.rb
index 8bbc1ef19e..cbd2cff2b8 100644
--- a/guides/bug_report_templates/active_record_master.rb
+++ b/guides/bug_report_templates/active_record_master.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
begin
require "bundler/inline"
rescue LoadError => e
diff --git a/guides/bug_report_templates/active_record_migrations_gem.rb b/guides/bug_report_templates/active_record_migrations_gem.rb
index 00ba3c1cd6..b931ed0beb 100644
--- a/guides/bug_report_templates/active_record_migrations_gem.rb
+++ b/guides/bug_report_templates/active_record_migrations_gem.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
begin
require "bundler/inline"
rescue LoadError => e
diff --git a/guides/bug_report_templates/active_record_migrations_master.rb b/guides/bug_report_templates/active_record_migrations_master.rb
index 52c9028b0f..2c009c0563 100644
--- a/guides/bug_report_templates/active_record_migrations_master.rb
+++ b/guides/bug_report_templates/active_record_migrations_master.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
begin
require "bundler/inline"
rescue LoadError => e
diff --git a/guides/bug_report_templates/benchmark.rb b/guides/bug_report_templates/benchmark.rb
index a0b541d012..d0f5a634bc 100644
--- a/guides/bug_report_templates/benchmark.rb
+++ b/guides/bug_report_templates/benchmark.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
begin
require "bundler/inline"
rescue LoadError => e
diff --git a/guides/bug_report_templates/generic_gem.rb b/guides/bug_report_templates/generic_gem.rb
index 4dcd04ea27..c990bda005 100644
--- a/guides/bug_report_templates/generic_gem.rb
+++ b/guides/bug_report_templates/generic_gem.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
begin
require "bundler/inline"
rescue LoadError => e
diff --git a/guides/bug_report_templates/generic_master.rb b/guides/bug_report_templates/generic_master.rb
index ed45726e92..1a9b99b624 100644
--- a/guides/bug_report_templates/generic_master.rb
+++ b/guides/bug_report_templates/generic_master.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
begin
require "bundler/inline"
rescue LoadError => e
diff --git a/guides/rails_guides.rb b/guides/rails_guides.rb
index 0f611c8f2b..f2d4d6f647 100644
--- a/guides/rails_guides.rb
+++ b/guides/rails_guides.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
$:.unshift __dir__
as_lib = File.expand_path("../activesupport/lib", __dir__)
diff --git a/guides/rails_guides/generator.rb b/guides/rails_guides/generator.rb
index 35f014747c..7205f37be7 100644
--- a/guides/rails_guides/generator.rb
+++ b/guides/rails_guides/generator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "set"
require "fileutils"
diff --git a/guides/rails_guides/helpers.rb b/guides/rails_guides/helpers.rb
index 520aa7f7cc..a6970fb90c 100644
--- a/guides/rails_guides/helpers.rb
+++ b/guides/rails_guides/helpers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "yaml"
module RailsGuides
diff --git a/guides/rails_guides/indexer.rb b/guides/rails_guides/indexer.rb
index c58b6b85a2..c707464cdf 100644
--- a/guides/rails_guides/indexer.rb
+++ b/guides/rails_guides/indexer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "active_support/core_ext/object/blank"
require "active_support/core_ext/string/inflections"
diff --git a/guides/rails_guides/kindle.rb b/guides/rails_guides/kindle.rb
index 9536d0bd3b..87a369a15a 100644
--- a/guides/rails_guides/kindle.rb
+++ b/guides/rails_guides/kindle.rb
@@ -1,4 +1,5 @@
#!/usr/bin/env ruby
+# frozen_string_literal: true
require "kindlerb"
require "nokogiri"
diff --git a/guides/rails_guides/levenshtein.rb b/guides/rails_guides/levenshtein.rb
index 40c6a5c372..bafa6bfe9d 100644
--- a/guides/rails_guides/levenshtein.rb
+++ b/guides/rails_guides/levenshtein.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module RailsGuides
module Levenshtein
# This code is based directly on the Text gem implementation.
diff --git a/guides/rails_guides/markdown.rb b/guides/rails_guides/markdown.rb
index 02d58601c4..84f95eec68 100644
--- a/guides/rails_guides/markdown.rb
+++ b/guides/rails_guides/markdown.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "redcarpet"
require "nokogiri"
require "rails_guides/markdown/renderer"
diff --git a/guides/rails_guides/markdown/renderer.rb b/guides/rails_guides/markdown/renderer.rb
index 7ac3d417a4..1f2fe91ea1 100644
--- a/guides/rails_guides/markdown/renderer.rb
+++ b/guides/rails_guides/markdown/renderer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module RailsGuides
class Markdown
class Renderer < Redcarpet::Render::HTML
diff --git a/guides/source/action_cable_overview.md b/guides/source/action_cable_overview.md
index 50a28571b4..31151e0329 100644
--- a/guides/source/action_cable_overview.md
+++ b/guides/source/action_cable_overview.md
@@ -64,7 +64,7 @@ module ApplicationCable
private
def find_verified_user
- if verified_user = User.find_by(id: cookies.signed[:user_id])
+ if verified_user = User.find_by(id: cookies.encrypted[:user_id])
verified_user
else
reject_unauthorized_connection
diff --git a/guides/source/action_mailer_basics.md b/guides/source/action_mailer_basics.md
index 7751ac00df..6562dc3a98 100644
--- a/guides/source/action_mailer_basics.md
+++ b/guides/source/action_mailer_basics.md
@@ -64,7 +64,7 @@ Rails. Mailers are conceptually similar to controllers, and so we get a mailer,
a directory for views, and a test.
If you didn't want to use a generator, you could create your own file inside of
-app/mailers, just make sure that it inherits from `ActionMailer::Base`:
+`app/mailers`, just make sure that it inherits from `ActionMailer::Base`:
```ruby
class MyMailer < ActionMailer::Base
diff --git a/guides/source/active_job_basics.md b/guides/source/active_job_basics.md
index 443be77934..2606bfe280 100644
--- a/guides/source/active_job_basics.md
+++ b/guides/source/active_job_basics.md
@@ -260,40 +260,48 @@ backends you need to specify the queues to listen to.
Callbacks
---------
-Active Job provides hooks during the life cycle of a job. Callbacks allow you to
-trigger logic during the life cycle of a job.
-
-### Available callbacks
-
-* `before_enqueue`
-* `around_enqueue`
-* `after_enqueue`
-* `before_perform`
-* `around_perform`
-* `after_perform`
-
-### Usage
+Active Job provides hooks to trigger logic during the life cycle of a job. Like
+other callbacks in Rails, you can implement the callbacks as ordinary methods
+and use a macro-style class method to register them as callbacks:
```ruby
class GuestsCleanupJob < ApplicationJob
queue_as :default
- before_enqueue do |job|
- # Do something with the job instance
- end
-
- around_perform do |job, block|
- # Do something before perform
- block.call
- # Do something after perform
- end
+ around_perform :around_cleanup
def perform
# Do something later
end
+
+ private
+ def around_cleanup(job)
+ # Do something before perform
+ yield
+ # Do something after perform
+ end
end
```
+The macro-style class methods can also receive a block. Consider using this
+style if the code inside your block is so short that it fits in a single line.
+For example, you could send metrics for every job enqueued:
+
+```ruby
+class ApplicationJob
+ before_enqueue { |job| $statsd.increment "#{job.name.underscore}.enqueue" }
+end
+```
+
+### Available callbacks
+
+* `before_enqueue`
+* `around_enqueue`
+* `after_enqueue`
+* `before_perform`
+* `around_perform`
+* `after_perform`
+
Action Mailer
------------
diff --git a/guides/source/active_record_callbacks.md b/guides/source/active_record_callbacks.md
index fc4f773e3c..53417f012e 100644
--- a/guides/source/active_record_callbacks.md
+++ b/guides/source/active_record_callbacks.md
@@ -429,7 +429,7 @@ end
WARNING. The `after_commit` and `after_rollback` callbacks are called for all models created, updated, or destroyed within a transaction block. However, if an exception is raised within one of these callbacks, the exception will bubble up and any remaining `after_commit` or `after_rollback` methods will _not_ be executed. As such, if your callback code could raise an exception, you'll need to rescue it and handle it within the callback in order to allow other callbacks to run.
-WARNING. Using `after_create_commit` and `after_update_commit` both in the same model will override the callback which was registered first amongst them.
+WARNING. Using both `after_create_commit` and `after_update_commit` in the same model will only allow the last callback defined to take effect, and will override all others.
```ruby
class User < ApplicationRecord
diff --git a/guides/source/asset_pipeline.md b/guides/source/asset_pipeline.md
index a02eebf263..383e972f74 100644
--- a/guides/source/asset_pipeline.md
+++ b/guides/source/asset_pipeline.md
@@ -283,10 +283,10 @@ You can view the search path by inspecting
`Rails.application.config.assets.paths` in the Rails console.
Besides the standard `assets/*` paths, additional (fully qualified) paths can be
-added to the pipeline in `config/application.rb`. For example:
+added to the pipeline in `config/initializers/assets.rb`. For example:
```ruby
-config.assets.paths << Rails.root.join("lib", "videoplayer", "flash")
+Rails.application.config.assets.paths << Rails.root.join("lib", "videoplayer", "flash")
```
Paths are traversed in the order they occur in the search path. By default,
diff --git a/guides/source/association_basics.md b/guides/source/association_basics.md
index bead931529..1212ae53bc 100644
--- a/guides/source/association_basics.md
+++ b/guides/source/association_basics.md
@@ -811,6 +811,7 @@ When you declare a `belongs_to` association, the declaring class automatically g
* `build_association(attributes = {})`
* `create_association(attributes = {})`
* `create_association!(attributes = {})`
+* `reload_association`
In all of these methods, `association` is replaced with the symbol passed as the first argument to `belongs_to`. For example, given the declaration:
@@ -828,6 +829,7 @@ author=
build_author
create_author
create_author!
+reload_author
```
NOTE: When initializing a new `has_one` or `belongs_to` association you must use the `build_` prefix to build the association, rather than the `association.build` method that would be used for `has_many` or `has_and_belongs_to_many` associations. To create one, use the `create_` prefix.
@@ -840,10 +842,10 @@ The `association` method returns the associated object, if any. If no associated
@author = @book.author
```
-If the associated object has already been retrieved from the database for this object, the cached version will be returned. To override this behavior (and force a database read), call `#reload` on the parent object.
+If the associated object has already been retrieved from the database for this object, the cached version will be returned. To override this behavior (and force a database read), call `#reload_association` on the parent object.
```ruby
-@author = @book.reload.author
+@author = @book.reload_author
```
##### `association=(associate)`
@@ -1161,6 +1163,7 @@ When you declare a `has_one` association, the declaring class automatically gain
* `build_association(attributes = {})`
* `create_association(attributes = {})`
* `create_association!(attributes = {})`
+* `reload_association`
In all of these methods, `association` is replaced with the symbol passed as the first argument to `has_one`. For example, given the declaration:
@@ -1178,6 +1181,7 @@ account=
build_account
create_account
create_account!
+reload_account
```
NOTE: When initializing a new `has_one` or `belongs_to` association you must use the `build_` prefix to build the association, rather than the `association.build` method that would be used for `has_many` or `has_and_belongs_to_many` associations. To create one, use the `create_` prefix.
@@ -1190,10 +1194,10 @@ The `association` method returns the associated object, if any. If no associated
@account = @supplier.account
```
-If the associated object has already been retrieved from the database for this object, the cached version will be returned. To override this behavior (and force a database read), call `#reload` on the parent object.
+If the associated object has already been retrieved from the database for this object, the cached version will be returned. To override this behavior (and force a database read), call `#reload_association` on the parent object.
```ruby
-@account = @supplier.reload.account
+@account = @supplier.reload_account
```
##### `association=(associate)`
@@ -1443,6 +1447,7 @@ When you declare a `has_many` association, the declaring class automatically gai
* `collection.build(attributes = {}, ...)`
* `collection.create(attributes = {})`
* `collection.create!(attributes = {})`
+* `collection.reload`
In all of these methods, `collection` is replaced with the symbol passed as the first argument to `has_many`, and `collection_singular` is replaced with the singularized version of that symbol. For example, given the declaration:
@@ -1471,11 +1476,12 @@ books.exists?(...)
books.build(attributes = {}, ...)
books.create(attributes = {})
books.create!(attributes = {})
+books.reload
```
##### `collection`
-The `collection` method returns an array of all of the associated objects. If there are no associated objects, it returns an empty array.
+The `collection` method returns a Relation of all of the associated objects. If there are no associated objects, it returns an empty Relation.
```ruby
@books = @author.books
@@ -1609,6 +1615,14 @@ The `collection.create` method returns a single or array of new objects of the a
Does the same as `collection.create` above, but raises `ActiveRecord::RecordInvalid` if the record is invalid.
+##### `collection.reload`
+
+The `collection.reload` method returns a Relation of all of the associated objects, forcing a database read. If there are no associated objects, it returns an empty Relation.
+
+```ruby
+@books = @author.books.reload
+```
+
#### Options for `has_many`
While Rails uses intelligent defaults that will work well in most situations, there may be times when you want to customize the behavior of the `has_many` association reference. Such customizations can easily be accomplished by passing options when you create the association. For example, this association uses two such options:
@@ -1965,6 +1979,7 @@ When you declare a `has_and_belongs_to_many` association, the declaring class au
* `collection.build(attributes = {})`
* `collection.create(attributes = {})`
* `collection.create!(attributes = {})`
+* `collection.reload`
In all of these methods, `collection` is replaced with the symbol passed as the first argument to `has_and_belongs_to_many`, and `collection_singular` is replaced with the singularized version of that symbol. For example, given the declaration:
@@ -1993,6 +2008,7 @@ assemblies.exists?(...)
assemblies.build(attributes = {}, ...)
assemblies.create(attributes = {})
assemblies.create!(attributes = {})
+assemblies.reload
```
##### Additional Column Methods
@@ -2004,7 +2020,7 @@ WARNING: The use of extra attributes on the join table in a `has_and_belongs_to_
##### `collection`
-The `collection` method returns an array of all of the associated objects. If there are no associated objects, it returns an empty array.
+The `collection` method returns a Relation of all of the associated objects. If there are no associated objects, it returns an empty Relation.
```ruby
@assemblies = @part.assemblies
@@ -2116,6 +2132,14 @@ The `collection.create` method returns a new object of the associated type. This
Does the same as `collection.create`, but raises `ActiveRecord::RecordInvalid` if the record is invalid.
+##### `collection.reload`
+
+The `collection.reload` method returns a Relation of all of the associated objects, forcing a database read. If there are no associated objects, it returns an empty Relation.
+
+```ruby
+@assemblies = @part.assemblies.reload
+```
+
#### Options for `has_and_belongs_to_many`
While Rails uses intelligent defaults that will work well in most situations, there may be times when you want to customize the behavior of the `has_and_belongs_to_many` association reference. Such customizations can easily be accomplished by passing options when you create the association. For example, this association uses two such options:
diff --git a/guides/source/command_line.md b/guides/source/command_line.md
index 9fddbf76b6..8ae01286e4 100644
--- a/guides/source/command_line.md
+++ b/guides/source/command_line.md
@@ -102,6 +102,7 @@ Please choose a generator below.
Rails:
assets
+ channel
controller
generator
...
@@ -241,6 +242,8 @@ $ bin/rails generate scaffold HighScore game:string score:integer
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
invoke assets
invoke coffee
create app/assets/javascripts/high_scores.coffee
diff --git a/guides/source/configuring.md b/guides/source/configuring.md
index 61c4bd1e61..f6b7b3b5a7 100644
--- a/guides/source/configuring.md
+++ b/guides/source/configuring.md
@@ -383,7 +383,7 @@ indicates whether boolean values are stored in sqlite3 databases as 1 and 0 or
set to false is deprecated. SQLite databases have used 't' and 'f' to serialize
boolean values and must have old data converted to 1 and 0 (its native boolean
serialization) before setting this flag to true. Conversion can be accomplished
-by setting up a rake task which runs
+by setting up a Rake task which runs
```ruby
ExampleModel.where("boolean_column = 't'").update_all(boolean_column: 1)
@@ -423,7 +423,7 @@ The schema dumper adds one additional configuration option:
* `config.action_controller.per_form_csrf_tokens` configures whether CSRF tokens are only valid for the method/action they were generated for.
-* `config.action_controller.default_protect_from_forgery` determines whether forgery protection is added on `ActionController:Base`. This is false by default, but enabled when loading defaults for Rails 5.2.
+* `config.action_controller.default_protect_from_forgery` determines whether forgery protection is added on `ActionController:Base`. This is false by default, but enabled when loading defaults for Rails 5.2.
* `config.action_controller.relative_url_root` can be used to tell Rails that you are [deploying to a subdirectory](configuring.html#deploy-to-a-subdirectory-relative-url-root). The default is `ENV['RAILS_RELATIVE_URL_ROOT']`.
diff --git a/guides/source/debugging_rails_applications.md b/guides/source/debugging_rails_applications.md
index 58aab774b3..7ea3646c30 100644
--- a/guides/source/debugging_rails_applications.md
+++ b/guides/source/debugging_rails_applications.md
@@ -162,20 +162,24 @@ class ArticlesController < ApplicationController
# ...
def create
- @article = Article.new(params[:article])
+ @article = Article.new(article_params)
logger.debug "New article: #{@article.attributes.inspect}"
logger.debug "Article should be valid: #{@article.valid?}"
if @article.save
- flash[:notice] = 'Article was successfully created.'
logger.debug "The article was saved and now the user is going to be redirected..."
- redirect_to(@article)
+ redirect_to @article, notice: 'Article was successfully created.'
else
- render action: "new"
+ render :new
end
end
# ...
+
+ private
+ def article_params
+ params.require(:article).permit(:title, :body, :published)
+ end
end
```
@@ -542,7 +546,7 @@ command later in this guide).
9
=> 10 respond_to do |format|
11 format.html # index.html.erb
- 12 format.json { render json: @articles }
+ 12 format.json { render json: @articles }
13 end
14 end
15
diff --git a/guides/source/engines.md b/guides/source/engines.md
index 2276f348a1..c7331b6ca4 100644
--- a/guides/source/engines.md
+++ b/guides/source/engines.md
@@ -346,6 +346,9 @@ invoke test_unit
create test/controllers/blorgh/articles_controller_test.rb
invoke helper
create app/helpers/blorgh/articles_helper.rb
+invoke test_unit
+create test/application_system_test_case.rb
+create test/system/articles_test.rb
invoke assets
invoke js
create app/assets/javascripts/blorgh/articles.js
diff --git a/guides/source/generators.md b/guides/source/generators.md
index be1be75e7a..37af7a1310 100644
--- a/guides/source/generators.md
+++ b/guides/source/generators.md
@@ -197,6 +197,9 @@ $ bin/rails generate scaffold User name:string
invoke jbuilder
create app/views/users/index.json.jbuilder
create app/views/users/show.json.jbuilder
+ invoke test_unit
+ create test/application_system_test_case.rb
+ create test/system/users_test.rb
invoke assets
invoke coffee
create app/assets/javascripts/users.coffee
@@ -415,6 +418,9 @@ $ bin/rails generate scaffold Comment body:text
invoke jbuilder
create app/views/comments/index.json.jbuilder
create app/views/comments/show.json.jbuilder
+ invoke test_unit
+ create test/application_system_test_case.rb
+ create test/system/comments_test.rb
invoke assets
invoke coffee
create app/assets/javascripts/comments.coffee
@@ -627,7 +633,7 @@ This method also takes a block:
```ruby
lib "super_special.rb" do
- puts "Super special!"
+ "puts 'Super special!'"
end
```
@@ -636,7 +642,7 @@ end
Creates a Rake file in the `lib/tasks` directory of the application.
```ruby
-rakefile "test.rake", "hello there"
+rakefile "test.rake", 'task(:hello) { puts "Hello, there" }'
```
This method also takes a block:
diff --git a/guides/source/plugins.md b/guides/source/plugins.md
index 8c2d56ceb8..164207a9f9 100644
--- a/guides/source/plugins.md
+++ b/guides/source/plugins.md
@@ -74,7 +74,7 @@ In this example you will add a method to String named `to_squawk`. To begin, cre
```ruby
# yaffle/test/core_ext_test.rb
-require 'test_helper'
+require "test_helper"
class CoreExtTest < ActiveSupport::TestCase
def test_to_squawk_prepends_the_word_squawk
@@ -104,14 +104,16 @@ Finished in 0.003358s, 595.6483 runs/s, 297.8242 assertions/s.
Great - now you are ready to start development.
-In `lib/yaffle.rb`, add `require 'yaffle/core_ext'`:
+In `lib/yaffle.rb`, add `require "yaffle/core_ext"`:
```ruby
# yaffle/lib/yaffle.rb
-require 'yaffle/core_ext'
+require "yaffle/railtie"
+require "yaffle/core_ext"
module Yaffle
+ # Your code goes here...
end
```
@@ -120,7 +122,7 @@ Finally, create the `core_ext.rb` file and add the `to_squawk` method:
```ruby
# yaffle/lib/yaffle/core_ext.rb
-String.class_eval do
+class String
def to_squawk
"squawk! #{self}".strip
end
@@ -152,7 +154,7 @@ To begin, set up your files so that you have:
```ruby
# yaffle/test/acts_as_yaffle_test.rb
-require 'test_helper'
+require "test_helper"
class ActsAsYaffleTest < ActiveSupport::TestCase
end
@@ -161,10 +163,12 @@ end
```ruby
# yaffle/lib/yaffle.rb
-require 'yaffle/core_ext'
-require 'yaffle/acts_as_yaffle'
+require "yaffle/railtie"
+require "yaffle/core_ext"
+require "yaffle/acts_as_yaffle"
module Yaffle
+ # Your code goes here...
end
```
@@ -173,7 +177,6 @@ end
module Yaffle
module ActsAsYaffle
- # your code will go here
end
end
```
@@ -189,7 +192,7 @@ To start out, write a failing test that shows the behavior you'd like:
```ruby
# yaffle/test/acts_as_yaffle_test.rb
-require 'test_helper'
+require "test_helper"
class ActsAsYaffleTest < ActiveSupport::TestCase
def test_a_hickwalls_yaffle_text_field_should_be_last_squawk
@@ -276,12 +279,8 @@ module Yaffle
module ActsAsYaffle
extend ActiveSupport::Concern
- included do
- end
-
- module ClassMethods
+ class_methods do
def acts_as_yaffle(options = {})
- # your code will go here
end
end
end
@@ -335,10 +334,7 @@ module Yaffle
module ActsAsYaffle
extend ActiveSupport::Concern
- included do
- end
-
- module ClassMethods
+ class_methods do
def acts_as_yaffle(options = {})
cattr_accessor :yaffle_text_field, default: (options[:yaffle_text_field] || :last_squawk).to_s
end
@@ -370,7 +366,7 @@ To start out, write a failing test that shows the behavior you'd like:
```ruby
# yaffle/test/acts_as_yaffle_test.rb
-require 'test_helper'
+require "test_helper"
class ActsAsYaffleTest < ActiveSupport::TestCase
def test_a_hickwalls_yaffle_text_field_should_be_last_squawk
@@ -406,19 +402,14 @@ module Yaffle
extend ActiveSupport::Concern
included do
+ def squawk(string)
+ write_attribute(self.class.yaffle_text_field, string.to_squawk)
+ end
end
- module ClassMethods
+ class_methods do
def acts_as_yaffle(options = {})
cattr_accessor :yaffle_text_field, default: (options[:yaffle_text_field] || :last_squawk).to_s
-
- include Yaffle::ActsAsYaffle::LocalInstanceMethods
- end
- end
-
- module LocalInstanceMethods
- def squawk(string)
- write_attribute(self.class.yaffle_text_field, string.to_squawk)
end
end
end
@@ -449,7 +440,7 @@ Generators
----------
Generators can be included in your gem simply by creating them in a lib/generators directory of your plugin. More information about
-the creation of generators can be found in the [Generators Guide](generators.html)
+the creation of generators can be found in the [Generators Guide](generators.html).
Publishing Your Gem
-------------------
@@ -458,13 +449,13 @@ Gem plugins currently in development can easily be shared from any Git repositor
commit the code to a Git repository (like GitHub) and add a line to the Gemfile of the application in question:
```ruby
-gem 'yaffle', git: 'git://github.com/yaffle_watcher/yaffle.git'
+gem "yaffle", git: "https://github.com/rails/yaffle.git"
```
After running `bundle install`, your gem functionality will be available to the application.
-When the gem is ready to be shared as a formal release, it can be published to [RubyGems](http://www.rubygems.org).
-For more information about publishing gems to RubyGems, see: [Creating and Publishing Your First Ruby Gem](http://blog.thepete.net/2010/11/creating-and-publishing-your-first-ruby.html).
+When the gem is ready to be shared as a formal release, it can be published to [RubyGems](https://rubygems.org).
+For more information about publishing gems to RubyGems, see: [Publishing your gem](http://guides.rubygems.org/publishing).
RDoc Documentation
------------------
@@ -478,7 +469,7 @@ The first step is to update the README file with detailed information about how
* How to add the functionality to the app (several examples of common use cases)
* Warnings, gotchas or tips that might help users and save them time
-Once your README is solid, go through and add rdoc comments to all of the methods that developers will use. It's also customary to add '#:nodoc:' comments to those parts of the code that are not included in the public API.
+Once your README is solid, go through and add rdoc comments to all of the methods that developers will use. It's also customary to add `#:nodoc:` comments to those parts of the code that are not included in the public API.
Once your comments are good to go, navigate to your plugin directory and run:
diff --git a/guides/source/routing.md b/guides/source/routing.md
index f52b1862a8..638f77be13 100644
--- a/guides/source/routing.md
+++ b/guides/source/routing.md
@@ -47,7 +47,7 @@ get '/patients/:id', to: 'patients#show', as: 'patient'
and your application contains this code in the controller:
```ruby
-@patient = Patient.find(17)
+@patient = Patient.find(params[:id])
```
and this in the corresponding view:
diff --git a/guides/source/security.md b/guides/source/security.md
index 297680b176..56969c7aea 100644
--- a/guides/source/security.md
+++ b/guides/source/security.md
@@ -1060,7 +1060,7 @@ Additional Resources
The security landscape shifts and it is important to keep up to date, because missing a new vulnerability can be catastrophic. You can find additional resources about (Rails) security here:
-* Subscribe to the Rails security [mailing list.](http://groups.google.com/group/rubyonrails-security)
+* Subscribe to the Rails security [mailing list](http://groups.google.com/group/rubyonrails-security).
* [Brakeman - Rails Security Scanner](http://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)
+* [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).
diff --git a/guides/source/testing.md b/guides/source/testing.md
index f71e963716..8141713e1e 100644
--- a/guides/source/testing.md
+++ b/guides/source/testing.md
@@ -367,9 +367,10 @@ All the basic assertions such as `assert_equal` defined in `Minitest::Assertions
* [`ActiveSupport::TestCase`](http://api.rubyonrails.org/classes/ActiveSupport/TestCase.html)
* [`ActionMailer::TestCase`](http://api.rubyonrails.org/classes/ActionMailer/TestCase.html)
* [`ActionView::TestCase`](http://api.rubyonrails.org/classes/ActionView/TestCase.html)
-* [`ActionDispatch::IntegrationTest`](http://api.rubyonrails.org/classes/ActionDispatch/IntegrationTest.html)
* [`ActiveJob::TestCase`](http://api.rubyonrails.org/classes/ActiveJob/TestCase.html)
+* [`ActionDispatch::IntegrationTest`](http://api.rubyonrails.org/classes/ActionDispatch/IntegrationTest.html)
* [`ActionDispatch::SystemTestCase`](http://api.rubyonrails.org/classes/ActionDispatch/SystemTestCase.html)
+* [`Rails::Generators::TestCase`](http://api.rubyonrails.org/classes/Rails/Generators/TestCase.html)
Each of these classes include `Minitest::Assertions`, allowing us to use all of the basic assertions in our tests.
@@ -451,7 +452,8 @@ You can run multiple files and directories at the same time:
By default test failures and errors are reported inline during a run.
Rails options:
- -e, --environment ENV Run tests in the ENV environment
+ -w, --warnings Run with Ruby warnings enabled
+ -e, --environment Run tests in the ENV environment
-b, --backtrace Show the complete backtrace
-d, --defer-output Output test failures and errors after the test run
-f, --fail-fast Abort test run on first failure or error
@@ -614,7 +616,7 @@ $ bin/rails generate system_test users
create test/system/users_test.rb
```
-Here's what a freshly-generated system test looks like:
+Here's what a freshly generated system test looks like:
```ruby
require "application_system_test_case"
@@ -788,7 +790,7 @@ $ bin/rails generate integration_test user_flows
create test/integration/user_flows_test.rb
```
-Here's what a freshly-generated integration test looks like:
+Here's what a freshly generated integration test looks like:
```ruby
require 'test_helper'
diff --git a/guides/source/upgrading_ruby_on_rails.md b/guides/source/upgrading_ruby_on_rails.md
index 6e927d7244..49a0584d51 100644
--- a/guides/source/upgrading_ruby_on_rails.md
+++ b/guides/source/upgrading_ruby_on_rails.md
@@ -278,7 +278,7 @@ You can now just call the dependency once with a wildcard.
### `ActionView::Helpers::RecordTagHelper` moved to external gem (record_tag_helper)
-`content_tag_for` and `div_for` has been removed in favor of just using `content_tag`. To continue using it, add the `record_tag_helper` gem to your Gemfile:
+`content_tag_for` and `div_for` have been removed in favor of just using `content_tag`. To continue using the older methods, add the `record_tag_helper` gem to your Gemfile:
```ruby
gem 'record_tag_helper', '~> 1.0'
diff --git a/guides/w3c_validator.rb b/guides/w3c_validator.rb
index 4671e040ca..f38b6c2639 100644
--- a/guides/w3c_validator.rb
+++ b/guides/w3c_validator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# ---------------------------------------------------------------------------
#
# This script validates the generated guides against the W3C Validator.
diff --git a/rails.gemspec b/rails.gemspec
index 1dbd86d2fb..4b57377871 100644
--- a/rails.gemspec
+++ b/rails.gemspec
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
version = File.read(File.expand_path("RAILS_VERSION", __dir__)).strip
Gem::Specification.new do |s|
@@ -26,6 +28,7 @@ Gem::Specification.new do |s|
s.add_dependency "actionmailer", version
s.add_dependency "activejob", version
s.add_dependency "actioncable", version
+ s.add_dependency "activestorage", version
s.add_dependency "railties", version
s.add_dependency "bundler", ">= 1.3.0"
diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md
index ec41d17a9f..7b720d6e18 100644
--- a/railties/CHANGELOG.md
+++ b/railties/CHANGELOG.md
@@ -1,5 +1,33 @@
+* Add `--skip-yarn` option to the plugin generator.
+
+ *bogdanvlviv*
+
+* Optimize routes indentation.
+
+ *Yoshiyuki Hirano*
+
+* Optimize indentation for generator actions.
+
+ *Yoshiyuki Hirano*
+
+* Skip unused components when running `bin/rails` in Rails plugin.
+
+ *Yoshiyuki Hirano*
+
+* Add `git_source` to `Gemfile` for plugin generator.
+
+ *Yoshiyuki Hirano*
+
+* Add `--skip-action-cable` option to the plugin generator.
+
+ *bogdanvlviv*
+
+* Deprecate support of use `Rails::Application` subclass to start Rails server.
+
+ *Yuji Yaginuma*
+
* Add `ruby x.x.x` version to `Gemfile` and create `.ruby-version`
- root file containing current Ruby version when new Rails applications are
+ root file containing the current Ruby version when new Rails applications are
created.
*Alberto Almagro*
@@ -32,7 +60,7 @@
*Robin Dupret*, *Kasper Timm Hansen*
-* Allow to pass a custom connection name to the `rails dbconsole`
+* Allow passing a custom connection name to the `rails dbconsole`
command when using a 3-level database configuration.
$ bin/rails dbconsole -c replica
@@ -56,7 +84,7 @@
* Load environment file in `dbconsole` command.
- Fixes #29717
+ Fixes #29717.
*Yuji Yaginuma*
diff --git a/railties/Rakefile b/railties/Rakefile
index d6284b7dc5..d41c6e7438 100644
--- a/railties/Rakefile
+++ b/railties/Rakefile
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "rake/testtask"
task default: :test
diff --git a/railties/bin/test b/railties/bin/test
index 470ce93f10..c53377cc97 100755
--- a/railties/bin/test
+++ b/railties/bin/test
@@ -1,4 +1,5 @@
#!/usr/bin/env ruby
+# frozen_string_literal: true
COMPONENT_ROOT = File.expand_path("..", __dir__)
require_relative "../../tools/test"
diff --git a/railties/exe/rails b/railties/exe/rails
index a5635c2297..79af4db6b6 100755
--- a/railties/exe/rails
+++ b/railties/exe/rails
@@ -1,4 +1,5 @@
#!/usr/bin/env ruby
+# frozen_string_literal: true
git_path = File.expand_path("../../.git", __dir__)
diff --git a/railties/lib/minitest/rails_plugin.rb b/railties/lib/minitest/rails_plugin.rb
index fec9ae64d8..6901b0bbc8 100644
--- a/railties/lib/minitest/rails_plugin.rb
+++ b/railties/lib/minitest/rails_plugin.rb
@@ -1,5 +1,8 @@
+# frozen_string_literal: true
+
require "active_support/core_ext/module/attribute_accessors"
require "rails/test_unit/reporter"
+require "rails/test_unit/runner"
module Minitest
class SuppressedSummaryReporter < SummaryReporter
@@ -10,6 +13,8 @@ module Minitest
end
def self.plugin_rails_options(opts, options)
+ Rails::TestUnit::Runner.attach_before_load_options(opts)
+
opts.on("-b", "--backtrace", "Show the complete backtrace") do
options[:full_backtrace] = true
end
diff --git a/railties/lib/rails.rb b/railties/lib/rails.rb
index a12ca42c30..04bb9ba94a 100644
--- a/railties/lib/rails.rb
+++ b/railties/lib/rails.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_relative "rails/ruby_version_check"
require "pathname"
diff --git a/railties/lib/rails/all.rb b/railties/lib/rails/all.rb
index 7606ea0e46..e55b2e2433 100644
--- a/railties/lib/rails/all.rb
+++ b/railties/lib/rails/all.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "rails"
%w(
@@ -7,6 +9,7 @@ require "rails"
action_mailer/railtie
active_job/railtie
action_cable/engine
+ active_storage/engine
rails/test_unit/railtie
sprockets/railtie
).each do |railtie|
diff --git a/railties/lib/rails/api/generator.rb b/railties/lib/rails/api/generator.rb
index dcc491783c..6e5eec2e34 100644
--- a/railties/lib/rails/api/generator.rb
+++ b/railties/lib/rails/api/generator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "sdoc"
class RDoc::Generator::API < RDoc::Generator::SDoc # :nodoc:
diff --git a/railties/lib/rails/api/task.rb b/railties/lib/rails/api/task.rb
index 49267c2329..595079e45f 100644
--- a/railties/lib/rails/api/task.rb
+++ b/railties/lib/rails/api/task.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "rdoc/task"
require_relative "generator"
@@ -64,6 +66,13 @@ module Rails
)
},
+ "activestorage" => {
+ include: %w(
+ README.md
+ lib/active_storage/**/*.rb
+ )
+ },
+
"railties" => {
include: %w(
README.rdoc
diff --git a/railties/lib/rails/app_loader.rb b/railties/lib/rails/app_loader.rb
index 9e4f715ca6..3e9b3bd4bb 100644
--- a/railties/lib/rails/app_loader.rb
+++ b/railties/lib/rails/app_loader.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "pathname"
require_relative "version"
diff --git a/railties/lib/rails/app_updater.rb b/railties/lib/rails/app_updater.rb
index e53d1cf093..c2436a69f9 100644
--- a/railties/lib/rails/app_updater.rb
+++ b/railties/lib/rails/app_updater.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "rails/generators"
require "rails/generators/rails/app/app_generator"
diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb
index a71f36840e..72f8bf0e14 100644
--- a/railties/lib/rails/application.rb
+++ b/railties/lib/rails/application.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "yaml"
require "active_support/core_ext/hash/keys"
require "active_support/core_ext/object/blank"
diff --git a/railties/lib/rails/application/bootstrap.rb b/railties/lib/rails/application/bootstrap.rb
index 44b49556d7..c24d4573a9 100644
--- a/railties/lib/rails/application/bootstrap.rb
+++ b/railties/lib/rails/application/bootstrap.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "fileutils"
require "active_support/notifications"
require "active_support/dependencies"
diff --git a/railties/lib/rails/application/configuration.rb b/railties/lib/rails/application/configuration.rb
index 4797223380..31e2a45bff 100644
--- a/railties/lib/rails/application/configuration.rb
+++ b/railties/lib/rails/application/configuration.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "active_support/core_ext/kernel/reporting"
require "active_support/file_update_checker"
require_relative "../engine/configuration"
diff --git a/railties/lib/rails/application/default_middleware_stack.rb b/railties/lib/rails/application/default_middleware_stack.rb
index 63300ffef3..ea2273c1f2 100644
--- a/railties/lib/rails/application/default_middleware_stack.rb
+++ b/railties/lib/rails/application/default_middleware_stack.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Rails
class Application
class DefaultMiddlewareStack
diff --git a/railties/lib/rails/application/finisher.rb b/railties/lib/rails/application/finisher.rb
index c027d06663..3d938be951 100644
--- a/railties/lib/rails/application/finisher.rb
+++ b/railties/lib/rails/application/finisher.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Rails
class Application
module Finisher
diff --git a/railties/lib/rails/application/routes_reloader.rb b/railties/lib/rails/application/routes_reloader.rb
index e02ef629f2..2ef09b4162 100644
--- a/railties/lib/rails/application/routes_reloader.rb
+++ b/railties/lib/rails/application/routes_reloader.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "active_support/core_ext/module/delegation"
module Rails
diff --git a/railties/lib/rails/application_controller.rb b/railties/lib/rails/application_controller.rb
index f7d112900a..fa8793d81a 100644
--- a/railties/lib/rails/application_controller.rb
+++ b/railties/lib/rails/application_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Rails::ApplicationController < ActionController::Base # :nodoc:
self.view_paths = File.expand_path("templates", __dir__)
layout "application"
diff --git a/railties/lib/rails/backtrace_cleaner.rb b/railties/lib/rails/backtrace_cleaner.rb
index 3bd18ebfb5..ae8db0f8ef 100644
--- a/railties/lib/rails/backtrace_cleaner.rb
+++ b/railties/lib/rails/backtrace_cleaner.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "active_support/backtrace_cleaner"
module Rails
diff --git a/railties/lib/rails/cli.rb b/railties/lib/rails/cli.rb
index 602e56c961..50a2ed30cf 100644
--- a/railties/lib/rails/cli.rb
+++ b/railties/lib/rails/cli.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_relative "app_loader"
# If we are inside a Rails application this method performs an exec and thus
diff --git a/railties/lib/rails/code_statistics.rb b/railties/lib/rails/code_statistics.rb
index 5e0d807a21..7ceb86198f 100644
--- a/railties/lib/rails/code_statistics.rb
+++ b/railties/lib/rails/code_statistics.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_relative "code_statistics_calculator"
require "active_support/core_ext/enumerable"
diff --git a/railties/lib/rails/code_statistics_calculator.rb b/railties/lib/rails/code_statistics_calculator.rb
index d0194af197..85f86bdbd0 100644
--- a/railties/lib/rails/code_statistics_calculator.rb
+++ b/railties/lib/rails/code_statistics_calculator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class CodeStatisticsCalculator #:nodoc:
attr_reader :lines, :code_lines, :classes, :methods
diff --git a/railties/lib/rails/command.rb b/railties/lib/rails/command.rb
index ee020b58f9..812e846837 100644
--- a/railties/lib/rails/command.rb
+++ b/railties/lib/rails/command.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "active_support"
require "active_support/dependencies/autoload"
require "active_support/core_ext/enumerable"
diff --git a/railties/lib/rails/command/actions.rb b/railties/lib/rails/command/actions.rb
index a00e58997c..2f6827b7f4 100644
--- a/railties/lib/rails/command/actions.rb
+++ b/railties/lib/rails/command/actions.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Rails
module Command
module Actions
diff --git a/railties/lib/rails/command/base.rb b/railties/lib/rails/command/base.rb
index cd0720be4e..e92b2042bd 100644
--- a/railties/lib/rails/command/base.rb
+++ b/railties/lib/rails/command/base.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "thor"
require "erb"
@@ -73,7 +75,7 @@ module Rails
# Use Rails' default banner.
def banner(*)
- "#{executable} #{arguments.map(&:usage).join(' ')} [options]".squish!
+ "#{executable} #{arguments.map(&:usage).join(' ')} [options]".squish
end
# Sets the base_name taking into account the current class namespace.
diff --git a/railties/lib/rails/command/behavior.rb b/railties/lib/rails/command/behavior.rb
index 4a92f72f16..7a6dd28e1a 100644
--- a/railties/lib/rails/command/behavior.rb
+++ b/railties/lib/rails/command/behavior.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "active_support"
module Rails
diff --git a/railties/lib/rails/command/environment_argument.rb b/railties/lib/rails/command/environment_argument.rb
index 9582509840..5dc98b113d 100644
--- a/railties/lib/rails/command/environment_argument.rb
+++ b/railties/lib/rails/command/environment_argument.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "active_support"
module Rails
diff --git a/railties/lib/rails/commands.rb b/railties/lib/rails/commands.rb
index a33e2778d4..1aea1e1a96 100644
--- a/railties/lib/rails/commands.rb
+++ b/railties/lib/rails/commands.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_relative "command"
aliases = {
diff --git a/railties/lib/rails/commands/application/application_command.rb b/railties/lib/rails/commands/application/application_command.rb
index e92daf9be6..13d47a63bc 100644
--- a/railties/lib/rails/commands/application/application_command.rb
+++ b/railties/lib/rails/commands/application/application_command.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_relative "../../generators"
require_relative "../../generators/rails/app/app_generator"
diff --git a/railties/lib/rails/commands/console/console_command.rb b/railties/lib/rails/commands/console/console_command.rb
index 6f9a1f022b..5dc695c240 100644
--- a/railties/lib/rails/commands/console/console_command.rb
+++ b/railties/lib/rails/commands/console/console_command.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "irb"
require "irb/completion"
diff --git a/railties/lib/rails/commands/dbconsole/dbconsole_command.rb b/railties/lib/rails/commands/dbconsole/dbconsole_command.rb
index 71b3455473..6296c95a87 100644
--- a/railties/lib/rails/commands/dbconsole/dbconsole_command.rb
+++ b/railties/lib/rails/commands/dbconsole/dbconsole_command.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_relative "../../command/environment_argument"
module Rails
@@ -58,7 +60,7 @@ module Rails
logon = ""
if config["username"]
- logon = config["username"]
+ logon = config["username"].dup
logon << "/#{config['password']}" if config["password"] && @options["include_password"]
logon << "@#{config['database']}" if config["database"]
end
diff --git a/railties/lib/rails/commands/destroy/destroy_command.rb b/railties/lib/rails/commands/destroy/destroy_command.rb
index 8a6fe67f77..686193ddb9 100644
--- a/railties/lib/rails/commands/destroy/destroy_command.rb
+++ b/railties/lib/rails/commands/destroy/destroy_command.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_relative "../../generators"
module Rails
diff --git a/railties/lib/rails/commands/generate/generate_command.rb b/railties/lib/rails/commands/generate/generate_command.rb
index c2089dbcb7..73f627637d 100644
--- a/railties/lib/rails/commands/generate/generate_command.rb
+++ b/railties/lib/rails/commands/generate/generate_command.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_relative "../../generators"
module Rails
diff --git a/railties/lib/rails/commands/help/help_command.rb b/railties/lib/rails/commands/help/help_command.rb
index 90d37217fc..8e5b4d68d3 100644
--- a/railties/lib/rails/commands/help/help_command.rb
+++ b/railties/lib/rails/commands/help/help_command.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Rails
module Command
class HelpCommand < Base # :nodoc:
diff --git a/railties/lib/rails/commands/new/new_command.rb b/railties/lib/rails/commands/new/new_command.rb
index 207dd5d995..d73d64d899 100644
--- a/railties/lib/rails/commands/new/new_command.rb
+++ b/railties/lib/rails/commands/new/new_command.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Rails
module Command
class NewCommand < Base # :nodoc:
diff --git a/railties/lib/rails/commands/plugin/plugin_command.rb b/railties/lib/rails/commands/plugin/plugin_command.rb
index e915a24e5b..5d3dfadf84 100644
--- a/railties/lib/rails/commands/plugin/plugin_command.rb
+++ b/railties/lib/rails/commands/plugin/plugin_command.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Rails
module Command
class PluginCommand < Base # :nodoc:
diff --git a/railties/lib/rails/commands/rake/rake_command.rb b/railties/lib/rails/commands/rake/rake_command.rb
index 075b1fd23d..535df0c430 100644
--- a/railties/lib/rails/commands/rake/rake_command.rb
+++ b/railties/lib/rails/commands/rake/rake_command.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Rails
module Command
class RakeCommand < Base # :nodoc:
diff --git a/railties/lib/rails/commands/runner/runner_command.rb b/railties/lib/rails/commands/runner/runner_command.rb
index c931fc2152..cd9462e08f 100644
--- a/railties/lib/rails/commands/runner/runner_command.rb
+++ b/railties/lib/rails/commands/runner/runner_command.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Rails
module Command
class RunnerCommand < Base # :nodoc:
diff --git a/railties/lib/rails/commands/secrets/secrets_command.rb b/railties/lib/rails/commands/secrets/secrets_command.rb
index 45e02fa730..d93c4de74e 100644
--- a/railties/lib/rails/commands/secrets/secrets_command.rb
+++ b/railties/lib/rails/commands/secrets/secrets_command.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "active_support"
require_relative "../../secrets"
diff --git a/railties/lib/rails/commands/server/server_command.rb b/railties/lib/rails/commands/server/server_command.rb
index ce258341f6..88e5698714 100644
--- a/railties/lib/rails/commands/server/server_command.rb
+++ b/railties/lib/rails/commands/server/server_command.rb
@@ -1,7 +1,11 @@
+# frozen_string_literal: true
+
require "fileutils"
require "optparse"
require "action_dispatch"
require "rails"
+require "active_support/deprecation"
+require "active_support/core_ext/string/filters"
require_relative "../../dev_caching"
module Rails
@@ -18,10 +22,15 @@ module Rails
set_environment
end
- # TODO: this is no longer required but we keep it for the moment to support older config.ru files.
def app
@app ||= begin
app = super
+ if app.is_a?(Class)
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
+ Use `Rails::Application` subclass to start the server is deprecated and will be removed in Rails 6.0.
+ Please change `run #{app}` to `run Rails.application` in config.ru.
+ MSG
+ end
app.respond_to?(:to_app) ? app.to_app : app
end
end
diff --git a/railties/lib/rails/commands/test/test_command.rb b/railties/lib/rails/commands/test/test_command.rb
index 5852f51a62..a2216553ca 100644
--- a/railties/lib/rails/commands/test/test_command.rb
+++ b/railties/lib/rails/commands/test/test_command.rb
@@ -1,33 +1,28 @@
+# frozen_string_literal: true
+
require_relative "../../command"
require_relative "../../test_unit/runner"
+require_relative "../../test_unit/reporter"
module Rails
module Command
class TestCommand < Base # :nodoc:
no_commands do
def help
- require "optparse"
- require "minitest/rails_plugin"
-
- opts = OptionParser.new
- opts.banner = "Usage: #{Rails::TestUnitReporter.executable} [options] [files or directories]"
- opts.separator ""
- opts.separator "You can run a single test by appending a line number to a filename:"
- opts.separator ""
- opts.separator " #{Rails::TestUnitReporter.executable} test/models/user_test.rb:27"
- opts.separator ""
- opts.separator "You can run multiple files and directories at the same time:"
- opts.separator ""
- opts.separator " #{Rails::TestUnitReporter.executable} test/controllers test/integration/login_test.rb"
- opts.separator ""
- opts.separator "By default test failures and errors are reported inline during a run."
- opts.separator ""
-
- opts.separator "Rails options:"
- Rails::TestUnit::Runner.options(opts)
- Minitest.plugin_rails_options(opts, {})
+ say "Usage: #{Rails::TestUnitReporter.executable} [options] [files or directories]"
+ say ""
+ say "You can run a single test by appending a line number to a filename:"
+ say ""
+ say " #{Rails::TestUnitReporter.executable} test/models/user_test.rb:27"
+ say ""
+ say "You can run multiple files and directories at the same time:"
+ say ""
+ say " #{Rails::TestUnitReporter.executable} test/controllers test/integration/login_test.rb"
+ say ""
+ say "By default test failures and errors are reported inline during a run."
+ say ""
- say opts
+ Minitest.run(%w(--help))
end
end
diff --git a/railties/lib/rails/commands/version/version_command.rb b/railties/lib/rails/commands/version/version_command.rb
index ac745594ee..3e2112b6d4 100644
--- a/railties/lib/rails/commands/version/version_command.rb
+++ b/railties/lib/rails/commands/version/version_command.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Rails
module Command
class VersionCommand < Base # :nodoc:
diff --git a/railties/lib/rails/configuration.rb b/railties/lib/rails/configuration.rb
index 89ed313ff0..70815d114d 100644
--- a/railties/lib/rails/configuration.rb
+++ b/railties/lib/rails/configuration.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "active_support/ordered_options"
require "active_support/core_ext/object"
require_relative "paths"
diff --git a/railties/lib/rails/console/app.rb b/railties/lib/rails/console/app.rb
index affadc8e09..c37583ce9a 100644
--- a/railties/lib/rails/console/app.rb
+++ b/railties/lib/rails/console/app.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "active_support/all"
require "action_controller"
diff --git a/railties/lib/rails/console/helpers.rb b/railties/lib/rails/console/helpers.rb
index a33f71dc5b..39fbc55606 100644
--- a/railties/lib/rails/console/helpers.rb
+++ b/railties/lib/rails/console/helpers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Rails
module ConsoleMethods
# Gets the helper methods available to the controller.
diff --git a/railties/lib/rails/dev_caching.rb b/railties/lib/rails/dev_caching.rb
index f69275a34a..efb523de4c 100644
--- a/railties/lib/rails/dev_caching.rb
+++ b/railties/lib/rails/dev_caching.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "fileutils"
module Rails
diff --git a/railties/lib/rails/engine.rb b/railties/lib/rails/engine.rb
index a80593d4b9..cc2030d37d 100644
--- a/railties/lib/rails/engine.rb
+++ b/railties/lib/rails/engine.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_relative "railtie"
require_relative "engine/railties"
require "active_support/core_ext/module/delegation"
diff --git a/railties/lib/rails/engine/commands.rb b/railties/lib/rails/engine/commands.rb
index 5267b23d6c..3854907507 100644
--- a/railties/lib/rails/engine/commands.rb
+++ b/railties/lib/rails/engine/commands.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
unless defined?(APP_PATH)
if File.exist?(File.expand_path("test/dummy/config/application.rb", ENGINE_ROOT))
APP_PATH = File.expand_path("test/dummy/config/application", ENGINE_ROOT)
diff --git a/railties/lib/rails/engine/configuration.rb b/railties/lib/rails/engine/configuration.rb
index 9c7e2942bc..16ba7f9eb8 100644
--- a/railties/lib/rails/engine/configuration.rb
+++ b/railties/lib/rails/engine/configuration.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_relative "../railtie/configuration"
module Rails
diff --git a/railties/lib/rails/engine/railties.rb b/railties/lib/rails/engine/railties.rb
index 9969a1475d..052b74c880 100644
--- a/railties/lib/rails/engine/railties.rb
+++ b/railties/lib/rails/engine/railties.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Rails
class Engine < Railtie
class Railties
diff --git a/railties/lib/rails/engine/updater.rb b/railties/lib/rails/engine/updater.rb
index af2bff1e59..21a0fc5562 100644
--- a/railties/lib/rails/engine/updater.rb
+++ b/railties/lib/rails/engine/updater.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_relative "../generators"
require_relative "../generators/rails/plugin/plugin_generator"
@@ -7,7 +9,7 @@ module Rails
class << self
def generator
@generator ||= Rails::Generators::PluginGenerator.new ["plugin"],
- { engine: true }, destination_root: ENGINE_ROOT
+ { engine: true }, { destination_root: ENGINE_ROOT }
end
def run(action)
diff --git a/railties/lib/rails/gem_version.rb b/railties/lib/rails/gem_version.rb
index 7bacf2e0ba..92b5e0392a 100644
--- a/railties/lib/rails/gem_version.rb
+++ b/railties/lib/rails/gem_version.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Rails
# Returns the version of the currently loaded Rails as a <tt>Gem::Version</tt>
def self.gem_version
diff --git a/railties/lib/rails/generators.rb b/railties/lib/rails/generators.rb
index b9a0fe07c8..a630d55e59 100644
--- a/railties/lib/rails/generators.rb
+++ b/railties/lib/rails/generators.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
activesupport_path = File.expand_path("../../../activesupport/lib", __dir__)
$:.unshift(activesupport_path) if File.directory?(activesupport_path) && !$:.include?(activesupport_path)
@@ -10,6 +12,7 @@ require "active_support/core_ext/kernel/singleton_class"
require "active_support/core_ext/array/extract_options"
require "active_support/core_ext/hash/deep_merge"
require "active_support/core_ext/module/attribute_accessors"
+require "active_support/core_ext/string/indent"
require "active_support/core_ext/string/inflections"
module Rails
@@ -270,7 +273,7 @@ module Rails
else
options = sorted_groups.flat_map(&:last)
suggestions = options.sort_by { |suggested| levenshtein_distance(namespace.to_s, suggested) }.first(3)
- msg = "Could not find generator '#{namespace}'. "
+ msg = "Could not find generator '#{namespace}'. ".dup
msg << "Maybe you meant #{ suggestions.map { |s| "'#{s}'" }.to_sentence(last_word_connector: " or ", locale: :en) }\n"
msg << "Run `rails generate --help` for more options."
puts msg
diff --git a/railties/lib/rails/generators/actions.rb b/railties/lib/rails/generators/actions.rb
index 2792d7636f..c773e07eba 100644
--- a/railties/lib/rails/generators/actions.rb
+++ b/railties/lib/rails/generators/actions.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Rails
module Generators
module Actions
@@ -97,16 +99,16 @@ module Rails
# "config.action_controller.asset_host = 'localhost:3000'"
# end
def environment(data = nil, options = {})
- sentinel = /class [a-z_:]+ < Rails::Application/i
- env_file_sentinel = /Rails\.application\.configure do/
- data = yield if !data && block_given?
+ sentinel = "class Application < Rails::Application\n"
+ env_file_sentinel = "Rails.application.configure do\n"
+ data ||= yield if block_given?
in_root do
if options[:env].nil?
- inject_into_file "config/application.rb", "\n #{data}", after: sentinel, verbose: false
+ inject_into_file "config/application.rb", optimize_indentation(data, 4), after: sentinel, verbose: false
else
Array(options[:env]).each do |env|
- inject_into_file "config/environments/#{env}.rb", "\n #{data}", after: env_file_sentinel, verbose: false
+ inject_into_file "config/environments/#{env}.rb", optimize_indentation(data, 2), after: env_file_sentinel, verbose: false
end
end
end
@@ -137,12 +139,13 @@ module Rails
# end
#
# vendor("foreign.rb", "# Foreign code is fun")
- def vendor(filename, data = nil, &block)
+ def vendor(filename, data = nil)
log :vendor, filename
- create_file("vendor/#{filename}", data, verbose: false, &block)
+ data ||= yield if block_given?
+ create_file("vendor/#{filename}", optimize_indentation(data), verbose: false)
end
- # Create a new file in the lib/ directory. Code can be specified
+ # Create a new file in the <tt>lib/</tt> directory. Code can be specified
# in a block or a data string can be given.
#
# lib("crypto.rb") do
@@ -150,9 +153,10 @@ module Rails
# end
#
# lib("foreign.rb", "# Foreign code is fun")
- def lib(filename, data = nil, &block)
+ def lib(filename, data = nil)
log :lib, filename
- create_file("lib/#{filename}", data, verbose: false, &block)
+ data ||= yield if block_given?
+ create_file("lib/#{filename}", optimize_indentation(data), verbose: false)
end
# Create a new +Rakefile+ with the provided code (either in a block or a string).
@@ -170,9 +174,10 @@ module Rails
# end
#
# rakefile('seed.rake', 'puts "Planting seeds"')
- def rakefile(filename, data = nil, &block)
+ def rakefile(filename, data = nil)
log :rakefile, filename
- create_file("lib/tasks/#{filename}", data, verbose: false, &block)
+ data ||= yield if block_given?
+ create_file("lib/tasks/#{filename}", optimize_indentation(data), verbose: false)
end
# Create a new initializer with the provided code (either in a block or a string).
@@ -188,9 +193,10 @@ module Rails
# end
#
# initializer("api.rb", "API_KEY = '123456'")
- def initializer(filename, data = nil, &block)
+ def initializer(filename, data = nil)
log :initializer, filename
- create_file("config/initializers/#{filename}", data, verbose: false, &block)
+ data ||= yield if block_given?
+ create_file("config/initializers/#{filename}", optimize_indentation(data), verbose: false)
end
# Generate something using a generator from Rails or a plugin.
@@ -240,7 +246,7 @@ module Rails
sentinel = /\.routes\.draw do\s*\n/m
in_root do
- inject_into_file "config/routes.rb", " #{routing_code}\n", after: sentinel, verbose: false, force: false
+ inject_into_file "config/routes.rb", optimize_indentation(routing_code, 2), after: sentinel, verbose: false, force: false
end
end
@@ -304,6 +310,17 @@ module Rails
"'#{value}'"
end
end
+
+ # Returns optimized string with indentation
+ def optimize_indentation(value, amount = 0) # :doc:
+ return "#{value}\n" unless value.is_a?(String)
+
+ if value.lines.size > 1
+ value.strip_heredoc.indent(amount)
+ else
+ "#{value.strip.indent(amount)}\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 d06609e91e..05bc242447 100644
--- a/railties/lib/rails/generators/actions/create_migration.rb
+++ b/railties/lib/rails/generators/actions/create_migration.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "fileutils"
require "thor/actions"
diff --git a/railties/lib/rails/generators/active_model.rb b/railties/lib/rails/generators/active_model.rb
index 2679d06fe4..8df8eb2438 100644
--- a/railties/lib/rails/generators/active_model.rb
+++ b/railties/lib/rails/generators/active_model.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Rails
module Generators
# ActiveModel is a class to be implemented by each ORM to allow Rails to
diff --git a/railties/lib/rails/generators/app_base.rb b/railties/lib/rails/generators/app_base.rb
index 4899a63917..c8688fb7f3 100644
--- a/railties/lib/rails/generators/app_base.rb
+++ b/railties/lib/rails/generators/app_base.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "fileutils"
require "digest/md5"
require "active_support/core_ext/string/strip"
diff --git a/railties/lib/rails/generators/base.rb b/railties/lib/rails/generators/base.rb
index 9705af8b40..5523a3f659 100644
--- a/railties/lib/rails/generators/base.rb
+++ b/railties/lib/rails/generators/base.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
begin
require "thor/group"
rescue LoadError
diff --git a/railties/lib/rails/generators/css/assets/assets_generator.rb b/railties/lib/rails/generators/css/assets/assets_generator.rb
index 40f3bb3c70..5f7be769b2 100644
--- a/railties/lib/rails/generators/css/assets/assets_generator.rb
+++ b/railties/lib/rails/generators/css/assets/assets_generator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_relative "../../named_base"
module Css # :nodoc:
diff --git a/railties/lib/rails/generators/css/scaffold/scaffold_generator.rb b/railties/lib/rails/generators/css/scaffold/scaffold_generator.rb
index b8c72828a1..5996cb1483 100644
--- a/railties/lib/rails/generators/css/scaffold/scaffold_generator.rb
+++ b/railties/lib/rails/generators/css/scaffold/scaffold_generator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_relative "../../named_base"
module Css # :nodoc:
diff --git a/railties/lib/rails/generators/erb.rb b/railties/lib/rails/generators/erb.rb
index 5314c42e46..2c20834611 100644
--- a/railties/lib/rails/generators/erb.rb
+++ b/railties/lib/rails/generators/erb.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_relative "named_base"
module Erb # :nodoc:
diff --git a/railties/lib/rails/generators/erb/controller/controller_generator.rb b/railties/lib/rails/generators/erb/controller/controller_generator.rb
index fb8222f654..1a6c84288b 100644
--- a/railties/lib/rails/generators/erb/controller/controller_generator.rb
+++ b/railties/lib/rails/generators/erb/controller/controller_generator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_relative "../../erb"
module Erb # :nodoc:
diff --git a/railties/lib/rails/generators/erb/mailer/mailer_generator.rb b/railties/lib/rails/generators/erb/mailer/mailer_generator.rb
index 53dd3289d4..5774d86c8e 100644
--- a/railties/lib/rails/generators/erb/mailer/mailer_generator.rb
+++ b/railties/lib/rails/generators/erb/mailer/mailer_generator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_relative "../../erb"
module Erb # :nodoc:
diff --git a/railties/lib/rails/generators/erb/scaffold/scaffold_generator.rb b/railties/lib/rails/generators/erb/scaffold/scaffold_generator.rb
index 0929425137..e80c6d4b7d 100644
--- a/railties/lib/rails/generators/erb/scaffold/scaffold_generator.rb
+++ b/railties/lib/rails/generators/erb/scaffold/scaffold_generator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_relative "../../erb"
require_relative "../../resource_helpers"
diff --git a/railties/lib/rails/generators/generated_attribute.rb b/railties/lib/rails/generators/generated_attribute.rb
index baed7bf1e3..2728459968 100644
--- a/railties/lib/rails/generators/generated_attribute.rb
+++ b/railties/lib/rails/generators/generated_attribute.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "active_support/time"
module Rails
@@ -151,7 +153,7 @@ module Rails
end
def inject_options
- "".tap { |s| options_for_migration.each { |k, v| s << ", #{k}: #{v.inspect}" } }
+ "".dup.tap { |s| options_for_migration.each { |k, v| s << ", #{k}: #{v.inspect}" } }
end
def inject_index_options
diff --git a/railties/lib/rails/generators/js/assets/assets_generator.rb b/railties/lib/rails/generators/js/assets/assets_generator.rb
index a4f84cc1ad..ea151aa04e 100644
--- a/railties/lib/rails/generators/js/assets/assets_generator.rb
+++ b/railties/lib/rails/generators/js/assets/assets_generator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_relative "../../named_base"
module Js # :nodoc:
diff --git a/railties/lib/rails/generators/migration.rb b/railties/lib/rails/generators/migration.rb
index 57c36f5668..7162b8c0b4 100644
--- a/railties/lib/rails/generators/migration.rb
+++ b/railties/lib/rails/generators/migration.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "active_support/concern"
require_relative "actions/create_migration"
diff --git a/railties/lib/rails/generators/model_helpers.rb b/railties/lib/rails/generators/model_helpers.rb
index 5bf480c5c0..aa3564476a 100644
--- a/railties/lib/rails/generators/model_helpers.rb
+++ b/railties/lib/rails/generators/model_helpers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_relative "active_model"
module Rails
diff --git a/railties/lib/rails/generators/named_base.rb b/railties/lib/rails/generators/named_base.rb
index dc1d6cab9d..fe8447be23 100644
--- a/railties/lib/rails/generators/named_base.rb
+++ b/railties/lib/rails/generators/named_base.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "active_support/core_ext/module/introspection"
require_relative "base"
require_relative "generated_attribute"
diff --git a/railties/lib/rails/generators/rails/app/app_generator.rb b/railties/lib/rails/generators/rails/app/app_generator.rb
index 507099ef57..0f73cc4755 100644
--- a/railties/lib/rails/generators/rails/app/app_generator.rb
+++ b/railties/lib/rails/generators/rails/app/app_generator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_relative "../../app_base"
module Rails
@@ -113,6 +115,7 @@ module Rails
template "cable.yml" unless options[:skip_action_cable]
template "puma.rb" unless options[:skip_puma]
template "spring.rb" if spring_install?
+ template "storage.yml"
directory "environments"
directory "initializers"
@@ -122,9 +125,10 @@ module Rails
def config_when_updating
cookie_serializer_config_exist = File.exist?("config/initializers/cookies_serializer.rb")
- action_cable_config_exist = File.exist?("config/cable.yml")
- rack_cors_config_exist = File.exist?("config/initializers/cors.rb")
- assets_config_exist = File.exist?("config/initializers/assets.rb")
+ action_cable_config_exist = File.exist?("config/cable.yml")
+ active_storage_config_exist = File.exist?("config/storage.yml")
+ rack_cors_config_exist = File.exist?("config/initializers/cors.rb")
+ assets_config_exist = File.exist?("config/initializers/assets.rb")
config
@@ -136,6 +140,10 @@ module Rails
template "config/cable.yml"
end
+ if !active_storage_config_exist
+ template "config/storage.yml"
+ end
+
unless rack_cors_config_exist
remove_file "config/initializers/cors.rb"
end
@@ -173,6 +181,11 @@ module Rails
directory "public", "public", recursive: false
end
+ def storage
+ empty_directory_with_keep_file "storage"
+ empty_directory_with_keep_file "tmp/storage"
+ end
+
def test
empty_directory_with_keep_file "test/fixtures"
empty_directory_with_keep_file "test/fixtures/files"
@@ -384,7 +397,6 @@ module Rails
def delete_action_cable_files_skipping_action_cable
if options[:skip_action_cable]
- remove_file "config/cable.yml"
remove_file "app/assets/javascripts/cable.js"
remove_dir "app/channels"
end
diff --git a/railties/lib/rails/generators/rails/app/templates/Rakefile b/railties/lib/rails/generators/rails/app/templates/Rakefile
index e85f913914..488c551fee 100644
--- a/railties/lib/rails/generators/rails/app/templates/Rakefile
+++ b/railties/lib/rails/generators/rails/app/templates/Rakefile
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Add your own tasks in files placed in lib/tasks ending in .rake,
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
diff --git a/railties/lib/rails/generators/rails/app/templates/app/assets/javascripts/application.js.tt b/railties/lib/rails/generators/rails/app/templates/app/assets/javascripts/application.js.tt
index 4206002a1b..62fd04f113 100644
--- a/railties/lib/rails/generators/rails/app/templates/app/assets/javascripts/application.js.tt
+++ b/railties/lib/rails/generators/rails/app/templates/app/assets/javascripts/application.js.tt
@@ -12,6 +12,7 @@
//
<% unless options[:skip_javascript] -%>
//= require rails-ujs
+//= require activestorage
<% unless options[:skip_turbolinks] -%>
//= require turbolinks
<% end -%>
diff --git a/railties/lib/rails/generators/rails/app/templates/app/channels/application_cable/channel.rb b/railties/lib/rails/generators/rails/app/templates/app/channels/application_cable/channel.rb
index d672697283..9aec230539 100644
--- a/railties/lib/rails/generators/rails/app/templates/app/channels/application_cable/channel.rb
+++ b/railties/lib/rails/generators/rails/app/templates/app/channels/application_cable/channel.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ApplicationCable
class Channel < ActionCable::Channel::Base
end
diff --git a/railties/lib/rails/generators/rails/app/templates/app/channels/application_cable/connection.rb b/railties/lib/rails/generators/rails/app/templates/app/channels/application_cable/connection.rb
index 0ff5442f47..8d6c2a1bf4 100644
--- a/railties/lib/rails/generators/rails/app/templates/app/channels/application_cable/connection.rb
+++ b/railties/lib/rails/generators/rails/app/templates/app/channels/application_cable/connection.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ApplicationCable
class Connection < ActionCable::Connection::Base
end
diff --git a/railties/lib/rails/generators/rails/app/templates/app/controllers/application_controller.rb.tt b/railties/lib/rails/generators/rails/app/templates/app/controllers/application_controller.rb.tt
index 185c0017f1..938eff8ed0 100644
--- a/railties/lib/rails/generators/rails/app/templates/app/controllers/application_controller.rb.tt
+++ b/railties/lib/rails/generators/rails/app/templates/app/controllers/application_controller.rb.tt
@@ -1,2 +1,2 @@
-class ApplicationController < ActionController::<%= options[:api] ? "API" : "Base" %>
+class ApplicationController < ActionController::<%= options.api? ? "API" : "Base" %>
end
diff --git a/railties/lib/rails/generators/rails/app/templates/app/helpers/application_helper.rb b/railties/lib/rails/generators/rails/app/templates/app/helpers/application_helper.rb
index de6be7945c..15b06f0f67 100644
--- a/railties/lib/rails/generators/rails/app/templates/app/helpers/application_helper.rb
+++ b/railties/lib/rails/generators/rails/app/templates/app/helpers/application_helper.rb
@@ -1,2 +1,4 @@
+# frozen_string_literal: true
+
module ApplicationHelper
end
diff --git a/railties/lib/rails/generators/rails/app/templates/app/jobs/application_job.rb b/railties/lib/rails/generators/rails/app/templates/app/jobs/application_job.rb
index a009ace51c..d92ffddcb5 100644
--- a/railties/lib/rails/generators/rails/app/templates/app/jobs/application_job.rb
+++ b/railties/lib/rails/generators/rails/app/templates/app/jobs/application_job.rb
@@ -1,2 +1,4 @@
+# frozen_string_literal: true
+
class ApplicationJob < ActiveJob::Base
end
diff --git a/railties/lib/rails/generators/rails/app/templates/app/mailers/application_mailer.rb b/railties/lib/rails/generators/rails/app/templates/app/mailers/application_mailer.rb
index 286b2239d1..d84cb6e71e 100644
--- a/railties/lib/rails/generators/rails/app/templates/app/mailers/application_mailer.rb
+++ b/railties/lib/rails/generators/rails/app/templates/app/mailers/application_mailer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ApplicationMailer < ActionMailer::Base
default from: 'from@example.com'
layout 'mailer'
diff --git a/railties/lib/rails/generators/rails/app/templates/app/models/application_record.rb b/railties/lib/rails/generators/rails/app/templates/app/models/application_record.rb
index 10a4cba84d..71fbba5b32 100644
--- a/railties/lib/rails/generators/rails/app/templates/app/models/application_record.rb
+++ b/railties/lib/rails/generators/rails/app/templates/app/models/application_record.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ApplicationRecord < ActiveRecord::Base
self.abstract_class = true
end
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 a53d1fb0a9..233b5a1d95 100644
--- a/railties/lib/rails/generators/rails/app/templates/bin/setup.tt
+++ b/railties/lib/rails/generators/rails/app/templates/bin/setup.tt
@@ -15,12 +15,12 @@ chdir APP_ROOT do
puts '== Installing dependencies =='
system! 'gem install bundler --conservative'
system('bundle check') || system!('bundle install')
-<% unless options[:skip_yarn] -%>
+<% unless options.skip_yarn? -%>
# Install JavaScript dependencies if using Yarn
# system('bin/yarn')
<% end -%>
-<% unless options.skip_active_record -%>
+<% unless options.skip_active_record? -%>
# puts "\n== Copying sample files =="
# unless File.exist?('config/database.yml')
diff --git a/railties/lib/rails/generators/rails/app/templates/bin/update.tt b/railties/lib/rails/generators/rails/app/templates/bin/update.tt
index 5b6e50883e..d744bec32f 100644
--- a/railties/lib/rails/generators/rails/app/templates/bin/update.tt
+++ b/railties/lib/rails/generators/rails/app/templates/bin/update.tt
@@ -15,7 +15,7 @@ chdir APP_ROOT do
puts '== Installing dependencies =='
system! 'gem install bundler --conservative'
system('bundle check') || system!('bundle install')
-<% unless options.skip_active_record -%>
+<% unless options.skip_active_record? -%>
puts "\n== Updating database =="
system! 'bin/rails db:migrate'
diff --git a/railties/lib/rails/generators/rails/app/templates/config/application.rb b/railties/lib/rails/generators/rails/app/templates/config/application.rb
index 0b1d22228e..eec973caa5 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/application.rb
+++ b/railties/lib/rails/generators/rails/app/templates/config/application.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_relative 'boot'
<% if include_all_railties? -%>
@@ -11,6 +13,7 @@ require "active_job/railtie"
require "action_controller/railtie"
<%= comment_if :skip_action_mailer %>require "action_mailer/railtie"
require "action_view/railtie"
+require "active_storage/engine"
<%= comment_if :skip_action_cable %>require "action_cable/engine"
<%= comment_if :skip_sprockets %>require "sprockets/railtie"
<%= comment_if :skip_test %>require "rails/test_unit/railtie"
@@ -28,7 +31,7 @@ module <%= app_const_base %>
# Settings in config/environments/* take precedence over those specified here.
# Application configuration should go into files in config/initializers
# -- all .rb files in that directory are automatically loaded.
-<%- if options[:api] -%>
+<%- if options.api? -%>
# Only loads a smaller set of middleware suitable for API only apps.
# Middleware like session, flash, cookies can be added back manually.
diff --git a/railties/lib/rails/generators/rails/app/templates/config/boot.rb b/railties/lib/rails/generators/rails/app/templates/config/boot.rb
index b9e460cef3..c04863fa7d 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/boot.rb
+++ b/railties/lib/rails/generators/rails/app/templates/config/boot.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
require 'bundler/setup' # Set up gems listed in the Gemfile.
diff --git a/railties/lib/rails/generators/rails/app/templates/config/environment.rb b/railties/lib/rails/generators/rails/app/templates/config/environment.rb
index 426333bb46..d5abe55806 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/environment.rb
+++ b/railties/lib/rails/generators/rails/app/templates/config/environment.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Load the Rails application.
require_relative 'application'
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 b75b65c8df..98689cc30d 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
@@ -19,13 +19,16 @@ Rails.application.configure do
config.cache_store = :memory_store
config.public_file_server.headers = {
- 'Cache-Control' => "public, max-age=#{2.days.seconds.to_i}"
+ 'Cache-Control' => "public, max-age=#{2.days.to_i}"
}
else
config.action_controller.perform_caching = false
config.cache_store = :null_store
end
+
+ # Store uploaded files on the local file system (see config/storage.yml for options)
+ config.active_storage.service = :local
<%- unless options.skip_action_mailer? -%>
# Don't care if the mailer can't send.
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 d44331a888..f68e13aa8b 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
@@ -45,6 +45,9 @@ Rails.application.configure do
# config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache
# config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX
+ # Store uploaded files on the local file system (see config/storage.yml for options)
+ config.active_storage.service = :local
+
<%- unless options[:skip_action_cable] -%>
# Mount Action Cable outside main process or domain
# config.action_cable.mount_path = nil
diff --git a/railties/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt
index 56416b3075..a53978bc6e 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt
+++ b/railties/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt
@@ -15,7 +15,7 @@ Rails.application.configure do
# Configure public file server for tests with Cache-Control for performance.
config.public_file_server.enabled = true
config.public_file_server.headers = {
- 'Cache-Control' => "public, max-age=#{1.hour.seconds.to_i}"
+ 'Cache-Control' => "public, max-age=#{1.hour.to_i}"
}
# Show full error reports and disable caching.
@@ -27,6 +27,9 @@ Rails.application.configure do
# Disable request forgery protection in test environment.
config.action_controller.allow_forgery_protection = false
+
+ # Store uploaded files on the local file system in a temporary directory
+ config.active_storage.service = :test
<%- unless options.skip_action_mailer? -%>
config.action_mailer.perform_caching = false
diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/application_controller_renderer.rb b/railties/lib/rails/generators/rails/app/templates/config/initializers/application_controller_renderer.rb
index 51639b67a0..6d56e43900 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/initializers/application_controller_renderer.rb
+++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/application_controller_renderer.rb
@@ -1,6 +1,10 @@
+# frozen_string_literal: true
+
# Be sure to restart your server when you modify this file.
-# ApplicationController.renderer.defaults.merge!(
-# http_host: 'example.org',
-# https: false
-# )
+# ActiveSupport::Reloader.to_prepare do
+# ApplicationController.renderer.defaults.merge!(
+# http_host: 'example.org',
+# https: false
+# )
+# end
diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/backtrace_silencers.rb b/railties/lib/rails/generators/rails/app/templates/config/initializers/backtrace_silencers.rb
index 59385cdf37..4b63f2893d 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/initializers/backtrace_silencers.rb
+++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/backtrace_silencers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Be sure to restart your server when you modify this file.
# You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.
diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/cookies_serializer.rb b/railties/lib/rails/generators/rails/app/templates/config/initializers/cookies_serializer.rb
index 5a6a32d371..ee8dff9c99 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/initializers/cookies_serializer.rb
+++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/cookies_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Be sure to restart your server when you modify this file.
# Specify a serializer for the signed and encrypted cookie jars.
diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/cors.rb b/railties/lib/rails/generators/rails/app/templates/config/initializers/cors.rb
index 3b1c1b5ed1..82eafe5ca4 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/initializers/cors.rb
+++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/cors.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Be sure to restart your server when you modify this file.
# Avoid CORS issues when API is called from the frontend app.
diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/filter_parameter_logging.rb b/railties/lib/rails/generators/rails/app/templates/config/initializers/filter_parameter_logging.rb
index 4a994e1e7b..7a4f47b4c2 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/initializers/filter_parameter_logging.rb
+++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/filter_parameter_logging.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Be sure to restart your server when you modify this file.
# Configure sensitive parameters which will be filtered from the log file.
diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/inflections.rb b/railties/lib/rails/generators/rails/app/templates/config/initializers/inflections.rb
index ac033bf9dc..dc84742212 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/initializers/inflections.rb
+++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/inflections.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Be sure to restart your server when you modify this file.
# Add new inflection rules using the following format. Inflections
diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/mime_types.rb b/railties/lib/rails/generators/rails/app/templates/config/initializers/mime_types.rb
index dc1899682b..be6fedc535 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/initializers/mime_types.rb
+++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/mime_types.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Be sure to restart your server when you modify this file.
# Add new mime types for use in respond_to blocks:
diff --git a/railties/lib/rails/generators/rails/app/templates/config/puma.rb b/railties/lib/rails/generators/rails/app/templates/config/puma.rb
index 1e19380dcb..55bd3da2a7 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/puma.rb
+++ b/railties/lib/rails/generators/rails/app/templates/config/puma.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Puma can serve each request in a thread from an internal thread pool.
# The `threads` method setting takes two numbers: a minimum and maximum.
# Any libraries that use thread pools should be configured to match
diff --git a/railties/lib/rails/generators/rails/app/templates/config/routes.rb b/railties/lib/rails/generators/rails/app/templates/config/routes.rb
index 787824f888..47cc16ea2f 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/routes.rb
+++ b/railties/lib/rails/generators/rails/app/templates/config/routes.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
Rails.application.routes.draw do
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
end
diff --git a/railties/lib/rails/generators/rails/app/templates/config/spring.rb b/railties/lib/rails/generators/rails/app/templates/config/spring.rb
index 9fa7863f99..c5933e491b 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/spring.rb
+++ b/railties/lib/rails/generators/rails/app/templates/config/spring.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
%w[
.ruby-version
.rbenv-vars
diff --git a/railties/lib/rails/generators/rails/app/templates/config/storage.yml b/railties/lib/rails/generators/rails/app/templates/config/storage.yml
new file mode 100644
index 0000000000..089ed4567a
--- /dev/null
+++ b/railties/lib/rails/generators/rails/app/templates/config/storage.yml
@@ -0,0 +1,35 @@
+test:
+ service: Disk
+ root: <%%= Rails.root.join("tmp/storage") %>
+
+local:
+ service: Disk
+ root: <%%= Rails.root.join("storage") %>
+
+# Use rails secrets:edit to set the AWS secrets (as shared:aws:access_key_id|secret_access_key)
+# amazon:
+# service: S3
+# access_key_id: <%%= Rails.application.secrets.dig(:aws, :access_key_id) %>
+# secret_access_key: <%%= Rails.application.secrets.dig(:aws, :secret_access_key) %>
+# region: us-east-1
+# bucket: your_own_bucket
+
+# Remember not to checkin your GCS keyfile to a repository
+# google:
+# service: GCS
+# project: your_project
+# keyfile: <%%= Rails.root.join("path/to/gcs.keyfile") %>
+# bucket: your_own_bucket
+
+# Use rails secrets:edit to set the Azure Storage secret (as shared:azure_storage:storage_access_key)
+# microsoft:
+# service: AzureStorage
+# path: your_azure_storage_path
+# storage_account_name: your_account_name
+# storage_access_key: <%%= Rails.application.secrets.dig(:azure_storage, :storage_access_key) %>
+# container: your_container_name
+
+# mirror:
+# service: Mirror
+# primary: local
+# mirrors: [ amazon, google, microsoft ]
diff --git a/railties/lib/rails/generators/rails/app/templates/gitignore b/railties/lib/rails/generators/rails/app/templates/gitignore
index 1e6b9afcd2..83a7b211aa 100644
--- a/railties/lib/rails/generators/rails/app/templates/gitignore
+++ b/railties/lib/rails/generators/rails/app/templates/gitignore
@@ -21,13 +21,15 @@
!/tmp/.keep
<% end -%>
-<% unless options[:skip_yarn] -%>
+# Ignore uploaded files in development
+/storage/*
+
+<% unless options.skip_yarn? -%>
/node_modules
/yarn-error.log
-
<% end -%>
-<% unless options[:api] -%>
+<% unless options.api? -%>
/public/assets
<% end -%>
.byebug_history
diff --git a/railties/lib/rails/generators/rails/app/templates/test/application_system_test_case.rb b/railties/lib/rails/generators/rails/app/templates/test/application_system_test_case.rb
index d19212abd5..c05709aff8 100644
--- a/railties/lib/rails/generators/rails/app/templates/test/application_system_test_case.rb
+++ b/railties/lib/rails/generators/rails/app/templates/test/application_system_test_case.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "test_helper"
class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
diff --git a/railties/lib/rails/generators/rails/app/templates/test/test_helper.rb b/railties/lib/rails/generators/rails/app/templates/test/test_helper.rb
index 6ad1f11781..b64e90522a 100644
--- a/railties/lib/rails/generators/rails/app/templates/test/test_helper.rb
+++ b/railties/lib/rails/generators/rails/app/templates/test/test_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_relative '../config/environment'
require 'rails/test_help'
diff --git a/railties/lib/rails/generators/rails/assets/assets_generator.rb b/railties/lib/rails/generators/rails/assets/assets_generator.rb
index 95d00c2d39..ffb695a1f3 100644
--- a/railties/lib/rails/generators/rails/assets/assets_generator.rb
+++ b/railties/lib/rails/generators/rails/assets/assets_generator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Rails
module Generators
class AssetsGenerator < NamedBase # :nodoc:
diff --git a/railties/lib/rails/generators/rails/controller/controller_generator.rb b/railties/lib/rails/generators/rails/controller/controller_generator.rb
index 06bdb8b5ce..6d45d6e8f8 100644
--- a/railties/lib/rails/generators/rails/controller/controller_generator.rb
+++ b/railties/lib/rails/generators/rails/controller/controller_generator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Rails
module Generators
class ControllerGenerator < NamedBase # :nodoc:
@@ -13,12 +15,8 @@ module Rails
end
def add_routes
- unless options[:skip_routes]
- actions.reverse_each do |action|
- # route prepends two spaces onto the front of the string that is passed, this corrects that.
- route indent(generate_routing_code(action), 2)[2..-1]
- end
- end
+ return if options[:skip_routes]
+ route generate_routing_code
end
hook_for :template_engine, :test_framework, :helper, :assets
@@ -26,14 +24,15 @@ module Rails
private
# This method creates nested route entry for namespaced resources.
- # For eg. rails g controller foo/bar/baz index
+ # For eg. rails g controller foo/bar/baz index show
# Will generate -
# namespace :foo do
# namespace :bar do
# get 'baz/index'
+ # get 'baz/show'
# end
# end
- def generate_routing_code(action)
+ def generate_routing_code
depth = 0
lines = []
@@ -47,7 +46,10 @@ module Rails
# Create route
# get 'baz/index'
- lines << indent(%{get '#{file_name}/#{action}'\n}, depth * 2)
+ # get 'baz/show'
+ actions.each do |action|
+ lines << indent(%{get '#{file_name}/#{action}'\n}, depth * 2)
+ end
# Create `end` ladder
# end
diff --git a/railties/lib/rails/generators/rails/controller/templates/controller.rb b/railties/lib/rails/generators/rails/controller/templates/controller.rb
index 633e0b3177..835b2923f3 100644
--- a/railties/lib/rails/generators/rails/controller/templates/controller.rb
+++ b/railties/lib/rails/generators/rails/controller/templates/controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
<% if namespaced? -%>
require_dependency "<%= namespaced_path %>/application_controller"
diff --git a/railties/lib/rails/generators/rails/encrypted_secrets/encrypted_secrets_generator.rb b/railties/lib/rails/generators/rails/encrypted_secrets/encrypted_secrets_generator.rb
index 8ba0fc4c5b..d054e8cad2 100644
--- a/railties/lib/rails/generators/rails/encrypted_secrets/encrypted_secrets_generator.rb
+++ b/railties/lib/rails/generators/rails/encrypted_secrets/encrypted_secrets_generator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_relative "../../base"
require_relative "../../../secrets"
diff --git a/railties/lib/rails/generators/rails/generator/generator_generator.rb b/railties/lib/rails/generators/rails/generator/generator_generator.rb
index 299a7da5f1..747acd68d1 100644
--- a/railties/lib/rails/generators/rails/generator/generator_generator.rb
+++ b/railties/lib/rails/generators/rails/generator/generator_generator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Rails
module Generators
class GeneratorGenerator < NamedBase # :nodoc:
diff --git a/railties/lib/rails/generators/rails/helper/helper_generator.rb b/railties/lib/rails/generators/rails/helper/helper_generator.rb
index e48b1b6fb3..3837c10ca0 100644
--- a/railties/lib/rails/generators/rails/helper/helper_generator.rb
+++ b/railties/lib/rails/generators/rails/helper/helper_generator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Rails
module Generators
class HelperGenerator < NamedBase # :nodoc:
diff --git a/railties/lib/rails/generators/rails/helper/templates/helper.rb b/railties/lib/rails/generators/rails/helper/templates/helper.rb
index b4173151b4..538ab221e7 100644
--- a/railties/lib/rails/generators/rails/helper/templates/helper.rb
+++ b/railties/lib/rails/generators/rails/helper/templates/helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
<% module_namespacing do -%>
module <%= class_name %>Helper
end
diff --git a/railties/lib/rails/generators/rails/integration_test/integration_test_generator.rb b/railties/lib/rails/generators/rails/integration_test/integration_test_generator.rb
index 70770ddcb8..975dd8b90c 100644
--- a/railties/lib/rails/generators/rails/integration_test/integration_test_generator.rb
+++ b/railties/lib/rails/generators/rails/integration_test/integration_test_generator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Rails
module Generators
class IntegrationTestGenerator < NamedBase # :nodoc:
diff --git a/railties/lib/rails/generators/rails/migration/migration_generator.rb b/railties/lib/rails/generators/rails/migration/migration_generator.rb
index fca2a8fef4..c331c135e3 100644
--- a/railties/lib/rails/generators/rails/migration/migration_generator.rb
+++ b/railties/lib/rails/generators/rails/migration/migration_generator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Rails
module Generators
class MigrationGenerator < NamedBase # :nodoc:
diff --git a/railties/lib/rails/generators/rails/model/model_generator.rb b/railties/lib/rails/generators/rails/model/model_generator.rb
index 8c8aac54bf..1dca03e0bb 100644
--- a/railties/lib/rails/generators/rails/model/model_generator.rb
+++ b/railties/lib/rails/generators/rails/model/model_generator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_relative "../../model_helpers"
module Rails
diff --git a/railties/lib/rails/generators/rails/plugin/plugin_generator.rb b/railties/lib/rails/generators/rails/plugin/plugin_generator.rb
index 61c54b4222..eb941adf95 100644
--- a/railties/lib/rails/generators/rails/plugin/plugin_generator.rb
+++ b/railties/lib/rails/generators/rails/plugin/plugin_generator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "active_support/core_ext/hash/slice"
require_relative "../app/app_generator"
require "date"
@@ -76,8 +78,8 @@ module Rails
template "test/test_helper.rb"
template "test/%namespaced_name%_test.rb"
append_file "Rakefile", <<-EOF
-#{rakefile_test_tasks}
+#{rakefile_test_tasks}
task default: :test
EOF
if engine?
@@ -86,15 +88,14 @@ task default: :test
end
PASSTHROUGH_OPTIONS = [
- :skip_active_record, :skip_action_mailer, :skip_javascript, :skip_sprockets, :database,
- :javascript, :quiet, :pretend, :force, :skip
+ :skip_active_record, :skip_action_mailer, :skip_javascript, :skip_action_cable, :skip_sprockets, :database,
+ :javascript, :skip_yarn, :api, :quiet, :pretend, :skip
]
def generate_test_dummy(force = false)
opts = (options || {}).slice(*PASSTHROUGH_OPTIONS)
opts[:force] = force
opts[:skip_bundle] = true
- opts[:api] = options.api?
opts[:skip_listen] = true
opts[:skip_git] = true
opts[:skip_turbolinks] = true
diff --git a/railties/lib/rails/generators/rails/plugin/templates/Gemfile b/railties/lib/rails/generators/rails/plugin/templates/Gemfile
index 22a4548ff2..290259b4db 100644
--- a/railties/lib/rails/generators/rails/plugin/templates/Gemfile
+++ b/railties/lib/rails/generators/rails/plugin/templates/Gemfile
@@ -1,4 +1,5 @@
source 'https://rubygems.org'
+git_source(:github) { |repo| "https://github.com/#{repo}.git" }
<% if options[:skip_gemspec] -%>
<%= '# ' if options.dev? || options.edge? -%>gem 'rails', '<%= Array(rails_version_specifier).join("', '") %>'
diff --git a/railties/lib/rails/generators/rails/plugin/templates/Rakefile b/railties/lib/rails/generators/rails/plugin/templates/Rakefile
index 3581dd401a..86307fa053 100644
--- a/railties/lib/rails/generators/rails/plugin/templates/Rakefile
+++ b/railties/lib/rails/generators/rails/plugin/templates/Rakefile
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
begin
require 'bundler/setup'
rescue LoadError
@@ -13,17 +15,16 @@ RDoc::Task.new(:rdoc) do |rdoc|
rdoc.rdoc_files.include('README.md')
rdoc.rdoc_files.include('lib/**/*.rb')
end
-
<% if engine? && !options[:skip_active_record] && with_dummy_app? -%>
+
APP_RAKEFILE = File.expand_path("<%= dummy_path -%>/Rakefile", __dir__)
load 'rails/tasks/engine.rake'
-<% end %>
-
+<% end -%>
<% if engine? -%>
-load 'rails/tasks/statistics.rake'
-<% end %>
+load 'rails/tasks/statistics.rake'
+<% end -%>
<% unless options[:skip_gemspec] -%>
require 'bundler/gem_tasks'
-<% end %>
+<% end -%>
diff --git a/railties/lib/rails/generators/rails/plugin/templates/bin/rails.tt b/railties/lib/rails/generators/rails/plugin/templates/bin/rails.tt
index ffa277e334..b3264509fc 100644
--- a/railties/lib/rails/generators/rails/plugin/templates/bin/rails.tt
+++ b/railties/lib/rails/generators/rails/plugin/templates/bin/rails.tt
@@ -3,11 +3,28 @@
ENGINE_ROOT = File.expand_path('..', __dir__)
ENGINE_PATH = File.expand_path('../lib/<%= namespaced_name -%>/engine', __dir__)
+<% if with_dummy_app? -%>
APP_PATH = File.expand_path('../<%= dummy_path -%>/config/application', __dir__)
+<% end -%>
# Set up gems listed in the Gemfile.
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
+<% if include_all_railties? -%>
require 'rails/all'
+<% else -%>
+require "rails"
+# Pick the frameworks you want:
+require "active_model/railtie"
+require "active_job/railtie"
+<%= comment_if :skip_active_record %>require "active_record/railtie"
+require "action_controller/railtie"
+<%= comment_if :skip_action_mailer %>require "action_mailer/railtie"
+require "action_view/railtie"
+require "active_storage/engine"
+<%= comment_if :skip_action_cable %>require "action_cable/engine"
+<%= comment_if :skip_sprockets %>require "sprockets/railtie"
+<%= comment_if :skip_test %>require "rails/test_unit/railtie"
+<% end -%>
require 'rails/engine/commands'
diff --git a/railties/lib/rails/generators/rails/plugin/templates/config/routes.rb b/railties/lib/rails/generators/rails/plugin/templates/config/routes.rb
index 154452bfe5..791e76f1c3 100644
--- a/railties/lib/rails/generators/rails/plugin/templates/config/routes.rb
+++ b/railties/lib/rails/generators/rails/plugin/templates/config/routes.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
<% if mountable? -%>
<%= camelized_modules %>::Engine.routes.draw do
<% else -%>
diff --git a/railties/lib/rails/generators/rails/plugin/templates/gitignore b/railties/lib/rails/generators/rails/plugin/templates/gitignore
index 54c78d7927..8c7cad74ed 100644
--- a/railties/lib/rails/generators/rails/plugin/templates/gitignore
+++ b/railties/lib/rails/generators/rails/plugin/templates/gitignore
@@ -1,9 +1,13 @@
.bundle/
log/*.log
pkg/
-<% unless options[:skip_test] && options[:dummy_path] == 'test/dummy' -%>
+<% if with_dummy_app? -%>
<%= dummy_path %>/db/*.sqlite3
<%= dummy_path %>/db/*.sqlite3-journal
<%= dummy_path %>/log/*.log
+<% unless options[:skip_yarn] -%>
+<%= dummy_path %>/node_modules/
+<%= dummy_path %>/yarn-error.log
+<% end -%>
<%= dummy_path %>/tmp/
<% end -%>
diff --git a/railties/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%.rb b/railties/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%.rb
index 3285055eb7..a3071ba45a 100644
--- a/railties/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%.rb
+++ b/railties/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
<% if engine? -%>
require "<%= namespaced_name %>/engine"
<% else -%>
diff --git a/railties/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%/engine.rb b/railties/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%/engine.rb
index 8938770fc4..91faa26120 100644
--- a/railties/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%/engine.rb
+++ b/railties/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%/engine.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
<%= wrap_in_modules <<-rb.strip_heredoc
class Engine < ::Rails::Engine
#{mountable? ? ' isolate_namespace ' + camelized_modules : ' '}
diff --git a/railties/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%/railtie.rb b/railties/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%/railtie.rb
index 7bdf4ee5fb..efae25ca4a 100644
--- a/railties/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%/railtie.rb
+++ b/railties/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%/railtie.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
<%= wrap_in_modules <<-rb.strip_heredoc
class Railtie < ::Rails::Railtie
end
diff --git a/railties/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%/version.rb b/railties/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%/version.rb
index b08f4ef9ae..c30d3460f3 100644
--- a/railties/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%/version.rb
+++ b/railties/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%/version.rb
@@ -1 +1,3 @@
+# frozen_string_literal: true
+
<%= wrap_in_modules "VERSION = '0.1.0'" %>
diff --git a/railties/lib/rails/generators/rails/plugin/templates/lib/tasks/%namespaced_name%_tasks.rake b/railties/lib/rails/generators/rails/plugin/templates/lib/tasks/%namespaced_name%_tasks.rake
index 88a2c4120f..9c8c0544b7 100644
--- a/railties/lib/rails/generators/rails/plugin/templates/lib/tasks/%namespaced_name%_tasks.rake
+++ b/railties/lib/rails/generators/rails/plugin/templates/lib/tasks/%namespaced_name%_tasks.rake
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# desc "Explaining what the task does"
# task :<%= underscored_name %> do
# # Task goes here
diff --git a/railties/lib/rails/generators/rails/plugin/templates/rails/application.rb b/railties/lib/rails/generators/rails/plugin/templates/rails/application.rb
index d03b1be878..9f9da645f9 100644
--- a/railties/lib/rails/generators/rails/plugin/templates/rails/application.rb
+++ b/railties/lib/rails/generators/rails/plugin/templates/rails/application.rb
@@ -1,17 +1,22 @@
+# frozen_string_literal: true
+
require_relative 'boot'
<% if include_all_railties? -%>
require 'rails/all'
<% else -%>
+require "rails"
# Pick the frameworks you want:
+require "active_model/railtie"
+require "active_job/railtie"
<%= comment_if :skip_active_record %>require "active_record/railtie"
require "action_controller/railtie"
-require "action_view/railtie"
<%= comment_if :skip_action_mailer %>require "action_mailer/railtie"
-require "active_job/railtie"
+require "action_view/railtie"
+require "active_storage/engine"
<%= comment_if :skip_action_cable %>require "action_cable/engine"
-<%= comment_if :skip_test %>require "rails/test_unit/railtie"
<%= comment_if :skip_sprockets %>require "sprockets/railtie"
+<%= comment_if :skip_test %>require "rails/test_unit/railtie"
<% end -%>
Bundler.require(*Rails.groups)
diff --git a/railties/lib/rails/generators/rails/plugin/templates/rails/boot.rb b/railties/lib/rails/generators/rails/plugin/templates/rails/boot.rb
index c9aef85d40..6d2cba07a2 100644
--- a/railties/lib/rails/generators/rails/plugin/templates/rails/boot.rb
+++ b/railties/lib/rails/generators/rails/plugin/templates/rails/boot.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Set up gems listed in the Gemfile.
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../../Gemfile', __dir__)
diff --git a/railties/lib/rails/generators/rails/plugin/templates/rails/dummy_manifest.js b/railties/lib/rails/generators/rails/plugin/templates/rails/dummy_manifest.js
index 8d21b2b6fb..03937cf8ff 100644
--- a/railties/lib/rails/generators/rails/plugin/templates/rails/dummy_manifest.js
+++ b/railties/lib/rails/generators/rails/plugin/templates/rails/dummy_manifest.js
@@ -1,4 +1,3 @@
-
<% unless api? -%>
//= link_tree ../images
<% end -%>
diff --git a/railties/lib/rails/generators/rails/plugin/templates/rails/routes.rb b/railties/lib/rails/generators/rails/plugin/templates/rails/routes.rb
index 694510edc0..f571f201a1 100644
--- a/railties/lib/rails/generators/rails/plugin/templates/rails/routes.rb
+++ b/railties/lib/rails/generators/rails/plugin/templates/rails/routes.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
Rails.application.routes.draw do
mount <%= camelized_modules %>::Engine => "/<%= name %>"
end
diff --git a/railties/lib/rails/generators/rails/plugin/templates/test/%namespaced_name%_test.rb b/railties/lib/rails/generators/rails/plugin/templates/test/%namespaced_name%_test.rb
index 1ee05d7871..f37369420f 100644
--- a/railties/lib/rails/generators/rails/plugin/templates/test/%namespaced_name%_test.rb
+++ b/railties/lib/rails/generators/rails/plugin/templates/test/%namespaced_name%_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'test_helper'
class <%= camelized_modules %>::Test < ActiveSupport::TestCase
diff --git a/railties/lib/rails/generators/rails/plugin/templates/test/application_system_test_case.rb b/railties/lib/rails/generators/rails/plugin/templates/test/application_system_test_case.rb
index d19212abd5..c05709aff8 100644
--- a/railties/lib/rails/generators/rails/plugin/templates/test/application_system_test_case.rb
+++ b/railties/lib/rails/generators/rails/plugin/templates/test/application_system_test_case.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "test_helper"
class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
diff --git a/railties/lib/rails/generators/rails/plugin/templates/test/integration/navigation_test.rb b/railties/lib/rails/generators/rails/plugin/templates/test/integration/navigation_test.rb
index f5d1ec2046..dc58e44e8b 100644
--- a/railties/lib/rails/generators/rails/plugin/templates/test/integration/navigation_test.rb
+++ b/railties/lib/rails/generators/rails/plugin/templates/test/integration/navigation_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'test_helper'
class NavigationTest < ActionDispatch::IntegrationTest
diff --git a/railties/lib/rails/generators/rails/plugin/templates/test/test_helper.rb b/railties/lib/rails/generators/rails/plugin/templates/test/test_helper.rb
index c281fc42ca..a0bab22764 100644
--- a/railties/lib/rails/generators/rails/plugin/templates/test/test_helper.rb
+++ b/railties/lib/rails/generators/rails/plugin/templates/test/test_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_relative "<%= File.join('..', options[:dummy_path], 'config/environment') -%>"
<% unless options[:skip_active_record] -%>
ActiveRecord::Migrator.migrations_paths = [File.expand_path("../<%= options[:dummy_path] -%>/db/migrate", __dir__)]
diff --git a/railties/lib/rails/generators/rails/resource/resource_generator.rb b/railties/lib/rails/generators/rails/resource/resource_generator.rb
index fa700c598a..74b1574863 100644
--- a/railties/lib/rails/generators/rails/resource/resource_generator.rb
+++ b/railties/lib/rails/generators/rails/resource/resource_generator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_relative "../../resource_helpers"
require_relative "../model/model_generator"
diff --git a/railties/lib/rails/generators/rails/resource_route/resource_route_generator.rb b/railties/lib/rails/generators/rails/resource_route/resource_route_generator.rb
index 42705107ae..9a92991efe 100644
--- a/railties/lib/rails/generators/rails/resource_route/resource_route_generator.rb
+++ b/railties/lib/rails/generators/rails/resource_route/resource_route_generator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Rails
module Generators
class ResourceRouteGenerator < NamedBase # :nodoc:
@@ -15,37 +17,32 @@ module Rails
def add_resource_route
return if options[:actions].present?
- # iterates over all namespaces and opens up blocks
- regular_class_path.each_with_index do |namespace, index|
- write("namespace :#{namespace} do", index + 1)
+ depth = 0
+ lines = []
+
+ # Create 'namespace' ladder
+ # namespace :foo do
+ # namespace :bar do
+ regular_class_path.each do |ns|
+ lines << indent("namespace :#{ns} do\n", depth * 2)
+ depth += 1
end
# inserts the primary resource
- write("resources :#{file_name.pluralize}", route_length + 1)
+ # Create route
+ # resources 'products'
+ lines << indent(%{resources :#{file_name.pluralize}\n}, depth * 2)
- # ends blocks
- regular_class_path.each_index do |index|
- write("end", route_length - index)
+ # Create `end` ladder
+ # end
+ # end
+ until depth.zero?
+ depth -= 1
+ lines << indent("end\n", depth * 2)
end
- # route prepends two spaces onto the front of the string that is passed, this corrects that.
- # Also it adds a \n to the end of each line, as route already adds that
- # we need to correct that too.
- route route_string[2..-2]
+ route lines.join
end
-
- private
- def route_string
- @route_string ||= ""
- end
-
- def write(str, indent)
- route_string << "#{" " * indent}#{str}\n"
- end
-
- def route_length
- regular_class_path.length
- end
end
end
end
diff --git a/railties/lib/rails/generators/rails/scaffold/scaffold_generator.rb b/railties/lib/rails/generators/rails/scaffold/scaffold_generator.rb
index 3f92af4295..c0fb873d36 100644
--- a/railties/lib/rails/generators/rails/scaffold/scaffold_generator.rb
+++ b/railties/lib/rails/generators/rails/scaffold/scaffold_generator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_relative "../resource/resource_generator"
module Rails
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 291a208498..601a4b3a6e 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
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_relative "../../resource_helpers"
module Rails
diff --git a/railties/lib/rails/generators/rails/scaffold_controller/templates/api_controller.rb b/railties/lib/rails/generators/rails/scaffold_controller/templates/api_controller.rb
index 400afec6dc..50fae26c80 100644
--- a/railties/lib/rails/generators/rails/scaffold_controller/templates/api_controller.rb
+++ b/railties/lib/rails/generators/rails/scaffold_controller/templates/api_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
<% if namespaced? -%>
require_dependency "<%= namespaced_path %>/application_controller"
diff --git a/railties/lib/rails/generators/rails/scaffold_controller/templates/controller.rb b/railties/lib/rails/generators/rails/scaffold_controller/templates/controller.rb
index 42b9e34274..fb3e778252 100644
--- a/railties/lib/rails/generators/rails/scaffold_controller/templates/controller.rb
+++ b/railties/lib/rails/generators/rails/scaffold_controller/templates/controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
<% if namespaced? -%>
require_dependency "<%= namespaced_path %>/application_controller"
diff --git a/railties/lib/rails/generators/rails/system_test/system_test_generator.rb b/railties/lib/rails/generators/rails/system_test/system_test_generator.rb
index 901120e892..7169e1bd3b 100644
--- a/railties/lib/rails/generators/rails/system_test/system_test_generator.rb
+++ b/railties/lib/rails/generators/rails/system_test/system_test_generator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Rails
module Generators
class SystemTestGenerator < NamedBase # :nodoc:
diff --git a/railties/lib/rails/generators/rails/task/task_generator.rb b/railties/lib/rails/generators/rails/task/task_generator.rb
index bb96bdf0dd..b7290a7447 100644
--- a/railties/lib/rails/generators/rails/task/task_generator.rb
+++ b/railties/lib/rails/generators/rails/task/task_generator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Rails
module Generators
class TaskGenerator < NamedBase # :nodoc:
diff --git a/railties/lib/rails/generators/rails/task/templates/task.rb b/railties/lib/rails/generators/rails/task/templates/task.rb
index 1e3ed5f158..82626afce0 100644
--- a/railties/lib/rails/generators/rails/task/templates/task.rb
+++ b/railties/lib/rails/generators/rails/task/templates/task.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
namespace :<%= file_name %> do
<% actions.each do |action| -%>
desc "TODO"
diff --git a/railties/lib/rails/generators/resource_helpers.rb b/railties/lib/rails/generators/resource_helpers.rb
index 170da6cb0d..8bd5110940 100644
--- a/railties/lib/rails/generators/resource_helpers.rb
+++ b/railties/lib/rails/generators/resource_helpers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_relative "active_model"
require_relative "model_helpers"
diff --git a/railties/lib/rails/generators/test_case.rb b/railties/lib/rails/generators/test_case.rb
index 6cebd217a7..9c06eed95a 100644
--- a/railties/lib/rails/generators/test_case.rb
+++ b/railties/lib/rails/generators/test_case.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_relative "../generators"
require_relative "testing/behaviour"
require_relative "testing/setup_and_teardown"
diff --git a/railties/lib/rails/generators/test_unit.rb b/railties/lib/rails/generators/test_unit.rb
index 986e673611..1aa03ace99 100644
--- a/railties/lib/rails/generators/test_unit.rb
+++ b/railties/lib/rails/generators/test_unit.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_relative "named_base"
module TestUnit # :nodoc:
diff --git a/railties/lib/rails/generators/test_unit/controller/controller_generator.rb b/railties/lib/rails/generators/test_unit/controller/controller_generator.rb
index 72a650e7d9..3947b74205 100644
--- a/railties/lib/rails/generators/test_unit/controller/controller_generator.rb
+++ b/railties/lib/rails/generators/test_unit/controller/controller_generator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_relative "../../test_unit"
module TestUnit # :nodoc:
diff --git a/railties/lib/rails/generators/test_unit/controller/templates/functional_test.rb b/railties/lib/rails/generators/test_unit/controller/templates/functional_test.rb
index ff41fef9e9..4efa977a89 100644
--- a/railties/lib/rails/generators/test_unit/controller/templates/functional_test.rb
+++ b/railties/lib/rails/generators/test_unit/controller/templates/functional_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'test_helper'
<% module_namespacing do -%>
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 abc3615cfd..9f79e9eee1 100644
--- a/railties/lib/rails/generators/test_unit/generator/generator_generator.rb
+++ b/railties/lib/rails/generators/test_unit/generator/generator_generator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_relative "../../test_unit"
module TestUnit # :nodoc:
diff --git a/railties/lib/rails/generators/test_unit/generator/templates/generator_test.rb b/railties/lib/rails/generators/test_unit/generator/templates/generator_test.rb
index a7f1fc4fba..e6fb6c5ff4 100644
--- a/railties/lib/rails/generators/test_unit/generator/templates/generator_test.rb
+++ b/railties/lib/rails/generators/test_unit/generator/templates/generator_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'test_helper'
require '<%= generator_path %>'
diff --git a/railties/lib/rails/generators/test_unit/helper/helper_generator.rb b/railties/lib/rails/generators/test_unit/helper/helper_generator.rb
index 2020e7785e..1c60c3573f 100644
--- a/railties/lib/rails/generators/test_unit/helper/helper_generator.rb
+++ b/railties/lib/rails/generators/test_unit/helper/helper_generator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_relative "../../test_unit"
module TestUnit # :nodoc:
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 a31a795212..c859ba5e4e 100644
--- a/railties/lib/rails/generators/test_unit/integration/integration_generator.rb
+++ b/railties/lib/rails/generators/test_unit/integration/integration_generator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_relative "../../test_unit"
module TestUnit # :nodoc:
diff --git a/railties/lib/rails/generators/test_unit/integration/templates/integration_test.rb b/railties/lib/rails/generators/test_unit/integration/templates/integration_test.rb
index 118e0f1271..65708b6c3b 100644
--- a/railties/lib/rails/generators/test_unit/integration/templates/integration_test.rb
+++ b/railties/lib/rails/generators/test_unit/integration/templates/integration_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'test_helper'
<% module_namespacing do -%>
diff --git a/railties/lib/rails/generators/test_unit/job/job_generator.rb b/railties/lib/rails/generators/test_unit/job/job_generator.rb
index f3ea7cd170..cbbcd1cd4e 100644
--- a/railties/lib/rails/generators/test_unit/job/job_generator.rb
+++ b/railties/lib/rails/generators/test_unit/job/job_generator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_relative "../../test_unit"
module TestUnit # :nodoc:
diff --git a/railties/lib/rails/generators/test_unit/mailer/mailer_generator.rb b/railties/lib/rails/generators/test_unit/mailer/mailer_generator.rb
index 274755b057..45f82158e9 100644
--- a/railties/lib/rails/generators/test_unit/mailer/mailer_generator.rb
+++ b/railties/lib/rails/generators/test_unit/mailer/mailer_generator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_relative "../../test_unit"
module TestUnit # :nodoc:
diff --git a/railties/lib/rails/generators/test_unit/mailer/templates/functional_test.rb b/railties/lib/rails/generators/test_unit/mailer/templates/functional_test.rb
index a2f2d30de5..1ec3a2f360 100644
--- a/railties/lib/rails/generators/test_unit/mailer/templates/functional_test.rb
+++ b/railties/lib/rails/generators/test_unit/mailer/templates/functional_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'test_helper'
<% module_namespacing do -%>
diff --git a/railties/lib/rails/generators/test_unit/mailer/templates/preview.rb b/railties/lib/rails/generators/test_unit/mailer/templates/preview.rb
index b063cbc47b..9876210b6c 100644
--- a/railties/lib/rails/generators/test_unit/mailer/templates/preview.rb
+++ b/railties/lib/rails/generators/test_unit/mailer/templates/preview.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
<% module_namespacing do -%>
# Preview all emails at http://localhost:3000/rails/mailers/<%= file_path %>_mailer
class <%= class_name %>MailerPreview < ActionMailer::Preview
diff --git a/railties/lib/rails/generators/test_unit/model/model_generator.rb b/railties/lib/rails/generators/test_unit/model/model_generator.rb
index fef640f947..2b1cc50c20 100644
--- a/railties/lib/rails/generators/test_unit/model/model_generator.rb
+++ b/railties/lib/rails/generators/test_unit/model/model_generator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_relative "../../test_unit"
module TestUnit # :nodoc:
diff --git a/railties/lib/rails/generators/test_unit/model/templates/unit_test.rb b/railties/lib/rails/generators/test_unit/model/templates/unit_test.rb
index c9bc7d5b90..5f1ffeb33b 100644
--- a/railties/lib/rails/generators/test_unit/model/templates/unit_test.rb
+++ b/railties/lib/rails/generators/test_unit/model/templates/unit_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'test_helper'
<% module_namespacing do -%>
diff --git a/railties/lib/rails/generators/test_unit/plugin/plugin_generator.rb b/railties/lib/rails/generators/test_unit/plugin/plugin_generator.rb
index f324000235..10fe27b2c3 100644
--- a/railties/lib/rails/generators/test_unit/plugin/plugin_generator.rb
+++ b/railties/lib/rails/generators/test_unit/plugin/plugin_generator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_relative "../../test_unit"
module TestUnit # :nodoc:
diff --git a/railties/lib/rails/generators/test_unit/plugin/templates/test_helper.rb b/railties/lib/rails/generators/test_unit/plugin/templates/test_helper.rb
index 30a861f09d..2147b09568 100644
--- a/railties/lib/rails/generators/test_unit/plugin/templates/test_helper.rb
+++ b/railties/lib/rails/generators/test_unit/plugin/templates/test_helper.rb
@@ -1,2 +1,4 @@
+# frozen_string_literal: true
+
require 'active_support/testing/autorun'
require 'active_support'
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 fbe5be3cdc..23d5c63614 100644
--- a/railties/lib/rails/generators/test_unit/scaffold/scaffold_generator.rb
+++ b/railties/lib/rails/generators/test_unit/scaffold/scaffold_generator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_relative "../../test_unit"
require_relative "../../resource_helpers"
diff --git a/railties/lib/rails/generators/test_unit/scaffold/templates/api_functional_test.rb b/railties/lib/rails/generators/test_unit/scaffold/templates/api_functional_test.rb
index f21861d8e6..2ef93b8aea 100644
--- a/railties/lib/rails/generators/test_unit/scaffold/templates/api_functional_test.rb
+++ b/railties/lib/rails/generators/test_unit/scaffold/templates/api_functional_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'test_helper'
<% module_namespacing do -%>
diff --git a/railties/lib/rails/generators/test_unit/scaffold/templates/functional_test.rb b/railties/lib/rails/generators/test_unit/scaffold/templates/functional_test.rb
index 195d60be20..bcf9392bd1 100644
--- a/railties/lib/rails/generators/test_unit/scaffold/templates/functional_test.rb
+++ b/railties/lib/rails/generators/test_unit/scaffold/templates/functional_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'test_helper'
<% module_namespacing do -%>
diff --git a/railties/lib/rails/generators/test_unit/scaffold/templates/system_test.rb b/railties/lib/rails/generators/test_unit/scaffold/templates/system_test.rb
index f83f5a5c62..ba8bdc192e 100644
--- a/railties/lib/rails/generators/test_unit/scaffold/templates/system_test.rb
+++ b/railties/lib/rails/generators/test_unit/scaffold/templates/system_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "application_system_test_case"
<% module_namespacing do -%>
diff --git a/railties/lib/rails/generators/test_unit/system/system_generator.rb b/railties/lib/rails/generators/test_unit/system/system_generator.rb
index 95cc86cc8a..d3acbfd738 100644
--- a/railties/lib/rails/generators/test_unit/system/system_generator.rb
+++ b/railties/lib/rails/generators/test_unit/system/system_generator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_relative "../../test_unit"
module TestUnit # :nodoc:
diff --git a/railties/lib/rails/generators/test_unit/system/templates/application_system_test_case.rb b/railties/lib/rails/generators/test_unit/system/templates/application_system_test_case.rb
index d19212abd5..c05709aff8 100644
--- a/railties/lib/rails/generators/test_unit/system/templates/application_system_test_case.rb
+++ b/railties/lib/rails/generators/test_unit/system/templates/application_system_test_case.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "test_helper"
class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
diff --git a/railties/lib/rails/generators/test_unit/system/templates/system_test.rb b/railties/lib/rails/generators/test_unit/system/templates/system_test.rb
index b5ce2ba5c8..cfac061cd1 100644
--- a/railties/lib/rails/generators/test_unit/system/templates/system_test.rb
+++ b/railties/lib/rails/generators/test_unit/system/templates/system_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "application_system_test_case"
class <%= class_name.pluralize %>Test < ApplicationSystemTestCase
diff --git a/railties/lib/rails/generators/testing/assertions.rb b/railties/lib/rails/generators/testing/assertions.rb
index 9ac70f60c0..c4cff9090b 100644
--- a/railties/lib/rails/generators/testing/assertions.rb
+++ b/railties/lib/rails/generators/testing/assertions.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Rails
module Generators
module Testing
diff --git a/railties/lib/rails/generators/testing/behaviour.rb b/railties/lib/rails/generators/testing/behaviour.rb
index ede181a1bc..97a09c1166 100644
--- a/railties/lib/rails/generators/testing/behaviour.rb
+++ b/railties/lib/rails/generators/testing/behaviour.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "active_support/core_ext/class/attribute"
require "active_support/core_ext/module/delegation"
require "active_support/core_ext/hash/reverse_merge"
diff --git a/railties/lib/rails/generators/testing/setup_and_teardown.rb b/railties/lib/rails/generators/testing/setup_and_teardown.rb
index 73102a283f..4374aa5b8c 100644
--- a/railties/lib/rails/generators/testing/setup_and_teardown.rb
+++ b/railties/lib/rails/generators/testing/setup_and_teardown.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Rails
module Generators
module Testing
diff --git a/railties/lib/rails/info.rb b/railties/lib/rails/info.rb
index db08d578c0..d8f361f524 100644
--- a/railties/lib/rails/info.rb
+++ b/railties/lib/rails/info.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cgi"
module Rails
@@ -39,7 +41,7 @@ module Rails
alias inspect to_s
def to_html
- "<table>".tap do |table|
+ "<table>".dup.tap do |table|
properties.each do |(name, value)|
table << %(<tr><td class="name">#{CGI.escapeHTML(name.to_s)}</td>)
formatted_value = if value.kind_of?(Array)
diff --git a/railties/lib/rails/info_controller.rb b/railties/lib/rails/info_controller.rb
index 9fe6ef5a93..6535b5b6ad 100644
--- a/railties/lib/rails/info_controller.rb
+++ b/railties/lib/rails/info_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_relative "application_controller"
require "action_dispatch/routing/inspector"
diff --git a/railties/lib/rails/initializable.rb b/railties/lib/rails/initializable.rb
index a2615d5efd..5410e17153 100644
--- a/railties/lib/rails/initializable.rb
+++ b/railties/lib/rails/initializable.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "tsort"
module Rails
diff --git a/railties/lib/rails/mailers_controller.rb b/railties/lib/rails/mailers_controller.rb
index 5b9af97617..6bd3161fdd 100644
--- a/railties/lib/rails/mailers_controller.rb
+++ b/railties/lib/rails/mailers_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_relative "application_controller"
class Rails::MailersController < Rails::ApplicationController # :nodoc:
diff --git a/railties/lib/rails/paths.rb b/railties/lib/rails/paths.rb
index 6bdb673215..87222563fd 100644
--- a/railties/lib/rails/paths.rb
+++ b/railties/lib/rails/paths.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Rails
module Paths
# This object is an extended hash that behaves as root of the <tt>Rails::Paths</tt> system.
diff --git a/railties/lib/rails/plugin/test.rb b/railties/lib/rails/plugin/test.rb
index 40070c0eb3..2426521e35 100644
--- a/railties/lib/rails/plugin/test.rb
+++ b/railties/lib/rails/plugin/test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_relative "../test_unit/runner"
require_relative "../test_unit/reporter"
diff --git a/railties/lib/rails/rack.rb b/railties/lib/rails/rack.rb
index a4c4527a72..579fb25cc4 100644
--- a/railties/lib/rails/rack.rb
+++ b/railties/lib/rails/rack.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Rails
module Rack
autoload :Logger, "rails/rack/logger"
diff --git a/railties/lib/rails/rack/logger.rb b/railties/lib/rails/rack/logger.rb
index 853fc26051..ec5212ee76 100644
--- a/railties/lib/rails/rack/logger.rb
+++ b/railties/lib/rails/rack/logger.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "active_support/core_ext/time/conversions"
require "active_support/core_ext/object/blank"
require "active_support/log_subscriber"
diff --git a/railties/lib/rails/railtie.rb b/railties/lib/rails/railtie.rb
index a2a0192cfc..42c4a82b1b 100644
--- a/railties/lib/rails/railtie.rb
+++ b/railties/lib/rails/railtie.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_relative "initializable"
require "active_support/inflector"
require "active_support/core_ext/module/introspection"
diff --git a/railties/lib/rails/railtie/configurable.rb b/railties/lib/rails/railtie/configurable.rb
index 2a8295426e..7f42fae10a 100644
--- a/railties/lib/rails/railtie/configurable.rb
+++ b/railties/lib/rails/railtie/configurable.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "active_support/concern"
module Rails
diff --git a/railties/lib/rails/railtie/configuration.rb b/railties/lib/rails/railtie/configuration.rb
index 275d2039f6..d9997759f2 100644
--- a/railties/lib/rails/railtie/configuration.rb
+++ b/railties/lib/rails/railtie/configuration.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_relative "../configuration"
module Rails
diff --git a/railties/lib/rails/ruby_version_check.rb b/railties/lib/rails/ruby_version_check.rb
index b212835df7..76b6b80d28 100644
--- a/railties/lib/rails/ruby_version_check.rb
+++ b/railties/lib/rails/ruby_version_check.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
if RUBY_VERSION < "2.2.2" && RUBY_ENGINE == "ruby"
desc = defined?(RUBY_DESCRIPTION) ? RUBY_DESCRIPTION : "ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE})"
abort <<-end_message
diff --git a/railties/lib/rails/secrets.rb b/railties/lib/rails/secrets.rb
index 0883857c55..33821dd759 100644
--- a/railties/lib/rails/secrets.rb
+++ b/railties/lib/rails/secrets.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "yaml"
require "active_support/message_encryptor"
require "active_support/core_ext/string/strip"
diff --git a/railties/lib/rails/source_annotation_extractor.rb b/railties/lib/rails/source_annotation_extractor.rb
index e9088c44ce..d6cde227c5 100644
--- a/railties/lib/rails/source_annotation_extractor.rb
+++ b/railties/lib/rails/source_annotation_extractor.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Implements the logic behind the rake tasks for annotations like
#
# rails notes
@@ -45,7 +47,7 @@ class SourceAnnotationExtractor
# If +options+ has a flag <tt>:tag</tt> the tag is shown as in the example above.
# Otherwise the string contains just line and text.
def to_s(options = {})
- s = "[#{line.to_s.rjust(options[:indent])}] "
+ s = "[#{line.to_s.rjust(options[:indent])}] ".dup
s << "[#{tag}] " if options[:tag]
s << text
end
diff --git a/railties/lib/rails/tasks.rb b/railties/lib/rails/tasks.rb
index c0a50e5bda..2f644a20c9 100644
--- a/railties/lib/rails/tasks.rb
+++ b/railties/lib/rails/tasks.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "rake"
# Load Rails Rakefile extensions
diff --git a/railties/lib/rails/tasks/annotations.rake b/railties/lib/rails/tasks/annotations.rake
index ae427249ef..931f81ac7e 100644
--- a/railties/lib/rails/tasks/annotations.rake
+++ b/railties/lib/rails/tasks/annotations.rake
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_relative "../source_annotation_extractor"
desc "Enumerate all annotations (use notes:optimize, :fixme, :todo for focus)"
diff --git a/railties/lib/rails/tasks/dev.rake b/railties/lib/rails/tasks/dev.rake
index b2447cbcfc..90fd3ccd8b 100644
--- a/railties/lib/rails/tasks/dev.rake
+++ b/railties/lib/rails/tasks/dev.rake
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_relative "../dev_caching"
namespace :dev do
diff --git a/railties/lib/rails/tasks/engine.rake b/railties/lib/rails/tasks/engine.rake
index a8d67322da..3ce49a1641 100644
--- a/railties/lib/rails/tasks/engine.rake
+++ b/railties/lib/rails/tasks/engine.rake
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
task "load_app" do
namespace :app do
load APP_RAKEFILE
diff --git a/railties/lib/rails/tasks/framework.rake b/railties/lib/rails/tasks/framework.rake
index 6c6e86d78d..8931aabcda 100644
--- a/railties/lib/rails/tasks/framework.rake
+++ b/railties/lib/rails/tasks/framework.rake
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
namespace :app do
desc "Update configs and some other initially generated files (or use just update:configs or update:bin)"
task update: [ "update:configs", "update:bin", "update:upgrade_guide_info" ]
@@ -9,7 +11,7 @@ namespace :app do
template = File.expand_path(template) if template !~ %r{\A[A-Za-z][A-Za-z0-9+\-\.]*://}
require_relative "../generators"
require_relative "../generators/rails/app/app_generator"
- generator = Rails::Generators::AppGenerator.new [Rails.root], {}, destination_root: Rails.root
+ generator = Rails::Generators::AppGenerator.new [Rails.root], {}, { destination_root: Rails.root }
generator.apply template, verbose: false
end
diff --git a/railties/lib/rails/tasks/initializers.rake b/railties/lib/rails/tasks/initializers.rake
index 6522f2ae5a..ae85cb0f86 100644
--- a/railties/lib/rails/tasks/initializers.rake
+++ b/railties/lib/rails/tasks/initializers.rake
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
desc "Print out all defined initializers in the order they are invoked by Rails."
task initializers: :environment do
Rails.application.initializers.tsort_each do |initializer|
diff --git a/railties/lib/rails/tasks/log.rake b/railties/lib/rails/tasks/log.rake
index ba796845d7..e219277d23 100644
--- a/railties/lib/rails/tasks/log.rake
+++ b/railties/lib/rails/tasks/log.rake
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
namespace :log do
##
diff --git a/railties/lib/rails/tasks/middleware.rake b/railties/lib/rails/tasks/middleware.rake
index fd98be1ea9..3a7f86fdcb 100644
--- a/railties/lib/rails/tasks/middleware.rake
+++ b/railties/lib/rails/tasks/middleware.rake
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
desc "Prints out your Rack middleware stack"
task middleware: :environment do
Rails.configuration.middleware.each do |middleware|
diff --git a/railties/lib/rails/tasks/misc.rake b/railties/lib/rails/tasks/misc.rake
index 29ea0ff804..e7786aa622 100644
--- a/railties/lib/rails/tasks/misc.rake
+++ b/railties/lib/rails/tasks/misc.rake
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
desc "Generate a cryptographically secure secret key (this is typically used to generate a secret for cookie sessions)."
task :secret do
require "securerandom"
diff --git a/railties/lib/rails/tasks/restart.rake b/railties/lib/rails/tasks/restart.rake
index 03177d9954..5b6bb8ddd6 100644
--- a/railties/lib/rails/tasks/restart.rake
+++ b/railties/lib/rails/tasks/restart.rake
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
desc "Restart app by touching tmp/restart.txt"
task :restart do
verbose(false) do
diff --git a/railties/lib/rails/tasks/routes.rake b/railties/lib/rails/tasks/routes.rake
index 215fb2ceb5..403286d280 100644
--- a/railties/lib/rails/tasks/routes.rake
+++ b/railties/lib/rails/tasks/routes.rake
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "optparse"
desc "Print out all defined routes in match order, with names. Target specific controller with -c option, or grep routes using -g option"
diff --git a/railties/lib/rails/tasks/statistics.rake b/railties/lib/rails/tasks/statistics.rake
index 0670050d34..0449a13586 100644
--- a/railties/lib/rails/tasks/statistics.rake
+++ b/railties/lib/rails/tasks/statistics.rake
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# While global constants are bad, many 3rd party tools depend on this one (e.g
# rspec-rails & cucumber-rails). So a deprecation warning is needed if we want
# to remove it.
diff --git a/railties/lib/rails/tasks/tmp.rake b/railties/lib/rails/tasks/tmp.rake
index 3d8ced1bf3..7340b41ee4 100644
--- a/railties/lib/rails/tasks/tmp.rake
+++ b/railties/lib/rails/tasks/tmp.rake
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
namespace :tmp do
desc "Clear cache, socket and screenshot files from tmp/ (narrow w/ tmp:cache:clear, tmp:sockets:clear, tmp:screenshots:clear)"
task clear: ["tmp:cache:clear", "tmp:sockets:clear", "tmp:screenshots:clear"]
diff --git a/railties/lib/rails/tasks/yarn.rake b/railties/lib/rails/tasks/yarn.rake
index 6cb19b7282..48da7ffc51 100644
--- a/railties/lib/rails/tasks/yarn.rake
+++ b/railties/lib/rails/tasks/yarn.rake
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
namespace :yarn do
desc "Install all JavaScript dependencies as specified via Yarn"
task :install do
diff --git a/railties/lib/rails/test_help.rb b/railties/lib/rails/test_help.rb
index 33b6740cf1..f755474488 100644
--- a/railties/lib/rails/test_help.rb
+++ b/railties/lib/rails/test_help.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Make double-sure the RAILS_ENV is not set to production,
# so fixtures aren't loaded into that environment
abort("Abort testing: Your Rails environment is running in production mode!") if Rails.env.production?
diff --git a/railties/lib/rails/test_unit/line_filtering.rb b/railties/lib/rails/test_unit/line_filtering.rb
index 289afae331..e8b891d07b 100644
--- a/railties/lib/rails/test_unit/line_filtering.rb
+++ b/railties/lib/rails/test_unit/line_filtering.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "method_source"
require "rails/test_unit/runner"
diff --git a/railties/lib/rails/test_unit/railtie.rb b/railties/lib/rails/test_unit/railtie.rb
index f1965f3b12..05f9a177d0 100644
--- a/railties/lib/rails/test_unit/railtie.rb
+++ b/railties/lib/rails/test_unit/railtie.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_relative "line_filtering"
if defined?(Rake.application) && Rake.application.top_level_tasks.grep(/^(default$|test(:|$))/).any?
diff --git a/railties/lib/rails/test_unit/reporter.rb b/railties/lib/rails/test_unit/reporter.rb
index ab28034d27..7d3164f1eb 100644
--- a/railties/lib/rails/test_unit/reporter.rb
+++ b/railties/lib/rails/test_unit/reporter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "active_support/core_ext/class/attribute"
require "minitest"
diff --git a/railties/lib/rails/test_unit/runner.rb b/railties/lib/rails/test_unit/runner.rb
index 1f3a248bcb..5c2f6451e2 100644
--- a/railties/lib/rails/test_unit/runner.rb
+++ b/railties/lib/rails/test_unit/runner.rb
@@ -11,7 +11,7 @@ module Rails
mattr_reader :filters, default: []
class << self
- def options(opts)
+ def attach_before_load_options(opts)
opts.on("--warnings", "-w", "Run with Ruby warnings enabled") {}
opts.on("--environment", "-e", "Run tests in the ENV environment") {}
end
diff --git a/railties/lib/rails/test_unit/testing.rake b/railties/lib/rails/test_unit/testing.rake
index 97dd5cef10..18d699f806 100644
--- a/railties/lib/rails/test_unit/testing.rake
+++ b/railties/lib/rails/test_unit/testing.rake
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
gem "minitest"
require "minitest"
require_relative "runner"
diff --git a/railties/lib/rails/version.rb b/railties/lib/rails/version.rb
index 3d8e8291d1..ba6763a572 100644
--- a/railties/lib/rails/version.rb
+++ b/railties/lib/rails/version.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_relative "gem_version"
module Rails
diff --git a/railties/lib/rails/welcome_controller.rb b/railties/lib/rails/welcome_controller.rb
index 3b417f65ba..0cb43e0f31 100644
--- a/railties/lib/rails/welcome_controller.rb
+++ b/railties/lib/rails/welcome_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_relative "application_controller"
class Rails::WelcomeController < Rails::ApplicationController # :nodoc:
diff --git a/railties/railties.gemspec b/railties/railties.gemspec
index e5587b531a..4c665cd546 100644
--- a/railties/railties.gemspec
+++ b/railties/railties.gemspec
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
version = File.read(File.expand_path("../RAILS_VERSION", __dir__)).strip
Gem::Specification.new do |s|
diff --git a/railties/test/abstract_unit.rb b/railties/test/abstract_unit.rb
index 2d4c7a0f0b..a63b7a8377 100644
--- a/railties/test/abstract_unit.rb
+++ b/railties/test/abstract_unit.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
ENV["RAILS_ENV"] ||= "test"
require "stringio"
diff --git a/railties/test/app_loader_test.rb b/railties/test/app_loader_test.rb
index 85f5502b4d..bb556f1968 100644
--- a/railties/test/app_loader_test.rb
+++ b/railties/test/app_loader_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "tmpdir"
require "abstract_unit"
require "rails/app_loader"
diff --git a/railties/test/application/asset_debugging_test.rb b/railties/test/application/asset_debugging_test.rb
index 3e17a1efa5..128040262f 100644
--- a/railties/test/application/asset_debugging_test.rb
+++ b/railties/test/application/asset_debugging_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "isolation/abstract_unit"
require "rack/test"
diff --git a/railties/test/application/assets_test.rb b/railties/test/application/assets_test.rb
index f38cacd6da..99fe6749d9 100644
--- a/railties/test/application/assets_test.rb
+++ b/railties/test/application/assets_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "isolation/abstract_unit"
require "rack/test"
require "active_support/json"
@@ -475,9 +477,9 @@ module ApplicationTests
class ::PostsController < ActionController::Base; end
- get "/posts", {}, "HTTPS" => "off"
+ get "/posts", {}, { "HTTPS" => "off" }
assert_match('src="http://example.com/assets/application.self.js', last_response.body)
- get "/posts", {}, "HTTPS" => "on"
+ get "/posts", {}, { "HTTPS" => "on" }
assert_match('src="https://example.com/assets/application.self.js', last_response.body)
end
diff --git a/railties/test/application/bin_setup_test.rb b/railties/test/application/bin_setup_test.rb
index 0fb995900f..a09c371033 100644
--- a/railties/test/application/bin_setup_test.rb
+++ b/railties/test/application/bin_setup_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "isolation/abstract_unit"
module ApplicationTests
diff --git a/railties/test/application/configuration/custom_test.rb b/railties/test/application/configuration/custom_test.rb
index 8360b7bf4b..05b17b4a7a 100644
--- a/railties/test/application/configuration/custom_test.rb
+++ b/railties/test/application/configuration/custom_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "isolation/abstract_unit"
module ApplicationTests
diff --git a/railties/test/application/configuration_test.rb b/railties/test/application/configuration_test.rb
index 9f62ca8eb8..ebe019f6a9 100644
--- a/railties/test/application/configuration_test.rb
+++ b/railties/test/application/configuration_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "isolation/abstract_unit"
require "rack/test"
require "env_helpers"
@@ -238,6 +240,66 @@ module ApplicationTests
assert_instance_of Pathname, Rails.public_path
end
+ test "does not eager load controller actions in development" do
+ app_file "app/controllers/posts_controller.rb", <<-RUBY
+ class PostsController < ActionController::Base
+ def index;end
+ def show;end
+ end
+ RUBY
+
+ app "development"
+
+ assert_nil PostsController.instance_variable_get(:@action_methods)
+ end
+
+ test "eager loads controller actions in production" do
+ app_file "app/controllers/posts_controller.rb", <<-RUBY
+ class PostsController < ActionController::Base
+ def index;end
+ def show;end
+ end
+ RUBY
+
+ add_to_config <<-RUBY
+ config.eager_load = true
+ config.cache_classes = true
+ RUBY
+
+ app "production"
+
+ assert_equal %w(index show).to_set, PostsController.instance_variable_get(:@action_methods)
+ end
+
+ test "does not eager load mailer actions in development" do
+ app_file "app/mailers/posts_mailer.rb", <<-RUBY
+ class PostsMailer < ActionMailer::Base
+ def noop_email;end
+ end
+ RUBY
+
+ app "development"
+
+ assert_nil PostsMailer.instance_variable_get(:@action_methods)
+ end
+
+ test "eager loads mailer actions in production" do
+ app_file "app/mailers/posts_mailer.rb", <<-RUBY
+ class PostsMailer < ActionMailer::Base
+ def noop_email;end
+ end
+ RUBY
+
+ add_to_config <<-RUBY
+ config.eager_load = true
+ config.cache_classes = true
+ RUBY
+
+ app "production"
+
+ assert_equal %w(noop_email).to_set, PostsMailer.instance_variable_get(:@action_methods)
+ end
+
test "initialize an eager loaded, cache classes app" do
add_to_config <<-RUBY
config.eager_load = true
@@ -453,10 +515,8 @@ module ApplicationTests
secret_key_base: 123
YAML
- app "development"
-
assert_raise(ArgumentError) do
- app.key_generator
+ app "development"
end
end
@@ -1131,6 +1191,7 @@ module ApplicationTests
app "development"
force_lazy_load_hooks { ActionController::Base }
+ force_lazy_load_hooks { ActionController::API }
assert_equal :raise, ActionController::Parameters.action_on_unpermitted_parameters
@@ -1142,6 +1203,7 @@ module ApplicationTests
app "development"
force_lazy_load_hooks { ActionController::Base }
+ force_lazy_load_hooks { ActionController::API }
assert_equal %w(controller action), ActionController::Parameters.always_permitted_parameters
end
@@ -1154,6 +1216,7 @@ module ApplicationTests
app "development"
force_lazy_load_hooks { ActionController::Base }
+ force_lazy_load_hooks { ActionController::API }
assert_equal %w( controller action format ), ActionController::Parameters.always_permitted_parameters
end
@@ -1178,6 +1241,7 @@ module ApplicationTests
app "development"
force_lazy_load_hooks { ActionController::Base }
+ force_lazy_load_hooks { ActionController::API }
assert_equal :raise, ActionController::Parameters.action_on_unpermitted_parameters
@@ -1189,6 +1253,7 @@ module ApplicationTests
app "development"
force_lazy_load_hooks { ActionController::Base }
+ force_lazy_load_hooks { ActionController::API }
assert_equal :log, ActionController::Parameters.action_on_unpermitted_parameters
end
@@ -1197,6 +1262,7 @@ module ApplicationTests
app "test"
force_lazy_load_hooks { ActionController::Base }
+ force_lazy_load_hooks { ActionController::API }
assert_equal :log, ActionController::Parameters.action_on_unpermitted_parameters
end
@@ -1205,6 +1271,7 @@ module ApplicationTests
app "production"
force_lazy_load_hooks { ActionController::Base }
+ force_lazy_load_hooks { ActionController::API }
assert_equal false, ActionController::Parameters.action_on_unpermitted_parameters
end
@@ -1224,6 +1291,7 @@ module ApplicationTests
app "development"
force_lazy_load_hooks { ActionController::Base }
+ force_lazy_load_hooks { ActionController::API }
assert_equal true, ActionController::Parameters.permit_all_parameters
end
@@ -1235,6 +1303,7 @@ module ApplicationTests
app "development"
force_lazy_load_hooks { ActionController::Base }
+ force_lazy_load_hooks { ActionController::API }
assert_equal [], ActionController::Parameters.always_permitted_parameters
end
@@ -1246,6 +1315,7 @@ module ApplicationTests
app "development"
force_lazy_load_hooks { ActionController::Base }
+ force_lazy_load_hooks { ActionController::API }
assert_equal :raise, ActionController::Parameters.action_on_unpermitted_parameters
end
@@ -1263,10 +1333,10 @@ module ApplicationTests
end
end
- get "/", {}, "HTTP_ACCEPT" => "application/xml"
+ get "/", {}, { "HTTP_ACCEPT" => "application/xml" }
assert_equal "HTML", last_response.body
- get "/", { format: :xml }, "HTTP_ACCEPT" => "application/xml"
+ get "/", { format: :xml }, { "HTTP_ACCEPT" => "application/xml" }
assert_equal "XML", last_response.body
end
@@ -1452,8 +1522,8 @@ module ApplicationTests
test "raises with proper error message if no database configuration found" do
FileUtils.rm("#{app_path}/config/database.yml")
- app "development"
err = assert_raises RuntimeError do
+ app "development"
Rails.application.config.database_configuration
end
assert_match "config/database", err.message
diff --git a/railties/test/application/console_test.rb b/railties/test/application/console_test.rb
index 31bef82ccc..13164f49c2 100644
--- a/railties/test/application/console_test.rb
+++ b/railties/test/application/console_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "isolation/abstract_unit"
require "console_helpers"
diff --git a/railties/test/application/current_attributes_integration_test.rb b/railties/test/application/current_attributes_integration_test.rb
index 5653ec0be1..811721b331 100644
--- a/railties/test/application/current_attributes_integration_test.rb
+++ b/railties/test/application/current_attributes_integration_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "isolation/abstract_unit"
require "rack/test"
diff --git a/railties/test/application/dbconsole_test.rb b/railties/test/application/dbconsole_test.rb
index 5d89d0e44d..ba04c81b4d 100644
--- a/railties/test/application/dbconsole_test.rb
+++ b/railties/test/application/dbconsole_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "isolation/abstract_unit"
require "console_helpers"
diff --git a/railties/test/application/generators_test.rb b/railties/test/application/generators_test.rb
index fe581db286..58197a8f6b 100644
--- a/railties/test/application/generators_test.rb
+++ b/railties/test/application/generators_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "isolation/abstract_unit"
module ApplicationTests
diff --git a/railties/test/application/help_test.rb b/railties/test/application/help_test.rb
index 0c3fe8bfa3..3a9384b927 100644
--- a/railties/test/application/help_test.rb
+++ b/railties/test/application/help_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "isolation/abstract_unit"
class HelpTest < ActiveSupport::TestCase
diff --git a/railties/test/application/initializers/frameworks_test.rb b/railties/test/application/initializers/frameworks_test.rb
index eb2c578f91..d643c85181 100644
--- a/railties/test/application/initializers/frameworks_test.rb
+++ b/railties/test/application/initializers/frameworks_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "isolation/abstract_unit"
module ApplicationTests
diff --git a/railties/test/application/initializers/hooks_test.rb b/railties/test/application/initializers/hooks_test.rb
index 36926c50ff..1e130c2f9e 100644
--- a/railties/test/application/initializers/hooks_test.rb
+++ b/railties/test/application/initializers/hooks_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "isolation/abstract_unit"
module ApplicationTests
diff --git a/railties/test/application/initializers/i18n_test.rb b/railties/test/application/initializers/i18n_test.rb
index cee198bd01..8058052771 100644
--- a/railties/test/application/initializers/i18n_test.rb
+++ b/railties/test/application/initializers/i18n_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "isolation/abstract_unit"
module ApplicationTests
diff --git a/railties/test/application/initializers/load_path_test.rb b/railties/test/application/initializers/load_path_test.rb
index dbefb22837..78cd4776d6 100644
--- a/railties/test/application/initializers/load_path_test.rb
+++ b/railties/test/application/initializers/load_path_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "isolation/abstract_unit"
module ApplicationTests
diff --git a/railties/test/application/initializers/notifications_test.rb b/railties/test/application/initializers/notifications_test.rb
index b847ac6b36..23b20d578c 100644
--- a/railties/test/application/initializers/notifications_test.rb
+++ b/railties/test/application/initializers/notifications_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "isolation/abstract_unit"
module ApplicationTests
diff --git a/railties/test/application/integration_test_case_test.rb b/railties/test/application/integration_test_case_test.rb
index 1118e5037a..88b54af8c8 100644
--- a/railties/test/application/integration_test_case_test.rb
+++ b/railties/test/application/integration_test_case_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "isolation/abstract_unit"
module ApplicationTests
diff --git a/railties/test/application/loading_test.rb b/railties/test/application/loading_test.rb
index c75a25bc6f..11c886e991 100644
--- a/railties/test/application/loading_test.rb
+++ b/railties/test/application/loading_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "isolation/abstract_unit"
class LoadingTest < ActiveSupport::TestCase
@@ -115,11 +117,11 @@ class LoadingTest < ActiveSupport::TestCase
require "#{rails_root}/config/environment"
setup_ar!
- assert_equal [ActiveRecord::SchemaMigration, ActiveRecord::InternalMetadata], ActiveRecord::Base.descendants
+ assert_equal [ActiveStorage::Blob, ActiveStorage::Attachment, ActiveRecord::SchemaMigration, ActiveRecord::InternalMetadata].collect(&:to_s).sort, ActiveRecord::Base.descendants.collect(&:to_s).sort
get "/load"
- assert_equal [ActiveRecord::SchemaMigration, ActiveRecord::InternalMetadata, Post], ActiveRecord::Base.descendants
+ assert_equal [ActiveStorage::Blob, ActiveStorage::Attachment, ActiveRecord::SchemaMigration, ActiveRecord::InternalMetadata, Post].collect(&:to_s).sort, ActiveRecord::Base.descendants.collect(&:to_s).sort
get "/unload"
- assert_equal [ActiveRecord::SchemaMigration, ActiveRecord::InternalMetadata], ActiveRecord::Base.descendants
+ assert_equal [ActiveStorage::Blob, ActiveStorage::Attachment, ActiveRecord::SchemaMigration, ActiveRecord::InternalMetadata].collect(&:to_s).sort, ActiveRecord::Base.descendants.collect(&:to_s).sort
end
test "initialize cant be called twice" do
diff --git a/railties/test/application/mailer_previews_test.rb b/railties/test/application/mailer_previews_test.rb
index f5c013dab6..4e77cece1b 100644
--- a/railties/test/application/mailer_previews_test.rb
+++ b/railties/test/application/mailer_previews_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "isolation/abstract_unit"
require "rack/test"
require "base64"
@@ -30,7 +32,7 @@ module ApplicationTests
test "/rails/mailers is accessible with correct configuration" do
add_to_config "config.action_mailer.show_previews = true"
app("production")
- get "/rails/mailers", {}, "REMOTE_ADDR" => "4.2.42.42"
+ get "/rails/mailers", {}, { "REMOTE_ADDR" => "4.2.42.42" }
assert_equal 200, last_response.status
end
@@ -480,7 +482,7 @@ module ApplicationTests
app("development")
- get "/rails/mailers", {}, "SCRIPT_NAME" => "/my_app"
+ get "/rails/mailers", {}, { "SCRIPT_NAME" => "/my_app" }
assert_match '<h3><a href="/my_app/rails/mailers/notifier">Notifier</a></h3>', last_response.body
assert_match '<li><a href="/my_app/rails/mailers/notifier/foo">foo</a></li>', last_response.body
end
diff --git a/railties/test/application/middleware/cache_test.rb b/railties/test/application/middleware/cache_test.rb
index 93b5263bf7..9822ec563d 100644
--- a/railties/test/application/middleware/cache_test.rb
+++ b/railties/test/application/middleware/cache_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "isolation/abstract_unit"
module ApplicationTests
@@ -54,7 +56,7 @@ module ApplicationTests
simple_controller
expected = "Wed, 30 May 1984 19:43:31 GMT"
- get "/expires/keeps_if_modified_since", {}, "HTTP_IF_MODIFIED_SINCE" => expected
+ get "/expires/keeps_if_modified_since", {}, { "HTTP_IF_MODIFIED_SINCE" => expected }
assert_equal 200, last_response.status
assert_equal expected, last_response.body, "cache should have kept If-Modified-Since"
@@ -119,7 +121,7 @@ module ApplicationTests
etag = last_response.headers["ETag"]
- get "/expires/expires_etag", {}, "HTTP_IF_NONE_MATCH" => etag
+ get "/expires/expires_etag", {}, { "HTTP_IF_NONE_MATCH" => etag }
assert_equal "stale, valid, store", last_response.headers["X-Rack-Cache"]
assert_equal 304, last_response.status
assert_equal "", last_response.body
@@ -137,7 +139,7 @@ module ApplicationTests
body = last_response.body
etag = last_response.headers["ETag"]
- get "/expires/expires_etag", { private: true }, "HTTP_IF_NONE_MATCH" => etag
+ get "/expires/expires_etag", { private: true }, { "HTTP_IF_NONE_MATCH" => etag }
assert_equal "miss", last_response.headers["X-Rack-Cache"]
assert_not_equal body, last_response.body
end
@@ -153,7 +155,7 @@ module ApplicationTests
last = last_response.headers["Last-Modified"]
- get "/expires/expires_last_modified", {}, "HTTP_IF_MODIFIED_SINCE" => last
+ get "/expires/expires_last_modified", {}, { "HTTP_IF_MODIFIED_SINCE" => last }
assert_equal "stale, valid, store", last_response.headers["X-Rack-Cache"]
assert_equal 304, last_response.status
assert_equal "", last_response.body
@@ -171,7 +173,7 @@ module ApplicationTests
body = last_response.body
last = last_response.headers["Last-Modified"]
- get "/expires/expires_last_modified", { private: true }, "HTTP_IF_MODIFIED_SINCE" => last
+ get "/expires/expires_last_modified", { private: true }, { "HTTP_IF_MODIFIED_SINCE" => last }
assert_equal "miss", last_response.headers["X-Rack-Cache"]
assert_not_equal body, last_response.body
end
diff --git a/railties/test/application/middleware/cookies_test.rb b/railties/test/application/middleware/cookies_test.rb
index 1e4b5d086c..23f1ec3e35 100644
--- a/railties/test/application/middleware/cookies_test.rb
+++ b/railties/test/application/middleware/cookies_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "isolation/abstract_unit"
module ApplicationTests
diff --git a/railties/test/application/middleware/exceptions_test.rb b/railties/test/application/middleware/exceptions_test.rb
index fe07ad3cbe..75afeec905 100644
--- a/railties/test/application/middleware/exceptions_test.rb
+++ b/railties/test/application/middleware/exceptions_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "isolation/abstract_unit"
require "rack/test"
diff --git a/railties/test/application/middleware/remote_ip_test.rb b/railties/test/application/middleware/remote_ip_test.rb
index c34d9d6ee7..83cf8a27f7 100644
--- a/railties/test/application/middleware/remote_ip_test.rb
+++ b/railties/test/application/middleware/remote_ip_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "ipaddr"
require "isolation/abstract_unit"
require "active_support/key_generator"
diff --git a/railties/test/application/middleware/sendfile_test.rb b/railties/test/application/middleware/sendfile_test.rb
index 4938402fdc..4731396029 100644
--- a/railties/test/application/middleware/sendfile_test.rb
+++ b/railties/test/application/middleware/sendfile_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "isolation/abstract_unit"
module ApplicationTests
diff --git a/railties/test/application/middleware/session_test.rb b/railties/test/application/middleware/session_test.rb
index a14ea589ed..15acfe93e9 100644
--- a/railties/test/application/middleware/session_test.rb
+++ b/railties/test/application/middleware/session_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "isolation/abstract_unit"
require "rack/test"
diff --git a/railties/test/application/middleware/static_test.rb b/railties/test/application/middleware/static_test.rb
index 5cd3e4325e..0977042cfe 100644
--- a/railties/test/application/middleware/static_test.rb
+++ b/railties/test/application/middleware/static_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "isolation/abstract_unit"
require "rack/test"
diff --git a/railties/test/application/middleware_test.rb b/railties/test/application/middleware_test.rb
index 0a6e5b52e9..0a5a524692 100644
--- a/railties/test/application/middleware_test.rb
+++ b/railties/test/application/middleware_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "isolation/abstract_unit"
module ApplicationTests
@@ -274,7 +276,7 @@ module ApplicationTests
assert_equal "max-age=0, private, must-revalidate", last_response.headers["Cache-Control"]
assert_equal etag, last_response.headers["Etag"]
- get "/", {}, "HTTP_IF_NONE_MATCH" => etag
+ get "/", {}, { "HTTP_IF_NONE_MATCH" => etag }
assert_equal 304, last_response.status
assert_equal "", last_response.body
assert_nil last_response.headers["Content-Type"]
diff --git a/railties/test/application/multiple_applications_test.rb b/railties/test/application/multiple_applications_test.rb
index 26b810af73..d6c81c1fe2 100644
--- a/railties/test/application/multiple_applications_test.rb
+++ b/railties/test/application/multiple_applications_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "isolation/abstract_unit"
module ApplicationTests
diff --git a/railties/test/application/paths_test.rb b/railties/test/application/paths_test.rb
index 515205296c..0abc5cc9aa 100644
--- a/railties/test/application/paths_test.rb
+++ b/railties/test/application/paths_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "isolation/abstract_unit"
module ApplicationTests
diff --git a/railties/test/application/per_request_digest_cache_test.rb b/railties/test/application/per_request_digest_cache_test.rb
index 6e6996a6ba..e9bc91785c 100644
--- a/railties/test/application/per_request_digest_cache_test.rb
+++ b/railties/test/application/per_request_digest_cache_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "isolation/abstract_unit"
require "rack/test"
require "minitest/mock"
diff --git a/railties/test/application/rack/logger_test.rb b/railties/test/application/rack/logger_test.rb
index e71bcbc536..d949a48366 100644
--- a/railties/test/application/rack/logger_test.rb
+++ b/railties/test/application/rack/logger_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "isolation/abstract_unit"
require "active_support/log_subscriber/test_helper"
require "rack/test"
diff --git a/railties/test/application/rackup_test.rb b/railties/test/application/rackup_test.rb
index 2943e9ee5d..383f18a7da 100644
--- a/railties/test/application/rackup_test.rb
+++ b/railties/test/application/rackup_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "isolation/abstract_unit"
module ApplicationTests
diff --git a/railties/test/application/rake/dbs_test.rb b/railties/test/application/rake/dbs_test.rb
index 3216121de3..db88eb0fd3 100644
--- a/railties/test/application/rake/dbs_test.rb
+++ b/railties/test/application/rake/dbs_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "isolation/abstract_unit"
module ApplicationTests
diff --git a/railties/test/application/rake/dev_test.rb b/railties/test/application/rake/dev_test.rb
index 4f992d9c8d..d56f315455 100644
--- a/railties/test/application/rake/dev_test.rb
+++ b/railties/test/application/rake/dev_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "isolation/abstract_unit"
module ApplicationTests
diff --git a/railties/test/application/rake/framework_test.rb b/railties/test/application/rake/framework_test.rb
index 40488a6aab..644b1924b5 100644
--- a/railties/test/application/rake/framework_test.rb
+++ b/railties/test/application/rake/framework_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "isolation/abstract_unit"
module ApplicationTests
diff --git a/railties/test/application/rake/log_test.rb b/railties/test/application/rake/log_test.rb
index fdd3c71fe8..c52be5f396 100644
--- a/railties/test/application/rake/log_test.rb
+++ b/railties/test/application/rake/log_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "isolation/abstract_unit"
module ApplicationTests
diff --git a/railties/test/application/rake/migrations_test.rb b/railties/test/application/rake/migrations_test.rb
index 51dfe2ef98..d130a58441 100644
--- a/railties/test/application/rake/migrations_test.rb
+++ b/railties/test/application/rake/migrations_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "isolation/abstract_unit"
module ApplicationTests
diff --git a/railties/test/application/rake/notes_test.rb b/railties/test/application/rake/notes_test.rb
index e7ffea2e71..8e9fe9b6b4 100644
--- a/railties/test/application/rake/notes_test.rb
+++ b/railties/test/application/rake/notes_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "isolation/abstract_unit"
require "rails/source_annotation_extractor"
@@ -90,7 +92,8 @@ module ApplicationTests
test "custom rake task finds specific notes in specific directories" do
app_file "app/controllers/some_controller.rb", "# TODO: note in app directory"
- app_file "lib/some_file.rb", "# OPTIMIZE: note in lib directory\n" << "# FIXME: note in lib directory"
+ app_file "lib/some_file.rb", "# OPTIMIZE: note in lib directory\n" \
+ "# FIXME: note in lib directory"
app_file "test/some_test.rb", 1000.times.map { "" }.join("\n") << "# TODO: note in test directory"
app_file "lib/tasks/notes_custom.rake", <<-EOS
diff --git a/railties/test/application/rake/restart_test.rb b/railties/test/application/rake/restart_test.rb
index 6ebd2d5461..bf06f4c522 100644
--- a/railties/test/application/rake/restart_test.rb
+++ b/railties/test/application/rake/restart_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "isolation/abstract_unit"
module ApplicationTests
diff --git a/railties/test/application/rake/tmp_test.rb b/railties/test/application/rake/tmp_test.rb
index 8423a98f84..8641d140cb 100644
--- a/railties/test/application/rake/tmp_test.rb
+++ b/railties/test/application/rake/tmp_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "isolation/abstract_unit"
module ApplicationTests
diff --git a/railties/test/application/rake_test.rb b/railties/test/application/rake_test.rb
index 134106812d..fe59bed874 100644
--- a/railties/test/application/rake_test.rb
+++ b/railties/test/application/rake_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "isolation/abstract_unit"
require "active_support/core_ext/string/strip"
@@ -129,7 +131,15 @@ module ApplicationTests
RUBY
output = Dir.chdir(app_path) { `bin/rails routes` }
- assert_equal "Prefix Verb URI Pattern Controller#Action\n cart GET /cart(.:format) cart#show\n", output
+ assert_equal <<-MESSAGE.strip_heredoc, output
+ Prefix Verb URI Pattern Controller#Action
+ cart GET /cart(.:format) cart#show
+ rails_service_blob GET /rails/active_storage/blobs/:signed_id/*filename(.:format) active_storage/blobs#show
+ rails_blob_variation GET /rails/active_storage/variants/:signed_blob_id/:variation_key/*filename(.:format) active_storage/variants#show
+ rails_disk_service GET /rails/active_storage/disk/:encoded_key/*filename(.:format) active_storage/disk#show
+ update_rails_disk_service PUT /rails/active_storage/disk/:encoded_token(.:format) active_storage/disk#update
+ rails_direct_uploads POST /rails/active_storage/direct_uploads(.:format) active_storage/direct_uploads#create
+ MESSAGE
end
def test_singular_resource_output_in_rake_routes
@@ -162,10 +172,20 @@ module ApplicationTests
RUBY
output = Dir.chdir(app_path) { `bin/rails routes -g show` }
- assert_equal "Prefix Verb URI Pattern Controller#Action\n cart GET /cart(.:format) cart#show\n", output
+ assert_equal <<-MESSAGE.strip_heredoc, output
+ Prefix Verb URI Pattern Controller#Action
+ cart GET /cart(.:format) cart#show
+ rails_service_blob GET /rails/active_storage/blobs/:signed_id/*filename(.:format) active_storage/blobs#show
+ rails_blob_variation GET /rails/active_storage/variants/:signed_blob_id/:variation_key/*filename(.:format) active_storage/variants#show
+ rails_disk_service GET /rails/active_storage/disk/:encoded_key/*filename(.:format) active_storage/disk#show
+ MESSAGE
output = Dir.chdir(app_path) { `bin/rails routes -g POST` }
- assert_equal "Prefix Verb URI Pattern Controller#Action\n POST /cart(.:format) cart#create\n", output
+ assert_equal <<-MESSAGE.strip_heredoc, output
+ Prefix Verb URI Pattern Controller#Action
+ POST /cart(.:format) cart#create
+ rails_direct_uploads POST /rails/active_storage/direct_uploads(.:format) active_storage/direct_uploads#create
+ MESSAGE
output = Dir.chdir(app_path) { `bin/rails routes -g basketballs` }
assert_equal " Prefix Verb URI Pattern Controller#Action\n" \
@@ -221,11 +241,12 @@ module ApplicationTests
RUBY
assert_equal <<-MESSAGE.strip_heredoc, Dir.chdir(app_path) { `bin/rails routes` }
- You don't have any routes defined!
-
- Please add some routes in config/routes.rb.
-
- For more information about routes, see the Rails guide: http://guides.rubyonrails.org/routing.html.
+ Prefix Verb URI Pattern Controller#Action
+ rails_service_blob GET /rails/active_storage/blobs/:signed_id/*filename(.:format) active_storage/blobs#show
+ rails_blob_variation GET /rails/active_storage/variants/:signed_blob_id/:variation_key/*filename(.:format) active_storage/variants#show
+ rails_disk_service GET /rails/active_storage/disk/:encoded_key/*filename(.:format) active_storage/disk#show
+ update_rails_disk_service PUT /rails/active_storage/disk/:encoded_token(.:format) active_storage/disk#update
+ rails_direct_uploads POST /rails/active_storage/direct_uploads(.:format) active_storage/direct_uploads#create
MESSAGE
end
@@ -237,7 +258,16 @@ module ApplicationTests
RUBY
output = Dir.chdir(app_path) { `bin/rake --rakefile Rakefile routes` }
- assert_equal "Prefix Verb URI Pattern Controller#Action\n cart GET /cart(.:format) cart#show\n", output
+
+ assert_equal <<-MESSAGE.strip_heredoc, output
+ Prefix Verb URI Pattern Controller#Action
+ cart GET /cart(.:format) cart#show
+ rails_service_blob GET /rails/active_storage/blobs/:signed_id/*filename(.:format) active_storage/blobs#show
+ rails_blob_variation GET /rails/active_storage/variants/:signed_blob_id/:variation_key/*filename(.:format) active_storage/variants#show
+ rails_disk_service GET /rails/active_storage/disk/:encoded_key/*filename(.:format) active_storage/disk#show
+ update_rails_disk_service PUT /rails/active_storage/disk/:encoded_token(.:format) active_storage/disk#update
+ rails_direct_uploads POST /rails/active_storage/direct_uploads(.:format) active_storage/direct_uploads#create
+ MESSAGE
end
def test_logger_is_flushed_when_exiting_production_rake_tasks
diff --git a/railties/test/application/rendering_test.rb b/railties/test/application/rendering_test.rb
index ccafc5b6f1..3724886c54 100644
--- a/railties/test/application/rendering_test.rb
+++ b/railties/test/application/rendering_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "isolation/abstract_unit"
require "rack/test"
diff --git a/railties/test/application/routing_test.rb b/railties/test/application/routing_test.rb
index bc7580d6f4..bec038fb51 100644
--- a/railties/test/application/routing_test.rb
+++ b/railties/test/application/routing_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "isolation/abstract_unit"
require "rack/test"
diff --git a/railties/test/application/runner_test.rb b/railties/test/application/runner_test.rb
index 81f717b2c3..a66f288795 100644
--- a/railties/test/application/runner_test.rb
+++ b/railties/test/application/runner_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "isolation/abstract_unit"
require "env_helpers"
diff --git a/railties/test/application/server_test.rb b/railties/test/application/server_test.rb
new file mode 100644
index 0000000000..97b3b4f6b3
--- /dev/null
+++ b/railties/test/application/server_test.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+require "isolation/abstract_unit"
+require "rails/command"
+require "rails/commands/server/server_command"
+
+module ApplicationTests
+ class ServerTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+
+ def setup
+ build_app
+ end
+
+ def teardown
+ teardown_app
+ end
+
+ test "deprecate support of older `config.ru`" do
+ remove_file "config.ru"
+ app_file "config.ru", <<-RUBY
+ require_relative 'config/environment'
+ run AppTemplate::Application
+ RUBY
+
+ server = Rails::Server.new(config: "#{app_path}/config.ru")
+ server.app
+
+ log = File.read(Rails.application.config.paths["log"].first)
+ assert_match(/DEPRECATION WARNING: Use `Rails::Application` subclass to start the server is deprecated/, log)
+ end
+ end
+end
diff --git a/railties/test/application/test_runner_test.rb b/railties/test/application/test_runner_test.rb
index be50fdb49b..1eda2a5eaf 100644
--- a/railties/test/application/test_runner_test.rb
+++ b/railties/test/application/test_runner_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "isolation/abstract_unit"
require "active_support/core_ext/string/strip"
require "env_helpers"
diff --git a/railties/test/application/test_test.rb b/railties/test/application/test_test.rb
index 2a1a6bcbfd..7e2364c0a6 100644
--- a/railties/test/application/test_test.rb
+++ b/railties/test/application/test_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "isolation/abstract_unit"
module ApplicationTests
diff --git a/railties/test/application/url_generation_test.rb b/railties/test/application/url_generation_test.rb
index 37f129475c..4f962db6c4 100644
--- a/railties/test/application/url_generation_test.rb
+++ b/railties/test/application/url_generation_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "isolation/abstract_unit"
module ApplicationTests
diff --git a/railties/test/application/version_test.rb b/railties/test/application/version_test.rb
index 6b419ae7ae..62d3c7b254 100644
--- a/railties/test/application/version_test.rb
+++ b/railties/test/application/version_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "isolation/abstract_unit"
require "rails/gem_version"
diff --git a/railties/test/backtrace_cleaner_test.rb b/railties/test/backtrace_cleaner_test.rb
index f71e56f323..70917ba20b 100644
--- a/railties/test/backtrace_cleaner_test.rb
+++ b/railties/test/backtrace_cleaner_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "abstract_unit"
require "rails/backtrace_cleaner"
diff --git a/railties/test/code_statistics_calculator_test.rb b/railties/test/code_statistics_calculator_test.rb
index 25a8a40d27..51917de2e0 100644
--- a/railties/test/code_statistics_calculator_test.rb
+++ b/railties/test/code_statistics_calculator_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "abstract_unit"
require "rails/code_statistics_calculator"
diff --git a/railties/test/code_statistics_test.rb b/railties/test/code_statistics_test.rb
index e6e3943117..7ad1ac3094 100644
--- a/railties/test/code_statistics_test.rb
+++ b/railties/test/code_statistics_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "abstract_unit"
require "rails/code_statistics"
diff --git a/railties/test/command/base_test.rb b/railties/test/command/base_test.rb
index bac3285f48..a49ae8aae7 100644
--- a/railties/test/command/base_test.rb
+++ b/railties/test/command/base_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "abstract_unit"
require "rails/command"
require "rails/commands/generate/generate_command"
diff --git a/railties/test/commands/console_test.rb b/railties/test/commands/console_test.rb
index a7169e16fb..7eb26c355c 100644
--- a/railties/test/commands/console_test.rb
+++ b/railties/test/commands/console_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "abstract_unit"
require "env_helpers"
require "rails/command"
diff --git a/railties/test/commands/dbconsole_test.rb b/railties/test/commands/dbconsole_test.rb
index 4f55eb9aa6..e922a23e15 100644
--- a/railties/test/commands/dbconsole_test.rb
+++ b/railties/test/commands/dbconsole_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "abstract_unit"
require "minitest/mock"
require "rails/command"
diff --git a/railties/test/commands/secrets_test.rb b/railties/test/commands/secrets_test.rb
index 3771919849..a1213a57f8 100644
--- a/railties/test/commands/secrets_test.rb
+++ b/railties/test/commands/secrets_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "isolation/abstract_unit"
require "rails/command"
require "rails/commands/secrets/secrets_command"
diff --git a/railties/test/commands/server_test.rb b/railties/test/commands/server_test.rb
index 722323efdc..eb739db9d1 100644
--- a/railties/test/commands/server_test.rb
+++ b/railties/test/commands/server_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "abstract_unit"
require "env_helpers"
require "rails/command"
diff --git a/railties/test/configuration/middleware_stack_proxy_test.rb b/railties/test/configuration/middleware_stack_proxy_test.rb
index 559ce72693..bc72b7f0c9 100644
--- a/railties/test/configuration/middleware_stack_proxy_test.rb
+++ b/railties/test/configuration/middleware_stack_proxy_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "active_support"
require "active_support/testing/autorun"
require "rails/configuration"
diff --git a/railties/test/console_helpers.rb b/railties/test/console_helpers.rb
index 4b11afa511..8350fce5ee 100644
--- a/railties/test/console_helpers.rb
+++ b/railties/test/console_helpers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
begin
require "pty"
rescue LoadError
@@ -7,7 +9,7 @@ module ConsoleHelpers
def assert_output(expected, io, timeout = 10)
timeout = Time.now + timeout
- output = ""
+ output = "".dup
until output.include?(expected) || Time.now > timeout
if IO.select([io], [], [], 0.1)
output << io.read(1)
diff --git a/railties/test/engine/commands_test.rb b/railties/test/engine/commands_test.rb
index 018c7c949e..aeb64d445b 100644
--- a/railties/test/engine/commands_test.rb
+++ b/railties/test/engine/commands_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "abstract_unit"
require "console_helpers"
diff --git a/railties/test/engine_test.rb b/railties/test/engine_test.rb
index 248afa2d2c..4bd8a07085 100644
--- a/railties/test/engine_test.rb
+++ b/railties/test/engine_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "abstract_unit"
class EngineTest < ActiveSupport::TestCase
diff --git a/railties/test/env_helpers.rb b/railties/test/env_helpers.rb
index 1f64d5fda3..336832b867 100644
--- a/railties/test/env_helpers.rb
+++ b/railties/test/env_helpers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "rails"
module EnvHelpers
diff --git a/railties/test/fixtures/about_yml_plugins/bad_about_yml/init.rb b/railties/test/fixtures/about_yml_plugins/bad_about_yml/init.rb
index 636bc1a8ab..1a82a2bdd4 100644
--- a/railties/test/fixtures/about_yml_plugins/bad_about_yml/init.rb
+++ b/railties/test/fixtures/about_yml_plugins/bad_about_yml/init.rb
@@ -1 +1,3 @@
+# frozen_string_literal: true
+
# intentionally empty
diff --git a/railties/test/fixtures/about_yml_plugins/plugin_without_about_yml/init.rb b/railties/test/fixtures/about_yml_plugins/plugin_without_about_yml/init.rb
index 636bc1a8ab..1a82a2bdd4 100644
--- a/railties/test/fixtures/about_yml_plugins/plugin_without_about_yml/init.rb
+++ b/railties/test/fixtures/about_yml_plugins/plugin_without_about_yml/init.rb
@@ -1 +1,3 @@
+# frozen_string_literal: true
+
# intentionally empty
diff --git a/railties/test/fixtures/lib/create_test_dummy_template.rb b/railties/test/fixtures/lib/create_test_dummy_template.rb
index e4378bbd1a..b9eb6a912d 100644
--- a/railties/test/fixtures/lib/create_test_dummy_template.rb
+++ b/railties/test/fixtures/lib/create_test_dummy_template.rb
@@ -1 +1,3 @@
+# frozen_string_literal: true
+
create_dummy_app("spec/dummy")
diff --git a/railties/test/fixtures/lib/generators/active_record/fixjour_generator.rb b/railties/test/fixtures/lib/generators/active_record/fixjour_generator.rb
index 1139350b94..f196971f20 100644
--- a/railties/test/fixtures/lib/generators/active_record/fixjour_generator.rb
+++ b/railties/test/fixtures/lib/generators/active_record/fixjour_generator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "rails/generators/active_record"
module ActiveRecord
diff --git a/railties/test/fixtures/lib/generators/fixjour_generator.rb b/railties/test/fixtures/lib/generators/fixjour_generator.rb
index ef3e9edbed..22197835a8 100644
--- a/railties/test/fixtures/lib/generators/fixjour_generator.rb
+++ b/railties/test/fixtures/lib/generators/fixjour_generator.rb
@@ -1,2 +1,4 @@
+# frozen_string_literal: true
+
class FixjourGenerator < Rails::Generators::NamedBase
end
diff --git a/railties/test/fixtures/lib/generators/model_generator.rb b/railties/test/fixtures/lib/generators/model_generator.rb
index 0905fa9631..3009472c3d 100644
--- a/railties/test/fixtures/lib/generators/model_generator.rb
+++ b/railties/test/fixtures/lib/generators/model_generator.rb
@@ -1 +1,3 @@
+# frozen_string_literal: true
+
raise "I should never be loaded"
diff --git a/railties/test/fixtures/lib/generators/usage_template/usage_template_generator.rb b/railties/test/fixtures/lib/generators/usage_template/usage_template_generator.rb
index 701515440a..5a847a8bd2 100644
--- a/railties/test/fixtures/lib/generators/usage_template/usage_template_generator.rb
+++ b/railties/test/fixtures/lib/generators/usage_template/usage_template_generator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "rails/generators"
class UsageTemplateGenerator < Rails::Generators::Base
diff --git a/railties/test/fixtures/lib/rails/generators/foobar/foobar_generator.rb b/railties/test/fixtures/lib/rails/generators/foobar/foobar_generator.rb
index d1de8c56fa..159843866c 100644
--- a/railties/test/fixtures/lib/rails/generators/foobar/foobar_generator.rb
+++ b/railties/test/fixtures/lib/rails/generators/foobar/foobar_generator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Foobar
class FoobarGenerator < Rails::Generators::Base
end
diff --git a/railties/test/fixtures/lib/template.rb b/railties/test/fixtures/lib/template.rb
index c14a1a8784..44083c25e8 100644
--- a/railties/test/fixtures/lib/template.rb
+++ b/railties/test/fixtures/lib/template.rb
@@ -1 +1,3 @@
+# frozen_string_literal: true
+
say "It works from file!"
diff --git a/railties/test/generators/actions_test.rb b/railties/test/generators/actions_test.rb
index 03b29be907..e2e5aec805 100644
--- a/railties/test/generators/actions_test.rb
+++ b/railties/test/generators/actions_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "generators/generators_test_helper"
require "rails/generators/rails/app/app_generator"
require "env_helpers"
@@ -139,14 +141,14 @@ class ActionsTest < Rails::Generators::TestCase
run_generator
autoload_paths = 'config.autoload_paths += %w["#{Rails.root}/app/extras"]'
action :environment, autoload_paths
- assert_file "config/application.rb", / class Application < Rails::Application\n #{Regexp.escape(autoload_paths)}/
+ assert_file "config/application.rb", / class Application < Rails::Application\n #{Regexp.escape(autoload_paths)}\n/
end
def test_environment_should_include_data_in_environment_initializer_block_with_env_option
run_generator
autoload_paths = 'config.autoload_paths += %w["#{Rails.root}/app/extras"]'
action :environment, autoload_paths, env: "development"
- assert_file "config/environments/development.rb", /Rails\.application\.configure do\n #{Regexp.escape(autoload_paths)}/
+ assert_file "config/environments/development.rb", /Rails\.application\.configure do\n #{Regexp.escape(autoload_paths)}\n/
end
def test_environment_with_block_should_include_block_contents_in_environment_initializer_block
@@ -163,6 +165,26 @@ class ActionsTest < Rails::Generators::TestCase
end
end
+ def test_environment_with_block_should_include_block_contents_with_multiline_data_in_environment_initializer_block
+ run_generator
+ data = <<-RUBY
+ config.encoding = "utf-8"
+ config.time_zone = "UTC"
+ RUBY
+ action(:environment) { data }
+ assert_file "config/application.rb", / class Application < Rails::Application\n#{Regexp.escape(data.strip_heredoc.indent(4))}/
+ end
+
+ def test_environment_should_include_block_contents_with_multiline_data_in_environment_initializer_block_with_env_option
+ run_generator
+ data = <<-RUBY
+ config.encoding = "utf-8"
+ config.time_zone = "UTC"
+ RUBY
+ action(:environment, nil, env: "development") { data }
+ assert_file "config/environments/development.rb", /Rails\.application\.configure do\n#{Regexp.escape(data.strip_heredoc.indent(2))}/
+ end
+
def test_git_with_symbol_should_run_command_using_git_scm
assert_called_with(generator, :run, ["git init"]) do
action :git, :init
@@ -177,22 +199,62 @@ class ActionsTest < Rails::Generators::TestCase
def test_vendor_should_write_data_to_file_in_vendor
action :vendor, "vendor_file.rb", "# vendor data"
- assert_file "vendor/vendor_file.rb", "# vendor data"
+ assert_file "vendor/vendor_file.rb", "# vendor data\n"
+ end
+
+ def test_vendor_should_write_data_to_file_with_block_in_vendor
+ code = <<-RUBY
+ puts "one"
+ puts "two"
+ puts "three"
+ RUBY
+ action(:vendor, "vendor_file.rb") { code }
+ assert_file "vendor/vendor_file.rb", code.strip_heredoc
end
def test_lib_should_write_data_to_file_in_lib
action :lib, "my_library.rb", "class MyLibrary"
- assert_file "lib/my_library.rb", "class MyLibrary"
+ assert_file "lib/my_library.rb", "class MyLibrary\n"
+ end
+
+ def test_lib_should_write_data_to_file_with_block_in_lib
+ code = <<-RUBY
+ class MyLib
+ MY_CONSTANT = 123
+ end
+ RUBY
+ action(:lib, "my_library.rb") { code }
+ assert_file "lib/my_library.rb", code.strip_heredoc
end
def test_rakefile_should_write_date_to_file_in_lib_tasks
action :rakefile, "myapp.rake", "task run: [:environment]"
- assert_file "lib/tasks/myapp.rake", "task run: [:environment]"
+ assert_file "lib/tasks/myapp.rake", "task run: [:environment]\n"
+ end
+
+ def test_rakefile_should_write_date_to_file_with_block_in_lib_tasks
+ code = <<-RUBY
+ task rock: :environment do
+ puts "Rockin'"
+ end
+ RUBY
+ action(:rakefile, "myapp.rake") { code }
+ assert_file "lib/tasks/myapp.rake", code.strip_heredoc
end
def test_initializer_should_write_date_to_file_in_config_initializers
action :initializer, "constants.rb", "MY_CONSTANT = 42"
- assert_file "config/initializers/constants.rb", "MY_CONSTANT = 42"
+ assert_file "config/initializers/constants.rb", "MY_CONSTANT = 42\n"
+ end
+
+ def test_initializer_should_write_date_to_file_with_block_in_config_initializers
+ code = <<-RUBY
+ MyLib.configure do |config|
+ config.value = 123
+ end
+ RUBY
+ action(:initializer, "constants.rb") { code }
+ assert_file "config/initializers/constants.rb", code.strip_heredoc
end
def test_generate_should_run_script_generate_with_argument_and_options
@@ -321,6 +383,7 @@ class ActionsTest < Rails::Generators::TestCase
File.open(route_path, "wb") { |file| file.write(content) }
routes = <<-F
+# frozen_string_literal: true
Rails.application.routes.draw do
root 'welcome#index'
end
@@ -331,6 +394,7 @@ F
action :route, "resources :product_lines"
routes = <<-F
+# frozen_string_literal: true
Rails.application.routes.draw do
resources :product_lines
root 'welcome#index'
diff --git a/railties/test/generators/api_app_generator_test.rb b/railties/test/generators/api_app_generator_test.rb
index a19e0f0dd8..d141b1d4b4 100644
--- a/railties/test/generators/api_app_generator_test.rb
+++ b/railties/test/generators/api_app_generator_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "generators/generators_test_helper"
require "rails/generators/rails/app/app_generator"
@@ -33,26 +35,26 @@ class ApiAppGeneratorTest < Rails::Generators::TestCase
def test_api_modified_files
run_generator
+ assert_file ".gitignore" do |content|
+ assert_no_match(/\/public\/assets/, content)
+ end
+
assert_file "Gemfile" do |content|
assert_no_match(/gem 'coffee-rails'/, content)
assert_no_match(/gem 'sass-rails'/, content)
assert_no_match(/gem 'web-console'/, content)
+ assert_no_match(/gem 'capybara'/, content)
+ assert_no_match(/gem 'selenium-webdriver'/, content)
assert_match(/# gem 'jbuilder'/, content)
+ assert_match(/# gem 'rack-cors'/, content)
end
- assert_file "config/application.rb" do |content|
- assert_match(/config.api_only = true/, content)
- end
-
- assert_file "config/initializers/cors.rb"
-
- assert_file "config/initializers/wrap_parameters.rb"
-
+ assert_file "config/application.rb", /config\.api_only = true/
assert_file "app/controllers/application_controller.rb", /ActionController::API/
end
def test_generator_if_skip_action_cable_is_given
- run_generator [destination_root, "--skip-action-cable"]
+ run_generator [destination_root, "--api", "--skip-action-cable"]
assert_file "config/application.rb", /#\s+require\s+["']action_cable\/engine["']/
assert_no_file "config/cable.yml"
assert_no_file "app/channels"
@@ -65,7 +67,7 @@ class ApiAppGeneratorTest < Rails::Generators::TestCase
run_generator
generator = Rails::Generators::AppGenerator.new ["rails"],
- { api: true, update: true }, destination_root: destination_root, shell: @shell
+ { api: true, update: true }, { destination_root: destination_root, shell: @shell }
quietly { generator.send(:update_config_files) }
assert_no_file "config/initializers/cookies_serializer.rb"
@@ -76,7 +78,7 @@ class ApiAppGeneratorTest < Rails::Generators::TestCase
run_generator
generator = Rails::Generators::AppGenerator.new ["rails"],
- { api: true, update: true }, destination_root: destination_root, shell: @shell
+ { api: true, update: true }, { destination_root: destination_root, shell: @shell }
quietly { generator.send(:update_bin_files) }
assert_no_file "bin/yarn"
@@ -85,20 +87,49 @@ class ApiAppGeneratorTest < Rails::Generators::TestCase
private
def default_files
- files = %W(
- .gitignore
+ %w(.gitignore
+ .ruby-version
+ README.md
Gemfile
Rakefile
config.ru
+ app/channels
app/controllers
app/mailers
app/models
+ app/views/layouts
app/views/layouts/mailer.html.erb
app/views/layouts/mailer.text.erb
+ bin/bundle
+ bin/rails
+ bin/rake
+ bin/setup
+ bin/update
+ config/application.rb
+ config/boot.rb
+ config/cable.yml
+ config/environment.rb
config/environments
+ config/environments/development.rb
+ config/environments/production.rb
+ config/environments/test.rb
config/initializers
+ config/initializers/application_controller_renderer.rb
+ config/initializers/backtrace_silencers.rb
+ config/initializers/cors.rb
+ config/initializers/filter_parameter_logging.rb
+ config/initializers/inflections.rb
+ config/initializers/mime_types.rb
+ config/initializers/wrap_parameters.rb
config/locales
+ config/locales/en.yml
+ config/puma.rb
+ config/routes.rb
+ config/secrets.yml
+ config/spring.rb
+ config/storage.yml
db
+ db/seeds.rb
lib
lib/tasks
log
@@ -109,8 +140,6 @@ class ApiAppGeneratorTest < Rails::Generators::TestCase
tmp
vendor
)
- files.concat %w(bin/bundle bin/rails bin/rake)
- files
end
def skipped_files
@@ -128,7 +157,7 @@ class ApiAppGeneratorTest < Rails::Generators::TestCase
public/500.html
public/apple-touch-icon-precomposed.png
public/apple-touch-icon.png
- public/favicon.icon
+ public/favicon.ico
package.json
)
end
diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb
index f243da5815..b50b9364d0 100644
--- a/railties/test/generators/app_generator_test.rb
+++ b/railties/test/generators/app_generator_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "generators/generators_test_helper"
require "rails/generators/rails/app/app_generator"
require "generators/shared_generator_tests"
@@ -64,6 +66,7 @@ DEFAULT_APP_FILES = %w(
config/routes.rb
config/secrets.yml
config/spring.rb
+ config/storage.yml
db
db/seeds.rb
lib
@@ -245,7 +248,7 @@ class AppGeneratorTest < Rails::Generators::TestCase
assert_no_file "#{app_root}/config/initializers/new_framework_defaults_5_2.rb"
stub_rails_application(app_root) do
- generator = Rails::Generators::AppGenerator.new ["rails"], { update: true }, destination_root: app_root, shell: @shell
+ generator = Rails::Generators::AppGenerator.new ["rails"], { update: true }, { destination_root: app_root, shell: @shell }
generator.send(:app_const)
quietly { generator.send(:update_config_files) }
@@ -393,6 +396,16 @@ class AppGeneratorTest < Rails::Generators::TestCase
end
end
+ def test_default_frameworks_are_required_when_others_are_removed
+ run_generator [destination_root, "--skip-active-record", "--skip-action-mailer", "--skip-action-cable", "--skip-sprockets", "--skip-test"]
+ assert_file "config/application.rb", /require\s+["']rails["']/
+ assert_file "config/application.rb", /require\s+["']active_model\/railtie["']/
+ assert_file "config/application.rb", /require\s+["']active_job\/railtie["']/
+ assert_file "config/application.rb", /require\s+["']action_controller\/railtie["']/
+ assert_file "config/application.rb", /require\s+["']action_view\/railtie["']/
+ assert_file "config/application.rb", /require\s+["']active_storage\/engine["']/
+ end
+
def test_generator_defaults_to_puma_version
run_generator [destination_root]
assert_gem "puma", "'~> 3.7'"
@@ -448,22 +461,26 @@ class AppGeneratorTest < Rails::Generators::TestCase
def test_generator_if_skip_sprockets_is_given
run_generator [destination_root, "--skip-sprockets"]
+
assert_no_file "config/initializers/assets.rb"
- assert_file "config/application.rb" do |content|
- assert_match(/#\s+require\s+["']sprockets\/railtie["']/, content)
- end
+
+ assert_file "config/application.rb", /#\s+require\s+["']sprockets\/railtie["']/
+
assert_file "Gemfile" do |content|
assert_no_match(/sass-rails/, content)
assert_no_match(/uglifier/, content)
assert_no_match(/coffee-rails/, content)
end
+
assert_file "config/environments/development.rb" do |content|
- assert_no_match(/config\.assets\.debug = true/, content)
+ assert_no_match(/config\.assets\.debug/, content)
end
+
assert_file "config/environments/production.rb" do |content|
- assert_no_match(/config\.assets\.digest = true/, content)
- assert_no_match(/config\.assets\.js_compressor = :uglifier/, content)
- assert_no_match(/config\.assets\.css_compressor = :sass/, content)
+ assert_no_match(/config\.assets\.digest/, content)
+ assert_no_match(/config\.assets\.js_compressor/, content)
+ assert_no_match(/config\.assets\.css_compressor/, content)
+ assert_no_match(/config\.assets\.compile/, content)
end
end
@@ -472,7 +489,8 @@ class AppGeneratorTest < Rails::Generators::TestCase
assert_file "config/application.rb", /#\s+require\s+["']action_cable\/engine["']/
assert_no_file "config/cable.yml"
assert_no_file "app/assets/javascripts/cable.js"
- assert_no_file "app/channels"
+ assert_no_directory "app/assets/javascripts/channels"
+ assert_no_directory "app/channels"
assert_file "Gemfile" do |content|
assert_no_match(/redis/, content)
end
@@ -485,10 +503,15 @@ class AppGeneratorTest < Rails::Generators::TestCase
def test_generator_if_skip_test_is_given
run_generator [destination_root, "--skip-test"]
+
+ assert_file "config/application.rb", /#\s+require\s+["']rails\/test_unit\/railtie["']/
+
assert_file "Gemfile" do |content|
assert_no_match(/capybara/, content)
assert_no_match(/selenium-webdriver/, content)
end
+
+ assert_no_directory("test")
end
def test_generator_if_skip_system_test_is_given
@@ -497,6 +520,10 @@ class AppGeneratorTest < Rails::Generators::TestCase
assert_no_match(/capybara/, content)
assert_no_match(/selenium-webdriver/, content)
end
+
+ assert_directory("test")
+
+ assert_no_directory("test/system")
end
def test_does_not_generate_system_test_files_if_skip_system_test_is_given
@@ -510,14 +537,6 @@ class AppGeneratorTest < Rails::Generators::TestCase
end
end
- def test_generator_if_api_is_given
- run_generator [destination_root, "--api"]
- assert_file "Gemfile" do |content|
- assert_no_match(/capybara/, content)
- assert_no_match(/selenium-webdriver/, content)
- end
- end
-
def test_inclusion_of_javascript_runtime
run_generator
if defined?(JRUBY_VERSION)
@@ -565,27 +584,6 @@ class AppGeneratorTest < Rails::Generators::TestCase
end
end
- def test_generator_for_yarn
- run_generator([destination_root])
- assert_file "package.json", /dependencies/
- assert_file "config/initializers/assets.rb", /node_modules/
- end
-
- def test_generator_for_yarn_skipped
- run_generator([destination_root, "--skip-yarn"])
- assert_no_file "package.json"
- assert_no_file "bin/yarn"
-
- assert_file "config/initializers/assets.rb" do |content|
- assert_no_match(/node_modules/, content)
- end
-
- assert_file ".gitignore" do |content|
- assert_no_match(/node_modules/, content)
- assert_no_match(/yarn-error\.log/, content)
- end
- end
-
def test_inclusion_of_jbuilder
run_generator
assert_gem "jbuilder"
@@ -653,18 +651,6 @@ class AppGeneratorTest < Rails::Generators::TestCase
assert_file "lib/test_file.rb", "heres test data"
end
- def test_tests_are_removed_from_frameworks_if_skip_test_is_given
- run_generator [destination_root, "--skip-test"]
- assert_file "config/application.rb", /#\s+require\s+["']rails\/test_unit\/railtie["']/
- end
-
- def test_no_active_record_or_tests_if_skips_given
- run_generator [destination_root, "--skip-test", "--skip-active-record"]
- assert_file "config/application.rb", /#\s+require\s+["']rails\/test_unit\/railtie["']/
- assert_file "config/application.rb", /#\s+require\s+["']active_record\/railtie["']/
- assert_file "config/application.rb", /\s+require\s+["']active_job\/railtie["']/
- end
-
def test_pretend_option
output = run_generator [File.join(destination_root, "myapp"), "--pretend"]
assert_no_match(/run bundle install/, output)
@@ -862,7 +848,7 @@ class AppGeneratorTest < Rails::Generators::TestCase
def test_after_bundle_callback
path = "http://example.org/rails_template"
- template = %{ after_bundle { run 'echo ran after_bundle' } }
+ template = %{ after_bundle { run 'echo ran after_bundle' } }.dup
template.instance_eval "def read; self; end" # Make the string respond to read
check_open = -> *args do
@@ -895,18 +881,6 @@ class AppGeneratorTest < Rails::Generators::TestCase
assert_directory("test/system")
end
- def test_system_tests_are_not_generated_on_system_test_skip
- run_generator [destination_root, "--skip-system-test"]
-
- assert_no_directory("test/system")
- end
-
- def test_system_tests_are_not_generated_on_test_skip
- run_generator [destination_root, "--skip-test"]
-
- assert_no_directory("test/system")
- end
-
private
def stub_rails_application(root)
Rails.application.config.root = root
diff --git a/railties/test/generators/application_record_generator_test.rb b/railties/test/generators/application_record_generator_test.rb
index b734d786c0..2c0aa7211b 100644
--- a/railties/test/generators/application_record_generator_test.rb
+++ b/railties/test/generators/application_record_generator_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "generators/generators_test_helper"
require "rails/generators/rails/application_record/application_record_generator"
diff --git a/railties/test/generators/argv_scrubber_test.rb b/railties/test/generators/argv_scrubber_test.rb
index 7f4295a20f..3f4c985a33 100644
--- a/railties/test/generators/argv_scrubber_test.rb
+++ b/railties/test/generators/argv_scrubber_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "active_support/test_case"
require "active_support/testing/autorun"
require "rails/generators/rails/app/app_generator"
diff --git a/railties/test/generators/assets_generator_test.rb b/railties/test/generators/assets_generator_test.rb
index 1c02c67e42..3cec41dbf8 100644
--- a/railties/test/generators/assets_generator_test.rb
+++ b/railties/test/generators/assets_generator_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "generators/generators_test_helper"
require "rails/generators/rails/assets/assets_generator"
diff --git a/railties/test/generators/channel_generator_test.rb b/railties/test/generators/channel_generator_test.rb
index af68a9c49f..e238197eba 100644
--- a/railties/test/generators/channel_generator_test.rb
+++ b/railties/test/generators/channel_generator_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "generators/generators_test_helper"
require "rails/generators/channel/channel_generator"
diff --git a/railties/test/generators/controller_generator_test.rb b/railties/test/generators/controller_generator_test.rb
index af86a0136f..a3218951a6 100644
--- a/railties/test/generators/controller_generator_test.rb
+++ b/railties/test/generators/controller_generator_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "generators/generators_test_helper"
require "rails/generators/rails/controller/controller_generator"
@@ -100,4 +102,11 @@ class ControllerGeneratorTest < Rails::Generators::TestCase
assert_match(/^ namespace :admin do\n get 'dashboard\/index'\n end$/, route)
end
end
+
+ def test_namespaced_routes_with_multiple_actions_are_created_in_routes
+ run_generator ["admin/dashboard", "index", "show"]
+ assert_file "config/routes.rb" do |route|
+ assert_match(/^ namespace :admin do\n get 'dashboard\/index'\n get 'dashboard\/show'\n end$/, route)
+ end
+ end
end
diff --git a/railties/test/generators/create_migration_test.rb b/railties/test/generators/create_migration_test.rb
index c7b0237f02..3cb7fd6baa 100644
--- a/railties/test/generators/create_migration_test.rb
+++ b/railties/test/generators/create_migration_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "generators/generators_test_helper"
require "rails/generators/rails/migration/migration_generator"
@@ -51,7 +53,7 @@ class CreateMigrationTest < Rails::Generators::TestCase
end
def test_invoke_pretended
- create_migration(default_destination_path, {}, pretend: true)
+ create_migration(default_destination_path, {}, { pretend: true })
assert_no_file @migration.destination
end
@@ -92,7 +94,7 @@ class CreateMigrationTest < Rails::Generators::TestCase
def test_invoke_forced_pretended_when_exists_not_identical
migration_exists!
- create_migration(default_destination_path, { force: true }, pretend: true) do
+ create_migration(default_destination_path, { force: true }, { pretend: true }) do
"different content"
end
@@ -104,7 +106,7 @@ class CreateMigrationTest < Rails::Generators::TestCase
def test_invoke_skipped_when_exists_not_identical
migration_exists!
- create_migration(default_destination_path, {}, skip: true) { "different content" }
+ create_migration(default_destination_path, {}, { skip: true }) { "different content" }
assert_match(/skip db\/migrate\/2_create_articles\.rb\n/, invoke!)
assert_no_file @migration.destination
@@ -120,7 +122,7 @@ class CreateMigrationTest < Rails::Generators::TestCase
def test_revoke_pretended
migration_exists!
- create_migration(default_destination_path, {}, pretend: true)
+ create_migration(default_destination_path, {}, { pretend: true })
assert_match(/remove db\/migrate\/1_create_articles\.rb\n/, revoke!)
assert_file @existing_migration.destination
diff --git a/railties/test/generators/encrypted_secrets_generator_test.rb b/railties/test/generators/encrypted_secrets_generator_test.rb
index 21fdcab19f..205827e498 100644
--- a/railties/test/generators/encrypted_secrets_generator_test.rb
+++ b/railties/test/generators/encrypted_secrets_generator_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "generators/generators_test_helper"
require "rails/generators/rails/encrypted_secrets/encrypted_secrets_generator"
diff --git a/railties/test/generators/generated_attribute_test.rb b/railties/test/generators/generated_attribute_test.rb
index 97847c8624..c6f7bdeda1 100644
--- a/railties/test/generators/generated_attribute_test.rb
+++ b/railties/test/generators/generated_attribute_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "generators/generators_test_helper"
require "rails/generators/generated_attribute"
diff --git a/railties/test/generators/generator_generator_test.rb b/railties/test/generators/generator_generator_test.rb
index 5ff8bb0357..eaa964cabc 100644
--- a/railties/test/generators/generator_generator_test.rb
+++ b/railties/test/generators/generator_generator_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "generators/generators_test_helper"
require "rails/generators/rails/generator/generator_generator"
diff --git a/railties/test/generators/generator_test.rb b/railties/test/generators/generator_test.rb
index 4444b3a56e..5f7daf5ac3 100644
--- a/railties/test/generators/generator_test.rb
+++ b/railties/test/generators/generator_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "active_support/test_case"
require "active_support/testing/autorun"
require "rails/generators/app_base"
diff --git a/railties/test/generators/generators_test_helper.rb b/railties/test/generators/generators_test_helper.rb
index 5fb331e197..cb5d8da7b1 100644
--- a/railties/test/generators/generators_test_helper.rb
+++ b/railties/test/generators/generators_test_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "abstract_unit"
require "active_support/core_ext/module/remove_method"
require "active_support/testing/stream"
diff --git a/railties/test/generators/helper_generator_test.rb b/railties/test/generators/helper_generator_test.rb
index d9e6e0a85a..4cdb6adf82 100644
--- a/railties/test/generators/helper_generator_test.rb
+++ b/railties/test/generators/helper_generator_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "generators/generators_test_helper"
require "rails/generators/rails/helper/helper_generator"
diff --git a/railties/test/generators/integration_test_generator_test.rb b/railties/test/generators/integration_test_generator_test.rb
index 9358b63bd4..82791f1a27 100644
--- a/railties/test/generators/integration_test_generator_test.rb
+++ b/railties/test/generators/integration_test_generator_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "generators/generators_test_helper"
require "rails/generators/rails/integration_test/integration_test_generator"
diff --git a/railties/test/generators/job_generator_test.rb b/railties/test/generators/job_generator_test.rb
index 68d158eb39..13276fac65 100644
--- a/railties/test/generators/job_generator_test.rb
+++ b/railties/test/generators/job_generator_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "generators/generators_test_helper"
require "rails/generators/job/job_generator"
diff --git a/railties/test/generators/mailer_generator_test.rb b/railties/test/generators/mailer_generator_test.rb
index 2ff03ea65e..ddac6e1a1e 100644
--- a/railties/test/generators/mailer_generator_test.rb
+++ b/railties/test/generators/mailer_generator_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "generators/generators_test_helper"
require "rails/generators/mailer/mailer_generator"
diff --git a/railties/test/generators/migration_generator_test.rb b/railties/test/generators/migration_generator_test.rb
index db0808750e..88a939a55a 100644
--- a/railties/test/generators/migration_generator_test.rb
+++ b/railties/test/generators/migration_generator_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "generators/generators_test_helper"
require "rails/generators/rails/migration/migration_generator"
diff --git a/railties/test/generators/model_generator_test.rb b/railties/test/generators/model_generator_test.rb
index 651713d3c0..516aa0704f 100644
--- a/railties/test/generators/model_generator_test.rb
+++ b/railties/test/generators/model_generator_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "generators/generators_test_helper"
require "rails/generators/rails/model/model_generator"
require "active_support/core_ext/string/strip"
diff --git a/railties/test/generators/named_base_test.rb b/railties/test/generators/named_base_test.rb
index 3015b5363b..67f05926e3 100644
--- a/railties/test/generators/named_base_test.rb
+++ b/railties/test/generators/named_base_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "generators/generators_test_helper"
require "rails/generators/rails/scaffold_controller/scaffold_controller_generator"
diff --git a/railties/test/generators/namespaced_generators_test.rb b/railties/test/generators/namespaced_generators_test.rb
index 9315a1b9da..205014c80a 100644
--- a/railties/test/generators/namespaced_generators_test.rb
+++ b/railties/test/generators/namespaced_generators_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "generators/generators_test_helper"
require "rails/generators/rails/controller/controller_generator"
require "rails/generators/rails/model/model_generator"
diff --git a/railties/test/generators/orm_test.rb b/railties/test/generators/orm_test.rb
index 88ae930554..6eaf2fbfd3 100644
--- a/railties/test/generators/orm_test.rb
+++ b/railties/test/generators/orm_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "generators/generators_test_helper"
require "rails/generators/rails/scaffold_controller/scaffold_controller_generator"
diff --git a/railties/test/generators/plugin_generator_test.rb b/railties/test/generators/plugin_generator_test.rb
index 1fa40f74b9..e94b1ac8fe 100644
--- a/railties/test/generators/plugin_generator_test.rb
+++ b/railties/test/generators/plugin_generator_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "generators/generators_test_helper"
require "rails/generators/rails/plugin/plugin_generator"
require "generators/shared_generator_tests"
@@ -24,6 +26,10 @@ class PluginGeneratorTest < Rails::Generators::TestCase
destination File.join(Rails.root, "tmp/bukkits")
arguments [destination_root]
+ def application_path
+ "#{destination_root}/test/dummy"
+ end
+
# brings setup, teardown, and some tests
include SharedGeneratorTests
@@ -75,6 +81,18 @@ class PluginGeneratorTest < Rails::Generators::TestCase
assert_no_file "bin/rails"
end
+ def test_generating_in_full_mode_with_almost_of_all_skip_options
+ run_generator [destination_root, "--full", "-M", "-O", "-C", "-S", "-T"]
+ assert_file "bin/rails" do |content|
+ assert_no_match(/\s+require\s+["']rails\/all["']/, content)
+ end
+ assert_file "bin/rails", /#\s+require\s+["']active_record\/railtie["']/
+ assert_file "bin/rails", /#\s+require\s+["']action_mailer\/railtie["']/
+ assert_file "bin/rails", /#\s+require\s+["']action_cable\/engine["']/
+ assert_file "bin/rails", /#\s+require\s+["']sprockets\/railtie["']/
+ assert_file "bin/rails", /#\s+require\s+["']rails\/test_unit\/railtie["']/
+ end
+
def test_generating_test_files_in_full_mode
run_generator [destination_root, "--full"]
assert_directory "test/integration/"
@@ -82,6 +100,11 @@ class PluginGeneratorTest < Rails::Generators::TestCase
assert_file "test/integration/navigation_test.rb", /ActionDispatch::IntegrationTest/
end
+ def test_inclusion_of_git_source
+ run_generator [destination_root]
+ assert_file "Gemfile", /git_source/
+ end
+
def test_inclusion_of_a_debugger
run_generator [destination_root, "--full"]
if defined?(JRUBY_VERSION) || RUBY_ENGINE == "rbx"
@@ -97,23 +120,44 @@ class PluginGeneratorTest < Rails::Generators::TestCase
run_generator [destination_root, "-T", "--full"]
assert_no_directory "test/integration/"
- assert_no_file "test"
+ assert_no_directory "test"
assert_file "Rakefile" do |contents|
assert_no_match(/APP_RAKEFILE/, contents)
end
+ assert_file "bin/rails" do |contents|
+ assert_no_match(/APP_PATH/, contents)
+ end
end
- def test_generating_adds_dummy_app_in_full_mode_without_sprockets
- run_generator [destination_root, "-S", "--full"]
+ def test_generating_adds_dummy_app_without_sprockets
+ run_generator [destination_root, "--skip-sprockets"]
+
+ assert_no_file "test/dummy/config/initializers/assets.rb"
- assert_file "test/dummy/config/environments/production.rb" do |contents|
- assert_no_match(/config\.assets/, contents)
+ assert_file "test/dummy/config/application.rb", /#\s+require\s+["']sprockets\/railtie["']/
+
+ assert_file "Gemfile" do |content|
+ assert_no_match(/sass-rails/, content)
+ assert_no_match(/uglifier/, content)
+ assert_no_match(/coffee-rails/, content)
+ end
+
+ assert_file "test/dummy/config/environments/development.rb" do |content|
+ assert_no_match(/config\.assets\.debug/, content)
+ end
+
+ assert_file "test/dummy/config/environments/production.rb" do |content|
+ assert_no_match(/config\.assets\.digest/, content)
+ assert_no_match(/config\.assets\.js_compressor/, content)
+ assert_no_match(/config\.assets\.css_compressor/, content)
+ assert_no_match(/config\.assets\.compile/, content)
end
end
def test_generating_adds_dummy_app_rake_tasks_without_unit_test_files
run_generator [destination_root, "-T", "--mountable", "--dummy-path", "my_dummy_app"]
assert_file "Rakefile", /APP_RAKEFILE/
+ assert_file "bin/rails", /APP_PATH/
end
def test_generating_adds_dummy_app_without_javascript_and_assets_deps
@@ -134,8 +178,8 @@ class PluginGeneratorTest < Rails::Generators::TestCase
def test_ensure_that_test_dummy_can_be_generated_from_a_template
FileUtils.cd(Rails.root)
run_generator([destination_root, "-m", "lib/create_test_dummy_template.rb", "--skip-test"])
- assert_file "spec/dummy"
- assert_no_file "test"
+ assert_directory "spec/dummy"
+ assert_no_directory "test"
end
def test_database_entry_is_generated_for_sqlite3_by_default_in_full_mode
@@ -169,9 +213,19 @@ class PluginGeneratorTest < Rails::Generators::TestCase
end
assert_file "test/dummy/config/environments/production.rb" do |content|
assert_match(/# config\.action_mailer\.raise_delivery_errors = false/, content)
+ assert_match(/^ config\.read_encrypted_secrets = true/, content)
end
end
+ def test_default_frameworks_are_required_when_others_are_removed
+ run_generator [destination_root, "--skip-active-record", "--skip-action-mailer", "--skip-action-cable", "--skip-sprockets"]
+ assert_file "test/dummy/config/application.rb", /require\s+["']rails["']/
+ assert_file "test/dummy/config/application.rb", /require\s+["']active_model\/railtie["']/
+ assert_file "test/dummy/config/application.rb", /require\s+["']active_job\/railtie["']/
+ assert_file "test/dummy/config/application.rb", /require\s+["']action_controller\/railtie["']/
+ assert_file "test/dummy/config/application.rb", /require\s+["']action_view\/railtie["']/
+ end
+
def test_active_record_is_removed_from_frameworks_if_skip_active_record_is_given
run_generator [destination_root, "--skip-active-record"]
assert_file "test/dummy/config/application.rb", /#\s+require\s+["']active_record\/railtie["']/
@@ -197,6 +251,18 @@ class PluginGeneratorTest < Rails::Generators::TestCase
assert_file "test/dummy/config/environments/production.rb" do |content|
assert_no_match(/config\.action_mailer/, content)
end
+ assert_no_directory "test/dummy/app/mailers"
+ end
+
+ def test_action_cable_is_removed_from_frameworks_if_skip_action_cable_is_given
+ run_generator [destination_root, "--skip-action-cable"]
+ assert_file "test/dummy/config/application.rb", /#\s+require\s+["']action_cable\/engine["']/
+ assert_no_file "test/dummy/config/cable.yml"
+ assert_no_file "test/dummy/app/assets/javascripts/cable.js"
+ assert_no_directory "test/dummy/app/channels"
+ assert_file "Gemfile" do |content|
+ assert_no_match(/redis/, content)
+ end
end
def test_ensure_that_database_option_is_passed_to_app_generator
@@ -255,7 +321,8 @@ class PluginGeneratorTest < Rails::Generators::TestCase
run_generator [destination_root, "--full", "--skip_active_record"]
FileUtils.cd destination_root
quietly { system "bundle install" }
- assert_match(/1 runs, 1 assertions, 0 failures, 0 errors/, `bundle exec rake test 2>&1`)
+ # FIXME: Active Storage will provoke a test error without ActiveRecord (fix by allowing to skip active storage)
+ assert_match(/1 runs, 0 assertions, 0 failures, 1 errors/, `bundle exec rake test 2>&1`)
end
def test_ensure_that_migration_tasks_work_with_mountable_option
@@ -276,7 +343,7 @@ class PluginGeneratorTest < Rails::Generators::TestCase
assert_file "app/views"
assert_file "app/helpers"
assert_file "app/mailers"
- assert_file "bin/rails"
+ assert_file "bin/rails", /\s+require\s+["']rails\/all["']/
assert_file "config/routes.rb", /Rails.application.routes.draw do/
assert_file "lib/bukkits/engine.rb", /module Bukkits\n class Engine < ::Rails::Engine\n end\nend/
assert_file "lib/bukkits.rb", /require "bukkits\/engine"/
@@ -458,10 +525,9 @@ class PluginGeneratorTest < Rails::Generators::TestCase
def test_creating_dummy_without_tests_but_with_dummy_path
run_generator [destination_root, "--dummy_path", "spec/dummy", "--skip-test"]
- assert_file "spec/dummy"
- assert_file "spec/dummy/config/application.rb"
- assert_no_file "test"
- assert_no_file "test/test_helper.rb"
+ assert_directory "spec/dummy"
+ assert_file "spec/dummy/config/application.rb", /#\s+require\s+["']rails\/test_unit\/railtie["']/
+ assert_no_directory "test"
assert_file ".gitignore" do |contents|
assert_match(/spec\/dummy/, contents)
end
@@ -498,9 +564,9 @@ class PluginGeneratorTest < Rails::Generators::TestCase
def test_skipping_test_files
run_generator [destination_root, "--skip-test"]
- assert_no_file "test"
+ assert_no_directory "test"
assert_file ".gitignore" do |contents|
- assert_no_match(/test\dummy/, contents)
+ assert_no_match(/test\/dummy/, contents)
end
end
diff --git a/railties/test/generators/plugin_test_helper.rb b/railties/test/generators/plugin_test_helper.rb
index 8ac90e3484..528f8d88f9 100644
--- a/railties/test/generators/plugin_test_helper.rb
+++ b/railties/test/generators/plugin_test_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "abstract_unit"
require "tmpdir"
diff --git a/railties/test/generators/plugin_test_runner_test.rb b/railties/test/generators/plugin_test_runner_test.rb
index f9c860b04a..28a76f0617 100644
--- a/railties/test/generators/plugin_test_runner_test.rb
+++ b/railties/test/generators/plugin_test_runner_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "generators/plugin_test_helper"
class PluginTestRunnerTest < ActiveSupport::TestCase
diff --git a/railties/test/generators/resource_generator_test.rb b/railties/test/generators/resource_generator_test.rb
index e976e58180..63a2cd3869 100644
--- a/railties/test/generators/resource_generator_test.rb
+++ b/railties/test/generators/resource_generator_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "generators/generators_test_helper"
require "rails/generators/rails/resource/resource_generator"
diff --git a/railties/test/generators/scaffold_controller_generator_test.rb b/railties/test/generators/scaffold_controller_generator_test.rb
index 9971626f9f..384524aba9 100644
--- a/railties/test/generators/scaffold_controller_generator_test.rb
+++ b/railties/test/generators/scaffold_controller_generator_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "generators/generators_test_helper"
require "rails/generators/rails/scaffold_controller/scaffold_controller_generator"
diff --git a/railties/test/generators/scaffold_generator_test.rb b/railties/test/generators/scaffold_generator_test.rb
index 9926d58d16..03322c1c59 100644
--- a/railties/test/generators/scaffold_generator_test.rb
+++ b/railties/test/generators/scaffold_generator_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "generators/generators_test_helper"
require "rails/generators/rails/scaffold/scaffold_generator"
diff --git a/railties/test/generators/shared_generator_tests.rb b/railties/test/generators/shared_generator_tests.rb
index 5e75879964..6c0775b50e 100644
--- a/railties/test/generators/shared_generator_tests.rb
+++ b/railties/test/generators/shared_generator_tests.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
#
# Tests, setup, and teardown common to the application and plugin generator suites.
#
@@ -20,6 +22,10 @@ module SharedGeneratorTests
Rails.application = TestApp::Application.instance
end
+ def application_path
+ destination_root
+ end
+
def test_skeleton_is_created
run_generator
@@ -77,7 +83,7 @@ module SharedGeneratorTests
def test_template_is_executed_when_supplied_an_https_path
path = "https://gist.github.com/josevalim/103208/raw/"
- template = %{ say "It works!" }
+ template = %{ say "It works!" }.dup
template.instance_eval "def read; self; end" # Make the string respond to read
check_open = -> *args do
@@ -121,4 +127,30 @@ module SharedGeneratorTests
assert_no_file("app/models/concerns/.keep")
end
+
+ def test_generator_for_yarn
+ run_generator
+ assert_file "#{application_path}/package.json", /dependencies/
+ assert_file "#{application_path}/config/initializers/assets.rb", /node_modules/
+
+ assert_file ".gitignore" do |content|
+ assert_match(/node_modules/, content)
+ assert_match(/yarn-error\.log/, content)
+ end
+ end
+
+ def test_generator_for_yarn_skipped
+ run_generator([destination_root, "--skip-yarn"])
+ assert_no_file "#{application_path}/package.json"
+ assert_no_file "#{application_path}/bin/yarn"
+
+ assert_file "#{application_path}/config/initializers/assets.rb" do |content|
+ assert_no_match(/node_modules/, content)
+ end
+
+ assert_file ".gitignore" do |content|
+ assert_no_match(/node_modules/, content)
+ assert_no_match(/yarn-error\.log/, content)
+ end
+ end
end
diff --git a/railties/test/generators/system_test_generator_test.rb b/railties/test/generators/system_test_generator_test.rb
index 4622360244..efa70a050b 100644
--- a/railties/test/generators/system_test_generator_test.rb
+++ b/railties/test/generators/system_test_generator_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "generators/generators_test_helper"
require "rails/generators/rails/system_test/system_test_generator"
diff --git a/railties/test/generators/task_generator_test.rb b/railties/test/generators/task_generator_test.rb
index 2285534bb9..5f162919d8 100644
--- a/railties/test/generators/task_generator_test.rb
+++ b/railties/test/generators/task_generator_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "generators/generators_test_helper"
require "rails/generators/rails/task/task_generator"
diff --git a/railties/test/generators/test_runner_in_engine_test.rb b/railties/test/generators/test_runner_in_engine_test.rb
index 680dc2608e..0e15b5e388 100644
--- a/railties/test/generators/test_runner_in_engine_test.rb
+++ b/railties/test/generators/test_runner_in_engine_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "generators/plugin_test_helper"
class TestRunnerInEngineTest < ActiveSupport::TestCase
diff --git a/railties/test/generators_test.rb b/railties/test/generators_test.rb
index 5063e864ca..28e7617d7f 100644
--- a/railties/test/generators_test.rb
+++ b/railties/test/generators_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "generators/generators_test_helper"
require "rails/generators/rails/model/model_generator"
require "rails/generators/test_unit/model/model_generator"
diff --git a/railties/test/initializable_test.rb b/railties/test/initializable_test.rb
index 4b67c91cc5..59fee245f9 100644
--- a/railties/test/initializable_test.rb
+++ b/railties/test/initializable_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "abstract_unit"
require "rails/initializable"
diff --git a/railties/test/isolation/abstract_unit.rb b/railties/test/isolation/abstract_unit.rb
index 7496b5f84a..e79bf09654 100644
--- a/railties/test/isolation/abstract_unit.rb
+++ b/railties/test/isolation/abstract_unit.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Note:
# It is important to keep this file as light as possible
# the goal for tests that require this is to test booting up
@@ -66,7 +68,7 @@ module TestHelpers
end
def extract_body(response)
- "".tap do |body|
+ "".dup.tap do |body|
response[2].each { |chunk| body << chunk }
end
end
@@ -222,8 +224,8 @@ module TestHelpers
FileUtils.mkdir_p(dir)
app = File.readlines("#{app_path}/config/application.rb")
- app.insert(2, "$:.unshift(\"#{dir}/lib\")")
- app.insert(3, "require #{name.inspect}")
+ app.insert(4, "$:.unshift(\"#{dir}/lib\")")
+ app.insert(5, "require #{name.inspect}")
File.open("#{app_path}/config/application.rb", "r+") do |f|
f.puts app
@@ -313,8 +315,6 @@ class ActiveSupport::TestCase
include TestHelpers::Rack
include TestHelpers::Generation
include ActiveSupport::Testing::Stream
-
- self.test_order = :sorted
end
# Create a scope and build a fixture rails app
diff --git a/railties/test/json_params_parsing_test.rb b/railties/test/json_params_parsing_test.rb
index 7fff3bb465..65ad9673ff 100644
--- a/railties/test/json_params_parsing_test.rb
+++ b/railties/test/json_params_parsing_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "abstract_unit"
require "action_dispatch"
require "active_record"
diff --git a/railties/test/path_generation_test.rb b/railties/test/path_generation_test.rb
index c0b03d0c15..d4dfa8e4a6 100644
--- a/railties/test/path_generation_test.rb
+++ b/railties/test/path_generation_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "abstract_unit"
require "active_support/core_ext/object/with_options"
require "active_support/core_ext/object/json"
diff --git a/railties/test/paths_test.rb b/railties/test/paths_test.rb
index f3db0a51d2..854b4448a4 100644
--- a/railties/test/paths_test.rb
+++ b/railties/test/paths_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "abstract_unit"
require "rails/paths"
require "minitest/mock"
diff --git a/railties/test/rack_logger_test.rb b/railties/test/rack_logger_test.rb
index 33b4bc6a3a..e47f30d5b6 100644
--- a/railties/test/rack_logger_test.rb
+++ b/railties/test/rack_logger_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "abstract_unit"
require "active_support/testing/autorun"
require "active_support/test_case"
diff --git a/railties/test/rails_info_controller_test.rb b/railties/test/rails_info_controller_test.rb
index d795629ccd..878a238f8d 100644
--- a/railties/test/rails_info_controller_test.rb
+++ b/railties/test/rails_info_controller_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "abstract_unit"
module ActionController
diff --git a/railties/test/rails_info_test.rb b/railties/test/rails_info_test.rb
index 383adcc55d..227a739b71 100644
--- a/railties/test/rails_info_test.rb
+++ b/railties/test/rails_info_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "abstract_unit"
unless defined?(Rails) && defined?(Rails::Info)
diff --git a/railties/test/railties/engine_test.rb b/railties/test/railties/engine_test.rb
index 6f762d2d3f..e6964b4b18 100644
--- a/railties/test/railties/engine_test.rb
+++ b/railties/test/railties/engine_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "isolation/abstract_unit"
require "stringio"
require "rack/test"
@@ -136,7 +138,7 @@ module RailtiesTest
output = `bundle exec rake railties:install:migrations`.split("\n")
assert_match(/Copied migration \d+_create_users\.bukkits\.rb from bukkits/, output.first)
- assert_match(/Copied migration \d+_create_blogs\.blog_engine\.rb from blog_engine/, output.last)
+ assert_match(/Copied migration \d+_create_blogs\.blog_engine\.rb from blog_engine/, output.second)
end
end
@@ -171,7 +173,7 @@ module RailtiesTest
Dir.chdir(app_path) do
output = `bundle exec rake railties:install:migrations`.split("\n")
- assert_match(/Copied migration \d+_create_users\.core_engine\.rb from core_engine/, output.first)
+ assert_match(/Copied migration \d+_create_users\.core_engine\.rb from core_engine/, output.second)
assert_match(/Copied migration \d+_create_keys\.api_engine\.rb from api_engine/, output.last)
end
end
@@ -503,7 +505,7 @@ YAML
def call(env)
response = @app.call(env)
- response[2].each(&:upcase!)
+ response[2] = response[2].collect(&:upcase)
response
end
end
@@ -882,7 +884,17 @@ YAML
end
RUBY
- add_to_config "isolate_namespace AppTemplate"
+ engine "loaded_first" do |plugin|
+ plugin.write "lib/loaded_first.rb", <<-RUBY
+ module AppTemplate
+ module LoadedFirst
+ class Engine < ::Rails::Engine
+ isolate_namespace(AppTemplate)
+ end
+ end
+ end
+ RUBY
+ end
app_file "config/routes.rb", <<-RUBY
Rails.application.routes.draw do end
@@ -890,7 +902,7 @@ YAML
boot_rails
- assert_equal AppTemplate::Engine, AppTemplate.railtie_namespace
+ assert_equal AppTemplate::LoadedFirst::Engine, AppTemplate.railtie_namespace
end
test "properly reload routes" do
@@ -1285,10 +1297,10 @@ YAML
boot_rails
- get("/bukkits/bukkit", {}, "SCRIPT_NAME" => "/foo")
+ get("/bukkits/bukkit", {}, { "SCRIPT_NAME" => "/foo" })
assert_equal "/foo/bar", last_response.body
- get("/bar", {}, "SCRIPT_NAME" => "/foo")
+ get("/bar", {}, { "SCRIPT_NAME" => "/foo" })
assert_equal "/foo/bukkits/bukkit", last_response.body
end
@@ -1334,10 +1346,10 @@ YAML
boot_rails
- get("/bukkits/bukkit", {}, "SCRIPT_NAME" => "/foo")
+ get("/bukkits/bukkit", {}, { "SCRIPT_NAME" => "/foo" })
assert_equal "/foo/bar", last_response.body
- get("/bar", {}, "SCRIPT_NAME" => "/foo")
+ get("/bar", {}, { "SCRIPT_NAME" => "/foo" })
assert_equal "/foo/bukkits/bukkit", last_response.body
end
diff --git a/railties/test/railties/generators_test.rb b/railties/test/railties/generators_test.rb
index 5c691b9ec4..8383cb3050 100644
--- a/railties/test/railties/generators_test.rb
+++ b/railties/test/railties/generators_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
RAILS_ISOLATED_ENGINE = true
require "isolation/abstract_unit"
diff --git a/railties/test/railties/mounted_engine_test.rb b/railties/test/railties/mounted_engine_test.rb
index 6eb2c5acc6..48f0fbc80f 100644
--- a/railties/test/railties/mounted_engine_test.rb
+++ b/railties/test/railties/mounted_engine_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "isolation/abstract_unit"
module ApplicationTests
@@ -206,7 +208,7 @@ module ApplicationTests
assert_equal "/ada/blog/posts/1", last_response.body
# test generating engine's route from engine with default_url_options
- get "/john/blog/posts", {}, "SCRIPT_NAME" => "/foo"
+ get "/john/blog/posts", {}, { "SCRIPT_NAME" => "/foo" }
assert_equal "/foo/john/blog/posts/1", last_response.body
# test generating engine's route from application
@@ -220,10 +222,10 @@ module ApplicationTests
assert_equal "/john/blog/posts", last_response.body
# test generating engine's route from application with default_url_options
- get "/engine_route", {}, "SCRIPT_NAME" => "/foo"
+ get "/engine_route", {}, { "SCRIPT_NAME" => "/foo" }
assert_equal "/foo/anonymous/blog/posts", last_response.body
- get "/url_for_engine_route", {}, "SCRIPT_NAME" => "/foo"
+ get "/url_for_engine_route", {}, { "SCRIPT_NAME" => "/foo" }
assert_equal "/foo/john/blog/posts", last_response.body
# test generating application's route from engine
@@ -241,14 +243,14 @@ module ApplicationTests
assert_equal "/anonymous/blog/posts/1", last_response.body
# test generating engine's route from other engine with default_url_options
- get "/metrics/generate_blog_route", {}, "SCRIPT_NAME" => "/foo"
+ get "/metrics/generate_blog_route", {}, { "SCRIPT_NAME" => "/foo" }
assert_equal "/foo/anonymous/blog/posts/1", last_response.body
- get "/metrics/generate_blog_route_in_view", {}, "SCRIPT_NAME" => "/foo"
+ get "/metrics/generate_blog_route_in_view", {}, { "SCRIPT_NAME" => "/foo" }
assert_equal "/foo/anonymous/blog/posts/1", last_response.body
# test generating application's route from engine with default_url_options
- get "/someone/blog/generate_application_route", {}, "SCRIPT_NAME" => "/foo"
+ get "/someone/blog/generate_application_route", {}, { "SCRIPT_NAME" => "/foo" }
assert_equal "/foo/", last_response.body
# test polymorphic routes
diff --git a/railties/test/railties/railtie_test.rb b/railties/test/railties/railtie_test.rb
index 30cd525266..359ab0fdae 100644
--- a/railties/test/railties/railtie_test.rb
+++ b/railties/test/railties/railtie_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "isolation/abstract_unit"
module RailtiesTest
diff --git a/railties/test/secrets_test.rb b/railties/test/secrets_test.rb
index d78c253765..445e216f16 100644
--- a/railties/test/secrets_test.rb
+++ b/railties/test/secrets_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "abstract_unit"
require "isolation/abstract_unit"
require "rails/generators"
@@ -133,7 +135,7 @@ class Rails::SecretsTest < ActiveSupport::TestCase
api_key: 00112233445566778899aabbccddeeff…
end_of_secrets
- Rails::Secrets.write(secrets.force_encoding(Encoding::ASCII_8BIT))
+ Rails::Secrets.write(secrets.dup.force_encoding(Encoding::ASCII_8BIT))
Rails::Secrets.read_for_editing do |tmp_path|
assert_match(/production:\n\s*api_key: 00112233445566778899aabbccddeeff…\n/, File.read(tmp_path))
@@ -153,7 +155,7 @@ class Rails::SecretsTest < ActiveSupport::TestCase
Rails::Secrets.write(secrets)
Rails::Secrets.read_for_editing do |tmp_path|
- assert_equal(secrets.force_encoding(Encoding::ASCII_8BIT), IO.binread(tmp_path))
+ assert_equal(secrets.dup.force_encoding(Encoding::ASCII_8BIT), IO.binread(tmp_path))
end
assert_equal "00112233445566778899aabbccddeeff…\n", `bin/rails runner -e production "puts Rails.application.secrets.api_key"`
diff --git a/railties/test/test_unit/reporter_test.rb b/railties/test/test_unit/reporter_test.rb
index 42f973357b..ad852d0f35 100644
--- a/railties/test/test_unit/reporter_test.rb
+++ b/railties/test/test_unit/reporter_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "abstract_unit"
require "rails/test_unit/reporter"
require "minitest/mock"
diff --git a/railties/test/version_test.rb b/railties/test/version_test.rb
index 86a482e091..17a024fe7f 100644
--- a/railties/test/version_test.rb
+++ b/railties/test/version_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "abstract_unit"
class VersionTest < ActiveSupport::TestCase
diff --git a/tasks/release.rb b/tasks/release.rb
index d0c93da187..aa8ba44c1a 100644
--- a/tasks/release.rb
+++ b/tasks/release.rb
@@ -1,4 +1,6 @@
-FRAMEWORKS = %w( activesupport activemodel activerecord actionview actionpack activejob actionmailer actioncable railties )
+# frozen_string_literal: true
+
+FRAMEWORKS = %w( activesupport activemodel activerecord actionview actionpack activejob actionmailer actioncable activestorage railties )
FRAMEWORK_NAMES = Hash.new { |h, k| k.split(/(?<=active|action)/).map(&:capitalize).join(" ") }
root = File.expand_path("..", __dir__)
@@ -72,9 +74,9 @@ npm_version = version.gsub(/\./).with_index { |s, i| i >= 2 ? "-" : s }
task gem => %w(update_versions pkg) do
cmd = ""
- cmd << "cd #{framework} && " unless framework == "rails"
- cmd << "bundle exec rake package && " unless framework == "rails"
- cmd << "gem build #{gemspec} && mv #{framework}-#{version}.gem #{root}/pkg/"
+ cmd += "cd #{framework} && " unless framework == "rails"
+ cmd += "bundle exec rake package && " unless framework == "rails"
+ cmd += "gem build #{gemspec} && mv #{framework}-#{version}.gem #{root}/pkg/"
sh cmd
end
@@ -104,7 +106,7 @@ namespace :changelog do
current_contents = File.read(fname)
header = "## Rails #{version} (#{Date.today.strftime('%B %d, %Y')}) ##\n\n"
- header << "* No changes.\n\n\n" if current_contents =~ /\A##/
+ header += "* No changes.\n\n\n" if current_contents =~ /\A##/
contents = header + current_contents
File.open(fname, "wb") { |f| f.write contents }
end
diff --git a/tools/console b/tools/console
index 58c76fb1b6..ee08e22502 100755
--- a/tools/console
+++ b/tools/console
@@ -1,4 +1,6 @@
#!/usr/bin/env ruby
+# frozen_string_literal: true
+
require "bundler"
Bundler.setup
diff --git a/tools/profile b/tools/profile
index 01e513c67a..6fb571f43b 100755
--- a/tools/profile
+++ b/tools/profile
@@ -1,4 +1,6 @@
#!/usr/bin/env ruby
+# frozen_string_literal: true
+
# Profile require calls giving information about the time and the files that are called
# when loading the provided file.
#
diff --git a/tools/test.rb b/tools/test.rb
index 774e4ec6c7..1fd3ee30eb 100644
--- a/tools/test.rb
+++ b/tools/test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
$: << File.expand_path("test", COMPONENT_ROOT)
require "bundler"
diff --git a/version.rb b/version.rb
index 7bacf2e0ba..92b5e0392a 100644
--- a/version.rb
+++ b/version.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Rails
# Returns the version of the currently loaded Rails as a <tt>Gem::Version</tt>
def self.gem_version