callbacks, events, notification in general

Discussion in 'Ruby' started by Damphyr, Feb 1, 2006.

  1. Damphyr

    Damphyr Guest

    OK, bare with me, this is brainstorming of a sorts:
    I want to have some kind of event notification from my classes.
    Typically what I would do would be to pass a logger object and let the
    class log by itself, but I'm not really satisfied with this solution in
    most cases.

    What I would prefer was define a callback method and give this to the
    object. Then I could do whatever I want with it and not only use logs.
    Mostly I want to have progress reports.

    something like

    class A
    def set_callback notify
    @notify=notify
    end
    def something
    puts "something"
    send(@notify) if @notify
    end
    end

    def coocoo
    puts "hey"
    end
    a=A.new
    a.something
    a.set_callback:)coocoo)
    a.something

    Now this does exactly what I want and can be wrapped up in a module to
    be included for general use.
    Question: Is there a "better" solution? What are my alternatives?
    Cheers,
    V.-


    --
    http://www.braveworld.net/riva

    ____________________________________________________________________
    http://www.freemail.gr - äùñåÜí õðçñåóßá çëåêôñïíéêïý ôá÷õäñïìåßïõ.
    http://www.freemail.gr - free email service for the Greek-speaking.
     
    Damphyr, Feb 1, 2006
    #1
    1. Advertisements

  2. Damphyr

    ara.t.howard Guest

    harp:~ > cat a.rb
    require "observer"

    class A
    include Observable
    def something
    changed and notify_observers 42
    end
    end

    class B
    def update arg
    p arg
    end
    end

    a = A::new
    b = B::new

    a.add_observer b
    a.something


    harp:~ > ruby a.rb
    42

    always a good start to read about the stdlibs! ;-)

    cheers.

    -a
     
    ara.t.howard, Feb 1, 2006
    #2
    1. Advertisements

  3. I find usage of a symbol quite clumsy. One of the drawbacks is that it
    cannot address a single instance's method easily. I'd use at least blocks
    for this. But why not simply use Observer? It's the exact thing built
    for this situation - and it's part of the std distribution.

    http://www.ruby-doc.org/stdlib/libdoc/observer/rdoc/index.html

    Kind regards

    robert
     
    Robert Klemme, Feb 1, 2006
    #3
  4. Damphyr

    Jacob Fugal Guest

    I second Ara's mention of the Observable module. That said, I'd also
    simplify this particular example using a block:

    class A
    def set_callback( &blk )
    @notify =3D blk
    end
    def something
    puts "something"
    @notify.call if @notify
    end
    end

    a =3D A.new
    a.something
    a.set_callback { puts "hey" }
    a.something

    My general rule of thumb is that if something needs to be *done*, I
    register a proc, but if someone needs to *know*, I register an
    observer (using Observable).

    Jacob Fugal
     
    Jacob Fugal, Feb 1, 2006
    #4
  5. Damphyr

    Damphyr Guest

    Well, actually it would be a good start to remember the names of the
    patterns :) .
    *Then* I can remember that there is an implementation in the standard
    library. I've been staring at the screen for too long I guess.
    There is a slight difference though: In Observer the observer is an
    object that needs to specify the update method.
    I was aiming to provide the notifier with a method, any method, to call.
    Mine is a one-to-one callback between instances.
    With the observer pattern you get one-to-many and the observer can
    observe *any* observable object.
    More generic and at the end more practical because I will end up naming
    everything the same every time I use it.
    Just need to be careful to add one/delete one instead of reassigning the
    callback in order to replace an observer.
    Cheers,
    V.-
    --
    http://www.braveworld.net/riva

    ____________________________________________________________________
    http://www.freemail.gr - äùñåÜí õðçñåóßá çëåêôñïíéêïý ôá÷õäñïìåßïõ.
    http://www.freemail.gr - free email service for the Greek-speaking.
     
    Damphyr, Feb 1, 2006
    #5
  6. If you want to use another method, you can use a lambda as adapter:

    def observe(observable)
    m = lambda {|*a| my_other_method(*a)}
    class <<m
    alias update call
    end
    observable.add_observer m
    end

    Kind regards

    robert
     
    Robert Klemme, Feb 1, 2006
    #6
  7. Damphyr

    ara.t.howard Guest

    heh. i'm guilty often on that count too - the design patterns books is pretty
    dry reading to be sure.
    you can leverage the exiting code:

    harp:~ > cat a.rb
    require "yaml"
    require "observer"

    module SoloObservable
    include Observable
    module Updateable
    attr_accessor "update_method"
    attr_accessor "update_context"
    def update *a, &b
    if Proc === update_method
    begin
    self.update_context = [a, b]
    instance_eval &update_method
    ensure
    self.update_context = nil
    end
    else
    send update_method, *a, &b
    end
    end
    end
    def add_observer observer, cb = nil, &cbb
    observer.extend Updateable
    observer.update_method = cb || cbb
    delete_observers
    super observer
    end
    def notify_observer *a, &b
    changed and notify_observers *a, &b
    end
    end

    class A
    include SoloObservable
    def method_that_notifies() notify_observer 42 end
    end

    class B
    def callback(arg) y "arg" => arg end
    end

    class C
    attr "answer"
    def initialize() @answer = "forty-two" end
    def klass() self.class.name end
    end


    a, b, c = [A,B,C].map{|k| k::new}

    a.add_observer b, "callback"
    a.method_that_notifies

    puts

    a.add_observer(c){ y "update_context" => update_context.inspect, "klass" => klass, "answer" => answer }
    a.method_that_notifies



    harp:~ > ruby a.rb
    ---
    arg: 42

    ---
    klass: C
    answer: forty-two
    update_context: "[[42], nil]"


    this allows arbitrary method/block callbacks and confines to a single observer.
    blocks are evaluated in instance scope and have access to notification params
    via update_context.

    hth.

    -a
     
    ara.t.howard, Feb 1, 2006
    #7
  8. Damphyr

    Payton Swick Guest

    Not sure if it's any help, but here's something I've been using in my
    code which is sort of the reverse of Observable.

    This is actually just a snippet of the whole thing without comments or
    some fancy features I added. I may put it up as a gem if there's interest.

    module HandlesEvents
    attr_reader :handlers

    def on_event(*triggers, &handler)
    @handlers ||= Hash.new
    triggers.each { |trigger| @handlers[trigger] = handler }
    end

    def handles?(trigger)
    @handlers ||= Hash.new
    return true unless @handlers[trigger].nil?
    false
    end

    def handle(trigger)
    @handlers ||= Hash.new
    @handlers[trigger].call(trigger) unless @handlers[trigger].nil?
    end
    end

    -Payton
     
    Payton Swick, Feb 1, 2006
    #8
  9. Damphyr

    Payton Swick Guest

    Usage, by the way:

    class A
    include HandlesEvents
    end

    a = A.new
    a.on_event:)hello, :hi) { puts "Hello world!" }
    a.handle:)hello) # => prints "Hello world!"

    -Payton

     
    Payton Swick, Feb 2, 2006
    #9
  10. T24gMi8xLzA2LCBEYW1waHlyIDxkYW1waHlyQGZyZWVtYWlsLmdyPiB3cm90ZToKPiBXaGF0IGFy
    ZSBteSBhbHRlcm5hdGl2ZXM/CgpIZXJlJ3MgYSB0aG91Z2h0OgoKY2xhc3MgV3JhcHBlcgogIGlu
    c3RhbmNlX21ldGhvZHMuZ3JlcCgvXlteX117MiwyfS8pLmVhY2h7fGltfAogICAgdW5kZWZfbWV0
    aG9kIGltCiAgfQogIGF0dHJfYWNjZXNzb3IgOmhhbmRsZXJzCgogIGRlZiBpbml0aWFsaXplKG9i
    aikKICAgIEBoYW5kbGVycyB8fD0gSGFzaC5uZXd7fGgsa3wgaFtrXSA9IFtdfQogICAgQG9iaiA9
    IG9iagogIGVuZAoKICBkZWYgbWV0aG9kX21pc3NpbmcobW5hbWUsICphcmdzLCAmYmxvY2spCiAg
    ICBpZiBtbmFtZS50b19zWzAsM10gPT0gIm9uXyIKICAgICAgQGhhbmRsZXJzW21uYW1lLnRvX3Nb
    My4uLTFdXSA8PCBbYmxvY2ssIGFyZ3NdCiAgICBlbHNlCiAgICAgIEBoYW5kbGVyc1ttbmFtZS50
    b19zXS5lYWNoe3xoYW5kbGVyLCBoYXJnc3wKICAgICAgICBoYW5kbGVyLmNhbGwoKihoYXJncytb
    bW5hbWVdK2FyZ3MpKQogICAgICB9CiAgICAgIEBvYmouc2VuZChtbmFtZSwgKmFyZ3MsICZibG9j
    aykKICAgIGVuZAogIGVuZAoKZW5kCgoKdyA9IFdyYXBwZXIubmV3KCJmb28iKQojPT4gImZvbyIK
    Cncub25fcmV2ZXJzZXsgcHV0cyAicmV2ZXJzZWQhIiB9Cncub25fc3BsaXR7fG0sc3BsaXR0ZXJ8
    IHB1dHMgInNvbWVvbmUncyBzcGxpdHRpbmcgd2l0aCAje3NwbGl0dGVyLmluc3BlY3R9IiB9Cgp3
    LnJldmVyc2UKI3JldmVyc2VkIQojPT4gIm9vZiIKCncuc3BsaXQoLy8pCiNzb21lb25lJ3Mgc3Bs
    aXR0aW5nIHdpdGggLy8KIz0+IFsiZiIsICJvIiwgIm8iXQo=
     
    Ilmari Heikkinen, Feb 2, 2006
    #10
    1. Advertisements

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 (here). After that, you can post your question and our members will help you out.