Works in irb but not elsewhere

Hi i’m creating a DSL, and would like to use the following
functionality.It works fine in irb but when I run it from the
commandline. Does it look like my fault or a ruby bug?

class A
def method
puts “a”
end
def A.create
A.new.instance_eval { yield }
end
end

A.create{method}

PS: I’m on “ruby 1.8.6 (2007-09-24 patchlevel 111) [i386-mswin32]”

Oh and I get:

Temp.rb:11:in method': wrong number of arguments (0 for 1) (ArgumentError) from Temp.rb:11 from Temp.rb:7:increate’
from Temp.rb:7:in instance_eval' from Temp.rb:7:increate’
from Temp.rb:11

When i run it from commandline.
Thanks a lot for your insight.
-Patrick

On Aug 8, 3:26 pm, Patrick Li [email protected] wrote:

end
end

A.create{method}

Maybe I’m missing something, but shouldn’t that be:

class A
def method
puts “a”
end
def A.create &block
A.new.instance_eval &block
end
end

On 8/8/08, [email protected] [email protected] wrote:

A.new.instance_eval { yield }

end
end

A.create{method}

I think the issue is in the last line:
you are calling Object#method’, which expects one parameter. You
ought to be using the String “method” to send to instance_eval.

-Adam

Thanks for all your comments:

I think there’s a bug in how ruby treats block parameters.

ie. the following works
def myMethod &block
A.new.instance_eval &block
end

but this doesn’t
def myMethod
A.new.instance_eval{yield}
end

Thanks goes to cardboard42 for noticing that.

Hey that works. Thanks that’s a suitable workaround for me. Though I see
nothing wrong with my version. I think it’s probably a bug. Can someone
confirm for me?

Thanks again for the workaround.
-Patrick

On Aug 8, 2008, at 22:16, Patrick Li wrote:

def myMethod
A.new.instance_eval{yield}
end

For reasons I cannot quite understand at the moment, the block for
instance_eval is executed in the scope of the receiver, but yield
inside that block will cause the block given to the caller to
execute, not raise a LocalJumpError as one would expect (since there’s
no block to yield inside the object). That block is executed at its
original scope:

@foo = :outside
=> :outside

class Klass
def initialize
@foo = :inside
end
end
Klass.new.instance_eval { @foo }
=> :inside # As expected

def lol; Klass.new.instance_eval { yield } end
lol do puts @foo end
=> :outside # o.O

I might be completely missing something here, but I would expect that
last line to raise that darn LocalJumpError. This shows that the
“current block” inside the instance_eval is indeed what’s passed to
the caller.

def lol; Klass.new.instance_eval { Proc.new } end # Proc.new with
no block returns the current block, cf. http://bit.ly/37NtlX

my_proc = proc { :test }
=> #Proc:[email protected]:15(irb)

lol &my_proc
=> #Proc:[email protected]:15(irb)

lol(&my_proc) == my_proc
=> true

This might be a bug.

On Fri, Aug 8, 2008 at 10:36 PM, Mikael Høilund [email protected] wrote:

end
a LocalJumpError as one would expect (since there’s no block to yield inside
the object). That block is executed at its original scope:

The only effect of instance_eval on the block it gets as parameter is
that self is changed. It influences only instance variables and method
calls without explicit receiver, nothing else. yield is tied to the
method it is in, not what self is at that point, so it is not affected
by instance_eval.

This should also answer the OP’s question: since yield is unaffected,
it doesn’t change self in the block it calls. So it’s definitely not a
bug. You’re calling #method on the toplevel object, which is an
instance of Object, and Object#method expects 1 parameter.

Peter

I think what’s happening is that yield is calling the block passed to
the caller of instance_eval so method is being called on self in that
block’s closure. Since it was executed at the top level that would be
Object. Object.method takes 1 parameter, thus causing the error you’re
seeing. The reason the code I posted works because it takes the block
passed to create and passes it directly to instance_eval as it’s
block. This means that method will be called on self in instance_eval,
where it is set to the object instance_eval is called on, in this case
the result of A.new.

Sorry if my explanation is hard to understand, but I’m positive this
is not a bug in ruby. instance_eval { yield } simply has very
different semantics from instance_eval &block.

Ken

On 08.08.2008 23:01, [email protected] wrote:

Sorry if my explanation is hard to understand, but I’m positive this
is not a bug in ruby. instance_eval { yield } simply has very
different semantics from instance_eval &block.

Absolutely correct, there is no bug - at least not in Ruby. You can
also see it from this:

$ ruby <<XXX

class A
def method
puts “a”
end
def A.create
A.new.instance_eval { yield }
end
end

A.create{ p self; method}

XXX
main
-:10:in method': wrong number of arguments (0 for 1) (ArgumentError) from -:10 from -:6:increate’
from -:6:in instance_eval' from -:6:increate’
from -:10

Note the output of “p self”: it’s main. And this is the case because
self is not changed for the block invoked via “yield”.

Note also that this does not work in IRB as well:

$ irb
irb(main):001:0> class A
irb(main):002:1> def method
irb(main):003:2> puts “a”
irb(main):004:2> end
irb(main):005:1> def A.create
irb(main):006:2> A.new.instance_eval { yield }
irb(main):007:2> end
irb(main):008:1> end
=> nil
irb(main):009:0>
irb(main):010:0* A.create{ p self; method}
main
ArgumentError: wrong number of arguments (0 for 1)
from (irb):10:in method' from (irb):10 from (irb):6:increate’
from (irb):6:in instance_eval' from (irb):6:increate’
from (irb):10
irb(main):011:0>

Kind regards

robert

This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.

| Privacy Policy | Terms of Service | Remote Ruby Jobs