aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--activestorage/CHANGELOG.md15
-rw-r--r--activestorage/lib/active_storage/attached/one.rb2
-rw-r--r--activesupport/CHANGELOG.md7
-rw-r--r--activesupport/lib/active_support/core_ext/module/delegation.rb11
-rw-r--r--activesupport/test/core_ext/module_test.rb14
5 files changed, 44 insertions, 5 deletions
diff --git a/activestorage/CHANGELOG.md b/activestorage/CHANGELOG.md
index 1a03ead870..351d8687a4 100644
--- a/activestorage/CHANGELOG.md
+++ b/activestorage/CHANGELOG.md
@@ -1,3 +1,18 @@
+* Method calls on singular attachments return `nil` when no file is attached.
+
+ Previously, assuming the following User model, `user.avatar.filename` would
+ raise a `Module::DelegationError` if no avatar was attached:
+
+ ```ruby
+ class User < ApplicationRecord
+ has_one_attached :avatar
+ end
+ ```
+
+ They now return `nil`.
+
+ *Matthew Tanous*
+
* The mirror service supports direct uploads.
New files are directly uploaded to the primary service. When a
diff --git a/activestorage/lib/active_storage/attached/one.rb b/activestorage/lib/active_storage/attached/one.rb
index c039226fcd..003be1cb43 100644
--- a/activestorage/lib/active_storage/attached/one.rb
+++ b/activestorage/lib/active_storage/attached/one.rb
@@ -3,7 +3,7 @@
module ActiveStorage
# Representation of a single attachment to a model.
class Attached::One < Attached
- delegate_missing_to :attachment
+ delegate_missing_to :attachment, allow_nil: true
# Returns the associated attachment record.
#
diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md
index e494f66c5a..3d1dd41085 100644
--- a/activesupport/CHANGELOG.md
+++ b/activesupport/CHANGELOG.md
@@ -1,3 +1,9 @@
+* `delegate_missing_to` would raise a `DelegationError` if the object
+ delegated to was `nil`. Now the `allow_nil` option has been added to enable
+ the user to specify they want `nil` returned in this case.
+
+ *Matthew Tanous*
+
* `truncate` would return the original string if it was too short to be truncated
and a frozen string if it were long enough to be truncated. Now truncate will
consistently return an unfrozen string regardless. This behavior is consistent
@@ -19,5 +25,4 @@
*Jordan Thomas*
-
Please check [6-0-stable](https://github.com/rails/rails/blob/6-0-stable/activesupport/CHANGELOG.md) for previous changes.
diff --git a/activesupport/lib/active_support/core_ext/module/delegation.rb b/activesupport/lib/active_support/core_ext/module/delegation.rb
index 9fe7f8fe01..2f88010d27 100644
--- a/activesupport/lib/active_support/core_ext/module/delegation.rb
+++ b/activesupport/lib/active_support/core_ext/module/delegation.rb
@@ -274,8 +274,9 @@ class Module
# variables, methods, constants, etc.
#
# The delegated method must be public on the target, otherwise it will
- # raise +NoMethodError+.
- def delegate_missing_to(target)
+ # raise +DelegationError+. If you wish to instead return +nil+,
+ # use the <tt>:allow_nil</tt> option.
+ def delegate_missing_to(target, allow_nil: nil)
target = target.to_s
target = "self.#{target}" if DELEGATION_RESERVED_METHOD_NAMES.include?(target)
@@ -295,7 +296,11 @@ class Module
super
rescue NoMethodError
if #{target}.nil?
- raise DelegationError, "\#{method} delegated to #{target}, but #{target} is nil"
+ if #{allow_nil == true}
+ return nil
+ else
+ raise DelegationError, "\#{method} delegated to #{target}, but #{target} is nil"
+ end
else
raise
end
diff --git a/activesupport/test/core_ext/module_test.rb b/activesupport/test/core_ext/module_test.rb
index 04692f1484..6e341480d1 100644
--- a/activesupport/test/core_ext/module_test.rb
+++ b/activesupport/test/core_ext/module_test.rb
@@ -92,6 +92,16 @@ DecoratedTester = Struct.new(:client) do
delegate_missing_to :client
end
+class DecoratedMissingAllowNil
+ delegate_missing_to :case, allow_nil: true
+
+ attr_reader :case
+
+ def initialize(kase)
+ @case = kase
+ end
+end
+
class DecoratedReserved
delegate_missing_to :case
@@ -382,6 +392,10 @@ class ModuleTest < ActiveSupport::TestCase
assert_equal "name delegated to client, but client is nil", e.message
end
+ def test_delegate_missing_to_returns_nil_if_allow_nil_and_nil_target
+ assert_nil DecoratedMissingAllowNil.new(nil).name
+ end
+
def test_delegate_missing_to_affects_respond_to
assert_respond_to DecoratedTester.new(@david), :name
assert_not_respond_to DecoratedTester.new(@david), :private_name