Hello --

The part I don't get, as a general matter, is why #send gets to bypass
the access rules.  Of course #send itself is a public method.  The
weird part is that if it sends the name of a private method, that
method acts public.

For example, #eval:

  irb(main):001:0> [].eval("puts 'hi'")
  NoMethodError: private method `eval' called for []:Array
	  from (irb):1
  irb(main):002:0> [].send(:eval,"puts 'hi'")
  hi

And that particular example, of course, relates closely to yours.
Anyway, with that in the background, here are some further comments.

For one thing, keep in mind that Class.send(:eval, str) is not the
same as Class.class_eval str.

  class Foo; end
  Foo.class_eval "def bar; puts 'bar'; end"
  Foo.new.bar  # => bar

  class Foo; end
  Foo.send(:eval, "def thing; end")
  [].thing
  -:3: private method `thing' called for []:Array (NoMethodError)

Note that the #eval in the second one executed at the top level, so
that every object (such as []) now has a private method 'thing'.
(That's what happens with top-level method definitions.)

So...

When you do this:

>   class Module
>     def foo
>       p self
>       Foo.__send__(:eval, "def bar; puts 'bar'; end")
>     end
>   end

and then call #foo, you're calling Kernel#eval in the context of class
Module.  The fact that Foo is the receiver doesn't actually matter.
(At least not as long as the 'send' trick works....)

So you could actually do this, and get, I think, exactly the same
result, visibly and internally:

  class Module
    def foo
      p self
      "some random object".send(:eval, "def bar; puts 'bar'; end")
    end
  end

or this:

  class Module
    def foo
      p self
      eval "def bar; puts 'bar'; end
    end
  end

or just forget about Foo and foo and do:

  class Module
    def bar
      puts 'bar'
    end
  end

which is really what you've ended up doing, at least in net effect.

And when you do this, at the top level:

>   class Foo
>   end
>
>   Foo.__send__(:eval, "def bar; puts 'bar'; end")

you could just as well replace the whole thing with:

  def bar; puts 'bar'; end

The status of Foo as receiver for #eval is irrelevant; you're eval'ing
at the top level.  Try adding these to your tests, along with Module
and f.bar:

  [].bar
  Hash.new("hi").bar
  123.bar

As you'll see, you given *every* object a private method 'bar',
because you've defined a top-level method 'bar'.  The whole Foo thing
is misleading.


On Fri, 31 Aug 2001, Paul Brannan wrote:

> So the questions are:
>
> 1) Why in (A) does bar get defined for Module but not for Foo?
> 2) Why in (B) does bar get defined for both Module and Foo, and why is it
> private?
> 3) Why in (D) does Ruby allow me to modify a frozen class, and why is the
> resulting method private?


I haven't organized my comments as well as you organized the question,
but I hope they help :-)


David

-- 
David Alan Black
home: dblack / candle.superlink.net
work: blackdav / shu.edu
Web:  http://pirate.shu.edu/~blackdav