aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.travis.yml2
-rw-r--r--Gemfile7
-rw-r--r--Gemfile.lock22
-rw-r--r--actioncable/CHANGELOG.md2
-rw-r--r--actionmailer/CHANGELOG.md4
-rw-r--r--actionmailer/lib/action_mailer/test_helper.rb6
-rw-r--r--actionmailer/test/test_helper_test.rb22
-rw-r--r--actionpack/CHANGELOG.md2
-rw-r--r--actionpack/lib/abstract_controller/callbacks.rb12
-rw-r--r--actionpack/lib/action_controller/metal/content_security_policy.rb12
-rw-r--r--actionpack/lib/action_controller/metal/strong_parameters.rb24
-rw-r--r--actionpack/lib/action_dispatch/middleware/ssl.rb4
-rw-r--r--actionpack/lib/action_dispatch/middleware/static.rb6
-rw-r--r--actionpack/lib/action_dispatch/routing.rb6
-rw-r--r--actionpack/lib/action_dispatch/routing/endpoint.rb15
-rw-r--r--actionpack/lib/action_dispatch/routing/inspector.rb105
-rw-r--r--actionpack/lib/action_dispatch/routing/mapper.rb3
-rw-r--r--actionpack/lib/action_dispatch/routing/polymorphic_routes.rb3
-rw-r--r--actionpack/lib/action_dispatch/routing/url_for.rb20
-rw-r--r--actionpack/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb1
-rw-r--r--actionpack/test/dispatch/content_security_policy_test.rb82
-rw-r--r--actionpack/test/dispatch/routing/inspector_test.rb50
-rw-r--r--actionpack/test/dispatch/ssl_test.rb8
-rw-r--r--actionpack/test/dispatch/static_test.rb11
-rw-r--r--actionpack/test/fixtures/public/foo/さようなら.html1
-rw-r--r--actionpack/test/fixtures/public/foo/さようなら.html.gzbin0 -> 67 bytes
-rw-r--r--actionpack/test/fixtures/公共/foo/さようなら.html1
-rw-r--r--actionpack/test/fixtures/公共/foo/さようなら.html.gzbin0 -> 67 bytes
-rw-r--r--actionview/CHANGELOG.md12
-rw-r--r--actionview/lib/action_view/digestor.rb6
-rw-r--r--actionview/lib/action_view/helpers/form_tag_helper.rb3
-rw-r--r--actionview/lib/action_view/helpers/tags/base.rb6
-rw-r--r--actionview/lib/action_view/helpers/translation_helper.rb7
-rw-r--r--actionview/test/activerecord/multifetch_cache_test.rb35
-rw-r--r--actionview/test/fixtures/digestor/comments/show.js.erb1
-rw-r--r--actionview/test/template/digestor_test.rb12
-rw-r--r--activejob/CHANGELOG.md10
-rw-r--r--activejob/README.md6
-rw-r--r--activejob/Rakefile1
-rw-r--r--activejob/lib/active_job/queue_adapters.rb4
-rw-r--r--activejob/lib/active_job/queue_adapters/qu_adapter.rb46
-rw-r--r--activejob/test/adapters/qu.rb5
-rw-r--r--activejob/test/integration/queuing_test.rb2
-rw-r--r--activejob/test/support/integration/adapters/qu.rb40
-rw-r--r--activemodel/CHANGELOG.md2
-rw-r--r--activemodel/lib/active_model/type/time.rb8
-rw-r--r--activerecord/CHANGELOG.md4
-rw-r--r--activerecord/lib/active_record.rb1
-rw-r--r--activerecord/lib/active_record/base.rb1
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/quoting.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/connection_specification.rb1
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite3/quoting.rb2
-rw-r--r--activerecord/lib/active_record/connection_handling.rb4
-rw-r--r--activerecord/lib/active_record/database_configurations.rb63
-rw-r--r--activerecord/lib/active_record/persistence.rb2
-rw-r--r--activerecord/lib/active_record/querying.rb2
-rw-r--r--activerecord/lib/active_record/railtie.rb7
-rw-r--r--activerecord/lib/active_record/railties/collection_cache_association_loading.rb34
-rw-r--r--activerecord/lib/active_record/railties/databases.rake62
-rw-r--r--activerecord/lib/active_record/relation.rb21
-rw-r--r--activerecord/lib/active_record/relation/batches.rb26
-rw-r--r--activerecord/lib/active_record/relation/query_methods.rb5
-rw-r--r--activerecord/lib/active_record/store.rb29
-rw-r--r--activerecord/lib/active_record/tasks/database_tasks.rb37
-rw-r--r--activerecord/test/cases/adapters/sqlite3/quoting_test.rb7
-rw-r--r--activerecord/test/cases/calculations_test.rb5
-rw-r--r--activerecord/test/cases/connection_adapters/connection_handler_test.rb48
-rw-r--r--activerecord/test/cases/date_time_precision_test.rb18
-rw-r--r--activerecord/test/cases/quoting_test.rb39
-rw-r--r--activerecord/test/cases/relation/delegation_test.rb28
-rw-r--r--activerecord/test/cases/relation/mutation_test.rb7
-rw-r--r--activerecord/test/cases/schema_dumper_test.rb2
-rw-r--r--activerecord/test/cases/store_test.rb22
-rw-r--r--activerecord/test/cases/tasks/database_tasks_test.rb129
-rw-r--r--activerecord/test/cases/time_precision_test.rb18
-rw-r--r--activerecord/test/cases/timestamp_test.rb10
-rw-r--r--activerecord/test/models/admin/user.rb3
-rw-r--r--activerecord/test/schema/schema.rb4
-rw-r--r--activestorage/CHANGELOG.md2
-rw-r--r--activestorage/lib/active_storage/service/azure_storage_service.rb22
-rw-r--r--activestorage/lib/active_storage/service/s3_service.rb4
-rw-r--r--activestorage/test/service/configurations.example.yml1
-rw-r--r--activestorage/test/service/configurations.yml.encbin2864 -> 2848 bytes
-rw-r--r--activestorage/test/service/s3_service_test.rb2
-rw-r--r--activesupport/CHANGELOG.md10
-rw-r--r--activesupport/lib/active_support/cache.rb27
-rw-r--r--activesupport/lib/active_support/cache/redis_cache_store.rb4
-rw-r--r--activesupport/lib/active_support/cache/strategy/local_cache.rb9
-rw-r--r--activesupport/lib/active_support/core_ext/uri.rb6
-rw-r--r--activesupport/lib/active_support/encrypted_configuration.rb4
-rw-r--r--activesupport/test/cache/behaviors/local_cache_behavior.rb12
-rw-r--r--activesupport/test/cache/stores/redis_cache_store_test.rb6
-rw-r--r--activesupport/test/core_ext/object/try_test.rb9
-rw-r--r--activesupport/test/core_ext/uri_ext_test.rb2
-rw-r--r--guides/CHANGELOG.md2
-rw-r--r--guides/source/5_2_release_notes.md728
-rw-r--r--guides/source/action_controller_overview.md4
-rw-r--r--guides/source/action_mailer_basics.md30
-rw-r--r--guides/source/action_view_overview.md6
-rw-r--r--guides/source/active_job_basics.md4
-rw-r--r--guides/source/active_record_migrations.md2
-rw-r--r--guides/source/active_storage_overview.md8
-rw-r--r--guides/source/asset_pipeline.md2
-rw-r--r--guides/source/caching_with_rails.md48
-rw-r--r--guides/source/configuring.md4
-rw-r--r--guides/source/getting_started.md8
-rw-r--r--guides/source/i18n.md25
-rw-r--r--guides/source/routing.md20
-rw-r--r--guides/source/security.md108
-rw-r--r--guides/source/upgrading_ruby_on_rails.md12
-rw-r--r--railties/CHANGELOG.md2
-rw-r--r--railties/lib/rails/application/configuration.rb12
-rw-r--r--railties/lib/rails/application_controller.rb11
-rw-r--r--railties/lib/rails/commands/dbconsole/dbconsole_command.rb2
-rw-r--r--railties/lib/rails/commands/routes/routes_command.rb42
-rw-r--r--railties/lib/rails/commands/server/server_command.rb11
-rw-r--r--railties/lib/rails/generators/app_base.rb2
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/storage.yml.tt1
-rw-r--r--railties/test/application/asset_debugging_test.rb3
-rw-r--r--railties/test/commands/routes_test.rb130
-rw-r--r--railties/test/generators/app_generator_test.rb4
123 files changed, 2053 insertions, 582 deletions
diff --git a/.travis.yml b/.travis.yml
index 09e16698e4..1c67c1475c 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -33,7 +33,7 @@ before_install:
- "travis_retry gem install bundler"
- "[ -f /tmp/beanstalkd-1.10/Makefile ] || (curl -L https://github.com/kr/beanstalkd/archive/v1.10.tar.gz | tar xz -C /tmp)"
- "pushd /tmp/beanstalkd-1.10 && make && (./beanstalkd &); popd"
- - "[[ -z $encrypted_8a915ebdd931_key && -z $encrypted_8a915ebdd931_iv ]] || openssl aes-256-cbc -K $encrypted_8a915ebdd931_key -iv $encrypted_8a915ebdd931_iv -in activestorage/test/service/configurations.yml.enc -out activestorage/test/service/configurations.yml -d"
+ - "[[ -z $encrypted_0fb9444d0374_key && -z $encrypted_0fb9444d0374_iv ]] || openssl aes-256-cbc -K $encrypted_0fb9444d0374_key -iv $encrypted_0fb9444d0374_iv -in activestorage/test/service/configurations.yml.enc -out activestorage/test/service/configurations.yml -d"
- "[[ $GEM != 'av:ujs' ]] || nvm install node"
- "[[ $GEM != 'av:ujs' ]] || node --version"
- "[[ $GEM != 'av:ujs' ]] || (cd actionview && npm install)"
diff --git a/Gemfile b/Gemfile
index 1ddda970dc..0730c1e9b0 100644
--- a/Gemfile
+++ b/Gemfile
@@ -45,7 +45,7 @@ group :doc do
end
# Active Support.
-gem "dalli", ">= 2.2.1"
+gem "dalli", "< 2.7.7"
gem "listen", ">= 3.0.5", "< 3.2", require: false
gem "libxml-ruby", platforms: :ruby
gem "connection_pool", require: false
@@ -64,9 +64,6 @@ group :job do
gem "sneakers", require: false
gem "que", require: false
gem "backburner", require: false
- # TODO: add qu after it support Rails 5.1
- # gem 'qu-rails', github: "bkeepers/qu", branch: "master", require: false
- # gem "qu-redis", require: false
gem "delayed_job_active_record", require: false
gem "sequel", require: false
end
@@ -150,7 +147,7 @@ end
platforms :rbx do
# The rubysl-yaml gem doesn't ship with Psych by default as it needs
# libyaml that isn't always available.
- gem "psych", "~> 2.0"
+ gem "psych", "~> 3.0"
end
# Gems that are necessary for Active Record tests with Oracle.
diff --git a/Gemfile.lock b/Gemfile.lock
index 5c93085395..19de9bc22b 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -285,7 +285,7 @@ GEM
logging (2.2.2)
little-plugger (~> 1.1)
multi_json (~> 1.10)
- loofah (2.1.1)
+ loofah (2.2.1)
crass (~> 1.0.2)
nokogiri (>= 1.5.9)
mail (2.7.0)
@@ -319,17 +319,17 @@ GEM
multipart-post (2.0.0)
mustache (1.0.5)
mustermann (1.0.2)
- mysql2 (0.4.10)
- mysql2 (0.4.10-x64-mingw32)
- mysql2 (0.4.10-x86-mingw32)
+ mysql2 (0.5.0)
+ mysql2 (0.5.0-x64-mingw32)
+ mysql2 (0.5.0-x86-mingw32)
nio4r (2.2.0)
nio4r (2.2.0-java)
- nokogiri (1.8.1)
+ nokogiri (1.8.2)
mini_portile2 (~> 2.3.0)
- nokogiri (1.8.1-java)
- nokogiri (1.8.1-x64-mingw32)
+ nokogiri (1.8.2-java)
+ nokogiri (1.8.2-x64-mingw32)
mini_portile2 (~> 2.3.0)
- nokogiri (1.8.1-x86-mingw32)
+ nokogiri (1.8.2-x86-mingw32)
mini_portile2 (~> 2.3.0)
os (0.9.6)
parallel (1.12.1)
@@ -340,7 +340,7 @@ GEM
pg (1.0.0-x64-mingw32)
pg (1.0.0-x86-mingw32)
powerpack (0.1.1)
- psych (2.2.4)
+ psych (3.0.2)
public_suffix (3.0.1)
puma (3.9.1)
puma (3.9.1-java)
@@ -510,7 +510,7 @@ DEPENDENCIES
chromedriver-helper
coffee-rails
connection_pool
- dalli (>= 2.2.1)
+ dalli (< 2.7.7)
delayed_job
delayed_job_active_record
google-cloud-storage (~> 1.8)
@@ -525,7 +525,7 @@ DEPENDENCIES
mysql2 (>= 0.4.10)
nokogiri (>= 1.8.1)
pg (>= 0.18.0)
- psych (~> 2.0)
+ psych (~> 3.0)
puma
que
queue_classic!
diff --git a/actioncable/CHANGELOG.md b/actioncable/CHANGELOG.md
index a28e2fe75d..959943016f 100644
--- a/actioncable/CHANGELOG.md
+++ b/actioncable/CHANGELOG.md
@@ -1,5 +1,3 @@
-## Rails 6.0.0.alpha (Unreleased) ##
-
* Rails 6 requires Ruby 2.4.1 or newer.
*Jeremy Daer*
diff --git a/actionmailer/CHANGELOG.md b/actionmailer/CHANGELOG.md
index c5dbb0f1d4..9fb2e44210 100644
--- a/actionmailer/CHANGELOG.md
+++ b/actionmailer/CHANGELOG.md
@@ -1,4 +1,6 @@
-## Rails 6.0.0.alpha (Unreleased) ##
+* Perform email jobs in `assert_emails`.
+
+ *Gannon McGibbon*
* Rails 6 requires Ruby 2.4.1 or newer.
diff --git a/actionmailer/lib/action_mailer/test_helper.rb b/actionmailer/lib/action_mailer/test_helper.rb
index 8ee4d06915..e9ddef3b94 100644
--- a/actionmailer/lib/action_mailer/test_helper.rb
+++ b/actionmailer/lib/action_mailer/test_helper.rb
@@ -28,13 +28,13 @@ module ActionMailer
#
# assert_emails 2 do
# ContactMailer.welcome.deliver_now
- # ContactMailer.welcome.deliver_now
+ # ContactMailer.welcome.deliver_later
# end
# end
- def assert_emails(number)
+ def assert_emails(number, &block)
if block_given?
original_count = ActionMailer::Base.deliveries.size
- yield
+ perform_enqueued_jobs(only: [ActionMailer::DeliveryJob, ActionMailer::Parameterized::DeliveryJob], &block)
new_count = ActionMailer::Base.deliveries.size
assert_equal number, new_count - original_count, "#{number} emails expected, but #{new_count - original_count} were sent"
else
diff --git a/actionmailer/test/test_helper_test.rb b/actionmailer/test/test_helper_test.rb
index 3866097389..8fdc687a8b 100644
--- a/actionmailer/test/test_helper_test.rb
+++ b/actionmailer/test/test_helper_test.rb
@@ -69,6 +69,16 @@ class TestHelperMailerTest < ActionMailer::TestCase
end
end
+ def test_assert_emails_with_enqueued_emails
+ assert_nothing_raised do
+ assert_emails 1 do
+ silence_stream($stdout) do
+ TestHelperMailer.test.deliver_later
+ end
+ end
+ end
+ end
+
def test_repeated_assert_emails_calls
assert_nothing_raised do
assert_emails 1 do
@@ -105,6 +115,18 @@ class TestHelperMailerTest < ActionMailer::TestCase
end
end
+ def test_assert_no_emails_with_enqueued_emails
+ error = assert_raise ActiveSupport::TestCase::Assertion do
+ assert_no_emails do
+ silence_stream($stdout) do
+ TestHelperMailer.test.deliver_later
+ end
+ end
+ end
+
+ assert_match(/0 .* but 1/, error.message)
+ end
+
def test_assert_emails_too_few_sent
error = assert_raise ActiveSupport::TestCase::Assertion do
assert_emails 2 do
diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md
index 44d87878a4..61451dd673 100644
--- a/actionpack/CHANGELOG.md
+++ b/actionpack/CHANGELOG.md
@@ -1,5 +1,3 @@
-## Rails 6.0.0.alpha (Unreleased) ##
-
* Rails 6 requires Ruby 2.4.1 or newer.
*Jeremy Daer*
diff --git a/actionpack/lib/abstract_controller/callbacks.rb b/actionpack/lib/abstract_controller/callbacks.rb
index 146d17cf40..42bab411d2 100644
--- a/actionpack/lib/abstract_controller/callbacks.rb
+++ b/actionpack/lib/abstract_controller/callbacks.rb
@@ -103,6 +103,10 @@ module AbstractController
# :call-seq: before_action(names, block)
#
# Append a callback before actions. See _insert_callbacks for parameter details.
+ #
+ # If the callback renders or redirects, the action will not run. If there
+ # are additional callbacks scheduled to run after that callback, they are
+ # also cancelled.
##
# :method: prepend_before_action
@@ -110,6 +114,10 @@ module AbstractController
# :call-seq: prepend_before_action(names, block)
#
# Prepend a callback before actions. See _insert_callbacks for parameter details.
+ #
+ # If the callback renders or redirects, the action will not run. If there
+ # are additional callbacks scheduled to run after that callback, they are
+ # also cancelled.
##
# :method: skip_before_action
@@ -124,6 +132,10 @@ module AbstractController
# :call-seq: append_before_action(names, block)
#
# Append a callback before actions. See _insert_callbacks for parameter details.
+ #
+ # If the callback renders or redirects, the action will not run. If there
+ # are additional callbacks scheduled to run after that callback, they are
+ # also cancelled.
##
# :method: after_action
diff --git a/actionpack/lib/action_controller/metal/content_security_policy.rb b/actionpack/lib/action_controller/metal/content_security_policy.rb
index 95f2f3242d..b8fab4ebe3 100644
--- a/actionpack/lib/action_controller/metal/content_security_policy.rb
+++ b/actionpack/lib/action_controller/metal/content_security_policy.rb
@@ -14,13 +14,17 @@ module ActionController #:nodoc:
end
module ClassMethods
- def content_security_policy(**options, &block)
+ def content_security_policy(enabled = true, **options, &block)
before_action(options) do
if block_given?
- policy = request.content_security_policy.clone
+ policy = current_content_security_policy
yield policy
request.content_security_policy = policy
end
+
+ unless enabled
+ request.content_security_policy = nil
+ end
end
end
@@ -40,5 +44,9 @@ module ActionController #:nodoc:
def content_security_policy_nonce
request.content_security_policy_nonce
end
+
+ def current_content_security_policy
+ request.content_security_policy.try(:clone) || ActionDispatch::ContentSecurityPolicy.new
+ end
end
end
diff --git a/actionpack/lib/action_controller/metal/strong_parameters.rb b/actionpack/lib/action_controller/metal/strong_parameters.rb
index 615c90c496..75ca282804 100644
--- a/actionpack/lib/action_controller/metal/strong_parameters.rb
+++ b/actionpack/lib/action_controller/metal/strong_parameters.rb
@@ -580,19 +580,17 @@ module ActionController
)
end
- if Hash.method_defined?(:dig)
- # Extracts the nested parameter from the given +keys+ by calling +dig+
- # at each step. Returns +nil+ if any intermediate step is +nil+.
- #
- # params = ActionController::Parameters.new(foo: { bar: { baz: 1 } })
- # params.dig(:foo, :bar, :baz) # => 1
- # params.dig(:foo, :zot, :xyz) # => nil
- #
- # params2 = ActionController::Parameters.new(foo: [10, 11, 12])
- # params2.dig(:foo, 1) # => 11
- def dig(*keys)
- convert_value_to_parameters(@parameters.dig(*keys))
- end
+ # Extracts the nested parameter from the given +keys+ by calling +dig+
+ # at each step. Returns +nil+ if any intermediate step is +nil+.
+ #
+ # params = ActionController::Parameters.new(foo: { bar: { baz: 1 } })
+ # params.dig(:foo, :bar, :baz) # => 1
+ # params.dig(:foo, :zot, :xyz) # => nil
+ #
+ # params2 = ActionController::Parameters.new(foo: [10, 11, 12])
+ # params2.dig(:foo, 1) # => 11
+ def dig(*keys)
+ convert_value_to_parameters(@parameters.dig(*keys))
end
# Returns a new <tt>ActionController::Parameters</tt> instance that
diff --git a/actionpack/lib/action_dispatch/middleware/ssl.rb b/actionpack/lib/action_dispatch/middleware/ssl.rb
index 6d9f36ad75..240269d1c7 100644
--- a/actionpack/lib/action_dispatch/middleware/ssl.rb
+++ b/actionpack/lib/action_dispatch/middleware/ssl.rb
@@ -15,6 +15,8 @@ module ActionDispatch
#
# config.ssl_options = { redirect: { exclude: -> request { request.path =~ /healthcheck/ } } }
#
+ # Cookies will not be flagged as secure for excluded requests.
+ #
# 2. <b>Secure cookies</b>: Sets the +secure+ flag on cookies to tell browsers they
# must not be sent along with +http://+ requests. Enabled by default. Set
# +config.ssl_options+ with <tt>secure_cookies: false</tt> to disable this feature.
@@ -71,7 +73,7 @@ module ActionDispatch
if request.ssl?
@app.call(env).tap do |status, headers, body|
set_hsts_header! headers
- flag_cookies_as_secure! headers if @secure_cookies
+ flag_cookies_as_secure! headers if @secure_cookies && !@exclude.call(request)
end
else
return redirect_to_https request unless @exclude.call(request)
diff --git a/actionpack/lib/action_dispatch/middleware/static.rb b/actionpack/lib/action_dispatch/middleware/static.rb
index 23492e14eb..acd999444a 100644
--- a/actionpack/lib/action_dispatch/middleware/static.rb
+++ b/actionpack/lib/action_dispatch/middleware/static.rb
@@ -16,7 +16,7 @@ module ActionDispatch
# does not exist, a 404 "File not Found" response will be returned.
class FileHandler
def initialize(root, index: "index", headers: {})
- @root = root.chomp("/")
+ @root = root.chomp("/").b
@file_server = ::Rack::File.new(@root, headers)
@index = index
end
@@ -35,7 +35,7 @@ module ActionDispatch
paths = [path, "#{path}#{ext}", "#{path}/#{@index}#{ext}"]
if match = paths.detect { |p|
- path = File.join(@root, p.dup.force_encoding(Encoding::UTF_8))
+ path = File.join(@root, p.b)
begin
File.file?(path) && File.readable?(path)
rescue SystemCallError
@@ -43,7 +43,7 @@ module ActionDispatch
end
}
- return ::Rack::Utils.escape_path(match)
+ return ::Rack::Utils.escape_path(match).b
end
end
diff --git a/actionpack/lib/action_dispatch/routing.rb b/actionpack/lib/action_dispatch/routing.rb
index 776058d98e..5cde677051 100644
--- a/actionpack/lib/action_dispatch/routing.rb
+++ b/actionpack/lib/action_dispatch/routing.rb
@@ -243,9 +243,9 @@ module ActionDispatch
#
# rails routes
#
- # Target specific controllers by prefixing the command with <tt>-c</tt> option. Use
- # <tt>--expanded</tt> to turn on the expanded table formatting mode.
- #
+ # Target a specific controller with <tt>-c</tt>, or grep routes
+ # using <tt>-g</tt>. Useful in conjunction with <tt>--expanded</tt>
+ # which displays routes vertically.
module Routing
extend ActiveSupport::Autoload
diff --git a/actionpack/lib/action_dispatch/routing/endpoint.rb b/actionpack/lib/action_dispatch/routing/endpoint.rb
index 24dced1efd..28bb20d688 100644
--- a/actionpack/lib/action_dispatch/routing/endpoint.rb
+++ b/actionpack/lib/action_dispatch/routing/endpoint.rb
@@ -3,12 +3,15 @@
module ActionDispatch
module Routing
class Endpoint # :nodoc:
- def dispatcher?; false; end
- def redirect?; false; end
- def engine?; rack_app.respond_to?(:routes); end
- def matches?(req); true; end
- def app; self; end
- def rack_app; app; end
+ def dispatcher?; false; end
+ def redirect?; false; end
+ def matches?(req); true; end
+ def app; self; end
+ def rack_app; app; end
+
+ def engine?
+ rack_app.is_a?(Class) && rack_app < Rails::Engine
+ end
end
end
end
diff --git a/actionpack/lib/action_dispatch/routing/inspector.rb b/actionpack/lib/action_dispatch/routing/inspector.rb
index de200fada0..bae50f6a43 100644
--- a/actionpack/lib/action_dispatch/routing/inspector.rb
+++ b/actionpack/lib/action_dispatch/routing/inspector.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true
require "delegate"
+require "io/console/size"
module ActionDispatch
module Routing
@@ -60,11 +61,11 @@ module ActionDispatch
@routes = routes
end
- def format(formatter, filter = nil)
+ def format(formatter, filter = {})
routes_to_display = filter_routes(normalize_filter(filter))
routes = collect_routes(routes_to_display)
if routes.none?
- formatter.no_routes(collect_routes(@routes))
+ formatter.no_routes(collect_routes(@routes), filter)
return formatter.result
end
@@ -80,12 +81,12 @@ module ActionDispatch
end
private
-
def normalize_filter(filter)
- if filter.is_a?(Hash) && filter[:controller]
+ if filter[:controller]
{ controller: /#{filter[:controller].downcase.sub(/_?controller\z/, '').sub('::', '/')}/ }
- elsif filter
- { controller: /#{filter}/, action: /#{filter}/, verb: /#{filter}/, name: /#{filter}/, path: /#{filter}/ }
+ elsif filter[:grep]
+ { controller: /#{filter[:grep]}/, action: /#{filter[:grep]}/,
+ verb: /#{filter[:grep]}/, name: /#{filter[:grep]}/, path: /#{filter[:grep]}/ }
end
end
@@ -126,7 +127,7 @@ module ActionDispatch
end
module ConsoleFormatter
- class Sheet
+ class Base
def initialize
@buffer = []
end
@@ -136,30 +137,44 @@ module ActionDispatch
end
def section_title(title)
- @buffer << "\n#{title}:"
end
def section(routes)
- @buffer << draw_section(routes)
end
def header(routes)
- @buffer << draw_header(routes)
end
- def no_routes(routes)
+ def no_routes(routes, filter)
@buffer <<
- if routes.none?
- <<~MESSAGE
- You don't have any routes defined!
+ if routes.none?
+ <<~MESSAGE
+ You don't have any routes defined!
+
+ Please add some routes in config/routes.rb.
+ MESSAGE
+ elsif filter.key?(:controller)
+ "No routes were found for this controller."
+ elsif filter.key?(:grep)
+ "No routes were found for this grep pattern."
+ end
- Please add some routes in config/routes.rb.
- MESSAGE
- else
- "No routes were found for this controller"
- end
@buffer << "For more information about routes, see the Rails guide: http://guides.rubyonrails.org/routing.html."
end
+ end
+
+ class Sheet < Base
+ def section_title(title)
+ @buffer << "\n#{title}:"
+ end
+
+ def section(routes)
+ @buffer << draw_section(routes)
+ end
+
+ def header(routes)
+ @buffer << draw_header(routes)
+ end
private
@@ -185,54 +200,36 @@ module ActionDispatch
end
end
- class Expanded
- def initialize
- @buffer = []
- end
-
- def result
- @buffer.join("")
- end
-
+ class Expanded < Base
def section_title(title)
- @buffer << "\n#{"[ #{title} ]"}\n"
+ @buffer << "\n#{"[ #{title} ]"}"
end
def section(routes)
@buffer << draw_expanded_section(routes)
end
- def header(routes)
- @buffer
- end
-
- def no_routes(routes)
- @buffer <<
- if routes.none?
- <<~MESSAGE
- You don't have any routes defined!
-
- Please add some routes in config/routes.rb.\n
- MESSAGE
- else
- "No routes were found for this controller\n"
- end
- @buffer << "For more information about routes, see the Rails guide: http://guides.rubyonrails.org/routing.html."
- end
-
private
def draw_expanded_section(routes)
routes.map.each_with_index do |r, i|
- <<~MESSAGE
- --[ Route #{i + 1} ]#{'-' * 60}
- Prefix | #{r[:name]}
- Verb | #{r[:verb]}
- URI | #{r[:path]}
- Controller#Action | #{r[:reqs]}
+ <<~MESSAGE.chomp
+ #{route_header(index: i + 1)}
+ Prefix | #{r[:name]}
+ Verb | #{r[:verb]}
+ URI | #{r[:path]}
+ Controller#Action | #{r[:reqs]}
MESSAGE
end
end
+
+ def route_header(index:)
+ console_width = IO.console_size.second
+ header_prefix = "--[ Route #{index} ]"
+ dash_remainder = [console_width - header_prefix.size, 0].max
+
+ "#{header_prefix}#{'-' * dash_remainder}"
+ end
end
end
@@ -264,7 +261,7 @@ module ActionDispatch
<a href="http://guides.rubyonrails.org/routing.html">Rails Routing from the Outside In</a>.
</li>
</ul>
- MESSAGE
+ MESSAGE
end
def result
diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb
index f3970d5445..d9dd24935b 100644
--- a/actionpack/lib/action_dispatch/routing/mapper.rb
+++ b/actionpack/lib/action_dispatch/routing/mapper.rb
@@ -664,6 +664,7 @@ module ActionDispatch
def define_generate_prefix(app, name)
_route = @set.named_routes.get name
_routes = @set
+ _url_helpers = @set.url_helpers
script_namer = ->(options) do
prefix_options = options.slice(*_route.segment_keys)
@@ -675,7 +676,7 @@ module ActionDispatch
# We must actually delete prefix segment keys to avoid passing them to next url_for.
_route.segment_keys.each { |k| options.delete(k) }
- _routes.url_helpers.send("#{name}_path", prefix_options)
+ _url_helpers.send("#{name}_path", prefix_options)
end
app.routes.define_mounted_helper(name, script_namer)
diff --git a/actionpack/lib/action_dispatch/routing/polymorphic_routes.rb b/actionpack/lib/action_dispatch/routing/polymorphic_routes.rb
index 6da869c0c2..e17ccaf986 100644
--- a/actionpack/lib/action_dispatch/routing/polymorphic_routes.rb
+++ b/actionpack/lib/action_dispatch/routing/polymorphic_routes.rb
@@ -120,8 +120,7 @@ module ActionDispatch
opts
end
- # Returns the path component of a URL for the given record. It uses
- # <tt>polymorphic_url</tt> with <tt>routing_type: :path</tt>.
+ # Returns the path component of a URL for the given record.
def polymorphic_path(record_or_hash_or_array, options = {})
if Hash === record_or_hash_or_array
options = record_or_hash_or_array.merge(options)
diff --git a/actionpack/lib/action_dispatch/routing/url_for.rb b/actionpack/lib/action_dispatch/routing/url_for.rb
index fa345dccdf..865d10f886 100644
--- a/actionpack/lib/action_dispatch/routing/url_for.rb
+++ b/actionpack/lib/action_dispatch/routing/url_for.rb
@@ -191,7 +191,25 @@ module ActionDispatch
end
end
- def route_for(name, *args) # :nodoc:
+ # Allows calling direct or regular named route.
+ #
+ # resources :buckets
+ #
+ # direct :recordable do |recording|
+ # route_for(:bucket, recording.bucket)
+ # end
+ #
+ # direct :threadable do |threadable|
+ # route_for(:recordable, threadable.parent)
+ # end
+ #
+ # This maintains the context of the original caller on
+ # whether to return a path or full url, e.g:
+ #
+ # threadable_path(threadable) # => "/buckets/1"
+ # threadable_url(threadable) # => "http://example.com/buckets/1"
+ #
+ def route_for(name, *args)
public_send(:"#{name}_url", *args)
end
diff --git a/actionpack/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb b/actionpack/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb
index ffa85f4e14..e47d5020f4 100644
--- a/actionpack/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb
+++ b/actionpack/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb
@@ -19,6 +19,7 @@ module ActionDispatch
def after_teardown
take_failed_screenshot
Capybara.reset_sessions!
+ ensure
super
end
end
diff --git a/actionpack/test/dispatch/content_security_policy_test.rb b/actionpack/test/dispatch/content_security_policy_test.rb
index b88f90190a..f133aae865 100644
--- a/actionpack/test/dispatch/content_security_policy_test.rb
+++ b/actionpack/test/dispatch/content_security_policy_test.rb
@@ -258,6 +258,8 @@ class ContentSecurityPolicyIntegrationTest < ActionDispatch::IntegrationTest
p.script_src :self
end
+ content_security_policy(false, only: :no_policy)
+
content_security_policy_report_only only: :report_only
def index
@@ -280,6 +282,10 @@ class ContentSecurityPolicyIntegrationTest < ActionDispatch::IntegrationTest
head :ok
end
+ def no_policy
+ head :ok
+ end
+
private
def condition?
params[:condition] == "true"
@@ -294,6 +300,7 @@ class ContentSecurityPolicyIntegrationTest < ActionDispatch::IntegrationTest
get "/conditional", to: "policy#conditional"
get "/report-only", to: "policy#report_only"
get "/script-src", to: "policy#script_src"
+ get "/no-policy", to: "policy#no_policy"
end
end
@@ -353,19 +360,14 @@ class ContentSecurityPolicyIntegrationTest < ActionDispatch::IntegrationTest
assert_policy "script-src 'self' 'nonce-iyhD0Yc0W+c='"
end
- private
-
- def env_config
- Rails.application.env_config
- end
+ def test_generates_no_content_security_policy
+ get "/no-policy"
- def content_security_policy
- env_config["action_dispatch.content_security_policy"]
- end
+ assert_nil response.headers["Content-Security-Policy"]
+ assert_nil response.headers["Content-Security-Policy-Report-Only"]
+ end
- def content_security_policy=(policy)
- env_config["action_dispatch.content_security_policy"] = policy
- end
+ private
def assert_policy(expected, report_only: false)
assert_response :success
@@ -382,3 +384,61 @@ class ContentSecurityPolicyIntegrationTest < ActionDispatch::IntegrationTest
assert_equal expected, response.headers[expected_header]
end
end
+
+class DisabledContentSecurityPolicyIntegrationTest < ActionDispatch::IntegrationTest
+ class PolicyController < ActionController::Base
+ content_security_policy only: :inline do |p|
+ p.default_src "https://example.com"
+ end
+
+ def index
+ head :ok
+ end
+
+ def inline
+ head :ok
+ end
+ end
+
+ ROUTES = ActionDispatch::Routing::RouteSet.new
+ ROUTES.draw do
+ scope module: "disabled_content_security_policy_integration_test" do
+ get "/", to: "policy#index"
+ get "/inline", to: "policy#inline"
+ end
+ end
+
+ class PolicyConfigMiddleware
+ def initialize(app)
+ @app = app
+ end
+
+ def call(env)
+ env["action_dispatch.content_security_policy"] = nil
+ env["action_dispatch.content_security_policy_nonce_generator"] = nil
+ env["action_dispatch.content_security_policy_report_only"] = false
+ env["action_dispatch.show_exceptions"] = false
+
+ @app.call(env)
+ end
+ end
+
+ APP = build_app(ROUTES) do |middleware|
+ middleware.use PolicyConfigMiddleware
+ middleware.use ActionDispatch::ContentSecurityPolicy::Middleware
+ end
+
+ def app
+ APP
+ end
+
+ def test_generates_no_content_security_policy_by_default
+ get "/"
+ assert_nil response.headers["Content-Security-Policy"]
+ end
+
+ def test_generates_content_security_policy_header_when_globally_disabled
+ get "/inline"
+ assert_equal "default-src https://example.com", response.headers["Content-Security-Policy"]
+ end
+end
diff --git a/actionpack/test/dispatch/routing/inspector_test.rb b/actionpack/test/dispatch/routing/inspector_test.rb
index 127212b228..9150d5010b 100644
--- a/actionpack/test/dispatch/routing/inspector_test.rb
+++ b/actionpack/test/dispatch/routing/inspector_test.rb
@@ -3,6 +3,7 @@
require "abstract_unit"
require "rails/engine"
require "action_dispatch/routing/inspector"
+require "io/console/size"
class MountedRackApp
def self.call(env)
@@ -15,16 +16,10 @@ end
module ActionDispatch
module Routing
class RoutesInspectorTest < ActiveSupport::TestCase
- def setup
+ setup do
@set = ActionDispatch::Routing::RouteSet.new
end
- def draw(options = nil, formater = ActionDispatch::Routing::ConsoleFormatter::Sheet.new, &block)
- @set.draw(&block)
- inspector = ActionDispatch::Routing::RoutesInspector.new(@set.routes)
- inspector.format(formater, options).split("\n")
- end
-
def test_displaying_routes_for_engines
engine = Class.new(Rails::Engine) do
def self.inspect
@@ -305,7 +300,7 @@ module ActionDispatch
end
def test_routes_can_be_filtered
- output = draw("posts") do
+ output = draw(grep: "posts") do
resources :articles
resources :posts
end
@@ -322,6 +317,9 @@ module ActionDispatch
end
def test_routes_when_expanded
+ previous_console_winsize = IO.console.winsize
+ IO.console.winsize = [0, 23]
+
engine = Class.new(Rails::Engine) do
def self.inspect
"Blog::Engine"
@@ -331,50 +329,51 @@ module ActionDispatch
get "/cart", to: "cart#show"
end
- output = draw(nil, ActionDispatch::Routing::ConsoleFormatter::Expanded.new) do
+ output = draw(formatter: ActionDispatch::Routing::ConsoleFormatter::Expanded.new) do
get "/custom/assets", to: "custom_assets#show"
get "/custom/furnitures", to: "custom_furnitures#show"
mount engine => "/blog", :as => "blog"
end
- assert_equal ["--[ Route 1 ]------------------------------------------------------------",
+ assert_equal ["--[ Route 1 ]----------",
"Prefix | custom_assets",
"Verb | GET",
"URI | /custom/assets(.:format)",
"Controller#Action | custom_assets#show",
- "--[ Route 2 ]------------------------------------------------------------",
+ "--[ Route 2 ]----------",
"Prefix | custom_furnitures",
"Verb | GET",
"URI | /custom/furnitures(.:format)",
"Controller#Action | custom_furnitures#show",
- "--[ Route 3 ]------------------------------------------------------------",
+ "--[ Route 3 ]----------",
"Prefix | blog",
"Verb | ",
"URI | /blog",
"Controller#Action | Blog::Engine",
"",
"[ Routes for Blog::Engine ]",
- "--[ Route 1 ]------------------------------------------------------------",
+ "--[ Route 1 ]----------",
"Prefix | cart",
"Verb | GET",
"URI | /cart(.:format)",
"Controller#Action | cart#show"], output
+ ensure
+ IO.console.winsize = previous_console_winsize
end
-
def test_no_routes_matched_filter_when_expanded
- output = draw("rails/dummy", ActionDispatch::Routing::ConsoleFormatter::Expanded.new) do
+ output = draw(grep: "rails/dummy", formatter: ActionDispatch::Routing::ConsoleFormatter::Expanded.new) do
get "photos/:id" => "photos#show", :id => /[A-Z]\d{5}/
end
assert_equal [
- "No routes were found for this controller",
+ "No routes were found for this grep pattern.",
"For more information about routes, see the Rails guide: http://guides.rubyonrails.org/routing.html."
], output
end
def test_not_routes_when_expanded
- output = draw("rails/dummy", ActionDispatch::Routing::ConsoleFormatter::Expanded.new) {}
+ output = draw(grep: "rails/dummy", formatter: ActionDispatch::Routing::ConsoleFormatter::Expanded.new) {}
assert_equal [
"You don't have any routes defined!",
@@ -386,7 +385,7 @@ module ActionDispatch
end
def test_routes_can_be_filtered_with_namespaced_controllers
- output = draw("admin/posts") do
+ output = draw(grep: "admin/posts") do
resources :articles
namespace :admin do
resources :posts
@@ -434,24 +433,24 @@ module ActionDispatch
end
assert_equal [
- "No routes were found for this controller",
+ "No routes were found for this controller.",
"For more information about routes, see the Rails guide: http://guides.rubyonrails.org/routing.html."
], output
end
def test_no_routes_matched_filter
- output = draw("rails/dummy") do
+ output = draw(grep: "rails/dummy") do
get "photos/:id" => "photos#show", :id => /[A-Z]\d{5}/
end
assert_equal [
- "No routes were found for this controller",
+ "No routes were found for this grep pattern.",
"For more information about routes, see the Rails guide: http://guides.rubyonrails.org/routing.html."
], output
end
def test_no_routes_were_defined
- output = draw("Rails::DummyController") {}
+ output = draw(grep: "Rails::DummyController") {}
assert_equal [
"You don't have any routes defined!",
@@ -484,6 +483,13 @@ module ActionDispatch
"custom_assets GET /custom/assets(.:format) custom_assets#show",
], output
end
+
+ private
+ def draw(formatter: ActionDispatch::Routing::ConsoleFormatter::Sheet.new, **options, &block)
+ @set.draw(&block)
+ inspector = ActionDispatch::Routing::RoutesInspector.new(@set.routes)
+ inspector.format(formatter, options).split("\n")
+ end
end
end
end
diff --git a/actionpack/test/dispatch/ssl_test.rb b/actionpack/test/dispatch/ssl_test.rb
index 90f2ee46ea..baf46e7c7e 100644
--- a/actionpack/test/dispatch/ssl_test.rb
+++ b/actionpack/test/dispatch/ssl_test.rb
@@ -208,6 +208,14 @@ class SecureCookiesTest < SSLTest
assert_cookies(*DEFAULT.split("\n"))
end
+ def test_cookies_as_not_secure_with_exclude
+ excluding = { exclude: -> request { request.domain =~ /example/ } }
+ get headers: { "Set-Cookie" => DEFAULT }, ssl_options: { redirect: excluding }
+
+ assert_cookies(*DEFAULT.split("\n"))
+ assert_response :ok
+ end
+
def test_no_cookies
get
assert_nil response.headers["Set-Cookie"]
diff --git a/actionpack/test/dispatch/static_test.rb b/actionpack/test/dispatch/static_test.rb
index 0bdff68692..6b69cd9999 100644
--- a/actionpack/test/dispatch/static_test.rb
+++ b/actionpack/test/dispatch/static_test.rb
@@ -71,7 +71,16 @@ module StaticTests
end
def test_served_static_file_with_non_english_filename
- assert_html "means hello in Japanese\n", get("/foo/#{Rack::Utils.escape("こんにちは.html")}")
+ assert_html "means hello in Japanese\n", get("/foo/%E3%81%93%E3%82%93%E3%81%AB%E3%81%A1%E3%81%AF.html")
+ end
+
+ def test_served_gzipped_static_file_with_non_english_filename
+ response = get("/foo/%E3%81%95%E3%82%88%E3%81%86%E3%81%AA%E3%82%89.html", "HTTP_ACCEPT_ENCODING" => "gzip")
+
+ assert_gzip "/foo/さようなら.html", response
+ assert_equal "text/html", response.headers["Content-Type"]
+ assert_equal "Accept-Encoding", response.headers["Vary"]
+ assert_equal "gzip", response.headers["Content-Encoding"]
end
def test_serves_static_file_with_exclamation_mark_in_filename
diff --git a/actionpack/test/fixtures/public/foo/さようなら.html b/actionpack/test/fixtures/public/foo/さようなら.html
new file mode 100644
index 0000000000..627bb2469f
--- /dev/null
+++ b/actionpack/test/fixtures/public/foo/さようなら.html
@@ -0,0 +1 @@
+means goodbye in Japanese
diff --git a/actionpack/test/fixtures/public/foo/さようなら.html.gz b/actionpack/test/fixtures/public/foo/さようなら.html.gz
new file mode 100644
index 0000000000..4f484cfe86
--- /dev/null
+++ b/actionpack/test/fixtures/public/foo/さようなら.html.gz
Binary files differ
diff --git a/actionpack/test/fixtures/公共/foo/さようなら.html b/actionpack/test/fixtures/公共/foo/さようなら.html
new file mode 100644
index 0000000000..627bb2469f
--- /dev/null
+++ b/actionpack/test/fixtures/公共/foo/さようなら.html
@@ -0,0 +1 @@
+means goodbye in Japanese
diff --git a/actionpack/test/fixtures/公共/foo/さようなら.html.gz b/actionpack/test/fixtures/公共/foo/さようなら.html.gz
new file mode 100644
index 0000000000..4f484cfe86
--- /dev/null
+++ b/actionpack/test/fixtures/公共/foo/さようなら.html.gz
Binary files differ
diff --git a/actionview/CHANGELOG.md b/actionview/CHANGELOG.md
index 55f2148640..f44f03f40d 100644
--- a/actionview/CHANGELOG.md
+++ b/actionview/CHANGELOG.md
@@ -1,12 +1,10 @@
-## Rails 6.0.0.alpha (Unreleased) ##
-
* Enable select tag helper to mark `prompt` option as `selected` and/or `disabled` for `required`
field. Example:
- select :post,
- :category,
- ["lifestyle", "programming", "spiritual"],
- { selected: "", disabled: "", prompt: "Choose one" },
+ select :post,
+ :category,
+ ["lifestyle", "programming", "spiritual"],
+ { selected: "", disabled: "", prompt: "Choose one" },
{ required: true }
Placeholder option would be selected and disabled. The HTML produced:
@@ -19,7 +17,7 @@
*Sergey Prikhodko*
-* Don't enforce UTF-8 by default
+* Don't enforce UTF-8 by default.
With the disabling of TLS 1.0 by most major websites, continuing to run
IE8 or lower becomes increasingly difficult so default to not enforcing
diff --git a/actionview/lib/action_view/digestor.rb b/actionview/lib/action_view/digestor.rb
index 1cf0bd3016..dbd7a4ee11 100644
--- a/actionview/lib/action_view/digestor.rb
+++ b/actionview/lib/action_view/digestor.rb
@@ -45,11 +45,9 @@ module ActionView
# Create a dependency tree for template named +name+.
def tree(name, finder, partial = false, seen = {})
logical_name = name.gsub(%r|/_|, "/")
+ finder.formats = [finder.rendered_format] if finder.rendered_format
- options = {}
- options[:formats] = [finder.rendered_format] if finder.rendered_format
-
- if template = finder.disable_cache { finder.find_all(logical_name, [], partial, [], options).first }
+ if template = finder.disable_cache { finder.find_all(logical_name, [], partial, []).first }
finder.rendered_format ||= template.formats.first
if node = seen[template.identifier] # handle cycles in the tree
diff --git a/actionview/lib/action_view/helpers/form_tag_helper.rb b/actionview/lib/action_view/helpers/form_tag_helper.rb
index 5a8b8555a0..54f82e058e 100644
--- a/actionview/lib/action_view/helpers/form_tag_helper.rb
+++ b/actionview/lib/action_view/helpers/form_tag_helper.rb
@@ -551,7 +551,8 @@ module ActionView
# # => <input src="/assets/save.png" data-confirm="Are you sure?" type="image" />
def image_submit_tag(source, options = {})
options = options.stringify_keys
- tag :input, { "type" => "image", "src" => path_to_image(source) }.update(options)
+ src = path_to_image(source, skip_pipeline: options.delete("skip_pipeline"))
+ tag :input, { "type" => "image", "src" => src }.update(options)
end
# Creates a field set for grouping HTML form elements.
diff --git a/actionview/lib/action_view/helpers/tags/base.rb b/actionview/lib/action_view/helpers/tags/base.rb
index f1eca2268a..eef527d36f 100644
--- a/actionview/lib/action_view/helpers/tags/base.rb
+++ b/actionview/lib/action_view/helpers/tags/base.rb
@@ -109,11 +109,11 @@ module ActionView
# a little duplication to construct less strings
case
when @object_name.empty?
- "#{sanitized_method_name}#{"[]" if multiple}"
+ "#{sanitized_method_name}#{multiple ? "[]" : ""}"
when index
- "#{@object_name}[#{index}][#{sanitized_method_name}]#{"[]" if multiple}"
+ "#{@object_name}[#{index}][#{sanitized_method_name}]#{multiple ? "[]" : ""}"
else
- "#{@object_name}[#{sanitized_method_name}]#{"[]" if multiple}"
+ "#{@object_name}[#{sanitized_method_name}]#{multiple ? "[]" : ""}"
end
end
diff --git a/actionview/lib/action_view/helpers/translation_helper.rb b/actionview/lib/action_view/helpers/translation_helper.rb
index 1860bc4732..80cb73d683 100644
--- a/actionview/lib/action_view/helpers/translation_helper.rb
+++ b/actionview/lib/action_view/helpers/translation_helper.rb
@@ -122,9 +122,12 @@ module ActionView
private
def scope_key_by_partial(key)
- if key.to_s.first == "."
+ stringified_key = key.to_s
+ if stringified_key.first == "."
if @virtual_path
- @virtual_path.gsub(%r{/_?}, ".") + key.to_s
+ @_scope_key_by_partial_cache ||= {}
+ @_scope_key_by_partial_cache[@virtual_path] ||= @virtual_path.gsub(%r{/_?}, ".")
+ "#{@_scope_key_by_partial_cache[@virtual_path]}#{stringified_key}"
else
raise "Cannot use t(#{key.inspect}) shortcut because path is not available"
end
diff --git a/actionview/test/activerecord/multifetch_cache_test.rb b/actionview/test/activerecord/multifetch_cache_test.rb
new file mode 100644
index 0000000000..12be069e69
--- /dev/null
+++ b/actionview/test/activerecord/multifetch_cache_test.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+require "active_record_unit"
+require "active_record/railties/collection_cache_association_loading"
+
+ActionView::PartialRenderer.prepend(ActiveRecord::Railties::CollectionCacheAssociationLoading)
+
+class MultifetchCacheTest < ActiveRecordTestCase
+ fixtures :topics, :replies
+
+ def setup
+ view_paths = ActionController::Base.view_paths
+
+ @view = Class.new(ActionView::Base) do
+ def view_cache_dependencies
+ []
+ end
+
+ def combined_fragment_cache_key(key)
+ [ :views, key ]
+ end
+ end.new(view_paths, {})
+ end
+
+ def test_only_preloading_for_records_that_miss_the_cache
+ @view.render partial: "test/partial", collection: [topics(:rails)], cached: true
+
+ @topics = Topic.preload(:replies)
+
+ @view.render partial: "test/partial", collection: @topics, cached: true
+
+ assert_not @topics.detect { |topic| topic.id == topics(:rails).id }.replies.loaded?
+ assert @topics.detect { |topic| topic.id != topics(:rails).id }.replies.loaded?
+ end
+end
diff --git a/actionview/test/fixtures/digestor/comments/show.js.erb b/actionview/test/fixtures/digestor/comments/show.js.erb
new file mode 100644
index 0000000000..38b37dfa2b
--- /dev/null
+++ b/actionview/test/fixtures/digestor/comments/show.js.erb
@@ -0,0 +1 @@
+alert("<%=j render("comments/comment") %>")
diff --git a/actionview/test/template/digestor_test.rb b/actionview/test/template/digestor_test.rb
index 1bfa39a319..ddaa7febb3 100644
--- a/actionview/test/template/digestor_test.rb
+++ b/actionview/test/template/digestor_test.rb
@@ -160,6 +160,18 @@ class TemplateDigestorTest < ActionView::TestCase
assert_equal [:html], tree_template_formats("messages/show").uniq
end
+ def test_template_dependencies_with_fallback_from_js_to_html_format
+ finder.rendered_format = :js
+ assert_equal ["comments/comment"], dependencies("comments/show")
+ end
+
+ def test_template_digest_with_fallback_from_js_to_html_format
+ finder.rendered_format = :js
+ assert_digest_difference("comments/show") do
+ change_template("comments/_comment")
+ end
+ end
+
def test_recursion_in_renders
assert digest("level/recursion") # assert recursion is possible
assert_not_nil digest("level/recursion") # assert digest is stored
diff --git a/activejob/CHANGELOG.md b/activejob/CHANGELOG.md
index c6a3ad8ade..a3d13ad162 100644
--- a/activejob/CHANGELOG.md
+++ b/activejob/CHANGELOG.md
@@ -1,6 +1,12 @@
-## Rails 6.0.0.alpha (Unreleased) ##
+* Remove support for Qu gem.
-* Add support for timezones to Active Job
+ Reasons are that the Qu gem wasn't compatible since Rails 5.1,
+ gem development was stopped in 2014 and maintainers have
+ confirmed its demise. See issue #32273
+
+ *Alberto Almagro*
+
+* Add support for timezones to Active Job.
Record what was the current timezone in effect when the job was
enqueued and then restore when the job is executed in same way
diff --git a/activejob/README.md b/activejob/README.md
index f1ebb76e08..d49fcfe3c2 100644
--- a/activejob/README.md
+++ b/activejob/README.md
@@ -88,6 +88,12 @@ Active Job has built-in adapters for multiple queueing backends (Sidekiq,
Resque, Delayed Job and others). To get an up-to-date list of the adapters
see the API Documentation for [ActiveJob::QueueAdapters](http://api.rubyonrails.org/classes/ActiveJob/QueueAdapters.html).
+**Please note:** We are not accepting pull requests for new adapters. We
+encourage library authors to provide an ActiveJob adapter as part of
+their gem, or as a stand-alone gem. For discussion about this see the
+following PRs: [23311](https://github.com/rails/rails/issues/23311#issuecomment-176275718),
+[21406](https://github.com/rails/rails/pull/21406#issuecomment-138813484), and [#32285](https://github.com/rails/rails/pull/32285).
+
## Auxiliary gems
* [activejob-stats](https://github.com/seuros/activejob-stats)
diff --git a/activejob/Rakefile b/activejob/Rakefile
index 77f3e7f8ff..b4da75adab 100644
--- a/activejob/Rakefile
+++ b/activejob/Rakefile
@@ -2,7 +2,6 @@
require "rake/testtask"
-# TODO: add qu back to the list after it support Rails 5.1
ACTIVEJOB_ADAPTERS = %w(async inline delayed_job que queue_classic resque sidekiq sneakers sucker_punch backburner test)
ACTIVEJOB_ADAPTERS.delete("queue_classic") if defined?(JRUBY_VERSION)
diff --git a/activejob/lib/active_job/queue_adapters.rb b/activejob/lib/active_job/queue_adapters.rb
index c1a1d3c510..7854467cc1 100644
--- a/activejob/lib/active_job/queue_adapters.rb
+++ b/activejob/lib/active_job/queue_adapters.rb
@@ -7,7 +7,6 @@ module ActiveJob
#
# * {Backburner}[https://github.com/nesquena/backburner]
# * {Delayed Job}[https://github.com/collectiveidea/delayed_job]
- # * {Qu}[https://github.com/bkeepers/qu]
# * {Que}[https://github.com/chanks/que]
# * {queue_classic}[https://github.com/QueueClassic/queue_classic]
# * {Resque}[https://github.com/resque/resque]
@@ -16,6 +15,7 @@ module ActiveJob
# * {Sucker Punch}[https://github.com/brandonhilkert/sucker_punch]
# * {Active Job Async Job}[http://api.rubyonrails.org/classes/ActiveJob/QueueAdapters/AsyncAdapter.html]
# * {Active Job Inline}[http://api.rubyonrails.org/classes/ActiveJob/QueueAdapters/InlineAdapter.html]
+ # * Please Note: We are not accepting pull requests for new adapters. See the README for more details.
#
# === Backends Features
#
@@ -23,7 +23,6 @@ module ActiveJob
# |-------------------|-------|--------|------------|------------|---------|---------|
# | Backburner | Yes | Yes | Yes | Yes | Job | Global |
# | Delayed Job | Yes | Yes | Yes | Job | Global | Global |
- # | Qu | Yes | Yes | No | No | No | Global |
# | Que | Yes | Yes | Yes | Job | No | Job |
# | queue_classic | Yes | Yes | Yes* | No | No | No |
# | Resque | Yes | Yes | Yes (Gem) | Queue | Global | Yes |
@@ -114,7 +113,6 @@ module ActiveJob
autoload :InlineAdapter
autoload :BackburnerAdapter
autoload :DelayedJobAdapter
- autoload :QuAdapter
autoload :QueAdapter
autoload :QueueClassicAdapter
autoload :ResqueAdapter
diff --git a/activejob/lib/active_job/queue_adapters/qu_adapter.rb b/activejob/lib/active_job/queue_adapters/qu_adapter.rb
deleted file mode 100644
index bd7003e177..0000000000
--- a/activejob/lib/active_job/queue_adapters/qu_adapter.rb
+++ /dev/null
@@ -1,46 +0,0 @@
-# frozen_string_literal: true
-
-require "qu"
-
-module ActiveJob
- module QueueAdapters
- # == Qu adapter for Active Job
- #
- # Qu is a Ruby library for queuing and processing background jobs. It is
- # heavily inspired by delayed_job and Resque. Qu was created to overcome
- # some shortcomings in the existing queuing libraries.
- # The advantages of Qu are: Multiple backends (redis, mongo), jobs are
- # requeued when worker is killed, resque-like API.
- #
- # Read more about Qu {here}[https://github.com/bkeepers/qu].
- #
- # To use Qu set the queue_adapter config to +:qu+.
- #
- # Rails.application.config.active_job.queue_adapter = :qu
- class QuAdapter
- def enqueue(job, *args) #:nodoc:
- qu_job = Qu::Payload.new(klass: JobWrapper, args: [job.serialize]).tap do |payload|
- payload.instance_variable_set(:@queue, job.queue_name)
- end.push
-
- # qu_job can be nil depending on the configured backend
- job.provider_job_id = qu_job.id unless qu_job.nil?
- qu_job
- end
-
- def enqueue_at(job, timestamp, *args) #:nodoc:
- raise NotImplementedError, "This queueing backend does not support scheduling jobs. To see what features are supported go to http://api.rubyonrails.org/classes/ActiveJob/QueueAdapters.html"
- end
-
- class JobWrapper < Qu::Job #:nodoc:
- def initialize(job_data)
- @job_data = job_data
- end
-
- def perform
- Base.execute @job_data
- end
- end
- end
- end
-end
diff --git a/activejob/test/adapters/qu.rb b/activejob/test/adapters/qu.rb
deleted file mode 100644
index 5b471fa347..0000000000
--- a/activejob/test/adapters/qu.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-# frozen_string_literal: true
-
-require "qu-immediate"
-
-ActiveJob::Base.queue_adapter = :qu
diff --git a/activejob/test/integration/queuing_test.rb b/activejob/test/integration/queuing_test.rb
index 7a95d3d039..32afb5ca62 100644
--- a/activejob/test/integration/queuing_test.rb
+++ b/activejob/test/integration/queuing_test.rb
@@ -82,7 +82,7 @@ class QueuingTest < ActiveSupport::TestCase
end
test "should supply a provider_job_id when available for immediate jobs" do
- skip unless adapter_is?(:async, :delayed_job, :sidekiq, :qu, :que, :queue_classic)
+ skip unless adapter_is?(:async, :delayed_job, :sidekiq, :que, :queue_classic)
test_job = TestJob.perform_later @id
assert test_job.provider_job_id, "Provider job id should be set by provider"
end
diff --git a/activejob/test/support/integration/adapters/qu.rb b/activejob/test/support/integration/adapters/qu.rb
deleted file mode 100644
index 67db03e279..0000000000
--- a/activejob/test/support/integration/adapters/qu.rb
+++ /dev/null
@@ -1,40 +0,0 @@
-# frozen_string_literal: true
-
-module QuJobsManager
- def setup
- require "qu-rails"
- require "qu-redis"
- ActiveJob::Base.queue_adapter = :qu
- ENV["REDISTOGO_URL"] = "redis://127.0.0.1:6379/12"
- backend = Qu::Backend::Redis.new
- backend.namespace = "active_jobs_int_test"
- Qu.backend = backend
- Qu.logger = Rails.logger
- Qu.interval = 0.5
- unless can_run?
- puts "Cannot run integration tests for qu. To be able to run integration tests for qu you need to install and start redis.\n"
- exit
- end
- end
-
- def clear_jobs
- Qu.clear "integration_tests"
- end
-
- def start_workers
- @thread = Thread.new { Qu::Worker.new("integration_tests").start }
- end
-
- def stop_workers
- @thread.kill
- end
-
- def can_run?
- begin
- Qu.backend.connection.client.connect
- rescue
- return false
- end
- true
- end
-end
diff --git a/activemodel/CHANGELOG.md b/activemodel/CHANGELOG.md
index b28c83e4ed..6b557a7cb1 100644
--- a/activemodel/CHANGELOG.md
+++ b/activemodel/CHANGELOG.md
@@ -1,5 +1,3 @@
-## Rails 6.0.0.alpha (Unreleased) ##
-
* Rails 6 requires Ruby 2.4.1 or newer.
*Jeremy Daer*
diff --git a/activemodel/lib/active_model/type/time.rb b/activemodel/lib/active_model/type/time.rb
index ad7ba0351a..c094ee0013 100644
--- a/activemodel/lib/active_model/type/time.rb
+++ b/activemodel/lib/active_model/type/time.rb
@@ -28,14 +28,10 @@ module ActiveModel
private
def cast_value(value)
- return value unless value.is_a?(::String)
+ return apply_seconds_precision(value) unless value.is_a?(::String)
return if value.empty?
- if value.start_with?("2000-01-01")
- dummy_time_value = value
- else
- dummy_time_value = "2000-01-01 #{value}"
- end
+ dummy_time_value = value.sub(/\A(\d\d\d\d-\d\d-\d\d |)/, "2000-01-01 ")
fast_string_to_time(dummy_time_value) || begin
time_hash = ::Date._parse(dummy_time_value)
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index 325c4abfc8..2623a3226a 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,4 +1,6 @@
-## Rails 6.0.0.alpha (Unreleased) ##
+* Add custom prefix option to ActiveRecord::Store.store_accessor.
+
+ *Tan Huynh*
* Rails 6 requires Ruby 2.4.1 or newer.
diff --git a/activerecord/lib/active_record.rb b/activerecord/lib/active_record.rb
index d43378c64f..d198466dbf 100644
--- a/activerecord/lib/active_record.rb
+++ b/activerecord/lib/active_record.rb
@@ -40,6 +40,7 @@ module ActiveRecord
autoload :Core
autoload :ConnectionHandling
autoload :CounterCache
+ autoload :DatabaseConfigurations
autoload :DynamicMatchers
autoload :Enum
autoload :InternalMetadata
diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb
index cc99401390..7ab9160265 100644
--- a/activerecord/lib/active_record/base.rb
+++ b/activerecord/lib/active_record/base.rb
@@ -290,6 +290,7 @@ module ActiveRecord #:nodoc:
extend CollectionCacheKey
include Core
+ include DatabaseConfigurations
include Persistence
include ReadonlyAttributes
include ModelSchema
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
index 92e46ccf9f..aec5fa6ba1 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
@@ -130,7 +130,7 @@ module ActiveRecord
end
def quoted_time(value) # :nodoc:
- quoted_date(value).sub(/\A2000-01-01 /, "")
+ quoted_date(value).sub(/\A\d\d\d\d-\d\d-\d\d /, "")
end
def quoted_binary(value) # :nodoc:
diff --git a/activerecord/lib/active_record/connection_adapters/connection_specification.rb b/activerecord/lib/active_record/connection_adapters/connection_specification.rb
index 508132accb..901717ae3d 100644
--- a/activerecord/lib/active_record/connection_adapters/connection_specification.rb
+++ b/activerecord/lib/active_record/connection_adapters/connection_specification.rb
@@ -156,7 +156,6 @@ module ActiveRecord
env_config = config[env] if config[env].is_a?(Hash) && !(config[env].key?("adapter") || config[env].key?("url"))
end
- config.reject! { |k, v| v.is_a?(Hash) && !(v.key?("adapter") || v.key?("url")) }
config.merge! env_config if env_config
config.each do |key, value|
diff --git a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb
index bfdc7995f0..4c57bd48ab 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb
@@ -3,7 +3,7 @@
require "active_record/connection_adapters/abstract_mysql_adapter"
require "active_record/connection_adapters/mysql/database_statements"
-gem "mysql2", "~> 0.4.4"
+gem "mysql2", ">= 0.4.4", "< 0.6.0"
require "mysql2"
module ActiveRecord
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
index 45b230f0f9..e20e5f2914 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
@@ -107,7 +107,7 @@ module ActiveRecord
oid = row[4]
comment = row[5]
- using, expressions, where = inddef.scan(/ USING (\w+?) \((.+?)\)(?: WHERE (.+))?\z/).flatten
+ using, expressions, where = inddef.scan(/ USING (\w+?) \((.+?)\)(?: WHERE (.+))?\z/m).flatten
orders = {}
opclasses = {}
diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3/quoting.rb b/activerecord/lib/active_record/connection_adapters/sqlite3/quoting.rb
index 8042dbfea2..70de96326c 100644
--- a/activerecord/lib/active_record/connection_adapters/sqlite3/quoting.rb
+++ b/activerecord/lib/active_record/connection_adapters/sqlite3/quoting.rb
@@ -17,7 +17,7 @@ module ActiveRecord
end
def quoted_time(value)
- quoted_date(value)
+ quoted_date(value).sub(/\A\d\d\d\d-\d\d-\d\d /, "2000-01-01 ")
end
def quoted_binary(value)
diff --git a/activerecord/lib/active_record/connection_handling.rb b/activerecord/lib/active_record/connection_handling.rb
index 88d28dc52a..ee0e651912 100644
--- a/activerecord/lib/active_record/connection_handling.rb
+++ b/activerecord/lib/active_record/connection_handling.rb
@@ -57,6 +57,10 @@ module ActiveRecord
spec = resolver.resolve(config).symbolize_keys
spec[:name] = spec_name
+ # use the primary config if a config is not passed in and
+ # it's a three tier config
+ spec = spec[spec_name.to_sym] if spec[spec_name.to_sym]
+
connection_handler.establish_connection(spec)
end
diff --git a/activerecord/lib/active_record/database_configurations.rb b/activerecord/lib/active_record/database_configurations.rb
new file mode 100644
index 0000000000..86624a41c9
--- /dev/null
+++ b/activerecord/lib/active_record/database_configurations.rb
@@ -0,0 +1,63 @@
+# frozen_string_literal: true
+
+module ActiveRecord
+ module DatabaseConfigurations # :nodoc:
+ class DatabaseConfig
+ attr_reader :env_name, :spec_name, :config
+
+ def initialize(env_name, spec_name, config)
+ @env_name = env_name
+ @spec_name = spec_name
+ @config = config
+ end
+ end
+
+ # Selects the config for the specified environment and specification name
+ #
+ # For example if passed :development, and :animals it will select the database
+ # under the :development and :animals configuration level
+ def self.config_for_env_and_spec(environment, specification_name, configs = ActiveRecord::Base.configurations) # :nodoc:
+ configs_for(environment, configs).find do |db_config|
+ db_config.spec_name == specification_name
+ end
+ end
+
+ # Collects the configs for the environment passed in.
+ #
+ # If a block is given returns the specification name and configuration
+ # otherwise returns an array of DatabaseConfig structs for the environment.
+ def self.configs_for(env, configs = ActiveRecord::Base.configurations, &blk) # :nodoc:
+ env_with_configs = db_configs(configs).select do |db_config|
+ db_config.env_name == env
+ end
+
+ if block_given?
+ env_with_configs.each do |env_with_config|
+ yield env_with_config.spec_name, env_with_config.config
+ end
+ else
+ env_with_configs
+ end
+ end
+
+ # Given an env, spec and config creates DatabaseConfig structs with
+ # each attribute set.
+ def self.walk_configs(env_name, spec_name, config) # :nodoc:
+ if config["database"] || env_name == "default"
+ DatabaseConfig.new(env_name, spec_name, config)
+ else
+ config.each_pair.map do |spec_name, sub_config|
+ walk_configs(env_name, spec_name, sub_config)
+ end
+ end
+ end
+
+ # Walks all the configs passed in and returns an array
+ # of DatabaseConfig structs for each configuration.
+ def self.db_configs(configs = ActiveRecord::Base.configurations) # :nodoc:
+ configs.each_pair.flat_map do |env_name, config|
+ walk_configs(env_name, "primary", config)
+ end
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb
index 6ec477c7f3..7721e6b691 100644
--- a/activerecord/lib/active_record/persistence.rb
+++ b/activerecord/lib/active_record/persistence.rb
@@ -658,7 +658,7 @@ module ActiveRecord
end
attribute_names = timestamp_attributes_for_update_in_model
- attribute_names.concat(names)
+ attribute_names |= names.map(&:to_s)
unless attribute_names.empty?
affected_rows = _touch_row(attribute_names, time)
diff --git a/activerecord/lib/active_record/querying.rb b/activerecord/lib/active_record/querying.rb
index 7c8f794910..d33d36ac02 100644
--- a/activerecord/lib/active_record/querying.rb
+++ b/activerecord/lib/active_record/querying.rb
@@ -13,7 +13,7 @@ module ActiveRecord
:where, :rewhere, :preload, :eager_load, :includes, :from, :lock, :readonly, :extending,
:having, :create_with, :distinct, :references, :none, :unscope, :merge, to: :all
delegate :count, :average, :minimum, :maximum, :sum, :calculate, to: :all
- delegate :pluck, :ids, to: :all
+ delegate :pluck, :pick, :ids, to: :all
# Executes a custom SQL query against your database and returns all the results. The results will
# be returned as an array with columns requested encapsulated as attributes of the model you call
diff --git a/activerecord/lib/active_record/railtie.rb b/activerecord/lib/active_record/railtie.rb
index ade5946dd5..6ab80a654d 100644
--- a/activerecord/lib/active_record/railtie.rb
+++ b/activerecord/lib/active_record/railtie.rb
@@ -157,6 +157,13 @@ end_warning
end
end
+ initializer "active_record.collection_cache_association_loading" do
+ require "active_record/railties/collection_cache_association_loading"
+ ActiveSupport.on_load(:action_view) do
+ ActionView::PartialRenderer.prepend(ActiveRecord::Railties::CollectionCacheAssociationLoading)
+ end
+ end
+
initializer "active_record.set_reloader_hooks" do
ActiveSupport.on_load(:active_record) do
ActiveSupport::Reloader.before_class_unload do
diff --git a/activerecord/lib/active_record/railties/collection_cache_association_loading.rb b/activerecord/lib/active_record/railties/collection_cache_association_loading.rb
new file mode 100644
index 0000000000..b5129e4239
--- /dev/null
+++ b/activerecord/lib/active_record/railties/collection_cache_association_loading.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+
+module ActiveRecord
+ module Railties # :nodoc:
+ module CollectionCacheAssociationLoading #:nodoc:
+ def setup(context, options, block)
+ @relation = relation_from_options(options)
+
+ super
+ end
+
+ def relation_from_options(cached: nil, partial: nil, collection: nil, **_)
+ return unless cached
+
+ relation = partial if partial.is_a?(ActiveRecord::Relation)
+ relation ||= collection if collection.is_a?(ActiveRecord::Relation)
+
+ if relation && !relation.loaded?
+ relation.skip_preloading!
+ end
+ end
+
+ def collection_without_template
+ @relation.preload_associations(@collection) if @relation
+ super
+ end
+
+ def collection_with_template
+ @relation.preload_associations(@collection) if @relation
+ super
+ end
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake
index 662a8bc720..76dbcafffb 100644
--- a/activerecord/lib/active_record/railties/databases.rake
+++ b/activerecord/lib/active_record/railties/databases.rake
@@ -22,6 +22,14 @@ db_namespace = namespace :db do
task all: :load_config do
ActiveRecord::Tasks::DatabaseTasks.create_all
end
+
+ ActiveRecord::Tasks::DatabaseTasks.for_each do |spec_name|
+ desc "Create #{spec_name} database for current environment"
+ task spec_name => :load_config do
+ db_config = ActiveRecord::DatabaseConfigurations.config_for_env_and_spec(Rails.env, spec_name)
+ ActiveRecord::Tasks::DatabaseTasks.create(db_config.config)
+ end
+ end
end
desc "Creates the database from DATABASE_URL or config/database.yml for the current RAILS_ENV (use db:create:all to create all databases in the config). Without RAILS_ENV or when RAILS_ENV is development, it defaults to creating the development and test databases."
@@ -33,6 +41,14 @@ db_namespace = namespace :db do
task all: [:load_config, :check_protected_environments] do
ActiveRecord::Tasks::DatabaseTasks.drop_all
end
+
+ ActiveRecord::Tasks::DatabaseTasks.for_each do |spec_name|
+ desc "Drop #{spec_name} database for current environment"
+ task spec_name => [:load_config, :check_protected_environments] do
+ db_config = ActiveRecord::DatabaseConfigurations.config_for_env_and_spec(Rails.env, spec_name)
+ ActiveRecord::Tasks::DatabaseTasks.drop(db_config.config)
+ end
+ end
end
desc "Drops the database from DATABASE_URL or config/database.yml for the current RAILS_ENV (use db:drop:all to drop all databases in the config). Without RAILS_ENV or when RAILS_ENV is development, it defaults to dropping the development and test databases."
@@ -57,7 +73,10 @@ db_namespace = namespace :db do
desc "Migrate the database (options: VERSION=x, VERBOSE=false, SCOPE=blog)."
task migrate: :load_config do
- ActiveRecord::Tasks::DatabaseTasks.migrate
+ ActiveRecord::DatabaseConfigurations.configs_for(Rails.env) do |spec_name, config|
+ ActiveRecord::Base.establish_connection(config)
+ ActiveRecord::Tasks::DatabaseTasks.migrate
+ end
db_namespace["_dump"].invoke
end
@@ -77,6 +96,15 @@ db_namespace = namespace :db do
end
namespace :migrate do
+ ActiveRecord::Tasks::DatabaseTasks.for_each do |spec_name|
+ desc "Migrate #{spec_name} database for current environment"
+ task spec_name => :load_config do
+ db_config = ActiveRecord::DatabaseConfigurations.config_for_env_and_spec(Rails.env, spec_name)
+ ActiveRecord::Base.establish_connection(db_config.config)
+ ActiveRecord::Tasks::DatabaseTasks.migrate
+ end
+ end
+
# desc 'Rollbacks the database one migration and re migrate up (options: STEP=x, VERSION=x).'
task redo: :load_config do
raise "Empty VERSION provided" if ENV["VERSION"] && ENV["VERSION"].empty?
@@ -246,10 +274,15 @@ db_namespace = namespace :db do
desc "Creates a db/schema.rb file that is portable against any DB supported by Active Record"
task dump: :load_config do
require "active_record/schema_dumper"
- filename = ENV["SCHEMA"] || File.join(ActiveRecord::Tasks::DatabaseTasks.db_dir, "schema.rb")
- File.open(filename, "w:utf-8") do |file|
- ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, file)
+
+ ActiveRecord::DatabaseConfigurations.configs_for(Rails.env) do |spec_name, config|
+ filename = ActiveRecord::Tasks::DatabaseTasks.dump_filename(spec_name, :ruby)
+ File.open(filename, "w:utf-8") do |file|
+ ActiveRecord::Base.establish_connection(config)
+ ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, file)
+ end
end
+
db_namespace["schema:dump"].reenable
end
@@ -276,22 +309,25 @@ db_namespace = namespace :db do
rm_f filename, verbose: false
end
end
-
end
namespace :structure do
desc "Dumps the database structure to db/structure.sql. Specify another file with SCHEMA=db/my_structure.sql"
task dump: :load_config do
- filename = ENV["SCHEMA"] || File.join(ActiveRecord::Tasks::DatabaseTasks.db_dir, "structure.sql")
- current_config = ActiveRecord::Tasks::DatabaseTasks.current_config
- ActiveRecord::Tasks::DatabaseTasks.structure_dump(current_config, filename)
-
- if ActiveRecord::SchemaMigration.table_exists?
- File.open(filename, "a") do |f|
- f.puts ActiveRecord::Base.connection.dump_schema_information
- f.print "\n"
+ ActiveRecord::DatabaseConfigurations.configs_for(Rails.env) do |spec_name, config|
+ ActiveRecord::Base.establish_connection(config)
+ filename = ActiveRecord::Tasks::DatabaseTasks.dump_filename(spec_name, :sql)
+ current_config = ActiveRecord::Tasks::DatabaseTasks.current_config
+ ActiveRecord::Tasks::DatabaseTasks.structure_dump(config, filename)
+
+ if ActiveRecord::SchemaMigration.table_exists?
+ File.open(filename, "a") do |f|
+ f.puts ActiveRecord::Base.connection.dump_schema_information
+ f.print "\n"
+ end
end
end
+
db_namespace["structure:dump"].reenable
end
diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb
index 736173ae1b..34e643b2de 100644
--- a/activerecord/lib/active_record/relation.rb
+++ b/activerecord/lib/active_record/relation.rb
@@ -8,7 +8,8 @@ module ActiveRecord
:extending, :unscope]
SINGLE_VALUE_METHODS = [:limit, :offset, :lock, :readonly, :reordering,
- :reverse_order, :distinct, :create_with, :skip_query_cache]
+ :reverse_order, :distinct, :create_with, :skip_query_cache,
+ :skip_preloading]
CLAUSE_METHODS = [:where, :having, :from]
INVALID_METHODS_FOR_DELETE_ALL = [:distinct, :group, :having]
@@ -546,6 +547,16 @@ module ActiveRecord
ActiveRecord::Associations::AliasTracker.create(connection, table.name, joins)
end
+ def preload_associations(records)
+ preload = preload_values
+ preload += includes_values unless eager_loading?
+ preloader = nil
+ preload.each do |associations|
+ preloader ||= build_preloader
+ preloader.preload records, associations
+ end
+ end
+
protected
def load_records(records)
@@ -575,13 +586,7 @@ module ActiveRecord
klass.find_by_sql(arel, &block).freeze
end
- preload = preload_values
- preload += includes_values unless eager_loading?
- preloader = nil
- preload.each do |associations|
- preloader ||= build_preloader
- preloader.preload @records, associations
- end
+ preload_associations(@records) unless skip_preloading_value
@records.each(&:readonly!) if readonly_value
diff --git a/activerecord/lib/active_record/relation/batches.rb b/activerecord/lib/active_record/relation/batches.rb
index 561869017a..ec4bb06c57 100644
--- a/activerecord/lib/active_record/relation/batches.rb
+++ b/activerecord/lib/active_record/relation/batches.rb
@@ -251,25 +251,31 @@ module ActiveRecord
end
end
- attr = Relation::QueryAttribute.new(primary_key, primary_key_offset, klass.type_for_attribute(primary_key))
- batch_relation = relation.where(arel_attribute(primary_key).gt(Arel::Nodes::BindParam.new(attr)))
+ bind = primary_key_bind(primary_key_offset)
+ batch_relation = relation.where(arel_attribute(primary_key).gt(bind))
end
end
private
def apply_limits(relation, start, finish)
- if start
- attr = Relation::QueryAttribute.new(primary_key, start, klass.type_for_attribute(primary_key))
- relation = relation.where(arel_attribute(primary_key).gteq(Arel::Nodes::BindParam.new(attr)))
- end
- if finish
- attr = Relation::QueryAttribute.new(primary_key, finish, klass.type_for_attribute(primary_key))
- relation = relation.where(arel_attribute(primary_key).lteq(Arel::Nodes::BindParam.new(attr)))
- end
+ relation = apply_start_limit(relation, start) if start
+ relation = apply_finish_limit(relation, finish) if finish
relation
end
+ def apply_start_limit(relation, start)
+ relation.where(arel_attribute(primary_key).gteq(primary_key_bind(start)))
+ end
+
+ def apply_finish_limit(relation, finish)
+ relation.where(arel_attribute(primary_key).lteq(primary_key_bind(finish)))
+ end
+
+ def primary_key_bind(value)
+ predicate_builder.build_bind_attribute(primary_key, value)
+ end
+
def batch_order
arel_attribute(primary_key).asc
end
diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb
index 3afa368575..4e60863e52 100644
--- a/activerecord/lib/active_record/relation/query_methods.rb
+++ b/activerecord/lib/active_record/relation/query_methods.rb
@@ -899,6 +899,11 @@ module ActiveRecord
self
end
+ def skip_preloading! # :nodoc:
+ self.skip_preloading_value = true
+ self
+ end
+
# Returns the Arel object associated with the relation.
def arel(aliases = nil) # :nodoc:
@arel ||= build_arel(aliases)
diff --git a/activerecord/lib/active_record/store.rb b/activerecord/lib/active_record/store.rb
index 6dbc977f9a..8d628359c3 100644
--- a/activerecord/lib/active_record/store.rb
+++ b/activerecord/lib/active_record/store.rb
@@ -17,8 +17,8 @@ module ActiveRecord
# You can set custom coder to encode/decode your serialized attributes to/from different formats.
# JSON, YAML, Marshal are supported out of the box. Generally it can be any wrapper that provides +load+ and +dump+.
#
- # NOTE: If you are using PostgreSQL specific columns like +hstore+ or +json+ there is no need for
- # the serialization provided by {.store}[rdoc-ref:rdoc-ref:ClassMethods#store].
+ # NOTE: If you are using structured database data types (eg. PostgreSQL +hstore+/+json+, or MySQL 5.7+
+ # +json+) there is no need for the serialization provided by {.store}[rdoc-ref:rdoc-ref:ClassMethods#store].
# Simply use {.store_accessor}[rdoc-ref:ClassMethods#store_accessor] instead to generate
# the accessor methods. Be aware that these columns use a string keyed hash and do not allow access
# using a symbol.
@@ -31,10 +31,14 @@ module ActiveRecord
#
# class User < ActiveRecord::Base
# store :settings, accessors: [ :color, :homepage ], coder: JSON
+ # store :parent, accessors: [ :name ], coder: JSON, prefix: true
+ # store :spouse, accessors: [ :name ], coder: JSON, prefix: :partner
# end
#
- # u = User.new(color: 'black', homepage: '37signals.com')
+ # u = User.new(color: 'black', homepage: '37signals.com', parent_name: 'Mary', partner_name: 'Lily')
# u.color # Accessor stored attribute
+ # u.parent_name # Accessor stored attribute with prefix
+ # u.partner_name # Accessor stored attribute with custom prefix
# u.settings[:country] = 'Denmark' # Any attribute, even if not specified with an accessor
#
# # There is no difference between strings and symbols for accessing custom attributes
@@ -44,6 +48,7 @@ module ActiveRecord
# # Add additional accessors to an existing store through store_accessor
# class SuperUser < User
# store_accessor :settings, :privileges, :servants
+ # store_accessor :parent, :birthday, prefix: true
# end
#
# The stored attribute names can be retrieved using {.stored_attributes}[rdoc-ref:rdoc-ref:ClassMethods#stored_attributes].
@@ -81,19 +86,29 @@ module ActiveRecord
module ClassMethods
def store(store_attribute, options = {})
serialize store_attribute, IndifferentCoder.new(store_attribute, options[:coder])
- store_accessor(store_attribute, options[:accessors]) if options.has_key? :accessors
+ store_accessor(store_attribute, options[:accessors], prefix: options[:prefix]) if options.has_key? :accessors
end
- def store_accessor(store_attribute, *keys)
+ def store_accessor(store_attribute, *keys, prefix: nil)
keys = keys.flatten
+ accessor_prefix =
+ case prefix
+ when String, Symbol
+ "#{prefix}_"
+ when TrueClass
+ "#{store_attribute}_"
+ else
+ ""
+ end
+
_store_accessors_module.module_eval do
keys.each do |key|
- define_method("#{key}=") do |value|
+ define_method("#{accessor_prefix}#{key}=") do |value|
write_store_attribute(store_attribute, key, value)
end
- define_method(key) do
+ define_method("#{accessor_prefix}#{key}") do
read_store_attribute(store_attribute, key)
end
end
diff --git a/activerecord/lib/active_record/tasks/database_tasks.rb b/activerecord/lib/active_record/tasks/database_tasks.rb
index af1bbc7e93..5787660148 100644
--- a/activerecord/lib/active_record/tasks/database_tasks.rb
+++ b/activerecord/lib/active_record/tasks/database_tasks.rb
@@ -134,6 +134,13 @@ module ActiveRecord
end
end
+ def for_each
+ databases = Rails.application.config.load_database_yaml
+ ActiveRecord::DatabaseConfigurations.configs_for(Rails.env, databases) do |spec_name, _|
+ yield spec_name
+ end
+ end
+
def create_current(environment = env)
each_current_configuration(environment) { |configuration|
create configuration
@@ -252,17 +259,31 @@ module ActiveRecord
end
def schema_file(format = ActiveRecord::Base.schema_format)
+ File.join(db_dir, schema_file_type(format))
+ end
+
+ def schema_file_type(format = ActiveRecord::Base.schema_format)
case format
when :ruby
- File.join(db_dir, "schema.rb")
+ "schema.rb"
when :sql
- File.join(db_dir, "structure.sql")
+ "structure.sql"
end
end
+ def dump_filename(namespace, format = ActiveRecord::Base.schema_format)
+ filename = if namespace == "primary"
+ schema_file_type(format)
+ else
+ "#{namespace}_#{schema_file_type(format)}"
+ end
+
+ ENV["SCHEMA"] || File.join(ActiveRecord::Tasks::DatabaseTasks.db_dir, filename)
+ end
+
def load_schema_current(format = ActiveRecord::Base.schema_format, file = nil, environment = env)
- each_current_configuration(environment) { |configuration, configuration_environment|
- load_schema configuration, format, file, configuration_environment
+ each_current_configuration(environment) { |configuration, spec_name, env|
+ load_schema configuration, format, file, env
}
ActiveRecord::Base.establish_connection(environment.to_sym)
end
@@ -312,10 +333,10 @@ module ActiveRecord
environments = [environment]
environments << "test" if environment == "development"
- ActiveRecord::Base.configurations.slice(*environments).each do |configuration_environment, configuration|
- next unless configuration["database"]
-
- yield configuration, configuration_environment
+ environments.each do |env|
+ ActiveRecord::DatabaseConfigurations.configs_for(env) do |spec_name, configuration|
+ yield configuration, spec_name, env
+ end
end
end
diff --git a/activerecord/test/cases/adapters/sqlite3/quoting_test.rb b/activerecord/test/cases/adapters/sqlite3/quoting_test.rb
index 6fdb353368..1c85ff5674 100644
--- a/activerecord/test/cases/adapters/sqlite3/quoting_test.rb
+++ b/activerecord/test/cases/adapters/sqlite3/quoting_test.rb
@@ -55,4 +55,11 @@ class SQLite3QuotingTest < ActiveRecord::SQLite3TestCase
assert_equal "'2000-01-01 12:30:00.999999'", @conn.quote(type.serialize(value))
end
+
+ def test_quoted_time_normalizes_date_qualified_time
+ value = ::Time.utc(2018, 3, 11, 12, 30, 0, 999999)
+ type = ActiveRecord::Type::Time.new
+
+ assert_equal "'2000-01-01 12:30:00.999999'", @conn.quote(type.serialize(value))
+ end
end
diff --git a/activerecord/test/cases/calculations_test.rb b/activerecord/test/cases/calculations_test.rb
index 74228b2796..080d2a54bc 100644
--- a/activerecord/test/cases/calculations_test.rb
+++ b/activerecord/test/cases/calculations_test.rb
@@ -811,6 +811,11 @@ class CalculationsTest < ActiveRecord::TestCase
assert_nil Topic.where("1=0").pick(:author_name, :author_email_address)
end
+ def test_pick_delegate_to_all
+ cool_first = minivans(:cool_first)
+ assert_equal cool_first.color, Minivan.pick(:color)
+ end
+
def test_grouped_calculation_with_polymorphic_relation
part = ShipPart.create!(name: "has trinket")
part.trinkets.create!
diff --git a/activerecord/test/cases/connection_adapters/connection_handler_test.rb b/activerecord/test/cases/connection_adapters/connection_handler_test.rb
index f4cc251fb9..c06a4e2c52 100644
--- a/activerecord/test/cases/connection_adapters/connection_handler_test.rb
+++ b/activerecord/test/cases/connection_adapters/connection_handler_test.rb
@@ -71,6 +71,54 @@ module ActiveRecord
ENV["RAILS_ENV"] = previous_env
end
+ unless in_memory_db?
+ def test_establish_connection_using_3_level_config_defaults_to_default_env_primary_db
+ previous_env, ENV["RAILS_ENV"] = ENV["RAILS_ENV"], "default_env"
+
+ config = {
+ "default_env" => {
+ "primary" => { "adapter" => "sqlite3", "database" => "db/primary.sqlite3" },
+ "readonly" => { "adapter" => "sqlite3", "database" => "db/readonly.sqlite3" }
+ },
+ "another_env" => {
+ "primary" => { "adapter" => "sqlite3", "database" => "db/another-primary.sqlite3" },
+ "readonly" => { "adapter" => "sqlite3", "database" => "db/another-readonly.sqlite3" }
+ }
+ }
+ @prev_configs, ActiveRecord::Base.configurations = ActiveRecord::Base.configurations, config
+
+ ActiveRecord::Base.establish_connection
+
+ assert_equal "db/primary.sqlite3", ActiveRecord::Base.connection.pool.spec.config[:database]
+ ensure
+ ActiveRecord::Base.configurations = @prev_configs
+ ENV["RAILS_ENV"] = previous_env
+ ActiveRecord::Base.establish_connection(:arunit)
+ end
+
+ def test_establish_connection_using_2_level_config_defaults_to_default_env_primary_db
+ previous_env, ENV["RAILS_ENV"] = ENV["RAILS_ENV"], "default_env"
+
+ config = {
+ "default_env" => {
+ "adapter" => "sqlite3", "database" => "db/primary.sqlite3"
+ },
+ "another_env" => {
+ "adapter" => "sqlite3", "database" => "db/bad-primary.sqlite3"
+ }
+ }
+ @prev_configs, ActiveRecord::Base.configurations = ActiveRecord::Base.configurations, config
+
+ ActiveRecord::Base.establish_connection
+
+ assert_equal "db/primary.sqlite3", ActiveRecord::Base.connection.pool.spec.config[:database]
+ ensure
+ ActiveRecord::Base.configurations = @prev_configs
+ ENV["RAILS_ENV"] = previous_env
+ ActiveRecord::Base.establish_connection(:arunit)
+ end
+ end
+
def test_establish_connection_using_two_level_configurations
config = { "development" => { "adapter" => "sqlite3", "database" => "db/primary.sqlite3" } }
@prev_configs, ActiveRecord::Base.configurations = ActiveRecord::Base.configurations, config
diff --git a/activerecord/test/cases/date_time_precision_test.rb b/activerecord/test/cases/date_time_precision_test.rb
index 51f6164138..e64a8372d0 100644
--- a/activerecord/test/cases/date_time_precision_test.rb
+++ b/activerecord/test/cases/date_time_precision_test.rb
@@ -27,6 +27,24 @@ if subsecond_precision_supported?
assert_equal 5, Foo.columns_hash["updated_at"].precision
end
+ def test_datetime_precision_is_truncated_on_assignment
+ @connection.create_table(:foos, force: true)
+ @connection.add_column :foos, :created_at, :datetime, precision: 0
+ @connection.add_column :foos, :updated_at, :datetime, precision: 6
+
+ time = ::Time.now.change(nsec: 123456789)
+ foo = Foo.new(created_at: time, updated_at: time)
+
+ assert_equal 0, foo.created_at.nsec
+ assert_equal 123456000, foo.updated_at.nsec
+
+ foo.save!
+ foo.reload
+
+ assert_equal 0, foo.created_at.nsec
+ assert_equal 123456000, foo.updated_at.nsec
+ end
+
def test_timestamps_helper_with_custom_precision
@connection.create_table(:foos, force: true) do |t|
t.timestamps precision: 4
diff --git a/activerecord/test/cases/quoting_test.rb b/activerecord/test/cases/quoting_test.rb
index 6534770c57..92eb0c814f 100644
--- a/activerecord/test/cases/quoting_test.rb
+++ b/activerecord/test/cases/quoting_test.rb
@@ -46,27 +46,60 @@ module ActiveRecord
assert_equal t.to_s(:db), @quoter.quoted_date(t)
end
- def test_quoted_time_utc
+ def test_quoted_timestamp_utc
with_timezone_config default: :utc do
t = Time.now.change(usec: 0)
assert_equal t.getutc.to_s(:db), @quoter.quoted_date(t)
end
end
- def test_quoted_time_local
+ def test_quoted_timestamp_local
with_timezone_config default: :local do
t = Time.now.change(usec: 0)
assert_equal t.getlocal.to_s(:db), @quoter.quoted_date(t)
end
end
- def test_quoted_time_crazy
+ def test_quoted_timestamp_crazy
with_timezone_config default: :asdfasdf do
t = Time.now.change(usec: 0)
assert_equal t.getlocal.to_s(:db), @quoter.quoted_date(t)
end
end
+ def test_quoted_time_utc
+ with_timezone_config default: :utc do
+ t = Time.now.change(usec: 0)
+
+ expected = t.getutc.change(year: 2000, month: 1, day: 1)
+ expected = expected.to_s(:db).sub("2000-01-01 ", "")
+
+ assert_equal expected, @quoter.quoted_time(t)
+ end
+ end
+
+ def test_quoted_time_local
+ with_timezone_config default: :local do
+ t = Time.now.change(usec: 0)
+
+ expected = t.change(year: 2000, month: 1, day: 1)
+ expected = expected.getlocal.to_s(:db).sub("2000-01-01 ", "")
+
+ assert_equal expected, @quoter.quoted_time(t)
+ end
+ end
+
+ def test_quoted_time_crazy
+ with_timezone_config default: :asdfasdf do
+ t = Time.now.change(usec: 0)
+
+ expected = t.change(year: 2000, month: 1, day: 1)
+ expected = expected.getlocal.to_s(:db).sub("2000-01-01 ", "")
+
+ assert_equal expected, @quoter.quoted_time(t)
+ end
+ end
+
def test_quoted_datetime_utc
with_timezone_config default: :utc do
t = Time.now.change(usec: 0).to_datetime
diff --git a/activerecord/test/cases/relation/delegation_test.rb b/activerecord/test/cases/relation/delegation_test.rb
index 2696d1bb00..3f3d41980c 100644
--- a/activerecord/test/cases/relation/delegation_test.rb
+++ b/activerecord/test/cases/relation/delegation_test.rb
@@ -54,4 +54,32 @@ module ActiveRecord
Comment.all
end
end
+
+ class QueryingMethodsDelegationTest < ActiveRecord::TestCase
+ QUERYING_METHODS = [
+ :find, :take, :take!, :first, :first!, :last, :last!, :exists?, :any?, :many?, :none?, :one?,
+ :second, :second!, :third, :third!, :fourth, :fourth!, :fifth, :fifth!, :forty_two, :forty_two!, :third_to_last, :third_to_last!, :second_to_last, :second_to_last!,
+ :first_or_create, :first_or_create!, :first_or_initialize,
+ :find_or_create_by, :find_or_create_by!, :create_or_find_by, :create_or_find_by!, :find_or_initialize_by,
+ :find_by, :find_by!,
+ :destroy_all, :delete_all, :update_all,
+ :find_each, :find_in_batches, :in_batches,
+ :select, :group, :order, :except, :reorder, :limit, :offset, :joins, :left_joins, :left_outer_joins, :or,
+ :where, :rewhere, :preload, :eager_load, :includes, :from, :lock, :readonly, :extending,
+ :having, :create_with, :distinct, :references, :none, :unscope, :merge,
+ :count, :average, :minimum, :maximum, :sum, :calculate,
+ :pluck, :pick, :ids,
+ ]
+
+ def test_delegate_querying_methods
+ klass = Class.new(ActiveRecord::Base) do
+ self.table_name = "posts"
+ end
+
+ QUERYING_METHODS.each do |method|
+ assert_respond_to klass.all, method
+ assert_respond_to klass, method
+ end
+ end
+ end
end
diff --git a/activerecord/test/cases/relation/mutation_test.rb b/activerecord/test/cases/relation/mutation_test.rb
index 1428b3e132..d6351bfe88 100644
--- a/activerecord/test/cases/relation/mutation_test.rb
+++ b/activerecord/test/cases/relation/mutation_test.rb
@@ -59,7 +59,7 @@ module ActiveRecord
assert_equal [], relation.extending_values
end
- (Relation::SINGLE_VALUE_METHODS - [:lock, :reordering, :reverse_order, :create_with, :skip_query_cache]).each do |method|
+ (Relation::SINGLE_VALUE_METHODS - [:lock, :reordering, :reverse_order, :create_with, :skip_query_cache, :skip_preloading]).each do |method|
test "##{method}!" do
assert relation.public_send("#{method}!", :foo).equal?(relation)
assert_equal :foo, relation.public_send("#{method}_value")
@@ -137,6 +137,11 @@ module ActiveRecord
assert relation.skip_query_cache_value
end
+ test "skip_preloading!" do
+ relation.skip_preloading!
+ assert relation.skip_preloading_value
+ end
+
private
def relation
@relation ||= Relation.new(FakeKlass)
diff --git a/activerecord/test/cases/schema_dumper_test.rb b/activerecord/test/cases/schema_dumper_test.rb
index a612ce9bb2..50d766a99e 100644
--- a/activerecord/test/cases/schema_dumper_test.rb
+++ b/activerecord/test/cases/schema_dumper_test.rb
@@ -298,7 +298,7 @@ class SchemaDumperTest < ActiveRecord::TestCase
def test_schema_dump_expression_indices
index_definition = dump_table_schema("companies").split(/\n/).grep(/t\.index.*company_expression_index/).first.strip
- assert_equal 't.index "lower((name)::text)", name: "company_expression_index"', index_definition
+ assert_match %r{CASE.+lower\(\(name\)::text\)}i, index_definition
end
def test_schema_dump_interval_type
diff --git a/activerecord/test/cases/store_test.rb b/activerecord/test/cases/store_test.rb
index a30d13632a..3bd480cfbd 100644
--- a/activerecord/test/cases/store_test.rb
+++ b/activerecord/test/cases/store_test.rb
@@ -8,7 +8,12 @@ class StoreTest < ActiveRecord::TestCase
fixtures :'admin/users'
setup do
- @john = Admin::User.create!(name: "John Doe", color: "black", remember_login: true, height: "tall", is_a_good_guy: true)
+ @john = Admin::User.create!(
+ name: "John Doe", color: "black", remember_login: true,
+ height: "tall", is_a_good_guy: true,
+ parent_name: "Quinn", partner_name: "Dallas",
+ partner_birthday: "1997-11-1"
+ )
end
test "reading store attributes through accessors" do
@@ -24,6 +29,21 @@ class StoreTest < ActiveRecord::TestCase
assert_equal "37signals.com", @john.homepage
end
+ test "reading store attributes through accessors with prefix" do
+ assert_equal "Quinn", @john.parent_name
+ assert_nil @john.parent_birthday
+ assert_equal "Dallas", @john.partner_name
+ assert_equal "1997-11-1", @john.partner_birthday
+ end
+
+ test "writing store attributes through accessors with prefix" do
+ @john.partner_name = "River"
+ @john.partner_birthday = "1999-2-11"
+
+ assert_equal "River", @john.partner_name
+ assert_equal "1999-2-11", @john.partner_birthday
+ end
+
test "accessing attributes not exposed by accessors" do
@john.settings[:icecream] = "graeters"
@john.save
diff --git a/activerecord/test/cases/tasks/database_tasks_test.rb b/activerecord/test/cases/tasks/database_tasks_test.rb
index 21226352ff..38c2c8b2f3 100644
--- a/activerecord/test/cases/tasks/database_tasks_test.rb
+++ b/activerecord/test/cases/tasks/database_tasks_test.rb
@@ -221,7 +221,76 @@ module ActiveRecord
ENV["RAILS_ENV"] = old_env
end
- def test_establishes_connection_for_the_given_environment
+ def test_establishes_connection_for_the_given_environments
+ ActiveRecord::Tasks::DatabaseTasks.stubs(:create).returns true
+
+ ActiveRecord::Base.expects(:establish_connection).with(:development)
+
+ ActiveRecord::Tasks::DatabaseTasks.create_current(
+ ActiveSupport::StringInquirer.new("development")
+ )
+ end
+ end
+
+ class DatabaseTasksCreateCurrentThreeTierTest < ActiveRecord::TestCase
+ def setup
+ @configurations = {
+ "development" => { "primary" => { "database" => "dev-db" }, "secondary" => { "database" => "secondary-dev-db" } },
+ "test" => { "primary" => { "database" => "test-db" }, "secondary" => { "database" => "secondary-test-db" } },
+ "production" => { "primary" => { "database" => "prod-db" }, "secondary" => { "database" => "secondary-prod-db" } }
+ }
+
+ ActiveRecord::Base.stubs(:configurations).returns(@configurations)
+ ActiveRecord::Base.stubs(:establish_connection).returns(true)
+ end
+
+ def test_creates_current_environment_database
+ ActiveRecord::Tasks::DatabaseTasks.expects(:create).
+ with("database" => "prod-db")
+
+ ActiveRecord::Tasks::DatabaseTasks.expects(:create).
+ with("database" => "secondary-prod-db")
+
+ ActiveRecord::Tasks::DatabaseTasks.create_current(
+ ActiveSupport::StringInquirer.new("production")
+ )
+ end
+
+ def test_creates_test_and_development_databases_when_env_was_not_specified
+ ActiveRecord::Tasks::DatabaseTasks.expects(:create).
+ with("database" => "dev-db")
+ ActiveRecord::Tasks::DatabaseTasks.expects(:create).
+ with("database" => "secondary-dev-db")
+ ActiveRecord::Tasks::DatabaseTasks.expects(:create).
+ with("database" => "test-db")
+ ActiveRecord::Tasks::DatabaseTasks.expects(:create).
+ with("database" => "secondary-test-db")
+
+ ActiveRecord::Tasks::DatabaseTasks.create_current(
+ ActiveSupport::StringInquirer.new("development")
+ )
+ end
+
+ def test_creates_test_and_development_databases_when_rails_env_is_development
+ old_env = ENV["RAILS_ENV"]
+ ENV["RAILS_ENV"] = "development"
+ ActiveRecord::Tasks::DatabaseTasks.expects(:create).
+ with("database" => "dev-db")
+ ActiveRecord::Tasks::DatabaseTasks.expects(:create).
+ with("database" => "secondary-dev-db")
+ ActiveRecord::Tasks::DatabaseTasks.expects(:create).
+ with("database" => "test-db")
+ ActiveRecord::Tasks::DatabaseTasks.expects(:create).
+ with("database" => "secondary-test-db")
+
+ ActiveRecord::Tasks::DatabaseTasks.create_current(
+ ActiveSupport::StringInquirer.new("development")
+ )
+ ensure
+ ENV["RAILS_ENV"] = old_env
+ end
+
+ def test_establishes_connection_for_the_given_environments_config
ActiveRecord::Tasks::DatabaseTasks.stubs(:create).returns true
ActiveRecord::Base.expects(:establish_connection).with(:development)
@@ -347,6 +416,64 @@ module ActiveRecord
end
end
+ class DatabaseTasksDropCurrentThreeTierTest < ActiveRecord::TestCase
+ def setup
+ @configurations = {
+ "development" => { "primary" => { "database" => "dev-db" }, "secondary" => { "database" => "secondary-dev-db" } },
+ "test" => { "primary" => { "database" => "test-db" }, "secondary" => { "database" => "secondary-test-db" } },
+ "production" => { "primary" => { "database" => "prod-db" }, "secondary" => { "database" => "secondary-prod-db" } }
+ }
+
+ ActiveRecord::Base.stubs(:configurations).returns(@configurations)
+ end
+
+ def test_drops_current_environment_database
+ ActiveRecord::Tasks::DatabaseTasks.expects(:drop).
+ with("database" => "prod-db")
+
+ ActiveRecord::Tasks::DatabaseTasks.expects(:drop).
+ with("database" => "secondary-prod-db")
+
+ ActiveRecord::Tasks::DatabaseTasks.drop_current(
+ ActiveSupport::StringInquirer.new("production")
+ )
+ end
+
+ def test_drops_test_and_development_databases_when_env_was_not_specified
+ ActiveRecord::Tasks::DatabaseTasks.expects(:drop).
+ with("database" => "dev-db")
+ ActiveRecord::Tasks::DatabaseTasks.expects(:drop).
+ with("database" => "secondary-dev-db")
+ ActiveRecord::Tasks::DatabaseTasks.expects(:drop).
+ with("database" => "test-db")
+ ActiveRecord::Tasks::DatabaseTasks.expects(:drop).
+ with("database" => "secondary-test-db")
+
+ ActiveRecord::Tasks::DatabaseTasks.drop_current(
+ ActiveSupport::StringInquirer.new("development")
+ )
+ end
+
+ def test_drops_testand_development_databases_when_rails_env_is_development
+ old_env = ENV["RAILS_ENV"]
+ ENV["RAILS_ENV"] = "development"
+ ActiveRecord::Tasks::DatabaseTasks.expects(:drop).
+ with("database" => "dev-db")
+ ActiveRecord::Tasks::DatabaseTasks.expects(:drop).
+ with("database" => "secondary-dev-db")
+ ActiveRecord::Tasks::DatabaseTasks.expects(:drop).
+ with("database" => "test-db")
+ ActiveRecord::Tasks::DatabaseTasks.expects(:drop).
+ with("database" => "secondary-test-db")
+
+ ActiveRecord::Tasks::DatabaseTasks.drop_current(
+ ActiveSupport::StringInquirer.new("development")
+ )
+ ensure
+ ENV["RAILS_ENV"] = old_env
+ end
+ end
+
if current_adapter?(:SQLite3Adapter) && !in_memory_db?
class DatabaseTasksMigrateTest < ActiveRecord::TestCase
self.use_transactional_tests = false
diff --git a/activerecord/test/cases/time_precision_test.rb b/activerecord/test/cases/time_precision_test.rb
index 41455637bb..086500de38 100644
--- a/activerecord/test/cases/time_precision_test.rb
+++ b/activerecord/test/cases/time_precision_test.rb
@@ -27,6 +27,24 @@ if subsecond_precision_supported?
assert_equal 6, Foo.columns_hash["finish"].precision
end
+ def test_time_precision_is_truncated_on_assignment
+ @connection.create_table(:foos, force: true)
+ @connection.add_column :foos, :start, :time, precision: 0
+ @connection.add_column :foos, :finish, :time, precision: 6
+
+ time = ::Time.now.change(nsec: 123456789)
+ foo = Foo.new(start: time, finish: time)
+
+ assert_equal 0, foo.start.nsec
+ assert_equal 123456000, foo.finish.nsec
+
+ foo.save!
+ foo.reload
+
+ assert_equal 0, foo.start.nsec
+ assert_equal 123456000, foo.finish.nsec
+ end
+
def test_passing_precision_to_time_does_not_set_limit
@connection.create_table(:foos, force: true) do |t|
t.time :start, precision: 3
diff --git a/activerecord/test/cases/timestamp_test.rb b/activerecord/test/cases/timestamp_test.rb
index e95446c0a7..d9f7a81ce4 100644
--- a/activerecord/test/cases/timestamp_test.rb
+++ b/activerecord/test/cases/timestamp_test.rb
@@ -96,6 +96,16 @@ class TimestampTest < ActiveRecord::TestCase
assert_not_equal @previously_updated_at, @developer.updated_at
end
+ def test_touching_update_at_attribute_as_symbol_updates_timestamp
+ travel(1.second) do
+ @developer.touch(:updated_at)
+ end
+
+ assert_not @developer.updated_at_changed?
+ assert_not @developer.changed?
+ assert_not_equal @previously_updated_at, @developer.updated_at
+ end
+
def test_touching_an_attribute_updates_it
task = Task.first
previous_value = task.ending
diff --git a/activerecord/test/models/admin/user.rb b/activerecord/test/models/admin/user.rb
index abb5cb28e7..3f55364510 100644
--- a/activerecord/test/models/admin/user.rb
+++ b/activerecord/test/models/admin/user.rb
@@ -19,6 +19,9 @@ class Admin::User < ActiveRecord::Base
store :params, accessors: [ :token ], coder: YAML
store :settings, accessors: [ :color, :homepage ]
store_accessor :settings, :favorite_food
+ store :parent, accessors: [:birthday, :name], prefix: true
+ store :spouse, accessors: [:birthday], prefix: :partner
+ store_accessor :spouse, :name, prefix: :partner
store :preferences, accessors: [ :remember_login ]
store :json_data, accessors: [ :height, :weight ], coder: Coder.new
store :json_data_empty, accessors: [ :is_a_good_guy ], coder: Coder.new
diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb
index 8b0106dbf0..b9c7d447af 100644
--- a/activerecord/test/schema/schema.rb
+++ b/activerecord/test/schema/schema.rb
@@ -21,6 +21,8 @@ ActiveRecord::Schema.define do
create_table :admin_users, force: true do |t|
t.string :name
t.string :settings, null: true, limit: 1024
+ t.string :parent, null: true, limit: 1024
+ t.string :spouse, null: true, limit: 1024
# MySQL does not allow default values for blobs. Fake it out with a
# big varchar below.
t.string :preferences, null: true, default: "", limit: 1024
@@ -210,7 +212,7 @@ ActiveRecord::Schema.define do
t.index [:firm_id, :type, :rating], name: "company_index", length: { type: 10 }, order: { rating: :desc }
t.index [:firm_id, :type], name: "company_partial_index", where: "(rating > 10)"
t.index :name, name: "company_name_index", using: :btree
- t.index "lower(name)", name: "company_expression_index" if supports_expression_index?
+ t.index "(CASE WHEN rating > 0 THEN lower(name) END)", name: "company_expression_index" if supports_expression_index?
end
create_table :content, force: true do |t|
diff --git a/activestorage/CHANGELOG.md b/activestorage/CHANGELOG.md
index 2f8b65766d..d794afb0e6 100644
--- a/activestorage/CHANGELOG.md
+++ b/activestorage/CHANGELOG.md
@@ -1,5 +1,3 @@
-## Rails 6.0.0.alpha (Unreleased) ##
-
* Rails 6 requires Ruby 2.4.1 or newer.
*Jeremy Daer*
diff --git a/activestorage/lib/active_storage/service/azure_storage_service.rb b/activestorage/lib/active_storage/service/azure_storage_service.rb
index 5682087469..2867a4e441 100644
--- a/activestorage/lib/active_storage/service/azure_storage_service.rb
+++ b/activestorage/lib/active_storage/service/azure_storage_service.rb
@@ -8,14 +8,13 @@ module ActiveStorage
# Wraps the Microsoft Azure Storage Blob Service as an Active Storage service.
# See ActiveStorage::Service for the generic API documentation that applies to all services.
class Service::AzureStorageService < Service
- attr_reader :client, :path, :blobs, :container, :signer
+ attr_reader :client, :blobs, :container, :signer
- def initialize(path:, storage_account_name:, storage_access_key:, container:)
+ def initialize(storage_account_name:, storage_access_key:, container:)
@client = Azure::Storage::Client.create(storage_account_name: storage_account_name, storage_access_key: storage_access_key)
@signer = Azure::Storage::Core::Auth::SharedAccessSignature.new(storage_account_name, storage_access_key)
@blobs = client.blob_client
@container = container
- @path = path
end
def upload(key, io, checksum: nil)
@@ -84,9 +83,9 @@ module ActiveStorage
def url(key, expires_in:, filename:, disposition:, content_type:)
instrument :url, key: key do |payload|
- base_url = url_for(key)
generated_url = signer.signed_uri(
- URI(base_url), false,
+ uri_for(key), false,
+ service: "b",
permissions: "r",
expiry: format_expiry(expires_in),
content_disposition: content_disposition_with(type: disposition, filename: filename),
@@ -101,9 +100,12 @@ module ActiveStorage
def url_for_direct_upload(key, expires_in:, content_type:, content_length:, checksum:)
instrument :url, key: key do |payload|
- base_url = url_for(key)
- generated_url = signer.signed_uri(URI(base_url), false, permissions: "rw",
- expiry: format_expiry(expires_in)).to_s
+ generated_url = signer.signed_uri(
+ uri_for(key), false,
+ service: "b",
+ permissions: "rw",
+ expiry: format_expiry(expires_in)
+ ).to_s
payload[:url] = generated_url
@@ -116,8 +118,8 @@ module ActiveStorage
end
private
- def url_for(key)
- "#{path}/#{container}/#{key}"
+ def uri_for(key)
+ blobs.generate_uri("#{container}/#{key}")
end
def blob_for(key)
diff --git a/activestorage/lib/active_storage/service/s3_service.rb b/activestorage/lib/active_storage/service/s3_service.rb
index 8ab7a44131..5e489f4be1 100644
--- a/activestorage/lib/active_storage/service/s3_service.rb
+++ b/activestorage/lib/active_storage/service/s3_service.rb
@@ -9,8 +9,8 @@ module ActiveStorage
class Service::S3Service < Service
attr_reader :client, :bucket, :upload_options
- def initialize(access_key_id:, secret_access_key:, region:, bucket:, upload: {}, **options)
- @client = Aws::S3::Resource.new(access_key_id: access_key_id, secret_access_key: secret_access_key, region: region, **options)
+ def initialize(bucket:, upload: {}, **options)
+ @client = Aws::S3::Resource.new(**options)
@bucket = @client.bucket(bucket)
@upload_options = upload
diff --git a/activestorage/test/service/configurations.example.yml b/activestorage/test/service/configurations.example.yml
index 43cc013bc8..a63aa33302 100644
--- a/activestorage/test/service/configurations.example.yml
+++ b/activestorage/test/service/configurations.example.yml
@@ -24,7 +24,6 @@
#
# azure:
# service: AzureStorage
-# path: ""
# storage_account_name: ""
# storage_access_key: ""
# container: ""
diff --git a/activestorage/test/service/configurations.yml.enc b/activestorage/test/service/configurations.yml.enc
index df11aac161..648924a562 100644
--- a/activestorage/test/service/configurations.yml.enc
+++ b/activestorage/test/service/configurations.yml.enc
Binary files differ
diff --git a/activestorage/test/service/s3_service_test.rb b/activestorage/test/service/s3_service_test.rb
index d6996209d2..7833e51122 100644
--- a/activestorage/test/service/s3_service_test.rb
+++ b/activestorage/test/service/s3_service_test.rb
@@ -3,7 +3,7 @@
require "service/shared_service_tests"
require "net/http"
-if SERVICE_CONFIGURATIONS[:s3] && SERVICE_CONFIGURATIONS[:s3][:access_key_id].present?
+if SERVICE_CONFIGURATIONS[:s3]
class ActiveStorage::Service::S3ServiceTest < ActiveSupport::TestCase
SERVICE = ActiveStorage::Service.configure(:s3, SERVICE_CONFIGURATIONS)
diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md
index 582209c2ae..e067c4dfba 100644
--- a/activesupport/CHANGELOG.md
+++ b/activesupport/CHANGELOG.md
@@ -1,4 +1,10 @@
-## Rails 6.0.0.alpha (Unreleased) ##
+* Fix bug where `URI.unscape` would fail with mixed Unicode/escaped character input:
+
+ URI.unescape("\xe3\x83\x90") # => "バ"
+ URI.unescape("%E3%83%90") # => "バ"
+ URI.unescape("\xe3\x83\x90%E3%83%90") # => Encoding::CompatibilityError
+
+ *Ashe Connor*, *Aaron Patterson*
* Add `before?` and `after?` methods to `Date`, `DateTime`,
`Time`, and `TimeWithZone`.
@@ -48,7 +54,7 @@
*Jeremy Daer*
-* Adds parallel testing to Rails
+* Adds parallel testing to Rails.
Parallelize your test suite with forked processes or threads.
diff --git a/activesupport/lib/active_support/cache.rb b/activesupport/lib/active_support/cache.rb
index 1ea2d0bbf2..6967c164ab 100644
--- a/activesupport/lib/active_support/cache.rb
+++ b/activesupport/lib/active_support/cache.rb
@@ -714,11 +714,9 @@ module ActiveSupport
# Creates a new cache entry for the specified value. Options supported are
# +:compress+, +:compress_threshold+, and +:expires_in+.
def initialize(value, options = {})
- if should_compress?(value, options)
- @value = compress(value)
- @compressed = true
- else
- @value = value
+ @value = value
+ if should_compress?(options)
+ compress!
end
@version = options[:version]
@@ -783,28 +781,31 @@ module ActiveSupport
end
private
- def should_compress?(value, options)
- if value && options.fetch(:compress, true)
+ def should_compress?(options)
+ if @value && options.fetch(:compress, true)
compress_threshold = options.fetch(:compress_threshold, DEFAULT_COMPRESS_LIMIT)
- serialized_value_size = (value.is_a?(String) ? value : Marshal.dump(value)).bytesize
+ serialized_value_size = (@value.is_a?(String) ? @value : marshaled_value).bytesize
- return true if serialized_value_size >= compress_threshold
+ serialized_value_size >= compress_threshold
end
-
- false
end
def compressed?
defined?(@compressed) ? @compressed : false
end
- def compress(value)
- Zlib::Deflate.deflate(Marshal.dump(value))
+ def compress!
+ @value = Zlib::Deflate.deflate(marshaled_value)
+ @compressed = true
end
def uncompress(value)
Marshal.load(Zlib::Inflate.inflate(value))
end
+
+ def marshaled_value
+ @marshaled_value ||= Marshal.dump(@value)
+ end
end
end
end
diff --git a/activesupport/lib/active_support/cache/redis_cache_store.rb b/activesupport/lib/active_support/cache/redis_cache_store.rb
index 64bc77cf22..a1cb6db25d 100644
--- a/activesupport/lib/active_support/cache/redis_cache_store.rb
+++ b/activesupport/lib/active_support/cache/redis_cache_store.rb
@@ -118,7 +118,7 @@ module ActiveSupport
def build_redis(redis: nil, url: nil, **redis_options) #:nodoc:
urls = Array(url)
- if redis.respond_to?(:call)
+ if redis.is_a?(Proc)
redis.call
elsif redis
redis
@@ -154,7 +154,7 @@ module ActiveSupport
# :url Array -> Redis::Distributed.new([{ url: … }, { url: … }, …])
#
# No namespace is set by default. Provide one if the Redis cache
- # server is shared with other apps: <tt>namespace: 'myapp-cache'<tt>.
+ # server is shared with other apps: <tt>namespace: 'myapp-cache'</tt>.
#
# Compression is enabled by default with a 1kB threshold, so cached
# values larger than 1kB are automatically compressed. Disable by
diff --git a/activesupport/lib/active_support/cache/strategy/local_cache.rb b/activesupport/lib/active_support/cache/strategy/local_cache.rb
index e17308f83e..39b32fc7f6 100644
--- a/activesupport/lib/active_support/cache/strategy/local_cache.rb
+++ b/activesupport/lib/active_support/cache/strategy/local_cache.rb
@@ -55,7 +55,14 @@ module ActiveSupport
end
def read_multi_entries(keys, options)
- Hash[keys.map { |name| [name, read_entry(name, options)] }.keep_if { |_name, value| value }]
+ values = {}
+
+ keys.each do |name|
+ entry = read_entry(name, options)
+ values[name] = entry.value if entry
+ end
+
+ values
end
def write_entry(key, value, options)
diff --git a/activesupport/lib/active_support/core_ext/uri.rb b/activesupport/lib/active_support/core_ext/uri.rb
index c93c0b5c2d..cdd81ae562 100644
--- a/activesupport/lib/active_support/core_ext/uri.rb
+++ b/activesupport/lib/active_support/core_ext/uri.rb
@@ -1,10 +1,8 @@
# frozen_string_literal: true
require "uri"
-str = "\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E" # Ni-ho-nn-go in UTF-8, means Japanese.
-parser = URI::Parser.new
-unless str == parser.unescape(parser.escape(str))
+if RUBY_VERSION < "2.6.0"
require "active_support/core_ext/module/redefine_method"
URI::Parser.class_eval do
silence_redefinition_of_method :unescape
@@ -13,7 +11,7 @@ unless str == parser.unescape(parser.escape(str))
# YK: My initial experiments say yes, but let's be sure please
enc = str.encoding
enc = Encoding::UTF_8 if enc == Encoding::US_ASCII
- str.gsub(escaped) { |match| [match[1, 2].hex].pack("C") }.force_encoding(enc)
+ str.dup.force_encoding(Encoding::ASCII_8BIT).gsub(escaped) { |match| [match[1, 2].hex].pack("C") }.force_encoding(enc)
end
end
end
diff --git a/activesupport/lib/active_support/encrypted_configuration.rb b/activesupport/lib/active_support/encrypted_configuration.rb
index dab953d5d5..3c6da10548 100644
--- a/activesupport/lib/active_support/encrypted_configuration.rb
+++ b/activesupport/lib/active_support/encrypted_configuration.rb
@@ -38,10 +38,6 @@ module ActiveSupport
@options ||= ActiveSupport::InheritableOptions.new(config)
end
- def serialize(config)
- config.present? ? YAML.dump(config) : ""
- end
-
def deserialize(config)
config.present? ? YAML.load(config, content_path) : {}
end
diff --git a/activesupport/test/cache/behaviors/local_cache_behavior.rb b/activesupport/test/cache/behaviors/local_cache_behavior.rb
index 363f2d1084..baa38ba6ac 100644
--- a/activesupport/test/cache/behaviors/local_cache_behavior.rb
+++ b/activesupport/test/cache/behaviors/local_cache_behavior.rb
@@ -129,6 +129,18 @@ module LocalCacheBehavior
end
end
+ def test_local_cache_of_read_multi
+ @cache.with_local_cache do
+ @cache.write("foo", "foo", raw: true)
+ @cache.write("bar", "bar", raw: true)
+ values = @cache.read_multi("foo", "bar")
+ assert_equal "foo", @cache.read("foo")
+ assert_equal "bar", @cache.read("bar")
+ assert_equal "foo", values["foo"]
+ assert_equal "bar", values["bar"]
+ end
+ end
+
def test_middleware
app = lambda { |env|
result = @cache.write("foo", "bar")
diff --git a/activesupport/test/cache/stores/redis_cache_store_test.rb b/activesupport/test/cache/stores/redis_cache_store_test.rb
index 375993a632..dda96b68fb 100644
--- a/activesupport/test/cache/stores/redis_cache_store_test.rb
+++ b/activesupport/test/cache/stores/redis_cache_store_test.rb
@@ -95,6 +95,12 @@ module ActiveSupport::Cache::RedisCacheStoreTests
end
end
+ test "instance of Redis uses given instance" do
+ redis_instance = Redis.new
+ @cache = build(redis: redis_instance)
+ assert_same @cache.redis, redis_instance
+ end
+
private
def build(**kwargs)
ActiveSupport::Cache::RedisCacheStore.new(driver: DRIVER, **kwargs).tap do |cache|
diff --git a/activesupport/test/core_ext/object/try_test.rb b/activesupport/test/core_ext/object/try_test.rb
index 40d6cdd28e..a838334034 100644
--- a/activesupport/test/core_ext/object/try_test.rb
+++ b/activesupport/test/core_ext/object/try_test.rb
@@ -78,7 +78,6 @@ class ObjectTryTest < ActiveSupport::TestCase
def test_try_with_private_method_bang
klass = Class.new do
private
-
def private_method
"private method"
end
@@ -90,7 +89,6 @@ class ObjectTryTest < ActiveSupport::TestCase
def test_try_with_private_method
klass = Class.new do
private
-
def private_method
"private method"
end
@@ -109,7 +107,6 @@ class ObjectTryTest < ActiveSupport::TestCase
end
private
-
def private_delegator_method
"private delegator method"
end
@@ -120,11 +117,11 @@ class ObjectTryTest < ActiveSupport::TestCase
end
def test_try_with_method_on_delegator_target
- assert_equal 5, Decorator.new(@string).size
+ assert_equal 5, Decorator.new(@string).try(:size)
end
def test_try_with_overridden_method_on_delegator
- assert_equal "overridden reverse", Decorator.new(@string).reverse
+ assert_equal "overridden reverse", Decorator.new(@string).try(:reverse)
end
def test_try_with_private_method_on_delegator
@@ -140,7 +137,6 @@ class ObjectTryTest < ActiveSupport::TestCase
def test_try_with_private_method_on_delegator_target
klass = Class.new do
private
-
def private_method
"private method"
end
@@ -152,7 +148,6 @@ class ObjectTryTest < ActiveSupport::TestCase
def test_try_with_private_method_on_delegator_target_bang
klass = Class.new do
private
-
def private_method
"private method"
end
diff --git a/activesupport/test/core_ext/uri_ext_test.rb b/activesupport/test/core_ext/uri_ext_test.rb
index 8816b0d392..c0686bc720 100644
--- a/activesupport/test/core_ext/uri_ext_test.rb
+++ b/activesupport/test/core_ext/uri_ext_test.rb
@@ -9,6 +9,6 @@ class URIExtTest < ActiveSupport::TestCase
str = "\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E" # Ni-ho-nn-go in UTF-8, means Japanese.
parser = URI.parser
- assert_equal str, parser.unescape(parser.escape(str))
+ assert_equal str + str, parser.unescape(str + parser.escape(str).encode(Encoding::UTF_8))
end
end
diff --git a/guides/CHANGELOG.md b/guides/CHANGELOG.md
index 21b4ed4b9f..0307e06fd9 100644
--- a/guides/CHANGELOG.md
+++ b/guides/CHANGELOG.md
@@ -1,5 +1,3 @@
-## Rails 6.0.0.alpha (Unreleased) ##
-
* Rails 6 requires Ruby 2.4.1 or newer.
*Jeremy Daer*
diff --git a/guides/source/5_2_release_notes.md b/guides/source/5_2_release_notes.md
index 7b5c4b87e3..541c025fac 100644
--- a/guides/source/5_2_release_notes.md
+++ b/guides/source/5_2_release_notes.md
@@ -7,9 +7,9 @@ Highlights in Rails 5.2:
* Active Storage
* Redis Cache Store
-* HTTP/2 Early hints support
+* HTTP/2 Early Hints
* Credentials
-* Default Content Security Policy
+* Content Security Policy
These release notes cover only the major changes. To learn about various bug
fixes and changes, please refer to the change logs or check out the [list of
@@ -24,39 +24,70 @@ Upgrading to Rails 5.2
If you're upgrading an existing application, it's a great idea to have good test
coverage before going in. You should also first upgrade to Rails 5.1 in case you
haven't and make sure your application still runs as expected before attempting
-an update to Rails 5.2.
-
+an update to Rails 5.2. A list of things to watch out for when upgrading is
+available in the
+[Upgrading Ruby on Rails](upgrading_ruby_on_rails.html#upgrading-from-rails-5-1-to-rails-5-2)
+guide.
Major Features
--------------
### Active Storage
-[README](https://github.com/rails/rails/blob/d3893ec38ec61282c2598b01a298124356d6b35a/activestorage/README.md)
+[Pull Request](https://github.com/rails/rails/pull/30020)
+
+[Active Storage](https://github.com/rails/rails/tree/5-2-stable/activestorage)
+facilitates uploading files to a cloud storage service like
+Amazon S3, Google Cloud Storage, or Microsoft Azure Storage and attaching
+those files to Active Record objects. It comes with a local disk-based service
+for development and testing and supports mirroring files to subordinate
+services for backups and migrations.
+You can read more about Active Storage in the
+[Active Storage Overview](active_storage_overview.html) guide.
### Redis Cache Store
[Pull Request](https://github.com/rails/rails/pull/31134)
+Rails 5.2 ships with built-in Redis cache store.
+You can read more about this in the
+[Caching with Rails: An Overview](caching_with_rails.html#activesupport-cache-rediscachestore)
+guide.
-### HTTP/2 Early hints support
+### HTTP/2 Early Hints
[Pull Request](https://github.com/rails/rails/pull/30744)
+Rails 5.2 supports [HTTP/2 Early Hints](https://tools.ietf.org/html/rfc8297).
+To start the server with Early Hints enabled pass `--early-hints`
+to `bin/rails server`.
### Credentials
[Pull Request](https://github.com/rails/rails/pull/30067)
+Added `config/credentials.yml.enc` file to store production app secrets.
+It allows saving any authentication credentials for third-party services
+directly in repository encrypted with a key in the `config/master.key` file or
+the `RAILS_MASTER_KEY` environment variable.
+This will eventually replace `Rails.application.secrets` and the encrypted
+secrets introduced in Rails 5.1.
+Furthermore, Rails 5.2
+[opens API underlying Credentials](https://github.com/rails/rails/pull/30940),
+so you can easily deal with other encrypted configurations, keys, and files.
-### Default Content Security Policy
+### Content Security Policy
[Pull Request](https://github.com/rails/rails/pull/31162)
-Incompatibilities
------------------
-
-ToDo
+Rails 5.2 ships with a new DSL that allows you to configure a
+[Content Security Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy)
+for your application. You can configure a global default policy and then
+override it on a per-resource basis and even use lambdas to inject per-request
+values into the header such as account subdomains in a multi-tenant application.
+You can read more about this in the
+[Securing Rails Applications](security.html#content-security-policy)
+guide.
Railties
--------
@@ -68,37 +99,105 @@ Please refer to the [Changelog][railties] for detailed changes.
* Deprecate `capify!` method in generators and templates.
([Pull Request](https://github.com/rails/rails/pull/29493))
-* Deprecated passing the environment's name as a regular argument to the
- `rails dbconsole` and `rails console` commands.
- ([Pull Request](https://github.com/rails/rails/pull/29358))
+* Passing the environment's name as a regular argument to the
+ `rails dbconsole` and `rails console` commands is deprecated.
+ The `-e` option should be used instead.
+ ([Commit](https://github.com/rails/rails/commit/48b249927375465a7102acc71c2dfb8d49af8309))
-* Deprecated using subclass of `Rails::Application` to start the Rails server.
+* Deprecate using subclass of `Rails::Application` to start the Rails server.
([Pull Request](https://github.com/rails/rails/pull/30127))
-* Deprecated `after_bundle` callback in Rails plugin templates.
+* Deprecate `after_bundle` callback in Rails plugin templates.
([Pull Request](https://github.com/rails/rails/pull/29446))
### Notable changes
-ToDo
+* Added a shared section to `config/database.yml` that will be loaded for
+ all environments.
+ ([Pull Request](https://github.com/rails/rails/pull/28896))
+
+* Add `railtie.rb` to the plugin generator.
+ ([Pull Request](https://github.com/rails/rails/pull/29576))
+
+* Clear screenshot files in `tmp:clear` task.
+ ([Pull Request](https://github.com/rails/rails/pull/29534))
+
+* Skip unused components when running `bin/rails app:update`.
+ If the initial app generation skipped Action Cable, Active Record etc.,
+ the update task honors those skips too.
+ ([Pull Request](https://github.com/rails/rails/pull/29645))
+
+* Allow passing a custom connection name to the `rails dbconsole`
+ command when using a 3-level database configuration.
+ Example: `bin/rails dbconsole -c replica`.
+ ([Commit](https://github.com/rails/rails/commit/1acd9a6464668d4d54ab30d016829f60b70dbbeb))
+
+* Properly expand shortcuts for environment's name running the `console`
+ and `dbconsole` commands.
+ ([Commit](https://github.com/rails/rails/commit/3777701f1380f3814bd5313b225586dec64d4104))
+
+* Add `bootsnap` to default `Gemfile`.
+ ([Pull Request](https://github.com/rails/rails/pull/29313))
+
+* Support `-` as a platform-agnostic way to run a script from stdin with
+ `rails runner`
+ ([Pull Request](https://github.com/rails/rails/pull/26343))
+
+* 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.
+ ([Pull Request](https://github.com/rails/rails/pull/30016))
+
+* Add `--skip-action-cable` option to the plugin generator.
+ ([Pull Request](https://github.com/rails/rails/pull/30164))
+
+* Add `git_source` to `Gemfile` for plugin generator.
+ ([Pull Request](https://github.com/rails/rails/pull/30110))
+
+* Skip unused components when running `bin/rails` in Rails plugin.
+ ([Commit](https://github.com/rails/rails/commit/62499cb6e088c3bc32a9396322c7473a17a28640))
+
+* Optimize indentation for generator actions.
+ ([Pull Request](https://github.com/rails/rails/pull/30166))
+
+* Optimize routes indentation.
+ ([Pull Request](https://github.com/rails/rails/pull/30241))
+
+* Add `--skip-yarn` option to the plugin generator.
+ ([Pull Request](https://github.com/rails/rails/pull/30238))
+
+* Support multiple versions arguments for `gem` method of Generators.
+ ([Pull Request](https://github.com/rails/rails/pull/30323))
+
+* Derive `secret_key_base` from the app name in development and test
+ environments.
+ ([Pull Request](https://github.com/rails/rails/pull/30067))
+
+* Add `mini_magick` to default `Gemfile` as comment.
+ ([Pull Request](https://github.com/rails/rails/pull/30633))
+
+* `rails new` and `rails plugin new` get `Active Storage` by default.
+ Add ability to skip `Active Storage` with `--skip-active-storage`
+ and do so automatically when `--skip-active-record` is used.
+ ([Pull Request](https://github.com/rails/rails/pull/30101))
Action Cable
------------
+------------
Please refer to the [Changelog][action-cable] for detailed changes.
### Removals
* Removed deprecated evented redis adapter.
- ([Commit](https://github.com/rails/rails/commit/48766e32d31))
+ ([Commit](https://github.com/rails/rails/commit/48766e32d31651606b9f68a16015ad05c3b0de2c))
### Notable changes
-* Added support for `host`, `port`, `db` and `password` options in cable.yml
+* Add support for `host`, `port`, `db` and `password` options in cable.yml
([Pull Request](https://github.com/rails/rails/pull/29528))
-* Added support for compatibility with redis-rb gem for 4.0 version.
- ([Pull Request](https://github.com/rails/rails/pull/30748))
+* Hash long stream identifiers when using PostgreSQL adapter.
+ ([Pull Request](https://github.com/rails/rails/pull/29297))
Action Pack
-----------
@@ -107,38 +206,131 @@ Please refer to the [Changelog][action-pack] for detailed changes.
### Removals
-* Removed deprecated `ActionController::ParamsParser::ParseError`.
- ([Commit](https://github.com/rails/rails/commit/e16c765ac6d))
+* Remove deprecated `ActionController::ParamsParser::ParseError`.
+ ([Commit](https://github.com/rails/rails/commit/e16c765ac6dcff068ff2e5554d69ff345c003de1))
### Deprecations
-* Deprecated `#success?`, `#missing?` and `#error?` aliases of
+* Deprecate `#success?`, `#missing?` and `#error?` aliases of
`ActionDispatch::TestResponse`.
([Pull Request](https://github.com/rails/rails/pull/30104))
### Notable changes
-ToDo
+* Add support for recyclable cache keys with fragment caching.
+ ([Pull Request](https://github.com/rails/rails/pull/29092))
+
+* Change the cache key format for fragments to make it easier to debug key
+ churn.
+ ([Pull Request](https://github.com/rails/rails/pull/29092))
+
+* AEAD encrypted cookies and sessions with GCM.
+ ([Pull Request](https://github.com/rails/rails/pull/28132))
+
+* Protect from forgery by default.
+ ([Pull Request](https://github.com/rails/rails/pull/29742))
+
+* Enforce signed/encrypted cookie expiry server side.
+ ([Pull Request](https://github.com/rails/rails/pull/30121))
+
+* Cookies `:expires` option supports `ActiveSupport::Duration` object.
+ ([Pull Request](https://github.com/rails/rails/pull/30121))
+
+* Use Capybara registered `:puma` server config.
+ ([Pull Request](https://github.com/rails/rails/pull/30638))
+
+* Simplify cookies middleware with key rotation support.
+ ([Pull Request](https://github.com/rails/rails/pull/29716))
+
+* Add ability to enable Early Hints for HTTP/2.
+ ([Pull Request](https://github.com/rails/rails/pull/30744))
+
+* Add headless chrome support to System Tests.
+ ([Pull Request](https://github.com/rails/rails/pull/30876))
+
+* Add `:allow_other_host` option to `redirect_back` method.
+ ([Pull Request](https://github.com/rails/rails/pull/30850))
+
+* Make `assert_recognizes` to traverse mounted engines.
+ ([Pull Request](https://github.com/rails/rails/pull/22435))
+
+* Add DSL for configuring Content-Security-Policy header.
+ ([Pull Request](https://github.com/rails/rails/pull/31162),
+ [Commit](https://github.com/rails/rails/commit/619b1b6353a65e1635d10b8f8c6630723a5a6f1a),
+ [Commit](https://github.com/rails/rails/commit/4ec8bf68ff92f35e79232fbd605012ce1f4e1e6e))
+
+* Register most popular audio/video/font mime types supported by modern
+ browsers.
+ ([Pull Request](https://github.com/rails/rails/pull/31251))
+
+* Changed the default system test screenshot output from `inline` to `simple`.
+ ([Commit](https://github.com/rails/rails/commit/9d6e288ee96d6241f864dbf90211c37b14a57632))
+
+* Add headless firefox support to System Tests.
+ ([Pull Request](https://github.com/rails/rails/pull/31365))
+
+* Add secure `X-Download-Options` and `X-Permitted-Cross-Domain-Policies` to
+ default headers set.
+ ([Commit](https://github.com/rails/rails/commit/5d7b70f4336d42eabfc403e9f6efceb88b3eff44))
+
+* Changed the system tests to set Puma as default server only when the
+ user haven't specified manually another server.
+ ([Pull Request](https://github.com/rails/rails/pull/31384))
+
+* Add `Referrer-Policy` header to default headers set.
+ ([Commit](https://github.com/rails/rails/commit/428939be9f954d39b0c41bc53d85d0d106b9d1a1))
+
+* Matches behavior of `Hash#each` in `ActionController::Parameters#each`.
+ ([Pull Request](https://github.com/rails/rails/pull/27790))
+
+* Add support for automatic nonce generation for Rails UJS.
+ ([Commit](https://github.com/rails/rails/commit/b2f0a8945956cd92dec71ec4e44715d764990a49))
+
+* Update the default HSTS max-age value to 31536000 seconds (1 year)
+ to meet the minimum max-age requirement for https://hstspreload.org/.
+ ([Commit](https://github.com/rails/rails/commit/30b5f469a1d30c60d1fb0605e84c50568ff7ed37))
+
+* Add alias method `to_hash` to `to_h` for `cookies`.
+ Add alias method `to_h` to `to_hash` for `session`.
+ ([Commit](https://github.com/rails/rails/commit/50a62499e41dfffc2903d468e8b47acebaf9b500))
Action View
--------------
+-----------
Please refer to the [Changelog][action-view] for detailed changes.
### Removals
-* Removed deprecated Erubis ERB handler.
- ([Commit](https://github.com/rails/rails/commit/7de7f12fd14))
+* Remove deprecated Erubis ERB handler.
+ ([Commit](https://github.com/rails/rails/commit/7de7f12fd140a60134defe7dc55b5a20b2372d06))
### Deprecations
-* Deprecated `image_alt` helper which used to add default alt text to
+* Deprecate `image_alt` helper which used to add default alt text to
the images generated by `image_tag`.
([Pull Request](https://github.com/rails/rails/pull/30213))
### Notable changes
-ToDo
+* Add `:json` type to `auto_discovery_link_tag` to support
+ [JSON Feeds](https://jsonfeed.org/version/1).
+ ([Pull Request](https://github.com/rails/rails/pull/29158))
+
+* Add `srcset` option to `image_tag` helper.
+ ([Pull Request](https://github.com/rails/rails/pull/29349))
+
+* Fix issues with `field_error_proc` wrapping `optgroup` and
+ select divider `option`.
+ ([Pull Request](https://github.com/rails/rails/pull/31088))
+
+* Change `form_with` to generates ids by default.
+ ([Commit](https://github.com/rails/rails/commit/260d6f112a0ffdbe03e6f5051504cb441c1e94cd))
+
+* Add `preload_link_tag` helper.
+ ([Pull Request](https://github.com/rails/rails/pull/31251))
+
+* Allow the use of callable objects as group methods for grouped selects.
+ ([Pull Request](https://github.com/rails/rails/pull/31578))
Action Mailer
-------------
@@ -147,35 +339,306 @@ Please refer to the [Changelog][action-mailer] for detailed changes.
### Notable changes
-ToDo
+* Allow Action Mailer classes to configure their delivery job.
+ ([Pull Request](https://github.com/rails/rails/pull/29457))
+
+* Add `assert_enqueued_email_with` test helper.
+ ([Pull Request](https://github.com/rails/rails/pull/30695))
Active Record
-------------
Please refer to the [Changelog][active-record] for detailed changes.
-ToDo
+### Removals
+
+* Remove deprecated `#migration_keys`.
+ ([Pull Request](https://github.com/rails/rails/pull/30337))
+
+* Remove deprecated support to `quoted_id` when typecasting
+ an Active Record object.
+ ([Commit](https://github.com/rails/rails/commit/82472b3922bda2f337a79cef961b4760d04f9689))
+
+* Remove deprecated argument `default` from `index_name_exists?`.
+ ([Commit](https://github.com/rails/rails/commit/8f5b34df81175e30f68879479243fbce966122d7))
+
+* Remove deprecated support to passing a class to `:class_name`
+ on associations.
+ ([Commit](https://github.com/rails/rails/commit/e65aff70696be52b46ebe57207ebd8bb2cfcdbb6))
+
+* Remove deprecated methods `initialize_schema_migrations_table` and
+ `initialize_internal_metadata_table`.
+ ([Commit](https://github.com/rails/rails/commit/c9660b5777707658c414b430753029cd9bc39934))
+
+* Remove deprecated method `supports_migrations?`.
+ ([Commit](https://github.com/rails/rails/commit/9438c144b1893f2a59ec0924afe4d46bd8d5ffdd))
+
+* Remove deprecated method `supports_primary_key?`.
+ ([Commit](https://github.com/rails/rails/commit/c56ff22fc6e97df4656ddc22909d9bf8b0c2cbb1))
+
+* Remove deprecated method
+ `ActiveRecord::Migrator.schema_migrations_table_name`.
+ ([Commit](https://github.com/rails/rails/commit/7df6e3f3cbdea9a0460ddbab445c81fbb1cfd012))
+
+* Remove deprecated argument `name` from `#indexes`.
+ ([Commit](https://github.com/rails/rails/commit/d6b779ecebe57f6629352c34bfd6c442ac8fba0e))
+
+* Remove deprecated arguments from `#verify!`.
+ ([Commit](https://github.com/rails/rails/commit/9c6ee1bed0292fc32c23dc1c68951ae64fc510be))
+
+* Remove deprecated configuration `.error_on_ignored_order_or_limit`.
+ ([Commit](https://github.com/rails/rails/commit/e1066f450d1a99c9a0b4d786b202e2ca82a4c3b3))
+
+* Remove deprecated method `#scope_chain`.
+ ([Commit](https://github.com/rails/rails/commit/ef7784752c5c5efbe23f62d2bbcc62d4fd8aacab))
+
+* Remove deprecated method `#sanitize_conditions`.
+ ([Commit](https://github.com/rails/rails/commit/8f5413b896099f80ef46a97819fe47a820417bc2))
### Deprecations
-ToDo
+* Deprecate `supports_statement_cache?`.
+ ([Pull Request](https://github.com/rails/rails/pull/28938))
+
+* Deprecate passing arguments and block at the same time to
+ `count` and `sum` in `ActiveRecord::Calculations`.
+ ([Pull Request](https://github.com/rails/rails/pull/29262))
+
+* Deprecate delegating to `arel` in `Relation`.
+ ([Pull Request](https://github.com/rails/rails/pull/29619))
+
+* Deprecate `set_state` method in `TransactionState`.
+ ([Commit](https://github.com/rails/rails/commit/608ebccf8f6314c945444b400a37c2d07f21b253))
+
+* Deprecate `expand_hash_conditions_for_aggregates` without replacement.
+ ([Commit](https://github.com/rails/rails/commit/7ae26885d96daee3809d0bd50b1a440c2f5ffb69))
### Notable changes
-ToDo
+* When calling the dynamic fixture accessor method with no arguments, it now
+ returns all fixtures of this type. Previously this method always returned
+ an empty array.
+ ([Pull Request](https://github.com/rails/rails/pull/28692))
+
+* Fix inconsistency with changed attributes when overriding
+ Active Record attribute reader.
+ ([Pull Request](https://github.com/rails/rails/pull/28661))
+
+* Support Descending Indexes for MySQL.
+ ([Pull Request](https://github.com/rails/rails/pull/28773))
+
+* Fix `bin/rails db:forward` first migration.
+ ([Commit](https://github.com/rails/rails/commit/b77d2aa0c336492ba33cbfade4964ba0eda3ef84))
+
+* Raise error `UnknownMigrationVersionError` on the movement of migrations
+ when the current migration does not exist.
+ ([Commit](https://github.com/rails/rails/commit/bb9d6eb094f29bb94ef1f26aa44f145f17b973fe))
+
+* Respect `SchemaDumper.ignore_tables` in rake tasks for
+ databases structure dump.
+ ([Pull Request](https://github.com/rails/rails/pull/29077))
+
+* Add `ActiveRecord::Base#cache_version` to support recyclable cache keys via
+ the new versioned entries in `ActiveSupport::Cache`. This also means that
+ `ActiveRecord::Base#cache_key` will now return a stable key that
+ does not include a timestamp any more.
+ ([Pull Request](https://github.com/rails/rails/pull/29092))
+
+* Prevent creation of bind param if casted value is nil.
+ ([Pull Request](https://github.com/rails/rails/pull/29282))
+
+* Use bulk INSERT to insert fixtures for better performance.
+ ([Pull Request](https://github.com/rails/rails/pull/29504))
+
+* Merging two relations representing nested joins no longer transforms
+ the joins of the merged relation into LEFT OUTER JOIN.
+ ([Pull Request](https://github.com/rails/rails/pull/27063))
+
+* Fix transactions to apply state to child transactions.
+ Previously, if you had a nested transaction and the outer transaction was
+ rolledback, the record from the inner transaction would still be marked
+ as persisted. It was fixed by applying the state of the parent
+ transaction to the child transaction when the parent transaction is
+ rolledback. This will correctly mark records from the inner transaction
+ as not persisted.
+ ([Commit](https://github.com/rails/rails/commit/0237da287eb4c507d10a0c6d94150093acc52b03))
+
+* Fix eager loading/preloading association with scope including joins.
+ ([Pull Request](https://github.com/rails/rails/pull/29413))
+
+* Prevent errors raised by `sql.active_record` notification subscribers
+ from being converted into `ActiveRecord::StatementInvalid` exceptions.
+ ([Pull Request](https://github.com/rails/rails/pull/29692))
+
+* Skip query caching when working with batches of records
+ (`find_each`, `find_in_batches`, `in_batches`).
+ ([Commit](https://github.com/rails/rails/commit/b83852e6eed5789b23b13bac40228e87e8822b4d))
+
+* Change sqlite3 boolean serialization to use 1 and 0.
+ SQLite natively recognizes 1 and 0 as true and false, but does not natively
+ recognize 't' and 'f' as was previously serialized.
+ ([Pull Request](https://github.com/rails/rails/pull/29699))
+
+* Values constructed using multi-parameter assignment will now use the
+ post-type-cast value for rendering in single-field form inputs.
+ ([Commit](https://github.com/rails/rails/commit/1519e976b224871c7f7dd476351930d5d0d7faf6))
+
+* `ApplicationRecord` is no longer generated when generating models. If you
+ need to generate it, it can be created with `rails g application_record`.
+ ([Pull Request](https://github.com/rails/rails/pull/29916))
+
+* `Relation#or` now accepts two relations who have different values for
+ `references` only, as `references` can be implicitly called by `where`.
+ ([Commit](https://github.com/rails/rails/commit/ea6139101ccaf8be03b536b1293a9f36bc12f2f7))
+
+* When using `Relation#or`, extract the common conditions and
+ put them before the OR condition.
+ ([Pull Request](https://github.com/rails/rails/pull/29950))
+
+* Add `binary` fixture helper method.
+ ([Pull Request](https://github.com/rails/rails/pull/30073))
+
+* Automatically guess the inverse associations for STI.
+ ([Pull Request](https://github.com/rails/rails/pull/23425))
+
+* Add new error class `LockWaitTimeout` which will be raised
+ when lock wait timeout exceeded.
+ ([Pull Request](https://github.com/rails/rails/pull/30360))
+
+* Update payload names for `sql.active_record` instrumentation to be
+ more descriptive.
+ ([Pull Request](https://github.com/rails/rails/pull/30619))
+
+* Use given algorithm while removing index from database.
+ ([Pull Request](https://github.com/rails/rails/pull/24199))
+
+* Passing a `Set` to `Relation#where` now behaves the same as passing
+ an array.
+ ([Commit](https://github.com/rails/rails/commit/9cf7e3494f5bd34f1382c1ff4ea3d811a4972ae2))
+
+* PostgreSQL `tsrange` now preserves subsecond precision.
+ ([Pull Request](https://github.com/rails/rails/pull/30725))
+
+* Raises when calling `lock!` in a dirty record.
+ ([Commit](https://github.com/rails/rails/commit/63cf15877bae859ff7b4ebaf05186f3ca79c1863))
+
+* Fixed a bug where column orders for an index weren't written to
+ `db/schema.rb` when using the sqlite adapter.
+ ([Pull Request](https://github.com/rails/rails/pull/30970))
+
+* Fix `bin/rails db:migrate` with specified `VERSION`.
+ `bin/rails db:migrate` with empty VERSION behaves as without `VERSION`.
+ Check a format of `VERSION`: Allow a migration version number
+ or name of a migration file. Raise error if format of `VERSION` is invalid.
+ Raise error if target migration doesn't exist.
+ ([Pull Request](https://github.com/rails/rails/pull/30714))
+
+* Add new error class `StatementTimeout` which will be raised
+ when statement timeout exceeded.
+ ([Pull Request](https://github.com/rails/rails/pull/31129))
+
+* `update_all` will now pass its values to `Type#cast` before passing them to
+ `Type#serialize`. This means that `update_all(foo: 'true')` will properly
+ persist a boolean.
+ ([Commit](https://github.com/rails/rails/commit/68fe6b08ee72cc47263e0d2c9ff07f75c4b42761))
+
+* Require raw SQL fragments to be explicitly marked when used in
+ relation query methods.
+ ([Commit](https://github.com/rails/rails/commit/a1ee43d2170dd6adf5a9f390df2b1dde45018a48),
+ [Commit](https://github.com/rails/rails/commit/e4a921a75f8702a7dbaf41e31130fe884dea93f9))
+
+* Add `#up_only` to database migrations for code that is only relevant when
+ migrating up, e.g. populating a new column.
+ ([Pull Request](https://github.com/rails/rails/pull/31082))
+
+* Add new error class `QueryCanceled` which will be raised
+ when canceling statement due to user request.
+ ([Pull Request](https://github.com/rails/rails/pull/31235))
+
+* Don't allow scopes to be defined which conflict with instance methods
+ on `Relation`.
+ ([Pull Request](https://github.com/rails/rails/pull/31179))
+
+* Add support for PostgreSQL operator classes to `add_index`.
+ ([Pull Request](https://github.com/rails/rails/pull/19090))
+
+* Log database query callers.
+ ([Pull Request](https://github.com/rails/rails/pull/26815),
+ [Pull Request](https://github.com/rails/rails/pull/31519),
+ [Pull Request](https://github.com/rails/rails/pull/31690))
+
+* Undefine attribute methods on descendants when resetting column information.
+ ([Pull Request](https://github.com/rails/rails/pull/31475))
+
+* Using subselect for `delete_all` with `limit` or `offset`.
+ ([Commit](https://github.com/rails/rails/commit/9e7260da1bdc0770cf4ac547120c85ab93ff3d48))
+
+* Fixed inconsistency with `first(n)` when used with `limit()`.
+ The `first(n)` finder now respects the `limit()`, making it consistent
+ with `relation.to_a.first(n)`, and also with the behavior of `last(n)`.
+ ([Pull Request](https://github.com/rails/rails/pull/27597))
+
+* Fix nested `has_many :through` associations on unpersisted parent instances.
+ ([Commit](https://github.com/rails/rails/commit/027f865fc8b262d9ba3ee51da3483e94a5489b66))
+
+* Take into account association conditions when deleting through records.
+ ([Commit](https://github.com/rails/rails/commit/ae48c65e411e01c1045056562319666384bb1b63))
+
+* Don't allow destroyed object mutation after `save` or `save!` is called.
+ ([Commit](https://github.com/rails/rails/commit/562dd0494a90d9d47849f052e8913f0050f3e494))
+
+* Fix relation merger issue with `left_outer_joins`.
+ ([Pull Request](https://github.com/rails/rails/pull/27860))
+
+* Support for PostgreSQL foreign tables.
+ ([Pull Request](https://github.com/rails/rails/pull/31549))
+
+* Clear the transaction state when an Active Record object is duped.
+ ([Pull Request](https://github.com/rails/rails/pull/31751))
+
+* Fix not expanded problem when passing an Array object as argument
+ to the where method using `composed_of` column.
+ ([Pull Request](https://github.com/rails/rails/pull/31724))
+
+* Make `reflection.klass` raise if `polymorphic?` not to be misused.
+ ([Commit](https://github.com/rails/rails/commit/63fc1100ce054e3e11c04a547cdb9387cd79571a))
+
+* Fix `#columns_for_distinct` of MySQL and PostgreSQL to make
+ `ActiveRecord::FinderMethods#limited_ids_for` use correct primary key values
+ even if `ORDER BY` columns include other table's primary key.
+ ([Commit](https://github.com/rails/rails/commit/851618c15750979a75635530200665b543561a44))
+
+* Fix `dependent: :destroy` issue for has_one/belongs_to relationship where
+ the parent class was getting deleted when the child was not.
+ ([Commit](https://github.com/rails/rails/commit/b0fc04aa3af338d5a90608bf37248668d59fc881))
Active Model
------------
Please refer to the [Changelog][active-model] for detailed changes.
-### Removals
+### Notable changes
-ToDo
+* Fix methods `#keys`, `#values` in `ActiveModel::Errors`.
+ Change `#keys` to only return the keys that don't have empty messages.
+ Change `#values` to only return the not empty values.
+ ([Pull Request](https://github.com/rails/rails/pull/28584))
-### Notable changes
+* Add method `#merge!` for `ActiveModel::Errors`.
+ ([Pull Request](https://github.com/rails/rails/pull/29714))
+
+* Allow passing a Proc or Symbol to length validator options.
+ ([Pull Request](https://github.com/rails/rails/pull/30674))
+
+* Execute `ConfirmationValidator` validation when `_confirmation`'s value
+ is `false`.
+ ([Pull Request](https://github.com/rails/rails/pull/31058))
-ToDo
+* Models using the attributes API with a proc default can now be marshalled.
+ ([Commit](https://github.com/rails/rails/commit/0af36c62a5710e023402e37b019ad9982e69de4b))
+
+* Do not lose all multiple `:includes` with options in serialization.
+ ([Commit](https://github.com/rails/rails/commit/853054bcc7a043eea78c97e7705a46abb603cc44))
Active Support
--------------
@@ -184,35 +647,203 @@ Please refer to the [Changelog][active-support] for detailed changes.
### Removals
-ToDo
+* Remove deprecated `:if` and `:unless` string filter for callbacks.
+ ([Commit](https://github.com/rails/rails/commit/c792354adcbf8c966f274915c605c6713b840548))
+
+* Remove deprecated `halt_callback_chains_on_return_false` option.
+ ([Commit](https://github.com/rails/rails/commit/19fbbebb1665e482d76cae30166b46e74ceafe29))
### Deprecations
-ToDo
+* Deprecate `Module#reachable?` method.
+ ([Pull Request](https://github.com/rails/rails/pull/30624))
+
+* Deprecate `secrets.secret_token`.
+ ([Commit](https://github.com/rails/rails/commit/fbcc4bfe9a211e219da5d0bb01d894fcdaef0a0e))
### Notable changes
-ToDo
+* Add `fetch_values` for `HashWithIndifferentAccess`.
+ ([Pull Request](https://github.com/rails/rails/pull/28316))
+
+* Add support for `:offset` to `Time#change`.
+ ([Commit](https://github.com/rails/rails/commit/851b7f866e13518d900407c78dcd6eb477afad06))
+
+* Add support for `:offset` and `:zone`
+ to `ActiveSupport::TimeWithZone#change`.
+ ([Commit](https://github.com/rails/rails/commit/851b7f866e13518d900407c78dcd6eb477afad06))
+
+* Pass gem name and deprecation horizon to deprecation notifications.
+ ([Pull Request](https://github.com/rails/rails/pull/28800))
+
+* Add support for versioned cache entries. This enables the cache stores to
+ recycle cache keys, greatly saving on storage in cases with frequent churn.
+ Works together with the separation of `#cache_key` and `#cache_version`
+ in Active Record and its use in Action Pack's fragment caching.
+ ([Pull Request](https://github.com/rails/rails/pull/29092))
+
+* Add `ActiveSupport::CurrentAttributes` to provide a thread-isolated
+ attributes singleton. Primary use case is keeping all the per-request
+ attributes easily available to the whole system.
+ ([Pull Request](https://github.com/rails/rails/pull/29180))
+
+* `#singularize` and `#pluralize` now respect uncountables for
+ the specified locale.
+ ([Commit](https://github.com/rails/rails/commit/352865d0f835c24daa9a2e9863dcc9dde9e5371a))
+
+* Add default option to `class_attribute`.
+ ([Pull Request](https://github.com/rails/rails/pull/29270))
+
+* Add `Date#prev_occurring` and `Date#next_occurring` to return
+ specified next/previous occurring day of week.
+ ([Pull Request](https://github.com/rails/rails/pull/26600))
+
+* Add default option to module and class attribute accessors.
+ ([Pull Request](https://github.com/rails/rails/pull/29294))
+
+* Cache: `write_multi`.
+ ([Pull Request](https://github.com/rails/rails/pull/29366))
+
+* Default `ActiveSupport::MessageEncryptor` to use AES 256 GCM encryption.
+ ([Pull Request](https://github.com/rails/rails/pull/29263))
+
+* Add `freeze_time` helper which freezes time to `Time.now` in tests.
+ ([Pull Request](https://github.com/rails/rails/pull/29681))
+
+* Make the order of `Hash#reverse_merge!` consistent
+ with `HashWithIndifferentAccess`.
+ ([Pull Request](https://github.com/rails/rails/pull/28077))
+
+* Add purpose and expiry support to `ActiveSupport::MessageVerifier` and
+ `ActiveSupport::MessageEncryptor`.
+ ([Pull Request](https://github.com/rails/rails/pull/29892))
+
+* Update `String#camelize` to provide feedback when wrong option is passed.
+ ([Pull Request](https://github.com/rails/rails/pull/30039))
+
+* `Module#delegate_missing_to` now raises `DelegationError` if target is nil,
+ similar to `Module#delegate`.
+ ([Pull Request](https://github.com/rails/rails/pull/30191))
+
+* Add `ActiveSupport::EncryptedFile` and
+ `ActiveSupport::EncryptedConfiguration`.
+ ([Pull Request](https://github.com/rails/rails/pull/30067))
+
+* Add `config/credentials.yml.enc` to store production app secrets.
+ ([Pull Request](https://github.com/rails/rails/pull/30067))
+
+* Add key rotation support to `MessageEncryptor` and `MessageVerifier`.
+ ([Pull Request](https://github.com/rails/rails/pull/29716))
+
+* Return an instance of `HashWithIndifferentAccess` from
+ `HashWithIndifferentAccess#transform_keys`.
+ ([Pull Request](https://github.com/rails/rails/pull/30728))
+
+* `Hash#slice` now falls back to Ruby 2.5+'s built-in definition if defined.
+ ([Commit](https://github.com/rails/rails/commit/01ae39660243bc5f0a986e20f9c9bff312b1b5f8))
+
+* `IO#to_json` now returns the `to_s` representation, rather than
+ attempting to convert to an array. This fixes a bug where `IO#to_json`
+ would raise an `IOError` when called on an unreadable object.
+ ([Pull Request](https://github.com/rails/rails/pull/30953))
+
+* Add same method signature for `Time#prev_day` and `Time#next_day`
+ in accordance with `Date#prev_day`, `Date#next_day`.
+ Allows pass argument for `Time#prev_day` and `Time#next_day`.
+ ([Commit](https://github.com/rails/rails/commit/61ac2167eff741bffb44aec231f4ea13d004134e))
+
+* Add same method signature for `Time#prev_month` and `Time#next_month`
+ in accordance with `Date#prev_month`, `Date#next_month`.
+ Allows pass argument for `Time#prev_month` and `Time#next_month`.
+ ([Commit](https://github.com/rails/rails/commit/f2c1e3a793570584d9708aaee387214bc3543530))
+
+* Add same method signature for `Time#prev_year` and `Time#next_year`
+ in accordance with `Date#prev_year`, `Date#next_year`.
+ Allows pass argument for `Time#prev_year` and `Time#next_year`.
+ ([Commit](https://github.com/rails/rails/commit/ee9d81837b5eba9d5ec869ae7601d7ffce763e3e))
+
+* Fix acronym support in `humanize`.
+ ([Commit](https://github.com/rails/rails/commit/0ddde0a8fca6a0ca3158e3329713959acd65605d))
+
+* Allow `Range#include?` on TWZ ranges.
+ ([Pull Request](https://github.com/rails/rails/pull/31081))
+
+* Cache: Enable compression by default for values > 1kB.
+ ([Pull Request](https://github.com/rails/rails/pull/31147))
+
+* Redis cache store.
+ ([Pull Request](https://github.com/rails/rails/pull/31134),
+ [Pull Request](https://github.com/rails/rails/pull/31866))
+
+* Handle `TZInfo::AmbiguousTime` errors.
+ ([Pull Request](https://github.com/rails/rails/pull/31128))
+
+* MemCacheStore: Support expiring counters.
+ ([Commit](https://github.com/rails/rails/commit/b22ee64b5b30c6d5039c292235e10b24b1057f6d))
+
+* Make `ActiveSupport::TimeZone.all` return only time zones that are in
+ `ActiveSupport::TimeZone::MAPPING`.
+ ([Pull Request](https://github.com/rails/rails/pull/31176))
+
+* Changed default behaviour of `ActiveSupport::SecurityUtils.secure_compare`,
+ to make it not leak length information even for variable length string.
+ Renamed old `ActiveSupport::SecurityUtils.secure_compare` to
+ `fixed_length_secure_compare`, and started raising `ArgumentError` in
+ case of length mismatch of passed strings.
+ ([Pull Request](https://github.com/rails/rails/pull/24510))
+
+* Use SHA-1 to generate non-sensitive digests, such as the ETag header.
+ ([Pull Request](https://github.com/rails/rails/pull/31289),
+ [Pull Request](https://github.com/rails/rails/pull/31651))
+
+* `assert_changes` will always assert that the expression changes,
+ regardless of `from:` and `to:` argument combinations.
+ ([Pull Request](https://github.com/rails/rails/pull/31011))
+
+* Add missing instrumentation for `read_multi`
+ in `ActiveSupport::Cache::Store`.
+ ([Pull Request](https://github.com/rails/rails/pull/30268))
+
+* Support hash as first argument in `assert_difference`.
+ This allows to specify multiple numeric differences in the same assertion.
+ ([Pull Request](https://github.com/rails/rails/pull/31600))
+
+* Caching: MemCache and Redis `read_multi` and `fetch_multi` speedup.
+ Read from the local in-memory cache before consulting the backend.
+ ([Commit](https://github.com/rails/rails/commit/a2b97e4ffef971607a1be8fc7909f099b6840f36))
Active Job
------------
+----------
Please refer to the [Changelog][active-job] for detailed changes.
-### Removals
+### Notable changes
+
+* Allow block to be passed to `ActiveJob::Base.discard_on` to allow custom
+ handling of discard jobs.
+ ([Pull Request](https://github.com/rails/rails/pull/30622))
-ToDo
+Ruby on Rails Guides
+--------------------
+
+Please refer to the [Changelog][guides] for detailed changes.
### Notable changes
-ToDo
+* Add
+ [Threading and Code Execution in Rails](threading_and_code_execution.html)
+ Guide.
+ ([Pull Request](https://github.com/rails/rails/pull/27494))
+
+* Add [Active Storage Overview](active_storage_overview.html) Guide.
+ ([Pull Request](https://github.com/rails/rails/pull/31037))
Credits
-------
See the
-[full list of contributors to Rails](http://contributors.rubyonrails.org/) for
-the many people who spent many hours making Rails, the stable and robust
+[full list of contributors to Rails](http://contributors.rubyonrails.org/)
+for the many people who spent many hours making Rails, the stable and robust
framework it is. Kudos to all of them.
[railties]: https://github.com/rails/rails/blob/5-2-stable/railties/CHANGELOG.md
@@ -224,3 +855,4 @@ framework it is. Kudos to all of them.
[active-model]: https://github.com/rails/rails/blob/5-2-stable/activemodel/CHANGELOG.md
[active-support]: https://github.com/rails/rails/blob/5-2-stable/activesupport/CHANGELOG.md
[active-job]: https://github.com/rails/rails/blob/5-2-stable/activejob/CHANGELOG.md
+[guides]: https://github.com/rails/rails/blob/5-2-stable/guides/CHANGELOG.md
diff --git a/guides/source/action_controller_overview.md b/guides/source/action_controller_overview.md
index 6ecfb57db3..5b421756e8 100644
--- a/guides/source/action_controller_overview.md
+++ b/guides/source/action_controller_overview.md
@@ -51,7 +51,7 @@ class ClientsController < ApplicationController
end
```
-As an example, if a user goes to `/clients/new` in your application to add a new client, Rails will create an instance of `ClientsController` and call its `new` method. Note that the empty method from the example above would work just fine because Rails will by default render the `new.html.erb` view unless the action says otherwise. The `new` method could make available to the view a `@client` instance variable by creating a new `Client`:
+As an example, if a user goes to `/clients/new` in your application to add a new client, Rails will create an instance of `ClientsController` and call its `new` method. Note that the empty method from the example above would work just fine because Rails will by default render the `new.html.erb` view unless the action says otherwise. By creating a new `Client`, the `new` method can make a `@client` instance variable accessible in the view:
```ruby
def new
@@ -855,7 +855,7 @@ If you want to set custom headers for a response then `response.headers` is the
response.headers["Content-Type"] = "application/pdf"
```
-Note: in the above case it would make more sense to use the `content_type` setter directly.
+NOTE: In the above case it would make more sense to use the `content_type` setter directly.
HTTP Authentications
--------------------
diff --git a/guides/source/action_mailer_basics.md b/guides/source/action_mailer_basics.md
index fe31e3403f..662f9ea38a 100644
--- a/guides/source/action_mailer_basics.md
+++ b/guides/source/action_mailer_basics.md
@@ -20,9 +20,18 @@ Introduction
------------
Action Mailer allows you to send emails from your application using mailer classes
-and views. Mailers work very similarly to controllers. They inherit from
-`ActionMailer::Base` and live in `app/mailers`, and they have associated views
-that appear in `app/views`.
+and views.
+
+#### Mailers are similar to controllers
+
+They inherit from `ActionMailer::Base` and live in `app/mailers`. Mailers also work
+very similarly to controllers. Some examples of similarities are enumerated below.
+Mailers have:
+
+* Actions, and also, associated views that appear in `app/views`.
+* Instance variables that are accessible in views.
+* The ability to utilise layouts and partials.
+* The ability to access a params hash.
Sending Emails
--------------
@@ -60,8 +69,7 @@ end
```
As you can see, you can generate mailers just like you use other generators with
-Rails. Mailers are conceptually similar to controllers, and so we get a mailer,
-a directory for views, and a test.
+Rails.
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`:
@@ -73,10 +81,9 @@ end
#### Edit the Mailer
-Mailers are very similar to Rails controllers. They also have methods called
-"actions" and use views to structure the content. Where a controller generates
-content like HTML to send back to the client, a Mailer creates a message to be
-delivered via email.
+Mailers have methods called "actions" and they use views to structure their content.
+Where a controller generates content like HTML to send back to the client, a Mailer
+creates a message to be delivered via email.
`app/mailers/user_mailer.rb` contains an empty mailer:
@@ -110,9 +117,6 @@ messages in this class. This can be overridden on a per-email basis.
* `mail` - The actual email message, we are passing the `:to` and `:subject`
headers in.
-Just like controllers, any instance variables we define in the method become
-available for use in the views.
-
#### Create a Mailer View
Create a file called `welcome_email.html.erb` in `app/views/user_mailer/`. This
@@ -807,7 +811,7 @@ config.action_mailer.smtp_settings = {
authentication: 'plain',
enable_starttls_auto: true }
```
-Note: As of July 15, 2014, Google increased [its security measures](https://support.google.com/accounts/answer/6010255) and now blocks attempts from apps it deems less secure.
+NOTE: As of July 15, 2014, Google increased [its security measures](https://support.google.com/accounts/answer/6010255) and now blocks attempts from apps it deems less secure.
You can change your Gmail settings [here](https://www.google.com/settings/security/lesssecureapps) to allow the attempts. If your Gmail account has 2-factor authentication enabled,
then you will need to set an [app password](https://myaccount.google.com/apppasswords) and use that instead of your regular password. Alternatively, you can
use another ESP to send email by replacing 'smtp.gmail.com' above with the address of your provider.
diff --git a/guides/source/action_view_overview.md b/guides/source/action_view_overview.md
index 1cba5c6fb6..c01d1082b6 100644
--- a/guides/source/action_view_overview.md
+++ b/guides/source/action_view_overview.md
@@ -1102,7 +1102,7 @@ Possible output:
</optgroup>
```
-Note: Only the `optgroup` and `option` tags are returned, so you still have to wrap the output in an appropriate `select` tag.
+NOTE: Only the `optgroup` and `option` tags are returned, so you still have to wrap the output in an appropriate `select` tag.
#### options_for_select
@@ -1113,7 +1113,7 @@ options_for_select([ "VISA", "MasterCard" ])
# => <option>VISA</option> <option>MasterCard</option>
```
-Note: Only the `option` tags are returned, you have to wrap this call in a regular HTML `select` tag.
+NOTE: Only the `option` tags are returned, you have to wrap this call in a regular HTML `select` tag.
#### options_from_collection_for_select
@@ -1130,7 +1130,7 @@ options_from_collection_for_select(@project.people, "id", "name")
# => <option value="#{person.id}">#{person.name}</option>
```
-Note: Only the `option` tags are returned, you have to wrap this call in a regular HTML `select` tag.
+NOTE: Only the `option` tags are returned, you have to wrap this call in a regular HTML `select` tag.
#### select
diff --git a/guides/source/active_job_basics.md b/guides/source/active_job_basics.md
index 97d98efba0..6d52ac0a99 100644
--- a/guides/source/active_job_basics.md
+++ b/guides/source/active_job_basics.md
@@ -147,7 +147,7 @@ class GuestsCleanupJob < ApplicationJob
#....
end
-# Now your job will use `resque` as it's backend queue adapter overriding what
+# Now your job will use `resque` as its backend queue adapter overriding what
# was configured in `config.active_job.queue_adapter`.
```
@@ -290,7 +290,7 @@ For example, you could send metrics for every job enqueued:
```ruby
class ApplicationJob
- before_enqueue { |job| $statsd.increment "#{job.name.underscore}.enqueue" }
+ before_enqueue { |job| $statsd.increment "#{job.class.name.underscore}.enqueue" }
end
```
diff --git a/guides/source/active_record_migrations.md b/guides/source/active_record_migrations.md
index ab3af438f5..5be514c786 100644
--- a/guides/source/active_record_migrations.md
+++ b/guides/source/active_record_migrations.md
@@ -442,7 +442,7 @@ change_column_default :products, :approved, from: true, to: false
This sets `:name` field on products to a `NOT NULL` column and the default
value of the `:approved` field from true to false.
-Note: You could also write the above `change_column_default` migration as
+NOTE: You could also write the above `change_column_default` migration as
`change_column_default :products, :approved, false`, but unlike the previous
example, this would make your migration irreversible.
diff --git a/guides/source/active_storage_overview.md b/guides/source/active_storage_overview.md
index 014fd57052..d67f65e88a 100644
--- a/guides/source/active_storage_overview.md
+++ b/guides/source/active_storage_overview.md
@@ -114,6 +114,13 @@ gem "aws-sdk-s3", require: false
NOTE: The core features of Active Storage require the following permissions: `s3:ListBucket`, `s3:PutObject`, `s3:GetObject`, and `s3:DeleteObject`. If you have additional upload options configured such as setting ACLs then additional permissions may be required.
+NOTE: If you want to use environment variables, standard SDK configuration files, profiles,
+IAM instance profiles or task roles, you can omit the `access_key_id`, `secret_access_key`,
+and `region` keys in the example above. The Amazon S3 Service supports all of the
+authentication options described in the [AWS SDK documentation]
+(https://docs.aws.amazon.com/sdk-for-ruby/v3/developer-guide/setup-config.html).
+
+
### Microsoft Azure Storage Service
Declare an Azure Storage service in `config/storage.yml`:
@@ -121,7 +128,6 @@ Declare an Azure Storage service in `config/storage.yml`:
```yaml
azure:
service: AzureStorage
- path: ""
storage_account_name: ""
storage_access_key: ""
container: ""
diff --git a/guides/source/asset_pipeline.md b/guides/source/asset_pipeline.md
index 618896d458..2f5854fed0 100644
--- a/guides/source/asset_pipeline.md
+++ b/guides/source/asset_pipeline.md
@@ -917,7 +917,7 @@ config.action_controller.asset_host = ENV['CDN_HOST']
-Note: You would need to set `CDN_HOST` on your server to `mycdnsubdomain
+NOTE: You would need to set `CDN_HOST` on your server to `mycdnsubdomain
.fictional-cdn.com` for this to work.
Once you have configured your server and your CDN when you serve a webpage that
diff --git a/guides/source/caching_with_rails.md b/guides/source/caching_with_rails.md
index 5dde6f34fa..3f357b532b 100644
--- a/guides/source/caching_with_rails.md
+++ b/guides/source/caching_with_rails.md
@@ -446,30 +446,28 @@ config.cache_store = :mem_cache_store, "cache-1.example.com", "cache-2.example.c
### ActiveSupport::Cache::RedisCacheStore
-The Redis cache store takes advantage of Redis support for least-recently-used
-and least-frequently-used key eviction when it reaches max memory, allowing it
-to behave much like a Memcached cache server.
+The Redis cache store takes advantage of Redis support for automatic eviction
+when it reaches max memory, allowing it to behave much like a Memcached cache server.
Deployment note: Redis doesn't expire keys by default, so take care to use a
dedicated Redis cache server. Don't fill up your persistent-Redis server with
volatile cache data! Read the
[Redis cache server setup guide](https://redis.io/topics/lru-cache) in detail.
-For an all-cache Redis server, set `maxmemory-policy` to an `allkeys` policy.
-Redis 4+ support least-frequently-used (`allkeys-lfu`) eviction, an excellent
-default choice. Redis 3 and earlier should use `allkeys-lru` for
-least-recently-used eviction.
+For a cache-only Redis server, set `maxmemory-policy` to one of the variants of allkeys.
+Redis 4+ supports least-frequently-used eviction (`allkeys-lfu`), an excellent
+default choice. Redis 3 and earlier should use least-recently-used eviction (`allkeys-lru`).
Set cache read and write timeouts relatively low. Regenerating a cached value
is often faster than waiting more than a second to retrieve it. Both read and
write timeouts default to 1 second, but may be set lower if your network is
-consistently low latency.
+consistently low-latency.
By default, the cache store will not attempt to reconnect to Redis if the
connection fails during a request. If you experience frequent disconnects you
may wish to enable reconnect attempts.
-Cache reads and writes never raise exceptions. They just return `nil` instead,
+Cache reads and writes never raise exceptions; they just return `nil` instead,
behaving as if there was nothing in the cache. To gauge whether your cache is
hitting exceptions, you may provide an `error_handler` to report to an
exception gathering service. It must accept three keyword arguments: `method`,
@@ -477,12 +475,33 @@ the cache store method that was originally called; `returning`, the value that
was returned to the user, typically `nil`; and `exception`, the exception that
was rescued.
-Putting it all together, a production Redis cache store may look something
-like this:
+To get started, add the redis gem to your Gemfile:
```ruby
-cache_servers = %w[ "redis://cache-01:6379/0", "redis://cache-02:6379/0", … ],
-config.cache_store = :redis_cache_store, url: cache_servers,
+gem 'redis'
+```
+
+You can enable support for the faster [hiredis](https://github.com/redis/hiredis)
+connection library by additionally adding its ruby wrapper to your Gemfile:
+
+```ruby
+gem 'hiredis'
+```
+
+Redis cache store will automatically require & use hiredis if available. No further
+configuration is needed.
+
+Finally, add the configuration in the relevant `config/environments/*.rb` file:
+
+```ruby
+config.cache_store = :redis_cache_store, { url: ENV['REDIS_URL'] }
+```
+
+A more complex, production Redis cache store may look something like this:
+
+```ruby
+cache_servers = %w(redis://cache-01:6379/0 redis://cache-02:6379/0)
+config.cache_store = :redis_cache_store, { url: cache_servers,
connect_timeout: 30, # Defaults to 20 seconds
read_timeout: 0.2, # Defaults to 1 second
@@ -491,9 +510,10 @@ config.cache_store = :redis_cache_store, url: cache_servers,
error_handler: -> (method:, returning:, exception:) {
# Report errors to Sentry as warnings
- Raven.capture_exception exception, level: 'warning",
+ Raven.capture_exception exception, level: 'warning',
tags: { method: method, returning: returning }
}
+}
```
### ActiveSupport::Cache::NullStore
diff --git a/guides/source/configuring.md b/guides/source/configuring.md
index a87b8a2f48..368b74f708 100644
--- a/guides/source/configuring.md
+++ b/guides/source/configuring.md
@@ -502,6 +502,10 @@ Defaults to `'signed cookie'`.
* `config.action_dispatch.cookies_rotations` allows rotating
secrets, ciphers, and digests for encrypted and signed cookies.
+* `config.action_dispatch.use_authenticated_cookie_encryption` controls whether
+ signed and encrypted cookies use the AES-256-GCM cipher or
+ the older AES-256-CBC cipher. It defaults to `true`.
+
* `config.action_dispatch.perform_deep_munge` configures whether `deep_munge`
method should be performed on the parameters. See [Security Guide](security.html#unsafe-query-generation)
for more information. It defaults to `true`.
diff --git a/guides/source/getting_started.md b/guides/source/getting_started.md
index f545b90103..5b6cfe6659 100644
--- a/guides/source/getting_started.md
+++ b/guides/source/getting_started.md
@@ -1125,7 +1125,7 @@ TIP: Rails automatically wraps fields that contain an error with a div
with class `field_with_errors`. You can define a CSS rule to make them
standout.
-Now you'll get a nice error message when saving an article without title when
+Now you'll get a nice error message when saving an article without a title when
you attempt to do just that on the new article form
<http://localhost:3000/articles/new>:
@@ -1522,7 +1522,7 @@ comments on articles.
### Generating a Model
We're going to see the same generator that we used before when creating
-the `Article` model. This time we'll create a `Comment` model to hold
+the `Article` model. This time we'll create a `Comment` model to hold a
reference to an article. Run this command in your terminal:
```bash
@@ -1857,7 +1857,7 @@ This will now render the partial in `app/views/comments/_comment.html.erb` once
for each comment that is in the `@article.comments` collection. As the `render`
method iterates over the `@article.comments` collection, it assigns each
comment to a local variable named the same as the partial, in this case
-`comment` which is then available in the partial for us to show.
+`comment`, which is then available in the partial for us to show.
### Rendering a Partial Form
@@ -2060,7 +2060,7 @@ What's Next?
Now that you've seen your first Rails application, you should feel free to
update it and experiment on your own.
-Remember you don't have to do everything without help. As you need assistance
+Remember, you don't have to do everything without help. As you need assistance
getting up and running with Rails, feel free to consult these support
resources:
diff --git a/guides/source/i18n.md b/guides/source/i18n.md
index 2b545e6b82..339b356a78 100644
--- a/guides/source/i18n.md
+++ b/guides/source/i18n.md
@@ -42,6 +42,8 @@ Internationalization is a complex problem. Natural languages differ in so many w
As part of this solution, **every static string in the Rails framework** - e.g. Active Record validation messages, time and date formats - **has been internationalized**. _Localization_ of a Rails application means defining translated values for these strings in desired languages.
+To localize store and update _content_ in your application (e.g. translate blog posts), see the [Translating model content](#translating-model-content) section.
+
### The Overall Architecture of the Library
Thus, the Ruby I18n gem is split into two parts:
@@ -105,7 +107,7 @@ This means, that in the `:en` locale, the key _hello_ will map to the _Hello wor
The I18n library will use **English** as a **default locale**, i.e. if a different locale is not set, `:en` will be used for looking up translations.
-NOTE: The i18n library takes a **pragmatic approach** to locale keys (after [some discussion](https://groups.google.com/forum/#!topic/rails-i18n/FN7eLH2-lHA)), including only the _locale_ ("language") part, like `:en`, `:pl`, not the _region_ part, like `:en-US` or `:en-GB`, which are traditionally used for separating "languages" and "regional setting" or "dialects". Many international applications use only the "language" element of a locale such as `:cs`, `:th` or `:es` (for Czech, Thai and Spanish). However, there are also regional differences within different language groups that may be important. For instance, in the `:en-US` locale you would have $ as a currency symbol, while in `:en-GB`, you would have £. Nothing stops you from separating regional and other settings in this way: you just have to provide full "English - United Kingdom" locale in a `:en-GB` dictionary. Few gems such as [Globalize3](https://github.com/globalize/globalize) may help you implement it.
+NOTE: The i18n library takes a **pragmatic approach** to locale keys (after [some discussion](https://groups.google.com/forum/#!topic/rails-i18n/FN7eLH2-lHA)), including only the _locale_ ("language") part, like `:en`, `:pl`, not the _region_ part, like `:en-US` or `:en-GB`, which are traditionally used for separating "languages" and "regional setting" or "dialects". Many international applications use only the "language" element of a locale such as `:cs`, `:th` or `:es` (for Czech, Thai and Spanish). However, there are also regional differences within different language groups that may be important. For instance, in the `:en-US` locale you would have $ as a currency symbol, while in `:en-GB`, you would have £. Nothing stops you from separating regional and other settings in this way: you just have to provide full "English - United Kingdom" locale in a `:en-GB` dictionary.
The **translations load path** (`I18n.load_path`) is an array of paths to files that will be loaded automatically. Configuring this path allows for customization of translations directory structure and file naming scheme.
@@ -1099,13 +1101,11 @@ Customize your I18n Setup
For several reasons the Simple backend shipped with Active Support only does the "simplest thing that could possibly work" _for Ruby on Rails_[^3] ... which means that it is only guaranteed to work for English and, as a side effect, languages that are very similar to English. Also, the simple backend is only capable of reading translations but cannot dynamically store them to any format.
-That does not mean you're stuck with these limitations, though. The Ruby I18n gem makes it very easy to exchange the Simple backend implementation with something else that fits better for your needs. E.g. you could exchange it with Globalize's Static backend:
+That does not mean you're stuck with these limitations, though. The Ruby I18n gem makes it very easy to exchange the Simple backend implementation with something else that fits better for your needs, by passing a backend instance to the `I18n.backend=` setter.
-```ruby
-I18n.backend = Globalize::Backend::Static.new
-```
+For example, you can replace the Simple backend with the the Chain backend to chain multiple backends together. This is useful when you want to use standard translations with a Simple backend but store custom application translations in a database or other backends.
-You can also use the Chain backend to chain multiple backends together. This is useful when you want to use standard translations with a Simple backend but store custom application translations in a database or other backends. For example, you could use the Active Record backend and fall back to the (default) Simple backend:
+With the Chain backend, you could use the Active Record backend and fall back to the (default) Simple backend:
```ruby
I18n.backend = I18n::Backend::Chain.new(I18n::Backend::ActiveRecord.new, I18n.backend)
@@ -1166,13 +1166,22 @@ To do so, the helper forces `I18n#translate` to raise exceptions no matter what
I18n.t :foo, raise: true # always re-raises exceptions from the backend
```
+Translating Model Content
+-------------------------
+
+The I18n API described in this guide is primarily intended for translating interface strings. If you are looking to translate model content (e.g. blog posts), you will need a different solution to help with this.
+
+Several gems can help with this:
+
+* [Globalize](https://github.com/globalize/globalize): Store translations on separate translation tables, one for each translated model
+* [Mobility](https://github.com/shioyama/mobility): Provides support for storing translations in many formats, including translation tables, json columns (Postgres), etc.
+* [Traco](https://github.com/barsoom/traco): Translatable columns for Rails 3 and 4, stored in the model table itself
+
Conclusion
----------
At this point you should have a good overview about how I18n support in Ruby on Rails works and are ready to start translating your project.
-If you want to discuss certain portions or have questions, please sign up to the [rails-i18n mailing list](https://groups.google.com/forum/#!forum/rails-i18n).
-
Contributing to Rails I18n
--------------------------
diff --git a/guides/source/routing.md b/guides/source/routing.md
index 98aa841938..3cf5b56340 100644
--- a/guides/source/routing.md
+++ b/guides/source/routing.md
@@ -58,6 +58,26 @@ and this in the corresponding view:
then the router will generate the path `/patients/17`. This reduces the brittleness of your view and makes your code easier to understand. Note that the id does not need to be specified in the route helper.
+### Configuring the Rails Router
+
+The routes for your application or engine live in the file `config/routes.rb` and typically looks like this:
+
+```ruby
+Rails.application.routes.draw do
+ resources :brands, only: [:index, :show] do
+ resources :products, only: [:index, :show]
+ end
+
+ resource :basket, only: [:show, :update, :destroy]
+
+ resolve("Basket") { route_for(:basket) }
+end
+```
+
+Since this is a regular Ruby source file you can use all of its features to help you define your routes but be careful with variable names as they can clash with the DSL methods of the router.
+
+NOTE: The `Rails.application.routes.draw do ... end` block that wraps your route definitions is required to establish the scope for the router DSL and must not be deleted.
+
Resource Routing: the Rails Default
-----------------------------------
diff --git a/guides/source/security.md b/guides/source/security.md
index 28ddbdc26a..b419f7b48d 100644
--- a/guides/source/security.md
+++ b/guides/source/security.md
@@ -325,7 +325,7 @@ Or the attacker places the code into the onmouseover event handler of an image:
There are many other possibilities, like using a `<script>` tag to make a cross-site request to a URL with a JSONP or JavaScript response. The response is executable code that the attacker can find a way to run, possibly extracting sensitive data. To protect against this data leakage, we must disallow cross-site `<script>` tags. Ajax requests, however, obey the browser's same-origin policy (only your own site is allowed to initiate `XmlHttpRequest`) so we can safely allow them to return JavaScript responses.
-Note: We can't distinguish a `<script>` tag's origin—whether it's a tag on your own site or on some other malicious site—so we must block all `<script>` across the board, even if it's actually a safe same-origin script served from your own site. In these cases, explicitly skip CSRF protection on actions that serve JavaScript meant for a `<script>` tag.
+NOTE: We can't distinguish a `<script>` tag's origin—whether it's a tag on your own site or on some other malicious site—so we must block all `<script>` across the board, even if it's actually a safe same-origin script served from your own site. In these cases, explicitly skip CSRF protection on actions that serve JavaScript meant for a `<script>` tag.
To protect against all other forged requests, we introduce a _required security token_ that our site knows but other sites don't know. We include the security token in requests and verify it on the server. This is a one-liner in your application controller, and is the default for newly created Rails applications:
@@ -1089,6 +1089,112 @@ Here is a list of common headers:
* **Access-Control-Allow-Origin:** Used to control which sites are allowed to bypass same origin policies and send cross-origin requests.
* **Strict-Transport-Security:** [Used to control if the browser is allowed to only access a site over a secure connection](https://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security)
+### Content Security Policy
+
+Rails provides a DSL that allows you to configure a
+[Content Security Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy)
+for your application. You can configure a global default policy and then
+override it on a per-resource basis and even use lambdas to inject per-request
+values into the header such as account subdomains in a multi-tenant application.
+
+Example global policy:
+
+```ruby
+# config/initializers/content_security_policy.rb
+Rails.application.config.content_security_policy do |policy|
+ policy.default_src :self, :https
+ policy.font_src :self, :https, :data
+ policy.img_src :self, :https, :data
+ policy.object_src :none
+ policy.script_src :self, :https
+ policy.style_src :self, :https
+
+ # Specify URI for violation reports
+ policy.report_uri "/csp-violation-report-endpoint"
+end
+```
+
+Example controller overrides:
+
+```ruby
+# Override policy inline
+class PostsController < ApplicationController
+ content_security_policy do |p|
+ p.upgrade_insecure_requests true
+ end
+end
+
+# Using literal values
+class PostsController < ApplicationController
+ content_security_policy do |p|
+ p.base_uri "https://www.example.com"
+ end
+end
+
+# Using mixed static and dynamic values
+class PostsController < ApplicationController
+ content_security_policy do |p|
+ p.base_uri :self, -> { "https://#{current_user.domain}.example.com" }
+ end
+end
+
+# Disabling the global CSP
+class LegacyPagesController < ApplicationController
+ content_security_policy false, only: :index
+end
+```
+
+Use the `content_security_policy_report_only`
+configuration attribute to set
+[Content-Security-Policy-Report-Only](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only)
+in order to report only content violations for migrating
+legacy content
+
+```ruby
+# config/initializers/content_security_policy.rb
+Rails.application.config.content_security_policy_report_only = true
+```
+
+```ruby
+# Controller override
+class PostsController < ApplicationController
+ content_security_policy_report_only only: :index
+end
+```
+
+You can enable automatic nonce generation:
+
+```ruby
+# config/initializers/content_security_policy.rb
+Rails.application.config.content_security_policy do |policy|
+ policy.script_src :self, :https
+end
+
+Rails.application.config.content_security_policy_nonce_generator = -> request { SecureRandom.base64(16) }
+```
+
+Then you can add an automatic nonce value by passing `nonce: true`
+as part of `html_options`. Example:
+
+```html+erb
+<%= javascript_tag nonce: true do -%>
+ alert('Hello, World!');
+<% end -%>
+```
+
+Use [`csp_meta_tag`](http://api.rubyonrails.org/classes/ActionView/Helpers/CspHelper.html#method-i-csp_meta_tag)
+helper to create a meta tag "csp-nonce" with the per-session nonce value
+for allowing inline `<script>` tags.
+
+```html+erb
+<head>
+ <%= csp_meta_tag %>
+</head>
+```
+
+This is used by the Rails UJS helper to create dynamically
+loaded inline `<script>` elements.
+
Environmental Security
----------------------
diff --git a/guides/source/upgrading_ruby_on_rails.md b/guides/source/upgrading_ruby_on_rails.md
index ff7ccfd47d..d5dfaef591 100644
--- a/guides/source/upgrading_ruby_on_rails.md
+++ b/guides/source/upgrading_ruby_on_rails.md
@@ -77,6 +77,16 @@ Rails 5.2 adds bootsnap gem in the [newly generated app's Gemfile](https://githu
The `app:update` task sets it up in `boot.rb`. If you want to use it, then add it in the Gemfile,
otherwise change the `boot.rb` to not use bootsnap.
+### Expiry in signed or encrypted cookie is now embedded in the cookies values
+
+To improve security, Rails now embeds the expiry information also in encrypted or signed cookies value.
+
+This new embed information make those cookies incompatible with versions of Rails older than 5.2.
+
+If you require your cookies to be read by 5.1 and older, or you are still validating your 5.2 deploy and want
+to allow you to rollback set
+`Rails.application.config.action_dispatch.use_authenticated_cookie_encryption` to `false`.
+
Upgrading from Rails 5.0 to Rails 5.1
-------------------------------------
@@ -660,7 +670,7 @@ xhr :get, :index, format: :js
to explicitly test an `XmlHttpRequest`.
-Note: Your own `<script>` tags are treated as cross-origin and blocked by
+NOTE: Your own `<script>` tags are treated as cross-origin and blocked by
default, too. If you really mean to load JavaScript from `<script>` tags,
you must now explicitly skip CSRF protection on those actions.
diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md
index 335eb6c4e3..3b4b5330f7 100644
--- a/railties/CHANGELOG.md
+++ b/railties/CHANGELOG.md
@@ -1,5 +1,3 @@
-## Rails 6.0.0.alpha (Unreleased) ##
-
* Deprecate passing Rack server name as a regular argument to `rails server`.
Previously:
diff --git a/railties/lib/rails/application/configuration.rb b/railties/lib/rails/application/configuration.rb
index 912faed3e4..f80da4b005 100644
--- a/railties/lib/rails/application/configuration.rb
+++ b/railties/lib/rails/application/configuration.rb
@@ -166,6 +166,18 @@ module Rails
end
end
+ # Loads the database YAML without evaluating ERB. People seem to
+ # write ERB that makes the database configuration depend on
+ # Rails configuration. But we want Rails configuration (specifically
+ # `rake` and `rails` tasks) to be generated based on information in
+ # the database yaml, so we need a method that loads the database
+ # yaml *without* the context of the Rails application.
+ def load_database_yaml # :nodoc:
+ path = paths["config/database"].existent.first
+ return {} unless path
+ YAML.load_file(path.to_s)
+ end
+
# Loads and returns the entire raw configuration of database from
# values stored in <tt>config/database.yml</tt>.
def database_configuration
diff --git a/railties/lib/rails/application_controller.rb b/railties/lib/rails/application_controller.rb
index fa8793d81a..b3fe822218 100644
--- a/railties/lib/rails/application_controller.rb
+++ b/railties/lib/rails/application_controller.rb
@@ -4,6 +4,13 @@ class Rails::ApplicationController < ActionController::Base # :nodoc:
self.view_paths = File.expand_path("templates", __dir__)
layout "application"
+ before_action :disable_content_security_policy_nonce!
+
+ content_security_policy do |policy|
+ policy.script_src :unsafe_inline
+ policy.style_src :unsafe_inline
+ end
+
private
def require_local!
@@ -15,4 +22,8 @@ class Rails::ApplicationController < ActionController::Base # :nodoc:
def local_request?
Rails.application.config.consider_all_requests_local || request.local?
end
+
+ def disable_content_security_policy_nonce!
+ request.content_security_policy_nonce_generator = nil
+ end
end
diff --git a/railties/lib/rails/commands/dbconsole/dbconsole_command.rb b/railties/lib/rails/commands/dbconsole/dbconsole_command.rb
index 8df548b5de..806b7de6d6 100644
--- a/railties/lib/rails/commands/dbconsole/dbconsole_command.rb
+++ b/railties/lib/rails/commands/dbconsole/dbconsole_command.rb
@@ -97,7 +97,7 @@ module Rails
elsif configurations[environment].blank? && configurations[connection].blank?
raise ActiveRecord::AdapterNotSpecified, "'#{environment}' database is not configured. Available configuration: #{configurations.inspect}"
else
- configurations[environment].presence || configurations[connection]
+ configurations[connection] || configurations[environment].presence
end
end
end
diff --git a/railties/lib/rails/commands/routes/routes_command.rb b/railties/lib/rails/commands/routes/routes_command.rb
index c4f3717095..b592a5212f 100644
--- a/railties/lib/rails/commands/routes/routes_command.rb
+++ b/railties/lib/rails/commands/routes/routes_command.rb
@@ -5,45 +5,33 @@ require "rails/command"
module Rails
module Command
class RoutesCommand < Base # :nodoc:
- class_option :controller, aliases: "-c", type: :string, desc: "Specifies the controller."
- class_option :grep_pattern, aliases: "-g", type: :string, desc: "Specifies grep pattern."
- class_option :expanded_format, aliases: "--expanded", type: :string, desc: "Turn on expanded format mode."
-
- no_commands do
- def help
- say "Usage: Print out all defined routes in match order, with names."
- say ""
- say "Target specific controller with -c option, or grep routes using -g option"
- say "Use expanded format with --expanded option"
- say ""
- end
- end
+ class_option :controller, aliases: "-c", desc: "Filter by a specific controller, e.g. PostsController or Admin::PostsController."
+ class_option :grep, aliases: "-g", desc: "Grep routes by a specific pattern."
+ class_option :expanded, type: :boolean, aliases: "-E", desc: "Print routes expanded vertically with parts explained."
def perform(*)
require_application_and_environment!
require "action_dispatch/routing/inspector"
- all_routes = Rails.application.routes.routes
- inspector = ActionDispatch::Routing::RoutesInspector.new(all_routes)
-
- if options.has_key?("expanded_format")
- say inspector.format(ActionDispatch::Routing::ConsoleFormatter::Expanded.new, routes_filter)
- else
- say inspector.format(ActionDispatch::Routing::ConsoleFormatter::Sheet.new, routes_filter)
- end
+ say inspector.format(formatter, routes_filter)
end
private
+ def inspector
+ ActionDispatch::Routing::RoutesInspector.new(Rails.application.routes.routes)
+ end
- def routes_filter
- if options.has_key?("controller")
- { controller: options["controller"] }
- elsif options.has_key?("grep_pattern")
- options["grep_pattern"]
+ def formatter
+ if options.key?("expanded")
+ ActionDispatch::Routing::ConsoleFormatter::Expanded.new
else
- nil
+ ActionDispatch::Routing::ConsoleFormatter::Sheet.new
end
end
+
+ def routes_filter
+ options.symbolize_keys.slice(:controller, :grep)
+ end
end
end
end
diff --git a/railties/lib/rails/commands/server/server_command.rb b/railties/lib/rails/commands/server/server_command.rb
index 64fb912b51..8588e2fd64 100644
--- a/railties/lib/rails/commands/server/server_command.rb
+++ b/railties/lib/rails/commands/server/server_command.rb
@@ -43,15 +43,15 @@ module Rails
ENV["RAILS_ENV"] ||= options[:environment]
end
- def start
+ def start(after_stop_callback = nil)
trap(:INT) { exit }
create_tmp_directories
setup_dev_caching
log_to_stdout if options[:log_stdout]
- super
+ super()
ensure
- yield
+ after_stop_callback.call if after_stop_callback
end
def serveable? # :nodoc:
@@ -157,9 +157,8 @@ module Rails
if server.serveable?
print_boot_information(server.server, server.served_url)
- server.start do
- say "Exiting" unless options[:daemon]
- end
+ after_stop_callback = -> { say "Exiting" unless options[:daemon] }
+ server.start(after_stop_callback)
else
say rack_server_suggestion(using)
end
diff --git a/railties/lib/rails/generators/app_base.rb b/railties/lib/rails/generators/app_base.rb
index e1889979d7..8c5d872573 100644
--- a/railties/lib/rails/generators/app_base.rb
+++ b/railties/lib/rails/generators/app_base.rb
@@ -299,7 +299,7 @@ module Rails
def gem_for_database
# %w( mysql postgresql sqlite3 oracle frontbase ibm_db sqlserver jdbcmysql jdbcsqlite3 jdbcpostgresql )
case options[:database]
- when "mysql" then ["mysql2", ["~> 0.4.4"]]
+ when "mysql" then ["mysql2", [">= 0.4.4", "< 0.6.0"]]
when "postgresql" then ["pg", [">= 0.18", "< 2.0"]]
when "oracle" then ["activerecord-oracle_enhanced-adapter", nil]
when "frontbase" then ["ruby-frontbase", nil]
diff --git a/railties/lib/rails/generators/rails/app/templates/config/storage.yml.tt b/railties/lib/rails/generators/rails/app/templates/config/storage.yml.tt
index 1c0cde0b09..7207c75086 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/storage.yml.tt
+++ b/railties/lib/rails/generators/rails/app/templates/config/storage.yml.tt
@@ -24,7 +24,6 @@ local:
# Use rails credentials:edit to set the Azure Storage secret (as azure_storage:storage_access_key)
# microsoft:
# service: AzureStorage
-# path: your_azure_storage_path
# storage_account_name: your_account_name
# storage_access_key: <%%= Rails.application.credentials.dig(:azure_storage, :storage_access_key) %>
# container: your_container_name
diff --git a/railties/test/application/asset_debugging_test.rb b/railties/test/application/asset_debugging_test.rb
index e56c7b958e..3e0f31860b 100644
--- a/railties/test/application/asset_debugging_test.rb
+++ b/railties/test/application/asset_debugging_test.rb
@@ -77,7 +77,8 @@ module ApplicationTests
stylesheet_link_tag: %r{<link rel="stylesheet" media="screen" href="/stylesheets/#{contents}.css" />},
javascript_include_tag: %r{<script src="/javascripts/#{contents}.js">},
audio_tag: %r{<audio src="/audios/#{contents}"></audio>},
- video_tag: %r{<video src="/videos/#{contents}"></video>}
+ video_tag: %r{<video src="/videos/#{contents}"></video>},
+ image_submit_tag: %r{<input type="image" src="/images/#{contents}" />}
}
cases.each do |(view_method, tag_match)|
diff --git a/railties/test/commands/routes_test.rb b/railties/test/commands/routes_test.rb
index b45fb05a6f..77ed2bda61 100644
--- a/railties/test/commands/routes_test.rb
+++ b/railties/test/commands/routes_test.rb
@@ -3,6 +3,7 @@
require "isolation/abstract_unit"
require "rails/command"
require "rails/commands/routes/routes_command"
+require "io/console/size"
class Rails::Command::RoutesTest < ActiveSupport::TestCase
setup :build_app
@@ -39,18 +40,18 @@ class Rails::Command::RoutesTest < ActiveSupport::TestCase
output = run_routes_command(["-g", "show"])
assert_equal <<~MESSAGE, output
- Prefix Verb URI Pattern Controller#Action
- cart GET /cart(.:format) cart#show
- rails_service_blob GET /rails/active_storage/blobs/:signed_id/*filename(.:format) active_storage/blobs#show
- rails_blob_representation GET /rails/active_storage/representations/:signed_blob_id/:variation_key/*filename(.:format) active_storage/representations#show
- rails_disk_service GET /rails/active_storage/disk/:encoded_key/*filename(.:format) active_storage/disk#show
+ Prefix Verb URI Pattern Controller#Action
+ cart GET /cart(.:format) cart#show
+ rails_service_blob GET /rails/active_storage/blobs/:signed_id/*filename(.:format) active_storage/blobs#show
+ rails_blob_representation GET /rails/active_storage/representations/:signed_blob_id/:variation_key/*filename(.:format) active_storage/representations#show
+ rails_disk_service GET /rails/active_storage/disk/:encoded_key/*filename(.:format) active_storage/disk#show
MESSAGE
output = run_routes_command(["-g", "POST"])
assert_equal <<~MESSAGE, output
- Prefix Verb URI Pattern Controller#Action
- POST /cart(.:format) cart#create
- rails_direct_uploads POST /rails/active_storage/direct_uploads(.:format) active_storage/direct_uploads#create
+ Prefix Verb URI Pattern Controller#Action
+ POST /cart(.:format) cart#create
+ rails_direct_uploads POST /rails/active_storage/direct_uploads(.:format) active_storage/direct_uploads#create
MESSAGE
output = run_routes_command(["-g", "basketballs"])
@@ -60,10 +61,10 @@ class Rails::Command::RoutesTest < ActiveSupport::TestCase
test "rails routes with controller search key" do
app_file "config/routes.rb", <<-RUBY
- Rails.application.routes.draw do
- get '/cart', to: 'cart#show'
- get '/basketball', to: 'basketball#index'
- end
+ Rails.application.routes.draw do
+ get '/cart', to: 'cart#show'
+ get '/basketball', to: 'basketball#index'
+ end
RUBY
output = run_routes_command(["-c", "cart"])
@@ -78,12 +79,13 @@ class Rails::Command::RoutesTest < ActiveSupport::TestCase
test "rails routes with namespaced controller search key" do
app_file "config/routes.rb", <<-RUBY
- Rails.application.routes.draw do
- namespace :admin do
- resource :post
- end
+ Rails.application.routes.draw do
+ namespace :admin do
+ resource :post
end
+ end
RUBY
+
expected_output = [" Prefix Verb URI Pattern Controller#Action",
" new_admin_post GET /admin/post/new(.:format) admin/posts#new",
"edit_admin_post GET /admin/post/edit(.:format) admin/posts#edit",
@@ -102,60 +104,68 @@ class Rails::Command::RoutesTest < ActiveSupport::TestCase
test "rails routes displays message when no routes are defined" do
app_file "config/routes.rb", <<-RUBY
- Rails.application.routes.draw do
- end
+ Rails.application.routes.draw do
+ end
RUBY
assert_equal <<~MESSAGE, run_routes_command
- Prefix Verb URI Pattern Controller#Action
- rails_service_blob GET /rails/active_storage/blobs/:signed_id/*filename(.:format) active_storage/blobs#show
- rails_blob_representation GET /rails/active_storage/representations/:signed_blob_id/:variation_key/*filename(.:format) active_storage/representations#show
- rails_disk_service GET /rails/active_storage/disk/:encoded_key/*filename(.:format) active_storage/disk#show
- update_rails_disk_service PUT /rails/active_storage/disk/:encoded_token(.:format) active_storage/disk#update
- rails_direct_uploads POST /rails/active_storage/direct_uploads(.:format) active_storage/direct_uploads#create
+ Prefix Verb URI Pattern Controller#Action
+ rails_service_blob GET /rails/active_storage/blobs/:signed_id/*filename(.:format) active_storage/blobs#show
+ rails_blob_representation GET /rails/active_storage/representations/:signed_blob_id/:variation_key/*filename(.:format) active_storage/representations#show
+ rails_disk_service GET /rails/active_storage/disk/:encoded_key/*filename(.:format) active_storage/disk#show
+ update_rails_disk_service PUT /rails/active_storage/disk/:encoded_token(.:format) active_storage/disk#update
+ rails_direct_uploads POST /rails/active_storage/direct_uploads(.:format) active_storage/direct_uploads#create
MESSAGE
end
test "rails routes with expanded option" do
- app_file "config/routes.rb", <<-RUBY
+ begin
+ previous_console_winsize = IO.console.winsize
+ IO.console.winsize = [0, 27]
+
+ app_file "config/routes.rb", <<-RUBY
Rails.application.routes.draw do
get '/cart', to: 'cart#show'
end
- RUBY
-
- output = rails("routes", "--expanded")
- assert_equal <<~MESSAGE, output
- --[ Route 1 ]------------------------------------------------------------
- Prefix | cart
- Verb | GET
- URI | /cart(.:format)
- Controller#Action | cart#show
- --[ Route 2 ]------------------------------------------------------------
- Prefix | rails_service_blob
- Verb | GET
- URI | /rails/active_storage/blobs/:signed_id/*filename(.:format)
- Controller#Action | active_storage/blobs#show
- --[ Route 3 ]------------------------------------------------------------
- Prefix | rails_blob_representation
- Verb | GET
- URI | /rails/active_storage/representations/:signed_blob_id/:variation_key/*filename(.:format)
- Controller#Action | active_storage/representations#show
- --[ Route 4 ]------------------------------------------------------------
- Prefix | rails_disk_service
- Verb | GET
- URI | /rails/active_storage/disk/:encoded_key/*filename(.:format)
- Controller#Action | active_storage/disk#show
- --[ Route 5 ]------------------------------------------------------------
- Prefix | update_rails_disk_service
- Verb | PUT
- URI | /rails/active_storage/disk/:encoded_token(.:format)
- Controller#Action | active_storage/disk#update
- --[ Route 6 ]------------------------------------------------------------
- Prefix | rails_direct_uploads
- Verb | POST
- URI | /rails/active_storage/direct_uploads(.:format)
- Controller#Action | active_storage/direct_uploads#create
- MESSAGE
+ RUBY
+
+ output = run_routes_command(["--expanded"])
+
+ assert_equal <<~MESSAGE, output
+ --[ Route 1 ]--------------
+ Prefix | cart
+ Verb | GET
+ URI | /cart(.:format)
+ Controller#Action | cart#show
+ --[ Route 2 ]--------------
+ Prefix | rails_service_blob
+ Verb | GET
+ URI | /rails/active_storage/blobs/:signed_id/*filename(.:format)
+ Controller#Action | active_storage/blobs#show
+ --[ Route 3 ]--------------
+ Prefix | rails_blob_representation
+ Verb | GET
+ URI | /rails/active_storage/representations/:signed_blob_id/:variation_key/*filename(.:format)
+ Controller#Action | active_storage/representations#show
+ --[ Route 4 ]--------------
+ Prefix | rails_disk_service
+ Verb | GET
+ URI | /rails/active_storage/disk/:encoded_key/*filename(.:format)
+ Controller#Action | active_storage/disk#show
+ --[ Route 5 ]--------------
+ Prefix | update_rails_disk_service
+ Verb | PUT
+ URI | /rails/active_storage/disk/:encoded_token(.:format)
+ Controller#Action | active_storage/disk#update
+ --[ Route 6 ]--------------
+ Prefix | rails_direct_uploads
+ Verb | POST
+ URI | /rails/active_storage/direct_uploads(.:format)
+ Controller#Action | active_storage/direct_uploads#create
+ MESSAGE
+ ensure
+ IO.console.winsize = previous_console_winsize
+ end
end
private
diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb
index 1d2e0fd354..294fdcd6a1 100644
--- a/railties/test/generators/app_generator_test.rb
+++ b/railties/test/generators/app_generator_test.rb
@@ -408,13 +408,13 @@ class AppGeneratorTest < Rails::Generators::TestCase
end
end
- def test_config_another_database
+ def test_config_mysql_database
run_generator([destination_root, "-d", "mysql"])
assert_file "config/database.yml", /mysql/
if defined?(JRUBY_VERSION)
assert_gem "activerecord-jdbcmysql-adapter"
else
- assert_gem "mysql2", "'~> 0.4.4'"
+ assert_gem "mysql2", "'>= 0.4.4', '< 0.6.0'"
end
end