views:

58

answers:

3

I'd like to add something like a callback function to a Ruby array, so that when elements are added to that array, this function is called. One thing I can think of is to override all methods (like <<, =, insert, ...) and call that callback from there.

Is there an easier solution?

+1  A: 

You should probably create your own class that wraps array. You don't want to override a core class with a callback like you are describing, not only does that make the code brittle but it becomes less expressive for future developers who may not be expecting Array to make a callback.

heavysixer
I could also just override the methods for the instances I am interested in. But I am looking for another way to add that functionality, as I don't like to override every method that could add an element. But on the other side, the instance must have the functionality to be treated as an array. Thanks for your help, too.
Zardoz
+2  A: 

Use the "Observer" pattern to be notified of changes in the size of the array you wish to observer: Ruby Observer This saves you from having to override all methods that add an element to the array

ennuikiller
Thanks for the help. As far as I understand the documentation in observer.rb I have to call "changed" to inform the listeners. But that brings me back to the original problem .. how do I know that the size of the array changed? From where do I call "changed"?
Zardoz
+2  A: 

The following code only invokes the size_changed hook when the array size has changed and it is passed the new size of the array:

a = []

class << a
  Array.instance_methods(false).each do |meth|
    old = instance_method(meth)
    define_method(meth) do |*args, &block|
      old_size = size
      old.bind(self).call(*args, &block)
      size_changed(size) if old_size != size
    end if meth != :size
  end
end

def a.size_changed(a)
  puts "size change to: #{a}"
end

a.push(:a) #=> size change to 1
a.push(:b) #=> size change to 2
a.length 
a.sort!
a.delete(:a) #=> size change to 1
banister