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
How to Validate the Presence of a Belongs to Association with Rails
Should I Check in '.Ruby-Gemset' And/Or '.Ruby-Version'
Ruby Equivalent of Perl Data::Dumper
How Does MACports Install Packages? How to Activate a Ruby Installation Done via MACports
Generating a Short Uuid String Using Uuidtools in Rails
Counting Days Excluding Weekends
Rails 3.1 Actioncontroller::Routingerror (No Route Matches [Get] "/Assets/Rails.Png"):
Sinatra with a Persistent Variable
How to Generate Coordinates in Between Two Known Points
How to Specify "Http Request Header" in Openuri
How to Check If There's a Nil Set or Not in an Array
How to Convert a Ruby String Range to a Range Object
How to Use Nokogiri::Xml::Reader to Parse Large Xml Files