Subclassing Array

H

Hans Fugal

I have a simple observer-like module:
module Notifier
def add_notification(&callback)
@notifications = [] if not defined? @notifications
@notifications << callback
end

def delete_notification( &callback )
@notifications.delete(callback) if @notifications
end

protected
def notify
@notifications.each { |o| o.call } if defined? @notifications
end
end

(If 'Notifier' bugs you, then do a semantic search and replace
/notify/observe/)

I want to mix this in to a subclass of Array. The resulting class would
behave exactly like an Array except whenever the array changed notify
would be called.

I *could* subclass Array and def each of
&, *, +, --, <<, <=>, ==, ===, [], [], []=, assoc, at, clear,
collect, collect!, compact, compact!, concat, delete, delete_at,
delete_if, each, each_index, empty?, eql?, fill, first, flatten,
flatten!, include?, index, indexes, indices, join, last, length,
map!, new, nitems, pack, pop, push, rassoc, reject!, replace,
reverse, reverse!, reverse_each, rindex, shift, size, slice,
slice!, sort, sort!, to_a, to_ary, to_s, uniq, uniq!, unshift, |
that changes the Array, but obviously this isn't an exciting prospect.
I'm hoping that most of the changing methods of Array are actually
implemented in terms of a couple of fundamental methods, e.g. insert and
delete. So I would do:

class NotifyingArray
include Notifier
def insert(o)
super(o)
notify
end
def delete(o)
super(o)
notify
end
end

Am I on the right track? Does anyone know which methods I would have to
override?
 
G

gabriele renzi

il 15 Dec 2003 14:50:51 GMT, (e-mail address removed) (Hans Fugal) ha
scritto::
I have a simple observer-like module:
I'm hoping that most of the changing methods of Array are actually
implemented in terms of a couple of fundamental methods, e.g. insert and
delete. So I would do:

class NotifyingArray
include Notifier
def insert(o)
super(o)
notify
end
def delete(o)
super(o)
notify
end
end

Am I on the right track? Does anyone know which methods I would have to
override?

A possible alternative may be something like this:

class SimilArray
def initialize(ary)
@ary=ary
end
include Notifier
def method_missing:)method,*args,&blk)
@ary.send:)method,*args,&blk)
end
end

This is a strange kind of "side inherithance", but it may work.
BTW, do you know that an observer module already exist in the standard
ruby lib ?
 
J

Jamis Buck

Hans said:
Am I on the right track? Does anyone know which methods I would have to
override?

Well, looking at the ruby sources, it appears that Array is completely
implemented in C (which makes sense, given that it's one of the core
classes). However, this means that each of the operations on Array
don't call a ruby method, they call the corresponding C function
directly. Thus, it doesn't appear that you can simply override one
single method and change the behavior of multiple other methods. :(
Unfortunate, but it makes for much faster Array methods, I suppose.
 
H

Hans Fugal

With a slight modification what you offered would work:

class SimilArray
def initialize(ary)
@ary=ary
end
include Notifier
def method_missing:)method,*args,&blk)
@ary.send:)method,*args,&blk)
notify # this is what's different
end
end

But it would always notify even on read-only operations, and so it's
wasteful. But I could check by name for writing operations.

Another approach would be a mixin that provides the Array operations based
BTW, do you know that an observer module already exist in the standard
ruby lib ?
Yeah, but it is callback-based, but I would like mine to be proc-based.
e.g.

Observable way:
class MyObservable
include Observable
attr_reader :foo
def foo=(o)
@foo = o
changed
notify_observers(self)
end
end
class MyObserver
def initialize
MyObservable.new.add_observer(self)
def update(o)
puts o.foo
end
end

Notifier way:
class MyObservable
include Notifier
attr_reader ;foo
def foo=(o)
@foo = o
notify
end
end
class MyObserver
def initialize
o = MyObservable.new
bar = "bar"
o.add_notification { puts "#{o.foo} #{bar}" }
end
end

My Notifier module is based on
http://www.rubygarden.org/ruby?ObserverPattern
 
M

Mauricio Fernández

I'm hoping that most of the changing methods of Array are actually
implemented in terms of a couple of fundamental methods, e.g. insert and
delete. So I would do:

That's unfortunately not the case (for performance reasons), but you
could take a look at matju's Metaruby which IIRC does exactly that.

OTOH, even wrapping all those methods need not be tiresome if you use
some meta-programming or AspectR for instance.

--
_ _
| |__ __ _| |_ ___ _ __ ___ __ _ _ __
| '_ \ / _` | __/ __| '_ ` _ \ / _` | '_ \
| |_) | (_| | |_\__ \ | | | | | (_| | | | |
|_.__/ \__,_|\__|___/_| |_| |_|\__,_|_| |_|
Running Debian GNU/Linux Sid (unstable)
batsman dot geo at yahoo dot com

Software is like sex; it's better when it's free.
-- Linus Torvalds
 
G

gabriele renzi

il 15 Dec 2003 15:52:20 GMT, (e-mail address removed) (Hans Fugal) ha
scritto::
With a slight modification what you offered would work:

oops forgot that :)
Yeah, but it is callback-based, but I would like mine to be proc-based.
e.g.

I see. Thanks for the answer
 
C

Chris Pine

----- Original Message -----
With a slight modification what you offered would work:

class SimilArray
def initialize(ary)
@ary=ary
end
include Notifier
def method_missing:)method,*args,&blk)
@ary.send:)method,*args,&blk)
notify # this is what's different
end
end

But it would always notify even on read-only operations, and so it's
wasteful. But I could check by name for writing operations.
------------------------------

You could also test to see if the array has changed (clone it before sending
the message): is it the same size with the same elements in the same order?
If so, then notify.

Chris Pine
 
C

Chris Pine

----- Original Message -----
From: "Chris Pine said:
You could also test to see if the array has changed (clone it before sending
the message): is it the same size with the same elements in the same order?
If so, then notify.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,774
Messages
2,569,596
Members
45,139
Latest member
JamaalCald
Top