Back
  1. Ruby ProTip: meta-programming with eval’s other other parameters

    January 5, 2011 by nickmulder comments

    Lets ignore the good vs evil side of eval and focus on making the times you do use it better.

    Consider the following class:

    http://gist.github.com/766616

    Although this is a contrived example, using method missing to create dynamic methods is a somewhat common patter in ruby land. This Foo class will create methods on demand if they happen to be standard US metasyntactic variables.

    Given this Foo class living in foo.rb we can do this in irb:

    http://gist.github.com/766757

    Lets quickly review this output for those of us who are not meta-programming veterans. We create an instance of our Foo class and store it in a local variable, foo. We then call the method bar, it doesn’t exist so method_missing creates it for us. (Note the puts line in the code and output.) The second call to foo.bar simply returns the string since the method exits as it was created during the first call to bar by method_missing. Same is true with the baz method call. Hopefully it’s clear why foo.asdf raised a method missing exception…

    What happens if we had made a mistake in our dynamically created method?

    http://gist.github.com/766859

    Notice the typo ‘emd’ on line 11. Here is the error and stack trace we get if we had tried to use this class with the typo:

    http://gist.github.com/766862

    The first line ‘SyntaxError: (eval):3’ isn’t very helpful at all. With this small example it is easy enough to figure out where the syntax error is coming from, but if you where trying to debug a dynamic block of code that was created in some other random file/class and passed around to be executed somewhere else it could be a little hard to track down where the bug originated.

    If you check out the ruby docs you will find most versions (if not all) of eval take a few optional parameters. These optional parameters are a file name and a line number. They serve as a reference point for the stack trace when an error happens inside of the eval. Here is our foo example with the optional parameters passed into the eval:

    http://gist.github.com/766870

    __FILE__ returns the name of the file it is being called from, and __LINE__ returns the line number it is being called from. The line parameter should be the first line of the eval’d code, thus the + 1. The syntax with the «-EOM is a little strange, it simply is a place holder for the here document that follows.

    Here is the output of the updated foo.rb file:

    http://gist.github.com/766880

    Now the stack trace in the error message points us straight to our problem.

    If you use evals, use the __FILE__, __LINE__ + 1 parameters.


  2. blog comments powered by Disqus