Add deferred loading for plugins which's requirements aren't all met yet #256
This commit is contained in:
parent
2753973ffe
commit
a24a24eb19
|
@ -38,7 +38,7 @@ class AdminController < ApplicationController
|
|||
end
|
||||
|
||||
def plugins
|
||||
@plugins = Redmine::Plugin.all
|
||||
@plugins = Redmine::Plugin.all.sort
|
||||
end
|
||||
|
||||
# Loads the default configuration
|
||||
|
|
|
@ -63,6 +63,10 @@ Rails::Initializer.run do |config|
|
|||
# It will automatically turn deliveries on
|
||||
config.action_mailer.perform_deliveries = false
|
||||
|
||||
# Use redmine's custom plugin locater
|
||||
require File.join(RAILS_ROOT, "lib/redmine_plugin_locator")
|
||||
config.plugin_locators << RedminePluginLocator
|
||||
|
||||
# Load any local configuration that is kept out of source control
|
||||
# (e.g. patches).
|
||||
if File.exists?(File.join(File.dirname(__FILE__), 'additional_environment.rb'))
|
||||
|
|
|
@ -13,8 +13,24 @@
|
|||
|
||||
module Redmine #:nodoc:
|
||||
|
||||
class PluginNotFound < StandardError; end
|
||||
class PluginRequirementError < StandardError; end
|
||||
class PluginError < StandardError
|
||||
attr_reader :plugin_id
|
||||
def initialize(plug_id=nil)
|
||||
super
|
||||
@plugin_id = plug_id
|
||||
end
|
||||
end
|
||||
class PluginNotFound < PluginError
|
||||
def to_s
|
||||
"Missing the plugin #{@plugin_id}"
|
||||
end
|
||||
end
|
||||
class PluginCircularDependency < PluginError
|
||||
def to_s
|
||||
"Circular plugin dependency in #{@plugin_id}"
|
||||
end
|
||||
end
|
||||
class PluginRequirementError < PluginError; end
|
||||
|
||||
# Base class for Redmine plugins.
|
||||
# Plugins are registered using the <tt>register</tt> class method that acts as the public constructor.
|
||||
|
@ -39,9 +55,11 @@ module Redmine #:nodoc:
|
|||
#
|
||||
# When rendered, the plugin settings value is available as the local variable +settings+
|
||||
class Plugin
|
||||
@registered_plugins = {}
|
||||
@registered_plugins = ActiveSupport::OrderedHash.new
|
||||
@deferred_plugins = {}
|
||||
|
||||
class << self
|
||||
attr_reader :registered_plugins
|
||||
attr_reader :registered_plugins, :deferred_plugins
|
||||
private :new
|
||||
|
||||
def def_field(*names)
|
||||
|
@ -59,6 +77,7 @@ module Redmine #:nodoc:
|
|||
|
||||
# Plugin constructor
|
||||
def self.register(id, &block)
|
||||
id = id.to_sym
|
||||
p = new(id)
|
||||
p.instance_eval(&block)
|
||||
# Set a default name if it was not provided during registration
|
||||
|
@ -67,17 +86,45 @@ module Redmine #:nodoc:
|
|||
# YAML translation files should be found under <plugin>/config/locales/
|
||||
::I18n.load_path += Dir.glob(File.join(RAILS_ROOT, 'vendor', 'plugins', id.to_s, 'config', 'locales', '*.yml'))
|
||||
registered_plugins[id] = p
|
||||
|
||||
# If there are plugins waiting for us to be loaded, we try loading those, again
|
||||
if deferred_plugins[id]
|
||||
deferred_plugins[id].each do |ary|
|
||||
plugin_id, block = ary
|
||||
register(plugin_id, &block)
|
||||
end
|
||||
deferred_plugins.delete(id)
|
||||
end
|
||||
|
||||
return p
|
||||
rescue PluginNotFound => e
|
||||
# find circular dependencies
|
||||
raise PluginCircularDependency.new(id) if self.dependencies_for(e.plugin_id).include?(id)
|
||||
if RedminePluginLocator.instance.has_plugin? e.plugin_id
|
||||
# The required plugin is going to be loaded later, defer loading this plugin
|
||||
(deferred_plugins[e.plugin_id] ||= []) << [id, block]
|
||||
return p
|
||||
else
|
||||
raise
|
||||
end
|
||||
end
|
||||
|
||||
# Returns an array off all registered plugins
|
||||
# returns an array of all dependencies we know of for plugin id
|
||||
# (might not be complete at all times!)
|
||||
def self.dependencies_for(id)
|
||||
direct_deps = deferred_plugins.keys.find_all{|k| deferred_plugins[k].collect(&:first).include?(id)}
|
||||
direct_deps.inject([]) {|deps,v| deps << v; deps += self.dependencies_for(v)}
|
||||
end
|
||||
|
||||
# Returns an array of all registered plugins
|
||||
def self.all
|
||||
registered_plugins.values.sort
|
||||
registered_plugins.values
|
||||
end
|
||||
|
||||
# Finds a plugin by its id
|
||||
# Returns a PluginNotFound exception if the plugin doesn't exist
|
||||
def self.find(id)
|
||||
registered_plugins[id.to_sym] || raise(PluginNotFound)
|
||||
registered_plugins[id.to_sym] || raise(PluginNotFound.new(id.to_sym))
|
||||
end
|
||||
|
||||
# Clears the registered plugins hash
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
class RedminePluginLocator < Rails::Plugin::FileSystemLocator
|
||||
def initialize(initializer)
|
||||
super
|
||||
@@instance = self
|
||||
end
|
||||
|
||||
def self.instance
|
||||
@@instance
|
||||
end
|
||||
|
||||
# This locator is not meant for loading plugins
|
||||
# The plugin loading is done by the default rails locator, this one is
|
||||
# only for querying available plugins easily
|
||||
def plugins(for_loading = true)
|
||||
return [] if for_loading
|
||||
super()
|
||||
end
|
||||
|
||||
def has_plugin?(name)
|
||||
plugins(false).collect(&:name).include? name.to_s
|
||||
end
|
||||
end
|
|
@ -36,7 +36,11 @@ namespace :db do
|
|||
|
||||
desc 'Migrate plugins to current status.'
|
||||
task :plugins => :environment do
|
||||
Engines.plugins.each do |plugin|
|
||||
redmine_plugins = Redmine::Plugin.all.collect(&:id)
|
||||
engines_plugins = Engines.plugins.collect(&:name).collect(&:to_sym)
|
||||
load_order = (engines_plugins - redmine_plugins) + (redmine_plugins & engines_plugins)
|
||||
load_order.each do |p|
|
||||
plugin = Engines.plugins[p]
|
||||
next unless plugin.respond_to?(:migration_directory)
|
||||
next unless File.exists? plugin.migration_directory
|
||||
puts "Migrating plugin #{plugin.name} ..."
|
||||
|
|
Loading…
Reference in New Issue