Eval, module_eval, define_method, and all that

I don't know how Ruby scoping works when the code involves:

    nested "def"s
    eval
    module_eval
    instance_eval
    define_method

It would be nice to find an app-note for this issue. (More generally,
it would be nice to see written down how 'self' and 'klass' are used in
the Ruby execution model.)

It would also be nice to see a history of these meta-programming
constructs. (I say "meta-programming", since we're talking about code
that executes code.) I wouldn't be surprised if once upon a time Ruby
had only nested "def"s and "eval"s. However, I don't know how to write
my own version of, say, "attr_reader" using just those two. For
example, this doesn't work:

  def my_attr_reader name
    eval <<-STOP
    def #{name}
      @#{name}
    end
    STOP
  end

I wonder if there is a way. Moving on, the following all work:

  def my_attr_reader name
    eval <<-STOP
    define_method :#{name} do
      p C # IGNORE FOR NOW
      @#{name}
    end
    STOP
  end

  def my_attr_reader name
    module_eval <<-STOP
    def #{name}
      p C # IGNORE FOR NOW
      @#{name}
    end
    STOP
  end

  def my_attr_reader name
    module_eval <<-STOP
    define_method :#{name} do
      p C # IGNORE FOR NOW
      @#{name}
    end
    STOP
  end

  def my_attr_reader name # MY FAVORITE, FYI
    define_method name do
      p C # IGNORE FOR NOW
      instance_variable_get "@"+name.to_s
    end
  end

Alas, There's More Than One Way To Do It. Now let's exercise those
"p C" statements:

module M
  C = "M"
  # INSERT ONE OF THE my_attr_reader DEFINITIONS HERE
end
class X
  C = "X"
  class <<X ; include M ; end
  attr_writer :a
  my_attr_reader :a
end
x = X.new
x.a = 4
x.a -> prints ???

With the four examples above, the printed value of C is:

    "M" "X" "X" "M"

My head is swimming. Ideally, I'll find my app-note, which will clear
my mind as marvelously as "How Classes and Objects Interact" did in the
Pickaxe book.

PS: "@"+name.to_s is gross. Why doesn't "instance_variable_get" accept
an *implicit* "@"?

···

--
Posted via http://www.ruby-forum.com/.

I wrote above that my head was swimming. Not enough, evidentally:

If I want my_attr_reader to handle multiple attributes, this works:

    def my_attr_reader *names
      names.each do |name| # THIS IS THE ONLY LINE THAT DIFFERS
        define_method name do
          instance_variable_get "@"+name.to_s
        end
      end
    end

But this doesn't:

    def my_attr_reader *names
      for name in names # THIS IS THE ONLY LINE THAT DIFFERS
        define_method name do
          instance_variable_get "@"+name.to_s
        end
      end
    end

Ouch.

···

--
Posted via http://www.ruby-forum.com/.

My interest (obsession?) with methods defining methods partly derives
from the fact Ruby got a huge boost (via Rails) because of it.
Unfortunately, Ruby requires *three* special behaviors to make it work,
and these behaviors are provided nonorthogonally. The following fails
for three reasons. (Please forgive how silly it looks at this point.)

class A
  class <<A
    def my_attr_reader var # 'var' will be bound to :foo.
      def var
        "@"+var.to_s
      end
    end
  end
  my_attr_reader :foo
  attr_writer :foo
end

1. In "def foo ; @foo ; end", "foo" and "@foo" are syntax literals.
Neither can be replaced with an expression.

2. The inner "def" is installed in <<A rather than in A.

3. Ruby tends to scope lexically rather than dynamically. "@foo" is
likely to be scoped as an instance variable of the A class rather than
of A instances. Similarly, a referenced constant is likely to be looked
up in <<A rather than A.

Problem #1 can be solved either by evaluating strings or by using
"define_method", "instance_variable_get", and such.

Problem #2 can be solved using "module_eval" and/or "define_method".

Problem #3 is solved (I think) by a combination of "module_eval" and
string evaluation.

"module_eval" with a string argument seems like the best technique from
an ease-of-use point of view. But string evaluation rubs some people
the wrong way. On the other hand, "module_eval" (or "define_method")
with a block argument scopes constants lexically and instance variables
dynamically:

CONST = "Object"

$block = proc do
  def get_stuff
    [@foo, CONST, self]
  end
end

class A
  CONST = "A"
  class <<A
    CONST = "<<A"
    def def_get_stuff
      module_eval &$block
    end
  end
  attr_accessor :foo
  def_get_stuff
end

a = A.new
a.foo = 22
p a.get_stuff -> [22, "Object", #<A:0x401c2448 @foo=22>]

I don't know if this result is intrinsically messy or if I'm looking at
it from the wrong angle.

···

--
Posted via http://www.ruby-forum.com/.

In this forum, I've mentioned several times that some people don't like
"eval". Here is the end of section 11.3.5 from "The Ruby Way":

  In previous versions of Ruby, we often defined methods at runtime by
  calling "eval". In essence, "define_method" can and should be used in
  all these circumstances.

"ALL these circumstances". That's strong stuff. Well, I'm ready to bow
to authority. But how can I see "define_method" as loveable? How about
this: "define_method" is Minimally Magical.

Here's what I mean. Given the name "define_method", we naturally
expect "foo" and "bar" below to have the same behavior:

CONST = "Object"

class A
  CONST = "A"
  @val = "A"
  attr_accessor :val
  def foo
    [CONST, @val, self]
  end
  define_method :bar do
    [CONST, @val, self]
  end
end

a = A.new
a.val = "a"
p a.foo -> ["A", "a", #<A:0x401c24fc @val="a">]
p a.bar -> ["A", "a", #<A:0x401c24fc @val="a">]

If "define_method" was not magical, then, within its block argument, the
values of CONST and @val and self would be "A" and "A" and A (the class
object). The minimal magic required to get the desired answer is to
make @val and self refer to the A instance, but leave CONST to scope
lexically. And that seems to be what "define_method" does.

I personally will follow "The Ruby Way" (second edition). Stand behind
me, *eval methods!

···

--
Posted via http://www.ruby-forum.com/.