[TIP] Proc as Observer

T

Tim Pease

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
 
T

Trans

Tim said:
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.
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.

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

class Proc
alias :update :call
end

T.
 
M

Marcel Molina Jr.

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
 
T

Tim Pease

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
 
J

James Edward Gray II

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

ara.t.howard

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
 
T

Trans

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

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

Tim Pease

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
 
A

ara.t.howard

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
 
T

Tim Pease

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
 
T

Trans

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

T.
 
T

Trans

Nobuyoshi said:
Hi,

At Thu, 9 Nov 2006 07:25:54 +0900,
Tim Pease 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?
 
N

Nobuyoshi Nakada

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

Trans

Nobuyoshi said:
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"

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

Trans

Trans said:
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/ :)

T.
 
N

Nobuyoshi Nakada

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

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.
Hmm... yes, I mean to go one step futher and catch the module and
create methods within it:

Point taken.
 
T

Trans

Nobuyoshi said:
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! :)

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.
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 :-( Oh, only if we could #unextend.

T.
 
N

Nobuyoshi Nakada

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 :-( Oh, only if we could #unextend.

Indeed, you can just do remove_method.
 
T

Trans

Nobuyoshi said:
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 :-( Oh, only if we could #unextend.

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.
 

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
473,769
Messages
2,569,578
Members
45,052
Latest member
LucyCarper

Latest Threads

Top