class AroundTheWorld::MethodWrapper

Attributes

allow_undefined_method[R]
block[R]
method_name[R]
prevent_double_wrapping_for[R]
target[R]

Public Class Methods

new(method_name:, target:, prevent_double_wrapping_for: nil, allow_undefined_method: false, &block) click to toggle source

@param :method_name [String, Symbol] The name of the method to be wrapped. @param :target [Module] The class or module containing the method to be wrapped. @param :prevent_double_wrapping_for [String, Symbol]

An identifier to define the proxy module's purpose in the ancestor tree.
A method can only be wrapped once for a given purpose, though it can be wrapped
again for other purposes, or for no given purpose.

@param :allow_undefined_method [Boolean] When false, an error is raised if the wrapped method is not

explicitly defined by the target. Default: false

@block The block that will be executed when the method is invoked.

Should always call super, at least conditionally.
# File lib/around_the_world/method_wrapper.rb, line 30
def initialize(method_name:, target:, prevent_double_wrapping_for: nil, allow_undefined_method: false, &block)
  raise TypeError, "target must be a module or a class" unless target.is_a?(Module)

  @method_name = method_name.to_sym
  @target = target
  @prevent_double_wrapping_for = prevent_double_wrapping_for || nil
  @allow_undefined_method = allow_undefined_method
  @block = block
end
wrap(**args, &block) click to toggle source

Passes arguments directly to {#new} - see {#initialize} for full docs

# File lib/around_the_world/method_wrapper.rb, line 13
def wrap(**args, &block)
  new(**args, &block).wrap
end

Public Instance Methods

wrap() click to toggle source

Defines the wrapped method inside a proxy module and prepends the proxy module to the target module if necessary.

# File lib/around_the_world/method_wrapper.rb, line 41
def wrap
  ensure_method_defined!
  prevent_double_wrapping! if prevent_double_wrapping?

  define_proxy_method
  target.prepend proxy_module unless target.ancestors.include?(proxy_module)
end

Private Instance Methods

define_proxy_method() click to toggle source
# File lib/around_the_world/method_wrapper.rb, line 70
def define_proxy_method
  proxy_module.define_method(method_name, &block)

  proxy_module.instance_exec(method_name, method_privacy) do |method_name, method_privacy|
    case method_privacy
    when :protected
      protected method_name
    when :private
      private method_name
    end
  end
end
ensure_method_defined!() click to toggle source
# File lib/around_the_world/method_wrapper.rb, line 57
def ensure_method_defined!
  return if allow_undefined_method
  return if target.instance_methods(true).include?(method_name) || target.private_method_defined?(method_name)

  raise MethodNotDefinedError, "#{target} does not define :#{method_name}"
end
method_privacy() click to toggle source
# File lib/around_the_world/method_wrapper.rb, line 83
def method_privacy
  if target.protected_method_defined?(method_name)
    :protected
  elsif target.private_method_defined?(method_name)
    :private
  end
end
prevent_double_wrapping!() click to toggle source
# File lib/around_the_world/method_wrapper.rb, line 64
def prevent_double_wrapping!
  return unless already_wrapped?(method_name, target, prevent_double_wrapping_for)

  raise DoubleWrapError, "Module #{proxy_module} already defines the method :#{method_name}"
end
prevent_double_wrapping?() click to toggle source
# File lib/around_the_world/method_wrapper.rb, line 53
def prevent_double_wrapping?
  prevent_double_wrapping_for.present?
end
proxy_module() click to toggle source

@return [AroundTheWorld::ProxyModule] The proxy module upon which the method wrapper will be defined

# File lib/around_the_world/method_wrapper.rb, line 92
def proxy_module
  @proxy_module ||= proxy_module_with_purpose(method_name, target, prevent_double_wrapping_for)
end