From: Mark Hubbart Date: 2005-05-11T03:54:59+09:00 Subject: Re: a class that looks like (kind_of?) several classes On 5/10/05, Eric Mahurin wrote: > > > > > Is there a way to make a class that looks like several > > > > classes? > > > > > For example: > > > > > > > > > > class IOArray < IO > > > > > include Array # doesn't work because Array is not a > > > > Module > > > > > # use IO methods calls to accomplish the Array > > methods > > > > > end > > > > > > > > > > I don't need multiple inheritance. All I want is these > > > > > additional classes to appear in the ancestors list. In > > the > > > > > above example, IOArray could act like an IO or an > > Array. > > > > It > > > > > looks like evil.rb might have a solution, but is there > > a > > > > > non-evil way? > > > > > > > > class IOArray < IO > > > > def kind_of?(cl) > > > > Array == cl || super > > > > end > > > > end > > > > > > Yep, that is pretty obvious. I think that is all I need, > > but I > > > guess overriding ancestors would complete this. > > > > > > > >> x=IOArray.allocate > > > > => # > > > > >> x.kind_of? Array > > > > => true > > > > > > > > But seriously, for what do you need that? I can't think > > of > > > > an application > > > > where I would want to make something look like an Array > > > > without being one. > > > > Maybe it's rather Enumerable that you want? > > > > > > Enumerable doesn't have random access and can't modify like > > > Array (or String). I think it would have been nice to have > > a > > > module like "Indexable" that would cover common methods in > > > Array and String. > > > > > > The example I have above should probably be combining an IO > > and > > > a String since an IO accesses characters like a String: > > > > > > class IOString < IO > > > def kind_of?(c); String==c || super; end > > > # use IO methods (seek/tell/read/write) to accomplish > > > # the String methods > > > end > > > > This is generally unneeded in Ruby; If duck typing is used > > properly, > > the ancestors and kind_of? shouldn't matter. And if it *does* > > matter, > > then masquerading like this could cause problems: > > > > if obj.kind_of? String > > var.replace obj > > end > > > > This code would break if obj is an IOString as defined above, > > even > > though this code has no logic errors. > > > > If you check out the StringIO class (require 'stringio'), you > > will > > find that it inherits neither from IO nor String. But it > > still works > > very nicely :) > > Thanks. I assumed that the StringIO class inherited from the > IO class since it has all of the same methods. So the proper > Ruby style is to use respond_to? instead of kind_of? to see > what "type" your arguments are? The usual Ruby way is to use Duck Typing. As I see it: - If you are accepting different kinds of objects in the same method, use respond_to? to determine how to use them. - If you only expect one kind of argument, don't even bother checking. So: # takes a file or a filename def read_data(file) if file.respond_to? :read file.read else File.open(file){|f| f.read} end end # takes an string def show_in_quotes(str) puts '"' + str + '"' end > You still should be able to make the above "replace" code work > above. For the IOString class I described, you'd just replace > the contents of the file with the string passed. seek(0); > truncate(write(value)). That would seem to accomplish the > replace function. Add that to a to_str method, and you'll have it. My example was flawed; the Ruby interpreter won't decide that it's a String anyway. But I still think that it could cause problems... cheers, Mark