From d33c43440e0b1c27e6401b4f549295d32061ba2e Mon Sep 17 00:00:00 2001
From: Takashi Kato <tohosaku@users.osdn.me>
Date: Thu, 10 Nov 2022 22:40:49 +0000
Subject: [PATCH 1/4] Enable instantiation of PluginLoader

---
 lib/redmine/plugin.rb                         | 18 +++---
 lib/redmine/plugin_loader.rb                  | 59 ++++++++++++-------
 .../plugins/foo_plugin/app/javascript/foo.js  |  2 +
 test/fixtures/plugins/foo_plugin/init.rb      |  8 +++
 test/generators/controller_generator_test.rb  |  4 +-
 test/generators/migration_generator_test.rb   |  4 +-
 test/generators/model_generator_test.rb       |  4 +-
 test/integration/routing/plugins_test.rb      | 15 ++---
 test/unit/lib/redmine/plugin_test.rb          | 10 ++--
 9 files changed, 77 insertions(+), 47 deletions(-)
 create mode 100644 test/fixtures/plugins/foo_plugin/app/javascript/foo.js
 create mode 100644 test/fixtures/plugins/foo_plugin/init.rb

diff --git a/lib/redmine/plugin.rb b/lib/redmine/plugin.rb
index 74ae4efafc7..a70b2a0beae 100644
--- a/lib/redmine/plugin.rb
+++ b/lib/redmine/plugin.rb
@@ -49,13 +49,17 @@ module Redmine
   #
   # See: http://www.redmine.org/projects/redmine/wiki/Plugin_Tutorial
   class Plugin
+    cattr_accessor :loader
+
     # Absolute path to the directory where plugins are located
-    cattr_accessor :directory
-    self.directory = PluginLoader.directory
+    def self.directory
+      loader.directory
+    end
 
     # Absolute path to the public directory where plugins assets are copied
-    cattr_accessor :public_directory
-    self.public_directory = PluginLoader.public_directory
+    def self.public_directory
+      loader.public_directory
+    end
 
     @registered_plugins = {}
     @used_partials = {}
@@ -103,7 +107,7 @@ module Redmine
         raise PluginNotFound, "Plugin not found. The directory for plugin #{p.id} should be #{p.directory}."
       end
 
-      p.path = PluginLoader.directories.find {|d| d.to_s == p.directory}
+      p.path = loader.directories.find {|d| d.to_s == p.directory}
 
       # Adds plugin locales if any
       # YAML translation files should be found under <plugin>/config/locales/
@@ -192,9 +196,7 @@ module Redmine
     def asset_paths
       return unless path.has_assets_dir?
 
-      base_dir = Pathname.new(path.assets_dir)
-      paths = base_dir.children.select(&:directory?)
-      Redmine::AssetPath.new(base_dir, paths, asset_prefix)
+      Redmine::AssetPath.new(path.base_dir, path.asset_paths, asset_prefix)
     end
 
     def <=>(plugin)
diff --git a/lib/redmine/plugin_loader.rb b/lib/redmine/plugin_loader.rb
index 9fc634a6dd7..da37e56b41b 100644
--- a/lib/redmine/plugin_loader.rb
+++ b/lib/redmine/plugin_loader.rb
@@ -19,12 +19,13 @@
 
 module Redmine
   class PluginPath
-    attr_reader :assets_dir, :initializer
+    attr_reader :assets_dir, :initializer, :prefix
 
-    def initialize(dir)
+    def initialize(dir, prefix)
       @dir = dir
-      @assets_dir = File.join dir, 'assets'
-      @initializer = File.join dir, 'init.rb'
+      @prefix = prefix
+      @assets_dir = File.join @dir, 'assets'
+      @initializer = File.join @dir, 'init.rb'
     end
 
     def run_initializer
@@ -42,39 +43,55 @@ module Redmine
     def has_initializer?
       File.file?(@initializer)
     end
+
+    def base_dir
+      @base_dir ||= Pathname.new(assets_dir)
+    end
+
+    def asset_paths
+      paths = base_dir.children.select(&:directory?)
+      paths
+    end
   end
 
   class PluginLoader
     # Absolute path to the directory where plugins are located
-    cattr_accessor :directory
-    self.directory = Rails.root.join Rails.application.config.redmine_plugins_directory
+    attr_accessor :directory
+
+    attr_accessor :prefix
 
     # Absolute path to the public directory where plugins assets are copied
-    cattr_accessor :public_directory
-    self.public_directory = Rails.public_path.join('plugin_assets')
+    attr_accessor :public_directory
 
-    def self.load
-      setup
-      add_autoload_paths
+    def initialize(directory:, prefix:)
+      @directory = directory
+      @prefix = prefix
+      @plugin_directories = []
 
-      Rails.application.config.to_prepare do
-        PluginLoader.directories.each(&:run_initializer)
+      Dir.glob(File.join(directory, '*')).each do |directory|
+        next unless File.directory?(directory)
 
-        Redmine::Hook.call_hook :after_plugins_loaded
+        @plugin_directories << PluginPath.new(directory, prefix)
       end
     end
 
-    def self.setup
-      @plugin_directories = []
+    def self.load
+      directory = Rails.root.join(Rails.application.config.redmine_plugins_directory)
+      prefix    = 'plugin_assets'
+      loader = new(directory: directory, prefix: prefix)
 
-      Dir.glob(File.join(directory, '*')).each do |directory|
-        next unless File.directory?(directory)
+      loader.public_directory = Rails.public_path.join(prefix)
+      loader.add_autoload_paths
+
+      Rails.application.config.to_prepare do
+        Redmine::Plugin.loader = loader
+        loader.directories.each(&:run_initializer)
 
-        @plugin_directories << PluginPath.new(directory)
+        Redmine::Hook.call_hook :after_plugins_loaded
       end
     end
 
-    def self.add_autoload_paths
+    def add_autoload_paths
       directories.each do |directory|
         # Add the plugin directories to rails autoload paths
         engine_cfg = Rails::Engine::Configuration.new(directory.to_s)
@@ -86,7 +103,7 @@ module Redmine
       end
     end
 
-    def self.directories
+    def directories
       @plugin_directories
     end
   end
diff --git a/test/fixtures/plugins/foo_plugin/app/javascript/foo.js b/test/fixtures/plugins/foo_plugin/app/javascript/foo.js
new file mode 100644
index 00000000000..dcf7f8eaaae
--- /dev/null
+++ b/test/fixtures/plugins/foo_plugin/app/javascript/foo.js
@@ -0,0 +1,2 @@
+export sample() {
+}
diff --git a/test/fixtures/plugins/foo_plugin/init.rb b/test/fixtures/plugins/foo_plugin/init.rb
new file mode 100644
index 00000000000..24d99b3ebf1
--- /dev/null
+++ b/test/fixtures/plugins/foo_plugin/init.rb
@@ -0,0 +1,8 @@
+Redmine::Plugin.register :foo_plugin do
+  name 'Test plugin redmine_test_plugin_foo'
+  author 'Author name'
+  description 'This is a plugin for Redmine test'
+  version '0.0.1'
+end
+
+
diff --git a/test/generators/controller_generator_test.rb b/test/generators/controller_generator_test.rb
index 46aeac88e4c..08610e52d1f 100644
--- a/test/generators/controller_generator_test.rb
+++ b/test/generators/controller_generator_test.rb
@@ -29,11 +29,11 @@ class ControllerGeneratorTest < Rails::Generators::TestCase
 
   setup do
     @plugin_directory = Redmine::Plugin.directory
-    Redmine::Plugin.directory = TMP_DIR
+    Redmine::Plugin.loader.directory = TMP_DIR
   end
 
   teardown do
-    Redmine::Plugin.directory = @plugin_directory
+    Redmine::Plugin.loader.directory = @plugin_directory
   end
 
   def test_generates_files_from_templates
diff --git a/test/generators/migration_generator_test.rb b/test/generators/migration_generator_test.rb
index 8342d574192..23eed7bbe26 100644
--- a/test/generators/migration_generator_test.rb
+++ b/test/generators/migration_generator_test.rb
@@ -29,11 +29,11 @@ class MigrationGeneratorTest < Rails::Generators::TestCase
 
   setup do
     @plugin_directory = Redmine::Plugin.directory
-    Redmine::Plugin.directory = TMP_DIR
+    Redmine::Plugin.loader.directory = TMP_DIR
   end
 
   teardown do
-    Redmine::Plugin.directory = @plugin_directory
+    Redmine::Plugin.loader.directory = @plugin_directory
   end
 
   def test_migration_file_name_is_snake_case
diff --git a/test/generators/model_generator_test.rb b/test/generators/model_generator_test.rb
index f030de6f79a..61100de9045 100644
--- a/test/generators/model_generator_test.rb
+++ b/test/generators/model_generator_test.rb
@@ -29,11 +29,11 @@ class ModelGeneratorTest < Rails::Generators::TestCase
 
   setup do
     @plugin_directory = Redmine::Plugin.directory
-    Redmine::Plugin.directory = TMP_DIR
+    Redmine::Plugin.loader.directory = TMP_DIR
   end
 
   teardown do
-    Redmine::Plugin.directory = @plugin_directory
+    Redmine::Plugin.loader.directory = @plugin_directory
   end
 
   def test_generates_files_from_templates
diff --git a/test/integration/routing/plugins_test.rb b/test/integration/routing/plugins_test.rb
index dceeec7209e..046457e21b3 100644
--- a/test/integration/routing/plugins_test.rb
+++ b/test/integration/routing/plugins_test.rb
@@ -21,20 +21,21 @@ require File.expand_path('../../test_helper', __dir__)
 
 class RoutingPluginsTest < Redmine::RoutingTest
   def setup
-    @original_plugin_dir = Redmine::PluginLoader.directory
+    @original_loader = Redmine::Plugin.loader
 
     Redmine::Plugin.clear
-    Redmine::PluginLoader.directory = Rails.root.join('test/fixtures/plugins')
-    Redmine::Plugin.directory = Rails.root.join('test/fixtures/plugins')
-    Redmine::PluginLoader.load
-    Redmine::PluginLoader.directories.each(&:run_initializer) # to define relative controllers
+    loader = Redmine::PluginLoader.new(directory: Rails.root.join('test/fixtures/plugins'), prefix: @original_loader.prefix)
+    loader.public_directory = Rails.public_path.join(@original_loader.prefix)
+    loader.add_autoload_paths
+    Redmine::Plugin.loader = loader
+
+    loader.directories.each(&:run_initializer) # to define relative controllers
     RedmineApp::Application.instance.routes_reloader.reload!
   end
 
   def teardown
     Redmine::Plugin.clear
-    Redmine::PluginLoader.directory = @original_plugin_dir
-    Redmine::Plugin.directory = @original_plugin_dir
+    Redmine::Plugin.loader = @original_loader
     Redmine::PluginLoader.load
     RedmineApp::Application.instance.routes_reloader.reload!
   end
diff --git a/test/unit/lib/redmine/plugin_test.rb b/test/unit/lib/redmine/plugin_test.rb
index e6945c4d8a5..edf9163dd85 100644
--- a/test/unit/lib/redmine/plugin_test.rb
+++ b/test/unit/lib/redmine/plugin_test.rb
@@ -22,19 +22,19 @@ require_relative '../../../test_helper'
 class Redmine::PluginTest < ActiveSupport::TestCase
   def setup
     @klass = Redmine::Plugin
+    @original_loader = Redmine::Plugin.loader
+
     # Change plugin directory for testing to default
     # plugins/foo => test/fixtures/plugins/foo
-    @klass.directory = Rails.root.join('test/fixtures/plugins')
+    loader = Redmine::PluginLoader.new(directory: Rails.root.join('test/fixtures/plugins'), prefix: @original_loader.prefix)
+    @klass.loader = loader
     # In case some real plugins are installed
     @klass.clear
-
-    # Change plugin loader's directory for testing
-    Redmine::PluginLoader.directory = @klass.directory
-    Redmine::PluginLoader.setup
   end
 
   def teardown
     @klass.clear
+    Redmine::Plugin.loader = @original_loader
   end
 
   def test_register
-- 
2.47.3

