Insertion reflection code with local variables in Ruby 2.0 - ruby

class Test
def method
a = 2
b = 3
yield
c = 4
a *= c
a
end
def method_x
method {}
end
def method_y
method { a += b }
end
end
test = Test.new
p test.method_x
p test.method_y
This above code doesn't work well.
$ ruby code.rb
8
code.rb:16:in `block in method_y': undefined local variable or method `b' for #<Test:0x007fe6da094890> (NameError)
from code.rb:5:in `method'
from code.rb:16:in `method_y'
from code.rb:22:in `<main>'
I had expected that test.method_y returns 20.
I'd like to insert reflection code within a specific process.
But I don't know to use local variables on destination code block within reflection code.
Could you tell me how to use local variables or a good design?

The block doesn't have access to the method variables. You need to pass those variables into the block.
class Test
def method
a = 2
b = 3
a = yield(a,b) || a
c = 4
a *= c
a
end
def method_x
method {}
end
def method_y
method { |x, y| x += y }
end
end
test = Test.new
p test.method_x
p test.method_y
Since the block might return nil, the above code only moves the result of the block into a if it's not nil, otherwise it leaves a unchanged.
EDIT
It's also possible that you don't pass a block into a method... the block can be optional.
In which case you might want to do...
(a = yield(a,b) || a) if block_given?

Following is the way, you can pass variables in the ruby code block:
class Test
def method
a = 2
b = 3
a = block_given? ? a : yield(a, b)
c = 4
a *= c
a
end
def method_x
method {}
end
def method_y
method {|a,b| a += b }
end
end
test = Test.new
p test.method_x
p test.method_y
The problem with your code is it doesn't modifies your local variable and the syntax is used wrong to pass variable in the code block. One useful link on how to use code block in ruby is here.

Related

Inject a line of code into an existing method at runtime

At runtime, I would like to inject a line of code at the - # <-- Injection point
class A
def some_method(a, b = 1, *c, d: 1, **e)
# <-- Injection point
# Code that does stuff
puts a
puts b
puts c
puts d
puts e
end
end
The reason I need this is so that I can use the Ruby Tracepoint Class to extract some run time information about the method signature.
Specifically the default parameter values in b = 1 and d: 1
I have looked at this question Inspect the default value for optional parameter in ruby's method? where a useful answer exists but want to inject the code that they suggest, dynamically.
I already have existing code that can extracts method signatures from ruby classes.
Example
Given this Class, which is defined with lots of methods using various parameterized signatures.
class A
def d; end
def e(a); end
def f(a, b = 1); end
def g(a, b = 1, c = 2); end
def h(*a); end
def i(a, b, *c); end
def j(**a); end
def k(a, *b, **c); end
def l(a, *b, **c, &d); end
def m(a:); end
def n(a:, b: 1); end
def p?; end
def z(a, b = 1, *c, d: 1, **e); end
end
I can run the following code to extract signature information. Unfortunately, it fails to resolve the default parameter or named values.
class_definition = MethodSignatures.new(A.new)
class_definition.print
Issues are Red
The reason I need this is so that I can use the Ruby Tracepoint Class to extract some run time information about the method signature.
Specifically the default parameter values in b = 1 and d: 1
TracePoint allows you to fetch information without patching the method. You just have to set up a :call hook and then call the method:
TracePoint.trace(:call) do |tp|
tp.parameters.each do |type, name|
value = tp.binding.local_variable_get(name)
puts format('%8s: %s = %s', type, name, value.inspect)
end
end
A.new.some_method(123)
Output: (omitting the method's output)
req: a = 123
opt: b = 1
rest: c = []
key: d = 1
keyrest: e = {}
Prior to Ruby v2.0 one would redefine A#some_method after aliasing the original A#some_method.
class A
def some_method(a, b, c)
puts a
puts b
puts c
end
end
A.public_send(:alias_method, :old_some_method, :some_method)
A.define_method(:some_method) do |a,b,c|
puts "a=#{a}, b=#{b}, c=#{c}"
old_some_method(a,b,c)
end
A.new.some_method(1,2,3)
a=1, b=2, c=3
1
2
3
A better way, however, is to define the new method A#some_method in a module and then Module#prepend that module to the class A. prepend made its debut in Ruby v2.0.
module M
def some_method(a,b,c)
puts "a=#{a}, b=#{b}, c=#{c}"
super
end
end
A.prepend M
A.new.some_method(1,2,3)
a=1, b=2, c=3
1
2
3
A.prepend M gives the methods of M priority over those of A.
A.ancestors
#=> [M, A, Object, Kernel, BasicObject]
shows this to be the case and also explains the role of super in the method M#some_method.

Forwarding an iterator

I have class (highly simplified for the sake of this discussion)
class MyClass
def initialize
#myrecs = %w(a b c d)
end
def each_rec
#myrecs.each {|r| yield(r)}
end
end
and use it as
x = MyClass.new
x.each_rec { |r| .... }
Since my method each_rec basically does just a myrecs.each, I wanted to define somehow that each_rec just forwards to each. I can achieve the desired effect by explicitly passing the block, i.e.
def each_rec(&block)
#myrecs.each(&block)
end
but I wonder wether it is also possible to achieve my goal without having the block as explicit parameter. I tried the following approaches without success:
(1) Exploit the fact that each returns an Enumerator when called without a block:
def each_rec
#myrecs.each
end
(2) Create an Enumerator:
def each_rec
#myrecs.enum_for(:each)
end
In both cases, I did not get an error, but the block passed to each_rec was simply not entered.
In Ruby, if you call Proc.new without a block inside a method that received a block, it'll convert that method's passed block to a proc. So, you can do:
class MyClass
def initialize
#myrecs = %w(a b c d)
end
def each_rec
#myrecs.each(&Proc.new)
end
end
x = MyClass.new
x.each_rec { |r| puts r }
and now, you no longer need an explicit parameter to your each_rec method. Now, however, you must supply a block to the method, or get an ArgumentError ("tried to create Proc object without a block"). We can fix that with a guard clause:
def each_rec
return #myrecs.each unless block_given?
#myrecs.each(&Proc.new)
end
and can now use it just like each:
x = MyClass.new
x.each_rec { |r| p r }
x.each_rec.with_index { |r, i| p [r, i] }

Useful alternative uses for yield beyond being an anonymous method?

Example:
a = Proc.new do
b = 'hey'
end
def a_method
yield
end
a_method(&a) #=> 'hey'
I understand that yield (or block.call) can be used as a simple co-routine, but I was wondering, are there any more (practical) uses for it beyond simply getting the return value from it? Can we get some of the local variables from the proc into the main method etc.?
If you're not picky about the return value of the block, you can do it using binding.
a = Proc.new do
b = 'hey'
binding
end
def a_method
new_binding = yield
p new_binding.eval("b")
end
a_method(&a)
# => "hey"
If you're using a version of Ruby >= 2.1.0, you can avoid the ickiness of eval by using local_variable_get instead:
p new_binding.local_variable_get(:b)
There are a lot of uses for Procs. For example, you can use it to give instructions to an object. I made a bubble sort using a proc that tells it how to sort. Here it is monkey patched in the array class:
class Array
def bubble_sort(&prc)
self.dup.bubble_sort!(&prc)
end
def bubble_sort!(&prc)
return self if count <= 1
prc = Proc.new { |x, y| x <=> y } unless prc.class == Proc
(0...self.count).each do |x|
((x + 1)...self.count).each do |y|
self[x], self[y] = self[y], self[x] if prc.call(self[x], self[y]) == 1
end
end
self
end
end

Calculate expression out of the class

I am trying to send an expression to a class and get the result. I don't know in advance what the expression would be and I get it as a string form the user. and I can't run it from in the class
class MyClass
def self.alias1
return 1
end
def self.alias2
return 2
end
# I can't do that:
##result = alias1+alias2
#print #result.to_s
end # end class MyClass
# That is working
inst1 = MyClass.new
result = inst1.send("alias1")
# result = 1
# I want to do that:
inst = MyClass.new
result = inst.send("alias1+alias2")
print result
expected result:
3
now I am getting an error that the method "alias1+alias2" doesn't exist.
From Ruby:
main: undefined method `alias1+alias2' for MyClass:Class (NoMethodError)
Let's clean up your class:
class MyClass
def one
1
end
def two
2
end
end
You can use instance_eval to evaluate arbitrary expression in the context of your MyClass instance:
m = MyClass.new
m.instance_eval("one + two")
#=> 3
But this is dangerous. The user could define additional methods, access the filesystem or run shell commands.
Therefore, you should parse and evaluate the expression yourself.

How does Ruby chaining work?

Why can you chain this:
"Test".upcase.reverse.next.swapcase
but not this:
x = My_Class.new
x.a.b.c
where
class My_Class
def a
#b = 1
end
def b
#b = #b + 2
end
def c
#b = #b -72
end
end
The upcase, reverse, next and swapcase methods all return String objects and all those methods are for... you guessed it, String objects!
When you call a method (more often than not, like 99.9999% of the time) it returns an object. This object has methods defined on it which can then be called which explains why you can do this:
"Test".upcase.reverse.next.swapcase
You can even call reverse as many times as you like:
"Test".reverse.reverse.reverse.reverse.reverse.reverse.reverse.reverse
All because it returns the same kind of object, a String object!
But you can't do this with your MyClass:
x = My_Class.new
x.a.b.c
For that to work, the a method would have to return an object which has the b method defined on it. Right now, that seems like only instances of MyClass would have that. To get this to work you could make the return value of a the object itself, like this:
def a
#b += 2
self
end
Extrapolating this, the b method would also need to return self as the c method is available only on instances of the MyClass class. It's not important what c returns in this example, because it's the end of the chain. It could return self, it could not. Schrödinger's cat method. Nobody knows until we open the box.
As support for other answers, this code:
"Test".upcase.reverse.next.swapcase
...is almost exactly the same as...
a = "Test"
b = a.upcase
c = b.reverse
d = c.next
e = d.swapcase
....except that my code above has extra variables left over pointing to the intermediary results, whereas the original leaves no extra references around. If we do this with your code:
x = MyClass.new # x is an instance of MyClass
y = x.a # y is 1, the last expression in the a method
z = y.b # Error: Fixnums have no method named 'b'
Using Ruby 1.9's tap method, we can even make this more explicit:
irb> "Test".upcase.tap{|o| p o}.reverse.tap{|o| p o}.next.tap{|o| p o}.swapcase
#=> "TEST"
#=> "TSET"
#=> "TSEU"
=> "tseu"
irb> class MyClass
irb> def a
irb> #b = 1
irb> end
irb> def b
irb> #b += 2
irb> end
irb> end
=> nil
irb(main):011:0> x = MyClass.new
=> #<MyClass:0x000001010202e0>
irb> x.a.tap{|o| p o}.b.tap{|o| p o}.c
#=> 1
NoMethodError: undefined method `b' for 1:Fixnum
from (irb):12
from /usr/local/bin/irb:12:in `<main>'
The last expression in a function is its implicit return value. You need to return self if you want to chain methods like that.
For example, your a method is currently returning 1. b is not a method for numbers. You'll want to modify it like so:
def a
#b = 1
self
end

Resources