Kowshik
Kowshik

Reputation: 1641

Issue with monkeypatching

I'm using Ruby 1.9.2-p180. The following piece of code throws the error:

"test.rb:20:in `': undefined local variable or method `val' for main:Object (NameError)"

Is there a way that I could make the monkeypatching work to return val?

class Test
  def m
    "m"
  end
end

def inject(testObj, val)
  def testObj.m
    val
  end

  testObj
end


t = Test.new
puts t.m
puts inject(t, val).m

Upvotes: 0

Views: 87

Answers (1)

Jörg W Mittag
Jörg W Mittag

Reputation: 369430

The immediate problem is that in line 20 you are calling

puts inject(t, val).m

I.e. you are passing val as an argument to inject, but you never actually defined val. You need to pass an actual value, for example:

puts inject(t, 'Hello, World!').m

Or define val first:

val = 42
puts inject(t, val).m

However, if you do this, you will get a new error:

NameError: undefined local variable or method `val' for #<Test:0x007fa1228439b8>

The reason for this is that in Ruby, only blocks can be closures. Methods do not close over their surrounding lexical environment.

Which means that here:

def inject(testObj, val)
  def testObj.m
    val
  end

  testObj
end

you have

  • an unused method argument val in inject
  • a completely unrelated method call to a method named val in testObj.m

If you want to have access to the surrounding scope, you need to use a block. Thankfully, there is a (family of) methods that take blocks and define methods:

def inject(testObj, val)
  testObj.define_singleton_method(:m) do
    val
  end

  testObj
end

Now, everything works as expected:

puts inject(t, 'Hello, World!').m
# Hello, World!

Upvotes: 3

Related Questions