module NamedImports

Constants

VERSION

Public Class Methods

from(raw_path, constant_names, context = Object) click to toggle source
# File lib/named_imports.rb, line 10
def from(raw_path, constant_names, context = Object)
  into_path, import_line = caller_path_and_line
  from_path = full_path_for_import(from_path: raw_path, into_path: into_path)
  sandbox = sandbox_eval(file_path: from_path)
  load_constants(constant_names, from_sandbox: sandbox, into_context: context)
end
import(context = Object, &block) click to toggle source
# File lib/named_imports.rb, line 17
def import(context = Object, &block)
  into_path, import_line = caller_path_and_line
  constant_names = []

  begin
    block.call
  rescue => e
    constant_name = constant_name_from_error(e)

    if constant_name.nil?
      raise NamedImports::Error::ImportBlockError.new(into_path, import_line, e)
    end

    if !constant_names.include?(constant_name)
      constant_names << constant_name
      context.const_set(constant_name, nil)
    end

    retry
  end

  constant_names
end
make_named_imports_available(in_context: Object) click to toggle source
# File lib/named_imports.rb, line 41
def make_named_imports_available(in_context: Object)
  def_method = in_context == Object ? :define_method : :define_singleton_method

  in_context.send(def_method, :from) do |path, constants|
    NamedImports.from(path, constants, in_context)
  end

  in_context.send(def_method, :import) do |&block|
    NamedImports.import(in_context, &block)
  end
end

Private Class Methods

caller_path_and_line() click to toggle source
# File lib/named_imports.rb, line 55
def caller_path_and_line
  caller_path_info = caller(3..3).first
  caller_path_info.match(/\A(.+?):(\d+):/)[1..2]
end
catchable_missing_constant_error?(error) click to toggle source
# File lib/named_imports.rb, line 67
def catchable_missing_constant_error?(error)
  error.is_a?(NameError) && error.message =~ /uninitialized constant /
end
constant_name_from_error(error) click to toggle source
# File lib/named_imports.rb, line 71
def constant_name_from_error(error)
  return unless error.is_a?(NameError)
  constant_matcher = /uninitialized constant (?:#<Module:[^>]+>::)?(\w*(?:::\w+)*)$/
  constant_match = error.message.match(constant_matcher)
  constant_match && constant_match[1]
end
full_path_for_import(from_path:, into_path:) click to toggle source
# File lib/named_imports.rb, line 60
def full_path_for_import(from_path:, into_path:)
  ext = %r{[^/]+\.[^/]+$}.match?(from_path) ? "" : ".rb"
  from_path_with_ext = "#{from_path}#{ext}"
  importer_dir = File.dirname(into_path)
  File.expand_path(from_path_with_ext, importer_dir)
end
load_constants(constant_names, from_sandbox:, into_context:) click to toggle source
# File lib/named_imports.rb, line 93
def load_constants(constant_names, from_sandbox:, into_context:)
  constant_names.each do |constant_name|
    if into_context.const_defined?(constant_name, false)
      into_context.send(:remove_const, constant_name)
    end

    sandboxed_constant = from_sandbox.const_get(constant_name)
    into_context.const_set(constant_name, sandboxed_constant)
  end
end
read_content(file_path:) click to toggle source
# File lib/named_imports.rb, line 78
def read_content(file_path:)
  File.open(file_path, &:read)
end
sandbox_eval(file_path:) click to toggle source
# File lib/named_imports.rb, line 82
def sandbox_eval(file_path:)
  return @@sandboxes[file_path] if @@sandboxes[file_path]

  @@sandboxes[file_path] = Module.new
  sandbox_module = @@sandboxes[file_path]
  make_named_imports_available(in_context: sandbox_module)
  ruby_code = read_content(file_path: file_path)
  sandbox_module.class_eval(ruby_code, file_path)
  sandbox_module
end