NoMethodError: undefined method `alias_method' - ruby

I am using some metaprogramming (using ruby 2.3.1) so that i can call a method before calling the actual method i want to call - like a before_filter/before_action.
The pseudocode below explains what i am trying to achieve
module Api
def call_this_method_everytime
if A
go ahead and call the actual method being called
else
stop here do not call he method it was supposed to call
end
end
def when_i_call_this_method
end
def or_this_method
end
end
With the help of a SO member i was able to theoretically understand what i want to do using metaprogramming - which i arrived at the code below.
module Api
def heartbeat
...
end
def interceptor(name)
original_method = "original #{name}"
alias_method original_method, name
define_method(name) do |*args|
heartbeat
result = send original_method, *args
puts "The method #{name} called!"
result
end
end
end
Instead of calling the method i want - i call interceptor method with the name of the actual function i want to call as an argument. I would then first call heartbeat function and if the check is OK then i proceed with actually calling the actual function.
However having limited knowledge of metaprogramming i am getting this error
NoMethodError: undefined method 'alias_method'
Searching around did not help. Any help appreciated.

A simpler way of doing the same thing:
def interceptor(name, *args)
if 1 == 1
send name, args
end
end
interceptor(:puts, "oi")

Related

Naive aspect implementation in ruby

I am trying to make a simplistic implementation of AOP in ruby. I was able to implement before and after advices, I got stuck with around advice.
This is the target class that is going to be advised:
class MyClass
def method
puts "running method"
end
end
This is the Aspect class to instantiate objects capable of making advices:
class Aspect
def advise(class_name, method, type, &block)
class_name.send(:alias_method, :proceed, :method)
class_name.send(:define_method, :method) do
case type
when :before
yield
proceed
when :after
proceed
yield
when :around
yield(proceed) # * proceed is the old version of the method
end
end
end
end
(*) Yield should execute the block around MyClass#proceed on the current object when method is invoked.
Creating the target and the aspect:
mc = MyClass.new
a = Aspect.new()
Invoking the method without advising it:
puts mc.method
Advising MyClass#method with around:
a.advise(MyClass, :method, :around) do |proceed|
puts "First"
proceed # this is not working *
puts "Last"
end
puts mc.method
(*) I am not being able to pass something to identify the call of proceed, that is the invocation of the old method without the advice.
The output should be:
First
running method
Last
In Ruby, a method call looks like this:
receiver.method(arguments)
Or, you can leave off the receiver if the receiver is self.
So, to call a method named proceed on some receiver, you would write
receiver.proceed
However, in your implementation, you don't keep track of what the receiver should be, so since you don't know the receiver, you simply cannot call the method.
Note that there are lots of other problems with your approach as well. For example, if you advise multiple methods, you will alias them all to the same method, overwriting each other.
I believe there are two things going wrong here.
This section of code
when :around
yield(proceed) # * proceed is the old version of the method
end
Calls the block given to advise providing the output of the proceed method as an argument.
So your output probably looks something like:
running method
First
Last
This block
a.advise(MyClass, :method, :around) do |proceed|
puts "First"
proceed # this is not working *
puts "Last"
end
Just evaluates the argument given as proceed. If a method is given it does not call it. So taking problem 1 into consideration in your case the original definition of method (aliased to proceed) returns nil (output of return) which will be passed as the value to the proceed argument in the block when yielded. the block ends up evaluating to something like
puts "First"
nil
puts "Last"
mc.method is called.
To address the second part, you may want to consider using send. Because the inner workings of your aspect may not be known to your code that calls it. It may change over time, so what ever calls Aspect.advise shouldn't make assumptions that the original method will still be accessible. Instead, it should take an argument (the new method name) and send it to the object. Making the block passed to advise:
a.advise(MyClass, :method, :around) do |aliased_method_name|
puts "First"
send(aliased_method_name)
puts "Last"
end
And adjusting the around item added to your class when advise is called to the following:
when :around
yield(:proceed) # * proceed is the old version of the method
end
If you do both of these things, your around section will calls the provided block, using the symbol for the new alias for the overridden method.
N.B.: This approach won't work for methods that require any arguments.
This is what I did. In the definition of Aspect#advise now I use a Proc, like this:
when :around
yield Proc.new { proceed }
end
And when calling the method to advise MyClass#method with :around parameter I use this:
a.advise(MyClass, :method, :around) do |original|
puts "First"
original.call
puts "Last"
end
I got:
First
running method
Last
Here's the fixed version that will work for arguments, and avoid clobbering.
class Aspect
##count = 0
def self.advise(class_name, method, type=nil, &block)
old_method = :"__aspect_#{method}_#{##count += 1}"
class_name.send(:alias_method, old_method, method)
class_name.send(:define_method, method) do |*args, &callblock|
case type
when :before
yield
send(old_method, *args, &callblock)
when :after
send(old_method, *args, &callblock)
yield
when :around, nil
yield lambda {
send(old_method, *args, &callblock)
}
end
end
end
end
class Foo
def foo(what)
puts "Hello, #{what}!"
end
end
Aspect.advise(Foo, :foo) do |y|
puts "before around"
y.yield
puts "after around"
end
Aspect.advise(Foo, :foo, :before) do
puts "before"
end
Aspect.advise(Foo, :foo, :after) do
puts "after"
end
Foo.new.foo("world")
# before
# before around
# Hello, world!
# after around
# after

ruby - how to pass on incoming named params to another method in one expression?

I have a method:
def move_knight(orig_x,orig_y,offset_x,offset_y)
if valid_knight_move(offset_x,offset_y)
move('knight',orig_x,orig_y,offset_x,offset_y)
end
end
It works. I would now like to refactor the call to move and pass on those last 4 arguments in one go in the call to move, as in
move('knight',*args)
but I get
<NameError: undefined local variable or method `args' for #<ChessGame:0x000...>>
All the examples I see have *args at in the calling methods params which I don't.
I would do as below :
def move_knight(*args)
if valid_knight_move(*args.last(2))
move('knight',*args)
end
end
Change the method definition to the following:
def move_knight(*args)
if valid_knight_move(args[2],args[3])
move('knight',*args)
end
end

Event declarations

I got the following code from #avdi's Ruby Tapas episode for today:
module Eventful
def self.included(other)
other.extend(Macros)
end
def add_listener(listener)
(#listeners ||= []) << listener
end
def notify_listeners(event, *args)
(#listeners || []).each do |listener|
listener.public_send("on_#{event}", *args)
end
end
module Macros
def event(name)
module_eval(%Q{
def #{name}(*args)
notify_listeners(:#{name}, *args)
end
})
end
end
end
class Dradis
include Eventful
event :new_contact
end
class ConsoleListener
def on_new_contact(direction, range)
puts "DRADIS contact! #{range} kilometers, bearing #{direction}"
end
end
dradis = Dradis.new
dradis.add_listener(ConsoleListener.new)
dradis.new_contact(120, 23000)
I understand the concept of events and listeners and the observer pattern, but don't get how/why this syntax is working, and haven't seen it in any manuals. The class Dradis has this:
event :new_contact
At first, I thought that event was a method and :new_contact was an argument so that I would call event on an instance of Dradis, something like:
dradis = Dradis.new
dradis.event
but instead, new_contact is called on an instance of Dradis like:
dradis = Dradis.new
dradis.add_listener(ConsoleListener.new)
dradis.new_contact(120, 23000)
and that triggers the event method in the Macro module.
Can anyone explain why it works like this? calling :new_contact on an instance dradis to trigger the event method?
I didn't watch the episode, but look, it's right there.
module Macros
def event(name)
module_eval(%Q{
def #{name}(*args)
notify_listeners(:#{name}, *args)
end
})
end
end
event is a method which defines another method (new_contact) which calls notify_listeners from Eventful.
and that triggers the event method in the Macro module
Incorrect. That method has finished its work a long time ago and it doesn't get invoked again. It produced a new method using module_eval / def and that new method (new_contact) is what's getting called.
It's important to understand that event method runs only once, when the Dradis class is parsed and loaded. It does not get run on every instantiation of Dradis.
Several separated features of ruby is used: In the line event :new_contact the "evnet" is class method (http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_classes.html#UE).
Usually class methods are defined by:
class A
def A.my_class_method
#code
end
end
A.my_class_method #executing class_method
or
class A
def <<self #usefull when you want delare several class methods
def A.my_class_method
#code
end
end
end
A.my_class_method #executing class_method
In the code the method is included by the module Macros.
The key thing is, that (class method) event is dynamicaly creating new (instance) method (in this case new_contact)
The name of the method is passed as argument to event). And this method providing calling of the listener.
Can anyone explain why it works like this? calling :new_contact on an instance dradis to trigger the event method?
This is by the dynammically created method new_contact which is calling notify_listeners(:#{name}, *args)

Generic methods/Default methods in Ruby, when some method is not defined

I want to do something but I'm not sure if it is possible. I want to use "generic methods" or "default methods" in case when some method is called but is not defined. This is a simple example so you can understand my point:
This is the class:
class XYZ
def a
#...
end
def b
#...
end
end
The instance of the class XYZ:
n = XYZ.new
n.a
n.b
n.c
As you can see, I'm calling the method "c" which is not defined and it will throw an error. Can I do something in the class XYZ so when someone calls a method not defined get the name of the method and do something, in base of the name of the method? And, is this possible in another languages (not making a compiler)? If this is possible, how is it called (theory speaking)?
Use method_missing:
class XYZ
def a; end
def b; end
def method_missing(name, *args)
"Called #{name} with args: #{args}"
end
end
XYZ.new.c #=> "Called c"
You should also define respond_to_missing? to get respond_to? to work nicer in 1.9.2+. You should read more about respond_to?/respond_to_missing? when using method_missing.
This, by the way, would be considered to be metaprogramming. This isn't typically possible in compiled languages because of the way they call functions.
its called method_missing.
When you call a method that is not defined on object, ruby redirects the call to method_missing method which raises the error for you.
you can do this:
class XYZ
def method_missing(method, *args, &blck)
puts "called #{method} with arguments #{args.join(',')}"
end
end
now instead of error you will get output to your console.

Ruby access to symbol "invoked by"

I want to (efficiently) get the symbol an aliased method is called with at runtime. A direct efficient access to a stack frame object of some sort to get it would be the fantasy.
ie:
class Foo
def generic_call(*args)
puts("generic_call() was called by using #{???}")
end
alias :specific_call1 :generic_call
alias :specific_call2 :generic_call
end
Foo.new.specific_call1
Foo.new.specific_call2
the result I'd want
generic_call() was called by using specific_call1()
generic_call() was called by using specific_call2()
class Foo
def generic_call()
puts "generic call was called by #{caller[0][/in `([^']+)'/, 1]}"
end
def specific_call1() generic_call end
def specific_call2() generic_call end
end
Foo.new.specific_call2 # Prints: generic call was called by specific_call2
This will however not work if you use alias to create specific_callN from generic_call because methods created by alias are actually a copy of the original method - they don't actually call the original method (which is why you can freely redefine the original without affecting the alias).
A code snippet to get the current method name:
module Kernel
private
# Defined in ruby 1.9
unless defined?(__method__)
def __method__
caller[0] =~ /`([^']*)'/ and $1
end
end
end
There's no built-in way to do this. You can kind of hack it like:
def current_method_name
caller[0].split('`').last.split('\'')[0]
end
Maybe, you want something like this?
class Object
def named_alias(name, generic_name)
([Class, Module].include?(self.class) ? self : self.class).class_eval do
define_method(name) { |*args| send(generic_name, name, *args) }
end
end
end
class Foo
def generic_call(f, *args)
puts("generic_call() was called by using #{f} with #{args}")
end
# def specific_call1(*args)
# generic_call(:specific_call1, *args)
# end
named_alias(:specific_call1, :generic_call)
named_alias(:specific_call2, :generic_call)
end
Foo.new.specific_call1
Foo.new.specific_call2
Disclaimer: I don't know Ruby, I've just Googled how one performs currying there, then adapted the code a bit.

Resources