# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://xmpp4r.github.io module Jabber ## # This class manages a list of callbacks. # # ==Callbacks management and priority # # Callbacks are managed by the class CallbackList. When they are added, a # priority (just a number or anything Comparable with other priorities) is # specified. The biggest the priority is, the earliest the callback will be # considered. # # Callbacks are processed for a given set of objects as long as they return # false. If you want to stop processing, you must return true. Example : # cbl = CallbackList.new # c1 = false # c2 = false # c3 = false # cbl.add(10) { c1 = true; 1 } # cbl.add(5) { c2 = true; true } # cbl.add(0) { c3 = true } # cbl.process('aa') # puts "#{c1} #{c2} #{c3}" # This example would display "true true false" as callbacks processing was # stopped after the second callback returned true. # # In XMPP4R, callbacks' priorities are quite normalized since we want to be # able to "cascade" callbacks in a clean way. Here are values your code should # take into account : # # >= 200:: logging & debugging callbacks. Those callbacks should not consume # elements. # 100-199:: Where Helpers register their callbacks. The normal value is 100, # and Helpers shouldn't register something else unless there's a # very good reason to. # < 100:: all those numbers are normally available for your application. # That's enough, don't you think ? class CallbackList include Enumerable # Create a new list of callbacks def initialize @list = [] end ## # Add a callback to the list # # List will be sorted afterwards # # prio:: [Integer] the callback's priority, the higher, the sooner. # ref:: [String] the callback's reference # block:: [Block] a block to execute # return:: [Jabber::CallbackList] The list, for chaining def add(prio = 0, ref = nil, proc = nil, &block) block = proc if proc @list.push(Callback.new(prio, ref, block)) @list.sort! { |a, b| b.priority <=> a.priority } self end ## # Delete a callback by reference # ref:: [String] the reference of the callback to delete # return:: [CallBackList] The list, for chaining def delete(ref) @list.delete_if { |item| item.ref == ref } self end def each(&block) @list.each(&block) end ## # Number of elements in the list # return:: [Integer] The number of elements def length @list.length end ## # Process an element through all my callbacks. returns e.consumed? # e:: [Object] The elements to pass to the callback. You can pass # several, but of course, you block must know how to handle them. # return:: [Boolean] true if the element has been consumed def process(*e) # If somebody adds a new callback the list will get modified # and sorted(!) while still iterating through it. So we use a # local copy of @list. Any freshly added callback will receive # the next stanzas, not the current. list = @list.dup # process through callbacks list.each do |item| return true if item.block.call(*e) == true end false end end # This class is used to store callbacks inside CallbackList. See the # CallbackList class for more detailed explanations. class Callback # The Callback's priority attr_reader :priority # The Callback's reference, using for deleting purposes attr_reader :ref # The Callback's block to execute attr_reader :block ## # Create a new callback # priority:: [Integer] the callback's priority. The higher, the sooner it # will be executed # ref:: [String] The callback's reference def initialize(priority = 0, ref = nil, block = Proc.new {}) @priority = priority @ref = ref @block = block end def to_s "#<#{[self.class, priority, ref].compact * " "}>" end end end