Class_eval used inline vs within class definition

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@(irb):3>
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@(irb):6>
irb(main):008:0> A.new.talk
NameError: undefined local variable or method `var' for #<A:0x337c30>
        from (irb):6:in `talk'
        from (irb):8

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

···

from :0

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

···

On 4/22/06, Pat Maddox <pergesu@gmail.com> wrote:

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

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

···

On 4/22/06, Sean O'Halpin <sean.ohalpin@gmail.com> wrote:

On 4/22/06, Pat Maddox <pergesu@gmail.com> 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

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

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@(irb):3>
>> A.new.talk
initialized variable
=> nil

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

James Edward Gray II

···

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

On 4/22/06, Pat Maddox <pergesu@gmail.com> wrote:

Hi --

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

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@(irb):3>
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

···

On Sat, 22 Apr 2006, Pat Maddox wrote:

On 4/22/06, Sean O'Halpin <sean.ohalpin@gmail.com> wrote:

On 4/22/06, Pat Maddox <pergesu@gmail.com> wrote:

--
David A. Black (dblack@wobblini.net)
Ruby Power and Light, LLC (http://www.rubypowerandlight.com)

"Ruby for Rails" PDF now on sale! Ruby for Rails
Paper version coming in early May!

Hi --

···

On Sat, 22 Apr 2006, James Edward Gray II wrote:

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

On 4/22/06, Pat Maddox <pergesu@gmail.com> 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

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@(irb):3>

A.new.talk

initialized variable
=> nil

I believe this to be an example of Dave Burt'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 (dblack@wobblini.net)
Ruby Power and Light, LLC (http://www.rubypowerandlight.com)

"Ruby for Rails" PDF now on sale! Ruby for Rails
Paper version coming in early May!

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

Sean

···

On 4/22/06, James Edward Gray II <james@grayproductions.net> wrote:

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

James Edward Gray II

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

···

On 4/22/06, dblack@wobblini.net <dblack@wobblini.net> wrote:

Hi --

On Sat, 22 Apr 2006, Pat Maddox wrote:

> On 4/22/06, Sean O'Halpin <sean.ohalpin@gmail.com> wrote:
>> On 4/22/06, Pat Maddox <pergesu@gmail.com> 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
>>
>>
>
> 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@(irb):3>
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 (dblack@wobblini.net)
Ruby Power and Light, LLC (http://www.rubypowerandlight.com)

"Ruby for Rails" PDF now on sale! Ruby for Rails
Paper version coming in early May!

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

···

On 4/22/06, dblack@wobblini.net <dblack@wobblini.net> 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