Mark Hubbart
5/10/2005 6:55:00 PM
On 5/10/05, Eric Mahurin <eric_mahurin@yahoo.com> 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
> > > > => #<IOArray:0x10184258>
> > > > >> 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