Dynamically adding statements to a method - ruby

I have a class Foo:
class Foo
def a
"something" if true
end
end
I want an add_statement method to add new statements to the method, keeping the old implementation. Is it possible?
I want to do something like this:
foo = Foo.new
foo.extend_method(:a, &block)
so now the source of my a method should be something like this:
def a
"something" if true
&block
end
where &block is the code I passed as argument in extend_method.

You want to use alias_method_chain, or Module#prepend.
There are many tutorials / documentation about them on the net, for example "AVOIDING ALIAS_METHOD_CHAIN IN RUBY" or "Module.prepend: a super story".
For your specific example (when you want to extend the function outside of the class), you have to use alias_method_chain, added in a new module, and included in your original class using Foo.send.
You can find more details and examples in "When to use alias_method_chain".

Here is an aspect oriented example:
require 'aspector'
class Foo
def a
puts "in Foo.a"
end
end
aspect = Aspector do
before :a do
puts 'this should print first'
end
after :a do |result_of_a|
puts 'this should print last'
end
end
puts "\n\nplain instance"
foo = Foo.new
foo.a
puts "\n\napply to an instance"
aspect.apply(foo)
foo.a
puts "\n\napply to all instances of Foo"
aspect.apply(Foo)
Foo.new.a
You will need to 'gem install aspector'.

Related

Execute method like before_filter in Rails

I try to write a metaprogramming for execute a method before 'master' method. Why ? Because, I have several class and it's ugly to repeat the call in the head of the method
Case :
class MyClass
include MySuperModule
before :method, call: before_method
def before_method
puts "Before.."
end
end
class SomeClass < MyClass
def method
puts "Method.."
end
end
module MySuperModule
# the awesome code
end
Output :
SomeClass.new.method => "Before.. Method.."
So, I try write a module with ClassMethodsor method_missingwithout success.
You don't need a gem for simple metaprogramming like this. What you can do is redefine the "after" method to call the "before" method and then the original "after" method.
This works even when using before multiple times on the same method or when creating a chain of before calls.
module MySuperModule
def before meth, opts
old_method = instance_method(meth)
define_method(meth) do
send opts[:call]
old_method.bind(self).call
end
end
end
class MyClass
extend MySuperModule
def foo
puts "foo"
end
def bar
puts "bar"
end
def baz
puts "baz"
end
before :foo, call: :bar
before :bar, call: :baz
end
MyClass.new.foo
# baz
# bar
# foo
If it is just for subclassing purposes you can take advantage of Module#prepend:
class Superclass
def self.inherited(subclass)
# subclass.send :prepend, Module.new { on Ruby < 2.1
subclass.prepend Module.new {
def method
before_method
super
end
}
end
def before_method
puts 'Before'
end
end
class Subclass < Superclass
def method
puts 'Method'
end
end
Subclass.new.method
#=> Before
#=> Method
What you are looking for is Aspect oriented programming support for ruby. There are several gems implementing this, like aquarium.
Another way to do this is to use the rcapture gem.
It is pretty awesome.
Eg:
require 'rcapture'
class A
# Makes the class intercept able
include RCapture::Interceptable
def first
puts 'first'
end
def second
puts 'second'
end
end
# injects methods to be called before each specified instance method.
A.capture_pre :methods => [:first, :second] do
puts "hello"
end
n = A.new
n.first
n.second
produces:
hello
first
hello
second
Maybe you can use a decorator. In ruby there is a nice gem called 'drapeer'. See Drapper Link
Every call in ruby runs through set_trace_func so you can hook into that and call exactly what you want. Not the prettiest solution and there are better ways but it does work. Another option is the Hooks gem, though I haven't tried it myself, it looks like it should give you the ability to do what you want.
module MySuperModule
# the awesome code
end
class MyClass
include MySuperModule
def before_method
puts "Before.."
end
end
class SomeClass < MyClass
def method
puts "Method.."
end
end
set_trace_func proc { |event, file, line, id, binding, class_name|
if event == "call" && class_name == SomeClass && id == :method
caller = binding.eval("self")
caller.send(:before_method)
end
}
SomeClass.new.method
#=> Before..
#=> Method..

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.

Ruby - How to use the method parameter as the name of the variable?

How would I use the parameter value as the instance variable name of an object?
This is the object
Class MyClass
def initialize(ex,ey)
#myvar = ex
#myothervar = ey
end
end
I have the following method
def test(element)
instanceofMyClass.element #this obviously doesnt work
end
How can I have the test method return either myvar or myothervar value depending on the element parameter. I don't want to write an if condition though, I want to pass myvar or myother var via element to the object instance if possible.
def test(element)
instanceofMyClass.send(element.to_sym)
end
You'll get a missing method error if instanceofMyClass doesn't respond to element.
def test(element)
instanceofmyclass.instance_variable_get element
end
test :#myvar # => ex
test :#myothervar # => ey
I like the simplicity of send(), though one bad thing with it is that it can be used to access privates. The issue is still remains solution below, but at least then it's explicitly specified, and reader can see which methods are to be forwarded. The first one just uses delegation, while the second one uses more dynamic way to define methods on the fly.
require 'forwardable'
class A
extend Forwardable
def_delegators :#myinstance, :foo, :bar
class B
def foo
puts 'foo called'
end
def bar
puts 'bar called'
end
def quux
puts 'quux called'
end
def bif
puts 'bif called'
end
end
def initialize
#myinstance = B.new
end
%i(quux bif).each do |meth| # note that only A#quux and A#bif are defined dynamically
define_method meth do |*args_but_we_do_not_have_any|
#myinstance.send(meth)
end
end
end
a = A.new
a.foo
a.bar
a.quux
a.bif

Add method to an instanced object

obj = SomeObject.new
def obj.new_method
"do some things"
end
puts obj.new_method
> "do some things"
This works ok. However, I need to do same thing inside an existing method:
def some_random_method
def obj.new_method
"do some things"
end
end
Works ok as well, but having a method inside a method looks pretty horrible. The question is, is there any alternate way of adding such a method?
In ruby 1.9+, there's a better way of doing this using define_singleton_method, as follows:
obj = SomeObject.new
obj.define_singleton_method(:new_method) do
"do some things"
end
Use a Mixin.
module AdditionalMethods
def new_method
"do some things"
end
end
obj = SomeObject.new
obj.extend(AdditionalMethods)
puts obj.new_method
> "do some things"
There are several ways to achieve this, and they are all related to the singleton class:
You can use class << idiom to open the singleton class definition:
obj = Object.new
class << obj
def my_new_method
...
end
end
Or you can use define_singleton_method on the obj:
obj = Object.new
obj.define_singleton_method(:my_new_method) do
...
end
You can also use define_method from the singleton class:
obj = Object.new
obj.singleton_class.define_method(:my_new_method) do
...
end
Or you can use def directly:
obj = Object.new
def obj.my_new_method
...
end
Pay attention to example 3, I think the concept of a singleton class becomes clearer on that one. There is a difference between these two examples:
a = Object.new
b = Object.new
# -- defining a new method in the object's "class" --
a.class.define_method(:abc) do
puts "hello abc"
end
a.abc # prints "hello abc"
b.abc # also prints "hello abc"
# -- defining a new method in the object's "singleton class" --
a.singleton_class.define_method(:bcd) do
puts "hello bcd"
end
a.bcd # prints "hello bcd"
b.bcd # error undefined method
This is because every object has its own singleton class:
a = Object.new
b = Object.new
p a.class # prints "Object"
p a.singleton_class # prints "#<Class:#<Object:0x000055ebc0b84438>>"
p b.class # also prints "Object"
p b.singleton_class # prints "#<Class:#<Object:0x000055ebc0b84410>>" (a different reference address)
Just an interesting point to note:
if you had instead gone:
def my_method
def my_other_method; end
end
Then my_other_method would actually be defined on the CLASS of the object not withstanding that the receiver ofmy_method is an instance.
However if you go (as you did):
def my_method
def self.my_other_method; end
end
Then my_other_method is defined on the eigenclass of the instance.
Not directly relevant to your question but kind of interesting nonetheless ;)
You can use modules.
module ObjSingletonMethods
def new_method
"do some things"
end
end
obj.extend ObjSingletonMethods
puts obj.new_method # => do some things
Now if you need to add more methods to that object, you just need to implement the methods in the module and you are done.
Use instance_eval:
obj = SomeObject.new
obj.instance_eval do
def new_method
puts 'do something new'
end
end
obj.new_method
> "do something new"
class Some
end
obj = Some.new
class << obj
def hello
puts 'hello'
end
end
obj.hello
obj2 = Some.new
obj2.hello # error
Syntax class << obj means that we are opening definition of the class for an object. As you probably know we can define Ruby class methods using syntax like this:
class Math
class << self
def cos(x)
...
end
def sin(x)
...
end
end
end
Then we can use those methods like this:
Math.cos(1)
In Ruby, everything is an object - even classes. self here is an object of Math class itself (you can access that object with Math.class). So syntax class << self means we are opening class for Math class object. Yes, it means that Math class has class too (Math.class.class).
Another way to use a Mixin
obj = SomeObject.new
class << obj
include AnotherModule
end
This includes all of the methods from AnotherModule into the current object.

ruby class extensions

I would like to write a method in ruby that takes a class with certain methods and modifies its behavior by adding methods or changing how existing methods work. I would like to do this in a way that doesn't modify the base class so basically I want a function that takes a class and returns a new modified class without harming the initial class. I'm pretty sure this is possible but I'm not sure where to start.
You have a couple options:
you can use x = Class.new(Parent) { def meth; puts "hello"; super; puts "bye"; end } to dynamically define a class and override methods (& define new ones)
you can use a Delegator
So for instance, if you wanted to dynamically create classes that logged certain method calls:
class Class
def logging_subclass(*methods)
Class.new(self) do
methods.each do |method|
define_method(method) do |*args,&blk|
puts "calling #{method}"
ret = super(*args,&blk)
puts "#{method} returned #{ret.inspect}"
ret
end
end
end
end
end
class One
def foo
"I'm foo!"
end
end
# this prints nothing
One.new.foo #=> returns :foo
# this prints:
# > calling foo
# > foo returned "I'm foo!"
One.logging_subclass(:foo).new.foo #=> returns :foo
Note that you need ruby 1.9 to support capturing do |&blk| (capturing blocks in block arguments).
I'd suggest using inheritance or a mixin; in my opinion, the use of a mixin would be a wiser idea though using inheritance is easier for a newbie.
Remember, you can always inherit from the class and change behavior or wrap it with new code as desired.
class Mammal
def speak
"..."
end
end
class Cat < Mammal
def speak
"meow"
end
end
class Lion < Cat
def speak
"get ready for a big " + super + "!"
end
end
module Asexual_Critter
def reproduce(critter_list)
puts "*poink!*"
critter_list << self.clone
end
end
class Mutated_Kitty < Cat
include Asexual_Critter # inane example I know, but functional...
end
Just remember that if you want to play with this not to do:
critters = [Mutated_Kitty.new]
begin
critters.each { |c| c.reproduce(critters) }
end while critters.length > 0
Or else you'll be in for a long wait until you run out of RAM, or perhaps segfault.

Resources