Class: Avo::PluginManager

Inherits:
Object
  • Object
show all
Defined in:
lib/avo/plugin_manager.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializePluginManager

Returns a new instance of PluginManager.



8
9
10
11
12
13
# File 'lib/avo/plugin_manager.rb', line 8

def initialize
  @plugins = []
  @engines = []
  @building_plugins = []
  @building_engines = []
end

Instance Attribute Details

#enginesObject (readonly)

Returns the value of attribute engines.



4
5
6
# File 'lib/avo/plugin_manager.rb', line 4

def engines
  @engines
end

#pluginsObject (readonly) Also known as: all

Returns the value of attribute plugins.



3
4
5
# File 'lib/avo/plugin_manager.rb', line 3

def plugins
  @plugins
end

Instance Method Details

#as_json(*arg) ⇒ Object



88
89
90
91
92
93
94
95
# File 'lib/avo/plugin_manager.rb', line 88

def as_json(*arg)
  plugins.map do |plugin|
    {
      klass: plugin.to_s,
      priority: plugin.priority,
    }
  end
end

#begin_reloadObject

Starts a re-registration cycle without touching the currently published @plugins/@engines, so concurrent readers (e.g. mount_avo's route-drawing loop) keep seeing the complete pre-reload list until #commit_reload publishes the new one. Called by Avo.boot inside its Mutex, so only one reload is ever building at a time.



20
21
22
23
# File 'lib/avo/plugin_manager.rb', line 20

def begin_reload
  @building_plugins = []
  @building_engines = []
end

#commit_reloadObject

Publishes the registrations collected since #begin_reload. Each of the two reassignments below is individually an atomic pointer swap, so a reader of .engines alone (or .plugins alone) always sees the complete old list or the complete new one, never a partially rebuilt one. The two fields are not published jointly -- a reader combining both in one operation could observe them from different reload generations. No current reader does that (mount_avo reads only .engines; installed? reads only .plugins).



33
34
35
36
37
38
39
40
41
42
43
44
45
# File 'lib/avo/plugin_manager.rb', line 33

def commit_reload
  @plugins = @building_plugins
  @engines = @building_engines
  # Reset to fresh arrays rather than nil: register/mount_engine must
  # stay callable even outside a begin_reload/commit_reload window --
  # e.g. avo-permissions calls Avo.plugin_manager.register at Rails
  # initializer time, before Avo.boot ever runs. Such a call is simply
  # discarded by the next #begin_reload rather than raising, matching
  # the pre-atomic-publish behavior where register/mount_engine never
  # crashed regardless of timing.
  @building_plugins = []
  @building_engines = []
end

#installed?(name) ⇒ Boolean

Returns:

  • (Boolean)


105
106
107
108
109
# File 'lib/avo/plugin_manager.rb', line 105

def installed?(name)
  plugins.any? do |plugin|
    plugin.name.to_s == name.to_s
  end
end

#mount_engine(klass, **options) ⇒ Object



111
112
113
114
115
116
117
# File 'lib/avo/plugin_manager.rb', line 111

def mount_engine(klass, **options)
  # Dedup by class so a plugin hook that (by bug) calls mount_engine
  # twice for the same engine within one boot can't leave a duplicate
  # entry for #commit_reload to publish.
  @building_engines.delete_if { |engine| engine[:klass] == klass }
  @building_engines << {klass:, options:}
end

#register(name, priority: 10) ⇒ Object



47
48
49
50
51
52
53
54
# File 'lib/avo/plugin_manager.rb', line 47

def register(name, priority: 10)
  # Capture the file that called `register` so the plugin can later resolve
  # the gem it actually ships in. Plugins register under nicknames
  # (e.g. `:rhino` for `avo-rhino_field`), so the name alone isn't enough.
  registered_from = caller_locations(1, 1)&.first&.path

  @building_plugins << Plugin.new(name:, priority: priority, registered_from: registered_from)
end

#register_field(method_name, klass) ⇒ Object



60
61
62
63
64
65
66
67
68
# File 'lib/avo/plugin_manager.rb', line 60

def register_field(method_name, klass)
  # Avo.boot method is executed multiple times.
  # During the first run, it correctly loads descendants of Avo::Fields::Base.
  # Plugins are then loaded, introducing additional descendants to Avo::Fields::Base.
  # On subsequent runs, Avo::Fields::Base descendants now include these plugin fields.
  # This field_name_attribute assign forces the field name to retain the registered name instead of being computed dynamically from the field class.
  klass.field_name_attribute = method_name
  Avo.field_manager.load_field method_name, klass
end

#register_menu_item(name, &block) ⇒ Object

Register a custom menu DSL method (e.g. form) contributed by a plugin, so it can be used inside config.main_menu and friends. Delegates to avo-menu's builder when it's installed; a no-op otherwise, so plugins can register unconditionally. The block is evaluated in the menu builder's context, so it can call link, resource, etc. See Avo::Menu::Builder.register_item.



76
77
78
79
80
# File 'lib/avo/plugin_manager.rb', line 76

def register_menu_item(name, &block)
  return unless defined?(Avo::Menu::Builder)

  Avo::Menu::Builder.register_item(name, &block)
end

#register_resource_toolObject



82
83
# File 'lib/avo/plugin_manager.rb', line 82

def register_resource_tool
end

#register_toolObject



85
86
# File 'lib/avo/plugin_manager.rb', line 85

def register_tool
end

#register_view_type(name, component:, icon:, active_icon:, translation_key: nil) ⇒ Object



56
57
58
# File 'lib/avo/plugin_manager.rb', line 56

def register_view_type(name, component:, icon:, active_icon:, translation_key: nil)
  Avo.view_type_manager.register(name, component:, icon:, active_icon:, translation_key:)
end

#to_sObject



97
98
99
100
101
102
103
# File 'lib/avo/plugin_manager.rb', line 97

def to_s
  plugins.map do |plugin|
    plugin.to_s
  end.join(",")
rescue
  "Failed to fetch plugins."
end