Reputation: 1975
I want to create a base lass and then load all sub classes (plugins) and process all of them via each
loop. This is working example:
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
This code sample works perfectly. Output is:
"Command received test"
"Command received test"
=> #<Set: {DogPlugin, CatPlugin}>
However, In my rails application, my custom implementations are in modules. Lets say I have an A
module. In this case it doesn't work.
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
is empty and nothing get executed. Why?
See live sample here: https://repl.it/repls/EllipticalDamagedCategory
There are other types of plugin samples on the net but they have to initialized one-by-one. This sample code loads all plugins and execute same method in all of them. I need this functionality with modules.
Upvotes: 1
Views: 74
Reputation: 21015
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
Upvotes: 1