From: David Alan Black Date: 2001-08-31T06:59:13+09:00 Subject: [ruby-talk:20591] Re: Weird things with eval 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