Including/selecting modules based on the methods they define

At http://spoofed.org/go.tar.gz I've put a simplified version of a problem
I haven't yet found a good way to solve. I'm looking for input as to how
you'd solve the problem.

Basically I have a command line utility that takes three arguments -- an
action, a vendor and a product. Only three actions (eat, drink, rest) are
supported. Any number of vendors and products can be added by simply
creating a specially configured plugin.rb in the vendor/product directory.

The issue is that not every vendor product combination will support all
three actions. When 'go' is run and an action is specified that the given
vendor/product doesn't support, I want to:

1) Handle this gracefully
2) Show all vendors + products that support this action

go is as simple as:

#!/usr/bin/env ruby

unless (ARGV.size == 3)
    raise "Usage: $0 <action> <vendor> <product>"
end

(action, vendor, product) = ARGV

require File.join("plugins", vendor, product, "plugin.rb")

case action
when /eat/
    eat
when /rest/
    rest
when /drink/
    drink
else
    raise "Unknown action #{action}"
end

And a given vendor + product, say Foo Bar, which lives in
plugins/foo/bar/plugin.rb contains:

module Go
    module Foo
        module Bar
            def eat
                puts "Foo::Bar eat!"
            end
            def rest
                puts "Foo::Bar rest!"
            end
        end
    end
end
include Go::Foo::Bar

Another vendor + product, Blaf Blarg is similarly implemented, but supports
all three methods.

As it stands today, if you specify an action that the vendor product does
not support (for example Foo::Bar.drink), an exception is thrown. Again, I
want to handle that more gracefully (which is easy enough with
begin/rescue) and then show all the vendor products that do support drink.

I feel like the answer lies somewhere around responds_to?, however I'd need
to have access to the module name to call responds_to?, and as currently
implemented this code doesn't know the module name.

Any feedback would be appreciated!

Thanks,

-jon

But you do in fact know the module name (as long as it's consistent
with the file name):

irb(main):013:0> vendor, product = 'foo', 'bar'
irb(main):019:0> module =
Go.const_get(vendor.capitalize).const_get(product.capitalize)
=> Go::Foo::Bar

I think you'll be able to work from here :slight_smile:

-- Matma Rex

···

2012/1/4 Jon Hart <jhart@spoofed.org>:

As it stands today, if you specify an action that the vendor product does
not support (for example Foo::Bar.drink), an exception is thrown. Again, I
want to handle that more gracefully (which is easy enough with
begin/rescue) and then show all the vendor products that do support drink.

I feel like the answer lies somewhere around responds_to?, however I'd need
to have access to the module name to call responds_to?, and as currently
implemented this code doesn't know the module name.

Interesting.

This gets me close, but for Go::Foo::Bar to be visible, I first have to
require that plugin. I'm looking to avoid loading every plugin unless
absolutely necessary, because I have this fear that there is a performance
hit that I'll incur by loading every plugin and including its namespace.
In my example, I only have two plugins, but in reality there can be dozens
or hundreds.

Thanks,

-jon

···

2012/1/4 Bartosz Dziewoński <matma.rex@gmail.com>

But you do in fact know the module name (as long as it's consistent
with the file name):

irb(main):013:0> vendor, product = 'foo', 'bar'
irb(main):019:0> module =
Go.const_get(vendor.capitalize).const_get(product.capitalize)
=> Go::Foo::Bar

I think you'll be able to work from here :slight_smile:

I was able to solve this by removing the imports from each plugin.rb,
computing the module name as suggested, then including the module in go.
For the use case of listing what plugins support what or ensuring that a
given plugin supports the method, I used method_defined?.

Thanks for your help.

-jon

···

On Wed, Jan 4, 2012 at 2:16 PM, Jon Hart <jhart@spoofed.org> wrote:

Interesting.

This gets me close, but for Go::Foo::Bar to be visible, I first have to
require that plugin. I'm looking to avoid loading every plugin unless
absolutely necessary, because I have this fear that there is a performance
hit that I'll incur by loading every plugin and including its namespace.
In my example, I only have two plugins, but in reality there can be dozens
or hundreds.