Class: AroundTheWorld::MethodWrapper

Inherits:
Object
  • Object
show all
Includes:
ProxyCreation
Defined in:
lib/around_the_world/method_wrapper.rb,
lib/around_the_world/method_wrapper/proxy_creation.rb

Defined Under Namespace

Modules: ProxyCreation

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from ProxyCreation

#already_wrapped?, #base_ancestry_index, #existing_proxy_module_with_purpose, #existing_proxy_modules, #proxy_module_with_purpose

Constructor Details

#initialize(method_name:, target:, prevent_double_wrapping_for: nil, allow_undefined_method: false, &block) ⇒ MethodWrapper

Returns a new instance of MethodWrapper.

Parameters:

  • :method_name (String, Symbol)

    The name of the method to be wrapped.

  • :target (Module)

    The class or module containing the method to be wrapped.

  • :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.

  • :allow_undefined_method (Boolean)

    When false, an error is raised if the wrapped method is not explicitly defined by the target. Default: false

Raises:

  • (TypeError)


30
31
32
33
34
35
36
37
38
# 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

Instance Attribute Details

#allow_undefined_methodObject (readonly, private)

Returns the value of attribute allow_undefined_method.



51
52
53
# File 'lib/around_the_world/method_wrapper.rb', line 51

def allow_undefined_method
  @allow_undefined_method
end

#blockObject (readonly, private)

Returns the value of attribute block.



51
52
53
# File 'lib/around_the_world/method_wrapper.rb', line 51

def block
  @block
end

#method_nameObject (readonly)

Returns the value of attribute method_name.



18
19
20
# File 'lib/around_the_world/method_wrapper.rb', line 18

def method_name
  @method_name
end

#prevent_double_wrapping_forObject (readonly, private)

Returns the value of attribute prevent_double_wrapping_for.



51
52
53
# File 'lib/around_the_world/method_wrapper.rb', line 51

def prevent_double_wrapping_for
  @prevent_double_wrapping_for
end

#targetObject (readonly)

Returns the value of attribute target.



18
19
20
# File 'lib/around_the_world/method_wrapper.rb', line 18

def target
  @target
end

Class Method Details

.wrap(**args, &block) ⇒ Object

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



13
14
15
# File 'lib/around_the_world/method_wrapper.rb', line 13

def wrap(**args, &block)
  new(**args, &block).wrap
end

Instance Method Details

#define_proxy_methodObject (private)



70
71
72
73
74
75
76
77
78
79
80
81
# 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!Object (private)



57
58
59
60
61
62
# 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_privacyObject (private)



83
84
85
86
87
88
89
# 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!Object (private)

Raises:



64
65
66
67
68
# 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?Boolean (private)

Returns:

  • (Boolean)


53
54
55
# File 'lib/around_the_world/method_wrapper.rb', line 53

def prevent_double_wrapping?
  prevent_double_wrapping_for.present?
end

#proxy_moduleAroundTheWorld::ProxyModule (private)

Returns The proxy module upon which the method wrapper will be defined.

Returns:



92
93
94
# 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

#wrapObject

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



41
42
43
44
45
46
47
# 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