module Requirium

Automatically calls Kernel#load or Kernel#require on first use. Example usage:

module M
  extend Requirium

  autoload :A
  autoload :B, 'b', 'b1', 'b2'
  autoload A: nil, B: ['b', 'b1', 'b2']
  autoload_relative :X
  autoload_relative :Y, 'y', 'y1', 'y2'
  autoload_relative X: nil, Y: ['y', 'y1', 'y2']

  autorequire :A
  autorequire :B, 'b', 'b1', 'b2'
  autorequire A: nil, B: ['b', 'b1', 'b2']
  autorequire_relative :X
  autorequire_relative :Y, 'y', 'y1', 'y2'
  autorequire_relative X: nil, Y: ['y', 'y1', 'y2']
end

Constants

VERSION

Attributes

loader_thread[R]
queue[R]

Private Class Methods

mri?() click to toggle source
# File lib/requirium.rb, line 153
def self.mri?
  (!defined?(RUBY_ENGINE) || RUBY_ENGINE == 'ruby') && RUBY_DESCRIPTION !~ /Enterprise/
end

Public Instance Methods

const_missing(sym) click to toggle source

def const_defined?(*args)

Requirium.synchronize { super }

end

Calls superclass method
# File lib/requirium.rb, line 66
def const_missing(sym)
  # if mri, use binding nesting
  nesting = nil
  if Requirium.mri?
    return unless nesting = caller_nesting
  end

  info = ConstInfo.new(self, sym, nesting)

  if Thread.current == Requirium.loader_thread
    # this avoids deadlocks. it uses the current loading to load the remaining dependencies
    has, value = internal_load(info)
    return has ? value : super
  end

  Requirium.queue.push(info)
  info.wait_ready
  raise info.error if info.error
  info.has_value? ? info.value : super
end

Private Instance Methods

add_loader(type, args, dirname = nil) click to toggle source
# File lib/requirium.rb, line 89
def add_loader(type, args, dirname = nil)
  with_args(args) do |sym, paths|
    load_list { |l| l[sym.to_s] = type.new(sym, paths, dirname) }
  end
end
caller_nesting() click to toggle source
# File lib/requirium.rb, line 95
def caller_nesting
  cc = nil
  nst = nil
  count = 0

  t = Thread.current

  set_trace_func(lambda do |event, _, _, _, binding, _|
    if Thread.current == t
      if count == 2
        set_trace_func nil
        cc.call(nst = eval('Module.nesting', binding))
      elsif event == 'return'
        count += 1
      end
    end
  end)

  callcc { |cont| cc = cont } && nst
end
internal_load(info) click to toggle source
# File lib/requirium.rb, line 116
def internal_load(info)
  info.lookup_list.find do |klass|
    klass.send(:try_load, info.sym) if klass.singleton_class.include?(Requirium)
    return [true, klass.const_get(info.sym)] if klass.const_defined?(info.sym)
  end

  [false, nil]
end
load_list() { |load_list| ... } click to toggle source
# File lib/requirium.rb, line 125
def load_list
  @mutex ||= Mutex.new
  @load_list ||= {}
  @mutex.synchronize { yield(@load_list) }
end
try_load(sym) click to toggle source
# File lib/requirium.rb, line 131
def try_load(sym)
  str_sym = sym.to_s
  loader = load_list { |l| l[str_sym] }
  return unless loader
  loader.call(self)
  load_list { |l| l.delete(sym.to_s) }
  nil
end
with_args(args) { |sym, paths| ... } click to toggle source
# File lib/requirium.rb, line 140
def with_args(args)
  if args.length == 1 && args.first.is_a?(Hash)
    args.first.each do |sym, paths|
      next if const_defined?(sym)
      yield sym, paths
    end
  elsif !const_defined?(args.first)
    yield args.first, args[1..-1]
  end

  nil
end