Module: Casting::SuperDelegate

Defined in:
lib/casting/super_delegate.rb

Class Attribute Summary collapse

Instance Method Summary collapse

Class Attribute Details

.casting_library_matcherObject

Returns the value of attribute casting_library_matcher.



9
10
11
# File 'lib/casting/super_delegate.rb', line 9

def casting_library_matcher
  @casting_library_matcher
end

.debugging_matcherObject

Returns the value of attribute debugging_matcher.



9
10
11
# File 'lib/casting/super_delegate.rb', line 9

def debugging_matcher
  @debugging_matcher
end

.gem_home_matcherObject

Returns the value of attribute gem_home_matcher.



9
10
11
# File 'lib/casting/super_delegate.rb', line 9

def gem_home_matcher
  @gem_home_matcher
end

Instance Method Details

#calling_location(call_stack) ⇒ Object



61
62
63
64
65
# File 'lib/casting/super_delegate.rb', line 61

def calling_location(call_stack)
  call_stack.reject { |line|
    line.to_s.match? Regexp.union(casting_library_matcher, gem_home_matcher, debugging_matcher)
  }.first
end

#casting_library_matcherObject



86
87
88
# File 'lib/casting/super_delegate.rb', line 86

def casting_library_matcher
  SuperDelegate.casting_library_matcher ||= Regexp.new(Dir.pwd.to_s + "/lib")
end

#debugging_matcherObject



94
95
96
# File 'lib/casting/super_delegate.rb', line 94

def debugging_matcher
  SuperDelegate.debugging_matcher
end

#gem_home_matcherObject



90
91
92
# File 'lib/casting/super_delegate.rb', line 90

def gem_home_matcher
  SuperDelegate.gem_home_matcher ||= Regexp.new(ENV["GEM_HOME"])
end

#method_delegate_skipping(meth, skipped) ⇒ Object



54
55
56
57
58
59
# File 'lib/casting/super_delegate.rb', line 54

def method_delegate_skipping(meth, skipped)
  skipped_index = __delegates__.index(skipped)
  __delegates__[(skipped_index + 1)..__delegates__.length].find { |attendant|
    attendant_methods(attendant).include?(meth)
  }
end

#name_and_owner_of_calling_method(call_stack) ⇒ Object



72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/casting/super_delegate.rb', line 72

def name_and_owner_of_calling_method(call_stack)
  label = calling_location(call_stack).label
  # Ruby 3.4.7+ includes module name in label (e.g., "ModuleName#method_name")
  # Ruby 3.3 and earlier just has method name
  parts = label.split("#")
  if parts.length > 1
    # Ruby 3.4.7+: has owner prefix
    [parts.last.to_sym, parts.first]
  else
    # Ruby 3.3 and earlier: no owner prefix
    [parts.first.to_sym, nil]
  end
end

#name_of_calling_method(call_stack) ⇒ Object



67
68
69
70
# File 'lib/casting/super_delegate.rb', line 67

def name_of_calling_method(call_stack)
  method_name, _owner = name_and_owner_of_calling_method(call_stack)
  method_name
end

#super_delegate(mod = :none, *args, **kwargs, &block) ⇒ Object

Call the method of the same name defined in the next delegate stored in your object

Because Casting creates an alternative method lookup path using a collection of delegates, you may use ‘super_delegate` to work like `super`.

If you use this feature, be sure that you have created a delegate collection which does have the method you need or you’ll see a NoMethodError.

Example:

module Greeter

def greet
  "Hello"
end

end

module FormalGreeter

include Casting::Super

def greet
  "#{super_delegate}, how do you do?"
end

end

some_object.cast_as(Greeter, FormalGreeter) some_object.greet #=> ‘Hello, how do you do?’



38
39
40
41
42
43
44
45
46
47
48
# File 'lib/casting/super_delegate.rb', line 38

def super_delegate(mod = :none, *args, **kwargs, &block)
  method_name, method_owner = name_and_owner_of_calling_method(caller_locations)
  owner = (mod unless mod == :none) || method_delegate(method_name)

  super_delegate_method = unbound_method_from_next_delegate(method_name, owner)
  super_delegate_method.bind_call(self, *args, **kwargs, &block)
rescue NameError
  # Use the method_owner from the call stack for consistent error messages
  owner_name = method_owner || owner
  raise NoMethodError.new("super_delegate: no delegate method `#{owner_name}##{method_name}' for #{inspect} from ")
end

#unbound_method_from_next_delegate(method_name, *skipped) ⇒ Object



50
51
52
# File 'lib/casting/super_delegate.rb', line 50

def unbound_method_from_next_delegate(method_name, *skipped)
  method_delegate_skipping(method_name, *skipped).instance_method(method_name)
end