# Copyright (c) 2009-2010 Paolo Capriotti # # This library is free software; you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as # published by the Free Software Foundation; either version 3 of the # License, or (at your option) any later version. require 'observer' require 'rui/utils' # # A mixin to make it easier to implement observers using the standard observer # library. # # Mixing Observer into a class generates a default update method, # which reacts to notification in the form of a hash, and calls appropriate # event handlers, obtained by prepending "on_" to each key, and passing it the # corresponding value. # # For example, an event containing the following data # # { :pressed => { :x => 34, :y => 11 }, # :released => { :x => 10, :y => 76 } } # # would result in the following method calls: # # on_pressed(:x => 34, :y => 11) # on_released(:x => 10, :y => 76) # # As a special case, if an event takes more than 1 parameter, the corresponding # value is assumed to be an array, and its elements are passed as arguments to # the event. # module Observer # # A default implementation for the update function. # # Parses notification data and dispatches to the corresponding events. # def update(data) data.each_key do |key| m = begin method("on_#{key}") rescue NameError end if m case m.arity when 0 m[] when 1 m[data[key]] else m[*data[key]] end end end end end # # Extensions to the standard Observable module of the observer library. # # This mixin allows to define event handlers dynamically, without having to # create an Observer class for each handler. # # For example, assuming button is an instance of some observable class: # # count = 0 # button.on(:clicked) do # count += 1 # puts "I have been clicked #{count} times" # end # # Events can be fired with the fire method, and support arbitrary # arguments. # module Observable # # Alias to observe. # def on(event, &blk) observe(event, &blk) end # # Create a dynamic observer handling a given event. # # @param event [Symbol] the event to handle # @param &blk [Block] event handler # @return an observer object, which can be later used to remove # the event handler. # def observe(event, &blk) obs = SimpleObserver.new(event, &blk) add_observer obs # return observer so that we can remove it later obs end # Create a limited observer handling a given event. # # A limited observer behaves similarly to a normal dynamic observer, but in # addition, it keeps track of the return valur of the handler. When the # handler returns true, the observer is destroyed. # # @param event [Symbol] the event to handle # @param &blk [Block] event handler # @return an observer object, which can be later used to remove # the event handler. # def observe_limited(event, &blk) obs = LimitedObserver.new(self, event, &blk) add_observer obs obs end # # Fire an event. # # @param e [Symbol, Hash] event and arguments. This needs to be either # a Symbol, or a Hash with a single key corresponding to the event, and the # value being the event data to pass to the handler. # def fire(e) changed notify_observers any_to_event(e) end private def any_to_event(e) if e.is_a? Symbol { e => nil } else e end end end class Proc def generic_call(args) case arity when 0 call when 1 call(args) else call(*args) end end end class SimpleObserver def initialize(event, &blk) @event = event @blk = blk end def update(data) if data.has_key?(@event) @blk.generic_call(data[@event]) end end end class LimitedObserver < SimpleObserver def initialize(observed, event, &blk) super(event, &blk) @observed = observed end def update(data) remove = super(data) @observed.delete_observer(self) if remove remove end end