method_added, when does it get called? How to call it? - ruby

I am confused on how and when method_added(method) gets called.
If I have
class Car
def method_added(method)
puts 'method added called'
super
end
def one
puts 'one method'
end
def two
puts 'two method'
end
end
Car.new.one
Why does this not work?

This is almost straight from the docs. Note this is plain Ruby, it is not a Rails thing.
class Car
def self.method_added(method_name)
puts "Adding #{method_name}"
end
def one
end
end
# => Adding one

Related

An unexpected result was encountered when invoking the instance_methods method, resulted in the return of an empty array

Please consider the below code
class Execute
def self.inherited(klass)
puts "Class Inherited: #{klass}"
klass.run
end
def self.run
puts "Running Instance Methods"
instance_methods(false).each do |m|
if m.to_s.start_with?("test_")
puts "Executing Method: #{m}"
new.send(m)
end
end
end
end
puts "Before class definition"
class Raja < Execute
def test_1
puts 'test_1'
end
def test_2
puts 'test_2'
end
def test_3
puts 'test_3'
end
end
puts "After class definition"
I am currently making an attempt to access the test_1, test_2, and test_3 methods by instantiating an object from the parent class. Upon examination of the program, one may comprehend the actions I am performing. Nonetheless, the issue arises as the invocation of the instance_methods method returns an empty array, contrary to its expected behavior of providing the names of the instance methods, i.e., test_1, test_2, and test_3. I am at a loss as to why this discrepancy is occurring and would greatly appreciate any assistance in resolving this issue
Class#inherited is called at the moment of inheritance.
At this point no instance_methods (non-inherited instance methods since you used false) have been defined.
class Raja < Execute #inherited is called here
# methods are defined here
end
It would be similar to the following
class Raja;end
Raja.instance_methods(false) #=> []
class Raja
def test_1 = 'test1'
end
You could possibly look into Module#method_added instead however I am unclear on what the reasoning behind this code is so all I can do is answer the question of Why you are experiencing what you you are experiencing.
Update
Based on OP comments looking for an auto run mechanism similar to test-unit:
As a very simplified Example:
class Tracker
##runner = {}
def self.runner
##runner
end
def self.inherited(klass)
runner[klass] = []
end
def self.method_added(method_name)
runner[self] << method_name if method_name.match?(/test_/)
end
end
class Runner
def self.run
Tracker.runner.each do |klass,tests|
tests.each do |m|
klass.new.send(m)
end
end
end
end
# Run the Runner before the program exits
at_exit { Runner.run}
Then (foo.rb)
require 'runner_tracker'
class Foo < Tracker
def test_1
puts 'test_1'
end
def test_2
puts 'test_2'
end
def test_3
puts 'test_3'
end
end
Output
> ruby foo.rb
test_1
test_2
test_3

Calling class methods from a module that is included by that class in Ruby

In the code below, I would like to call the class method done of the class that includes the module from inside self.hello
Explanation:
A::bonjour will call Mod::hello so will B::ciao
I would like to be able to detect the "calling class" (A or B) in Mod::hello in order to be able to call the A::done or B::done
module Mod
def self.hello
puts "saying hello..."
end
end
class A
include Mod
def self.bonjour
Mod::hello
end
def self.done
puts "fini"
end
end
class B
include Mod
def self.ciao
Mod::hello
end
def self.done
puts "finitto"
end
end
While (perhaps) not as clean as Niklas' answer, it's still easily doable, and IMO cleaner than the usage pattern shown in the OP which relies on knowing which module is mixed in.
(I prefer not having to pass an argument to mixin methods like this when other means exist.)
The output:
pry(main)> A::bonjour
saying hello...
fini
pry(main)> B::ciao
saying hello...
finitto
The guts:
module Mod
module ClassMethods
def hello
puts "saying hello..."
done
end
end
def self.included(clazz)
clazz.extend ClassMethods
end
end
The modified class declarations, removing the explicit module reference:
class A
include Mod
def self.bonjour
hello
end
def self.done
puts "fini"
end
end
class B
include Mod
def self.ciao
hello
end
def self.done
puts "finitto"
end
end
You may also supply a default implementation of done:
module Mod
module ModMethods
def hello
puts "saying hello..."
done
end
def done
throw "Missing implementation of 'done'"
end
end
def self.included(clazz)
clazz.extend ModMethods
end
end
As a comment to this post points out, if the snippet in the OP is a faithful representation of the actual usecase, you might as well use extend (instead of include), leaving everything cleaner:
module Mod
def hello
puts "saying hello..."
done
end
def done
raise NotImplementError("Missing implementation of 'done'")
end
end
And the classes using extend:
class A
extend Mod
def self.bonjour
hello
end
def self.done
puts "fini"
end
end

Executing code for every method call in a Ruby module

I'm writing a module in Ruby 1.9.2 that defines several methods. When any of these methods is called, I want each of them to execute a certain statement first.
module MyModule
def go_forth
a re-used statement
# code particular to this method follows ...
end
def and_multiply
a re-used statement
# then something completely different ...
end
end
But I want to avoid putting that a re-used statement code explicitly in every single method. Is there a way to do so?
(If it matters, a re-used statement will have each method, when called, print its own name. It will do so via some variant of puts __method__.)
Like this:
module M
def self.before(*names)
names.each do |name|
m = instance_method(name)
define_method(name) do |*args, &block|
yield
m.bind(self).(*args, &block)
end
end
end
end
module M
def hello
puts "yo"
end
def bye
puts "bum"
end
before(*instance_methods) { puts "start" }
end
class C
include M
end
C.new.bye #=> "start" "bum"
C.new.hello #=> "start" "yo"
This is exactly what aspector is created for.
With aspector you don't need to write the boilerplate metaprogramming code. You can even go one step further to extract the common logic into a separate aspect class and test it independently.
require 'aspector'
module MyModule
aspector do
before :go_forth, :add_multiply do
...
end
end
def go_forth
# code particular to this method follows ...
end
def and_multiply
# then something completely different ...
end
end
You can implement it with method_missing through proxy Module, like this:
module MyModule
module MyRealModule
def self.go_forth
puts "it works!"
# code particular to this method follows ...
end
def self.and_multiply
puts "it works!"
# then something completely different ...
end
end
def self.method_missing(m, *args, &block)
reused_statement
if MyModule::MyRealModule.methods.include?( m.to_s )
MyModule::MyRealModule.send(m)
else
super
end
end
def self.reused_statement
puts "reused statement"
end
end
MyModule.go_forth
#=> it works!
MyModule.stop_forth
#=> NoMethodError...
You can do this by metaprogramming technique, here's an example:
module YourModule
def included(mod)
def mod.method_added(name)
return if #added
#added = true
original_method = "original #{name}"
alias_method original_method, name
define_method(name) do |*args|
reused_statement
result = send original_method, *args
puts "The method #{name} called!"
result
end
#added = false
end
end
def reused_statement
end
end
module MyModule
include YourModule
def go_forth
end
def and_multiply
end
end
works only in ruby 1.9 and higher
UPDATE: and also can't use block, i.e. no yield in instance methods
I dunno, why I was downvoted - but a proper AOP framework is better than meta-programming hackery. And thats what OP was trying to achieve.
http://debasishg.blogspot.com/2006/06/does-ruby-need-aop.html
Another Solution could be:
module Aop
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
def before_filter(method_name, options = {})
aop_methods = Array(options[:only]).compact
return if aop_methods.empty?
aop_methods.each do |m|
alias_method "#{m}_old", m
class_eval <<-RUBY,__FILE__,__LINE__ + 1
def #{m}
#{method_name}
#{m}_old
end
RUBY
end
end
end
end
module Bar
def hello
puts "Running hello world"
end
end
class Foo
include Bar
def find_hello
puts "Running find hello"
end
include Aop
before_filter :find_hello, :only => :hello
end
a = Foo.new()
a.hello()
It is possible with meta-programming.
Another alternative is Aquarium. Aquarium is a framework that implements Aspect-Oriented Programming (AOP) for Ruby. AOP allow you to implement functionality across normal object and method boundaries. Your use case, applying a pre-action on every method, is a basic task of AOP.

Understanding Ruby class vs instance methods

I have the following code:
#!/usr/bin/ruby
class Person
def self.speak
p = self.new
puts "Hello"
p.chatter
end
private
def chatter
puts "Chattering"
end
end
p = Person.new
Person.speak
I'd like to make chatter private, accessible only within p.. but I want p to be able to access it within the class method. Is there a better way to design this so chatter isn't available to the public, but a "factory" method like self.speak can call chatter?
In Ruby 1.8.7, "send" bypasses the usual protections against calling private methods:
#!/usr/bin/ruby1.8
class Person
def self.speak
puts "Hello"
new.send(:chatter)
end
def speak
puts "Hello"
puts chatter
end
private
def chatter
puts "Chattering"
end
end
Person.speak # => Hello
# => Chattering
Person.new.speak # => Hello
# => Chattering
However, what you want can be achieved without any voodoo, by simply having the class method do all the work, and the instance method defer to the class method:
class Person
def self.speak
puts "Hello"
puts "Chatter"
end
def speak
self.class.speak
end
end
If you had more than a few of these forwarding methods, it might be convenient to make a helper method that makes them for you:
module DelegateToClass
def delegate_to_class(name)
define_method(name) do |*args|
self.class.send(name, *args)
end
end
end
class Person
extend DelegateToClass
def self.speak
puts "Hello"
puts "Chatter"
end
delegate_to_class :speak
end
The built-in module Forwardable can do this just as well:
require 'forwardable'
class Person
extend Forwardable
def self.speak
puts "Hello"
puts "Chatter"
end
def_delegator self, :speak
end
def_delegator also bypasses protections against private methods.
There's a number of ways to do this. One approach would be a class method which instantiates a new instance and calls the #speak method on the instance.
class Person
def self.speak
new.speak
end
def speak
puts "Hello"
chatter
end
private
def chatter
puts "Chattering"
end
end
Then you could call it in either the class or instance context:
p = Person.new
p.speak
# ...or...
Person.speak

Cross-cutting logging in Ruby

I'm trying to add logging to a method from the outside (Aspect-oriented-style)
class A
def test
puts "I'm Doing something..."
end
end
class A # with logging!
alias_method :test_orig, :test
def test
puts "Log Message!"
test_orig
end
end
a = A.new
a.test
The above works alright, except that if I ever needed to do alias the method again, it goes into an infinite loop. I want something more like super, where I could extend it as many times as I needed, and each extension with alias its parent.
Another alternative is to use unbound methods:
class A
original_test = instance_method(:test)
define_method(:test) do
puts "Log Message!"
original_test.bind(self).call
end
end
class A
original_test = instance_method(:test)
counter = 0
define_method(:test) do
counter += 1
puts "Counter = #{counter}"
original_test.bind(self).call
end
end
irb> A.new.test
Counter = 1
Log Message!
#=> #....
irb> A.new.test
Counter = 2
Log Message!
#=> #.....
This has the advantage that it doesn't pollute the namespace with additional method names, and is fairly easily abstracted, if you want to make a class method add_logging or what have you.
class Module
def add_logging(*method_names)
method_names.each do |method_name|
original_method = instance_method(method_name)
define_method(method_name) do |*args,&blk|
puts "logging #{method_name}"
original_method.bind(self).call(*args,&blk)
end
end
end
end
class A
add_logging :test
end
Or, if you wanted to be able to do a bunch of aspects w/o a lot of boiler plate, you could write a method that writes aspect-adding methods!
class Module
def self.define_aspect(aspect_name, &definition)
define_method(:"add_#{aspect_name}") do |*method_names|
method_names.each do |method_name|
original_method = instance_method(method_name)
define_method(method_name, &(definition[method_name, original_method]))
end
end
end
# make an add_logging method
define_aspect :logging do |method_name, original_method|
lambda do |*args, &blk|
puts "Logging #{method_name}"
original_method.bind(self).call(*args, &blk)
end
end
# make an add_counting method
global_counter = 0
define_aspect :counting do |method_name, original_method|
local_counter = 0
lambda do |*args, &blk|
global_counter += 1
local_counter += 1
puts "Counters: global##{global_counter}, local##{local_counter}"
original_method.bind(self).call(*args, &blk)
end
end
end
class A
def test
puts "I'm Doing something..."
end
def test1
puts "I'm Doing something once..."
end
def test2
puts "I'm Doing something twice..."
puts "I'm Doing something twice..."
end
def test3
puts "I'm Doing something thrice..."
puts "I'm Doing something thrice..."
puts "I'm Doing something thrice..."
end
def other_tests
puts "I'm Doing something else..."
end
add_logging :test, :test2, :test3
add_counting :other_tests, :test1, :test3
end
First choice: subclass instead of overriding:
class AWithLogging < A\
def test
puts "Log Message!"
super
end
end
Second choice: name your orig methods more carefully:
class A # with logging!
alias_method :test_without_logging, :test
def test
puts "Log Message!"
test_without_logging
end
end
Then another aspect uses a different orig name:
class A # with frobnication!
alias_method :test_without_frobnication, :test
def test
Frobnitz.frobnicate(self)
test_without_frobnication
end
end

Resources