Module: Aws::Templates::Utils::Autoload

Included in:
Module
Defined in:
lib/aws/templates/utils/autoload.rb

Overview

Lazy load implementation

It allows to skip ‘require’ definitions and load all classes and modules by convention.

Constant Summary collapse

REQUIRE_LOCKER =
Concurrent::Map.new
MODULE_LOCKER =
Concurrent::Map.new
Trace =
TracePoint.new(:class) { |tp| Autoload.autoload!(tp.self) }

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

._check_if_required(path, obj) ⇒ Object



28
29
30
31
32
33
34
35
# File 'lib/aws/templates/utils/autoload.rb', line 28

def self._check_if_required(path, obj)
  raise(obj) if obj.is_a?(::Exception)
  return if obj.owned?

  obj.lock.unlock
  locker_obj = REQUIRE_LOCKER[path]
  raise locker_obj if locker_obj.is_a?(Exception)
end

._try_to_require(path, mutex) ⇒ Object



19
20
21
22
23
24
25
26
# File 'lib/aws/templates/utils/autoload.rb', line 19

def self._try_to_require(path, mutex)
  require path
rescue ScriptError, NoMemoryError, StandardError => e
  REQUIRE_LOCKER.get_and_set(path, e)
  raise e
ensure
  mutex.unlock
end

.atomic_require(path) ⇒ Object



37
38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/aws/templates/utils/autoload.rb', line 37

def self.atomic_require(path)
  mutex = Mutex.new.lock

  obj = REQUIRE_LOCKER.put_if_absent(path, mutex)

  if obj.nil?
    _try_to_require(path, mutex)
  else
    _check_if_required(path, obj)
  end

  true
end

.autoload!(mod) ⇒ Object



51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# File 'lib/aws/templates/utils/autoload.rb', line 51

def self.autoload!(mod)
  return if mod.name.nil?

  MODULE_LOCKER.compute_if_absent(mod) do
    path = mod.pathize

    begin
      atomic_require path
    rescue LoadError => e
      sanitize_load_exception(e, path)
    end

    true
  end
end

.const_is_loaded?(mod, const_name) ⇒ Boolean

Returns:

  • (Boolean)


81
82
83
84
85
86
87
88
89
90
91
# File 'lib/aws/templates/utils/autoload.rb', line 81

def self.const_is_loaded?(mod, const_name)
  const_path = Autoload.const_path_for(mod, const_name)

  begin
    atomic_require const_path
    true
  rescue LoadError => e
    Autoload.sanitize_load_exception(e, const_path)
    false
  end
end

.const_path_for(mod, const_name) ⇒ Object

Raises:

  • (ScriptError)


67
68
69
70
71
72
73
74
75
# File 'lib/aws/templates/utils/autoload.rb', line 67

def self.const_path_for(mod, const_name)
  raise ScriptError.new("Autoload is not supported for #{mod}") if mod.name.nil?

  path = const_name.to_s.pathize

  path = "#{mod.pathize}/#{path}" unless mod.root_namespace?

  path
end

.sanitize_load_exception(e, path) ⇒ Object



77
78
79
# File 'lib/aws/templates/utils/autoload.rb', line 77

def self.sanitize_load_exception(e, path)
  raise e unless e.path == path
end

Instance Method Details

#const_missing(const_name) ⇒ Object



93
94
95
96
97
98
99
100
101
102
103
# File 'lib/aws/templates/utils/autoload.rb', line 93

def const_missing(const_name)
  super(const_name) unless Autoload.const_is_loaded?(self, const_name)

  unless const_defined?(const_name)
    raise NameError.new(
      "::#{self}::#{const_name} is loaded but the constant is missing"
    )
  end

  const_get(const_name)
end

#lazyObject



105
106
107
# File 'lib/aws/templates/utils/autoload.rb', line 105

def lazy
  Lazy.new(self)
end

#reduce(_ = false) ⇒ Object



109
110
111
# File 'lib/aws/templates/utils/autoload.rb', line 109

def reduce(_ = false)
  self
end

#root_namespace?Boolean

Returns:

  • (Boolean)


113
114
115
# File 'lib/aws/templates/utils/autoload.rb', line 113

def root_namespace?
  (self == ::Kernel) || (self == ::Object)
end