Module: Modulation

Defined in:
lib/modulation/core.rb,
lib/modulation.rb,
lib/modulation/paths.rb,
lib/modulation/builder.rb,
lib/modulation/exports.rb,
lib/modulation/version.rb,
lib/modulation/module_mixin.rb,
lib/modulation/export_default.rb,
lib/modulation/export_from_receiver.rb

Overview

Implements main Modulation functionality

Defined Under Namespace

Modules: Builder, ExportDefault, ExportFromReceiver, Exports, ModuleMixin, Paths

Constant Summary collapse

DIR =
__dir__
RE_CONST =
/^[A-Z]/.freeze
CALLER_RANGE =
(1..1).freeze
GEM_REQUIRE_ERROR_MESSAGE =
<<~MSG
  Can't import from a gem that doesn't depend on Modulation. Please use `require` instead of `import`.
MSG
VERSION =
'1.2'

Class Attribute Summary collapse

Class Method Summary collapse

Class Attribute Details

.loaded_modulesHash (readonly)

Returns hash of loaded modules, mapping absolute paths to modules.

Returns:

  • (Hash)

    hash of loaded modules, mapping absolute paths to modules



15
16
17
# File 'lib/modulation/core.rb', line 15

def loaded_modules
  @loaded_modules
end

Class Method Details

.add_tags(tags) ⇒ Object



167
168
169
# File 'lib/modulation/core.rb', line 167

def add_tags(tags)
  Paths.add_tags(tags, caller(CALLER_RANGE).first)
end

.auto_import_map(path, options = {}, caller_location = caller(CALLER_RANGE).first) ⇒ Hash

Returns a hash mapping filenames to modules, with modules being imported upon This allows an application to load dependencies only when they are needed.

Parameters:

  • path (String)

    relative directory path

  • options (Hash) (defaults to: {})

    options

  • caller_location (String) (defaults to: caller(CALLER_RANGE).first)

    caller location

Returns:

  • (Hash)

    hash mapping filenames to modules



88
89
90
91
92
93
94
# File 'lib/modulation/core.rb', line 88

def auto_import_map(path, options = {}, caller_location = caller(CALLER_RANGE).first)
  abs_path = Paths.absolute_dir_path(path, caller_location)
  Hash.new do |h, k|
    fn = Paths.check_path(File.join(abs_path, k.to_s))
    h[k] = find_auto_import_module(fn, options)
  end
end

.create(arg = nil, &block) ⇒ Object



171
172
173
174
175
176
177
178
179
180
181
182
183
# File 'lib/modulation/core.rb', line 171

def create(arg = nil, &block)
  creator = import '@modulation/creator'
  return creator.from_block(block) if block

  case arg
  when Hash
    creator.from_hash(arg)
  when String
    creator.from_string(arg)
  else
    raise 'Invalid argument'
  end
end

.find_auto_import_module(filename, options) ⇒ Module

Imports and returns the given filename. If the module does not exist, returns the value in ‘options`, otherwise raises an error.

Parameters:

  • filename (String)

    filename

  • options (Hash)

    hash of options

Returns:

  • (Module)

    imported module



102
103
104
105
106
107
108
109
110
111
# File 'lib/modulation/core.rb', line 102

def find_auto_import_module(filename, options)
  if filename
    return @loaded_modules[filename] ||
           import_module_from_file(filename, caller)
  end

  return options[:not_found] if options.key?(:not_found)

  raise "Module not found #{filename}"
end

.full_backtrace!Object

Show full backtrace for errors occuring while loading a module. Normally Modulation will remove stack frames occurring inside the modulation.rb code in order to make backtraces more readable when debugging.



25
26
27
# File 'lib/modulation/core.rb', line 25

def full_backtrace!
  @full_backtrace = true
end

.import(path, caller_location = caller(CALLER_RANGE).first) ⇒ Module

Imports a module from a file If the module is already loaded, returns the loaded module.

Parameters:

  • path (String)

    unqualified file name

  • caller_location (String) (defaults to: caller(CALLER_RANGE).first)

    caller location

Returns:

  • (Module)

    loaded module object



38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/modulation/core.rb', line 38

def import(path, caller_location = caller(CALLER_RANGE).first)
  abs_path = Paths.process(path, caller_location)

  case abs_path
  when String
    @loaded_modules[abs_path] || import_module_from_file(abs_path, caller)
  when :require_gem
    raise_error(LoadError.new(GEM_REQUIRE_ERROR_MESSAGE), caller)
  else
    raise_error(LoadError.new("Module not found: #{path}"), caller)
  end
end

.import_all(path, caller_location = caller(CALLER_RANGE).first) ⇒ Array

Imports all source files in given directory

Parameters:

  • path (String)

    relative directory path

  • caller_location (String) (defaults to: caller(CALLER_RANGE).first)

    caller location

Returns:

  • (Array)

    array of module objects



56
57
58
59
60
61
# File 'lib/modulation/core.rb', line 56

def import_all(path, caller_location = caller(CALLER_RANGE).first)
  abs_path = Paths.absolute_dir_path(path, caller_location)
  Dir["#{abs_path}/**/*.rb"].map do |fn|
    @loaded_modules[fn] || import_module_from_file(fn, caller)
  end
end

.import_map(path, options = {}, caller_location = caller(CALLER_RANGE).first) ⇒ Hash

Imports all source files in given directory, returning a hash mapping filenames to modules.

Parameters:

  • path (String)

    relative directory path

  • options (Hash) (defaults to: {})

    options

  • caller_location (String) (defaults to: caller(CALLER_RANGE).first)

    caller location

Returns:

  • (Hash)

    hash mapping filenames to modules



70
71
72
73
74
75
76
77
78
79
# File 'lib/modulation/core.rb', line 70

def import_map(path, options = {},
               caller_location = caller(CALLER_RANGE).first)
  abs_path = Paths.absolute_dir_path(path, caller_location)
  use_symbols = options[:symbol_keys]
  Dir["#{abs_path}/*.rb"].each_with_object({}) do |fn, h|
    mod = @loaded_modules[fn] || import_module_from_file(fn, caller)
    name = File.basename(fn) =~ /^(.+)\.rb$/ && Regexp.last_match(1)
    h[use_symbols ? name.to_sym : name] = mod
  end
end

.import_module_from_file(path, import_caller) ⇒ Module

Creates a new module from a source file

Parameters:

  • path (String)

    source file name

Returns:



116
117
118
# File 'lib/modulation/core.rb', line 116

def import_module_from_file(path, import_caller)
  Builder.make(location: path, caller: import_caller)
end

.mock(path, mod, caller_location = caller(CALLER_RANGE).first) ⇒ void

This method returns an undefined value.

Maps the given path to the given mock module, restoring the previously loaded module (if any) after calling the given block

Parameters:

  • path (String)

    module path

  • mod (Module)

    module

  • caller_location (String) (defaults to: caller(CALLER_RANGE).first)

    caller location



158
159
160
161
162
163
164
165
# File 'lib/modulation/core.rb', line 158

def mock(path, mod, caller_location = caller(CALLER_RANGE).first)
  path = Paths.absolute_path(path, caller_location)
  old_module = @loaded_modules[path]
  @loaded_modules[path] = mod
  yield if block_given?
ensure
  @loaded_modules[path] = old_module if block_given?
end

.raise_error(error, backtrace = nil) ⇒ void

This method returns an undefined value.

(Re-)raises an error, potentially filtering its backtrace to remove stack frames occuring in Modulation code

Parameters:

  • error (Error)

    raised error

  • caller (Array)

    error backtrace



125
126
127
128
129
130
131
132
133
134
135
136
# File 'lib/modulation/core.rb', line 125

def raise_error(error, backtrace = nil)
  if backtrace
    unless @full_backtrace
      i = 0
      backtrace = backtrace.reject do |l|
        (i += 1) > 1 && l =~ /^#{Modulation::DIR}/
      end
    end
    error.set_backtrace(backtrace)
  end
  raise error
end

.reload(mod) ⇒ Module

Reloads the given module from its source file

Parameters:

  • mod (Module, String)

    module to reload

Returns:



141
142
143
144
145
146
147
148
149
150
# File 'lib/modulation/core.rb', line 141

def reload(mod)
  if mod.is_a?(String)
    path = mod
    mod = @loaded_modules[File.expand_path(mod)]
    raise "No module loaded from #{path}" unless mod
  end

  Builder.reload_module_code(mod)
  mod
end

.reset!Object

Resets the loaded modules hash



18
19
20
# File 'lib/modulation/core.rb', line 18

def reset!
  @loaded_modules = {}
end