Subclassing Array


#1

I’m sure I’m just being an idiot here… my mind is a little foggy this
morning, but I’m having a hard time understanding how to accomplish
this. I’ve written a class (I’ll just put some snippets in for
understanding) in which I’d like to be able to use the following
behavior:

irb> columns = [[1, “Hostname”], [2, “Model”], [5, “OS Version”]]
irb> report = InventoryReport.new(“HARDWARE_QUERY”, columns)
irb> report.query.map {|a| a.first.downcase!; a}.save “/tmp/test.xls”

Where query() returns an array like [[“host1”, “Dell”, “Windows”],
[“host2”, “Hitachi”, “Windows”], …]

However, throwing a map (or select or whatever) in there (obviously)
ends up returning an Array. How would I handle this so that it works as
I would like? Do I need to move my save(), to_csv(), and to_xls() stuff
into the Array class?

Sorry if this is a dumb question.

class InventoryReport < Array
def initialize(query_name, fields)
@query_name = query_name
@fields = fields
super()
end

def query
    cols = @fields.map {|a| a.first - 1}
    %x{runquery #{@query_name}}.each_line do |l|
       t = l.chomp.split
       tmp = []
       cols.each do |a|
           tmp << t[a].strip
       end
       self << tmp
    end
    self
end

def save(filename)
    case filename
        when /.*\.csv/ then to_csv(filename)
        when /.*\.xls/ then
            to_csv(filename.gsub(/xls$/, "csv"))
            to_xls(filename, filename.gsub(/xls$/, "csv")
     end
end

def to_csv(...)
end

def to_xls(...)
end

end


#2

El Gato wrote:

I’m sure I’m just being an idiot here… my mind is a little foggy this
morning, but I’m having a hard time understanding how to accomplish
this. I’ve written a class (I’ll just put some snippets in for
understanding) in which I’d like to be able to use the following
behavior:

El Gato,

My suggestion is to take a good look at Ruby’s incredibly powerfull use
of delegates
http://www.ruby-doc.org/stdlib/libdoc/delegate/rdoc/index.html
(documentation provided by James E. Gray II)

Secondly, there is a great book entitled “Ruby Recipes”, which discusses
your question in great detail and I higly recommend that you pick it
up…

Hope this helps and this is only a suggestion as there exists 100 ways
to do what you are asking… :slight_smile:

ilan


#3

On Feb 26, 2007, at 9:58 AM, Ilan B. wrote:

(documentation provided by James E. Gray II)

I have become a tea.

James Edward Gray II


#4

James G. wrote:

On Feb 26, 2007, at 9:58 AM, Ilan B. wrote:

(documentation provided by James E. Gray II)

I have become a tea.

James Edward Gray II

Ouch!!! Apologies… Not enough coffee this morning and guess what my
wife’s favourite is… Also forgot to mention Gavin as well… :frowning:

I will keep quiet for the remainder of the day… :slight_smile:

ilan


#5

On Feb 26, 2007, at 10:07 AM, Ilan B. wrote:

wife’s favourite is…
No worries. Hal listed me as James Edward G. III in the second
edition of The Ruby Way. Good to know he gives me the +1 I guess… :wink:

James Edward G. II


#6

On Tue, 27 Feb 2007, James Edward G. II wrote:

On Feb 26, 2007, at 9:58 AM, Ilan B. wrote:

(documentation provided by James E. Gray II)

I have become a tea.

So what did you do to get yourself in such hot water?

– Matt
It’s not what I know that counts.
It’s what I can remember in time to use.


#7

Ilan B. wrote:

El Gato wrote:

I’m sure I’m just being an idiot here… my mind is a little foggy this
morning, but I’m having a hard time understanding how to accomplish
this. I’ve written a class (I’ll just put some snippets in for
understanding) in which I’d like to be able to use the following
behavior:

El Gato,

My suggestion is to take a good look at Ruby’s incredibly powerfull use
of delegates
http://www.ruby-doc.org/stdlib/libdoc/delegate/rdoc/index.html
(documentation provided by James E. Gray II)

Secondly, there is a great book entitled “Ruby Recipes”, which discusses
your question in great detail and I higly recommend that you pick it
up…

Hope this helps and this is only a suggestion as there exists 100 ways
to do what you are asking… :slight_smile:

ilan

I’m not sure if I’m missing something, but it appears that calling map()
on a DelegateClass still returns an Array object. I guess I would need
map() and friends to return an InventoryReport class (i.e., Array
doesn’t have a save method, whereas InventoryReport does, so calling
report.query.map {…}.save “/tmp/filename.xls” fails)


#8

On Tue, 27 Feb 2007, El Gato wrote:

Ilan B. wrote:

El Gato wrote:

I’m sure I’m just being an idiot here… my mind is a little foggy this
morning, but I’m having a hard time understanding how to accomplish
this. I’ve written a class (I’ll just put some snippets in for
understanding) in which I’d like to be able to use the following
behavior:

check out arrayfields - on rubyforge or

http://codeforpeople.com/lib/ruby/arrayfields/

by using it you can name your fields for the result set only

-a


#9

On Tue, Feb 27, 2007 at 01:04:39AM +0900, James Edward G. II wrote:

On Feb 26, 2007, at 9:58 AM, Ilan B. wrote:

(documentation provided by James E. Gray II)

I have become a tea.

Not just any tea. A tea that speaks in the voice of Darth Vader.

_why


#10

On Tue, Feb 27, 2007 at 01:10:46AM +0900, James Edward G. II wrote:

Ouch!!! Apologies… Not enough coffee this morning and guess what my
wife’s favourite is…

No worries. Hal listed me as James Edward G. III in the second
edition of The Ruby Way. Good to know he gives me the +1 I guess… :wink:

James Edward G. II

James Edward G. II ++


#11

El Gato wrote:

I’m not sure if I’m missing something, but it appears that calling map()
on a DelegateClass still returns an Array object. I guess I would need
map() and friends to return an InventoryReport class (i.e., Array
doesn’t have a save method, whereas InventoryReport does, so calling
report.query.map {…}.save “/tmp/filename.xls” fails)

El Gato,

No, you didn’t miss anything, your delegate class would return and
InventoryReport on map() (on a side note, I dislike the re-usage of the
“map” name for alternate functionality but that’s another discussion)
which would have the save() method. The delegate pattern allows you to
“intercept” selectively in order to accomplish the task at hand.

ilan


#12

On Tue, Feb 27, 2007 at 12:42:15AM +0900, El Gato wrote:

Where query() returns an array like [[“host1”, “Dell”, “Windows”],
[“host2”, “Hitachi”, “Windows”], …]

However, throwing a map (or select or whatever) in there (obviously)
ends up returning an Array. How would I handle this so that it works as
I would like? Do I need to move my save(), to_csv(), and to_xls() stuff
into the Array class?

It was a Moment of Enlightenment for me when I realised that all this
stuff
they teach you about OO, class hierarchies and inheritance is actually
irrelevant. It’s mainly there just to keep C++ and Java compilers happy.

A more flexible approach is composition and delegation. (Object A has
a
B,
not Object A is a B)

In this model, what you’d get is something like this:

class InventoryReport
def initialize(query_name, fields, data = [])
@query_name = query_name
@fields = fields
@data = data
end

def query
  ans = ...do database query...
  self.class.new(@query_name, @fields, ans)
end

def map(&blk)
  self.class.new(@query_name, @fields, @data.map(&blk))
end

... etc

end

But in that case, InventoryReport#query could modify its own @data
instance
in-place, in which case you’d have

def query
  @data = ...do database query...
end

def map(&blk)
  @data = @data.map(&blk)
end

(Then you can argue about whether the methods should be called ‘query!’
and
‘map!’ but I won’t go there)

Anyway, I find this approach is much more powerful. For example, your
object
A can contain instances of B, C and D. When an incoming method call
arrives,
it can forward to one of these, or use them in sequence to perform
whatever
task is required.

If you find you are writing lots of explicit delegation, then use one of
the
delegation patterns to help you - or just write a method_missing()
function.
And if you’re using inheritance as a form of implementation code sharing
between your own objects, then just use mixins for the common code.

Going down this route, a lot of the OO conundrums simply disappear -
such as
“is a circle an oval, or is an oval a circle?”

I notice that your InventoryReport is already composed of three
elements: a
query name, a field array, and a data array. If you use inheritance, you
have to worry about which of these elements is the “primary” one which
inherits from some other class, and deal with all the nits of inherited
methods (such as Array#map always returning an Array, as you
discovered).
However if you use composition, all the elements are equal, and you can
pick
whatever behaviour you want from all three.

Regards,

Brian.


#13

On 2/26/07, James Edward G. II removed_email_address@domain.invalid wrote:

On Feb 26, 2007, at 9:58 AM, Ilan B. wrote:

(documentation provided by James E. Gray II)

I have become a tea.

James Edward Gray II

Tea for II, very nice, watch out for Jean-Luc :wink:


#14

On Feb 26, 9:46 am, _why removed_email_address@domain.invalid wrote:

On Tue, Feb 27, 2007 at 01:04:39AM +0900, James Edward G. II wrote:

On Feb 26, 2007, at 9:58 AM, Ilan B. wrote:

(documentation provided by James E. Gray II)

I have become a tea.

Not just any tea. A tea that speaks in the voice of Darth Vader.

LOL

Awesome :slight_smile:


#15

(documentation provided by James E. Gray II)

I have become a tea.

Not just any tea. A tea that speaks in the voice of Darth Vader.

LOL

Awesome :slight_smile:

I love it. I get these images of the Lord of the Sith relaxing with a
nice McVittie’s digestive biscuit. “The Force is strong in this cup.”


#16

From: _why [mailto:removed_email_address@domain.invalid] :

fr: James Edward G. II wrote:

> >(documentation provided by James E. Gray II)

> I have become a tea.

Not just any tea. A tea that speaks in the voice of Darth Vader.

can’t resist my stomach, sorry list, but i really love this list! rotfl
:)))