Ruby中的内部插件系统和模块(用作框架的Rails)

时间:2019-04-03 17:42:18

标签: ruby-on-rails ruby

我想创建一个基础类,然后加载所有子类(插件)并通过each循环处理它们。这是工作示例:

require 'set'

class Plugin
  # Keep the plugin list inside a set so we don't double-load plugins
  @plugins = Set.new

  def self.plugins
    @plugins
  end

  def self.register_plugins
    # Iterate over each symbol in the object space
    Object.constants.each do |klass|
      # Get the constant from the Kernel using the symbol
      const = Kernel.const_get(klass)
      # Check if the plugin has a super class and if the type is Plugin
      if const.respond_to?(:superclass) and const.superclass == Plugin
        @plugins << const
      end
    end
  end
end

class DogPlugin < Plugin

  def self.handle_command(cmd)
    p "Command received #{cmd}"
  end

end
class CatPlugin < Plugin

  def self.handle_command(cmd)
    p "Command received #{cmd}"
  end

end

Plugin.register_plugins

# Test that we can send a message to each plugin
Plugin.plugins.each do |plugin|
  plugin.handle_command('test')
end

此代码示例完美运行。输出为:

"Command received test"
"Command received test"
=> #<Set: {DogPlugin, CatPlugin}>

但是,在我的rails应用程序中,我的自定义实现位于模块中。可以说我有一个A模块。在这种情况下,它不起作用。

require 'set'

module A
  class Plugin
    @plugins = Set.new

    class << self
      attr_reader :plugins
    end

    def self.register_plugins
      # Iterate over each symbol in the object space
      Object.constants.each do |klass|
        # Get the constant from the Kernel using the symbol
        const = Kernel.const_get(klass)
        # Check if the plugin has a super class and if the type is Plugin
        if const.respond_to?(:superclass) && (const.superclass == A::Plugin)
          @plugins << const
        end
      end
    end
  end
end

module A
  class MyAction < Plugin
    def self.handle_command(cmd)
      puts "Command received #{cmd}"
    end
   end
end

A::Plugin.register_plugins
A::Plugin.plugins.each do |plugin|
  plugin.handle_command('test')
end

Set为空,没有任何执行。为什么?

在此处查看实时示例:https://repl.it/repls/EllipticalDamagedCategory

网络上还有其他类型的插件示例,但它们必须一个接一个地初始化。此示例代码将加载所有插件并在所有插件中执行相同的方法。我需要模块具有此功能。

1 个答案:

答案 0 :(得分:1)

require 'set'

module A
  class Plugin
    @plugins = Set.new

    def self.plugins
      @plugins
    end

    def self.register_plugins
      # Iterate over each symbol in the object space
      ::A.constants.each do |klass|
        # Get the constant from the Kernel using the symbol
        const = A.const_get(klass)
        puts const
        # Check if the plugin has a super class and if the type is Plugin

        if const.respond_to?(:superclass) && (const.superclass == Plugin)
          @plugins << const
        end
      end
    end
  end
end

module A
  class MyAction < Plugin
    def self.handle_command(cmd)
      puts "Command received #{cmd}"
    end
   end
end

A::Plugin.register_plugins
A::Plugin.plugins.each do |plugin|
  plugin.handle_command('test')
end