module Blather
##
# Base XML Node
# All XML classes subclass XMPPNode
# it allows the addition of helpers
class XMPPNode < XML::Node
@@registrations = {}
class_inheritable_accessor :xmlns,
:name
##
# Automatically sets the namespace registered by the subclass
def self.new(name = nil, content = nil)
name ||= self.name
args = []
args << name.to_s if name
args << content if content
elem = super *args
elem.xmlns = xmlns
elem
end
##
# Lets a subclass register itself
#
# This registers a namespace that is used when looking
# up the class name of the object to instantiate when a new
# stanza is received
def self.register(name, xmlns = nil)
self.name = name.to_s
self.xmlns = xmlns
@@registrations[[self.name, self.xmlns]] = self
end
##
# Find the class to use given the name and namespace of a stanza
def self.class_from_registration(name, xmlns)
name = name.to_s
@@registrations[[name, xmlns]] || @@registrations[[name, nil]]
end
##
# Looks up the class to use then instantiates an object
# of that class and imports all the node's attributes
# and children into it.
def self.import(node)
klass = class_from_registration(node.element_name, node.xmlns)
if klass && klass != self
klass.import(node)
else
new(node.element_name).inherit(node)
end
end
##
# Quickway of turning itself into a proper object
def to_stanza
self.class.import self
end
def xmlns=(ns)
attributes.remove :xmlns
self['xmlns'] = ns if ns
end
def xmlns
self['xmlns']
end
##
# Remove a child with the name and (optionally) namespace given
def remove_child(name, ns = nil)
name = name.to_s
self.each { |n| n.remove! if n.element_name == name && (!ns || n.xmlns == ns) }
end
##
# Remove all children with a given name
def remove_children(name)
name = name.to_s
self.find(name).each { |n| n.remove! }
end
##
# Pull the content from a child
def content_from(name)
name = name.to_s
(child = self.detect { |n| n.element_name == name }) ? child.content : nil
end
##
# Create a copy
def copy(deep = true)
self.class.new(self.element_name).inherit(self)
end
##
# Inherit all of stanza's attributes and children
def inherit(stanza)
inherit_attrs stanza.attributes
stanza.children.each { |c| self << c.copy(true) }
self
end
##
# Inherit only stanza's attributes
def inherit_attrs(attrs)
attrs.each { |a| self[a.name] = a.value }
self
end
##
# Turn itself into a string and remove all whitespace between nodes
def to_s
# TODO: Fix this for HTML nodes (and any other that might require whitespace)
super.gsub(">\n<", '><')
end
##
# Override #find to work when a node isn't attached to a document
def find(what, nslist = nil)
(self.doc ? super(what, nslist) : select { |i| i.element_name == what})
end
end #XMPPNode
end