I have a class which I want other developers to be able to create modules to extend my class and give it more instance variables.
How can I automatically include modules in my class based on files in a particular folder? i.e. load all files and include them as modules for my class
How can I do this and somehow avoid method naming collisions?
I was thinking I could pass the name of the plugin in the initialize method, and do this:
class MyClass
def initialize(plugin_name=nil)
end
def method_missing(method_name)
"#{plugin_name}".send(method_name)
end
end
Would something like this work in theory, please help out in the method_missing as I have never written one before. I’m trying to basically specify a particular plugin name to avoid any name collisions.
Or should I, when including the plugins, output an error if there is a method collision?
Allowing external code to be melted in your class sounds dangerous and drives into names collisions as you guessed. It cans also drive into unexpected behavior, since you allow external code to mess with your class internals.
The best way to handle a plugin would be to encaspulate the behavior that would be replaced.
Let’s take an example :
Given you’re building a Logger, we can imagine that you can create plugins to specify where the logs will be wrote.
You can then create the following class :
Then you can you instanciate the right plugin this way :
This way of doing things provides modularity, more flexibility and is more solid than overloading things in your class with external code.
Loading modules as they are found in a directory is a matter of toying with Dir.glob and require. To explore what had been found under your plugin directory, you can force your users to write plugins in a module, like Foo::Plugins::FileStore and inspect which constants are present in the Foo::Plugins module after requiring the files. See http://www.ruby-doc.org/core-1.9.3/Module.html#method-i-constants for more informations.
Edit: As you don’t have any interface to map to since you provides the methods to an ERB template, you could do the following :
Plugins are in the following form :
You can then load them with the following :
And include them, given TemplateMethods is a module included in the context of your ERB template.
Using send isn’t very elegant, I’d suggest building a class method on TemplateMethods that encapsulate the whole thing, like TemplateMethods.load_plugins_method! The bang here would warn about the intrusive action done by the dynamic modules inclusion.
That should do the job 🙂