diff options
-rw-r--r-- | actionview/lib/action_view/helpers/tag_helper.rb | 5 | ||||
-rw-r--r-- | activerecord/CHANGELOG.md | 5 | ||||
-rw-r--r-- | activerecord/lib/active_record/associations/collection_association.rb | 6 | ||||
-rw-r--r-- | activerecord/lib/arel/nodes/count.rb | 2 | ||||
-rw-r--r-- | activerecord/test/cases/associations/belongs_to_associations_test.rb | 7 | ||||
-rw-r--r-- | activerecord/test/cases/associations/has_many_associations_test.rb | 76 | ||||
-rw-r--r-- | activestorage/lib/active_storage/attached/one.rb | 4 | ||||
-rw-r--r-- | activestorage/test/models/presence_validation_test.rb | 30 | ||||
-rw-r--r-- | activesupport/lib/active_support/json/encoding.rb | 8 | ||||
-rw-r--r-- | activesupport/lib/active_support/tagged_logging.rb | 4 | ||||
-rw-r--r-- | activesupport/lib/active_support/test_case.rb | 2 | ||||
-rw-r--r-- | activesupport/lib/active_support/testing/setup_and_teardown.rb | 14 | ||||
-rw-r--r-- | activesupport/test/testing/after_teardown_test.rb | 8 | ||||
-rw-r--r-- | guides/assets/images/getting_started/rails_welcome.png | bin | 732190 -> 282547 bytes | |||
-rw-r--r-- | guides/source/routing.md | 2 |
15 files changed, 151 insertions, 22 deletions
diff --git a/actionview/lib/action_view/helpers/tag_helper.rb b/actionview/lib/action_view/helpers/tag_helper.rb index b73c4be1ee..d12989ea64 100644 --- a/actionview/lib/action_view/helpers/tag_helper.rb +++ b/actionview/lib/action_view/helpers/tag_helper.rb @@ -88,9 +88,10 @@ module ActionView if value.is_a?(Array) value = escape ? safe_join(value, " ".freeze) : value.join(" ".freeze) else - value = escape ? ERB::Util.unwrapped_html_escape(value) : value.to_s + value = escape ? ERB::Util.unwrapped_html_escape(value) : value.to_s.dup end - %(#{key}="#{value.gsub('"'.freeze, '"'.freeze)}") + value.gsub!('"'.freeze, """.freeze) + %(#{key}="#{value}") end private diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 0d8fa48235..36a3d59784 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,8 @@ +* Ensure `Associations::CollectionAssociation#size` and `Associations::CollectionAssociation#empty?` + use loaded association ids if present. + + *Graham Turner* + * Add support to preload associations of polymorphic associations when not all the records have the requested associations. *Dana Sherson* diff --git a/activerecord/lib/active_record/associations/collection_association.rb b/activerecord/lib/active_record/associations/collection_association.rb index 671c4c56df..d61d105544 100644 --- a/activerecord/lib/active_record/associations/collection_association.rb +++ b/activerecord/lib/active_record/associations/collection_association.rb @@ -45,6 +45,8 @@ module ActiveRecord def ids_reader if loaded? target.pluck(reflection.association_primary_key) + elsif !target.empty? + load_target.pluck(reflection.association_primary_key) else @association_ids ||= scope.pluck(reflection.association_primary_key) end @@ -212,6 +214,8 @@ module ActiveRecord def size if !find_target? || loaded? target.size + elsif @association_ids + @association_ids.size elsif !association_scope.group_values.empty? load_target.size elsif !association_scope.distinct_value && !target.empty? @@ -231,7 +235,7 @@ module ActiveRecord # loaded and you are going to fetch the records anyway it is better to # check <tt>collection.length.zero?</tt>. def empty? - if loaded? + if loaded? || @association_ids size.zero? else target.empty? && !scope.exists? diff --git a/activerecord/lib/arel/nodes/count.rb b/activerecord/lib/arel/nodes/count.rb index c8e409ea8b..880464639d 100644 --- a/activerecord/lib/arel/nodes/count.rb +++ b/activerecord/lib/arel/nodes/count.rb @@ -3,8 +3,6 @@ module Arel # :nodoc: all module Nodes class Count < Arel::Nodes::Function - include Math - def initialize(expr, distinct = false, aliaz = nil) super(expr, aliaz) @distinct = distinct diff --git a/activerecord/test/cases/associations/belongs_to_associations_test.rb b/activerecord/test/cases/associations/belongs_to_associations_test.rb index 6b4f826766..a85b56ac4b 100644 --- a/activerecord/test/cases/associations/belongs_to_associations_test.rb +++ b/activerecord/test/cases/associations/belongs_to_associations_test.rb @@ -37,6 +37,13 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase assert_equal companies(:first_firm).name, firm.name end + def test_assigning_belongs_to_on_destroyed_object + client = Client.create!(name: "Client") + client.destroy! + assert_raise(frozen_error_class) { client.firm = nil } + assert_raise(frozen_error_class) { client.firm = Firm.new(name: "Firm") } + end + def test_missing_attribute_error_is_raised_when_no_foreign_key_attribute assert_raises(ActiveModel::MissingAttributeError) { Client.select(:id).first.firm } end diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb index 431c4f36fa..bf2e051def 100644 --- a/activerecord/test/cases/associations/has_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_associations_test.rb @@ -989,10 +989,20 @@ class HasManyAssociationsTest < ActiveRecord::TestCase assert_equal 0, post.readers.size post.readers.reset post.readers.build - assert_equal [], post.reader_ids + assert_equal [nil], post.reader_ids assert_equal 1, post.readers.size end + def test_collection_empty_with_dirty_target + post = posts(:thinking) + assert_equal [], post.reader_ids + assert_empty post.readers + post.readers.reset + post.readers.build + assert_equal [nil], post.reader_ids + assert_not_empty post.readers + end + def test_collection_size_twice_for_regressions post = posts(:thinking) assert_equal 0, post.readers.size @@ -2577,6 +2587,70 @@ class HasManyAssociationsTest < ActiveRecord::TestCase end end + test "calling size on an association that has not been loaded performs a query" do + car = Car.create! + Bulb.create(car_id: car.id) + + car_two = Car.create! + + assert_queries(1) do + assert_equal 1, car.bulbs.size + end + + assert_queries(1) do + assert_equal 0, car_two.bulbs.size + end + end + + test "calling size on an association that has been loaded does not perform query" do + car = Car.create! + Bulb.create(car_id: car.id) + car.bulb_ids + + car_two = Car.create! + car_two.bulb_ids + + assert_no_queries do + assert_equal 1, car.bulbs.size + end + + assert_no_queries do + assert_equal 0, car_two.bulbs.size + end + end + + test "calling empty on an association that has not been loaded performs a query" do + car = Car.create! + Bulb.create(car_id: car.id) + + car_two = Car.create! + + assert_queries(1) do + assert_not_empty car.bulbs + end + + assert_queries(1) do + assert_empty car_two.bulbs + end + end + + test "calling empty on an association that has been loaded does not performs query" do + car = Car.create! + Bulb.create(car_id: car.id) + car.bulb_ids + + car_two = Car.create! + car_two.bulb_ids + + assert_no_queries do + assert_not_empty car.bulbs + end + + assert_no_queries do + assert_empty car_two.bulbs + end + end + class AuthorWithErrorDestroyingAssociation < ActiveRecord::Base self.table_name = "authors" has_many :posts_with_error_destroying, diff --git a/activestorage/lib/active_storage/attached/one.rb b/activestorage/lib/active_storage/attached/one.rb index a582ed0137..f992cb5f84 100644 --- a/activestorage/lib/active_storage/attached/one.rb +++ b/activestorage/lib/active_storage/attached/one.rb @@ -13,6 +13,10 @@ module ActiveStorage record.public_send("#{name}_attachment") end + def blank? + attachment.blank? + end + # Associates a given attachment with the current record, saving it to the database. # # person.avatar.attach(params[:avatar]) # ActionDispatch::Http::UploadedFile object diff --git a/activestorage/test/models/presence_validation_test.rb b/activestorage/test/models/presence_validation_test.rb new file mode 100644 index 0000000000..aa804506dd --- /dev/null +++ b/activestorage/test/models/presence_validation_test.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +require "test_helper" +require "database/setup" + +class ActiveStorage::PresenceValidationTest < ActiveSupport::TestCase + class Admin < User; end + + teardown do + Admin.clear_validators! + end + + test "validates_presence_of has_one_attached" do + Admin.validates_presence_of :avatar + a = Admin.new + assert_predicate a, :invalid? + + a.avatar.attach create_blob(filename: "funky.jpg") + assert_predicate a, :valid? + end + + test "validates_presence_of has_many_attached" do + Admin.validates_presence_of :highlights + a = Admin.new + assert_predicate a, :invalid? + + a.highlights.attach create_blob(filename: "funky.jpg") + assert_predicate a, :valid? + end +end diff --git a/activesupport/lib/active_support/json/encoding.rb b/activesupport/lib/active_support/json/encoding.rb index 1339c75ffe..de1b8ac8cf 100644 --- a/activesupport/lib/active_support/json/encoding.rb +++ b/activesupport/lib/active_support/json/encoding.rb @@ -54,9 +54,13 @@ module ActiveSupport class EscapedString < String #:nodoc: def to_json(*) if Encoding.escape_html_entities_in_json - super.gsub ESCAPE_REGEX_WITH_HTML_ENTITIES, ESCAPED_CHARS + s = super + s.gsub! ESCAPE_REGEX_WITH_HTML_ENTITIES, ESCAPED_CHARS + s else - super.gsub ESCAPE_REGEX_WITHOUT_HTML_ENTITIES, ESCAPED_CHARS + s = super + s.gsub! ESCAPE_REGEX_WITHOUT_HTML_ENTITIES, ESCAPED_CHARS + s end end diff --git a/activesupport/lib/active_support/tagged_logging.rb b/activesupport/lib/active_support/tagged_logging.rb index 8561cba9f1..b069ac94d4 100644 --- a/activesupport/lib/active_support/tagged_logging.rb +++ b/activesupport/lib/active_support/tagged_logging.rb @@ -52,7 +52,9 @@ module ActiveSupport def tags_text tags = current_tags - if tags.any? + if tags.one? + "[#{tags[0]}] " + elsif tags.any? tags.collect { |tag| "[#{tag}] " }.join end end diff --git a/activesupport/lib/active_support/test_case.rb b/activesupport/lib/active_support/test_case.rb index 4e42db4500..f17743b6db 100644 --- a/activesupport/lib/active_support/test_case.rb +++ b/activesupport/lib/active_support/test_case.rb @@ -130,7 +130,7 @@ module ActiveSupport alias_method :method_name, :name include ActiveSupport::Testing::TaggedLogging - include ActiveSupport::Testing::SetupAndTeardown + prepend ActiveSupport::Testing::SetupAndTeardown include ActiveSupport::Testing::Assertions include ActiveSupport::Testing::Deprecation include ActiveSupport::Testing::TimeHelpers diff --git a/activesupport/lib/active_support/testing/setup_and_teardown.rb b/activesupport/lib/active_support/testing/setup_and_teardown.rb index 35236f1401..35321cd157 100644 --- a/activesupport/lib/active_support/testing/setup_and_teardown.rb +++ b/activesupport/lib/active_support/testing/setup_and_teardown.rb @@ -1,6 +1,5 @@ # frozen_string_literal: true -require "active_support/concern" require "active_support/callbacks" module ActiveSupport @@ -19,11 +18,10 @@ module ActiveSupport # end # end module SetupAndTeardown - extend ActiveSupport::Concern - - included do - include ActiveSupport::Callbacks - define_callbacks :setup, :teardown + def self.prepended(klass) + klass.include ActiveSupport::Callbacks + klass.define_callbacks :setup, :teardown + klass.extend ClassMethods end module ClassMethods @@ -47,12 +45,10 @@ module ActiveSupport begin run_callbacks :teardown rescue => e - error = e + self.failures << Minitest::UnexpectedError.new(e) end super - ensure - raise error if error end end end diff --git a/activesupport/test/testing/after_teardown_test.rb b/activesupport/test/testing/after_teardown_test.rb index 68c368909c..961af49479 100644 --- a/activesupport/test/testing/after_teardown_test.rb +++ b/activesupport/test/testing/after_teardown_test.rb @@ -4,13 +4,14 @@ require "abstract_unit" module OtherAfterTeardown def after_teardown + super + @witness = true end end -class AfterTeardownTest < Minitest::Test +class AfterTeardownTest < ActiveSupport::TestCase include OtherAfterTeardown - include ActiveSupport::Testing::SetupAndTeardown attr_writer :witness @@ -21,11 +22,12 @@ class AfterTeardownTest < Minitest::Test end def after_teardown - assert_raises MyError do + assert_changes -> { failures.count }, from: 0, to: 1 do super end assert_equal true, @witness + failures.clear end def test_teardown_raise_but_all_after_teardown_method_are_called diff --git a/guides/assets/images/getting_started/rails_welcome.png b/guides/assets/images/getting_started/rails_welcome.png Binary files differindex 44f89ec8de..88efe34a9d 100644 --- a/guides/assets/images/getting_started/rails_welcome.png +++ b/guides/assets/images/getting_started/rails_welcome.png diff --git a/guides/source/routing.md b/guides/source/routing.md index 3cf5b56340..7a83fee617 100644 --- a/guides/source/routing.md +++ b/guides/source/routing.md @@ -36,6 +36,8 @@ get '/patients/:id', to: 'patients#show' the request is dispatched to the `patients` controller's `show` action with `{ id: '17' }` in `params`. +NOTE: Rails uses snake_case for controller names here, if you have a multiple word controller like `MonsterTrucksController`, you want to use `monster_trucks#show` for example. + ### Generating Paths and URLs from Code You can also generate paths and URLs. If the route above is modified to be: |