How to implement a "callback" in Ruby? - 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

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 "magically" add code to all public class methods in ruby?

I would like to be able to insert some code at the beginning and at the end of methods in my class. I would like to avoid repetition as well.
I found this answer helpful, however it doesn't help with the repetition.
class MyClass
def initialize
[:a, :b].each{ |method| add_code(method) }
end
def a
sleep 1
"returning from a"
end
def b
sleep 1
"returning from b"
end
private
def elapsed
start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
block_value = yield
finish = Process.clock_gettime(Process::CLOCK_MONOTONIC)
puts "elapsed: #{finish - start} seconds, block_value: #{block_value}."
block_value
end
def add_code(meth)
meth = meth.to_sym
self.singleton_class.send(:alias_method, "old_#{meth}".to_sym, meth)
self.singleton_class.send(:define_method, meth) do
elapsed do
send("old_#{meth}".to_sym)
end
end
end
end
The above does work, but what would be a more elegant solution? I would love to be able to, for example, put attr_add_code at the beginning of the class definition and list the methods I want the code added to, or perhaps even specify that I want it added to all public methods.
Note: The self.singleton_class is just a workaround since I am adding code during the initialisation.
If by repetition you mean the listing of methods you want to instrument, then you can do something like:
module Measure
def self.prepended(base)
method_names = base.instance_methods(false)
base.instance_eval do
method_names.each do |method_name|
alias_method "__#{method_name}_without_timing", method_name
define_method(method_name) do
t1 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
public_send("__#{method_name}_without_timing")
t2 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
puts "Method #{method_name} took #{t2 - t1}"
end
end
end
end
end
class Foo
def a
puts "a"
sleep(1)
end
def b
puts "b"
sleep(2)
end
end
Foo.prepend(Measure)
foo = Foo.new
foo.a
foo.b
# => a
# => Method a took 1.0052679998334497
# => b
# => Method b took 2.0026899999938905
Main change is that i use prepend and inside the prepended callback you can find the list of methods defined on the class with instance_methods(false), the falseparameter indicating that ancestors should not be considered.
Instead of using method aliasing, which in my opinion is something of the past since the introduction of Module#prepend, we can prepend an anonymous module that has a method for each instance method of the class to be measured. This will cause calling MyClass#a to invoke the method in this anonymous module, which measures the time and simply resorts to super to invoke the actual MyClass#a implementation.
def measure(klass)
mod = Module.new do
klass.instance_methods(false).each do |method|
define_method(method) do |*args, &blk|
start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
value = super(*args, &blk)
finish = Process.clock_gettime(Process::CLOCK_MONOTONIC)
puts "elapsed: #{finish - start} seconds, value: #{value}."
value
end
end
end
klass.prepend(mod)
end
Alternatively, you can use class_eval, which is also faster and allows you to just call super without specifying any arguments to forward all arguments from the method call, which isn't possible with define_method.
def measure(klass)
mod = Module.new do
klass.instance_methods(false).each do |method|
class_eval <<-CODE, __FILE__, __LINE__ + 1
def #{method}(*)
start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
value = super
finish = Process.clock_gettime(Process::CLOCK_MONOTONIC)
puts "elapsed: \#{finish - start} seconds, value: \#{value}."
value
end
CODE
end
end
klass.prepend(mod)
end
To use this, simply do:
measure(MyClass)
It looks like you're trying to do some benchmarking. Have you checked out the benchmark library? It's in the standard library.
require 'benchmark'
puts Benchmark.measure { MyClass.new.a }
puts Benchmark.measure { MyClass.new.b }
Another possibility would be to create a wrapper class like so:
class Measure < BasicObject
def initialize(target)
#target = target
end
def method_missing(name, *args)
t1 = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
target.public_send(name, *args)
t2 = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
::Kernel.puts "Method #{name} took #{t2 - t1}"
end
def respond_to_missing?(*args)
target.respond_to?(*args)
end
private
attr_reader :target
end
foo = Measure.new(Foo.new)
foo.a
foo.b

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.

How do I attach an observer to a method without adding code to the method itself?

I'm trying to figure out a way to have a method trigger another method by creating a new Listener class. I'd really like the code to be simplified and not involve adding anything specific to the callback method or the trigger method. Basically, what I'm trying to do is this:
def level_up
level += 1
end
def print_level
puts "Level Up! (#{level})"
end
notify_level = Listener.new(:level_up, :print_level);
What my Listener class is (right now) is this:
# Listener.new(attached_to, callbacks)
class Listener
def initialize(attached_to, function)
#owner, #callback = attached_to, function
end
def owner
#owner
end
def callback
#callback
end
def trigger
# execute callback manually
self.method(#owner).call
self.method(#callback).call
end
end
In order to call both, I need to execute notify_level.trigger itself, but what I want is to execute level_up and call print_level. I know someone will mention something about observers, but I need more than just that. I want to hold fast to DRY. Manually adding observers and listeners to every single method is just terrible, especially since I can't add or remove them with ease.
Personally I'm not a big fan of this pattern but this is kind of a fun question so here is my solution. Should work in Ruby 1.9 and greater.
module MethodListener
##observed_methods = {}
def method_added(method)
alias_name = "__#{method}_orig"
return if method_defined?(alias_name) || method.match(/__.*_orig/)
alias_method alias_name, method
define_method(method) do |*args|
ret = send(alias_name, *args)
(##observed_methods[method] || []).each {|callback| send(callback)}
ret
end
end
def listen(owner, callback)
(##observed_methods[owner] ||= []) << callback
end
end
Usage example:
class A
extend MethodListener
def b(a,b)
puts "b #{a} #{b}"
true
end
def c
puts 'c'
end
listen :b, :c
end
A.new.b(1,2) # => true
# Prints:
# b 1 2
# c
I changed my original code to be more semantic and so it would make more sense.
class Event
def initialize(event, callback_array = [])
if callback_array.kind_of? Array
#callbacks = callback_array
else
#callbacks = [callback_array]
end
#event = event
end
def trigger(*args)
self.method(#event).call *args
#callbacks.each{ |callback|
if callback.instance_of? Event
callback.trigger *args
else
method(callback).call *args
end
}
end
def add(callback)
#callbacks.push callback
end
def remove(callback)
#callbacks.delete_at(#callbacks.index(callback) || #callbacks.length)
end
def event_name
#event
end
end
Usage:
$infinite_break = 10
def infinite_loop_a(type)
puts "#{$infinite_break} points of #{type} damage taken"
$infinite_break -= 1
if $infinite_break > 0
$infinite.trigger(type)
else
$infinite.remove(:infinite_loop_a)
end
end
def infinite_loop_b(type)
puts "player is dealing #{$infinite_break} damage"
end
$infinite = Event.new(:infinite_loop_b, :infinite_loop_a)
$infinite.trigger('fire')
Also, I know I'm calling the infinite_loop_b inside infinite_loop_a, but that's for a specific reason. The Event instances can have another Event as a callback.

How to use Ruby mixins as patches to classes

I started looking into Ruby, since I am looking to a more dynamic alternative to Java.
I like how you can modify a class in Ruby after it's definition, for example like this:
class A
def print
"A"
end
end
class B < A
def print
super + "B"
end
end
class A
alias_method :print_orig, :print
def print
print_orig + "+"
end
end
puts B.new.print # A+B
Now I try to do the same with mixins:
class A
def print
"A"
end
end
class B < A
def print
super + "B"
end
end
module Plus
alias_method :print_orig, :print
def print
print_orig + "+"
end
end
A.extend(Plus) # variant 1
B.extend(Plus) # variant 2
class A # variant 3
include Plus
end
class B # variant 4
include Plus
end
puts B.new.print
However none of the variants produce the expected result. BTW, the expected result is the following: I want to be able to 'patch' class A with a mixin, in order to modify its behavior. I want to use mixins, since I want to 'patch' several classes with the same behavior.
Is it possible to do what I want? If yes, how?
Your module code doesn't work because it is executed in wrong context. You need to execute it in context of A, but it is instead evaluated in context of Plus. This means, you need to change self from Plus to A.
Observe:
class A
def print
"A"
end
end
class B < A
def print
super + "B"
end
end
module Plus
self # => Plus
def self.included base
self # => Plus
base # => A
base.class_eval do
self # => A
alias_method :print_orig, :print
def print
print_orig + "+"
end
end
end
end
A.send :include, Plus
B.new.print # => "A+B"
You can't really use Mixins in this way. You're generating a conflict between the class and its mixin. Mixins implicitly resolve the conflict by linearization. Bottom line is: In case of conflict, the class's method is preferred over the mixin. To fix that, you can use Sergio' Tulentsev's approach and have the mixin change its base class aggressively.
Or, you can add methods reflectively. Consider this example, which I've stolen from Mark's blog.
class Talker
[:hello, :good_bye].each do |arg|
method_name = ("say_" + arg.to_s).to_sym
send :define_method, method_name do
puts arg
end
end
end
t = Talker.new
t.say_hello
t.say_good_bye

Resources