Ruby: Alter class static method in a code block - ruby-on-rails

Given the Thread class with it current method.
Now inside a test, I want to do this:
def test_alter_current_thread
Thread.current = a_stubbed_method
# do something that involve the work of Thread.current
Thread.current = default_thread_current
end
Basically, I want to alter the method of a class inside a test method and recover it after that.
I know it sound complex for another language, like Java & C# (in Java, only powerful mock framework can do it). But it's ruby and I hope such nasty stuff would be available

You might want to take a look at a Ruby mocking framework like Mocha, but in terms of using plain Ruby it can be done using alias_method (documentation here) e.g.
beforehand:
class Thread
class << self
alias_method :old_current, :current
end
end
then define your new method
class Thread
def self.current
# implementation here
end
end
then afterwards restore the old method:
class Thread
class << self
alias_method :current, :old_current
end
end
Update to illustrate doing this from within a test
If you want to do this from within a test you could define some helper methods as follows:
def replace_class_method(cls, meth, new_impl)
cls.class_eval("class << self; alias_method :old_#{meth}, :#{meth}; end")
cls.class_eval(new_impl)
end
def restore_class_method(cls, meth)
cls.class_eval("class << self; alias_method :#{meth}, :old_#{meth}; end")
end
replace_class_method is expecting a class constant, the name of a class method and the new method definition as a string. restore_class_method takes the class and the method name and then aliases the original method back in place.
Your test would then be along the lines of:
def test
new_impl = <<EOM
def self.current
"replaced!"
end
EOM
replace_class_method(Thread, 'current', s)
puts "Replaced method call: #{Thread.current}"
restore_class_method(Thread, 'current')
puts "Restored method call: #{Thread.current}"
end
You could also write a little wrapper method which would replace a method, yield to a block and then ensure that the original method was reinstated afterwards e.g.
def with_replaced_method(cls, meth, new_impl)
replace_class_method(cls, meth, new_impl)
begin
result = yield
ensure
restore_class_method(cls, meth)
end
return result
end
Inside your test method this could then be used as:
with_replaced_method(Thread, 'current', new_impl) do
# test code using the replaced method goes here
end
# after this point the original method definition is restored
As mentioned in the original answer, you can probably find a framework to do this for you but hopefully the above code is interesting and useful anyway.

Related

Wrap Ruby methods at run-time to intercept method calls

I have a parent class that is similar to ActiveRecord, and I'm trying to open the class on load in our test suite. I want to wrap the initialize method so that it maintains a list of all of the subclasses that are initialized so I can make sure that all of the data for those classes is cleaned up between tests. Running a full wipe between tests winds up being too inefficient (plus I'm just interested in what the code would like to do this is).
My goal is to insert a new initialize method in the inheritance tree and just call super. All the while maintaining some list of all of the instantiated classes inside the parent class.
My attempt so far:
class NewActiveRecord
##__instantiated_classes = Set.new
def initialize(*args)
##__instantiated_classes.add(self.class)
super *args
end
def self.reset_tracking
##__instantiated_classes = Set.new
end
def self.get_instantiated_classes
##__instantiated_classes.to_a
end
end
class ActiveSupport::TestCase
teardown do
NewActiveRecord.get_instantiated_classes.each {|c| c.destroy_all}
NewActiveRecord.reset_tracking
end
end
Basically I want to wrap all methods that are called on subclasses of some parent to send their class to some predefined object their class
Did some exploring today and came up with the following solution. I'm still not sure how to unspy something yet though:
module Spy
def self.on_instance_method(mod, method, &block)
mod.class_eval do
# Stash the old method
old_method = instance_method(method)
# Create a new proc that will call both our block and the old method
proc = Proc.new do
block.call if block
old_method.bind(self).call
end
# Bind that proc to the original module
define_method(method, proc)
end
end
def self.on_class_method(mod, method, &block)
mod.class_eval do
# Stash the old method
old_method = singleton_method(method)
# Create a new proc that will call both our block and the old method
proc = Proc.new do
block.call if block
old_method.call
end
# Bind that proc to the original module
define_singleton_method(method, proc)
end
end
end
Usage from my tests looks like:
count = 0
Spy.on_instance_method(FakeClass, :value) { count += 1 }
fake = FakeClass.new(6)
fake.value.must_equal 6
count.must_equal 1

several methods to same instance - DRY

Sorry if this is too simple. I'm looking for a way to make my ruby code dry : I want to call a number of methods on the same instance variable #var = Model.new(param) :
#var.method1
#var.method2
#var.method3
...
Is it possible to use the send method to write one line of code ? Btw, is it possible to call a block on Model.new to produce some more concise code ?
I believe that DRY should be used to make your code more maintainable, and more readable. I don't think it should be used to shorten the number of characters you type, or show-off your code acrobatics.
Both #Arup's and #p11y's solutions are great, within a context, but as a general rule (before knowing anything about your class or methods), I believe that writing
#var.method1
#var.method2
#var.method3
is more readable and maintainable than writing either
%i[method1 method2 method3].each(&#var.method(:send))
(you need to be fluent in advanced ruby to understand this)
or
#var.method1
.method2
.method3
(again the vanishing act is more confusing to the future reader than helpful)
Always think about who will read your code in 6 months, and what will be the clearest way for him to understand what's happening.
If you build method1, method2, etc. such that they return the instance itself using self, you can build a chainable interface. For example:
class Foo
def method1
# do something
self
end
def method2
# do something
self
end
def method3
# do something
self
end
# more methods...
end
#var = Foo.new
#var.method1.method2.method3
# or if this gets too long
#var.method1
.method2
.method3
Do as below :
%i[method1 method2 method3].each { |m| #var.send(m) }
If you want to make it more short,use :
%i[method1 method2 method3].each(&#var.method(:send))
When I wrote my original answer, I missed the last sentence in your question:
Btw, is it possible to call a block on Model.new to produce some more concise code ?
And the answer to this question is YES. This pattern is a builder pattern, which is implemented in several gems in ruby (such as tire).
The pattern states that the initialize method receives a block, which is run in the context of the created object, using instance_eval. If the block receives a parameter, the instance object is passed to it instead of changing the block's scope:
class Model
def initialize(name, &block)
#name = name
block.arity < 1 ? instance_eval(&block) : block.call(self) if block_given?
end
def method1
# something
end
def method2
# something
end
def method3
# something
end
end
And its usage will be something either like this:
#var = Model.new('model') do
method1
method2
method3
end
or, alternatively:
#var = Model.new('model') do |m|
m.method1
m.method2
m.method3
end

Dynamically defining instance method within an instance method

I have a several classes, each of which define various statistics.
class MonthlyStat
attr_accessor :cost, :size_in_meters
end
class DailyStat
attr_accessor :cost, :weight
end
I want to create a decorator/presenter for a collection of these objects, that lets me easily access aggregate information about each collection, for example:
class YearDecorator
attr_accessor :objs
def self.[]= *objs
new objs
end
def initialize objs
self.objs = objs
define_helpers
end
def define_helpers
if o=objs.first # assume all objects are the same
o.instance_methods.each do |method_name|
# sums :cost, :size_in_meters, :weight etc
define_method "yearly_#{method_name}_sum" do
objs.inject(0){|o,sum| sum += o.send(method_name)}
end
end
end
end
end
YearDecorator[mstat1, mstat2].yearly_cost_sum
Unfortunately define method isn't available from within an instance method.
Replacing this with:
class << self
define_method "yearly_#{method_name}_sum" do
objs.inject(0){|o,sum| sum += o.send(method_name)}
end
end
...also fails because the variables method_name and objs which are defined in the instance are no longer available. Is there an idomatic was to accomplish this in ruby?
(EDITED: I get what you're trying to do now.)
Well, I tried the same approaches that you probably did, but ended up having to use eval
class Foo
METHOD_NAMES = [:foo]
def def_foo
METHOD_NAMES.each { |method_name|
eval <<-EOF
def self.#{method_name}
\"#{method_name}\".capitalize
end
EOF
}
end
end
foo=Foo.new
foo.def_foo
p foo.foo # => "Foo"
f2 = Foo.new
p f2.foo # => "undefined method 'foo'..."
I myself will admit it's not the most elegant solution (may not even be the most idiomatic) but I've run into similar situations in the past where the most blunt approach that worked was eval.
I'm curious what you're getting for o.instance_methods. This is a class-level method and isn't generally available on instances of objects, which from what I can tell, is what you're dealing with here.
Anyway, you probably are looking for method_missing, which will define the method dynamically the first time you call it, and will let you send :define_method to the object's class. You don't need to redefine the same instance methods every time you instantiate a new object, so method_missing will allow you to alter the class at runtime only if the called method hasn't already been defined.
Since you're expecting the name of a method from your other classes surrounded by some pattern (i.e., yearly_base_sum would correspond to a base method), I'd recommend writing a method that returns a matching pattern if it finds one. Note: this would NOT involve making a list of methods on the other class - you should still rely on the built-in NoMethodError for cases when one of your objects doesn't know how to respond to message you send it. This keeps your API a bit more flexible, and would be useful in cases where your stats classes might also be modified at runtime.
def method_missing(name, *args, &block)
method_name = matching_method_name(name)
if method_name
self.class.send :define_method, name do |*args|
objs.inject(0) {|obj, sum| sum + obj.send(method_name)}
end
send name, *args, &block
else
super(name, *args, &block)
end
end
def matching_method_name(name)
# ... this part's up to you
end

Call module subclass without specify the name

I would like to access my subclass using only the name of my module.
module MyModule
class UselessName
include OtherModel
# only self method
def self.x
end
end
# No other class
end
And I would like to write MyModule.x and not MyModule::UselessName.x
I could transform my module in class, but I use RoR Helpers, and I would prefer that MyModule remains a module and not a class.
Is there a way to do this ?
Thanks ;)
OK, let's split problem into two - getting list of such methods and making proxies in the module.
Getting list might be a little tricky:
MyModule::UselessName.public_methods(false) - MyModule::UselessName.superclass.public_methods(false)
Here we start with list of all public class methods and subtract list of all superclass's public class methods from it.
Now, assuming we know method's name, we need to make proxy method.
metaclass = class << MyModule; self; end
metaclass.send(:define_method, :x) do |*args, &block|
MyModule::UselessName.send(:x, *args, &block)
end
This code will just make equivalent of following definition at runtime.
module MyModule
def x(*args, &block)
MyModule::UselessName.send(:x, *args, &block)
end
end
So let's put it together in simple function.
def make_proxies(mod, cls)
methods = cls.public_methods(false) - cls.superclass.public_methods(false)
metaclass = class << mod; self; end
methods.each do |method|
metaclass.send(:define_method, method) do |*args, &block|
cls.send(method, *args, &block)
end
end
end
So now you'll just need to call it for needed modules and classes. Note that "destination" module can be different from "source" module owning the class, so you can slurp all methods (given they have different names or you'll prefix them using class name) to one module. E.g. for your case just make following call.
make_proxies(MyModule, MyModule::UselessName)
Ok, I've found a VERY DIRTY way to accomplish what I THINK you mean:
module MyModule
class UselessName
include OtherModule
# have whatever you want here
end
def self.x
# whatever
end
end
So somewhere in your code you can do, and I repeat THIS IS VERY, VERY DIRTY!
MyModule.methods(false).each do |m|
# m = method
# now you can redefine it in your class
# as an instance method. Not sure if this
# is what you want though
MyModule::UselessName.send(:define_method, :m) do
# in this NEW (it's not the same) method you can
# call the method from the module to get the same
# behaviour
MyModule.send(m)
end
end
I don't know if this overwrites an instance method with the same name if it's in the class before or if it throws an exception, you have to try that.
In my opinion you should overthink your app design, because this is not the way it should be, but here you go...

why use include module when class_eval by itself would suffice

In the following code include module is used. The way I see it if include module is removed then also an instance method would be created. Then why user include module ?
http://github.com/rails/rails/blob/master/activerecord/lib/active_record/associations.rb#L1416
include Module.new {
class_eval <<-RUBY, __FILE__, __LINE__ + 1
def destroy # def destroy
super # super
#{reflection.name}.clear # posts.clear
end # end
RUBY
}
First of all let's make one thing clear. When they call super inside the class_eval — it has absolutely nothing to do with why they used include Module.new {} thing. In fact the super which was called inside the destroy method is completely irrelevant to answering your question. There could be any arbitrary code inside that destroy method.
Now that we got it out of the way, here's what's going on.
In ruby, if you simply define a class method, and then define it again in the same class, you will not be able to call super to access the previous method.
For example:
class Foo
def foo
'foo'
end
def foo
super + 'bar'
end
end
Foo.new.foo # => NoMethodError: super: no superclass method `foo' for #<Foo:0x101358098>
This makes sense, because the first foo was not defined in some superclass, or anywhere up the lookup chain (which is where super points). However, you can define the first foo in such a way that when you later overwrite it — it will be available by calling super. This is exactly what they wanted to achieve with doing module include.
class Foo
include Module.new { class_eval "def foo; 'foo' end" }
def foo
super + 'bar'
end
end
Foo.new.foo # => "foobar"
This works, because when you include a module, ruby inserts it into the lookup chain. This way you can subsequently call super in the second method, and expect the included method to be called. Great.
However, you may wonder, why not simply include a module without all the tricks? Why are they using block syntax? We know that my above example is exactly equivalent to the following:
module A
def foo
'foo'
end
end
class Foo
include A
def foo
super + 'bar'
end
end
Foo.new.foo # => "foobar"
So why didn't they do that? The answer is — the call to reflection. They needed to capture the variable (or method) which was available in the current context, which is reflection.
Since they are defining the new module using block syntax, all the variables outside of the block are available for usage inside the block. Convenient.
Just to illustrate.
class Foo
def self.add_foo_to_lookup_chain_which_returns(something)
# notice how I can use variable something in the class_eval string
include Module.new { class_eval "def foo; '#{something}' end" }
end
end
# so somewhere else I can do
class Foo
add_foo_to_lookup_chain_which_returns("hello")
def foo
super + " world"
end
end
Foo.new.foo # => "hello world"
Neat, huh?
Now let me stress it again. The call to super inside of the destroy method in your example has nothing to do with any of the above. They called it for their own reasons, because maybe the class where this is happening is subclassing another class which already defined destroy.
I hope this made it clear.
I'm guessing but... they don't want to overwrite the "destroy" method, and want to leave it available to be overloaded by some end-user (you or me), without it removing this "reflection.clear" functionality.
So - by including it as a module, they can call "super" which will call the original destroy or the overloaded version (written by the end-user).
Thanks to include, destroy method is not overwritten. It lands in ghost class that actual class derives from. That way, when one will call destroy on AR object, original one will be called, and super will call one from anonymous module (which will later call original destroy from class that it derived from).
A bit tricky, indeed.

Resources