Question

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.

Was it helpful?

Solution

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.]

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top