#send and private methods

B

Brian Palmer

I apologize if this has been discussed before and I missed it...

What's the rationale for having #send bypass the private status of a
method? For instance:

irb(main):001:0> b = Object.new
=> #<Object:0x2ae06d8>
irb(main):002:0> b.gets
NoMethodError: private method `gets' called for #<Object:0x2ae06d8>
from (irb):2
irb(main):003:0> b.send("gets")
no!
=> "no!\n"
irb(main):004:0>

In my mind, #send should honor the visibility of a method and only allow
calls to private methods if called like

self.send("gets")

But maybe there's a good reason for this behavior that I'm simply missing.

- Brian Palmer
 
D

David Heinemeier Hansson

What's the rationale for having #send bypass the private status of a

That it's endlessly useful to be able to mindfully sidestep the rules
when the pressures are right. I use this to much delight in various
places in Rails where I'm well aware of the fact that the call is to a
private, but I choose to anyway.

To me, that's the essence of Ruby. Guidance, not constraints. Make it
ugly instead of impossible.

Three cheers for a send that sidesteps visibility ;)!
--
David Heinemeier Hansson,
http://www.basecamphq.com/ -- Web-based Project Management
http://www.rubyonrails.org/ -- Web-application framework for Ruby
http://macromates.com/ -- TextMate: Code and markup editor (OS X)
http://www.loudthinking.com/ -- Broadcasting Brain
 
J

James Britt

Brian said:
In my mind, #send should honor the visibility of a method and only allow
calls to private methods if called like

self.send("gets")

But maybe there's a good reason for this behavior that I'm simply missing.

Off the top of my head:

Ruby is (almost) pure OO if that is what you want. And it behaves that
way so long as you follow the OO conventions. And most Ruby developers do.

However, it provides numerous hooks and inlets such that you can pretty
much do as you like; the proverbial enough rope to shoot yourself in the
foot.

Why might this be? I would posit that programming languages exist to
aid the developer, not the computer, and so should be enabling, not
prohibitive.

OOP|A|D is/are a set of design and development guidelines, not the Ten
Commandments, so a language should not dictate, merely encourage. (Maybe
even strongly encourage.)

Any reasonably powerful language provides the wherewithal to write
difficult, ugly, unreadable, unmaintainable, dangerous code. What stops
most people from doing so is typically not the constraints of a
language, but common sense and self-preservation.

Using something like 'send' on private methods is similar to off-road
driving. You can deliberately choose to step outside the boundaries, but
you have to be responsible for your actions.

Ruby treats you like an adult.


James
 
G

Glenn Parker

Marc said:
In other words, information hiding is a way of making software systems
resilient in the face of change. It is not simply a way "nanny"
programming languages try to keep "top-gun" programmers from getting their
job done.

Except that, if a private function is irrevocably private, it actually
*does* occasionally keep top-gun programmers from getting their job
done. Information hiding is a good thing, but absolute enforcement of
information hiding is not a good thing, at least in Ruby. This probably
precludes Ruby from certain applications.

The normal Ruby syntax enforces information hiding as you would expect,
while using "send" is ugly and it stands out clearly. People that use
"send" to subvert information hiding are fully aware that they are
breaking the rules and introducing an ugly dependency on a hidden API
into their system. Sometimes that is the right tradeoff for a good
engineer to make.

Why should the creator of the "private" API be the one to decide, once
and for all, that this is unacceptable? The creator cannot anticipate
every situation that will occur in the future, they can only designate
areas of relative instability using "private" and "protected".
 
J

James Britt

Marc said:
...
In other words, information hiding is a way of making software systems
resilient in the face of change. It is not simply a way "nanny"
programming languages try to keep "top-gun" programmers from getting their
job done. Of course, for small projects with a restricted user base these
constraints hardly matter. If Ruby were intended to be only a prototyping
tool, then bypassing access controls would be no big deal. But Ruby
apparently is on the way to being adopted as a bona fide
progamming-language-in-the-large, as evidenced by such trailblazing
projects as the Rails framework. (I certainly hope so!) Consequently I
think that the adherence to a rigorous informtation hiding schema should be
taken seriously and mechanisms to bypass it should be brought into
question.

I agree; any mechanism for by-passing orderly program control should be
viewed with suspicion. But such things need to be there, as the
programmer must always be the ultimate arbiter.

Every modern language has such mechanisms; they are either explicit or
buried, but they are there. Better they be made clear and well
documented so all involved can make informed choices.

Even if strict data hiding was enforced there would be countless other
ways to write poor code; I (sadly) have too much have proof of this.
The end-user must always rely on the judgment of the developer, in any
language.

I haven't looked at all that much of the Rails source code, so I don't
know the they answer, but here's something to ponder: Were strict data
+ method access enforced, would Rails be (as) feasible?

Further, C is certainly well accepted as a mainstream language, and it
allows for far more easier tomfoolery than does Ruby.

I don't view OO and data hiding as a way to hamstring advanced
developers, but I do believe some languages gain traction precisely
because their built-in constraints serve as an effective form of mob
control for projects involving large numbers of inexperienced (and less
expensive) developers who might otherwise run amok.

I believe, too, that it can be argued that these constraints lead to
overly complex code that ages poorly compared to applications built
using agile languages. While some might have a warm fuzzy feeling
during production, you end up with code less resilient to change.


James

P.S.

I suspect concerns over malleable object boundaries are nearly identical
to concerns over dynamic typing. Proper use of unit testing will do a
lot more for ensuring robust code than either strict encapsulation or
typing.
 
B

Brian Palmer

I certainly see what you mean--I can think of cases where it would be
useful to bypass the private status of a method, as well. I was curious
how others felt, though. The reason I ran across the #send behavior is
this--I've been toying with different ways of writing a Infocom-style
text adventure interpreter in Ruby, and one idea I had was a 'command'
object or module that would contain a bunch of methods named after
verbs, such as 'look' and 'west'. Then I would just have the
interpreter do a #send with whatever the user typed in, and let
#method_missing handle the "I don't understand what you mean" type
messages. I was just a bit shocked when I discovered that typing 'gets'
into my interpreter actually called the private method 'gets' (and I
started thinking about what the adventurous adventurer could do with a
well-designed #instance_eval command :)

But of course, this is easily avoided by checking whether #send would
call a public method, or using the awesome "evil ruby" to make a class
without Object and Kernel methods. Not to mention that, while an
interesting idea, it's not necessarily the most elegant way to do things
anyway.

Thanks for the feedback!

- Brian Palmer
 
E

Eric Hodel

--Apple-Mail-25--614053791
Content-Transfer-Encoding: 7bit
Content-Type: text/plain; charset=US-ASCII; format=flowed


Consequently I
think that the adherence to a rigorous informtation hiding schema
should be
taken seriously and mechanisms to bypass it should be brought into
question.

Ruby is too dynamic to do this:

$ ruby
class X; private; def a; puts 5; end; end
i = X.new
begin i.a; rescue Exception => e then puts e.class end
class X; public :a; end
i.a
NoMethodError
5

But then, there are ways to exploit it to not allow these things to
happen, like redefining send and __send__ and method and running with
$SAFE = 4

--
Eric Hodel - (e-mail address removed) - http://segment7.net
FEC2 57F1 D465 EB15 5D6E 7C11 332A 551C 796C 9F04

--Apple-Mail-25--614053791
content-type: application/pgp-signature; x-mac-type=70674453;
name=PGP.sig
content-description: This is a digitally signed message part
content-disposition: inline; filename=PGP.sig
content-transfer-encoding: 7bit

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.4 (Darwin)

iD8DBQFB07m5MypVHHlsnwQRArg9AKDuPU+CaZqxtOwtMlBtJLglk7puNgCg4TnL
aT+d5spTxzk0VD2sAeSSbUE=
=xjJX
-----END PGP SIGNATURE-----

--Apple-Mail-25--614053791--
 
M

Michael Neumann

Marc said:
[...]
In other words, information hiding is a way of making software systems
resilient in the face of change. It is not simply a way "nanny"
programming languages try to keep "top-gun" programmers from getting their
job done. Of course, for small projects with a restricted user base these
constraints hardly matter. If Ruby were intended to be only a prototyping
tool, then bypassing access controls would be no big deal. But Ruby
apparently is on the way to being adopted as a bona fide
progamming-language-in-the-large, as evidenced by such trailblazing
projects as the Rails framework. (I certainly hope so!) Consequently I
think that the adherence to a rigorous informtation hiding schema should be
taken seriously and mechanisms to bypass it should be brought into
question.

I think #send should not be able to call private methods (from a proper
OO perspective, not neccessarily mine ;-), but it should be able to call
protected methods. Nevertheless, I think deciding whether a method
should be declared as protected or private is not always easy, and I
would not matter if there were only "protected" methods in Ruby (which
means that they have to be called with "self" as receiver, regardless of
the class they were defined). Of course there are for sure many
situations where you'd want to have private methods.


What you want is probably this:

class Object
def send(id, *args, &block)
if private_methods.include?(id.to_s)
raise "private method `#{ id } called for #{ self }"
end
__send__(id, *args, &block)
end
end

That's the good thing with Ruby. Without the current behaviour of
__send__ (or send), you could not implement what currently is possible
(call private methods).

Regards,

Michael
 
T

trans. (T. Onoma)

It goes both ways, you need a solid foundation to build on, agreed, but you
also need flexibility across the board. What separates private? Is it a
road-block or a sign-post? What's a sign-post going to do? Well, it will
_deter_. If that is a goal then perhaps a 'deter' directive is in order. On
the otherhand, if there are things necessarily private, does it indicate a
flaw in design? Is it rightful for Classes to have local namespace?

Or you could just say screw it and just reduce them to what they are, a Scope.
A Scope is just a callable container, a scope can be public, private,
protected, or deterred (and passive vs. active, see aop). Any one up for a

public class String
end

%String{This is a new string}

or,

String("This is a new string")

or Classicaly,

String.new("This is a new string")

I believe it is things like this that Rails is teaching, but David has better
words... http://rubyonrails.org

T.

P.S.
Hmm... I wonder how fast Ruby method lookup would be if it was in a Database.
Then I could access through a marginally distinct map of nomenclature.
 
G

Glenn Parker

Brian said:
Then I would just have the
interpreter do a #send with whatever the user typed in, and let
#method_missing handle the "I don't understand what you mean" type
messages.

I expect this would be a fairly easy system to compromise. Think of all
the cross-scripting hacks that web developers have to guard against
today, hence the tainted concept. If you really trust your users, or if
you don't care what happens to your computer, then OK. Otherwise, you
should maintain strict isolation between user input and executable code.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,981
Messages
2,570,187
Members
46,731
Latest member
MarcyGipso

Latest Threads

Top