Sam
Sam

Reputation: 47

Dynamic methods in a Module?

I've got a module called AB. Right now it looks something like this:

module AB
    extend self

    def some_method(hash)
        ....
    end

    ....
end

We use it like this: AB.some_method(:thing=>:whatever,:etc=>'you get the idea'). There are about a half-dozen strings that the user has to pass in that I'd like to turn into dynamic methods so that instead of AB.some_method(:thing => :whatever...) They'd just call AB.whatever(...) or AB::whatever(...). I thought I could do this with method_missing, but I guess I don't get it. I did something like this:

module AB
    def method_missing(name,*args)
        super unless THINGS.include?(name.to_s)
        ...
    end
end

But I never get into that method when trying to call AB::whatever. I thought about looping over THINGS and using define_method, but I wasn't sure how to define methods that take arguments.

Any help appreciated.

Upvotes: 1

Views: 1449

Answers (2)

Arto Bendiken
Arto Bendiken

Reputation: 2621

The problem in your second code example is that the method_missing ought to be declared as self.method_missing. The following works as expected:

module AB
  THINGS = %w(whatever)

  def self.method_missing(name, *args)
    super unless THINGS.include?(name.to_s)
    "responding to #{name}"
  end
end

p AB.whatever    #=> "responding to whatever"
p AB.something   #=> NoMethodError

Upvotes: 4

Mladen Jablanović
Mladen Jablanović

Reputation: 44080

I think you have two different approaches here you can use, one is defining methods dynamically, another is relying on method_missing. Here are examples of both:

# first approach, define methods
class MyClassDefine
  def some_method args
    p args
  end

  [:foo,:bar].each do |name|
    define_method name do |args|
      # assume args is a hash, fix me
      some_method args.merge({:thing => name})
    end
  end
end

# second approach, use method_missing
class MyClassMissing
    def some_method args
    p args
  end

  def method_missing name, args
    super unless [:foo, :bar].include? name
    # again, assuming argument is a hash
    some_method args.merge(:thing => name)
  end
end

mcd = MyClassDefine.new
mcd.foo :etc => 'you get idea'
#=> {:etc=>"you get idea", :thing=>:foo}

mcm = MyClassMissing.new
mcm.foo :etc => 'you get idea'
#=> {:etc=>"you get idea", :thing=>:foo}

What's left to do here is cover the cases when the methods are passed something else than a hash, as an argument.

Upvotes: 0

Related Questions