From: Brian Candler Date: 2004-10-08T04:54:09+09:00 Subject: Re: Extending existing classes On Thu, Oct 07, 2004 at 04:13:19AM +0900, Mark Hubbart wrote: > What I was getting at here, is that #rot13 could be reasonably applied > to any text; and that is the primary use of strings in ruby: a text > container. Whereas, #remove_html_attributes would only be used on > special types of text: html documents/snippets. In this case, I would > subclass; in the rot13 case, I would extend. But that's just me, you > might feel differently. :) There are a couple of caveats to extending or subclassing built-in classes though: (1) There is no way to 'coerce' an object of class X into an instance of class Y, where Y is a subclass of X. So subclassing built-in types relies on the existence of a constructor which takes an instance of an existing object. e.g. class HTMLString < String ... end foo = HTMLString.new("hello") In this case, HTMLString#initialize(str) can call String#initialize(str) - implicitly in this case, or explicitly using 'super'. Or it could use the 'replace' method: foo = HTMLString.new foo.class # => HTMLString foo.replace("abc") foo.class # => HTMLString But this makes me a bit uncomfortable because no class is *required* to have an initializer which takes an existing instance of the same class, nor a 'replace' method. And when you're talking about a built-in class, the value is not accessible via an instance variable. So, in theory at least, this approach might not work at all. Consider subclassing Range, for example; you have to be careful to pass the right bits to the superclass constructor, if you want to allow an existing Range or MyRange object to be passed in. class MyRange < Range def initialize(r) super(r.begin, r.end, r.exclude_end?) end end (2) If you're running multiple applications within a shared interpreter, such as mod_ruby, then clearly you don't want these applications to step on each others' toes, so leaving the core classes alone is usually a good principle. I personally prefer two alternative strategies: (a) using the singleton class of objects module HTMLString def flatten gsub(/<[^>]*>/,' ') end end a = "hello world" a.extend HTMLString a.flatten # => " hello world " Now string 'a' has this method, but we've not polluted String. This is the nearest to coercion that I can find in Ruby; don't change the actual class, but mix in something else. (b) using delegation ("has_a") rather than inheritance ("is_a"). It's often a hands-down win in terms of flexibility and clarity anyway. For a HTMLString object, I would write something like class HTMLString def initialize(str) @str = str end def flatten @str.gsub(/<[^>]*>/,' ') end end Working with an instance variable gives more control over which methods you wish to delegate, which you wish to hide (e.g. if they make no sense for this new class), and which to make behave differently; and you can easily compose objects out of two or more other objects this way. Regards, Brian.