Skip to content

Instantly share code, notes, and snippets.

@julien51
Created March 11, 2009 00:15
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save julien51/77215 to your computer and use it in GitHub Desktop.
Save julien51/77215 to your computer and use it in GitHub Desktop.
require "rubygems"
require "nokogiri"
require "eventmachine"
##
# This is the XML SAX Parser that accepts "pushed" content used by Babylon Gem.
class XmppParser < Nokogiri::XML::SAX::Document
##
# Initialize the parser and adds the callback that will be called upen stanza completion
def initialize(&callback)
@callback = callback
super()
@parser = Nokogiri::XML::SAX::Parser.new(self)
@doc = nil
@elem = nil
end
##
# Parses the received data
def parse(data)
@parser.parse data
end
##
# Called when the document received in the stream is started
def start_document
@doc = Nokogiri::XML::Document.new
end
##
# Adds characters to the current element (being parsed)
def characters(string)
@elem.add_child(Nokogiri::XML::Text.new(string, @doc))
end
##
# Instantiate a new current Element, adds the corresponding attributes and namespaces
# The new element is eventually added to a parent element (if present).
# If this element is the first element (the root of the document), then instead of adding it to a parent, we add it to the document itself. In this case, the current element will not be terminated, so we activate the callback immediately.
def start_element(qname, attributes = [])
e = Nokogiri::XML::Element.new(qname, @doc)
add_namespaces_and_attributes_to_node(attributes, e)
# Adding the newly created element to the @elem that is being parsed, or, if no element is being parsed, then we set the @root and the @elem to be this newly created element.
@elem = @elem ? @elem.add_child(e) : (@root = e)
if @elem.name == "stream:stream"
# Should be called only for stream:stream.
# We re-initialize the document and set its root to be the doc.
# Also, we activate the callback since this element will never end.
@doc = Nokogiri::XML::Document.new
@doc.root = @root = @elem
@callback.call(@elem)
end
end
##
# Terminates the current element and calls the callback
#
# BUG : We might have a problem of missed stanzas : (this was taken from the log, the second iq stanza hasn't been parsed!)
# DEBUG BABYLON: RECEIVED : <iq from='julien@superfeedr.com/babylon_client_6970' to='firehoser.superfeedr.com' type='set' id='1419'>
# <pubsub xmlns='http://jabber.org/protocol/pubsub'>
# <subscribe jid='julien@superfeedr.com' node='http://feeds2.feedburner.com/NotifixiousFoundersBlog'/>
# </pubsub>
# </iq><iq from='julien@superfeedr.com/babylon_client_6970' to='firehoser.superfeedr.com' type='set' id='7858'>
# <pubsub xmlns='http://jabber.org/protocol/pubsub'>
# <unsubscribe jid='julien@superfeedr.com' node='http://feeds2.feedburner.com/NotifixiousFoundersBlog'/>
# </pubsub>
# </iq>
# DEBUG BABYLON: PARSED : <iq from="julien@superfeedr.com/babylon_client_6970" to="firehoser.superfeedr.com" type="set" id="1419">
# <pubsub xmlns:xmlns="http://jabber.org/protocol/pubsub" xmlns:http://jabber.org/protocol/pubsub="xmlns">
# <subscribe jid="julien@superfeedr.com" node="http://feeds2.feedburner.com/NotifixiousFoundersBlog"/>
# </pubsub>
# </iq>
# INFO BABYLON: ROUTING TO SubscriptionsController::subscribe
def end_element(name)
if @elem
if @elem.parent == @root
@callback.call(@elem)
# And we also need to remove @elem from its tree
@elem.unlink
# And the current elem is the next sibling or the root
@elem = @root
else
@elem = @elem.parent
end
end
end
private
##
# Adds namespaces and attributes. Nokogiri passes them as a array of [name, value, name, value]...
def add_namespaces_and_attributes_to_node(attrs, node)
(attrs.size / 2).times do |i|
name, value = attrs[2 * i], attrs[2 * i + 1]
if name =~ /xmlns/
node.add_namespace(name, value)
else
node.set_attribute name, value
end
end
end
end
# We're simulating an XMPP session to show that the last element it not parsed correctly...
content = ["<?xml version='1.0'?><stream:stream xmlns:stream='http://etherx.jabber.org/streams' xmlns='jabber:component:accept' id='314321979' from='superfeedr.com'>",
"<handshake/>",
"<iq from='julien@superfeedr.com/babylon_client_6970' to='firehoser.superfeedr.com' type='set' id='1419'>
<pubsub xmlns='http://jabber.org/protocol/pubsub'>
<subscribe jid='julien@superfeedr.com' node='http://feeds2.feedburner.com/NotifixiousFoundersBlog'/>
</pubsub>
</iq><iq from='julien@superfeedr.com/babylon_client_6970' to='firehoser.superfeedr.com' type='set' id='7858'>
<pubsub xmlns='http://jabber.org/protocol/pubsub'>
<unsubscribe jid='julien@superfeedr.com' node='http://feeds2.feedburner.com/NotifixiousFoundersBlog'/>
</pubsub>
</iq>"]
@parser = XmppParser.new do |element|
puts "========="
puts "PARSED : "
puts "#{element} "
puts ""
end
EM.run do
index = 0
EM.add_periodic_timer(1) {
if index < content.size
@parser.parse(content[index])
index = index + 1
else
EM.stop_event_loop
end
}
end
# Actual output is :
# $ ruby saxparser.rb
# =========
# PARSED :
# <stream:stream xmlns:xmlns:stream="http://etherx.jabber.org/streams" xmlns:xmlns="jabber:component:accept" xmlns:stream:http://etherx.jabber.org/streams="xmlns:stream" xmlns:jabber:component:accept="xmlns" id="314321979" from="superfeedr.com"/>
#
# =========
# PARSED :
# <handshake/>
#
# =========
# PARSED :
# <iq from="julien@superfeedr.com/babylon_client_6970" to="firehoser.superfeedr.com" type="set" id="1419">
# <pubsub xmlns:xmlns="http://jabber.org/protocol/pubsub" xmlns:http://jabber.org/protocol/pubsub="xmlns">
# <subscribe jid="julien@superfeedr.com" node="http://feeds2.feedburner.com/NotifixiousFoundersBlog"/>
# </pubsub>
# </iq>
# While it should probably be :
# $ ruby saxparser.rb
# =========
# PARSED :
# <stream:stream xmlns:xmlns:stream="http://etherx.jabber.org/streams" xmlns:xmlns="jabber:component:accept" xmlns:stream:http://etherx.jabber.org/streams="xmlns:stream" xmlns:jabber:component:accept="xmlns" id="314321979" from="superfeedr.com"/>
#
# =========
# PARSED :
# <handshake/>
#
# =========
# PARSED :
# <iq from="julien@superfeedr.com/babylon_client_6970" to="firehoser.superfeedr.com" type="set" id="1419">
# <pubsub xmlns:xmlns="http://jabber.org/protocol/pubsub" xmlns:http://jabber.org/protocol/pubsub="xmlns">
# <subscribe jid="julien@superfeedr.com" node="http://feeds2.feedburner.com/NotifixiousFoundersBlog"/>
# </pubsub>
# </iq>
#
# =========
# PARSED :
# <iq from='julien@superfeedr.com/babylon_client_6970' to='firehoser.superfeedr.com' type='set' id='7858'>
# <pubsub xmlns='http://jabber.org/protocol/pubsub'>
# <unsubscribe jid='julien@superfeedr.com' node='http://feeds2.feedburner.com/NotifixiousFoundersBlog'/>
# </pubsub>
# </iq>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment