aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Gemfile2
-rw-r--r--Gemfile.lock2
-rw-r--r--actioncable/lib/rails/generators/channel/templates/javascript/channel.js.tt6
-rw-r--r--actioncable/lib/rails/generators/channel/templates/javascript/consumer.js.tt4
-rw-r--r--actionpack/CHANGELOG.md2
-rw-r--r--actionpack/lib/action_dispatch/middleware/cookies.rb9
-rw-r--r--actionpack/lib/action_dispatch/middleware/host_authorization.rb4
-rw-r--r--actionpack/test/dispatch/cookies_test.rb11
-rw-r--r--actionpack/test/dispatch/host_authorization_test.rb6
-rw-r--r--actionview/lib/action_view/template/handlers.rb2
-rw-r--r--actionview/test/template/render_test.rb9
-rw-r--r--activerecord/lib/active_record/associations/collection_association.rb5
-rw-r--r--activerecord/lib/active_record/associations/has_many_association.rb24
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb3
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb2
-rw-r--r--activerecord/lib/active_record/connection_handling.rb9
-rw-r--r--activerecord/lib/active_record/core.rb13
-rw-r--r--activerecord/lib/active_record/middleware/database_selector/resolver.rb6
-rw-r--r--activerecord/lib/active_record/railtie.rb1
-rw-r--r--activerecord/test/cases/associations/has_many_associations_test.rb40
-rw-r--r--activerecord/test/cases/connection_adapters/connection_handler_test.rb5
-rw-r--r--activerecord/test/cases/connection_adapters/connection_handlers_multi_db_test.rb42
-rw-r--r--activerecord/test/cases/query_cache_test.rb38
-rw-r--r--activerecord/test/support/connection.rb1
-rw-r--r--activestorage/lib/active_storage/service/azure_storage_service.rb4
-rw-r--r--activesupport/CHANGELOG.md7
-rw-r--r--activesupport/lib/active_support/current_attributes.rb6
-rw-r--r--activesupport/test/current_attributes_test.rb37
-rw-r--r--guides/assets/images/getting_started/template_is_missing_articles_new.pngbin472167 -> 26796 bytes
-rw-r--r--guides/bug_report_templates/active_record_gem.rb2
-rw-r--r--guides/bug_report_templates/active_record_migrations_gem.rb2
-rw-r--r--guides/source/active_support_core_extensions.md2
-rw-r--r--guides/source/api_app.md2
-rw-r--r--guides/source/getting_started.md23
-rw-r--r--railties/CHANGELOG.md4
-rw-r--r--railties/test/application/configuration_test.rb2
37 files changed, 251 insertions, 88 deletions
diff --git a/Gemfile b/Gemfile
index e182bf0578..f665321625 100644
--- a/Gemfile
+++ b/Gemfile
@@ -118,7 +118,7 @@ platforms :ruby, :mswin, :mswin64, :mingw, :x64_mingw do
gem "racc", ">=1.4.6", require: false
# Active Record.
- gem "sqlite3", "~> 1.3.6"
+ gem "sqlite3", "~> 1.3", ">= 1.3.6"
group :db do
gem "pg", ">= 0.18.0"
diff --git a/Gemfile.lock b/Gemfile.lock
index 996e9d5942..b05ae7915e 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -577,7 +577,7 @@ DEPENDENCIES
sidekiq
sneakers
sprockets-export
- sqlite3 (~> 1.3.6)
+ sqlite3 (~> 1.3, >= 1.3.6)
stackprof
sucker_punch
turbolinks (~> 5)
diff --git a/actioncable/lib/rails/generators/channel/templates/javascript/channel.js.tt b/actioncable/lib/rails/generators/channel/templates/javascript/channel.js.tt
index 33baaa5a22..ddf6b2d79b 100644
--- a/actioncable/lib/rails/generators/channel/templates/javascript/channel.js.tt
+++ b/actioncable/lib/rails/generators/channel/templates/javascript/channel.js.tt
@@ -1,15 +1,15 @@
import consumer from "./consumer"
consumer.subscriptions.create("<%= class_name %>Channel", {
- connected: function() {
+ connected() {
// Called when the subscription is ready for use on the server
},
- disconnected: function() {
+ disconnected() {
// Called when the subscription has been terminated by the server
},
- received: function(data) {
+ received(data) {
// Called when there's incoming data on the websocket for this channel
}<%= actions.any? ? ",\n" : '' %>
<% actions.each do |action| -%>
diff --git a/actioncable/lib/rails/generators/channel/templates/javascript/consumer.js.tt b/actioncable/lib/rails/generators/channel/templates/javascript/consumer.js.tt
index eec7e54b8a..0eceb59b18 100644
--- a/actioncable/lib/rails/generators/channel/templates/javascript/consumer.js.tt
+++ b/actioncable/lib/rails/generators/channel/templates/javascript/consumer.js.tt
@@ -1,6 +1,6 @@
// Action Cable provides the framework to deal with WebSockets in Rails.
// You can generate new channels where WebSocket features live using the `rails generate channel` command.
-import ActionCable from "@rails/actioncable"
+import { createConsumer } from "@rails/actioncable"
-export default ActionCable.createConsumer()
+export default createConsumer()
diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md
index 807e9a0719..23ac619f50 100644
--- a/actionpack/CHANGELOG.md
+++ b/actionpack/CHANGELOG.md
@@ -24,7 +24,7 @@
* Introduce ActionDispatch::HostAuthorization
This is a new middleware that guards against DNS rebinding attacks by
- white-listing the allowed hosts a request can be made to.
+ explicitly permitting the hosts a request can be made to.
Each host is checked with the case operator (`#===`) to support `RegExp`,
`Proc`, `IPAddr` and custom objects as host allowances.
diff --git a/actionpack/lib/action_dispatch/middleware/cookies.rb b/actionpack/lib/action_dispatch/middleware/cookies.rb
index cb28baa229..1611a8b3dd 100644
--- a/actionpack/lib/action_dispatch/middleware/cookies.rb
+++ b/actionpack/lib/action_dispatch/middleware/cookies.rb
@@ -488,13 +488,8 @@ module ActionDispatch
end
def cookie_metadata(name, options)
- if request.use_cookies_with_metadata
- metadata = expiry_options(options)
- metadata[:purpose] = "cookie.#{name}"
-
- metadata
- else
- {}
+ expiry_options(options).tap do |metadata|
+ metadata[:purpose] = "cookie.#{name}" if request.use_cookies_with_metadata
end
end
diff --git a/actionpack/lib/action_dispatch/middleware/host_authorization.rb b/actionpack/lib/action_dispatch/middleware/host_authorization.rb
index 447b70112a..b7dff1df41 100644
--- a/actionpack/lib/action_dispatch/middleware/host_authorization.rb
+++ b/actionpack/lib/action_dispatch/middleware/host_authorization.rb
@@ -3,8 +3,8 @@
require "action_dispatch/http/request"
module ActionDispatch
- # This middleware guards from DNS rebinding attacks by white-listing the
- # hosts a request can be sent to.
+ # This middleware guards from DNS rebinding attacks by explicitly permitting
+ # the hosts a request can be sent to.
#
# When a request comes to an unauthorized host, the +response_app+
# application will be executed and rendered. If no +response_app+ is given, a
diff --git a/actionpack/test/dispatch/cookies_test.rb b/actionpack/test/dispatch/cookies_test.rb
index 8f9dbaf4b3..4aaac1320e 100644
--- a/actionpack/test/dispatch/cookies_test.rb
+++ b/actionpack/test/dispatch/cookies_test.rb
@@ -1193,11 +1193,7 @@ class CookiesTest < ActionController::TestCase
get :encrypted_discount_and_user_id_cookie
travel 2.hours
- assert_equal 50, cookies.encrypted[:user_id]
-
- cookies[:discount_percentage] = cookies[:user_id]
- assert_not_equal 10, cookies.encrypted[:discount_percentage]
- assert_equal 50, cookies.encrypted[:discount_percentage]
+ assert_nil cookies.signed[:user_id]
end
def test_switch_off_metadata_for_signed_cookies_if_config_is_false
@@ -1206,11 +1202,8 @@ class CookiesTest < ActionController::TestCase
get :signed_discount_and_user_id_cookie
travel 2.hours
- assert_equal 50, cookies.signed[:user_id]
- cookies[:discount_percentage] = cookies[:user_id]
- assert_not_equal 10, cookies.signed[:discount_percentage]
- assert_equal 50, cookies.signed[:discount_percentage]
+ assert_nil cookies.signed[:user_id]
end
def test_read_rails_5_2_stable_encrypted_cookies_if_config_is_false
diff --git a/actionpack/test/dispatch/host_authorization_test.rb b/actionpack/test/dispatch/host_authorization_test.rb
index dae7b08ec1..5263dd2597 100644
--- a/actionpack/test/dispatch/host_authorization_test.rb
+++ b/actionpack/test/dispatch/host_authorization_test.rb
@@ -15,7 +15,7 @@ class HostAuthorizationTest < ActionDispatch::IntegrationTest
assert_match "Blocked host: www.example.com", response.body
end
- test "passes all requests to if the whitelist is empty" do
+ test "allows all requests if hosts is empty" do
@app = ActionDispatch::HostAuthorization.new(App, nil)
get "/"
@@ -24,7 +24,7 @@ class HostAuthorizationTest < ActionDispatch::IntegrationTest
assert_equal "Success", body
end
- test "passes requests to allowed host" do
+ test "hosts can be a single element array" do
@app = ActionDispatch::HostAuthorization.new(App, %w(www.example.com))
get "/"
@@ -33,7 +33,7 @@ class HostAuthorizationTest < ActionDispatch::IntegrationTest
assert_equal "Success", body
end
- test "the whitelist could be a single element" do
+ test "hosts can be a string" do
@app = ActionDispatch::HostAuthorization.new(App, "www.example.com")
get "/"
diff --git a/actionview/lib/action_view/template/handlers.rb b/actionview/lib/action_view/template/handlers.rb
index f2a2341b8e..ddaac7a100 100644
--- a/actionview/lib/action_view/template/handlers.rb
+++ b/actionview/lib/action_view/template/handlers.rb
@@ -43,7 +43,7 @@ module ActionView #:nodoc:
handler.method(:call).parameters
end
- unless params.find_all { |type, _| type == :req }.length >= 2
+ unless params.find_all { |type, _| type == :req || type == :opt }.length >= 2
ActiveSupport::Deprecation.warn <<~eowarn
Single arity template handlers are deprecated. Template handlers must
now accept two parameters, the view object and the source for the view object.
diff --git a/actionview/test/template/render_test.rb b/actionview/test/template/render_test.rb
index f5d251da20..f89d087c34 100644
--- a/actionview/test/template/render_test.rb
+++ b/actionview/test/template/render_test.rb
@@ -471,6 +471,15 @@ module RenderTestCases
ActionView::Template.unregister_template_handler :ruby_handler
end
+ def test_optional_second_arg_works_without_deprecation
+ assert_not_deprecated do
+ ActionView::Template.register_template_handler :ruby_handler, ->(view, source = nil) { source }
+ end
+ assert_equal "3", @view.render(inline: "(1 + 2).to_s", type: :ruby_handler)
+ ensure
+ ActionView::Template.unregister_template_handler :ruby_handler
+ end
+
def test_render_inline_with_compilable_custom_type
ActionView::Template.register_template_handler :foo, CustomHandler
assert_equal 'source: "Hello, World!"', @view.render(inline: "Hello, World!", type: :foo)
diff --git a/activerecord/lib/active_record/associations/collection_association.rb b/activerecord/lib/active_record/associations/collection_association.rb
index 4a25567c9d..6f5df807fe 100644
--- a/activerecord/lib/active_record/associations/collection_association.rb
+++ b/activerecord/lib/active_record/associations/collection_association.rb
@@ -210,7 +210,8 @@ module ActiveRecord
# This method is abstract in the sense that it relies on
# +count_records+, which is a method descendants have to provide.
def size
- if !find_target? || loaded?
+ if !find_target?
+ loaded! unless loaded?
target.size
elsif @association_ids
@association_ids.size
@@ -233,7 +234,7 @@ module ActiveRecord
# loaded and you are going to fetch the records anyway it is better to
# check <tt>collection.length.zero?</tt>.
def empty?
- if loaded? || @association_ids
+ if loaded? || @association_ids || reflection.has_cached_counter?
size.zero?
else
target.empty? && !scope.exists?
diff --git a/activerecord/lib/active_record/associations/has_many_association.rb b/activerecord/lib/active_record/associations/has_many_association.rb
index eb22db838c..6f67934a79 100644
--- a/activerecord/lib/active_record/associations/has_many_association.rb
+++ b/activerecord/lib/active_record/associations/has_many_association.rb
@@ -36,14 +36,6 @@ module ActiveRecord
super
end
- def empty?
- if reflection.has_cached_counter?
- size.zero?
- else
- super
- end
- end
-
private
# Returns the number of records in this collection.
@@ -60,20 +52,24 @@ module ActiveRecord
# If the collection is empty the target is set to an empty array and
# the loaded flag is set to true as well.
def count_records
- count = if reflection.has_cached_counter?
- owner._read_attribute(reflection.counter_cache_column).to_i
- else
- scope.count(:all)
- end
+ count = counter_cache_value || scope.count(:all)
# If there's nothing in the database and @target has no new records
# we are certain the current target is an empty array. This is a
# documented side-effect of the method that may avoid an extra SELECT.
- (@target ||= []) && loaded! if count == 0
+ loaded! if count == 0
[association_scope.limit_value, count].compact.min
end
+ def counter_cache_value
+ reflection.has_cached_counter? ? owner._read_attribute(reflection.counter_cache_column).to_i : nil
+ end
+
+ def find_target?
+ super && !counter_cache_value&.zero?
+ end
+
def update_counter(difference, reflection = reflection())
if reflection.has_cached_counter?
owner.increment!(reflection.counter_cache_column, difference)
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb b/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb
index 8aeb934ec2..4e55fcae2f 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb
@@ -17,7 +17,7 @@ module ActiveRecord
method_names.each do |method_name|
base.class_eval <<-end_code, __FILE__, __LINE__ + 1
def #{method_name}(*)
- clear_query_cache if @query_cache_enabled
+ ActiveRecord::Base.clear_query_caches_for_current_thread if @query_cache_enabled
super
end
end_code
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
index 16344b160d..b2a6109548 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
@@ -682,9 +682,10 @@ module ActiveRecord
end
alias :remove_belongs_to :remove_references
- # Adds a foreign key.
+ # Adds a foreign key to the table using a supplied table name.
#
# t.foreign_key(:authors)
+ # t.foreign_key(:authors, column: :author_id, primary_key: "id")
#
# See {connection.add_foreign_key}[rdoc-ref:SchemaStatements#add_foreign_key]
def foreign_key(*args)
diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
index 14dbd20bcd..7b3630662b 100644
--- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
@@ -9,7 +9,7 @@ require "active_record/connection_adapters/sqlite3/schema_definitions"
require "active_record/connection_adapters/sqlite3/schema_dumper"
require "active_record/connection_adapters/sqlite3/schema_statements"
-gem "sqlite3", "~> 1.3.6"
+gem "sqlite3", "~> 1.3", ">= 1.3.6"
require "sqlite3"
module ActiveRecord
diff --git a/activerecord/lib/active_record/connection_handling.rb b/activerecord/lib/active_record/connection_handling.rb
index 558cdeccf2..53069cd899 100644
--- a/activerecord/lib/active_record/connection_handling.rb
+++ b/activerecord/lib/active_record/connection_handling.rb
@@ -176,6 +176,15 @@ module ActiveRecord
config_hash
end
+ # Clears the query cache for all connections associated with the current thread.
+ def clear_query_caches_for_current_thread
+ ActiveRecord::Base.connection_handlers.each_value do |handler|
+ handler.connection_pool_list.each do |pool|
+ pool.connection.clear_query_cache if pool.active_connection?
+ end
+ end
+ end
+
# Returns the connection currently associated with the class. This can
# also be used to "borrow" the connection to do database work unrelated
# to any of the specific Active Records.
diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb
index 519acd7605..c67980173f 100644
--- a/activerecord/lib/active_record/core.rb
+++ b/activerecord/lib/active_record/core.rb
@@ -125,6 +125,10 @@ module ActiveRecord
mattr_accessor :connection_handlers, instance_accessor: false, default: {}
+ mattr_accessor :writing_role, instance_accessor: false, default: :writing
+
+ mattr_accessor :reading_role, instance_accessor: false, default: :reading
+
class_attribute :default_connection_handler, instance_writer: false
self.filter_attributes = []
@@ -138,7 +142,6 @@ module ActiveRecord
end
self.default_connection_handler = ConnectionAdapters::ConnectionHandler.new
- self.connection_handlers = { writing: ActiveRecord::Base.default_connection_handler }
end
module ClassMethods
@@ -474,6 +477,14 @@ module ActiveRecord
end
end
+ def present? # :nodoc:
+ true
+ end
+
+ def blank? # :nodoc:
+ false
+ end
+
# Returns +true+ if the record is read only. Records loaded through joins with piggy-back
# attributes will be marked as read only since they cannot be saved.
def readonly?
diff --git a/activerecord/lib/active_record/middleware/database_selector/resolver.rb b/activerecord/lib/active_record/middleware/database_selector/resolver.rb
index 0eeb0453ef..a84c292714 100644
--- a/activerecord/lib/active_record/middleware/database_selector/resolver.rb
+++ b/activerecord/lib/active_record/middleware/database_selector/resolver.rb
@@ -47,7 +47,7 @@ module ActiveRecord
def read_from_primary(&blk)
ActiveRecord::Base.connection.while_preventing_writes do
- ActiveRecord::Base.connected_to(role: :writing) do
+ ActiveRecord::Base.connected_to(role: ActiveRecord::Base.writing_role) do
instrumenter.instrument("database_selector.active_record.read_from_primary") do
yield
end
@@ -56,7 +56,7 @@ module ActiveRecord
end
def read_from_replica(&blk)
- ActiveRecord::Base.connected_to(role: :reading) do
+ ActiveRecord::Base.connected_to(role: ActiveRecord::Base.reading_role) do
instrumenter.instrument("database_selector.active_record.read_from_replica") do
yield
end
@@ -64,7 +64,7 @@ module ActiveRecord
end
def write_to_primary(&blk)
- ActiveRecord::Base.connected_to(role: :writing) do
+ ActiveRecord::Base.connected_to(role: ActiveRecord::Base.writing_role) do
instrumenter.instrument("database_selector.active_record.wrote_to_primary") do
yield
ensure
diff --git a/activerecord/lib/active_record/railtie.rb b/activerecord/lib/active_record/railtie.rb
index 017e279080..aac49a92b4 100644
--- a/activerecord/lib/active_record/railtie.rb
+++ b/activerecord/lib/active_record/railtie.rb
@@ -197,6 +197,7 @@ end_error
# and then establishes the connection.
initializer "active_record.initialize_database" do
ActiveSupport.on_load(:active_record) do
+ self.connection_handlers = { writing_role => ActiveRecord::Base.default_connection_handler }
self.configurations = Rails.application.config.database_configuration
establish_connection
end
diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb
index 2f090d9862..4c9e4d0ad2 100644
--- a/activerecord/test/cases/associations/has_many_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_associations_test.rb
@@ -1224,12 +1224,15 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_has_many_without_counter_cache_option
# Ship has a conventionally named `treasures_count` column, but the counter_cache
# option is not given on the association.
- ship = Ship.create(name: "Countless", treasures_count: 10)
+ ship = Ship.create!(name: "Countless", treasures_count: 10)
assert_not_predicate Ship.reflect_on_association(:treasures), :has_cached_counter?
# Count should come from sql count() of treasures rather than treasures_count attribute
- assert_equal ship.treasures.size, 0
+ assert_queries(1) do
+ assert_equal ship.treasures.size, 0
+ assert_predicate ship.treasures, :loaded?
+ end
assert_no_difference lambda { ship.reload.treasures_count }, "treasures_count should not be changed" do
ship.treasures.create(name: "Gold")
@@ -1350,6 +1353,20 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
post = posts(:welcome)
assert_no_queries do
assert_not_empty post.comments
+ assert_equal 2, post.comments.size
+ assert_not_predicate post.comments, :loaded?
+ end
+ post = posts(:misc_by_bob)
+ assert_no_queries do
+ assert_empty post.comments
+ assert_predicate post.comments, :loaded?
+ end
+ end
+
+ def test_empty_association_loading_with_counter_cache
+ post = posts(:misc_by_bob)
+ assert_no_queries do
+ assert_empty post.comments.to_a
end
end
@@ -1986,9 +2003,22 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_not_predicate company.clients, :loaded?
end
- def test_counter_cache_on_unloaded_association
- car = Car.create(name: "My AppliCar")
- assert_equal car.engines.size, 0
+ def test_zero_counter_cache_usage_on_unloaded_association
+ car = Car.create!(name: "My AppliCar")
+ assert_no_queries do
+ assert_equal car.engines.size, 0
+ assert_predicate car.engines, :loaded?
+ end
+ end
+
+ def test_counter_cache_on_new_record_unloaded_association
+ car = Car.new(name: "My AppliCar")
+ # Ensure no schema queries inside assertion
+ Engine.primary_key
+ assert_no_queries do
+ assert_equal car.engines.size, 0
+ assert_predicate car.engines, :loaded?
+ end
end
def test_get_ids_ignores_include_option
diff --git a/activerecord/test/cases/connection_adapters/connection_handler_test.rb b/activerecord/test/cases/connection_adapters/connection_handler_test.rb
index 51d0cc3d12..6282759a10 100644
--- a/activerecord/test/cases/connection_adapters/connection_handler_test.rb
+++ b/activerecord/test/cases/connection_adapters/connection_handler_test.rb
@@ -382,6 +382,11 @@ module ActiveRecord
assert_not_nil ActiveRecord::Base.connection
assert_same klass2.connection, ActiveRecord::Base.connection
end
+
+ def test_default_handlers_are_writing_and_reading
+ assert_equal :writing, ActiveRecord::Base.writing_role
+ assert_equal :reading, ActiveRecord::Base.reading_role
+ end
end
end
end
diff --git a/activerecord/test/cases/connection_adapters/connection_handlers_multi_db_test.rb b/activerecord/test/cases/connection_adapters/connection_handlers_multi_db_test.rb
index 8988755d24..36591097b6 100644
--- a/activerecord/test/cases/connection_adapters/connection_handlers_multi_db_test.rb
+++ b/activerecord/test/cases/connection_adapters/connection_handlers_multi_db_test.rb
@@ -126,6 +126,30 @@ module ActiveRecord
ENV["RAILS_ENV"] = previous_env
end
+ def test_establish_connection_using_3_levels_config_with_non_default_handlers
+ previous_env, ENV["RAILS_ENV"] = ENV["RAILS_ENV"], "default_env"
+
+ config = {
+ "default_env" => {
+ "readonly" => { "adapter" => "sqlite3", "database" => "db/readonly.sqlite3" },
+ "primary" => { "adapter" => "sqlite3", "database" => "db/primary.sqlite3" }
+ }
+ }
+ @prev_configs, ActiveRecord::Base.configurations = ActiveRecord::Base.configurations, config
+
+ ActiveRecord::Base.connects_to(database: { default: :primary, readonly: :readonly })
+
+ assert_not_nil pool = ActiveRecord::Base.connection_handlers[:default].retrieve_connection_pool("primary")
+ assert_equal "db/primary.sqlite3", pool.spec.config[:database]
+
+ assert_not_nil pool = ActiveRecord::Base.connection_handlers[:readonly].retrieve_connection_pool("primary")
+ assert_equal "db/readonly.sqlite3", pool.spec.config[:database]
+ ensure
+ ActiveRecord::Base.configurations = @prev_configs
+ ActiveRecord::Base.establish_connection(:arunit)
+ ENV["RAILS_ENV"] = previous_env
+ end
+
def test_switching_connections_with_database_url
previous_env, ENV["RAILS_ENV"] = ENV["RAILS_ENV"], "default_env"
previous_url, ENV["DATABASE_URL"] = ENV["DATABASE_URL"], "postgres://localhost/foo"
@@ -344,6 +368,24 @@ module ActiveRecord
assert_equal "No connection pool with 'primary' found for the 'reading' role.", error.message
end
+
+ def test_default_handlers_are_writing_and_reading
+ assert_equal :writing, ActiveRecord::Base.writing_role
+ assert_equal :reading, ActiveRecord::Base.reading_role
+ end
+
+ def test_an_application_can_change_the_default_handlers
+ old_writing = ActiveRecord::Base.writing_role
+ old_reading = ActiveRecord::Base.reading_role
+ ActiveRecord::Base.writing_role = :default
+ ActiveRecord::Base.reading_role = :readonly
+
+ assert_equal :default, ActiveRecord::Base.writing_role
+ assert_equal :readonly, ActiveRecord::Base.reading_role
+ ensure
+ ActiveRecord::Base.writing_role = old_writing
+ ActiveRecord::Base.reading_role = old_reading
+ end
end
end
end
diff --git a/activerecord/test/cases/query_cache_test.rb b/activerecord/test/cases/query_cache_test.rb
index 04bbc7d136..eb32b690aa 100644
--- a/activerecord/test/cases/query_cache_test.rb
+++ b/activerecord/test/cases/query_cache_test.rb
@@ -502,6 +502,44 @@ class QueryCacheTest < ActiveRecord::TestCase
}.call({})
end
+ def test_clear_query_cache_is_called_on_all_connections
+ skip "with in memory db, reading role won't be able to see database on writing role" if in_memory_db?
+ with_temporary_connection_pool do
+ ActiveRecord::Base.connection_handlers = {
+ writing: ActiveRecord::Base.default_connection_handler,
+ reading: ActiveRecord::ConnectionAdapters::ConnectionHandler.new
+ }
+
+ ActiveRecord::Base.connected_to(role: :reading) do
+ ActiveRecord::Base.establish_connection(ActiveRecord::Base.configurations["arunit"])
+ end
+
+ mw = middleware { |env|
+ ActiveRecord::Base.connected_to(role: :reading) do
+ @topic = Topic.first
+ end
+
+ assert @topic
+
+ ActiveRecord::Base.connected_to(role: :writing) do
+ @topic.title = "It doesn't have to be crazy at work"
+ @topic.save!
+ end
+
+ assert_equal "It doesn't have to be crazy at work", @topic.title
+
+ ActiveRecord::Base.connected_to(role: :reading) do
+ @topic = Topic.first
+ assert_equal "It doesn't have to be crazy at work", @topic.title
+ end
+ }
+
+ mw.call({})
+ end
+ ensure
+ ActiveRecord::Base.connection_handlers = { writing: ActiveRecord::Base.default_connection_handler }
+ end
+
private
def with_temporary_connection_pool
diff --git a/activerecord/test/support/connection.rb b/activerecord/test/support/connection.rb
index 2a4fa53460..367309dd85 100644
--- a/activerecord/test/support/connection.rb
+++ b/activerecord/test/support/connection.rb
@@ -21,6 +21,7 @@ module ARTest
def self.connect
puts "Using #{connection_name}"
ActiveRecord::Base.logger = ActiveSupport::Logger.new("debug.log", 0, 100 * 1024 * 1024)
+ ActiveRecord::Base.connection_handlers = { ActiveRecord::Base.writing_role => ActiveRecord::Base.default_connection_handler }
ActiveRecord::Base.configurations = connection_config
ActiveRecord::Base.establish_connection :arunit
ARUnit2Model.establish_connection :arunit2
diff --git a/activestorage/lib/active_storage/service/azure_storage_service.rb b/activestorage/lib/active_storage/service/azure_storage_service.rb
index 17cecd891c..993cc0e5f7 100644
--- a/activestorage/lib/active_storage/service/azure_storage_service.rb
+++ b/activestorage/lib/active_storage/service/azure_storage_service.rb
@@ -10,8 +10,8 @@ module ActiveStorage
class Service::AzureStorageService < Service
attr_reader :client, :blobs, :container, :signer
- 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)
+ def initialize(storage_account_name:, storage_access_key:, container:, **options)
+ @client = Azure::Storage::Client.create(storage_account_name: storage_account_name, storage_access_key: storage_access_key, **options)
@signer = Azure::Storage::Core::Auth::SharedAccessSignature.new(storage_account_name, storage_access_key)
@blobs = client.blob_client
@container = container
diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md
index 1e726ceb54..2da774ca66 100644
--- a/activesupport/CHANGELOG.md
+++ b/activesupport/CHANGELOG.md
@@ -1,4 +1,8 @@
-* Remove the `` Kernel#` `` override that suppresses ENOENT and accidentally returns nil on Unix systems
+* Add `before_reset` callback to `CurrentAttributes` and define `after_reset` as an alias of `resets` for symmetry.
+
+ *Rosa Gutierrez*
+
+* Remove the `` Kernel#` `` override that suppresses ENOENT and accidentally returns nil on Unix systems.
*Akinori Musha*
@@ -8,7 +12,6 @@
*Stefan Schüßler*
-
## Rails 6.0.0.beta1 (January 18, 2019) ##
* Remove deprecated `Module#reachable?` method.
diff --git a/activesupport/lib/active_support/current_attributes.rb b/activesupport/lib/active_support/current_attributes.rb
index 3145ff87a1..67ebe102d7 100644
--- a/activesupport/lib/active_support/current_attributes.rb
+++ b/activesupport/lib/active_support/current_attributes.rb
@@ -119,10 +119,16 @@ module ActiveSupport
end
end
+ # Calls this block before #reset is called on the instance. Used for resetting external collaborators that depend on current values.
+ def before_reset(&block)
+ set_callback :reset, :before, &block
+ end
+
# Calls this block after #reset is called on the instance. Used for resetting external collaborators, like Time.zone.
def resets(&block)
set_callback :reset, :after, &block
end
+ alias_method :after_reset, :resets
delegate :set, :reset, to: :instance
diff --git a/activesupport/test/current_attributes_test.rb b/activesupport/test/current_attributes_test.rb
index 1669f08f68..adbdc646bc 100644
--- a/activesupport/test/current_attributes_test.rb
+++ b/activesupport/test/current_attributes_test.rb
@@ -3,13 +3,18 @@
require "abstract_unit"
class CurrentAttributesTest < ActiveSupport::TestCase
- Person = Struct.new(:name, :time_zone)
+ Person = Struct.new(:id, :name, :time_zone)
class Current < ActiveSupport::CurrentAttributes
attribute :world, :account, :person, :request
delegate :time_zone, to: :person
- resets { Time.zone = "UTC" }
+ before_reset { Session.previous = person.try(:id) }
+
+ resets do
+ Time.zone = "UTC"
+ Session.current = nil
+ end
def account=(account)
super
@@ -19,6 +24,7 @@ class CurrentAttributesTest < ActiveSupport::TestCase
def person=(person)
super
Time.zone = person.try(:time_zone)
+ Session.current = person.try(:id)
end
def request
@@ -30,9 +36,14 @@ class CurrentAttributesTest < ActiveSupport::TestCase
end
end
+ class Session < ActiveSupport::CurrentAttributes
+ attribute :current, :previous
+ end
+
setup do
@original_time_zone = Time.zone
Current.reset
+ Session.reset
end
teardown do
@@ -56,16 +67,28 @@ class CurrentAttributesTest < ActiveSupport::TestCase
end
test "set auxiliary class via overwritten method" do
- Current.person = Person.new("David", "Central Time (US & Canada)")
+ Current.person = Person.new(42, "David", "Central Time (US & Canada)")
assert_equal "Central Time (US & Canada)", Time.zone.name
+ assert_equal 42, Session.current
end
- test "resets auxiliary class via callback" do
- Current.person = Person.new("David", "Central Time (US & Canada)")
+ test "resets auxiliary classes via callback" do
+ Current.person = Person.new(42, "David", "Central Time (US & Canada)")
assert_equal "Central Time (US & Canada)", Time.zone.name
Current.reset
assert_equal "UTC", Time.zone.name
+ assert_nil Session.current
+ end
+
+ test "set auxiliary class based on current attributes via before callback" do
+ Current.person = Person.new(42, "David", "Central Time (US & Canada)")
+ assert_nil Session.previous
+ assert_equal 42, Session.current
+
+ Current.reset
+ assert_equal 42, Session.previous
+ assert_nil Session.current
end
test "set attribute only via scope" do
@@ -92,13 +115,13 @@ class CurrentAttributesTest < ActiveSupport::TestCase
end
test "delegation" do
- Current.person = Person.new("David", "Central Time (US & Canada)")
+ Current.person = Person.new(42, "David", "Central Time (US & Canada)")
assert_equal "Central Time (US & Canada)", Current.time_zone
assert_equal "Central Time (US & Canada)", Current.instance.time_zone
end
test "all methods forward to the instance" do
- Current.person = Person.new("David", "Central Time (US & Canada)")
+ Current.person = Person.new(42, "David", "Central Time (US & Canada)")
assert_equal "David, in Central Time (US & Canada)", Current.intro
assert_equal "David, in Central Time (US & Canada)", Current.instance.intro
end
diff --git a/guides/assets/images/getting_started/template_is_missing_articles_new.png b/guides/assets/images/getting_started/template_is_missing_articles_new.png
index a1603f5d28..830b19bd9d 100644
--- a/guides/assets/images/getting_started/template_is_missing_articles_new.png
+++ b/guides/assets/images/getting_started/template_is_missing_articles_new.png
Binary files differ
diff --git a/guides/bug_report_templates/active_record_gem.rb b/guides/bug_report_templates/active_record_gem.rb
index d88304a219..74b329115d 100644
--- a/guides/bug_report_templates/active_record_gem.rb
+++ b/guides/bug_report_templates/active_record_gem.rb
@@ -9,7 +9,7 @@ gemfile(true) do
# Activate the gem you are reporting the issue against.
gem "activerecord", "5.2.0"
- gem "sqlite3"
+ gem "sqlite3", "~> 1.3.6"
end
require "active_record"
diff --git a/guides/bug_report_templates/active_record_migrations_gem.rb b/guides/bug_report_templates/active_record_migrations_gem.rb
index 5dfd49fb38..8d71863034 100644
--- a/guides/bug_report_templates/active_record_migrations_gem.rb
+++ b/guides/bug_report_templates/active_record_migrations_gem.rb
@@ -9,7 +9,7 @@ gemfile(true) do
# Activate the gem you are reporting the issue against.
gem "activerecord", "5.2.0"
- gem "sqlite3"
+ gem "sqlite3", "~> 1.3.6"
end
require "active_record"
diff --git a/guides/source/active_support_core_extensions.md b/guides/source/active_support_core_extensions.md
index 3db46bc42e..903f39994f 100644
--- a/guides/source/active_support_core_extensions.md
+++ b/guides/source/active_support_core_extensions.md
@@ -1201,6 +1201,8 @@ The `inquiry` method converts a string into a `StringInquirer` object making equ
"active".inquiry.inactive? # => false
```
+NOTE: Defined in `active_support/core_ext/string/inquiry.rb`.
+
### `starts_with?` and `ends_with?`
Active Support defines 3rd person aliases of `String#start_with?` and `String#end_with?`:
diff --git a/guides/source/api_app.md b/guides/source/api_app.md
index 85367c50e7..870f5f7b87 100644
--- a/guides/source/api_app.md
+++ b/guides/source/api_app.md
@@ -374,7 +374,7 @@ controller modules by default:
- `ActionController::Renderers::All`: Support for `render :json` and friends.
- `ActionController::ConditionalGet`: Support for `stale?`.
- `ActionController::BasicImplicitRender`: Makes sure to return an empty response, if there isn't an explicit one.
-- `ActionController::StrongParameters`: Support for parameters white-listing in combination with Active Model mass assignment.
+- `ActionController::StrongParameters`: Support for parameters filtering in combination with Active Model mass assignment.
- `ActionController::DataStreaming`: Support for `send_file` and `send_data`.
- `AbstractController::Callbacks`: Support for `before_action` and
similar helpers.
diff --git a/guides/source/getting_started.md b/guides/source/getting_started.md
index 264c94326e..be59cd0cfa 100644
--- a/guides/source/getting_started.md
+++ b/guides/source/getting_started.md
@@ -461,22 +461,19 @@ available, Rails will raise an exception.
Let's look at the full error message again:
->ArticlesController#new is missing a template for this request format and variant. request.formats: ["text/html"] request.variant: [] NOTE! For XHR/Ajax or API requests, this action would normally respond with 204 No Content: an empty white screen. Since you're loading it in a web browser, we assume that you expected to actually render a template, not… nothing, so we're showing an error to be extra-clear. If you expect 204 No Content, carry on. That's what you'll get from an XHR or API request. Give it a shot.
+>ArticlesController#new is missing a template for request formats: text/html
-That's quite a lot of text! Let's quickly go through and understand what each
-part of it means.
+>NOTE!
+>Unless told otherwise, Rails expects an action to render a template with the same name, contained in a folder named after its controller. If this controller is an API responding with 204 (No Content), which does not require a template, then this error will occur when trying to access it via browser, since we expect an HTML template to be rendered for such requests. If that's the case, carry on.
-The first part identifies which template is missing. In this case, it's the
+The message identifies which template is missing. In this case, it's the
`articles/new` template. Rails will first look for this template. If not found,
-then it will attempt to load a template called `application/new`. It looks for
-one here because the `ArticlesController` inherits from `ApplicationController`.
-
-The next part of the message contains `request.formats` which specifies
-the format of template to be served in response. It is set to `text/html` as we
-requested this page via browser, so Rails is looking for an HTML template.
-`request.variant` specifies what kind of physical devices would be served by
-the response and helps Rails determine which template to use in the response.
-It is empty because no information has been provided.
+then it will attempt to load a template called `application/new`, because the
+`ArticlesController` inherits from `ApplicationController`.
+
+Next the message contains `request.formats` which specifies the format of
+template to be served in response. It is set to `text/html` as we requested
+this page via browser, so Rails is looking for an HTML template.
The simplest template that would work in this case would be one located at
`app/views/articles/new.html.erb`. The extension of this file name is important:
diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md
index e55217c5c4..19f4de8a1d 100644
--- a/railties/CHANGELOG.md
+++ b/railties/CHANGELOG.md
@@ -75,7 +75,7 @@
In other environments `Rails.application.config.hosts` is empty and no
`Host` header checks will be done. If you want to guard against header
- attacks on production, you have to manually whitelist the allowed hosts
+ attacks on production, you have to manually permit the allowed hosts
with:
Rails.application.config.hosts << "product.com"
@@ -88,7 +88,7 @@
# `beta1.product.com`.
Rails.application.config.hosts << /.*\.product\.com/
- A special case is supported that allows you to whitelist all sub-domains:
+ A special case is supported that allows you to permit all sub-domains:
# Allow requests from subdomains like `www.product.com` and
# `beta1.product.com`.
diff --git a/railties/test/application/configuration_test.rb b/railties/test/application/configuration_test.rb
index 9da3956dda..7006b0855f 100644
--- a/railties/test/application/configuration_test.rb
+++ b/railties/test/application/configuration_test.rb
@@ -2289,7 +2289,7 @@ module ApplicationTests
MESSAGE
end
- test "the host whitelist includes .localhost in development" do
+ test "hosts include .localhost in development" do
app "development"
assert_includes Rails.application.config.hosts, ".localhost"
end