From f002be148e1377709ed28b8e80c5db76ee2fa410 Mon Sep 17 00:00:00 2001 From: Matt Tanous Date: Wed, 25 Jul 2018 17:04:34 -0400 Subject: Add :allow_nil option to delegate_missing_to; use in ActiveStorage attachment --- activestorage/CHANGELOG.md | 15 +++++++++++++++ activestorage/lib/active_storage/attached/one.rb | 2 +- activesupport/CHANGELOG.md | 7 ++++++- .../lib/active_support/core_ext/module/delegation.rb | 11 ++++++++--- activesupport/test/core_ext/module_test.rb | 14 ++++++++++++++ 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 :allow_nil 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 -- cgit v1.2.3