Calling an arbitrary method as and when an array's contents


#1

I have a collection class that extends the Array class and I wish to
have a particular method to be called whenever the contents of an
instance of the class change. Can anyone advise on how I can achieve
this?

I’m aware that I can compare #hash with a previous saved value to
determine if the contents of the instance have changed since the last
time #hash was called. Therefore, if I can find a means of calling an
arbitrary method or piece of code every time any method is called on
the instance, I could achieve what I’m after. Can I override
Object#send?


#2

On Fri, 6 Jul 2007 07:13:31 +0900, “Dan Stevens (IAmAI)”
removed_email_address@domain.invalid wrote:

I have a collection class that extends the Array class and I wish to
have a particular method to be called whenever the contents of an
instance of the class change. Can anyone advise on how I can achieve
this?

For this purpose, it’s probably better to wrap/delegate to Array than
extend it.

That way, you can have control of all the methods that change something,
and
guarantee that they will perform the appropriate notification. I’d also
recommend looking at Observer for the notification of the equation.

-mental


#3

Great. Houlihans is quiet enough for us to talk. 5:00pm work for you?

Sent from my iPhone


#4

On 7/5/07, Dan Stevens (IAmAI) removed_email_address@domain.invalid wrote:

Object#send?
It sounds like you might be able to set up something like this with
Observer.
http://www.ruby-doc.org/stdlib/libdoc/observer/rdoc/index.html


#5

Hi –

On Fri, 6 Jul 2007, Micah M. wrote:

Great. Houlihans is quiet enough for us to talk. 5:00pm work for you?

You forgot to tell us which Houlihans :slight_smile:

David


#6

On Jul 5, 2007, at 5:57 PM, Micah M. wrote:

Great. Houlihans is quiet enough for us to talk. 5:00pm work for you?

We will be there!

Be sure to grab a big enough table to hold us. A few thousand will
be fine.

Sent from my iPhone

Does it help you see who you are sending messages to by chance? :wink:

James Edward G. II


#7

Hi –

On Fri, 6 Jul 2007, James Edward G. II wrote:

Does it help you see who you are sending messages to by chance? :wink:
Hey, it’s only the first iteration of the iPhone – you can’t expect
all the bells and whistles yet :slight_smile:

David


#8

Ah! I’d forgotten about the observer pattern. That should do the trick. Thanks!

After looking at the Observable module, it only really solves half of
my problem. While it’s a nice mechanism for notify observers that an
object has changed, it does help me determine whether or not an object
has changed; it leaves that up to the programmer.

For this purpose, it’s probably better to wrap/delegate to Array than extend it.

This is a potential solution - I could override all the methods of the
Array class that I think modify the contents of the Array in a
superclass, calling Observable#changed before/after calling the
parent’s method. However, what if I think there’s one too many methods
to be overriding or a forget to override one?

Truth is, I’ve now realised that I actually don’t need an answer to
this question to solve my problem (helps to have full insight into
one’s problems first). However, for the sake of argument and my
curiosity, does anyone thing there’s an easier and more reliable
solution?


#9

On 7/5/07, Dan Stevens (IAmAI) removed_email_address@domain.invalid wrote:

Array class that I think modify the contents of the Array in a
superclass, calling Observable#changed before/after calling the
parent’s method. However, what if I think there’s one too many methods
to be overriding or a forget to override one?

Um… do you really need an observable array or are you building an
object that uses an array under the hood that needs to be observable?
Big difference there.

In the former case, you’ll likely want to use a blank slate proxy that
does the notification on delegation. On the latter case it’s really
easy, just notify on the methods you’re actually using. :slight_smile:

Word on the street is that subclassing core classes to change
behaviour is a bad idea anyway. You might risk your subclass methods
not getting called in favor of the C methods being called without you
knowing…


#10

On 7/5/07, Micah M. removed_email_address@domain.invalid wrote:

Great. Houlihans is quiet enough for us to talk. 5:00pm work for you?

I changed my mind. Red Lobster is better.


#11

Hi –

On Fri, 6 Jul 2007, Gregory B. wrote:

On 7/5/07, Micah M. removed_email_address@domain.invalid wrote:

Great. Houlihans is quiet enough for us to talk. 5:00pm work for you?

I changed my mind. Red Lobster is better.

I vote for Ruby Tuesday.

David


#12

On Jul 5, 2007, at 6:04 PM, Dan Stevens (IAmAI) wrote:

For this purpose, it’s probably better to wrap/delegate to Array
than extend it.

This is a potential solution - I could override all the methods of the
Array class that I think modify the contents of the Array in a
superclass, calling Observable#changed before/after calling the
parent’s method. However, what if I think there’s one too many methods
to be overriding or a forget to override one?

It’s really not too much work:

#!/usr/bin/env ruby -wKU

require “observer”

class OArray
instance_methods.each { |meth| undef_method(meth) unless meth =~ /
\A__/ }

include Observable

def initialize(array)
@array = array
end

def method_missing(meth, *args, &block)
@array.send(meth, *args, &block)

 if %w{ []= clear concat delete delete_at
        delete_if fill pop push replace shift }.include?

(meth.to_s) or
meth.to_s[-1] == ?!
changed
notify_observers(@array, meth, *args, &block)
end
end
end

class Array
def to_oarray
OArray.new(self)
end
end

observer = Object.new
class << observer
def update(array, *call)
puts “You called #{call.first} and the Array is now #
{array.inspect}.”
end
end

observed = [1, 2, 3].to_oarray
observed.add_observer(observer)
observed[1]
observed[1] = “2”

END

James Edward G. II


#13

On 7/5/07, removed_email_address@domain.invalid removed_email_address@domain.invalid wrote:

David

Yeah, Houlihans might be at Bennigan’s.


#14

On 7/5/07, Todd B. removed_email_address@domain.invalid wrote:

David

You think this will be a common iPhone thing?

Maybe that’s a clever marketing ploy. Apple has joined forces with
Houlihans!


#15

On 7/5/07, removed_email_address@domain.invalid removed_email_address@domain.invalid wrote:

Hi –

On Fri, 6 Jul 2007, Micah M. wrote:

Great. Houlihans is quiet enough for us to talk. 5:00pm work for you?

You forgot to tell us which Houlihans :slight_smile:

David

You think this will be a common iPhone thing?


#16

On 7/5/07, Gregory B. removed_email_address@domain.invalid wrote:

Maybe that’s a clever marketing ploy. Apple has joined forces with Houlihans!

First Google, then AT&T, and now Houlihans! The trifecta of evil
corporation partnerships is complete! Our world is going to be taken
over! I for one welcome our stylish, heavily indexed, delicious
tasting, cellular providing overlords!


#17

It sounds like you might be able to set up something like this with Observer.

Ah! I’d forgotten about the observer pattern. That should do the trick.
Thanks!


#18

I contemplated using the #hash method to determine if the array had
changed:

[snip]

def method_missing(meth, *args, &block)
@last_hash = @array.hash
@array.send(meth, *args, &block)
if @array.hash != @last_hash
changed
notify_observers(@array, meth, *args, &block)
end
end

[snip]

That way I don’t have to worry about which methods change the contents
of the array.