Array#modified

I’m looking for a simple and short solution/library that does this:

a = []

def a.modified
puts “Array a changed”
end

a << ‘x’
puts a
a[0] = ‘y’
puts a

----OUTPUT----
Array a changed
x
Array a changed
y

Juozas G. wrote:

I’m looking for a simple and short solution/library that does this:
(snip)
----OUTPUT----
Array a changed
x
Array a changed
y

Check out the Observable class in the standard library.

Tim H. wrote:

Juozas G. wrote:

I’m looking for a simple and short solution/library that does this:
(snip)
----OUTPUT----
Array a changed
x
Array a changed
y

Check out the Observable class in the standard library.

Thanks for your prompt reply. Unfortunately Observable is not what I’m
looking for. It requires manually setting “changed” state to true on
every operation that modifies the array. I’m looking for something that
overrides all of the methods that can modify an array (<<, pop, shift,
push, []=, filter, etc.) then calls the old method and finally invokes
the “modified” callback.

I’m assuming something like this exists (specifically for the Array
class). I could write it myself, but I’d prefer to reuse existing code.

“I’m assuming something like this exists (specifically for the Array
class). I could write it myself, but I’d prefer to reuse existing code.”

Ok, I dont know myself, but there seems to exist a very
small “event” based … uhm thing, made possible in an easy
manner already, like so

def StdLiving.inherited(sub)

Sure, its not what you need though. :slight_smile:

I’d love to get event states from classes like Array in this case,
would remind me a bit of the basic GUI state-events, like
button_pressed … or value_changed :wink:

PS: I think there exists a bigger event framework for Ruby too
though… havent checked it yet so cant tell anything

class Changed
def initialize(obj, &changed)
@obj = obj
@changed = changed
end

def method_missing(id, *a, &b)
obj_before = @obj.clone
@obj.send(id, *a, &b)
if obj_before != @obj
@changed[@obj, id, a, b]
end
end
end

I like this solution because it’s short. However, it requires cloning
the array every time a method is invoked on it, it seems that it would
slow it down too much for my needs. I’m not sure.

I think I’ll google for “ruby event framework” as Marc suggested.

Juozas G. wrote:

…, but I’d prefer to reuse existing code.

Maybe also check out some design patterns (state pattern, observer
pattern):

http://rightfootin.blogspot.com/2006/08/implementing-state-pattern-in-ruby.html

http://cwilliams.textdriven.com/articles/2006/11/02/patterns-in-ruby-observer-pattern

Cheers,
verno

Juozas G. wrote:

a[0] = ‘y’
puts a

----OUTPUT----
Array a changed
x
Array a changed
y

class Changed
def initialize(obj, &changed)
@obj = obj
@changed = changed
end

def method_missing(id, *a, &b)
obj_before = @obj.clone
@obj.send(id, *a, &b)
if obj_before != @obj
@changed[@obj, id, a, b]
end
end
end

if $0 == FILE
a = []
c = Changed.new(a) do |obj, id, args, block|
STDOUT << “#{obj.inspect} changed by #{id}”
STDOUT << “(#{args.map { |x| x.inspect } * ‘,’})” unless args.empty?
STDOUT << “\n”
end
c << 1

[1] changed by <<(1)

c.to_s
c << 2

[1, 2] changed by <<(2)

c.delete 2

[1] changed by delete(2)

c.delete 2
c << 3

[1, 3] changed by <<(3)

c[0] = 4

[4, 3] changed by []=(0,4)

c.replace [6,6,6]

[6, 6, 6] changed by replace([6, 6, 6])

c.clear

[] changed by clear

end

On Mon, Jan 15, 2007 at 10:52:37PM +0900, Juozas G. wrote:

a[0] = ‘y’
puts a

----OUTPUT----
Array a changed
x
Array a changed
y

You can sort of do this, but an object does not know what variable(s) it
is
assigned to, so you will never be able to get the “Array a changed”
message
from it directly. Try the following, however:

module ArrayWatch
def is_modified?
@modified
end

def reset_modified
@modified = false
end

def []=(k, v)
@modified = true
super(k, v)
end

def <<(v)
@modified = true
super(v)
end

def concat(v)
@modified = true
super(v)
end

def push(*v)
@modified = true
super(*v)
end

def unshift(*v)
@modified = true
super(*v)
end

def replace(v)
@modified = true
super(v)
end

def slice!(*v)
@modified = true
super(*v)
end

def compact!
@modified = true
super
end

def sort!(&block)
@modified = true
super &block
end

def uniq!
@modified = true
super
end

def reverse!
@modified = true
super
end

def flatten!
@modified = true
super
end

def collect!(&block)
@modified = true
super &block
end

def map!(&block)
@modified = true
super &block
end

def delete_at(i)
@modified = true
super(i)
end

def delete(v, &block)
@modified = true
super(v, &block)
end

def delete_if(&block)
@modified = true
super &block
end

def reject!(&block)
@modified = true
super &block
end

def shift
@modified = true
super
end

def pop
@modified = true
super
end

end

a = []
a.extend ArrayWatch

def a.to_s
“a #{ is_modified? ? ‘is’ : ‘is not’ } modified”
end

puts a
a << ‘x’
puts a
a.reset_modified
puts a
a[0] = ‘y’
puts a

–Greg

On 15.01.2007 15:20, Gregory S. wrote:

puts a
assigned to, so you will never be able to get the “Array a changed” message
from it directly. Try the following, however:

module ArrayWatch
def is_modified?
@modified
end

Alternative approach would be to use Delegator to wrap an Array
instance.

OP, what are you trying to achieve? What is the problem you are trying
to solve?

Kind regards

robert