class Toys::ModuleLookup

A helper module that provides methods to do module lookups. This is used to obtain named helpers, middleware, and templates from the respective modules.

Public Class Methods

new() click to toggle source

Create an empty ModuleLookup

# File lib/toys/module_lookup.rb, line 58
def initialize
  @mutex = ::Monitor.new
  @paths = []
  @paths_locked = false
end
path_to_module(path) click to toggle source

Given a require path, return the module expected to be defined.

@param path [String] File path, delimited by forward slash @return [Module] The module loaded from that path

# File lib/toys/module_lookup.rb, line 44
def path_to_module(path)
  path.split("/").reduce(::Object) do |running_mod, seg|
    mod_name = to_module_name(seg)
    unless running_mod.constants.include?(mod_name)
      raise ::NameError, "Module #{running_mod.name}::#{mod_name} not found"
    end
    running_mod.const_get(mod_name)
  end
end
to_module_name(str) click to toggle source

Convert the given string to a module name. Specifically, converts to `UpperCamelCase`, and then to a symbol.

@param str [String,Symbol] String to convert. @return [Symbol] Converted name

# File lib/toys/module_lookup.rb, line 33
def to_module_name(str)
  str = str.to_s.sub(/^_/, "").sub(/_$/, "").gsub(/_+/, "_")
  str.to_s.gsub(/(?:^|_)([a-zA-Z])/) { ::Regexp.last_match(1).upcase }.to_sym
end
to_path_name(str) click to toggle source

Convert the given string to a path element. Specifically, converts to `lower_snake_case`.

@param str [String,Symbol] String to convert. @return [String] Converted string

# File lib/toys/module_lookup.rb, line 20
def to_path_name(str)
  str = str.to_s.sub(/^_/, "").sub(/_$/, "").gsub(/_+/, "_")
  while str.sub!(/([^_])([A-Z])/, "\\1_\\2") do end
  str.downcase
end

Public Instance Methods

add_path(path_base, module_base: nil, high_priority: false) click to toggle source

Add a lookup path for modules.

@param path_base [String] The base require path @param module_base [Module] The base module, or `nil` (the default) to

infer a default from the path base.

@param high_priority [Boolean] If true, add to the head of the lookup

path, otherwise add to the end.

@return [self]

# File lib/toys/module_lookup.rb, line 74
def add_path(path_base, module_base: nil, high_priority: false)
  module_base ||= ModuleLookup.path_to_module(path_base)
  @mutex.synchronize do
    raise "You cannot add a path after a lookup has already occurred." if @paths_locked
    if high_priority
      @paths.unshift([path_base, module_base])
    else
      @paths << [path_base, module_base]
    end
  end
  self
end
lookup(name) click to toggle source

Obtain a named module. Returns `nil` if the name is not present.

@param name [String,Symbol] The name of the module to return. @return [Module] The specified module

# File lib/toys/module_lookup.rb, line 93
def lookup(name)
  @mutex.synchronize do
    @paths_locked = true
    @paths.each do |path_base, module_base|
      path = "#{path_base}/#{ModuleLookup.to_path_name(name)}"
      begin
        require path
      rescue ::LoadError
        next
      end
      mod_name = ModuleLookup.to_module_name(name)
      unless module_base.constants.include?(mod_name)
        raise ::NameError,
              "File #{path.inspect} did not define #{module_base.name}::#{mod_name}"
      end
      return module_base.const_get(mod_name)
    end
  end
  nil
end