123456789_123456789_123456789_123456789_123456789_

Module: ActiveSupport::Delegation

Do not use. This module is for internal use only.
Relationships & Source Files
Defined in: activesupport/lib/active_support/delegation.rb

Constant Summary

Class Method Summary

Class Method Details

.generate(owner, methods, location: nil, to: nil, prefix: nil, allow_nil: nil, nilable: true, private: nil, as: nil, signature: nil)

[ GitHub ]

  
# File 'activesupport/lib/active_support/delegation.rb', line 21

def generate(owner, methods, location: nil, to: nil, prefix: nil, allow_nil: nil, nilable: true, private: nil, as: nil, signature: nil)
  unless to
    raise ArgumentError, "Delegation needs a target. Supply a keyword argument 'to' (e.g. delegate :hello, to: :greeter)."
  end

  if prefix == true && /^[^a-z_]/.match?(to)
    raise ArgumentError, "Can only automatically set the delegation prefix when delegating to a method."
  end

  method_prefix = \
    if prefix
      "#{prefix == true ? to : prefix}_"
    else
      ""
    end

  location ||= caller_locations(1, 1).first
  file, line = location.path, location.lineno

  receiver = if to.is_a?(Module)
    if to.name.nil?
      raise ArgumentError, "Can't delegate to anonymous class or module: #{to}"
    end

    unless Inflector.safe_constantize(to.name).equal?(to)
      raise ArgumentError, "Can't delegate to detached class or module: #{to.name}"
    end

    "::#{to.name}"
  else
    to.to_s
  end
  receiver = "self.#{receiver}" if RESERVED_METHOD_NAMES.include?(receiver)

  explicit_receiver = false
  receiver_class = if as
    explicit_receiver = true
    as
  elsif to.is_a?(Module)
    to.singleton_class
  elsif receiver == "self.class"
    nilable = false # self.class can't possibly be nil
    owner.singleton_class
  end

  method_def = []
  method_names = []

  method_def << "self.private" if private

  methods.each do |method|
    method_name = prefix ? "#{method_prefix}#{method}" : method
    method_names << method_name.to_sym

    # Attribute writer methods only accept one argument. Makes sure []=
    # methods still accept two arguments.
    definition = \
      if signature
        signature
      elsif /[^\]]=\z/.match?(method)
        "arg"
      else
        method_object = if receiver_class
          begin
            receiver_class.public_instance_method(method)
          rescue NameError
            raise if explicit_receiver
            # Do nothing. Fall back to `"..."`
          end
        end

        if method_object
          parameters = method_object.parameters

          if parameters.map(&:first).intersect?([:opt, :rest, :keyreq, :key, :keyrest])
            "..."
          else
            defn = parameters.filter_map { |type, arg| arg if type == :req }
            defn << "&"
            defn.join(", ")
          end
        else
          "..."
        end
      end

    # The following generated method calls the target exactly once, storing
    # the returned value in a dummy variable.
    #
    # Reason is twofold: On one hand doing less calls is in general better.
    # On the other hand it could be that the target has side-effects,
    # whereas conceptually, from the user point of view, the delegator should
    # be doing one call.
    if nilable == false
      method_def <<
        "def #{method_name}(#{definition})" <<
        "  (#{receiver}).#{method}(#{definition})" <<
        "end"
    elsif allow_nil
      method = method.to_s

      method_def <<
        "def #{method_name}(#{definition})" <<
        "  _ = #{receiver}" <<
        "  if !_.nil? || nil.respond_to?(:#{method})" <<
        "    _.#{method}(#{definition})" <<
        "  end" <<
        "end"
    else
      method = method.to_s
      method_name = method_name.to_s

      method_def <<
        "def #{method_name}(#{definition})" <<
        "  _ = #{receiver}" <<
        "  _.#{method}(#{definition})" <<
        "rescue NoMethodError => e" <<
        "  if _.nil? && e.name == :#{method}" <<
        "    raise ::ActiveSupport::DelegationError.nil_target(:#{method_name}, :'#{receiver}')" <<
        "  else" <<
        "    raise" <<
        "  end" <<
        "end"
    end
  end
  owner.module_eval(method_def.join(";"), file, line)
  method_names
end

.generate_method_missing(owner, target, allow_nil: nil)

[ GitHub ]

  
# File 'activesupport/lib/active_support/delegation.rb', line 150

def generate_method_missing(owner, target, allow_nil: nil)
  target = target.to_s
  target = "self.#{target}" if RESERVED_METHOD_NAMES.include?(target) || target == "__target"

  if allow_nil
    owner.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, ...)
        __target = #{target}
        if __target.nil? && !nil.respond_to?(method)
          nil
        elsif __target.respond_to?(method)
          __target.public_send(method, ...)
        else
          super
        end
      end
    RUBY
  else
    owner.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, ...)
        __target = #{target}
        if __target.nil? && !nil.respond_to?(method)
          raise ::ActiveSupport::DelegationError.nil_target(method, :'#{target}')
        elsif __target.respond_to?(method)
          __target.public_send(method, ...)
        else
          super
        end
      end
    RUBY
  end
end