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

  • Thread starter Dan Stevens (IAmAI)
  • Start date
D

Dan Stevens (IAmAI)

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?
 
G

Gregory Brown

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?

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
 
M

MenTaLguY

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
 
D

Dan Stevens (IAmAI)

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!
 
M

Micah Martin

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

Sent from my iPhone
 
J

James Edward Gray II

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? ;)

James Edward Gray II
 
D

Dan Stevens (IAmAI)

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?
 
D

dblack

Hi --

We will be there!

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


Does it help you see _who_ you are sending messages to by chance? ;)

Hey, it's only the first iteration of the iPhone -- you can't expect
all the bells and whistles yet :)


David

--
* Books:
RAILS ROUTING (new! http://www.awprofessional.com/title/0321509242)
RUBY FOR RAILS (http://www.manning.com/black)
* Ruby/Rails training
& consulting: Ruby Power and Light, LLC (http://www.rubypal.com)
 
G

Gregory Brown

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.


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?

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. :)

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...
 
J

James Edward Gray II

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 Gray II
 
C

Chris Carter

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!
 
D

Dan Stevens (IAmAI)

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.

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 Gray II
 

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

Forum statistics

Threads
474,438
Messages
2,571,699
Members
48,796
Latest member
Greg L.
Top