aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.rubocop.yml10
-rw-r--r--.travis.yml2
-rw-r--r--Gemfile7
-rw-r--r--Rakefile2
-rw-r--r--actioncable/README.md2
-rw-r--r--actioncable/lib/action_cable/connection/base.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/railtie.rb6
-rw-r--r--actionmailer/test/message_delivery_test.rb5
-rw-r--r--actionpack/CHANGELOG.md8
-rw-r--r--actionpack/lib/action_controller/railtie.rb6
-rw-r--r--actionpack/lib/action_dispatch/testing/test_response.rb24
-rw-r--r--actionpack/test/dispatch/response_test.rb9
-rw-r--r--actionpack/test/dispatch/test_response_test.rb7
-rw-r--r--actionview/lib/action_view/helpers/form_helper.rb22
-rw-r--r--actionview/lib/action_view/renderer/partial_renderer.rb2
-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/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.rb30
-rw-r--r--activerecord/lib/active_record/associations/collection_proxy.rb5
-rw-r--r--activerecord/lib/active_record/attribute_methods/dirty.rb18
-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/migration/compatibility.rb2
-rw-r--r--activerecord/lib/active_record/reflection.rb26
-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/table_metadata.rb2
-rw-r--r--activerecord/lib/active_record/tasks/postgresql_database_tasks.rb4
-rw-r--r--activerecord/lib/active_record/transactions.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/json_test.rb13
-rw-r--r--activerecord/test/cases/associations/has_many_through_associations_test.rb8
-rw-r--r--activerecord/test/cases/associations/has_one_associations_test.rb2
-rw-r--r--activerecord/test/cases/associations/join_model_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/finder_test.rb2
-rw-r--r--activerecord/test/cases/json_shared_test_cases.rb9
-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/schema/schema.rb20
-rw-r--r--activestorage/README.md8
-rw-r--r--activestorage/Rakefile2
-rw-r--r--activestorage/app/controllers/active_storage/blobs_controller.rb3
-rw-r--r--activestorage/app/controllers/active_storage/direct_uploads_controller.rb2
-rw-r--r--activestorage/app/controllers/active_storage/disk_controller.rb2
-rw-r--r--activestorage/app/controllers/active_storage/variants_controller.rb3
-rw-r--r--activestorage/app/jobs/active_storage/purge_job.rb4
-rw-r--r--activestorage/app/models/active_storage/attachment.rb4
-rw-r--r--activestorage/app/models/active_storage/blob.rb38
-rw-r--r--activestorage/app/models/active_storage/filename.rb2
-rw-r--r--activestorage/app/models/active_storage/variant.rb16
-rw-r--r--activestorage/app/models/active_storage/variation.rb10
-rw-r--r--activestorage/config/routes.rb2
-rw-r--r--activestorage/db/migrate/20170806125915_create_active_storage_tables.rb27
-rw-r--r--activestorage/lib/active_storage.rb2
-rw-r--r--activestorage/lib/active_storage/attached.rb6
-rw-r--r--activestorage/lib/active_storage/attached/macros.rb48
-rw-r--r--activestorage/lib/active_storage/attached/many.rb5
-rw-r--r--activestorage/lib/active_storage/attached/one.rb2
-rw-r--r--activestorage/lib/active_storage/engine.rb29
-rw-r--r--activestorage/lib/active_storage/gem_version.rb2
-rw-r--r--activestorage/lib/active_storage/log_subscriber.rb4
-rw-r--r--activestorage/lib/active_storage/migration.rb27
-rw-r--r--activestorage/lib/active_storage/service.rb26
-rw-r--r--activestorage/lib/active_storage/service/azure_storage_service.rb9
-rw-r--r--activestorage/lib/active_storage/service/configurator.rb2
-rw-r--r--activestorage/lib/active_storage/service/disk_service.rb5
-rw-r--r--activestorage/lib/active_storage/service/gcs_service.rb4
-rw-r--r--activestorage/lib/active_storage/service/mirror_service.rb14
-rw-r--r--activestorage/lib/active_storage/service/s3_service.rb4
-rw-r--r--activestorage/lib/active_storage/version.rb2
-rw-r--r--activestorage/lib/tasks/activestorage.rake11
-rw-r--r--activestorage/test/controllers/blobs_controller_test.rb17
-rw-r--r--activestorage/test/controllers/direct_uploads_controller_test.rb2
-rw-r--r--activestorage/test/controllers/disk_controller_test.rb2
-rw-r--r--activestorage/test/controllers/variants_controller_test.rb2
-rw-r--r--activestorage/test/database/create_users_migration.rb2
-rw-r--r--activestorage/test/database/setup.rb5
-rw-r--r--activestorage/test/dummy/Rakefile2
-rw-r--r--activestorage/test/dummy/app/controllers/application_controller.rb2
-rw-r--r--activestorage/test/dummy/app/helpers/application_helper.rb2
-rw-r--r--activestorage/test/dummy/app/jobs/application_job.rb2
-rw-r--r--activestorage/test/dummy/app/models/application_record.rb2
-rwxr-xr-xactivestorage/test/dummy/bin/bundle2
-rwxr-xr-xactivestorage/test/dummy/bin/rails2
-rwxr-xr-xactivestorage/test/dummy/bin/rake2
-rwxr-xr-xactivestorage/test/dummy/bin/yarn2
-rw-r--r--activestorage/test/dummy/config.ru2
-rw-r--r--activestorage/test/dummy/config/application.rb2
-rw-r--r--activestorage/test/dummy/config/boot.rb2
-rw-r--r--activestorage/test/dummy/config/environment.rb2
-rw-r--r--activestorage/test/dummy/config/environments/development.rb2
-rw-r--r--activestorage/test/dummy/config/environments/production.rb2
-rw-r--r--activestorage/test/dummy/config/environments/test.rb2
-rw-r--r--activestorage/test/dummy/config/initializers/application_controller_renderer.rb1
-rw-r--r--activestorage/test/dummy/config/initializers/assets.rb2
-rw-r--r--activestorage/test/dummy/config/initializers/backtrace_silencers.rb1
-rw-r--r--activestorage/test/dummy/config/initializers/cookies_serializer.rb2
-rw-r--r--activestorage/test/dummy/config/initializers/filter_parameter_logging.rb2
-rw-r--r--activestorage/test/dummy/config/initializers/inflections.rb1
-rw-r--r--activestorage/test/dummy/config/initializers/mime_types.rb1
-rw-r--r--activestorage/test/dummy/config/initializers/wrap_parameters.rb2
-rw-r--r--activestorage/test/dummy/config/routes.rb2
-rw-r--r--activestorage/test/dummy/config/spring.rb2
-rw-r--r--activestorage/test/filename_test.rb6
-rw-r--r--activestorage/test/models/attachments_test.rb17
-rw-r--r--activestorage/test/models/blob_test.rb2
-rw-r--r--activestorage/test/models/variant_test.rb2
-rw-r--r--activestorage/test/service/azure_storage_service_test.rb11
-rw-r--r--activestorage/test/service/configurations.yml14
-rw-r--r--activestorage/test/service/configurator_test.rb2
-rw-r--r--activestorage/test/service/disk_service_test.rb2
-rw-r--r--activestorage/test/service/gcs_service_test.rb2
-rw-r--r--activestorage/test/service/mirror_service_test.rb2
-rw-r--r--activestorage/test/service/s3_service_test.rb2
-rw-r--r--activestorage/test/service/shared_service_tests.rb4
-rw-r--r--activestorage/test/template/image_tag_test.rb6
-rw-r--r--activestorage/test/test_helper.rb19
-rw-r--r--activesupport/CHANGELOG.md5
-rw-r--r--activesupport/lib/active_support/core_ext/module/delegation.rb12
-rw-r--r--activesupport/lib/active_support/current_attributes.rb2
-rw-r--r--activesupport/test/core_ext/module_test.rb8
-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/association_basics.md36
-rw-r--r--guides/source/generators.md4
-rw-r--r--guides/source/testing.md4
-rw-r--r--guides/w3c_validator.rb2
-rw-r--r--rails.gemspec2
-rw-r--r--railties/CHANGELOG.md20
-rw-r--r--railties/lib/rails/commands/server/server_command.rb9
-rw-r--r--railties/lib/rails/generators.rb1
-rw-r--r--railties/lib/rails/generators/actions.rb41
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/initializers/application_controller_renderer.rb10
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/storage.yml2
-rw-r--r--railties/lib/rails/generators/rails/plugin/plugin_generator.rb4
-rw-r--r--railties/lib/rails/generators/rails/plugin/templates/Gemfile1
-rw-r--r--railties/lib/rails/generators/rails/plugin/templates/Rakefile11
-rw-r--r--railties/lib/rails/generators/rails/plugin/templates/bin/rails.tt17
-rw-r--r--railties/lib/rails/generators/rails/plugin/templates/gitignore2
-rw-r--r--railties/lib/rails/generators/rails/plugin/templates/rails/application.rb8
-rw-r--r--railties/lib/rails/generators/rails/plugin/templates/rails/dummy_manifest.js1
-rw-r--r--railties/test/application/configuration_test.rb60
-rw-r--r--railties/test/application/server_test.rb31
-rw-r--r--railties/test/generators/actions_test.rb72
-rw-r--r--railties/test/generators/app_generator_test.rb69
-rw-r--r--railties/test/generators/plugin_generator_test.rb87
-rw-r--r--railties/test/isolation/abstract_unit.rb2
-rw-r--r--railties/test/railties/engine_test.rb4
-rw-r--r--tasks/release.rb10
-rwxr-xr-xtools/console2
-rwxr-xr-xtools/profile2
-rw-r--r--tools/test.rb2
-rw-r--r--version.rb2
183 files changed, 1062 insertions, 459 deletions
diff --git a/.rubocop.yml b/.rubocop.yml
index 2889acfdd1..87b5c30496 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -82,16 +82,8 @@ Style/MethodDefParentheses:
Style/FrozenStringLiteralComment:
Enabled: true
EnforcedStyle: always
- Include:
- - 'activesupport/**/*'
- - 'activemodel/**/*'
- - 'actioncable/**/*'
- - 'activejob/**/*'
- - 'activerecord/**/*'
- - 'actionmailer/**/*'
- - 'actionview/**/*'
- - 'actionpack/**/*'
Exclude:
+ - 'railties/**/*'
- 'actionview/test/**/*.builder'
- 'actionview/test/**/*.ruby'
- 'actionpack/test/**/*.builder'
diff --git a/.travis.yml b/.travis.yml
index d1b384720a..46b9c45133 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -45,7 +45,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 4b86ad6931..7fc8aa7552 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
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/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/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/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 1bc26cd5e1..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`,
diff --git a/actionpack/lib/action_controller/railtie.rb b/actionpack/lib/action_controller/railtie.rb
index 7dea22931c..cc02c9a53a 100644
--- a/actionpack/lib/action_controller/railtie.rb
+++ b/actionpack/lib/action_controller/railtie.rb
@@ -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/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/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/actionview/lib/action_view/helpers/form_helper.rb b/actionview/lib/action_view/helpers/form_helper.rb
index 8b6322f8ad..e5a2f7e520 100644
--- a/actionview/lib/action_view/helpers/form_helper.rb
+++ b/actionview/lib/action_view/helpers/form_helper.rb
@@ -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/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/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/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..38e7ba39d3 100644
--- a/activerecord/lib/active_record/associations/collection_association.rb
+++ b/activerecord/lib/active_record/associations/collection_association.rb
@@ -59,7 +59,9 @@ module ActiveRecord
r.send(reflection.association_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.send(reflection.association_primary_key) }
+ not_found_ids = ids - found_ids
+ klass.all.raise_record_not_found_exception!(ids, records.size, ids.size, reflection.association_primary_key, not_found_ids)
else
replace(records)
end
@@ -72,23 +74,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/attribute_methods/dirty.rb b/activerecord/lib/active_record/attribute_methods/dirty.rb
index 5efe051125..48d33e6744 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,27 @@ 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, *)
+ def write_attribute_without_type_cast(attr_name, *) # :nodoc:
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 +85,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
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/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..d2b85e168b 100644
--- a/activerecord/lib/active_record/reflection.rb
+++ b/activerecord/lib/active_record/reflection.rb
@@ -305,13 +305,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
@@ -325,10 +329,6 @@ module ActiveRecord
def join_pk(_)
foreign_key
end
-
- def join_fk
- active_record_primary_key
- end
end
# Base class for AggregateReflection and AssociationReflection. Objects of
@@ -754,16 +754,16 @@ module ActiveRecord
owner[foreign_key]
end
+ def join_foreign_key
+ foreign_key
+ end
+
private
def calculate_constructable(macro, options)
!polymorphic?
end
- def join_fk
- foreign_key
- end
-
def join_pk(klass)
polymorphic? ? association_primary_key(klass) : association_primary_key
end
@@ -1073,10 +1073,6 @@ module ActiveRecord
@reflection.scope
end
- def table_name
- @reflection.table_name
- end
-
def plural_name
@reflection.plural_name
end
@@ -1112,10 +1108,6 @@ module ActiveRecord
@association.klass
end
- def table_name
- klass.table_name
- end
-
def constraints
@reflection.constraints
end
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/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..b2f5e39e09 100644
--- a/activerecord/lib/active_record/transactions.rb
+++ b/activerecord/lib/active_record/transactions.rb
@@ -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/test/cases/adapters/postgresql/json_test.rb b/activerecord/test/cases/adapters/postgresql/json_test.rb
index 79dcfe110c..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
@@ -33,15 +33,6 @@ module PostgresqlJSONSharedTestCases
x.reload
assert_equal ["foo" => "bar"], x.objects
end
-
- def test_not_compatible_with_serialize_macro
- new_klass = Class.new(klass) do
- serialize :payload, JSON
- end
- assert_raises(ActiveRecord::AttributeMethods::Serialization::ColumnNotSerializableError) do
- new_klass.new
- end
- end
end
class PostgresqlJSONTest < ActiveRecord::PostgreSQLTestCase
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..4c2723addc 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
@@ -891,7 +891,8 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
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)
+ 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_setter_raises_exception_when_invalid_ids_set_with_changed_primary_key
@@ -905,7 +906,8 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
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
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..9d3d5353ff 100644
--- a/activerecord/test/cases/associations/join_model_test.rb
+++ b/activerecord/test/cases/associations/join_model_test.rb
@@ -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/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/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/json_shared_test_cases.rb b/activerecord/test/cases/json_shared_test_cases.rb
index f708acf0aa..952194c6dc 100644
--- a/activerecord/test/cases/json_shared_test_cases.rb
+++ b/activerecord/test/cases/json_shared_test_cases.rb
@@ -216,6 +216,15 @@ module JSONSharedTestCases
assert_equal true, json.payload
end
+ def test_not_compatible_with_serialize_macro
+ new_klass = Class.new(klass) do
+ serialize :payload, JSON
+ end
+ assert_raises(ActiveRecord::AttributeMethods::Serialization::ColumnNotSerializableError) do
+ new_klass.new
+ end
+ 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/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/README.md b/activestorage/README.md
index 957adc05c3..d4ffe0c484 100644
--- a/activestorage/README.md
+++ b/activestorage/README.md
@@ -83,14 +83,6 @@ Variation of image attachment:
<%= image_tag user.avatar.variant(resize: "100x100") %>
```
-## Installation
-
-1. Run `rails activestorage:install` to create needed directories, migrations, and configuration.
-2. Optional: Add `gem "aws-sdk", "~> 2"` to your Gemfile if you want to use AWS S3.
-3. Optional: Add `gem "google-cloud-storage", "~> 1.3"` to your Gemfile if you want to use Google Cloud Storage.
-4. Optional: Add `gem "azure-storage"` to your Gemfile if you want to use Microsoft Azure.
-5. Optional: Add `gem "mini_magick"` to your Gemfile if you want to use variants.
-
## Direct uploads
Active Storage, with its included JavaScript library, supports uploading directly from the client to the cloud.
diff --git a/activestorage/Rakefile b/activestorage/Rakefile
index a41e07f373..aa71a65f6e 100644
--- a/activestorage/Rakefile
+++ b/activestorage/Rakefile
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "bundler/setup"
require "bundler/gem_tasks"
require "rake/testtask"
diff --git a/activestorage/app/controllers/active_storage/blobs_controller.rb b/activestorage/app/controllers/active_storage/blobs_controller.rb
index 05af29f8b2..00aa8567c8 100644
--- a/activestorage/app/controllers/active_storage/blobs_controller.rb
+++ b/activestorage/app/controllers/active_storage/blobs_controller.rb
@@ -1,3 +1,5 @@
+# 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
@@ -5,6 +7,7 @@
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
diff --git a/activestorage/app/controllers/active_storage/direct_uploads_controller.rb b/activestorage/app/controllers/active_storage/direct_uploads_controller.rb
index 0d93985897..205d173648 100644
--- a/activestorage/app/controllers/active_storage/direct_uploads_controller.rb
+++ b/activestorage/app/controllers/active_storage/direct_uploads_controller.rb
@@ -1,3 +1,5 @@
+# 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.
diff --git a/activestorage/app/controllers/active_storage/disk_controller.rb b/activestorage/app/controllers/active_storage/disk_controller.rb
index 76377a0f20..b10d4e2cac 100644
--- a/activestorage/app/controllers/active_storage/disk_controller.rb
+++ b/activestorage/app/controllers/active_storage/disk_controller.rb
@@ -1,3 +1,5 @@
+# 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
diff --git a/activestorage/app/controllers/active_storage/variants_controller.rb b/activestorage/app/controllers/active_storage/variants_controller.rb
index 994c57aafd..02e3010626 100644
--- a/activestorage/app/controllers/active_storage/variants_controller.rb
+++ b/activestorage/app/controllers/active_storage/variants_controller.rb
@@ -1,3 +1,5 @@
+# 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
@@ -5,6 +7,7 @@
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
diff --git a/activestorage/app/jobs/active_storage/purge_job.rb b/activestorage/app/jobs/active_storage/purge_job.rb
index 815f908e6c..404ccefd05 100644
--- a/activestorage/app/jobs/active_storage/purge_job.rb
+++ b/activestorage/app/jobs/active_storage/purge_job.rb
@@ -1,4 +1,6 @@
-# Provides delayed purging of attachments or blobs using their `#purge_later` method.
+# 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
diff --git a/activestorage/app/models/active_storage/attachment.rb b/activestorage/app/models/active_storage/attachment.rb
index e94fc69bba..adfa6d4aa5 100644
--- a/activestorage/app/models/active_storage/attachment.rb
+++ b/activestorage/app/models/active_storage/attachment.rb
@@ -1,3 +1,5 @@
+# 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,
@@ -21,7 +23,7 @@ class ActiveStorage::Attachment < ActiveRecord::Base
# 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.
+ # so it doesn't hold up other operations. That's what +#purge_later+ provides.
def purge_later
ActiveStorage::PurgeJob.perform_later(self)
end
diff --git a/activestorage/app/models/active_storage/blob.rb b/activestorage/app/models/active_storage/blob.rb
index c72073f9f6..dc91a9265f 100644
--- a/activestorage/app/models/active_storage/blob.rb
+++ b/activestorage/app/models/active_storage/blob.rb
@@ -1,3 +1,5 @@
+# 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:
#
@@ -29,7 +31,7 @@ class ActiveStorage::Blob < ActiveRecord::Base
find ActiveStorage.verifier.verify(id, purpose: :blob_id)
end
- # Returns a new, unsaved blob instance after the `io` has been uploaded to the service.
+ # 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
@@ -40,8 +42,8 @@ class ActiveStorage::Blob < ActiveRecord::Base
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 doing to avoid opening a transaction and talking to
+ # 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 doing to avoid opening a transaction and talking to
# the service during that (which is a bad idea and leads to deadlocks).
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!)
@@ -72,7 +74,7 @@ class ActiveStorage::Blob < ActiveRecord::Base
self[:key] ||= self.class.generate_unique_secure_token
end
- # Returns a `ActiveStorage::Filename` instance of the filename that can be queried for basename, extension, and
+ # Returns a 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])
@@ -98,7 +100,7 @@ class ActiveStorage::Blob < ActiveRecord::Base
content_type.start_with?("text")
end
- # Returns a `ActiveStorage::Variant` instance with the set of `transformations` passed in. This is only relevant
+ # Returns a 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
@@ -111,7 +113,7 @@ class ActiveStorage::Blob < ActiveRecord::Base
#
# <%= 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`
+ # 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))
@@ -119,9 +121,9 @@ class ActiveStorage::Blob < ActiveRecord::Base
# 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.
+ # 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
@@ -132,21 +134,21 @@ class ActiveStorage::Blob < ActiveRecord::Base
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.
+ # 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
+ # 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.
+ # 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!`.
+ # 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
@@ -162,7 +164,7 @@ class ActiveStorage::Blob < ActiveRecord::Base
# 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`
+ # 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
@@ -170,13 +172,13 @@ class ActiveStorage::Blob < ActiveRecord::Base
# 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.
+ # 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 a `ActiveStorage::PurgeJob` job that'll call `#purge`. This is the recommended way to purge blobs when the call
+ # Enqueues a 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)
diff --git a/activestorage/app/models/active_storage/filename.rb b/activestorage/app/models/active_storage/filename.rb
index 35f4a8ac59..6a9889addf 100644
--- a/activestorage/app/models/active_storage/filename.rb
+++ b/activestorage/app/models/active_storage/filename.rb
@@ -1,3 +1,5 @@
+# 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
diff --git a/activestorage/app/models/active_storage/variant.rb b/activestorage/app/models/active_storage/variant.rb
index e3f22bcb25..7b4ca18c8c 100644
--- a/activestorage/app/models/active_storage/variant.rb
+++ b/activestorage/app/models/active_storage/variant.rb
@@ -1,3 +1,5 @@
+# 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.
@@ -9,17 +11,17 @@
# 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`.
+# 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`
+# 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
+# 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:
#
@@ -52,12 +54,12 @@ class ActiveStorage::Variant
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 redirec to the `service_url` to be cached in the view.
+ # 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 `url_for(variant)` (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 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
diff --git a/activestorage/app/models/active_storage/variation.rb b/activestorage/app/models/active_storage/variation.rb
index e784506b4c..396ce85ef1 100644
--- a/activestorage/app/models/active_storage/variation.rb
+++ b/activestorage/app/models/active_storage/variation.rb
@@ -1,3 +1,5 @@
+# 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
@@ -13,12 +15,12 @@ class ActiveStorage::Variation
attr_reader :transformations
class << self
- # Returns a variation instance with the transformations that were encoded by `#encode`.
+ # 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
+ # Returns a signed key for the +transformations+, which can be used to refer to a specific
# variation in a URL or combined key (like `ActiveStorage::Variant#key`).
def encode(transformations)
ActiveStorage.verifier.generate(transformations, purpose: :variation)
@@ -30,7 +32,7 @@ class ActiveStorage::Variation
end
# Accepts an open MiniMagick image instance, like what's return by `MiniMagick::Image.read(io)`,
- # and performs the `transformations` against it. The transformed image instance is then returned.
+ # 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)
@@ -41,7 +43,7 @@ class ActiveStorage::Variation
end
end
- # Returns a signed key for all the `transformations` that this variation was instantiated with.
+ # Returns a signed key for all the +transformations+ that this variation was instantiated with.
def key
self.class.encode(transformations)
end
diff --git a/activestorage/config/routes.rb b/activestorage/config/routes.rb
index fddbb93255..168788475c 100644
--- a/activestorage/config/routes.rb
+++ b/activestorage/config/routes.rb
@@ -1,3 +1,5 @@
+# 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
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
index 412f08e8f5..ccc1d4a163 100644
--- a/activestorage/lib/active_storage.rb
+++ b/activestorage/lib/active_storage.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
#--
# Copyright (c) 2017 David Heinemeier Hansson
#
diff --git a/activestorage/lib/active_storage/attached.rb b/activestorage/lib/active_storage/attached.rb
index 5ac8ba5377..e90b75afd0 100644
--- a/activestorage/lib/active_storage/attached.rb
+++ b/activestorage/lib/active_storage/attached.rb
@@ -1,9 +1,11 @@
+# frozen_string_literal: true
+
require "action_dispatch"
require "action_dispatch/http/upload"
require "active_support/core_ext/module/delegation"
module ActiveStorage
-# Abstract baseclass for the concrete `ActiveStorage::Attached::One` and `ActiveStorage::Attached::Many`
+# Abstract baseclass 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
@@ -17,7 +19,7 @@ module ActiveStorage
case attachable
when ActiveStorage::Blob
attachable
- when ActionDispatch::Http::UploadedFile
+ when ActionDispatch::Http::UploadedFile, Rack::Test::UploadedFile
ActiveStorage::Blob.create_after_upload! \
io: attachable.open,
filename: attachable.original_filename,
diff --git a/activestorage/lib/active_storage/attached/macros.rb b/activestorage/lib/active_storage/attached/macros.rb
index 5779348148..027112195f 100644
--- a/activestorage/lib/active_storage/attached/macros.rb
+++ b/activestorage/lib/active_storage/attached/macros.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ActiveStorage
# Provides the class-level DSL for declaring that an Active Record model has attached blobs.
module Attached::Macros
@@ -10,25 +12,23 @@ module ActiveStorage
# 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
+ # 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`.
+ # 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)
- define_method(name) do
- if instance_variable_defined?("@active_storage_attached_#{name}")
- instance_variable_get("@active_storage_attached_#{name}")
- else
- instance_variable_set("@active_storage_attached_#{name}", ActiveStorage::Attached::One.new(name, self))
+ class_eval <<-CODE, __FILE__, __LINE__ + 1
+ def #{name}
+ @active_storage_attached_#{name} ||= ActiveStorage::Attached::One.new("#{name}", self)
end
- 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
@@ -51,25 +51,23 @@ module ActiveStorage
#
# 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
+ # 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`.
+ # 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)
- define_method(name) do
- if instance_variable_defined?("@active_storage_attached_#{name}")
- instance_variable_get("@active_storage_attached_#{name}")
- else
- instance_variable_set("@active_storage_attached_#{name}", ActiveStorage::Attached::Many.new(name, self))
+ class_eval <<-CODE, __FILE__, __LINE__ + 1
+ def #{name}
+ @active_storage_attached_#{name} ||= ActiveStorage::Attached::Many.new("#{name}", self)
end
- 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
@@ -81,4 +79,4 @@ module ActiveStorage
end
end
end
-end \ No newline at end of file
+end
diff --git a/activestorage/lib/active_storage/attached/many.rb b/activestorage/lib/active_storage/attached/many.rb
index 82989e4605..59b7d7d559 100644
--- a/activestorage/lib/active_storage/attached/many.rb
+++ b/activestorage/lib/active_storage/attached/many.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ActiveStorage
# Decorated proxy object representing of multiple attachments to a model.
class Attached::Many < Attached
@@ -5,7 +7,7 @@ module ActiveStorage
# Returns all the associated attachment records.
#
- # All methods called on this proxy object that aren't listed here will automatically be delegated to `attachments`.
+ # 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
@@ -51,4 +53,3 @@ module ActiveStorage
end
end
end
-
diff --git a/activestorage/lib/active_storage/attached/one.rb b/activestorage/lib/active_storage/attached/one.rb
index 6b34b30f1c..2e5831348e 100644
--- a/activestorage/lib/active_storage/attached/one.rb
+++ b/activestorage/lib/active_storage/attached/one.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ActiveStorage
# Representation of a single attachment to a model.
class Attached::One < Attached
diff --git a/activestorage/lib/active_storage/engine.rb b/activestorage/lib/active_storage/engine.rb
index da83d3908a..a5562b32d3 100644
--- a/activestorage/lib/active_storage/engine.rb
+++ b/activestorage/lib/active_storage/engine.rb
@@ -5,6 +5,8 @@ require "active_storage"
module ActiveStorage
class Engine < Rails::Engine # :nodoc:
+ isolate_namespace ActiveStorage
+
config.active_storage = ActiveSupport::OrderedOptions.new
config.eager_load_namespaces << ActiveStorage
@@ -34,22 +36,21 @@ module ActiveStorage
end
initializer "active_storage.services" do
- config.after_initialize do |app|
- if config_choice = app.config.active_storage.service
- config_file = Pathname.new(Rails.root.join("config/storage.yml"))
- raise("Couldn't find Active Storage configuration in #{config_file}") unless config_file.exist?
+ 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"
+ require "yaml"
+ require "erb"
- configs =
- begin
- 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
+ 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
diff --git a/activestorage/lib/active_storage/gem_version.rb b/activestorage/lib/active_storage/gem_version.rb
index db79c669bf..bb6241ae47 100644
--- a/activestorage/lib/active_storage/gem_version.rb
+++ b/activestorage/lib/active_storage/gem_version.rb
@@ -1,3 +1,5 @@
+# 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
diff --git a/activestorage/lib/active_storage/log_subscriber.rb b/activestorage/lib/active_storage/log_subscriber.rb
index 5c1b8d23ef..80cc5d5c80 100644
--- a/activestorage/lib/active_storage/log_subscriber.rb
+++ b/activestorage/lib/active_storage/log_subscriber.rb
@@ -1,10 +1,12 @@
+# 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]
+ message += " (checksum: #{event.payload[:checksum]})" if event.payload[:checksum]
info event, color(message, GREEN)
end
diff --git a/activestorage/lib/active_storage/migration.rb b/activestorage/lib/active_storage/migration.rb
deleted file mode 100644
index 2e35e163cd..0000000000
--- a/activestorage/lib/active_storage/migration.rb
+++ /dev/null
@@ -1,27 +0,0 @@
-class ActiveStorageCreateTables < ActiveRecord::Migration[5.1]
- def change
- create_table :active_storage_blobs do |t|
- t.string :key
- t.string :filename
- t.string :content_type
- t.text :metadata
- t.integer :byte_size
- t.string :checksum
- t.datetime :created_at
-
- t.index [ :key ], unique: true
- end
-
- create_table :active_storage_attachments do |t|
- t.string :name
- t.string :record_type
- t.integer :record_id
- t.integer :blob_id
-
- t.datetime :created_at
-
- t.index :blob_id
- 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/service.rb b/activestorage/lib/active_storage/service.rb
index eb25e9f001..ce736b8728 100644
--- a/activestorage/lib/active_storage/service.rb
+++ b/activestorage/lib/active_storage/service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "active_storage/log_subscriber"
module ActiveStorage
@@ -59,43 +61,43 @@ module ActiveStorage
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`.
+ # 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`.
+ # Return the content of the file at the +key+.
def download(key)
raise NotImplementedError
end
- # Delete the file at the `key`.
+ # Delete the file at the +key+.
def delete(key)
raise NotImplementedError
end
- # Return true if a file exists at the `key`.
+ # 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.
+ # 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
+ # 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.
+ # Returns a Hash of headers for +url_for_direct_upload+ requests.
def headers_for_direct_upload(key, filename:, content_type:, content_length:, checksum:)
{}
end
diff --git a/activestorage/lib/active_storage/service/azure_storage_service.rb b/activestorage/lib/active_storage/service/azure_storage_service.rb
index c2e1b25a43..d066deaa20 100644
--- a/activestorage/lib/active_storage/service/azure_storage_service.rb
+++ b/activestorage/lib/active_storage/service/azure_storage_service.rb
@@ -1,10 +1,12 @@
+# 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 a Active Storage service.
- # See `ActiveStorage::Service` for the generic API documentation that applies to all services.
+ # See ActiveStorage::Service for the generic API documentation that applies to all services.
class Service::AzureStorageService < Service
attr_reader :client, :path, :blobs, :container, :signer
@@ -57,11 +59,12 @@ module ActiveStorage
end
end
- def url(key, expires_in:, disposition:, filename:)
+ 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_disposition: "#{disposition}; filename=\"#{filename}\"").to_s
+ expiry: format_expiry(expires_in), content_type: content_type,
+ content_disposition: "#{disposition}; filename=\"#{filename}\"").to_s
payload[:url] = generated_url
diff --git a/activestorage/lib/active_storage/service/configurator.rb b/activestorage/lib/active_storage/service/configurator.rb
index 5d6475a8ae..39951fd026 100644
--- a/activestorage/lib/active_storage/service/configurator.rb
+++ b/activestorage/lib/active_storage/service/configurator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ActiveStorage
class Service::Configurator #:nodoc:
attr_reader :configurations
diff --git a/activestorage/lib/active_storage/service/disk_service.rb b/activestorage/lib/active_storage/service/disk_service.rb
index 3d92102cf0..cf4019966f 100644
--- a/activestorage/lib/active_storage/service/disk_service.rb
+++ b/activestorage/lib/active_storage/service/disk_service.rb
@@ -1,10 +1,12 @@
+# 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 a Active Storage service. See `ActiveStorage::Service` for the generic API
+ # Wraps a local disk path as a Active Storage service. See ActiveStorage::Service for the generic API
# documentation that applies to all services.
class Service::DiskService < Service
attr_reader :root
@@ -124,4 +126,3 @@ module ActiveStorage
end
end
end
-
diff --git a/activestorage/lib/active_storage/service/gcs_service.rb b/activestorage/lib/active_storage/service/gcs_service.rb
index ea4ec5a790..32dfb4851b 100644
--- a/activestorage/lib/active_storage/service/gcs_service.rb
+++ b/activestorage/lib/active_storage/service/gcs_service.rb
@@ -1,8 +1,10 @@
+# frozen_string_literal: true
+
require "google/cloud/storage"
require "active_support/core_ext/object/to_query"
module ActiveStorage
- # Wraps the Google Cloud Storage as a Active Storage service. See `ActiveStorage::Service` for the generic API
+ # Wraps the Google Cloud Storage as a Active Storage service. See ActiveStorage::Service for the generic API
# documentation that applies to all services.
class Service::GCSService < Service
attr_reader :client, :bucket
diff --git a/activestorage/lib/active_storage/service/mirror_service.rb b/activestorage/lib/active_storage/service/mirror_service.rb
index 2403eeb1e9..39e922f7ab 100644
--- a/activestorage/lib/active_storage/service/mirror_service.rb
+++ b/activestorage/lib/active_storage/service/mirror_service.rb
@@ -1,9 +1,11 @@
+# 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`.
+ # 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
@@ -20,15 +22,15 @@ module ActiveStorage
@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`.
+ # 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.
+ # Delete the file at the +key+ on all services.
def delete(key)
perform_across_services :delete, key
end
diff --git a/activestorage/lib/active_storage/service/s3_service.rb b/activestorage/lib/active_storage/service/s3_service.rb
index 5153f5db0d..98c9dd2972 100644
--- a/activestorage/lib/active_storage/service/s3_service.rb
+++ b/activestorage/lib/active_storage/service/s3_service.rb
@@ -1,9 +1,11 @@
+# frozen_string_literal: true
+
require "aws-sdk"
require "active_support/core_ext/numeric/bytes"
module ActiveStorage
# Wraps the Amazon Simple Storage Service (S3) as a Active Storage service.
- # See `ActiveStorage::Service` for the generic API documentation that applies to all services.
+ # See ActiveStorage::Service for the generic API documentation that applies to all services.
class Service::S3Service < Service
attr_reader :client, :bucket, :upload_options
diff --git a/activestorage/lib/active_storage/version.rb b/activestorage/lib/active_storage/version.rb
index 8f45480712..4b6631832b 100644
--- a/activestorage/lib/active_storage/version.rb
+++ b/activestorage/lib/active_storage/version.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_relative "gem_version"
module ActiveStorage
diff --git a/activestorage/lib/tasks/activestorage.rake b/activestorage/lib/tasks/activestorage.rake
index 1d386e67df..746bc5b28a 100644
--- a/activestorage/lib/tasks/activestorage.rake
+++ b/activestorage/lib/tasks/activestorage.rake
@@ -1,13 +1,8 @@
-require "fileutils"
+# frozen_string_literal: true
namespace :activestorage do
desc "Copy over the migration needed to the application"
- task :install do
- migration_file_path = "db/migrate/#{Time.now.utc.strftime("%Y%m%d%H%M%S")}_active_storage_create_tables.rb"
- FileUtils.mkdir_p Rails.root.join("db/migrate")
- FileUtils.cp File.expand_path("../../active_storage/migration.rb", __FILE__), Rails.root.join(migration_file_path)
- puts "Copied migration to #{migration_file_path}"
-
- puts "Now run rails db:migrate to create the tables for Active Storage"
+ task install: :environment do
+ Rake::Task["active_storage:install:migrations"].invoke
end
end
diff --git a/activestorage/test/controllers/blobs_controller_test.rb b/activestorage/test/controllers/blobs_controller_test.rb
new file mode 100644
index 0000000000..d682893e1d
--- /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
index 4f335c7759..888767086c 100644
--- a/activestorage/test/controllers/direct_uploads_controller_test.rb
+++ b/activestorage/test/controllers/direct_uploads_controller_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "test_helper"
require "database/setup"
diff --git a/activestorage/test/controllers/disk_controller_test.rb b/activestorage/test/controllers/disk_controller_test.rb
index df7954d6b4..53a086f214 100644
--- a/activestorage/test/controllers/disk_controller_test.rb
+++ b/activestorage/test/controllers/disk_controller_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "test_helper"
require "database/setup"
diff --git a/activestorage/test/controllers/variants_controller_test.rb b/activestorage/test/controllers/variants_controller_test.rb
index b3ff83e95e..d9a85e16d9 100644
--- a/activestorage/test/controllers/variants_controller_test.rb
+++ b/activestorage/test/controllers/variants_controller_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "test_helper"
require "database/setup"
diff --git a/activestorage/test/database/create_users_migration.rb b/activestorage/test/database/create_users_migration.rb
index a0b72a90ee..317daa87b5 100644
--- a/activestorage/test/database/create_users_migration.rb
+++ b/activestorage/test/database/create_users_migration.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ActiveStorageCreateUsers < ActiveRecord::Migration[5.1]
def change
create_table :users do |t|
diff --git a/activestorage/test/database/setup.rb b/activestorage/test/database/setup.rb
index b12038ee68..87564499e6 100644
--- a/activestorage/test/database/setup.rb
+++ b/activestorage/test/database/setup.rb
@@ -1,6 +1,7 @@
-require "active_storage/migration"
+# frozen_string_literal: true
+
require_relative "create_users_migration"
ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
-ActiveStorageCreateTables.migrate(:up)
+ActiveRecord::Migrator.migrate File.expand_path("../../../db/migrate", __FILE__)
ActiveStorageCreateUsers.migrate(:up)
diff --git a/activestorage/test/dummy/Rakefile b/activestorage/test/dummy/Rakefile
index d1baef0699..c4f9523878 100644
--- a/activestorage/test/dummy/Rakefile
+++ b/activestorage/test/dummy/Rakefile
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_relative "config/application"
Rails.application.load_tasks
diff --git a/activestorage/test/dummy/app/controllers/application_controller.rb b/activestorage/test/dummy/app/controllers/application_controller.rb
index 1c07694e9d..280cc28ce2 100644
--- a/activestorage/test/dummy/app/controllers/application_controller.rb
+++ b/activestorage/test/dummy/app/controllers/application_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
end
diff --git a/activestorage/test/dummy/app/helpers/application_helper.rb b/activestorage/test/dummy/app/helpers/application_helper.rb
index de6be7945c..15b06f0f67 100644
--- a/activestorage/test/dummy/app/helpers/application_helper.rb
+++ b/activestorage/test/dummy/app/helpers/application_helper.rb
@@ -1,2 +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
index a009ace51c..d92ffddcb5 100644
--- a/activestorage/test/dummy/app/jobs/application_job.rb
+++ b/activestorage/test/dummy/app/jobs/application_job.rb
@@ -1,2 +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
index 10a4cba84d..71fbba5b32 100644
--- a/activestorage/test/dummy/app/models/application_record.rb
+++ b/activestorage/test/dummy/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/activestorage/test/dummy/bin/bundle b/activestorage/test/dummy/bin/bundle
index fe1874509a..277e128251 100755
--- a/activestorage/test/dummy/bin/bundle
+++ b/activestorage/test/dummy/bin/bundle
@@ -1,3 +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
index efc0377492..22f2d8deee 100755
--- a/activestorage/test/dummy/bin/rails
+++ b/activestorage/test/dummy/bin/rails
@@ -1,4 +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
index 4fbf10b960..e436ea54a1 100755
--- a/activestorage/test/dummy/bin/rake
+++ b/activestorage/test/dummy/bin/rake
@@ -1,4 +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
index b8fe4eeaf3..c9b7498378 100755
--- a/activestorage/test/dummy/bin/yarn
+++ b/activestorage/test/dummy/bin/yarn
@@ -1,4 +1,6 @@
#!/usr/bin/env ruby
+# frozen_string_literal: true
+
VENDOR_PATH = File.expand_path("..", __dir__)
Dir.chdir(VENDOR_PATH) do
begin
diff --git a/activestorage/test/dummy/config.ru b/activestorage/test/dummy/config.ru
index 441e6ff0c3..bff88d608a 100644
--- a/activestorage/test/dummy/config.ru
+++ b/activestorage/test/dummy/config.ru
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# This file is used by Rack-based servers to start the application.
require_relative "config/environment"
diff --git a/activestorage/test/dummy/config/application.rb b/activestorage/test/dummy/config/application.rb
index 5c59401358..7ee6625bb5 100644
--- a/activestorage/test/dummy/config/application.rb
+++ b/activestorage/test/dummy/config/application.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_relative "boot"
require "rails"
diff --git a/activestorage/test/dummy/config/boot.rb b/activestorage/test/dummy/config/boot.rb
index 116591a4ed..59459d4ae3 100644
--- a/activestorage/test/dummy/config/boot.rb
+++ b/activestorage/test/dummy/config/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/activestorage/test/dummy/config/environment.rb b/activestorage/test/dummy/config/environment.rb
index cac5315775..7df99e89c6 100644
--- a/activestorage/test/dummy/config/environment.rb
+++ b/activestorage/test/dummy/config/environment.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Load the Rails application.
require_relative "application"
diff --git a/activestorage/test/dummy/config/environments/development.rb b/activestorage/test/dummy/config/environments/development.rb
index 5f3d463210..47fc5bf25c 100644
--- a/activestorage/test/dummy/config/environments/development.rb
+++ b/activestorage/test/dummy/config/environments/development.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
Rails.application.configure do
# Settings specified here will take precedence over those in config/application.rb.
diff --git a/activestorage/test/dummy/config/environments/production.rb b/activestorage/test/dummy/config/environments/production.rb
index 343da23f0b..34ac3bc561 100644
--- a/activestorage/test/dummy/config/environments/production.rb
+++ b/activestorage/test/dummy/config/environments/production.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
Rails.application.configure do
# Settings specified here will take precedence over those in config/application.rb.
diff --git a/activestorage/test/dummy/config/environments/test.rb b/activestorage/test/dummy/config/environments/test.rb
index 4f2c048600..ce0889e8ae 100644
--- a/activestorage/test/dummy/config/environments/test.rb
+++ b/activestorage/test/dummy/config/environments/test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
Rails.application.configure do
# Settings specified here will take precedence over those in config/application.rb.
diff --git a/activestorage/test/dummy/config/initializers/application_controller_renderer.rb b/activestorage/test/dummy/config/initializers/application_controller_renderer.rb
index 51639b67a0..315ac48a9a 100644
--- a/activestorage/test/dummy/config/initializers/application_controller_renderer.rb
+++ b/activestorage/test/dummy/config/initializers/application_controller_renderer.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: true
# Be sure to restart your server when you modify this file.
# ApplicationController.renderer.defaults.merge!(
diff --git a/activestorage/test/dummy/config/initializers/assets.rb b/activestorage/test/dummy/config/initializers/assets.rb
index c1f948d018..ba194685a2 100644
--- a/activestorage/test/dummy/config/initializers/assets.rb
+++ b/activestorage/test/dummy/config/initializers/assets.rb
@@ -1,3 +1,5 @@
+# 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.
diff --git a/activestorage/test/dummy/config/initializers/backtrace_silencers.rb b/activestorage/test/dummy/config/initializers/backtrace_silencers.rb
index 59385cdf37..d0f0d3b5df 100644
--- a/activestorage/test/dummy/config/initializers/backtrace_silencers.rb
+++ b/activestorage/test/dummy/config/initializers/backtrace_silencers.rb
@@ -1,3 +1,4 @@
+# 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/activestorage/test/dummy/config/initializers/cookies_serializer.rb b/activestorage/test/dummy/config/initializers/cookies_serializer.rb
index 5a6a32d371..ee8dff9c99 100644
--- a/activestorage/test/dummy/config/initializers/cookies_serializer.rb
+++ b/activestorage/test/dummy/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/activestorage/test/dummy/config/initializers/filter_parameter_logging.rb b/activestorage/test/dummy/config/initializers/filter_parameter_logging.rb
index 4a994e1e7b..7a4f47b4c2 100644
--- a/activestorage/test/dummy/config/initializers/filter_parameter_logging.rb
+++ b/activestorage/test/dummy/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/activestorage/test/dummy/config/initializers/inflections.rb b/activestorage/test/dummy/config/initializers/inflections.rb
index ac033bf9dc..aa7435fbc9 100644
--- a/activestorage/test/dummy/config/initializers/inflections.rb
+++ b/activestorage/test/dummy/config/initializers/inflections.rb
@@ -1,3 +1,4 @@
+# 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/activestorage/test/dummy/config/initializers/mime_types.rb b/activestorage/test/dummy/config/initializers/mime_types.rb
index dc1899682b..6e1d16f027 100644
--- a/activestorage/test/dummy/config/initializers/mime_types.rb
+++ b/activestorage/test/dummy/config/initializers/mime_types.rb
@@ -1,3 +1,4 @@
+# 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/activestorage/test/dummy/config/initializers/wrap_parameters.rb b/activestorage/test/dummy/config/initializers/wrap_parameters.rb
index bbfc3961bf..2f3c0db471 100644
--- a/activestorage/test/dummy/config/initializers/wrap_parameters.rb
+++ b/activestorage/test/dummy/config/initializers/wrap_parameters.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Be sure to restart your server when you modify this file.
# This file contains settings for ActionController::ParamsWrapper which
diff --git a/activestorage/test/dummy/config/routes.rb b/activestorage/test/dummy/config/routes.rb
index 1daf9a4121..edf04d2d63 100644
--- a/activestorage/test/dummy/config/routes.rb
+++ b/activestorage/test/dummy/config/routes.rb
@@ -1,2 +1,4 @@
+# frozen_string_literal: true
+
Rails.application.routes.draw do
end
diff --git a/activestorage/test/dummy/config/spring.rb b/activestorage/test/dummy/config/spring.rb
index c9119b40c0..ff5ba06b6d 100644
--- a/activestorage/test/dummy/config/spring.rb
+++ b/activestorage/test/dummy/config/spring.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
%w(
.ruby-version
.rbenv-vars
diff --git a/activestorage/test/filename_test.rb b/activestorage/test/filename_test.rb
index e448238675..f1e4a467ba 100644
--- a/activestorage/test/filename_test.rb
+++ b/activestorage/test/filename_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "test_helper"
class ActiveStorage::FilenameTest < ActiveSupport::TestCase
@@ -10,8 +12,8 @@ class ActiveStorage::FilenameTest < ActiveSupport::TestCase
end
test "sanitize transcodes to valid UTF-8" do
- { "\xF6".force_encoding(Encoding::ISO8859_1) => "ö",
- "\xC3".force_encoding(Encoding::ISO8859_1) => "Ã",
+ { "\xF6".dup.force_encoding(Encoding::ISO8859_1) => "ö",
+ "\xC3".dup.force_encoding(Encoding::ISO8859_1) => "Ã",
"\xAD" => "�",
"\xCF" => "�",
"\x00" => "",
diff --git a/activestorage/test/models/attachments_test.rb b/activestorage/test/models/attachments_test.rb
index 82256e1f44..7cfd8683db 100644
--- a/activestorage/test/models/attachments_test.rb
+++ b/activestorage/test/models/attachments_test.rb
@@ -1,13 +1,8 @@
+# frozen_string_literal: true
+
require "test_helper"
require "database/setup"
-# ActiveRecord::Base.logger = Logger.new(STDOUT)
-
-class User < ActiveRecord::Base
- has_one_attached :avatar
- has_many_attached :highlights
-end
-
class ActiveStorage::AttachmentsTest < ActiveSupport::TestCase
include ActiveJob::TestHelper
@@ -25,11 +20,17 @@ class ActiveStorage::AttachmentsTest < ActiveSupport::TestCase
assert_equal "funky.jpg", @user.avatar.filename.to_s
end
- test "attach new blob" do
+ 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
diff --git a/activestorage/test/models/blob_test.rb b/activestorage/test/models/blob_test.rb
index 8b14bcec87..1c50d18994 100644
--- a/activestorage/test/models/blob_test.rb
+++ b/activestorage/test/models/blob_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "test_helper"
require "database/setup"
diff --git a/activestorage/test/models/variant_test.rb b/activestorage/test/models/variant_test.rb
index 04ebaccb6a..f4e1e9e51d 100644
--- a/activestorage/test/models/variant_test.rb
+++ b/activestorage/test/models/variant_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "test_helper"
require "database/setup"
diff --git a/activestorage/test/service/azure_storage_service_test.rb b/activestorage/test/service/azure_storage_service_test.rb
index e2be510b60..dac154cf11 100644
--- a/activestorage/test/service/azure_storage_service_test.rb
+++ b/activestorage/test/service/azure_storage_service_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "service/shared_service_tests"
require "uri"
@@ -6,8 +8,15 @@ if SERVICE_CONFIGURATIONS[:azure]
SERVICE = ActiveStorage::Service.configure(:azure, SERVICE_CONFIGURATIONS)
include ActiveStorage::Service::SharedServiceTests
- 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(/(\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
index d7aa672573..56ed37be5d 100644
--- a/activestorage/test/service/configurations.yml
+++ b/activestorage/test/service/configurations.yml
@@ -1,10 +1,10 @@
-s3:
- service: S3
- access_key_id: <%= ENV["AWS_ACCESS_KEY_ID"] %>
- secret_access_key: <%= ENV["AWS_SECRET_KEY"] %>
- region: us-east-2
- bucket: rails-ci-activestorage
-
+# s3:
+# service: S3
+# access_key_id: ""
+# secret_access_key: ""
+# region: ""
+# bucket: ""
+#
# gcs:
# service: GCS
# keyfile: {
diff --git a/activestorage/test/service/configurator_test.rb b/activestorage/test/service/configurator_test.rb
index c69b8d5087..a2fd035e02 100644
--- a/activestorage/test/service/configurator_test.rb
+++ b/activestorage/test/service/configurator_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "service/shared_service_tests"
class ActiveStorage::Service::ConfiguratorTest < ActiveSupport::TestCase
diff --git a/activestorage/test/service/disk_service_test.rb b/activestorage/test/service/disk_service_test.rb
index 2fdb113616..835a3a2971 100644
--- a/activestorage/test/service/disk_service_test.rb
+++ b/activestorage/test/service/disk_service_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "service/shared_service_tests"
class ActiveStorage::Service::DiskServiceTest < ActiveSupport::TestCase
diff --git a/activestorage/test/service/gcs_service_test.rb b/activestorage/test/service/gcs_service_test.rb
index 03048731bc..5f53f61baf 100644
--- a/activestorage/test/service/gcs_service_test.rb
+++ b/activestorage/test/service/gcs_service_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "service/shared_service_tests"
require "net/http"
diff --git a/activestorage/test/service/mirror_service_test.rb b/activestorage/test/service/mirror_service_test.rb
index 129e11d06f..93e86eff70 100644
--- a/activestorage/test/service/mirror_service_test.rb
+++ b/activestorage/test/service/mirror_service_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "service/shared_service_tests"
class ActiveStorage::Service::MirrorServiceTest < ActiveSupport::TestCase
diff --git a/activestorage/test/service/s3_service_test.rb b/activestorage/test/service/s3_service_test.rb
index 57e13e6538..98f520741d 100644
--- a/activestorage/test/service/s3_service_test.rb
+++ b/activestorage/test/service/s3_service_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "service/shared_service_tests"
require "net/http"
diff --git a/activestorage/test/service/shared_service_tests.rb b/activestorage/test/service/shared_service_tests.rb
index 07620d91e4..a9e1cb6ce9 100644
--- a/activestorage/test/service/shared_service_tests.rb
+++ b/activestorage/test/service/shared_service_tests.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "test_helper"
require "active_support/core_ext/securerandom"
@@ -5,7 +7,7 @@ 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".force_encoding(Encoding::BINARY)
+ FIXTURE_DATA = "\211PNG\r\n\032\n\000\000\000\rIHDR\000\000\000\020\000\000\000\020\001\003\000\000\000%=m\"\000\000\000\006PLTE\000\000\000\377\377\377\245\331\237\335\000\000\0003IDATx\234c\370\377\237\341\377_\206\377\237\031\016\2603\334?\314p\1772\303\315\315\f7\215\031\356\024\203\320\275\317\f\367\201R\314\f\017\300\350\377\177\000Q\206\027(\316]\233P\000\000\000\000IEND\256B`\202".dup.force_encoding(Encoding::BINARY)
included do
setup do
diff --git a/activestorage/test/template/image_tag_test.rb b/activestorage/test/template/image_tag_test.rb
index 83c95c01a1..46dd97b3e9 100644
--- a/activestorage/test/template/image_tag_test.rb
+++ b/activestorage/test/template/image_tag_test.rb
@@ -1,10 +1,8 @@
+# frozen_string_literal: true
+
require "test_helper"
require "database/setup"
-class User < ActiveRecord::Base
- has_one_attached :avatar
-end
-
class ActiveStorage::ImageTagTest < ActionView::TestCase
tests ActionView::Helpers::AssetTagHelper
diff --git a/activestorage/test/test_helper.rb b/activestorage/test/test_helper.rb
index 5b6d3b64c2..0bb3a925b8 100644
--- a/activestorage/test/test_helper.rb
+++ b/activestorage/test/test_helper.rb
@@ -1,16 +1,24 @@
+# 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"
-require "byebug"
+
+begin
+ require "byebug"
+rescue LoadError
+end
require "active_job"
ActiveJob::Base.queue_adapter = :test
-ActiveJob::Base.logger = nil
+ActiveJob::Base.logger = ActiveSupport::Logger.new(nil)
-require "active_storage"
+# Filter out Minitest backtrace while allowing backtrace from other libraries
+# to be shown.
+Minitest.backtrace_filter = Minitest::BacktraceFilter.new
require "yaml"
SERVICE_CONFIGURATIONS = begin
@@ -54,3 +62,8 @@ 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/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md
index d5087f67af..ac1043df78 100644
--- a/activesupport/CHANGELOG.md
+++ b/activesupport/CHANGELOG.md
@@ -1,3 +1,8 @@
+* `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
diff --git a/activesupport/lib/active_support/core_ext/module/delegation.rb b/activesupport/lib/active_support/core_ext/module/delegation.rb
index c979af9de3..1840dc942f 100644
--- a/activesupport/lib/active_support/core_ext/module/delegation.rb
+++ b/activesupport/lib/active_support/core_ext/module/delegation.rb
@@ -273,10 +273,16 @@ class Module
def method_missing(method, *args, &block)
if #{target}.respond_to?(method)
#{target}.public_send(method, *args, &block)
- elsif #{target}.nil?
- raise DelegationError, "\#{method} delegated to #{target}, but #{target} is nil"
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/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/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/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/generators.md b/guides/source/generators.md
index be1be75e7a..389224d908 100644
--- a/guides/source/generators.md
+++ b/guides/source/generators.md
@@ -627,7 +627,7 @@ This method also takes a block:
```ruby
lib "super_special.rb" do
- puts "Super special!"
+ "puts 'Super special!'"
end
```
@@ -636,7 +636,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/testing.md b/guides/source/testing.md
index f71e963716..1c648ac47f 100644
--- a/guides/source/testing.md
+++ b/guides/source/testing.md
@@ -614,7 +614,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 +788,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/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 31eee025bf..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|
diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md
index 43210ead16..4ee30faabb 100644
--- a/railties/CHANGELOG.md
+++ b/railties/CHANGELOG.md
@@ -1,3 +1,23 @@
+* 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 the current Ruby version when new Rails applications are
created.
diff --git a/railties/lib/rails/commands/server/server_command.rb b/railties/lib/rails/commands/server/server_command.rb
index ce258341f6..de69b4586b 100644
--- a/railties/lib/rails/commands/server/server_command.rb
+++ b/railties/lib/rails/commands/server/server_command.rb
@@ -2,6 +2,8 @@ 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 +20,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/generators.rb b/railties/lib/rails/generators.rb
index b9a0fe07c8..8915441d4c 100644
--- a/railties/lib/rails/generators.rb
+++ b/railties/lib/rails/generators.rb
@@ -10,6 +10,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
diff --git a/railties/lib/rails/generators/actions.rb b/railties/lib/rails/generators/actions.rb
index 2792d7636f..9baf53c1d0 100644
--- a/railties/lib/rails/generators/actions.rb
+++ b/railties/lib/rails/generators/actions.rb
@@ -97,16 +97,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,9 +137,10 @@ 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
@@ -150,9 +151,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 +172,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 +191,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.
@@ -304,6 +308,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/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..89d2efab2b 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,8 @@
# 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/storage.yml b/railties/lib/rails/generators/rails/app/templates/config/storage.yml
index 1afe3eb4b3..089ed4567a 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/storage.yml
+++ b/railties/lib/rails/generators/rails/app/templates/config/storage.yml
@@ -26,7 +26,7 @@ local:
# service: AzureStorage
# path: your_azure_storage_path
# storage_account_name: your_account_name
-# storage_access_key: <%%= Rails.application.secrets.dig(:azure_storage, :secret_access_key) %>
+# storage_access_key: <%%= Rails.application.secrets.dig(:azure_storage, :storage_access_key) %>
# container: your_container_name
# mirror:
diff --git a/railties/lib/rails/generators/rails/plugin/plugin_generator.rb b/railties/lib/rails/generators/rails/plugin/plugin_generator.rb
index 61c54b4222..a40fa78132 100644
--- a/railties/lib/rails/generators/rails/plugin/plugin_generator.rb
+++ b/railties/lib/rails/generators/rails/plugin/plugin_generator.rb
@@ -76,8 +76,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,7 +86,7 @@ task default: :test
end
PASSTHROUGH_OPTIONS = [
- :skip_active_record, :skip_action_mailer, :skip_javascript, :skip_sprockets, :database,
+ :skip_active_record, :skip_action_mailer, :skip_javascript, :skip_action_cable, :skip_sprockets, :database,
:javascript, :quiet, :pretend, :force, :skip
]
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..f3efe21cf1 100644
--- a/railties/lib/rails/generators/rails/plugin/templates/Rakefile
+++ b/railties/lib/rails/generators/rails/plugin/templates/Rakefile
@@ -13,17 +13,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/gitignore b/railties/lib/rails/generators/rails/plugin/templates/gitignore
index 54c78d7927..757172e6a6 100644
--- a/railties/lib/rails/generators/rails/plugin/templates/gitignore
+++ b/railties/lib/rails/generators/rails/plugin/templates/gitignore
@@ -1,7 +1,7 @@
.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
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 5e0cfd6c21..47b56ae3df 100644
--- a/railties/lib/rails/generators/rails/plugin/templates/rails/application.rb
+++ b/railties/lib/rails/generators/rails/plugin/templates/rails/application.rb
@@ -3,16 +3,18 @@ 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/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/test/application/configuration_test.rb b/railties/test/application/configuration_test.rb
index c2f6a5a95c..482da98a45 100644
--- a/railties/test/application/configuration_test.rb
+++ b/railties/test/application/configuration_test.rb
@@ -238,6 +238,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
diff --git a/railties/test/application/server_test.rb b/railties/test/application/server_test.rb
new file mode 100644
index 0000000000..07880a5025
--- /dev/null
+++ b/railties/test/application/server_test.rb
@@ -0,0 +1,31 @@
+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/generators/actions_test.rb b/railties/test/generators/actions_test.rb
index 03b29be907..d65ced55b8 100644
--- a/railties/test/generators/actions_test.rb
+++ b/railties/test/generators/actions_test.rb
@@ -139,14 +139,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 +163,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 +197,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
diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb
index 44c4688aa4..ff73014046 100644
--- a/railties/test/generators/app_generator_test.rb
+++ b/railties/test/generators/app_generator_test.rb
@@ -394,6 +394,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'"
@@ -449,22 +459,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
@@ -473,7 +487,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
@@ -486,10 +501,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
@@ -498,6 +518,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
@@ -570,6 +594,11 @@ class AppGeneratorTest < Rails::Generators::TestCase
run_generator([destination_root])
assert_file "package.json", /dependencies/
assert_file "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
@@ -654,18 +683,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)
@@ -896,18 +913,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/plugin_generator_test.rb b/railties/test/generators/plugin_generator_test.rb
index 1dfff38543..01a9168f38 100644
--- a/railties/test/generators/plugin_generator_test.rb
+++ b/railties/test/generators/plugin_generator_test.rb
@@ -75,6 +75,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 +94,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 +114,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/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 |contents|
- assert_no_match(/config\.assets/, contents)
+ 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 +172,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 +207,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 +245,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
@@ -277,7 +337,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"/
@@ -459,10 +519,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
@@ -499,9 +558,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/isolation/abstract_unit.rb b/railties/test/isolation/abstract_unit.rb
index 7496b5f84a..7fa523f58d 100644
--- a/railties/test/isolation/abstract_unit.rb
+++ b/railties/test/isolation/abstract_unit.rb
@@ -313,8 +313,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/railties/engine_test.rb b/railties/test/railties/engine_test.rb
index 145c2ec7b6..ea942a3975 100644
--- a/railties/test/railties/engine_test.rb
+++ b/railties/test/railties/engine_test.rb
@@ -136,7 +136,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 +171,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
diff --git a/tasks/release.rb b/tasks/release.rb
index 6ff37fb415..aa8ba44c1a 100644
--- a/tasks/release.rb
+++ b/tasks/release.rb
@@ -1,3 +1,5 @@
+# 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(" ") }
@@ -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