Class: AIA::PluginLoader

Inherits:
Object
  • Object
show all
Defined in:
lib/aia/plugin_loader.rb

Overview

Loads, reloads, and unloads top-level Ruby plugin files from the configured plugins directory. A plugin is any *.rb file directly under config.paths.plugins_dir; it may define top-level (Kernel) methods and/or top-level constants. The loader tracks exactly what each file defines so a changed plugin can be cleanly reloaded and a deleted plugin fully removed from the running process.

Class Method Summary collapse

Class Method Details

.load!(config) ⇒ Array<String>

Load every *.rb file at the top level of the plugins directory, starting from a clean slate (any previously-tracked definitions are removed first). Returns the sorted basenames of the loaded plugins.

Parameters:

  • config (#paths, #loaded_plugins=)

Returns:

  • (Array<String>)


28
29
30
31
32
33
34
35
36
37
# File 'lib/aia/plugin_loader.rb', line 28

def load!(config)
  @config = config
  reset!(config)

  dir = plugins_dir(config)
  return [] unless dir

  Dir.glob(File.join(dir, '*.rb')).each { |file| load_file(file, config) }
  loaded_names
end

.load_file(path, config = @config) ⇒ String?

Load — or, if already loaded, reload — a single plugin file. On reload, the file's previous definitions are removed before re-executing so that renamed/deleted methods don't linger. Returns the basename, or nil on failure.

Parameters:

  • path (String)

    absolute path to a *.rb plugin file

  • config (Object, nil) (defaults to: @config)

    config to keep loaded_plugins in sync with

Returns:

  • (String, nil)


47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/aia/plugin_loader.rb', line 47

def load_file(path, config = @config)
  mutex.synchronize do
    name = File.basename(path, '.rb')
    remove_definitions(registry[name]) if registry.key?(name)

    before = current_definitions
    load(path)
    defined = diff_definitions(before, current_definitions)

    registry[name] = { path: path, signature: signature(path), defined: defined }
    sync_config(config)
    name
  end
rescue ScriptError, StandardError => e
  warn_failure(path, e)
  nil
end

.loaded_namesArray<String>

Returns sorted basenames currently loaded.

Returns:

  • (Array<String>)

    sorted basenames currently loaded



81
82
83
# File 'lib/aia/plugin_loader.rb', line 81

def loaded_names
  registry.keys.sort
end

.mutexObject



18
19
20
# File 'lib/aia/plugin_loader.rb', line 18

def mutex
  @mutex ||= Mutex.new
end

.registryObject

Per-process registry of loaded plugins, keyed by basename. Each value: { path:, signature:, defined: { methods: [Symbol], constants: [Symbol] } }



14
15
16
# File 'lib/aia/plugin_loader.rb', line 14

def registry
  @registry ||= {}
end

.reset!(config = @config) ⇒ void

This method returns an undefined value.

Remove all tracked plugin definitions and clear the registry. Used by load! for a clean reload and by tests for isolation.

Parameters:

  • config (Object, nil) (defaults to: @config)


90
91
92
93
94
95
96
# File 'lib/aia/plugin_loader.rb', line 90

def reset!(config = @config)
  mutex.synchronize do
    registry.each_value { |entry| remove_definitions(entry) }
    registry.clear
    sync_config(config)
  end
end

.unload_file(name, config = @config) ⇒ String?

Unload a plugin by basename: remove the methods and constants it defined and forget it. Returns the basename if it was loaded, else nil.

Parameters:

  • name (String)

    plugin basename (no extension)

  • config (Object, nil) (defaults to: @config)

Returns:

  • (String, nil)


71
72
73
74
75
76
77
78
# File 'lib/aia/plugin_loader.rb', line 71

def unload_file(name, config = @config)
  mutex.synchronize do
    entry = registry.delete(name)
    remove_definitions(entry)
    sync_config(config)
    entry && name
  end
end