From 32438ed64fea7dbe52ea0898be3d4276e63baa78 Mon Sep 17 00:00:00 2001
From: Abhishek Chandrasekhar <me@abhchand.me>
Date: Wed, 20 Feb 2019 23:37:10 -0500
Subject: [ActiveStorage] Ensure that the `_blob` association is properly
 loaded when attaching `::One`

Consider a model with `One` and `Many` attachments configured:

    class User < ActiveRecord::Base
      has_one_attached :avatar
      has_many_attached :highlights
    end

=== One Attachment

After attaching `One` attachment (`:avatar`), we can see that the associated
`_blob` record (`:avatar_blob`) still returns as `nil`.

    user.avatar.attach(blob)
    user.avatar_attachment.present?  => true
    user.avatar_blob.present?        => false    # Incorrect!

This is a false negative. It happens because after the attachment and blob
are built:

  1. The record already has its `_blob` association loaded, as `nil`
  2. the `::Attachment` is associated with the record but the `::Blob` only gets
    associated with the `::Attachment`, not the record itself

In reality, the blob does in fact exist. We can verify this as follows:

    user.avatar.attach(blob)
    user.avatar_attachment.blob.present?    => true  # Blob does exist!

The fix in this change is to simply assign the `::Blob` when assigning
the `::Attachment`. After this fix is applied, we correctly observe:

    user.avatar.attach(blob)
    user.avatar_attachment.present?  => true
    user.avatar_blob.present?        => true    # Woohoo!

=== Many Attachments

We don't see this issue with `Many` attachments because the `_blob` association
is already loaded as part of attaching more/newer blobs.

    user.highlights.attach(blob)
    user.highlights_attachments.any?    => true
    user.highlights_blobs.any?          => true
---
 activestorage/lib/active_storage/attached/changes/create_one.rb | 1 +
 activestorage/test/models/attached/many_test.rb                 | 3 +++
 activestorage/test/models/attached/one_test.rb                  | 3 +++
 3 files changed, 7 insertions(+)

diff --git a/activestorage/lib/active_storage/attached/changes/create_one.rb b/activestorage/lib/active_storage/attached/changes/create_one.rb
index 5812fd2b08..89cccfb58a 100644
--- a/activestorage/lib/active_storage/attached/changes/create_one.rb
+++ b/activestorage/lib/active_storage/attached/changes/create_one.rb
@@ -30,6 +30,7 @@ module ActiveStorage
 
     def save
       record.public_send("#{name}_attachment=", attachment)
+      record.public_send("#{name}_blob=", blob)
     end
 
     private
diff --git a/activestorage/test/models/attached/many_test.rb b/activestorage/test/models/attached/many_test.rb
index 8fede0e682..e826109874 100644
--- a/activestorage/test/models/attached/many_test.rb
+++ b/activestorage/test/models/attached/many_test.rb
@@ -16,6 +16,9 @@ class ActiveStorage::ManyAttachedTest < ActiveSupport::TestCase
     @user.highlights.attach create_blob(filename: "funky.jpg"), 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
+
+    assert_not_empty @user.highlights_attachments
+    assert_equal @user.highlights_blobs.count, 2
   end
 
   test "attaching existing blobs from signed IDs to an existing record" do
diff --git a/activestorage/test/models/attached/one_test.rb b/activestorage/test/models/attached/one_test.rb
index 7fb3262781..ac08d324bb 100644
--- a/activestorage/test/models/attached/one_test.rb
+++ b/activestorage/test/models/attached/one_test.rb
@@ -15,6 +15,9 @@ class ActiveStorage::OneAttachedTest < ActiveSupport::TestCase
   test "attaching an existing blob to an existing record" do
     @user.avatar.attach create_blob(filename: "funky.jpg")
     assert_equal "funky.jpg", @user.avatar.filename.to_s
+
+    assert_not_nil @user.avatar_attachment
+    assert_not_nil @user.avatar_blob
   end
 
   test "attaching an existing blob from a signed ID to an existing record" do
-- 
cgit v1.2.3