instance method delegate_missing_to

Ruby on Rails 7.1.6

Since v5.2.8.1

Available in: v5.2.8.1 v6.0.6 v6.1.7.10 v7.0.10 v7.1.6 v7.2.3 v8.0.4 v8.1.2

Signature

delegate_missing_to(target, allow_nil: nil)

When building decorators, a common pattern may emerge:

class Partition
  def initialize(event)
    @event = event
  end

  def person
    detail.person || creator
  end

  private
    def respond_to_missing?(name, include_private = false)
      @event.respond_to?(name, include_private)
    end

    def method_missing(method, *args, &block)
      @event.send(method, *args, &block)
    end
end

With Module#delegate_missing_to, the above is condensed to:

class Partition
  delegate_missing_to :@event

  def initialize(event)
    @event = event
  end

  def person
    detail.person || creator
  end
end

The target can be anything callable within the object, e.g. instance variables, methods, constants, etc.

The delegated method must be public on the target, otherwise it will raise DelegationError. If you wish to instead return nil, use the :allow_nil option.

The marshal_dump and _dump methods are exempt from delegation due to possible interference when calling Marshal.dump(object), should the delegation target method of object add or remove instance variables.

Parameters

target req
allow_nil key = nil
Source
# File activesupport/lib/active_support/core_ext/module/delegation.rb, line 318
  def delegate_missing_to(target, allow_nil: nil)
    target = target.to_s
    target = "self.#{target}" if DELEGATION_RESERVED_METHOD_NAMES.include?(target) || target == "__target"

    if allow_nil
      module_eval <<~RUBY, __FILE__, __LINE__ + 1
        def respond_to_missing?(name, include_private = false)
          # It may look like an oversight, but we deliberately do not pass
          # +include_private+, because they do not get delegated.

          return false if name == :marshal_dump || name == :_dump
          #{target}.respond_to?(name) || super
        end

        def method_missing(method, *args, &block)
          __target = #{target}
          if __target.nil? && !nil.respond_to?(method)
            nil
          elsif __target.respond_to?(method)
            __target.public_send(method, *args, &block)
          else
            super
          end
        end
        ruby2_keywords(:method_missing)
      RUBY
    else
      module_eval <<~RUBY, __FILE__, __LINE__ + 1
        def respond_to_missing?(name, include_private = false)
          # It may look like an oversight, but we deliberately do not pass
          # +include_private+, because they do not get delegated.

          return false if name == :marshal_dump || name == :_dump
          #{target}.respond_to?(name) || super
        end

        def method_missing(method, *args, &block)
          __target = #{target}
          if __target.nil? && !nil.respond_to?(method)
            raise DelegationError, "\#{method} delegated to #{target}, but #{target} is nil"
          elsif __target.respond_to?(method)
            __target.public_send(method, *args, &block)
          else
            super
          end
        end
        ruby2_keywords(:method_missing)
      RUBY
    end
  end

Defined in activesupport/lib/active_support/core_ext/module/delegation.rb line 318 · View on GitHub · Improve this page · Find usages on GitHub

Defined in Module

Type at least 2 characters to search.

↑↓ navigate · open · esc close