Class Jabber::Roster::Helper
In: lib/xmpp4r/roster/helper/roster.rb
Parent: Object

The Roster helper intercepts <iq/> stanzas with Jabber::IqQueryRoster and <presence/> stanzas, but provides cbs which allow the programmer to keep track of updates.

Methods

Classes and Modules

Class Jabber::Roster::Helper::RosterItem

Attributes

items  [R]  All items in your roster
items:[Hash] ([JID] => [Roster::Helper::RosterItem])

Public Class methods

Initialize a new Roster helper

Registers its cbs (prio = 120, ref = self)

Request a roster (Remember to send initial presence afterwards!)

[Source]

    # File lib/xmpp4r/roster/helper/roster.rb, line 28
28:       def initialize(stream)
29:         @stream = stream
30:         @items = {}
31:         @items_lock = Mutex.new
32:         @query_cbs = CallbackList.new
33:         @update_cbs = CallbackList.new
34:         @presence_cbs = CallbackList.new
35:         @subscription_cbs = CallbackList.new
36:         @subscription_request_cbs = CallbackList.new
37: 
38:         # Register cbs
39:         stream.add_iq_callback(120, self) { |iq|
40:           handle_iq(iq)
41:         }
42:         stream.add_presence_callback(120, self) { |pres|
43:           handle_presence(pres)
44:         }
45:         
46:         # Request the roster
47:         rosterget = Iq.new_rosterget
48:         stream.send(rosterget)
49:       end

Public Instance methods

Get an item by jid

If not available tries to look for it with the resource stripped

[Source]

     # File lib/xmpp4r/roster/helper/roster.rb, line 224
224:       def [](jid)
225:         jid = JID.new(jid) unless jid.kind_of? JID
226: 
227:         @items_lock.synchronize {
228:           if @items.has_key?(jid)
229:             @items[jid]
230:           elsif @items.has_key?(jid.strip)
231:             @items[jid.strip]
232:           else
233:             nil
234:           end
235:         }
236:       end

Accept a subscription request

  • Sends a <presence type=‘subscribed’/> stanza
  • Adds the contact to your roster
jid:[JID] of contact
iname:[String] Optional roster item name

[Source]

     # File lib/xmpp4r/roster/helper/roster.rb, line 327
327:       def accept_subscription(jid, iname=nil)
328:         pres = Presence.new.set_type(:subscribed).set_to(jid.strip)
329:         @stream.send(pres)
330: 
331:         unless self[jid.strip]
332:           request = Iq.new_rosterset
333:           request.query.add(Jabber::Roster::RosterItem.new(jid.strip, iname))
334:           @stream.send_with_id(request) { true }
335:         end
336:       end

Add a user to your roster

Threading is encouraged as the function waits for a result. ErrorException is thrown upon error.

See Jabber::Roster::Helper::RosterItem#subscribe for details about subscribing. (This method isn‘t used here but the same functionality applies.)

If the item is already in the local roster it will simply send itself

jid:[JID] to add
iname:[String] Optional item name
subscribe:[Boolean] Whether to subscribe to this jid

[Source]

     # File lib/xmpp4r/roster/helper/roster.rb, line 303
303:       def add(jid, iname=nil, subscribe=false)
304:         if self[jid]
305:           self[jid].send
306:         else
307:           request = Iq.new_rosterset
308:           request.query.add(Jabber::Roster::RosterItem.new(jid, iname))
309:           @stream.send_with_id(request) { true }
310:           # Adding to list is handled by handle_iq
311:         end
312: 
313:         if subscribe
314:           # Actually the item *should* already be known now,
315:           # but we do it manually to exclude conditions.
316:           pres = Presence.new.set_type(:subscribe).set_to(jid.strip)
317:           @stream.send(pres)
318:         end
319:       end

Add a callback for Jabber::Presence updates

This will be called for <presence/> stanzas for known RosterItems. Unknown JIDs may still pass and can be caught via Jabber::Stream#add_presence_callback.

The block receives three objects:

[Source]

    # File lib/xmpp4r/roster/helper/roster.rb, line 85
85:       def add_presence_callback(prio = 0, ref = nil, &block)
86:         @presence_cbs.add(prio, ref, block)
87:       end

Add a callback to be called when a query has been processed

Because update callbacks are called for each roster item, this may be appropriate to notify that anything has updated.

Arguments for callback block: The received <iq/> stanza

[Source]

    # File lib/xmpp4r/roster/helper/roster.rb, line 58
58:       def add_query_callback(prio = 0, ref = nil, &block)
59:         @query_cbs.add(prio, ref, block)
60:       end

Add a callback for subscription updates, which will be called upon receiving a <presence/> stanza with type:

  • :subscribed
  • :unsubscribe
  • :unsubscribed

The block receives two objects:

[Source]

     # File lib/xmpp4r/roster/helper/roster.rb, line 100
100:       def add_subscription_callback(prio = 0, ref = nil, &block)
101:         @subscription_cbs.add(prio, ref, block)
102:       end

Add a callback for subscription requests, which will be called upon receiving a <presence type=‘subscribe’/> stanza

The block receives two objects:

Response to this event can be taken with accept_subscription and decline_subscription.

Example usage:

 my_roster.add_subscription_request_callback do |item,presence|
   if accept_subscription_requests
     my_roster.accept_subscription(presence.from)
   else
     my_roster.decline_subscription(presence.from)
   end
 end

[Source]

     # File lib/xmpp4r/roster/helper/roster.rb, line 123
123:       def add_subscription_request_callback(prio = 0, ref = nil, &block)
124:         @subscription_request_cbs.add(prio, ref, block)
125:       end

Add a callback for Jabber::Roster::Helper::RosterItem updates

Note that this will be called much after initialization for the answer of the initial roster request

The block receives two objects:

[Source]

    # File lib/xmpp4r/roster/helper/roster.rb, line 71
71:       def add_update_callback(prio = 0, ref = nil, &block)
72:         @update_cbs.add(prio, ref, block)
73:       end

Decline a subscription request

  • Sends a <presence type=‘unsubscribed’/> stanza

[Source]

     # File lib/xmpp4r/roster/helper/roster.rb, line 341
341:       def decline_subscription(jid)
342:         pres = Presence.new.set_type(:unsubscribed).set_to(jid.strip)
343:         @stream.send(pres)
344:       end

Returns the list of RosterItems which, stripped, are equal to the one you are looking for.

[Source]

     # File lib/xmpp4r/roster/helper/roster.rb, line 241
241:       def find(jid)
242:         jid = JID.new(jid) unless jid.kind_of? JID
243: 
244:         j = jid.strip
245:         l = {}
246:         @items_lock.synchronize {
247:           @items.each_pair do |k, v|
248:             l[k] = v if k.strip == j
249:           end
250:         }
251:         l
252:       end

Get items in a group

When group is nil, return ungrouped items

group:[String] Group name
result:Array of [RosterItem]

[Source]

     # File lib/xmpp4r/roster/helper/roster.rb, line 277
277:       def find_by_group(group)
278:         res = []
279:         @items_lock.synchronize {
280:           @items.each_pair do |jid,item|
281:             res.push(item) if item.groups.include?(group)
282:             res.push(item) if item.groups == [] and group.nil?
283:           end
284:         }
285:         res
286:       end

Groups in this Roster, sorted by name

Contains nil if there are ungrouped items

result:[Array] containing group names (String)

[Source]

     # File lib/xmpp4r/roster/helper/roster.rb, line 260
260:       def groups
261:         res = []
262:         @items_lock.synchronize {
263:           @items.each_pair do |jid,item|
264:             res += item.groups
265:             res += [nil] if item.groups == []
266:           end
267:         }
268:         res.uniq.sort { |a,b| a.to_s <=> b.to_s }
269:       end

Private Instance methods

Handle received <iq/> stanzas, used internally

[Source]

     # File lib/xmpp4r/roster/helper/roster.rb, line 132
132:       def handle_iq(iq)
133:         if iq.query.kind_of?(IqQueryRoster)
134:           # If the <iq/> contains <error/> we just ignore that
135:           # and assume an empty roster
136:           iq.query.each_element('item') do |item|
137:             # Handle deletion of item
138:             if item.subscription == :remove
139:               @items_lock.synchronize {
140:                 @items.delete(item.jid)
141:               }
142: 
143:             else
144:               olditem = nil
145:               @items_lock.synchronize {
146:                 if @items.has_key?(item.jid)
147:                   olditem = RosterItem.new(@stream).import(@items[item.jid])
148: 
149:                   # Clear first, because import doesn't
150:                   @items[item.jid].iname = nil
151:                   @items[item.jid].subscription = nil
152:                   @items[item.jid].ask = nil
153: 
154:                   @items[item.jid].import(item)
155:                 else
156:                   @items[item.jid] = RosterItem.new(@stream).import(item)
157:                 end
158:               }
159:               @update_cbs.process(olditem, @items[item.jid])
160:             end
161:           end
162: 
163:           @query_cbs.process(iq)
164:         else
165:           false
166:         end
167:       end

Handle received <presence/> stanzas, used internally

[Source]

     # File lib/xmpp4r/roster/helper/roster.rb, line 172
172:       def handle_presence(pres)
173:         item = self[pres.from]
174:         if [:subscribed, :unsubscribe, :unsubscribed].include?(pres.type)
175:           @subscription_cbs.process(item, pres)
176:           true
177:         elsif pres.type == :subscribe
178:           @subscription_request_cbs.process(item, pres)
179:           true
180:         else
181:           unless item.nil?
182:             update_presence(item, pres)
183:             true  # Callback consumed stanza
184:           else
185:             false # Callback did not consume stanza
186:           end
187:         end
188:       end

Update the presence of an item, used internally

Callbacks are called here

[Source]

     # File lib/xmpp4r/roster/helper/roster.rb, line 195
195:       def update_presence(item, pres)
196: 
197:         # This requires special handling, to announce all resources offline
198:         if pres.from.resource.nil? and pres.type == :error
199:           oldpresences = []
200:           item.each_presence do |oldpres|
201:             oldpresences << oldpres
202:           end
203: 
204:           item.add_presence(pres)
205:           oldpresences.each { |oldpres|
206:             @presence_cbs.process(item, oldpres, pres)
207:           }
208:         else
209:           oldpres = item.presence(pres.from).nil? ?
210:             nil :
211:             Presence.new.import(item.presence(pres.from))
212: 
213:           item.add_presence(pres)
214:           @presence_cbs.process(item, oldpres, pres)
215:         end
216:       end

[Validate]