Class: Cabriolet::PluginManager

Inherits:
Object
  • Object
show all
Includes:
Singleton
Defined in:
lib/cabriolet/plugin_manager.rb

Overview

Manages plugin lifecycle and registry for Cabriolet

The PluginManager is a thread-safe singleton that handles plugin discovery, loading, activation, and deactivation. It maintains plugin states and resolves dependencies.

Examples:

Access the plugin manager

manager = Cabriolet::PluginManager.instance
manager.discover_plugins
manager.load_plugin("my-plugin")
manager.activate_plugin("my-plugin")

Using global accessor

Cabriolet.plugin_manager.discover_plugins
Cabriolet.plugin_manager.list_plugins(state: :active)

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializePluginManager

Initialize the plugin manager



32
33
34
35
36
37
# File 'lib/cabriolet/plugin_manager.rb', line 32

def initialize
  @plugins = {}
  @formats = {}
  @mutex = Mutex.new
  @config = load_config
end

Instance Attribute Details

#formatsHash (readonly)

Returns Format registry.

Returns:

  • (Hash)

    Format registry



29
30
31
# File 'lib/cabriolet/plugin_manager.rb', line 29

def formats
  @formats
end

#pluginsHash (readonly)

Returns Plugin registry by name.

Returns:

  • (Hash)

    Plugin registry by name



26
27
28
# File 'lib/cabriolet/plugin_manager.rb', line 26

def plugins
  @plugins
end

Instance Method Details

#activate_plugin(name) ⇒ Boolean

Activate a plugin

Activates a loaded plugin. Calls the plugin’s activate method and transitions to :active state.

Examples:

Activate a plugin

manager.activate_plugin("my-plugin")

Parameters:

  • name (String)

    Plugin name

Returns:

  • (Boolean)

    True if activated successfully

Raises:



165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
# File 'lib/cabriolet/plugin_manager.rb', line 165

def activate_plugin(name)
  @mutex.synchronize do
    entry = @plugins[name]
    raise PluginError, "Plugin '#{name}' not found" unless entry

    if entry[:state] == :active
      return true
    end

    unless entry[:state] == :loaded
      raise PluginError,
            "Plugin '#{name}' must be loaded before activation"
    end

    begin
      plugin = entry[:instance]
      plugin.activate
      plugin.send(:update_state, :active)
      entry[:state] = :active

      true
    rescue StandardError => e
      plugin.send(:update_state, :failed)
      entry[:state] = :failed
      entry[:error] = e.message
      raise PluginError,
            "Failed to activate plugin '#{name}': #{e.message}"
    end
  end
end

#deactivate_plugin(name) ⇒ Boolean

Deactivate a plugin

Deactivates an active plugin. Calls the plugin’s deactivate method and transitions back to :loaded state.

Examples:

Deactivate a plugin

manager.deactivate_plugin("my-plugin")

Parameters:

  • name (String)

    Plugin name

Returns:

  • (Boolean)

    True if deactivated successfully

Raises:



209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
# File 'lib/cabriolet/plugin_manager.rb', line 209

def deactivate_plugin(name)
  @mutex.synchronize do
    entry = @plugins[name]
    raise PluginError, "Plugin '#{name}' not found" unless entry

    if entry[:state] != :active
      return true
    end

    begin
      plugin = entry[:instance]
      plugin.deactivate
      plugin.send(:update_state, :loaded)
      entry[:state] = :loaded

      true
    rescue StandardError => e
      entry[:error] = e.message
      raise PluginError,
            "Failed to deactivate plugin '#{name}': #{e.message}"
    end
  end
end

#discover_pluginsArray<String>

Discover available plugins

Searches for plugins in gem paths using the pattern ‘cabriolet/plugins/*/.rb’. Discovered plugins are added to the registry in :discovered state.

Examples:

Discover plugins

manager.discover_plugins
#=> ["plugin1", "plugin2"]

Returns:

  • (Array<String>)

    List of discovered plugin names



50
51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/cabriolet/plugin_manager.rb', line 50

def discover_plugins
  @mutex.synchronize do
    plugin_files = Gem.find_files("cabriolet/plugins/**/*.rb")

    plugin_files.each do |file|
      load_plugin_file(file)
    rescue StandardError => e
      warn "Failed to load plugin from #{file}: #{e.message}"
    end

    @plugins.keys
  end
end

#format_handler(format) ⇒ Class?

Get format handler

Examples:

Get format handler

handler = manager.format_handler(:myformat)

Parameters:

  • format (Symbol)

    Format identifier

Returns:

  • (Class, nil)

    Handler class or nil



325
326
327
328
329
# File 'lib/cabriolet/plugin_manager.rb', line 325

def format_handler(format)
  @mutex.synchronize do
    @formats[format]
  end
end

#list_plugins(state: nil) ⇒ Hash

List plugins

Returns plugin information, optionally filtered by state.

Examples:

List all plugins

manager.list_plugins
#=> { "plugin1" => {...}, "plugin2" => {...} }

List only active plugins

manager.list_plugins(state: :active)
#=> { "active-plugin" => {...} }

Parameters:

  • state (Symbol, nil) (defaults to: nil)

    Optional state filter (:discovered, :loaded, :active, :failed, :disabled)

Returns:

  • (Hash)

    Plugin information keyed by name



249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
# File 'lib/cabriolet/plugin_manager.rb', line 249

def list_plugins(state: nil)
  @mutex.synchronize do
    if state.nil?
      @plugins.transform_values do |entry|
        {
          metadata: entry[:metadata],
          state: entry[:state],
          error: entry[:error],
        }
      end
    else
      @plugins.select { |_, entry| entry[:state] == state }
        .transform_values do |entry|
          {
            metadata: entry[:metadata],
            state: entry[:state],
            error: entry[:error],
          }
      end
    end
  end
end

#load_plugin(name) ⇒ Boolean

Load a plugin

Loads and validates a discovered plugin. Calls the plugin’s setup method and transitions to :loaded state.

Examples:

Load a plugin

manager.load_plugin("my-plugin")

Parameters:

  • name (String)

    Plugin name

Returns:

  • (Boolean)

    True if loaded successfully

Raises:



122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
# File 'lib/cabriolet/plugin_manager.rb', line 122

def load_plugin(name)
  @mutex.synchronize do
    entry = @plugins[name]
    raise PluginError, "Plugin '#{name}' not found" unless entry

    if i[loaded active].include?(entry[:state])
      return true
    end

    begin
      plugin = entry[:instance]

      # Check dependencies
      check_dependencies!(entry[:metadata])

      # Call setup
      plugin.setup
      plugin.send(:update_state, :loaded)
      entry[:state] = :loaded

      true
    rescue StandardError => e
      plugin.send(:update_state, :failed)
      entry[:state] = :failed
      entry[:error] = e.message
      raise PluginError, "Failed to load plugin '#{name}': #{e.message}"
    end
  end
end

#plugin(name) ⇒ Plugin?

Get a plugin by name

Examples:

Get a plugin

plugin = manager.plugin("my-plugin")

Parameters:

  • name (String)

    Plugin name

Returns:

  • (Plugin, nil)

    Plugin instance or nil if not found



280
281
282
283
284
# File 'lib/cabriolet/plugin_manager.rb', line 280

def plugin(name)
  @mutex.synchronize do
    @plugins[name]&.dig(:instance)
  end
end

#plugin_active?(name) ⇒ Boolean

Check if a plugin is active

Examples:

Check plugin status

manager.plugin_active?("my-plugin") #=> true

Parameters:

  • name (String)

    Plugin name

Returns:

  • (Boolean)

    True if plugin is active



294
295
296
297
298
# File 'lib/cabriolet/plugin_manager.rb', line 294

def plugin_active?(name)
  @mutex.synchronize do
    @plugins[name]&.dig(:state) == :active
  end
end

#register(plugin_instance) ⇒ Boolean

Register a plugin instance

Adds a plugin to the registry. The plugin must be a valid Plugin instance with proper metadata.

Examples:

Register a plugin

plugin = MyPlugin.new(manager)
manager.register(plugin)

Parameters:

  • plugin_instance (Plugin)

    Plugin instance to register

Returns:

  • (Boolean)

    True if registered successfully

Raises:



78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# File 'lib/cabriolet/plugin_manager.rb', line 78

def register(plugin_instance)
  @mutex.synchronize do
    unless plugin_instance.is_a?(Plugin)
      raise PluginError,
            "Plugin must inherit from Cabriolet::Plugin"
    end

    # Validate plugin
    validation = PluginValidator.validate(plugin_instance.class)
    unless validation[:valid]
      raise PluginError,
            "Plugin validation failed: #{validation[:errors].join(', ')}"
    end

    meta = plugin_instance.
    name = meta[:name]

    if @plugins.key?(name)
      raise PluginError, "Plugin '#{name}' already registered"
    end

    @plugins[name] = {
      instance: plugin_instance,
      metadata: meta,
      state: :discovered,
    }

    true
  end
end

#register_format(format, handler) ⇒ void

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

This method returns an undefined value.

Register a format handler

Called by plugins to register format handlers. This is used internally by Plugin#register_format.

Parameters:

  • format (Symbol)

    Format identifier

  • handler (Class)

    Handler class



311
312
313
314
315
# File 'lib/cabriolet/plugin_manager.rb', line 311

def register_format(format, handler)
  @mutex.synchronize do
    @formats[format] = handler
  end
end