Evaluate instance variable in block passed to class_eval through wrapper - ruby

I want to extend the functionality of a method asdf at runtime.
A method append_asdf should allow modifying the method more easily.
I understand how to call the previous method but the private variable #b evaluates to nil inside the block used to specify the additional behaviour - if passed through the wrapper.
It works as expected when passed to class_eval directly (which is not what I want).
Why?
class A
def initialize
#b = "144"
end
def asdf
puts "12"
end
def self.append_asdf(&n)
m = instance_method(:asdf)
define_method(:asdf) {
m.bind(self).call
n.call
}
end
end
a = A.new
p = proc {
puts #b
}
The proc p is used here to drive home the point that it doesn't depend on the block. It behaves the same as a literal.
This doesn't work:
A.append_asdf(&p)
a.asdf
=>
12
Note the empty line. The same proc used here evaluates as expected:
A.class_eval {
define_method(:asdf, p)
}
a.asdf
=>
144

You need to evaluate that block in the proper context. In other words:
instance_eval(&n)
Instead of a regular call.

Related

Local variables and methods with the same name in Ruby?

def gen_times(factor) do
return Proc.new {|n| n*factor}
end
gen_times.class # ArgumentError 0 for 1
gen_times(3).class # Proc
gen_times = 2
gen_times.class # Fixnum
times3 = gen_times(3) # A normal, working Proc
The first gen_times.class gives an ArgumentError, so I assume it returns the class name of gen_times's return value, which is confirmed in the next line.
But then, I assign gen_times, and it becomes a Fixnum. However, I can still use gen_times to return Procs.
I recall that Fixnum objects have immediate values, and that the object itself is used in assignment, rather than a reference to it.
So, is it right to say that gen_times is a Fixnum object that refers to a method?
In ruby you can have local variables and methods with the same name. This has some complications for example with setter methods in classes:
class Test
def active
#active
end
def active=(value)
#active = value
end
def make_active
active = true
end
end
t1 = Test.new
t1.active = true
t1.active #=> true
t2 = Test.new
t2.make_active
t2.active #=> nil
Code for t1 object will return expected result, but code for t2 returns nil, because make_active method is actually creating local variable and not calling active= method. You need to write self.active = true to make this work.
When you write gen_class, ruby tries to access local variable, if it is not defined ruby tries to call method. You can call your method explicit by writing gen_class().

Create blank binding in the scope of an object

class Foo
def self.run(n,code)
foo = self.new(n)
#env = foo.instance_eval{ binding }
#env.eval(code)
end
def initialize(n)
#n = n
end
end
Foo.run( 42, "p #n, defined? foo" )
#=> 42
#=> "local-variable"
The sample program above is intended to evaluate arbitrary code within the scope of a Foo instance. It does that, but the binding is "polluted" with the local variables from the code method. I don't want foo, n, or code to be visible to the eval'd code. The desired output is:
#=> 42
#=> nil
How can I create a binding that is (a) in the scope of the object instance, but (b) devoid of any local variables?
The reason that I am creating a binding instead of just using instance_eval(code) is that in the real usage I need to keep the binding around for later usage, to preserve the local variables created in it.
so like this? or did i miss something important here?
class Foo
attr_reader :b
def initialize(n)
#n = n
#b = binding
end
def self.run(n, code)
foo = self.new(n)
foo.b.eval(code)
end
end
Foo.run(42, "p #n, defined?(foo)")
# 42
# nil
or move it further down to have even less context
class Foo
def initialize(n)
#n = n
end
def b
#b ||= binding
end
def self.run(n, code)
foo = self.new(n)
foo.b.eval(code)
end
end
Foo.run(42, "p #n, defined?(foo), defined?(n)")
# 42
# nil
# nil
Answer:
module BlankBinding
def self.for(object)
#object = object
create
end
def self.create
#object.instance_eval{ binding }
end
end
Description:
In order to get a binding with no local variables, you must call binding in a scope without any of them. Calling a method resets the local variables, so we need to do that. However, if we do something like this:
def blank_binding_for(obj)
obj.instance_eval{ binding }
end
…the resulting binding will have an obj local variable. You can hide this fact like so:
def blank_binding_for(_)
_.instance_eval{ binding }.tap{ |b| b.eval("_=nil") }
end
…but this only removes the value of the local variable. (There is no remove_local_variable method in Ruby currently.) This is sufficient if you are going to use the binding in a place like IRB or ripl where the _ variable is set after every evaluation, and thus will run over your shadow.
However, as shown in the answer at top, there's another way to pass a value to a method, and that's through an instance variable (or class variable, or global variable). Since we are using instance_eval to shift the self to our object, any instance variables we create in order to invoke the method will not be available in the binding.

Why do instance variables seemingly disappear when inside a block?

Forgive me, guys. I am at best a novice when it comes to Ruby. I'm just curious to know the explanation for what seems like pretty odd behavior to me.
I'm using the Savon library to interact with a SOAP service in my Ruby app. What I noticed is that the following code (in a class I've written to handle this interaction) seems to pass empty values where I expect the values of member fields to go:
create_session_response = client.request "createSession" do
soap.body = {
:user => #user, # This ends up being empty in the SOAP request,
:pass => #pass # as does this.
}
end
This is despite the fact that both #user and #pass have been initialized as non-empty strings.
When I change the code to use locals instead, it works the way I expect:
user = #user
pass = #pass
create_session_response = client.request "createSession" do
soap.body = {
:user => user, # Now this has the value I expect in the SOAP request,
:pass => pass # and this does too.
}
end
I'm guessing this strange (to me) behavior must have something to do with the fact that I'm inside a block; but really, I have no clue. Could someone enlighten me on this one?
First off, #user is not a "private variable" in Ruby; it is an instance variable. Instance variables are available within the the scope of the current object (what self refers to). I have edited the title of your question to more accurately reflect your question.
A block is like a function, a set of code to be executed at a later date. Often that block will be executed in the scope where the block was defined, but it is also possible to evaluate the block in another context:
class Foo
def initialize( bar )
# Save the value as an instance variable
#bar = bar
end
def unchanged1
yield if block_given? # call the block with its original scope
end
def unchanged2( &block )
block.call # another way to do it
end
def changeself( &block )
# run the block in the scope of self
self.instance_eval &block
end
end
#bar = 17
f = Foo.new( 42 )
f.unchanged1{ p #bar } #=> 17
f.unchanged2{ p #bar } #=> 17
f.changeself{ p #bar } #=> 42
So either you are defining the block outside the scope where #user is set, or else the implementation of client.request causes the block to be evaluated in another scope later on. You could find out by writing:
client.request("createSession"){ p [self.class,self] }
to gain some insight into what sort of object is the current self in your block.
The reason they "disappear" in your case—instead of throwing an error—is that Ruby permissively allows you to ask for the value of any instance variable, even if the value has never been set for the current object. If the variable has never been set, you'll just get back nil (and a warning, if you have them enabled):
$ ruby -e "p #foo"
nil
$ ruby -we "p #foo"
-e:1: warning: instance variable #foo not initialized
nil
As you found, blocks are also closures. This means that when they run they have access to local variables defined in the same scope as the block is defined. This is why your second set of code worked as desired. Closures are one excellent way to latch onto a value for use later on, for example in a callback.
Continuing the code example above, you can see that the local variable is available regardless of the scope in which the block is evaluated, and takes precedence over same-named methods in that scope (unless you provide an explicit receiver):
class Foo
def x
123
end
end
x = 99
f.changeself{ p x } #=> 99
f.unchanged1{ p x } #=> 99
f.changeself{ p self.x } #=> 123
f.unchanged1{ p self.x } #=> Error: undefined method `x' for main:Object
From the documentation:
Savon::Client.new accepts a block inside which you can access local variables and even public methods from your own class, but instance variables won’t work. If you want to know why that is, I’d recommend reading about instance_eval with delegation.
Possibly not as well documented when this question was asked.
In the first case, self evaluates to client.request('createSession'), which doesn't have these instance variables.
In the second, the variables are brought into the block as part of the closure.
Another way to fix the issue would be to carry a reference to your object into the block rather than enumerating each needed attribute more than once:
o = self
create_session_response = client.request "createSession" do
soap.body = {
:user => o.user,
:pass => o.pass
}
end
But now you need attribute accessors.

Ruby: convert proc to lambda?

Is it possible to convert a proc-flavored Proc into a lambda-flavored Proc?
Bit surprised that this doesn't work, at least in 1.9.2:
my_proc = proc {|x| x}
my_lambda = lambda &p
my_lambda.lambda? # => false!
This one was a bit tricky to track down. Looking at the docs for Proc#lambda? for 1.9, there's a fairly lengthy discussion about the difference between procs and lamdbas.
What it comes down to is that a lambda enforces the correct number of arguments, and a proc doesn't. And from that documentation, about the only way to convert a proc into a lambda is shown in this example:
define_method always defines a method without the tricks, even if a non-lambda Proc object is given. This is the only exception which the tricks are not preserved.
class C
define_method(:e, &proc {})
end
C.new.e(1,2) => ArgumentError
C.new.method(:e).to_proc.lambda? => true
If you want to avoid polluting any class, you can just define a singleton method on an anonymous object in order to coerce a proc to a lambda:
def convert_to_lambda &block
obj = Object.new
obj.define_singleton_method(:_, &block)
return obj.method(:_).to_proc
end
p = Proc.new {}
puts p.lambda? # false
puts(convert_to_lambda(&p).lambda?) # true
puts(convert_to_lambda(&(lambda {})).lambda?) # true
It is not possible to convert a proc to a lambda without trouble. The answer by Mark Rushakoff doesn't preserve the value of self in the block, because self becomes Object.new. The answer by Pawel Tomulik can't work with Ruby 2.1, because define_singleton_method now returns a Symbol, so to_lambda2 returns :_.to_proc.
My answer is also wrong:
def convert_to_lambda &block
obj = block.binding.eval('self')
Module.new.module_exec do
define_method(:_, &block)
instance_method(:_).bind(obj).to_proc
end
end
It preserves the value of self in the block:
p = 42.instance_exec { proc { self }}
puts p.lambda? # false
puts p.call # 42
q = convert_to_lambda &p
puts q.lambda? # true
puts q.call # 42
But it fails with instance_exec:
puts 66.instance_exec &p # 66
puts 66.instance_exec &q # 42, should be 66
I must use block.binding.eval('self') to find the correct object. I put my method in an anonymous module, so it never pollutes any class. Then I bind my method to the correct object. This works though the object never included the module! The bound method makes a lambda.
66.instance_exec &q fails because q is secretly a method bound to 42, and instance_exec can't rebind the method. One might fix this by extending q to expose the unbound method, and redefining instance_exec to bind the unbound method to a different object. Even so, module_exec and class_exec would still fail.
class Array
$p = proc { def greet; puts "Hi!"; end }
end
$q = convert_to_lambda &$p
Hash.class_exec &$q
{}.greet # undefined method `greet' for {}:Hash (NoMethodError)
The problem is that Hash.class_exec &$q defines Array#greet and not Hash#greet. (Though $q is secretly a method of an anonymous module, it still defines methods in Array, not in the anonymous module.) With the original proc, Hash.class_exec &$p would define Hash#greet. I conclude that convert_to_lambda is wrong because it doesn't work with class_exec.
Here is possible solution:
class Proc
def to_lambda
return self if lambda?
# Save local reference to self so we can use it in module_exec/lambda scopes
source_proc = self
# Convert proc to unbound method
unbound_method = Module.new.module_exec do
instance_method( define_method( :_proc_call, &source_proc ))
end
# Return lambda which binds our unbound method to correct receiver and calls it with given args/block
lambda do |*args, &block|
# If binding doesn't changed (eg. lambda_obj.call) then bind method to original proc binding,
# otherwise bind to current binding (eg. instance_exec(&lambda_obj)).
unbound_method.bind( self == source_proc ? source_proc.receiver : self ).call( *args, &block )
end
end
def receiver
binding.eval( "self" )
end
end
p1 = Proc.new { puts "self = #{self.inspect}" }
l1 = p1.to_lambda
p1.call #=> self = main
l1.call #=> self = main
p1.call( 42 ) #=> self = main
l1.call( 42 ) #=> ArgumentError: wrong number of arguments (1 for 0)
42.instance_exec( &p1 ) #=> self = 42
42.instance_exec( &l1 ) #=> self = 42
p2 = Proc.new { return "foo" }
l2 = p2.to_lambda
p2.call #=> LocalJumpError: unexpected return
l2.call #=> "foo"
Should work on Ruby 2.1+
Cross ruby compatiable library for converting procs to lambdas:
https://github.com/schneems/proc_to_lambda
The Gem:
http://rubygems.org/gems/proc_to_lambda
The above code doesn't play nicely with instance_exec but I think there is simple fix for that. Here I have an example which illustrates the issue and solution:
# /tmp/test.rb
def to_lambda1(&block)
obj = Object.new
obj.define_singleton_method(:_,&block)
obj.method(:_).to_proc
end
def to_lambda2(&block)
Object.new.define_singleton_method(:_,&block).to_proc
end
l1 = to_lambda1 do
print "to_lambda1: #{self.class.name}\n"
end
print "l1.lambda?: #{l1.lambda?}\n"
l2 = to_lambda2 do
print "to_lambda2: #{self.class.name}\n"
end
print "l2.lambda?: #{l2.lambda?}\n"
class A; end
A.new.instance_exec &l1
A.new.instance_exec &l2
to_lambda1 is basically the implementation proposed by Mark, to_lambda2 is a "fixed" code.
The output from above script is:
l1.lambda?: true
l2.lambda?: true
to_lambda1: Object
to_lambda2: A
In fact I'd expect instance_exec to output A, not Object (instance_exec should change binding). I don't know why this work differently, but I suppose that define_singleton_method returns a method that is not yet bound to Object and Object#method returns an already bound method.

How do I call a method that is a hash value?

Previously, I asked about a clever way to execute a method on a given condition "Ruby a clever way to execute a function on a condition."
The solutions and response time was great, though, upon implementation, having a hash of lambdas gets ugly quite quickly. So I started experimenting.
The following code works:
def a()
puts "hello world"
end
some_hash = { 0 => a() }
some_hash[0]
But if I wrap this in a class it stops working:
class A
#a = { 0 => a()}
def a()
puts "hello world"
end
def b()
#a[0]
end
end
d = A.new()
d.b()
I can't see why it should stop working, can anyone suggest how to make it work?
that code doesn't work. it executes a at the time it is added to the hash, not when it is retrieved from the hash (try it in irb).
It doesn't work in the class because there is no a method defined on the class (you eventually define a method a on the instance.
Try actually using lambdas like
{0 => lambda { puts "hello world" }}
instead
First of all, you are not putting a lambda in the hash. You're putting the result of calling a() in the current context.
Given this information, consider what the code in your class means. The context of a class definition is the class. So you define an instance method called a, and assign a class instance variable to the a hash containing the result of calling a in the current context. The current context is the class A, and class A does not have a class method called a, so you're trying to put the result of a nonexistent method there. Then in the instance method b, you try to access an instance variable called #a -- but there isn't one. The #a defined in the class context belongs to the class itself, not any particular instance.
So first of all, if you want a lambda, you need to make a lambda. Second, you need to be clear about the difference between a class and an instance of that class.
If you want to make a list of method names to be called on certain conditions, you can do it like this:
class A
def self.conditions() { 0 => :a } end
def a
puts "Hello!"
end
def b(arg)
send self.class.conditions[arg]
end
end
This defines the conditions hash as a method of the class (making it easy to access), and the hash merely contains the name of the method to call rather than a lambda or anything like that. So when you call b(0), it sends itself the message contained in A.conditions[0], which is a.
If you really just want to pretty this sort of thing up,
why not wrap all your methods in a class like so:
# a container to store all your methods you want to use a hash to access
class MethodHash
alias [] send
def one
puts "I'm one"
end
def two
puts "I'm two"
end
end
x = MethodHash.new
x[:one] # prints "I'm one"
x.two # prints "I'm one"
or, to use your example:
# a general purpose object that transforms a hash into calls on methods of some given object
class DelegateHash
def initialize(target, method_hash)
#target = target
#method_hash = method_hash.dup
end
def [](k)
#target.send(#method_hash[k])
end
end
class A
def initialize
#a = DelegateHash.new(self, { 0 => :a })
end
def a()
puts "hello world"
end
def b()
#a[0]
end
end
x = A.new
x.a #=> prints "hello world"
x.b #=> prints "hello world"
One other basic error that you made is that you initialized #a outside of any instance method -
just bare inside of the definition of A. This is a big time no-no, because it just doesn't work.
Remember, in ruby, everything is an object, including classes, and the # prefix means the instance
variable of whatever object is currently self. Inside an instance method definitions, self is an instance
of the class. But outside of that, just inside the class definition, self is the class object - so you defined
an instance variable named #a for the class object A, which none of the instances of A can get to directly.
Ruby does have a reason for this behaviour (class instance variables can be really handy if you know what
you're doing), but this is a more advanced technique.
In short, only initialize instance variables in the initialize method.
table = {
:a => 'test',
:b => 12,
:c => lambda { "Hallo" },
:d => def print(); "Hallo in test"; end
}
puts table[:a]
puts table[:b]
puts table[:c].call
puts table[:d].send( :print )
Well, the first line in your class calls a method that doesn't exist yet. It won't even exist after the whole class is loaded though, since that would be a call to the class method and you've only defined instance methods.
Also keep in mind that {0 => a()} will call the method a(), not create a reference to the method a(). If you wanted to put a function in there that doesn't get evaluated until later, you'd have to use some kind of Lambda.
I am pretty new to using callbacks in Ruby and this is how I explained it to myself using an example:
require 'logger'
log = Logger.new('/var/tmp/log.out')
def callit(severity, msg, myproc)
myproc.call(sev, msg)
end
lookup_severity = {}
lookup_severity['info'] = Proc.new { |x| log.info(x) }
lookup_severity['debug'] = Proc.new { |x| log.debug(x) }
logit = Proc.new { |x,y| lookup_sev[x].call(y) }
callit('info', "check4", logit)
callit('debug', "check5", logit)
a = ->(string="No string passed") do
puts string
end
some_hash = { 0 => a }
some_hash[0].call("Hello World")
some_hash[0][]

Resources