Class: Takagi::Plugin

Inherits:
Object
  • Object
show all
Defined in:
lib/takagi/plugin.rb

Overview

Plugin manager: registers plugins, validates metadata/config, resolves dependencies, and emits lifecycle events.

Defined Under Namespace

Classes: PluginInfo, RoutePrefixProxy

Class Attribute Summary collapse

Class Method Summary collapse

Class Attribute Details

.registryObject (readonly)

Returns the value of attribute registry.



15
16
17
# File 'lib/takagi/plugin.rb', line 15

def registry
  @registry
end

Class Method Details

.auto_discover!Object

Auto-discover plugins under Takagi::Plugins namespace and takagi-plugin-* gems



97
98
99
100
# File 'lib/takagi/plugin.rb', line 97

def auto_discover!
  discover_namespace_plugins
  discover_gem_plugins
end

.disable(name, app:) ⇒ Object



72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
# File 'lib/takagi/plugin.rb', line 72

def disable(name, app:)
  info = @mutex.synchronize { @registry[name.to_sym] }
  return unless info&.enabled

  Takagi::Hooks.emit(:plugin_disabling, name: name, metadata: info.)
  plugin = info.module
  plugin.before_unload(app) if plugin.respond_to?(:before_unload)
  plugin.shutdown(app) if plugin.respond_to?(:shutdown)
  info.enabled = false
  Takagi::Hooks.emit(:plugin_disabled, name: name, metadata: info.)
  info
rescue StandardError => e
  Takagi::Hooks.emit(:plugin_error, name: name, metadata: info&., error: e)
  raise
end

.enable(name, app:, options: {}) ⇒ Object

Enable a plugin by name. Optionally pass options to apply.



46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/takagi/plugin.rb', line 46

def enable(name, app:, options: {})
  info = @mutex.synchronize { @registry[name.to_sym] }
  raise ArgumentError, "Plugin #{name} not registered" unless info
  return info if info.enabled

  validate_version!(info)
  resolve_dependencies!(info, app: app)

  validated_options = validate_config!(info.module, options, plugin_name: info.name)

  app_for_plugin = wrap_app_with_prefix(app, info)

  Takagi::Hooks.emit(:plugin_enabling, name: name, metadata: info., options: validated_options)
  plugin = info.module
  plugin.before_apply(app_for_plugin, validated_options) if plugin.respond_to?(:before_apply)
  plugin.apply(app_for_plugin, validated_options)
  plugin.after_apply(app_for_plugin, validated_options) if plugin.respond_to?(:after_apply)

  info.enabled = true
  Takagi::Hooks.emit(:plugin_enabled, name: name, metadata: info.)
  info
rescue StandardError => e
  Takagi::Hooks.emit(:plugin_error, name: name, metadata: info&., error: e)
  raise
end

.listObject



92
93
94
# File 'lib/takagi/plugin.rb', line 92

def list
  @mutex.synchronize { @registry.values.map { |info| { name: info.name, enabled: info.enabled, metadata: info. } } }
end

.register(plugin_module) ⇒ Object

Register a plugin module that responds to .apply(app, opts = {}) and .metadata



18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# File 'lib/takagi/plugin.rb', line 18

def register(plugin_module)
   = (plugin_module)
  raw_name = [:name] || plugin_module.name&.split('::')&.last || "plugin_#{plugin_module.object_id}"
  name = raw_name.to_sym

  @mutex.synchronize do
    # Return existing info if already registered
    return @registry[name] if @registry.key?(name)

    deps = Array([:dependencies]).map { |dep| normalize_dependency(dep) }
    requires = [:requires]

    info = PluginInfo.new(
      name: name.to_sym,
      module: plugin_module,
      enabled: false,
      metadata: ,
      dependencies: deps,
      requires: requires
    )

    @registry[name.to_sym] = info
    Takagi::Hooks.emit(:plugin_registered, name: name, metadata: )
    info
  end
end

.unregister(name) ⇒ Object



88
89
90
# File 'lib/takagi/plugin.rb', line 88

def unregister(name)
  @mutex.synchronize { @registry.delete(name.to_sym) }
end