[TIP] Proc as Observer

Discussion in 'Ruby' started by Tim Pease, Nov 8, 2006.

  1. Tim Pease

    Tim Pease Guest

    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 Pease, Nov 8, 2006
    #1
    1. Advertising

  2. Tim Pease

    Trans Guest

    Re: Proc as Observer

    Tim Pease 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.

    > 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.
     
    Trans, Nov 8, 2006
    #2
    1. Advertising

  3. Re: Proc as Observer

    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
    --
    Marcel Molina Jr. <>
     
    Marcel Molina Jr., Nov 8, 2006
    #3
  4. Tim Pease

    Tim Pease Guest

    Re: Proc as Observer

    On 11/8/06, Marcel Molina Jr. <> wrote:
    > 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.
    >


    Good to know. Thanks for the tip.

    TwP
     
    Tim Pease, Nov 8, 2006
    #4
  5. Re: Proc as Observer

    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 Gray II
     
    James Edward Gray II, Nov 8, 2006
    #5
  6. Tim Pease

    Guest

    Re: Proc as Observer

    On Thu, 9 Nov 2006, James Edward Gray 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
    --
    my religion is very simple. my religion is kindness. -- the dalai lama
     
    , Nov 8, 2006
    #6
  7. Tim Pease

    Trans Guest

    Re: Proc as Observer

    wrote:
    > On Thu, 9 Nov 2006, James Edward Gray 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


    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.
     
    Trans, Nov 8, 2006
    #7
  8. Tim Pease

    Tim Pease Guest

    Re: Proc as Observer

    On 11/8/06, <> 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
     
    Tim Pease, Nov 8, 2006
    #8
  9. Tim Pease

    Guest

    Re: Proc as Observer

    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
    --
    my religion is very simple. my religion is kindness. -- the dalai lama
     
    , Nov 8, 2006
    #9
  10. Tim Pease

    Tim Pease Guest

    Re: Proc as Observer

    On 11/8/06, <> 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
     
    Tim Pease, Nov 8, 2006
    #10
  11. Tim Pease

    Trans Guest

    Re: Proc as Observer

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

    T.
     
    Trans, Nov 15, 2006
    #11
  12. Re: Proc as Observer

    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>

    --
    Nobu Nakada
     
    Nobuyoshi Nakada, Nov 15, 2006
    #12
  13. Tim Pease

    Trans Guest

    Re: Proc as Observer

    Nobuyoshi Nakada wrote:
    > 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?
     
    Trans, Nov 15, 2006
    #13
  14. Re: Proc as Observer

    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.

    --
    Nobu Nakada
     
    Nobuyoshi Nakada, Nov 15, 2006
    #14
  15. Tim Pease

    Trans Guest

    Re: Proc as Observer

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

    > > 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, Nov 15, 2006
    #15
  16. Tim Pease

    Trans Guest

    Re: Proc as Observer

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

    T.
     
    Trans, Nov 15, 2006
    #16
  17. Re: Proc as Observer

    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.

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

    --
    Nobu Nakada
     
    Nobuyoshi Nakada, Nov 15, 2006
    #17
  18. Tim Pease

    Trans Guest

    Re: Proc as Observer

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

    T.
     
    Trans, Nov 15, 2006
    #18
  19. Re: Proc as Observer

    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.

    --
    Nobu Nakada
     
    Nobuyoshi Nakada, Nov 15, 2006
    #19
  20. Tim Pease

    Trans Guest

    Re: Proc as Observer

    Nobuyoshi Nakada wrote:
    > 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.
     
    Trans, Nov 15, 2006
    #20
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. David Lozzi
    Replies:
    3
    Views:
    1,997
    David Lozzi
    Jun 1, 2005
  2. NevilleDNZ
    Replies:
    9
    Views:
    463
    NevilleDNZ
    Aug 16, 2006
  3. Jean-Hugues ROBERT

    Why no Proc##[]=() ? Why no Proc##replace() ?

    Jean-Hugues ROBERT, May 1, 2004, in forum: Ruby
    Replies:
    14
    Views:
    316
    Jean-Hugues ROBERT
    May 5, 2004
  4. David Mark
    Replies:
    16
    Views:
    942
    Scott Sauyet
    Nov 11, 2011
  5. David Mark
    Replies:
    58
    Views:
    1,543
    David Mark
    Dec 6, 2011
Loading...

Share This Page