1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
|
# frozen_string_literal: true
module Contracts
# MethodReference represents original method reference that was
# decorated by contracts.ruby. Used for instance methods.
class MethodReference
attr_reader :name
# name - name of the method
# method - method object
def initialize(name, method)
@name = name
@method = method
end
# Returns method_position, delegates to Support.method_position
def method_position
Support.method_position(@method)
end
# Makes a method re-definition in proper way
def make_definition(this, &blk)
is_private = private?(this)
is_protected = protected?(this)
alias_target(this).send(:define_method, name, &blk)
make_private(this) if is_private
make_protected(this) if is_protected
end
# Aliases original method to a special unique name, which is known
# only to this class. Usually done right before re-defining the
# method.
def make_alias(this)
_aliased_name = aliased_name
original_name = name
alias_target(this).class_eval do
alias_method _aliased_name, original_name
end
end
# Calls original method on specified `this` argument with
# specified arguments `args` and block `&blk`.
def send_to(this, *args, **kargs, &blk)
this.send(aliased_name, *args, **kargs, &blk)
end
private
# Makes a method private
def make_private(this)
original_name = name
alias_target(this).class_eval { private original_name }
end
def private?(this)
this.private_instance_methods.map(&:to_sym).include?(name)
end
def protected?(this)
this.protected_instance_methods.map(&:to_sym).include?(name)
end
# Makes a method protected
def make_protected(this)
original_name = name
alias_target(this).class_eval { protected original_name }
end
# Returns alias target for instance methods, subject to be
# overriden in subclasses.
def alias_target(this)
this
end
def aliased_name
@_original_name ||= construct_unique_name
end
def construct_unique_name
:"__contracts_ruby_original_#{name}_#{Support.unique_id}"
end
end
# The same as MethodReference, but used for singleton methods.
class SingletonMethodReference < MethodReference
private
def private?(this)
this.private_methods.map(&:to_sym).include?(name)
end
def protected?(this)
this.protected_methods.map(&:to_sym).include?(name)
end
# Return alias target for singleton methods.
def alias_target(this)
Support.eigenclass_of this
end
end
end
|