[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

callbacks, events, notification in general

Vassilis Rizopoulos

2/1/2006 3:31:00 PM

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

____________________________________________________________________
http://www.f... - äùñåÜí õðçñåóßá çëåêôñïíéêïý ôá÷õäñïìåßïõ.
http://www.f... - free email service for the Greek-speaking.


9 Answers

Ara.T.Howard

2/1/2006 4:02:00 PM

0

Robert Klemme

2/1/2006 4:03:00 PM

0

Damphyr wrote:
> 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.-

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

Kind regards

robert


Jacob Fugal

2/1/2006 4:26:00 PM

0

On 2/1/06, Damphyr <damphyr@freemail.gr> wrote:
> 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

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 = blk
end
def something
puts "something"
@notify.call if @notify
end
end

a = 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


Vassilis Rizopoulos

2/1/2006 4:46:00 PM

0

ara.t.howard@noaa.gov wrote:
> On Thu, 2 Feb 2006, Damphyr wrote:
>
>> 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.-
>
> 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! ;-)
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.braveworl...

____________________________________________________________________
http://www.f... - äùñåÜí õðçñåóßá çëåêôñïíéêïý ôá÷õäñïìåßïõ.
http://www.f... - free email service for the Greek-speaking.


Robert Klemme

2/1/2006 5:25:00 PM

0

Damphyr wrote:
> ara.t.howard@noaa.gov wrote:
>> On Thu, 2 Feb 2006, Damphyr wrote:
>>
>>> 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.-
>>
>> 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! ;-)
> 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,

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

Ara.T.Howard

2/1/2006 6:11:00 PM

0

Payton Swick

2/1/2006 10:21:00 PM

0

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

Damphyr wrote:
> 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.-
>
>


Payton Swick

2/2/2006 1:35:00 PM

0

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 wrote:
> 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
>
> Damphyr wrote:
>
>> 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.-


Ilmari Heikkinen

2/2/2006 2:42:00 PM

0

On 2/1/06, Damphyr <damphyr@freemail.gr> wrote:> What are my alternatives?Here's a thought:class Wrapper instance_methods.grep(/^[^_]{2,2}/).each{|im| undef_method im } attr_accessor :handlers def initialize(obj) @handlers ||= Hash.new{|h,k| h[k] = []} @obj = obj end def method_missing(mname, *args, &block) if mname.to_s[0,3] == "on_" @handlers[mname.to_s[3..-1]] << [block, args] else @handlers[mname.to_s].each{|handler, hargs| handler.call(*(hargs+[mname]+args)) } @obj.send(mname, *args, &block) end endendw = Wrapper.new("foo")#=> "foo"w.on_reverse{ puts "reversed!" }w.on_split{|m,splitter| puts "someone's splitting with #{splitter.inspect}" }w.reverse#reversed!#=> "oof"w.split(//)#someone's splitting with //#=> ["f", "o", "o"]