Extending Core Classes - Best Practices


#1

Hi, all.

I’ve been following the “Sharp knives and glue” thread. It’s gotten me
thinking about the things I’ve already been doing with Ruby. I wanted
to ask for opinions on when it is good, bad, acceptable to extend core
classes like String, Fixnum, Array, Hash.

To be a bit more concrete: Would it be okay to tack on “is?” and “has?”
type methods? Contrived example:

class String
VOGON_VOCABULARY = [
‘freddled’, ‘gruntbuggly’, ‘micturations’, ‘plurdled’,
‘gabbleblotchits’, ‘lurgid’, ‘groop’, ‘foonting’,
‘turlingdromes’, ‘hooptiously’, ‘drangle’, ‘bindlewurdles’,
‘gobberwarts’, ‘blurglecruncheon’,
]

VOGON_REGEXP = /#{VOGON_VOCABULARY.join('|')}/i

def vogon?
    ( VOGON_REGEXP =~ self ).respond_to? '>'
end

end

Pistos


#2

2006/5/5, Pistos C. removed_email_address@domain.invalid:

class String
( VOGON_REGEXP =~ self ).respond_to? ‘>’
end
end

What exactly do you intend to achieve with “.respond_to? ‘>’”?

And, btw, this sounds very specialized - it’s certainly ok as part of
a script. I wouldn’t put it into a lib though.

A simpler solution might be to just do

VOGON === “bluh”

where VOGON is your RX.

Kind regards

robert


#3

On May 5, 2006, at 4:33 PM, Robert K. wrote:

]

VOGON_REGEXP = /#{VOGON_VOCABULARY.join('|')}/i

def vogon?
    ( VOGON_REGEXP =~ self ).respond_to? '>'
end

end

What exactly do you intend to achieve with “.respond_to? ‘>’”?

I think he’s trying to make sure the result is a number and therefore
the regexp matched, rather than nil or false. Of course I don’t see
why he didn’t just do

def vogon?
VOGON_REGEXP =~ self
end

Or if he want to make sure vogon? would return either true or false:

def vogon?
!!(VOGON_REGEXP =~ self)
end

or as you mentioned ===


#4

To be safe, you might just want to subclass String instead:

#!/usr/bin/env ruby

class VWord < String
  V_VOC = %w{freddled gruntbuggly miturations} # ...
  V_RGX = Regexp.new( V_VOC.join('|'), ?i )

  def vogon?
    V_RGX === self
  end
end

word = VWord.new 'freddled'
p word.vogon?

word = VWord.new 'foobar'
p word.vogon?

Sure. It’s annoying to have to call the constructor everywhere but at
least you don’t mess up everyone who wants to use String. I agree
with Robert that it’s probably alright for short scripts, but then
again you know what happens when scripts decide to hang around longer
than you intended =)


#5

Quoting removed_email_address@domain.invalid, on Sat, May 06, 2006 at 11:01:14AM
+0900:

To be safe, you might just want to subclass String instead:

Sure. It’s annoying to have to call the constructor everywhere but at

This pattern could help in some cases, but doesn’t address a number of
common use cases in which you don’t create the string. Like when the
string came out of a library you call, or you are implementing a library
and the string was an argument.

Cheers,
Sam


#6

On 5/6/06, Sam R. removed_email_address@domain.invalid wrote:

This pattern could help in some cases, but doesn’t address a number of
common use cases in which you don’t create the string. Like when the
string came out of a library you call,

Why can’t you wrap the string after you get it?

object = SomeLibraryClass.new( #… )
wrapped = WrapperClass.new(object)

do your thing with wrapped

or you are implementing a library and the string was an argument.

module M
class C
def lib_function ( string_from_caller )
wrapped = WrapperClass.new(string_from_caller)
# again do your thing with wrapped
end
end
end

The probem I see with all that is you risk what makes people–myself
included–hate Java: you need to instanciate about 7 different
classes to write some output (or whatever).


#7

On 5/7/06, Pistos C. removed_email_address@domain.invalid wrote:

The other responses which gave “why don’t you do X instead” look like a
parent giving into the whinings of a spoiled brat, and I thought the
philosophy of Ruby was that computers and software are our slaves, not
the other way around.

Honestly, that’s a little childish, don’t you think? You supposedly
wanted opinions on the matter, and you got exactly what you asked for.
I’m not telling you how to do things, I’m simply offering
suggestions.

Computers and software are not slaves. They are tools, and you need
to handle them with care. I don’t think that ability to reopen a
class should be taken away, but at the same time I do think you need
to be careful and think about what you are doing. If you want to be
all gung-ho with the nail gun, that’s fine, but you better know your
stuff.

— EOF
foods = load_food_array
foods.each do |food|
serve food if food.tasty_food?
end
— EOF

Sure, that’s bad enough, but that’s not the real problem. The BIG
problem is not that it will mess up your client code but that it has
the potential to muck things up in the library. If you have more code
in human.rb that depends on the definition of tasty_food? it’s all
going to blow up the minute you import martian.

So far, I don’t feel convinced that it’s a bad idea to create
String#vogon?.

If you’ve thought it through, and you’re comfortable with the risks it
entails, then by all means go for it. I just hope it doesn’t come
back to bite you later.


#8

Logan C. wrote:

What exactly do you intend to achieve with “.respond_to? ‘>’”?
or as you mentioned ===

Thanks for the tips about Regexp#===, guys.

However, I didn’t mean for anyone to focus on the details of
String#vogon?, I only wanted to ask whether people considered it
acceptable practice to even create String#vogon?.

The other responses which gave “why don’t you do X instead” look like a
parent giving into the whinings of a spoiled brat, and I thought the
philosophy of Ruby was that computers and software are our slaves, not
the other way around.

This pattern could help in some cases, but doesn’t address a number of
common use cases in which you don’t create the string. Like when the
string came out of a library you call, or you are implementing a library
and the string was an argument.

This is indeed one of the problems with not extending String itself.

Are we phobic to change core classes because one file/app/library might
expect the method to behave one way, while another would expect
something else?

— human.rb
class String
def tasty_food?
/pizza|sushi|curry|steak/ === self
end
end
— EOF
— martian.rb
class String
def tasty_food?
/roaches|slime mold|pond scum|worms/ === self
end
end
— EOF
— intergalactic-dinner-party.rb
require ‘human’
require ‘martian’
foods = load_food_array
foods.each do |food|
serve food if food.tasty_food?
end
— EOF

So far, I don’t feel convinced that it’s a bad idea to create
String#vogon?.

Pistos


#9

Lou S. wrote:

Honestly, that’s a little childish, don’t you think? You supposedly
wanted opinions on the matter, and you got exactly what you asked for.
I’m not telling you how to do things, I’m simply offering
suggestions.

If there are sensible restrictions that exist with good reason, I am
willing to “play inside the fenced area”. I just want to make sure the
fences have good reason to be there, to be the size and shape that they
are.

Sure, that’s bad enough, but that’s not the real problem. The BIG
problem is not that it will mess up your client code but that it has
the potential to muck things up in the library. If you have more code
in human.rb that depends on the definition of tasty_food? it’s all
going to blow up the minute you import martian.

Right, so is this the sole problem? People coding app X assuming
CoreClass Y behaves a certain way, and then seeing that importing lib Z
causes borkage in X (due to a change of Y)?

Is it ever safe to assume that Y#is_x? is so obscure or domain-specific
that nobody would ever use it outside one’s own application, and hence
it is okay to extend the core class with?

— lemming-army-application.rb
class String
def suitable_name_for_lemming_drill_sergeant?
# …
end
end
— EOF

So far, I don’t feel convinced that it’s a bad idea to create
String#vogon?.
If you’ve thought it through, and you’re comfortable with the risks it
entails, then by all means go for it. I just hope it doesn’t come
back to bite you later.

I’m in the process of thinking it through, hoping to get some thoughts
from others. I don’t know for sure yet either way, whether it’s good or
bad. If it’s bad, I want to stop doing it. If it’s good, then I can
feel less trepidation whenever I do do it.

Pistos


#10

On 5/7/06, Pistos C. removed_email_address@domain.invalid wrote:

Right, so is this the sole problem? People coding app X assuming
CoreClass Y behaves a certain way, and then seeing that importing lib Z
causes borkage in X (due to a change of Y)?

Yes, I think that’s probably the biggest problem. The other main
issue is the one you pointed out in the human/martian example. I
believe that messing up the library is a worse situation, because the
library isn’t in as good of a position as your code in terms of
knowing when things have been altered. In your client code, you are
explicitly doing a require, so hypothetically you are aware of the
consequences of importing the second library.

Is it ever safe to assume that Y#is_x? is so obscure or domain-specific
that nobody would ever use it outside one’s own application, and hence
it is okay to extend the core class with?

I agree with you that you would probably be safe in 99.8% of cases,
but sometimes you are better off safe than sorry. Ruby is flexible
enough to let you go either way, and that’s a good thing. If you are
confident go for it and modify the class; if not code more
defensively.

I’m in the process of thinking it through, hoping to get some thoughts
from others. I don’t know for sure yet either way, whether it’s good or
bad. If it’s bad, I want to stop doing it. If it’s good, then I can
feel less trepidation whenever I do do it.

When it comes down to it, you probably need to evaluate the decision
on a case by case basis. On a short script I might go ahead and open
String back up, but in a larger application, I’d tend to be more
careful.

Perhaps another thing to consider is on a more logical level. If the
method is not something that any string should be able to do, you’ve
probably put it into the wrong class.

Good luck either way you decide, and write lots of tests =)


#11

Lou S. wrote:

I agree with you that you would probably be safe in 99.8% of cases,
but sometimes you are better off safe than sorry. Ruby is flexible
enough to let you go either way, and that’s a good thing. If you are
confident go for it and modify the class; if not code more
defensively.

Hm, okay. I think I’ll keep living a bit dangerously in this regard,
then. :slight_smile: I would tread much more carefully, though, if I were naming
things that people might possibly use or assume in other places.

Perhaps another thing to consider is on a more logical level. If the
method is not something that any string should be able to do, you’ve
probably put it into the wrong class.

Well, if I’m interested if a string is #vogon? or not, I can’t see why I
would try to segregate strings into two groups.

The only other alternative that comes to mind is writing un-OO stuff
like:

class SomeApplication
def is_vogon?( str )
VOGON_REGEXP === str
end
end

Which just feels unRubylike to me.

Good luck either way you decide, and write lots of tests =)

Thanks. Your input has been helpful, and I appreciate you taking the
time to share it.

Pistos


#12

On 5/7/06, Pistos C. removed_email_address@domain.invalid wrote:

Perhaps another thing to consider is on a more logical level. If the
method is not something that any string should be able to do, you’ve
probably put it into the wrong class.

Well, if I’m interested if a string is #vogon? or not, I can’t see why I
would try to segregate strings into two groups.

I agree. I think it is a reasonable expectation for a string to tell
you if it is_vogon? Let’s say you wanted to inflect a word based on
vogon grammar rules. That would be a method that only vogon words
should respond to, so at that point I think you’d want a subclass.

It all depends on your intentions and on your design decisions.


#13

Pistos C. wrote:

Lou S. wrote:

Perhaps another thing to consider is on a more logical level. If the
method is not something that any string should be able to do, you’ve
probably put it into the wrong class.

Well, if I’m interested if a string is #vogon? or not, I can’t see why I
would try to segregate strings into two groups.

The only other alternative that comes to mind is writing un-OO stuff
like:

class SomeApplication
def is_vogon?( str )
VOGON_REGEXP === str
end
end

Which just feels unRubylike to me.

Just 2 cents here…

I think what Lou was suggesting is that normally, something like a Vogon
module might be a better place for the is_vogon? method…

module Vogon
def is_vogon?( str )
VOGON_REGEXP === str
end

module_function :is_vogon?
end
.
.
.
Vogon.is_vogon?( “bleem” )