Proc as Observer

Working with an Observable object, I wanted to be able to add a Proc
as an observer. Observable requires all observers to implement an
update method. When the Observable state changes, this is the method
that will be called to notify observers of the state change.

My generic solution was to create a module that adds an update method
for Proc objects.

module ProcAsObserver
def update( *args )
self.call *args
end
end

To use this module …

method = lambda {|*args| args.each {|x| puts x}}
method.extend ProcAsObserver

my_observable_object.add_observer(method)

That’s it! Just a handy little tip for anyone working with Observable
objects.

Blessings,
TwP

Tim P. wrote:

Working with an Observable object, I wanted to be able to add a Proc
as an observer. Observable requires all observers to implement an
update method. When the Observable state changes, this is the method
that will be called to notify observers of the state change.

Interesting… I’d like to see a simple example of this.

method = lambda {|*args| args.each {|x| puts x}}
method.extend ProcAsObserver

my_observable_object.add_observer(method)

That’s it! Just a handy little tip for anyone working with Observable objects.

Given your approach I think I’d just go ahead and do:

class Proc
alias :update :call
end

T.

On Thu, Nov 09, 2006 at 03:55:13AM +0900, Trans wrote:

my_observable_object.add_observer(method)

That’s it! Just a handy little tip for anyone working with Observable objects.

Given your approach I think I’d just go ahead and do:

class Proc
alias :update :call
end

In 1.9 the method signature of add_observer is:

def add_observer(observer, func=:update)

In your case, the second argument could be :call.

marcel

On 11/8/06, Marcel Molina Jr. [email protected] wrote:

In 1.9 the method signature of add_observer is:

def add_observer(observer, func=:update)

In your case, the second argument could be :call.

Good to know. Thanks for the tip.

TwP

On Nov 8, 2006, at 12:55 PM, Trans wrote:

Given your approach I think I’d just go ahead and do:

class Proc
alias :update :call
end

I thought Tim’s solution was far more elegant. Notice how he
resisted gratuitous hacking of the core.

James Edward G. II

[email protected] wrote:

I thought Tim’s solution was far more elegant. Notice how he resisted
end

in fact, you’d thing add_observer would accept a block and do exactly this.

regards.

-a

I think that’s over zealous about avoidance of core extension. This is
an excellent example of when a core extension is useful. Use of a
module and/or singleton here adds an additional layer of class
hierarchy that is simply unnecessary. There’s nothing wrong with adding
#update to Proc in this case. I’m not sure why this general perception
of core extension as “hack” has gained so much footing. It’s really
unfortunate since open classes are one the most unique and powerful
features of Ruby.

T.

On Thu, 9 Nov 2006, James Edward G. II wrote:

On Nov 8, 2006, at 12:55 PM, Trans wrote:

Given your approach I think I’d just go ahead and do:

class Proc
alias :update :call
end

I thought Tim’s solution was far more elegant. Notice how he resisted
gratuitous hacking of the core.

not to mention tim’s solution can be neatly extended to

def observer &b
o = lambda &b
class << o
alias_method ‘update’, ‘call’
end
o
end

in fact, you’d thing add_observer would accept a block and do exactly
this.

regards.

-a

On 11/8/06, [email protected] [email protected] wrote:

in fact, you’d thing add_observer would accept a block and do exactly this.

That’s a fantastic idea. The only caveat is that the add_observer
method would need to return a reference to the created Proc object so
it can later be removed (if desired) using the delete_observer method.

I always forget that blocks can be passed around and turned into Procs.

TwP

On Thu, 9 Nov 2006, Trans wrote:

I think that’s over zealous about avoidance of core extension. This is an
excellent example of when a core extension is useful. Use of a module and/or
singleton here adds an additional layer of class hierarchy that is simply
unnecessary. There’s nothing wrong with adding #update to Proc in this case.
I’m not sure why this general perception of core extension as “hack” has
gained so much footing. It’s really unfortunate since open classes are one
the most unique and powerful features of Ruby.

i’m sure not saying it’s a bad idea, but i do avoid it when it’s not
needed
and, here, it doesn’t seem to be since the &b arg of add_observer is not
currently used.

regards.

-a

[email protected] wrote:

On Thu, 9 Nov 2006, Trans wrote:

i’m sure not saying it’s a bad idea, but i do avoid it when it’s not needed
and, here, it doesn’t seem to be since the &b arg of add_observer is not
currently used.

Cool. I agree with your there.

BTW, (*a, &b) is my new favorite way to express generic passthru
parameters. I’m tired of the old (*args, &blk) :wink:

T.

Hi,

At Thu, 9 Nov 2006 07:25:54 +0900,
Tim P. wrote in [ruby-talk:224083]:

I’m done posting about this now. Promise!

Sorry to bother you, but it reminded me the proposal of
Kernel#behaving in [ruby-dev:25772].

http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-dev/25772

On 11/8/06, [email protected] [email protected] wrote:

in fact, you’d thing add_observer would accept a block and do exactly this.

New and improved tip …

class MyObservableClass
include Observable

def add_observer( observer = nil, &block )
unless block.nil?
observer = block.to_proc
class << observer
alias_method :update, :call
end
end
super observer
end
end

Now you can do this …

observable = MyObservableClass.new
proc = observable.add_observer {|*args| puts args.inspect}

observable.delete_observer proc

I’m done posting about this now. Promise!

TwP

Nobuyoshi N. wrote:

Hi,

At Thu, 9 Nov 2006 07:25:54 +0900,
Tim P. wrote in [ruby-talk:224083]:

I’m done posting about this now. Promise!

Sorry to bother you, but it reminded me the proposal of
Kernel#behaving in [ruby-dev:25772].

http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-dev/25772

Whoa! I had just written the equivalent code for NilClass a few days
ago, and was going to work on generalizing it. Check that one off the
list! --But your’s doesn’t work for immutables like NilClass. It’s a a
bit more difficult in these cases. Have a clever solution? In any case
I’ll take this and what I have and add it to Facets.

Very cool. This goes along way toward making core extensions safe.

T.

P.S. Would a cache on Behavior.new be a good optimization?

Hi,

At Wed, 15 Nov 2006 12:15:05 +0900,
Trans wrote in [ruby-talk:225063]:

But your’s doesn’t work for immutables like NilClass. It’s a a
bit more difficult in these cases. Have a clever solution? In any case
I’ll take this and what I have and add it to Facets.

Sorry, I’m not sure what you expect for NilClass. Isn’t this the
case?

$ ./ruby -e ‘nil.behaving(:foo){“Foo”}’ -e ‘p nil.foo’
“Foo”

P.S. Would a cache on Behavior.new be a good optimization?

It might be doubtful to be effective, since a Proc doesn’t equal
another Proc created in the same place almost.

Nobuyoshi N. wrote:

$ ./ruby -e ‘nil.behaving(:foo){“Foo”}’ -e ‘p nil.foo’
“Foo”

My mistake. It is for Symbols that it doesn’t work. I was mistakingly
thinking nil was the same, but that it not the case --which is great
b/c it makes my code obsolete! :slight_smile:

P.S. Would a cache on Behavior.new be a good optimization?

It might be doubtful to be effective, since a Proc doesn’t equal
another Proc created in the same place almost.

Hmm… yes, I mean to go one step futher and catch the module and
create methods within it:

class Behavior < Module
def define(behavior, &body)
if body
define_method(behavior, &body)
else
behavior.each do |behavior, body|
if body
define_method(behavior, &body)
elsif body.nil?
remove_method(behavior)
else
undef_method(behavior)
end
end
end
end
end

module Kernel
def behaving(behavior, &body)
unless @_behaviors
extend(@_behaviors ||= Behavior.new)
end
@_behaviors.define(behavior, &body)
end
end

T.

Trans wrote:

Hmm… yes, I mean to go one step futher and catch the module and
create methods within it:

Hehe. You realize I’ve habitualized the mispronounciation of that silly
French word: s/catch/cache/ :slight_smile:

T.

Hi,

At Wed, 15 Nov 2006 20:50:05 +0900,
Trans wrote in [ruby-talk:225107]:

My mistake. It is for Symbols that it doesn’t work. I was mistakingly
thinking nil was the same, but that it not the case --which is great
b/c it makes my code obsolete! :slight_smile:

I guess that Symbol in current 1.9 could have singleton
methods. Or, it would be possible by a hack similar to
instance variables of them, if really desirable.

It might be doubtful to be effective, since a Proc doesn’t equal
another Proc created in the same place almost.

Hmm… yes, I mean to go one step futher and catch the module and
create methods within it:

Point taken.

Hi,

At Wed, 15 Nov 2006 21:50:04 +0900,
Trans wrote in [ruby-talk:225115]:

Oh, there was one more thing I got confused about last night (sorry, it
was late). Actually, the code I wrote for NilClass used delegation and
#method_missing rather than an extension. This allowed me to turn the
behavior off on the fly. And that’s what I was referring to when I
spoke of safe core extensions. But we can’t do that using extending
modules :frowning: Oh, only if we could #unextend.

Indeed, you can just do remove_method.

Nobuyoshi N. wrote:

Indeed, you can just do remove_method.

(slaps forehead) pfff. Of course. It’s right there in front of me and
I totally spaced it. Sorry.

Well very cool Nobu, that’s all we need to do selector namespaces. Just
need to add an interface for it.

Thanks,
T.

Nobuyoshi N. wrote:

instance variables of them, if really desirable.

It might be doubtful to be effective, since a Proc doesn’t equal
another Proc created in the same place almost.

Hmm… yes, I mean to go one step futher and catch the module and
create methods within it:

Point taken.

Oh, there was one more thing I got confused about last night (sorry, it
was late). Actually, the code I wrote for NilClass used delegation and
#method_missing rather than an extension. This allowed me to turn the
behavior off on the fly. And that’s what I was referring to when I
spoke of safe core extensions. But we can’t do that using extending
modules :frowning: Oh, only if we could #unextend.

T.