Module: T::Private::Abstract::Declare

Defined in:
lib/types/private/abstract/declare.rb

Overview

typed: true

Constant Summary collapse

Abstract =
T::Private::Abstract
AbstractUtils =
T::AbstractUtils

Class Method Summary collapse

Class Method Details

.declare_abstract(mod, type:) ⇒ Object



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
# File 'lib/types/private/abstract/declare.rb', line 8

def self.declare_abstract(mod, type:)
  if AbstractUtils.abstract_module?(mod)
    raise "#{mod} is already declared as abstract"
  end
  if T::Private::Final.final_module?(mod)
    raise "#{mod} was already declared as final and cannot be declared as abstract"
  end

  Abstract::Data.set(mod, :can_have_abstract_methods, true)
  Abstract::Data.set(mod.singleton_class, :can_have_abstract_methods, true)
  Abstract::Data.set(mod, :abstract_type, type)

  mod.extend(Abstract::Hooks)
  mod.extend(T::InterfaceWrapper::Helpers)

  if mod.is_a?(Class)
    if type == :interface
      # Since `interface!` is just `abstract!` with some extra validation, we could technically
      # allow this, but it's unclear there are good use cases, and it might be confusing.
      raise "Classes can't be interfaces. Use `abstract!` instead of `interface!`."
    end

    if mod.instance_method(:initialize).owner == mod
      raise "You must call `abstract!` *before* defining an initialize method"
    end

    # Don't need to silence warnings via without_ruby_warnings when calling
    # define_method because of the guard above

    mod.send(:define_method, :initialize) do |*args, &blk|
      if self.class == mod
        raise "#{mod} is declared as abstract; it cannot be instantiated"
      end
      super(*args, &blk)
    end
  end
end