Class_eval used inline vs within class definition


#1

I’m reading the Ruby for Rails book, and on page 350 it has the example

irb(main):001:0> var = “initialized variable”
=> “initialized variable”
irb(main):002:0> class C; end
=> nil
irb(main):003:0> C.class_eval { define_method(“talk”) { puts var } }
=> #Proc:0x00355c30@:3(irb)
irb(main):004:0> C.new.talk
initialized variable
=> nil

So I wanted to see if I could do that within the class. I’ve tried
the following approach:
irb(main):005:0> class A
irb(main):006:1> class_eval { define_method(“talk”) { puts var } }
irb(main):007:1> end
=> #Proc:0x0033f0ac@:6(irb)
irb(main):008:0> A.new.talk
NameError: undefined local variable or method var' for #<A:0x337c30> from (irb):6:intalk’
from (irb):8
from :0

I’ve also tried it with self.class_eval within the class definition.
Can someone explain to me what the difference is between the inline
code example, and specifying it inside the class? Obviously they’re
not equivalent, otherwise my attempt would work…I just don’t
understand what the difference is.

Pat


#2

On 4/22/06, Pat M. removed_email_address@domain.invalid wrote:

Can someone explain to me what the difference is between the inline
code example, and specifying it inside the class?

A class definition introduces a new scope (similar to def) so for your
example to work you need to define the local variable ~within~ the
class definition for it to be visible from the closure, e.g.

class A
var = “initialized variable”
class_eval { define_method(:talk) { puts var } }
end

A.new.talk

#=> initialized variable

Regards,

Sean


#3

Hi –

On Sat, 22 Apr 2006, Pat M. wrote:

class A
Sean

The point of this is to be able to access ‘var’ even though it’s
normally outside of the class’s scope. See the very first example,
which does this.

But look closely at it:

irb(main):001:0> var = “initialized variable”
=> “initialized variable”
irb(main):002:0> class C; end
=> nil
irb(main):003:0> C.class_eval { define_method(“talk”) { puts var } }
=> #Proc:0x00355c30@:3(irb)
irb(main):004:0> C.new.talk
initialized variable

The variable var is defined at the top level, and the call to
class_eval takes place at the top level. So var is in scope, and can
be used inside the class_eval block.

In your example, var is on one side of a class keyword, and the use of
var is on the other. What you’ve done is like:

var = 1
class C
puts var
end

which will give you the same error.

David


David A. Black (removed_email_address@domain.invalid)
Ruby Power and Light, LLC (http://www.rubypowerandlight.com)

“Ruby for Rails” PDF now on sale! http://www.manning.com/black
Paper version coming in early May!


#4

On 4/22/06, removed_email_address@domain.invalid removed_email_address@domain.invalid wrote:

A class definition introduces a new scope (similar to def) so for your
#=> initialized variable

end
“Ruby for Rails” PDF now on sale! http://www.manning.com/black
Paper version coming in early May!

Okay, that makes sense now. I think that’s what Sean was telling me
as well…I just didn’t get it as quickly. Thanks for the
explanation.

Pat


#5

On 4/22/06, Sean O’Halpin removed_email_address@domain.invalid wrote:

var = "initialized variable"

The point of this is to be able to access ‘var’ even though it’s
normally outside of the class’s scope. See the very first example,
which does this.

Pat


#6

Hi –

On Sat, 22 Apr 2006, James Edward G. II wrote:

class:
I believe this to be an example of Dave B.'s new refactoring, Remove Unused
Scope.

To be fair to Sean, though, the question really had to do with
clarifying how the scope of class_eval worked – so removing it kind
of defeats that particular purpose :slight_smile:

David


David A. Black (removed_email_address@domain.invalid)
Ruby Power and Light, LLC (http://www.rubypowerandlight.com)

“Ruby for Rails” PDF now on sale! http://www.manning.com/black
Paper version coming in early May!


#7

On Apr 22, 2006, at 6:52 AM, Sean O’Halpin wrote:

var = "initialized variable"
class_eval { define_method(:talk) { puts var } }

end

A.new.talk

#=> initialized variable

We might as well drop the class_eval { … }, since we are already in
the class:

class A
var = “initialized variable”
define_method(:talk) { puts var }
end
=> #Proc:0x0033307c@:3(irb)

A.new.talk
initialized variable
=> nil

I believe this to be an example of Dave B.'s new refactoring,
Remove Unused Scope.

James Edward G. II


#8

On 4/22/06, removed_email_address@domain.invalid removed_email_address@domain.invalid wrote:

To be fair to Sean, though, the question really had to do with
clarifying how the scope of class_eval worked – so removing it kind
of defeats that particular purpose :slight_smile:

David

Thanks for leaping to my defence David but I think James is right.

Anyway, back to Pat’s question. I’ll try to be more explicit in future.

The main difference between

var = “initialized variable”
class C;end
C.class_eval { define_method(:test) { puts var} }

and

var = “initialized variable”
class D
define_method(:test) { puts var } # will fail
end

is that the closure passed to define_method in D is
within a new scope, i.e. the class definition, which means
it does not have access to the local variables in the outer scope, i.e.
var.

You can access variables local to the class definition like this:

class D
var = “initialized variable”
define_method(:test) { puts var } # will work
end

which is quite interesting in its own right.

But if you want access to the outer scope, then use the first method.
Now you know why the code on page 350 of the RoR book was written that
way!

Regards,

Sean


#9

On 4/22/06, James Edward G. II removed_email_address@domain.invalid wrote:

We might as well drop the class_eval { … }, since we are already in
the class:

James Edward G. II

Quite right! The perils of cut and paste :wink:

Sean