Understanding Method_Added for Class Methods

Understanding method_added for class methods

you are looking for singleton_method_added:

module Magic
def self.included(base)
base.extend ClassMethods
end
module ClassMethods
def method_added(name)
puts "instance method '#{name}' added"
end

def singleton_method_added(name)
puts "class method '#{name}' added"
end
end
end

class Foo
include Magic

def bla
end

def blubb
end

def self.foobar
end
end

Output:

instance method 'bla' added
instance method 'blubb' added
class method 'foobar' added

Enjoy!

Understanding ruby metaprogramming using method_added to overwrite instance methods dynamically

Trace::works as follows: set self to the context that exists within the class definition using instance_eval. Therefore the scope(?) is
modified to how it would be within that class definition.

Using instance eval you evaluate the block with self bound to the object, which in this case will be the class that is including the module. (I.e. the culprit). Just for clarity, there is a difference between:

o = Object.new
o.instance_eval do
puts self
end

and

class Foo < Object end
Foo.instance_eval do puts self end

Answer: So yes you are correct in this assumption!


Then we set method_object to instance_method(meth) which will get the original method that will added. Since instance_method does not
have an explicit receiver, it will default to self which will be the
same as the context of being within the class definition?

Yes, you are correct in your assumption. Do note that, with asking:

culprit.instance_methods(false) => [:someselector, :someotherselector]

And calling instance method in this context is indeed the same as calling self.instance_method.


This method is unbound, so we must bind it to the current context
using bind(self) so it has the same context as it would originally?

Yes. When you get a method in the way defined in the trace module, you get an unbound method object which can be bound again as described.


If you want to dive into Ruby's metaprogramming, I do recommend the following book: http://pragprog.com/book/ppmetr/metaprogramming-ruby it explains all the gritty details behind Ruby's object system, mixins, blocks and anything you can imagine.

How do you access `this` in a method added to a class via a decorator in Typescript?

If you look at the official documentation, this is how a classDecorator should look like:

function classDecorator<T extends {new(...args:any[]):{}}>(constructor:T) {
return class extends constructor {
newProperty = "new property";
hello = "override";
}
}

In your case:

function decorator<T extends {new(...args:any[]):{}}>(constructor:T) {
return class extends constructor {
doSomething = function() {
console.log("Value: " + this.myValue);
}
}
}

@decorator
class Something {
public myValue = 42;

public doSomething(){}
}

const obj = new Something();
obj.doSomething();

The class definition will be replaced by whatever is returned from the decorator and this way the "this" keyword will have the correct value.

The empty doSomething function in the original class is just a placeholder, because typescript has some problems understanding that a class has been changed after a decorator and will give you an error at the last line. Look at this issue or this stackoverflow question for more information and other possibilities.

Method Added in Python

In Python, methods are just attributes that happen to be callable*. You'd have to hook in to attributes being set to see new methods being added to a class.

You'd have to use a metaclass to intercept new attributes being added to a class:

import types

class NewMethodAlerter(type):
def __setattr__(self, name, obj):
if isinstance(obj, types.FunctionType):
print(f'New method {name} added!')
super().__setattr__(name, obj)

class Demo(metaclass=NewMethodAlerter):
def existing_method(self):
pass

def new_method(self): pass
Demo.new_method = new_method

which then looks like this:

>>> class Demo(metaclass=NewMethodAlerter):
... def existing_method(self):
... pass
...
>>> def new_method(self): pass
>>> Demo.new_method = new_method
New method new_method added!

If you wanted to know about the initial set of attributes, the result of executing the class body, then you have two options: use a metaclass, or in Python 3.6 and up, the __init_subclass__ method. Either one is called to create new classes, and can be used to inspect the attributes:

class InitialMethodAlerter(type):
def __new__(typ, name, bases, attrs):
for name, obj in attrs.items():
if isinstance(obj, types.FunctionType):
print(f'Method {name} defined!')
return super().__new__(typ, name, bases, attrs)

class Demo(metaclass=InitialMethodAlerter):
def existing_method(self):
pass

or the __init_subclass__ method:

class InitialMethodAlerter:
@classmethod
def __init_subclass__(cls, **kwargs):
for name, obj in vars(cls).items():
if isinstance(obj, types.FunctionType):
print(f'Method {name} defined!')

class Demo(InitialMethodAlerter):
def existing_method(self):
pass

You may want to read up on metaclasses at What is a metaclass in Python?


*Well, the attributes are functions actually. Functions are descriptor objects, which causes them to be bound when accessed via an instance. That binding process produces a method object, that when called takes the original function and passes in the instance to which it was bound.

Use define_method with method_added

The error is caused by an infinite recursion. Once you've defined the hook, whenever you call define_method, it also triggers the method_added that in turns recalls method_added and so on.

Regardless any clean solution you may end up to, the approach itself is wrong. You said

I want to count each method call.

But using method_added will not cover several cases. In fact, method_added will not get triggered for all the methods that existed before the callback was created. Moreover, it will not work for all the methods you inherit from the parent class, including methods inherited from Object such as to_s.

If you want to count the method calls, you have other approaches.

The simplest one, without knowing too much of the Ruby internals, is to use a proxy object that delegates every method call to your object, but keeping track of the method calls. This is known as the Proxy Design Pattern.

class MethodCounter < BasicObject
def initialize(instance)
@counter = 0
@instance = instance
end

def method_missing(*args)
@counter += 1
@instance.send(*args, &block)
end
end

Then instead of using your instance directly

t = Test.new
t.method

wrap it inside the proxy

t = MethodCounter.new(Test.new)
t.method

Another approach is to use Ruby tracking methods such as set_trace_funct to add a callback whenever a method is called.

Here's a practical example: Logging all method calls in a Rails app

iOS: Class method added by class_addMethod can't be used by NSInvocation

You need to pass the signature string of the method as the 4th argument of class_addMethod. You are passing NULL.

Java 9: How to find every new method added

You're probably looking for something like jdkapidiff which uses japicmp to generate reports similar to one hosted here by the author - jdk8-jdk9-api-diff.

You can clone the project and execute mvn clean install to get the similar report on your local.

Provide a file ~.m2/toolchains.xml like this:

<?xml version="1.0" encoding="UTF8"?>
<toolchains>
<toolchain>
<type>jdk</type>
<provides>
<version>1.8</version>
<vendor>oracle</vendor>
</provides>
<configuration>
<jdkHome>/path/to/jdk-1.8</jdkHome>
</configuration>
</toolchain>
<toolchain>
<type>jdk</type>
<provides>
<version>9</version>
<vendor>oracle</vendor>
</provides>
<configuration>
<jdkHome>/path/to/jdk-9</jdkHome>
</configuration>
</toolchain>
</toolchains>

Methods_added for Singleton methods, but not Class methods

Eureka!

AN_OBJECT = Object.new

def AN_OBJECT.methods_added
@@methods_added ||= []
end

def AN_OBJECT.singleton_methods_added
@@singleton_methods_added ||= []
end

def AN_OBJECT.singleton_method_added(method_name)
@@singleton_methods_added ||= []
@@singleton_methods_added << method_name
end

If you look at the previous excercise if you scroll up, it should give you enough hint on how to handle it. It's a tragedy that I posted this so much later..

How to dynamically add method to class with `functools.partial()`

This is a problem for making partial for class methods, so in Python 3.4 we introduced partialmethod as the alternative. The way that works is the following:

import functools

class Creator:
def __init__(self, params):
self.params = params

class Stitch:
__tablename__ = 'stitch'
def __init__(self, params, name):
self.name = name
self.params = params

def create(self, clz, *args, **kwargs):
return clz(self.params, *args, **kwargs)

for clazz in [Stitch]:
setattr(Creator, 'create_%s' % clazz.__tablename__, functools.partialmethod(create, clz=clazz))
# use partialmethod instead here

creator = Creator('params')

stitch = creator.create_stitch(name='myname')
# works!


Related Topics



Leave a reply



Submit