Metaprogramming with String Evaluations
Problem
You're trying to write some metaprogramming code using define_method, but there's too much reflection going on for your code to be readable. It gets confusing and is almost as frustrating as having to write out the code in longhand.
Solution
You can define new methods by generating the definitions as strings and running them as Ruby code with one of the eval methods.
Here's a reprint of the metaprogramming example from the previous recipe, which uses define_method:
class Numeric [['add', '+'], ['subtract', '-'], ['multiply', '*',], ['divide', '/']].each do |method, operator| define_method("#{method}_2") do method(operator).call(2) end end end
The important line of code, method(operator).call(2), isn't something you'd write in normal programming. You'd write something like self + 2 or self / 2, depending on which operator you wanted to apply. By writing your method definitions as strings, you can do metaprogramming that looks more like regular programming:
class Numeric [['add', '+'], ['subtract', '-'], ['multiply', '*',], ['divide', '/']].each do |method, operator| module_eval %{ def #{method}_2 self.#{operator}(2) end } end end 4.add_2 # => 6 10.divide_2 # => 5
Discussion
You can do all of your metaprogramming with define_method, but the code doesn't look a lot like the code you'd write in normal programming. You can't set an instance variable with @foo=4; you have to call instance_variable_set('foo', 4).
The alternative is to generate a method definition as a string and execute the string as Ruby code. Most interpreted languages have a way of parsing and executing arbitrary strings as code, but it's usually regarded as a toy or a hazard, and not given much attention. Ruby breaks this taboo.
The most common evalutation method used for metaprogramming is Module#module_eval. This method executes a string as Ruby code, within the context of a class or module. Any methods or class variables you define within the string will be attached to the class or module, just as if you'd typed the string within the class or module definition. Thanks to the variable substitutions, the generated string looks exactly like the code you'd type in manually.
The following four pieces of code all define a new method String#last:
class String def last(n) self[-n, n] end end "Here's a string.".last(7) # => "string." class String define_method('last') do |n| self[-n, n] end end "Here's a string.".last(7) # => "string." class String module_eval %{def last(n) self[-n, n] end} end "Here's a string.".last(7) # => "string." String.module_eval %{def last(n) self[-n, n] end} "Here's a string.".last(7) # => "string."
The instance_eval method is less popular than module_eval. It works just like module_eval, but it runs inside an instance of a class rather than the class itself. You can use it to define singleton methods on a particular object, or to set instance variables. Of course, you can also call define_method on a specific object.
The other evaluation method is just plain eval. This method executes a string exactly as though you had written it as Ruby code in the same spot:
class String eval %{def last(n) self[-n, n] end} end "Here's a string.".last(7) # => "string."
You must be very careful when you use the eval methods, lest the end-user of a program trick you into running arbitrary Ruby code. When you're metaprogramming, though, it's not usually a problem: the only strings that get evaluated are ones you constructed yourself from hardcoded data, and by the time your class is loaded and ready to use, the eval calls have already run. You should be safe unless your eval statement contains strings obtained from untrusted sources. This might happen if you're creating a custom class, or modifying a class in response to user input.