Inject a line of code into an existing method at runtime - ruby

At runtime, I would like to inject a line of code at the - # <-- Injection point
class A
def some_method(a, b = 1, *c, d: 1, **e)
# <-- Injection point
# Code that does stuff
puts a
puts b
puts c
puts d
puts e
end
end
The reason I need this is so that I can use the Ruby Tracepoint Class to extract some run time information about the method signature.
Specifically the default parameter values in b = 1 and d: 1
I have looked at this question Inspect the default value for optional parameter in ruby's method? where a useful answer exists but want to inject the code that they suggest, dynamically.
I already have existing code that can extracts method signatures from ruby classes.
Example
Given this Class, which is defined with lots of methods using various parameterized signatures.
class A
def d; end
def e(a); end
def f(a, b = 1); end
def g(a, b = 1, c = 2); end
def h(*a); end
def i(a, b, *c); end
def j(**a); end
def k(a, *b, **c); end
def l(a, *b, **c, &d); end
def m(a:); end
def n(a:, b: 1); end
def p?; end
def z(a, b = 1, *c, d: 1, **e); end
end
I can run the following code to extract signature information. Unfortunately, it fails to resolve the default parameter or named values.
class_definition = MethodSignatures.new(A.new)
class_definition.print
Issues are Red

The reason I need this is so that I can use the Ruby Tracepoint Class to extract some run time information about the method signature.
Specifically the default parameter values in b = 1 and d: 1
TracePoint allows you to fetch information without patching the method. You just have to set up a :call hook and then call the method:
TracePoint.trace(:call) do |tp|
tp.parameters.each do |type, name|
value = tp.binding.local_variable_get(name)
puts format('%8s: %s = %s', type, name, value.inspect)
end
end
A.new.some_method(123)
Output: (omitting the method's output)
req: a = 123
opt: b = 1
rest: c = []
key: d = 1
keyrest: e = {}

Prior to Ruby v2.0 one would redefine A#some_method after aliasing the original A#some_method.
class A
def some_method(a, b, c)
puts a
puts b
puts c
end
end
A.public_send(:alias_method, :old_some_method, :some_method)
A.define_method(:some_method) do |a,b,c|
puts "a=#{a}, b=#{b}, c=#{c}"
old_some_method(a,b,c)
end
A.new.some_method(1,2,3)
a=1, b=2, c=3
1
2
3
A better way, however, is to define the new method A#some_method in a module and then Module#prepend that module to the class A. prepend made its debut in Ruby v2.0.
module M
def some_method(a,b,c)
puts "a=#{a}, b=#{b}, c=#{c}"
super
end
end
A.prepend M
A.new.some_method(1,2,3)
a=1, b=2, c=3
1
2
3
A.prepend M gives the methods of M priority over those of A.
A.ancestors
#=> [M, A, Object, Kernel, BasicObject]
shows this to be the case and also explains the role of super in the method M#some_method.

Related

Identifying a Ruby module method invoked by super?

Ruby 2.7+.
I have methods in a couple of modules that are mixed in, and are invoked with super. Each of the module methods invokes super in turn, so all methods by that name in the mixed-in modules are invoked, although perhaps not in a deterministic order.
My question is: Can a method tell programmatically (as opposed to hard-coding) from what module it's been mixed in?
module A
def initialize(*args, **kwargs)
puts("something magic")
end
end
module B
def initialize(*args, **kwargs)
puts("something magic")
end
end
class C
include A
include B
def initialize(*args, **kwargs)
puts("actual #{class.name} initialize")
super
end
end
When run, this will print three lines. What I'm seeking is something like class.name, specific to each module, that identifies the module that supplied the initialize method that's running. The "something magic* strings would be replaced with this actual magic. 🙂
Thanks!
My first attempt was to call super_method to get all the initializers up the stack:
module A
def initialize
A # return for comparison
end
end
module B
def initialize
B
end
end
class C
include A
include B
def initialize
C
end
def super_initializers
init = method(:initialize)
while init
print init.call, " == " # get hardcoded module from `initialize`
p init.owner # get the module dynamically
init = init.super_method # keep getting the super method
end
end
end
>> C.new.super_initializers
C == C
B == B
A == A
== BasicObject
Second idea is to use Module.nesting, I think this is what you're looking for:
module A
def initialize
# i, o, m = method(:initialize), [], Module.nesting[0]
# while i; o << i.owner; i = i.super_method; end
# print "prev "; p o[o.index(m)-1] # previous super
puts "A == #{Module.nesting[0]}"
# print "next "; p o[o.index(m)+1] # next super
super
end
end
module B
def initialize
puts "B == #{Module.nesting[0]}"
super
end
end
# add a class
class Y
def initialize
puts "Y == #{Module.nesting[0]}"
super
end
end
# add some nesting
module X
class Z < Y
def initialize
puts "Z == #{Module.nesting[0]}"
super
end
end
end
class C < X::Z
include A
include B
def initialize
puts "C == #{Module.nesting[0]}"
super
end
end
>> C.new
C == C
B == B
A == A
Z == X::Z
Y == Y
Actually, never thought about that this could be useful, but it works:
def super_trace m
while m;
p m.owner; m = m.super_method
end
end
>> super_trace User.new.method(:save)
ActiveRecord::Suppressor
ActiveRecord::Transactions
ActiveRecord::Validations
ActiveRecord::Persistence
https://rubyapi.org/3.1/o/module#method-c-nesting
https://rubyapi.org/3.1/o/method#method-i-super_method

How to get a list of used methods in Ruby?

hey I want that my method logify puts each method with its parameters and return value of my class A. I wrote for example a simple class A with two methods add and sub and the output should look like that:
Output:
Method add(1, 2) called
return value 3
Method sub(1, 2) called
return value -1
I know that I can get each method with self.instance_methods(false) but can someone please help me further?
require_relative "log"
class A
extend Log
def add(a, b)
a + b
end
def sub(a, b)
a - b
end
logify
end
a = A.new
a.add(2,1)
a.sub(2,1)
module Log
def logify
puts self.instance_methods(false)
end
end
You can use Module#prepend and Module#prepended to help with this like so:
module Log
def self.prepended(base)
base.instance_methods(false).each do |m|
define_method(m) do |*args, &block|
puts "Method #{m}(#{args.join(',')}) called"
val = super(*args, &block)
puts "return value #{val}"
val
end
end
end
end
class A
def add(a, b)
a + b
end
def sub(a, b)
a - b
end
end
A.prepend(Log)
What this does is it defines a method in the prepended module with the same name as the original then builds your output and delagets to the original method in the middle (super) to obtain the return value.
Examples
a = A.new
a.add(2,1)
# Method add(2,1) called
# return value 3
#=> 3
a.sub(2,1)
# Method sub(2,1) called
# return value 1
#=> 1
Caveat: this will only show the provided arguments and will not output default arguments in the method signature
The ruby core library includes the class TracePoint, which can be used to trace just about anything - from methods being defined, or invoked, or exceptions being raised, ...
Here is an example usage, which will perform the tracking you desired:
class A
def add(a, b)
a + b
end
def sub(a, b)
a - b
end
end
TracePoint.trace(:call, :return) do |tp|
next unless tp.defined_class == A
case tp.event
when :call
params = tp.parameters.map { |arg| eval(arg[1].to_s, tp.binding) }
puts "Method #{tp.method_id}(#{params.join(', ')}) called"
when :return
puts "return value #{tp.return_value}"
end
end
# The trace has been enabled! Any time one of those events occurs, the block is evaluated.
a = A.new
a.add(2,1)
a.sub(2,1)
Output:
Method add(2, 1) called
return value 3
Method sub(2, 1) called
return value 1
Fetching the params data is, as you can see, a little troublesome. TracePoint has access to the method signature, but you need to make use of the trace's binding to see what values it's actually been called with.

Insertion reflection code with local variables in Ruby 2.0

class Test
def method
a = 2
b = 3
yield
c = 4
a *= c
a
end
def method_x
method {}
end
def method_y
method { a += b }
end
end
test = Test.new
p test.method_x
p test.method_y
This above code doesn't work well.
$ ruby code.rb
8
code.rb:16:in `block in method_y': undefined local variable or method `b' for #<Test:0x007fe6da094890> (NameError)
from code.rb:5:in `method'
from code.rb:16:in `method_y'
from code.rb:22:in `<main>'
I had expected that test.method_y returns 20.
I'd like to insert reflection code within a specific process.
But I don't know to use local variables on destination code block within reflection code.
Could you tell me how to use local variables or a good design?
The block doesn't have access to the method variables. You need to pass those variables into the block.
class Test
def method
a = 2
b = 3
a = yield(a,b) || a
c = 4
a *= c
a
end
def method_x
method {}
end
def method_y
method { |x, y| x += y }
end
end
test = Test.new
p test.method_x
p test.method_y
Since the block might return nil, the above code only moves the result of the block into a if it's not nil, otherwise it leaves a unchanged.
EDIT
It's also possible that you don't pass a block into a method... the block can be optional.
In which case you might want to do...
(a = yield(a,b) || a) if block_given?
Following is the way, you can pass variables in the ruby code block:
class Test
def method
a = 2
b = 3
a = block_given? ? a : yield(a, b)
c = 4
a *= c
a
end
def method_x
method {}
end
def method_y
method {|a,b| a += b }
end
end
test = Test.new
p test.method_x
p test.method_y
The problem with your code is it doesn't modifies your local variable and the syntax is used wrong to pass variable in the code block. One useful link on how to use code block in ruby is here.

Ruby: what method is called?

Assume, I have an object x of MyClass. What method is called, when I do puts x? I need to override it with my own one.
I thought it was .inspect, but somehow overridden inspect isn't being called.
For example, I have a class Sum:
class Sum
initiazlie a, b
#x = a + b
end
end
And I wanna access the result like this:
s = Sum.new(3,4)
puts s #=> 7 How do I do this?
puts 10 + s #=> 17 or even this...?
It calls: to_s
class Sum
def to_s
"foobar"
end
end
puts Sum.new #=> 'foobar'
Or if you want, you can just call inspect from to_s, so that you have a consistent string representation of your object.
class Sum
def to_s
inspect
end
end
First, your Sum class is invalid. The definition should be.
class Sum
def initialize(a, b)
#x = a + b
end
end
By default, the method called to get a human readable representation is inspect. Try this in irb
$ s = Sum.new(3, 4)
# => #<Sum:0x10041e7a8 #x=7>
$ s.inspect
# => "#<Sum:0x10041e7a8 #x=7>"
But in your case, you use the puts method which forces a string conversion. For this reason, the Sum object is first converted to string using the to_s method.
$ s = Sum.new(3, 4)
# => #<Sum:0x10041e7a8 #x=7>
$ puts s
# => #<Sum:0x10041e7a8>
$ puts s.to_s
# => #<Sum:0x10041e7a8>
Also note your last example fall into a third case. Because you sum a Fixnum + an other object, the result is expected to be a Fixnum and the method called is to_s but the one defined in the Fixnum class.
In order to use the one in your Sum class, you need to switch the items in the sum and define the + in your object.
class Sum
def initialize(a, b)
#x = a + b
end
def +(other)
#x + other
end
def to_s
#x.to_s
end
end
s = Sum.new(3, 4)
s + 10
puts s
# => 17

How to implement a "callback" in Ruby?

I'm not sure of the best idiom for C style call-backs in Ruby - or if there is something even better ( and less like C ). In C, I'd do something like:
void DoStuff( int parameter, CallbackPtr callback )
{
// Do stuff
...
// Notify we're done
callback( status_code )
}
Whats a good Ruby equivalent? Essentially I want to call a passed in class method, when a certain condition is met within "DoStuff"
The ruby equivalent, which isn't idiomatic, would be:
def my_callback(a, b, c, status_code)
puts "did stuff with #{a}, #{b}, #{c} and got #{status_code}"
end
def do_stuff(a, b, c, callback)
sum = a + b + c
callback.call(a, b, c, sum)
end
def main
a = 1
b = 2
c = 3
do_stuff(a, b, c, method(:my_callback))
end
The idiomatic approach would be to pass a block instead of a reference to a method. One advantage a block has over a freestanding method is context - a block is a closure, so it can refer to variables from the scope in which it was declared. This cuts down on the number of parameters do_stuff needs to pass to the callback. For instance:
def do_stuff(a, b, c, &block)
sum = a + b + c
yield sum
end
def main
a = 1
b = 2
c = 3
do_stuff(a, b, c) { |status_code|
puts "did stuff with #{a}, #{b}, #{c} and got #{status_code}"
}
end
This "idiomatic block" is a very core part of everyday Ruby and is covered frequently in books and tutorials. The Ruby information section provides links to useful [online] learning resources.
The idiomatic way is to use a block:
def x(z)
yield z # perhaps used in conjunction with #block_given?
end
x(3) {|y| y*y} # => 9
Or perhaps converted to a Proc; here I show that the "block", converted to a Proc implicitly with &block, is just another "callable" value:
def x(z, &block)
callback = block
callback.call(z)
end
# look familiar?
x(4) {|y| y * y} # => 16
(Only use the above form to save the block-now-Proc for later use or in other special cases as it adds overhead and syntax noise.)
However, a lambda can be use just as easily (but this is not idiomatic):
def x(z,fn)
fn.call(z)
end
# just use a lambda (closure)
x(5, lambda {|y| y * y}) # => 25
While the above approaches can all wrap "calling a method" as they create closures, bound Methods can also be treated as first-class callable objects:
class A
def b(z)
z*z
end
end
callable = A.new.method(:b)
callable.call(6) # => 36
# and since it's just a value...
def x(z,fn)
fn.call(z)
end
x(7, callable) # => 49
In addition, sometimes it's useful to use the #send method (in particular if a method is known by name). Here it saves an intermediate Method object that was created in the last example; Ruby is a message-passing system:
# Using A from previous
def x(z, a):
a.__send__(:b, z)
end
x(8, A.new) # => 64
Happy coding!
Explored the topic a bit more and updated the code.
The following version is an attempt to generalize the technique, although remaining extremely simplified and incomplete.
I largely stole - hem, found inspiration in - the implementation of callbacks of DataMapper, which seems to me quite complete and beatiful.
I strongly suggest to have a look at the code # http://github.com/datamapper/dm-core/blob/master/lib/dm-core/support/hook.rb
Anyway, trying to reproduce the functionality using the Observable module was quite engaging and instructive.
A few notes:
method added seems to be require because the original instance methods are not available at the moment of registering the callbacks
the including class is made both observed and self-observer
the example is limited to the instance methods, does not support blocks, args and so on
code:
require 'observer'
module SuperSimpleCallbacks
include Observable
def self.included(klass)
klass.extend ClassMethods
klass.initialize_included_features
end
# the observed is made also observer
def initialize
add_observer(self)
end
# TODO: dry
def update(method_name, callback_type) # hook for the observer
case callback_type
when :before then self.class.callbacks[:before][method_name.to_sym].each{|callback| send callback}
when :after then self.class.callbacks[:after][method_name.to_sym].each{|callback| send callback}
end
end
module ClassMethods
def initialize_included_features
#callbacks = Hash.new
#callbacks[:before] = Hash.new{|h,k| h[k] = []}
#callbacks[:after] = #callbacks[:before].clone
class << self
attr_accessor :callbacks
end
end
def method_added(method)
redefine_method(method) if is_a_callback?(method)
end
def is_a_callback?(method)
registered_methods.include?(method)
end
def registered_methods
callbacks.values.map(&:keys).flatten.uniq
end
def store_callbacks(type, method_name, *callback_methods)
callbacks[type.to_sym][method_name.to_sym] += callback_methods.flatten.map(&:to_sym)
end
def before(original_method, *callbacks)
store_callbacks(:before, original_method, *callbacks)
end
def after(original_method, *callbacks)
store_callbacks(:after, original_method, *callbacks)
end
def objectify_and_remove_method(method)
if method_defined?(method.to_sym)
original = instance_method(method.to_sym)
remove_method(method.to_sym)
original
else
nil
end
end
def redefine_method(original_method)
original = objectify_and_remove_method(original_method)
mod = Module.new
mod.class_eval do
define_method(original_method.to_sym) do
changed; notify_observers(original_method, :before)
original.bind(self).call if original
changed; notify_observers(original_method, :after)
end
end
include mod
end
end
end
class MyObservedHouse
include SuperSimpleCallbacks
before :party, [:walk_dinosaure, :prepare, :just_idle]
after :party, [:just_idle, :keep_house, :walk_dinosaure]
before :home_office, [:just_idle, :prepare, :just_idle]
after :home_office, [:just_idle, :walk_dinosaure, :just_idle]
before :second_level, [:party]
def home_office
puts "learning and working with ruby...".upcase
end
def party
puts "having party...".upcase
end
def just_idle
puts "...."
end
def prepare
puts "preparing snacks..."
end
def keep_house
puts "house keeping..."
end
def walk_dinosaure
puts "walking the dinosaure..."
end
def second_level
puts "second level..."
end
end
MyObservedHouse.new.tap do |house|
puts "-------------------------"
puts "-- about calling party --"
puts "-------------------------"
house.party
puts "-------------------------------"
puts "-- about calling home_office --"
puts "-------------------------------"
house.home_office
puts "--------------------------------"
puts "-- about calling second_level --"
puts "--------------------------------"
house.second_level
end
# => ...
# -------------------------
# -- about calling party --
# -------------------------
# walking the dinosaure...
# preparing snacks...
# ....
# HAVING PARTY...
# ....
# house keeping...
# walking the dinosaure...
# -------------------------------
# -- about calling home_office --
# -------------------------------
# ....
# preparing snacks...
# ....
# LEARNING AND WORKING WITH RUBY...
# ....
# walking the dinosaure...
# ....
# --------------------------------
# -- about calling second_level --
# --------------------------------
# walking the dinosaure...
# preparing snacks...
# ....
# HAVING PARTY...
# ....
# house keeping...
# walking the dinosaure...
# second level...
This simple presentation of the use of Observable could be useful: http://www.oreillynet.com/ruby/blog/2006/01/ruby_design_patterns_observer.html
So, this may be very "un-ruby", and I am not a "professional" Ruby developer, so if you guys are going to smack be, be gentle please :)
Ruby has a built-int module called Observer. I have not found it easy to use, but to be fair I did not give it much of a chance. In my projects I have resorted to creating my own EventHandler type (yes, I use C# a lot). Here is the basic structure:
class EventHandler
def initialize
#client_map = {}
end
def add_listener(id, func)
(#client_map[id.hash] ||= []) << func
end
def remove_listener(id)
return #client_map.delete(id.hash)
end
def alert_listeners(*args)
#client_map.each_value { |v| v.each { |func| func.call(*args) } }
end
end
So, to use this I expose it as a readonly member of a class:
class Foo
attr_reader :some_value_changed
def initialize
#some_value_changed = EventHandler.new
end
end
Clients of the "Foo" class can subscribe to an event like this:
foo.some_value_changed.add_listener(self, lambda { some_func })
I am sure this is not idiomatic Ruby and I am just shoehorning my C# experience into a new language, but it has worked for me.
If you are willing to use ActiveSupport (from Rails), you have a straightforward implementation
class ObjectWithCallbackHooks
include ActiveSupport::Callbacks
define_callbacks :initialize # Your object supprots an :initialize callback chain
include ObjectWithCallbackHooks::Plugin
def initialize(*)
run_callbacks(:initialize) do # run `before` callbacks for :initialize
puts "- initializing" # then run the content of the block
end # then after_callbacks are ran
end
end
module ObjectWithCallbackHooks::Plugin
include ActiveSupport::Concern
included do
# This plugin injects an "after_initialize" callback
set_callback :initialize, :after, :initialize_some_plugin
end
end
I know this is an old post, but I found it when tried to solve a similar problem.
It's a really elegant solution, and most importantly, it can work with and without a callback.
Let's say we have the Arithmetic class which implements basic operations on them — addition and subtraction.
class Arithmetic
def addition(a, b)
a + b
end
def subtraction(a, b)
a - b
end
end
And we want to add a callback for each operation which will do something with the input data and result.
In the below example we will implement the after_operation method which accepts the Ruby block which will be executed after an operation.
class Arithmetic
def after_operation(&block)
#after_operation_callback = block
end
def addition(a, b)
do_operation('+', a, b)
end
def subtraction(a, b)
do_operation('-', a, b)
end
private
def do_operation(sign, a, b)
result =
case sign
when '+'
a + b
when '-'
a - b
end
if callback = #after_operation_callback
callback.call(sign, a, b, result)
end
result
end
end
Using with callback:
callback = -> (sign, a, b, result) do
puts "#{a} #{sign} #{b} = #{result}"
end
arithmetic = Arithmetic.new
arithmetic.after_operation(&callback)
puts arithmetic.addition(1, 2)
puts arithmetic.subtraction(3, 1)
Output:
1 + 2 = 3
3
3 - 1 = 2
2
I often implement callbacks in Ruby like in the following example. It's very comfortable to use.
class Foo
# Declare a callback.
def initialize
callback( :on_die_cast )
end
# Do some stuff.
# The callback event :on_die_cast is triggered.
# The variable "die" is passed to the callback block.
def run
while( true )
die = 1 + rand( 6 )
on_die_cast( die )
sleep( die )
end
end
# A method to define callback methods.
# When the latter is called with a block, it's saved into a instance variable.
# Else a saved code block is executed.
def callback( *names )
names.each do |name|
eval <<-EOF
##{name} = false
def #{name}( *args, &block )
if( block )
##{name} = block
elsif( ##{name} )
##{name}.call( *args )
end
end
EOF
end
end
end
foo = Foo.new
# What should be done when the callback event is triggered?
foo.on_die_cast do |number|
puts( number )
end
foo.run
I know this is an old post, but others that come across this may find my solution helpful.
http://chrisshepherddev.blogspot.com/2015/02/callbacks-in-pure-ruby-prepend-over.html

Resources