diff options
40 files changed, 230 insertions, 145 deletions
diff --git a/.github/autolabeler.yml b/.github/autolabeler.yml index c8033d8c30..d73b2e3362 100644 --- a/.github/autolabeler.yml +++ b/.github/autolabeler.yml @@ -4,8 +4,6 @@ actionmailer: - "actionmailer/**/*" actionpack: - "actionpack/**/*" -routing: - - "actionpack/**/*routing*" actionview: - "actionview/**/*" activejob: @@ -14,12 +12,6 @@ activemodel: - "activemodel/**/*" activerecord: - "activerecord/**/*" -MySQL: - - "activerecord/**/*mysql*" -PostgreSQL: - - "activerecord/**/*postgresql*" -enum: - - "activerecord/**/*enum*" activestorage: - "activestorage/**/*" activesupport: @@ -28,19 +20,5 @@ rails-ujs: - "actionview/app/assets/javascripts/rails-ujs*/*" railties: - "railties/**/*" -engines: - - "railties/lib/rails/engine/**/*" - - "railties/test/railties/**/*engine*" docs: - "guides/**/*" -asset pipeline: - - "guides/source/asset_pipeline.md" -ci issues: - - "ci/**/*" -security: - - "**/*security*" - - "**/*secure*" - - "**/*sanitize*" - - "**/*sanitization*" -i18n: - - "**/*i18n*" diff --git a/.rubocop.yml b/.rubocop.yml index 33791588cb..615a229432 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -8,6 +8,7 @@ AllCops: - '**/vendor/**/*' - 'actionpack/lib/action_dispatch/journey/parser.rb' - 'railties/test/fixtures/tmp/**/*' + - 'node_modules/**/*' Performance: Exclude: diff --git a/Gemfile.lock b/Gemfile.lock index 727489b50a..b4f46698bc 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -339,12 +339,12 @@ GEM mysql2 (0.5.2-x86-mingw32) nio4r (2.3.1) nio4r (2.3.1-java) - nokogiri (1.8.4) + nokogiri (1.8.5) mini_portile2 (~> 2.3.0) - nokogiri (1.8.4-java) - nokogiri (1.8.4-x64-mingw32) + nokogiri (1.8.5-java) + nokogiri (1.8.5-x64-mingw32) mini_portile2 (~> 2.3.0) - nokogiri (1.8.4-x86-mingw32) + nokogiri (1.8.5-x86-mingw32) mini_portile2 (~> 2.3.0) os (1.0.0) parallel (1.12.1) diff --git a/actionmailer/CHANGELOG.md b/actionmailer/CHANGELOG.md index 1aa7485d3e..db6fc7ee9c 100644 --- a/actionmailer/CHANGELOG.md +++ b/actionmailer/CHANGELOG.md @@ -1,3 +1,14 @@ +* Allow ActionMailer classes to configure the parameterized delivery job + Example: + ``` + class MyMailer < ApplicationMailer + self.parameterized_delivery_job = MyCustomDeliveryJob + ... + end + ``` + + *Luke Pearce* + * `ActionDispatch::IntegrationTest` includes `ActionMailer::TestHelper` module by default. *Ricardo Díaz* diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index 02e5ac2a3e..509d859ac3 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -462,6 +462,7 @@ module ActionMailer helper ActionMailer::MailHelper class_attribute :delivery_job, default: ::ActionMailer::DeliveryJob + class_attribute :parameterized_delivery_job, default: ::ActionMailer::Parameterized::DeliveryJob class_attribute :default_params, default: { mime_version: "1.0", charset: "UTF-8", diff --git a/actionmailer/lib/action_mailer/parameterized.rb b/actionmailer/lib/action_mailer/parameterized.rb index 5e768e7106..0fe417affe 100644 --- a/actionmailer/lib/action_mailer/parameterized.rb +++ b/actionmailer/lib/action_mailer/parameterized.rb @@ -140,7 +140,8 @@ module ActionMailer super else args = @mailer_class.name, @action.to_s, delivery_method.to_s, @params, *@args - ActionMailer::Parameterized::DeliveryJob.set(options).perform_later(*args) + job = @mailer_class.parameterized_delivery_job + job.set(options).perform_later(*args) end end end diff --git a/actionmailer/test/parameterized_test.rb b/actionmailer/test/parameterized_test.rb index ec6c5e9e67..d3b34fc3e3 100644 --- a/actionmailer/test/parameterized_test.rb +++ b/actionmailer/test/parameterized_test.rb @@ -53,4 +53,17 @@ class ParameterizedTest < ActiveSupport::TestCase invitation = mailer.method(:anything) end end + + test "should enqueue a parameterized request with the correct delivery job" do + old_delivery_job = ParamsMailer.parameterized_delivery_job + ParamsMailer.parameterized_delivery_job = ParameterizedDummyJob + + assert_performed_with(job: ParameterizedDummyJob, args: ["ParamsMailer", "invitation", "deliver_now", { inviter: "david@basecamp.com", invitee: "jason@basecamp.com" } ]) do + @mail.deliver_later + end + + ParamsMailer.parameterized_delivery_job = old_delivery_job + end + + class ParameterizedDummyJob < ActionMailer::Parameterized::DeliveryJob; end end diff --git a/actionpack/lib/abstract_controller/railties/routes_helpers.rb b/actionpack/lib/abstract_controller/railties/routes_helpers.rb index b6e5631a4e..fbd93705ed 100644 --- a/actionpack/lib/abstract_controller/railties/routes_helpers.rb +++ b/actionpack/lib/abstract_controller/railties/routes_helpers.rb @@ -7,7 +7,7 @@ module AbstractController Module.new do define_method(:inherited) do |klass| super(klass) - if namespace = klass.parents.detect { |m| m.respond_to?(:railtie_routes_url_helpers) } + if namespace = klass.module_parents.detect { |m| m.respond_to?(:railtie_routes_url_helpers) } klass.include(namespace.railtie_routes_url_helpers(include_path_helpers)) else klass.include(routes.url_helpers(include_path_helpers)) diff --git a/activejob/lib/active_job/core.rb b/activejob/lib/active_job/core.rb index 61d402cfca..62bb5861bb 100644 --- a/activejob/lib/active_job/core.rb +++ b/activejob/lib/active_job/core.rb @@ -6,35 +6,33 @@ module ActiveJob module Core extend ActiveSupport::Concern - included do - # Job arguments - attr_accessor :arguments - attr_writer :serialized_arguments + # Job arguments + attr_accessor :arguments + attr_writer :serialized_arguments - # Timestamp when the job should be performed - attr_accessor :scheduled_at + # Timestamp when the job should be performed + attr_accessor :scheduled_at - # Job Identifier - attr_accessor :job_id + # Job Identifier + attr_accessor :job_id - # Queue in which the job will reside. - attr_writer :queue_name + # Queue in which the job will reside. + attr_writer :queue_name - # Priority that the job will have (lower is more priority). - attr_writer :priority + # Priority that the job will have (lower is more priority). + attr_writer :priority - # ID optionally provided by adapter - attr_accessor :provider_job_id + # ID optionally provided by adapter + attr_accessor :provider_job_id - # Number of times this job has been executed (which increments on every retry, like after an exception). - attr_accessor :executions + # Number of times this job has been executed (which increments on every retry, like after an exception). + attr_accessor :executions - # I18n.locale to be used during the job. - attr_accessor :locale + # I18n.locale to be used during the job. + attr_accessor :locale - # Timezone to be used during the job. - attr_accessor :timezone - end + # Timezone to be used during the job. + attr_accessor :timezone # These methods will be included into any Active Job object, adding # helpers for de/serialization and creation of job instances. diff --git a/activerecord/test/cases/associations/belongs_to_associations_test.rb b/activerecord/test/cases/associations/belongs_to_associations_test.rb index 1fca1be181..d520919332 100644 --- a/activerecord/test/cases/associations/belongs_to_associations_test.rb +++ b/activerecord/test/cases/associations/belongs_to_associations_test.rb @@ -694,7 +694,7 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase line_item = LineItem.create! Invoice.create!(line_items: [line_item]) - assert_queries(0) { line_item.save } + assert_no_queries { line_item.save } end def test_belongs_to_with_touch_option_on_destroy @@ -789,7 +789,7 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase def test_dont_find_target_when_foreign_key_is_null tagging = taggings(:thinking_general) - assert_queries(0) { tagging.super_tag } + assert_no_queries { tagging.super_tag } end def test_dont_find_target_when_saving_foreign_key_after_stale_association_loaded diff --git a/activerecord/test/cases/associations/eager_load_nested_include_test.rb b/activerecord/test/cases/associations/eager_load_nested_include_test.rb index c5b2b77bd4..525ad3197a 100644 --- a/activerecord/test/cases/associations/eager_load_nested_include_test.rb +++ b/activerecord/test/cases/associations/eager_load_nested_include_test.rb @@ -92,7 +92,7 @@ class EagerLoadPolyAssocsTest < ActiveRecord::TestCase def test_include_query res = ShapeExpression.all.merge!(includes: [ :shape, { paint: :non_poly } ]).to_a assert_equal NUM_SHAPE_EXPRESSIONS, res.size - assert_queries(0) do + assert_no_queries do res.each do |se| assert_not_nil se.paint.non_poly, "this is the association that was loading incorrectly before the change" assert_not_nil se.shape, "just making sure other associations still work" diff --git a/activerecord/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb index 79b3b4a6ad..39034746c9 100644 --- a/activerecord/test/cases/associations/eager_test.rb +++ b/activerecord/test/cases/associations/eager_test.rb @@ -1346,7 +1346,7 @@ class EagerAssociationTest < ActiveRecord::TestCase def test_joins_with_includes_should_preload_via_joins post = assert_queries(1) { Post.includes(:comments).joins(:comments).order("posts.id desc").to_a.first } - assert_queries(0) do + assert_no_queries do assert_not_equal 0, post.comments.to_a.count end end diff --git a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb index 482302055d..a90dcc0576 100644 --- a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb @@ -310,7 +310,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase def test_build devel = Developer.find(1) - proj = assert_no_queries(ignore_none: false) { devel.projects.build("name" => "Projekt") } + proj = assert_no_queries { devel.projects.build("name" => "Projekt") } assert_not_predicate devel.projects, :loaded? assert_equal devel.projects.last, proj @@ -325,7 +325,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase def test_new_aliased_to_build devel = Developer.find(1) - proj = assert_no_queries(ignore_none: false) { devel.projects.new("name" => "Projekt") } + proj = assert_no_queries { devel.projects.new("name" => "Projekt") } assert_not_predicate devel.projects, :loaded? assert_equal devel.projects.last, proj @@ -546,7 +546,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase developer = project.developers.first - assert_no_queries(ignore_none: false) do + assert_no_queries do assert_predicate project.developers, :loaded? assert_includes project.developers, developer end @@ -741,7 +741,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase def test_get_ids_for_loaded_associations developer = developers(:david) developer.projects.reload - assert_queries(0) do + assert_no_queries do developer.project_ids developer.project_ids end @@ -859,7 +859,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase def test_has_and_belongs_to_many_associations_on_new_records_use_null_relations projects = Developer.new.projects - assert_no_queries(ignore_none: false) do + assert_no_queries do assert_equal [], projects assert_equal [], projects.where(title: "omg") assert_equal [], projects.pluck(:title) diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb index 0b44515e00..036df1c31e 100644 --- a/activerecord/test/cases/associations/has_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_associations_test.rb @@ -458,7 +458,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase def test_finder_method_with_dirty_target company = companies(:first_firm) new_clients = [] - assert_no_queries(ignore_none: false) do + assert_no_queries do new_clients << company.clients_of_firm.build(name: "Another Client") new_clients << company.clients_of_firm.build(name: "Another Client II") new_clients << company.clients_of_firm.build(name: "Another Client III") @@ -478,7 +478,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase def test_finder_bang_method_with_dirty_target company = companies(:first_firm) new_clients = [] - assert_no_queries(ignore_none: false) do + assert_no_queries do new_clients << company.clients_of_firm.build(name: "Another Client") new_clients << company.clients_of_firm.build(name: "Another Client II") new_clients << company.clients_of_firm.build(name: "Another Client III") @@ -955,7 +955,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase end def test_transactions_when_adding_to_new_record - assert_no_queries(ignore_none: false) do + assert_no_queries do firm = Firm.new firm.clients_of_firm.concat(Client.new("name" => "Natural Company")) end @@ -970,7 +970,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase def test_new_aliased_to_build company = companies(:first_firm) - new_client = assert_no_queries(ignore_none: false) { company.clients_of_firm.new("name" => "Another Client") } + new_client = assert_no_queries { company.clients_of_firm.new("name" => "Another Client") } assert_not_predicate company.clients_of_firm, :loaded? assert_equal "Another Client", new_client.name @@ -980,7 +980,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase def test_build company = companies(:first_firm) - new_client = assert_no_queries(ignore_none: false) { company.clients_of_firm.build("name" => "Another Client") } + new_client = assert_no_queries { company.clients_of_firm.build("name" => "Another Client") } assert_not_predicate company.clients_of_firm, :loaded? assert_equal "Another Client", new_client.name @@ -1037,7 +1037,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase def test_build_many company = companies(:first_firm) - new_clients = assert_no_queries(ignore_none: false) { company.clients_of_firm.build([{ "name" => "Another Client" }, { "name" => "Another Client II" }]) } + new_clients = assert_no_queries { company.clients_of_firm.build([{ "name" => "Another Client" }, { "name" => "Another Client II" }]) } assert_equal 2, new_clients.size end @@ -1063,7 +1063,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase def test_build_via_block company = companies(:first_firm) - new_client = assert_no_queries(ignore_none: false) { company.clients_of_firm.build { |client| client.name = "Another Client" } } + new_client = assert_no_queries { company.clients_of_firm.build { |client| client.name = "Another Client" } } assert_not_predicate company.clients_of_firm, :loaded? assert_equal "Another Client", new_client.name @@ -1073,7 +1073,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase def test_build_many_via_block company = companies(:first_firm) - new_clients = assert_no_queries(ignore_none: false) do + new_clients = assert_no_queries do company.clients_of_firm.build([{ "name" => "Another Client" }, { "name" => "Another Client II" }]) do |client| client.name = "changed" end @@ -1266,7 +1266,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase def test_calling_empty_with_counter_cache post = posts(:welcome) - assert_queries(0) do + assert_no_queries do assert_not_empty post.comments end end @@ -1364,7 +1364,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase end def test_transaction_when_deleting_new_record - assert_no_queries(ignore_none: false) do + assert_no_queries do firm = Firm.new client = Client.new("name" => "New Client") firm.clients_of_firm << client @@ -1800,7 +1800,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase firm.clients = [] firm.save - assert_queries(0, ignore_none: true) do + assert_no_queries do firm.clients = [] end @@ -1822,7 +1822,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase end def test_transactions_when_replacing_on_new_record - assert_no_queries(ignore_none: false) do + assert_no_queries do firm = Firm.new firm.clients_of_firm = [Client.new("name" => "New Client")] end @@ -1835,7 +1835,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase def test_get_ids_for_loaded_associations company = companies(:first_firm) company.clients.reload - assert_queries(0) do + assert_no_queries do company.client_ids company.client_ids end @@ -1862,11 +1862,11 @@ class HasManyAssociationsTest < ActiveRecord::TestCase end def test_get_ids_for_association_on_new_record_does_not_try_to_find_records - Company.columns # Load schema information so we don't query below - Contract.columns # if running just this test. + # Load schema information so we don't query below if running just this test. + companies(:first_client).contract_ids company = Company.new - assert_queries(0) do + assert_no_queries do company.contract_ids end @@ -1972,7 +1972,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase firm.clients.load_target assert_predicate firm.clients, :loaded? - assert_no_queries(ignore_none: false) do + assert_no_queries do firm.clients.first assert_equal 2, firm.clients.first(2).size firm.clients.last @@ -2385,7 +2385,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase test "has many associations on new records use null relations" do post = Post.new - assert_no_queries(ignore_none: false) do + assert_no_queries do assert_equal [], post.comments assert_equal [], post.comments.where(body: "omg") assert_equal [], post.comments.pluck(:body) diff --git a/activerecord/test/cases/associations/has_many_through_associations_test.rb b/activerecord/test/cases/associations/has_many_through_associations_test.rb index 442f4a93d4..94ad6e2d4d 100644 --- a/activerecord/test/cases/associations/has_many_through_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb @@ -274,7 +274,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase assert_queries(1) { posts(:thinking) } new_person = nil # so block binding catches it - assert_queries(0) do + assert_no_queries do new_person = Person.new first_name: "bob" end @@ -294,7 +294,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase def test_associate_new_by_building assert_queries(1) { posts(:thinking) } - assert_queries(0) do + assert_no_queries do posts(:thinking).people.build(first_name: "Bob") posts(:thinking).people.new(first_name: "Ted") end @@ -571,10 +571,10 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase posts(:welcome).people = [people(:david)] end - assert_queries(0) { + assert_no_queries do assert_includes posts(:welcome).people, people(:david) assert_not_includes posts(:welcome).people, people(:michael) - } + end assert_includes posts(:welcome).reload.people.reload, people(:david) assert_not_includes posts(:welcome).reload.people.reload, people(:michael) @@ -698,7 +698,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase posts(:welcome).people.clear end - assert_queries(0) do + assert_no_queries do assert_empty posts(:welcome).people end @@ -788,7 +788,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase def test_get_ids_for_loaded_associations person = people(:michael) person.posts.reload - assert_queries(0) do + assert_no_queries do person.post_ids person.post_ids end @@ -1198,7 +1198,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase def test_has_many_through_associations_on_new_records_use_null_relations person = Person.new - assert_no_queries(ignore_none: false) do + assert_no_queries do assert_equal [], person.posts assert_equal [], person.posts.where(body: "omg") assert_equal [], person.posts.pluck(:body) diff --git a/activerecord/test/cases/associations/has_one_associations_test.rb b/activerecord/test/cases/associations/has_one_associations_test.rb index 9eea34d2b9..885d9d7c2c 100644 --- a/activerecord/test/cases/associations/has_one_associations_test.rb +++ b/activerecord/test/cases/associations/has_one_associations_test.rb @@ -37,10 +37,10 @@ class HasOneAssociationsTest < ActiveRecord::TestCase def test_has_one_cache_nils firm = companies(:another_firm) assert_queries(1) { assert_nil firm.account } - assert_queries(0) { assert_nil firm.account } + assert_no_queries { assert_nil firm.account } - firms = Firm.all.merge!(includes: :account).to_a - assert_queries(0) { firms.each(&:account) } + firms = Firm.includes(:account).to_a + assert_no_queries { firms.each(&:account) } end def test_with_select @@ -231,7 +231,7 @@ class HasOneAssociationsTest < ActiveRecord::TestCase end def test_build_association_dont_create_transaction - assert_no_queries(ignore_none: false) { + assert_no_queries { Firm.new.build_account } end diff --git a/activerecord/test/cases/associations/nested_through_associations_test.rb b/activerecord/test/cases/associations/nested_through_associations_test.rb index 03ed1c1d47..5821744530 100644 --- a/activerecord/test/cases/associations/nested_through_associations_test.rb +++ b/activerecord/test/cases/associations/nested_through_associations_test.rb @@ -137,7 +137,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase def test_has_many_through_has_one_through_with_has_one_source_reflection_preload members = assert_queries(4) { Member.includes(:nested_sponsors).to_a } mustache = sponsors(:moustache_club_sponsor_for_groucho) - assert_no_queries(ignore_none: false) do + assert_no_queries do assert_equal [mustache], members.first.nested_sponsors end end @@ -196,7 +196,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase # postgresql test if randomly executed then executes "SHOW max_identifier_length". Hence # the need to ignore certain predefined sqls that deal with system calls. - assert_no_queries(ignore_none: false) do + assert_no_queries do assert_equal [groucho_details, other_details], members.first.organization_member_details_2.sort_by(&:id) end end diff --git a/activerecord/test/cases/autosave_association_test.rb b/activerecord/test/cases/autosave_association_test.rb index db3a58eba9..42d47527b2 100644 --- a/activerecord/test/cases/autosave_association_test.rb +++ b/activerecord/test/cases/autosave_association_test.rb @@ -642,7 +642,7 @@ class TestDefaultAutosaveAssociationOnAHasManyAssociation < ActiveRecord::TestCa def test_build_before_save company = companies(:first_firm) - new_client = assert_no_queries(ignore_none: false) { company.clients_of_firm.build("name" => "Another Client") } + new_client = assert_no_queries { company.clients_of_firm.build("name" => "Another Client") } assert_not_predicate company.clients_of_firm, :loaded? company.name += "-changed" @@ -653,7 +653,7 @@ class TestDefaultAutosaveAssociationOnAHasManyAssociation < ActiveRecord::TestCa def test_build_many_before_save company = companies(:first_firm) - assert_no_queries(ignore_none: false) { company.clients_of_firm.build([{ "name" => "Another Client" }, { "name" => "Another Client II" }]) } + assert_no_queries { company.clients_of_firm.build([{ "name" => "Another Client" }, { "name" => "Another Client II" }]) } company.name += "-changed" assert_queries(3) { assert company.save } @@ -662,7 +662,7 @@ class TestDefaultAutosaveAssociationOnAHasManyAssociation < ActiveRecord::TestCa def test_build_via_block_before_save company = companies(:first_firm) - new_client = assert_no_queries(ignore_none: false) { company.clients_of_firm.build { |client| client.name = "Another Client" } } + new_client = assert_no_queries { company.clients_of_firm.build { |client| client.name = "Another Client" } } assert_not_predicate company.clients_of_firm, :loaded? company.name += "-changed" @@ -673,7 +673,7 @@ class TestDefaultAutosaveAssociationOnAHasManyAssociation < ActiveRecord::TestCa def test_build_many_via_block_before_save company = companies(:first_firm) - assert_no_queries(ignore_none: false) do + assert_no_queries do company.clients_of_firm.build([{ "name" => "Another Client" }, { "name" => "Another Client II" }]) do |client| client.name = "changed" end @@ -1100,7 +1100,7 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase assert @pirate.save Pirate.transaction do - assert_queries(0) do + assert_no_queries do assert @pirate.save end end @@ -1181,12 +1181,12 @@ class TestAutosaveAssociationOnAHasOneAssociation < ActiveRecord::TestCase def test_changed_for_autosave_should_handle_cycles @ship.pirate = @pirate - assert_queries(0) { @ship.save! } + assert_no_queries { @ship.save! } @parrot = @pirate.parrots.create(name: "some_name") @parrot.name = "changed_name" assert_queries(1) { @ship.save! } - assert_queries(0) { @ship.save! } + assert_no_queries { @ship.save! } end def test_should_automatically_save_bang_the_associated_model diff --git a/activerecord/test/cases/collection_cache_key_test.rb b/activerecord/test/cases/collection_cache_key_test.rb index a5d908344a..844b2b2162 100644 --- a/activerecord/test/cases/collection_cache_key_test.rb +++ b/activerecord/test/cases/collection_cache_key_test.rb @@ -91,12 +91,12 @@ module ActiveRecord developers = Developer.where(name: "David") assert_queries(1) { developers.cache_key } - assert_queries(0) { developers.cache_key } + assert_no_queries { developers.cache_key } end test "it doesn't trigger any query if the relation is already loaded" do developers = Developer.where(name: "David").load - assert_queries(0) { developers.cache_key } + assert_no_queries { developers.cache_key } end test "relation cache_key changes when the sql query changes" do diff --git a/activerecord/test/cases/connection_adapters/connection_handler_test.rb b/activerecord/test/cases/connection_adapters/connection_handler_test.rb index 5e3447efde..51d0cc3d12 100644 --- a/activerecord/test/cases/connection_adapters/connection_handler_test.rb +++ b/activerecord/test/cases/connection_adapters/connection_handler_test.rb @@ -170,6 +170,11 @@ module ActiveRecord ActiveRecord::Base.configurations = config ActiveRecord::Base.configurations.configs_for.each do |db_config| assert_instance_of ActiveRecord::DatabaseConfigurations::HashConfig, db_config + assert_instance_of String, db_config.env_name + assert_instance_of String, db_config.spec_name + db_config.config.keys.each do |key| + assert_instance_of String, key + end end ensure ActiveRecord::Base.configurations = @prev_configs diff --git a/activerecord/test/cases/dirty_test.rb b/activerecord/test/cases/dirty_test.rb index b1ebd20d6b..dfd74bfcb4 100644 --- a/activerecord/test/cases/dirty_test.rb +++ b/activerecord/test/cases/dirty_test.rb @@ -336,7 +336,7 @@ class DirtyTest < ActiveRecord::TestCase end with_partial_writes Pirate, true do - assert_queries(0) { 2.times { pirate.save! } } + assert_no_queries { 2.times { pirate.save! } } assert_equal old_updated_on, pirate.reload.updated_on assert_queries(1) { pirate.catchphrase = "bar"; pirate.save! } @@ -355,7 +355,7 @@ class DirtyTest < ActiveRecord::TestCase old_lock_version = person.lock_version with_partial_writes Person, true do - assert_queries(0) { 2.times { person.save! } } + assert_no_queries { 2.times { person.save! } } assert_equal old_lock_version, person.reload.lock_version assert_queries(1) { person.first_name = "bar"; person.save! } diff --git a/activerecord/test/cases/null_relation_test.rb b/activerecord/test/cases/null_relation_test.rb index 17527568f8..c5d5ded5ab 100644 --- a/activerecord/test/cases/null_relation_test.rb +++ b/activerecord/test/cases/null_relation_test.rb @@ -10,26 +10,26 @@ class NullRelationTest < ActiveRecord::TestCase fixtures :posts, :comments def test_none - assert_no_queries(ignore_none: false) do + assert_no_queries do assert_equal [], Developer.none assert_equal [], Developer.all.none end end def test_none_chainable - assert_no_queries(ignore_none: false) do + assert_no_queries do assert_equal [], Developer.none.where(name: "David") end end def test_none_chainable_to_existing_scope_extension_method - assert_no_queries(ignore_none: false) do + assert_no_queries do assert_equal 1, Topic.anonymous_extension.none.one end end def test_none_chained_to_methods_firing_queries_straight_to_db - assert_no_queries(ignore_none: false) do + assert_no_queries do assert_equal [], Developer.none.pluck(:id, :name) assert_equal 0, Developer.none.delete_all assert_equal 0, Developer.none.update_all(name: "David") @@ -39,7 +39,7 @@ class NullRelationTest < ActiveRecord::TestCase end def test_null_relation_content_size_methods - assert_no_queries(ignore_none: false) do + assert_no_queries do assert_equal 0, Developer.none.size assert_equal 0, Developer.none.count assert_equal true, Developer.none.empty? @@ -61,7 +61,7 @@ class NullRelationTest < ActiveRecord::TestCase [:count, :sum].each do |method| define_method "test_null_relation_#{method}" do - assert_no_queries(ignore_none: false) do + assert_no_queries do assert_equal 0, Comment.none.public_send(method, :id) assert_equal Hash.new, Comment.none.group(:post_id).public_send(method, :id) end @@ -70,7 +70,7 @@ class NullRelationTest < ActiveRecord::TestCase [:average, :minimum, :maximum].each do |method| define_method "test_null_relation_#{method}" do - assert_no_queries(ignore_none: false) do + assert_no_queries do assert_nil Comment.none.public_send(method, :id) assert_equal Hash.new, Comment.none.group(:post_id).public_send(method, :id) end diff --git a/activerecord/test/cases/persistence_test.rb b/activerecord/test/cases/persistence_test.rb index 8073cabae6..4830ff2b5f 100644 --- a/activerecord/test/cases/persistence_test.rb +++ b/activerecord/test/cases/persistence_test.rb @@ -446,19 +446,17 @@ class PersistenceTest < ActiveRecord::TestCase end def test_update_attribute_does_not_run_sql_if_attribute_is_not_changed - klass = Class.new(Topic) do - def self.name; "Topic"; end - end - topic = klass.create(title: "Another New Topic") - assert_queries(0) do + topic = Topic.create(title: "Another New Topic") + assert_no_queries do assert topic.update_attribute(:title, "Another New Topic") end end def test_update_does_not_run_sql_if_record_has_not_changed topic = Topic.create(title: "Another New Topic") - assert_queries(0) { assert topic.update(title: "Another New Topic") } - assert_queries(0) { assert topic.update(title: "Another New Topic") } + assert_no_queries do + assert topic.update(title: "Another New Topic") + end end def test_delete diff --git a/activerecord/test/cases/query_cache_test.rb b/activerecord/test/cases/query_cache_test.rb index 3eb4e04cb7..565190c476 100644 --- a/activerecord/test/cases/query_cache_test.rb +++ b/activerecord/test/cases/query_cache_test.rb @@ -190,7 +190,7 @@ class QueryCacheTest < ActiveRecord::TestCase Task.cache do assert_queries(2) { Task.find(1); Task.find(2) } end - assert_queries(0) { Task.find(1); Task.find(1); Task.find(2) } + assert_no_queries { Task.find(1); Task.find(1); Task.find(2) } end end @@ -372,7 +372,7 @@ class QueryCacheTest < ActiveRecord::TestCase end # Check that if the same query is run again, no queries are executed - assert_queries(0) do + assert_no_queries do assert_equal 0, Post.where(title: "test").to_a.count end @@ -427,8 +427,9 @@ class QueryCacheTest < ActiveRecord::TestCase # Clear places where type information is cached Task.reset_column_information Task.initialize_find_by_cache + Task.define_attribute_methods - assert_queries(0) do + assert_no_queries do Task.find(1) end end diff --git a/activerecord/test/cases/touch_later_test.rb b/activerecord/test/cases/touch_later_test.rb index 925a4609a2..cd3d5ed7d1 100644 --- a/activerecord/test/cases/touch_later_test.rb +++ b/activerecord/test/cases/touch_later_test.rb @@ -100,7 +100,7 @@ class TouchLaterTest < ActiveRecord::TestCase def test_touch_later_dont_hit_the_db invoice = Invoice.create! - assert_queries(0) do + assert_no_queries do invoice.touch_later end end diff --git a/activesupport/lib/active_support/cache.rb b/activesupport/lib/active_support/cache.rb index 222ef2b515..2be79d86dd 100644 --- a/activesupport/lib/active_support/cache.rb +++ b/activesupport/lib/active_support/cache.rb @@ -647,7 +647,7 @@ module ActiveSupport if key.size > 1 key = key.collect { |element| expanded_key(element) } else - key = key.first + key = expanded_key(key.first) end when Hash key = key.sort_by { |k, _| k.to_s }.collect { |k, v| "#{k}=#{v}" } diff --git a/activesupport/lib/active_support/core_ext/object/try.rb b/activesupport/lib/active_support/core_ext/object/try.rb index aa6896af32..ef8a1f476d 100644 --- a/activesupport/lib/active_support/core_ext/object/try.rb +++ b/activesupport/lib/active_support/core_ext/object/try.rb @@ -143,14 +143,14 @@ class NilClass # # With +try+ # @person.try(:children).try(:first).try(:name) - def try(*args) + def try(method_name = nil, *args) nil end # Calling +try!+ on +nil+ always returns +nil+. # # nil.try!(:name) # => nil - def try!(*args) + def try!(method_name = nil, *args) nil end end diff --git a/activesupport/lib/active_support/encrypted_configuration.rb b/activesupport/lib/active_support/encrypted_configuration.rb index 3c6da10548..cc1d026737 100644 --- a/activesupport/lib/active_support/encrypted_configuration.rb +++ b/activesupport/lib/active_support/encrypted_configuration.rb @@ -39,7 +39,7 @@ module ActiveSupport end def deserialize(config) - config.present? ? YAML.load(config, content_path) : {} + YAML.load(config).presence || {} end end end diff --git a/activesupport/test/cache/behaviors/cache_store_behavior.rb b/activesupport/test/cache/behaviors/cache_store_behavior.rb index 30735eb0eb..9f54b1e7de 100644 --- a/activesupport/test/cache/behaviors/cache_store_behavior.rb +++ b/activesupport/test/cache/behaviors/cache_store_behavior.rb @@ -290,6 +290,55 @@ module CacheStoreBehavior assert_equal "bar", @cache.read("fu/foo") end + InstanceTest = Struct.new(:name, :id) do + def cache_key + "#{name}/#{id}" + end + + def to_param + "hello" + end + end + + def test_array_with_single_instance_as_cache_key_uses_cache_key_method + test_instance_one = InstanceTest.new("test", 1) + test_instance_two = InstanceTest.new("test", 2) + + @cache.write([test_instance_one], "one") + @cache.write([test_instance_two], "two") + + assert_equal "one", @cache.read([test_instance_one]) + assert_equal "two", @cache.read([test_instance_two]) + end + + def test_array_with_multiple_instances_as_cache_key_uses_cache_key_method + test_instance_one = InstanceTest.new("test", 1) + test_instance_two = InstanceTest.new("test", 2) + test_instance_three = InstanceTest.new("test", 3) + + @cache.write([test_instance_one, test_instance_three], "one") + @cache.write([test_instance_two, test_instance_three], "two") + + assert_equal "one", @cache.read([test_instance_one, test_instance_three]) + assert_equal "two", @cache.read([test_instance_two, test_instance_three]) + end + + def test_format_of_expanded_key_for_single_instance + test_instance_one = InstanceTest.new("test", 1) + + expanded_key = @cache.send(:expanded_key, test_instance_one) + + assert_equal expanded_key, test_instance_one.cache_key + end + + def test_format_of_expanded_key_for_single_instance_in_array + test_instance_one = InstanceTest.new("test", 1) + + expanded_key = @cache.send(:expanded_key, [test_instance_one]) + + assert_equal expanded_key, test_instance_one.cache_key + end + def test_hash_as_cache_key @cache.write({ foo: 1, fu: 2 }, "bar") assert_equal "bar", @cache.read("foo=1/fu=2") diff --git a/activesupport/test/encrypted_configuration_test.rb b/activesupport/test/encrypted_configuration_test.rb index 93ccf457de..387d6e1c1f 100644 --- a/activesupport/test/encrypted_configuration_test.rb +++ b/activesupport/test/encrypted_configuration_test.rb @@ -42,6 +42,12 @@ class EncryptedConfigurationTest < ActiveSupport::TestCase assert @credentials.something[:good] end + test "reading comment-only configuration" do + @credentials.write("# comment") + + assert_equal @credentials.config, {} + end + test "change configuration by key file" do @credentials.write({ something: { good: true } }.to_yaml) @credentials.change do |config_file| diff --git a/guides/rails_guides/markdown/renderer.rb b/guides/rails_guides/markdown/renderer.rb index 82bb4d6de1..f186ac526f 100644 --- a/guides/rails_guides/markdown/renderer.rb +++ b/guides/rails_guides/markdown/renderer.rb @@ -31,7 +31,7 @@ HTML header_with_id = text.scan(/(.*){#(.*)}/) unless header_with_id.empty? - %(<h#{header_level} id=#{header_with_id[0][1].strip}>#{header_with_id[0][0].strip}</h#{header_level}>) + %(<h#{header_level} id="#{header_with_id[0][1].strip}">#{header_with_id[0][0].strip}</h#{header_level}>) else %(<h#{header_level}>#{text}</h#{header_level}>) end diff --git a/guides/source/contributing_to_ruby_on_rails.md b/guides/source/contributing_to_ruby_on_rails.md index 01848bdc11..1beadd78d6 100644 --- a/guides/source/contributing_to_ruby_on_rails.md +++ b/guides/source/contributing_to_ruby_on_rails.md @@ -336,6 +336,26 @@ $ bundle exec ruby -w -Itest test/mail_layout_test.rb -n test_explicit_class_lay The `-n` option allows you to run a single method instead of the whole file. +#### Running tests with a specific seed + +Test execution is randomized with a randomization seed. If you are experiencing random +test failures you can more accurately reproduce a failing test scenario by specifically +setting the randomization seed. + +Running all tests for a component: + +```bash +$ cd actionmailer +$ SEED=15002 bundle exec rake test +``` + +Running a single test file: + +```bash +$ cd actionmailer +$ SEED=15002 bundle exec ruby -w -Itest test/mail_layout_test.rb +``` + #### Testing Active Record First, create the databases you'll need. You can find a list of the required diff --git a/railties/lib/rails/generators/rails/app/templates/app/jobs/application_job.rb.tt b/railties/lib/rails/generators/rails/app/templates/app/jobs/application_job.rb.tt index a009ace51c..d394c3d106 100644 --- a/railties/lib/rails/generators/rails/app/templates/app/jobs/application_job.rb.tt +++ b/railties/lib/rails/generators/rails/app/templates/app/jobs/application_job.rb.tt @@ -1,2 +1,7 @@ class ApplicationJob < ActiveJob::Base + # Automatically retry jobs that encountered a deadlock + # retry_on ActiveRecord::Deadlocked + + # Most jobs are safe to ignore if the underlying records are no longer available + # discard_on ActiveJob::DeserializationError end diff --git a/railties/lib/rails/generators/rails/app/templates/bin/setup.tt b/railties/lib/rails/generators/rails/app/templates/bin/setup.tt index 233b5a1d95..955a424878 100644 --- a/railties/lib/rails/generators/rails/app/templates/bin/setup.tt +++ b/railties/lib/rails/generators/rails/app/templates/bin/setup.tt @@ -1,5 +1,4 @@ require 'fileutils' -include FileUtils # path to your application root. APP_ROOT = File.expand_path('..', __dir__) @@ -8,7 +7,7 @@ def system!(*args) system(*args) || abort("\n== Command #{args} failed ==") end -chdir APP_ROOT do +FileUtils.chdir APP_ROOT do # This script is a starting point to setup your application. # Add necessary setup steps to this file. diff --git a/railties/lib/rails/generators/rails/app/templates/bin/update.tt b/railties/lib/rails/generators/rails/app/templates/bin/update.tt index 99c2430bc6..ed17959e1d 100644 --- a/railties/lib/rails/generators/rails/app/templates/bin/update.tt +++ b/railties/lib/rails/generators/rails/app/templates/bin/update.tt @@ -1,5 +1,4 @@ require 'fileutils' -include FileUtils # path to your application root. APP_ROOT = File.expand_path('..', __dir__) @@ -8,7 +7,7 @@ def system!(*args) system(*args) || abort("\n== Command #{args} failed ==") end -chdir APP_ROOT do +FileUtils.chdir APP_ROOT do # This script is a way to update your development environment automatically. # Add necessary update steps to this file. diff --git a/railties/test/application/bin_setup_test.rb b/railties/test/application/bin_setup_test.rb index 54934dbe24..d02100d94c 100644 --- a/railties/test/application/bin_setup_test.rb +++ b/railties/test/application/bin_setup_test.rb @@ -43,18 +43,20 @@ module ApplicationTests # Ignore line that's only output by Bundler < 1.14 output.sub!(/^Resolving dependencies\.\.\.\n/, "") + # Suppress Bundler platform warnings from output + output.gsub!(/^The dependency .* will be unused .*\.\n/, "") - assert_equal(<<-OUTPUT, output) -== Installing dependencies == -The Gemfile's dependencies are satisfied + assert_equal(<<~OUTPUT, output) + == Installing dependencies == + The Gemfile's dependencies are satisfied -== Preparing database == -Created database 'db/development.sqlite3' -Created database 'db/test.sqlite3' + == Preparing database == + Created database 'db/development.sqlite3' + Created database 'db/test.sqlite3' -== Removing old logs and tempfiles == + == Removing old logs and tempfiles == -== Restarting application server == + == Restarting application server == OUTPUT end end diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb index 48e8b7123f..cd6bdd11c0 100644 --- a/railties/test/generators/app_generator_test.rb +++ b/railties/test/generators/app_generator_test.rb @@ -214,14 +214,14 @@ class AppGeneratorTest < Rails::Generators::TestCase def test_new_application_load_defaults app_root = File.join(destination_root, "myfirstapp") - run_generator [app_root, "--no-skip-javascript"] + run_generator [app_root] output = nil assert_file "#{app_root}/config/application.rb", /\s+config\.load_defaults #{Rails::VERSION::STRING.to_f}/ Dir.chdir(app_root) do - output = `./bin/rails r "puts Rails.application.config.assets.unknown_asset_fallback"` + output = `SKIP_REQUIRE_WEBPACKER=true ./bin/rails r "puts Rails.application.config.assets.unknown_asset_fallback"` end assert_equal "false\n", output @@ -603,7 +603,7 @@ class AppGeneratorTest < Rails::Generators::TestCase def test_javascript_is_skipped_if_required run_generator [destination_root, "--skip-javascript"] - assert_no_file "app/assets/javascripts" + assert_no_file "app/javascript" assert_file "app/views/layouts/application.html.erb" do |contents| assert_match(/stylesheet_link_tag\s+'application', media: 'all' %>/, contents) diff --git a/railties/test/generators/channel_generator_test.rb b/railties/test/generators/channel_generator_test.rb index 265d7c9618..1cb8465539 100644 --- a/railties/test/generators/channel_generator_test.rb +++ b/railties/test/generators/channel_generator_test.rb @@ -57,7 +57,7 @@ class ChannelGeneratorTest < Rails::Generators::TestCase assert_no_file "app/javascript/channels/chat_channel.js" end - def test_cable_js_is_created_if_not_present_already + def test_consumer_js_is_created_if_not_present_already run_generator ["chat"] FileUtils.rm("#{destination_root}/app/javascript/channels/index.js") FileUtils.rm("#{destination_root}/app/javascript/channels/consumer.js") @@ -72,7 +72,7 @@ class ChannelGeneratorTest < Rails::Generators::TestCase run_generator ["chat"], behavior: :revoke assert_no_file "app/channels/chat_channel.rb" - assert_no_file "app/assets/javascripts/channels/chat.js" + assert_no_file "app/javascript/channels/chat_channel.js" assert_file "app/channels/application_cable/channel.rb" assert_file "app/channels/application_cable/connection.rb" @@ -86,7 +86,7 @@ class ChannelGeneratorTest < Rails::Generators::TestCase assert_no_file "app/channels/chat_channel_channel.rb" assert_file "app/channels/chat_channel.rb" - assert_no_file "app/assets/javascripts/channels/chat_channel.js" + assert_no_file "app/javascript/channels/chat_channel_channel.js" assert_file "app/javascript/channels/chat_channel.js" end end diff --git a/railties/test/generators/controller_generator_test.rb b/railties/test/generators/controller_generator_test.rb index adef56255a..8786756c68 100644 --- a/railties/test/generators/controller_generator_test.rb +++ b/railties/test/generators/controller_generator_test.rb @@ -130,8 +130,6 @@ class ControllerGeneratorTest < Rails::Generators::TestCase assert_no_file "app/helpers/account_controller_helper.rb" assert_file "app/helpers/account_helper.rb" - assert_no_file "app/assets/javascripts/account_controller.js" - assert_no_file "app/assets/stylesheets/account_controller.css" assert_file "app/assets/stylesheets/account.css" end diff --git a/railties/test/generators/shared_generator_tests.rb b/railties/test/generators/shared_generator_tests.rb index 521f775553..b766fa1a71 100644 --- a/railties/test/generators/shared_generator_tests.rb +++ b/railties/test/generators/shared_generator_tests.rb @@ -305,8 +305,8 @@ module SharedGeneratorTests run_generator [destination_root, "--skip-action-cable"] assert_file "#{application_path}/config/application.rb", /#\s+require\s+["']action_cable\/engine["']/ assert_no_file "#{application_path}/config/cable.yml" - assert_no_file "#{application_path}/app/assets/javascripts/cable.js" - assert_no_directory "#{application_path}/app/assets/javascripts/channels" + assert_no_file "#{application_path}/app/javascript/consumer.js" + assert_no_directory "#{application_path}/app/javascript/channels" assert_no_directory "#{application_path}/app/channels" assert_file "Gemfile" do |content| assert_no_match(/redis/, content) |