aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--actionpack/lib/action_controller/metal/http_authentication.rb5
-rw-r--r--actionpack/lib/action_dispatch/testing/request_encoder.rb2
-rw-r--r--actionpack/test/controller/integration_test.rb14
-rw-r--r--actionview/CHANGELOG.md11
-rw-r--r--actionview/lib/action_view/helpers/url_helper.rb4
-rw-r--r--actionview/test/template/url_helper_test.rb9
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/quoting.rb1
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb11
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql/database_statements.rb36
-rw-r--r--activerecord/lib/active_record/relation.rb4
-rw-r--r--activerecord/lib/active_record/relation/spawn_methods.rb1
-rw-r--r--activerecord/test/cases/adapters/mysql2/datetime_precision_quoting_test.rb7
-rw-r--r--activerecord/test/cases/fixtures_test.rb113
-rw-r--r--activerecord/test/cases/quoting_test.rb26
-rw-r--r--activerecord/test/cases/scoping/relation_scoping_test.rb5
-rw-r--r--activerecord/test/cases/tasks/database_tasks_test.rb129
-rw-r--r--activerecord/test/cases/tasks/mysql_rake_test.rb75
-rw-r--r--activerecord/test/cases/tasks/postgresql_rake_test.rb98
-rw-r--r--activerecord/test/cases/tasks/sqlite_rake_test.rb16
-rw-r--r--activerecord/test/models/post.rb1
-rw-r--r--activestorage/app/models/active_storage/attachment.rb4
-rw-r--r--activestorage/app/models/active_storage/blob.rb10
-rw-r--r--activestorage/lib/active_storage/attached/changes/create_many.rb12
-rw-r--r--activestorage/lib/active_storage/attached/changes/delete_many.rb4
-rw-r--r--activestorage/test/models/attached/many_test.rb59
-rw-r--r--activestorage/test/models/attached/one_test.rb31
-rw-r--r--guides/source/active_record_validations.md18
-rw-r--r--guides/source/association_basics.md2
-rw-r--r--railties/lib/rails/commands/notes/notes_command.rb2
-rw-r--r--railties/test/application/rake/notes_test.rb3
30 files changed, 513 insertions, 200 deletions
diff --git a/actionpack/lib/action_controller/metal/http_authentication.rb b/actionpack/lib/action_controller/metal/http_authentication.rb
index 01676f3237..51d25ac1e0 100644
--- a/actionpack/lib/action_controller/metal/http_authentication.rb
+++ b/actionpack/lib/action_controller/metal/http_authentication.rb
@@ -56,8 +56,9 @@ module ActionController
# In your integration tests, you can do something like this:
#
# def test_access_granted_from_xml
- # @request.env['HTTP_AUTHORIZATION'] = ActionController::HttpAuthentication::Basic.encode_credentials(users(:dhh).name, users(:dhh).password)
- # get "/notes/1.xml"
+ # authorization = ActionController::HttpAuthentication::Basic.encode_credentials(users(:dhh).name, users(:dhh).password)
+ #
+ # get "/notes/1.xml", headers: { 'HTTP_AUTHORIZATION' => authorization }
#
# assert_equal 200, status
# end
diff --git a/actionpack/lib/action_dispatch/testing/request_encoder.rb b/actionpack/lib/action_dispatch/testing/request_encoder.rb
index 01246b7a2e..9889f61951 100644
--- a/actionpack/lib/action_dispatch/testing/request_encoder.rb
+++ b/actionpack/lib/action_dispatch/testing/request_encoder.rb
@@ -34,7 +34,7 @@ module ActionDispatch
end
def encode_params(params)
- @param_encoder.call(params)
+ @param_encoder.call(params) if params
end
def self.parser(content_type)
diff --git a/actionpack/test/controller/integration_test.rb b/actionpack/test/controller/integration_test.rb
index 41812a82e1..39ede1442a 100644
--- a/actionpack/test/controller/integration_test.rb
+++ b/actionpack/test/controller/integration_test.rb
@@ -1079,6 +1079,20 @@ class IntegrationRequestEncodersTest < ActionDispatch::IntegrationTest
end
end
+ def test_get_request_with_json_excludes_null_query_string
+ with_routing do |routes|
+ routes.draw do
+ ActiveSupport::Deprecation.silence do
+ get ":action" => FooController
+ end
+ end
+
+ get "/foos_json", as: :json
+
+ assert_equal "http://www.example.com/foos_json", request.url
+ end
+ end
+
private
def post_to_foos(as:)
with_routing do |routes|
diff --git a/actionview/CHANGELOG.md b/actionview/CHANGELOG.md
index 7d3ca7735e..6d45cc1d8a 100644
--- a/actionview/CHANGELOG.md
+++ b/actionview/CHANGELOG.md
@@ -1,3 +1,14 @@
+* Fix issue with `button_to`'s `to_form_params`
+
+ `button_to` was throwing exception when invoked with `params` hash that
+ contains symbol and string keys. The reason for the exception was that
+ `to_form_params` was comparing the given symbol and string keys.
+
+ The issue is fixed by turning all keys to strings inside
+ `to_form_params` before comparing them.
+
+ *Georgi Georgiev*
+
* Mark arrays of translations as trusted safe by using the `_html` suffix.
Example:
diff --git a/actionview/lib/action_view/helpers/url_helper.rb b/actionview/lib/action_view/helpers/url_helper.rb
index cae62f2312..52bffaab84 100644
--- a/actionview/lib/action_view/helpers/url_helper.rb
+++ b/actionview/lib/action_view/helpers/url_helper.rb
@@ -634,7 +634,7 @@ module ActionView
# suitable for use as the names and values of form input fields:
#
# to_form_params(name: 'David', nationality: 'Danish')
- # # => [{name: :name, value: 'David'}, {name: 'nationality', value: 'Danish'}]
+ # # => [{name: 'name', value: 'David'}, {name: 'nationality', value: 'Danish'}]
#
# to_form_params(country: { name: 'Denmark' })
# # => [{name: 'country[name]', value: 'Denmark'}]
@@ -666,7 +666,7 @@ module ActionView
params.push(*to_form_params(value, array_prefix))
end
else
- params << { name: namespace, value: attribute.to_param }
+ params << { name: namespace.to_s, value: attribute.to_param }
end
params.sort_by { |pair| pair[:name] }
diff --git a/actionview/test/template/url_helper_test.rb b/actionview/test/template/url_helper_test.rb
index 08cb5dfea7..9d91dbb72b 100644
--- a/actionview/test/template/url_helper_test.rb
+++ b/actionview/test/template/url_helper_test.rb
@@ -77,11 +77,18 @@ class UrlHelperTest < ActiveSupport::TestCase
def test_to_form_params_with_hash
assert_equal(
- [{ name: :name, value: "David" }, { name: :nationality, value: "Danish" }],
+ [{ name: "name", value: "David" }, { name: "nationality", value: "Danish" }],
to_form_params(name: "David", nationality: "Danish")
)
end
+ def test_to_form_params_with_hash_having_symbol_and_string_keys
+ assert_equal(
+ [{ name: "name", value: "David" }, { name: "nationality", value: "Danish" }],
+ to_form_params("name" => "David", :nationality => "Danish")
+ )
+ end
+
def test_to_form_params_with_nested_hash
assert_equal(
[{ name: "country[name]", value: "Denmark" }],
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
index aec5fa6ba1..98b1348135 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
@@ -130,6 +130,7 @@ module ActiveRecord
end
def quoted_time(value) # :nodoc:
+ value = value.change(year: 2000, month: 1, day: 1)
quoted_date(value).sub(/\A\d\d\d\d-\d\d-\d\d /, "")
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
index 07acb5425e..284b38ed7b 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
@@ -558,17 +558,6 @@ module ActiveRecord
@max_allowed_packet ||= (show_variable("max_allowed_packet") - bytes_margin)
end
- def with_multi_statements
- previous_flags = @config[:flags]
- @config[:flags] = Mysql2::Client::MULTI_STATEMENTS
- reconnect!
-
- yield
- ensure
- @config[:flags] = previous_flags
- reconnect!
- end
-
def initialize_type_map(m = type_map)
super
diff --git a/activerecord/lib/active_record/connection_adapters/mysql/database_statements.rb b/activerecord/lib/active_record/connection_adapters/mysql/database_statements.rb
index 4106ce01be..d89eeb7f54 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql/database_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql/database_statements.rb
@@ -62,6 +62,42 @@ module ActiveRecord
@connection.abandon_results!
end
+ def supports_set_server_option?
+ @connection.respond_to?(:set_server_option)
+ end
+
+ def multi_statements_enabled?(flags)
+ if flags.is_a?(Array)
+ flags.include?("MULTI_STATEMENTS")
+ else
+ (flags & Mysql2::Client::MULTI_STATEMENTS) != 0
+ end
+ end
+
+ def with_multi_statements
+ previous_flags = @config[:flags]
+
+ unless multi_statements_enabled?(previous_flags)
+ if supports_set_server_option?
+ @connection.set_server_option(Mysql2::Client::OPTION_MULTI_STATEMENTS_ON)
+ else
+ @config[:flags] = Mysql2::Client::MULTI_STATEMENTS
+ reconnect!
+ end
+ end
+
+ yield
+ ensure
+ unless multi_statements_enabled?(previous_flags)
+ if supports_set_server_option?
+ @connection.set_server_option(Mysql2::Client::OPTION_MULTI_STATEMENTS_OFF)
+ else
+ @config[:flags] = previous_flags
+ reconnect!
+ end
+ end
+ end
+
def exec_stmt_and_free(sql, name, binds, cache_stmt: false)
# make sure we carry over any changes to ActiveRecord::Base.default_timezone that have been
# made since we established the connection
diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb
index 7ab9bb2d2d..fce5b66719 100644
--- a/activerecord/lib/active_record/relation.rb
+++ b/activerecord/lib/active_record/relation.rb
@@ -309,10 +309,10 @@ module ActiveRecord
# Please check unscoped if you want to remove all previous scopes (including
# the default_scope) during the execution of a block.
def scoping
- previous, klass.current_scope = klass.current_scope(true), self
+ previous, klass.current_scope = klass.current_scope(true), self unless @delegate_to_klass
yield
ensure
- klass.current_scope = previous
+ klass.current_scope = previous unless @delegate_to_klass
end
def _exec_scope(*args, &block) # :nodoc:
diff --git a/activerecord/lib/active_record/relation/spawn_methods.rb b/activerecord/lib/active_record/relation/spawn_methods.rb
index b092399657..562e04194c 100644
--- a/activerecord/lib/active_record/relation/spawn_methods.rb
+++ b/activerecord/lib/active_record/relation/spawn_methods.rb
@@ -10,7 +10,6 @@ module ActiveRecord
def spawn #:nodoc:
clone
end
- alias :all :spawn
# Merges in the conditions from <tt>other</tt>, if <tt>other</tt> is an ActiveRecord::Relation.
# Returns an array representing the intersection of the resulting records with <tt>other</tt>, if <tt>other</tt> is an array.
diff --git a/activerecord/test/cases/adapters/mysql2/datetime_precision_quoting_test.rb b/activerecord/test/cases/adapters/mysql2/datetime_precision_quoting_test.rb
index fa54f39992..00a075e063 100644
--- a/activerecord/test/cases/adapters/mysql2/datetime_precision_quoting_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/datetime_precision_quoting_test.rb
@@ -45,9 +45,10 @@ class Mysql2DatetimePrecisionQuotingTest < ActiveRecord::Mysql2TestCase
end
def stub_version(full_version_string)
- @connection.stubs(:full_version).returns(full_version_string)
- @connection.remove_instance_variable(:@version) if @connection.instance_variable_defined?(:@version)
- yield
+ @connection.stub(:full_version, full_version_string) do
+ @connection.remove_instance_variable(:@version) if @connection.instance_variable_defined?(:@version)
+ yield
+ end
ensure
@connection.remove_instance_variable(:@version) if @connection.instance_variable_defined?(:@version)
end
diff --git a/activerecord/test/cases/fixtures_test.rb b/activerecord/test/cases/fixtures_test.rb
index fcb72c2904..c65523d8c1 100644
--- a/activerecord/test/cases/fixtures_test.rb
+++ b/activerecord/test/cases/fixtures_test.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true
require "cases/helper"
+require "support/connection_helper"
require "models/admin"
require "models/admin/account"
require "models/admin/randomly_named_c1"
@@ -32,6 +33,8 @@ require "models/treasure"
require "tempfile"
class FixturesTest < ActiveRecord::TestCase
+ include ConnectionHelper
+
self.use_instantiated_fixtures = true
self.use_transactional_tests = false
@@ -114,9 +117,96 @@ class FixturesTest < ActiveRecord::TestCase
end
end
end
+
+ def test_bulk_insert_with_a_multi_statement_query_in_a_nested_transaction
+ fixtures = {
+ "traffic_lights" => [
+ { "location" => "US", "state" => ["NY"], "long_state" => ["a"] },
+ ]
+ }
+
+ assert_difference "TrafficLight.count" do
+ ActiveRecord::Base.transaction do
+ conn = ActiveRecord::Base.connection
+ assert_equal 1, conn.open_transactions
+ conn.insert_fixtures_set(fixtures)
+ assert_equal 1, conn.open_transactions
+ end
+ end
+ end
end
if current_adapter?(:Mysql2Adapter)
+ def test_bulk_insert_with_multi_statements_enabled
+ run_without_connection do |orig_connection|
+ ActiveRecord::Base.establish_connection(
+ orig_connection.merge(flags: %w[MULTI_STATEMENTS])
+ )
+
+ fixtures = {
+ "traffic_lights" => [
+ { "location" => "US", "state" => ["NY"], "long_state" => ["a"] },
+ ]
+ }
+
+ ActiveRecord::Base.connection.stub(:supports_set_server_option?, false) do
+ assert_nothing_raised do
+ conn = ActiveRecord::Base.connection
+ conn.execute("SELECT 1; SELECT 2;")
+ conn.raw_connection.abandon_results!
+ end
+
+ assert_difference "TrafficLight.count" do
+ ActiveRecord::Base.transaction do
+ conn = ActiveRecord::Base.connection
+ assert_equal 1, conn.open_transactions
+ conn.insert_fixtures_set(fixtures)
+ assert_equal 1, conn.open_transactions
+ end
+ end
+
+ assert_nothing_raised do
+ conn = ActiveRecord::Base.connection
+ conn.execute("SELECT 1; SELECT 2;")
+ conn.raw_connection.abandon_results!
+ end
+ end
+ end
+ end
+
+ def test_bulk_insert_with_multi_statements_disabled
+ run_without_connection do |orig_connection|
+ ActiveRecord::Base.establish_connection(
+ orig_connection.merge(flags: [])
+ )
+
+ fixtures = {
+ "traffic_lights" => [
+ { "location" => "US", "state" => ["NY"], "long_state" => ["a"] },
+ ]
+ }
+
+ ActiveRecord::Base.connection.stub(:supports_set_server_option?, false) do
+ assert_raises(ActiveRecord::StatementInvalid) do
+ conn = ActiveRecord::Base.connection
+ conn.execute("SELECT 1; SELECT 2;")
+ conn.raw_connection.abandon_results!
+ end
+
+ assert_difference "TrafficLight.count" do
+ conn = ActiveRecord::Base.connection
+ conn.insert_fixtures_set(fixtures)
+ end
+
+ assert_raises(ActiveRecord::StatementInvalid) do
+ conn = ActiveRecord::Base.connection
+ conn.execute("SELECT 1; SELECT 2;")
+ conn.raw_connection.abandon_results!
+ end
+ end
+ end
+ end
+
def test_insert_fixtures_set_raises_an_error_when_max_allowed_packet_is_smaller_than_fixtures_set_size
conn = ActiveRecord::Base.connection
mysql_margin = 2
@@ -846,9 +936,9 @@ class TransactionalFixturesOnConnectionNotification < ActiveRecord::TestCase
def lock_thread=(lock_thread); end
end.new
- connection.expects(:begin_transaction).with(joinable: false)
-
- fire_connection_notification(connection)
+ assert_called_with(connection, :begin_transaction, [joinable: false]) do
+ fire_connection_notification(connection)
+ end
end
def test_notification_established_transactions_are_rolled_back
@@ -876,15 +966,16 @@ class TransactionalFixturesOnConnectionNotification < ActiveRecord::TestCase
private
def fire_connection_notification(connection)
- ActiveRecord::Base.connection_handler.stubs(:retrieve_connection).with("book").returns(connection)
- message_bus = ActiveSupport::Notifications.instrumenter
- payload = {
- spec_name: "book",
- config: nil,
- connection_id: connection.object_id
- }
+ assert_called_with(ActiveRecord::Base.connection_handler, :retrieve_connection, ["book"], returns: connection) do
+ message_bus = ActiveSupport::Notifications.instrumenter
+ payload = {
+ spec_name: "book",
+ config: nil,
+ connection_id: connection.object_id
+ }
- message_bus.instrument("!connection.active_record", payload) {}
+ message_bus.instrument("!connection.active_record", payload) {}
+ end
end
end
diff --git a/activerecord/test/cases/quoting_test.rb b/activerecord/test/cases/quoting_test.rb
index 92eb0c814f..f875dc96f7 100644
--- a/activerecord/test/cases/quoting_test.rb
+++ b/activerecord/test/cases/quoting_test.rb
@@ -71,8 +71,8 @@ module ActiveRecord
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 ", "")
+ expected = t.change(year: 2000, month: 1, day: 1)
+ expected = expected.getutc.to_s(:db).slice(11..-1)
assert_equal expected, @quoter.quoted_time(t)
end
@@ -89,6 +89,28 @@ module ActiveRecord
end
end
+ def test_quoted_time_dst_utc
+ with_timezone_config default: :utc do
+ t = Time.new(2000, 7, 1, 0, 0, 0, "+04:30")
+
+ expected = t.change(year: 2000, month: 1, day: 1)
+ expected = expected.getutc.to_s(:db).slice(11..-1)
+
+ assert_equal expected, @quoter.quoted_time(t)
+ end
+ end
+
+ def test_quoted_time_dst_local
+ with_timezone_config default: :local do
+ t = Time.new(2000, 7, 1, 0, 0, 0, "+04:30")
+
+ expected = t.change(year: 2000, month: 1, day: 1)
+ expected = expected.getlocal.to_s(:db).slice(11..-1)
+
+ 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)
diff --git a/activerecord/test/cases/scoping/relation_scoping_test.rb b/activerecord/test/cases/scoping/relation_scoping_test.rb
index f18f1ed981..544adc9b39 100644
--- a/activerecord/test/cases/scoping/relation_scoping_test.rb
+++ b/activerecord/test/cases/scoping/relation_scoping_test.rb
@@ -254,6 +254,11 @@ class RelationScopingTest < ActiveRecord::TestCase
end
end
+ def test_scoping_works_in_the_scope_block
+ expected = SpecialPostWithDefaultScope.unscoped.to_a
+ assert_equal expected, SpecialPostWithDefaultScope.unscoped_all
+ end
+
def test_circular_joins_with_scoping_does_not_crash
posts = Post.joins(comments: :post).scoping do
Post.first(10)
diff --git a/activerecord/test/cases/tasks/database_tasks_test.rb b/activerecord/test/cases/tasks/database_tasks_test.rb
index 80437d4400..1f79f1a630 100644
--- a/activerecord/test/cases/tasks/database_tasks_test.rb
+++ b/activerecord/test/cases/tasks/database_tasks_test.rb
@@ -15,6 +15,7 @@ module ActiveRecord
def charset; end
def collation; end
def structure_dump(*); end
+ def structure_load(*); end
end.new
)
@@ -119,8 +120,9 @@ module ActiveRecord
ADAPTERS_TASKS.each do |k, v|
define_method("test_#{k}_create") do
with_stubbed_new do
- eval("@#{v}").expects(:create)
- ActiveRecord::Tasks::DatabaseTasks.create "adapter" => k
+ assert_called(eval("@#{v}"), :create) do
+ ActiveRecord::Tasks::DatabaseTasks.create "adapter" => k
+ end
end
end
end
@@ -141,7 +143,6 @@ module ActiveRecord
def setup
@configurations = { "development" => { "database" => "my-db" } }
- ActiveRecord::Base.stubs(:configurations).returns(@configurations)
# To refrain from connecting to a newly created empty DB in sqlite3_mem tests
ActiveRecord::Base.connection_handler.stubs(:establish_connection)
@@ -156,52 +157,71 @@ module ActiveRecord
def test_ignores_configurations_without_databases
@configurations["development"].merge!("database" => nil)
- assert_not_called(ActiveRecord::Tasks::DatabaseTasks, :create) do
- ActiveRecord::Tasks::DatabaseTasks.create_all
+ with_stubbed_configurations do
+ assert_not_called(ActiveRecord::Tasks::DatabaseTasks, :create) do
+ ActiveRecord::Tasks::DatabaseTasks.create_all
+ end
end
end
def test_ignores_remote_databases
@configurations["development"].merge!("host" => "my.server.tld")
- $stderr.stubs(:puts)
- assert_not_called(ActiveRecord::Tasks::DatabaseTasks, :create) do
- ActiveRecord::Tasks::DatabaseTasks.create_all
+ with_stubbed_configurations do
+ assert_not_called(ActiveRecord::Tasks::DatabaseTasks, :create) do
+ ActiveRecord::Tasks::DatabaseTasks.create_all
+ end
end
end
def test_warning_for_remote_databases
@configurations["development"].merge!("host" => "my.server.tld")
- ActiveRecord::Tasks::DatabaseTasks.create_all
+ with_stubbed_configurations do
+ ActiveRecord::Tasks::DatabaseTasks.create_all
- assert_match "This task only modifies local databases. my-db is on a remote host.",
- $stderr.string
+ assert_match "This task only modifies local databases. my-db is on a remote host.",
+ $stderr.string
+ end
end
def test_creates_configurations_with_local_ip
@configurations["development"].merge!("host" => "127.0.0.1")
- assert_called(ActiveRecord::Tasks::DatabaseTasks, :create) do
- ActiveRecord::Tasks::DatabaseTasks.create_all
+ with_stubbed_configurations do
+ assert_called(ActiveRecord::Tasks::DatabaseTasks, :create) do
+ ActiveRecord::Tasks::DatabaseTasks.create_all
+ end
end
end
def test_creates_configurations_with_local_host
@configurations["development"].merge!("host" => "localhost")
- assert_called(ActiveRecord::Tasks::DatabaseTasks, :create) do
- ActiveRecord::Tasks::DatabaseTasks.create_all
+ with_stubbed_configurations do
+ assert_called(ActiveRecord::Tasks::DatabaseTasks, :create) do
+ ActiveRecord::Tasks::DatabaseTasks.create_all
+ end
end
end
def test_creates_configurations_with_blank_hosts
@configurations["development"].merge!("host" => nil)
- assert_called(ActiveRecord::Tasks::DatabaseTasks, :create) do
- ActiveRecord::Tasks::DatabaseTasks.create_all
+ with_stubbed_configurations do
+ assert_called(ActiveRecord::Tasks::DatabaseTasks, :create) do
+ ActiveRecord::Tasks::DatabaseTasks.create_all
+ end
end
end
+
+ private
+
+ def with_stubbed_configurations
+ ActiveRecord::Base.stub(:configurations, @configurations) do
+ yield
+ end
+ end
end
class DatabaseTasksCreateCurrentTest < ActiveRecord::TestCase
@@ -389,11 +409,15 @@ module ActiveRecord
def test_establishes_connection_for_the_given_environments_config
ActiveRecord::Tasks::DatabaseTasks.stub(:create, nil) do
- ActiveRecord::Base.expects(:establish_connection).with(:development)
-
- ActiveRecord::Tasks::DatabaseTasks.create_current(
- ActiveSupport::StringInquirer.new("development")
- )
+ assert_called_with(
+ ActiveRecord::Base,
+ :establish_connection,
+ [:development]
+ ) do
+ ActiveRecord::Tasks::DatabaseTasks.create_current(
+ ActiveSupport::StringInquirer.new("development")
+ )
+ end
end
end
@@ -414,8 +438,9 @@ module ActiveRecord
ADAPTERS_TASKS.each do |k, v|
define_method("test_#{k}_drop") do
with_stubbed_new do
- eval("@#{v}").expects(:drop)
- ActiveRecord::Tasks::DatabaseTasks.drop "adapter" => k
+ assert_called(eval("@#{v}"), :drop) do
+ ActiveRecord::Tasks::DatabaseTasks.drop "adapter" => k
+ end
end
end
end
@@ -794,8 +819,9 @@ module ActiveRecord
ADAPTERS_TASKS.each do |k, v|
define_method("test_#{k}_purge") do
with_stubbed_new do
- eval("@#{v}").expects(:purge)
- ActiveRecord::Tasks::DatabaseTasks.purge "adapter" => k
+ assert_called(eval("@#{v}"), :purge) do
+ ActiveRecord::Tasks::DatabaseTasks.purge "adapter" => k
+ end
end
end
end
@@ -809,11 +835,14 @@ module ActiveRecord
"production" => { "database" => "prod-db" }
}
ActiveRecord::Base.stub(:configurations, configurations) do
- ActiveRecord::Tasks::DatabaseTasks.expects(:purge).
- with("database" => "prod-db")
-
- assert_called_with(ActiveRecord::Base, :establish_connection, [:production]) do
- ActiveRecord::Tasks::DatabaseTasks.purge_current("production")
+ assert_called_with(
+ ActiveRecord::Tasks::DatabaseTasks,
+ :purge,
+ ["database" => "prod-db"]
+ ) do
+ assert_called_with(ActiveRecord::Base, :establish_connection, [:production]) do
+ ActiveRecord::Tasks::DatabaseTasks.purge_current("production")
+ end
end
end
end
@@ -823,10 +852,13 @@ module ActiveRecord
def test_purge_all_local_configurations
configurations = { development: { "database" => "my-db" } }
ActiveRecord::Base.stub(:configurations, configurations) do
- ActiveRecord::Tasks::DatabaseTasks.expects(:purge).
- with("database" => "my-db")
-
- ActiveRecord::Tasks::DatabaseTasks.purge_all
+ assert_called_with(
+ ActiveRecord::Tasks::DatabaseTasks,
+ :purge,
+ ["database" => "my-db"]
+ ) do
+ ActiveRecord::Tasks::DatabaseTasks.purge_all
+ end
end
end
end
@@ -837,8 +869,9 @@ module ActiveRecord
ADAPTERS_TASKS.each do |k, v|
define_method("test_#{k}_charset") do
with_stubbed_new do
- eval("@#{v}").expects(:charset)
- ActiveRecord::Tasks::DatabaseTasks.charset "adapter" => k
+ assert_called(eval("@#{v}"), :charset) do
+ ActiveRecord::Tasks::DatabaseTasks.charset "adapter" => k
+ end
end
end
end
@@ -850,8 +883,9 @@ module ActiveRecord
ADAPTERS_TASKS.each do |k, v|
define_method("test_#{k}_collation") do
with_stubbed_new do
- eval("@#{v}").expects(:collation)
- ActiveRecord::Tasks::DatabaseTasks.collation "adapter" => k
+ assert_called(eval("@#{v}"), :collation) do
+ ActiveRecord::Tasks::DatabaseTasks.collation "adapter" => k
+ end
end
end
end
@@ -965,8 +999,12 @@ module ActiveRecord
ADAPTERS_TASKS.each do |k, v|
define_method("test_#{k}_structure_dump") do
with_stubbed_new do
- eval("@#{v}").expects(:structure_dump).with("awesome-file.sql", nil)
- ActiveRecord::Tasks::DatabaseTasks.structure_dump({ "adapter" => k }, "awesome-file.sql")
+ assert_called_with(
+ eval("@#{v}"), :structure_dump,
+ ["awesome-file.sql", nil]
+ ) do
+ ActiveRecord::Tasks::DatabaseTasks.structure_dump({ "adapter" => k }, "awesome-file.sql")
+ end
end
end
end
@@ -978,8 +1016,13 @@ module ActiveRecord
ADAPTERS_TASKS.each do |k, v|
define_method("test_#{k}_structure_load") do
with_stubbed_new do
- eval("@#{v}").expects(:structure_load).with("awesome-file.sql", nil)
- ActiveRecord::Tasks::DatabaseTasks.structure_load({ "adapter" => k }, "awesome-file.sql")
+ assert_called_with(
+ eval("@#{v}"),
+ :structure_load,
+ ["awesome-file.sql", nil]
+ ) do
+ ActiveRecord::Tasks::DatabaseTasks.structure_load({ "adapter" => k }, "awesome-file.sql")
+ end
end
end
end
diff --git a/activerecord/test/cases/tasks/mysql_rake_test.rb b/activerecord/test/cases/tasks/mysql_rake_test.rb
index 6ed9fdb7fa..a86ddd3c16 100644
--- a/activerecord/test/cases/tasks/mysql_rake_test.rb
+++ b/activerecord/test/cases/tasks/mysql_rake_test.rb
@@ -31,28 +31,29 @@ if current_adapter?(:Mysql2Adapter)
def test_creates_database_with_no_default_options
with_stubbed_connection_establish_connection do
- @connection.expects(:create_database).
- with("my-app-db", {})
-
- ActiveRecord::Tasks::DatabaseTasks.create @configuration
+ assert_called_with(@connection, :create_database, ["my-app-db", {}]) do
+ ActiveRecord::Tasks::DatabaseTasks.create @configuration
+ end
end
end
def test_creates_database_with_given_encoding
with_stubbed_connection_establish_connection do
- @connection.expects(:create_database).
- with("my-app-db", charset: "latin1")
-
- ActiveRecord::Tasks::DatabaseTasks.create @configuration.merge("encoding" => "latin1")
+ assert_called_with(@connection, :create_database, ["my-app-db", charset: "latin1"]) do
+ ActiveRecord::Tasks::DatabaseTasks.create @configuration.merge("encoding" => "latin1")
+ end
end
end
def test_creates_database_with_given_collation
with_stubbed_connection_establish_connection do
- @connection.expects(:create_database).
- with("my-app-db", collation: "latin1_swedish_ci")
-
- ActiveRecord::Tasks::DatabaseTasks.create @configuration.merge("collation" => "latin1_swedish_ci")
+ assert_called_with(
+ @connection,
+ :create_database,
+ ["my-app-db", collation: "latin1_swedish_ci"]
+ ) do
+ ActiveRecord::Tasks::DatabaseTasks.create @configuration.merge("collation" => "latin1_swedish_ci")
+ end
end
end
@@ -90,7 +91,7 @@ if current_adapter?(:Mysql2Adapter)
private
def with_stubbed_connection_establish_connection
- ActiveRecord::Base.stub(:establish_connection, true) do
+ ActiveRecord::Base.stub(:establish_connection, nil) do
ActiveRecord::Base.stub(:connection, @connection) do
yield
end
@@ -116,11 +117,9 @@ if current_adapter?(:Mysql2Adapter)
end
def test_raises_error
- ActiveRecord::Base.stub(:connection, @connection) do
- ActiveRecord::Base.stub(:establish_connection, -> * { raise @error }) do
- assert_raises(Mysql2::Error, "Invalid permissions") do
- ActiveRecord::Tasks::DatabaseTasks.create @configuration
- end
+ ActiveRecord::Base.stub(:establish_connection, -> * { raise @error }) do
+ assert_raises(Mysql2::Error, "Invalid permissions") do
+ ActiveRecord::Tasks::DatabaseTasks.create @configuration
end
end
end
@@ -151,9 +150,9 @@ if current_adapter?(:Mysql2Adapter)
def test_drops_database
with_stubbed_connection_establish_connection do
- @connection.expects(:drop_database).with("my-app-db")
-
- ActiveRecord::Tasks::DatabaseTasks.drop @configuration
+ assert_called_with(@connection, :drop_database, ["my-app-db"]) do
+ ActiveRecord::Tasks::DatabaseTasks.drop @configuration
+ end
end
end
@@ -168,7 +167,7 @@ if current_adapter?(:Mysql2Adapter)
private
def with_stubbed_connection_establish_connection
- ActiveRecord::Base.stub(:establish_connection, true) do
+ ActiveRecord::Base.stub(:establish_connection, nil) do
ActiveRecord::Base.stub(:connection, @connection) do
yield
end
@@ -195,27 +194,29 @@ if current_adapter?(:Mysql2Adapter)
def test_recreates_database_with_no_default_options
with_stubbed_connection_establish_connection do
- @connection.expects(:recreate_database).
- with("test-db", {})
-
- ActiveRecord::Tasks::DatabaseTasks.purge @configuration
+ assert_called_with(@connection, :recreate_database, ["test-db", {}]) do
+ ActiveRecord::Tasks::DatabaseTasks.purge @configuration
+ end
end
end
def test_recreates_database_with_the_given_options
with_stubbed_connection_establish_connection do
- @connection.expects(:recreate_database).
- with("test-db", charset: "latin", collation: "latin1_swedish_ci")
-
- ActiveRecord::Tasks::DatabaseTasks.purge @configuration.merge(
- "encoding" => "latin", "collation" => "latin1_swedish_ci")
+ assert_called_with(
+ @connection,
+ :recreate_database,
+ ["test-db", charset: "latin", collation: "latin1_swedish_ci"]
+ ) do
+ ActiveRecord::Tasks::DatabaseTasks.purge @configuration.merge(
+ "encoding" => "latin", "collation" => "latin1_swedish_ci")
+ end
end
end
private
def with_stubbed_connection_establish_connection
- ActiveRecord::Base.stub(:establish_connection, true) do
+ ActiveRecord::Base.stub(:establish_connection, nil) do
ActiveRecord::Base.stub(:connection, @connection) do
yield
end
@@ -234,8 +235,9 @@ if current_adapter?(:Mysql2Adapter)
def test_db_retrieves_charset
ActiveRecord::Base.stub(:connection, @connection) do
- @connection.expects(:charset)
- ActiveRecord::Tasks::DatabaseTasks.charset @configuration
+ assert_called(@connection, :charset) do
+ ActiveRecord::Tasks::DatabaseTasks.charset @configuration
+ end
end
end
end
@@ -251,8 +253,9 @@ if current_adapter?(:Mysql2Adapter)
def test_db_retrieves_collation
ActiveRecord::Base.stub(:connection, @connection) do
- @connection.expects(:collation)
- ActiveRecord::Tasks::DatabaseTasks.collation @configuration
+ assert_called_with(@connection, :collation) do
+ ActiveRecord::Tasks::DatabaseTasks.collation @configuration
+ end
end
end
end
diff --git a/activerecord/test/cases/tasks/postgresql_rake_test.rb b/activerecord/test/cases/tasks/postgresql_rake_test.rb
index 1dcb06ceff..289847e514 100644
--- a/activerecord/test/cases/tasks/postgresql_rake_test.rb
+++ b/activerecord/test/cases/tasks/postgresql_rake_test.rb
@@ -34,30 +34,46 @@ if current_adapter?(:PostgreSQLAdapter)
def test_creates_database_with_default_encoding
with_stubbed_connection_establish_connection do
- @connection.expects(:create_database).
- with("my-app-db", @configuration.merge("encoding" => "utf8"))
-
- ActiveRecord::Tasks::DatabaseTasks.create @configuration
+ assert_called_with(
+ @connection,
+ :create_database,
+ ["my-app-db", @configuration.merge("encoding" => "utf8")]
+ ) do
+ ActiveRecord::Tasks::DatabaseTasks.create @configuration
+ end
end
end
def test_creates_database_with_given_encoding
with_stubbed_connection_establish_connection do
- @connection.expects(:create_database).
- with("my-app-db", @configuration.merge("encoding" => "latin"))
-
- ActiveRecord::Tasks::DatabaseTasks.create @configuration.
- merge("encoding" => "latin")
+ assert_called_with(
+ @connection,
+ :create_database,
+ ["my-app-db", @configuration.merge("encoding" => "latin")]
+ ) do
+ ActiveRecord::Tasks::DatabaseTasks.create @configuration.
+ merge("encoding" => "latin")
+ end
end
end
def test_creates_database_with_given_collation_and_ctype
with_stubbed_connection_establish_connection do
- @connection.expects(:create_database).
- with("my-app-db", @configuration.merge("encoding" => "utf8", "collation" => "ja_JP.UTF8", "ctype" => "ja_JP.UTF8"))
-
- ActiveRecord::Tasks::DatabaseTasks.create @configuration.
- merge("collation" => "ja_JP.UTF8", "ctype" => "ja_JP.UTF8")
+ assert_called_with(
+ @connection,
+ :create_database,
+ [
+ "my-app-db",
+ @configuration.merge(
+ "encoding" => "utf8",
+ "collation" => "ja_JP.UTF8",
+ "ctype" => "ja_JP.UTF8"
+ )
+ ]
+ ) do
+ ActiveRecord::Tasks::DatabaseTasks.create @configuration.
+ merge("collation" => "ja_JP.UTF8", "ctype" => "ja_JP.UTF8")
+ end
end
end
@@ -139,9 +155,13 @@ if current_adapter?(:PostgreSQLAdapter)
def test_drops_database
with_stubbed_connection_establish_connection do
- @connection.expects(:drop_database).with("my-app-db")
-
- ActiveRecord::Tasks::DatabaseTasks.drop @configuration
+ assert_called_with(
+ @connection,
+ :drop_database,
+ ["my-app-db"]
+ ) do
+ ActiveRecord::Tasks::DatabaseTasks.drop @configuration
+ end
end
end
@@ -179,9 +199,9 @@ if current_adapter?(:PostgreSQLAdapter)
def test_clears_active_connections
with_stubbed_connection do
ActiveRecord::Base.stub(:establish_connection, nil) do
- ActiveRecord::Base.expects(:clear_active_connections!)
-
- ActiveRecord::Tasks::DatabaseTasks.purge @configuration
+ assert_called(ActiveRecord::Base, :clear_active_connections!) do
+ ActiveRecord::Tasks::DatabaseTasks.purge @configuration
+ end
end
end
end
@@ -202,20 +222,23 @@ if current_adapter?(:PostgreSQLAdapter)
def test_drops_database
with_stubbed_connection do
ActiveRecord::Base.stub(:establish_connection, nil) do
- @connection.expects(:drop_database).with("my-app-db")
-
- ActiveRecord::Tasks::DatabaseTasks.purge @configuration
+ assert_called_with(@connection, :drop_database, ["my-app-db"]) do
+ ActiveRecord::Tasks::DatabaseTasks.purge @configuration
+ end
end
end
end
def test_creates_database
with_stubbed_connection do
- ActiveRecord::Base.stub(:establish_connection, true) do
- @connection.expects(:create_database).
- with("my-app-db", @configuration.merge("encoding" => "utf8"))
-
- ActiveRecord::Tasks::DatabaseTasks.purge @configuration
+ ActiveRecord::Base.stub(:establish_connection, nil) do
+ assert_called_with(
+ @connection,
+ :create_database,
+ ["my-app-db", @configuration.merge("encoding" => "utf8")]
+ ) do
+ ActiveRecord::Tasks::DatabaseTasks.purge @configuration
+ end
end
end
end
@@ -240,7 +263,10 @@ if current_adapter?(:PostgreSQLAdapter)
class PostgreSQLDBCharsetTest < ActiveRecord::TestCase
def setup
- @connection = Class.new { def create_database(*); end }.new
+ @connection = Class.new do
+ def create_database(*); end
+ def encoding; end
+ end.new
@configuration = {
"adapter" => "postgresql",
"database" => "my-app-db"
@@ -249,16 +275,16 @@ if current_adapter?(:PostgreSQLAdapter)
def test_db_retrieves_charset
ActiveRecord::Base.stub(:connection, @connection) do
- @connection.expects(:encoding)
-
- ActiveRecord::Tasks::DatabaseTasks.charset @configuration
+ assert_called(@connection, :encoding) do
+ ActiveRecord::Tasks::DatabaseTasks.charset @configuration
+ end
end
end
end
class PostgreSQLDBCollationTest < ActiveRecord::TestCase
def setup
- @connection = Class.new { def create_database(*); end }.new
+ @connection = Class.new { def collation; end }.new
@configuration = {
"adapter" => "postgresql",
"database" => "my-app-db"
@@ -267,9 +293,9 @@ if current_adapter?(:PostgreSQLAdapter)
def test_db_retrieves_collation
ActiveRecord::Base.stub(:connection, @connection) do
- @connection.expects(:collation)
-
- ActiveRecord::Tasks::DatabaseTasks.collation @configuration
+ assert_called(@connection, :collation) do
+ ActiveRecord::Tasks::DatabaseTasks.collation @configuration
+ end
end
end
end
diff --git a/activerecord/test/cases/tasks/sqlite_rake_test.rb b/activerecord/test/cases/tasks/sqlite_rake_test.rb
index 2fe7f54b17..7eb062b456 100644
--- a/activerecord/test/cases/tasks/sqlite_rake_test.rb
+++ b/activerecord/test/cases/tasks/sqlite_rake_test.rb
@@ -22,7 +22,7 @@ if current_adapter?(:SQLite3Adapter)
end
def test_db_checks_database_exists
- ActiveRecord::Base.stub(:establish_connection, true) do
+ ActiveRecord::Base.stub(:establish_connection, nil) do
assert_called_with(File, :exist?, [@database], returns: false) do
ActiveRecord::Tasks::DatabaseTasks.create @configuration, "/rails/root"
end
@@ -30,7 +30,7 @@ if current_adapter?(:SQLite3Adapter)
end
def test_when_db_created_successfully_outputs_info_to_stdout
- ActiveRecord::Base.stub(:establish_connection, true) do
+ ActiveRecord::Base.stub(:establish_connection, nil) do
ActiveRecord::Tasks::DatabaseTasks.create @configuration, "/rails/root"
assert_equal "Created database '#{@database}'\n", $stdout.string
@@ -54,10 +54,8 @@ if current_adapter?(:SQLite3Adapter)
end
def test_db_create_establishes_a_connection
- File.stub(:exist?, false) do
- assert_called_with(ActiveRecord::Base, :establish_connection, [@configuration]) do
- ActiveRecord::Tasks::DatabaseTasks.create @configuration, "/rails/root"
- end
+ assert_called_with(ActiveRecord::Base, :establish_connection, [@configuration]) do
+ ActiveRecord::Tasks::DatabaseTasks.create @configuration, "/rails/root"
end
end
@@ -90,9 +88,9 @@ if current_adapter?(:SQLite3Adapter)
end
def test_creates_path_from_database
- Pathname.expects(:new).with(@database).returns(@path)
-
- ActiveRecord::Tasks::DatabaseTasks.drop @configuration, "/rails/root"
+ assert_called_with(Pathname, :new, [@database], returns: @path) do
+ ActiveRecord::Tasks::DatabaseTasks.drop @configuration, "/rails/root"
+ end
end
def test_removes_file_with_absolute_path
diff --git a/activerecord/test/models/post.rb b/activerecord/test/models/post.rb
index 54eb5e6783..640cdb33b4 100644
--- a/activerecord/test/models/post.rb
+++ b/activerecord/test/models/post.rb
@@ -253,6 +253,7 @@ class SpecialPostWithDefaultScope < ActiveRecord::Base
self.inheritance_column = :disabled
self.table_name = "posts"
default_scope { where(id: [1, 5, 6]) }
+ scope :unscoped_all, -> { unscoped { all } }
end
class PostThatLoadsCommentsInAnAfterSaveHook < ActiveRecord::Base
diff --git a/activestorage/app/models/active_storage/attachment.rb b/activestorage/app/models/active_storage/attachment.rb
index bb80799044..5d36990fb9 100644
--- a/activestorage/app/models/active_storage/attachment.rb
+++ b/activestorage/app/models/active_storage/attachment.rb
@@ -17,13 +17,13 @@ class ActiveStorage::Attachment < ActiveRecord::Base
after_create_commit :analyze_blob_later, :identify_blob
after_destroy_commit :purge_dependent_blob_later
- # Synchronously purges the blob (deletes it from the configured service) and deletes the attachment.
+ # Synchronously deletes the attachment and {purges the blob}[rdoc-ref:ActiveStorage::Blob#purge].
def purge
blob.purge
delete
end
- # Deletes the attachment and queues a background job to purge the blob (delete it from the configured service).
+ # Deletes the attachment and {enqueues a background job}[rdoc-ref:ActiveStorage::Blob#purge_later] to purge the blob.
def purge_later
blob.purge_later
delete
diff --git a/activestorage/app/models/active_storage/blob.rb b/activestorage/app/models/active_storage/blob.rb
index 86f3dba524..b72f2e796d 100644
--- a/activestorage/app/models/active_storage/blob.rb
+++ b/activestorage/app/models/active_storage/blob.rb
@@ -193,8 +193,8 @@ class ActiveStorage::Blob < ActiveRecord::Base
end
- # Deletes the file on the service that's associated with this blob. This should only be done if the blob is going to be
- # deleted as well or you will essentially have a dead reference. It's recommended to use the +#purge+ and +#purge_later+
+ # Deletes the files on the service associated with the blob. This should only be done if the blob is going to be
+ # deleted as well or you will essentially have a dead reference. It's recommended to use #purge and #purge_later
# methods in most circumstances.
def delete
service.delete(key)
@@ -203,14 +203,14 @@ class ActiveStorage::Blob < ActiveRecord::Base
# Deletes the file on the service and then destroys the blob record. This is the recommended way to dispose of unwanted
# blobs. Note, though, that deleting the file off the service will initiate a HTTP connection to the service, which may
- # be slow or prevented, so you should not use this method inside a transaction or in callbacks. Use +#purge_later+ instead.
+ # be slow or prevented, so you should not use this method inside a transaction or in callbacks. Use #purge_later instead.
def purge
delete
destroy
end
- # Enqueues an ActiveStorage::PurgeJob job that'll call +purge+. This is the recommended way to purge blobs when the call
- # needs to be made from a transaction, a callback, or any other real-time scenario.
+ # Enqueues an ActiveStorage::PurgeJob to call #purge. This is the recommended way to purge blobs from a transaction,
+ # an Active Record callback, or in any other real-time scenario.
def purge_later
ActiveStorage::PurgeJob.perform_later(self)
end
diff --git a/activestorage/lib/active_storage/attached/changes/create_many.rb b/activestorage/lib/active_storage/attached/changes/create_many.rb
index af19328a61..a7a8553e0f 100644
--- a/activestorage/lib/active_storage/attached/changes/create_many.rb
+++ b/activestorage/lib/active_storage/attached/changes/create_many.rb
@@ -21,7 +21,8 @@ module ActiveStorage
end
def save
- record.public_send("#{name}_attachments=", attachments)
+ assign_associated_attachments
+ reset_associated_blobs
end
private
@@ -32,5 +33,14 @@ module ActiveStorage
def build_subchange_from(attachable)
ActiveStorage::Attached::Changes::CreateOneOfMany.new(name, record, attachable)
end
+
+
+ def assign_associated_attachments
+ record.public_send("#{name}_attachments=", attachments)
+ end
+
+ def reset_associated_blobs
+ record.public_send("#{name}_blobs").reset
+ end
end
end
diff --git a/activestorage/lib/active_storage/attached/changes/delete_many.rb b/activestorage/lib/active_storage/attached/changes/delete_many.rb
index 5c7fe385de..6cbd1158dc 100644
--- a/activestorage/lib/active_storage/attached/changes/delete_many.rb
+++ b/activestorage/lib/active_storage/attached/changes/delete_many.rb
@@ -12,6 +12,10 @@ module ActiveStorage
ActiveStorage::Attachment.none
end
+ def blobs
+ ActiveStorage::Blob.none
+ end
+
def save
record.public_send("#{name}_attachments=", [])
end
diff --git a/activestorage/test/models/attached/many_test.rb b/activestorage/test/models/attached/many_test.rb
index fc89dae658..82be88af08 100644
--- a/activestorage/test/models/attached/many_test.rb
+++ b/activestorage/test/models/attached/many_test.rb
@@ -106,6 +106,17 @@ class ActiveStorage::ManyAttachedTest < ActiveSupport::TestCase
assert_equal "video.mp4", @user.highlights.second.filename.to_s
end
+ test "attaching existing blobs to an existing record one at a time" do
+ @user.highlights.attach create_blob(filename: "funky.jpg")
+ @user.highlights.attach create_blob(filename: "town.jpg")
+ assert_equal "funky.jpg", @user.highlights.first.filename.to_s
+ assert_equal "town.jpg", @user.highlights.second.filename.to_s
+
+ @user.reload
+ assert_equal "funky.jpg", @user.highlights.first.filename.to_s
+ assert_equal "town.jpg", @user.highlights.second.filename.to_s
+ end
+
test "updating an existing record to attach existing blobs" do
@user.update! highlights: [ create_file_blob(filename: "racecar.jpg"), create_file_blob(filename: "video.mp4") ]
assert_equal "racecar.jpg", @user.highlights.first.filename.to_s
@@ -138,6 +149,40 @@ class ActiveStorage::ManyAttachedTest < ActiveSupport::TestCase
assert_not ActiveStorage::Blob.service.exist?(@user.highlights.second.key)
end
+ test "replacing existing, dependent attachments on an existing record via assign and attach" do
+ [ create_blob(filename: "funky.jpg"), create_blob(filename: "town.jpg") ].tap do |old_blobs|
+ @user.highlights.attach old_blobs
+
+ @user.highlights = []
+ assert_not @user.highlights.attached?
+
+ perform_enqueued_jobs do
+ @user.highlights.attach create_blob(filename: "whenever.jpg"), create_blob(filename: "wherever.jpg")
+ end
+
+ assert_equal "whenever.jpg", @user.highlights.first.filename.to_s
+ assert_equal "wherever.jpg", @user.highlights.second.filename.to_s
+ assert_not ActiveStorage::Blob.exists?(old_blobs.first.id)
+ assert_not ActiveStorage::Blob.exists?(old_blobs.second.id)
+ assert_not ActiveStorage::Blob.service.exist?(old_blobs.first.key)
+ assert_not ActiveStorage::Blob.service.exist?(old_blobs.second.key)
+ end
+ end
+
+ test "replacing existing, independent attachments on an existing record via assign and attach" do
+ @user.vlogs.attach create_blob(filename: "funky.mp4"), create_blob(filename: "town.mp4")
+
+ @user.vlogs = []
+ assert_not @user.vlogs.attached?
+
+ assert_no_enqueued_jobs only: ActiveStorage::PurgeJob do
+ @user.vlogs.attach create_blob(filename: "whenever.mp4"), create_blob(filename: "wherever.mp4")
+ end
+
+ assert_equal "whenever.mp4", @user.vlogs.first.filename.to_s
+ assert_equal "wherever.mp4", @user.vlogs.second.filename.to_s
+ end
+
test "successfully updating an existing record to replace existing, dependent attachments" do
[ create_blob(filename: "funky.jpg"), create_blob(filename: "town.jpg") ].tap do |old_blobs|
@user.highlights.attach old_blobs
@@ -195,21 +240,21 @@ class ActiveStorage::ManyAttachedTest < ActiveSupport::TestCase
end
end
- test "successfully updating an existing record to remove dependent attachments" do
+ test "updating an existing record to remove dependent attachments" do
[ create_blob(filename: "funky.jpg"), create_blob(filename: "town.jpg") ].tap do |blobs|
@user.highlights.attach blobs
- perform_enqueued_jobs do
- @user.update! highlights: []
+ assert_enqueued_with job: ActiveStorage::PurgeJob, args: [ blobs.first ] do
+ assert_enqueued_with job: ActiveStorage::PurgeJob, args: [ blobs.second ] do
+ @user.update! highlights: []
+ end
end
assert_not @user.highlights.attached?
- assert_not ActiveStorage::Blob.service.exist?(blobs.first.key)
- assert_not ActiveStorage::Blob.service.exist?(blobs.second.key)
end
end
- test "successfully updating an existing record to remove independent attachments" do
+ test "updating an existing record to remove independent attachments" do
[ create_blob(filename: "funky.mp4"), create_blob(filename: "town.mp4") ].tap do |blobs|
@user.vlogs.attach blobs
@@ -218,8 +263,6 @@ class ActiveStorage::ManyAttachedTest < ActiveSupport::TestCase
end
assert_not @user.vlogs.attached?
- assert ActiveStorage::Blob.service.exist?(blobs.first.key)
- assert ActiveStorage::Blob.service.exist?(blobs.second.key)
end
end
diff --git a/activestorage/test/models/attached/one_test.rb b/activestorage/test/models/attached/one_test.rb
index 33f9122644..01caaf0b55 100644
--- a/activestorage/test/models/attached/one_test.rb
+++ b/activestorage/test/models/attached/one_test.rb
@@ -195,11 +195,35 @@ class ActiveStorage::OneAttachedTest < ActiveSupport::TestCase
end
end
- test "successfully updating an existing record to remove a dependent attachment" do
+ test "removing a dependent attachment from an existing record" do
create_blob(filename: "funky.jpg").tap do |blob|
@user.avatar.attach blob
- perform_enqueued_jobs do
+ assert_enqueued_with job: ActiveStorage::PurgeJob, args: [ blob ] do
+ @user.avatar.attach nil
+ end
+
+ assert_not @user.avatar.attached?
+ end
+ end
+
+ test "removing an independent attachment from an existing record" do
+ create_blob(filename: "funky.jpg").tap do |blob|
+ @user.cover_photo.attach blob
+
+ assert_no_enqueued_jobs only: ActiveStorage::PurgeJob do
+ @user.cover_photo.attach nil
+ end
+
+ assert_not @user.cover_photo.attached?
+ end
+ end
+
+ test "updating an existing record to remove a dependent attachment" do
+ create_blob(filename: "funky.jpg").tap do |blob|
+ @user.avatar.attach blob
+
+ assert_enqueued_with job: ActiveStorage::PurgeJob, args: [ blob ] do
@user.update! avatar: nil
end
@@ -207,7 +231,7 @@ class ActiveStorage::OneAttachedTest < ActiveSupport::TestCase
end
end
- test "successfully updating an existing record to remove an independent attachment" do
+ test "updating an existing record to remove an independent attachment" do
create_blob(filename: "funky.jpg").tap do |blob|
@user.cover_photo.attach blob
@@ -216,7 +240,6 @@ class ActiveStorage::OneAttachedTest < ActiveSupport::TestCase
end
assert_not @user.cover_photo.attached?
- assert ActiveStorage::Blob.service.exist?(blob.key)
end
end
diff --git a/guides/source/active_record_validations.md b/guides/source/active_record_validations.md
index c7846a0283..3c51313906 100644
--- a/guides/source/active_record_validations.md
+++ b/guides/source/active_record_validations.md
@@ -1133,24 +1133,6 @@ person.errors.full_messages
# => ["Name cannot contain the characters !@#%*()_-+="]
```
-An equivalent to `errors#add` is to use `<<` to append a message to the `errors.messages` array for an attribute:
-
-```ruby
- class Person < ApplicationRecord
- def a_method_used_for_validation_purposes
- errors.messages[:name] << "cannot contain the characters !@#%*()_-+="
- end
- end
-
- person = Person.create(name: "!@#")
-
- person.errors[:name]
- # => ["cannot contain the characters !@#%*()_-+="]
-
- person.errors.to_a
- # => ["Name cannot contain the characters !@#%*()_-+="]
-```
-
### `errors.details`
You can specify a validator type to the returned error details hash using the
diff --git a/guides/source/association_basics.md b/guides/source/association_basics.md
index e7408b5a7f..67844ae7a4 100644
--- a/guides/source/association_basics.md
+++ b/guides/source/association_basics.md
@@ -96,7 +96,7 @@ end
![belongs_to Association Diagram](images/association_basics/belongs_to.png)
-NOTE: `belongs_to` associations _must_ use the singular term. If you used the pluralized form in the above example for the `author` association in the `Book` model, you would be told that there was an "uninitialized constant Book::Authors". This is because Rails automatically infers the class name from the association name. If the association name is wrongly pluralized, then the inferred class will be wrongly pluralized too.
+NOTE: `belongs_to` associations _must_ use the singular term. If you used the pluralized form in the above example for the `author` association in the `Book` model and tried to create the instance by `Book.create(authors: @author)`, you would be told that there was an "uninitialized constant Book::Authors". This is because Rails automatically infers the class name from the association name. If the association name is wrongly pluralized, then the inferred class will be wrongly pluralized too.
The corresponding migration might look like this:
diff --git a/railties/lib/rails/commands/notes/notes_command.rb b/railties/lib/rails/commands/notes/notes_command.rb
index a0faaeff8f..64b339b3cd 100644
--- a/railties/lib/rails/commands/notes/notes_command.rb
+++ b/railties/lib/rails/commands/notes/notes_command.rb
@@ -28,7 +28,7 @@ module Rails
def deprecation_warning
return if source_annotation_directories.empty?
- ActiveSupport::Deprecation.warn("`SOURCE_ANNOTATION_DIRECTORIES` will be deprecated in Rails 6.1. You can add default directories by using config.annotations.register_directories instead.")
+ ActiveSupport::Deprecation.warn("`SOURCE_ANNOTATION_DIRECTORIES` is deprecated and will be removed in Rails 6.1. You can add default directories by using config.annotations.register_directories instead.")
end
def source_annotation_directories
diff --git a/railties/test/application/rake/notes_test.rb b/railties/test/application/rake/notes_test.rb
index d73e5cdfa3..999155fa93 100644
--- a/railties/test/application/rake/notes_test.rb
+++ b/railties/test/application/rake/notes_test.rb
@@ -87,6 +87,9 @@ module ApplicationTests
assert_equal 6, lines.size
assert_equal [4], lines.map(&:size).uniq
+
+ log = File.read(Rails.application.config.paths["log"].first)
+ assert_match(/`SOURCE_ANNOTATION_DIRECTORIES` is deprecated/, log)
end
end