Say, I have:
class Test
def initialize(m)
@m = m
end
def test
@m
end
end
How can I temporarily make method #test
of all instances (both existing and new ones) of Test
return 113
, and then restore the original method later?
It sounds like such a simple thing, yet I can't find a nice way to achieve it. Probably because of my poor knowledge of Ruby.
What I have found so far is:
# saving the original method
Test.send(:alias_method, :old_test, :test)
# redefining the method
Test.send(:define_method, :test) { 113 }
# restore the original method
Test.send(:alias_method, :test, :old_test)
Which does the job but, as I understand, it would also redefine the existing #old_test
if one existed?.. And it just feels like a hack rather than proper use of metaprogramming?..
- Is it possible to do something like this without using aliases (but also without modifying the source code of
Test
)? - If not (or if there are easier/nicer ways) how would you do this if you could modify the source code of
Test
?
I would appreciate if you could describe multiple ways of achieving the same thing, even those that are hard or impractical. Just to give an idea about the flexibility and limitations of metaprogramming in Ruby :)
Thank you very much
P.S. The reason I started all of this:
I am using gem rack-throttle
to throttle requests starting with /api
, but other urls shouldn't be affected., and I want to test all of this to make sure it works. In order to test the throttling I had to add the middleware to the test environment too. I've successfully tested it (using minitest), however all other tests that test ApiController
shouldn't be throttled because it makes tests take much longer if we need to wait 1 second after each request.
I decided to monkey patch the RequestSpecificIntervalThrottle#allowed?
with { true }
in minitest's #setup
s to temporarily disable throttling for all of those tests, and then reenable it again in #teardown
s (as otherwise the tests testing the throttling itself will fail). I would appreciate if you tell me how you would approach this.
However now that I've already started digging into metaprogramming, I am also just curious how to achieve this (temporarily redefining a method) even if I am not actually going to use it.
You can use
instance_method
to get aUnboundMethod
object from any instance method:Unbound methods are a reference to the method at the time it was objectified: subsequent changes to the underlying class will not affect the unbound method.
The equivilent for class methods is:
If you want to make the worlds smallest (and perhaps the most useless) stubbing library you could do it with: