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
Related
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
I have a parent class:
class Base
def my_method
block_method do
# EXECUTE WHATEVER'S IN THE CHILD VERSION OF my_method
# HOW TO DO?
end
end
def block_method
original_foo = 'foo'
foo = 'CUSTOM FOO'
yield
foo = original_foo
end
end
and some child classes--currently five and there will be more:
class ChildA < Base
def my_method
puts 'apple'
puts 'aardvark'
end
end
class ChildE < Base
def my_method
puts 'eel'
puts 'elephant'
end
end
I want to change what a variable refers to just for the duration of each Child's #my_method.
My thought is to do this by wrapping the functionality of each Child's #my_method in a block. But I'd rather do that in the parent class than have to wrap each child class's #my_method in the exact same block.
Any insights on how I can do this?
If there's some opposite of super, I guess that would be one way to accomplish what I want to do.
You could give the idea of what you are calling "some opposite of super" using a module and prepend like so:
module MethodThing
# added to remove binding from class since
# #my_method relies on the existence of #foo and #foo=
def self.prepended(base)
base.attr_reader(:foo) unless base.method_defined?(:foo)
base.attr_writer(:foo) unless base.method_defined?(:foo=)
end
def my_method
puts "Before: #{foo}"
original_foo = foo
self.foo= 'CUSTOM FOO'
begin
super
rescue NoMethodError
warn "(skipped) #{self.class}##{__method__} not defined"
end
self.foo = original_foo
puts "After: #{foo}"
end
end
Prepend the module on inheritance
class Base
def self.inherited(child)
child.prepend(MethodThing)
end
attr_accessor :foo
def initialize
#foo = 12
end
end
class ChildA < Base
def my_method
puts 'apple'
puts "During: #{foo}"
puts 'aardvark'
end
end
class ChildE < Base
end
Output:
ChildA.new.my_method
# Before: 12
# apple
# During: CUSTOM FOO
# aardvark
# After: 12
ChildE.new.my_method
# Before: 12
# (skipped) ChildE#my_method not defined
# After: 12
There are other strange ways to accomplish this with inheritance as well such as
class Base
class << self
attr_accessor :delegate_my_method
def method_added(method_name)
if method_name.to_s == "my_method" && self.name != "Base"
warn "#{self.name}#my_method has been overwritten use delegate_my_method instead"
end
end
end
attr_accessor :foo
def my_method
puts "Before: #{foo}"
original_foo = foo
self.foo= 'CUSTOM FOO'
begin
method(self.class.delegate_my_method.to_s).()
rescue NameError, TypeError
warn "(skipped) #{self.class} method delegation not defined"
end
self.foo = original_foo
puts "After: #{foo}"
end
end
class ChildA < Base
self.delegate_my_method = :delegation_method
def delegation_method
puts 'apple'
puts "During: #{foo}"
puts 'aardvark'
end
end
I could probably keep going with stranger and stranger ways to solve this problem but I think these will get you where you need to go.
One option would be to define a private or nodoc method which the parent class can call and is defined in each of the children.
class Parent
def my_method
block_method do
my_method_behavior
end
end
def block_method
original_foo = #foo
#foo = 'CUSTOM FOO'
yield
#foo = original_foo
end
end
class Child1 < Parent
def my_method_behavior
puts #foo
end
end
class Child2 < Parent
def my_method_behavior
puts #foo
end
end
class Main
def say_hello
puts "Hello"
end
private
def say_hi
puts "hi"
end
end
class SubMain < Main
def say_hello
puts "Testing #{say_hi}"
end
end
test = SubMain.new
test.say_hello()
OUTPUT:
hi
Testing
The difference is that in ruby you can call private methods in subclasses implicitly but not explicitly. Protected can be called both ways. As for why? I guess you would have to ask Matz.
Example:
class TestMain
protected
def say_hola
puts "hola"
end
def say_ni_hao
puts "ni hao"
end
private
def say_hi
puts "hi"
end
def say_bonjour
puts "bonjour"
end
end
class SubMain < TestMain
def say_hellos
# works - protected/implicit
say_hola
# works - protected/explicit
self.say_ni_hao
# works - private/implicit
say_hi
# fails - private/explicit
self.say_bonjour
end
end
test = SubMain.new
test.say_hellos()
In ruby, you can do this:
class Thing
public
def f1
puts "f1"
end
private
def f2
puts "f2"
end
public
def f3
puts "f3"
end
private
def f4
puts "f4"
end
end
where now f1 and f3 and public, f2 and f4 is private. What is going on internally that allows you to invoke a class method that then changes the method definition? How can I implement the same functionality (ostensibly to create my own java like annotations)
for example...
class Thing
fun
def f1
puts "hey"
end
notfun
def f2
puts "hey"
end
end
and fun and notfun would change the following function definitions.
Thanks
You can sometimes shove Ruby into an espressso cup. Let's see how.
Here's a module FunNotFun...
module FunNotFun
def fun
#method_type = 'fun'
end
def notfun
#method_type = 'not fun'
end
def method_added(id)
return unless #method_type
return if #bypass_method_added_hook
orig_method = instance_method(id)
#bypass_method_added_hook = true
method_type = #method_type
define_method(id) do |*args|
orig_method.bind(self).call(*args).tap do
puts "That was #{method_type}"
end
end
#bypass_method_added_hook = false
end
end
... that you can use to extend a class ...
class Thing
extend FunNotFun
fun
def f1
puts "hey"
end
notfun
def f2
puts "hey"
end
end
... with this result:
Thing.new.f1
# => hey
# => That was fun
Thing.new.f2
# => hey
# => That was not fun
But see below the line for a better way.
Annotations (see normalocity's answer) are less trouble and, being a common Ruby idiom, will more easily communicate your code's intent. Here's how to do it with annotations:
module FunNotFun
def fun(method_id)
wrap_method(method_id, "fun")
end
def notfun(method_id)
wrap_method(method_id, "not fun")
end
def wrap_method(method_id, type_of_method)
orig_method = instance_method(method_id)
define_method(method_id) do |*args|
orig_method.bind(self).call(*args).tap do
puts "That was #{type_of_method}"
end
end
end
end
In use, the annotation comes after the method is defined, rather than before:
class Thing
extend FunNotFun
def f1
puts "hey"
end
fun :f1
def f2
puts "hey"
end
notfun :f2
end
The result is the same:
Thing.new.f1
# => hey
# => That was fun
Thing.new.f2
# => hey
# => That was not fun
Sounds like you want to write extensions to the Ruby language itself, which is possible. It's not something that can be explained briefly, but this link should get you started:
http://ruby-doc.org/docs/ProgrammingRuby/html/ext_ruby.html
This reference, having to do with annotations in Ruby, might also be helpful/relevant:
http://martinfowler.com/bliki/RubyAnnotations.html
Here's a pure-ruby solution to get you in the right direction. It hinges on method_added. Be careful to avoid recursion by using a guard clause.
module Annotations
def fun
#state = :fun
end
def not_fun
#state = :not_fun
end
def inject_label(method_name)
state = #state
define_method(:"#{method_name}_with_label") do |*args, &block|
puts "Invoking #{method_name} in state #{state}"
send(:"#{method_name}_without_label", *args, &block)
end
alias_method :"#{method_name}_without_label", :"#{method_name}"
alias_method :"#{method_name}", :"#{method_name}_with_label"
end
def self.extended(base)
base.instance_eval do
def self.method_added(method_name)
return if method_name.to_s =~ /_with(out)?_label\Z/
#seen ||= {}
unless #seen[method_name]
#seen[method_name] = true
inject_label(method_name)
end
end
end
end
end
class Foo
extend Annotations
fun
def something
puts "I'm something"
end
not_fun
def other
puts "I'm the other"
end
end
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.