Extending Code Cleanly

(Molitor, Stephen L) #1

I'd add that modifying is worse than extending. If a library adds a
method to an existing class, that's one thing. Sometimes it can be
nice -- 1.day.ago is pretty cool! Does Rails use that stuff internally,
or do you only get it if you ask for it? If you can't turn it off the
main risk to adding methods is name collisions; someone else could add
the same 'day' method to Number. Wasn't there a namespace feature of
some sort proposed for Ruby 2 that would allow you to localize
extensions? That would help.

But swapping out method or constant definitions in standard libraries
sounds really evil. If I read correctly, that was the real problem in
the 'Chainsaw' message. (Great post.) Unless there's a name conflict
with something else I use, I can live with something adding a method to
a standard class. If I don't like that method I just won't use it. But
if it changes the behavior of existing methods without even telling me,
well that's just diabolical.



-----Original Message-----
From: Austin Ziegler [mailto:halostatue@gmail.com]
Sent: Wednesday, August 24, 2005 3:31 PM
To: ruby-talk ML
Subject: Extending Code Cleanly

[NOTE: This is an offshoot of Zed Shaw's rant on Chainsaw Infanticide.]

On 8/24/05, Jim Weirich <jim@weirichhouse.org> wrote:

Austin Ziegler said:

On 8/24/05, Jeff Wood <jeff.darklight@gmail.com> wrote:

You shouldn't be afraid of having power. That's why you have tests.
You do have tests, right? ... right??? ... RIGHT !!?!?!?!??!

I think the real problem is when this is done in released code. If
you're going to extend code, extend it cleanly -- IMO.

Well said.

It is probably worth having a public discussion on the meaning of
"extend cleanly".

Aye, so I'll start. If you're going to extend core or standard library
classes, you should:

1. Do so only at the user's request. Diff::LCS *can* extend String and
   Array but does not do so by default. If you are going to extend by
   default, then you must document it in a very loud tone of voice, as
   it were.

2. Don't depend on extensions to the core or standard library classes if
   you're working on a *library* of code for others to use. Subclass,
   extend (with a module), or delegate if you absolutely must. The
   predecessor to Diff::LCS (Algorithm::Diff) added #map_with_index or
   something similar to Array and depended on it. I don't think that
   Diff::LCS does that.

   Applications and application frameworks may have exemptions. This
   sort of allows for 1.day.ago notation as in Rails.

3. If you absolutely must extend the core and depend on it in a library,
   try to use names that don't interfere with others.
   Transaction::Simple follows #1, but it also follows this.

Austin Ziegler * halostatue@gmail.com
               * Alternate: austin@halostatue.ca