views:

594

answers:

1

Is it possible to define an instance method in ruby from a string (the method name) and a block (the method's contents)?

I imagine this will need to use instance_eval(), but I haven't figured out how to mix the two data types yet. Both the string and the block are dynamically determined, so it would work to create the block with the "def #{string}" at the beginning - I don't know how to do this.

My use case is a class that represents a Bacula configuration file. The configuration file can have many different types of resources. They're all stored in a relatively complex data structure behind the scenes (for other reasons, simplifying this structure will not accomplish what I'm looking for). I'd like the resources to be rapidly accessible via named methods.

For example, A represents one config file, B represents another. A has resources Director, Client, Job and B has Messages and Director.

In this case, A should have methods director(), client(), and job() while B has messages() and director(). Each of these would return the relevant resource from the object's respective config file.

I know there are easier ways of doing this (like implementing a [] method), but at this point I'm pursuing the harder solution for curiosity's sake.

+4  A: 

I think what you're looking for is the method define_method on Module; however, it's private so you have to use class_eval or something else to run it.

body = proc { self * 3 }
name = "triple"
c = Numeric

c.class_eval { define_method(name.to_sym, &body) }

3.triple # 9

And a method with arguments:

body = proc { |second| [self * 3, second * 3] }
name = "triple_both"
c = Numeric

c.class_eval { define_method(name.to_sym, &body) }

puts 3.triple_both(5) # [9, 15]

To put a new method on a singleton object (or Eigenclass, or whatever they're called):

body = proc { puts @meme + @meme + @meme }
name = "meme"
class SwedishChef; def initialize; @meme = "bork"; end; end    
sc = SwedishChef.new
(class << sc; self; end).class_eval { 
  define_method(name.to_sym, &body)
}
sc.meme # borkborkbork

[EDIT (Jörg W Mittag): I fixed the singleton method example.]

Jesse Millikan
This will work for class methods, but I specifically need instance methods.
sh-beta
What do you mean by instance methods? You mean you want them on just one object?
Jesse Millikan
Exactly. Use case added to my original post.
sh-beta
After Jorg W Mittag's edit, the singleton class does the trick. Thanks!
sh-beta
Thanks. I honestly had no idea how to get the variables into that scope...
Jesse Millikan
Me neither :-) .
Jörg W Mittag
+1 for Bork Bork Bork!
James A. Rosen