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
Create an empty ModuleLookup
# File lib/toys/module_lookup.rb, line 58 def initialize @mutex = ::Monitor.new @paths = [] @paths_locked = false end
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
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
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 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
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