Module: TypeToolkit::HasAbstractMethods

Defined in:
lib/type_toolkit/has_abstract_methods.rb

Overview

Given a class Foo, abstract methods can be defined at two levels:

  1. Instance methods available on the instance of Foo
  2. "Class methods" available on the Foo class itself

In case 1, the storage belongs one level up, in the class Foo. In case 2, the storage belongs two levels up, in the singleton class of Foo (Foo::<Foo>)

Instance Method Summary collapse

Instance Method Details

#__register_abstract_method(method_name) ⇒ Object

Private API, do not use directly. Only meant to be called from the abstract macro.

Signature:

  • (Symbol) -> void



14
15
16
17
18
# File 'lib/type_toolkit/has_abstract_methods.rb', line 14

def __register_abstract_method(method_name) # :nodoc:
  (
    @__abstract_methods ||= Set.new #: Set[Symbol]?
  ) << method_name
end

#abstract_instance_methods(include_super = true) ⇒ Object

Returns all methods that are abstract and have not been implemented.

Signature:

  • (?bool) -> Array[Symbol]



54
55
56
57
58
59
60
# File 'lib/type_toolkit/has_abstract_methods.rb', line 54

def abstract_instance_methods(include_super = true)
  #: self as HasAbstractMethods & Module[top]

  declared_abstract_instance_methods(include_super).reject do |m|
    method_defined?(m) || private_method_defined?(m)
  end
end

#abstract_method?(method_name) ⇒ Boolean

Returns true if the given method is abstract, and has not been implemented. Calling it will raise an AbstractMethodNotImplementedError.

Similar to public_method_defined? and friends, this method is called on a class to check if the method is defined for instances of that class. For example:

if Foo.abstract_method?(:instance_method)
  Foo.new.instance_method # Will raise AbstractMethodNotImplementedError
end

if Foo.singleton_class.abstract_method?(:class_method)
  Foo.class_method # Will raise AbstractMethodNotImplementedError
end

Signature:

  • (Symbol) -> bool

Returns:

  • (Boolean)


99
100
101
102
103
104
105
106
# File 'lib/type_toolkit/has_abstract_methods.rb', line 99

def abstract_method?(method_name)
  #: self as (HasAbstractMethods & Module[top])

  # If the method is defined, it has a concrete implementation, so it's not abstract.
  return false if method_defined?(method_name) || private_method_defined?(method_name)

  abstract_method_declared?(method_name)
end

#abstract_method_declared?(method_name) ⇒ Boolean

Returns true if the given method name was ever marked abstract, even if it has a concrete implementation.

Similar to public_method_defined? and friends, this method is called on a class to check if the method is defined for instances of that class. For example:

if Foo.abstract_method_declared?(:instance_method)
  Foo.new.instance_method # Might raise AbstractMethodNotImplementedError
end

if Foo.singleton_class.abstract_method_declared?(:class_method)
  Foo.class_method # Might raise AbstractMethodNotImplementedError
end

Signature:

  • (Symbol) -> bool

Returns:

  • (Boolean)


76
77
78
79
80
81
82
# File 'lib/type_toolkit/has_abstract_methods.rb', line 76

def abstract_method_declared?(method_name)
  #: self as Module[top]

  @__abstract_methods&.include?(method_name) ||
    included_modules.any? { |m| m.is_a?(HasAbstractMethods) && m.abstract_method_declared?(method_name) } ||
    (defined?(super) && super)
end

#declared_abstract_instance_methods(include_super = true) ⇒ Object

Returns all methods that were marked abstract (even those which are implemented).

Signature:

  • (?bool) -> Array[Symbol]



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
# File 'lib/type_toolkit/has_abstract_methods.rb', line 22

def declared_abstract_instance_methods(include_super = true)
  #: self as HasAbstractMethods & Module[top]

  result = @__abstract_methods

  return result.to_a unless include_super

  if defined?(super) && (super_abstract_methods = super)
    if result
      result.merge(super_abstract_methods)
    else
      result = super_abstract_methods
    end
  end

  abstract_methods_in_interfaces = included_modules.flat_map do |m|
    m.is_a?(HasAbstractMethods) ? m.declared_abstract_instance_methods : []
  end

  if abstract_methods_in_interfaces.any?
    if result&.any?
      result.merge(abstract_methods_in_interfaces)
    else
      result = abstract_methods_in_interfaces
    end
  end

  result.to_a
end