|
From: Mark H. <di...@ma...> - 2005-07-14 22:20:39
|
On Jul 14, 2005, at 2:53 PM, Dave Howell wrote:
> Climb over one hill, find another behind it. I think this is mostly
> a plain Ruby question, but let's see what happens.
>
> I have a RubyCocoa app I'm working on, a To Do list manager. I can
> pass a "ToDo" task to a subclassed NSWindowManager, and voila! I
> have a window with that task in it. Happy me! Make changes, make
> changes, have changes written back to the task, et cetera.
>
> If I open a *second* window on the same task, however, they're each
> writing back to the task but not aware of what the other window's
> doing. Obviously (I hope?) my task needs to know what windows are
> open onto it, and when it changes, it should tell them all to update.
>
> [Because of RubyCocoa's incomplete support for Bindings and
> whatnot, I have abandoned trying to do this with CoreData. I figure
> I'm probably better off learning this one the "old fashioned way,"
> especially since it's not really that big a project.]
>
> My plan is to give each task a "ViewsOfMe" array property. Whenever
> a task window controller opens a window, it tells the task it did,
> and when it closes, ditto. The task would tell all registered
> window controllers to update whenever one of its relevant
> properties is changed.
>
> This seemed like a good plan up to this point.
>
>
> But my Task class starts out like this:
>
> attr_accessor :title, :description, :priority [8 more
> symbols here....]
>
> I *could* just replace all this with
>
> def title
> @title
> end
> def title=
> @title=title
> #put update windows code here
> end
> [repeat 10 more times]
>
> But that seems horribly unRubyesque. I figured I'd just modify/mix-
> in/replace attr_accessor with one that also sets up the window
> update triggers. OTOH, I'm absolutely mystified how to modify
> attr_accessor to stick the updating code in the right spot. I
> cannot find any reference for how attr actually works. I couldn't
> even find a file that contained its code. One doc said it was in
> "objects.c" but I can't find that either. LOL.
>
> Part of what makes this tricky is that I can't just add something
> to attr, I have to change something it already does.
>
> Thoughts? Suggestions?
Roll your own attr_accessor. attr_accessor is just a method:
Class#attr_accessor.[1] Define one that creates methods that you
want. In this case, you want instance variables set, and a hook that
will let you notify observers.
Here's a quick example done in irb:
class Class
def observable_attr(*names)
# Make sure we have the right scope
self.class_eval do
names.each do |name|
define_method name do # the getter
instance_variable_get :"@#{name}"
end
define_method :"#{name}=" do |value| # the setter
instance_variable_set :"@#{name}", value
update_observers(name)
end
end
end
end
end
==>nil
class Foo
def update_observers(name) # the hook method
puts "updating observers regarding '#{name}'"
end
observable_attr :foo, :bar, :baz
end
==>[:foo, :bar, :baz]
f = Foo.new
==>#<Foo:0x121eaa0>
f.foo, f.bar, f.baz = 23, 42, 93
updating observers regarding 'foo'
updating observers regarding 'bar'
updating observers regarding 'baz'
==>[23, 42, 93]
[f.foo, f.bar, f.baz]
==>[23, 42, 93]
[1] actually, it's Module#attr_accessor, but that's another story.
|