This post originated from an RSS feed registered with Ruby Buzz
by Jay Fields.
Original Post: Ruby: LocalJumpError workaround
Feed Title: Jay Fields Thoughts
Feed URL: http://feeds.feedburner.com/jayfields/mjKQ
Feed Description: Blog about Ruby, Agile, Testing and other topics related to software development.
I was recently working on some code very similar to the following distilled example.
class Foo
attr_accessor :some_val
def self.method_with_additional_behavior(method_name, &block) define_method method_name do # do some stuff before calling block instance_eval &block # do some stuff after calling block end end
method_with_additional_behavior :foo do return 0 if self.some_val.nil? some_val * 2 end end
Foo.new.foo
Executing the above code gives the following error.
LocalJumpError: unexpected return
The error occurs because the return statement attempts to return from the method_with_additional_behavior. There are a few ways to solve this, but I also had some constraints that influenced my decision.
In my codebase the method_with_additional_behavior is defined in a superclass, and the subclasses use this method to create methods with additional behavior. I (believe I) could have forced them to use lambda and gotten around the problem, but that wouldn't be very friendly. I could also define another method, such as "__#{method_name}__", and call the underscored method from the desired method, but that would have left my classes with an additional method that should never be called in isolation.
I solved the problem by defining a method, storing that method in a local variable and then overriding the newly defined method with my additional behavior and a call to the method stored in the local variable.
class Foo
attr_accessor :some_val
def self.method_with_additional_behavior(method_name, &block) define_method method_name, &block new_method = instance_method(method_name) define_method method_name do # do some stuff before calling block new_method.bind(self).call # do some stuff after calling block end end
method_with_additional_behavior :foo do return 0 if self.some_val.nil? some_val * 2 end end