diff options
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 |